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