Options
- Rust course
- Expert
- Lesson 28/46
- 4 min
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]Practice 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).
Starter code
fn main() {
// buraya yaz
}Sample 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));
}Expected output
15
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 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 Correct answer
Explanation: Options are promises to the compiler and aren't checked; a false promise means a wrong optimization.
2 Pure block
asm!(
"lea {o}, [{a} + {a}*2]",
a = in(reg) x,
o = lateout(reg) y,
options(____, nomem, nostack),
);
asm!(
"lea {o}, [{a} + {a}*2]",
a = in(reg) x,
o = lateout(reg) y,
options(pure, nomem, nostack),
);
Explanation: pure declares that the block is side-effect-free and its result depends only on the inputs.
3 Option and promise
| Term | Meaning |
|---|---|
pure |
No side effects |
nomem |
Doesn't touch memory |
nostack |
Doesn't write the stack |
readonly |
Only reads memory |