Skip to content

Commit 372ddc3

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 6b8141d commit 372ddc3

File tree

4 files changed

+247
-33
lines changed

4 files changed

+247
-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
@@ -112,7 +112,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
112112
sym::rustc_allocator_zeroed => {
113113
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
114114
}
115-
sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
115+
sym::naked => {
116+
// this attribute is ignored during codegen, because a function marked as naked is
117+
// turned into a global asm block.
118+
}
116119
sym::no_mangle => {
117120
if tcx.opt_item_name(did.to_def_id()).is_some() {
118121
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE
@@ -647,7 +650,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
647650
}
648651

649652
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
650-
codegen_fn_attrs.inline = InlineAttr::Never;
653+
// naked functions are generated using an extern function and global assembly. To
654+
// make sure that these can be linked together by LLVM, the linkage should be external,
655+
// unless the user explicitly configured something else (in which case any linker errors
656+
// they encounter are their responsibility).
657+
codegen_fn_attrs.linkage = codegen_fn_attrs.linkage.or(Some(Linkage::External));
651658
}
652659

653660
// 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 @@ pub 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;
@@ -170,6 +171,32 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
170171
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
171172
debug!("fn_abi: {:?}", fn_abi);
172173

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

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

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

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

0 commit comments

Comments
 (0)