Skip to main content

rustc_codegen_ssa/mir/
naked_asm.rs

1use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind};
2use rustc_hir::attrs::{InstructionSetAttr, Linkage};
3use rustc_hir::def_id::LOCAL_CRATE;
4use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, Scalar};
5use rustc_middle::mir::{self, InlineAsmOperand, START_BLOCK};
6use rustc_middle::mono::{MonoItemData, Visibility};
7use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
8use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt};
9use rustc_middle::{bug, span_bug, ty};
10use rustc_span::sym;
11use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
12use rustc_target::spec::{Arch, BinaryFormat, Env, Os};
13
14use crate::common;
15use crate::mir::AsmCodegenMethods;
16use crate::traits::GlobalAsmOperandRef;
17
18pub fn codegen_naked_asm<
19    'a,
20    'tcx,
21    Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>>
22        + FnAbiOf<'tcx, FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>>
23        + AsmCodegenMethods<'tcx>,
24>(
25    cx: &'a mut Cx,
26    instance: Instance<'tcx>,
27    item_data: MonoItemData,
28) {
29    if !!instance.args.has_infer() {
    ::core::panicking::panic("assertion failed: !instance.args.has_infer()")
};assert!(!instance.args.has_infer());
30    let mir = cx.tcx().instance_mir(instance.def);
31
32    let rustc_middle::mir::TerminatorKind::InlineAsm {
33        asm_macro: _,
34        template,
35        ref operands,
36        options,
37        line_spans,
38        targets: _,
39        unwind: _,
40    } = mir.basic_blocks[START_BLOCK].terminator().kind
41    else {
42        ::rustc_middle::util::bug::bug_fmt(format_args!("#[naked] functions should always terminate with an asm! block"))bug!("#[naked] functions should always terminate with an asm! block")
43    };
44
45    let operands: Vec<_> =
46        operands.iter().map(|op| inline_to_global_operand::<Cx>(cx, instance, op)).collect();
47
48    let name = cx.mangled_name(instance);
49    let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
50    let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data, fn_abi);
51
52    let mut template_vec = Vec::new();
53    template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into()));
54    template_vec.extend(template.iter().cloned());
55    template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into()));
56
57    cx.codegen_global_asm(&template_vec, &operands, options, line_spans);
58}
59
60fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>>>(
61    cx: &'a Cx,
62    instance: Instance<'tcx>,
63    op: &InlineAsmOperand<'tcx>,
64) -> GlobalAsmOperandRef<'tcx> {
65    match op {
66        InlineAsmOperand::Const { value } => {
67            let const_value = instance
68                .instantiate_mir_and_normalize_erasing_regions(
69                    cx.tcx(),
70                    cx.typing_env(),
71                    ty::EarlyBinder::bind(cx.tcx(), value.const_),
72                )
73                .eval(cx.tcx(), cx.typing_env(), value.span)
74                .expect("erroneous constant missed by mono item collection");
75
76            let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
77                cx.tcx(),
78                cx.typing_env(),
79                ty::EarlyBinder::bind(cx.tcx(), value.ty()),
80            );
81            let mir::ConstValue::Scalar(scalar) = const_value else {
82                ::rustc_middle::util::bug::span_bug_fmt(value.span,
    format_args!("expected Scalar for promoted asm const, but got {0:#?}",
        const_value))span_bug!(
83                    value.span,
84                    "expected Scalar for promoted asm const, but got {:#?}",
85                    const_value
86                )
87            };
88
89            GlobalAsmOperandRef::Const {
90                value: common::asm_const_ptr_clean(cx.tcx(), scalar),
91                ty: mono_type,
92            }
93        }
94        InlineAsmOperand::SymFn { value } => {
95            let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
96                cx.tcx(),
97                cx.typing_env(),
98                ty::EarlyBinder::bind(cx.tcx(), value.ty()),
99            );
100
101            let instance = match mono_type.kind() {
102                &ty::FnDef(def_id, args) => Instance::expect_resolve(
103                    cx.tcx(),
104                    cx.typing_env(),
105                    def_id,
106                    args.no_bound_vars().unwrap(),
107                    value.span,
108                ),
109                _ => ::rustc_middle::util::bug::bug_fmt(format_args!("asm sym is not a function"))bug!("asm sym is not a function"),
110            };
111
112            GlobalAsmOperandRef::Const {
113                value: Scalar::from_pointer(
114                    cx.tcx().reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT).into(),
115                    cx,
116                ),
117                ty: Ty::new_fn_ptr(cx.tcx(), mono_type.fn_sig(cx.tcx())),
118            }
119        }
120        InlineAsmOperand::SymStatic { def_id } => {
121            if cx.tcx().is_thread_local_static(*def_id) {
122                GlobalAsmOperandRef::SymThreadLocalStatic { def_id: *def_id }
123            } else {
124                GlobalAsmOperandRef::Const {
125                    value: Scalar::from_pointer(
126                        cx.tcx().reserve_and_set_static_alloc(*def_id).into(),
127                        cx,
128                    ),
129                    ty: cx.tcx().static_ptr_ty(*def_id, cx.typing_env()),
130                }
131            }
132        }
133        InlineAsmOperand::In { .. }
134        | InlineAsmOperand::Out { .. }
135        | InlineAsmOperand::InOut { .. }
136        | InlineAsmOperand::Label { .. } => {
137            ::rustc_middle::util::bug::bug_fmt(format_args!("invalid operand type for naked_asm!"))bug!("invalid operand type for naked_asm!")
138        }
139    }
140}
141
142fn prefix_and_suffix<'tcx>(
143    tcx: TyCtxt<'tcx>,
144    instance: Instance<'tcx>,
145    asm_name: &str,
146    item_data: MonoItemData,
147    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
148) -> (String, String) {
149    use std::fmt::Write;
150
151    let asm_binary_format = &tcx.sess.target.binary_format;
152
153    let is_arm = tcx.sess.target.arch == Arch::Arm;
154    let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode);
155    let function_sections =
156        tcx.sess.opts.unstable_opts.function_sections.unwrap_or(tcx.sess.target.function_sections);
157
158    // If we're compiling the compiler-builtins crate, e.g., the equivalent of
159    // compiler-rt, then we want to implicitly compile everything with hidden
160    // visibility as we're going to link this object all over the place but
161    // don't want the symbols to get exported. For naked asm we set the visibility here.
162    let mut visibility = item_data.visibility;
163    if item_data.linkage != Linkage::Internal && tcx.is_compiler_builtins(LOCAL_CRATE) {
164        visibility = Visibility::Hidden;
165    }
166
167    let attrs = tcx.codegen_instance_attrs(instance.def);
168    let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
169
170    // Pick a default alignment when the alignment is not explicitly specified.
171    let align_bytes = match attrs.alignment {
172        Some(align) => align.bytes(),
173        None => match asm_binary_format {
174            BinaryFormat::Coff => 16,
175            _ => 4,
176        },
177    };
178
179    // In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`.
180    let (arch_prefix, arch_suffix) = if is_arm {
181        (
182            match attrs.instruction_set {
183                None => match is_thumb {
184                    true => ".thumb\n.thumb_func",
185                    false => ".arm",
186                },
187                Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func",
188                Some(InstructionSetAttr::ArmA32) => ".arm",
189            },
190            match is_thumb {
191                true => ".thumb",
192                false => ".arm",
193            },
194        )
195    } else {
196        ("", "")
197    };
198
199    let emit_fatal = |msg| tcx.dcx().span_fatal(tcx.def_span(instance.def_id()), msg);
200
201    // see https://godbolt.org/z/cPK4sxKor.
202    let write_linkage = |w: &mut String| -> std::fmt::Result {
203        match item_data.linkage {
204            Linkage::External => {
205                w.write_fmt(format_args!(".globl {0}\n", asm_name))writeln!(w, ".globl {asm_name}")?;
206            }
207            Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => {
208                match asm_binary_format {
209                    BinaryFormat::Elf | BinaryFormat::Coff | BinaryFormat::Wasm => {
210                        w.write_fmt(format_args!(".weak {0}\n", asm_name))writeln!(w, ".weak {asm_name}")?;
211                    }
212                    BinaryFormat::Xcoff => {
213                        // FIXME: there is currently no way of defining a weak symbol in inline assembly
214                        // for AIX. See https://github.com/llvm/llvm-project/issues/130269
215                        emit_fatal(
216                            "cannot create weak symbols from inline assembly for this target",
217                        )
218                    }
219                    BinaryFormat::MachO => {
220                        w.write_fmt(format_args!(".globl {0}\n", asm_name))writeln!(w, ".globl {asm_name}")?;
221                        w.write_fmt(format_args!(".weak_definition {0}\n", asm_name))writeln!(w, ".weak_definition {asm_name}")?;
222                    }
223                }
224            }
225            Linkage::Internal => {
226                // LTO can fail when internal linkage is used.
227                emit_fatal("naked functions may not have internal linkage")
228            }
229            Linkage::Common => emit_fatal("Functions may not have common linkage"),
230            Linkage::AvailableExternally => {
231                // this would make the function equal an extern definition
232                emit_fatal("Functions may not have available_externally linkage")
233            }
234            Linkage::ExternalWeak => {
235                // FIXME: actually this causes a SIGILL in LLVM
236                emit_fatal("Functions may not have external weak linkage")
237            }
238        }
239
240        Ok(())
241    };
242
243    let mut begin = String::new();
244    let mut end = String::new();
245    match asm_binary_format {
246        BinaryFormat::Elf => {
247            let progbits = match is_arm {
248                true => "%progbits",
249                false => "@progbits",
250            };
251
252            let function = match is_arm {
253                true => "%function",
254                false => "@function",
255            };
256
257            if let Some(section) = &link_section {
258                begin.write_fmt(format_args!(".pushsection {0},\"ax\", {1}\n", section,
        progbits))writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
259            } else if function_sections {
260                begin.write_fmt(format_args!(".pushsection .text.{0},\"ax\", {1}\n", asm_name,
        progbits))writeln!(begin, ".pushsection .text.{asm_name},\"ax\", {progbits}").unwrap();
261            } else {
262                begin.write_fmt(format_args!(".text\n"))writeln!(begin, ".text").unwrap();
263            }
264            begin.write_fmt(format_args!(".balign {0}\n", align_bytes))writeln!(begin, ".balign {align_bytes}").unwrap();
265            write_linkage(&mut begin).unwrap();
266            match visibility {
267                Visibility::Default => {}
268                Visibility::Protected => begin.write_fmt(format_args!(".protected {0}\n", asm_name))writeln!(begin, ".protected {asm_name}").unwrap(),
269                Visibility::Hidden => begin.write_fmt(format_args!(".hidden {0}\n", asm_name))writeln!(begin, ".hidden {asm_name}").unwrap(),
270            }
271            begin.write_fmt(format_args!(".type {0}, {1}\n", asm_name, function))writeln!(begin, ".type {asm_name}, {function}").unwrap();
272            if !arch_prefix.is_empty() {
273                begin.write_fmt(format_args!("{0}\n", arch_prefix))writeln!(begin, "{}", arch_prefix).unwrap();
274            }
275            begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
276
277            end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
278            // emit a label starting with `func_end` for `cargo asm` and other tooling that might
279            // pattern match on assembly generated by LLVM.
280            end.write_fmt(format_args!(".Lfunc_end_{0}:\n", asm_name))writeln!(end, ".Lfunc_end_{asm_name}:").unwrap();
281            end.write_fmt(format_args!(".size {0}, . - {0}\n", asm_name))writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap();
282            if link_section.is_some() || function_sections {
283                end.write_fmt(format_args!(".popsection\n"))writeln!(end, ".popsection").unwrap();
284            }
285            if !arch_suffix.is_empty() {
286                end.write_fmt(format_args!("{0}\n", arch_suffix))writeln!(end, "{}", arch_suffix).unwrap();
287            }
288        }
289        BinaryFormat::MachO => {
290            // NOTE: LLVM ignores `-Zfunction-sections` on macos. Instead the Mach-O symbol
291            // subsection splitting feature is used, which can be enabled with the
292            // `.subsections_via_symbols` global directive. LLVM already enables this directive.
293            if let Some(section) = &link_section {
294                begin.write_fmt(format_args!(".pushsection {0},regular,pure_instructions\n",
        section))writeln!(begin, ".pushsection {section},regular,pure_instructions").unwrap();
295            } else {
296                begin.write_fmt(format_args!(".section __TEXT,__text,regular,pure_instructions\n"))writeln!(begin, ".section __TEXT,__text,regular,pure_instructions").unwrap();
297            }
298            begin.write_fmt(format_args!(".balign {0}\n", align_bytes))writeln!(begin, ".balign {align_bytes}").unwrap();
299            write_linkage(&mut begin).unwrap();
300            match visibility {
301                Visibility::Default | Visibility::Protected => {}
302                Visibility::Hidden => begin.write_fmt(format_args!(".private_extern {0}\n", asm_name))writeln!(begin, ".private_extern {asm_name}").unwrap(),
303            }
304            begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
305
306            end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
307            end.write_fmt(format_args!(".Lfunc_end_{0}:\n", asm_name))writeln!(end, ".Lfunc_end_{asm_name}:").unwrap();
308            if link_section.is_some() {
309                end.write_fmt(format_args!(".popsection\n"))writeln!(end, ".popsection").unwrap();
310            }
311            if !arch_suffix.is_empty() {
312                end.write_fmt(format_args!("{0}\n", arch_suffix))writeln!(end, "{}", arch_suffix).unwrap();
313            }
314        }
315        BinaryFormat::Coff => {
316            begin.write_fmt(format_args!(".def {0}\n", asm_name))writeln!(begin, ".def {asm_name}").unwrap();
317            begin.write_fmt(format_args!(".scl 2\n"))writeln!(begin, ".scl 2").unwrap();
318            begin.write_fmt(format_args!(".type 32\n"))writeln!(begin, ".type 32").unwrap();
319            begin.write_fmt(format_args!(".endef\n"))writeln!(begin, ".endef").unwrap();
320
321            if let Some(section) = &link_section {
322                begin.write_fmt(format_args!(".section {0},\"xr\"\n", section))writeln!(begin, ".section {section},\"xr\"").unwrap()
323            } else if !function_sections {
324                // Function sections are enabled by default on MSVC and windows-gnullvm,
325                // but disabled by default on GNU.
326                begin.write_fmt(format_args!(".text\n"))writeln!(begin, ".text").unwrap();
327            } else {
328                // LLVM uses an extension to the section directive to support defining multiple
329                // sections with the same name and comdat. It adds `unique,<id>` at the end of the
330                // `.section` directive. We have no way of generating that unique ID here, so don't
331                // emit it.
332                //
333                // See https://llvm.org/docs/Extensions.html#id2.
334                match &tcx.sess.target.options.env {
335                    Env::Gnu => {
336                        begin.write_fmt(format_args!(".section .text${0},\"xr\",one_only,{0}\n",
        asm_name))writeln!(begin, ".section .text${asm_name},\"xr\",one_only,{asm_name}")
337                            .unwrap();
338                    }
339                    Env::Msvc => {
340                        begin.write_fmt(format_args!(".section .text,\"xr\",one_only,{0}\n",
        asm_name))writeln!(begin, ".section .text,\"xr\",one_only,{asm_name}").unwrap();
341                    }
342                    Env::Unspecified => match &tcx.sess.target.options.os {
343                        Os::Uefi => {
344                            begin.write_fmt(format_args!(".section .text,\"xr\",one_only,{0}\n",
        asm_name))writeln!(begin, ".section .text,\"xr\",one_only,{asm_name}").unwrap();
345                        }
346                        _ => ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected coff target {0}",
        tcx.sess.target.llvm_target))bug!("unexpected coff target {}", tcx.sess.target.llvm_target),
347                    },
348                    other => ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected coff env {0:?}",
        other))bug!("unexpected coff env {other:?}"),
