【问题标题】:What is the difference between assembler generated for functions with and without returns_twice and noreturn?为带有和不带有returns_twice和noreturn的函数生成的汇编程序有什么区别?
【发布时间】:2013-08-31 14:32:32
【问题描述】:

我知道 noreturn 应该用于预期不会将控制权返回给调用者的函数,但我在生成的汇编代码中找不到差异。 有谁知道会生成不同代码的示例吗?

编辑:noreturn 之后不会生成清理代码。

【问题讨论】:

  • 查看 Release 配置,查看调用者代码,而不是 noreturn 函数本身。
  • 谢谢我在 noreturn 中犯了愚蠢的错误。但returns_twice 不受影响
  • 嗯,这不是一件小事,请参阅:jeanjacques.lacrampe.free.fr/webada/doc/gnat/gcc_6.html“此类函数的示例是 setjmp 和 vfork”
  • 所以它只产生警告?
  • “编译器将确保在调用此类函数之前所有寄存器都已失效” - 来自同一来源。其实我从来没有写过fork这样的函数,所以很难理解这个……也许你有一个想法,什么是死寄存器,反正这又是一个调用者大小。

标签: c assembly noreturn


【解决方案1】:

returns_twice 禁用一些 gcc 优化。

作为一个例子,在一个旧的 gcc 版本中,我有:尾调用优化、全局公共子表达式消除、跳转绕过。

returns_twice 使用 calls_setjmp 机器(在源代码树周围):

          if (flags & ECF_RETURNS_TWICE)
            cfun->calls_setjmp = true;

tco (gcc/tree-tailcall.c):

static bool
suitable_for_tail_call_opt_p (void)
{
  [...]
  /* Any function that calls setjmp might have longjmp called from
     any called function.  ??? We really should represent this
     properly in the CFG so that this needn't be special cased.  */
  if (cfun->calls_setjmp)
    return false;

gcse (gcc/gcse.c):

static int
gcse_main (rtx f ATTRIBUTE_UNUSED)
{
  [...]
  /* We do not construct an accurate cfg in functions which call
     setjmp, so just punt to be safe.  */
  if (cfun->calls_setjmp)
    return 0;

跳转绕过(gcc/gcse.c):

static int
bypass_jumps (void)
{
  [...]
  /* We do not construct an accurate cfg in functions which call
     setjmp, so just punt to be safe.  */
  if (cfun->calls_setjmp)
    return 0;

函数既不能是纯函数也不能是常量(gcc/ipa-pure-const.c):

/* Check the parameters of a function call to CALL_EXPR to see if
   there are any references in the parameters that are not allowed for
   pure or const functions.  Also check to see if this is either an
   indirect call, a call outside the compilation unit, or has special
   attributes that may also effect the purity.  The CALL_EXPR node for
   the entire call expression.  */

static void
check_call (funct_state local, gimple call)
{
  [...]
      /* When bad things happen to bad functions, they cannot be const
         or pure.  */
      if (setjmp_call_p (callee_t))
        {
          local->pure_const_state = IPA_NEITHER;
          local->looping = false;
        }

函数不能内联(gcc/tree-inline.c):

/* A callback for walk_gimple_seq to handle statements.  Returns
   non-NULL iff a function can not be inlined.  Also sets the reason
   why. */

static tree
inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
                         struct walk_stmt_info *wip)
{
  [...]
     /* We cannot inline functions that call setjmp.  */
      if (setjmp_call_p (t))
        {
          inline_forbidden_reason
            = G_("function %q+F can never be inlined because it uses setjmp");
          *handled_ops_p = true;
          return t;
        }

它还会影响函数堆栈帧中的寄存器保存区域。

示例(tco):

func.c:

int func(void)
{
        return 0;
}

tco.c:

extern int func(void)  /*__attribute__((returns_twice))*/;

int main()
{
        return func();
}

不会返回两次:

00000000004003a0 <main>:
  4003a0:       e9 0b 01 00 00          jmpq   4004b0 <func>
  4003a5:       90                      nop
  4003a6:       90                      nop
  4003a7:       90                      nop

返回两次:

00000000004003a0 <main>:
  4003a0:       48 83 ec 08             sub    $0x8,%rsp
  4003a4:       e8 17 01 00 00          callq  4004c0 <func>
  4003a9:       48 83 c4 08             add    $0x8,%rsp
  4003ad:       c3                      retq   
  4003ae:       90                      nop
  4003af:       90                      nop

【讨论】:

    猜你喜欢
    • 2019-10-28
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 2021-07-24
    • 1970-01-01
    • 2020-06-13
    • 2020-01-16
    • 1970-01-01
    相关资源
    最近更新 更多