1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
// Copyright (c) 2017-2018 Jeremy Davis (jeremydavis519@gmail.com)
//
// Licensed under the Apache License, Version 2.0 (located at /LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0), or the MIT license
// (located at /LICENSE-MIT or http://opensource.org/licenses/MIT), at your
// option. The file may not be copied, modified, or distributed except
// according to those terms.
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
// ANY KIND, either express or implied. See the applicable license for the
// specific language governing permissions and limitations under that license.
//! A layer of syntactic sugar between Rust and inline assembly
//!
//! Rust currently has the [`asm!`] macro for writing inline ASM within a function defined in Rust. It uses the same basic
//! format as GCC uses for its own inline ASM--and that format isn't the most ergonomic. Here's a small example, taken from
//! [the OSDev wiki] and translated into Rust:
//!
//! [`asm!`]: https://doc.rust-lang.org/1.12.0/book/inline-assembly.html
//! [the OSDev wiki]: https://wiki.osdev.org/Inline_Assembly/Examples
//!
//! ```no_run
//! # #![feature(asm)]
//! // Retrieves a value from memory in a different segment than the one currently being used (x86[-64])
//! unsafe fn farpeekl(segment_selector: u16, offset: *const u32) -> u32 {
//! let ret: u32;
//! asm!("
//! push %fs
//! mov $1, %fs
//! mov %fs:($2), $0
//! pop %fs
//! " : "=r"(ret) : "r"(segment_selector), "r"(offset)
//! );
//! ret
//! }
//! # fn main() {}
//! ```
//!
//! (This example actually looks a little cleaner in my opinion than it does when written for GCC, but it could still use some work.)
//!
//! The `asm!` macro is currently an unstable, nightly-only feature. From what I've seen, there are several reasons, but one of
//! them is the syntax. It's too easy to forget the precise order of things (which come first: inputs or outputs?), and parts of
//! it are needlessly redundant. Using `"=r"`, `"r"`, or `"~r"` means the register is, respectively, an output, an input, or
//! clobbered, but the different types also have to be separated by colons. So using `asm!`, the programmer has to remember both
//! ways to tell the compiler what it should expect to happen to each register.
//!
//! This crate attempts to improve the syntax surrounding inline ASM so that it's both easier to read and easier to write without
//! looking up the required format every time. It works by using a procedural macro (1) to overload Rust's `let` statements, making
//! variables capable of storing information about how they'll be used in upcoming inline ASM blocks, and (2) to parse `asm`
//! blocks that allow variables defined with the new syntax to be used directly in the ASM code.
//!
//! ### Change Log
//!
//! * 0.1 - Initial release
//! * 0.2 - Inner blocks are now supported.
//!
//! ## Setup
//!
//! To use this crate, add the following to `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! rusty-asm = "0.2.1"
//! ```
//!
//! Then reference the crate in your main source file and activate the features you'll need:
//!
//! ```ignore
//! #![feature(proc_macro_hygiene, asm)]
//! extern crate rusty_asm;
//! use rusty_asm::rusty_asm; // Because who wants to write `rusty_asm::rusty_asm!`?
//! # fn main() {}
//! ```
//!
//! Note that you'll still need a nightly compiler for this. `rusty_asm` doesn't make inline ASM stable.
//!
//! ### Supported Features
//!
//! The following features are available:
//!
//! * `proc-macro`: Causes [`proc-macro2`](https://crates.io/crates/proc-macro2) to act as a thin wrapper over
//! [`proc_macro`](https://doc.rust-lang.org/proc_macro/index.html), including the parts that are still unstable.
//! The benefit of this feature is that it allows `rusty-asm` to provide its own warnings, which should make
//! debugging your own code easier.
//!
//! ## Basic Syntax
//!
//! In the place where you want to add some inline ASM, call `rusty_asm!` like so:
//!
//! ```ignore
//! # extern crate rusty_asm;
//! # use rusty_asm::rusty_asm;
//! # fn main() {
//! # unsafe {
//! rusty_asm! {
//! // (arbitrary Rust statements go here)
//!
//! asm (/* maybe some options in here */) {
//! // (insert your ASM code here, in quotes)
//! }
//!
//! // (possibly some cleanup code here)
//! }
//! # }
//! # }
//! ```
//!
//! The contents of the `asm` block need to be a string literal to make sure that Rust's parser doesn't mess up the
//! formatting. (Macros currently don't have access to whitespace information.) See the examples below for more specifics
//! about how it should look.
//!
//! Also, it's possible to have multiple `asm` blocks in the same `rusty_asm!` block, in case you want to reuse your bridge
//! variables (see below).
//!
//! ## Bridge Variables
//!
//! A _bridge variable_ is a variable that bridges the gap between Rust and ASM by incorporating the input/ouput/clobber
//! information in its definition. They can only be defined inside `rusty_asm!` blocks, and because the macro makes a new scope,
//! they are dropped when execution leaves those blocks (along with any other variables that are defined in the same scope). In
//! order to define a bridge variable, you'll need to use one of three keywords that are only valid inside `rusty_asm!` blocks:
//!
//! * `in`
//! * `out`
//! * `inout`
//!
//! Each of these keywords is used in a "let" statement to define a bridge variable. The exact syntax is as follows:
//!
//! ```text
//! let [mut] <identifier>: [<type>:] in(<constraint>) [= <expression>];
//! let [mut] <identifier>: [<type>:] out(<constraint>) [= <expression>];
//! let [mut] <identifier>: [<type>:] inout(<constraint>) [= <expression>];
//! ```
//!
//! The optional `<type>` is any Rust type, as far as the macro knows, but it should be something that makes sense to put in the
//! appropriate register (e.g. `usize`, `i8`, etc. for a general-purpose integer register).
//!
//! In addition, you can specify that you'll clobber a particular register (or that you'll clobber memory) with this syntax:
//!
//! ```text
//! clobber(<constraint>);
//! ```
//!
//! where `<constraint>` is either the name of a register (like `"eax"`) or `"memory"`.
//!
//! These statements correspond to LLVM constraints in the following way:
//!
//! ```text
//! // in, out, or inout:
//! <new-constraint>(<identifier>)
//! // clobber
//! <new-constraint>
//! ```
//!
//! In each case, `<new_constraint>` is equivalent to `<constraint>` except that for the `out` and `clobber` keywords, the `'='`
//! or `'~'` is prepended to satisfy `asm!` and LLVM. So, for instance, if you write the constraint as `"r"`, it will be
//! automatically translated to `"=r"` or `"~r"` as needed before being given to the compiler. The `inout` keyword results in two
//! new constraints: (1) the equivalent constraint for the `out` keyword (e.g. `"=r"`) and (2) an input constraint that's tied to
//! it (e.g. `"0"`).
//!
//! In order to let Rust know how to work with the bridge variables, `rusty_asm!` removes the new keywords and constraints during
//! macro expansion, so as far as Rust knows, they're just ordinary variables.
//!
//! ## The `asm` Block
//!
//! When an `asm` block is encountered, it is converted directly into an asm! invocation, using all of the constraints that have
//! been created thus far. The `asm` block's syntax is as follows:
//!
//! ```text
//! asm [(<options>)] {
//! "<asm-code>"
//! }
//! ```
//!
//! `<options>` is an optional comma-separated list of the options that would be after the 4th colon if `asm!` were being used, such
//! as `"volatile"`. `<asm-code>` is pure ASM code, enclosed in quotes, except that it can (and should) use the bridge variables
//! that have been defined above the `asm` block.
//!
//! In order to reference a bridge variable from inside an `asm` block, insert `$<ident>` into the code, where `<ident>` is the
//! variable's identifier. As with the `asm!` macro, `$$` encodes a literal dollar sign.
//!
//! ## The `rusty_asm!` Block and Scope
//!
//! The new macro puts its entire contents inside a new scope, so that any variables defined therein are dropped at the end. Their
//! values can be moved to variables outside the macro's scope before it ends, using regular Rust code, if they need to be preserved.
//! In addition, just like any of Rust's code blocks, this one has a return value that can be used by ending the block with an
//! expression.
//!
//! Also, as of version 0.2, the macro also correctly handles inner blocks, shadowing and dropping bridge variables just like Rust
//! shadows and drops regular variables. That means you can now write code like this:
//!
//! ```ignore
//! # #![feature(asm)]
//! # extern crate rusty_asm;
//! # use rusty_asm::rusty_asm;
//! #
//! # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
//! // Sends 1, 2, or 4 bytes at once to an ISA address (x86/x64).
//! unsafe fn poke_isa(port: u16, value: usize, bytes: u8) {
//! rusty_asm! {
//! let port: in("{dx}") = port;
//! if bytes == 1 {
//! let value: in("{al}") = value as u8;
//! asm("volatile", "intel") {
//! "out $port, $value"
//! }
//! } else if bytes == 2 {
//! let value: in("{ax}") = value as u16;
//! asm("volatile", "intel") {
//! "out $port, $value"
//! }
//! } else {
//! assert_eq!(bytes, 4);
//! let value: in("{eax}") = value as u32;
//! asm("volatile", "intel") {
//! "out $port, $value"
//! }
//! }
//! };
//! }
//! # fn main() {}
//! ```
//!
//! Defining bridge variables in `if let` and `while let` constructions is still not supported, since Rust doesn't support explicit
//! type annotations in them either, and I imagine the syntax would become overly complex.
//!
//! ## Further Reading
//!
//! There are too many platform-specific constraints and options that you can specify to list them all here. Follow these links for
//! more information.
//!
//! * [The Rust book: Inline Assembly chapter]. Discusses what can be done with the `asm!` macro.
//! * [LLVM's inline assembly documentation]. Documents exactly what is allowed in LLVM inline assembly (and therefore in Rust's `asm!`
//! invocations), along with platform-specific details.
//!
//! [The Rust book: Inline Assembly chapter]: https://doc.rust-lang.org/1.12.0/book/inline-assembly.html
//! [LLVM's inline assembly documentation]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions
//!
//! ## Usage Examples
//!
//! Note that while all of these examples use x86 assembly, `rusty_asm!` should work with any assembly dialect that Rust supports (which
//! probably means any dialect that LLVM supports).
//!
//! ```ignore
//! # #![feature(asm)]
//! # extern crate rusty_asm;
//! # use rusty_asm::rusty_asm;
//! #
//! # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
//! // Disables interrupts on an x86 CPU.
//! unsafe fn disable_interrupts() {
//! rusty_asm! {
//! asm("volatile") { // This block has to be marked "volatile" to make sure the compiler, seeing
//! "cli" // no outputs and no clobbers, doesn't assume it does nothing and
//! } // decide to "optimize" it away.
//! };
//! }
//! # fn main() {}
//! ```
//!
//! ```ignore
//! # #![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
//! # #![feature(asm)]
//! # extern crate rusty_asm;
//! # use rusty_asm::rusty_asm;
//! #
//! // Shifts the hexadecimal digits of `existing` up and puts `digit` in the resulting gap.
//! fn append_hex_digit(existing: usize, digit: u8) -> usize {
//! assert!(digit < 0x10);
//! unsafe {
//! rusty_asm! {
//! let mut big: inout("r") = existing;
//! let little: in("r") = digit as usize;
//!
//! asm {"
//! shll %4, $big
//! orl $little, $big
//! "}
//!
//! big
//! }
//! }
//! }
//!
//! # fn main() {
//! assert_eq!(append_hex_digit(0, 0), 0);
//! assert_eq!(append_hex_digit(0, 0xf), 0xf);
//! assert_eq!(append_hex_digit(4, 2), 0x42);
//! # }
//! ```
//!
//! ## Limitations
//!
//! The bridge variable declaration syntax is slightly more restrictive than that of general `let` statements in that it only allows
//! an identifier after the `let` keyword, not an arbitrary pattern. So, for instance, this statement would not work:
//!
//! ```compile_fail
//! # #![feature(asm)]
//! # extern crate rusty_asm
//! # use rusty_asm::rusty_asm;
//! #
//! rusty_asm! {
//! let (a, b): (in("r"), out("r")) = (12, 14);
//! /* ... */
//! }
//! ```
extern crate proc_macro;
extern crate quote;
extern crate syn;
use TokenStream;
use RustyAsmBlock;
/// Allows bridge variables, clobbers, and `asm` blocks to be defined.
///
/// See the [module documentation] for details.
///
/// [module documentation]: index.html