349                }
350            }
351            write_linkage(&mut begin).unwrap();
352            begin.write_fmt(format_args!(".balign {0}\n", align_bytes))writeln!(begin, ".balign {align_bytes}").unwrap();
353            begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
354
355            end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
356            if !arch_suffix.is_empty() {
357                end.write_fmt(format_args!("{0}\n", arch_suffix))writeln!(end, "{}", arch_suffix).unwrap();
358            }
359        }
360        BinaryFormat::Wasm => {
361            let section = link_section.unwrap_or_else(|| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(".text.{0}", asm_name))
    })format!(".text.{asm_name}"));
362
363            begin.write_fmt(format_args!(".section {0},\"\",@\n", section))writeln!(begin, ".section {section},\"\",@").unwrap();
364            // wasm functions cannot be aligned, so skip
365            write_linkage(&mut begin).unwrap();
366            if let Visibility::Hidden = visibility {
367                begin.write_fmt(format_args!(".hidden {0}\n", asm_name))writeln!(begin, ".hidden {asm_name}").unwrap();
368            }
369            begin.write_fmt(format_args!(".type {0}, @function\n", asm_name))writeln!(begin, ".type {asm_name}, @function").unwrap();
370            if !arch_prefix.is_empty() {
371                begin.write_fmt(format_args!("{0}\n", arch_prefix))writeln!(begin, "{}", arch_prefix).unwrap();
372            }
373            begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
374            begin.write_fmt(format_args!(".functype {1} {0}\n",
        wasm_functype(tcx, fn_abi), asm_name))writeln!(begin, ".functype {asm_name} {}", wasm_functype(tcx, fn_abi)).unwrap();
