www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.ldc - RISC-V port

reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
I have druntime working with the RISC-V backend (32-bit / Newlib 
based; I got backend errors for 64-bit; I can't test glibc in 
32-bit because AFAIK there's no 32-bit RISC-V Linux).

It needed some workarounds due to the lack of proper atomics and 
TLS support in the backend, as well as Newlib limitations, but 
that's mostly taken care of. For Phobos to work properly the 
backend needs to support exceptions. Currently the backend seems 
to only emit code for the non-exceptional code path.

Does anyone here understand what an LLVM backend needs to do to 
get code generated for the exception stuff (e.g. landingpad, 
etc.)? I tried to look in the ARM backend but it wasn't very 
illuminating...
May 17 2018
next sibling parent =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Thursday, 17 May 2018 at 15:36:05 UTC, Luís Marques wrote:
 I have druntime working with the RISC-V backend (32-bit / 
 Newlib based; I got backend errors for 64-bit; I can't test 
 glibc in 32-bit because AFAIK there's no 32-bit RISC-V Linux).

 It needed some workarounds due to the lack of proper atomics 
 and TLS support in the backend, as well as Newlib limitations, 
 but that's mostly taken care of. For Phobos to work properly 
 the backend needs to support exceptions. Currently the backend 
 seems to only emit code for the non-exceptional code path.

 Does anyone here understand what an LLVM backend needs to do to 
 get code generated for the exception stuff (e.g. landingpad, 
 etc.)? I tried to look in the ARM backend but it wasn't very 
 illuminating...
To clarify: the LLVM IR generated by LDC has the whole code, but the backend behaves as if an exception could never be thrown, and the exception handling stuff gets optimized away. Or so it seems, anyway.
May 17 2018
prev sibling next sibling parent reply Johan Engelen <j j.nl> writes:
On Thursday, 17 May 2018 at 15:36:05 UTC, Luís Marques wrote:
 Does anyone here understand what an LLVM backend needs to do to 
 get code generated for the exception stuff (e.g. landingpad, 
 etc.)? I tried to look in the ARM backend but it wasn't very 
 illuminating...
Are you using LLVM trunk? RISC-V appears to be under active development: http://www.lowrisc.org/blog/2017/09/moving-risc-v-llvm-forwards/ http://www.lowrisc.org/llvm/status/ More experts to answer your question are to be found on the llvm-dev mailinglist. -Johan
May 17 2018
parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Thursday, 17 May 2018 at 17:45:24 UTC, Johan Engelen wrote:
 Are you using LLVM trunk? RISC-V appears to be under active 
 development:
 http://www.lowrisc.org/blog/2017/09/moving-risc-v-llvm-forwards/
 http://www.lowrisc.org/llvm/status/
Yes and no. I used to use the lowRISC patches, since the trunk version used to be severely behind. But there were some bugs in the patched backend that were preventing further progress. With the latest merges the trunk now seems more usable (more complete, and the bugs exhibited by the patches seem to no longer be there) so I'm using that. In any case, the author is the same (Alex Bradbury). I've been in contact with him, but he goes several months without replying, due to competing priorities and lack of funding. I guess I could ask for help in the llvm-dev mailing list. I thought people here might be more motivated to help, given the focus on D support.
May 17 2018
parent Joakim <dlang joakim.fea.st> writes:
On Thursday, 17 May 2018 at 18:05:37 UTC, Luís Marques wrote:
 On Thursday, 17 May 2018 at 17:45:24 UTC, Johan Engelen wrote:
 Are you using LLVM trunk? RISC-V appears to be under active 
 development:
 http://www.lowrisc.org/blog/2017/09/moving-risc-v-llvm-forwards/
 http://www.lowrisc.org/llvm/status/
Yes and no. I used to use the lowRISC patches, since the trunk version used to be severely behind. But there were some bugs in the patched backend that were preventing further progress. With the latest merges the trunk now seems more usable (more complete, and the bugs exhibited by the patches seem to no longer be there) so I'm using that. In any case, the author is the same (Alex Bradbury). I've been in contact with him, but he goes several months without replying, due to competing priorities and lack of funding. I guess I could ask for help in the llvm-dev mailing list. I thought people here might be more motivated to help, given the focus on D support.
I doubt any of us had to do much with exception-handling in a backend, it's just one of those features we get from llvm for free. Since we don't develop backends, you're better off asking the llvm guys, who do.
May 19 2018
prev sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
Hi Luís,

On 17 May 2018, at 16:36, Luís Marques via digitalmars-d-ldc wrote:
 Does anyone here understand what an LLVM backend needs to do to get 
 code generated for the exception stuff (e.g. landingpad, etc.)? I 
 tried to look in the ARM backend but it wasn't very illuminating...
I'm not sure there is a single answer to this question, as it is rather broad in scope. First of all, what is the exception ABI for your target? Is it table-based using DWARF frame information? Or is it {set,long}jmp-based? I don't quite remember whether this question has even been addressed for RISC-V so far, that is, whether there even is a compiler port that supports C++ exceptions, or an ABI document that prescribes a particular implementation. If there is none and you want to play around with an initial implementation of *something*, I'd probably choose the vanilla DWARF-based implementation as supported by libunwind. From there, it's a matter of setting the ExceptionType correctly in your MCAsmInfo subclass, and fixing up various other bits and pieces as required. (For example, the code generator might need some tweaks to make sure the frame offset information is up to date, etc.) Grepping lib/Backend for `DwarfCFI` should provide you with some inspiration. Best, David
May 20 2018
parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Sunday, 20 May 2018 at 17:32:53 UTC, David Nadlinger wrote:
 First of all, what is the exception ABI for your target? Is it 
 table-based using DWARF frame information? Or is it 
 {set,long}jmp-based?
It's table-based DWARF.
 I don't quite remember whether this question has even been 
 addressed for RISC-V so far, that is, whether there even is a 
 compiler port that supports C++ exceptions, or an ABI document 
 that prescribes a particular implementation.
Yeah, the GCC port of RISC-V includes exception support.
 From there, it's a matter of setting the ExceptionType 
 correctly in your MCAsmInfo subclass, and fixing up various 
 other bits and pieces as required. (For example, the code 
 generator might need some tweaks to make sure the frame offset 
 information is up to date, etc.) Grepping lib/Backend for 
 `DwarfCFI` should provide you with some inspiration.
Cool, I'll look into that.
May 21 2018
parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Monday, 21 May 2018 at 13:48:36 UTC, Luís Marques wrote:
 From there, it's a matter of setting the ExceptionType 
 correctly in your MCAsmInfo subclass, and fixing up various 
 other bits and pieces as required. (For example, the code 
 generator might need some tweaks to make sure the frame offset 
 information is up to date, etc.) Grepping lib/Backend for 
 `DwarfCFI` should provide you with some inspiration.
Cool, I'll look into that.
After setting the ExceptionType the exceptions weren't working correctly even with clang++. After diving into the details I found out that the use of .uleb128 in .gcc_except_table was preventing relocations of symbols referenced in that table from being generated. I changed LLVM's backend configuration in MCObjectFileInfo.cpp to use the correct types and with clang++ exceptions now seem to be working, at least the minimal case I tested. LDC even after rebuilding is still using .uleb128 in .gcc_except_table, which is surprising, since it would seem that that configuration only depends on the chosen triple. So now I'm solving that part of the puzzle.
May 22 2018
parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Tuesday, 22 May 2018 at 12:42:47 UTC, Luís Marques wrote:
 After setting the ExceptionType the exceptions weren't working 
 correctly even with clang++. After diving into the details I 
 found out that the use of .uleb128 in .gcc_except_table was 
 preventing relocations of symbols referenced in that table from 
 being generated. I changed  LLVM's backend configuration in 
 MCObjectFileInfo.cpp to use the correct types and with clang++ 
 exceptions now seem to be working, at least the minimal case I 
 tested. LDC even after rebuilding is still using .uleb128 in 
 .gcc_except_table, which is surprising, since it would seem 
 that that configuration only depends on the chosen triple. So 
 now I'm solving that part of the puzzle.
I still haven't found it. Since this part is LDC specific and not LLVM specific, help here would be most appreciated :D
May 22 2018
next sibling parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Tuesday, 22 May 2018 at 14:31:17 UTC, Luís Marques wrote:
 I still haven't found it. Since this part is LDC specific and 
 not LLVM specific, help here would be most appreciated :D
I think I mixed GCC code with clang++ code and came to the wrong conclusions. But I found in LLVM where I actually have to change to emit the table with .4byte instead of .uleb128.
May 22 2018
parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Tuesday, 22 May 2018 at 15:40:14 UTC, Luís Marques wrote:
 I think I mixed GCC code with clang++ code and came to the 
 wrong conclusions.
 But I found in LLVM where I actually have to change to emit the 
 table with .4byte instead of .uleb128.
Yup, I got the C++ exceptions to more or less work (it now jumps to the catch block, although it currently crashes in __cxa_end_catch with a misaligned instruction access). But that only works if I generate textual assembly and assemble that. If I generate an object file directly no relocations are generated for the table, and so it doesn't work.
May 22 2018
parent =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Tuesday, 22 May 2018 at 18:29:44 UTC, Luís Marques wrote:
 Yup, I got the C++ exceptions to more or less work (it now 
 jumps to the catch block, although it currently crashes in 
 __cxa_end_catch with a misaligned instruction access). But that 
 only works if I generate textual assembly and assemble that. If 
 I generate an object file directly no relocations are generated 
 for the table, and so it doesn't work.
The crashes were because the target didn't implement `getExceptionPointerRegister` and `getExceptionSelectorRegister`. In LLVM and druntime I have assumed those are x10 and x11, AKA a0 and a1, the argument passing/return value registers. Now I have to implement the CFI directives for stack adjustment.
May 23 2018
prev sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
On 22 May 2018, at 15:31, Luís Marques via digitalmars-d-ldc wrote:
 I still haven't found it. Since this part is LDC specific and not LLVM 
 specific, help here would be most appreciated :D
For future reference: In situations like that, it might be useful to compile both C++ and D sources to LLVM IR (`-emit-llvm -S` for Clang), and then manually assemble them using `llc`. Any difference in behaviour that remains must then be visible from the IR, rather than some potentially different codegen settings, etc. — David
May 23 2018
parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Wednesday, 23 May 2018 at 15:35:12 UTC, David Nadlinger wrote:
 For future reference: In situations like that, it might be 
 useful to compile both C++ and D sources to LLVM IR 
 (`-emit-llvm -S` for Clang), and then manually assemble them 
 using `llc`.

 Any difference in behaviour that remains must then be visible 
 from the IR, rather than some potentially different codegen 
 settings, etc.
Thanks! You know what's silly? I had activated the debug flag `debug = EH_personality`, but that debug code uses printf with varargs, and I hand't yet ported varargs, so that's what was crashing on the D side... arrg!
May 23 2018
parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Wednesday, 23 May 2018 at 16:10:47 UTC, Luís Marques wrote:
 On Wednesday, 23 May 2018 at 15:35:12 UTC, David Nadlinger 
 wrote:
 You know what's silly? I had activated the debug flag `debug = 
 EH_personality`, but that debug code uses printf with varargs, 
 and I hand't yet ported varargs, so that's what was crashing on 
 the D side... arrg!
Consider this code: extern(C) void drun() { try { throw new Exception("throw test"); } catch(Exception e) { writeln_("caught from D"); } } When an expection is thrown the function _d_throw_exception is called. In turn, it calls _Unwind_RaiseException. But that function fails, returning a high numeric value that isn't part of the expected return type enum (_Unwind_Reason_Code) values. So the D exception personality is never called, and the catch block is never executed. The only thing that is currently missing in the assembly is the CFI stack adjustment directives. But I don't think that's what's causing the problem, because 1) when those are missing in the C++ code basic exception handling still works, and 2) I've tried to manually add those to the assembly file and it makes no difference. I've also tried to make drun only throw a pre-existing __gshared global Exception instance object, to simplify the function and stack adjustment, but it makes no difference. The problem seems to be elsewhere. Here's the assembly for the following code: https://gist.github.com/luismarques/238f5bd98f8dfbf9c6356071dfc9c2c5 pragma(LDC_no_moduleinfo); extern(C) void writeln_(const(char)[] s) nothrow nogc; __gshared Exception e; extern(C) void allocException() { e = new Exception("test exception"); } extern(C) void drun() { try { throw e; } catch(Exception e) { writeln_("caught from D"); } } Any idea on what might be wrong?
May 24 2018
parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Thursday, 24 May 2018 at 14:01:48 UTC, Luís Marques wrote:
 When an expection is thrown the function _d_throw_exception is 
 called. In turn, it calls _Unwind_RaiseException. But that 
 function fails, returning a high numeric value that isn't part 
 of the expected return type enum (_Unwind_Reason_Code) values. 
 So the D exception personality is never called, and the catch 
 block is never executed. The only thing that is currently 
 missing in the assembly is the CFI stack adjustment directives. 
 But I don't think that's what's causing the problem, because 1) 
 when those are missing in the C++ code basic exception handling 
 still works, and 2) I've tried to manually add those to the 
 assembly file and it makes no difference.
Clue: when I call __cxa_throw in D code instead of throwing normally (i.e., calling _d_throw_exception), the D personality handler *is* called. Which indicates that, indeed, the problem is *not* in the generated assembly file. So I guess it's something that _d_throw_exception does which isn't quite right, although I can't see what.
May 24 2018
parent reply =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Thursday, 24 May 2018 at 14:45:26 UTC, Luís Marques wrote:
 Clue: when I call __cxa_throw in D code instead of throwing 
 normally (i.e., calling _d_throw_exception), the D personality 
 handler *is* called. Which indicates that, indeed, the problem 
 is *not* in the generated assembly file. So I guess it's 
 something that _d_throw_exception does which isn't quite right, 
 although I can't see what.
There is this fragment of code in libgcc's _Unwind_RaiseException (printfs inserted by me): code = uw_frame_state_for (&cur_context, &fs); printf("_Unwind_RaiseException uw_frame_state_for %d\n", code); if (code == _URC_END_OF_STACK) { /* Hit end of stack with no handler found. */ printf("_Unwind_RaiseException code == _URC_END_OF_STACK\n"); return _URC_END_OF_STACK; } The code prints: _Unwind_RaiseException uw_frame_state_for 5 _Unwind_RaiseException code == _URC_END_OF_STACK But then the function actually returns a nonsense number instead of the expected 5/_URC_END_OF_STACK. Looking at the disassembly of _Unwind_RaiseException what seems to happen is that the function starts with: 00001db0 <_Unwind_RaiseException>: 1db0: a7010113 addi sp,sp,-1424 1db4: 58912223 sw s1,1412(sp) 1db8: 58a12023 sw a0,1408(sp) // store a0 And then before it returns it does this: 2034: 58012503 lw a0,1408(sp) // load original a0 2038: 59010113 addi sp,sp,1424 203c: 00e10133 add sp,sp,a4 2040: 00008067 ret No other part of that function stores to 1408(sp), so of course a0 (the main return register) returns garbage instead of the more meaningful 5/_URC_END_OF_STACK. GCC is supposed to be reliable, so I would lean towards believing this isn't a miscompilation. The function prototype is: _Unwind_Reason_Code LIBGCC2_UNWIND_ATTRIBUTE _Unwind_RaiseException(struct _Unwind_Exception *exc) So I though maybe LIBGCC2_UNWIND_ATTRIBUTE was modifying the ABI somehow, and the return value wouldn't be in a0 as usual. But grepping my RISC-V GCC source code for that didn't find anything meaningful (only mips seems to define it). And looking at the complete disassembly it doesn't explain how the return value would be returned anyway. So maybe that's a miscompilation? I'll ask in the RISC-V forums.
May 24 2018
parent =?UTF-8?B?THXDrXM=?= Marques <luis luismarques.eu> writes:
On Thursday, 24 May 2018 at 18:31:01 UTC, Luís Marques wrote:
 GCC is supposed to be reliable, so I would lean towards 
 believing this isn't a miscompilation.
Turns out it really was a GCC bug! https://groups.google.com/a/groups.riscv.org/d/msg/sw-dev/_F9m7rDH8YM/UuNUJyC_CgAJ
May 24 2018