Skip to content

Commit

Permalink
squashed changes to tests
Browse files Browse the repository at this point in the history
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
  • Loading branch information
folkertdev committed Oct 7, 2024
1 parent 7ebfe9e commit 20012cc
Show file tree
Hide file tree
Showing 11 changed files with 417 additions and 181 deletions.
21 changes: 11 additions & 10 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
}
sym::naked => {
// this attribute is ignored during codegen, because a function marked as naked is
// turned into a global asm block.
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;

// naked functions are generated as an extern function, and a global asm block that
// contains the naked function's body. In order to link these together, the linkage
// of the extern function must be external.
codegen_fn_attrs.linkage = Some(Linkage::External);
}
sym::no_mangle => {
if tcx.opt_item_name(did.to_def_id()).is_some() {
Expand Down Expand Up @@ -544,6 +548,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
});

// naked function MUST NOT be inlined!
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
codegen_fn_attrs.inline = InlineAttr::Never;
}

codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| {
if !attr.has_name(sym::optimize) {
return ia;
Expand Down Expand Up @@ -628,14 +637,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
}

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

// Weak lang items have the same semantics as "std internal" symbols in the
// sense that they're preserved through all our LTO passes and only
// strippable by the linker.
Expand Down
25 changes: 2 additions & 23 deletions compiler/rustc_codegen_ssa/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,29 +173,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
debug!("fn_abi: {:?}", fn_abi);

if cx.tcx().has_attr(instance.def.def_id(), rustc_span::sym::naked) {
let cached_llbbs = IndexVec::new();

let fx: FunctionCx<'_, '_, Bx> = FunctionCx {
instance,
mir,
llfn,
fn_abi,
cx,
personality_slot: None,
cached_llbbs,
unreachable_block: None,
terminate_block: None,
cleanup_kinds: None,
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
locals: locals::Locals::empty(),
debug_context: None,
per_local_var_debug_info: None,
caller_location: None,
};

fx.codegen_naked_asm(instance);
if cx.tcx().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
crate::mir::naked_asm::codegen_naked_asm::<Bx>(cx, &mir, instance);
return;
}

Expand Down
191 changes: 105 additions & 86 deletions compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
Original file line number Diff line number Diff line change
@@ -1,74 +1,100 @@
use crate::common;
use crate::mir::FunctionCx;
use crate::traits::{AsmMethods, BuilderMethods, GlobalAsmOperandRef};
use crate::traits::{AsmMethods, BuilderMethods, GlobalAsmOperandRef, MiscMethods};
use rustc_attr::InstructionSetAttr;
use rustc_middle::bug;
use rustc_middle::mir::mono::{MonoItem, MonoItemData, Visibility};
use rustc_middle::mir::Body;
use rustc_middle::mir::InlineAsmOperand;
use rustc_middle::ty;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_middle::ty::{Instance, TyCtxt};

use rustc_span::sym;
use rustc_target::asm::InlineAsmArch;

impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
pub fn codegen_naked_asm(&self, instance: Instance<'tcx>) {
let cx = &self.cx;

let rustc_middle::mir::TerminatorKind::InlineAsm {
asm_macro: _,
template,
ref operands,
options,
line_spans,
targets: _,
unwind: _,
} = self.mir.basic_blocks.iter().next().unwrap().terminator().kind
else {
bug!("#[naked] functions should always terminate with an asm! block")
};

let operands: Vec<_> =
operands.iter().map(|op| self.inline_to_global_operand(op)).collect();

let (begin, end) = crate::mir::naked_asm::prefix_and_suffix(cx.tcx(), instance);

let mut template_vec = Vec::new();
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin));
template_vec.extend(template.iter().cloned());
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end));

