在 x86 汇编代码(GAS 语法)中有效乘以 2 的幂而不影响标志

Posted

技术标签:

【中文标题】在 x86 汇编代码(GAS 语法)中有效乘以 2 的幂而不影响标志【英文标题】:Efficient multiplication by a power of 2 in x86 Assembly code (GAS syntax) without affecting flags 【发布时间】:2021-12-03 10:35:19 【问题描述】:

假设我们有以下一段汇编代码:

mulx const, %rax, %r11

其中 const 是硬编码常数,已知是 2 的幂(让我们假设它是 2^24)。 根据https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-large-integer-arithmetic-paper.pdf第8页:

mulx 指令是现有 mul 指令的扩展,具有 不同之处在于对标志的影响:

mulx dest_hi, dest_lo, src1  

该指令还使用隐式 src2 寄存器,edxrdx,具体取决于 使用的是 32 位还是 64 位版本。 操作是:

dest_hi:dest_lo = src1 * r/edx

转换为 GAS 语法(另请参见 What is the AT&T / GAS syntax for mulx?)我们应该有:

%r11:%rax = const * %rdx

在我们的例子中,我们希望找到一段不涉及乘法(并且不修改标志)的等效代码。我们可以这样写:

movq %rdx, %r11
movq %rdx, %rax
shl $24, %rax
shr $40, %r11

但这是修改进位标志。我还尝试了其他一些移位指令的变体(不修改标志),但它们给出了错误。 我的电脑配备 Intel Core i7 处理器。

【问题讨论】:

应该是等价的;你确定你的mulx 源操作数的值是正确的吗?如果你真的写了myconst: .quad 2^24,你会得到一个汇编时的异或运算,而不是求幂。这些指令都没有读取 CF,但是如果您考虑输出,它们当然会对 CF 产生不同的影响。 (班次设置 CF,mulx 保留所有 FLAGS 未修改。) 彼得所说的。请提供minimal reproducible example 和预期与实际输出。 @PeterCordes,谢谢。其实情况是这样的:我用的是宏llvm_asm!锈。我不希望更改进位标志(因此我们可以说我将它们视为输出)。我看到应该使用移位命令 shlx/salx 和 shrx/sarx,以避免进位标志更新。然而,看起来他们不被认可。错误消息是“指令操作数无效”。 您应该编辑您的问题,以便更清楚地了解 CF 是您关心的输出,这是输出的唯一区别。一般来说,这是非常不寻常的。大多数 x86 指令都基于标志。 (并且 GNU C 内联 asm 对 i386 和 x86-64 目标有一个隐含的“cc”clobber;如果 Rust 内联 asm 可能不同,则为 IDK。)不幸的是,根据 ISA 参考 @987654324,SHLX/SHRX 仅适用于寄存器源@。唯一需要立即进行的无标志移位实际上是轮换,BMI2 RORX。 可能没用的想法:lea 让您移动 1、2 或 3。rorx $56, %rax, %rax ; mov $0, %al 移动 8。rorx $48, %rax, %rax ; mov $0, %ax 移动 16。 【参考方案1】:

不幸的是,根据 ISA 参考 https://felixcloutier.com/x86/sarx:shlx:shrx,SHLX/SHRX 仅适用于寄存器源。唯一需要立即执行的无标志移位实际上是轮换,BMI2 RORX。

因此,如果您在寄存器中有4024,他们会为此工作(并避免mov 指令)。如果您可以将移位计数寄存器设置提升到循环之外,那将在 2 微秒内完成工作(对于 HSW/SKL/ICL 上的端口 0 或 6)。


带有寄存器源的 64 位 mulx 在 Intel HSW/SKL/ICL 上也只有 2 微指令,对于端口 1 和 5 (https://uops.info)。 (32 位操作数大小为 3 个微指令,可能需要额外的一个微指令将乘法单元的低 64 输出分成 32 位两半。)因此,如果您无法提升常量设置并且需要 @ 987654327@进行设置。

如果您有其他乘法或其他需要特定端口的东西,您需要小心 mulx 的 3c / 4c 延迟或英特尔 CPU 上的端口 1(或可能 5)吞吐量的瓶颈。已经设置了寄存器常量,它通常比 SHLX/SHRX 差,除了代码大小。


很少有不破坏 FLAGS 的好处。有时确实会发生,但由于大多数 ALU 指令都会写入 FLAGS,因此很难 这样做。以至于 GNU C inline asm for x86(-64) 有一个隐含的 "cc" clobber。

【讨论】:

以上是关于在 x86 汇编代码(GAS 语法)中有效乘以 2 的幂而不影响标志的主要内容,如果未能解决你的问题,请参考以下文章

AT&T 语法中的 3 或 4 参数 x86 程序集[重复]

x86 程序集:比较(GAS 语法)

将 scanf 与 x86-64 GAS 程序集一起使用

AT&T汇编语法与x86语法基本区别

刚开始使用汇编(GAS),并且在这个短代码中有分段错误

如何在 x86 汇编语言中创建嵌套循环