Skip to main content

rustc_builtin_macros/
asm.rs

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