Code generation for noexcept functions

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?

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);
   }

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",@progbits
.LLSDA1:
  .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,@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",@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",@progbits,__clang_call_terminate,comdat
  .hidden __clang_call_terminate
  .weak __clang_call_terminate
  .align 16, 0x90
  .type __clang_call_terminate,@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

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.)

    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

    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:

             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