May 22, 2018
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 23, 2018
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
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
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 24, 2018
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
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
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 25, 2018
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
1 2
Next ›   Last »