【问题标题】:`std::terminate` invocation in a `noexcept` function with limited visibility - gcc vs clang codegen`std::terminate` 在可见性有限的`noexcept`函数中调用-gcc vs clang codegen
【发布时间】:2018-10-07 10:17:44
【问题描述】:

考虑以下代码sn-p:

void f();

void a()          { f(); }
void b() noexcept { f(); }

在上述场景中,f 的主体在当前翻译单元中对编译器不可见。因此,由于b 被标记为noexcept,因此必须在调用方生成额外的代码以确保捕获到异常并调用std::terminate

这就是clang++ -Ofast -std=c++2a 所做的(主干版本)

a(): # @a()
  jmp f() # TAILCALL
b(): # @b()
  push rax
  call f()
  pop rax
  ret
  mov rdi, rax
  call __clang_call_terminate
__clang_call_terminate: # @__clang_call_terminate
  push rax
  call __cxa_begin_catch
  call std::terminate()

但是,g++ -Ofast -std=c++2a 没有(中继版)

a():
  jmp f()
b():
  jmp f()

live example on godbolt.org


g++ 是如何解决这个问题的?由于f 的主体不可见,不应该在调用方生成代码吗?

...或者这只是一个奇怪的 Compiler Explorer 怪癖?

【问题讨论】:

  • Afaik gcc 只是使用 eh 个性函数以不同方式处理异常。 Godbolt 只是剥离它。这个discussion 似乎相关。
  • 如果使用-O3,这个问题可能会更准确,因为-Ofast 无视标准合规性。
  • @DanM。确实如此,在 Godbolt 输出中也可以看到,只需打开“.text”即可。然而,目前尚不清楚 EH 人格函数如何诊断 f() 的当前实例是否是从 b() 调用的。跳转指令执行后,堆栈上将没有证据表明它来自那里。

标签: c++ c++11 exception code-generation noexcept


【解决方案1】:

正如@ach 回答的那样,有一个bug opened on gcc bug tracker。但如果我可以这么说,这不是一个大问题。

影响是异常会泄漏而不是终止程序,但是:

  • 如果你想在未捕获的异常上调用终止,已经是这样了
  • 如果您想要不抛出异常的安全保证,那不提供。

我能想到的唯一情况是在开发过程中。或者在环境中,如果合同被破坏(当生命受到威胁),则必须失败;在这种情况下,编译器和要使用的功能受到严格控制。

Andrzej 在his article - noexcept — what for ? 中提出了一个很好的案例

【讨论】:

    猜你喜欢
    • 2018-03-21
    • 2020-04-27
    • 2016-12-17
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    • 2020-06-12
    • 2016-07-13
    • 2018-02-08
    相关资源
    最近更新 更多