Skip to content

Commit a10debc

Browse files
committed
squashed changes to tests
correctly emit `.hidden` this test was added in rust-lang#105193 but actually NO_COVERAGE is no longer a thing in the compiler. Sadly, the link to the issue is broken, so I don't know what the problem was originally, but I don't think this is relevant any more with the global asm approach rename test file because it now specifically checks for directives only used by non-macos, non-windows x86_64 add codegen tests for 4 interesting platforms add codegen test for the `#[instruction_set]` attribute add test for `#[link_section]` use `tcx.codegen_fn_attrs` to get attribute info Fix rust-lang#124375 inline const monomorphization/evaluation getting rid of FunctionCx mark naked functions as `InstantiatedMode::GloballyShared` this makes sure that the function prototype is defined correctly, and we don't see LLVM complaining about a global value with invalid linkage monomorphize type given to `SymFn` remove hack that always emits `.globl` monomorphize type given to `Const` remove `linkage_directive` make naked functions always have external linkage mark naked functions as `#[inline(never)]` add test file for functional generics/const/impl/trait usage of naked functions
1 parent 3e3f3eb commit a10debc

File tree

14 files changed

+443
-202
lines changed

14 files changed

+443
-202
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -395,17 +395,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
395395
to_add.push(MemoryEffects::None.create_attr(cx.llcx));
396396
}
397397
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
398-
to_add.push(AttributeKind::Naked.create_attr(cx.llcx));
399-
// HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions.
400-
// And it is a module-level attribute, so the alternative is pulling naked functions into
401-
// new LLVM modules. Otherwise LLVM's "naked" functions come with endbr prefixes per
402-
// https://github.com./rust-lang/rust/issues/98768
403-
to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx));
404-
if llvm_util::get_version() < (19, 0, 0) {
405-
// Prior to LLVM 19, branch-target-enforcement was disabled by setting the attribute to
406-
// the string "false". Now it is disabled by absence of the attribute.
407-
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
408-
}
398+
// do nothing; a naked function is converted into an extern function
399+
// and a global assembly block. LLVM's support for naked functions is
400+
// not used.
409401
} else {
410402
// Do not set sanitizer attributes for naked functions.
411403
to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));

compiler/rustc_codegen_llvm/src/mono_item.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use rustc_codegen_ssa::traits::*;
22
use rustc_hir::def::DefKind;
33
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
44
use rustc_middle::bug;
5+
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
56
use rustc_middle::mir::mono::{Linkage, Visibility};
67
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf};
78
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
@@ -58,8 +59,15 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
5859

5960
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
6061
let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
61-
llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage));
6262
let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
63+
let llvm_linkage =
64+
if attrs.flags.contains(CodegenFnAttrFlags::NAKED) && linkage == Linkage::Internal {
65+
// this is effectively an extern fn, and must have external linkage
66+
llvm::Linkage::ExternalLinkage
67+
} else {
68+
base::linkage_to_llvm(linkage)
69+
};
70+
llvm::set_linkage(lldecl, llvm_linkage);
6371
base::set_link_section(lldecl, attrs);
6472
if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
6573
&& self.tcx.sess.target.supports_comdat()

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+8-12
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,7 @@ 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 => {
117-
// this attribute is ignored during codegen, because a function marked as naked is
118-
// turned into a global asm block.
119-
}
116+
sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
120117
sym::no_mangle => {
121118
if tcx.opt_item_name(did.to_def_id()).is_some() {
122119
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE
@@ -545,6 +542,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
545542
}
546543
});
547544

545+
// naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
546+
// but not for the code generation backend because at that point the naked function will just be
547+
// a declaration, with a definition provided in global assembly.
548+
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
549+
codegen_fn_attrs.inline = InlineAttr::Never;
550+
}
551+
548552
codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| {
549553
if !attr.has_name(sym::optimize) {
550554
return ia;
@@ -629,14 +633,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
629633
}
630634
}
631635

