spilling & restoring registers for EHReturn & return _Unwind_Reason_Code

Hi

I’m working on the XCore target and am having difficulty building libgcc.

Background:
If I use a libgcc built by llvm3.0-gcc with my current clang-llvm3.3 compiler, exceptions ‘seem’ to work.
Trying to rebuild libgcc however breaks exception handling - they aren’t caught!
I thus assumed I needed to focus on the unwind code and particularly functions that call @llvm.eh.unwind.init().
I reinforced this assumption by swapping the assembler output from the llvm-gcc lib into my new lib for some such functions - and exceptions ‘seem’ to work again.
(the critical functions being found in eh_personality.o, unwind-dw2.o, eh_throw.o)

Looking at functions built using the llvm3.0-gcc front end, I noticed that R0 & R1 were spilled/restored (clobbering ‘_Unwind_Reason_Code’) , along with the callee saved regs.

So how are things meant to work?

For example take:
define i32 @_Unwind_RaiseException(%struct._Unwind_Exception* %exc)

This calls:
@llvm.eh.unwind.init()
followed by:
call void @llvm.eh.return()
ret i32 ‘_Unwind_Reason_Code’
call void @abort()

From my reading, I need to spill all callee save regs (R4-R10) during the prologue.
(R0-R3, LR & FP are caller saved/argument registers)
BUT
Q: what about exception info (and what is this)?
Q: is it %exc (R0)?
Q: is it ‘ExceptionPointerRegister’ & ‘ExceptionSelectorRegister’ (R0&R1)?

Then:

Calling abort, does not restore callee save regs.

For ‘return’ epilogues, I restore callee save regs (R4-10) & R0 will hold the ‘_Unwind_Reason_Code’.

However, what must I do for llvm.eh.return() epilogues?
Q: do I restore callee save regs (R4-R10)?
Q: do I restore exception info (R0-R1)?

I have tried only spilling/restoring callee save regs - but this doesn’t work, I end up with an uncaught exception.

So I tried spilling R0 & R1 and trying to restore them only for llvm.eh.return() but this will not compile.
viz unwind-dw2.c will report:
BB#0: derived from LLVM BB %entry
Live Ins: %R0 %R0 %R1 %R4 %R5 %R6 %R7 %R8 %R9 %R10 %LR %R0 %R1
ENTSP_lu6 120, %SP, %SP, %LR<imp-use,kill>
PROLOG_LABEL <MCSym=.Ltmp68>
PROLOG_LABEL <MCSym=.Ltmp69>
STWSP_lru6 %R0, 119, %SP
PROLOG_LABEL <MCSym=.Ltmp59>
STWSP_lru6 %R1, 118, %SP
PROLOG_LABEL <MCSym=.Ltmp60>
STWSP_lru6 %R4, 117, %SP
PROLOG_LABEL <MCSym=.Ltmp61>
STWSP_lru6 %R5, 116, %SP
PROLOG_LABEL <MCSym=.Ltmp62>
STWSP_lru6 %R6, 115, %SP
PROLOG_LABEL <MCSym=.Ltmp63>
STWSP_lru6 %R7, 114, %SP
PROLOG_LABEL <MCSym=.Ltmp64>
STWSP_lru6 %R8, 113, %SP
PROLOG_LABEL <MCSym=.Ltmp65>
STWSP_lru6 %R9, 112, %SP
PROLOG_LABEL <MCSym=.Ltmp66>
STWSP_lru6 %R10, 111, %SP
PROLOG_LABEL <MCSym=.Ltmp67>
%R4 = COPY %R0

*** Bad machine code: Using an undefined physical register ***

  • function: _Unwind_RaiseException
  • basic block: BB#0 entry (0x33d9f20)
  • instruction: %R4 = COPY %R0
  • operand 1: %R0

To see if I am on the right track I may try to:

  1. hack llvm to not kill R0 during the spill
  2. manually add spilled/restore R0 & R1 into the assembler output for functions that call llvm.eh.return().
    However, I would prefer knowledgeable input at this stage!

Thank you.

Robert

Hi

I'm working on the XCore target and am having difficulty building libgcc.

Background:
If I use a libgcc built by llvm3.0-gcc with my current clang-llvm3.3 compiler, exceptions 'seem' to work.
Trying to rebuild libgcc however breaks exception handling - they aren't caught!
I thus assumed I needed to focus on the unwind code and particularly functions that call @llvm.eh.unwind.init().
I reinforced this assumption by swapping the assembler output from the llvm-gcc lib into my new lib for some such functions - and exceptions 'seem' to work again.
(the critical functions being found in eh_personality.o, unwind-dw2.o, eh_throw.o)

Looking at functions built using the llvm3.0-gcc front end, I noticed that R0 & R1 were spilled/restored (clobbering '_Unwind_Reason_Code') , along with the callee saved regs.

So how are things meant to work?

Hi, this has been a long standing issue in llvm, and is tracked in following bug report: http://llvm.org/bugs/show_bug.cgi?id=8541

You might find my attempt for fixing this issue for x86 target informational. It is a hack, but a working one at least (at the time):
http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20130415/172180.html

