如何在Rust的内联汇编宏中使用常量?

7

我正在将一些旧的汇编代码移植到Rust,并且需要通过asm!宏调用它。但是,汇编代码依赖于存储在C头文件中的一些常量。我想保持相似性,在Rust中定义常量,并在asm宏中使用常量的名称。

旧的C头文件:

#define HCR_VALUE 0xffff0000

传统的ASM文件:

.func
...
ldr    x0, =HCR_VALUE
...

Rust 代码:

pub const HCR_VALUE: u32 = 0xffff0000;

unsafe { asm!("ldr x0, HCR_VALUE":::"x0"); }

构建应用程序时出现链接器错误:
lld-link: error: undefined symbol: HCR_VALUE

这个不起作用的原因是Rust编译器没有理由发出像.equ HCR_VALUE, 0xffff0000这样的汇编行来将该名称定义为汇编时常量。将内联汇编视为只是让您在编译器将其馈送到汇编器之前将自己的内容放入编译器的汇编输出中;模板字符串除了替换%0%[operand_name]之外,不能被编译器理解。 - Peter Cordes
2个回答

6

您需要使用合适的约束条件传递常量,就像这样:

unsafe { asm!("ldr x0, =${0:c}" : : "i" (HCR_VALUE) : "x0"); }

正确的限制取决于架构;在RISC CPU上,不是所有常量都可以表示为立即数。因此,您可能需要使用寄存器限制,并让LLVM将常量物化到那里。

感谢@Florian。这绝对是正确的方向。我按照您的建议尝试了一下,但出现了以下错误:<inline asm>:11:16:错误:预期标签或可编码整数PC偏移量 ldr x0,-453 - phodina
2
我没有Rust或arm(ldr看起来像是arm),但你可以尝试:asm!("ldr x0, $0":: "i" (HCR_VALUE):"x0");asm!("ldr x0, =$0":: "i" (HCR_VALUE):"x0"); - David Wohlferd
@phodina 抱歉,你仍然需要像David所说的那样在内联汇编中放置= - Florian Weimer
感谢@Florian和@David。加上=就可以了。很抱歉没有说明这是AArch64指令。 - phodina

3

当前的 asm 语法已经被弃用,采用了更具有 Rust 特色的语法。不过,在汇编中使用常量仍然需要开启一个特性标记,并且只在夜间版中可用,如果我没记错的话。只需在主文件顶部添加 #![feature(asm_const)] 即可使用此功能。

pub const HCR_VALUE: u32 = 0xffff0000;
// just assume we are in an unsafe block
asm!(
  "ldr x0, ={hcr}",
  "...",

  hcr = const HCR_VALUE,
  // this is technically necessary to tell the compiler that you have 
  // changed the value of x0 in your assembly code (called clobbering)
  out ("x0") _ 
);

更多信息可在此问题中找到。

通常情况下,您不希望在内联汇编中使用具有const操作数的ldr:如果需要该值,则通常会要求编译器将常量已经存储在寄存器中。然后您不需要将其标记为out寄存器,并且编译器将在asm语句之后仍知道该值保留在寄存器中。在非常大的汇编语句中,您可能会用完寄存器,或者在函数中,编译器将不会为您生成指令。

"orr x1, x2, # {hcr}"(硬编码寄存器)或and {foo}, {bar}, #{hcr}(编译器选择的寄存器)等指令中,使用HCR_VALUE作为布尔指令的立即数就是有意义的。 (按位布尔指令对于立即数使用位模式编码,可以使用具有一个连续块设置位或重复模式的常量进行编码,因此0xffff0000可以直接使用,与0xffef0000或类似的东西不同。)


当需要在寄存器中使用该值时,请尽可能要求编译器在您的asm语句之前执行:

asm!(
  "use {reg} here where you would have used x0", 
  reg = in(reg) HCR_VALUE
);

这可能会产生与上面相同的编译结果。

如果您想将一个值放入特定的寄存器中,您可以:

asm!(
  "...", // You don't even have to alter your code
  in ("x0") HCR_VALUE
);

第一次我并没有考虑确切的汇编代码。显然,如果你可以让Rust为你完成这项工作,你就不会想要像这样加载一个const。这更多是关于一般性的想法。在答案中,我应该用and还是orr来替换ldr - Hacker
另外,你可能是我在SO上遇到的第二个友好的人,哈哈 - Hacker
其实,我刚刚想到了一些事情。在ldr中使用常量有一个很好的理由,特别是当你有一个庞大的asm!块时,你可能有太多的常量需要加载,无法一次装入所有寄存器。或者你只想在一个块中使用一个寄存器,但有多个常量。 - Hacker
1
好的,你仍然不能在没有告诉编译器的情况下编写一个 in 操作数,而使用一些任意的 ORR 常量,比如 #0x10,并不是我所说的。我想用 #{hcr} 作为立即数,因为这是你希望它作为常量而不是寄存器的原因之一。 - Peter Cordes
1
关于必须编写“out”操作数约束的观点是正确的,但是在您的初始示例中(使用“ldr”将其写入),或者任何后来的汇编块中,您已经错过了对“x0”的操作数约束。 早期的一些汇编块不需要任何输出约束,因为它们只要求编译器将一个值放入asm模板字符串之外的寄存器中。 - Peter Cordes
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接