632-
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
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));
638-
}
639-
640636
// Weak lang items have the same semantics as "std internal" symbols in the
641637
// sense that they're preserved through all our LTO passes and only
642638
// strippable by the linker.

compiler/rustc_codegen_ssa/src/mir/mod.rs

+2-23
Original file line numberDiff line numberDiff line change
@@ -177,29 +177,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
177177
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
178178
debug!("fn_abi: {:?}", fn_abi);
179179

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);
180+
if cx.tcx().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
181+
crate::mir::naked_asm::codegen_naked_asm::<Bx>(cx, &mir, instance);
203182
return;
204183
}
205184

compiler/rustc_codegen_ssa/src/mir/naked_asm.rs

+109-91
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,99 @@
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};
1+
use rustc_attr::InstructionSetAttr;
2+
use rustc_middle::mir::mono::{MonoItem, MonoItemData, Visibility};
3+
use rustc_middle::mir::{Body, InlineAsmOperand};
4+
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf};
85
use rustc_middle::ty::{Instance, TyCtxt};
9-
10-
use rustc_span::sym;
6+
use rustc_middle::{bug, ty};
117
use rustc_target::asm::InlineAsmArch;
128

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-
}
9+
use crate::common;
10+
use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods};
11+
12+
pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
13+
cx: &'a Bx::CodegenCx,
14+
mir: &Body<'tcx>,
15+
instance: Instance<'tcx>,
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+
} = mir.basic_blocks.iter().next().unwrap().terminator().kind
26+
else {
27+
bug!("#[naked] functions should always terminate with an asm! block")
28+
};
4229

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-
};
30+
let operands: Vec<_> =
31+
operands.iter().map(|op| inline_to_global_operand::<Bx>(cx, instance, op)).collect();
6032

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-
}
33+
let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap();
34+
let (begin, end) = crate::mir::naked_asm::prefix_and_suffix(cx.tcx(), instance, item_data);
35+
36+
let mut template_vec = Vec::new();
37+
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into()));
38+
template_vec.extend(template.iter().cloned());
39+
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into()));
40+
41+
cx.codegen_global_asm(&template_vec, &operands, options, line_spans);
42+
}
43+
44+
fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
45+
cx: &'a Bx::CodegenCx,
46+
instance: Instance<'tcx>,
47+
op: &InlineAsmOperand<'tcx>,
48+
) -> GlobalAsmOperandRef<'tcx> {
49+
match op {
50+
InlineAsmOperand::Const { value } => {
51+
let const_value = instance
52+
.instantiate_mir_and_normalize_erasing_regions(
53+
cx.tcx(),
54+
cx.typing_env(),
55+
ty::EarlyBinder::bind(value.const_),
56+
)
57+
.eval(cx.tcx(), cx.typing_env(), value.span)
58+
.expect("erroneous constant missed by mono item collection");
59+
60+
let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
61+
cx.tcx(),
62+
cx.typing_env(),
63+
ty::EarlyBinder::bind(value.ty()),
64+
);
65+
66+
let string = common::asm_const_to_str(
67+
cx.tcx(),
68+
value.span,
69+
const_value,
70+
cx.layout_of(mono_type),
71+
);
72+
73+
GlobalAsmOperandRef::Const { string }
74+
}
75+
InlineAsmOperand::SymFn { value } => {
76+
let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
77+
cx.tcx(),
78+
cx.typing_env(),
79+
ty::EarlyBinder::bind(value.ty()),
80+
);
81+
82+
let instance = match mono_type.kind() {
83+
&ty::FnDef(def_id, args) => Instance::new(def_id, args),
84+
_ => bug!("asm sym is not a function"),
85+
};
86+
87+
GlobalAsmOperandRef::SymFn { instance }
88+
}
89+
InlineAsmOperand::SymStatic { def_id } => {
90+
GlobalAsmOperandRef::SymStatic { def_id: *def_id }
91+
}
92+
InlineAsmOperand::In { .. }
93+
| InlineAsmOperand::Out { .. }
94+
| InlineAsmOperand::InOut { .. }
95+
| InlineAsmOperand::Label { .. } => {
96+
bug!("invalid operand type for naked_asm!")
7297
}
7398
}
7499
}
@@ -91,7 +116,11 @@ impl AsmBinaryFormat {
91116
}
92117
}
93118

