Assembly 当arg为64位长时,为什么gcc使用mov%esi,%ecx进行移位计数?
因此,我正在阅读《计算机系统:程序员的观点》第三版。对于其中一个实践问题解决方案,它说以下函数:Assembly 当arg为64位长时,为什么gcc使用mov%esi,%ecx进行移位计数?,assembly,x86,x86-64,Assembly,X86,X86 64,因此,我正在阅读《计算机系统:程序员的观点》第三版。对于其中一个实践问题解决方案,它说以下函数: long shift(long x, long n) { x <<= 4; x >>= n; return x; } 长移位(长x,长n) { x=n; 返回x; } 输出汇编代码,其中一部分将是(及其注释): #x在%rdi中,n在%rsi中 movq%rdi,%rax#得到x salq$4,%rax#x=n 假设基于x86-64 linux,
long shift(long x, long n)
{
x <<= 4;
x >>= n;
return x;
}
长移位(长x,长n)
{
x=n;
返回x;
}
输出汇编代码,其中一部分将是(及其注释):
#x在%rdi中,n在%rsi中
movq%rdi,%rax#得到x
salq$4,%rax#x=n
假设基于x86-64 linux,“long”是四字大小
我的问题是关于我添加“问题”的那一行。既然n是“long”类型,为什么那行不是“movq%rsi,%rcx”?我认为这不是一个打字错误,因为他们特别添加了“Get n(4字节)”。只使用了
rcx
(cl
)的低位字节。但是,movb%sil,%cl
可能会导致部分寄存器暂停,而movl%esi,%ecx
不会(AFAIK),因为它会隐式清除rcx
的上半部分。虽然只有8位cl
用作sar
的参数,但实际上只有cl
的低6位用于移位,即,对于n=0x12345678
而言,CPU执行的实际移位仅为0x78&0x3F=0x38=56
位移位。对于32位目标,仅使用5位,以此类推:。。。定义为64b长的n
在这种特定情况下已经在C源代码级别上过多了。这让我想知道,C操作符>
是否是n>63
的UB。。。关于设置完整rcx
的原因(写入ecx
setrcx
)=性能。现在不用担心了?因为编译器知道该值将以任何方式被截断为6位(通过sar
指令-我的意思是暂时截断,用于移位,而不是将其写回cl
truncated),并且mov%esi,%ecx
的操作码(-1B)比mov%rsi,%rcx
短,因此,它具有相同或更好的性能。(较短的机器代码会使指令缓存变得不那么杂乱,因此它为性能关键型代码提供了更大的缓冲区)为什么movb%sil,%cl
次优:它需要一个REX前缀。它不会直接导致部分寄存器暂停,@Michael,(因为您以后不会阅读%rcx
的其余部分),但mov
本身确实会在某些CPU上创建错误的依赖关系:。32位mov
是最佳选择,因为它只有2个字节,而mov%rsi,%rcx
也需要一个REX前缀。可能重复:。我认为这些问答涵盖了这一点,尤其是评论。如果有人认为它需要自己的答案,我们可以重新打开它。
# x in %rdi, n in %rsi
movq %rdi, %rax # Get x
salq $4, %rax # x <<= 4
movl %esi, %ecx # Get n (4 bytes) **QUESTION**
sarq %cl, %rax # x >>= n