Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2008/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将低位字节从int复制到char的说明:只加载字节更简单?_C_Assembly_X86 64_Micro Optimization_Instructions - Fatal编程技术网

将低位字节从int复制到char的说明:只加载字节更简单?

将低位字节从int复制到char的说明:只加载字节更简单?,c,assembly,x86-64,micro-optimization,instructions,C,Assembly,X86 64,Micro Optimization,Instructions,我在读一本教科书,书中有一个练习,是基于C代码编写x86-64汇编代码 //Assume that the values of sp and dp are stored in registers %rdi and %rsi int *sp; char *dp; *dp = (char) *sp; 答案是: //first approach movl (%rdi), %eax //Read 4 bytes movb %al, (%rsi) //Store low-order b

我在读一本教科书,书中有一个练习,是基于C代码编写x86-64汇编代码

//Assume that the values of sp and dp are stored in registers %rdi and %rsi

int *sp;
char *dp;
*dp = (char) *sp;
答案是:

//first approach

movl (%rdi), %eax    //Read 4 bytes
movb %al, (%rsi)     //Store low-order byte
我能理解,但只是想知道我们能不能先做一些简单的事情:

//second approach

movb (%rdi), %al    //Read one bytes only rather than read all four bytes
movb %al, (%rsi)     //Store low-order byte
与第一种方法相比,第二种方法不是更简洁、更直接吗?因为我们只关心
%rdi
的较低字节,而对其较高的3个字节不感兴趣。第一种方法有点不明显(OP将问题从一个带有示例的更一般的问题改为一个非常具体的问题,这或许可以解释为什么这个答案看起来很有趣。)

对于您的问题,更一般的答案是,对于要编译为机器代码的HLL中的任何操作,通常有许多方法编写机器指令来执行该操作

一个好的编译器会知道这些变体中的许多。它的问题是,对于程序中的所有操作,为每个操作符选择通常更有效的变体,以使它们缝合在一起以实现一个工作程序。例如,如果实现了一个HLL操作,其结果保留在寄存器中,则后续HLL操作应该使用该结果,然后编译器会选择第一个运算符和第二个运算符的实现,其中第一个运算符将值保留在寄存器中,而第二个运算符恰好将该寄存器用作输入,否则程序将无法工作


当你认为一个真正的程序由数千个HLL运算符组成,并且它们的各个实现必须是一致的,你可以看到编译器有一个非常复杂的工作,确保所有的事情都在一起,并且它是相当有效的。

< P> <强>是的,你的字节加载方式是正确的,但实际上并不是更有效。在大多数CPU上。
TL:DR:当您有同样方便的选项时,通常避免写入字节或16位寄存器

(顺便说一句,您在评论中得到的建议都是错误的:x86是little endian,存储转发问题不太可能发生(尽管在一些较旧的CPU、IDK上可能不完全错误)


写入部分寄存器(小于32位,因此不会隐式零扩展到完整寄存器)对某些微体系结构上的旧值存在错误依赖。例如,
movb(%rdi),%al
在Intel Haswell/Skylake上解码为微融合加载+合并ALU操作。(.特别是对于Intel Haswell/Skylake,)

如果只执行零扩展字节加载,
movzbl(%rdi),%eax
会更有效

或者,由于我们可以假设
(%rdi)
的最后一个存储是dword或更大的存储(因此,如果存储转发仍在运行中,则会更有效),使用
movl(%rdi)执行dword加载实际上是最有效的,%eax
。这避免了可能的部分寄存器惩罚,并且机器代码的大小比
movzbl
小(更小更好,因为在UOP方面其他相等选项之间的平衡)。此外,一些旧的AMD CPU运行
movzbl
的效率略低于dword
mov
加载。(如零扩展需要ALU端口)

(大多数CPU在加载端口中“免费”运行
movzbl
,有些CPU也在加载端口中运行
movsbl
符号扩展,而不需要任何ALU端口,特别是Intel Sandybridge系列。)


存储转发不是问题: 所有(?)当前CPU都可以有效地从dword存储转发到任何单个字节的字节重新加载,当然还有低位字节,特别是当dword存储对齐时(如C
int
将对齐)。请参阅

当然,如果以后要将
char
值符号或零扩展到寄存器中,请以这种方式加载。


或者更好,正如@Ira所指出的,如果您正在优化此代码以及存储到
*sp
的内容,理想情况下,您可以使用寄存器中的任何内容并优化存储/重新加载。(在C中,任何其他线程异步更改该内存都是未定义的行为,因为它是
int*
,不是易失性的或
\u原子int*

您需要向
%rdi
添加1或3个字节(取决于endianness)要获取指向
*sp
@Barmar的低位字节的指针,假设机器是little endian,那么为什么我们需要添加1个字节?默认情况下,地址是第一个字节,不是吗?您的方式可能会暂停存储到加载转发器。@Barmar:x86-64是little endian。
int
的低位字节是最不重要的。BotOP的h版本是正确的,但第二个版本有一个部分寄存器假依赖项。@slowjams您所做的是将赋值重写到
*dp=*(char*)sp;
。教科书提供了原始C代码的直译,没有任何优化,以避免混淆读者。您的重写在功能上是等效的(假设指针不跨越页面边界),但可能会遇到微妙的CPU性能问题,这些问题可能超出您正在阅读的教科书章节的范围。