Skip to content
30 changes: 26 additions & 4 deletions src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
#[cfg(not(feature = "master"))]
let apply_attrs = |ty: Type<'gcc>, _attrs: &ArgAttributes, _arg_index: usize| ty;

for arg in self.args.iter() {
for (source_arg_index, arg) in self.args.iter().enumerate() {
#[cfg(not(feature = "master"))]
let _ = source_arg_index;

let arg_ty = match arg.mode {
PassMode::Ignore => continue,
PassMode::Pair(a, b) => {
Expand All @@ -177,9 +180,28 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
apply_attrs(ty, &cast.attrs, argument_tys.len())
}
PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: true } => {
// This is a "byval" argument, so we don't apply the `restrict` attribute on it.
on_stack_param_indices.insert(argument_tys.len());
arg.layout.gcc_type(cx)
let x86_interrupt_first_arg = {
#[cfg(feature = "master")]
{
source_arg_index == 0
&& matches!(self.conv, CanonAbi::Interrupt(InterruptKind::X86))
}
#[cfg(not(feature = "master"))]
{
false
}
};

if x86_interrupt_first_arg {
// Rust lowers the first `x86-interrupt` argument as a byval stack slot.
// LLVM represents that as a pointer parameter with `byval`; GCC's
// interrupt attribute likewise requires a pointer-shaped first parameter.
cx.type_ptr_to(arg.layout.gcc_type(cx))
} else {
// This is a "byval" argument, so we don't apply the `restrict` attribute on it.
on_stack_param_indices.insert(argument_tys.len());
arg.layout.gcc_type(cx)
}
}
PassMode::Direct(attrs) => {
apply_attrs(arg.layout.immediate_gcc_type(cx), &attrs, argument_tys.len())
Expand Down
85 changes: 54 additions & 31 deletions src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
use gccjit::FnAttribute;
use gccjit::Function;
#[cfg(feature = "master")]
use rustc_abi::{CanonAbi, InterruptKind};
#[cfg(feature = "master")]
use rustc_hir::attrs::InlineAttr;
use rustc_hir::attrs::InstructionSetAttr;
#[cfg(feature = "master")]
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
#[cfg(feature = "master")]
use rustc_middle::mir::TerminatorKind;
use rustc_middle::ty;
use rustc_target::callconv::FnAbi;
#[cfg(feature = "master")]
use rustc_target::spec::Arch;

Expand Down Expand Up @@ -82,12 +85,23 @@ fn inline_attr<'gcc, 'tcx>(
}
}

#[cfg(feature = "master")]
fn is_x86_interrupt<'tcx>(fn_abi: Option<&FnAbi<'tcx, ty::Ty<'tcx>>>) -> bool {
matches!(
fn_abi,
Some(fn_abi) if matches!(fn_abi.conv, CanonAbi::Interrupt(InterruptKind::X86))
)
}

/// Composite function which sets GCC attributes for function depending on its AST (`#[attribute]`)
/// attributes.
pub fn from_fn_attrs<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
#[cfg_attr(not(feature = "master"), expect(unused_variables))] func: Function<'gcc>,
instance: ty::Instance<'tcx>,
#[cfg_attr(not(feature = "master"), expect(unused_variables))] fn_abi: Option<
&FnAbi<'tcx, ty::Ty<'tcx>>,
>,
) {
let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def);

Expand Down Expand Up @@ -120,39 +134,48 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
}
}

let mut function_features = codegen_fn_attrs
.target_features
.iter()
.map(|features| features.name.as_str())
.flat_map(|feat| to_gcc_features(cx.tcx.sess, feat).into_iter())
.chain(codegen_fn_attrs.instruction_set.iter().map(|x| match *x {
InstructionSetAttr::ArmA32 => "-thumb-mode", // FIXME(antoyo): support removing feature.
InstructionSetAttr::ArmT32 => "thumb-mode",
}))
.collect::<Vec<_>>();
#[cfg(feature = "master")]
let x86_interrupt = is_x86_interrupt(fn_abi);
#[cfg(not(feature = "master"))]
let x86_interrupt = false;

