[PATCH] PR8541; X86: Handle unwind.init and eh.return intrinsics.

Dimitry Andric dimitry at andric.com
Wed May 15 07:17:36 PDT 2013


On 2013-05-15 14:36, Pasi Parviainen wrote:
> On 15.5.2013 14:54, Dimitry Andric wrote:
>> On 2013-05-15 02:20, Pasi Parviainen wrote:
...
>>> So considering previous point, workaround used in FreeBSD is also
>>> flawed, since it is trashing return values on non-eh.return epilogues :)
>>
>> Interesting, but we do not seem to see any effect of such thrashing in
>> FreeBSD.  It seems that in the relevant cases, libgcc always returns via
>> __builtin_eh_return()?  E.g., would the non-eh.return cases not go via a
>> different code path?
>
> Yes, the common code path would be trough __builtin_eh_return. But when
> unwinder encounters some exceptional situation/failure, it will return a
> reason code for it. So, now instead of a proper reason code, client of
> unwinder interface will get a more or less random value and this breaks
> API contract between unwinder and its client.

Hmm, you are quite right, apparently.  Take for example the libgcc
function _Unwind_ForcedUnwind():

   _Unwind_Reason_Code
   _Unwind_ForcedUnwind (struct _Unwind_Exception *exc,
                         _Unwind_Stop_Fn stop, void * stop_argument)
   {
     struct _Unwind_Context this_context, cur_context;
     _Unwind_Reason_Code code;

   [...]
     code = _Unwind_ForcedUnwind_Phase2 (exc, &cur_context);
     if (code != _URC_INSTALL_CONTEXT)
       return code;

     uw_install_context (&this_context, &cur_context);
   }

The uw_install_context macro eventually calls __builtin_eh_return.  With
gcc, the produced assembly goes like this (modified slightly to improve
readability):

   _Unwind_ForcedUnwind:
           pushq   %rbp
           movq    %rsp, %rbp
           movq    %rax, -56(%rbp)
           movq    %rdx, -48(%rbp)
           leaq    -304(%rbp), %rax
           movq    %rbx, -40(%rbp)
           movq    %r12, -32(%rbp)
           movq    %r13, -24(%rbp)
           movq    %r14, -16(%rbp)
           movq    %r15, -8(%rbp)
           subq    $592, %rsp
   [...]
           call    _Unwind_ForcedUnwind_Phase2
           cmpl    $7, %eax                # 7 is _URC_INSTALL_CONTEXT
           je      .L612
           movq    -40(%rbp), %rbx         # restores just a few regs
           movq    -32(%rbp), %r12
           movq    -24(%rbp), %r13
           movq    -16(%rbp), %r14
           movq    -8(%rbp), %r15
           leave
           ret                             # non-eh return
   .L612:
           movq    -584(%rbp), %rsi
           movq    -576(%rbp), %rdi
           call    uw_install_context_1
           movq    %rax, %rcx
           movq    -392(%rbp), %rax
           movq    %rax, 8(%rbp,%rcx)
           leaq    8(%rbp,%rcx), %rcx
           movq    -56(%rbp), %rax         # restores most regs
           movq    -48(%rbp), %rdx
           movq    -40(%rbp), %rbx
           movq    -32(%rbp), %r12
           movq    -24(%rbp), %r13
           movq    -16(%rbp), %r14
           movq    -8(%rbp), %r15
           movq    0(%rbp), %rbp
           movq    %rcx, %rsp
           ret                             # eh return

With clang, and our clobber hack, it goes like this:

   _Unwind_ForcedUnwind:                   # @_Unwind_ForcedUnwind
           pushq   %rbp
           movq    %rsp, %rbp
           pushq   %r15
           pushq   %r14
           pushq   %r13
           pushq   %r12
           pushq   %rbx
           pushq   %rdx
           pushq   %rax
           subq    $488, %rsp              # imm = 0x1E8
   [...]
           callq   _Unwind_ForcedUnwind_Phase2
           cmpl    $7, %eax                # 7 is _URC_INSTALL_CONTEXT
           je      .LBB17_2
           addq    $488, %rsp              # imm = 0x1E8
           popq    %rax                    # restores most regs, and clobbers %eax!
           popq    %rdx
           popq    %rbx
           popq    %r12
           popq    %r13
           popq    %r14
           popq    %r15
           popq    %rbp
           ret                             # non-eh return
   .LBB17_2:
           leaq    -296(%rbp), %rdi
           leaq    -536(%rbp), %rsi
           callq   uw_install_context_1
           movq    %rax, %rcx
           movq    -384(%rbp), %rsi
           movq    %rsi, 8(%rbp,%rcx)
           leaq    8(%rbp,%rcx), %rcx
           addq    $488, %rsp              # imm = 0x1E8
           popq    %rax                    # restores most regs
           popq    %rdx
           popq    %rbx
           popq    %r12
           popq    %r13
           popq    %r14
           popq    %r15
           popq    %rbp
           movq    %rcx, %rsp
           ret                             # eh_return, addr: %rcx

So clearly, clang restores too much in the non-eh case, and clobbers the
return value from _Unwind_ForcedUnwind_Phase2().

I am not sure if there is a way to force it to restore all registers
just in the eh_return case, except by using your patch. :-)

-Dimitry



More information about the llvm-commits mailing list