Skip to content

Commit 733869a

Browse files
joshtriplettAmanieu
authored andcommitted
RFC 2873 (asm!): Allow multiple template string arguments
Interpret them as newline-separated. Update examples and explanations for this change.
1 parent 087ac5c commit 733869a

File tree

1 file changed

+46
-22
lines changed

1 file changed

+46
-22
lines changed

text/0000-inline-asm.md

+46-22
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,13 @@ Let us see another example that also uses an input:
8080
let i: u64 = 3;
8181
let o: u64;
8282
unsafe {
83-
asm!("
84-
mov {0}, {1}
85-
add {0}, {number}
86-
", out(reg) o, in(reg) i, number = const 5);
83+
asm!(
84+
"mov {0}, {1}",
85+
"add {0}, {number}",
86+
out(reg) o,
87+
in(reg) i,
88+
number = const 5,
89+
);
8790
}
8891
assert_eq!(o, 8);
8992
```
@@ -94,13 +97,18 @@ and then adding `5` to it.
9497

9598
The example shows a few things:
9699

97-
First we can see that inputs are declared by writing `in` instead of `out`.
100+
First, we can see that `asm!` allows multiple template string arguments; each
101+
one is treated as a separate line of assembly code, as if they were all joined
102+
together with newlines between them. This makes it easy to format assembly
103+
code.
104+
105+
Second, we can see that inputs are declared by writing `in` instead of `out`.
98106

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

103-
Third we can see that we can specify an argument number, or name as in any format string.
111+
Fourth, we can see that we can specify an argument number, or name as in any format string.
104112
For inline assembly templates this is particularly useful as arguments are often used more than once.
105113
For more complex inline assembly using this facility is generally recommended, as it improves
106114
readability, and allows reordering instructions without changing the argument order.
@@ -146,10 +154,13 @@ let mut a: u64 = 4;
146154
let b: u64 = 4;
147155
let c: u64 = 4;
148156
unsafe {
149-
asm!("
150-
add {0}, {1}
151-
add {0}, {2}
152-
", inout(reg) a, in(reg) b, in(reg) c);
157+
asm!(
158+
"add {0}, {1}",
159+
"add {0}, {2}",
160+
inout(reg) a,
161+
in(reg) b,
162+
in(reg) c,
163+
);
153164
}
154165
assert_eq!(a, 12);
155166
```
@@ -203,7 +214,7 @@ fn mul(a: u64, b: u64) -> u128 {
203214
"mul {}",
204215
in(reg) a,
205216
inlateout("rax") b => lo,
206-
lateout("rdx") hi
217+
lateout("rdx") hi,
207218
);
208219
}
209220

@@ -238,7 +249,7 @@ unsafe {
238249
// ECX 0 selects the L0 cache information.
239250
inout("ecx") 0 => ecx,
240251
lateout("ebx") ebx,
241-
lateout("edx") _
252+
lateout("edx") _,
242253
);
243254
}
244255

@@ -259,12 +270,14 @@ This can also be used with a general register class (e.g. `reg`) to obtain a scr
259270
// Multiply x by 6 using shifts and adds
260271
let mut x: u64 = 4;
261272
unsafe {
262-
asm!("
263-
mov {tmp}, {x}
264-
shl {tmp}, 1
265-
shl {x}, 2
266-
add {x}, {tmp}
267-
", x = inout(reg) x, tmp = out(reg) _);
273+
asm!(
274+
"mov {tmp}, {x}",
275+
"shl {tmp}, 1",
276+
"shl {x}, 2",
277+
"add {x}, {tmp}",
278+
x = inout(reg) x,
279+
tmp = out(reg) _,
280+
);
268281
}
269282
assert_eq!(x, 4 * 6);
270283
```
@@ -359,6 +372,7 @@ See the reference for the full list of available options and their effects.
359372

360373
Inline assembler is implemented as an unsafe macro `asm!()`.
361374
The first argument to this macro is a template string literal used to build the final assembly.
375+
Additional template string literal arguments may be provided; all of the template string arguments are interpreted as if concatenated into a single template string with `\n` between them.
362376
The following arguments specify input and output operands.
363377
When required, options are specified as the final argument.
364378

@@ -372,17 +386,19 @@ reg_operand := dir_spec "(" reg_spec ")" operand_expr
372386
operand := reg_operand / "const" const_expr / "sym" path
373387
option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "att_syntax"
374388
options := "options(" option *["," option] [","] ")"
375-
asm := "asm!(" format_string *("," [ident "="] operand) ["," options] [","] ")"
389+
asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")"
376390
```
377391

378392
The macro will initially be supported only on ARM, AArch64, 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.
379393

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

382-
## Template string
396+
## Template string arguments
383397

384398
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.
385399

400+
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.
401+
386402
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.
387403

388404
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.
@@ -1007,6 +1023,12 @@ Including the name of the target architecture as part of the `asm!` invocation c
10071023

10081024
The operands could be placed before the template string, which could make the asm easier to read in some cases. However we decided against it because the benefits are small and the syntax would no longer mirror that of Rust format string.
10091025

1026+
## Operands interleaved with template string arguments
1027+
1028+
An asm directive could contain a series of template string arguments, each followed by the operands referenced in that template string argument. This could potentially simplify long blocks of assembly. However, this could introduce significant complexity and difficulty of reading, due to the numbering of positional arguments, and the possibility of referencing named or numbered arguments other than those that appear grouped with a given template string argument.
1029+
1030+
Experimentation with such mechanisms could take place in wrapper macros around `asm!`, rather than in `asm!` itself.
1031+
10101032
# Prior art
10111033
[prior-art]: #prior-art
10121034

@@ -1043,7 +1065,9 @@ GCC supports passing C labels (the ones used with `goto`) to an inline asm block
10431065
This could be supported by allowing code blocks to be specified as operand types. The following code will print `a` if the input value is `42`, or print `b` otherwise.
10441066

10451067
```rust
1046-
asm!("cmp {}, 42; jeq {}",
1068+
asm!(
1069+
"cmp {}, 42",
1070+
"jeq {}",
10471071
in(reg) val,
10481072
label { println!("a"); },
10491073
fallthrough { println!("b"); }

0 commit comments

Comments
 (0)