Failed to catch exception across function calls

Dear LLVMers,

I’m working on the openrisc backend which is not currently upstreamed, I want to use llvm as the compiler and linking with gcc/g++. I have a simple test program below, right now if I run the compiled binary, it hangs and never returned to the catch block. If I throw exception directly in the try block instead of in another function, it works fine.

I’ve looked at the generated assembly instructions and they all look right to me, except for the generated exception table which I don’t know enough to tell whether it’s right or nor, my first guess is that maybe there is some compatibility issue for the exception table between llvm and g++, so I try to manually modify according to that generated by g++, but the bug is still there.

I wonder how should I approach this issue, is there anything in the backend that could cause the bug ?

I attached the assembly in case anyone wants to take a closer look.

Any suggestion is greatly appreciated.
Patrick

#include <stdlib.h>
#include <stdio.h>

void foo(int a) {
throw -15;
}

int main() {
int error = 0;
try {
foo(0);
}
catch (int e) {
error = e;
}
printf(“Error = %d\n”, error);
return 0;
}

throw-llvm.s (3.19 KB)

I wonder how should I approach this issue, is there anything in the backend
that could cause the bug ?

Some cursory research suggests that r1 is the stack pointer on
OpenRISC. You seem to be modifying that (& storing callee-saved
registers?) but not emitting the required .cfi_XYZ directives to tell
the unwinder about those changes.

The way exceptions work is that they need extra information to restore
the CPU state to what it was in each caller until they reach a catch
clause that can handle the exception. This information is provided by
each function saying "I changed sp by X, and stored the registers my
caller expects to be saved at locations Y, Z and W". It does this via
.cfi directives.

This is usually handled in XYZFrameLowering.cpp (though I vaguely
remember x86 uses a separate pass which re-parses the frame to
generate them -- don't quote me on that). I'd suggest searching around
for .cfi_def_cfa and .cfi_offset as the most important two:

  + ".cfi_def_cfa R, off" says "when this function was first called,
SP == R + off".
  + ".cfi_offset R, off" says "to find out what R used to be when the
function was first called, load the value at offset "off" from where
SP was when this function was called.

It's slightly more subtle than that, particularly on CISC
architectures (I'd suggest ignoring x86, for example), but that's the
essence of it. Hopefully you'll also be able to get the hang of it
from what GCC produces.

Cheers.

Tim.