了解 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 -O3
在 g++ 8.1
和 clang++ 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
.
考虑以下 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 -O3
在 g++ 8.1
和 clang++ 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
.