在 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 寄存器,
edx
或rdx
,具体取决于 使用的是 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。
因此,如果您在寄存器中有40
和24
,他们会为此工作(并避免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 的幂而不影响标志的主要内容,如果未能解决你的问题,请参考以下文章