Skip to content

Commit 1078b6f

Browse files
committed
asm: Allow multiple template strings; 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. In order to get srcloc mappings right for macros that generate multi-line string literals, create one line_span for each line in the string literal, each pointing to the macro. Make `rustc_parse_format::Parser::curarg` `pub`, so that we can propagate it from one template string argument to the next.
1 parent 50d6d4d commit 1078b6f

File tree

8 files changed

+530
-154
lines changed

8 files changed

+530
-154
lines changed

src/librustc_builtin_macros/asm.rs

Lines changed: 182 additions & 145 deletions
Large diffs are not rendered by default.

src/librustc_parse_format/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ pub struct Parser<'a> {
178178
/// Error messages accumulated during parsing
179179
pub errors: Vec<ParseError>,
180180
/// Current position of implicit positional argument pointer
181-
curarg: usize,
181+
pub curarg: usize,
182182
/// `Some(raw count)` when the string is "raw", used to position spans correctly
183183
style: Option<usize>,
184184
/// Start and end byte offset of every successfully parsed argument
@@ -243,11 +243,13 @@ impl<'a> Iterator for Parser<'a> {
243243
_ => Some(String(self.string(pos))),
244244
}
245245
} else {
246-
if self.is_literal && self.cur_line_start != self.input.len() {
246+
if self.is_literal {
247247
let start = self.to_span_index(self.cur_line_start);
248248
let end = self.to_span_index(self.input.len());
249-
self.line_spans.push(start.to(end));
250-
self.cur_line_start = self.input.len();
249+
let span = start.to(end);
250+
if self.line_spans.last() != Some(&span) {
251+
self.line_spans.push(span);
252+
}
251253
}
252254
None
253255
}

src/test/pretty/asm.pp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,13 @@
2222
asm!("{0}", inout(reg) b);
2323
asm!("{0} {1}", out(reg) _, inlateout(reg) b => _);
2424
asm!("", out("al") _, lateout("rbx") _);
25+
asm!("inst1\ninst2");
26+
asm!("inst1 {0}, 42\ninst2 {1}, 24", in(reg) a, out(reg) b);
27+
asm!("inst2 {1}, 24\ninst1 {0}, 42", in(reg) a, out(reg) b);
28+
asm!("inst1 {0}, 42\ninst2 {1}, 24", in(reg) a, out(reg) b);
29+
asm!("inst1\ninst2");
30+
asm!("inst1\ninst2");
31+
asm!("inst1\n\tinst2");
32+
asm!("inst1\ninst2\ninst3\ninst4");
2533
}
2634
}

src/test/pretty/asm.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,14 @@ pub fn main() {
1616
asm!("{name}", name = inout(reg) b);
1717
asm!("{} {}", out(reg) _, inlateout(reg) b => _);
1818
asm!("", out("al") _, lateout("rbx") _);
19+
asm!("inst1", "inst2");
20+
asm!("inst1 {}, 42", "inst2 {}, 24", in(reg) a, out(reg) b);
21+
asm!("inst2 {1}, 24", "inst1 {0}, 42", in(reg) a, out(reg) b);
22+
asm!("inst1 {}, 42", "inst2 {name}, 24", in(reg) a, name = out(reg) b);
23+
asm!("inst1
24+
inst2");
25+
asm!("inst1\ninst2");
26+
asm!("inst1\n\tinst2");
27+
asm!("inst1\ninst2", "inst3\ninst4");
1928
}
2029
}

src/test/ui/asm/parse-error.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn main() {
1313
asm!("{}" foo);
1414
//~^ ERROR expected token: `,`
1515
asm!("{}", foo);
16-
//~^ ERROR expected one of
16+
//~^ ERROR expected operand, options, or additional template string
1717
asm!("{}", in foo);
1818
//~^ ERROR expected `(`, found `foo`
1919
asm!("{}", in(reg foo));
@@ -52,5 +52,13 @@ fn main() {
5252
//~^ ERROR named arguments cannot follow explicit register arguments
5353
asm!("{1}", in("eax") foo, const bar);
5454
//~^ ERROR positional arguments cannot follow named arguments or explicit register arguments
55+
asm!("", options(), "");
56+
//~^ ERROR expected one of
57+
asm!("{}", in(reg) foo, "{}", out(reg) foo);
58+
//~^ ERROR expected one of
59+
asm!(format!("{{{}}}", 0), in(reg) foo);
60+
//~^ ERROR asm template must be a string literal
61+
asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
62+
//~^ ERROR asm template must be a string literal
5563
}
5664
}

