phodina
phodina

Reputation: 1441

How do I use a constant in Rust's inline assembly macro?

I'm porting some legacy assembly code to Rust and I need to call it through the asm! macro. However, the assembly code depends on some constants stored in a C header file. I'd like to keep it similar, define the constants in Rust and have the names of the constants in the asm macro.

Legacy C header:

#define HCR_VALUE 0xffff0000

Legacy ASM file:

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

Rust code:

pub const HCR_VALUE: u32 = 0xffff0000;
unsafe { asm!("ldr x0, HCR_VALUE":::"x0"); }

Building the application ends up with a linker error:

lld-link: error: undefined symbol: HCR_VALUE

Upvotes: 7

Views: 2527

Answers (2)

Hacker
Hacker

Reputation: 357

The current answers asm style was deprecated in favor of a more rustacean syntax. However, constants in assembly are still behind a feature flag and only in nightly if I recall correctly. Just add #![feature(asm_const)] at the top of your main file to use this amazing feature.

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") _ 
);

More information can be found in this issue

You normally don't want to use ldr with a const operand in inline asm: more normally you'd ask the compiler for the constant already in a register if you want that. Then you don't need to mark it as an out register, and the compiler will know about that value still being in a register after the asm statement. In a very large asm statement you might run out of registers, or in a naked function the compiler won't emit instructions for you.

In instructions like "orr x1, x2, #{hcr}" (hard-coded registers) or and {foo}, {bar}, #{hcr} (compiler's choice of registers) it makes sense to use HCR_VALUE directly as an immediate with boolean instructions. (Bitwise boolean instructions use a bit-pattern encoding for immediates that can encode constants with one contiguous block of set bits, or a repeating pattern, so 0xffff0000 is usable directly, unlike 0xffef0000 or similar.)


When you want the value in a register, preferably ask the compiler to do it before your asm instructions:

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

This will probably result in the same compiled result as above.

If you want to put a value in a specific register, you can:

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

Upvotes: 3

Florian Weimer
Florian Weimer

Reputation: 33719

You need to pass the constant with a suitable constraint, like this:

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

The right constraint depends on the architecture; on RISC CPUs, not all constants can be represented as immediate values. So you may have to use a register constraint instead and have LLVM materialize the constant there.

Upvotes: 6

Related Questions