1
Fork 0

codegen #[naked] functions using global_asm!

This commit is contained in:
Folkert 2024-08-08 10:20:40 +02:00 committed by Folkert de Vries
parent 9c707a8b76
commit bd8f8e0631
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
18 changed files with 624 additions and 102 deletions

View file

@ -867,6 +867,13 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
template_str.push_str("\n.popsection");
self.context.add_top_level_asm(None, &template_str);
}
fn mangled_name(&self, instance: Instance<'tcx>) -> String {
// TODO(@Amanieu): Additional mangling is needed on
// some targets to add a leading underscore (Mach-O)
// or byte count suffixes (x86 Windows).
self.tcx.symbol_name(instance).name.to_string()
}
}
fn modifier_to_gcc(

View file

@ -442,6 +442,14 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
);
}
}
fn mangled_name(&self, instance: Instance<'tcx>) -> String {
let llval = self.get_fn(instance);
llvm::build_string(|s| unsafe {
llvm::LLVMRustGetMangledName(llval, s);
})
.expect("symbol is not valid UTF-8")
}
}
pub(crate) fn inline_asm_call<'ll>(

View file

@ -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));

View file

@ -542,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;
@ -626,10 +633,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
}
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
codegen_fn_attrs.inline = InlineAttr::Never;
}
// 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.

View file

@ -20,6 +20,7 @@ mod coverageinfo;
pub mod debuginfo;
mod intrinsic;
mod locals;
mod naked_asm;
pub mod operand;
pub mod place;
mod rvalue;
@ -176,6 +177,11 @@ 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().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
crate::mir::naked_asm::codegen_naked_asm::<Bx>(cx, &mir, instance);
return;
}
let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, mir);
let start_llbb = Bx::append_block(cx, llfn, "start");

View file

@ -0,0 +1,257 @@
use rustc_attr::InstructionSetAttr;
use rustc_middle::mir::mono::{Linkage, 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_middle::{bug, ty};
use rustc_span::sym;
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")
};
let operands: Vec<_> =
operands.iter().map(|op| inline_to_global_operand::<Bx>(cx, instance, op)).collect();
let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap();
let name = cx.mangled_name(instance);
let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, 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!")
}
}
}
enum AsmBinaryFormat {
Elf,
Macho,
Coff,
}
impl AsmBinaryFormat {
fn from_target(target: &rustc_target::spec::Target) -> Self {
if target.is_like_windows {
Self::Coff
} else if target.is_like_osx {
Self::Macho
} else {
Self::Elf
}
}
}
fn prefix_and_suffix<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
asm_name: &str,
item_data: &MonoItemData,
) -> (String, String) {
use std::fmt::Write;
let is_arm = tcx.sess.target.arch == "arm";
let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode);
let attrs = tcx.codegen_fn_attrs(instance.def_id());
let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
let align = attrs.alignment.map(|a| a.bytes()).unwrap_or(4);
// See https://sourceware.org/binutils/docs/as/ARM-Directives.html for info on these directives.
// In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`.
let (arch_prefix, arch_suffix) = if is_arm {
(
match attrs.instruction_set {
None => match is_thumb {
true => ".thumb\n.thumb_func",
false => ".arm",
},
Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func",
Some(InstructionSetAttr::ArmA32) => ".arm",
},
match is_thumb {
true => ".thumb",
false => ".arm",
},
)
} else {
("", "")
};
let emit_fatal = |msg| tcx.dcx().span_fatal(tcx.def_span(instance.def_id()), msg);
// see https://godbolt.org/z/cPK4sxKor.
// None means the default, which corresponds to internal linkage
let linkage = match item_data.linkage {
Linkage::External => Some(".globl"),
Linkage::LinkOnceAny => Some(".weak"),
Linkage::LinkOnceODR => Some(".weak"),
Linkage::WeakAny => Some(".weak"),
Linkage::WeakODR => Some(".weak"),
Linkage::Internal => None,
Linkage::Private => None,
Linkage::Appending => emit_fatal("Only global variables can have appending linkage!"),
Linkage::Common => emit_fatal("Functions may not have common linkage"),
Linkage::AvailableExternally => {
// this would make the function equal an extern definition
emit_fatal("Functions may not have available_externally linkage")
}
Linkage::ExternalWeak => {
// FIXME: actually this causes a SIGILL in LLVM
emit_fatal("Functions may not have external weak linkage")
}
};
let mut begin = String::new();
let mut end = String::new();
match AsmBinaryFormat::from_target(&tcx.sess.target) {
AsmBinaryFormat::Elf => {
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
let progbits = match is_arm {
true => "%progbits",
false => "@progbits",
};
let function = match is_arm {
true => "%function",
false => "@function",
};
writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
writeln!(begin, ".balign {align}").unwrap();
if let Some(linkage) = linkage {
writeln!(begin, "{linkage} {asm_name}").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();
}
writeln!(begin, "{asm_name}:").unwrap();
writeln!(end).unwrap();
writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap();
writeln!(end, ".popsection").unwrap();
if !arch_suffix.is_empty() {
writeln!(end, "{}", arch_suffix).unwrap();
}
}
AsmBinaryFormat::Macho => {
let section = link_section.unwrap_or("__TEXT,__text".to_string());
writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
writeln!(begin, ".balign {align}").unwrap();
if let Some(linkage) = linkage {
writeln!(begin, "{linkage} {asm_name}").unwrap();
}
if let Visibility::Hidden = item_data.visibility {
writeln!(begin, ".private_extern {asm_name}").unwrap();
}
writeln!(begin, "{asm_name}:").unwrap();
writeln!(end).unwrap();
writeln!(end, ".popsection").unwrap();
if !arch_suffix.is_empty() {
writeln!(end, "{}", arch_suffix).unwrap();
}
}
AsmBinaryFormat::Coff => {
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
writeln!(begin, ".balign {align}").unwrap();
if let Some(linkage) = linkage {
writeln!(begin, "{linkage} {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();
writeln!(begin, "{asm_name}:").unwrap();
writeln!(end).unwrap();
writeln!(end, ".popsection").unwrap();
if !arch_suffix.is_empty() {
writeln!(end, "{}", arch_suffix).unwrap();
}
}
}
(begin, end)
}

View file

@ -1,4 +1,5 @@
use rustc_hir as hir;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility};
use rustc_middle::ty::Instance;
@ -135,7 +136,13 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
cx.predefine_static(def_id, linkage, visibility, symbol_name);
}
MonoItem::Fn(instance) => {
let attrs = cx.tcx().codegen_fn_attrs(instance.def_id());
if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
// do not define this function; it will become a global assembly block
} else {
cx.predefine_fn(instance, linkage, visibility, symbol_name);
};
}
MonoItem::GlobalAsm(..) => {}
}

