[LLVMdev] spilling & restoring registers for EHReturn & return _Unwind_Reason_Code

Pasi Parviainen pasi.parviainen at iki.fi
Sat Dec 21 04:54:46 PST 2013


On 20.12.2013 19:33, Robert Lytton wrote:
> 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.

> However, I would prefer knowledgeable input at this stage!
>
> Thank you.
>
> Robert
>




More information about the llvm-dev mailing list