From 053b0fb97a08add9612de5fedb203064b6d6209e Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 30 May 2026 17:21:25 -0300 Subject: [PATCH 1/8] Emit a clean error for x86-interrupt functions with a non-pointer first argument GCC's `interrupt` attribute requires the first parameter of an `x86-interrupt` function to be a pointer. When it is not (for example a scalar `i64` or a by-value struct), libgccjit aborts codegen with an internal error and leaves no object file behind, which then surfaces as a confusing linker failure. Detect that case and emit a clear diagnostic from `predefine_fn`, while dropping the calling-convention attribute in `declare_fn` so libgccjit does not abort. The detection runs on the already-lowered GCC argument types, so `declare_fn` reuses the single ABI lowering it already performs instead of computing it twice. --- src/abi.rs | 31 +++++++++++++++++++++++++++++++ src/declare.rs | 37 ++++++++++++++++++++++++------------- src/errors.rs | 9 +++++++++ src/mono_item.rs | 10 ++++++++++ 4 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/abi.rs b/src/abi.rs index 2f5c555b702..83331730d25 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -16,6 +16,8 @@ use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; use rustc_target::spec::Arch; use crate::builder::Builder; +#[cfg(feature = "master")] +use crate::common::type_is_pointer; use crate::context::CodegenCx; use crate::type_of::LayoutGccExt; @@ -277,3 +279,32 @@ pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option( + conv: CanonAbi, + arguments_type: &[Type<'gcc>], +) -> bool { + matches!(conv, CanonAbi::Interrupt(InterruptKind::X86)) + && arguments_type.first().map_or(true, |ty| !type_is_pointer(*ty)) +} + +/// Convenience wrapper around [`x86_interrupt_first_arg_is_invalid`] for callers that only +/// hold the `FnAbi` and must lower it themselves. Short-circuits before lowering for the +/// common non-`x86-interrupt` case. +#[cfg(feature = "master")] +pub fn x86_interrupt_has_invalid_first_arg<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, +) -> bool { + matches!(fn_abi.conv, CanonAbi::Interrupt(InterruptKind::X86)) + && x86_interrupt_first_arg_is_invalid(fn_abi.conv, &fn_abi.gcc_type(cx).arguments_type) +} diff --git a/src/declare.rs b/src/declare.rs index 4174eebcf7b..c54ea62eb4a 100644 --- a/src/declare.rs +++ b/src/declare.rs @@ -6,7 +6,9 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use crate::abi::{FnAbiGcc, FnAbiGccExt}; +#[cfg(feature = "master")] +use crate::abi::x86_interrupt_first_arg_is_invalid; +use crate::abi::FnAbiGccExt; use crate::context::CodegenCx; impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { @@ -110,22 +112,31 @@ 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); + // Lower the ABI once and reuse it below, including for the `x86-interrupt` check, + // instead of lowering it a second time inside a helper. + let fn_abi_gcc = fn_abi.gcc_type(self); #[cfg(feature = "master")] - let conv = fn_abi.gcc_cconv(self); + let conv = if x86_interrupt_first_arg_is_invalid(fn_abi.conv, &fn_abi_gcc.arguments_type) { + // GCC rejects an `x86-interrupt` function whose first argument is not a + // pointer. Drop the calling-convention attribute so libgccjit does not abort; + // a clean error is emitted in `predefine_fn` instead (issue #833). + None + } else { + 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 diff --git a/src/errors.rs b/src/errors.rs index de633d3bdde..814ed6718bc 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -30,3 +30,12 @@ pub(crate) struct NulBytesInAsm { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag( + "the GCC backend requires the first argument of an `x86-interrupt` function to be a pointer" +)] +pub(crate) struct X86InterruptBadFirstArg { + #[primary_span] + pub span: Span, +} diff --git a/src/mono_item.rs b/src/mono_item.rs index d5874779021..148ce4cf092 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -10,7 +10,11 @@ use rustc_middle::mono::Visibility; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; +#[cfg(feature = "master")] +use crate::abi::x86_interrupt_has_invalid_first_arg; use crate::context::CodegenCx; +#[cfg(feature = "master")] +use crate::errors::X86InterruptBadFirstArg; use crate::type_of::LayoutGccExt; use crate::{attributes, base}; @@ -51,6 +55,12 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { assert!(!instance.args.has_infer()); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); + #[cfg(feature = "master")] + if x86_interrupt_has_invalid_first_arg(self, fn_abi) { + self.tcx + .dcx() + .emit_err(X86InterruptBadFirstArg { span: self.tcx.def_span(instance.def_id()) }); + } self.linkage.set(base::linkage_to_gcc(linkage)); let decl = self.declare_fn(symbol_name, fn_abi); //let attrs = self.tcx.codegen_instance_attrs(instance.def); From 5880eab88c1ecfcc35d8695cfecbf32e0e87da43 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 30 May 2026 17:21:35 -0300 Subject: [PATCH 2/8] Add regression test for x86-interrupt non-pointer first argument Check that an `x86-interrupt` function whose first argument is not a pointer produces a clean compiler error instead of an internal libgccjit error. --- tests/compile/x86_interrupt_bad_first_arg.rs | 17 +++++++++++++++++ tests/lang_tests.rs | 8 +++++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/compile/x86_interrupt_bad_first_arg.rs diff --git a/tests/compile/x86_interrupt_bad_first_arg.rs b/tests/compile/x86_interrupt_bad_first_arg.rs new file mode 100644 index 00000000000..ee0abeed8e0 --- /dev/null +++ b/tests/compile/x86_interrupt_bad_first_arg.rs @@ -0,0 +1,17 @@ +// Compiler: +// status: error +// stderr: +// error: the GCC backend requires the first argument of an `x86-interrupt` function to be a pointer +// ... + +// Test that an `x86-interrupt` function whose first argument is not a pointer emits a +// clean error instead of an internal libgccjit error (issue #833). + +#![feature(abi_x86_interrupt)] + +pub extern "x86-interrupt" fn f(_a: i64) {} + +fn main() { + // Take the function's address so that it is codegened. + let _f: extern "x86-interrupt" fn(i64) = f; +} diff --git a/tests/lang_tests.rs b/tests/lang_tests.rs index e3baf1e038f..64b7d380116 100644 --- a/tests/lang_tests.rs +++ b/tests/lang_tests.rs @@ -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_bad_first_arg.rs", + ], ); } From 2b59b53c53113285cdf46c6c48e49d855faa587e Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 30 May 2026 17:35:36 -0300 Subject: [PATCH 3/8] Sort declare.rs abi imports to satisfy rustfmt --- src/declare.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/declare.rs b/src/declare.rs index c54ea62eb4a..06213da55be 100644 --- a/src/declare.rs +++ b/src/declare.rs @@ -6,9 +6,9 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; +use crate::abi::FnAbiGccExt; #[cfg(feature = "master")] use crate::abi::x86_interrupt_first_arg_is_invalid; -use crate::abi::FnAbiGccExt; use crate::context::CodegenCx; impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { From 319aa208df7a2decdbc64bba159e42fdde949777 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 30 May 2026 17:35:36 -0300 Subject: [PATCH 4/8] Reword x86-interrupt doc comment to avoid cspell flag --- src/abi.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/abi.rs b/src/abi.rs index 83331730d25..3947d93a000 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -287,7 +287,8 @@ pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option( conv: CanonAbi, From 09fe6fe9f80483b5a4d52e243bbad2522151cee5 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 30 May 2026 17:37:55 -0300 Subject: [PATCH 5/8] Use is_none_or to satisfy clippy unnecessary_map_or --- src/abi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/abi.rs b/src/abi.rs index 3947d93a000..4d5aa5e9486 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -295,7 +295,7 @@ pub fn x86_interrupt_first_arg_is_invalid<'gcc>( arguments_type: &[Type<'gcc>], ) -> bool { matches!(conv, CanonAbi::Interrupt(InterruptKind::X86)) - && arguments_type.first().map_or(true, |ty| !type_is_pointer(*ty)) + && arguments_type.first().is_none_or(|ty| !type_is_pointer(*ty)) } /// Convenience wrapper around [`x86_interrupt_first_arg_is_invalid`] for callers that only From 0a3beda44c370637618cc5b1d69c6a460e210c47 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 30 May 2026 17:41:26 -0300 Subject: [PATCH 6/8] Gate x86-interrupt error struct behind master feature --- src/errors.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/errors.rs b/src/errors.rs index 814ed6718bc..e0459325f9e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -31,6 +31,7 @@ pub(crate) struct NulBytesInAsm { pub span: Span, } +#[cfg(feature = "master")] #[derive(Diagnostic)] #[diag( "the GCC backend requires the first argument of an `x86-interrupt` function to be a pointer" From c5cdf21b7468242a51b5ab975832cbc89b63ae03 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sun, 5 Jul 2026 16:39:47 -0300 Subject: [PATCH 7/8] Lower x86-interrupt byval first arg as pointer --- src/abi.rs | 62 ++++++++----------- src/declare.rs | 13 +--- src/errors.rs | 10 --- src/mono_item.rs | 10 --- tests/compile/x86_interrupt_bad_first_arg.rs | 17 ----- .../compile/x86_interrupt_first_arg_byval.rs | 21 +++++++ tests/lang_tests.rs | 2 +- 7 files changed, 49 insertions(+), 86 deletions(-) delete mode 100644 tests/compile/x86_interrupt_bad_first_arg.rs create mode 100644 tests/compile/x86_interrupt_first_arg_byval.rs diff --git a/src/abi.rs b/src/abi.rs index 4d5aa5e9486..56662a3d1e7 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -16,8 +16,6 @@ use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; use rustc_target::spec::Arch; use crate::builder::Builder; -#[cfg(feature = "master")] -use crate::common::type_is_pointer; use crate::context::CodegenCx; use crate::type_of::LayoutGccExt; @@ -153,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) => { @@ -179,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()) @@ -279,33 +299,3 @@ pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option( - conv: CanonAbi, - arguments_type: &[Type<'gcc>], -) -> bool { - matches!(conv, CanonAbi::Interrupt(InterruptKind::X86)) - && arguments_type.first().is_none_or(|ty| !type_is_pointer(*ty)) -} - -/// Convenience wrapper around [`x86_interrupt_first_arg_is_invalid`] for callers that only -/// hold the `FnAbi` and must lower it themselves. Short-circuits before lowering for the -/// common non-`x86-interrupt` case. -#[cfg(feature = "master")] -pub fn x86_interrupt_has_invalid_first_arg<'gcc, 'tcx>( - cx: &CodegenCx<'gcc, 'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, -) -> bool { - matches!(fn_abi.conv, CanonAbi::Interrupt(InterruptKind::X86)) - && x86_interrupt_first_arg_is_invalid(fn_abi.conv, &fn_abi.gcc_type(cx).arguments_type) -} diff --git a/src/declare.rs b/src/declare.rs index 06213da55be..2d97c10c5f9 100644 --- a/src/declare.rs +++ b/src/declare.rs @@ -7,8 +7,6 @@ use rustc_span::Symbol; use rustc_target::callconv::FnAbi; use crate::abi::FnAbiGccExt; -#[cfg(feature = "master")] -use crate::abi::x86_interrupt_first_arg_is_invalid; use crate::context::CodegenCx; impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { @@ -112,18 +110,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Function<'gcc> { - // Lower the ABI once and reuse it below, including for the `x86-interrupt` check, - // instead of lowering it a second time inside a helper. let fn_abi_gcc = fn_abi.gcc_type(self); #[cfg(feature = "master")] - let conv = if x86_interrupt_first_arg_is_invalid(fn_abi.conv, &fn_abi_gcc.arguments_type) { - // GCC rejects an `x86-interrupt` function whose first argument is not a - // pointer. Drop the calling-convention attribute so libgccjit does not abort; - // a clean error is emitted in `predefine_fn` instead (issue #833). - None - } else { - fn_abi.gcc_cconv(self) - }; + let conv = fn_abi.gcc_cconv(self); #[cfg(not(feature = "master"))] let conv = None; let func = declare_raw_fn( diff --git a/src/errors.rs b/src/errors.rs index e0459325f9e..de633d3bdde 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -30,13 +30,3 @@ pub(crate) struct NulBytesInAsm { #[primary_span] pub span: Span, } - -#[cfg(feature = "master")] -#[derive(Diagnostic)] -#[diag( - "the GCC backend requires the first argument of an `x86-interrupt` function to be a pointer" -)] -pub(crate) struct X86InterruptBadFirstArg { - #[primary_span] - pub span: Span, -} diff --git a/src/mono_item.rs b/src/mono_item.rs index 148ce4cf092..d5874779021 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -10,11 +10,7 @@ use rustc_middle::mono::Visibility; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; -#[cfg(feature = "master")] -use crate::abi::x86_interrupt_has_invalid_first_arg; use crate::context::CodegenCx; -#[cfg(feature = "master")] -use crate::errors::X86InterruptBadFirstArg; use crate::type_of::LayoutGccExt; use crate::{attributes, base}; @@ -55,12 +51,6 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { assert!(!instance.args.has_infer()); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); - #[cfg(feature = "master")] - if x86_interrupt_has_invalid_first_arg(self, fn_abi) { - self.tcx - .dcx() - .emit_err(X86InterruptBadFirstArg { span: self.tcx.def_span(instance.def_id()) }); - } self.linkage.set(base::linkage_to_gcc(linkage)); let decl = self.declare_fn(symbol_name, fn_abi); //let attrs = self.tcx.codegen_instance_attrs(instance.def); diff --git a/tests/compile/x86_interrupt_bad_first_arg.rs b/tests/compile/x86_interrupt_bad_first_arg.rs deleted file mode 100644 index ee0abeed8e0..00000000000 --- a/tests/compile/x86_interrupt_bad_first_arg.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Compiler: -// status: error -// stderr: -// error: the GCC backend requires the first argument of an `x86-interrupt` function to be a pointer -// ... - -// Test that an `x86-interrupt` function whose first argument is not a pointer emits a -// clean error instead of an internal libgccjit error (issue #833). - -#![feature(abi_x86_interrupt)] - -pub extern "x86-interrupt" fn f(_a: i64) {} - -fn main() { - // Take the function's address so that it is codegened. - let _f: extern "x86-interrupt" fn(i64) = f; -} diff --git a/tests/compile/x86_interrupt_first_arg_byval.rs b/tests/compile/x86_interrupt_first_arg_byval.rs new file mode 100644 index 00000000000..ef1e97140ca --- /dev/null +++ b/tests/compile/x86_interrupt_first_arg_byval.rs @@ -0,0 +1,21 @@ +// Compiler: + +// Test that an `x86-interrupt` function whose first argument is passed by value +// emits a pointer-shaped GCC parameter instead of tripping libgccjit (issue #833). + +#![feature(abi_x86_interrupt)] + +#[repr(C)] +pub struct Frame { + ip: u64, +} + +pub extern "x86-interrupt" fn scalar(_a: i64) {} + +pub extern "x86-interrupt" fn aggregate(_frame: Frame) {} + +fn main() { + // Take the functions' addresses so that they are codegened. + let _scalar: extern "x86-interrupt" fn(i64) = scalar; + let _aggregate: extern "x86-interrupt" fn(Frame) = aggregate; +} diff --git a/tests/lang_tests.rs b/tests/lang_tests.rs index 64b7d380116..f3b4ad34bc9 100644 --- a/tests/lang_tests.rs +++ b/tests/lang_tests.rs @@ -216,7 +216,7 @@ fn compile_tests(tempdir: PathBuf, current_dir: String) { "asm_nul_byte.rs", "global_asm_nul_byte.rs", "naked_asm_nul_byte.rs", - "x86_interrupt_bad_first_arg.rs", + "x86_interrupt_first_arg_byval.rs", ], ); } From 516f0554eb1df72f315a549da974a97c5185afad Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sun, 5 Jul 2026 17:01:52 -0300 Subject: [PATCH 8/8] Compile x86 interrupt functions with general registers --- src/attributes.rs | 85 ++++++++++++------- src/callee.rs | 2 +- src/intrinsic/mod.rs | 2 +- src/mono_item.rs | 2 +- .../compile/x86_interrupt_first_arg_byval.rs | 11 +-- 5 files changed, 60 insertions(+), 42 deletions(-) diff --git a/src/attributes.rs b/src/attributes.rs index ce1877b308e..83d0c27d519 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -2,6 +2,8 @@ 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")] @@ -9,6 +11,7 @@ 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; @@ -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); @@ -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::>(); + #[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::>(); - 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::>() - .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::>() + .join(",") + }; if !target_features.is_empty() { #[cfg(feature = "master")] match cx.sess().target.arch { diff --git a/src/callee.rs b/src/callee.rs index 00f095ed543..d3f412180da 100644 --- a/src/callee.rs +++ b/src/callee.rs @@ -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")] { diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index dc45fc49a79..6f1417ad412 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -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 }; diff --git a/src/mono_item.rs b/src/mono_item.rs index d5874779021..622b0613454 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -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 diff --git a/tests/compile/x86_interrupt_first_arg_byval.rs b/tests/compile/x86_interrupt_first_arg_byval.rs index ef1e97140ca..4b6bbd48f7a 100644 --- a/tests/compile/x86_interrupt_first_arg_byval.rs +++ b/tests/compile/x86_interrupt_first_arg_byval.rs @@ -1,9 +1,10 @@ // Compiler: -// Test that an `x86-interrupt` function whose first argument is passed by value -// emits a pointer-shaped GCC parameter instead of tripping libgccjit (issue #833). +// 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 { @@ -13,9 +14,3 @@ pub struct Frame { pub extern "x86-interrupt" fn scalar(_a: i64) {} pub extern "x86-interrupt" fn aggregate(_frame: Frame) {} - -fn main() { - // Take the functions' addresses so that they are codegened. - let _scalar: extern "x86-interrupt" fn(i64) = scalar; - let _aggregate: extern "x86-interrupt" fn(Frame) = aggregate; -}