Skip to main content

rustc_builtin_macros/
asm.rs

1use rustc_ast::tokenstream::TokenStream;
2use rustc_ast::{AsmMacro, token};
3use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
4use rustc_errors::PResult;
5use rustc_expand::base::*;
6use rustc_index::bit_set::GrowableBitSet;
7use rustc_parse::parser::asm::*;
8use rustc_session::lint;
9use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym};
10use rustc_target::asm::InlineAsmArch;
11use smallvec::smallvec;
12use {rustc_ast as ast, rustc_parse_format as parse};
13
14use crate::errors;
15use crate::util::{ExprToSpannedString, expr_to_spanned_string};
16
17/// Validated assembly arguments, ready for macro expansion.
18struct ValidatedAsmArgs {
19    pub templates: Vec<Box<ast::Expr>>,
20    pub operands: Vec<(ast::InlineAsmOperand, Span)>,
21    named_args: FxIndexMap<Symbol, usize>,
22    reg_args: GrowableBitSet<usize>,
23    pub clobber_abis: Vec<(Symbol, Span)>,
24    options: ast::InlineAsmOptions,
25    pub options_spans: Vec<Span>,
26}
27
28fn parse_args<'a>(
29    ecx: &ExtCtxt<'a>,
30    sp: Span,
31    tts: TokenStream,
32    asm_macro: AsmMacro,
33) -> PResult<'a, ValidatedAsmArgs> {
34    let args = parse_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?;
35    validate_asm_args(ecx, asm_macro, args)
36}
37
38fn validate_asm_args<'a>(
39    ecx: &ExtCtxt<'a>,
40    asm_macro: AsmMacro,
41    args: Vec<AsmArg>,
42) -> PResult<'a, ValidatedAsmArgs> {
43    let dcx = ecx.dcx();
44
45    let strip_unconfigured = rustc_expand::config::StripUnconfigured {
46        sess: ecx.sess,
47        features: Some(ecx.ecfg.features),
48        config_tokens: false,
49        lint_node_id: ecx.current_expansion.lint_node_id,
50    };
51
52    let mut validated = ValidatedAsmArgs {
53        templates: ::alloc::vec::Vec::new()vec![],
54        operands: ::alloc::vec::Vec::new()vec![],
55        named_args: Default::default(),
56        reg_args: Default::default(),
57        clobber_abis: Vec::new(),
58        options: ast::InlineAsmOptions::empty(),
59        options_spans: ::alloc::vec::Vec::new()vec![],
60    };
61
62    let mut allow_templates = true;
63
64    for arg in args {
65        for attr in arg.attributes.0.iter() {
66            if !#[allow(non_exhaustive_omitted_patterns)] match attr.name() {
    Some(sym::cfg | sym::cfg_attr) => true,
    _ => false,
}matches!(attr.name(), Some(sym::cfg | sym::cfg_attr)) {
67                ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() });
68            }
69        }
70
71        // Skip arguments that are configured out.
72        if strip_unconfigured.configure(arg.attributes).is_none() {
73            continue;
74        }
75
76        match arg.kind {
77            AsmArgKind::Template(template) => {
78                // The error for the first template is delayed.
79                if !allow_templates {
80                    match template.kind {
81                        ast::ExprKind::Lit(token_lit)
82                            if #[allow(non_exhaustive_omitted_patterns)] match token_lit.kind {
    token::LitKind::Str | token::LitKind::StrRaw(_) => true,
    _ => false,
}matches!(
83                                token_lit.kind,
84                                token::LitKind::Str | token::LitKind::StrRaw(_)
85                            ) => {}
86                        ast::ExprKind::MacCall(..) => {}
87                        _ => {
88                            let err = dcx.create_err(errors::AsmExpectedOther {
89                                span: template.span,
90                                is_inline_asm: #[allow(non_exhaustive_omitted_patterns)] match asm_macro {
    AsmMacro::Asm => true,
    _ => false,
}matches!(asm_macro, AsmMacro::Asm),
91                            });
92                            return Err(err);
93                        }
94                    }
95                }
96
97                validated.templates.push(template);
98            }
99            AsmArgKind::Operand(name, op) => {
100                allow_templates = false;
101
102                let explicit_reg = #[allow(non_exhaustive_omitted_patterns)] match op.reg() {
    Some(ast::InlineAsmRegOrRegClass::Reg(_)) => true,
    _ => false,
}matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_)));
103                let span = arg.span;
104                let slot = validated.operands.len();
105                validated.operands.push((op, span));
106
107                // Validate the order of named, positional & explicit register operands and
108                // clobber_abi/options. We do this at the end once we have the full span
109                // of the argument available.
110
111                if explicit_reg {
112                    if name.is_some() {
113                        dcx.emit_err(errors::AsmExplicitRegisterName { span });
114                    }
115                    validated.reg_args.insert(slot);
116                } else if let Some(name) = name {
117                    if let Some(&prev) = validated.named_args.get(&name) {
118                        dcx.emit_err(errors::AsmDuplicateArg {
119                            span,
120                            name,
121                            prev: validated.operands[prev].1,
122                        });
123                        continue;
124                    }
125                    validated.named_args.insert(name, slot);
126                } else if !validated.named_args.is_empty() || !validated.reg_args.is_empty() {
127                    let named =
128                        validated.named_args.values().map(|p| validated.operands[*p].1).collect();
129                    let explicit =
130                        validated.reg_args.iter().map(|p| validated.operands[p].1).collect();
131
132                    dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
133                }
134            }
135            AsmArgKind::Options(new_options) => {
136                allow_templates = false;
137
138                for asm_option in new_options {
139                    let AsmOption { span, symbol, span_with_comma, options } = asm_option;
140
141                    if !asm_macro.is_supported_option(options) {
142                        // Tool-only output.
143                        dcx.emit_err(errors::AsmUnsupportedOption {
144                            span,
145                            symbol,
146                            span_with_comma,
147                            macro_name: asm_macro.macro_name(),
148                        });
149                    } else if validated.options.contains(options) {
150                        // Tool-only output.
151                        dcx.emit_err(errors::AsmOptAlreadyprovided {
152                            span,
153                            symbol,
154                            span_with_comma,
155                        });
156                    } else {
157                        validated.options |= asm_option.options;
158                    }
159                }
160
161                validated.options_spans.push(arg.span);
162            }
163            AsmArgKind::ClobberAbi(new_abis) => {
164                allow_templates = false;
165
166                match &new_abis[..] {
167                    // This should have errored above during parsing.
168                    [] => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
169                    [(abi, _span)] => validated.clobber_abis.push((*abi, arg.span)),
170                    _ => validated.clobber_abis.extend(new_abis),
171                }
172            }
173        }
174    }
175
176    if validated.options.contains(ast::InlineAsmOptions::NOMEM)
177        && validated.options.contains(ast::InlineAsmOptions::READONLY)
178    {
179        let spans = validated.options_spans.clone();
180        dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
181    }
182    if validated.options.contains(ast::InlineAsmOptions::PURE)
183        && validated.options.contains(ast::InlineAsmOptions::NORETURN)
184    {
185        let spans = validated.options_spans.clone();
186        dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
187    }
188    if validated.options.contains(ast::InlineAsmOptions::PURE)
189        && !validated
190            .options
191            .intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
192    {
193        let spans = validated.options_spans.clone();
194        dcx.emit_err(errors::AsmPureCombine { spans });
195    }
196
197    let mut have_real_output = false;
198    let mut outputs_sp = ::alloc::vec::Vec::new()vec![];
199    let mut regclass_outputs = ::alloc::vec::Vec::new()vec![];
200    let mut labels_sp = ::alloc::vec::Vec::new()vec![];
201    for (op, op_sp) in &validated.operands {
202        match op {
203            ast::InlineAsmOperand::Out { reg, expr, .. }
204            | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
205                outputs_sp.push(*op_sp);
206                have_real_output |= expr.is_some();
207                if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
208                    regclass_outputs.push(*op_sp);
209                }
210            }
211            ast::InlineAsmOperand::InOut { reg, .. } => {
212                outputs_sp.push(*op_sp);
213                have_real_output = true;
214                if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
215                    regclass_outputs.push(*op_sp);
216                }
217            }
218            ast::InlineAsmOperand::Label { .. } => {
219                labels_sp.push(*op_sp);
220            }
221            _ => {}
222        }
223    }
224    if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
225        dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() });
226    }
227    if validated.options.contains(ast::InlineAsmOptions::NORETURN)
228        && !outputs_sp.is_empty()
229        && labels_sp.is_empty()
230    {
231        let err = dcx.create_err(errors::AsmNoReturn { outputs_sp });
232        // Bail out now since this is likely to confuse MIR
233        return Err(err);
234    }
235    if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() {
236        dcx.emit_err(errors::AsmMayUnwind { labels_sp });
237    }
238
239    if !validated.clobber_abis.is_empty() {
240        match asm_macro {
241            AsmMacro::GlobalAsm | AsmMacro::NakedAsm => {
242                let err = dcx.create_err(errors::AsmUnsupportedClobberAbi {
243                    spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
244                    macro_name: asm_macro.macro_name(),
245                });
246
247                // Bail out now since this is likely to confuse later stages
248                return Err(err);
249            }
250            AsmMacro::Asm => {
251                if !regclass_outputs.is_empty() {
252                    dcx.emit_err(errors::AsmClobberNoReg {
253                        spans: regclass_outputs,
254                        clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
255                    });
256                }
257            }
258        }
259    }
260
261    Ok(validated)
262}
263
264fn expand_preparsed_asm(
265    ecx: &mut ExtCtxt<'_>,
266    asm_macro: AsmMacro,
267    args: ValidatedAsmArgs,
268) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> {
269    let mut template = ::alloc::vec::Vec::new()vec![];
270    // Register operands are implicitly used since they are not allowed to be
271    // referenced in the template string.
272    let mut used = ::alloc::vec::from_elem(false, args.operands.len())vec![false; args.operands.len()];
273    for pos in args.reg_args.iter() {
274        used[pos] = true;
275    }
276    let named_pos: FxHashMap<usize, Symbol> =
277        args.named_args.iter().map(|(&sym, &idx)| (idx, sym)).collect();
278    let mut line_spans = Vec::with_capacity(args.templates.len());
279    let mut curarg = 0;
280
281    let mut template_strs = Vec::with_capacity(args.templates.len());
282
283    for (i, template_expr) in args.templates.into_iter().enumerate() {
284        if i != 0 {
285            template.push(ast::InlineAsmTemplatePiece::String("\n".into()));
286        }
287
288        let msg = "asm template must be a string literal";
289        let template_sp = template_expr.span;
290        let template_is_mac_call = #[allow(non_exhaustive_omitted_patterns)] match template_expr.kind {
    ast::ExprKind::MacCall(_) => true,
    _ => false,
}matches!(template_expr.kind, ast::ExprKind::MacCall(_));
291
292        // Gets the span inside `template_sp` corresponding to the given range
293        let span_in_template = |range: std::ops::Range<usize>| -> Span {
294            if template_is_mac_call {
295                // When the template is a macro call we can't reliably get inner spans
296                // so just use the entire template span (see ICEs #129503, #131292)
297                template_sp
298            } else {
299                template_sp.from_inner(InnerSpan::new(range.start, range.end))
300            }
301        };
302
303        let ExprToSpannedString {
304            symbol: template_str,
305            style: template_style,
306            span: template_span,
307            ..
308        } = {
309            let ExpandResult::Ready(mac) = expr_to_spanned_string(ecx, template_expr, msg) else {
310                return ExpandResult::Retry(());
311            };
312            match mac {
313                Ok(template_part) => template_part,
314                Err(err) => {
315                    return ExpandResult::Ready(Err(match err {
316                        Ok((err, _)) => err.emit(),
317                        Err(guar) => guar,
318                    }));
319                }
320            }
321        };
322
323        let str_style = match template_style {
324            ast::StrStyle::Cooked => None,
325            ast::StrStyle::Raw(raw) => Some(raw as usize),
326        };
327
328        let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok();
329        template_strs.push((
330            template_str,
331            template_snippet.as_deref().map(Symbol::intern),
332            template_sp,
333        ));
334        let template_str = template_str.as_str();
335
336        if let Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) = ecx.sess.asm_arch {
337            let find_span = |needle: &str| -> Span {
338                if let Some(snippet) = &template_snippet {
339                    if let Some(pos) = snippet.find(needle) {
340                        let end = pos
341                            + snippet[pos..]
342                                .find(|c| #[allow(non_exhaustive_omitted_patterns)] match c {
    '\n' | ';' | '\\' | '"' => true,
    _ => false,
}matches!(c, '\n' | ';' | '\\' | '"'))
343                                .unwrap_or(snippet[pos..].len() - 1);
344                        let inner = InnerSpan::new(pos, end);
345                        return template_sp.from_inner(inner);
346                    }
347                }
348                template_sp
349            };
350
351            if template_str.contains(".intel_syntax") {
352                ecx.psess().buffer_lint(
353                    lint::builtin::BAD_ASM_STYLE,
354                    find_span(".intel_syntax"),
355                    ecx.current_expansion.lint_node_id,
356                    errors::AvoidIntelSyntax,
357                );
358            }
359            if template_str.contains(".att_syntax") {
360                ecx.psess().buffer_lint(
361                    lint::builtin::BAD_ASM_STYLE,
362                    find_span(".att_syntax"),
363                    ecx.current_expansion.lint_node_id,
364                    errors::AvoidAttSyntax,
365                );
366            }
367        }
368
369        // Don't treat raw asm as a format string.
370        if args.options.contains(ast::InlineAsmOptions::RAW) {
371            template.push(ast::InlineAsmTemplatePiece::String(template_str.to_string().into()));
372            let template_num_lines = 1 + template_str.matches('\n').count();
373            line_spans.extend(std::iter::repeat_n(template_sp, template_num_lines));
374            continue;
375        }
376
377        let mut parser = parse::Parser::new(
378            template_str,
379            str_style,
380            template_snippet,
381            false,
382            parse::ParseMode::InlineAsm,
383        );
384        parser.curarg = curarg;
385
386        let mut unverified_pieces = Vec::new();
387        while let Some(piece) = parser.next() {
388            if !parser.errors.is_empty() {
389                break;
390            } else {
391                unverified_pieces.push(piece);
392            }
393        }
394
395        if !parser.errors.is_empty() {
396            let err = parser.errors.remove(0);
397
398            let err_sp = span_in_template(err.span);
399
400            let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("invalid asm template string: {0}",
                err.description))
    })format!("invalid asm template string: {}", err.description);
