[cfe-dev] Code generation for noexcept functions

Stephan Tolksdorf st at quanttec.com
Wed May 14 08:38:12 PDT 2014


On 2014-05-13 23:26, Richard Smith wrote:
> On Tue, May 13, 2014 at 12:36 PM, Stephan Tolksdorf <st at quanttec.com
> <mailto:st at quanttec.com>> wrote:
>
>     On 2014-05-11 Richard Smith wrote:
>
>         On Sun, May 11, 2014 at 8:19 AM, Stephan Tolksdorf
>         <st at quanttec.com <mailto:st at quanttec.com>
>         <mailto:st at quanttec.com <mailto:st at quanttec.com>>> wrote:
>
>              Hi,
>
>              When clang/LLVM can't prove that a noexcept function only
>         contains
>              non-throwing code, it seems to insert an explicit exception
>         handler
>              that calls std::terminate. Why doesn't clang leave it to the eh
>              personality function to call std::terminate when an
>         exception is
>              thrown inside a noexcept function, as GCC does?
>
>
>         As far as I can see, this is impossible to represent in LLVM IR. (If
>         there is a way, I'm sure we'd be happy to make clang emit that IR.)
>
>
>     Thanks for the reply! Do you or maybe somebody else have an opinion
>     on what the best way would be to efficiently support this case in
>     LLVM IR?
>
>
> Here's a reduced testcase:
>
> void f();
> void g() noexcept { f(); }
>
> One obvious approach would be to emit the call to 'f' as a 'call', not
> an 'invoke', with some attribute indicating that the program is
> terminated if it unwinds. (We can't use 'nounwind' for this, because
> that gives us UB on unwind, not a call to terminate.)
>
> Not-very-well-thought-through strawman: add a function attribute to
> indicate that that the function does not return if a call unwinds (and
> pass it the personality function so that we can generate the right CFI
> stuff).
>
> call void @_Z1fv() terminateonunwind(i32 (...)* @__gxx_personality_v0)
>
> This would result in a call that is not covered by any range in the call
> site table in the calling function's LSDA.

I suppose the principal alternative would be to introduce a "terminate" 
clause to the landingpad instruction.

One difficulty of implementing either approach seems to be that LLVM 
will need to synthesize an actual terminate call when it inlines a 
"terminateonunwind" call to a function that contains an invoke without a 
catch-all clause and there's no direct way to represent the terminate 
action in the LSDA.

> Another case:
>
> void h() noexcept { try { f(); } catch (int) {} }
>
> Here, I don't think there's a better representation than the one we're
> currently using (though we should at least omit the pointless
> __cxa_begin_catch immediately before our synthesized call to
> std::terminate).

If landingpad had a terminate clause, you could leave the synthesizing 
of the terminate call to LLVM in this case too.

The call to __cxa_begin_catch seems to be due to llvm.org/PR11893. The 
libsupc++ and libc++abi personality functions also call 
__cxa_begin_catch before calling std::terminate when possible.

One (ugly) way to prevent clang/LLVM from generating bloated code for 
noexcept functions that contain calls to functions which are known to be 
non-throwing for the specified arguments but which aren't annotated as 
noexcept is to move the calls to an inline wrapper function with 
__attribute__((nothrow)). I'll probably resort to this workaround for my 
code.

- Stephan

