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