From a10debcfd95209d67f12e624365fd7f62cad8b43 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Jul 2024 13:23:34 +0200 Subject: [PATCH] squashed changes to tests correctly emit `.hidden` this test was added in https://github.com/rust-lang/rust/pull/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 #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 --- compiler/rustc_codegen_llvm/src/attributes.rs | 14 +- compiler/rustc_codegen_llvm/src/mono_item.rs | 10 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 20 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 25 +-- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 200 ++++++++++-------- compiler/rustc_codegen_ssa/src/mono_item.rs | 12 +- compiler/rustc_middle/src/mir/mono.rs | 8 +- tests/codegen/naked-fn/generics.rs | 117 ++++++++++ tests/codegen/naked-fn/instruction-set.rs | 43 ++++ tests/codegen/naked-fn/naked-functions.rs | 158 +++++++++++--- tests/codegen/naked-fn/naked-nocoverage.rs | 19 -- tests/crashes/124375.rs | 11 - .../ui/asm/naked-functions-instruction-set.rs | 2 +- tests/ui/asm/naked-functions.rs | 6 + 14 files changed, 443 insertions(+), 202 deletions(-) create mode 100644 tests/codegen/naked-fn/generics.rs create mode 100644 tests/codegen/naked-fn/instruction-set.rs delete mode 100644 tests/codegen/naked-fn/naked-nocoverage.rs delete mode 100644 tests/crashes/124375.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index cb958c1d4d771..5552a2410601d 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -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)); diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 33789c6261f10..4d1704f2c998f 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -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}; @@ -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() diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 0768b01cb15e5..01dd75c5ee85e 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -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 @@ -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; @@ -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. diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index b14e43acb6078..62f69af3f2f75 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -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::(cx, &mir, instance); return; } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 1f41a9aefeb73..b4eb6110da8ca 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -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::(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!") } } } @@ -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; @@ -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", @@ -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", @@ -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(); } @@ -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(); @@ -187,7 +208,7 @@ 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(); @@ -195,9 +216,6 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri 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(); diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 04159422e3cb2..085ef1cd8ea36 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -1,8 +1,9 @@ use rustc_hir as hir; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::mono::{LinkageInfo, MonoItem, Visibility}; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::Instance; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::{span_bug, ty}; use tracing::debug; @@ -135,7 +136,14 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { cx.predefine_static(def_id, linkage_info.into_linkage(), visibility, symbol_name); } MonoItem::Fn(instance) => { - cx.predefine_fn(instance, linkage_info.into_linkage(), visibility, symbol_name); + let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()); + let linkage = if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + linkage_info.into_naked_linkage() + } else { + linkage_info.into_linkage() + }; + + cx.predefine_fn(instance, linkage, visibility, symbol_name); } MonoItem::GlobalAsm(..) => {} } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index ca230d7fa8584..4287b2c574ffe 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -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. @@ -104,7 +105,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 { @@ -302,6 +305,9 @@ impl LinkageInfo { /// Naked functions are generated as a separate declaration (effectively an extern fn) and /// definition (using global assembly). To link them together, some flavor of external linkage /// must be used. + /// + /// This should be just an implementation detail of the backend, which is why this translation + /// is made at the final moment. pub const fn into_naked_linkage(self) -> Linkage { match self { // promote Weak linkage to ExternalWeak diff --git a/tests/codegen/naked-fn/generics.rs b/tests/codegen/naked-fn/generics.rs new file mode 100644 index 0000000000000..5bb40cddea56c --- /dev/null +++ b/tests/codegen/naked-fn/generics.rs @@ -0,0 +1,117 @@ +//@ compile-flags: -O +//@ only-x86_64 + +#![crate_type = "lib"] +#![feature(naked_functions, asm_const)] + +use std::arch::asm; + +#[no_mangle] +fn test(x: u64) { + // just making sure these symbols get used + using_const_generics::<1>(x); + using_const_generics::<2>(x); + + generic_function::(x as i64); + + let foo = Foo(x); + + foo.method(); + foo.trait_method(); +} + +// CHECK: .balign 4 +// CHECK: add rax, 2 +// CHECK: add rax, 42 + +// CHECK: .balign 4 +// CHECK: add rax, 1 +// CHECK: add rax, 42 + +#[naked] +pub extern "C" fn using_const_generics(x: u64) -> u64 { + const M: u64 = 42; + + unsafe { + asm!( + "xor rax, rax", + "add rax, rdi", + "add rax, {}", + "add rax, {}", + "ret", + const N, + const M, + options(noreturn), + ) + } +} + +trait Invert { + fn invert(self) -> Self; +} + +impl Invert for i64 { + fn invert(self) -> Self { + -1 * self + } +} + +// CHECK-LABEL: generic_function +// CHECK: .balign 4 +// CHECK: call +// CHECK: ret + +#[naked] +pub extern "C" fn generic_function(x: i64) -> i64 { + unsafe { + asm!( + "call {}", + "ret", + sym ::invert, + options(noreturn), + ) + } +} + +#[derive(Copy, Clone)] +#[repr(transparent)] +struct Foo(u64); + +// CHECK-LABEL: method +// CHECK: .balign 4 +// CHECK: mov rax, rdi + +impl Foo { + #[naked] + #[no_mangle] + extern "C" fn method(self) -> u64 { + unsafe { asm!("mov rax, rdi", "ret", options(noreturn)) } + } +} + +// CHECK-LABEL: trait_method +// CHECK: .balign 4 +// CHECK: mov rax, rdi + +trait Bar { + extern "C" fn trait_method(self) -> u64; +} + +impl Bar for Foo { + #[naked] + #[no_mangle] + extern "C" fn trait_method(self) -> u64 { + unsafe { asm!("mov rax, rdi", "ret", options(noreturn)) } + } +} + +// CHECK-LABEL: naked_with_args_and_return +// CHECK: .balign 4 +// CHECK: lea rax, [rdi + rsi] + +// this previously ICE'd, see https://github.com/rust-lang/rust/issues/124375 +#[naked] +#[no_mangle] +pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { + asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); +} diff --git a/tests/codegen/naked-fn/instruction-set.rs b/tests/codegen/naked-fn/instruction-set.rs new file mode 100644 index 0000000000000..509c24f9457cf --- /dev/null +++ b/tests/codegen/naked-fn/instruction-set.rs @@ -0,0 +1,43 @@ +//@ compile-flags: --target armv5te-none-eabi +//@ needs-llvm-components: arm + +#![crate_type = "lib"] +#![feature(no_core, lang_items, rustc_attrs, naked_functions)] +#![no_core] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +// CHECK-LABEL: test_unspecified: +// CHECK: .arm +#[no_mangle] +#[naked] +unsafe extern "C" fn test_unspecified() { + asm!("bx lr", options(noreturn)); +} + +// CHECK-LABEL: test_thumb: +// CHECK: .thumb +// CHECK: .thumb_func +#[no_mangle] +#[naked] +#[instruction_set(arm::t32)] +unsafe extern "C" fn test_thumb() { + asm!("bx lr", options(noreturn)); +} + +// CHECK-LABEL: test_arm: +// CHECK: .arm +#[no_mangle] +#[naked] +#[instruction_set(arm::t32)] +unsafe extern "C" fn test_arm() { + asm!("bx lr", options(noreturn)); +} diff --git a/tests/codegen/naked-fn/naked-functions.rs b/tests/codegen/naked-fn/naked-functions.rs index 00d64089ad8bc..8b12693da8f66 100644 --- a/tests/codegen/naked-fn/naked-functions.rs +++ b/tests/codegen/naked-fn/naked-functions.rs @@ -1,49 +1,147 @@ -//@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -//@ needs-asm-support -//@ only-x86_64 +//@ revisions: linux windows macos thumb +// +//@[linux] compile-flags: --target x86_64-unknown-linux-gnu +//@[linux] needs-llvm-components: x86 +//@[windows] compile-flags: --target x86_64-pc-windows-gnu +//@[windows] needs-llvm-components: x86 +//@[macos] compile-flags: --target aarch64-apple-darwin +//@[macos] needs-llvm-components: arm +//@[thumb] compile-flags: --target thumbv7em-none-eabi +//@[thumb] needs-llvm-components: arm #![crate_type = "lib"] -#![feature(naked_functions)] -use std::arch::naked_asm; - -// CHECK: module asm ".intel_syntax" -// CHECK: .pushsection .text.naked_empty,\22ax\22, @progbits -// CHECK: .balign 4" -// CHECK: .globl naked_empty" -// CHECK: .hidden naked_empty" -// CHECK: .type naked_empty, @function" +#![feature(no_core, lang_items, rustc_attrs, naked_functions)] +#![no_core] + +#[rustc_builtin_macro] +macro_rules! naked_asm { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +// linux,windows: .intel_syntax +// +// linux: .pushsection .text.naked_empty,\22ax\22, @progbits +// macos: .pushsection __TEXT,__text,regular,pure_instructions +// windows: .pushsection .text.naked_empty,\22xr\22 +// thumb: .pushsection .text.naked_empty,\22ax\22, %progbits +// +// CHECK: .balign 4 +// +// linux,windows,thumb: .globl naked_empty +// macos: .globl _naked_empty +// +// CHECK-NOT: .private_extern +// CHECK-NOT: .hidden +// +// linux: .type naked_empty, @function +// +// windows: .def naked_empty +// windows: .scl 2 +// windows: .type 32 +// windows: .endef naked_empty +// +// thumb: .type naked_empty, %function +// thumb: .thumb +// thumb: .thumb_func +// // CHECK-LABEL: naked_empty: -// CHECK: ret +// +// linux,macos,windows: ret +// thumb: bx lr +// // CHECK: .popsection -// CHECK: .att_syntax +// +// thumb: .thumb +// +// linux,windows: .att_syntax #[no_mangle] #[naked] pub unsafe extern "C" fn naked_empty() { - // CHECK-NEXT: {{.+}}: - // CHECK-NEXT: call void asm - // CHECK-NEXT: unreachable + #[cfg(not(all(target_arch = "arm", target_feature = "thumb-mode")))] naked_asm!("ret"); + + #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] + naked_asm!("bx lr"); } -// CHECK: .intel_syntax -// CHECK: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits +// linux,windows: .intel_syntax +// +// linux: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits +// macos: .pushsection __TEXT,__text,regular,pure_instructions +// windows: .pushsection .text.naked_with_args_and_return,\22xr\22 +// thumb: .pushsection .text.naked_with_args_and_return,\22ax\22, %progbits +// // CHECK: .balign 4 -// CHECK: .globl naked_with_args_and_return -// CHECK: .hidden naked_with_args_and_return -// CHECK: .type naked_with_args_and_return, @function +// +// linux,windows,thumb: .globl naked_with_args_and_return +// macos: .globl _naked_with_args_and_return +// +// CHECK-NOT: .private_extern +// CHECK-NOT: .hidden +// +// linux: .type naked_with_args_and_return, @function +// +// windows: .def naked_with_args_and_return +// windows: .scl 2 +// windows: .type 32 +// windows: .endef naked_with_args_and_return +// +// thumb: .type naked_with_args_and_return, %function +// thumb: .thumb +// thumb: .thumb_func +// // CHECK-LABEL: naked_with_args_and_return: -// CHECK: lea rax, [rdi + rsi] -// CHECK: ret -// CHECK: .size naked_with_args_and_return, . - naked_with_args_and_return +// +// linux, windows: lea rax, [rdi + rsi] +// macos: add x0, x0, x1 +// thumb: adds r0, r0, r1 +// +// linux,macos,windows: ret +// thumb: bx lr +// // CHECK: .popsection -// CHECK: .att_syntax +// +// thumb: .thumb +// +// linux,windows: .att_syntax #[no_mangle] #[naked] pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { - // CHECK-NEXT: {{.+}}: - // CHECK-NEXT: call void asm - // CHECK-NEXT: unreachable - naked_asm!("lea rax, [rdi + rsi]", "ret"); + #[cfg(any(target_os = "windows", target_os = "linux"))] + { + naked_asm!("lea rax, [rdi + rsi]", "ret") + } + + #[cfg(target_os = "macos")] + { + naked_asm!("add x0, x0, x1", "ret") + } + + #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] + { + naked_asm!("adds r0, r0, r1", "bx lr") + } +} + +// linux: .pushsection .text.some_different_name,\22ax\22, @progbits +// macos: .pushsection .text.some_different_name,regular,pure_instructions +// windows: .pushsection .text.some_different_name,\22xr\22 +// thumb: .pushsection .text.some_different_name,\22ax\22, %progbits +// CHECK-LABEL: test_link_section: +#[no_mangle] +#[naked] +#[link_section = ".text.some_different_name"] +pub unsafe extern "C" fn test_link_section() { + #[cfg(not(all(target_arch = "arm", target_feature = "thumb-mode")))] + naked_asm!("ret", options(noreturn)); + + #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] + naked_asm!("bx lr", options(noreturn)); } diff --git a/tests/codegen/naked-fn/naked-nocoverage.rs b/tests/codegen/naked-fn/naked-nocoverage.rs deleted file mode 100644 index f63661bcd3a7a..0000000000000 --- a/tests/codegen/naked-fn/naked-nocoverage.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Checks that naked functions are not instrumented by -Cinstrument-coverage. -// Regression test for issue #105170. -// -//@ needs-asm-support -//@ compile-flags: -Zno-profiler-runtime -//@ compile-flags: -Cinstrument-coverage -#![crate_type = "lib"] -#![feature(naked_functions)] -use std::arch::naked_asm; - -#[naked] -#[no_mangle] -pub unsafe extern "C" fn f() { - // CHECK: define {{(dso_local )?}}void @f() - // CHECK-NEXT: start: - // CHECK-NEXT: call void asm - // CHECK-NEXT: unreachable - naked_asm!(""); -} diff --git a/tests/crashes/124375.rs b/tests/crashes/124375.rs deleted file mode 100644 index 1d877caeb8bc1..0000000000000 --- a/tests/crashes/124375.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: #124375 -//@ compile-flags: -Zmir-opt-level=0 -//@ only-x86_64 -#![crate_type = "lib"] -#![feature(naked_functions)] -use std::arch::naked_asm; - -#[naked] -pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { - naked_asm!("lea rax, [rdi + rsi]", "ret"); -} diff --git a/tests/ui/asm/naked-functions-instruction-set.rs b/tests/ui/asm/naked-functions-instruction-set.rs index 37c7b52c191cd..3a6e7a46ce5a7 100644 --- a/tests/ui/asm/naked-functions-instruction-set.rs +++ b/tests/ui/asm/naked-functions-instruction-set.rs @@ -24,7 +24,7 @@ unsafe extern "C" fn test_thumb() { #[no_mangle] #[naked] -#[instruction_set(arm::t32)] +#[instruction_set(arm::a32)] unsafe extern "C" fn test_arm() { naked_asm!("bx lr"); } diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index 5c58f1498cc97..0939b5b57e5e1 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -183,6 +183,12 @@ pub unsafe extern "C" fn invalid_asm_syntax(a: u32) -> u32 { //~^ ERROR asm template must be a string literal } +// this previously ICE'd, see https://github.com/rust-lang/rust/issues/124375 +#[naked] +pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { + asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); +} + #[cfg(target_arch = "x86_64")] #[cfg_attr(target_pointer_width = "64", no_mangle)] #[naked]