View file

@ -68,4 +68,11 @@ pub trait AsmCodegenMethods<'tcx> {
options: InlineAsmOptions,
line_spans: &[Span],
);
/// The mangled name of this instance
///
/// Additional mangling is used on
/// some targets to add a leading underscore (Mach-O)
/// or byte count suffixes (x86 Windows).
fn mangled_name(&self, instance: Instance<'tcx>) -> String;
}

View file

@ -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
{

View file

@ -8,7 +8,11 @@
#![no_std]
#![feature(abi_x86_interrupt, naked_functions)]
// CHECK: define x86_intrcc void @page_fault_handler(ptr {{.*}}%0, i64 {{.*}}%1){{.*}}#[[ATTRS:[0-9]+]] {
pub fn caller() {
page_fault_handler(1, 2);
}
// CHECK: declare x86_intrcc void @page_fault_handler(ptr {{.*}}, i64 {{.*}}){{.*}}#[[ATTRS:[0-9]+]]
// CHECK-NOT: memcpy
#[naked]
#[no_mangle]

View file

@ -6,15 +6,12 @@
#![feature(naked_functions, fn_align)]
use std::arch::naked_asm;
// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}void @naked_empty()
// CHECK: align 16
// CHECK: .balign 16
// CHECK-LABEL: naked_empty:
#[repr(align(16))]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn naked_empty() {
// CHECK-NEXT: start:
// CHECK-NEXT: call void asm
// CHECK-NEXT: unreachable
// CHECK: ret
naked_asm!("ret");
}

View file

@ -0,0 +1,115 @@
//@ compile-flags: -O
//@ only-x86_64
#![crate_type = "lib"]
#![feature(naked_functions, asm_const)]
use std::arch::naked_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::<i64>(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<const N: u64>(x: u64) -> u64 {
const M: u64 = 42;
unsafe {
naked_asm!(
"xor rax, rax",
"add rax, rdi",
"add rax, {}",
"add rax, {}",
"ret",
const N,
const M,
)
}
}
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<T: Invert>(x: i64) -> i64 {
unsafe {
naked_asm!(
"call {}",
"ret",
sym <T as Invert>::invert,
)
}
}
#[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 { naked_asm!("mov rax, rdi", "ret") }
}
}
// 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 { naked_asm!("mov rax, rdi", "ret") }
}
}
// 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 {
naked_asm!("lea rax, [rdi + rsi]", "ret");
}

View file

