Skip to content

Commit 687f929

Browse files
authored
Rollup merge of #73364 - joshtriplett:inline-asm, r=Amanieu
asm: Allow multiple template string arguments; interpret them as newline-separated Allow the `asm!` macro to accept a series of template arguments, and interpret them as if they were concatenated with a '\n' between them. This allows writing an `asm!` where each line of assembly appears in a separate template string argument. This syntax makes it possible for rustfmt to reliably format and indent each line of assembly, without risking changes to the inside of a template string. It also avoids the complexity of having the user carefully format and indent a multi-line string (including where to put the surrounding quotes), and avoids the extra indentation and lines of a call to `concat!`. For example, rewriting the second example from the [blog post on the new inline assembly syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html) using multiple template strings: ```rust 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() => _, ); } println!("bits of {}: {:?}", value, &bits[0..popcnt]); } } ``` Note that all the template strings must appear before all other arguments; you cannot, for instance, provide a series of template strings intermixed with the corresponding operands.
2 parents 65c33ed + fd9ed30 commit 687f929

File tree

10 files changed

+570
-178
lines changed

10 files changed

+570
-178
lines changed

src/doc/unstable-book/src/library-features/asm.md

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,13 @@ Let us see another example that also uses an input:
6868
let i: u64 = 3;
6969
let o: u64;
7070
unsafe {
71-
asm!("
72-
mov {0}, {1}
73-
add {0}, {number}
74-
", out(reg) o, in(reg) i, number = const 5);
71+
asm!(
72+
"mov {0}, {1}",
73+
"add {0}, {number}",
74+
out(reg) o,
75+
in(reg) i,
76+
number = const 5,
77+
);
7578
}
7679
assert_eq!(o, 8);
7780
```
@@ -82,13 +85,18 @@ and then adding `5` to it.
8285

8386
The example shows a few things:
8487

85-
First we can see that inputs are declared by writing `in` instead of `out`.
88+
First, we can see that `asm!` allows multiple template string arguments; each
89+
one is treated as a separate line of assembly code, as if they were all joined
90+
together with newlines between them. This makes it easy to format assembly
91+
code.
92+
93+
Second, we can see that inputs are declared by writing `in` instead of `out`.
8694

87-
Second one of our operands has a type we haven't seen yet, `const`.
95+
Third, one of our operands has a type we haven't seen yet, `const`.
8896
This tells the compiler to expand this argument to value directly inside the assembly template.
8997
This is only possible for constants and literals.
9098

91-
Third we can see that we can specify an argument number, or name as in any format string.
99+
Fourth, we can see that we can specify an argument number, or name as in any format string.
92100
For inline assembly templates this is particularly useful as arguments are often used more than once.
93101
For more complex inline assembly using this facility is generally recommended, as it improves
94102
readability, and allows reordering instructions without changing the argument order.
@@ -137,10 +145,13 @@ let mut a: u64 = 4;
137145
let b: u64 = 4;
138146
let c: u64 = 4;
139147
unsafe {
140-
asm!("
141-
add {0}, {1}
142-
add {0}, {2}
143-
", inout(reg) a, in(reg) b, in(reg) c);
148+
asm!(
149+
"add {0}, {1}",
150+
"add {0}, {2}",
151+
inout(reg) a,
152+
in(reg) b,
153+
in(reg) c,
154+
);
144155
}
145156
assert_eq!(a, 12);
146157
```
@@ -233,7 +244,7 @@ unsafe {
233244
// ECX 0 selects the L0 cache information.
234245
inout("ecx") 0 => ecx,
235246
lateout("ebx") ebx,
236-
lateout("edx") _
247+
lateout("edx") _,
237248
);
238249
}
239250
@@ -255,12 +266,14 @@ This can also be used with a general register class (e.g. `reg`) to obtain a scr
255266
// Multiply x by 6 using shifts and adds
256267
let mut x: u64 = 4;
257268
unsafe {
258-
asm!("
259-
mov {tmp}, {x}
260-
shl {tmp}, 1
261-
shl {x}, 2
262-
add {x}, {tmp}
263-
", x = inout(reg) x, tmp = out(reg) _);
269+
asm!(
270+
"mov {tmp}, {x}",
271+
"shl {tmp}, 1",
272+
"shl {x}, 2",
273+
"add {x}, {tmp}",
274+
x = inout(reg) x,
275+
tmp = out(reg) _,
276+
);
264277
}
265278
assert_eq!(x, 4 * 6);
266279
```
@@ -338,7 +351,7 @@ unsafe {
338351
asm!(
339352
"add {0}, {1}",
340353
inlateout(reg) a, in(reg) b,
341-
options(pure, nomem, nostack)
354+
options(pure, nomem, nostack),
342355
);
343356
}
344357
assert_eq!(a, 8);
@@ -371,17 +384,19 @@ reg_operand := dir_spec "(" reg_spec ")" operand_expr
371384
operand := reg_operand / "const" const_expr / "sym" path
372385
option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "att_syntax"
373386
options := "options(" option *["," option] [","] ")"
374-
asm := "asm!(" format_string *("," [ident "="] operand) ["," options] [","] ")"
387+
asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")"
375388
```
376389

377390
The macro will initially be supported only on ARM, AArch64, Hexagon, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.
378391

379392
[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax
380393

381-
## Template string
394+
## Template string arguments
382395

383396
The assembler template uses the same syntax as [format strings][format-syntax] (i.e. placeholders are specified by curly braces). The corresponding arguments are accessed in order, by index, or by name. However, implicit named arguments (introduced by [RFC #2795][rfc-2795]) are not supported.
384397

398+
An `asm!` invocation may have one or more template string arguments; an `asm!` with multiple template string arguments is treated as if all the strings were concatenated with a `\n` between them. The expected usage is for each template string argument to correspond to a line of assembly code. All template string arguments must appear before any other arguments.
399+
385400
As with format strings, named arguments must appear after positional arguments. Explicit register operands must appear at the end of the operand list, after named arguments if any.
386401

387402
Explicit register operands cannot be used by placeholders in the template string. All other named and positional operands must appear at least once in the template string, otherwise a compiler error is generated.

src/librustc_ast/ast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1905,7 +1905,7 @@ impl fmt::Display for InlineAsmTemplatePiece {
19051905
match c {
19061906
'{' => f.write_str("{{")?,
19071907
'}' => f.write_str("}}")?,
1908-
_ => write!(f, "{}", c.escape_debug())?,
1908+
_ => c.fmt(f)?,
19091909
}
19101910
}
19111911
Ok(())

0 commit comments

Comments
 (0)