cx.codegen_global_asm(&template_vec, &operands, options, line_spans);
}
pub fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cx: &'a Bx::CodegenCx,
mir: &Body<'tcx>,
instance: Instance<'tcx>,
) {
let rustc_middle::mir::TerminatorKind::InlineAsm {
asm_macro: _,
template,
ref operands,
options,
line_spans,
targets: _,
unwind: _,
} = mir.basic_blocks.iter().next().unwrap().terminator().kind
else {
bug!("#[naked] functions should always terminate with an asm! block")
};

fn inline_to_global_operand(&self, op: &InlineAsmOperand<'tcx>) -> GlobalAsmOperandRef<'tcx> {
match op {
InlineAsmOperand::Const { value } => {
let const_value = self.eval_mir_constant(value);
let string = common::asm_const_to_str(
self.cx.tcx(),
value.span,
const_value,
self.cx.layout_of(value.ty()),
);
GlobalAsmOperandRef::Const { string }
}
InlineAsmOperand::SymFn { value } => {
let instance = match value.ty().kind() {
&ty::FnDef(def_id, args) => Instance::new(def_id, args),
_ => bug!("asm sym is not a function"),
};
let operands: Vec<_> =
operands.iter().map(|op| inline_to_global_operand::<Bx>(cx, instance, op)).collect();

GlobalAsmOperandRef::SymFn { instance }
}
InlineAsmOperand::SymStatic { def_id } => {
GlobalAsmOperandRef::SymStatic { def_id: *def_id }
}
InlineAsmOperand::In { .. }
| InlineAsmOperand::Out { .. }
| InlineAsmOperand::InOut { .. }
| InlineAsmOperand::Label { .. } => {
bug!("invalid operand type for naked_asm!")
}
let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap();
let (begin, end) = crate::mir::naked_asm::prefix_and_suffix(cx.tcx(), instance, item_data);

let mut template_vec = Vec::new();
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into()));
template_vec.extend(template.iter().cloned());
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into()));

cx.codegen_global_asm(&template_vec, &operands, options, line_spans);
}

fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cx: &'a Bx::CodegenCx,
instance: Instance<'tcx>,
op: &InlineAsmOperand<'tcx>,
) -> GlobalAsmOperandRef<'tcx> {
match op {
InlineAsmOperand::Const { value } => {
let const_value = instance
.instantiate_mir_and_normalize_erasing_regions(
cx.tcx(),
ty::ParamEnv::reveal_all(),
ty::EarlyBinder::bind(value.const_),
)
.eval(cx.tcx(), ty::ParamEnv::reveal_all(), value.span)
.expect("erroneous constant missed by mono item collection");

let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
cx.tcx(),
ty::ParamEnv::reveal_all(),
ty::EarlyBinder::bind(value.ty()),
);

let string = common::asm_const_to_str(
cx.tcx(),
value.span,
const_value,
cx.layout_of(mono_type),
);

GlobalAsmOperandRef::Const { string }
}
InlineAsmOperand::SymFn { value } => {
let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
cx.tcx(),
ty::ParamEnv::reveal_all(),
ty::EarlyBinder::bind(value.ty()),
);

let instance = match mono_type.kind() {
&ty::FnDef(def_id, args) => Instance::new(def_id, args),
_ => bug!("asm sym is not a function"),
};

GlobalAsmOperandRef::SymFn { instance }
}
InlineAsmOperand::SymStatic { def_id } => {
GlobalAsmOperandRef::SymStatic { def_id: *def_id }
}
InlineAsmOperand::In { .. }
| InlineAsmOperand::Out { .. }
| InlineAsmOperand::InOut { .. }
| InlineAsmOperand::Label { .. } => {
bug!("invalid operand type for naked_asm!")
}
}
}
Expand All @@ -91,7 +117,11 @@ impl AsmBinaryFormat {
}
}

fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (String, String) {
fn prefix_and_suffix<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
item_data: &MonoItemData,
) -> (String, String) {
use std::fmt::Write;

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

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

let opt_section = tcx
.get_attr(instance.def.def_id(), sym::link_section)
.and_then(|attr| attr.value_str())
.map(|attr| attr.as_str().to_string());

let instruction_set =
tcx.get_attr(instance.def.def_id(), sym::instruction_set).and_then(|attr| attr.value_str());
let attrs = tcx.codegen_fn_attrs(instance.def_id());
let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());