401            let mut e = ecx.dcx().struct_span_err(err_sp, msg);
402            e.span_label(err_sp, err.label + " in asm template string");
403            if let Some(note) = err.note {
404                e.note(note);
405            }
406            if let Some((label, span)) = err.secondary_label {
407                e.span_label(span_in_template(span), label);
408            }
409            let guar = e.emit();
410            return ExpandResult::Ready(Err(guar));
411        }
412
413        curarg = parser.curarg;
414
415        let mut arg_spans = parser
416            .arg_places
417            .iter()
418            .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end)));
419        for piece in unverified_pieces {
420            match piece {
421                parse::Piece::Lit(s) => {
422                    template.push(ast::InlineAsmTemplatePiece::String(s.to_string().into()))
423                }
424                parse::Piece::NextArgument(arg) => {
425                    let span = arg_spans.next().unwrap_or(template_sp);
426
427                    let operand_idx = match arg.position {
428                        parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
429                            if idx >= args.operands.len()
430                                || named_pos.contains_key(&idx)
431                                || args.reg_args.contains(idx)
432                            {
433                                let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("invalid reference to argument at index {0}",
                idx))
    })format!("invalid reference to argument at index {idx}");
434                                let mut err = ecx.dcx().struct_span_err(span, msg);
435                                err.span_label(span, "from here");
436
437                                let positional_args = args.operands.len()
438                                    - args.named_args.len()
439                                    - args.reg_args.len();
440                                let positional = if positional_args != args.operands.len() {
441                                    "positional "
442                                } else {
443                                    ""
444                                };
445                                let msg = match positional_args {
446                                    0 => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("no {0}arguments were given",
                positional))
    })format!("no {positional}arguments were given"),
