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

了解clang在汇编中所做的工作,为递增的循环递减

  •  0
  • Vincent  · 技术社区  · 6 年前

    在C++中考虑下面的代码:

    #include <cstdlib>
    
    std::size_t count(std::size_t n)
    {
        std::size_t i = 0;
        while (i < n) {
            asm volatile("": : :"memory");
            ++i;
        }
        return i;
    }
    
    int main(int argc, char* argv[])
    {
        return count(argc > 1 ? std::atoll(argv[1]) : 1);
    }
    

    它只是一个递增其值的循环,并在结束时返回它。这个 asm volatile 防止循环被优化掉。我们把它编译成 g++ 8.1 clang++ 5.0 带着争论 -Wall -Wextra -std=c++11 -g -O3 .

    现在,如果我们看看 compiler explorer 正在生产,我们有,因为 g++ :

    count(unsigned long):
      mov rax, rdi
      test rdi, rdi
      je .L2
      xor edx, edx
    .L3:
      add rdx, 1
      cmp rax, rdx
      jne .L3
    .L2:
      ret
    main:
      mov eax, 1
      xor edx, edx
      cmp edi, 1
      jg .L25
    .L21:
      add rdx, 1
      cmp rdx, rax
      jb .L21
      mov eax, edx
      ret
    .L25:
      push rcx
      mov rdi, QWORD PTR [rsi+8]
      mov edx, 10
      xor esi, esi
      call strtoll
      mov rdx, rax
      test rax, rax
      je .L11
      xor edx, edx
    .L12:
      add rdx, 1
      cmp rdx, rax
      jb .L12
    .L11:
      mov eax, edx
      pop rdx
      ret
    

    对于clang++:

    count(unsigned long): # @count(unsigned long)
      test rdi, rdi
      je .LBB0_1
      mov rax, rdi
    .LBB0_3: # =>This Inner Loop Header: Depth=1
      dec rax
      jne .LBB0_3
      mov rax, rdi
      ret
    .LBB0_1:
      xor edi, edi
      mov rax, rdi
      ret
    main: # @main
      push rbx
      cmp edi, 2
      jl .LBB1_1
      mov rdi, qword ptr [rsi + 8]
      xor ebx, ebx
      xor esi, esi
      mov edx, 10
      call strtoll
      test rax, rax
      jne .LBB1_3
      mov eax, ebx
      pop rbx
      ret
    .LBB1_1:
      mov eax, 1
    .LBB1_3:
      mov rcx, rax
    .LBB1_4: # =>This Inner Loop Header: Depth=1
      dec rcx
      jne .LBB1_4
      mov rbx, rax
      mov eax, ebx
      pop rbx
      ret
    

    理解g++生成的代码并没有那么复杂,循环是:

    .L3:
      add rdx, 1
      cmp rax, rdx
      jne .L3
    

    每次迭代增量 rdx ,并将其与 rax 存储循环大小的。

    现在,我不知道clang++在做什么。显然它使用 dec ,这对我来说很奇怪,我甚至不知道实际的循环在哪里。我的问题是:叮当在干什么?

    (我正在寻找有关clang程序集代码的注释,以描述在每个步骤中所做的操作以及它的实际工作方式)。

    1 回复  |  直到 6 年前
        1
  •  5
  •   Pete Becker    6 年前

    函数的作用是返回 n ,或者数到 n 返回结果,或者只返回 . 宗族法典做的是后者。计数循环在这里:

       mov rax, rdi
    .LBB0_3: # =>This Inner Loop Header: Depth=1
      dec rax
      jne .LBB0_3
      mov rax, rdi
      ret
    

    它首先复制 n 进入之内 rax . 它将值递减为 拉克斯 ,如果结果不是0,则跳回到 .LBB0_3 . 如果值 0它通过下一个指令,它复制原始值 n 进入之内 拉克斯 然后返回。

    没有 i 存储,但代码循环指定的次数,并返回值 也就是说, .