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