Merge commit '6ba33f5e1189a5ae58fb96ce3546e76b13d090f5' into subtree-update_cg_gcc_2025-05-14

This commit is contained in:
Guillaume Gomez
2025-05-14 13:51:02 +02:00
29 changed files with 909 additions and 247 deletions

View File

@@ -9,9 +9,9 @@ use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf;
#[cfg(feature = "master")]
use rustc_session::config;
#[cfg(feature = "master")]
use rustc_target::callconv::Conv;
use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode};
#[cfg(feature = "master")]
use rustc_target::callconv::{Conv, RiscvInterruptKind};
use crate::builder::Builder;
use crate::context::CodegenCx;
@@ -240,38 +240,57 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
#[cfg(feature = "master")]
pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option<FnAttribute<'gcc>> {
// TODO: handle the calling conventions returning None.
let attribute = match conv {
Conv::C
| Conv::Rust
| Conv::CCmseNonSecureCall
| Conv::CCmseNonSecureEntry
| Conv::RiscvInterrupt { .. } => return None,
Conv::Cold => return None,
Conv::C | Conv::Rust => return None,
Conv::CCmseNonSecureCall => {
if arch == "arm" {
FnAttribute::ArmCmseNonsecureCall
} else {
return None;
}
}
Conv::CCmseNonSecureEntry => {
if arch == "arm" {
FnAttribute::ArmCmseNonsecureEntry
} else {
return None;
}
}
Conv::Cold => FnAttribute::Cold,
// NOTE: the preserve attributes are not yet implemented in GCC:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899
Conv::PreserveMost => return None,
Conv::PreserveAll => return None,
Conv::GpuKernel => {
// TODO(antoyo): remove clippy allow attribute when this is implemented.
#[allow(clippy::if_same_then_else)]
if arch == "amdgpu" {
return None;
FnAttribute::GcnAmdGpuHsaKernel
} else if arch == "nvptx64" {
return None;
FnAttribute::NvptxKernel
} else {
panic!("Architecture {} does not support GpuKernel calling convention", arch);
}
}
Conv::AvrInterrupt => return None,
Conv::AvrNonBlockingInterrupt => return None,
Conv::ArmAapcs => return None,
Conv::Msp430Intr => return None,
Conv::X86Fastcall => return None,
Conv::X86Intr => return None,
Conv::X86Stdcall => return None,
Conv::X86ThisCall => return None,
// TODO(antoyo): check if those AVR attributes are mapped correctly.
Conv::AvrInterrupt => FnAttribute::AvrSignal,
Conv::AvrNonBlockingInterrupt => FnAttribute::AvrInterrupt,
Conv::ArmAapcs => FnAttribute::ArmPcs("aapcs"),
Conv::Msp430Intr => FnAttribute::Msp430Interrupt,
Conv::RiscvInterrupt { kind } => {
let kind = match kind {
RiscvInterruptKind::Machine => "machine",
RiscvInterruptKind::Supervisor => "supervisor",
};
FnAttribute::RiscvInterrupt(kind)
}
Conv::X86Fastcall => FnAttribute::X86FastCall,
Conv::X86Intr => FnAttribute::X86Interrupt,
Conv::X86Stdcall => FnAttribute::X86Stdcall,
Conv::X86ThisCall => FnAttribute::X86ThisCall,
// NOTE: the vectorcall calling convention is not yet implemented in GCC:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485
Conv::X86VectorCall => return None,
Conv::X86_64SysV => FnAttribute::SysvAbi,
Conv::X86_64Win64 => FnAttribute::MsAbi,
Conv::X86_64SysV => FnAttribute::X86SysvAbi,
Conv::X86_64Win64 => FnAttribute::X86MsAbi,
};
Some(attribute)
}

View File

