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.
9 #![feature(rustc_private, decl_macro, associated_type_bounds, never_type, trusted_len)]
10 #![allow(broken_intra_doc_links)]
11 #![recursion_limit="256"]
12 #![warn(rust_2018_idioms)]
13 #![warn(unused_lifetimes)]
15 extern crate rustc_ast;
16 extern crate rustc_codegen_ssa;
17 extern crate rustc_data_structures;
18 extern crate rustc_errors;
19 extern crate rustc_hir;
20 extern crate rustc_metadata;
21 extern crate rustc_middle;
22 extern crate rustc_session;
23 extern crate rustc_span;
24 extern crate rustc_target;
25 extern crate tempfile;
27 // This prevents duplicating functions and statics that are already part of the host rustc process.
28 #[allow(unused_extern_crates)]
29 extern crate rustc_driver;
52 use std::sync::{Arc, Mutex};
54 use gccjit::{Context, OptimizationLevel, CType};
55 use rustc_ast::expand::allocator::AllocatorKind;
56 use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
57 use rustc_codegen_ssa::base::codegen_crate;
58 use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryFn};
59 use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
60 use rustc_codegen_ssa::target_features::supported_target_features;
61 use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
62 use rustc_data_structures::fx::FxHashMap;
63 use rustc_errors::{ErrorGuaranteed, Handler};
64 use rustc_metadata::EncodedMetadata;
65 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
66 use rustc_middle::ty::TyCtxt;
67 use rustc_middle::ty::query::Providers;
68 use rustc_session::config::{Lto, OptLevel, OutputFilenames};
69 use rustc_session::Session;
70 use rustc_span::Symbol;
71 use rustc_span::fatal_error::FatalError;
72 use tempfile::TempDir;
74 pub struct PrintOnPanic<F: Fn() -> String>(pub F);
76 impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
78 if ::std::thread::panicking() {
79 println!("{}", (self.0)());
85 pub struct GccCodegenBackend {
86 supports_128bit_integers: Arc<Mutex<bool>>,
89 impl CodegenBackend for GccCodegenBackend {
90 fn init(&self, sess: &Session) {
91 if sess.lto() != Lto::No {
92 sess.warn("LTO is not supported. You may get a linker error.");
95 let temp_dir = TempDir::new().expect("cannot create temporary directory");
96 let temp_file = temp_dir.into_path().join("result.asm");
97 let check_context = Context::default();
98 check_context.set_print_errors_to_stderr(false);
99 let _int128_ty = check_context.new_c_type(CType::UInt128t);
100 // NOTE: we cannot just call compile() as this would require other files than libgccjit.so.
101 check_context.compile_to_file(gccjit::OutputKind::Assembler, temp_file.to_str().expect("path to str"));
102 *self.supports_128bit_integers.lock().expect("lock") = check_context.get_last_error() == Ok(None);
105 fn provide(&self, providers: &mut Providers) {
106 // FIXME(antoyo) compute list of enabled features from cli flags
107 providers.global_backend_features = |_tcx, ()| vec![];
110 fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool) -> Box<dyn Any> {
111 let target_cpu = target_cpu(tcx.sess);
112 let res = codegen_crate(self.clone(), tcx, target_cpu.to_string(), metadata, need_metadata_module);
117 fn join_codegen(&self, ongoing_codegen: Box<dyn Any>, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
118 let (codegen_results, work_products) = ongoing_codegen
119 .downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
120 .expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
123 Ok((codegen_results, work_products))
126 fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) -> Result<(), ErrorGuaranteed> {
127 use rustc_codegen_ssa::back::link::link_binary;
129 link_binary::<crate::archive::ArArchiveBuilder<'_>>(
136 fn target_features(&self, sess: &Session) -> Vec<Symbol> {
137 target_features(sess)
141 impl ExtraBackendMethods for GccCodegenBackend {
142 fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) -> Self::Module {
143 let mut mods = GccContext {
144 context: Context::default(),
146 unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, has_alloc_error_handler); }
150 fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
151 base::compile_codegen_unit(tcx, cgu_name, *self.supports_128bit_integers.lock().expect("lock"))
154 fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel, _features: &[String]) -> TargetMachineFactoryFn<Self> {
155 // TODO(antoyo): set opt level.
161 fn target_cpu<'b>(&self, _sess: &'b Session) -> &'b str {
165 fn tune_cpu<'b>(&self, _sess: &'b Session) -> Option<&'b str> {
171 pub struct ModuleBuffer;
173 impl ModuleBufferMethods for ModuleBuffer {
174 fn data(&self) -> &[u8] {
179 pub struct ThinBuffer;
181 impl ThinBufferMethods for ThinBuffer {
182 fn data(&self) -> &[u8] {
187 pub struct GccContext {
188 context: Context<'static>,
191 unsafe impl Send for GccContext {}
192 // FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here.
193 unsafe impl Sync for GccContext {}
195 impl WriteBackendMethods for GccCodegenBackend {
196 type Module = GccContext;
197 type TargetMachine = ();
198 type ModuleBuffer = ModuleBuffer;
201 type ThinBuffer = ThinBuffer;
203 fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLTOInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> {
204 // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins.
205 // NOTE: implemented elsewhere.
206 // TODO(antoyo): what is implemented elsewhere ^ ?
208 match modules.remove(0) {
209 FatLTOInput::InMemory(module) => module,
210 FatLTOInput::Serialized { .. } => {
214 Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] })
217 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> {
221 fn print_pass_timings(&self) {
225 unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
226 module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
230 fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &mut ModuleCodegen<Self::Module>) -> Result<(), FatalError> {
235 unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
239 unsafe fn codegen(cgcx: &CodegenContext<Self>, diag_handler: &Handler, module: ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
240 back::write::codegen(cgcx, diag_handler, module, config)
243 fn prepare_thin(_module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
247 fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
251 fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
252 back::write::link(cgcx, diag_handler, modules)
256 /// This is the entrypoint for a hot plugged rustc_codegen_gccjit
258 pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
259 Box::new(GccCodegenBackend {
260 supports_128bit_integers: Arc::new(Mutex::new(false)),
264 fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
266 None => OptimizationLevel::None,
269 OptLevel::No => OptimizationLevel::None,
270 OptLevel::Less => OptimizationLevel::Limited,
271 OptLevel::Default => OptimizationLevel::Standard,
272 OptLevel::Aggressive => OptimizationLevel::Aggressive,
273 OptLevel::Size | OptLevel::SizeMin => OptimizationLevel::Limited,
279 fn handle_native(name: &str) -> &str {
280 if name != "native" {
287 pub fn target_cpu(sess: &Session) -> &str {
288 match sess.opts.cg.target_cpu {
289 Some(ref name) => handle_native(name),
290 None => handle_native(sess.target.cpu.as_ref()),
294 pub fn target_features(sess: &Session) -> Vec<Symbol> {
295 supported_target_features(sess)
299 if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None }
303 // TODO(antoyo): implement a way to get enabled feature in libgccjit.
304 // Probably using the equivalent of __builtin_cpu_supports.
305 #[cfg(feature="master")]
307 _feature.contains("sse") || _feature.contains("avx")
309 #[cfg(not(feature="master"))]
314 adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512gfni,
315 avx512ifma, avx512pf, avx512vaes, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpclmulqdq,
316 avx512vpopcntdq, bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
317 sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, xsave, xsavec, xsaveopt, xsaves
321 .map(|feature| Symbol::intern(feature))