src/test/ui/asm/parse-error.stderr

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ error: expected token: `,`
1616
LL | asm!("{}" foo);
1717
| ^^^ expected `,`
1818

19-
error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `foo`
19+
error: expected operand, options, or additional template string
2020
--> $DIR/parse-error.rs:15:20
2121
|
2222
LL | asm!("{}", foo);
23-
| ^^^ expected one of 8 possible tokens
23+
| ^^^ expected operand, options, or additional template string
2424

2525
error: expected `(`, found `foo`
2626
--> $DIR/parse-error.rs:17:23
@@ -160,5 +160,33 @@ LL | asm!("{1}", in("eax") foo, const bar);
160160
| |
161161
| explicit register argument
162162

163-
error: aborting due to 24 previous errors
163+
error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
164+
--> $DIR/parse-error.rs:55:29
165+
|
166+
LL | asm!("", options(), "");
167+
| ^^ expected one of 8 possible tokens
168+
169+
error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
170+
--> $DIR/parse-error.rs:57:33
171+
|
172+
LL | asm!("{}", in(reg) foo, "{}", out(reg) foo);
173+
| ^^^^ expected one of 8 possible tokens
174+
175+
error: asm template must be a string literal
176+
--> $DIR/parse-error.rs:59:14
177+
|
178+
LL | asm!(format!("{{{}}}", 0), in(reg) foo);
179+
| ^^^^^^^^^^^^^^^^^^^^
180+
|
181+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
182+
183+
error: asm template must be a string literal
184+
--> $DIR/parse-error.rs:61:21
185+
|
186+
LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
187+
| ^^^^^^^^^^^^^^^^^^^^
188+
|
189+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
190+
191+
error: aborting due to 28 previous errors
164192

src/test/ui/asm/srcloc.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,85 @@ fn main() {
4040

4141
asm!("movaps %xmm3, (%esi, 2)", options(att_syntax));
4242
//~^ WARN: scale factor without index register is ignored
43+
44+
asm!(
45+
"invalid_instruction",
46+
);
47+
//~^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
48+
49+
asm!(
50+
"mov eax, eax",
51+
"invalid_instruction",
52+
"mov eax, eax",
53+
);
54+
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
55+
56+
asm!(
57+
"mov eax, eax\n",
58+
"invalid_instruction",
59+
"mov eax, eax",
60+
);
61+
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
62+
63+
asm!(
64+
"mov eax, eax",
65+
concat!("invalid", "_", "instruction"),
66+
"mov eax, eax",
67+
);
68+
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
69+
70+
asm!(
71+
concat!("mov eax", ", ", "eax"),
72+
concat!("invalid", "_", "instruction"),
73+
concat!("mov eax", ", ", "eax"),
74+
);
75+
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
76+
77+
// Make sure template strings get separated
78+
asm!(
79+
"invalid_instruction1",
80+
"invalid_instruction2",
81+
);
82+
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1'
83+
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2'
84+
85+
asm!(
86+
concat!(
87+
"invalid", "_", "instruction1", "\n",
88+
"invalid", "_", "instruction2",
89+
),
90+
);
91+
//~^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1'
92+
//~^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2'
93+
94+
asm!(
95+
concat!(
96+
"invalid", "_", "instruction1", "\n",
97+
"invalid", "_", "instruction2",
98+
),
99+
concat!(
100+
"invalid", "_", "instruction3", "\n",
101+
"invalid", "_", "instruction4",
102+
),
103+
);
104+
//~^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1'
105+
//~^^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2'
106+
//~^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction3'
107+
//~^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction4'
108+
109+
asm!(
110+
concat!(
111+
"invalid", "_", "instruction1", "\n",
112+
"invalid", "_", "instruction2", "\n",
113+
),
114+
concat!(
115+
"invalid", "_", "instruction3", "\n",
116+
"invalid", "_", "instruction4", "\n",
117+
),
118+
);
119+
//~^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1'
120+
//~^^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2'
121+
//~^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction3'
122+
//~^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction4'
43123
}
44124
}

0 commit comments

Comments
 (0)