代码之家  ›  专栏  ›  技术社区  ›  ar2015

在汇编代码中查找循环或数字

  •  2
  • ar2015  · 技术社区  · 7 年前

    我已将C++代码转换为具有高优化级别的程序集

    #include <iostream>
    using namespace std;
    
    int main()
    {
        float sum=0;
        for(int i = 0; i < 10; i++)
            sum += 1.0f/float(i+1);
        cout<<sum<<endl;
        return 0;
    }
    

    通过

    g++ -O3 -S main.cpp
    g++ -O3 main.cpp && ./a.out
    

    结果是

    2.92897

    但当我把它转换成汇编时,我不知道这个数字在哪里。应该有一个循环或(如果展开)最终结果 2.92897 . 但我在以下代码中找不到它:

        .file   "main.cpp"
        .section    .text.startup,"ax",@progbits
        .p2align 4,,15
        .globl  main
        .type   main, @function
    main:
    .LFB1561:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movl    $_ZSt4cout, %edi
        movsd   .LC0(%rip), %xmm0
        call    _ZNSo9_M_insertIdEERSoT_
        movq    %rax, %rdi
        call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        xorl    %eax, %eax
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
    .LFE1561:
        .size   main, .-main
        .p2align 4,,15
        .type   _GLOBAL__sub_I_main, @function
    _GLOBAL__sub_I_main:
    .LFB2048:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movl    $_ZStL8__ioinit, %edi
        call    _ZNSt8ios_base4InitC1Ev
        movl    $__dso_handle, %edx
        movl    $_ZStL8__ioinit, %esi
        movl    $_ZNSt8ios_base4InitD1Ev, %edi
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        jmp __cxa_atexit
        .cfi_endproc
    .LFE2048:
        .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
        .section    .init_array,"aw"
        .align 8
        .quad   _GLOBAL__sub_I_main
        .local  _ZStL8__ioinit
        .comm   _ZStL8__ioinit,1,1
        .section    .rodata.cst8,"aM",@progbits,8
        .align 8
    .LC0:
        .long   0
        .long   1074228871
        .hidden __dso_handle
        .ident  "GCC: (Ubuntu 7.2.0-1ubuntu1~16.04) 7.2.0"
        .section    .note.GNU-stack,"",@progbits
    

    我被怀疑 .LC0 1074228871 . 但这种转换通过 another code 给我2.11612,这是一个不同的数字。

    那么,汇编代码中的计算或结果在哪里?

    1 回复  |  直到 7 年前
        1
  •  5
  •   Peter Cordes    7 年前

    循环不仅仅是展开的,它还通过不断的传播进行了彻底的优化。这就是为什么 main 没有分支,除了 call .

    movsd .LC0(%rip), %xmm0 (MOV Scalar Double)将8字节FP参数加载到 cout<<sum 从中的静态常量 .rodata ,就像大多数编译器处理FP常量的正常方式一样。

    .LC0 ,我们发现:

    .LC0:
        .long   0
        .long   1074228871
    

    这些伪指令组合成8字节的数据。这是位模式的整数表示,表示 2.92897... 在里面 IEE754 double-precision ( binary64 ) . x86是FP和integer的小端点,因此 0 在第一个(低位)中,4个字节是有效位(又名尾数)的底部。

    有一个交互式单精度转换器 https://www.h-schmidt.net/FloatConverter/IEEE754.html ,但IDK为 double 您可以插入位模式的整数值,并将其解码为 双重的 .

    但通过另一个代码进行这样的转换,我得到了2.11612,这是一个不同的数字。

    您链接了将位模式的上半部分双关的代码 float (违反C++指针别名规则,顺便说一句。请使用 memcpy 用于类型punning)。 如果你 1074228871ULL << 32 然后键入双关语 双重的 .


    clang将asm注释放在FP常量上,以十进制显示其值,但gcc没有。例如,从 Godbolt compiler explorer : clang5.0 -O3 将循环优化为相同的常量,但在asm中的表示略有不同:

    .LCPI0_0:
        .quad   4613777869364002816     # double 2.9289684295654297
        # exactly equivalent to what gcc emits,
        # just different syntax for the same 8 bytes
    

    它只是字节,十进制整数是gcc在编译器生成的asm中对所有常量所做的,尽管这对人类来说几乎是无用的(甚至比十六进制还要糟糕)。

    我不确定GAS语法是否处理FP常量;NASM做到了。但正如我所说的,都是字节。