Skip to content

Commit b589049

Browse files
committed
squashed changes to inlining and const eval
first steps of codegen for `#[naked]` functions using `global_asm!` configure external linkage if no linkage is explicitly set create a `FunctionCx` and properly evaluate consts inline attribute is no longer relevant for naked functions the naked attribute no longer needs to be set by llvm/... we're generating global asm now, so this attribute is meaningless for the codegen backend
1 parent 8b9af81 commit b589049

File tree

4 files changed

+248
-33
lines changed

4 files changed

+248
-33
lines changed

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
113113
sym::rustc_allocator_zeroed => {
114114
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
115115
}
116-
sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
116+
sym::naked => {
117+
// this attribute is ignored during codegen, because a function marked as naked is
118+
// turned into a global asm block.
119+
}
117120
sym::no_mangle => {
118121
if tcx.opt_item_name(did.to_def_id()).is_some() {
119122
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE
@@ -627,7 +630,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
627630
}
628631

629632
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
630-
codegen_fn_attrs.inline = InlineAttr::Never;
633+
// naked functions are generated using an extern function and global assembly. To
634+
// make sure that these can be linked together by LLVM, the linkage should be external,
635+
// unless the user explicitly configured something else (in which case any linker errors
636+
// they encounter are their responsibility).
637+
codegen_fn_attrs.linkage = codegen_fn_attrs.linkage.or(Some(Linkage::External));
631638
}
632639

633640
// Weak lang items have the same semantics as "std internal" symbols in the

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod coverageinfo;
2020
pub mod debuginfo;
2121
mod intrinsic;
2222
mod locals;
23+
mod naked_asm;
2324
pub mod operand;
2425
pub mod place;
2526
mod rvalue;
@@ -176,6 +177,32 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
176177
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
177178
debug!("fn_abi: {:?}", fn_abi);
178179

180+
if cx.tcx().has_attr(instance.def.def_id(), rustc_span::sym::naked) {
181+
let cached_llbbs = IndexVec::new();
182+
183+
let fx: FunctionCx<'_, '_, Bx> = FunctionCx {
184+
instance,
185+
mir,
186+
llfn,
187+
fn_abi,
188+
cx,
189+
personality_slot: None,
190+
cached_llbbs,
191+
unreachable_block: None,
192+
terminate_block: None,
193+
cleanup_kinds: None,
194+
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
195+
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
196+
locals: locals::Locals::empty(),
197+
debug_context: None,
198+
per_local_var_debug_info: None,
199+
caller_location: None,
200+
};
201+
202+
fx.codegen_naked_asm(instance);
203+
return;
204+
}
205+
179206
let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, mir);
180207

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

tests/codegen/naked-fn/naked-noinline.rs

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)