@@ -6,21 +6,69 @@ use rustc_attr_parsing::InlineAttr;
use rustc_attr_parsing::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 crate::context::CodegenCx;
use crate::gcc_util::to_gcc_features;
/// Get GCC attribute for the provided inline heuristic.
/// Checks if the function `instance` is recursively inline.
/// Returns `false` if a functions is guaranteed to be non-recursive, and `true` if it *might* be recursive.
#[cfg(feature = "master")]
fn resursively_inline<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
instance: ty::Instance<'tcx>,
) -> bool {
// No body, so we can't check if this is recursively inline, so we assume it is.
if !cx.tcx.is_mir_available(instance.def_id()) {
return true;
}
// `expect_local` ought to never fail: we should be checking a function within this codegen unit.
let body = cx.tcx.optimized_mir(instance.def_id());
for block in body.basic_blocks.iter() {
let Some(ref terminator) = block.terminator else { continue };
// I assume that the recursive-inline issue applies only to functions, and not to drops.
// In principle, a recursive, `#[inline(always)]` drop could(?) exist, but I don't think it does.
let TerminatorKind::Call { ref func, .. } = terminator.kind else { continue };
let Some((def, _args)) = func.const_fn_def() else { continue };
// Check if the called function is recursively inline.
if matches!(
cx.tcx.codegen_fn_attrs(def).inline,
InlineAttr::Always | InlineAttr::Force { .. }
) {
return true;
}
}
false
}
/// Get GCC attribute for the provided inline heuristic, attached to `instance`.
#[cfg(feature = "master")]
#[inline]
fn inline_attr<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
inline: InlineAttr,
instance: ty::Instance<'tcx>,
) -> Option<FnAttribute<'gcc>> {
match inline {
InlineAttr::Always => {
// We can't simply always return `always_inline` unconditionally.
// It is *NOT A HINT* and does not work for recursive functions.
//
// So, it can only be applied *if*:
// The current function does not call any functions marked `#[inline(always)]`.
//
// That prevents issues steming from recursive `#[inline(always)]` at a *relatively* small cost.
// We *only* need to check all the terminators of a function marked with this attribute.
if resursively_inline(cx, instance) {
Some(FnAttribute::Inline)
} else {
Some(FnAttribute::AlwaysInline)
}
}
InlineAttr::Hint => Some(FnAttribute::Inline),
InlineAttr::Always | InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline),
InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline),
InlineAttr::Never => {
if cx.sess().target.arch != "amdgpu" {
Some(FnAttribute::NoInline)
@@ -52,7 +100,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
} else {
codegen_fn_attrs.inline
};
if let Some(attr) = inline_attr(cx, inline) {
if let Some(attr) = inline_attr(cx, inline, instance) {
if let FnAttribute::AlwaysInline = attr {
func.add_attribute(FnAttribute::Inline);
}
@@ -88,14 +136,8 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
let target_features = function_features
.iter()
.filter_map(|feature| {
// FIXME(antoyo): for some reasons, disabling SSE results in the following error when
// compiling Rust for Linux:
// SSE register return with SSE disabled
// TODO(antoyo): support soft-float and retpoline-external-thunk.
if feature.contains("soft-float")
|| feature.contains("retpoline-external-thunk")
|| *feature == "-sse"
{
// TODO(antoyo): support soft-float.
if feature.contains("soft-float") {
return None;
}

View File

@@ -593,7 +593,7 @@ fn thin_lto(
Ok((opt_jobs, copy_jobs))
}
pub unsafe fn optimize_thin_module(
pub fn optimize_thin_module(
thin_module: ThinModule<GccCodegenBackend>,
_cgcx: &CodegenContext<GccCodegenBackend>,
) -> Result<ModuleCodegen<GccContext>, FatalError> {

View File

@@ -14,7 +14,7 @@ use crate::base::add_pic_option;
use crate::errors::CopyBitcode;
use crate::{GccCodegenBackend, GccContext};
pub(crate) unsafe fn codegen(
pub(crate) fn codegen(
cgcx: &CodegenContext<GccCodegenBackend>,
dcx: DiagCtxtHandle<'_>,
module: ModuleCodegen<GccContext>,

View File

@@ -568,11 +568,28 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
) {
let mut gcc_cases = vec![];
let typ = self.val_ty(value);
for (on_val, dest) in cases {
let on_val = self.const_uint_big(typ, on_val);
gcc_cases.push(self.context.new_case(on_val, on_val, dest));
// FIXME(FractalFir): This is a workaround for a libgccjit limitation.
// Currently, libgccjit can't directly create 128 bit integers.
// Since switch cases must be values, and casts are not constant, we can't use 128 bit switch cases.
// In such a case, we will simply fall back to an if-ladder.
// This *may* be slower than a native switch, but a slow working solution is better than none at all.
if typ.is_i128(self) || typ.is_u128(self) {
for (on_val, dest) in cases {
let on_val = self.const_uint_big(typ, on_val);
let is_case =
self.context.new_comparison(self.location, ComparisonOp::Equals, value, on_val);
let next_block = self.current_func().new_block("case");
self.block.end_with_conditional(self.location, is_case, dest, next_block);
self.block = next_block;
}
self.block.end_with_jump(self.location, default_block);
} else {
for (on_val, dest) in cases {
let on_val = self.const_uint_big(typ, on_val);
gcc_cases.push(self.context.new_case(on_val, on_val, dest));
}
self.block.end_with_switch(self.location, value, default_block, &gcc_cases);
}
self.block.end_with_switch(self.location, value, default_block, &gcc_cases);
}
#[cfg(feature = "master")]

View File

@@ -191,13 +191,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
// TODO(antoyo): check if it's okay that no link_section is set.
let typ = self.val_ty(cv).get_aligned(align.bytes());
let global = self.declare_private_global(&name[..], typ);
global
self.declare_private_global(&name[..], typ)
}
_ => {
let typ = self.val_ty(cv).get_aligned(align.bytes());
let global = self.declare_unnamed_global(typ);
global
self.declare_unnamed_global(typ)
}
};
global.global_set_initializer_rvalue(cv);

View File

@@ -289,7 +289,7 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
) -> Self::DILocation {
let pos = span.lo();
let DebugLoc { file, line, col } = self.lookup_debug_loc(pos);
let loc = match file.name {
match file.name {
rustc_span::FileName::Real(ref name) => match *name {
rustc_span::RealFileName::LocalPath(ref name) => {
if let Some(name) = name.to_str() {
@@ -314,7 +314,6 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
}
},
_ => Location::null(),
};
loc
}
}
}

View File

@@ -157,6 +157,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
///
/// If theres a value with the same name already declared, the function will
/// update the declaration and return existing Value instead.
#[allow(clippy::let_and_return)]
fn declare_raw_fn<'gcc>(
cx: &CodegenCx<'gcc, '_>,
name: &str,

File diff suppressed because it is too large Load Diff

View File

@@ -4,9 +4,7 @@ mod simd;
#[cfg(feature = "master")]
use std::iter;
#[cfg(feature = "master")]
use gccjit::FunctionType;
use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp};
use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp};
#[cfg(feature = "master")]
use rustc_abi::ExternAbi;
use rustc_abi::{BackendRepr, HasDataLayout};
@@ -132,6 +130,72 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
Some(cx.context.get_builtin_function(gcc_name))
}
// TODO(antoyo): We can probably remove these and use the fallback intrinsic implementation.
fn get_simple_function<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
name: Symbol,
) -> Option<Function<'gcc>> {
let (return_type, parameters, func_name) = match name {
sym::minimumf32 => {
let parameters = [
cx.context.new_parameter(None, cx.float_type, "a"),
cx.context.new_parameter(None, cx.float_type, "b"),
];
(cx.float_type, parameters, "fminimumf")
}
sym::minimumf64 => {
let parameters = [
cx.context.new_parameter(None, cx.double_type, "a"),
cx.context.new_parameter(None, cx.double_type, "b"),
];
(cx.double_type, parameters, "fminimum")
}
sym::minimumf128 => {
let f128_type = cx.type_f128();
// GCC doesn't have the intrinsic we want so we use the compiler-builtins one
// https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html
let parameters = [
cx.context.new_parameter(None, f128_type, "a"),
cx.context.new_parameter(None, f128_type, "b"),
];
(f128_type, parameters, "fminimumf128")
}
sym::maximumf32 => {
let parameters = [
cx.context.new_parameter(None, cx.float_type, "a"),
cx.context.new_parameter(None, cx.float_type, "b"),
];
(cx.float_type, parameters, "fmaximumf")
}
sym::maximumf64 => {
let parameters = [
cx.context.new_parameter(None, cx.double_type, "a"),
cx.context.new_parameter(None, cx.double_type, "b"),
];
(cx.double_type, parameters, "fmaximum")
}
sym::maximumf128 => {
let f128_type = cx.type_f128();
// GCC doesn't have the intrinsic we want so we use the compiler-builtins one
// https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html
let parameters = [
cx.context.new_parameter(None, f128_type, "a"),
cx.context.new_parameter(None, f128_type, "b"),
];
(f128_type, parameters, "fmaximumf128")
}
_ => return None,
};
Some(cx.context.new_function(
None,
FunctionType::Extern,
return_type,
&parameters,
func_name,
false,
))
}
impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
fn codegen_intrinsic_call(
&mut self,
@@ -160,6 +224,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout);
let simple = get_simple_intrinsic(self, name);
let simple_func = get_simple_function(self, name);
// FIXME(tempdragon): Re-enable `clippy::suspicious_else_formatting` if the following issue is solved:
// https://github.com/rust-lang/rust-clippy/issues/12497
@@ -167,7 +232,15 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
#[allow(clippy::suspicious_else_formatting)]
let value = match name {
_ if simple.is_some() => {
let func = simple.expect("simple function");
let func = simple.expect("simple intrinsic function");
self.cx.context.new_call(
self.location,
func,
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
)
}
_ if simple_func.is_some() => {
let func = simple_func.expect("simple function");
self.cx.context.new_call(
self.location,
func,

View File

@@ -16,7 +16,7 @@
#![allow(internal_features)]
#![doc(rust_logo)]
#![feature(rustdoc_internals)]
#![feature(rustc_private, decl_macro, never_type, trusted_len, let_chains)]
#![feature(rustc_private, decl_macro, never_type, trusted_len)]
#![allow(broken_intra_doc_links)]
#![recursion_limit = "256"]
#![warn(rust_2018_idioms)]
@@ -454,7 +454,7 @@ impl WriteBackendMethods for GccCodegenBackend {
}
/// This is the entrypoint for a hot plugged rustc_codegen_gccjit
#[no_mangle]
#[unsafe(no_mangle)]
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
#[cfg(feature = "master")]
let info = {