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, alloc_error_handler_kind: AllocatorKind) -> Self::Module {
157 let mut mods = GccContext {
158 context: Context::default(),
160 unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, alloc_error_handler_kind); }
164 fn compile_codegen_unit(&self, tcx: TyCtxt<'_>, 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.
176 pub struct ModuleBuffer;
178 impl ModuleBufferMethods for ModuleBuffer {
179 fn data(&self) -> &[u8] {
184 pub struct ThinBuffer;
186 impl ThinBufferMethods for ThinBuffer {
187 fn data(&self) -> &[u8] {
192 pub struct GccContext {
193 context: Context<'static>,
196 unsafe impl Send for GccContext {}
197 // FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here.
198 unsafe impl Sync for GccContext {}
200 impl WriteBackendMethods for GccCodegenBackend {
201 type Module = GccContext;
202 type TargetMachine = ();
203 type TargetMachineError = ();
204 type ModuleBuffer = ModuleBuffer;
206 type ThinBuffer = ThinBuffer;
208 fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLTOInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> {
209 // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins.
210 // NOTE: implemented elsewhere.
211 // TODO(antoyo): what is implemented elsewhere ^ ?
213 match modules.remove(0) {
214 FatLTOInput::InMemory(module) => module,
215 FatLTOInput::Serialized { .. } => {
219 Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] })
222 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> {
226 fn print_pass_timings(&self) {
230 unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
231 module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
235 fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &mut ModuleCodegen<Self::Module>) -> Result<(), FatalError> {
240 unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
244 unsafe fn codegen(cgcx: &CodegenContext<Self>, diag_handler: &Handler, module: ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
245 back::write::codegen(cgcx, diag_handler, module, config)
248 fn prepare_thin(_module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
252 fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
256 fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
257 back::write::link(cgcx, diag_handler, modules)
261 /// This is the entrypoint for a hot plugged rustc_codegen_gccjit
263 pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
264 Box::new(GccCodegenBackend {
265 supports_128bit_integers: Arc::new(Mutex::new(false)),
269 fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
271 None => OptimizationLevel::None,
274 OptLevel::No => OptimizationLevel::None,
275 OptLevel::Less => OptimizationLevel::Limited,
276 OptLevel::Default => OptimizationLevel::Standard,
277 OptLevel::Aggressive => OptimizationLevel::Aggressive,
278 OptLevel::Size | OptLevel::SizeMin => OptimizationLevel::Limited,
284 fn handle_native(name: &str) -> &str {
285 if name != "native" {
292 pub fn target_cpu(sess: &Session) -> &str {
293 match sess.opts.cg.target_cpu {
294 Some(ref name) => handle_native(name),
295 None => handle_native(sess.target.cpu.as_ref()),
299 pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
300 supported_target_features(sess)
304 if sess.is_nightly_build() || allow_unstable || gate.is_none() { Some(feature) } else { None }
308 // TODO(antoyo): implement a way to get enabled feature in libgccjit.
309 // Probably using the equivalent of __builtin_cpu_supports.
310 #[cfg(feature="master")]
312 _feature.contains("sse") || _feature.contains("avx")
314 #[cfg(not(feature="master"))]
319 adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512ifma,
320 avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
321 bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
322 sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
326 .map(|feature| Symbol::intern(feature))