@ -0,0 +1,59 @@
//@ revisions: arm-mode thumb-mode
//@ [arm-mode] compile-flags: --target armv5te-none-eabi
//@ [thumb-mode] compile-flags: --target thumbv5te-none-eabi
//@ [arm-mode] needs-llvm-components: arm
//@ [thumb-mode] needs-llvm-components: arm
#![crate_type = "lib"]
#![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 {}
// arm-mode: .arm
// thumb-mode: .thumb
// CHECK-LABEL: test_unspecified:
// CHECK: bx lr
// CHECK: .popsection
// arm-mode: .arm
// thumb-mode: .thumb
#[no_mangle]
#[naked]
unsafe extern "C" fn test_unspecified() {
naked_asm!("bx lr");
}
// CHECK: .thumb
// CHECK: .thumb_func
// CHECK-LABEL: test_thumb:
// CHECK: bx lr
// CHECK: .popsection
// arm-mode: .arm
// thumb-mode: .thumb
#[no_mangle]
#[naked]
#[instruction_set(arm::t32)]
unsafe extern "C" fn test_thumb() {
naked_asm!("bx lr");
}
// CHECK: .arm
// CHECK-LABEL: test_arm:
// CHECK: bx lr
// CHECK: .popsection
// arm-mode: .arm
// thumb-mode: .thumb
#[no_mangle]
#[naked]
#[instruction_set(arm::a32)]
unsafe extern "C" fn test_arm() {
naked_asm!("bx lr");
}

View file

@ -1,29 +1,147 @@
//@ compile-flags: -C no-prepopulate-passes -Copt-level=0
//@ needs-asm-support
//@ only-x86_64
//@ revisions: linux win macos thumb
//
//@[linux] compile-flags: --target x86_64-unknown-linux-gnu
//@[linux] needs-llvm-components: x86
//@[win] compile-flags: --target x86_64-pc-windows-gnu
//@[win] 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;
#![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,win: .intel_syntax
//
// linux: .pushsection .text.naked_empty,\22ax\22, @progbits
// macos: .pushsection __TEXT,__text,regular,pure_instructions
// win: .pushsection .text.naked_empty,\22xr\22
// thumb: .pushsection .text.naked_empty,\22ax\22, %progbits
//
// CHECK: .balign 4
//
// linux,win,thumb: .globl naked_empty
// macos: .globl _naked_empty
//
// CHECK-NOT: .private_extern
// CHECK-NOT: .hidden
//
// linux: .type naked_empty, @function
//
// win: .def naked_empty
// win: .scl 2
// win: .type 32
// win: .endef naked_empty
//
// thumb: .type naked_empty, %function
// thumb: .thumb
// thumb: .thumb_func
//
// CHECK-LABEL: naked_empty:
//
// linux,macos,win: ret
// thumb: bx lr
//
// CHECK: .popsection
//
// thumb: .thumb
//
// linux,win: .att_syntax
// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}void @naked_empty()
#[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: Function Attrs: naked
// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_args_and_return(i64 %0, i64 %1)
// linux,win: .intel_syntax
//
// linux: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits
// macos: .pushsection __TEXT,__text,regular,pure_instructions
// win: .pushsection .text.naked_with_args_and_return,\22xr\22
// thumb: .pushsection .text.naked_with_args_and_return,\22ax\22, %progbits
//
// CHECK: .balign 4
//
// linux,win,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
//
// win: .def naked_with_args_and_return
// win: .scl 2
// win: .type 32
// win: .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:
//
// linux, win: lea rax, [rdi + rsi]
// macos: add x0, x0, x1
// thumb: adds r0, r0, r1
//
// linux,macos,win: ret
// thumb: bx lr
//
// CHECK: .popsection
//
// thumb: .thumb
//
// linux,win: .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
// win: .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");
#[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))]
naked_asm!("bx lr");
}

View file

@ -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!("");
}

View file

@ -1,31 +0,0 @@
// Checks that naked functions are never inlined.
//@ compile-flags: -O -Zmir-opt-level=3
//@ needs-asm-support
//@ ignore-wasm32
#![crate_type = "lib"]
#![feature(naked_functions)]
use std::arch::naked_asm;
#[naked]
#[no_mangle]
pub unsafe extern "C" fn f() {
// Check that f has naked and noinline attributes.
//
// CHECK: define {{(dso_local )?}}void @f() unnamed_addr [[ATTR:#[0-9]+]]
// CHECK-NEXT: start:
// CHECK-NEXT: call void asm
naked_asm!("");
}
#[no_mangle]
pub unsafe fn g() {
// Check that call to f is not inlined.
//
// CHECK-LABEL: define {{(dso_local )?}}void @g()
// CHECK-NEXT: start:
// CHECK-NEXT: call void @f()
f();
}
// CHECK: attributes [[ATTR]] = { naked{{.*}}noinline{{.*}} }

View file

@ -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");
}

View file

@ -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");
}