375
376            end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
377            // .size is ignored for function symbols, so we can skip it
378            end.write_fmt(format_args!("end_function\n"))writeln!(end, "end_function").unwrap();
379            end.write_fmt(format_args!(".Lfunc_end_{0}:\n", asm_name))writeln!(end, ".Lfunc_end_{asm_name}:").unwrap();
380        }
381        BinaryFormat::Xcoff => {
382            // the LLVM XCOFFAsmParser is extremely incomplete and does not implement many of the
383            // documented directives.
384            //
385            // - https://github.com/llvm/llvm-project/blob/1b25c0c4da968fe78921ce77736e5baef4db75e3/llvm/lib/MC/MCParser/XCOFFAsmParser.cpp
386            // - https://www.ibm.com/docs/en/ssw_aix_71/assembler/assembler_pdf.pdf
387            //
388            // Consequently, we try our best here but cannot do as good a job as for other binary
389            // formats.
390
391            // FIXME: start a section. `.csect` is not currently implemented in LLVM
392
393            // fun fact: according to the assembler documentation, .align takes an exponent,
394            // but LLVM only accepts powers of 2 (but does emit the exponent)
395            // so when we hand `.align 32` to LLVM, the assembly output will contain `.align 5`
396            begin.write_fmt(format_args!(".align {0}\n", align_bytes))writeln!(begin, ".align {}", align_bytes).unwrap();
397
398            write_linkage(&mut begin).unwrap();
399            if let Visibility::Hidden = visibility {
400                // FIXME apparently `.globl {asm_name}, hidden` is valid
401                // but due to limitations with `.weak` (see above) we can't really use that in general yet
402            }
403            begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
404
405            end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
406            // FIXME: end the section?
407        }
408    }
409
410    (begin, end)
411}
412
413/// The webassembly type signature for the given function.
414///
415/// Used by the `.functype` directive on wasm targets.
416fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String {
417    let mut signature = String::with_capacity(64);
418
419    let ptr_type = match tcx.data_layout.pointer_size().bits() {
420        32 => "i32",
421        64 => "i64",
422        other => ::rustc_middle::util::bug::bug_fmt(format_args!("wasm pointer size cannot be {0} bits",
        other))bug!("wasm pointer size cannot be {other} bits"),
423    };
424
425    let hidden_return = #[allow(non_exhaustive_omitted_patterns)] match fn_abi.ret.mode {
    PassMode::Indirect { .. } => true,
    _ => false,
}matches!(fn_abi.ret.mode, PassMode::Indirect { .. });
426
427    signature.push('(');
428
429    if hidden_return {
430        signature.push_str(ptr_type);
431        if !fn_abi.args.is_empty() {
432            signature.push_str(", ");
433        }
434    }
435
436    let mut it = fn_abi.args.iter().peekable();
437    while let Some(arg_abi) = it.next() {
438        wasm_type(&mut signature, arg_abi, ptr_type);
439        if it.peek().is_some() {
440            signature.push_str(", ");
441        }
442    }
443
444    signature.push_str(") -> (");
445
446    if !hidden_return {
447        wasm_type(&mut signature, &fn_abi.ret, ptr_type);
448    }
449
450    signature.push(')');
451
452    signature
453}
454
455fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_type: &'static str) {
456    match arg_abi.mode {
457        PassMode::Ignore => { /* do nothing */ }
458        PassMode::Direct(_) => {
459            let direct_type = match arg_abi.layout.backend_repr {
460                BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type),
461                BackendRepr::SimdVector { .. } => "v128",
462                other => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("unexpected BackendRepr: {0:?}", other)));
}unreachable!("unexpected BackendRepr: {:?}", other),
463            };
464
465            signature.push_str(direct_type);
466        }
467        PassMode::Pair(_, _) => match arg_abi.layout.backend_repr {
468            BackendRepr::ScalarPair { a, b, b_offset: _ } => {
469                signature.push_str(wasm_primitive(a.primitive(), ptr_type));
470                signature.push_str(", ");
471                signature.push_str(wasm_primitive(b.primitive(), ptr_type));
472            }
473            other => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("{0:?}", other)));
}unreachable!("{other:?}"),
474        },
475        PassMode::Cast { pad_i32, ref cast } => {
476            // For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);`
477            if !!pad_i32 {
    {
        ::core::panicking::panic_fmt(format_args!("not currently used by wasm calling convention"));
    }
};assert!(!pad_i32, "not currently used by wasm calling convention");
478            if !cast.prefix.is_empty() {
    { ::core::panicking::panic_fmt(format_args!("no prefix")); }
};assert!(cast.prefix.is_empty(), "no prefix");
479            {
    match (&cast.rest.total, &arg_abi.layout.size) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("single item")));
            }
        }
    }
};assert_eq!(cast.rest.total, arg_abi.layout.size, "single item");
480
481            let wrapped_wasm_type = match cast.rest.unit.kind {
482                RegKind::Integer => match cast.rest.unit.size.bytes() {
483                    ..=4 => "i32",
484                    ..=8 => "i64",
485                    _ => ptr_type,
486                },
487                RegKind::Float => match cast.rest.unit.size.bytes() {
488                    ..=4 => "f32",
489                    ..=8 => "f64",
490                    _ => ptr_type,
491                },
492                RegKind::Vector { .. } => "v128",
493            };
494
495            signature.push_str(wrapped_wasm_type);
496        }
497        PassMode::Indirect { .. } => signature.push_str(ptr_type),
498    }
499}
500
501fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str {
502    match primitive {
503        Primitive::Int(integer, _) => match integer {
504            Integer::I8 | Integer::I16 | Integer::I32 => "i32",
505            Integer::I64 => "i64",
506            Integer::I128 => "i64, i64",
507        },
508        Primitive::Float(float) => match float {
509            Float::F16 | Float::F32 => "f32",
510            Float::F64 => "f64",
511            Float::F128 => "i64, i64",
512        },
513        Primitive::Pointer(_) => ptr_type,
514    }
515}