1use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind};
2use rustc_attr_parsing::InstructionSetAttr;
3use rustc_hir::def_id::DefId;
4use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility};
5use rustc_middle::mir::{Body, InlineAsmOperand};
6use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf};
7use rustc_middle::ty::{Instance, Ty, TyCtxt};
8use rustc_middle::{bug, span_bug, ty};
9use rustc_span::sym;
10use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
11use rustc_target::spec::{BinaryFormat, WasmCAbi};
12
13use crate::common;
14use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods};
15
16pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
17 cx: &'a Bx::CodegenCx,
18 mir: &Body<'tcx>,
19 instance: Instance<'tcx>,
20) {
21 let rustc_middle::mir::TerminatorKind::InlineAsm {
22 asm_macro: _,
23 template,
24 ref operands,
25 options,
26 line_spans,
27 targets: _,
28 unwind: _,
29 } = mir.basic_blocks.iter().next().unwrap().terminator().kind
30 else {
31 bug!("#[naked] functions should always terminate with an asm! block")
32 };
33
34 let operands: Vec<_> =
35 operands.iter().map(|op| inline_to_global_operand::<Bx>(cx, instance, op)).collect();
36
37 let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap();
38 let name = cx.mangled_name(instance);
39 let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
40 let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data, fn_abi);
41
42 let mut template_vec = Vec::new();
43 template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into()));
44 template_vec.extend(template.iter().cloned());
45 template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into()));
46
47 cx.codegen_global_asm(&template_vec, &operands, options, line_spans);
48}
49
50fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
51 cx: &'a Bx::CodegenCx,
52 instance: Instance<'tcx>,
53 op: &InlineAsmOperand<'tcx>,
54) -> GlobalAsmOperandRef<'tcx> {
55 match op {
56 InlineAsmOperand::Const { value } => {
57 let const_value = instance
58 .instantiate_mir_and_normalize_erasing_regions(
59 cx.tcx(),
60 cx.typing_env(),
61 ty::EarlyBinder::bind(value.const_),
62 )
63 .eval(cx.tcx(), cx.typing_env(), value.span)
64 .expect("erroneous constant missed by mono item collection");
65
66 let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
67 cx.tcx(),
68 cx.typing_env(),
69 ty::EarlyBinder::bind(value.ty()),
70 );
71
72 let string = common::asm_const_to_str(
73 cx.tcx(),
74 value.span,
75 const_value,
76 cx.layout_of(mono_type),
77 );
78
79 GlobalAsmOperandRef::Const { string }
80 }
81 InlineAsmOperand::SymFn { value } => {
82 let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
83 cx.tcx(),
84 cx.typing_env(),
85 ty::EarlyBinder::bind(value.ty()),
86 );
87
88 let instance = match mono_type.kind() {
89 &ty::FnDef(def_id, args) => Instance::new(def_id, args),
90 _ => bug!("asm sym is not a function"),
91 };
92
93 GlobalAsmOperandRef::SymFn { instance }
94 }
95 InlineAsmOperand::SymStatic { def_id } => {
96 GlobalAsmOperandRef::SymStatic { def_id: *def_id }
97 }
98 InlineAsmOperand::In { .. }
99 | InlineAsmOperand::Out { .. }
100 | InlineAsmOperand::InOut { .. }
101 | InlineAsmOperand::Label { .. } => {
102 bug!("invalid operand type for naked_asm!")
103 }
104 }
105}
106
107fn prefix_and_suffix<'tcx>(
108 tcx: TyCtxt<'tcx>,
109 instance: Instance<'tcx>,
110 asm_name: &str,
111 item_data: &MonoItemData,
112 fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
113) -> (String, String) {
114 use std::fmt::Write;
115
116 let asm_binary_format = &tcx.sess.target.binary_format;
117
118 let is_arm = tcx.sess.target.arch == "arm";
119 let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode);
120
121 let attrs = tcx.codegen_fn_attrs(instance.def_id());
122 let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
123
124 let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment;
128 let align_bytes =
129 Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4);
130
131 let (arch_prefix, arch_suffix) = if is_arm {
133 (
134 match attrs.instruction_set {
135 None => match is_thumb {
136 true => ".thumb\n.thumb_func",
137 false => ".arm",
138 },
139 Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func",
140 Some(InstructionSetAttr::ArmA32) => ".arm",
141 },
142 match is_thumb {
143 true => ".thumb",
144 false => ".arm",
145 },
146 )
147 } else {
148 ("", "")
149 };
150
151 let emit_fatal = |msg| tcx.dcx().span_fatal(tcx.def_span(instance.def_id()), msg);
152
153 let write_linkage = |w: &mut String| -> std::fmt::Result {
155 match item_data.linkage {
156 Linkage::External => {
157 writeln!(w, ".globl {asm_name}")?;
158 }
159 Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => {
160 match asm_binary_format {
161 BinaryFormat::Elf | BinaryFormat::Coff | BinaryFormat::Wasm => {
162 writeln!(w, ".weak {asm_name}")?;
163 }
164 BinaryFormat::Xcoff => {
165 emit_fatal(
168 "cannot create weak symbols from inline assembly for this target",
169 )
170 }
171 BinaryFormat::MachO => {
172 writeln!(w, ".globl {asm_name}")?;
173 writeln!(w, ".weak_definition {asm_name}")?;
174 }
175 }
176 }
177 Linkage::Internal => {
178 }
180 Linkage::Common => emit_fatal("Functions may not have common linkage"),
181 Linkage::AvailableExternally => {
182 emit_fatal("Functions may not have available_externally linkage")
184 }
185 Linkage::ExternalWeak => {
186 emit_fatal("Functions may not have external weak linkage")
188 }
189 }
190
191 Ok(())
192 };
193
194 let mut begin = String::new();
195 let mut end = String::new();
196 match asm_binary_format {
197 BinaryFormat::Elf => {
198 let section = link_section.unwrap_or(format!(".text.{asm_name}"));
199
200 let progbits = match is_arm {
201 true => "%progbits",
202 false => "@progbits",
203 };
204
205 let function = match is_arm {
206 true => "%function",
207 false => "@function",
208 };
209
210 writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
211 writeln!(begin, ".balign {align_bytes}").unwrap();
212 write_linkage(&mut begin).unwrap();
213 if let Visibility::Hidden = item_data.visibility {
214 writeln!(begin, ".hidden {asm_name}").unwrap();
215 }
216 writeln!(begin, ".type {asm_name}, {function}").unwrap();
217 if !arch_prefix.is_empty() {
218 writeln!(begin, "{}", arch_prefix).unwrap();
219 }
220 writeln!(begin, "{asm_name}:").unwrap();
221
222 writeln!(end).unwrap();
223 writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap();
224 writeln!(end, ".popsection").unwrap();
225 if !arch_suffix.is_empty() {
226 writeln!(end, "{}", arch_suffix).unwrap();
227 }
228 }
229 BinaryFormat::MachO => {
230 let section = link_section.unwrap_or("__TEXT,__text".to_string());
231 writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
232 writeln!(begin, ".balign {align_bytes}").unwrap();
233 write_linkage(&mut begin).unwrap();
234 if let Visibility::Hidden = item_data.visibility {
235 writeln!(begin, ".private_extern {asm_name}").unwrap();
236 }
237 writeln!(begin, "{asm_name}:").unwrap();
238
239 writeln!(end).unwrap();
240 writeln!(end, ".popsection").unwrap();
241 if !arch_suffix.is_empty() {
242 writeln!(end, "{}", arch_suffix).unwrap();
243 }
244 }
245 BinaryFormat::Coff => {
246 let section = link_section.unwrap_or(format!(".text.{asm_name}"));
247 writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
248 writeln!(begin, ".balign {align_bytes}").unwrap();
249 write_linkage(&mut begin).unwrap();
250 writeln!(begin, ".def {asm_name}").unwrap();
251 writeln!(begin, ".scl 2").unwrap();
252 writeln!(begin, ".type 32").unwrap();
253 writeln!(begin, ".endef").unwrap();
254 writeln!(begin, "{asm_name}:").unwrap();
255
256 writeln!(end).unwrap();
257 writeln!(end, ".popsection").unwrap();
258 if !arch_suffix.is_empty() {
259 writeln!(end, "{}", arch_suffix).unwrap();
260 }
261 }
262 BinaryFormat::Wasm => {
263 let section = link_section.unwrap_or(format!(".text.{asm_name}"));
264
265 writeln!(begin, ".section {section},\"\",@").unwrap();
266 write_linkage(&mut begin).unwrap();
268 if let Visibility::Hidden = item_data.visibility {
269 writeln!(begin, ".hidden {asm_name}").unwrap();
270 }
271 writeln!(begin, ".type {asm_name}, @function").unwrap();
272 if !arch_prefix.is_empty() {
273 writeln!(begin, "{}", arch_prefix).unwrap();
274 }
275 writeln!(begin, "{asm_name}:").unwrap();
276 writeln!(
277 begin,
278 ".functype {asm_name} {}",
279 wasm_functype(tcx, fn_abi, instance.def_id())
280 )
281 .unwrap();
282
283 writeln!(end).unwrap();
284 writeln!(end, "end_function").unwrap();
286 }
287 BinaryFormat::Xcoff => {
288 writeln!(begin, ".align {}", align_bytes).unwrap();
303
304 write_linkage(&mut begin).unwrap();
305 if let Visibility::Hidden = item_data.visibility {
306 }
309 writeln!(begin, "{asm_name}:").unwrap();
310
311 writeln!(end).unwrap();
312 }
314 }
315
316 (begin, end)
317}
318
319fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id: DefId) -> String {
323 let mut signature = String::with_capacity(64);
324
325 let ptr_type = match tcx.data_layout.pointer_size.bits() {
326 32 => "i32",
327 64 => "i64",
328 other => bug!("wasm pointer size cannot be {other} bits"),
329 };
330
331 if let PassMode::Pair { .. } = fn_abi.ret.mode {
335 let _ = WasmCAbi::Legacy { with_lint: true };
336 span_bug!(
337 tcx.def_span(def_id),
338 "cannot return a pair (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666"
339 );
340 }
341
342 let hidden_return = matches!(fn_abi.ret.mode, PassMode::Indirect { .. });
343
344 signature.push('(');
345
346 if hidden_return {
347 signature.push_str(ptr_type);
348 if !fn_abi.args.is_empty() {
349 signature.push_str(", ");
350 }
351 }
352
353 let mut it = fn_abi.args.iter().peekable();
354 while let Some(arg_abi) = it.next() {
355 wasm_type(tcx, &mut signature, arg_abi, ptr_type, def_id);
356 if it.peek().is_some() {
357 signature.push_str(", ");
358 }
359 }
360
361 signature.push_str(") -> (");
362
363 if !hidden_return {
364 wasm_type(tcx, &mut signature, &fn_abi.ret, ptr_type, def_id);
365 }
366
367 signature.push(')');
368
369 signature
370}
371
372fn wasm_type<'tcx>(
373 tcx: TyCtxt<'tcx>,
374 signature: &mut String,
375 arg_abi: &ArgAbi<'_, Ty<'tcx>>,
376 ptr_type: &'static str,
377 def_id: DefId,
378) {
379 match arg_abi.mode {
380 PassMode::Ignore => { }
381 PassMode::Direct(_) => {
382 let direct_type = match arg_abi.layout.backend_repr {
383 BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type),
384 BackendRepr::SimdVector { .. } => "v128",
385 BackendRepr::Memory { .. } => {
386 let _ = WasmCAbi::Legacy { with_lint: true };
388 span_bug!(
389 tcx.def_span(def_id),
390 "cannot use memory args (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666"
391 );
392 }
393 other => unreachable!("unexpected BackendRepr: {:?}", other),
394 };
395
396 signature.push_str(direct_type);
397 }
398 PassMode::Pair(_, _) => match arg_abi.layout.backend_repr {
399 BackendRepr::ScalarPair(a, b) => {
400 signature.push_str(wasm_primitive(a.primitive(), ptr_type));
401 signature.push_str(", ");
402 signature.push_str(wasm_primitive(b.primitive(), ptr_type));
403 }
404 other => unreachable!("{other:?}"),
405 },
406 PassMode::Cast { pad_i32, ref cast } => {
407 assert!(!pad_i32, "not currently used by wasm calling convention");
409 assert!(cast.prefix[0].is_none(), "no prefix");
410 assert_eq!(cast.rest.total, arg_abi.layout.size, "single item");
411
412 let wrapped_wasm_type = match cast.rest.unit.kind {
413 RegKind::Integer => match cast.rest.unit.size.bytes() {
414 ..=4 => "i32",
415 ..=8 => "i64",
416 _ => ptr_type,
417 },
418 RegKind::Float => match cast.rest.unit.size.bytes() {
419 ..=4 => "f32",
420 ..=8 => "f64",
421 _ => ptr_type,
422 },
423 RegKind::Vector => "v128",
424 };
425
426 signature.push_str(wrapped_wasm_type);
427 }
428 PassMode::Indirect { .. } => signature.push_str(ptr_type),
429 }
430}
431
432fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str {
433 match primitive {
434 Primitive::Int(integer, _) => match integer {
435 Integer::I8 | Integer::I16 | Integer::I32 => "i32",
436 Integer::I64 => "i64",
437 Integer::I128 => "i64, i64",
438 },
439 Primitive::Float(float) => match float {
440 Float::F16 | Float::F32 => "f32",
441 Float::F64 => "f64",
442 Float::F128 => "i64, i64",
443 },
444 Primitive::Pointer(_) => ptr_type,
445 }
446}