【问题标题】:Determine whether a routine has gcc's tail call optimisation applied to it确定例程是否应用了 gcc 的尾调用优化
【发布时间】:2014-07-22 04:00:21
【问题描述】:

我需要确定共享对象中的一组例程是否应用了 gcc 的尾调用优化。 需要 C 代码,最好使用 API 而不是“nm”或“objdump”系统命令。

任何获取更多信息的链接都会有所帮助

详情: 例如。原来的C代码是这样的

void myFunc1(){
   ... // Do something
   myFunc2();
   return;
}

函数 myFunc1 的汇编代码转储:

   0x57ad50b0 <+0>:     push   %ebp
   0x57ad50b1 <+1>:     mov    %esp,%ebp
   0x57ad50b3 <+3>:     mov    0x8(%ebp),%eax
   0x57ad50b6 <+6>:     pop    %ebp
   0x57ad50b7 <+7>:     lea    0x8(%eax),%edx
   0x57ad50ba <+10>:    mov    (%eax),%eax
   0x57ad50bc <+12>:    jmp    0x57ab8010 <myFunc2>

我们可以看到最后一条指令不是“call myFunc2”,而只是“jmp myFunc2”。 因此,我们无法从堆栈跟踪中获取有关“myFunc1”的信息。 所以我们需要事先确定“myFunc1”可能有这个问题,这样我们才能解决它。

【问题讨论】:

    标签: gcc compiler-optimization tail-recursion


    【解决方案1】:

    最直接的方法是使用编译器转储

    int bar (int x);
    
    int
    foo (int x)
    {
      return bar(x);
    }
    

    编译:

    gcc -O2 -S tailcall.c -fdump-tree-tailc-lineno-details
    

    在转储文件tailcall.c.137t.tailc中,你会看到:

    ;; Function foo (foo, funcdef_no=0, decl_uid=1721, cgraph_uid=0)
    
    Found tail call [tailcall.c : 6:3] _4 = bar (x_2(D)); [tail call]
    

    接下来你可以用这种方式分析你的所有项目。 GCC 会写出所有找到尾调用的地方。

    但是这种方式是 gcc 版本相关的。我的输出对 gcc 4.8.x 有效,但对于其他分支,tailcall tr​​ee pass 的数量,其名称或转储格式可能会有所不同。

    另外,如果尾调用对您来说是个问题,那么只需使用 -fno-optimize-sibling-calls 选项

    gcc -O2 -S tailcall.c -fno-optimize-sibling-calls
    

    将产生没有兄弟调用的输出。

    【讨论】:

    • 这是一个非常有用的信息。但是有没有办法直接从共享对象中确定它,而无需重新编译代码。我们也不能禁用此优化,因为这不是我们的代码。我们的程序只能访问主程序的堆栈跟踪,并且可以分析共享对象以进行优化。
    • 如果编译时没有DWARF调试信息,那我不信,有可能。
    • 我认为一些调试信息可用,例如例程名称及其大小。是否有一些确定性的方法可以通过反汇编代码来确定是否应用了尾调用。我正在考虑在例程结束时寻找 jmp 指令的存在而不是 call/ret。
    猜你喜欢
    • 2014-04-15
    • 1970-01-01
    • 2020-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-23
    • 2012-04-18
    • 2011-07-16
    相关资源
    最近更新 更多