Reputation: 1441
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
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
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