Skip to content

Commit c602377

Browse files
committed
first steps of codegen for #[naked] functions using global_asm!
1 parent d3dd34a commit c602377

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub mod coverageinfo;
2121
pub mod debuginfo;
2222
mod intrinsic;
2323
mod locals;
24+
mod naked_asm;
2425
pub mod operand;
2526
pub mod place;
2627
mod rvalue;
@@ -166,6 +167,11 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
166167

167168
let llfn = cx.get_fn(instance);
168169

170+
if cx.tcx().has_attr(instance.def.def_id(), rustc_span::sym::naked) {
171+
crate::mir::naked_asm::codegen_naked_asm::<Bx>(cx, instance);
172+
return;
173+
}
174+
169175
let mir = cx.tcx().instance_mir(instance.def);
170176

171177
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
use crate::common;
2+
use crate::traits::{AsmMethods, BuilderMethods, GlobalAsmOperandRef};
3+
use rustc_middle::bug;
4+
use rustc_middle::mir::{Const, InlineAsmOperand};
5+
use rustc_middle::ty;
6+
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
7+
use rustc_middle::ty::{Instance, TyCtxt};
8+
9+
use rustc_span::sym;
10+
use rustc_target::asm::InlineAsmArch;
11+
12+
pub fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
13+
cx: &'a Bx::CodegenCx,
14+
instance: Instance<'tcx>,
15+
) {
16+
let mir = cx.tcx().instance_mir(instance.def);
17+
18+
let rustc_middle::mir::TerminatorKind::InlineAsm {
19+
template,
20+
ref operands,
21+
options,
22+
line_spans,
23+
targets: _,
24+
unwind: _,
25+
} = mir.basic_blocks.iter().next().unwrap().terminator().kind
26+
else {
27+
bug!("#[naked] functions should always terminate with an asm! block")
28+
};
29+
30+
let operands: Vec<_> = operands
31+
.iter()
32+
.map(|op| crate::mir::naked_asm::inline_to_global_operand::<Bx>(cx, op))
33+
.collect();
34+
35+
let (begin, end) = crate::mir::naked_asm::prefix_and_suffix(cx.tcx(), instance);
36+
37+
let mut template_vec = Vec::new();
38+
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin));
39+
template_vec.extend(template.iter().cloned());
40+
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end));
41+
42+
cx.codegen_global_asm(&template_vec, &operands, options, line_spans);
43+
}
44+
45+
enum AsmBinaryFormat {
46+
Elf,
47+
Macho,
48+
Coff,
49+
}
50+
51+
impl AsmBinaryFormat {
52+
fn from_target(target: &rustc_target::spec::Target) -> Self {
53+
if target.is_like_windows {
54+
Self::Coff
55+
} else if target.options.vendor == "apple" {
56+
Self::Macho
57+
} else {
58+
Self::Elf
59+
}
60+
}
61+
}
62+
63+
fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (String, String) {
64+
use std::fmt::Write;
65+
66+
let target = &tcx.sess.target;
67+
let target_arch = tcx.sess.asm_arch;
68+
69+
let is_arm = target.arch == "arm";
70+
let is_thumb = is_arm && target.llvm_target.contains("thumb");
71+
72+
let mangle = (target.is_like_windows && matches!(target_arch, Some(InlineAsmArch::X86)))
73+
|| target.options.vendor == "apple";
74+
75+
let asm_name = format!("{}{}", if mangle { "_" } else { "" }, tcx.symbol_name(instance).name);
76+
77+
let opt_section = tcx
78+
.get_attr(instance.def.def_id(), sym::link_section)
79+
.and_then(|attr| attr.value_str())
80+
.map(|attr| attr.as_str().to_string());
81+
82+
let instruction_set =
83+
tcx.get_attr(instance.def.def_id(), sym::instruction_set).and_then(|attr| attr.value_str());
84+
85+
let (arch_prefix, arch_suffix) = if is_arm {
86+
(
87+
match instruction_set {
88+
None => match is_thumb {
89+
true => ".thumb\n.thumb_func",
90+
false => ".arm",
91+
},
92+
Some(sym::a32) => ".arm",
93+
Some(sym::t32) => ".thumb\n.thumb_func",
94+
Some(other) => bug!("invalid instruction set: {other}"),
95+
},
96+
match is_thumb {
97+
true => ".thumb",
98+
false => ".arm",
99+
},
100+
)
101+
} else {
102+
("", "")
103+
};
104+
105+
let mut begin = String::new();
106+
let mut end = String::new();
107+
match AsmBinaryFormat::from_target(&tcx.sess.target) {
108+
AsmBinaryFormat::Elf => {
109+
let section = opt_section.unwrap_or(format!(".text.{asm_name}"));
110+
111+
let progbits = match is_arm {
112+
true => "%progbits",
113+
false => "@progbits",
114+
};
115+
116+
let function = match is_arm {
117+
true => "%function",
118+
false => "@function",
119+
};
120+
121+
writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
122+
writeln!(begin, ".balign 4").unwrap();
123+
writeln!(begin, ".globl {asm_name}").unwrap();
124+
writeln!(begin, ".hidden {asm_name}").unwrap();
125+
writeln!(begin, ".type {asm_name}, {function}").unwrap();
126+
if let Some(instruction_set) = instruction_set {
127+
writeln!(begin, "{}", instruction_set.as_str()).unwrap();
128+
}
129+
if !arch_prefix.is_empty() {
130+
writeln!(begin, "{}", arch_prefix).unwrap();
131+
}
132+
writeln!(begin, "{asm_name}:").unwrap();
133+
134+
writeln!(end).unwrap();
135+
writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap();
136+
writeln!(end, ".popsection").unwrap();
137+
if !arch_suffix.is_empty() {
138+
writeln!(end, "{}", arch_suffix).unwrap();
139+
}
140+
}
141+
AsmBinaryFormat::Macho => {
142+
let section = opt_section.unwrap_or("__TEXT,__text".to_string());
143+
writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
144+
writeln!(begin, ".balign 4").unwrap();
145+
writeln!(begin, ".globl {asm_name}").unwrap();
146+
writeln!(begin, ".private_extern {asm_name}").unwrap();
147+
if let Some(instruction_set) = instruction_set {
148+
writeln!(begin, "{}", instruction_set.as_str()).unwrap();
149+
}
150+
writeln!(begin, "{asm_name}:").unwrap();
151+
152+
writeln!(end).unwrap();
153+
writeln!(end, ".popsection").unwrap();
154+
if !arch_suffix.is_empty() {
155+
writeln!(end, "{}", arch_suffix).unwrap();
156+
}
157+
}
158+
AsmBinaryFormat::Coff => {
159+
let section = opt_section.unwrap_or(format!(".text.{asm_name}"));
160+
writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
161+
writeln!(begin, ".balign 4").unwrap();
162+
writeln!(begin, ".globl {asm_name}").unwrap();
163+
writeln!(begin, ".def {asm_name}").unwrap();
164+
writeln!(begin, ".scl 2").unwrap();
165+
writeln!(begin, ".type 32").unwrap();
166+
writeln!(begin, ".endef {asm_name}").unwrap();
167+
if let Some(instruction_set) = instruction_set {
168+
writeln!(begin, "{}", instruction_set.as_str()).unwrap();
169+
}
170+
writeln!(begin, "{asm_name}:").unwrap();
171+
172+
writeln!(end).unwrap();
173+
writeln!(end, ".popsection").unwrap();
174+
if !arch_suffix.is_empty() {
175+
writeln!(end, "{}", arch_suffix).unwrap();
176+
}
177+
}
178+
}
179+
180+
(begin, end)
181+
}
182+
183+
fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
184+
cx: &'a Bx::CodegenCx,
185+
op: &InlineAsmOperand<'tcx>,
186+
) -> GlobalAsmOperandRef<'tcx> {
187+
match op {
188+
InlineAsmOperand::Const { value } => match value.const_ {
189+
Const::Ty(_ty, _const_) => todo!(),
190+
Const::Unevaluated(_unevaluated_const, _ty) => todo!(),
191+
Const::Val(const_value, ty) => {
192+
let string = common::asm_const_to_str(
193+
cx.tcx(),
194+
value.span,
195+
const_value.clone(),
196+
cx.layout_of(ty),
197+
);
198+
GlobalAsmOperandRef::Const { string }
199+
}
200+
},
201+
InlineAsmOperand::SymFn { value } => {
202+
let instance = match value.const_ {
203+
// &ty::FnDef(def_id, args) => Instance::new(def_id, args),
204+
// _ => bug!("asm sym is not a function"),
205+
Const::Ty(_ty, _const_) => todo!(),
206+
Const::Unevaluated(_unevaluated_const, _ty) => todo!(),
207+
Const::Val(_, ty) => match ty.kind() {
208+
ty::FnDef(def_id, args) => Instance::new(*def_id, *args),
209+
_ => bug!("asm sym is not a function"),
210+
},
211+
};
212+
213+
GlobalAsmOperandRef::SymFn { instance }
214+
}
215+
InlineAsmOperand::SymStatic { def_id } => {
216+
GlobalAsmOperandRef::SymStatic { def_id: *def_id }
217+
}
218+
InlineAsmOperand::In { .. }
219+
| InlineAsmOperand::Out { .. }
220+
| InlineAsmOperand::InOut { .. }
221+
| InlineAsmOperand::Label { .. } => {
222+
bug!("invalid operand type for naked_asm!")
223+
}
224+
}
225+
}

0 commit comments

Comments
 (0)