Rust
Options
Use the pure, nomem and nostack options.
Options
By default the compiler assumes an assembly block may read and write memory, use the stack and have side effects; this restricts optimization. Stronger promises are given with options(..):
let y: u64;
unsafe {
asm!(
"lea {o}, [{a} + {a}*2]", // o = a * 3
a = in(reg) x,
o = lateout(reg) y,
options(pure, nomem, nostack),
);
}
pure: no side effects, the output depends only on the inputs; if the result isn't used, the block can be deleted.nomem: doesn't touch memory.nostack: doesn't write to the stack.
Fast arithmetic with lea
lea (load effective address) doesn't actually read memory; using the address-calculation hardware, it does addition and multiplication of the form a + b*k (k = 1, 2, 4, 8) in a single instruction. Compilers often produce multiplications like x * 3 and x * 5 this way. That also shows hand-written assembly is often unnecessary: writing x * 3 produces the same instruction.
// typical code the compiler produces for x * 3:
// lea rax, [rdi + rdi*2]
Exercise Times three
For x86-64, write fn uc_kat(x: u64) -> u64 using lea and options(pure, nomem, nostack) (x * 3 on other architectures). Print the result of uc_kat(5).
Show example solution
#[cfg(target_arch = "x86_64")]
fn uc_kat(x: u64) -> u64 {
let y: u64;
// SAFETY: lea belleğe erişmez; seçenekler doğrudur.
unsafe {
std::arch::asm!(
"lea {o}, [{a} + {a}*2]",
a = in(reg) x,
o = lateout(reg) y,
options(pure, nomem, nostack),
);
}
y
}
#[cfg(not(target_arch = "x86_64"))]
fn uc_kat(x: u64) -> u64 {
x * 3
}
fn main() {
println!("{}", uc_kat(5));
}
Test yourself
-
What happens if a block that writes to memory is marked <code>nomem</code>?
- Compile error
- It slows down
- Nothing happens
- Undefined behaviour: the compiler assumes memory values didn't change
Show answer
Undefined behaviour: the compiler assumes memory values didn't change — Options are promises to the compiler and aren't checked; a false promise means a wrong optimization.
-
Pure block
asm!(
"lea {o}, [{a} + {a}*2]",
a = in(reg) x,
o = lateout(reg) y,
options(____, nomem, nostack),
);
Show answer
asm!(
"lea {o}, [{a} + {a}*2]",
a = in(reg) x,
o = lateout(reg) y,
options(pure, nomem, nostack),
);
-
Option and promise
- pure
- No side effects
- nomem
- Doesn't touch memory
- nostack
- Doesn't write the stack
- readonly
- Only reads memory
Next lesson: CPUID