| use std::collections::HashSet; | |
| use std::env; | |
| use std::sync::Arc; | |
| use std::time::Instant; | |
| use gccjit::{CType, Context, FunctionType, GlobalKind}; | |
| use rustc_codegen_ssa::ModuleCodegen; | |
| use rustc_codegen_ssa::base::maybe_create_entry_wrapper; | |
| use rustc_codegen_ssa::mono_item::MonoItemExt; | |
| use rustc_hir::attrs::{AttributeKind, Linkage}; | |
| use rustc_hir::find_attr; | |
| use rustc_middle::dep_graph; | |
| #[cfg(feature = "master")] | |
| use rustc_middle::mono::Visibility; | |
| use rustc_middle::ty::TyCtxt; | |
| use rustc_session::config::DebugInfo; | |
| use rustc_span::Symbol; | |
| #[cfg(feature = "master")] | |
| use rustc_target::spec::SymbolVisibility; | |
| use rustc_target::spec::{Arch, RelocModel}; | |
| use crate::builder::Builder; | |
| use crate::context::CodegenCx; | |
| use crate::{GccContext, LockedTargetInfo, LtoMode, SyncContext, gcc_util, new_context}; | |
| #[cfg(feature = "master")] | |
| pub fn visibility_to_gcc(visibility: Visibility) -> gccjit::Visibility { | |
| match visibility { | |
| Visibility::Default => gccjit::Visibility::Default, | |
| Visibility::Hidden => gccjit::Visibility::Hidden, | |
| Visibility::Protected => gccjit::Visibility::Protected, | |
| } | |
| } | |
| #[cfg(feature = "master")] | |
| pub fn symbol_visibility_to_gcc(visibility: SymbolVisibility) -> gccjit::Visibility { | |
| match visibility { | |
| SymbolVisibility::Hidden => gccjit::Visibility::Hidden, | |
| SymbolVisibility::Protected => gccjit::Visibility::Protected, | |
| SymbolVisibility::Interposable => gccjit::Visibility::Default, | |
| } | |
| } | |
| pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { | |
| match linkage { | |
| Linkage::External => GlobalKind::Imported, | |
| Linkage::AvailableExternally => GlobalKind::Imported, | |
| Linkage::LinkOnceAny => unimplemented!(), | |
| Linkage::LinkOnceODR => unimplemented!(), | |
| Linkage::WeakAny => unimplemented!(), | |
| Linkage::WeakODR => unimplemented!(), | |
| Linkage::Internal => GlobalKind::Internal, | |
| Linkage::ExternalWeak => GlobalKind::Imported, // FIXME(antoyo): should be weak linkage. | |
| Linkage::Common => unimplemented!(), | |
| } | |
| } | |
| pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { | |
| match linkage { | |
| Linkage::External => FunctionType::Exported, | |
| // FIXME(antoyo): set the attribute externally_visible. | |
| Linkage::AvailableExternally => FunctionType::Extern, | |
| Linkage::LinkOnceAny => unimplemented!(), | |
| Linkage::LinkOnceODR => unimplemented!(), | |
| Linkage::WeakAny => FunctionType::Exported, // FIXME(antoyo): should be similar to linkonce. | |
| Linkage::WeakODR => unimplemented!(), | |
| Linkage::Internal => FunctionType::Internal, | |
| Linkage::ExternalWeak => unimplemented!(), | |
| Linkage::Common => unimplemented!(), | |
| } | |
| } | |
| pub fn compile_codegen_unit( | |
| tcx: TyCtxt<'_>, | |
| cgu_name: Symbol, | |
| target_info: LockedTargetInfo, | |
| lto_supported: bool, | |
| ) -> (ModuleCodegen<GccContext>, u64) { | |
| let prof_timer = tcx.prof.generic_activity("codegen_module"); | |
| let start_time = Instant::now(); | |
| let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); | |
| let (module, _) = tcx.dep_graph.with_task( | |
| dep_node, | |
| tcx, | |
| || module_codegen(tcx, cgu_name, target_info, lto_supported), | |
| Some(dep_graph::hash_result), | |
| ); | |
| let time_to_codegen = start_time.elapsed(); | |
| drop(prof_timer); | |
| // We assume that the cost to run GCC on a CGU is proportional to | |
| // the time we needed for codegenning it. | |
| let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64; | |
| fn module_codegen( | |
| tcx: TyCtxt<'_>, | |
| cgu_name: Symbol, | |
| target_info: LockedTargetInfo, | |
| lto_supported: bool, | |
| ) -> ModuleCodegen<GccContext> { | |
| let cgu = tcx.codegen_unit(cgu_name); | |
| // Instantiate monomorphizations without filling out definitions yet... | |
| let context = new_context(tcx); | |
| if tcx.sess.panic_strategy().unwinds() { | |
| context.add_command_line_option("-fexceptions"); | |
| context.add_driver_option("-fexceptions"); | |
| } | |
| let disabled_features: HashSet<_> = tcx | |
| .sess | |
| .opts | |
| .cg | |
| .target_feature | |
| .split(',') | |
| .filter(|feature| feature.starts_with('-')) | |
| .map(|string| &string[1..]) | |
| .collect(); | |
| if !disabled_features.contains("avx") && tcx.sess.target.arch == Arch::X86_64 { | |
| // NOTE: we always enable AVX because the equivalent of llvm.x86.sse2.cmp.pd in GCC for | |
| // SSE2 is multiple builtins, so we use the AVX __builtin_ia32_cmppd instead. | |
| // FIXME(antoyo): use the proper builtins for llvm.x86.sse2.cmp.pd and similar. | |
| context.add_command_line_option("-mavx"); | |
| } | |
| for arg in &tcx.sess.opts.cg.llvm_args { | |
| context.add_command_line_option(arg); | |
| } | |
| // NOTE: This is needed to compile the file src/intrinsic/archs.rs during a bootstrap of rustc. | |
| context.add_command_line_option("-fno-var-tracking-assignments"); | |
| // NOTE: an optimization (https://github.com/rust-lang/rustc_codegen_gcc/issues/53). | |
| context.add_command_line_option("-fno-semantic-interposition"); | |
| // NOTE: Rust relies on LLVM not doing TBAA (https://github.com/rust-lang/unsafe-code-guidelines/issues/292). | |
| context.add_command_line_option("-fno-strict-aliasing"); | |
| // NOTE: Rust relies on LLVM doing wrapping on overflow. | |
| context.add_command_line_option("-fwrapv"); | |
| // NOTE: We need to honor the `#![no_builtins]` attribute to prevent GCC from | |
| // replacing code patterns (like loops) with calls to builtins (like memset). | |
| // The `-fno-tree-loop-distribute-patterns` flag disables the loop distribution pass | |
| // that transforms loops into calls to library functions (memset, memcpy, etc.). | |
| // See GCC handling for more details: | |
| // https://github.com/rust-lang/gcc/blob/efdd0a7290c22f5438d7c5380105d353ee3e8518/gcc/c-family/c-opts.cc#L953 | |
| let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); | |
| if find_attr!(crate_attrs, AttributeKind::NoBuiltins) { | |
| context.add_command_line_option("-fno-tree-loop-distribute-patterns"); | |
| } | |
| if let Some(model) = tcx.sess.code_model() { | |
| use rustc_target::spec::CodeModel; | |
| context.add_command_line_option(match model { | |
| CodeModel::Tiny => "-mcmodel=tiny", | |
| CodeModel::Small => "-mcmodel=small", | |
| CodeModel::Kernel => "-mcmodel=kernel", | |
| CodeModel::Medium => "-mcmodel=medium", | |
| CodeModel::Large => "-mcmodel=large", | |
| }); | |
| } | |
| add_pic_option(&context, tcx.sess.relocation_model()); | |
| let target_cpu = gcc_util::target_cpu(tcx.sess); | |
| if target_cpu != "generic" { | |
| context.add_command_line_option(format!("-march={}", target_cpu)); | |
| } | |
| if tcx | |
| .sess | |
| .opts | |
| .unstable_opts | |
| .function_sections | |
| .unwrap_or(tcx.sess.target.function_sections) | |
| { | |
| context.add_command_line_option("-ffunction-sections"); | |
| context.add_command_line_option("-fdata-sections"); | |
| } | |
| if env::var("CG_GCCJIT_DUMP_RTL").as_deref() == Ok("1") { | |
| context.add_command_line_option("-fdump-rtl-vregs"); | |
| } | |
| if env::var("CG_GCCJIT_DUMP_RTL_ALL").as_deref() == Ok("1") { | |
| context.add_command_line_option("-fdump-rtl-all"); | |
| } | |
| if env::var("CG_GCCJIT_DUMP_TREE_ALL").as_deref() == Ok("1") { | |
| context.add_command_line_option("-fdump-tree-all-eh"); | |
| } | |
| if env::var("CG_GCCJIT_DUMP_IPA_ALL").as_deref() == Ok("1") { | |
| context.add_command_line_option("-fdump-ipa-all-eh"); | |
| } | |
| if env::var("CG_GCCJIT_DUMP_CODE").as_deref() == Ok("1") { | |
| context.set_dump_code_on_compile(true); | |
| } | |
| if env::var("CG_GCCJIT_DUMP_GIMPLE").as_deref() == Ok("1") { | |
| context.set_dump_initial_gimple(true); | |
| } | |
| if env::var("CG_GCCJIT_DUMP_EVERYTHING").as_deref() == Ok("1") { | |
| context.set_dump_everything(true); | |
| } | |
| if env::var("CG_GCCJIT_KEEP_INTERMEDIATES").as_deref() == Ok("1") { | |
| context.set_keep_intermediates(true); | |
| } | |
| if env::var("CG_GCCJIT_VERBOSE").as_deref() == Ok("1") { | |
| context.add_driver_option("-v"); | |
| } | |
| // NOTE: The codegen generates unreachable blocks. | |
| context.set_allow_unreachable_blocks(true); | |
| { | |
| // FIXME: to make it less error-prone (calling get_target_info() will add the flag | |
| // -fsyntax-only), forbid the compilation when get_target_info() is called on a | |
| // context. | |
| let f16_type_supported = target_info.supports_target_dependent_type(CType::Float16); | |
| let f32_type_supported = target_info.supports_target_dependent_type(CType::Float32); | |
| let f64_type_supported = target_info.supports_target_dependent_type(CType::Float64); | |
| let f128_type_supported = target_info.supports_target_dependent_type(CType::Float128); | |
| let u128_type_supported = target_info.supports_target_dependent_type(CType::UInt128t); | |
| // FIXME: improve this to avoid passing that many arguments. | |
| let mut cx = CodegenCx::new( | |
| &context, | |
| cgu, | |
| tcx, | |
| u128_type_supported, | |
| f16_type_supported, | |
| f32_type_supported, | |
| f64_type_supported, | |
| f128_type_supported, | |
| ); | |
| let mono_items = cgu.items_in_deterministic_order(tcx); | |
| for &(mono_item, data) in &mono_items { | |
| mono_item.predefine::<Builder<'_, '_, '_>>( | |
| &mut cx, | |
| cgu_name.as_str(), | |
| data.linkage, | |
| data.visibility, | |
| ); | |
| } | |
| // ... and now that we have everything pre-defined, fill out those definitions. | |
| for &(mono_item, item_data) in &mono_items { | |
| mono_item.define::<Builder<'_, '_, '_>>(&mut cx, cgu_name.as_str(), item_data); | |
| } | |
| // If this codegen unit contains the main function, also create the | |
| // wrapper here | |
| maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx, cx.codegen_unit); | |
| // Finalize debuginfo | |
| if cx.sess().opts.debuginfo != DebugInfo::None { | |
| cx.debuginfo_finalize(); | |
| } | |
| } | |
| ModuleCodegen::new_regular( | |
| cgu_name.to_string(), | |
| GccContext { | |
| context: Arc::new(SyncContext::new(context)), | |
| relocation_model: tcx.sess.relocation_model(), | |
| lto_supported, | |
| lto_mode: LtoMode::None, | |
| temp_dir: None, | |
| }, | |
| ) | |
| } | |
| (module, cost) | |
| } | |
| pub fn add_pic_option<'gcc>(context: &Context<'gcc>, relocation_model: RelocModel) { | |
| match relocation_model { | |
| rustc_target::spec::RelocModel::Static => { | |
| context.add_command_line_option("-fno-pie"); | |
| context.add_driver_option("-fno-pie"); | |
| } | |
| rustc_target::spec::RelocModel::Pic => { | |
| context.add_command_line_option("-fPIC"); | |
| // NOTE: we use both add_command_line_option and add_driver_option because the usage in | |
| // this module (compile_codegen_unit) requires add_command_line_option while the usage | |
| // in the back::write module (codegen) requires add_driver_option. | |
| context.add_driver_option("-fPIC"); | |
| } | |
| rustc_target::spec::RelocModel::Pie => { | |
| context.add_command_line_option("-fPIE"); | |
| context.add_driver_option("-fPIE"); | |
| } | |
| model => eprintln!("Unsupported relocation model: {:?}", model), | |
| } | |
| } |