【问题标题】:g++ -no optimization- skips asm code after gotog++ -no optimization- 在 goto 之后跳过 asm 代码
【发布时间】:2014-03-20 12:34:01
【问题描述】:

拥有这段代码:

int main(){
        int x = 13;
        goto f;
        asm __volatile__ (".byte 0xff");
        f:
        return 0;
}

我不明白为什么g++优化它并且不包含操作码(在反汇编中):

# 5 "q.c" 1
        .byte 0xff
# 0 "" 2

即使我在没有任何优化的情况下进行编译: g++ -g -O0 -S q.c。我尝试单独使用g++ -gg++ -O0,因为我读到它在某些情况下可能不兼容。

如果我评论goto f; 行,它将插入操作码。

        .file   "q.c"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $13, -4(%rbp)
#APP
# 5 "q.c" 1<<<<<<<<<<
        .byte 0xff<<<<<<<<<<
# 0 "" 2<<<<<<<<<<
.L2:
#NO_APP
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
        .section        .note.GNU-stack,"",@progbits

问题是:

g++ 是否不包含一段代码,即使我完全没有优化编译也不会使用它?

我想知道为什么它不包含没有找到其他解决方案的那段代码。

更新

我在 cmets 中读到:这是糟糕的代码。但如果我想拥有它怎么办?如果我想在那里注入一段代码,它本身不做任何事情怎么办? g++ 会限制我吗?

更新 2

因为它的死代码不是解释。我在 Windows VS2012 上编译了这段代码

int main() {
    std::cout << "something ";
    goto foo;
     __asm _emit 0xff
     __asm _emit 0xfe;
    foo :
    std::cout << "other thing";
}

你猜怎么着?当它使用 Debug 配置编译时,asm 代码包含在二进制文件中:

.text:00414ECE                 push    offset aSomething ; "something "
.text:00414ED3                 mov     eax, ds:__imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ; std::basic_ostream<char,std::char_traits<char>> std::cout
.text:00414ED8                 push    eax
.text:00414ED9                 call    loc_41129E
.text:00414EDE                 add     esp, 8
.text:00414EE1                 jmp     short loc_414EE7
.text:00414EE1 ; ---------------------------------------------------------------------------
.text:00414EE3                 db 0EBh ; d
.text:00414EE4                 db    2
.text:00414EE5                 db 0FFh<<<<<<<<<<<<<<
.text:00414EE6                 db 0FEh ; ¦<<<<<<<<<<<<
.text:00414EE7 ; ---------------------------------------------------------------------------
.text:00414EE7
.text:00414EE7 loc_414EE7:                             ; CODE XREF: main+31j
.text:00414EE7                 push    offset aOtherThing ; "other thing"
.text:00414EEC                 mov     eax, ds:__imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ; std::basic_ostream<char,std::char_traits<char>> std::cout
.text:00414EF1                 push    eax
.text:00414EF2                 call    loc_41129E
.text:00414EF7                 add     esp, 8
.text:00414EFA                 jmp     short loc_414EFE

【问题讨论】:

  • 因为它是死代码?
  • 但是如果我想注入那个代码并且我希望它在那里呢?
  • 我和埃米尔在这里;确定 volatile 的全部意义在于阻止它进行优化?另外,@EmilCondrea,您是否尝试过使用volatile 而不是__volatile__
  • 尝试使用-fno-dce(无死代码消除)标志。
  • @OMGtechy 是的,我试过了

标签: c++ linux assembly


【解决方案1】:

有趣的是,我可以尝试的所有编译器(gcc、llvm-gcc、icc、clang)都优化了代码。

作为一种解决方法,您可以在 asm 本身中包含 goto:

__asm__ __volatile__ (
    "jmp 1f\n\t"
    ".byte 0xff\n\t"
    "1:");

不幸的是,这是特定于架构的,而您的原始代码可能不是。对于这种情况,我能想到的最好的方法是:

volatile int false = 0;
if (false) __asm__ __volatile__ (".byte 0xff");

当然,这会导致运行时负载和测试。 即使启用了优化,这两种方法也能正常工作。

【讨论】:

    【解决方案2】:

    编译器通常将代码转换为所谓的基本块的内部图。基本块是一系列指令,除了基本块的最后一条指令外没有跳转。

    因此,您提供的代码将产生如下基本块:

    然后编译器在第一个基本块开始遍历基本块图,但从不访问第二个基本块,因为没有其他基本块跳转到它(即第二个基本块在图中没有传入边) ,因此它不会转换为程序集。换句话说,省略第二个基本块的组装生成不是故意的,而只是图遍历的副作用。

    在这方面,Microsoft 的编译器可能与其他编译器的工作方式不同。例如,它可以将所有基本块转换为汇编,连接结果并然后解决跳转,而不是相反。

    【讨论】:

      【解决方案3】:

      您所拥有的是无法访问的代码。删除它不是优化,它只是节省空间。

      编译器有义务提供执行源代码描述的功能的代码。 asm 定义是实现定义的——编译器可以做它想做的任何事情,你不能抱怨。它做它该做的事。

      显而易见的建议是在代码上贴一个标签,让编译器至少有一种方法可以到达那里,即使它永远不会这样做。

      编辑:省略无法访问的代码不在我检查的任何编译器优化列表中,但在某些时候这变成了定义问题。编译器编写者可以轻松地安排在无条件分支之后省略代码,而无需任何复杂的块分析。我想你得问问他们。

      【讨论】:

      • 标签是函数局部的,编译器很容易看穿它。
      • @david.pfx 我更新了我的问题。用VS2012编译时包含在二进制文件中的不可达代码
      • “节省空间”是一种优化。
      • @rightfold:不,节省空间只是优化的可能目标之一。我检查的大多数编译器优化列表不包括遗漏无法访问的代码(死代码不同)。但我可能会改写我的答案。
      • @keltar:你可以总是欺骗优化者。 if (tm.tm_mon &gt; 12) goto ...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-07
      • 2015-07-05
      • 1970-01-01
      相关资源
      最近更新 更多