Richard Smith
2014-May-11 20:57 UTC
[LLVMdev] [cfe-dev] Code generation for noexcept functions
On Sun, May 11, 2014 at 8:19 AM, Stephan Tolksdorf <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.)> 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 > http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140511/1293f7d0/attachment.html>
Stephan Tolksdorf
2014-May-13 19:36 UTC
[LLVMdev] [cfe-dev] Code generation for noexcept functions
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>> 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? - 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> > http://lists.cs.uiuc.edu/__mailman/listinfo/cfe-dev > <http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev> > >
Richard Smith
2014-May-13 21:26 UTC
[LLVMdev] [cfe-dev] Code generation for noexcept functions
On Tue, May 13, 2014 at 12:36 PM, Stephan Tolksdorf <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>> 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. 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). 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> >> http://lists.cs.uiuc.edu/__mailman/listinfo/cfe-dev >> <http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev> >> >> >>-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140513/dd0106c7/attachment.html>