如何在不使用 SHL 的情况下使用 ADD 向左移动?
How do I shift left in assembly with ADD, not using SHL?
我们 class 的老师给我们布置了作业。问题是我们没有涵盖 class 中的位移位,因此我对如何执行此操作有点迷茫。在作业说明中,他告诉我们以下内容:
- 只能用mov,add,sub指令来完成赋值
- 他给了我们如下提示:想想如何用add指令实现移位。例如,假设我们有以下 8 位二进制数:00000011,将这个二进制数左移后我们有。 00001100.
我们从 4 个变量开始:
- var1 字节 41h
- var2 字节 42h
- var3 字节 43h
- var4 字节 44h
作业的第一部分是移动它们,所以我们最终得到以下顺序:
- var1 = 44h
- var2 = 41h
- var3 = 42h
- var4 = 43h
我成功完成了这部分,我只是提供这个作为上下文。下一部分是我遇到问题的地方。在第二部分中,我们需要将这些变量移动到寄存器 eax 中。 var1必须存放在eax的最高字节,var2存放在次高字节,war3存放在次低字节,var4存放在最低字节。
最终结果应该是eax = 444144243
下面是我对这个问题的了解:
- 我知道不能直接引用eax的高16位
- 我可以参考ax和al
- 我知道我需要使用这些十六进制值的二进制值通过加 1 或类似的方式将它们向左移动。
我如何移动 var1 以使其在 eax 的高 16 位中结束,并与其他 var 一起依此类推?
注意:我没有代码,因为我什至不知道从哪里开始这部分作业。我不想解决它,我只是想帮助找到解决问题的正确途径。
要向左移动一个值,您需要将相同的值添加到自身
例如,如果您有 0011
0011 + 0011 = 0110 (shift 1 left)
0110 + 0110 = 1100 (shift 1 left again)
为了解决您的问题,我将采用以下方法(快捷方式)
MOV ah, var1 (move 44h to 0000h -> 4400h)
MOV al, var2 (move 41h to 4400h -> 4441h)
ADD eax, eax (4441h + 4441h = 8882h)
ADD eax, eax (8882h + 8882h = 11104h)
ADD eax, eax (11104h + 11104h = 22208h)
ADD eax, eax (22208h + 22208h = 44410h)
ADD eax, eax
ADD eax, eax
ADD eax, eax
ADD eax, eax (444100h)
ADD eax, eax
ADD eax, eax
ADD eax, eax
ADD eax, eax (4441000h)
ADD eax, eax
ADD eax, eax
ADD eax, eax
ADD eax, eax (44410000h)
现在进入另一部分
MOV ah, var3 (move 42h to 44410000h -> 44414200h)
MOV al, var4 (move 43h to 44414200h -> 44414243h)
现在你的 eax 寄存器如下:4441 4243h
I know I need to use the binary values of these hex values to shift them to the left by adding 1 or something like that.
十六进制是一种人类可读的二进制值打印方式。寄存器和内存中的字节只是由位组成,因为计算机使用二进制逻辑。
由于您不能使用移位或旋转指令(除了使用 add same,same
左移 1),我会考虑使用存储/重新加载来按所需顺序临时获取字节。
请注意,所需结果仅以字节粒度重新排序数据,即围绕整个字节移动。字节之间没有位必须移动。 所以你不一定需要移位。
你的想法没有错,但需要多多指教。我只是介绍其他方式。
练习的重点似乎是教您 AX、AH 和 AL 如何将别名添加到 EAX 的各个部分。 (对于 ECX、EDX 和 EBX 也是类似的)。 And/or 双字 load/store 如何影响它接触的 4 个单独的内存字节,即小端字节顺序。
我真的不明白禁止 shl eax,8
或 16 的意义,如果预期的解决方案是使用 add
16 次作为轮班。仍然存在使用部分寄存器修改 EAX 字节的问题。我的意思是,使用 x+x = 2*x = x<<1
标识通常很有用,例如在使用 LEA 的寻址模式中进行数学运算,例如 x*3
= lea eax, [eax + eax*2]
。或者使用 add same,same
作为更有效的 shl eax,1
.
I successfully completed this part, I just provided this for context. The next part is where I am having issues. In part two we need to move these vars into register EAX. var1
must be stored in the highest byte of eax [...]
假设变量按照显示的顺序存储(首先是 var1
,因此在最低地址:
您指定的顺序与您从涵盖所有 4 个字节的 4 字节加载中获得的顺序相反:x86 是小端序,因此最低地址字节结束于 EAX(又名 AL)的低字节。
通常你会做一个双字加载,然后 bswap eax
来反转寄存器中 4 个字节的顺序。 (或者在支持它的较新的 CPUs 上,movbe
加载:mov big-endian 数据通常至少与单独加载 + bswap 一样有效。)但是练习迫使你变得更高级。
更宽 load/store 跨越多个字节变量
假设您的 4 个变量是连续声明的,首先是 var1
(在最低地址),您可以使用一个字或双字 mov
加载一次加载多个字节。例如
mov eax, dword ptr [var1] ; AL=var1, AH=var2, higher bytes = var3, var4
MASM 将大小与数据标签相关联,因此为了让它开心,我们必须对内存操作数应用 dword ptr
大小覆盖,以使其与寄存器 EAX 的双字大小相匹配。 mov
总是要求两个操作数的大小相同,但是对于像 mov [esp], eax
这样的情况,寄存器源操作数 EAX 隐含了 目标内存的大小。没有标签(或 2 个寄存器)就不可能出现不匹配。当尺寸自己匹配时,它也是可选的,例如 mov al, [var1]
就 CPU 而言,一切都只是字节; dword ptr
覆盖纯粹是源代码级的事情,可以让汇编程序满意。 NASM 等其他汇编程序不需要它。只是因为寻址模式包含一个数据标签,MASM 将其视为具有与其关联的大小的“变量”。
鉴于您已经完成第 1 部分并将其存储到内存中,因此您在内存中的顺序为 44h, 41h, 42h, 43h
(即 43'42'41'44h
作为小端双字)你可能会像这样去了解第 2 部分:
mov ax, word ptr [var1] ; AX = 41'44h = AH:AL
mov dx, word ptr [var1 + 2] ; DX = 43'42h = DH:DL
sub esp, 4 ; reserve stack space
mov [esp+0], DH
mov [esp+1], DL
mov [esp+2], AH ; 4x byte stores
mov [esp+3], AL ; constructing a byte-reversed copy
mov eax, [esp] ; reload that
add esp, 4 ; release the stack allocation
在我的评论中,我在编写 EAX 值的 32 位十六进制表示时用 '
分隔字节,以便更容易看到字节边界与 44414243h
这比@Pablo 的回答提出的指令要少得多。它确实有导致存储转发停顿的性能缺点。 (EAX 的 dword 重新加载是刚刚由 4 个单独的字节存储写入的数据)。但是与 16 add
条指令相比,延迟和吞吐量可能仍然更好!存储转发停顿“只有”大约 15 个周期的总存储-> 重新加载延迟,但是 may block other store-forwarding during that time,或者至少多个存储转发失败似乎无法同时进行。请参阅下面的另一段。
执行 4x 字节加载和两个 word
存储是一个选项,但这需要部分寄存器合并并且在 Intel 上可能更糟。请参阅下面的第 1 部分实现,接近此答案的末尾。
高效地完成第一部分
如果你可以使用你想要的任何指令,你只需使用左循环将高 8 位向下移动到开始,并将其他位向左移动腾出空间。
; MSB(var4) LSB(var1)
; dword at var1 = 44'43'42'41h
rol dword ptr [var1], 8 ; dword at var1 = 43'42'41'44h
或者您可以使用左移 1 个字节(8 位):
mov eax, dword ptr [var1] ; EAX = 44'43'42'41h
add eax, eax ; shift left by 8 bits, 1 at a time
add eax, eax
add eax, eax
add eax, eax
add eax, eax
add eax, eax
add eax, eax
add eax, eax ; EAX = 43'42'41'00h ; after shifting
mov al, [var4] ; EAX = 43'42'41'44h ; replace the low byte
mov dword ptr [var1], eax ; overwrite var1..4 with the bytes of EAX
写入 AL 然后读取 EAX 可能会导致部分寄存器在某些旧 CPUs 上停止,但我们不需要单独写入 AH,因此现代 CPUs 不会有这里有任何性能问题。
如果我们可以做一个写在 4 个字节之外的 DWORD 存储,那将非常方便,即踩到一个假设的 var5
。那我们就可以做
;; writes 1 byte past var4
mov eax, dword ptr [var1]
mov dl, [var4]
mov dword ptr [var2], eax ; overwrite var2..4 and 1 extra byte past the end
mov [var1], dl
我们可以在堆栈上使用一些临时存储来完成这项工作,然后复制回 var1
,但这有点笨拙:
sub esp, 8 ; reserve some stack space
mov eax, dword ptr [var1] ; EAX = 44'43'42'41h
mov [esp], eax
mov [esp+4], eax ; 2 copies of EAX so we can take any 4-byte window
mov eax, [esp+3] ; EAX = 43'42'41'44h = rotated
mov dword ptr [var1], eax ; and store that over var1..4
add esp, 8 ; and dealloc it.
这通过连接内存中字节序列的 2 个副本(在两个双字中)来模拟循环。然后我们可以加载任何 4 字节 window。我们选择低字节来自原值高字节的那个,一个1字节(8位)左旋=3字节(24位)右旋。
不幸的是,这将导致存储转发停止。 (两个单词存储被重叠的负载读取)。所以大约 15 个周期延迟而不是现代 x86 上通常的 5 个周期。 (https://agner.org/optimize).
吞吐量可能比 8 倍 add
的版本更好,但延迟更差。或者吞吐量也可能更糟,因为 add
的独立链可以重叠它们的执行。多个存储转发停顿 can't be happening at once on Intel CPUs at least,因此如果您正在做很多 这个 ,那么这是一个吞吐量限制,而不仅仅是在很多其他工作之间。
只有字节加载:
在进行任何存储之前加载所有内容意味着我们不必担心会破坏尚未读取的数据。
mov ah, [var1] ; EAX = ??'??'41'??h
mov al, [var4] ; EAX = ??'??'41'44h
mov ch, [var3] ; ECX = ??'??'43'??h
mov cl, [var2] ; ECX = ??'??'43'42h
mov word ptr [var3], cx ; var3 = CL = 42h var4 = CH = 43h
mov word ptr [var1], ax ; var1 = AL = 44h var2 = AH = 41h
另一个明显的选择是两个 word
负载和 4 个 byte
存储。对于性能,这将避免在 Intel CPUs 上的部分寄存器合并停顿(从写入 AL/AH 然后读取 AX),但加载吞吐量通常优于存储吞吐量。虽然 AH 合并惩罚可能比 Haswell/Skylake
但是因为我们想保持 2 个源字节的顺序,我们可以将 CL 和 CH 加载合并为一个 CX 加载:
可能是第 1 部分最有效的版本
mov ah, [var1] ; EAX = ??'??'41'??h
mov al, [var4] ; EAX = ??'??'41'44h
mov cx, word ptr [var2] ; ECX = ??'??'43'42h
mov word ptr [var3], cx ; var3 = CL = 42h var4 = CH = 43h
mov word ptr [var1], ax ; var1 = AL = 44h var2 = AH = 41h
请注意,我们正在从 var2
进行字加载,然后将字存储到 var3
,将这 2 个字节向左移动 8 位/1 个字节。
这里唯一的停顿是写入AL+AH后读取AX,在Intel Sandybridge-family上这最多是前端的一个周期(在此期间合并uop本身问题)。
如果稍后的代码要对我们在这之后很快重新排列的 4 个字节进行双字加载,将其写成 2 个字的一半将导致存储转发停顿。但是我上面的 bswap 仿真只重新加载两个 word
一半,所以没关系。
这也错误地依赖于 EAX 和 ECX 的旧值(对于 ECX,您可以通过执行双字加载来避免这种情况,但仍然是字存储)。或者对于 EAX,mov eax,0
或 sub eax,eax
会将其归零并且(在大多数 CPUs 上)打破对旧值的依赖,允许乱序执行该指令序列,即使将 ECX 用于不同值的最后一个块仍然停滞不前。
这比 8x add
版本或在堆栈上使用 scratch space 的版本更少的指令(并且每条指令仍然相当有效)。而且不会 store/reload 到一个临时的。
合并第 1 部分和第 2 部分:
在 EAX 中得到所需的数据并在内存中进行字节反转可以合并为一个步骤,或许可以在堆栈上重用一个临时文件。
当然,如果你在指令选择上没有限制你可以做的非常有效:
mov eax, dword ptr [var1]
ror eax, 8
mov dword ptr [var1], eax
bswap eax
仅将第 1 部分和第 2 部分与 mov
和 add/sub 一起优化留作 reader.
的练习
其他有趣的想法:异或交换也适用于 SUB,因此您可以在寄存器和内存之间低效地交换字节。但是你并不局限于使用 tmp regs,所以加载到一个单独的字节 regs 会更好。
我们 class 的老师给我们布置了作业。问题是我们没有涵盖 class 中的位移位,因此我对如何执行此操作有点迷茫。在作业说明中,他告诉我们以下内容:
- 只能用mov,add,sub指令来完成赋值
- 他给了我们如下提示:想想如何用add指令实现移位。例如,假设我们有以下 8 位二进制数:00000011,将这个二进制数左移后我们有。 00001100.
我们从 4 个变量开始:
- var1 字节 41h
- var2 字节 42h
- var3 字节 43h
- var4 字节 44h
作业的第一部分是移动它们,所以我们最终得到以下顺序:
- var1 = 44h
- var2 = 41h
- var3 = 42h
- var4 = 43h
我成功完成了这部分,我只是提供这个作为上下文。下一部分是我遇到问题的地方。在第二部分中,我们需要将这些变量移动到寄存器 eax 中。 var1必须存放在eax的最高字节,var2存放在次高字节,war3存放在次低字节,var4存放在最低字节。
最终结果应该是eax = 444144243
下面是我对这个问题的了解:
- 我知道不能直接引用eax的高16位
- 我可以参考ax和al
- 我知道我需要使用这些十六进制值的二进制值通过加 1 或类似的方式将它们向左移动。
我如何移动 var1 以使其在 eax 的高 16 位中结束,并与其他 var 一起依此类推?
注意:我没有代码,因为我什至不知道从哪里开始这部分作业。我不想解决它,我只是想帮助找到解决问题的正确途径。
要向左移动一个值,您需要将相同的值添加到自身
例如,如果您有 0011
0011 + 0011 = 0110 (shift 1 left)
0110 + 0110 = 1100 (shift 1 left again)
为了解决您的问题,我将采用以下方法(快捷方式)
MOV ah, var1 (move 44h to 0000h -> 4400h)
MOV al, var2 (move 41h to 4400h -> 4441h)
ADD eax, eax (4441h + 4441h = 8882h)
ADD eax, eax (8882h + 8882h = 11104h)
ADD eax, eax (11104h + 11104h = 22208h)
ADD eax, eax (22208h + 22208h = 44410h)
ADD eax, eax
ADD eax, eax
ADD eax, eax
ADD eax, eax (444100h)
ADD eax, eax
ADD eax, eax
ADD eax, eax
ADD eax, eax (4441000h)
ADD eax, eax
ADD eax, eax
ADD eax, eax
ADD eax, eax (44410000h)
现在进入另一部分
MOV ah, var3 (move 42h to 44410000h -> 44414200h)
MOV al, var4 (move 43h to 44414200h -> 44414243h)
现在你的 eax 寄存器如下:4441 4243h
I know I need to use the binary values of these hex values to shift them to the left by adding 1 or something like that.
十六进制是一种人类可读的二进制值打印方式。寄存器和内存中的字节只是由位组成,因为计算机使用二进制逻辑。
由于您不能使用移位或旋转指令(除了使用 add same,same
左移 1),我会考虑使用存储/重新加载来按所需顺序临时获取字节。
请注意,所需结果仅以字节粒度重新排序数据,即围绕整个字节移动。字节之间没有位必须移动。 所以你不一定需要移位。
你的想法没有错,但需要多多指教。我只是介绍其他方式。
练习的重点似乎是教您 AX、AH 和 AL 如何将别名添加到 EAX 的各个部分。 (对于 ECX、EDX 和 EBX 也是类似的)。 And/or 双字 load/store 如何影响它接触的 4 个单独的内存字节,即小端字节顺序。
我真的不明白禁止 shl eax,8
或 16 的意义,如果预期的解决方案是使用 add
16 次作为轮班。仍然存在使用部分寄存器修改 EAX 字节的问题。我的意思是,使用 x+x = 2*x = x<<1
标识通常很有用,例如在使用 LEA 的寻址模式中进行数学运算,例如 x*3
= lea eax, [eax + eax*2]
。或者使用 add same,same
作为更有效的 shl eax,1
.
I successfully completed this part, I just provided this for context. The next part is where I am having issues. In part two we need to move these vars into register EAX.
var1
must be stored in the highest byte of eax [...]
假设变量按照显示的顺序存储(首先是 var1
,因此在最低地址:
您指定的顺序与您从涵盖所有 4 个字节的 4 字节加载中获得的顺序相反:x86 是小端序,因此最低地址字节结束于 EAX(又名 AL)的低字节。
通常你会做一个双字加载,然后 bswap eax
来反转寄存器中 4 个字节的顺序。 (或者在支持它的较新的 CPUs 上,movbe
加载:mov big-endian 数据通常至少与单独加载 + bswap 一样有效。)但是练习迫使你变得更高级。
更宽 load/store 跨越多个字节变量
假设您的 4 个变量是连续声明的,首先是 var1
(在最低地址),您可以使用一个字或双字 mov
加载一次加载多个字节。例如
mov eax, dword ptr [var1] ; AL=var1, AH=var2, higher bytes = var3, var4
MASM 将大小与数据标签相关联,因此为了让它开心,我们必须对内存操作数应用 dword ptr
大小覆盖,以使其与寄存器 EAX 的双字大小相匹配。 mov
总是要求两个操作数的大小相同,但是对于像 mov [esp], eax
这样的情况,寄存器源操作数 EAX 隐含了 目标内存的大小。没有标签(或 2 个寄存器)就不可能出现不匹配。当尺寸自己匹配时,它也是可选的,例如 mov al, [var1]
就 CPU 而言,一切都只是字节; dword ptr
覆盖纯粹是源代码级的事情,可以让汇编程序满意。 NASM 等其他汇编程序不需要它。只是因为寻址模式包含一个数据标签,MASM 将其视为具有与其关联的大小的“变量”。
鉴于您已经完成第 1 部分并将其存储到内存中,因此您在内存中的顺序为 44h, 41h, 42h, 43h
(即 43'42'41'44h
作为小端双字)你可能会像这样去了解第 2 部分:
mov ax, word ptr [var1] ; AX = 41'44h = AH:AL
mov dx, word ptr [var1 + 2] ; DX = 43'42h = DH:DL
sub esp, 4 ; reserve stack space
mov [esp+0], DH
mov [esp+1], DL
mov [esp+2], AH ; 4x byte stores
mov [esp+3], AL ; constructing a byte-reversed copy
mov eax, [esp] ; reload that
add esp, 4 ; release the stack allocation
在我的评论中,我在编写 EAX 值的 32 位十六进制表示时用 '
分隔字节,以便更容易看到字节边界与 44414243h
这比@Pablo 的回答提出的指令要少得多。它确实有导致存储转发停顿的性能缺点。 (EAX 的 dword 重新加载是刚刚由 4 个单独的字节存储写入的数据)。但是与 16 add
条指令相比,延迟和吞吐量可能仍然更好!存储转发停顿“只有”大约 15 个周期的总存储-> 重新加载延迟,但是 may block other store-forwarding during that time,或者至少多个存储转发失败似乎无法同时进行。请参阅下面的另一段。
执行 4x 字节加载和两个 word
存储是一个选项,但这需要部分寄存器合并并且在 Intel 上可能更糟。请参阅下面的第 1 部分实现,接近此答案的末尾。
高效地完成第一部分
如果你可以使用你想要的任何指令,你只需使用左循环将高 8 位向下移动到开始,并将其他位向左移动腾出空间。
; MSB(var4) LSB(var1)
; dword at var1 = 44'43'42'41h
rol dword ptr [var1], 8 ; dword at var1 = 43'42'41'44h
或者您可以使用左移 1 个字节(8 位):
mov eax, dword ptr [var1] ; EAX = 44'43'42'41h
add eax, eax ; shift left by 8 bits, 1 at a time
add eax, eax
add eax, eax
add eax, eax
add eax, eax
add eax, eax
add eax, eax
add eax, eax ; EAX = 43'42'41'00h ; after shifting
mov al, [var4] ; EAX = 43'42'41'44h ; replace the low byte
mov dword ptr [var1], eax ; overwrite var1..4 with the bytes of EAX
写入 AL 然后读取 EAX 可能会导致部分寄存器在某些旧 CPUs 上停止,但我们不需要单独写入 AH,因此现代 CPUs 不会有这里有任何性能问题。
如果我们可以做一个写在 4 个字节之外的 DWORD 存储,那将非常方便,即踩到一个假设的 var5
。那我们就可以做
;; writes 1 byte past var4
mov eax, dword ptr [var1]
mov dl, [var4]
mov dword ptr [var2], eax ; overwrite var2..4 and 1 extra byte past the end
mov [var1], dl
我们可以在堆栈上使用一些临时存储来完成这项工作,然后复制回 var1
,但这有点笨拙:
sub esp, 8 ; reserve some stack space
mov eax, dword ptr [var1] ; EAX = 44'43'42'41h
mov [esp], eax
mov [esp+4], eax ; 2 copies of EAX so we can take any 4-byte window
mov eax, [esp+3] ; EAX = 43'42'41'44h = rotated
mov dword ptr [var1], eax ; and store that over var1..4
add esp, 8 ; and dealloc it.
这通过连接内存中字节序列的 2 个副本(在两个双字中)来模拟循环。然后我们可以加载任何 4 字节 window。我们选择低字节来自原值高字节的那个,一个1字节(8位)左旋=3字节(24位)右旋。
不幸的是,这将导致存储转发停止。 (两个单词存储被重叠的负载读取)。所以大约 15 个周期延迟而不是现代 x86 上通常的 5 个周期。 (https://agner.org/optimize).
吞吐量可能比 8 倍 add
的版本更好,但延迟更差。或者吞吐量也可能更糟,因为 add
的独立链可以重叠它们的执行。多个存储转发停顿 can't be happening at once on Intel CPUs at least,因此如果您正在做很多 这个 ,那么这是一个吞吐量限制,而不仅仅是在很多其他工作之间。
只有字节加载:
在进行任何存储之前加载所有内容意味着我们不必担心会破坏尚未读取的数据。
mov ah, [var1] ; EAX = ??'??'41'??h
mov al, [var4] ; EAX = ??'??'41'44h
mov ch, [var3] ; ECX = ??'??'43'??h
mov cl, [var2] ; ECX = ??'??'43'42h
mov word ptr [var3], cx ; var3 = CL = 42h var4 = CH = 43h
mov word ptr [var1], ax ; var1 = AL = 44h var2 = AH = 41h
另一个明显的选择是两个 word
负载和 4 个 byte
存储。对于性能,这将避免在 Intel CPUs 上的部分寄存器合并停顿(从写入 AL/AH 然后读取 AX),但加载吞吐量通常优于存储吞吐量。虽然 AH 合并惩罚可能比 Haswell/Skylake
但是因为我们想保持 2 个源字节的顺序,我们可以将 CL 和 CH 加载合并为一个 CX 加载:
可能是第 1 部分最有效的版本
mov ah, [var1] ; EAX = ??'??'41'??h
mov al, [var4] ; EAX = ??'??'41'44h
mov cx, word ptr [var2] ; ECX = ??'??'43'42h
mov word ptr [var3], cx ; var3 = CL = 42h var4 = CH = 43h
mov word ptr [var1], ax ; var1 = AL = 44h var2 = AH = 41h
请注意,我们正在从 var2
进行字加载,然后将字存储到 var3
,将这 2 个字节向左移动 8 位/1 个字节。
这里唯一的停顿是写入AL+AH后读取AX,在Intel Sandybridge-family上这最多是前端的一个周期(在此期间合并uop本身问题)。
如果稍后的代码要对我们在这之后很快重新排列的 4 个字节进行双字加载,将其写成 2 个字的一半将导致存储转发停顿。但是我上面的 bswap 仿真只重新加载两个 word
一半,所以没关系。
这也错误地依赖于 EAX 和 ECX 的旧值(对于 ECX,您可以通过执行双字加载来避免这种情况,但仍然是字存储)。或者对于 EAX,mov eax,0
或 sub eax,eax
会将其归零并且(在大多数 CPUs 上)打破对旧值的依赖,允许乱序执行该指令序列,即使将 ECX 用于不同值的最后一个块仍然停滞不前。
这比 8x add
版本或在堆栈上使用 scratch space 的版本更少的指令(并且每条指令仍然相当有效)。而且不会 store/reload 到一个临时的。
合并第 1 部分和第 2 部分:
在 EAX 中得到所需的数据并在内存中进行字节反转可以合并为一个步骤,或许可以在堆栈上重用一个临时文件。
当然,如果你在指令选择上没有限制你可以做的非常有效:
mov eax, dword ptr [var1]
ror eax, 8
mov dword ptr [var1], eax
bswap eax
仅将第 1 部分和第 2 部分与 mov
和 add/sub 一起优化留作 reader.
其他有趣的想法:异或交换也适用于 SUB,因此您可以在寄存器和内存之间低效地交换字节。但是你并不局限于使用 tmp regs,所以加载到一个单独的字节 regs 会更好。