如何在GCC的内联程序集中指定一个特定的寄存器来分配C表达式的结果



我想使用GCC将C表达式的结果存储到RISC-V程序中的寄存器a0中(为了下面的示例,假设有volatile int val = 42或类似的(。当显式调用mv指令时,我能够使用内联汇编实现这一点:

asm ("mv a0, %0nt" :: "r" ((val == 0) ? 1 : val) : "a0");

然而,如果已经计算出结果",那么移动将是不必要的;在";正确的寄存器。如文档中所述,我尝试过使用register变量的各种方法,但都失败了。例如,以下内容被完全优化:

register volatile int res asm("a0") = (val == 0) ? 1 : val;

与";"空";asm语句:

register int res asm("a0") = (val == 0) ? 1 : val;
asm volatile ( "" /* just store */ : "=r" (res) );

有更好的方法吗?我忽略了什么吗?

你很接近,但"=r"(res)告诉编译器res的旧值已经死了,所以它没有在任何地方实现它。


register ... asm("regname")局部变量仅在用作Extendedasm语句的操作数时保证执行任何操作。如果asm语句本身是asm volatile,那么即使其输入未使用,也无法对其进行优化。(不要使register ... asm变为volatile;这没有用。(

因此,为了迫使编译器在a0中具体化表达式的结果(例如,对于一个微基准标记,您出于某种原因想要部分失败优化,否则https://gcc.gnu.org/wiki/DontUseInlineAsm):

register int res asm("a0") = (val == 0) ? 1 : val;
// then read that reg var, forcing the compiler to actually materialize it
asm volatile("# just a comment, input picked %0" : : "r"(res));

这可以移植到其他支持GNUC内联asm的编译器,包括clang。

Godbolt上的演示(我调整了asm模板,在注释前使用nop,这样编译器资源管理器注释过滤器就不会删除它。空的模板字符串不会改变任何东西;这只是为了确认编译器"知道"它将res放入了什么寄存器。(

void foo(int dummy, int val) {
register int res asm("a3") = (val == 0) ? 1 : val;
asm volatile("nop # just a comment, input picked %0" : : "r"(res));
}

我选择了a3void函数,以确保GCC没有理由碰巧选择那个寄存器,甚至根本没有理由实现res;我不会退货的。

# RISC-V gcc 10.2  -O3
foo:
mv      a3,a1
bne     a1,zero,.L2
li      a3,1
.L2:
nop # just a comment, input picked a3
# if we had returned res, we'd have   mv  a0,a3   here 
ret

请注意,此asm语句没有输出操作数,也没有clobber,因此编译器知道它对任何寄存器或内存都没有影响

如果我使用了"=r"(res),它会告诉编译器res的原始值已经死了,所以在asm语句执行并产生新值之前,不需要在该寄存器中实现它。就像你写了res = rand();或其他东西一样,如果没有人读到res的前一个值,甚至不需要计算它。

"+r"(res)会告诉编译器asm指令读取该寄存器,然后在那里写入一个新值。例如,您可以使用它向优化器隐藏常量,因此编译器必须假设在此之后使用res是一个完全未知的运行时变量,即使您使用了register int res asm("a0") = 1;


出错,没有asm()

int bar(int dummy, int val) {
register int res asm("a3") = (val == 0) ? 1 : val;
return res;
}

对于a0;工作;只是因为a0是返回值寄存器,并且我们返回res(以阻止其优化(。

bar:                   # same computation, but without using a3
mv      a0,a1
bne     a1,zero,.L5
li      a0,1
.L5:
ret

如果我没有return res,它将完全优化掉未使用变量的计算。volatile register int res asm("a3")无法改变这一点。

相关内容

最新更新