]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | use rustc_ast as ast; |
f9f354fc | 2 | use rustc_ast::ptr::P; |
04454e1e | 3 | use rustc_ast::token::{self, Delimiter}; |
f9f354fc XL |
4 | use rustc_ast::tokenstream::TokenStream; |
5 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | |
5e7ed085 | 6 | use rustc_errors::{Applicability, PResult}; |
f9f354fc XL |
7 | use rustc_expand::base::{self, *}; |
8 | use rustc_parse::parser::Parser; | |
f035d41b | 9 | use rustc_parse_format as parse; |
cdc7bbd5 | 10 | use rustc_session::lint; |
a2a8927a | 11 | use rustc_session::parse::ParseSess; |
17df50a5 | 12 | use rustc_span::symbol::Ident; |
cdc7bbd5 | 13 | use rustc_span::symbol::{kw, sym, Symbol}; |
f9f354fc | 14 | use rustc_span::{InnerSpan, Span}; |
cdc7bbd5 | 15 | use rustc_target::asm::InlineAsmArch; |
17df50a5 | 16 | use smallvec::smallvec; |
f9f354fc | 17 | |
a2a8927a | 18 | pub struct AsmArgs { |
5099ac24 FG |
19 | pub templates: Vec<P<ast::Expr>>, |
20 | pub operands: Vec<(ast::InlineAsmOperand, Span)>, | |
f9f354fc XL |
21 | named_args: FxHashMap<Symbol, usize>, |
22 | reg_args: FxHashSet<usize>, | |
5099ac24 | 23 | pub clobber_abis: Vec<(Symbol, Span)>, |
f9f354fc | 24 | options: ast::InlineAsmOptions, |
5099ac24 | 25 | pub options_spans: Vec<Span>, |
f9f354fc XL |
26 | } |
27 | ||
28 | fn parse_args<'a>( | |
29 | ecx: &mut ExtCtxt<'a>, | |
30 | sp: Span, | |
31 | tts: TokenStream, | |
17df50a5 | 32 | is_global_asm: bool, |
5e7ed085 | 33 | ) -> PResult<'a, AsmArgs> { |
f9f354fc | 34 | let mut p = ecx.new_parser_from_tts(tts); |
a2a8927a XL |
35 | let sess = &ecx.sess.parse_sess; |
36 | parse_asm_args(&mut p, sess, sp, is_global_asm) | |
37 | } | |
38 | ||
39 | // Primarily public for rustfmt consumption. | |
40 | // Internal consumers should continue to leverage `expand_asm`/`expand__global_asm` | |
41 | pub fn parse_asm_args<'a>( | |
42 | p: &mut Parser<'a>, | |
43 | sess: &'a ParseSess, | |
44 | sp: Span, | |
45 | is_global_asm: bool, | |
5e7ed085 | 46 | ) -> PResult<'a, AsmArgs> { |
a2a8927a | 47 | let diag = &sess.span_diagnostic; |
f9f354fc XL |
48 | |
49 | if p.token == token::Eof { | |
a2a8927a | 50 | return Err(diag.struct_span_err(sp, "requires at least a template string argument")); |
f9f354fc XL |
51 | } |
52 | ||
f035d41b | 53 | let first_template = p.parse_expr()?; |
f9f354fc | 54 | let mut args = AsmArgs { |
f035d41b | 55 | templates: vec![first_template], |
f9f354fc XL |
56 | operands: vec![], |
57 | named_args: FxHashMap::default(), | |
58 | reg_args: FxHashSet::default(), | |
3c0e092e | 59 | clobber_abis: Vec::new(), |
f9f354fc | 60 | options: ast::InlineAsmOptions::empty(), |
f035d41b | 61 | options_spans: vec![], |
f9f354fc XL |
62 | }; |
63 | ||
f035d41b | 64 | let mut allow_templates = true; |
f9f354fc XL |
65 | while p.token != token::Eof { |
66 | if !p.eat(&token::Comma) { | |
f035d41b XL |
67 | if allow_templates { |
68 | // After a template string, we always expect *only* a comma... | |
a2a8927a | 69 | let mut err = diag.struct_span_err(p.token.span, "expected token: `,`"); |
f9f354fc XL |
70 | err.span_label(p.token.span, "expected `,`"); |
71 | p.maybe_annotate_with_ascription(&mut err, false); | |
72 | return Err(err); | |
73 | } else { | |
74 | // ...after that delegate to `expect` to also include the other expected tokens. | |
75 | return Err(p.expect(&token::Comma).err().unwrap()); | |
76 | } | |
77 | } | |
f9f354fc XL |
78 | if p.token == token::Eof { |
79 | break; | |
80 | } // accept trailing commas | |
81 | ||
94222f64 XL |
82 | // Parse clobber_abi |
83 | if p.eat_keyword(sym::clobber_abi) { | |
a2a8927a | 84 | parse_clobber_abi(p, &mut args)?; |
94222f64 XL |
85 | allow_templates = false; |
86 | continue; | |
87 | } | |
88 | ||
f9f354fc | 89 | // Parse options |
29967ef6 | 90 | if p.eat_keyword(sym::options) { |
a2a8927a | 91 | parse_options(p, &mut args, is_global_asm)?; |
f035d41b | 92 | allow_templates = false; |
f9f354fc XL |
93 | continue; |
94 | } | |
95 | ||
96 | let span_start = p.token.span; | |
97 | ||
98 | // Parse operand names | |
99 | let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { | |
100 | let (ident, _) = p.token.ident().unwrap(); | |
101 | p.bump(); | |
102 | p.expect(&token::Eq)?; | |
f035d41b | 103 | allow_templates = false; |
f9f354fc XL |
104 | Some(ident.name) |
105 | } else { | |
106 | None | |
107 | }; | |
108 | ||
109 | let mut explicit_reg = false; | |
17df50a5 | 110 | let op = if !is_global_asm && p.eat_keyword(kw::In) { |
a2a8927a | 111 | let reg = parse_reg(p, &mut explicit_reg)?; |
c295e0f8 | 112 | if p.eat_keyword(kw::Underscore) { |
a2a8927a | 113 | let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands"); |
c295e0f8 XL |
114 | return Err(err); |
115 | } | |
f9f354fc XL |
116 | let expr = p.parse_expr()?; |
117 | ast::InlineAsmOperand::In { reg, expr } | |
17df50a5 | 118 | } else if !is_global_asm && p.eat_keyword(sym::out) { |
a2a8927a | 119 | let reg = parse_reg(p, &mut explicit_reg)?; |
f9f354fc XL |
120 | let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; |
121 | ast::InlineAsmOperand::Out { reg, expr, late: false } | |
17df50a5 | 122 | } else if !is_global_asm && p.eat_keyword(sym::lateout) { |
a2a8927a | 123 | let reg = parse_reg(p, &mut explicit_reg)?; |
f9f354fc XL |
124 | let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; |
125 | ast::InlineAsmOperand::Out { reg, expr, late: true } | |
17df50a5 | 126 | } else if !is_global_asm && p.eat_keyword(sym::inout) { |
a2a8927a | 127 | let reg = parse_reg(p, &mut explicit_reg)?; |
c295e0f8 | 128 | if p.eat_keyword(kw::Underscore) { |
a2a8927a | 129 | let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands"); |
c295e0f8 XL |
130 | return Err(err); |
131 | } | |
f9f354fc XL |
132 | let expr = p.parse_expr()?; |
133 | if p.eat(&token::FatArrow) { | |
134 | let out_expr = | |
135 | if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; | |
136 | ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } | |
137 | } else { | |
138 | ast::InlineAsmOperand::InOut { reg, expr, late: false } | |
139 | } | |
17df50a5 | 140 | } else if !is_global_asm && p.eat_keyword(sym::inlateout) { |
a2a8927a | 141 | let reg = parse_reg(p, &mut explicit_reg)?; |
c295e0f8 | 142 | if p.eat_keyword(kw::Underscore) { |
a2a8927a | 143 | let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands"); |
c295e0f8 XL |
144 | return Err(err); |
145 | } | |
f9f354fc XL |
146 | let expr = p.parse_expr()?; |
147 | if p.eat(&token::FatArrow) { | |
148 | let out_expr = | |
149 | if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; | |
150 | ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } | |
151 | } else { | |
152 | ast::InlineAsmOperand::InOut { reg, expr, late: true } | |
153 | } | |
29967ef6 | 154 | } else if p.eat_keyword(kw::Const) { |
cdc7bbd5 XL |
155 | let anon_const = p.parse_anon_const_expr()?; |
156 | ast::InlineAsmOperand::Const { anon_const } | |
04454e1e | 157 | } else if p.eat_keyword(sym::sym) { |
f9f354fc | 158 | let expr = p.parse_expr()?; |
04454e1e FG |
159 | let ast::ExprKind::Path(qself, path) = &expr.kind else { |
160 | let err = diag | |
161 | .struct_span_err(expr.span, "expected a path for argument to `sym`"); | |
162 | return Err(err); | |
163 | }; | |
164 | let sym = ast::InlineAsmSym { | |
165 | id: ast::DUMMY_NODE_ID, | |
166 | qself: qself.clone(), | |
167 | path: path.clone(), | |
168 | }; | |
169 | ast::InlineAsmOperand::Sym { sym } | |
f035d41b XL |
170 | } else if allow_templates { |
171 | let template = p.parse_expr()?; | |
172 | // If it can't possibly expand to a string, provide diagnostics here to include other | |
173 | // things it could have been. | |
174 | match template.kind { | |
487cf647 FG |
175 | ast::ExprKind::Lit(token_lit) |
176 | if matches!( | |
177 | token_lit.kind, | |
178 | token::LitKind::Str | token::LitKind::StrRaw(_) | |
179 | ) => {} | |
f035d41b XL |
180 | ast::ExprKind::MacCall(..) => {} |
181 | _ => { | |
94222f64 XL |
182 | let errstr = if is_global_asm { |
183 | "expected operand, options, or additional template string" | |
184 | } else { | |
185 | "expected operand, clobber_abi, options, or additional template string" | |
186 | }; | |
a2a8927a | 187 | let mut err = diag.struct_span_err(template.span, errstr); |
f035d41b XL |
188 | err.span_label(template.span, errstr); |
189 | return Err(err); | |
190 | } | |
191 | } | |
192 | args.templates.push(template); | |
193 | continue; | |
194 | } else { | |
29967ef6 | 195 | return p.unexpected(); |
f9f354fc XL |
196 | }; |
197 | ||
f035d41b | 198 | allow_templates = false; |
f9f354fc XL |
199 | let span = span_start.to(p.prev_token.span); |
200 | let slot = args.operands.len(); | |
201 | args.operands.push((op, span)); | |
202 | ||
94222f64 XL |
203 | // Validate the order of named, positional & explicit register operands and |
204 | // clobber_abi/options. We do this at the end once we have the full span | |
205 | // of the argument available. | |
f035d41b | 206 | if !args.options_spans.is_empty() { |
a2a8927a | 207 | diag.struct_span_err(span, "arguments are not allowed after options") |
f035d41b | 208 | .span_labels(args.options_spans.clone(), "previous options") |
f9f354fc XL |
209 | .span_label(span, "argument") |
210 | .emit(); | |
3c0e092e | 211 | } else if let Some((_, abi_span)) = args.clobber_abis.last() { |
a2a8927a | 212 | diag.struct_span_err(span, "arguments are not allowed after clobber_abi") |
3c0e092e | 213 | .span_label(*abi_span, "clobber_abi") |
94222f64 XL |
214 | .span_label(span, "argument") |
215 | .emit(); | |
f9f354fc XL |
216 | } |
217 | if explicit_reg { | |
218 | if name.is_some() { | |
a2a8927a | 219 | diag.struct_span_err(span, "explicit register arguments cannot have names").emit(); |
f9f354fc XL |
220 | } |
221 | args.reg_args.insert(slot); | |
222 | } else if let Some(name) = name { | |
223 | if let Some(&prev) = args.named_args.get(&name) { | |
a2a8927a | 224 | diag.struct_span_err(span, &format!("duplicate argument named `{}`", name)) |
f9f354fc XL |
225 | .span_label(args.operands[prev].1, "previously here") |
226 | .span_label(span, "duplicate argument") | |
227 | .emit(); | |
228 | continue; | |
229 | } | |
230 | if !args.reg_args.is_empty() { | |
a2a8927a | 231 | let mut err = diag.struct_span_err( |
f9f354fc XL |
232 | span, |
233 | "named arguments cannot follow explicit register arguments", | |
234 | ); | |
235 | err.span_label(span, "named argument"); | |
236 | for pos in &args.reg_args { | |
237 | err.span_label(args.operands[*pos].1, "explicit register argument"); | |
238 | } | |
239 | err.emit(); | |
240 | } | |
241 | args.named_args.insert(name, slot); | |
242 | } else { | |
243 | if !args.named_args.is_empty() || !args.reg_args.is_empty() { | |
a2a8927a | 244 | let mut err = diag.struct_span_err( |
f9f354fc XL |
245 | span, |
246 | "positional arguments cannot follow named arguments \ | |
247 | or explicit register arguments", | |
248 | ); | |
249 | err.span_label(span, "positional argument"); | |
250 | for pos in args.named_args.values() { | |
251 | err.span_label(args.operands[*pos].1, "named argument"); | |
252 | } | |
253 | for pos in &args.reg_args { | |
254 | err.span_label(args.operands[*pos].1, "explicit register argument"); | |
255 | } | |
256 | err.emit(); | |
257 | } | |
258 | } | |
259 | } | |
260 | ||
261 | if args.options.contains(ast::InlineAsmOptions::NOMEM) | |
262 | && args.options.contains(ast::InlineAsmOptions::READONLY) | |
263 | { | |
f035d41b | 264 | let spans = args.options_spans.clone(); |
a2a8927a | 265 | diag.struct_span_err(spans, "the `nomem` and `readonly` options are mutually exclusive") |
f9f354fc XL |
266 | .emit(); |
267 | } | |
268 | if args.options.contains(ast::InlineAsmOptions::PURE) | |
269 | && args.options.contains(ast::InlineAsmOptions::NORETURN) | |
270 | { | |
f035d41b | 271 | let spans = args.options_spans.clone(); |
a2a8927a | 272 | diag.struct_span_err(spans, "the `pure` and `noreturn` options are mutually exclusive") |
f9f354fc XL |
273 | .emit(); |
274 | } | |
275 | if args.options.contains(ast::InlineAsmOptions::PURE) | |
276 | && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) | |
277 | { | |
f035d41b | 278 | let spans = args.options_spans.clone(); |
a2a8927a | 279 | diag.struct_span_err( |
f035d41b | 280 | spans, |
f9f354fc XL |
281 | "the `pure` option must be combined with either `nomem` or `readonly`", |
282 | ) | |
283 | .emit(); | |
284 | } | |
285 | ||
286 | let mut have_real_output = false; | |
287 | let mut outputs_sp = vec![]; | |
94222f64 | 288 | let mut regclass_outputs = vec![]; |
f9f354fc XL |
289 | for (op, op_sp) in &args.operands { |
290 | match op { | |
94222f64 XL |
291 | ast::InlineAsmOperand::Out { reg, expr, .. } |
292 | | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => { | |
f9f354fc XL |
293 | outputs_sp.push(*op_sp); |
294 | have_real_output |= expr.is_some(); | |
94222f64 XL |
295 | if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg { |
296 | regclass_outputs.push(*op_sp); | |
297 | } | |
f9f354fc | 298 | } |
94222f64 | 299 | ast::InlineAsmOperand::InOut { reg, .. } => { |
f9f354fc XL |
300 | outputs_sp.push(*op_sp); |
301 | have_real_output = true; | |
94222f64 XL |
302 | if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg { |
303 | regclass_outputs.push(*op_sp); | |
304 | } | |
f9f354fc XL |
305 | } |
306 | _ => {} | |
307 | } | |
308 | } | |
309 | if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { | |
a2a8927a | 310 | diag.struct_span_err( |
f035d41b | 311 | args.options_spans.clone(), |
94222f64 | 312 | "asm with the `pure` option must have at least one output", |
f9f354fc XL |
313 | ) |
314 | .emit(); | |
315 | } | |
316 | if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() { | |
a2a8927a | 317 | let err = diag |
f9f354fc XL |
318 | .struct_span_err(outputs_sp, "asm outputs are not allowed with the `noreturn` option"); |
319 | ||
320 | // Bail out now since this is likely to confuse MIR | |
321 | return Err(err); | |
322 | } | |
3c0e092e XL |
323 | |
324 | if args.clobber_abis.len() > 0 { | |
94222f64 | 325 | if is_global_asm { |
a2a8927a | 326 | let err = diag.struct_span_err( |
3c0e092e XL |
327 | args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(), |
328 | "`clobber_abi` cannot be used with `global_asm!`", | |
329 | ); | |
94222f64 XL |
330 | |
331 | // Bail out now since this is likely to confuse later stages | |
332 | return Err(err); | |
333 | } | |
334 | if !regclass_outputs.is_empty() { | |
a2a8927a | 335 | diag.struct_span_err( |
94222f64 XL |
336 | regclass_outputs.clone(), |
337 | "asm with `clobber_abi` must specify explicit registers for outputs", | |
338 | ) | |
3c0e092e XL |
339 | .span_labels( |
340 | args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(), | |
341 | "clobber_abi", | |
342 | ) | |
94222f64 XL |
343 | .span_labels(regclass_outputs, "generic outputs") |
344 | .emit(); | |
345 | } | |
346 | } | |
f9f354fc XL |
347 | |
348 | Ok(args) | |
349 | } | |
350 | ||
f035d41b XL |
351 | /// Report a duplicate option error. |
352 | /// | |
353 | /// This function must be called immediately after the option token is parsed. | |
354 | /// Otherwise, the suggestion will be incorrect. | |
355 | fn err_duplicate_option<'a>(p: &mut Parser<'a>, symbol: Symbol, span: Span) { | |
356 | let mut err = p | |
357 | .sess | |
358 | .span_diagnostic | |
359 | .struct_span_err(span, &format!("the `{}` option was already provided", symbol)); | |
360 | err.span_label(span, "this option was already provided"); | |
361 | ||
362 | // Tool-only output | |
363 | let mut full_span = span; | |
364 | if p.token.kind == token::Comma { | |
365 | full_span = full_span.to(p.token.span); | |
366 | } | |
367 | err.tool_only_span_suggestion( | |
368 | full_span, | |
369 | "remove this option", | |
923072b8 | 370 | "", |
f035d41b XL |
371 | Applicability::MachineApplicable, |
372 | ); | |
373 | ||
374 | err.emit(); | |
375 | } | |
376 | ||
377 | /// Try to set the provided option in the provided `AsmArgs`. | |
378 | /// If it is already set, report a duplicate option error. | |
379 | /// | |
380 | /// This function must be called immediately after the option token is parsed. | |
381 | /// Otherwise, the error will not point to the correct spot. | |
382 | fn try_set_option<'a>( | |
383 | p: &mut Parser<'a>, | |
384 | args: &mut AsmArgs, | |
385 | symbol: Symbol, | |
386 | option: ast::InlineAsmOptions, | |
387 | ) { | |
388 | if !args.options.contains(option) { | |
389 | args.options |= option; | |
390 | } else { | |
391 | err_duplicate_option(p, symbol, p.prev_token.span); | |
392 | } | |
393 | } | |
394 | ||
17df50a5 XL |
395 | fn parse_options<'a>( |
396 | p: &mut Parser<'a>, | |
397 | args: &mut AsmArgs, | |
398 | is_global_asm: bool, | |
5e7ed085 | 399 | ) -> PResult<'a, ()> { |
f9f354fc XL |
400 | let span_start = p.prev_token.span; |
401 | ||
04454e1e | 402 | p.expect(&token::OpenDelim(Delimiter::Parenthesis))?; |
f9f354fc | 403 | |
04454e1e | 404 | while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { |
17df50a5 | 405 | if !is_global_asm && p.eat_keyword(sym::pure) { |
f035d41b | 406 | try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE); |
17df50a5 | 407 | } else if !is_global_asm && p.eat_keyword(sym::nomem) { |
f035d41b | 408 | try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM); |
17df50a5 | 409 | } else if !is_global_asm && p.eat_keyword(sym::readonly) { |
f035d41b | 410 | try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY); |
17df50a5 | 411 | } else if !is_global_asm && p.eat_keyword(sym::preserves_flags) { |
f035d41b | 412 | try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS); |
17df50a5 | 413 | } else if !is_global_asm && p.eat_keyword(sym::noreturn) { |
f035d41b | 414 | try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN); |
17df50a5 | 415 | } else if !is_global_asm && p.eat_keyword(sym::nostack) { |
f035d41b | 416 | try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK); |
064997fb FG |
417 | } else if !is_global_asm && p.eat_keyword(sym::may_unwind) { |
418 | try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::MAY_UNWIND); | |
29967ef6 | 419 | } else if p.eat_keyword(sym::att_syntax) { |
f035d41b | 420 | try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX); |
136023e0 XL |
421 | } else if p.eat_keyword(kw::Raw) { |
422 | try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::RAW); | |
29967ef6 XL |
423 | } else { |
424 | return p.unexpected(); | |
f9f354fc XL |
425 | } |
426 | ||
427 | // Allow trailing commas | |
04454e1e | 428 | if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { |
f9f354fc XL |
429 | break; |
430 | } | |
431 | p.expect(&token::Comma)?; | |
432 | } | |
433 | ||
434 | let new_span = span_start.to(p.prev_token.span); | |
f035d41b | 435 | args.options_spans.push(new_span); |
f9f354fc XL |
436 | |
437 | Ok(()) | |
438 | } | |
439 | ||
5e7ed085 | 440 | fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> { |
94222f64 XL |
441 | let span_start = p.prev_token.span; |
442 | ||
04454e1e | 443 | p.expect(&token::OpenDelim(Delimiter::Parenthesis))?; |
94222f64 | 444 | |
04454e1e | 445 | if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { |
3c0e092e XL |
446 | let err = p.sess.span_diagnostic.struct_span_err( |
447 | p.token.span, | |
448 | "at least one abi must be provided as an argument to `clobber_abi`", | |
449 | ); | |
450 | return Err(err); | |
451 | } | |
94222f64 | 452 | |
3c0e092e XL |
453 | let mut new_abis = Vec::new(); |
454 | loop { | |
455 | match p.parse_str_lit() { | |
456 | Ok(str_lit) => { | |
457 | new_abis.push((str_lit.symbol_unescaped, str_lit.span)); | |
458 | } | |
459 | Err(opt_lit) => { | |
460 | // If the non-string literal is a closing paren then it's the end of the list and is fine | |
04454e1e | 461 | if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { |
3c0e092e XL |
462 | break; |
463 | } | |
464 | let span = opt_lit.map_or(p.token.span, |lit| lit.span); | |
465 | let mut err = | |
466 | p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); | |
467 | err.span_label(span, "not a string literal"); | |
468 | return Err(err); | |
469 | } | |
470 | }; | |
94222f64 | 471 | |
3c0e092e | 472 | // Allow trailing commas |
04454e1e | 473 | if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { |
3c0e092e XL |
474 | break; |
475 | } | |
476 | p.expect(&token::Comma)?; | |
477 | } | |
94222f64 | 478 | |
3c0e092e XL |
479 | let full_span = span_start.to(p.prev_token.span); |
480 | ||
481 | if !args.options_spans.is_empty() { | |
94222f64 XL |
482 | let mut err = p |
483 | .sess | |
484 | .span_diagnostic | |
3c0e092e | 485 | .struct_span_err(full_span, "clobber_abi is not allowed after options"); |
94222f64 XL |
486 | err.span_labels(args.options_spans.clone(), "options"); |
487 | return Err(err); | |
488 | } | |
489 | ||
3c0e092e XL |
490 | match &new_abis[..] { |
491 | // should have errored above during parsing | |
492 | [] => unreachable!(), | |
493 | [(abi, _span)] => args.clobber_abis.push((*abi, full_span)), | |
494 | [abis @ ..] => { | |
495 | for (abi, span) in abis { | |
496 | args.clobber_abis.push((*abi, *span)); | |
497 | } | |
498 | } | |
499 | } | |
94222f64 XL |
500 | |
501 | Ok(()) | |
502 | } | |
503 | ||
f9f354fc XL |
504 | fn parse_reg<'a>( |
505 | p: &mut Parser<'a>, | |
506 | explicit_reg: &mut bool, | |
5e7ed085 | 507 | ) -> PResult<'a, ast::InlineAsmRegOrRegClass> { |
04454e1e | 508 | p.expect(&token::OpenDelim(Delimiter::Parenthesis))?; |
1b1a35ee | 509 | let result = match p.token.uninterpolate().kind { |
f9f354fc XL |
510 | token::Ident(name, false) => ast::InlineAsmRegOrRegClass::RegClass(name), |
511 | token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { | |
512 | *explicit_reg = true; | |
513 | ast::InlineAsmRegOrRegClass::Reg(symbol) | |
514 | } | |
515 | _ => { | |
516 | return Err( | |
517 | p.struct_span_err(p.token.span, "expected register class or explicit register") | |
518 | ); | |
519 | } | |
520 | }; | |
521 | p.bump(); | |
04454e1e | 522 | p.expect(&token::CloseDelim(Delimiter::Parenthesis))?; |
f9f354fc XL |
523 | Ok(result) |
524 | } | |
525 | ||
17df50a5 | 526 | fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm> { |
f035d41b | 527 | let mut template = vec![]; |
f9f354fc XL |
528 | // Register operands are implicitly used since they are not allowed to be |
529 | // referenced in the template string. | |
530 | let mut used = vec![false; args.operands.len()]; | |
531 | for pos in &args.reg_args { | |
532 | used[*pos] = true; | |
533 | } | |
f035d41b XL |
534 | let named_pos: FxHashMap<usize, Symbol> = |
535 | args.named_args.iter().map(|(&sym, &idx)| (idx, sym)).collect(); | |
536 | let mut line_spans = Vec::with_capacity(args.templates.len()); | |
537 | let mut curarg = 0; | |
538 | ||
94222f64 XL |
539 | let mut template_strs = Vec::with_capacity(args.templates.len()); |
540 | ||
064997fb FG |
541 | for (i, template_expr) in args.templates.into_iter().enumerate() { |
542 | if i != 0 { | |
f035d41b XL |
543 | template.push(ast::InlineAsmTemplatePiece::String("\n".to_string())); |
544 | } | |
f9f354fc | 545 | |
f035d41b XL |
546 | let msg = "asm template must be a string literal"; |
547 | let template_sp = template_expr.span; | |
548 | let (template_str, template_style, template_span) = | |
549 | match expr_to_spanned_string(ecx, template_expr, msg) { | |
550 | Ok(template_part) => template_part, | |
551 | Err(err) => { | |
c295e0f8 | 552 | if let Some((mut err, _)) = err { |
f035d41b XL |
553 | err.emit(); |
554 | } | |
17df50a5 | 555 | return None; |
f035d41b XL |
556 | } |
557 | }; | |
558 | ||
559 | let str_style = match template_style { | |
560 | ast::StrStyle::Cooked => None, | |
561 | ast::StrStyle::Raw(raw) => Some(raw as usize), | |
562 | }; | |
563 | ||
f035d41b | 564 | let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok(); |
94222f64 XL |
565 | template_strs.push(( |
566 | template_str, | |
487cf647 | 567 | template_snippet.as_deref().map(Symbol::intern), |
94222f64 XL |
568 | template_sp, |
569 | )); | |
a2a8927a | 570 | let template_str = template_str.as_str(); |
6a06907d | 571 | |
cdc7bbd5 XL |
572 | if let Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) = ecx.sess.asm_arch { |
573 | let find_span = |needle: &str| -> Span { | |
574 | if let Some(snippet) = &template_snippet { | |
575 | if let Some(pos) = snippet.find(needle) { | |
576 | let end = pos | |
3c0e092e | 577 | + snippet[pos..] |
cdc7bbd5 XL |
578 | .find(|c| matches!(c, '\n' | ';' | '\\' | '"')) |
579 | .unwrap_or(snippet[pos..].len() - 1); | |
580 | let inner = InnerSpan::new(pos, end); | |
581 | return template_sp.from_inner(inner); | |
6a06907d XL |
582 | } |
583 | } | |
cdc7bbd5 XL |
584 | template_sp |
585 | }; | |
6a06907d | 586 | |
cdc7bbd5 XL |
587 | if template_str.contains(".intel_syntax") { |
588 | ecx.parse_sess().buffer_lint( | |
589 | lint::builtin::BAD_ASM_STYLE, | |
590 | find_span(".intel_syntax"), | |
136023e0 | 591 | ecx.current_expansion.lint_node_id, |
cdc7bbd5 XL |
592 | "avoid using `.intel_syntax`, Intel syntax is the default", |
593 | ); | |
594 | } | |
595 | if template_str.contains(".att_syntax") { | |
596 | ecx.parse_sess().buffer_lint( | |
597 | lint::builtin::BAD_ASM_STYLE, | |
598 | find_span(".att_syntax"), | |
136023e0 | 599 | ecx.current_expansion.lint_node_id, |
cdc7bbd5 XL |
600 | "avoid using `.att_syntax`, prefer using `options(att_syntax)` instead", |
601 | ); | |
6a06907d XL |
602 | } |
603 | } | |
604 | ||
136023e0 XL |
605 | // Don't treat raw asm as a format string. |
606 | if args.options.contains(ast::InlineAsmOptions::RAW) { | |
607 | template.push(ast::InlineAsmTemplatePiece::String(template_str.to_string())); | |
608 | let template_num_lines = 1 + template_str.matches('\n').count(); | |
609 | line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines)); | |
610 | continue; | |
611 | } | |
612 | ||
f035d41b XL |
613 | let mut parser = parse::Parser::new( |
614 | template_str, | |
615 | str_style, | |
616 | template_snippet, | |
617 | false, | |
618 | parse::ParseMode::InlineAsm, | |
619 | ); | |
620 | parser.curarg = curarg; | |
621 | ||
622 | let mut unverified_pieces = Vec::new(); | |
623 | while let Some(piece) = parser.next() { | |
624 | if !parser.errors.is_empty() { | |
625 | break; | |
626 | } else { | |
627 | unverified_pieces.push(piece); | |
f9f354fc | 628 | } |
f035d41b XL |
629 | } |
630 | ||
631 | if !parser.errors.is_empty() { | |
632 | let err = parser.errors.remove(0); | |
04454e1e | 633 | let err_sp = template_span.from_inner(InnerSpan::new(err.span.start, err.span.end)); |
f035d41b XL |
634 | let msg = &format!("invalid asm template string: {}", err.description); |
635 | let mut e = ecx.struct_span_err(err_sp, msg); | |
636 | e.span_label(err_sp, err.label + " in asm template string"); | |
637 | if let Some(note) = err.note { | |
638 | e.note(¬e); | |
639 | } | |
640 | if let Some((label, span)) = err.secondary_label { | |
04454e1e | 641 | let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end)); |
f035d41b XL |
642 | e.span_label(err_sp, label); |
643 | } | |
644 | e.emit(); | |
17df50a5 | 645 | return None; |
f035d41b XL |
646 | } |
647 | ||
648 | curarg = parser.curarg; | |
649 | ||
04454e1e FG |
650 | let mut arg_spans = parser |
651 | .arg_places | |
652 | .iter() | |
653 | .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))); | |
f035d41b XL |
654 | for piece in unverified_pieces { |
655 | match piece { | |
656 | parse::Piece::String(s) => { | |
657 | template.push(ast::InlineAsmTemplatePiece::String(s.to_string())) | |
658 | } | |
659 | parse::Piece::NextArgument(arg) => { | |
660 | let span = arg_spans.next().unwrap_or(template_sp); | |
661 | ||
662 | let operand_idx = match arg.position { | |
663 | parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => { | |
664 | if idx >= args.operands.len() | |
665 | || named_pos.contains_key(&idx) | |
666 | || args.reg_args.contains(&idx) | |
667 | { | |
668 | let msg = format!("invalid reference to argument at index {}", idx); | |
669 | let mut err = ecx.struct_span_err(span, &msg); | |
670 | err.span_label(span, "from here"); | |
671 | ||
672 | let positional_args = args.operands.len() | |
673 | - args.named_args.len() | |
674 | - args.reg_args.len(); | |
675 | let positional = if positional_args != args.operands.len() { | |
676 | "positional " | |
677 | } else { | |
678 | "" | |
679 | }; | |
680 | let msg = match positional_args { | |
681 | 0 => format!("no {}arguments were given", positional), | |
682 | 1 => format!("there is 1 {}argument", positional), | |
683 | x => format!("there are {} {}arguments", x, positional), | |
684 | }; | |
685 | err.note(&msg); | |
686 | ||
687 | if named_pos.contains_key(&idx) { | |
688 | err.span_label(args.operands[idx].1, "named argument"); | |
689 | err.span_note( | |
690 | args.operands[idx].1, | |
691 | "named arguments cannot be referenced by position", | |
692 | ); | |
693 | } else if args.reg_args.contains(&idx) { | |
694 | err.span_label( | |
695 | args.operands[idx].1, | |
696 | "explicit register argument", | |
697 | ); | |
698 | err.span_note( | |
699 | args.operands[idx].1, | |
700 | "explicit register arguments cannot be used in the asm template", | |
701 | ); | |
702 | } | |
703 | err.emit(); | |
704 | None | |
f9f354fc | 705 | } else { |
f035d41b | 706 | Some(idx) |
f9f354fc | 707 | } |
f9f354fc | 708 | } |
064997fb | 709 | parse::ArgumentNamed(name) => { |
04454e1e FG |
710 | match args.named_args.get(&Symbol::intern(name)) { |
711 | Some(&idx) => Some(idx), | |
712 | None => { | |
713 | let msg = format!("there is no argument named `{}`", name); | |
064997fb | 714 | let span = arg.position_span; |
04454e1e FG |
715 | ecx.struct_span_err( |
716 | template_span | |
717 | .from_inner(InnerSpan::new(span.start, span.end)), | |
718 | &msg, | |
719 | ) | |
720 | .emit(); | |
721 | None | |
722 | } | |
f035d41b | 723 | } |
04454e1e | 724 | } |
f035d41b XL |
725 | }; |
726 | ||
727 | let mut chars = arg.format.ty.chars(); | |
728 | let mut modifier = chars.next(); | |
729 | if chars.next().is_some() { | |
730 | let span = arg | |
731 | .format | |
732 | .ty_span | |
04454e1e | 733 | .map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end))) |
f035d41b XL |
734 | .unwrap_or(template_sp); |
735 | ecx.struct_span_err( | |
736 | span, | |
737 | "asm template modifier must be a single character", | |
738 | ) | |
f9f354fc | 739 | .emit(); |
f035d41b XL |
740 | modifier = None; |
741 | } | |
f9f354fc | 742 | |
f035d41b XL |
743 | if let Some(operand_idx) = operand_idx { |
744 | used[operand_idx] = true; | |
745 | template.push(ast::InlineAsmTemplatePiece::Placeholder { | |
746 | operand_idx, | |
747 | modifier, | |
748 | span, | |
749 | }); | |
750 | } | |
f9f354fc XL |
751 | } |
752 | } | |
753 | } | |
f035d41b XL |
754 | |
755 | if parser.line_spans.is_empty() { | |
756 | let template_num_lines = 1 + template_str.matches('\n').count(); | |
757 | line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines)); | |
758 | } else { | |
04454e1e FG |
759 | line_spans.extend( |
760 | parser | |
761 | .line_spans | |
762 | .iter() | |
763 | .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))), | |
764 | ); | |
f035d41b | 765 | }; |
f9f354fc XL |
766 | } |
767 | ||
f035d41b XL |
768 | let mut unused_operands = vec![]; |
769 | let mut help_str = String::new(); | |
770 | for (idx, used) in used.into_iter().enumerate() { | |
771 | if !used { | |
772 | let msg = if let Some(sym) = named_pos.get(&idx) { | |
773 | help_str.push_str(&format!(" {{{}}}", sym)); | |
774 | "named argument never used" | |
f9f354fc | 775 | } else { |
f035d41b XL |
776 | help_str.push_str(&format!(" {{{}}}", idx)); |
777 | "argument never used" | |
778 | }; | |
779 | unused_operands.push((args.operands[idx].1, msg)); | |
780 | } | |
781 | } | |
f9f354fc XL |
782 | match unused_operands.len() { |
783 | 0 => {} | |
784 | 1 => { | |
785 | let (sp, msg) = unused_operands.into_iter().next().unwrap(); | |
786 | let mut err = ecx.struct_span_err(sp, msg); | |
787 | err.span_label(sp, msg); | |
f035d41b XL |
788 | err.help(&format!( |
789 | "if this argument is intentionally unused, \ | |
790 | consider using it in an asm comment: `\"/*{} */\"`", | |
791 | help_str | |
792 | )); | |
f9f354fc XL |
793 | err.emit(); |
794 | } | |
795 | _ => { | |
796 | let mut err = ecx.struct_span_err( | |
797 | unused_operands.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(), | |
798 | "multiple unused asm arguments", | |
799 | ); | |
800 | for (sp, msg) in unused_operands { | |
801 | err.span_label(sp, msg); | |
802 | } | |
f035d41b XL |
803 | err.help(&format!( |
804 | "if these arguments are intentionally unused, \ | |
805 | consider using them in an asm comment: `\"/*{} */\"`", | |
806 | help_str | |
807 | )); | |
f9f354fc XL |
808 | err.emit(); |
809 | } | |
810 | } | |
811 | ||
94222f64 XL |
812 | Some(ast::InlineAsm { |
813 | template, | |
814 | template_strs: template_strs.into_boxed_slice(), | |
815 | operands: args.operands, | |
3c0e092e | 816 | clobber_abis: args.clobber_abis, |
94222f64 XL |
817 | options: args.options, |
818 | line_spans, | |
819 | }) | |
f9f354fc XL |
820 | } |
821 | ||
a2a8927a | 822 | pub(super) fn expand_asm<'cx>( |
f9f354fc XL |
823 | ecx: &'cx mut ExtCtxt<'_>, |
824 | sp: Span, | |
825 | tts: TokenStream, | |
826 | ) -> Box<dyn base::MacResult + 'cx> { | |
17df50a5 XL |
827 | match parse_args(ecx, sp, tts, false) { |
828 | Ok(args) => { | |
829 | let expr = if let Some(inline_asm) = expand_preparsed_asm(ecx, args) { | |
830 | P(ast::Expr { | |
831 | id: ast::DUMMY_NODE_ID, | |
832 | kind: ast::ExprKind::InlineAsm(P(inline_asm)), | |
833 | span: sp, | |
834 | attrs: ast::AttrVec::new(), | |
835 | tokens: None, | |
836 | }) | |
837 | } else { | |
838 | DummyResult::raw_expr(sp, true) | |
839 | }; | |
840 | MacEager::expr(expr) | |
841 | } | |
842 | Err(mut err) => { | |
843 | err.emit(); | |
844 | DummyResult::any(sp) | |
845 | } | |
846 | } | |
847 | } | |
848 | ||
a2a8927a | 849 | pub(super) fn expand_global_asm<'cx>( |
17df50a5 XL |
850 | ecx: &'cx mut ExtCtxt<'_>, |
851 | sp: Span, | |
852 | tts: TokenStream, | |
853 | ) -> Box<dyn base::MacResult + 'cx> { | |
854 | match parse_args(ecx, sp, tts, true) { | |
855 | Ok(args) => { | |
856 | if let Some(inline_asm) = expand_preparsed_asm(ecx, args) { | |
857 | MacEager::items(smallvec![P(ast::Item { | |
3c0e092e | 858 | ident: Ident::empty(), |
f2b60f7d | 859 | attrs: ast::AttrVec::new(), |
17df50a5 | 860 | id: ast::DUMMY_NODE_ID, |
3c0e092e | 861 | kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), |
17df50a5 XL |
862 | vis: ast::Visibility { |
863 | span: sp.shrink_to_lo(), | |
864 | kind: ast::VisibilityKind::Inherited, | |
865 | tokens: None, | |
866 | }, | |
867 | span: ecx.with_def_site_ctxt(sp), | |
868 | tokens: None, | |
869 | })]) | |
870 | } else { | |
871 | DummyResult::any(sp) | |
872 | } | |
873 | } | |
f9f354fc XL |
874 | Err(mut err) => { |
875 | err.emit(); | |
876 | DummyResult::any(sp) | |
877 | } | |
878 | } | |
879 | } |