tinker
tinker

Reputation: 3424

How do I translate x86 GCC-style C inline assembly to Rust inline assembly?

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?

Upvotes: 5

Views: 2246

Answers (1)

Shepmaster
Shepmaster

Reputation: 430791

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.

  1. 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.

  2. Literals must be prefaced with $$

  3. 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:

Upvotes: 6

Related Questions