>
>              For example, GCC generates more efficient code for this
>         test case:
>
>                 using FP = void(*)();
>
>                 inline void test(FP fp) noexcept {
>                   fp();
>                 }
>
>                 void test2(FP fp) {
>                   test(fp);
>                   test(fp);
>                 }
>
>              The code generated by GCC (ToT, -O3, Linux x64) is:
>
>              .LHOTB0:
>                       .p2align 4,,15
>                       .globl  _Z5test2PFvvE
>                       .type   _Z5test2PFvvE, @function
>              _Z5test2PFvvE:
>              .LFB1:
>                       .cfi_startproc
>                       .cfi_personality 0x3,__gxx_personality_v0
>                       .cfi_lsda 0x3,.LLSDA1
>                       pushq   %rbx
>                       .cfi_def_cfa_offset 16
>                       .cfi_offset 3, -16
>                       movq    %rdi, %rbx
>                       call    *%rdi
>                       movq    %rbx, %rax
>                       popq    %rbx
>                       .cfi_def_cfa_offset 8
>                       jmp     *%rax
>                       .cfi_endproc
>              .LFE1:
>                       .globl  __gxx_personality_v0
>                       .section        .gcc_except_table,"a", at ____progbits
>
>              .LLSDA1:
>                       .byte   0xff
>                       .byte   0xff
>                       .byte   0x1
>                       .uleb128 .LLSDACSE1-.LLSDACSB1
>              .LLSDACSB1:
>              .LLSDACSE1:
>                       .text
>                       .size   _Z5test2PFvvE, .-_Z5test2PFvvE
>                       .section        .text.unlikely
>
>
>              The code generated by clang (ToT, -O3, Linux x64) is:
>
>              .globl  _Z5test2PFvvE
>                       .align  16, 0x90
>                       .type   _Z5test2PFvvE, at function
>              _Z5test2PFvvE:                          # @_Z5test2PFvvE
>                       .cfi_startproc
>                       .cfi_personality 3, __gxx_personality_v0
>              .Leh_func_begin0:
>                       .cfi_lsda 3, .Lexception0
>              # BB#0:                                 # %entry
>                       pushq   %rbx
>              .Ltmp6:
>                       .cfi_def_cfa_offset 16
>              .Ltmp7:
>                       .cfi_offset %rbx, -16
>                       movq    %rdi, %rbx
>              .Ltmp0:
>                       callq   *%rbx
>              .Ltmp1:
>              # BB#1:                                 # %_Z4testPFvvE.exit
>              .Ltmp3:
>                       callq   *%rbx
>              .Ltmp4:
>              # BB#2:                                 # %_Z4testPFvvE.exit3
>                       popq    %rbx
>                       retq
>              .LBB0_3:                                # %terminate.lpad.i
>              .Ltmp2:
>                       movq    %rax, %rdi
>                       callq   __clang_call_terminate
>              .LBB0_4:                                # %terminate.lpad.i2
>              .Ltmp5:
>                       movq    %rax, %rdi
>                       callq   __clang_call_terminate
>              .Ltmp8:
>                       .size   _Z5test2PFvvE, .Ltmp8-_Z5test2PFvvE
>                       .cfi_endproc
>              .Leh_func_end0:
>                       .section        .gcc_except_table,"a", at ____progbits
>
>                       .align  4
>              GCC_except_table0:
>              .Lexception0:
>                       .byte   255                     # @LPStart
>         Encoding = omit
>                       .byte   3                       # @TType Encoding
>         = udata4
>                       .asciz  "\242\200\200"          # @TType base offset
>                       .byte   3                       # Call site
>         Encoding = udata4
>                       .byte   26                      # Call site table
>         length
>              .Lset0 = .Ltmp0-.Leh_func_begin0        # >> Call Site 1 <<
>                       .long   .Lset0
>              .Lset1 = .Ltmp1-.Ltmp0                  #   Call between
>         .Ltmp0 and
>              .Ltmp1
>                       .long   .Lset1
>              .Lset2 = .Ltmp2-.Leh_func_begin0        #     jumps to .Ltmp2
>                       .long   .Lset2
>                       .byte   1                       #   On action: 1
>              .Lset3 = .Ltmp3-.Leh_func_begin0        # >> Call Site 2 <<
>                       .long   .Lset3
>              .Lset4 = .Ltmp4-.Ltmp3                  #   Call between
>         .Ltmp3 and
>              .Ltmp4
>                       .long   .Lset4
>              .Lset5 = .Ltmp5-.Leh_func_begin0        #     jumps to .Ltmp5
>                       .long   .Lset5
>                       .byte   1                       #   On action: 1
>                       .byte   1                       # >> Action Record
>         1 <<
>                                                       #   Catch TypeInfo 1
>                       .byte   0                       #   No further actions
>                                                       # >> Catch
>         TypeInfos <<
>                       .long   0                       # TypeInfo 1
>                       .align  4
>
>                       .section
>
>         .text.__clang_call_terminate,"____axG", at progbits,__clang_call_____terminate,comdat
>
>                       .hidden __clang_call_terminate
>                       .weak   __clang_call_terminate
>                       .align  16, 0x90
>                       .type   __clang_call_terminate, at ____function
>
>              __clang_call_terminate:                 #
>         @__clang_call_terminate
>              # BB#0:
>                       pushq   %rax
>                       callq   __cxa_begin_catch
>                       callq   _ZSt9terminatev
>              .Ltmp9:
>                       .size   __clang_call_terminate,
>         .Ltmp9-__clang_call_terminate
>
>              - Stephan
>              ___________________________________________________
>              cfe-dev mailing list
>         cfe-dev at cs.uiuc.edu <mailto:cfe-dev at cs.uiuc.edu>
>         <mailto:cfe-dev at cs.uiuc.edu <mailto:cfe-dev at cs.uiuc.edu>>
>         http://lists.cs.uiuc.edu/____mailman/listinfo/cfe-dev
>         <http://lists.cs.uiuc.edu/__mailman/listinfo/cfe-dev>
>              <http://lists.cs.uiuc.edu/__mailman/listinfo/cfe-dev
>         <http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev>>
>
>
>



More information about the cfe-dev mailing list