447                                    1 => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("there is 1 {0}argument",
                positional))
    })format!("there is 1 {positional}argument"),
448                                    x => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("there are {0} {1}arguments", x,
                positional))
    })format!("there are {x} {positional}arguments"),
449                                };
450                                err.note(msg);
451
452                                if named_pos.contains_key(&idx) {
453                                    err.span_label(args.operands[idx].1, "named argument");
454                                    err.span_note(
455                                        args.operands[idx].1,
456                                        "named arguments cannot be referenced by position",
457                                    );
458                                } else if args.reg_args.contains(idx) {
459                                    err.span_label(
460                                        args.operands[idx].1,
461                                        "explicit register argument",
462                                    );
463                                    err.span_note(
464                                        args.operands[idx].1,
465                                        "explicit register arguments cannot be used in the asm template",
466                                    );
467                                    err.span_help(
468                                        args.operands[idx].1,
469                                        "use the register name directly in the assembly code",
470                                    );
471                                }
472                                err.emit();
473                                None
474                            } else {
475                                Some(idx)
476                            }
477                        }
478                        parse::ArgumentNamed(name) => {
479                            match args.named_args.get(&Symbol::intern(name)) {
480                                Some(&idx) => Some(idx),
481                                None => {
482                                    let span = arg.position_span;
483                                    ecx.dcx()
484                                        .create_err(errors::AsmNoMatchedArgumentName {
485                                            name: name.to_owned(),
486                                            span: span_in_template(span),
487                                        })
488                                        .emit();
489                                    None
490                                }
491                            }
492                        }
493                    };
494
495                    let mut chars = arg.format.ty.chars();
496                    let mut modifier = chars.next();
497                    if chars.next().is_some() {
498                        let span = arg.format.ty_span.map(span_in_template).unwrap_or(template_sp);
499                        ecx.dcx().emit_err(errors::AsmModifierInvalid { span });
500                        modifier = None;
501                    }
502
503                    if let Some(operand_idx) = operand_idx {
504                        used[operand_idx] = true;
505                        template.push(ast::InlineAsmTemplatePiece::Placeholder {
506                            operand_idx,
507                            modifier,
508                            span,
509                        });
510                    }
511                }
512            }
513        }
514
515        if parser.line_spans.is_empty() {
516            let template_num_lines = 1 + template_str.matches('\n').count();
517            line_spans.extend(std::iter::repeat_n(template_sp, template_num_lines));
518        } else {
519            line_spans.extend(
520                parser
521                    .line_spans
522                    .iter()
523                    .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))),
524            );
525        };
526    }
527
528    let mut unused_operands = ::alloc::vec::Vec::new()vec![];
529    let mut help_str = String::new();
530    for (idx, used) in used.into_iter().enumerate() {
531        if !used {
532            let msg = if let Some(sym) = named_pos.get(&idx) {
533                help_str.push_str(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" {{{0}}}", sym))
    })format!(" {{{}}}", sym));
