| #[cfg(feature = "master")] | |
| use gccjit::FnAttribute; | |
| use gccjit::Function; | |
| #[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; | |
| #[cfg(feature = "master")] | |
| use rustc_target::spec::Arch; | |
| use crate::context::CodegenCx; | |
| use crate::gcc_util::to_gcc_features; | |
| /// 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 recursively_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 recursively_inline(cx, instance) { | |
| Some(FnAttribute::Inline) | |
| } else { | |
| Some(FnAttribute::AlwaysInline) | |
| } | |
| } | |
| InlineAttr::Hint => Some(FnAttribute::Inline), | |
| InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline), | |
| InlineAttr::Never => { | |
| if cx.sess().target.arch != Arch::AmdGpu { | |
| Some(FnAttribute::NoInline) | |
| } else { | |
| None | |
| } | |
| } | |
| InlineAttr::None => None, | |
| } | |
| } | |
| /// 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>, | |
| ) { | |
| let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def); | |
| #[cfg(feature = "master")] | |
| { | |
| let inline = if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { | |
| InlineAttr::Never | |
| } else if codegen_fn_attrs.inline == InlineAttr::None | |
| && instance.def.requires_inline(cx.tcx) | |
| { | |
| InlineAttr::Hint | |
| } else { | |
| codegen_fn_attrs.inline | |
| }; | |
| if let Some(attr) = inline_attr(cx, inline, instance) { | |
| if let FnAttribute::AlwaysInline = attr { | |
| func.add_attribute(FnAttribute::Inline); | |
| } | |
| func.add_attribute(attr); | |
| } | |
| if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { | |
| func.add_attribute(FnAttribute::Cold); | |
| } | |
| if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) { | |
| func.add_attribute(FnAttribute::Pure); | |
| } | |
| if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) { | |
| func.add_attribute(FnAttribute::Const); | |
| } | |
| } | |
| 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", // TODO(antoyo): support removing feature. | |
| InstructionSetAttr::ArmT32 => "thumb-mode", | |
| })) | |
| .collect::<Vec<_>>(); | |
| // TODO(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| { | |
| // TODO(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 { | |
| Arch::X86 | Arch::X86_64 | Arch::PowerPC => { | |
| func.add_attribute(FnAttribute::Target(&target_features)) | |
| } | |
| // The target attribute is not supported on other targets in GCC. | |
| _ => (), | |
| } | |
| } | |
| } |