// FIXME(antoyo): cg_llvm adds global features to each function so that LTO keep them.
// Check if GCC requires the same.
let mut global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
function_features.extend(&mut global_features);
let target_features = function_features
.iter()
.filter_map(|feature| {
// FIXME(antoyo): support soft-float.
if feature.contains("soft-float") {
return None;
}
let target_features = if x86_interrupt {
"general-regs-only".to_string()
} else {
let mut function_features = codegen_fn_attrs
.target_features
.iter()
.map(|features| features.name.as_str())
.flat_map(|feat| to_gcc_features(cx.tcx.sess, feat).into_iter())
.chain(codegen_fn_attrs.instruction_set.iter().map(|x| match *x {
InstructionSetAttr::ArmA32 => "-thumb-mode", // FIXME(antoyo): support removing feature.
InstructionSetAttr::ArmT32 => "thumb-mode",
}))
.collect::<Vec<_>>();

if feature.starts_with('-') {
Some(format!("no{}", feature))
} else if let Some(stripped) = feature.strip_prefix('+') {
Some(stripped.to_string())
} else {
Some(feature.to_string())
}
})
.collect::<Vec<_>>()
.join(",");
// FIXME(antoyo): cg_llvm adds global features to each function so that LTO keep them.
// Check if GCC requires the same.
let mut global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
function_features.extend(&mut global_features);
function_features
.iter()
.filter_map(|feature| {
// FIXME(antoyo): support soft-float.
if feature.contains("soft-float") {
return None;
}

if feature.starts_with('-') {
Some(format!("no{}", feature))
} else if let Some(stripped) = feature.strip_prefix('+') {
Some(stripped.to_string())
} else {
Some(feature.to_string())
}
})
.collect::<Vec<_>>()
.join(",")
};
if !target_features.is_empty() {
#[cfg(feature = "master")]
match cx.sess().target.arch {
Expand Down
2 changes: 1 addition & 1 deletion src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
cx.linkage.set(FunctionType::Extern);
let func = cx.declare_fn(sym, fn_abi);

attributes::from_fn_attrs(cx, func, instance);
attributes::from_fn_attrs(cx, func, instance, Some(fn_abi));

#[cfg(feature = "master")]
{
Expand Down
24 changes: 12 additions & 12 deletions src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::FnAbi;

use crate::abi::{FnAbiGcc, FnAbiGccExt};
use crate::abi::FnAbiGccExt;
use crate::context::CodegenCx;

impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
Expand Down Expand Up @@ -110,22 +110,22 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}

pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Function<'gcc> {
let FnAbiGcc {
return_type,
arguments_type,
is_c_variadic,
on_stack_param_indices,
#[cfg(feature = "master")]
fn_attributes,
} = fn_abi.gcc_type(self);
let fn_abi_gcc = fn_abi.gcc_type(self);
#[cfg(feature = "master")]
let conv = fn_abi.gcc_cconv(self);
#[cfg(not(feature = "master"))]
let conv = None;
let func = declare_raw_fn(self, name, conv, return_type, &arguments_type, is_c_variadic);
self.on_stack_function_params.borrow_mut().insert(func, on_stack_param_indices);
let func = declare_raw_fn(
self,
name,
conv,
fn_abi_gcc.return_type,
&fn_abi_gcc.arguments_type,
fn_abi_gcc.is_c_variadic,
);
self.on_stack_function_params.borrow_mut().insert(func, fn_abi_gcc.on_stack_param_indices);
#[cfg(feature = "master")]
for fn_attr in fn_attributes {
for fn_attr in fn_abi_gcc.fn_attributes {
func.add_attribute(fn_attr);
}
func
Expand Down
2 changes: 1 addition & 1 deletion src/intrinsic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc

self.on_stack_function_params.borrow_mut().insert(func, FxHashSet::default());

crate::attributes::from_fn_attrs(self, func, instance);
crate::attributes::from_fn_attrs(self, func, instance, None);

func
};
Expand Down
2 changes: 1 addition & 1 deletion src/mono_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
let decl = self.declare_fn(symbol_name, fn_abi);
//let attrs = self.tcx.codegen_instance_attrs(instance.def);

attributes::from_fn_attrs(self, decl, instance);
attributes::from_fn_attrs(self, decl, instance, Some(fn_abi));

// If we're compiling the compiler-builtins crate, e.g., the equivalent of
// compiler-rt, then we want to implicitly compile everything with hidden
Expand Down
16 changes: 16 additions & 0 deletions tests/compile/x86_interrupt_first_arg_byval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Compiler:

// Test that `x86-interrupt` functions whose first argument is passed by value
// emit pointer-shaped GCC parameters and compile with interrupt-safe target features.

#![feature(abi_x86_interrupt)]
#![crate_type = "lib"]

#[repr(C)]
pub struct Frame {
ip: u64,
}

pub extern "x86-interrupt" fn scalar(_a: i64) {}

pub extern "x86-interrupt" fn aggregate(_frame: Frame) {}
8 changes: 7 additions & 1 deletion tests/lang_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,13 @@ fn compile_tests(tempdir: PathBuf, current_dir: String) {
"lang compile",
"tests/compile",
TestMode::Compile,
&["simd-ffi.rs", "asm_nul_byte.rs", "global_asm_nul_byte.rs", "naked_asm_nul_byte.rs"],
&[
"simd-ffi.rs",
"asm_nul_byte.rs",
"global_asm_nul_byte.rs",
"naked_asm_nul_byte.rs",
"x86_interrupt_first_arg_byval.rs",
],
);
}

Expand Down
Loading