了解 clang 在汇编中的作用,递减递增的循环

Understanding what clang is doing in assembly, decrementing for a loop that is incrementing

考虑以下 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);
}

它只是一个递增其值的循环,returns 它在最后。 asm volatile 防止循环被优化掉。我们使用参数 -Wall -Wextra -std=c++11 -g -O3g++ 8.1clang++ 5.0 下编译它。

现在,如果我们看看 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 在做什么?

(我正在寻找有关 clang 汇编代码的评论,以描述每一步都做了什么以及它是如何工作的)。

函数的效果是 return n,通过计数到 n 和 return 结果,或者简单地 returning n 的传入值。 clang 代码执行后者。计数循环在这里:

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

它首先将 n 的值复制到 rax 中。它递减 rax 中的值,如果结果不为 0,则跳回到 .LBB0_3。如果值 0,它会进入下一条指令,它将 n 的原始值复制到 rax 和 returns.

没有存储 i,但代码会循环规定的次数,并且 returns 是 i 本来应该有的值,即 n.