Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blog post on new inline assembly syntax #600

Merged
merged 11 commits into from
Jun 8, 2020
130 changes: 130 additions & 0 deletions posts/inside-rust/2020-05-25-new-inline-asm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
layout: post
title: "New inline assembly syntax available in nightly"
author: Josh Triplett
description: "Rust has a new inline assembly syntax in nightly, please test"
team: the language team <https://www.rust-lang.org/governance/teams/lang>
---

In the course of optimization, OS or embedded development, or other kinds of
low-level programming, you may sometimes need to write native assembly code for
the processor you're running on. "Inline assembly" provides a simple way to
integrate some assembly instructions into a Rust program, feeding Rust
expressions in as input registers, and getting output directly into Rust
variables. We've introduced a new syntax for inline assembly in nightly Rust,
and we're seeking feedback on it; we believe this new syntax has a path to
stabilization in the future.

Nightly Rust has had a syntax for "inline assembly" (`asm!`) for a long time;
however, this syntax just exposed a very raw version of LLVM's assembly
construct, with no safeguards to help developers use it. Getting any detail of
this syntax even slightly wrong tended to produce an Internal Compiler Error
(ICE) rather than the kind of friendly error message you've come to expect from
rustc. This syntax also had little to no hope of being supported on any
non-LLVM backend. As a result of these limitations, the `asm!` syntax was
highly unlikely to ever graduate from nightly to stable Rust, despite it being
one of the most requested features.
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved

In an effort to improve `asm!` and bring it to more users, [Amanieu
d'Antras](https://github.com/Amanieu) designed and implemented a new,
friendlier syntax for `asm!`. This syntax has had a long road:
- The proposal first started as a [pre-RFC on
internals](https://internals.rust-lang.org/t/pre-rfc-2-inline-assembly/11310).
- Inline assembly became the language team's first [project
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
group](https://github.com/rust-lang/rfcs/blob/master/text/2836-project-asm.md),
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
and iteratively designed RFCs in [the project group
repository](https://github.com/rust-lang/project-inline-asm/).
- [RFC 2873](https://github.com/rust-lang/rfcs/pull/2873) (still under
discussion) provides a specification for the syntax and its interaction with
the Rust language.
- We [renamed the existing `asm!` to
`llvm_asm!`](https://github.com/rust-lang/rust/pull/68404), so that people
currently using inline assembly on nightly can continue to use the existing
syntax for now. (We plan to remove this syntax eventually, given its fragile
ICE-happy nature, but while evaluating the new syntax we want the old syntax
available for comparison and alternatives.)
- [PR 69171](https://github.com/rust-lang/rust/pull/69171) (also by Amanieu)
implemented the new `asm!` syntax in nightly.

Here's an example of using the new inline assembly syntax, to print a message
to standard output using a direct [`write`
syscall](https://man7.org/linux/man-pages/man2/write.2.html) on x86-64 Linux:

```rust
#![feature(asm)]

fn main() {
let buf = "Hello from asm!\n";
let ret: i32;
unsafe {
asm!(
"syscall",
in("rax") 1, // syscall number
in("rdi") 1, // fd
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
in("rsi") buf.as_ptr(),
in("rdx") buf.len(),
out("rcx") _, // clobbered by syscalls
out("r11") _, // clobbered by syscalls
lateout("rax") ret,
);
}
println!("write returned: {}", ret);
}
```

(You can [try this example on the
playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=e983a5f5cffa51f4320f1176465d3a56).)

The example above specifies the exact inputs, outputs, and clobbers required by
the Linux syscall calling convention. You can also provide inputs and outputs
via arbitrary registers, and the compiler will select appropriate registers for
you. The following example uses [bit manipulation
instructions](https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets)
to compute the bit numbers of all set bits in a value, and stores them in a
slice of memory:

```rust
#![feature(asm)]

fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!("
popcnt {popcnt}, {v}
2:
blsi rax, {v}
jz 1f
xor {v}, rax
tzcnt rax, rax
stosb
jmp 2b
1:
",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options(nostack) could be used here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to make any difference in the generated code.

Along the same lines, I could use option(readonly) in the syscall example, but I don't think doing so adds any value. These are supposed to be very simple examples, and people will have the full RFC available for reference.

);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```

(You can [try this example on the
playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=38874735e48aa20289f23f5a3cbeae0c).
Note that this code serves to demonstrate inline assembly, not to demonstrate
an efficient implementation of any particular algorithm.)

Notice that `value` and `popcnt` have registers selected for them, while
`bits.as_mut_ptr()` must go in the `rdi` register for use with the `stosb`
instruction.

For full details on the new `asm!` syntax, see [RFC
2873](https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md).
Please try it out, and [report any bugs via the rust issue
tracker](https://github.com/rust-lang/rust/issues/) with the tag `F-asm`. You
can also [report your experiences on the tracking
issue](https://github.com/rust-lang/rust/issues/72016).
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved