为什么在将 C 语言从 unsigned int 向下转换为 unsigned char 时,movl 优于 movb?
Why is movl preferred to movb when translating a C downcast from unsigned int to unsigned char?
考虑将 unsigned
向下转换为 unsigned char
、
的精简示例
void unsigned_to_unsigned_char(unsigned *sp, unsigned char *dp)
{
*dp = (unsigned char)*sp;
}
上面的C代码用gcc -Og -S
翻译成汇编代码为
movl (%rdi), %eax
movb %al, (%rsi)
为什么 C 语言到汇编的翻译不是如下所示?
movb (%rdi), %al
movb %al, (%rsi)
是因为这是不正确的,还是因为 movl
比 movb
更传统,或者编码更短?
当新低字节与相应 32/64 位寄存器的旧高字节合并时,写入 8 位 x86 寄存器可能会产生额外的合并微操作。这也可能导致对寄存器先前值的意外数据依赖。
出于这个原因,在 x86 上只写入通用寄存器的 32/64 位变体通常是个好主意。
你问题中的强制转换是完全不必要的,因为语言无论如何都会在赋值之前有效地执行强制转换,因此它对生成的代码没有任何贡献(删除它并且看不到任何更改,没有错误或警告)。
右边的引用是 unsigned int
类型的,所以,这就是它所做的。给定 32 位总线,执行字解引用(模对齐问题)不会影响性能。
如果你想要其他的,你可以在解引用之前强制转换,如下:
void unsigned_to_unsigned_char(unsigned *sp, unsigned char *dp)
{
*dp = *(unsigned char *)sp;
}
这将生成您期望的字节移动指令。
考虑将 unsigned
向下转换为 unsigned char
、
void unsigned_to_unsigned_char(unsigned *sp, unsigned char *dp)
{
*dp = (unsigned char)*sp;
}
上面的C代码用gcc -Og -S
翻译成汇编代码为
movl (%rdi), %eax
movb %al, (%rsi)
为什么 C 语言到汇编的翻译不是如下所示?
movb (%rdi), %al
movb %al, (%rsi)
是因为这是不正确的,还是因为 movl
比 movb
更传统,或者编码更短?
当新低字节与相应 32/64 位寄存器的旧高字节合并时,写入 8 位 x86 寄存器可能会产生额外的合并微操作。这也可能导致对寄存器先前值的意外数据依赖。
出于这个原因,在 x86 上只写入通用寄存器的 32/64 位变体通常是个好主意。
你问题中的强制转换是完全不必要的,因为语言无论如何都会在赋值之前有效地执行强制转换,因此它对生成的代码没有任何贡献(删除它并且看不到任何更改,没有错误或警告)。
右边的引用是 unsigned int
类型的,所以,这就是它所做的。给定 32 位总线,执行字解引用(模对齐问题)不会影响性能。
如果你想要其他的,你可以在解引用之前强制转换,如下:
void unsigned_to_unsigned_char(unsigned *sp, unsigned char *dp)
{
*dp = *(unsigned char *)sp;
}
这将生成您期望的字节移动指令。