-
Notifications
You must be signed in to change notification settings - Fork 301
Blog post on new inline assembly syntax #600
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
Changes from 1 commit
eaaa0bc
db01c81
4374650
6b114d2
746757a
36efb2b
99055a7
edd036d
cc2509a
b1fb3b4
28e3167
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. | ||
|
||
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() => _, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
); | ||
} | ||
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
|
Uh oh!
There was an error while loading. Please reload this page.