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<'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.
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 ModuleBuffer = ModuleBuffer;
205 type ThinBuffer = ThinBuffer;
207 fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLTOInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> {
208 // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins.
209 // NOTE: implemented elsewhere.
210 // TODO(antoyo): what is implemented elsewhere ^ ?
212 match modules.remove(0) {
213 FatLTOInput::InMemory(module) => module,
214 FatLTOInput::Serialized { .. } => {
218 Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] })
221 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> {
225 fn print_pass_timings(&self) {
229 unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
230 module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
234 fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &mut ModuleCodegen<Self::Module>) -> Result<(), FatalError> {
239 unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
243 unsafe fn codegen(cgcx: &CodegenContext<Self>, diag_handler: &Handler, module: ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
244 back::write::codegen(cgcx, diag_handler, module, config)
247 fn prepare_thin(_module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
251 fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
255 fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
256 back::write::link(cgcx, diag_handler, modules)
260 /// This is the entrypoint for a hot plugged rustc_codegen_gccjit
262 pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
263 Box::new(GccCodegenBackend {
264 supports_128bit_integers: Arc::new(Mutex::new(false)),
268 fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
270 None => OptimizationLevel::None,
273 OptLevel::No => OptimizationLevel::None,
274 OptLevel::Less => OptimizationLevel::Limited,
275 OptLevel::Default => OptimizationLevel::Standard,
276 OptLevel::Aggressive => OptimizationLevel::Aggressive,
277 OptLevel::Size | OptLevel::SizeMin => OptimizationLevel::Limited,
283 fn handle_native(name: &str) -> &str {
284 if name != "native" {
291 pub fn target_cpu(sess: &Session) -> &str {
292 match sess.opts.cg.target_cpu {
293 Some(ref name) => handle_native(name),
294 None => handle_native(sess.target.cpu.as_ref()),
298 pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
299 supported_target_features(sess)
303 if sess.is_nightly_build() || allow_unstable || gate.is_none() { Some(feature) } else { None }
307 // TODO(antoyo): implement a way to get enabled feature in libgccjit.
308 // Probably using the equivalent of __builtin_cpu_supports.
309 #[cfg(feature="master")]
311 _feature.contains("sse") || _feature.contains("avx")
313 #[cfg(not(feature="master"))]
318 adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512ifma,
319 avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
320 bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
321 sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
325 .map(|feature| Symbol::intern(feature))