94-
fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (String, String) {
119+
fn prefix_and_suffix<'tcx>(
120+
tcx: TyCtxt<'tcx>,
121+
instance: Instance<'tcx>,
122+
item_data: &MonoItemData,
123+
) -> (String, String) {
95124
use std::fmt::Write;
96125

97126
let target = &tcx.sess.target;
@@ -105,24 +134,18 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri
105134

106135
let asm_name = format!("{}{}", if mangle { "_" } else { "" }, tcx.symbol_name(instance).name);
107136

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());
137+
let attrs = tcx.codegen_fn_attrs(instance.def_id());
138+
let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
115139

116140
let (arch_prefix, arch_suffix) = if is_arm {
117141
(
118-
match instruction_set {
142+
match attrs.instruction_set {
119143
None => match is_thumb {
120144
true => ".thumb\n.thumb_func",
121145
false => ".arm",
122146
},
123-
Some(sym::a32) => ".arm",
124-
Some(sym::t32) => ".thumb\n.thumb_func",
125-
Some(other) => bug!("invalid instruction set: {other}"),
147+
Some(InstructionSetAttr::ArmA32) => ".arm",
148+
Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func",
126149
},
127150
match is_thumb {
128151
true => ".thumb",
@@ -137,7 +160,7 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri
137160
let mut end = String::new();
138161
match AsmBinaryFormat::from_target(&tcx.sess.target) {
139162
AsmBinaryFormat::Elf => {
140-
let section = opt_section.unwrap_or(format!(".text.{asm_name}"));
163+
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
141164

142165
let progbits = match is_arm {
143166
true => "%progbits",
@@ -152,11 +175,10 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri
152175
writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
153176
writeln!(begin, ".balign 4").unwrap();
154177
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();
178+
if let Visibility::Hidden = item_data.visibility {
179+
writeln!(begin, ".hidden {asm_name}").unwrap();
159180
}
181+
writeln!(begin, ".type {asm_name}, {function}").unwrap();
160182
if !arch_prefix.is_empty() {
161183
writeln!(begin, "{}", arch_prefix).unwrap();
162184
}
@@ -170,13 +192,12 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri
170192
}
171193
}
172194
AsmBinaryFormat::Macho => {
173-
let section = opt_section.unwrap_or("__TEXT,__text".to_string());
195+
let section = link_section.unwrap_or("__TEXT,__text".to_string());
174196
writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
175197
writeln!(begin, ".balign 4").unwrap();
176198
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();
199+
if let Visibility::Hidden = item_data.visibility {
200+
writeln!(begin, ".private_extern {asm_name}").unwrap();
180201
}
181202
writeln!(begin, "{asm_name}:").unwrap();
182203

@@ -187,17 +208,14 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri
187208
}
188209
}
189210
AsmBinaryFormat::Coff => {
190-
let section = opt_section.unwrap_or(format!(".text.{asm_name}"));
211+
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
191212
writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
192213
writeln!(begin, ".balign 4").unwrap();
193214
writeln!(begin, ".globl {asm_name}").unwrap();
194215
writeln!(begin, ".def {asm_name}").unwrap();
195216
writeln!(begin, ".scl 2").unwrap();
196217
writeln!(begin, ".type 32").unwrap();
197218
writeln!(begin, ".endef {asm_name}").unwrap();
198-
if let Some(instruction_set) = instruction_set {
199-
writeln!(begin, "{}", instruction_set.as_str()).unwrap();
200-
}
201219
writeln!(begin, "{asm_name}:").unwrap();
202220

203221
writeln!(end).unwrap();

0 commit comments

Comments
 (0)