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 Dec 5, 2024
1 parent 3e3f3eb commit a10debc
Show file tree
Hide file tree
Showing 14 changed files with 443 additions and 202 deletions.
14 changes: 3 additions & 11 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,17 +395,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
to_add.push(MemoryEffects::None.create_attr(cx.llcx));
}
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
to_add.push(AttributeKind::Naked.create_attr(cx.llcx));
// HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions.
// And it is a module-level attribute, so the alternative is pulling naked functions into
// new LLVM modules. Otherwise LLVM's "naked" functions come with endbr prefixes per
// https://github.com/rust-lang/rust/issues/98768
to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx));
if llvm_util::get_version() < (19, 0, 0) {
// Prior to LLVM 19, branch-target-enforcement was disabled by setting the attribute to
// the string "false". Now it is disabled by absence of the attribute.
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
}
// do nothing; a naked function is converted into an extern function
// and a global assembly block. LLVM's support for naked functions is
// not used.
} else {
// Do not set sanitizer attributes for naked functions.
to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_codegen_llvm/src/mono_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use rustc_codegen_ssa::traits::*;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_middle::bug;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::mono::{Linkage, Visibility};
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf};
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
Expand Down Expand Up @@ -58,8 +59,15 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {

let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage));
let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
let llvm_linkage =
if attrs.flags.contains(CodegenFnAttrFlags::NAKED) && linkage == Linkage::Internal {
// this is effectively an extern fn, and must have external linkage
llvm::Linkage::ExternalLinkage
} else {
base::linkage_to_llvm(linkage)
};
llvm::set_linkage(lldecl, llvm_linkage);
base::set_link_section(lldecl, attrs);
if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
&& self.tcx.sess.target.supports_comdat()
Expand Down
20 changes: 8 additions & 12 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
sym::rustc_allocator_zeroed => {
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.
}
sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
sym::no_mangle => {
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE
Expand Down Expand Up @@ -545,6 +542,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
});

// naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
// but not for the code generation backend because at that point the naked function will just be
// a declaration, with a definition provided in global assembly.
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 @@ -629,14 +633,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 @@ -177,29 +177,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
200 changes: 109 additions & 91 deletions compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
Original file line number Diff line number Diff line change
@@ -1,74 +1,99 @@
use crate::common;
use crate::mir::FunctionCx;
use crate::traits::{AsmMethods, BuilderMethods, GlobalAsmOperandRef};
use rustc_middle::bug;
use rustc_middle::mir::InlineAsmOperand;
use rustc_middle::ty;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_attr::InstructionSetAttr;
use rustc_middle::mir::mono::{MonoItem, MonoItemData, Visibility};
use rustc_middle::mir::{Body, InlineAsmOperand};
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf};
use rustc_middle::ty::{Instance, TyCtxt};

use rustc_span::sym;
use rustc_middle::{bug, ty};
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);
}
use crate::common;
use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods};

pub(crate) 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(),
cx.typing_env(),
ty::EarlyBinder::bind(value.const_),
)
.eval(cx.tcx(), cx.typing_env(), value.span)
.expect("erroneous constant missed by mono item collection");

let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
cx.tcx(),
cx.typing_env(),
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(),
cx.typing_env(),
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 +116,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 +134,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 +160,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 +175,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 +192,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 +208,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
Loading

0 comments on commit a10debc

Please sign in to comment.