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