gpt4 book ai didi

gcc - 在内联汇编的多个替代操作数约束之间进行选择时,GCC 能否发出不同的指令助记符?

转载 作者:行者123 更新时间:2023-12-04 05:40:15 25 4
gpt4 key购买 nike

我正在尝试为 GCC 编写内联 x86-64 程序集以有效地使用 MULQ 指令。 MULQ 将 64 位寄存器 RAX 与另一个 64 位值相乘。另一个值可以是任何 64 位寄存器(甚至 RAX)或内存中的值。 MULQ 将乘积的高 64 位放入 RDX,将低 64 位放入 RAX。

现在,很容易将正确的 mulq 表达为内联汇编:

#include <stdint.h>
static inline void mulq(uint64_t *high, uint64_t *low, uint64_t x, uint64_t y)
{
asm ("mulq %[y]"
: "=d" (*high), "=a" (*low)
: "a" (x), [y] "rm" (y)
);
}

此代码是正确的,但不是最佳的。 MULQ 是可交换的,所以如果 y碰巧已经在RAX,那么离开是正确的 y它在哪里并进行乘法运算。但是 GCC 不知道这一点,所以它会发出额外的指令来将操作数移动到它们预定义的位置。我想告诉 GCC 它可以将任一输入放在任一位置,只要一个输入以 RAX 结束并且 MULQ 引用另一个位置。 GCC 对此有一个语法,称为“多个替代约束”。请注意逗号(但整个 asm() 已损坏;见下文):
asm ("mulq %[y]" 
: "=d,d" (*high), "=a,a" (*low)
: "a,rm" (x), [y] "rm,a" (y)
);

不幸的是,这是错误的。如果 GCC 选择第二个替代约束,它将发出“mulq %rax”。为了清楚起见,请考虑以下功能:
uint64_t f()
{
uint64_t high, low;
uint64_t rax;
asm("or %0,%0": "=a" (rax));
mulq(&high, &low, 7, rax);
return high;
}

编译 gcc -O3 -c -fkeep-inline-functions mulq.c , GCC 发出这个程序集:
0000000000000010 <f>:
10: or %rax,%rax
13: mov $0x7,%edx
18: mul %rax
1b: mov %rdx,%rax
1e: retq

“mul %rax”应该是“mul %rdx”。

如何重写这个内联汇编,以便在每种情况下都能生成正确的输出?

最佳答案

这个 2012 年的问题在 2019 年仍然非常重要。虽然 gcc 已经改变并且生成的一些代码在 2012 年不是最佳的,但现在是,反过来也成立。

灵感来自 Whitlock的分析,我已经测试过 mulq在 9 种不同的情况下,每个 xy是常量( 56 )或内存中的值( barzar )或 rax 中的值( f1() , f2() ):

uint64_t h1() { uint64_t h, l; mulq(&h, &l,    5,    6); return h + l; }
uint64_t h2() { uint64_t h, l; mulq(&h, &l, 5, bar); return h + l; }
uint64_t h3() { uint64_t h, l; mulq(&h, &l, 5, f1()); return h + l; }
uint64_t h4() { uint64_t h, l; mulq(&h, &l, bar, 5); return h + l; }
uint64_t h5() { uint64_t h, l; mulq(&h, &l, bar, zar); return h + l; }
uint64_t h6() { uint64_t h, l; mulq(&h, &l, bar, f1()); return h + l; }
uint64_t h7() { uint64_t h, l; mulq(&h, &l, f1(), 5); return h + l; }
uint64_t h8() { uint64_t h, l; mulq(&h, &l, f1(), bar); return h + l; }
uint64_t h9() { uint64_t h, l; mulq(&h, &l, f1(), f2()); return h + l; }

我已经测试了 5 个实现: Staufk , Whitlock , Hale , Burdo和我自己的:
inline void mulq(uint64_t *high, uint64_t *low, uint64_t x, uint64_t y) {
asm("mulq %[y]" : [a]"=a,a"(*low), "=d,d"(*high) : "%a,rm"(x), [y]"rm,a"(y) : "cc");
}

所有实现仍然无法在所有情况下生成最佳代码。虽然其他人无法为 h3, 生成最佳代码 h4h6 , Whitlock's 和我的失败仅适用于 h3 :
h3():
callq 4004d0 <f1()>
mov %rax,%r8
mov $0x5,%eax
mul %r8
add %rdx,%rax
retq

在其他条件相同的情况下,可以看出我的比 Whitlock 的简单。通过额外的间接级别并使用 gcc 的内置函数(也可在 clang 中使用,但我尚未测试),可以获得最佳 h3通过调用此函数而不是 mulq :
inline void mulq_fixed(uint64_t* high, uint64_t* low, uint64_t x, uint64_t y) {
if (__builtin_constant_p(x))
mulq(high, low, y, x);
else
mulq(high, low, x, y);
}

产量:
h3():
callq 4004d0 <f1()>
mov $0x5,%edx
mul %rdx
add %rdx,%rax
retq

使用思路 __builtin_constant_p实际上取自 gcc的文档:

There is no way within the template to determine which alternative was chosen. However you may be able to wrap your asm statements with builtins such as __builtin_constant_p to achieve the desired results.



亲自查看 Compiler Explorer .

注意:Whitlock 的实现还有另一个较小且意想不到的缺点。您需要在 Compiler Explorer 中检查选项 11010否则输出会产生误导和功能 h1 , ..., h9出现使用说明 mulq两次。这是因为编译器资源管理器的解析器不处理汇编指令 .ifnc/ .else/ .endif properly并简单地删除它们,显示两个可能的路径( .if.else )。或者,您可以取消选中选项 .text。

关于gcc - 在内联汇编的多个替代操作数约束之间进行选择时,GCC 能否发出不同的指令助记符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13617962/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com