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_lint_defs::builtin::BAD_ASM_STYLE;
9use rustc_parse::parser::asm::*;
10use rustc_parse_format as parse;
11use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym};
12use rustc_target::asm::InlineAsmArch;
13use smallvec::smallvec;
14
15use crate::diagnostics;
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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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(diagnostics::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_err(),
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(['\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                    BAD_ASM_STYLE,
355                    find_span(".intel_syntax"),
356                    ecx.current_expansion.lint_node_id,
357                    diagnostics::AvoidIntelSyntax,
358                );
359            }
360            if template_str.contains(".att_syntax") {
361                ecx.psess().buffer_lint(
362                    BAD_ASM_STYLE,
363                    find_span(".att_syntax"),
364                    ecx.current_expansion.lint_node_id,
365                    diagnostics::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_err();
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.count();
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().emit_err(diagnostics::AsmNoMatchedArgumentName {
485                                        name: name.to_owned(),
486                                        span: span_in_template(span),
487                                    });
488                                    None
489                                }
490                            }
491                        }
492                    };
493
494                    let mut chars = arg.format.ty.chars();
495                    let mut modifier = chars.next();
496                    if chars.next().is_some() {
497                        let span = arg.format.ty_span.map(span_in_template).unwrap_or(template_sp);
498                        ecx.dcx().emit_err(diagnostics::AsmModifierInvalid { span });
499                        modifier = None;
500                    }
501
502                    if let Some(operand_idx) = operand_idx {
503                        used[operand_idx] = true;
504                        template.push(ast::InlineAsmTemplatePiece::Placeholder {
505                            operand_idx,
506                            modifier,
507                            span,
508                        });
509                    }
510                }
511            }
512        }
513
514        if parser.line_spans.is_empty() {
515            let template_num_lines = 1 + template_str.matches('\n').count();
516            line_spans.extend(std::iter::repeat_n(template_sp, template_num_lines));
517        } else {
518            line_spans.extend(
519                parser
520                    .line_spans
521                    .iter()
522                    .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))),
523            );
524        };
525    }
526
527    let mut unused_operands = ::alloc::vec::Vec::new()vec![];
528    let mut help_str = String::new();
529    for (idx, used) in used.into_iter().enumerate() {
530        if !used {
531            let msg = if let Some(sym) = named_pos.get(&idx) {
532                help_str.push_str(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" {{{0}}}", sym))
    })format!(" {{{}}}", sym));
533                "named argument never used"
534            } else {
535                help_str.push_str(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" {{{0}}}", idx))
    })format!(" {{{}}}", idx));
536                "argument never used"
537            };
538            unused_operands.push((args.operands[idx].1, msg));
539        }
540    }
541    match unused_operands[..] {
542        [] => {}
543        [(sp, msg)] => {
544            ecx.dcx()
545                .struct_span_err(sp, msg)
546                .with_span_label(sp, msg)
547                .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!(
548                    "if this argument is intentionally unused, \
549                     consider using it in an asm comment: `\"/*{help_str} */\"`"
550                ))
551                .emit();
552        }
553        _ => {
554            let mut err = ecx.dcx().struct_span_err(
555                unused_operands.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
556                "multiple unused asm arguments",
557            );
558            for (sp, msg) in unused_operands {
559                err.span_label(sp, msg);
560            }
561            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!(
562                "if these arguments are intentionally unused, \
563                 consider using them in an asm comment: `\"/*{help_str} */\"`"
564            ));
565            err.emit();
566        }
567    }
568
569    ExpandResult::Ready(Ok(ast::InlineAsm {
570        asm_macro,
571        template,
572        template_strs: template_strs.into_boxed_slice(),
573        operands: args.operands,
574        clobber_abis: args.clobber_abis,
575        options: args.options,
576        line_spans,
577    }))
578}
579
580pub(super) fn expand_asm<'cx>(
581    ecx: &'cx mut ExtCtxt<'_>,
582    sp: Span,
583    tts: TokenStream,
584) -> MacroExpanderResult<'cx> {
585    ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::Asm) {
586        Ok(args) => {
587            let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::Asm, args) else {
588                return ExpandResult::Retry(());
589            };
590            let expr = match mac {
591                Ok(inline_asm) => Box::new(ast::Expr {
592                    id: ast::DUMMY_NODE_ID,
593                    kind: ast::ExprKind::InlineAsm(Box::new(inline_asm)),
594                    span: sp,
595                    attrs: ast::AttrVec::new(),
596                    tokens: None,
597                }),
598                Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
599            };
600            MacEager::expr(expr)
601        }
602        Err(err) => {
603            let guar = err.emit_err();
604            DummyResult::any(sp, guar)
605        }
606    })
607}
608
609pub(super) fn expand_naked_asm<'cx>(
610    ecx: &'cx mut ExtCtxt<'_>,
611    sp: Span,
612    tts: TokenStream,
613) -> MacroExpanderResult<'cx> {
614    ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) {
615        Ok(args) => {
616            let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args)
617            else {
618                return ExpandResult::Retry(());
619            };
620            let expr = match mac {
621                Ok(inline_asm) => Box::new(ast::Expr {
622                    id: ast::DUMMY_NODE_ID,
623                    kind: ast::ExprKind::InlineAsm(Box::new(inline_asm)),
624                    span: sp,
625                    attrs: ast::AttrVec::new(),
626                    tokens: None,
627                }),
628                Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
629            };
630            MacEager::expr(expr)
631        }
632        Err(err) => {
633            let guar = err.emit_err();
634            DummyResult::any(sp, guar)
635        }
636    })
637}
638
639pub(super) fn expand_global_asm<'cx>(
640    ecx: &'cx mut ExtCtxt<'_>,
641    sp: Span,
642    tts: TokenStream,
643) -> MacroExpanderResult<'cx> {
644    ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::GlobalAsm) {
645        Ok(args) => {
646            let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::GlobalAsm, args)
647            else {
648                return ExpandResult::Retry(());
649            };
650            match mac {
651                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,
                    },
                    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,
                                    },
                                    span: sp,
                                    tokens: None,
                                })])))
    }
}smallvec![Box::new(ast::Item {
652                    attrs: ast::AttrVec::new(),
653                    id: ast::DUMMY_NODE_ID,
654                    kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
655                    vis: ast::Visibility {
656                        span: sp.shrink_to_lo(),
657                        kind: ast::VisibilityKind::Inherited,
658                    },
659                    span: sp,
660                    tokens: None,
661                })]),
662                Err(guar) => DummyResult::any(sp, guar),
663            }
664        }
665        Err(err) => {
666            let guar = err.emit_err();
667            DummyResult::any(sp, guar)
668        }
669    })
670}