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)]
22 extern crate rustc_ast;
23 extern crate rustc_codegen_ssa;
24 extern crate rustc_data_structures;
25 extern crate rustc_errors;
26 extern crate rustc_hir;
27 extern crate rustc_metadata;
28 extern crate rustc_middle;
29 extern crate rustc_session;
30 extern crate rustc_span;
31 extern crate rustc_target;
32 extern crate tempfile;
34 // This prevents duplicating functions and statics that are already part of the host rustc process.
35 #[allow(unused_extern_crates)]
36 extern crate rustc_driver;
59 use std::sync::{Arc, Mutex};
61 use gccjit::{Context, OptimizationLevel, CType};
62 use rustc_ast::expand::allocator::AllocatorKind;
63 use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
64 use rustc_codegen_ssa::base::codegen_crate;
65 use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryFn};
66 use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
67 use rustc_codegen_ssa::target_features::supported_target_features;
68 use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
69 use rustc_data_structures::fx::FxHashMap;
70 use rustc_errors::{ErrorGuaranteed, Handler};
71 use rustc_metadata::EncodedMetadata;
72 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
73 use rustc_middle::ty::TyCtxt;
74 use rustc_middle::ty::query::Providers;
75 use rustc_session::config::{Lto, OptLevel, OutputFilenames};
76 use rustc_session::Session;
77 use rustc_span::Symbol;
78 use rustc_span::fatal_error::FatalError;
79 use tempfile::TempDir;
81 pub struct PrintOnPanic<F: Fn() -> String>(pub F);
83 impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
85 if ::std::thread::panicking() {
86 println!("{}", (self.0)());
92 pub struct GccCodegenBackend {
93 supports_128bit_integers: Arc<Mutex<bool>>,
96 impl CodegenBackend for GccCodegenBackend {
97 fn init(&self, sess: &Session) {
98 if sess.lto() != Lto::No {
99 sess.warn("LTO is not supported. You may get a linker error.");
102 let temp_dir = TempDir::new().expect("cannot create temporary directory");
103 let temp_file = temp_dir.into_path().join("result.asm");
104 let check_context = Context::default();
105 check_context.set_print_errors_to_stderr(false);
106 let _int128_ty = check_context.new_c_type(CType::UInt128t);
107 // NOTE: we cannot just call compile() as this would require other files than libgccjit.so.
108 check_context.compile_to_file(gccjit::OutputKind::Assembler, temp_file.to_str().expect("path to str"));
109 *self.supports_128bit_integers.lock().expect("lock") = check_context.get_last_error() == Ok(None);
112 fn provide(&self, providers: &mut Providers) {
113 // FIXME(antoyo) compute list of enabled features from cli flags
114 providers.global_backend_features = |_tcx, ()| vec![];
117 fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool) -> Box<dyn Any> {
118 let target_cpu = target_cpu(tcx.sess);
119 let res = codegen_crate(self.clone(), tcx, target_cpu.to_string(), metadata, need_metadata_module);
124 fn join_codegen(&self, ongoing_codegen: Box<dyn Any>, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
125 let (codegen_results, work_products) = ongoing_codegen
126 .downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
127 .expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
130 Ok((codegen_results, work_products))
133 fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) -> Result<(), ErrorGuaranteed> {
134 use rustc_codegen_ssa::back::link::link_binary;
138 &crate::archive::ArArchiveBuilderBuilder,
144 fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
145 target_features(sess, allow_unstable)
149 impl ExtraBackendMethods for GccCodegenBackend {
150 fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) -> Self::Module {
151 let mut mods = GccContext {
152 context: Context::default(),
154 unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, has_alloc_error_handler); }
158 fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
159 base::compile_codegen_unit(tcx, cgu_name, *self.supports_128bit_integers.lock().expect("lock"))
162 fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel, _features: &[String]) -> TargetMachineFactoryFn<Self> {
163 // TODO(antoyo): set opt level.
169 fn target_cpu<'b>(&self, _sess: &'b Session) -> &'b str {
173 fn tune_cpu<'b>(&self, _sess: &'b Session) -> Option<&'b str> {
179 pub struct ModuleBuffer;
181 impl ModuleBufferMethods for ModuleBuffer {
182 fn data(&self) -> &[u8] {
187 pub struct ThinBuffer;
189 impl ThinBufferMethods for ThinBuffer {
190 fn data(&self) -> &[u8] {
195 pub struct GccContext {
196 context: Context<'static>,
199 unsafe impl Send for GccContext {}
200 // FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here.
201 unsafe impl Sync for GccContext {}
203 impl WriteBackendMethods for GccCodegenBackend {
204 type Module = GccContext;
205 type TargetMachine = ();
206 type ModuleBuffer = ModuleBuffer;
209 type ThinBuffer = ThinBuffer;
211 fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLTOInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> {
212 // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins.
213 // NOTE: implemented elsewhere.
214 // TODO(antoyo): what is implemented elsewhere ^ ?
216 match modules.remove(0) {
217 FatLTOInput::InMemory(module) => module,
218 FatLTOInput::Serialized { .. } => {
222 Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] })
225 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> {
229 fn print_pass_timings(&self) {
233 unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
234 module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
238 fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &mut ModuleCodegen<Self::Module>) -> Result<(), FatalError> {
243 unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
247 unsafe fn codegen(cgcx: &CodegenContext<Self>, diag_handler: &Handler, module: ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
248 back::write::codegen(cgcx, diag_handler, module, config)
251 fn prepare_thin(_module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
255 fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
259 fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
260 back::write::link(cgcx, diag_handler, modules)
264 /// This is the entrypoint for a hot plugged rustc_codegen_gccjit
266 pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
267 Box::new(GccCodegenBackend {
268 supports_128bit_integers: Arc::new(Mutex::new(false)),
272 fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
274 None => OptimizationLevel::None,
277 OptLevel::No => OptimizationLevel::None,
278 OptLevel::Less => OptimizationLevel::Limited,
279 OptLevel::Default => OptimizationLevel::Standard,
280 OptLevel::Aggressive => OptimizationLevel::Aggressive,
281 OptLevel::Size | OptLevel::SizeMin => OptimizationLevel::Limited,
287 fn handle_native(name: &str) -> &str {
288 if name != "native" {
295 pub fn target_cpu(sess: &Session) -> &str {
296 match sess.opts.cg.target_cpu {
297 Some(ref name) => handle_native(name),
298 None => handle_native(sess.target.cpu.as_ref()),
302 pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
303 supported_target_features(sess)
307 if sess.is_nightly_build() || allow_unstable || gate.is_none() { Some(feature) } else { None }
311 // TODO(antoyo): implement a way to get enabled feature in libgccjit.
312 // Probably using the equivalent of __builtin_cpu_supports.
313 #[cfg(feature="master")]
315 _feature.contains("sse") || _feature.contains("avx")
317 #[cfg(not(feature="master"))]
322 adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512gfni,
323 avx512ifma, avx512pf, avx512vaes, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpclmulqdq,
324 avx512vpopcntdq, bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
325 sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, xsave, xsavec, xsaveopt, xsaves
329 .map(|feature| Symbol::intern(feature))