And resulting discussion, with more detailed description of underlying issue, can be found from here: http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20130513/174459.html

At least that resulted to a proper handling of llvm.eh.unwind.init intrinsic in PrologueEpilogueInserter, to spill callee saved registers. What needs to be done, is to have a general interface to handle exception registers.

Also you might find http://gcc.gnu.org/wiki/Dwarf2EHNewbiesHowto helpful for understanding all of this.

All of this is really a target specific and rest of the reply is based on my (now diminished) understanding how things work on x86.

For example take:
     define i32 @_Unwind_RaiseException(%struct._Unwind_Exception* %exc)

This calls:
     @llvm.eh.unwind.init()
followed by:
     call void @llvm.eh.return()
     ret i32 '_Unwind_Reason_Code'
     call void @abort()

From my reading, I need to spill all callee save regs (R4-R10) during the prologue.
(R0-R3, LR & FP are caller saved/argument registers)

Spilling of callee saved registers are taken care of by llvm.eh.unwind.init and it should be already ensured by PrologueEpilogueEmitter automatically.

What llvm.eh.return() does from a spilling stand point of view is that it reserves spill slots for a exception registers in the prologue (i.e. you don't need to actually spill the registers in prologue, just need to reserve space for them and have proper CFI register specs generated where these are locate in the stack). On normal return paths you just ignore these extra spill slots by adjusting the stack properly.

The need for CFI register specs for these spill slots is so that the unwinder knows where to put the exception information when exception occurs.

BUT
Q: what about exception info (and what is this)?
Q: is it %exc (R0)?
Q: is it 'ExceptionPointerRegister' & 'ExceptionSelectorRegister' (R0&R1)?

It is information delivered by both ExceptionPointer and ExceptionSelector registers to a landing pad on llvm.eh.return() return paths.

Then:

Calling abort, does not restore callee save regs.

For 'return' epilogues, I restore callee save regs (R4-10) & R0 will hold the '_Unwind_Reason_Code'.

However, what must I do for llvm.eh.return() epilogues?
Q: do I restore callee save regs (R4-R10)?
Q: do I restore exception info (R0-R1)?

On llvm.eh.return() epilogues, you must restore both, callee saved registers and exception info registers (and be sure when lowering llvm.eh.return not to clobber restored exception info registers! i.e. neither of those are used as a temporary after restoring.).

In a essence llvm.eh.return() intrinsic is a terminator and must be treated as a such from a lowering stand point of view.

I have tried only spilling/restoring callee save regs - but this doesn't work, I end up with an uncaught exception.

So I tried spilling R0 & R1 and trying to restore them only for llvm.eh.return() but this will not compile.
viz unwind-dw2.c will report:
     BB#0: derived from LLVM BB %entry
         Live Ins: %R0 %R0 %R1 %R4 %R5 %R6 %R7 %R8 %R9 %R10 %LR %R0 %R1
         ENTSP_lu6 120, %SP<imp-def>, %SP<imp-use>, %LR<imp-use,kill>
         PROLOG_LABEL <MCSym=.Ltmp68>
         PROLOG_LABEL <MCSym=.Ltmp69>
         STWSP_lru6 %R0<kill>, 119, %SP<imp-use>
         PROLOG_LABEL <MCSym=.Ltmp59>
         STWSP_lru6 %R1<kill>, 118, %SP<imp-use>
         PROLOG_LABEL <MCSym=.Ltmp60>
         STWSP_lru6 %R4<kill>, 117, %SP<imp-use>
         PROLOG_LABEL <MCSym=.Ltmp61>
         STWSP_lru6 %R5<kill>, 116, %SP<imp-use>
         PROLOG_LABEL <MCSym=.Ltmp62>
         STWSP_lru6 %R6<kill>, 115, %SP<imp-use>
         PROLOG_LABEL <MCSym=.Ltmp63>
         STWSP_lru6 %R7<kill>, 114, %SP<imp-use>
         PROLOG_LABEL <MCSym=.Ltmp64>
         STWSP_lru6 %R8<kill>, 113, %SP<imp-use>
         PROLOG_LABEL <MCSym=.Ltmp65>
         STWSP_lru6 %R9<kill>, 112, %SP<imp-use>
         PROLOG_LABEL <MCSym=.Ltmp66>
         STWSP_lru6 %R10<kill>, 111, %SP<imp-use>
         PROLOG_LABEL <MCSym=.Ltmp67>
         %R4<def> = COPY %R0
...
     *** Bad machine code: Using an undefined physical register ***
     - function: _Unwind_RaiseException
     - basic block: BB#0 entry (0x33d9f20)
     - instruction: %R4<def> = COPY %R0
     - operand 1: %R0

To see if I am on the right track I may try to:
     1. hack llvm to not kill R0 during the spill
     2. manually add spilled/restore R0 & R1 into the assembler output for functions that call llvm.eh.return().

Hopefully above descriptions gave you insight how to handle llvm.eh.return(). In short, have a spill slots reserved for these registers in prologue, and most importantly have CFI register specs for these spill slots. On llvm.eh.return epilogues restore R0 & R1 from these slots and on normal epilogues just ignore this extra space.

Pasi.