代码之家  ›  专栏  ›  技术社区  ›  Erel Segal-Halevi

未优化的clang++代码在平凡的main()中生成不需要的“movl$0,-4(%rbp)”

  •  3
  • Erel Segal-Halevi  · 技术社区  · 7 年前

    我创建了一个最小的C++程序:

    int main() {
        return 1234;
    }
    

    并使用clang++5.0编译,禁用了优化功能(默认值 -O0 )。 The resulting assembly code is :

      pushq %rbp
      movq %rsp, %rbp
      movl $1234, %eax # imm = 0x4D2
      movl $0, -4(%rbp)
      popq %rbp
      retq
    

    我理解大多数行,但我不理解“movl$0,-4(%rbp)”。程序似乎将一些局部变量初始化为0。为什么?

    哪个编译器内部细节导致此存储与源代码中的任何内容都不对应?

    1 回复  |  直到 7 年前
        1
  •  7
  •   Michael Petch    7 年前

    TL;DR公司 :在未优化的代码中,CLANG++为返回值留出4个字节 main 并按照C++(包括C++11)标准将其设置为零。它生成了 主要的 不需要它的函数。这是未优化的副作用。通常,一个未优化的编译器会生成它可能需要的代码,然后就不再需要它了,而且也没有做任何事情来清理它。


    因为您正在使用 -O0 对代码进行的优化非常少( -O0级 可能会删除死代码等)。试图理解未优化代码中的工件通常是徒劳的。未优化代码的结果是额外的加载和存储以及原始代码生成的其他工件。

    在这种情况下 主要的 是特别的,因为在C99/C11和C++中,标准有效地说,当到达 主要的 默认返回值为0。 The C11 standard 表示:

    5.1.2.2.3程序终止

    1如果main函数的返回类型是与int兼容的类型,则从 对main函数的初始调用等效于使用值调用exit函数 由main函数返回作为其参数;11) 到达终止的} main函数返回值0 。如果返回类型与int不兼容,则 未指定返回到主机环境的终止状态。

    这个 C++11 standard 表示:

    3.6.1主要功能

    5) main中的return语句具有离开main函数的效果(使用automatic 并使用返回值作为参数调用std::exit。 如果控制到达终点 在不遇到return语句的情况下,其效果是执行

     return 0;
    

    在CLANG++版本中,您使用的是未优化的64位代码,默认情况下,返回值0位于 dword ptr [rbp-4]

    问题是,您的测试代码有点过于琐碎,无法看到这个默认返回值是如何发挥作用的。以下是一个更好的示例:

    int main() {
        int a = 3;
        if (a > 3) return 5678;
        else if (a == 3) return 42;
    }
    

    此代码有两个退出显式退出点,通过 return 5678 return 42; 但没有决赛 return 在函数末尾。如果 } 默认值为返回0。如果我们检查 godbolt output 我们看到:

    main:                                   # @main
            push    rbp
            mov     rbp, rsp
            mov     dword ptr [rbp - 4], 0        # Default return value of 0
            mov     dword ptr [rbp - 8], 3
            cmp     dword ptr [rbp - 8], 3        # Is a > 3
            jle     .LBB0_2
            mov     dword ptr [rbp - 4], 5678     # Set return value to 5678
            jmp     .LBB0_5                       # Go to common exit point .LBB0_5
    .LBB0_2:
            cmp     dword ptr [rbp - 8], 3        # Is a == 3?
            jne     .LBB0_4
            mov     dword ptr [rbp - 4], 42       # Set return value to 42
            jmp     .LBB0_5                       # Go to common exit point .LBB0_5
    .LBB0_4:
            jmp     .LBB0_5                       # Extraneous unoptimized jump artifact 
    # This is common exit point of all the returns from `main`
    .LBB0_5:
            mov     eax, dword ptr [rbp - 4]      # Use return value from memory
            pop     rbp
            ret
    

    可以看到,编译器生成了一个设置返回值的公共退出点( EAX公司 )从堆栈地址 dword ptr[rbp-4] 。在代码开头 dword ptr[rbp-4] 显式设置为0。在更简单的情况下,未优化的代码仍然生成该指令,但没有使用。

    如果使用选项构建代码 -ffreestanding 您应该看到的默认返回值 主要的 不再设置为0。这是因为 主要的 适用于主体环境,而非独立环境。