CodeBlanks Logo Icon CodeBlanks

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]

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

main.rs
fn main() {
    // buraya yaz
}

Sample solution

main.rs
#[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

Question
asm!(
    "lea {o}, [{a} + {a}*2]",
    a = in(reg) x,
    o = lateout(reg) y,
    options(____, nomem, nostack),
);
Answer
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

All lessons in Expert