汇编语言 - 从 ecx 中减去 edx 并将结果放入 ebx

Assembly Language - Subtract edx from ecx and place result in ebx

如何在不更改任何其他寄存器的情况下执行此操作(也就是保持 ecxedx 与以前相同)?

在 C++ 中,它将是这样的:

int ecx = 3;
int edx = 1;
int ebx = ecx - edx;

到目前为止,我已经这样做了:

mov ecx, 1
mov edx, 3
sub ecx, edx
mov ebx, ecx

您始终可以使用堆栈来保存寄存器:

    push ecx
    push edx
    mov ecx, 1
    mov edx, 3
    sub ecx, edx
    mov ebx, ecx
    pop edx
    pop ecx

使用 x86 风格的 2 操作数指令破坏它们的目的地,你总是可以模拟一个非破坏性的 3 操作数指令 mov 将一个操作数复制到目的地,然后 运行 对该目的地的破坏性指令。

# with ecx and edx holding your inputs (which I'm calling C and D).

mov  ebx, ecx      ; ebx = C
sub  ebx, edx      ; ebx = C - D

对于这种情况,您可以做到最好,因为您不需要破坏 ECX 和 EDX 中的值。

如果您 运行 可用寄存器不足,将 ECX 保存在堆栈上然后在 ECX 中生成 C - D 结果而不是新寄存器可能是一个不错的选择。

通常您可以在整个函数中对同一个变量使用同一个寄存器,但这不是必需的,有时也不是最佳选择。使用评论来跟踪事情。

编译器通常非常擅长寄存器分配,但它们的代码可能难以阅读,因为它们甚至不尝试与寄存器使用保持一致。对于非破坏性操作,他们通常会无缘无故地将结果放入新的寄存器中。不过,编译器输出通常是优化的良好起点。 (写一个做某事的小函数,看看它是如何编译的。或者用函数参数而不是常量作为输入用 C 编写你的整个东西,然后编译它。)


x86 有一些用于其他操作的复制和操作指令(不是 sub),.

lea  ebx, [ecx + ecx*4]     ; ebx = C * 5
lea  ebx, [ecx + ebx - 2]   ; ebx = C + D - 2

x86寻址模式可以加减常量,但只能左移加寄存器。


immediate-operand form of imul 也是 3 操作数,用于乘法器,而您不能使用 1 或 2 个 LEA:

imul   ebx,  ecx,  0x01010101     ;  ebx = cl repeated 4 times, if upper bytes were zero

与大多数直接操作数指令不同,imul 不会 overload the /r field in the ModRM byte as extra opcode bits. So it has room to encode a register destination and a reg/mem source, because 186 将整个操作码字节专门用于它。


ISA 扩展,例如 BMI1 和 BMI2 添加了一些新的 3 操作数整数指令,例如 ANDN and SHRX.

andn   ebx,  ecx, edx             ; ebx = (~C) & D   ; BMI1

shrx   ebx,  edx, ecx             ; ebx = D >> C     ; BMI2

但它们并不是普遍可用的,只有 Haswell 和更高版本,以及 Ryzen。 (并且 Haswell/Skylake 的 Pentium/Celeron 版本在没有它们的情况下仍在销售,进一步延迟了它们成为基准的时间点。谢谢,英特尔。)

当然对于矢量指令,AVX 提供所有 SSE 指令的非破坏性版本

movaps    xmm2, xmm0         ; copy a whole register
subsd     xmm2, xmm1         ; scalar double-precision FP subtract: xmm0-xmm1

vsubsd    xmm3, xmm0, xmm1

或不太明显的用例

xorps     xmm0, xmm0    ; zero the register and break any false dependencies
cvtsi2sd  xmm0, eax     ; convert to double-precision FP, with the upper element = 0

xorps     xmm1, xmm1
cvtsi2sd  xmm1, edx

对比AVX:

vxorps    xmm1,  xmm1,xmm1   ; xmm1 = all-zero

vcvtsi2sd  xmm0, xmm1, eax
vcvtsi2sd  xmm1, xmm1, edx

这将重复使用相同的置零寄存器作为合并目标以避免错误依赖(并使 128 位寄存器的高 64 位为零)。