534                "named argument never used"
535            } else {
536                help_str.push_str(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" {{{0}}}", idx))
    })format!(" {{{}}}", idx));
537                "argument never used"
538            };
539            unused_operands.push((args.operands[idx].1, msg));
540        }
541    }
542    match unused_operands[..] {
543        [] => {}
544        [(sp, msg)] => {
545            ecx.dcx()
546                .struct_span_err(sp, msg)
547                .with_span_label(sp, msg)
548                .with_help(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("if this argument is intentionally unused, consider using it in an asm comment: `\"/*{0} */\"`",
                help_str))
    })format!(
549                    "if this argument is intentionally unused, \
550                     consider using it in an asm comment: `\"/*{help_str} */\"`"
551                ))
552                .emit();
553        }
554        _ => {
555            let mut err = ecx.dcx().struct_span_err(
556                unused_operands.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
557                "multiple unused asm arguments",
558            );
559            for (sp, msg) in unused_operands {
560                err.span_label(sp, msg);
561            }
562            err.help(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("if these arguments are intentionally unused, consider using them in an asm comment: `\"/*{0} */\"`",
                help_str))
    })format!(
563                "if these arguments are intentionally unused, \
564                 consider using them in an asm comment: `\"/*{help_str} */\"`"
565            ));
566            err.emit();
567        }
568    }
569
570    ExpandResult::Ready(Ok(ast::InlineAsm {
571        asm_macro,
572        template,
573        template_strs: template_strs.into_boxed_slice(),
574        operands: args.operands,
575        clobber_abis: args.clobber_abis,
576        options: args.options,
577        line_spans,
578    }))
579}
580
581pub(super) fn expand_asm<'cx>(
582    ecx: &'cx mut ExtCtxt<'_>,
583    sp: Span,
584    tts: TokenStream,
585) -> MacroExpanderResult<'cx> {
586    ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::Asm) {
587        Ok(args) => {
588            let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::Asm, args) else {
589                return ExpandResult::Retry(());
590            };
591            let expr = match mac {
592                Ok(inline_asm) => Box::new(ast::Expr {
593                    id: ast::DUMMY_NODE_ID,
594                    kind: ast::ExprKind::InlineAsm(Box::new(inline_asm)),
595                    span: sp,
596                    attrs: ast::AttrVec::new(),
597                    tokens: None,
598                }),
599                Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
600            };
601            MacEager::expr(expr)
602        }
603        Err(err) => {
604            let guar = err.emit();
605            DummyResult::any(sp, guar)
606        }
607    })
608}
609
610pub(super) fn expand_naked_asm<'cx>(
611    ecx: &'cx mut ExtCtxt<'_>,
612    sp: Span,
613    tts: TokenStream,
614) -> MacroExpanderResult<'cx> {
615    ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) {
616        Ok(args) => {
617            let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args)
618            else {
619                return ExpandResult::Retry(());
620            };
621            let expr = match mac {
622                Ok(inline_asm) => Box::new(ast::Expr {
623                    id: ast::DUMMY_NODE_ID,
624                    kind: ast::ExprKind::InlineAsm(Box::new(inline_asm)),
625                    span: sp,
626                    attrs: ast::AttrVec::new(),
627                    tokens: None,
628                }),
629                Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
630            };
631            MacEager::expr(expr)
632        }
633        Err(err) => {
634            let guar = err.emit();
635            DummyResult::any(sp, guar)
636        }
637    })
638}
639
640pub(super) fn expand_global_asm<'cx>(
641    ecx: &'cx mut ExtCtxt<'_>,
642    sp: Span,
643    tts: TokenStream,
644) -> MacroExpanderResult<'cx> {
645    ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::GlobalAsm) {
646        Ok(args) => {
647            let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::GlobalAsm, args)
648            else {
649                return ExpandResult::Retry(());
650            };
651            match mac {
652                Ok(inline_asm) => MacEager::items({
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(Box::new(ast::Item {
                    attrs: ast::AttrVec::new(),
                    id: ast::DUMMY_NODE_ID,
                    kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
                    vis: ast::Visibility {
                        span: sp.shrink_to_lo(),
                        kind: ast::VisibilityKind::Inherited,
                        tokens: None,
                    },
                    span: sp,
                    tokens: None,
                }));
        vec
    } else {
        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [Box::new(ast::Item {
                                    attrs: ast::AttrVec::new(),
                                    id: ast::DUMMY_NODE_ID,
                                    kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
                                    vis: ast::Visibility {
                                        span: sp.shrink_to_lo(),
                                        kind: ast::VisibilityKind::Inherited,
                                        tokens: None,
                                    },
                                    span: sp,
                                    tokens: None,
                                })])))
    }
}smallvec![Box::new(ast::Item {
653                    attrs: ast::AttrVec::new(),
654                    id: ast::DUMMY_NODE_ID,
655                    kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
656                    vis: ast::Visibility {
657                        span: sp.shrink_to_lo(),
658                        kind: ast::VisibilityKind::Inherited,
659                        tokens: None,
660                    },
661                    span: sp,
662                    tokens: None,
663                })]),
664                Err(guar) => DummyResult::any(sp, guar),
665            }
666        }
667        Err(err) => {
668            let guar = err.emit();
669            DummyResult::any(sp, guar)
670        }
671    })
672}