我正在分析一段低效的代码,但其中有些代码是如此令人困惑?
原始代码:
#include <string.h>
void lowwer(char *str) {
for (int i = 0; i < strlen(str); ++i) {
str[i] -= ('A' - 'a');
}
}
Run Code Online (Sandbox Code Playgroud)
汇编代码(由 clang 13 使用 -Og 选项生成):
lowwer:
pushq %r14 # use saved-registers
pushq %rbx
pushq %rax
# guard do while
cmpb $0, (%rdi) # compare &str with null (check if strlen(str) == 0)
je .LBB0_3
# loop initialization
movq %rdi, %r14 # %r14 = str
xorl %ebx, %ebx # clear %rbx (for more compact encoding)
.LBB0_2: # =>This Inner Loop Header: Depth=1
addb $32, (%r14,%rbx) # subtract -32 from str[i] ('A' - 'a' = -32)
addq $1, %rbx # ++i
movq %r14, %rdi # seems meaningless here?
callq strlen@PLT
cmpq %rbx, %rax # check i < strlen(str)
ja .LBB0_2
.LBB0_3: # end
addq $8, %rsp # ???
popq %rbx # free registers
popq %r14
retq
Run Code Online (Sandbox Code Playgroud)
movq %r14, %rdi正在做什么?这看起来毫无意义,因为%r14持有字符串指针和 therdi是一样的。addq $8, %rsp。看起来毫无头绪。rdi是一个调用者保存的寄存器,因此会被调用而废弃strlen。为了保留其内容,编译器发出代码将其内容移动到r14,每次迭代将其作为参数复制回来strlen。
该addq $8, %rsp指令释放先前由 分配的堆栈空间pushq %rax。分配此堆栈空间是为了满足 amd64 SysV ABI 施加的堆栈对齐要求。
有关完整的调用约定和调用者/被调用者保存的寄存器列表,请参阅 amd64 SysV ABI 补充。