x86-64汇编中仅用xorl清除eax作为64位循环计数器是否合理?
关于x86-64汇编中仅清除RAX低32位作为64位计数器的疑问
原始C函数
#include <stdint.h> #include <stdlib.h> #include <math.h> #define SIZE (1L << 16) void test(uint8_t * a , uint8_t * b) { uint64_t i; for (i = 0; i < SIZE; i++) { a[i] += b[i]; } }
生成的x86-64汇编片段
# %bb.0: #DEBUG_VALUE: test:a <- $rdi #DEBUG_VALUE: test:b <- $rsi #DEBUG_VALUE: test:i <- 0 .loc 0 15 3 prologue_end # example1.c:15:3 leaq 65536(%rsi), %rax cmpq %rdi, %rax jbe .LBB0_2 .Ltmp0: # %bb.1: #DEBUG_VALUE: test:a <- $rdi #DEBUG_VALUE: test:b <- $rsi #DEBUG_VALUE: test:i <- 0 leaq 65536(%rdi), %rax cmpq %rsi, %rax jbe .LBB0_2 .Ltmp1: # %bb.4: #DEBUG_VALUE: test:a <- $rdi #DEBUG_VALUE: test:b <- $rsi #DEBUG_VALUE: test:i <- 0 .loc 0 0 3 is_stmt 0 # example1.c:0:3 xorl %eax, %eax .Ltmp2: .p2align 4, 0x90 .LBB0_5: # =>This Inner Loop Header: Depth=1 #DEBUG_VALUE: test:a <- $rdi #DEBUG_VALUE: test:b <- $rsi #DEBUG_VALUE: test:i <- $rax .loc 0 16 13 is_stmt 1 # example1.c:16:13 movzbl (%rsi,%rax), %ecx .loc 0 16 10 is_stmt 0 # example1.c:16:10 addb %cl, (%rdi,%rax)
问题
汇编代码前几段用于检查两个数组的地址是否重叠,之后进入逐元素相加循环(.LBB0_5块)。重叠检查中使用了RAX寄存器,而循环开始前仅执行xorl %eax,%eax清除RAX的低32位。但循环中RAX作为64位计数器使用,为何不清除整个RAX寄存器的64位?
解答
这是x86-64架构的核心设计特性:当对32位通用寄存器(比如EAX)执行写操作时,CPU会自动将对应64位寄存器(RAX)的高32位清零。
所以xorl %eax, %eax这条指令,不仅把EAX(RAX的低32位)置为0,还会直接将RAX的高32位也设为0——相当于整个64位的RAX寄存器被完全清零,完全满足循环计数器i从0开始的需求。
另外编译器这么做还有效率考量:xorl %eax, %eax是3字节指令,而xorq %rax, %rax是4字节指令,前者更紧凑,执行效率更高。编译器会优先选择这种更高效的指令完成相同功能。
总结来说,这条32位异或指令已经足够把整个RAX寄存器清零,完全能支撑后续64位计数器的使用,没必要多此一举去清除整个64位寄存器。
内容的提问来源于stack exchange,提问作者Tarique
相关产品推荐
相关产品推荐

