1use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind};
2use rustc_attr_ir::{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;
8use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
9use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt};
10use rustc_span::{bug, span_bug, 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_span::macros::bug_impl(None,
format_args!("#[naked] functions should always terminate with an asm! block"),
Location::caller())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_span::macros::bug_impl(Some(value.span),
format_args!("expected Scalar for promoted asm const, but got {0:#?}",
const_value), Location::caller())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_span::macros::bug_impl(None,
format_args!("asm sym is not a function"), Location::caller())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_span::macros::bug_impl(None,
format_args!("invalid operand type for naked_asm!"), Location::caller())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 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 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 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 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 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 emit_fatal("naked functions may not have internal linkage")
230 }
231 Linkage::AvailableExternally => {
232 emit_fatal("Functions may not have available_externally linkage")
234 }
235 Linkage::ExternalWeak => {
236 emit_fatal("Functions may not have external weak linkage")
238 }
239 }
240
241 Ok(())
242 };
243
244 let mut begin = String::new();
245 let mut end = String::new();
246 match asm_binary_format {
247 BinaryFormat::Elf => {
248 let progbits = match is_arm {
249 true => "%progbits",
250 false => "@progbits",
251 };
252
253 let function = match is_arm {
254 true => "%function",
255 false => "@function",
256 };
257
258 if let Some(section) = &link_section {
259 begin.write_fmt(format_args!(".pushsection {0},\"ax\", {1}\n", section,
progbits))writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
260 } else if function_sections {
261 begin.write_fmt(format_args!(".pushsection .text.{0},\"ax\", {1}\n", asm_name,
progbits))writeln!(begin, ".pushsection .text.{asm_name},\"ax\", {progbits}").unwrap();
262 } else {
263 begin.write_fmt(format_args!(".text\n"))writeln!(begin, ".text").unwrap();
264 }
265 begin.write_fmt(format_args!(".balign {0}\n", align_bytes))writeln!(begin, ".balign {align_bytes}").unwrap();
266 write_linkage(&mut begin).unwrap();
267 match visibility {
268 Visibility::Default => {}
269 Visibility::Protected => begin.write_fmt(format_args!(".protected {0}\n", asm_name))writeln!(begin, ".protected {asm_name}").unwrap(),
270 Visibility::Hidden => begin.write_fmt(format_args!(".hidden {0}\n", asm_name))writeln!(begin, ".hidden {asm_name}").unwrap(),
271 }
272 begin.write_fmt(format_args!(".type {0}, {1}\n", asm_name, function))writeln!(begin, ".type {asm_name}, {function}").unwrap();
273 if !arch_prefix.is_empty() {
274 begin.write_fmt(format_args!("{0}\n", arch_prefix))writeln!(begin, "{}", arch_prefix).unwrap();
275 }
276 begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
277
278 end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
279 end.write_fmt(format_args!(".Lfunc_end_{0}:\n", asm_name))writeln!(end, ".Lfunc_end_{asm_name}:").unwrap();
282 end.write_fmt(format_args!(".size {0}, . - {0}\n", asm_name))writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap();
283 if link_section.is_some() || function_sections {
284 end.write_fmt(format_args!(".popsection\n"))writeln!(end, ".popsection").unwrap();
285 }
286 if !arch_suffix.is_empty() {
287 end.write_fmt(format_args!("{0}\n", arch_suffix))writeln!(end, "{}", arch_suffix).unwrap();
288 }
289 }
290 BinaryFormat::MachO => {
291 if let Some(section) = &link_section {
295 begin.write_fmt(format_args!(".pushsection {0},regular,pure_instructions\n",
section))writeln!(begin, ".pushsection {section},regular,pure_instructions").unwrap();
296 } else {
297 begin.write_fmt(format_args!(".section __TEXT,__text,regular,pure_instructions\n"))writeln!(begin, ".section __TEXT,__text,regular,pure_instructions").unwrap();
298 }
299 begin.write_fmt(format_args!(".balign {0}\n", align_bytes))writeln!(begin, ".balign {align_bytes}").unwrap();
300 write_linkage(&mut begin).unwrap();
301 match visibility {
302 Visibility::Default | Visibility::Protected => {}
303 Visibility::Hidden => begin.write_fmt(format_args!(".private_extern {0}\n", asm_name))writeln!(begin, ".private_extern {asm_name}").unwrap(),
304 }
305 begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
306
307 end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
308 end.write_fmt(format_args!(".Lfunc_end_{0}:\n", asm_name))writeln!(end, ".Lfunc_end_{asm_name}:").unwrap();
309 if link_section.is_some() {
310 end.write_fmt(format_args!(".popsection\n"))writeln!(end, ".popsection").unwrap();
311 }
312 if !arch_suffix.is_empty() {
313 end.write_fmt(format_args!("{0}\n", arch_suffix))writeln!(end, "{}", arch_suffix).unwrap();
314 }
315 }
316 BinaryFormat::Coff => {
317 begin.write_fmt(format_args!(".def {0}\n", asm_name))writeln!(begin, ".def {asm_name}").unwrap();
318 begin.write_fmt(format_args!(".scl 2\n"))writeln!(begin, ".scl 2").unwrap();
319 begin.write_fmt(format_args!(".type 32\n"))writeln!(begin, ".type 32").unwrap();
320 begin.write_fmt(format_args!(".endef\n"))writeln!(begin, ".endef").unwrap();
321
322 if let Some(section) = &link_section {
323 begin.write_fmt(format_args!(".section {0},\"xr\"\n", section))writeln!(begin, ".section {section},\"xr\"").unwrap()
324 } else if !function_sections {
325 begin.write_fmt(format_args!(".text\n"))writeln!(begin, ".text").unwrap();
328 } else {
329 match &tcx.sess.target.options.env {
336 Env::Gnu => {
337 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}")
338 .unwrap();
339 }
340 Env::Msvc => {
341 begin.write_fmt(format_args!(".section .text,\"xr\",one_only,{0}\n",
asm_name))writeln!(begin, ".section .text,\"xr\",one_only,{asm_name}").unwrap();
342 }
343 Env::Unspecified => match &tcx.sess.target.options.os {
344 Os::Uefi => {
345 begin.write_fmt(format_args!(".section .text,\"xr\",one_only,{0}\n",
asm_name))writeln!(begin, ".section .text,\"xr\",one_only,{asm_name}").unwrap();
346 }
347 _ => ::rustc_span::macros::bug_impl(None,
format_args!("unexpected coff target {0}", tcx.sess.target.llvm_target),
Location::caller())bug!("unexpected coff target {}", tcx.sess.target.llvm_target),
348 },
349 other => ::rustc_span::macros::bug_impl(None,
format_args!("unexpected coff env {0:?}", other), Location::caller())bug!("unexpected coff env {other:?}"),
350 }
351 }
352 write_linkage(&mut begin).unwrap();
353 begin.write_fmt(format_args!(".balign {0}\n", align_bytes))writeln!(begin, ".balign {align_bytes}").unwrap();
354 begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
355
356 end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
357 if !arch_suffix.is_empty() {
358 end.write_fmt(format_args!("{0}\n", arch_suffix))writeln!(end, "{}", arch_suffix).unwrap();
359 }
360 }
361 BinaryFormat::Wasm => {
362 let section = link_section.unwrap_or_else(|| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".text.{0}", asm_name))
})format!(".text.{asm_name}"));
363
364 begin.write_fmt(format_args!(".section {0},\"\",@\n", section))writeln!(begin, ".section {section},\"\",@").unwrap();
365 write_linkage(&mut begin).unwrap();
367 if let Visibility::Hidden = visibility {
368 begin.write_fmt(format_args!(".hidden {0}\n", asm_name))writeln!(begin, ".hidden {asm_name}").unwrap();
369 }
370 begin.write_fmt(format_args!(".type {0}, @function\n", asm_name))writeln!(begin, ".type {asm_name}, @function").unwrap();
371 if !arch_prefix.is_empty() {
372 begin.write_fmt(format_args!("{0}\n", arch_prefix))writeln!(begin, "{}", arch_prefix).unwrap();
373 }
374 begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
375 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();
376
377 end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
378 end.write_fmt(format_args!("end_function\n"))writeln!(end, "end_function").unwrap();
380 end.write_fmt(format_args!(".Lfunc_end_{0}:\n", asm_name))writeln!(end, ".Lfunc_end_{asm_name}:").unwrap();
381 }
382 BinaryFormat::Xcoff => {
383 begin.write_fmt(format_args!(".align {0}\n", align_bytes))writeln!(begin, ".align {}", align_bytes).unwrap();
398
399 write_linkage(&mut begin).unwrap();
400 if let Visibility::Hidden = visibility {
401 }
404 begin.write_fmt(format_args!("{0}:\n", asm_name))writeln!(begin, "{asm_name}:").unwrap();
405
406 end.write_fmt(format_args!("\n"))writeln!(end).unwrap();
407 }
409 }
410
411 (begin, end)
412}
413
414fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String {
418 let mut signature = String::with_capacity(64);
419
420 let ptr_type = match tcx.data_layout.pointer_size().bits() {
421 32 => "i32",
422 64 => "i64",
423 other => ::rustc_span::macros::bug_impl(None,
format_args!("wasm pointer size cannot be {0} bits", other),
Location::caller())bug!("wasm pointer size cannot be {other} bits"),
424 };
425
426 let hidden_return = #[allow(non_exhaustive_omitted_patterns)] match fn_abi.ret.mode {
PassMode::Indirect { .. } => true,
_ => false,
}matches!(fn_abi.ret.mode, PassMode::Indirect { .. });
427
428 signature.push('(');
429
430 if hidden_return {
431 signature.push_str(ptr_type);
432 if !fn_abi.args.is_empty() {
433 signature.push_str(", ");
434 }
435 }
436
437 let mut it = fn_abi.args.iter().peekable();
438 while let Some(arg_abi) = it.next() {
439 wasm_type(&mut signature, arg_abi, ptr_type);
440 if it.peek().is_some() {
441 signature.push_str(", ");
442 }
443 }
444
445 signature.push_str(") -> (");
446
447 if !hidden_return {
448 wasm_type(&mut signature, &fn_abi.ret, ptr_type);
449 }
450
451 signature.push(')');
452
453 signature
454}
455
456fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_type: &'static str) {
457 match arg_abi.mode {
458 PassMode::Ignore => { }
459 PassMode::Direct(_) => {
460 let direct_type = match arg_abi.layout.backend_repr {
461 BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type),
462 BackendRepr::SimdVector { .. } => "v128",
463 other => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("unexpected BackendRepr: {0:?}", other)));
}unreachable!("unexpected BackendRepr: {:?}", other),
464 };
465
466 signature.push_str(direct_type);
467 }
468 PassMode::Pair(_, _) => match arg_abi.layout.backend_repr {
469 BackendRepr::ScalarPair { a, b, b_offset: _ } => {
470 signature.push_str(wasm_primitive(a.primitive(), ptr_type));
471 signature.push_str(", ");
472 signature.push_str(wasm_primitive(b.primitive(), ptr_type));
473 }
474 other => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("{0:?}", other)));
}unreachable!("{other:?}"),
475 },
476 PassMode::Cast { pad_i32_count, ref cast } => {
477 {
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");
479 if !cast.prefix.is_empty() {
{ ::core::panicking::panic_fmt(format_args!("no prefix")); }
};assert!(cast.prefix.is_empty(), "no prefix");
480 {
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");
481
482 let wrapped_wasm_type = match cast.rest.unit.kind {
483 RegKind::Integer => match cast.rest.unit.size.bytes() {
484 ..=4 => "i32",
485 ..=8 => "i64",
486 _ => ptr_type,
487 },
488 RegKind::Float => match cast.rest.unit.size.bytes() {
489 ..=4 => "f32",
490 ..=8 => "f64",
491 _ => ptr_type,
492 },
493 RegKind::PpcF128 => ::rustc_span::macros::bug_impl(None, format_args!("not a valid wasm type"),
Location::caller())bug!("not a valid wasm type"),
494 RegKind::Vector { .. } => "v128",
495 };
496
497 signature.push_str(wrapped_wasm_type);
498 }
499 PassMode::Indirect { .. } => signature.push_str(ptr_type),
500 PassMode::IndirectUnsized { .. } => ::rustc_span::macros::bug_impl(None,
format_args!("unsized argument to naked asm"), Location::caller())bug!("unsized argument to naked asm"),
501 }
502}
503
504fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str {
505 match primitive {
506 Primitive::Int(integer, _) => match integer {
507 Integer::I8 | Integer::I16 | Integer::I32 => "i32",
508 Integer::I64 => "i64",
509 Integer::I128 => "i64, i64",
510 },
511 Primitive::Float(float) => match float {
512 Float::F16B => ::rustc_span::macros::bug_impl(None,
format_args!("`f16b` unsupported on wasm"), Location::caller())bug!("`f16b` unsupported on wasm"),
515 Float::F16 | Float::F32 => "f32",
516 Float::F64 => "f64",
517 Float::F128 => "i64, i64",
518 Float::PpcF128 => ::rustc_span::macros::bug_impl(None,
format_args!("`ppcf128` unsupported on wasm"), Location::caller())bug!("`ppcf128` unsupported on wasm"),
519 },
520 Primitive::Pointer(_) => ptr_type,
521 }
522}