繁体   English   中英

如何在 GCC 的内联汇编中指定特定的寄存器来分配 C 表达式的结果?

[英]How to specify a specific register to assign the result of a C expression in inline assembly in GCC?

我想使用 GCC 将 C 表达式的结果存储到 RISC-V 程序中的寄存器a0中(为了下面的示例,假设存在volatile int val = 42或类似值)。 在显式调用mv指令时,我可以使用内联汇编来完成此操作:

asm ("mv a0, %0\n\t" :: "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")局部变量仅在用作扩展asm语句的操作数时才能保证执行任何操作。 如果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));

这可移植到支持 GNU C 内联汇编的其他编译器,包括 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));
}

我选择了a3和一个void function 以确保 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语句没有 output 操作数和破坏器,因此编译器知道它对任何寄存器或 memory 没有影响。

如果我使用了"=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")无法改变这一点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM