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 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 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 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 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 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 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 emit_fatal("Functions may not have available_externally linkage")
213 }
214 Linkage::ExternalWeak => {
215 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 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 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 begin.write_fmt(format_args!(".text\n"))writeln!(begin, ".text").unwrap();
307 } else {
308 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 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 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 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 }
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 }
388 }
389
390 (begin, end)
391}
392
393fn 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 => { }
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 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}