let (arch_prefix, arch_suffix) = if is_arm {
(
match instruction_set {
match attrs.instruction_set {
None => match is_thumb {
true => ".thumb\n.thumb_func",
false => ".arm",
},
Some(sym::a32) => ".arm",
Some(sym::t32) => ".thumb\n.thumb_func",
Some(other) => bug!("invalid instruction set: {other}"),
Some(InstructionSetAttr::ArmA32) => ".arm",
Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func",
},
match is_thumb {
true => ".thumb",
Expand All @@ -137,7 +161,7 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri
let mut end = String::new();
match AsmBinaryFormat::from_target(&tcx.sess.target) {
AsmBinaryFormat::Elf => {
let section = opt_section.unwrap_or(format!(".text.{asm_name}"));
let section = link_section.unwrap_or(format!(".text.{asm_name}"));

let progbits = match is_arm {
true => "%progbits",
Expand All @@ -152,11 +176,10 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri
writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
writeln!(begin, ".balign 4").unwrap();
writeln!(begin, ".globl {asm_name}").unwrap();
writeln!(begin, ".hidden {asm_name}").unwrap();
writeln!(begin, ".type {asm_name}, {function}").unwrap();
if let Some(instruction_set) = instruction_set {
writeln!(begin, "{}", instruction_set.as_str()).unwrap();
if let Visibility::Hidden = item_data.visibility {
writeln!(begin, ".hidden {asm_name}").unwrap();
}
writeln!(begin, ".type {asm_name}, {function}").unwrap();
if !arch_prefix.is_empty() {
writeln!(begin, "{}", arch_prefix).unwrap();
}
Expand All @@ -170,13 +193,12 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri
}
}
AsmBinaryFormat::Macho => {
let section = opt_section.unwrap_or("__TEXT,__text".to_string());
let section = link_section.unwrap_or("__TEXT,__text".to_string());
writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
writeln!(begin, ".balign 4").unwrap();
writeln!(begin, ".globl {asm_name}").unwrap();
writeln!(begin, ".private_extern {asm_name}").unwrap();
if let Some(instruction_set) = instruction_set {
writeln!(begin, "{}", instruction_set.as_str()).unwrap();
if let Visibility::Hidden = item_data.visibility {
writeln!(begin, ".private_extern {asm_name}").unwrap();
}
writeln!(begin, "{asm_name}:").unwrap();

Expand All @@ -187,17 +209,14 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri
}
}
AsmBinaryFormat::Coff => {
let section = opt_section.unwrap_or(format!(".text.{asm_name}"));
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
writeln!(begin, ".balign 4").unwrap();
writeln!(begin, ".globl {asm_name}").unwrap();
writeln!(begin, ".def {asm_name}").unwrap();
writeln!(begin, ".scl 2").unwrap();
writeln!(begin, ".type 32").unwrap();
writeln!(begin, ".endef {asm_name}").unwrap();
if let Some(instruction_set) = instruction_set {
writeln!(begin, "{}", instruction_set.as_str()).unwrap();
}
writeln!(begin, "{asm_name}:").unwrap();

writeln!(end).unwrap();
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_middle/src/mir/mono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use rustc_target::spec::SymbolVisibility;
use tracing::debug;

use crate::dep_graph::{DepNode, WorkProduct, WorkProductId};
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::ty::{GenericArgs, Instance, InstanceKind, SymbolName, TyCtxt};

/// Describes how a monomorphization will be instantiated in object files.
Expand Down Expand Up @@ -119,7 +120,9 @@ impl<'tcx> MonoItem<'tcx> {
let entry_def_id = tcx.entry_fn(()).map(|(id, _)| id);
// If this function isn't inlined or otherwise has an extern
// indicator, then we'll be creating a globally shared version.
if tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator()
let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
if codegen_fn_attrs.contains_extern_indicator()
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED)
|| !instance.def.generates_cgu_internal_copy(tcx)
|| Some(instance.def_id()) == entry_def_id
{
Expand Down
Loading

0 comments on commit 20012cc

Please sign in to comment.