Summary - Inline assembly
- Rust course
- Expert
- Lesson 30/46
- 5 min
Combine asm!, in/out and options to read time with rdtsc.
Inline assembly summary
asm! writes processor instructions inside unsafe. Operands: in, out, inout, lateout; a register class (reg) or an explicit register ("rax"). Clobbered registers are declared with out("..") _ or clobber_abi. options(pure, nomem, nostack, readonly) enables optimization, but correctness is on you. For portability use #[cfg(target_arch)] and a fallback; in most cases std::arch intrinsics or the compiler are enough.
Swapping byte order
bswap reverses the byte order of a 32- or 64-bit value (big-endian ↔ little-endian). Its Rust counterpart is swap_bytes; we can verify the result by comparing with it:
let mut x: u32 = 0x11223344;
unsafe {
asm!("bswap {0:e}", inout(reg) x, options(pure, nomem, nostack));
}
assert_eq!(x, 0x11223344u32.swap_bytes()); // 0x44332211{0:e} is for using the 32-bit register name (like eax); it matters because the upper 32 bits may be undefined for a u32 value.
Practice bswap and verification
For x86-64, write fn ters_bayt(x: u32) -> u32 using bswap {0:e} (x.swap_bytes() on other architectures). For 0x11223344, print the result with {:#x} and its equality with swap_bytes, separated by a space.
Starter code
fn main() {
// buraya yaz
}Sample solution
#[cfg(target_arch = "x86_64")]
fn ters_bayt(mut x: u32) -> u32 {
// SAFETY: bswap yalnızca operand yazmacını değiştirir.
unsafe {
std::arch::asm!("bswap {0:e}", inout(reg) x, options(pure, nomem, nostack));
}
x
}
#[cfg(not(target_arch = "x86_64"))]
fn ters_bayt(x: u32) -> u32 {
x.swap_bytes()
}
fn main() {
let y = ters_bayt(0x11223344);
println!("{:#x} {}", y, y == 0x11223344u32.swap_bytes());
}Expected output
0x44332211 true
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 What is a good way to test an assembly block's correctness?
- It can't be tested
- Comparing its result with a pure Rust version doing the same job Correct answer
- Only that it compiles
- Adding comments
Explanation: The reference implementation is both the fallback path and the test oracle.
2 32-bit register name
asm!("bswap {0:____}", inout(reg) x, options(pure, nomem, nostack));
asm!("bswap {0:e}", inout(reg) x, options(pure, nomem, nostack));
Explanation: The :e modifier selects the 32-bit register name.
3 Group summary
| Term | Meaning |
|---|---|
asm! |
Inline instruction |
inout |
Input + output |
options(pure) |
No side-effect promise |
swap_bytes |
Pure Rust counterpart |