assembly - 汇编语言 - 从 ecx 中减去 edx 并将结果放入 ebx
问题描述
我将如何在不更改任何其他寄存器的情况下做到这一点(又名保持ecx
&edx
与以前相同)?
在 C++ 中,它会是这样的:
int ecx = 3;
int edx = 1;
int ebx = ecx - edx;
到目前为止,我已经这样做了:
mov ecx, 1
mov edx, 3
sub ecx, edx
mov ebx, 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 保存在堆栈中,然后C - D
在 ECX 中生成结果而不是新寄存器可能是一个不错的选择。
通常,您可以在整个函数中为相同的变量继续使用相同的寄存器,但这不是必需的,有时也不是最佳的。使用评论来跟踪事情。
编译器通常非常擅长寄存器分配,但它们的代码可能难以阅读,因为它们甚至不尝试与寄存器使用保持一致。对于非破坏性操作,他们通常会无缘无故地将结果放入新的寄存器中。尽管如此,编译器输出通常是优化的良好起点。(写一个做某事的小函数,看看它是如何编译的。或者用函数 args 而不是常量作为输入,用 C 语言编写你的整个东西,然后编译它。)
sub
x86 对其他操作(不是)有一些复制和操作指令,最值得注意的是 LEA。
lea ebx, [ecx + ecx*4] ; ebx = C * 5
lea ebx, [ecx + ebx - 2] ; ebx = C + D - 2
x86 寻址模式可以添加或减去常量,但只能左移和添加寄存器。
的立即操作数形式imul
也是 3 操作数,用于使用 1 或 2 个 LEA 无法实现的乘法器:
imul ebx, ecx, 0x01010101 ; ebx = cl repeated 4 times, if upper bytes were zero
与大多数立即操作数指令不同,imul
它不会将 ModRM 字节中的字段作为额外的操作码位重载/r
。所以它有空间来编码一个寄存器目标和一个 reg/mem 源,因为186专用一个完整的操作码字节给它。
像 BMI1 和 BMI2 这样的ISA 扩展添加了一些新的 3 操作数整数指令,比如ANDN和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
这将重用相同的归零 reg 作为合并目标,以避免错误的依赖关系(并且使 128 位寄存器的高 64 位为零)。
推荐阅读
- python - 无法在带有 Python 的 VS Code 中使用终端
- sharepoint - Sharepoint Online 编辑但不发布权限
- r - 计算两个向量之间相同值的数量
- ios - 具有自定义框架的 iOS 应用程序 Appstore 提交问题
- navigation - 如何将具有多个值的非二进制占用网格映射到 ROS 中的成本图?
- c++ - 调用 erase() 函数是否会在元素被擦除之前更改迭代器值?
- javascript - history.back() 在 Firefox 和 Internet Explorer 中不起作用,但在 google chrome 中起作用
- sql - SQL Server 日期格式到具有特定格式的 Datetimeoffset
- python - 单击按钮时如何停止线程(QRunnable)?
- loops - 使用 runas 命令提示符 (cmd) 循环