Exception handling support for a target

Hi All,

I would like to know in order to support exception handling for particular target, what need to be done. After doing some investigation, I can think of the following items with questions:

  • CFI directives:

This is for .eh_frame section. Basically all the targets insert CFI directives in FrameLowering, but I am not sure exactly when/where I should do so.

  • getExceptionPointerRegister and getExceptionSelectorRegister:

TargetLowering subclass should implement both functions. The former specifies the register used to pass the exception object to the landing pad (or catch clause), and the latter specifies the register used to pass the typeid object to the landing pad. Where can I know what registers should be used? Or it’s just the same as those specified in calling convention?

  • EH_RETURN:

I see some targets define their own EH_RETURN SDNode, others don’t. What is EH_RETURN, and under what circumstances I should define my own EH_RETURN SDNode?

Is the above list complete? Do I understand their purpose correctly (sort of)?

Thanks.

Regards,
chenwj

  - CFI directives:

    This is for .eh_frame section. Basically all the targets insert CFI
directives in FrameLowering, but I am not sure exactly when/where I should
do so.

The directives are there to describe where the unwinder should look to
find out what each register's value was when this function was called
(well, each register that the caller expected to be preserved). So the
directives have to have been emitted by the time any instruction is
executed that could cause an exception to be thrown (typically either
a call to another function or a compiler-generated call to __cxa_throw
or similar when the source code itself contains "throw X").

So normally the directives are emitted in the prologue when the
registers actually get saved. Sometimes all in a bunch, sometimes
interspersed with the saves, but it usually doesn't matter which. As
you've discovered that happens in XYZFrameLowering

  - getExceptionPointerRegister and getExceptionSelectorRegister:

    TargetLowering subclass should implement both functions. The former
specifies the register used to pass the exception object to the landing pad
(or catch clause), and the latter specifies the register used to pass the
typeid object to the landing pad. Where can I know what registers should be
used? Or it's just the same as those specified in calling convention?

This is an ABI decision that your unwinder needs to know about, but
the ones used in a normal function call are fairly typical. I believe
the information is transmitted to the __gxx_personality_v0 function
via Clang's __builtin_eh_return_data_regno intrinsic, which should be
consistent.

  - EH_RETURN:

    I see some targets define their own EH_RETURN SDNode, others don't. What
is EH_RETURN, and under what circumstances I should define my own EH_RETURN
SDNode?

Not sure about this, I'm afraid. My suggestion would be to try without
if you've got a reasonably normal target.

  Is the above list complete? Do I understand their purpose correctly (sort
of)?

There are components in libunwind (especially) and compiler-rt you'll
also need to look at if you're not on top of an existing runtime.

Cheers.

Tim.

The libUnwind parts are relatively small. You need to write two assembly functions, one which dumps all registers to memory and one which restores them. Next you need to add enum values for all of your registers that correspond to their DWARF register numbres. You then need to implement a C++ class that sets and gets register values from the in-memory structure based on the DWARF number. This is usually quite small because the easiest way of dumping the registers is to store each register set contiguously in memory in the same order that as their DWARF numbers, so you end up with a struct something like this:

struct MyArch_Regs
{
  int64_t GPRs[32];
};

And the get function is just doing GPRs[RegNo - MyArch_GPR0].

LLVM’s libUnwind has a very clean structure. Adding MIPS support took a couple of hours. All of the unwinder support is generic other than the mapping from DWARF register numbers to some concrete mechanism for getting and setting them.

David

I’d like to thank Tim and David for answering this and Chenwj for asking.

I personally don’t know much at all about exception handling in LLVM - it’s something that was implemented for my target before I started working on it. The information you provided is concise, clear and a great start for getting a handle on how it all comes together in the back end.

In addition to thanking you for providing the info, I’m writing in the hopes that there is some documentation that exists or that someone is willing to add to http://llvm.org/docs/ExceptionHandling.html (or a more appropriate place with a link there) that would cover the back end (target-specific) aspects of EH. Perhaps just a quick description of what a target needs to do to get EH working.

Nemanja

​Do we have to emit directives in the epilogue, too?​ One of my test case
fail due to the directives in the epilogue have been executed. After remov
​ing
them from epilogue, the exception is caught as expected. Also, the
directives are also for debug purpose (.debug_frame), right? I guess I only
have to make sure directives work for exception handling, then debug works
as well?

​Thanks for the hint, David. Right now I just leverage on existing runtime
(libstdc++ and libgcc). Your hint would help if I move to libUwind.​

In addition to thanking you for providing the info, I'm writing in the
hopes that there is some documentation that exists or that someone is
willing to add to http://llvm.org/docs/ExceptionHandling.html (or a more
appropriate place with a link there) that would cover the back end
(target-specific) aspects of EH. Perhaps just a quick description of what a
target needs to do to get EH working.

​The link you gave mostly focus on IR level , I believe.

http://llvm.org/docs/WritingAnLLVMBackend.html would be more appropriate.

Sure. But as I mentioned, I think a link in the EH document would be appropriate. Perhaps something like “For Target-Specific back end requirements for EH, please refer to: …”

I think it is logical that someone wanting to learn about EH would start with the EH document.

Do we have to emit directives in the epilogue, too? One of my test case fail
due to the directives in the epilogue have been executed. After removing
them from epilogue, the exception is caught as expected.

Emitting directives in the epilogue is hard because the directives
apply to all instructions after in program-counter order. So if you
have an epilogue in the middle of a function and emit CFI directives
saying the registers are back where they were then the unwinder will
think that applies to the rest of the function too.

To fix that you'd have to emit yet more directives in the basic block
immeditately following the epilogue. Most people don't bother with
either because you'd only really need it if you expected to have to
catch an exception in the epilogue (which is impossible on most
platforms).

Also, the
directives are also for debug purpose (.debug_frame), right? I guess I only
have to make sure directives work for exception handling, then debug works
as well?

Yep. Technically you can stop the debugger in the middle of a prologue
or epilogue, at which point having more fine-grained directives can
help. In practice there's no real reason to do that so people don't
emit directives for it (at least not as a high priority).

Cheers.

Tim.

​Will come up a patch once I sort things out.​

I think so, though the assembly isn't really complete enough to tell.
In this case if the .cfi_def_cfa_offset is describing the epilogue it
would mean that all code in what you've labelled _Unwind_Resume
(assuming that's actually a cleanup landing pad or something) will use
the wrong SP to load the saved registers from. The unwinder will think
it's executing directly after the epilogue since it's later on in the
function.

Cheers.

Tim.

Time, David and Nemanja, I add you as the reviewer for https://reviews.llvm.org/D42178 , feel free to comment on it.

Alex, could you add D42178 into your review corner? I hope someone who has more exception handling experience can review on it as well.

Thanks.

Thank you very much for making a start on this. I’ve added some review comments with some of the missing detail. It would be great to see a polished version of this.

David

This corresponds to a GCC intrinsic used in their unwind routines. It's not really known for its exemplary documentation, so you may need to read GCC's sources to see what they are doing with it.
Unless you plan to compile GCC's unwind library, you don't really need to worry about it.

-Krzysztof

I see X86, Mips, XCore and Hexagon define their own EH_RETURN and lower to
it, but others don't. May I know why it's so on Hexagon?
​​

Our exception handling runtime uses __builtin_eh_return.

-Krzysztof

Does this mean that you know what it does? If so, please could you document it somewhere?

David

I don't actually, but I can find out.

-Krzysztof

​Thanks! Welcome leave comment on https://reviews.llvm.org/D42178 . ;-)​