|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "New inline assembly syntax available in nightly" |
| 4 | +author: Josh Triplett |
| 5 | +description: "Rust has a new inline assembly syntax in nightly, please test" |
| 6 | +team: the language team <https://www.rust-lang.org/governance/teams/lang> |
| 7 | +--- |
| 8 | + |
| 9 | +In the course of optimization, OS or embedded development, or other kinds of |
| 10 | +low-level programming, you may sometimes need to write native assembly code for |
| 11 | +the processor you're running on. "Inline assembly" provides a simple way to |
| 12 | +integrate some assembly instructions into a Rust program, feeding Rust |
| 13 | +expressions in as input registers, and getting output directly into Rust |
| 14 | +variables. We've introduced a new syntax for inline assembly in nightly Rust, |
| 15 | +and we're seeking feedback on it; we believe this new syntax has a path to |
| 16 | +stabilization in the future. |
| 17 | + |
| 18 | +Nightly Rust has had a syntax for "inline assembly" (`asm!`) for a long time; |
| 19 | +however, this syntax just exposed a very raw version of LLVM's assembly |
| 20 | +construct, with no safeguards to help developers use it. Getting any detail of |
| 21 | +this syntax even slightly wrong tended to produce an Internal Compiler Error |
| 22 | +(ICE) rather than the kind of friendly error message you've come to expect from |
| 23 | +rustc. This syntax was also error-prone for another reason: it looks similar to |
| 24 | +GCC's inline assembly syntax, but has subtle differences (such as the names in |
| 25 | +register constraints). This syntax also had little to no hope of being |
| 26 | +supported on any non-LLVM backend. As a result of all these limitations, the |
| 27 | +`asm!` syntax was highly unlikely to ever graduate from nightly to stable Rust, |
| 28 | +despite being one of the most requested features. |
| 29 | + |
| 30 | +In an effort to improve `asm!` and bring it to more users, [Amanieu |
| 31 | +d'Antras](https://github.com/Amanieu) designed and implemented a new, |
| 32 | +friendlier syntax for `asm!`. This syntax has had a long road from concept to |
| 33 | +compiler implementation: |
| 34 | +- The proposal first started as a [pre-RFC on |
| 35 | + internals](https://internals.rust-lang.org/t/pre-rfc-2-inline-assembly/11310). |
| 36 | +- Inline assembly became one of the language team's first [project |
| 37 | + groups](https://github.com/rust-lang/rfcs/blob/master/text/2836-project-asm.md), |
| 38 | + and iteratively designed RFCs in [the project group |
| 39 | + repository](https://github.com/rust-lang/project-inline-asm/). |
| 40 | +- [RFC 2873](https://github.com/rust-lang/rfcs/pull/2873) (still under |
| 41 | + discussion) provides a specification for the syntax and its interaction with |
| 42 | + the Rust language. |
| 43 | +- We [renamed the existing `asm!` to |
| 44 | + `llvm_asm!`](https://github.com/rust-lang/rust/pull/68404), so that people |
| 45 | + currently using inline assembly on nightly can continue to use the existing |
| 46 | + syntax for now. (We plan to remove this syntax eventually, given its fragile |
| 47 | + ICE-happy nature, but while evaluating the new syntax we want the old syntax |
| 48 | + available for comparison and alternatives.) |
| 49 | +- [PR 69171](https://github.com/rust-lang/rust/pull/69171) (also by Amanieu) |
| 50 | + implemented the new `asm!` syntax in nightly. |
| 51 | + |
| 52 | +Here's an example of using the new inline assembly syntax, to print a message |
| 53 | +to standard output using a direct [`write` |
| 54 | +syscall](https://man7.org/linux/man-pages/man2/write.2.html) on x86-64 Linux: |
| 55 | + |
| 56 | +```rust |
| 57 | +#![feature(asm)] |
| 58 | + |
| 59 | +fn main() { |
| 60 | + let buf = "Hello from asm!\n"; |
| 61 | + let ret: i32; |
| 62 | + unsafe { |
| 63 | + asm!( |
| 64 | + "syscall", |
| 65 | + in("rax") 1, // syscall number |
| 66 | + in("rdi") 1, // fd (stdout) |
| 67 | + in("rsi") buf.as_ptr(), |
| 68 | + in("rdx") buf.len(), |
| 69 | + out("rcx") _, // clobbered by syscalls |
| 70 | + out("r11") _, // clobbered by syscalls |
| 71 | + lateout("rax") ret, |
| 72 | + ); |
| 73 | + } |
| 74 | + println!("write returned: {}", ret); |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +(You can [try this example on the |
| 79 | +playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=e983a5f5cffa51f4320f1176465d3a56).) |
| 80 | + |
| 81 | +The example above specifies the exact inputs, outputs, and clobbers required by |
| 82 | +the Linux syscall calling convention. You can also provide inputs and outputs |
| 83 | +via arbitrary registers, and the compiler will select appropriate registers for |
| 84 | +you. The following example uses [bit manipulation |
| 85 | +instructions](https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets) |
| 86 | +to compute the bit numbers of all set bits in a value, and stores them in a |
| 87 | +slice of memory: |
| 88 | + |
| 89 | +```rust |
| 90 | +#![feature(asm)] |
| 91 | + |
| 92 | +fn main() { |
| 93 | + let mut bits = [0u8; 64]; |
| 94 | + for value in 0..=1024u64 { |
| 95 | + let popcnt; |
| 96 | + unsafe { |
| 97 | + asm!(" |
| 98 | + popcnt {popcnt}, {v} |
| 99 | + 2: |
| 100 | + blsi rax, {v} |
| 101 | + jz 1f |
| 102 | + xor {v}, rax |
| 103 | + tzcnt rax, rax |
| 104 | + stosb |
| 105 | + jmp 2b |
| 106 | + 1: |
| 107 | + ", |
| 108 | + v = inout(reg) value => _, |
| 109 | + popcnt = out(reg) popcnt, |
| 110 | + out("rax") _, // scratch |
| 111 | + inout("rdi") bits.as_mut_ptr() => _, |
| 112 | + ); |
| 113 | + } |
| 114 | + println!("bits of {}: {:?}", value, &bits[0..popcnt]); |
| 115 | + } |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +(You can [try this example on the |
| 120 | +playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=38874735e48aa20289f23f5a3cbeae0c). |
| 121 | +Note that this code serves to demonstrate inline assembly, not to demonstrate |
| 122 | +an efficient implementation of any particular algorithm.) |
| 123 | + |
| 124 | +Notice that `value` and `popcnt` have registers selected for them, while |
| 125 | +`bits.as_mut_ptr()` must go in the `rdi` register for use with the `stosb` |
| 126 | +instruction. |
| 127 | + |
| 128 | +For full details on the new `asm!` syntax, see [RFC |
| 129 | +2873](https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md). |
| 130 | +Please try it out (including translating existing inline assembly to the new |
| 131 | +syntax), and [report any bugs via the rust issue |
| 132 | +tracker](https://github.com/rust-lang/rust/issues/) with the tag `F-asm`. You |
| 133 | +can also discuss inline assembly by creating a topic on [the project-inline-asm |
| 134 | +stream in |
| 135 | +Zulip](https://rust-lang.zulipchat.com/#narrow/stream/216763-project-inline-asm). |
0 commit comments