I have the following inline assembly in C:
unsigned long long result;
asm volatile(".byte 15;.byte 49;shlq $32,%%rdx;orq %%rdx,%%rax"
: "=a" (result) :: "%rdx");
return result;
I tried to rewrite it in Rust:
let result: u64;
unsafe {
asm!(".byte 15\n\t
.byte 49\n\t
shlq 32, rdx\n\t
orq rdx, rax"
: "=a"(result)
:
: "rdx"
: "volatile"
);
}
result
It doesn't recognize the =a
constraint an it gives me an invalid operand error for rdx
and rax
at shlq
and orq
instructions. What is the proper way to rewrite the above C inline assembly in Rust?
Rust provides support for inline assembly via the asm! macro. It can be used to embed handwritten assembly in the assembly output generated by the compiler. Generally this should not be necessary, but might be where the required performance or timing cannot be otherwise achieved.
There are, in general, two types of inline assembly supported by C/C++ compilers: asm (or __asm__) in GCC. GCC uses a direct extension of the ISO rules: assembly code template is written in strings, with inputs, outputs, and clobbered registers specified after the strings in colons.
The __asm keyword invokes the inline assembler and can appear wherever a C or C++ statement is legal. It cannot appear by itself. It must be followed by an assembly instruction, a group of instructions enclosed in braces, or, at the very least, an empty pair of braces.
The well-known GNU C/C++ Compiler (GCC), an optimizing 32-bit compiler at the heart of the GNU project, supports the x86 architecture quite well, and includes the ability to insert assembly code in C programs, in such a way that register allocation can be either specified or left to GCC.
Rust is built on top of LLVM, so a lot of low-level detail like this can be gleaned from what LLVM or Clang do.
If you want to specify a specific register, you use the register name as the constraint: "={rax}"(result)
. Based on the GCC documentation, the a
constraint is the "a" register.
Literals must be prefaced with $$
Registers must be prefaced with %
let result: u64;
unsafe {
asm!(".byte 15
.byte 49
shlq $$32, %rdx
orq %rdx, %rax"
: "={rax}"(result)
:
: "rdx"
: "volatile"
);
}
result
If I'm understanding the discussion about rdtsc
correctly, you can also do:
let upper: u64;
let lower: u64;
unsafe {
asm!("rdtsc"
: "={rax}"(lower),
"={rdx}"(upper)
:
:
: "volatile"
);
}
upper << 32 | lower
I advise getting out of inline assembly as soon as it's practical.
The assembly of each function:
playground::thing1:
#APP
.byte 15
.byte 49
shlq $32, %rdx
orq %rdx, %rax
#NO_APP
retq
playground::thing2:
#APP
rdtsc
#NO_APP
shlq $32, %rdx
orq %rdx, %rax
retq
For completeness, here is the same code using the LLVM intrinsic. This requires a different unstable attribute:
#![feature(link_llvm_intrinsics)]
extern "C" {
#[link_name = "llvm.x86.rdtsc"]
fn rdtsc() -> u64;
}
fn main() {
println!("{}", unsafe { rdtsc() })
}
Sources:
asm
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With