1use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind};
2use rustc_attr_data_structures::InstructionSetAttr;
3use rustc_hir::def_id::DefId;
4use rustc_middle::mir::mono::{Linkage, MonoItemData, Visibility};
5use rustc_middle::mir::{InlineAsmOperand, START_BLOCK};
6use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
7use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt};
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::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 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 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(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(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(value.ty()),
95 );
96
97 let instance = match mono_type.kind() {
98 &ty::FnDef(def_id, args) => {
99 Instance::expect_resolve(cx.tcx(), cx.typing_env(), def_id, args, value.span)
100 }
101 _ => bug!("asm sym is not a function"),
102 };
103
104 GlobalAsmOperandRef::SymFn { instance }
105 }
106 InlineAsmOperand::SymStatic { def_id } => {
107 GlobalAsmOperandRef::SymStatic { def_id: *def_id }
108 }
109 InlineAsmOperand::In { .. }
110 | InlineAsmOperand::Out { .. }
111 | InlineAsmOperand::InOut { .. }
112 | InlineAsmOperand::Label { .. } => {
113 bug!("invalid operand type for naked_asm!")
114 }
115 }
116}
117
118fn prefix_and_suffix<'tcx>(
119 tcx: TyCtxt<'tcx>,
120 instance: Instance<'tcx>,
121 asm_name: &str,
122 item_data: MonoItemData,
123 fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
124) -> (String, String) {
125 use std::fmt::Write;
126
127 let asm_binary_format = &tcx.sess.target.binary_format;
128
129 let is_arm = tcx.sess.target.arch == "arm";
130 let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode);
131
132 let attrs = tcx.codegen_fn_attrs(instance.def_id());
133 let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
134
135 let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment;
139 let align_bytes =
140 Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4);
141
142 let (arch_prefix, arch_suffix) = if is_arm {
144 (
145 match attrs.instruction_set {
146 None => match is_thumb {
147 true => ".thumb\n.thumb_func",
148 false => ".arm",
149 },
150 Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func",
151 Some(InstructionSetAttr::ArmA32) => ".arm",
152 },
153 match is_thumb {
154 true => ".thumb",
155 false => ".arm",
156 },
157 )
158 } else {
159 ("", "")
160 };
161
162 let emit_fatal = |msg| tcx.dcx().span_fatal(tcx.def_span(instance.def_id()), msg);
163
164 let write_linkage = |w: &mut String| -> std::fmt::Result {
166 match item_data.linkage {
167 Linkage::External => {
168 writeln!(w, ".globl {asm_name}")?;
169 }
170 Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => {
171 match asm_binary_format {
172 BinaryFormat::Elf | BinaryFormat::Coff | BinaryFormat::Wasm => {
173 writeln!(w, ".weak {asm_name}")?;
174 }
175 BinaryFormat::Xcoff => {
176 emit_fatal(
179 "cannot create weak symbols from inline assembly for this target",
180 )
181 }
182 BinaryFormat::MachO => {
183 writeln!(w, ".globl {asm_name}")?;
184 writeln!(w, ".weak_definition {asm_name}")?;
185 }
186 }
187 }
188 Linkage::Internal => {
189 }
191 Linkage::Common => emit_fatal("Functions may not have common linkage"),
192 Linkage::AvailableExternally => {
193 emit_fatal("Functions may not have available_externally linkage")
195 }
196 Linkage::ExternalWeak => {
197 emit_fatal("Functions may not have external weak linkage")
199 }
200 }
201
202 Ok(())
203 };
204
205 let mut begin = String::new();
206 let mut end = String::new();
207 match asm_binary_format {
208 BinaryFormat::Elf => {
209 let section = link_section.unwrap_or(format!(".text.{asm_name}"));
210
211 let progbits = match is_arm {
212 true => "%progbits",
213 false => "@progbits",
214 };
215
216 let function = match is_arm {
217 true => "%function",
218 false => "@function",
219 };
220
221 writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
222 writeln!(begin, ".balign {align_bytes}").unwrap();
223 write_linkage(&mut begin).unwrap();
224 match item_data.visibility {
225 Visibility::Default => {}
226 Visibility::Protected => writeln!(begin, ".protected {asm_name}").unwrap(),
227 Visibility::Hidden => writeln!(begin, ".hidden {asm_name}").unwrap(),
228 }
229 writeln!(begin, ".type {asm_name}, {function}").unwrap();
230 if !arch_prefix.is_empty() {
231 writeln!(begin, "{}", arch_prefix).unwrap();
232 }
233 writeln!(begin, "{asm_name}:").unwrap();
234
235 writeln!(end).unwrap();
236 writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap();
237 writeln!(end, ".popsection").unwrap();
238 if !arch_suffix.is_empty() {
239 writeln!(end, "{}", arch_suffix).unwrap();
240 }
241 }
242 BinaryFormat::MachO => {
243 let section = link_section.unwrap_or("__TEXT,__text".to_string());
244 writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
245 writeln!(begin, ".balign {align_bytes}").unwrap();
246 write_linkage(&mut begin).unwrap();
247 match item_data.visibility {
248 Visibility::Default | Visibility::Protected => {}
249 Visibility::Hidden => writeln!(begin, ".private_extern {asm_name}").unwrap(),
250 }
251 writeln!(begin, "{asm_name}:").unwrap();
252
253 writeln!(end).unwrap();
254 writeln!(end, ".popsection").unwrap();
255 if !arch_suffix.is_empty() {
256 writeln!(end, "{}", arch_suffix).unwrap();
257 }
258 }
259 BinaryFormat::Coff => {
260 let section = link_section.unwrap_or(format!(".text.{asm_name}"));
261 writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
262 writeln!(begin, ".balign {align_bytes}").unwrap();
263 write_linkage(&mut begin).unwrap();
264 writeln!(begin, ".def {asm_name}").unwrap();
265 writeln!(begin, ".scl 2").unwrap();
266 writeln!(begin, ".type 32").unwrap();
267 writeln!(begin, ".endef").unwrap();
268 writeln!(begin, "{asm_name}:").unwrap();
269
270 writeln!(end).unwrap();
271 writeln!(end, ".popsection").unwrap();
272 if !arch_suffix.is_empty() {
273 writeln!(end, "{}", arch_suffix).unwrap();
274 }
275 }
276 BinaryFormat::Wasm => {
277 let section = link_section.unwrap_or(format!(".text.{asm_name}"));
278
279 writeln!(begin, ".section {section},\"\",@").unwrap();
280 write_linkage(&mut begin).unwrap();
282 if let Visibility::Hidden = item_data.visibility {
283 writeln!(begin, ".hidden {asm_name}").unwrap();
284 }
285 writeln!(begin, ".type {asm_name}, @function").unwrap();
286 if !arch_prefix.is_empty() {
287 writeln!(begin, "{}", arch_prefix).unwrap();
288 }
289 writeln!(begin, "{asm_name}:").unwrap();
290 writeln!(
291 begin,
292 ".functype {asm_name} {}",
293 wasm_functype(tcx, fn_abi, instance.def_id())
294 )
295 .unwrap();
296
297 writeln!(end).unwrap();
298 writeln!(end, "end_function").unwrap();
300 }
301 BinaryFormat::Xcoff => {
302 writeln!(begin, ".align {}", align_bytes).unwrap();
317
318 write_linkage(&mut begin).unwrap();
319 if let Visibility::Hidden = item_data.visibility {
320 }
323 writeln!(begin, "{asm_name}:").unwrap();
324
325 writeln!(end).unwrap();
326 }
328 }
329
330 (begin, end)
331}
332
333fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id: DefId) -> String {
337 let mut signature = String::with_capacity(64);
338
339 let ptr_type = match tcx.data_layout.pointer_size.bits() {
340 32 => "i32",
341 64 => "i64",
342 other => bug!("wasm pointer size cannot be {other} bits"),
343 };
344
345 if let PassMode::Pair { .. } = fn_abi.ret.mode {
349 let _ = WasmCAbi::Legacy { with_lint: true };
350 span_bug!(
351 tcx.def_span(def_id),
352 "cannot return a pair (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666"
353 );
354 }
355
356 let hidden_return = matches!(fn_abi.ret.mode, PassMode::Indirect { .. });
357
358 signature.push('(');
359
360 if hidden_return {
361 signature.push_str(ptr_type);
362 if !fn_abi.args.is_empty() {
363 signature.push_str(", ");
364 }
365 }
366
367 let mut it = fn_abi.args.iter().peekable();
368 while let Some(arg_abi) = it.next() {
369 wasm_type(tcx, &mut signature, arg_abi, ptr_type, def_id);
370 if it.peek().is_some() {
371 signature.push_str(", ");
372 }
373 }
374
375 signature.push_str(") -> (");
376
377 if !hidden_return {
378 wasm_type(tcx, &mut signature, &fn_abi.ret, ptr_type, def_id);
379 }
380
381 signature.push(')');
382
383 signature
384}
385
386fn wasm_type<'tcx>(
387 tcx: TyCtxt<'tcx>,
388 signature: &mut String,
389 arg_abi: &ArgAbi<'_, Ty<'tcx>>,
390 ptr_type: &'static str,
391 def_id: DefId,
392) {
393 match arg_abi.mode {
394 PassMode::Ignore => { }
395 PassMode::Direct(_) => {
396 let direct_type = match arg_abi.layout.backend_repr {
397 BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type),
398 BackendRepr::SimdVector { .. } => "v128",
399 BackendRepr::Memory { .. } => {
400 let _ = WasmCAbi::Legacy { with_lint: true };
402 span_bug!(
403 tcx.def_span(def_id),
404 "cannot use memory args (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666"
405 );
406 }
407 other => unreachable!("unexpected BackendRepr: {:?}", other),
408 };
409
410 signature.push_str(direct_type);
411 }
412 PassMode::Pair(_, _) => match arg_abi.layout.backend_repr {
413 BackendRepr::ScalarPair(a, b) => {
414 signature.push_str(wasm_primitive(a.primitive(), ptr_type));
415 signature.push_str(", ");
416 signature.push_str(wasm_primitive(b.primitive(), ptr_type));
417 }
418 other => unreachable!("{other:?}"),
419 },
420 PassMode::Cast { pad_i32, ref cast } => {
421 assert!(!pad_i32, "not currently used by wasm calling convention");
423 assert!(cast.prefix[0].is_none(), "no prefix");
424 assert_eq!(cast.rest.total, arg_abi.layout.size, "single item");
425
426 let wrapped_wasm_type = match cast.rest.unit.kind {
427 RegKind::Integer => match cast.rest.unit.size.bytes() {
428 ..=4 => "i32",
429 ..=8 => "i64",
430 _ => ptr_type,
431 },
432 RegKind::Float => match cast.rest.unit.size.bytes() {
433 ..=4 => "f32",
434 ..=8 => "f64",
435 _ => ptr_type,
436 },
437 RegKind::Vector => "v128",
438 };
439
440 signature.push_str(wrapped_wasm_type);
441 }
442 PassMode::Indirect { .. } => signature.push_str(ptr_type),
443 }
444}
445
446fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str {
447 match primitive {
448 Primitive::Int(integer, _) => match integer {
449 Integer::I8 | Integer::I16 | Integer::I32 => "i32",
450 Integer::I64 => "i64",
451 Integer::I128 => "i64, i64",
452 },
453 Primitive::Float(float) => match float {
454 Float::F16 | Float::F32 => "f32",
455 Float::F64 => "f64",
456 Float::F128 => "i64, i64",
457 },
458 Primitive::Pointer(_) => ptr_type,
459 }
460}