2 * TODO(antoyo): implement equality in libgccjit based on https://zpz.github.io/blog/overloading-equality-operator-in-cpp-class-hierarchy/ (for type equality?)
3 * TODO(antoyo): support #[inline] attributes.
4 * TODO(antoyo): support LTO (gcc's equivalent to Thin LTO is enabled by -fwhopr: https://stackoverflow.com/questions/64954525/does-gcc-have-thin-lto).
6 * TODO(antoyo): remove the patches.
12 associated_type_bounds,
17 #![allow(broken_intra_doc_links)]
18 #![recursion_limit="256"]
19 #![warn(rust_2018_idioms)]
20 #![warn(unused_lifetimes)]
21 #![deny(rustc::untranslatable_diagnostic)]
22 #![deny(rustc::diagnostic_outside_of_impl)]
24 extern crate rustc_apfloat;
25 extern crate rustc_ast;
26 extern crate rustc_codegen_ssa;
27 extern crate rustc_data_structures;
28 extern crate rustc_errors;
29 extern crate rustc_hir;
30 extern crate rustc_macros;
31 extern crate rustc_metadata;
32 extern crate rustc_middle;
33 extern crate rustc_session;
34 extern crate rustc_span;
35 extern crate rustc_target;
36 extern crate tempfile;
38 // This prevents duplicating functions and statics that are already part of the host rustc process.
39 #[allow(unused_extern_crates)]
40 extern crate rustc_driver;
64 use std::sync::{Arc, Mutex};
66 use crate::errors::LTONotSupported;
67 use gccjit::{Context, OptimizationLevel, CType};
68 use rustc_ast::expand::allocator::AllocatorKind;
69 use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
70 use rustc_codegen_ssa::base::codegen_crate;
71 use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryFn};
72 use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
73 use rustc_codegen_ssa::target_features::supported_target_features;
74 use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
75 use rustc_data_structures::fx::FxHashMap;
76 use rustc_errors::{ErrorGuaranteed, Handler};
77 use rustc_metadata::EncodedMetadata;
78 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
79 use rustc_middle::ty::TyCtxt;
80 use rustc_middle::ty::query::Providers;
81 use rustc_session::config::{Lto, OptLevel, OutputFilenames};
82 use rustc_session::Session;
83 use rustc_span::Symbol;
84 use rustc_span::fatal_error::FatalError;
85 use tempfile::TempDir;
87 pub struct PrintOnPanic<F: Fn() -> String>(pub F);
89 impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
91 if ::std::thread::panicking() {
92 println!("{}", (self.0)());
98 pub struct GccCodegenBackend {
99 supports_128bit_integers: Arc<Mutex<bool>>,
102 impl CodegenBackend for GccCodegenBackend {
103 fn init(&self, sess: &Session) {
104 if sess.lto() != Lto::No {
105 sess.emit_warning(LTONotSupported {});
108 let temp_dir = TempDir::new().expect("cannot create temporary directory");
109 let temp_file = temp_dir.into_path().join("result.asm");
110 let check_context = Context::default();
111 check_context.set_print_errors_to_stderr(false);
112 let _int128_ty = check_context.new_c_type(CType::UInt128t);
113 // NOTE: we cannot just call compile() as this would require other files than libgccjit.so.
114 check_context.compile_to_file(gccjit::OutputKind::Assembler, temp_file.to_str().expect("path to str"));
115 *self.supports_128bit_integers.lock().expect("lock") = check_context.get_last_error() == Ok(None);
118 fn provide(&self, providers: &mut Providers) {
119 // FIXME(antoyo) compute list of enabled features from cli flags
120 providers.global_backend_features = |_tcx, ()| vec![];
123 fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool) -> Box<dyn Any> {
124 let target_cpu = target_cpu(tcx.sess);
125 let res = codegen_crate(self.clone(), tcx, target_cpu.to_string(), metadata, need_metadata_module);
130 fn join_codegen(&self, ongoing_codegen: Box<dyn Any>, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
131 let (codegen_results, work_products) = ongoing_codegen
132 .downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
133 .expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
136 Ok((codegen_results, work_products))
139 fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) -> Result<(), ErrorGuaranteed> {
140 use rustc_codegen_ssa::back::link::link_binary;
144 &crate::archive::ArArchiveBuilderBuilder,
150 fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
151 target_features(sess, allow_unstable)
155 impl ExtraBackendMethods for GccCodegenBackend {
156 fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) -> Self::Module {
157 let mut mods = GccContext {
158 context: Context::default(),
160 unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, has_alloc_error_handler); }
164 fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
165 base::compile_codegen_unit(tcx, cgu_name, *self.supports_128bit_integers.lock().expect("lock"))
168 fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel, _features: &[String]) -> TargetMachineFactoryFn<Self> {
169 // TODO(antoyo): set opt level.
175 fn target_cpu<'b>(&self, _sess: &'b Session) -> &'b str {
179 fn tune_cpu<'b>(&self, _sess: &'b Session) -> Option<&'b str> {
185 pub struct ModuleBuffer;
187 impl ModuleBufferMethods for ModuleBuffer {
188 fn data(&self) -> &[u8] {
193 pub struct ThinBuffer;
195 impl ThinBufferMethods for ThinBuffer {
196 fn data(&self) -> &[u8] {
201 pub struct GccContext {
202 context: Context<'static>,
205 unsafe impl Send for GccContext {}
206 // FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here.
207 unsafe impl Sync for GccContext {}
209 impl WriteBackendMethods for GccCodegenBackend {
210 type Module = GccContext;
211 type TargetMachine = ();
212 type ModuleBuffer = ModuleBuffer;
215 type ThinBuffer = ThinBuffer;
217 fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLTOInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> {
218 // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins.
219 // NOTE: implemented elsewhere.
220 // TODO(antoyo): what is implemented elsewhere ^ ?
222 match modules.remove(0) {
223 FatLTOInput::InMemory(module) => module,
224 FatLTOInput::Serialized { .. } => {
228 Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] })
231 fn run_thin_lto(_cgcx: &CodegenContext<Self>, _modules: Vec<(String, Self::ThinBuffer)>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> {
235 fn print_pass_timings(&self) {
239 unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
240 module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
244 fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &mut ModuleCodegen<Self::Module>) -> Result<(), FatalError> {
249 unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
253 unsafe fn codegen(cgcx: &CodegenContext<Self>, diag_handler: &Handler, module: ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
254 back::write::codegen(cgcx, diag_handler, module, config)
257 fn prepare_thin(_module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
261 fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
265 fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
266 back::write::link(cgcx, diag_handler, modules)
270 /// This is the entrypoint for a hot plugged rustc_codegen_gccjit
272 pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
273 Box::new(GccCodegenBackend {
274 supports_128bit_integers: Arc::new(Mutex::new(false)),
278 fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
280 None => OptimizationLevel::None,
283 OptLevel::No => OptimizationLevel::None,
284 OptLevel::Less => OptimizationLevel::Limited,
285 OptLevel::Default => OptimizationLevel::Standard,
286 OptLevel::Aggressive => OptimizationLevel::Aggressive,
287 OptLevel::Size | OptLevel::SizeMin => OptimizationLevel::Limited,
293 fn handle_native(name: &str) -> &str {
294 if name != "native" {
301 pub fn target_cpu(sess: &Session) -> &str {
302 match sess.opts.cg.target_cpu {
303 Some(ref name) => handle_native(name),
304 None => handle_native(sess.target.cpu.as_ref()),
308 pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
309 supported_target_features(sess)
313 if sess.is_nightly_build() || allow_unstable || gate.is_none() { Some(feature) } else { None }
317 // TODO(antoyo): implement a way to get enabled feature in libgccjit.
318 // Probably using the equivalent of __builtin_cpu_supports.
319 #[cfg(feature="master")]
321 _feature.contains("sse") || _feature.contains("avx")
323 #[cfg(not(feature="master"))]
328 adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512gfni,
329 avx512ifma, avx512pf, avx512vaes, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpclmulqdq,
330 avx512vpopcntdq, bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
331 sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, xsave, xsavec, xsaveopt, xsaves
335 .map(|feature| Symbol::intern(feature))