c - 将无符号字符转换为有符号数据类型时,为什么在汇编中使用 movzbl?

标签 c assembly casting mov zero-extension

我正在学习汇编中的数据移动( MOV )。
我尝试编译一些代码以在 x86_64 Ubuntu 18.04 机器中查看程序集:

typedef unsigned char src_t;
typedef xxx dst_t;

dst_t cast(src_t *sp, dst_t *dp) {
    *dp = (dst_t)*sp;
    return *dp;
}

哪里src_tunsigned char .至于dst_t , 我试过 char , short , intlong .
结果如下所示:
// typedef unsigned char src_t;
// typedef char dst_t;
//  movzbl  (%rdi), %eax
//  movb    %al, (%rsi)

// typedef unsigned char src_t;
// typedef short dst_t;
//  movzbl  (%rdi), %eax
//  movw    %ax, (%rsi)

// typedef unsigned char src_t;
// typedef int dst_t;
//  movzbl  (%rdi), %eax
//  movl    %eax, (%rsi)

// typedef unsigned char src_t;
// typedef long dst_t;
//  movzbl  (%rdi), %eax
//  movq    %rax, (%rsi)

我想知道为什么movzbl在每种情况下都使用?不是应该对应dst_t ?
谢谢!

最佳答案

如果您想知道为什么不movzbw (%rdi), %axshort ,那是因为写入 8 位和 16 位部分寄存器必须与之前的高字节合并。

写一个像 EAX 这样的 32 位寄存器隐式地将零扩展到完整的 RAX,避免对 RAX 的旧值或任何 ALU 合并 uop 的错误依赖。 ( Why do x86-64 instructions on 32-bit registers zero the upper part of the full 64-bit register? )

在 x86 上加载字节的“正常”方式是使用 movzblmovsbl , 与 ARM 等 RISC 机器相同 ldrbldrsb , 或 MIPS lbu/lb .

GCC通常避免的奇怪的CISC事情是与仅替换低位的旧值合并,例如movb (%rdi), %al . Why doesn't GCC use partial registers? Clang 更加鲁莽,并且会更频繁地编写部分 regs,而不仅仅是为商店读取它们。您可能会看到 clang 加载到 %al和存储时间 dst_tsigned char .

如果您想知道为什么不movsbl (%rdi), %eax (符号扩展)

源值是无符号的,因此零扩展名 (不是符号扩展)是根据 C 语义扩展它的正确方法。获取movsbl ,你需要 return (int)(signed char)c .

*dp = (dst_t)*sp;投到dst_t从对 *dp 的赋值已经是隐含的.

unsigned char 的值范围是 0..255(在 x86 上,CHAR_BIT = 8)。

将此零扩展到 signed int可以产生一个值范围从 0..255 ,即将每个值保留为有符号的非负整数。

将此符号扩展到 signed int将产生一个值范围从 -128..+127 ,更改 unsigned char 的值values >= 128。这与 C 语义冲突,用于扩大转换保留值。

Shouldn't it correspond to dst_t?



它必须至少加宽为 dst_t .事实证明,使用 movzbl 扩展到 64 位(前 32 位由隐式零扩展写入 32 位 reg 处理)是最有效的加宽方式。

存储到 *dp是一个很好的演示,asm 适用于 dst_t宽度不是 32 位。

无论如何,请注意只有一次转换发生。您的 src_t转换为 dst_t在 al/ax/eax/rax 中使用加载指令,并存储到任何宽度的 dst_t 中。并且还留在那里作为返回值。

即使您只是要读取该结果的低字节,零扩展负载也是正常的。

关于c - 将无符号字符转换为有符号数据类型时,为什么在汇编中使用 movzbl?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58539038/

相关文章:

assembly - EMU8086显示屏(黑屏)

汇编语言随机数生成器

c++ - 为什么要经历 static_cast 的麻烦——将数字转换为 double ?

c# - 枚举未设置时会发生什么?

C++如何编写通用结构来保存任何枚举

c - C中Winsock的HTTPS/SSL连接

c - 为什么模数操作数不适用于我的 C 代码中的输入变量?

c - C中结构的通用排序函数

c - C中的黄金分割法

linux - 递归删除 x64 程序集中的文件