1#![allow(rustc::diagnostic_outside_of_impl)]
3#![allow(rustc::untranslatable_diagnostic)]
4#![feature(assert_matches)]
5#![feature(box_patterns)]
6#![feature(file_buffered)]
7#![feature(if_let_guard)]
8#![feature(negative_impls)]
9#![feature(string_from_utf8_lossy_owned)]
10#![feature(trait_alias)]
11#![feature(try_blocks)]
12#![recursion_limit = "256"]
13#![feature(rustc_private)]
33#![allow(
35 unsafe_code, clippy::enum_glob_use, clippy::todo, mismatched_lifetime_syntaxes,
41 clippy::needless_lifetimes,
42)]
43
44#[cfg(all(feature = "use-compiled-tools", feature = "use-installed-tools"))]
51compile_error!(
52 "Either \"use-compiled-tools\" (enabled by default) or \"use-installed-tools\" may be enabled."
53);
54
55#[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
61include!(concat!(env!("OUT_DIR"), "/pqp_cg_ssa.rs"));
62
63#[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
67mod _rustc_codegen_ssa_transitive_deps_hack {
68 extern crate rustc_codegen_ssa as _;
69}
70
71#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
74use rustc_codegen_ssa as maybe_pqp_cg_ssa;
75
76#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
79extern crate rustc_abi;
80extern crate rustc_apfloat;
81#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
82extern crate rustc_arena;
83#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
84extern crate rustc_ast;
85#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
86extern crate rustc_attr_parsing;
87#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
88extern crate rustc_codegen_ssa;
89#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
90extern crate rustc_data_structures;
91extern crate rustc_driver;
92#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
93extern crate rustc_errors;
94#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
95extern crate rustc_hashes;
96#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
97extern crate rustc_hir;
98#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
99extern crate rustc_index;
100extern crate rustc_interface;
101#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
102extern crate rustc_metadata;
103#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
104extern crate rustc_middle;
105#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
106extern crate rustc_session;
107#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
108extern crate rustc_span;
109#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
110extern crate rustc_target;
111
112macro_rules! assert_ty_eq {
113 ($codegen_cx:expr, $left:expr, $right:expr) => {
114 assert!(
115 $left == $right,
116 "Expected types to be equal:\n{}\n==\n{}",
117 $codegen_cx.debug_type($left),
118 $codegen_cx.debug_type($right)
119 )
120 };
121}
122
123mod abi;
124mod attr;
125mod builder;
126mod builder_spirv;
127mod codegen_cx;
128mod custom_decorations;
129mod custom_insts;
130mod link;
131mod linker;
132mod spirv_type;
133mod spirv_type_constraints;
134mod symbols;
135mod target;
136mod target_feature;
137
138use builder::Builder;
139use codegen_cx::CodegenCx;
140use maybe_pqp_cg_ssa::back::lto::{SerializedModule, ThinModule};
141use maybe_pqp_cg_ssa::back::write::{
142 CodegenContext, FatLtoInput, ModuleConfig, OngoingCodegen, TargetMachineFactoryConfig,
143};
144use maybe_pqp_cg_ssa::base::maybe_create_entry_wrapper;
145use maybe_pqp_cg_ssa::mono_item::MonoItemExt;
146use maybe_pqp_cg_ssa::traits::{
147 CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods,
148 WriteBackendMethods,
149};
150use maybe_pqp_cg_ssa::{CodegenResults, CompiledModule, ModuleCodegen, ModuleKind, TargetConfig};
151use rspirv::binary::Assemble;
152use rustc_ast::expand::allocator::AllocatorMethod;
153use rustc_data_structures::fx::FxIndexMap;
154use rustc_errors::DiagCtxtHandle;
155use rustc_metadata::EncodedMetadata;
156use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
157use rustc_middle::mir::mono::{MonoItem, MonoItemData};
158use rustc_middle::mir::pretty::write_mir_pretty;
159use rustc_middle::ty::print::with_no_trimmed_paths;
160use rustc_middle::ty::{InstanceKind, TyCtxt};
161use rustc_session::Session;
162use rustc_session::config::{self, OutputFilenames, OutputType};
163use rustc_span::symbol::Symbol;
164use std::any::Any;
165use std::fs;
166use std::io::Cursor;
167use std::io::Write;
168use std::path::{Path, PathBuf};
169use std::sync::Arc;
170use tracing::{error, warn};
171
172fn dump_mir(tcx: TyCtxt<'_>, mono_items: &[(MonoItem<'_>, MonoItemData)], path: &Path) {
173 fs::create_dir_all(path.parent().unwrap()).unwrap();
174 let mut file = fs::File::create(path).unwrap();
175 for &(mono_item, _) in mono_items {
176 if let MonoItem::Fn(instance) = mono_item
177 && matches!(instance.def, InstanceKind::Item(_))
178 {
179 let mut mir = Cursor::new(Vec::new());
180 if write_mir_pretty(tcx, Some(instance.def_id()), &mut mir).is_ok() {
181 writeln!(file, "{}", String::from_utf8(mir.into_inner()).unwrap()).unwrap();
182 }
183 }
184 }
185}
186
187#[derive(Clone)]
188struct SpirvCodegenBackend;
189
190impl CodegenBackend for SpirvCodegenBackend {
191 fn init(&self, sess: &Session) {
192 init_logging(sess);
194 }
195
196 fn locale_resource(&self) -> &'static str {
197 rustc_errors::DEFAULT_LOCALE_RESOURCE
198 }
199
200 fn target_config(&self, sess: &Session) -> TargetConfig {
201 let cmdline = sess.opts.cg.target_feature.split(',');
202 let cfg = sess.target.options.features.split(',');
203
204 let target_features: Vec<_> = cfg
205 .chain(cmdline)
206 .filter(|l| l.starts_with('+'))
207 .map(|l| &l[1..])
208 .filter(|l| !l.is_empty())
209 .map(Symbol::intern)
210 .collect();
211
212 let unstable_target_features = target_features.clone();
216
217 TargetConfig {
218 target_features,
219 unstable_target_features,
220
221 has_reliable_f16: false,
223 has_reliable_f16_math: false,
224 has_reliable_f128: false,
225 has_reliable_f128_math: false,
226 }
227 }
228
229 fn provide(&self, providers: &mut rustc_middle::util::Providers) {
230 providers.global_backend_features = |_tcx, ()| vec![];
234
235 crate::abi::provide(providers);
236 crate::attr::provide(providers);
237 }
238
239 fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> {
240 Box::new(maybe_pqp_cg_ssa::base::codegen_crate(
241 Self,
242 tcx,
243 tcx.sess
244 .opts
245 .cg
246 .target_cpu
247 .clone()
248 .unwrap_or_else(|| tcx.sess.target.cpu.to_string()),
249 ))
250 }
251
252 fn join_codegen(
253 &self,
254 ongoing_codegen: Box<dyn Any>,
255 sess: &Session,
256 _outputs: &OutputFilenames,
257 ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
258 ongoing_codegen
259 .downcast::<OngoingCodegen<Self>>()
260 .expect("Expected OngoingCodegen, found Box<Any>")
261 .join(sess)
262 }
263
264 fn link(
265 &self,
266 sess: &Session,
267 codegen_results: CodegenResults,
268 metadata: EncodedMetadata,
269 outputs: &OutputFilenames,
270 ) {
271 let timer = sess.timer("link_crate");
272 link::link(
273 sess,
274 &codegen_results,
275 &metadata,
276 outputs,
277 codegen_results.crate_info.local_crate_name.as_str(),
278 );
279 drop(timer);
280 }
281
282 fn name(&self) -> &'static str {
283 "SpirvCodegenBackend"
284 }
285}
286
287struct SpirvModuleBuffer(Vec<u32>);
288
289impl SpirvModuleBuffer {
290 fn as_bytes(&self) -> &[u8] {
291 spirv_tools::binary::from_binary(&self.0)
292 }
293}
294impl ModuleBufferMethods for SpirvModuleBuffer {
295 fn data(&self) -> &[u8] {
296 self.as_bytes()
297 }
298}
299impl ThinBufferMethods for SpirvModuleBuffer {
300 fn data(&self) -> &[u8] {
301 self.as_bytes()
302 }
303}
304
305impl SpirvCodegenBackend {
306 fn optimize_common(
307 _cgcx: &CodegenContext<Self>,
308 module: &mut ModuleCodegen<<Self as WriteBackendMethods>::Module>,
309 ) {
310 linker::dce::dce(&mut module.module_llvm);
316
317 }
319}
320
321impl WriteBackendMethods for SpirvCodegenBackend {
322 type Module = rspirv::dr::Module;
323 type TargetMachine = ();
324 type TargetMachineError = String;
325 type ModuleBuffer = SpirvModuleBuffer;
326 type ThinData = ();
327 type ThinBuffer = SpirvModuleBuffer;
328
329 fn run_and_optimize_fat_lto(
334 cgcx: &CodegenContext<Self>,
335 _exported_symbols_for_lto: &[String],
336 _each_linked_rlib_for_lto: &[PathBuf],
337 _modules: Vec<FatLtoInput<Self>>,
338 ) -> ModuleCodegen<Self::Module> {
339 assert!(
340 cgcx.lto == rustc_session::config::Lto::Fat,
341 "`run_and_optimize_fat_lto` (for `WorkItemResult::NeedsFatLto`) should \
342 only be invoked due to `-Clto` (or equivalent)"
343 );
344 unreachable!("Rust-GPU does not support fat LTO")
345 }
346
347 fn run_thin_lto(
348 cgcx: &CodegenContext<Self>,
349 _exported_symbols_for_lto: &[String],
351 _each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>,
353 cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
354 ) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) {
355 link::run_thin(cgcx, modules, cached_modules)
356 }
357
358 fn print_pass_timings(&self) {
359 warn!("TODO: Implement print_pass_timings");
360 }
361
362 fn print_statistics(&self) {
363 warn!("TODO: Implement print_statistics");
364 }
365
366 fn optimize(
367 cgcx: &CodegenContext<Self>,
368 _dcx: DiagCtxtHandle<'_>,
369 module: &mut ModuleCodegen<Self::Module>,
370 _config: &ModuleConfig,
371 ) {
372 Self::optimize_common(cgcx, module);
373 }
374
375 fn optimize_thin(
376 cgcx: &CodegenContext<Self>,
377 thin_module: ThinModule<Self>,
378 ) -> ModuleCodegen<Self::Module> {
379 let mut module = ModuleCodegen {
383 module_llvm: link::with_rspirv_loader(|loader| {
384 rspirv::binary::parse_bytes(thin_module.data(), loader)
385 })
386 .unwrap(),
387 name: thin_module.name().to_string(),
388 kind: ModuleKind::Regular,
389 thin_lto_buffer: None,
390 };
391 Self::optimize_common(cgcx, &mut module);
392 module
393 }
394
395 fn codegen(
396 cgcx: &CodegenContext<Self>,
397 module: ModuleCodegen<Self::Module>,
398 _config: &ModuleConfig,
399 ) -> CompiledModule {
400 let kind = module.kind;
401 let (name, module_buffer) = Self::serialize_module(module);
402
403 let path = cgcx.output_filenames.temp_path_for_cgu(
404 OutputType::Object,
405 &name,
406 cgcx.invocation_temp.as_deref(),
407 );
408 fs::write(&path, module_buffer.as_bytes()).unwrap();
409
410 CompiledModule {
411 name,
412 kind,
413 object: Some(path),
414 dwarf_object: None,
415 bytecode: None,
416 assembly: None,
417 llvm_ir: None,
418 links_from_incr_cache: vec![],
419 }
420 }
421
422 fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
423 Self::serialize_module(module)
424 }
425
426 fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
427 (
428 module.name,
429 SpirvModuleBuffer(module.module_llvm.assemble()),
430 )
431 }
432}
433
434impl ExtraBackendMethods for SpirvCodegenBackend {
435 fn codegen_allocator(&self, _: TyCtxt<'_>, _: &str, _: &[AllocatorMethod]) -> Self::Module {
436 todo!()
437 }
438
439 fn compile_codegen_unit<'tcx>(
440 &self,
441 tcx: TyCtxt<'tcx>,
442 cgu_name: Symbol,
443 ) -> (ModuleCodegen<Self::Module>, u64) {
444 let _timer = tcx
445 .prof
446 .verbose_generic_activity_with_arg("codegen_module", cgu_name.to_string());
447
448 let cgu = tcx.codegen_unit(cgu_name);
450
451 let mut cx = CodegenCx::new(tcx, cgu);
452 let do_codegen = |cx: &mut CodegenCx<'tcx>| {
453 let mono_items = cgu.items_in_deterministic_order(cx.tcx);
454
455 if let Some(dir) = &cx.codegen_args.dump_mir {
456 dump_mir(tcx, mono_items.as_slice(), &dir.join(cgu_name.to_string()));
457 }
458
459 for &(mono_item, mono_item_data) in mono_items.iter() {
460 mono_item.predefine::<Builder<'_, '_>>(
461 cx,
462 cgu_name.as_str(),
463 mono_item_data.linkage,
464 mono_item_data.visibility,
465 );
466 }
467
468 for &(mono_item, mono_item_data) in &mono_items {
470 tracing::trace!(?mono_item, "defining");
471 mono_item.define::<Builder<'_, '_>>(cx, cgu_name.as_str(), mono_item_data);
472 }
473
474 if let Some(_entry) = maybe_create_entry_wrapper::<Builder<'_, '_>>(cx, cgu) {
475 }
477 };
478 if let Some(path) = cx.codegen_args.dump_module_on_panic.clone() {
482 let module_dumper = DumpModuleOnPanic {
483 cx: &mut cx,
484 path: &path,
485 };
486 with_no_trimmed_paths!(do_codegen(module_dumper.cx));
487 drop(module_dumper);
488 } else {
489 with_no_trimmed_paths!(do_codegen(&mut cx));
490 }
491
492 (
493 ModuleCodegen {
494 name: cgu_name.to_string(),
495 module_llvm: cx.finalize_module(),
496 kind: ModuleKind::Regular,
497 thin_lto_buffer: None,
498 },
499 0,
500 )
501 }
502
503 fn target_machine_factory(
504 &self,
505 _sess: &Session,
506 _opt_level: config::OptLevel,
507 _target_features: &[String],
508 ) -> Arc<dyn Fn(TargetMachineFactoryConfig) -> Result<(), String> + Send + Sync + 'static> {
509 Arc::new(|_| Ok(()))
510 }
511}
512
513struct DumpModuleOnPanic<'a, 'cx, 'tcx> {
514 cx: &'cx mut CodegenCx<'tcx>,
515 path: &'a Path,
516}
517
518impl Drop for DumpModuleOnPanic<'_, '_, '_> {
519 fn drop(&mut self) {
520 if std::thread::panicking() {
521 if self.path.has_root() {
522 self.cx.builder.dump_module(self.path);
523 } else {
524 error!("{}", self.cx.builder.dump_module_str());
525 }
526 }
527 }
528}
529
530#[unsafe(no_mangle)]
532pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
533 rustc_driver::install_ice_hook("https://github.com/rust-gpu/rust-gpu/issues/new", |dcx| {
535 dcx.handle().note(concat!(
536 "`rust-gpu` version `",
537 env!("CARGO_PKG_VERSION"),
538 "`"
539 ));
540 });
541
542 Box::new(SpirvCodegenBackend)
543}
544
545fn init_logging(sess: &Session) {
547 use std::env::{self, VarError};
548 use std::io::{self, IsTerminal};
549 use tracing_subscriber::layer::SubscriberExt;
550
551 let filter = tracing_subscriber::EnvFilter::from_env("RUSTGPU_LOG");
553 #[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
554 let filter = filter.add_directive("rustc_codegen_spirv::maybe_pqp_cg_ssa=off".parse().unwrap());
555 let subscriber = tracing_subscriber::Registry::default().with(filter);
556
557 #[derive(Debug, Default)]
558 enum OutputFormat {
559 #[default]
560 Tree,
561 Flat,
562 Json,
563 }
564
565 let output_format = match env::var("RUSTGPU_LOG_FORMAT").as_deref() {
566 Ok("tree") | Err(VarError::NotPresent) => OutputFormat::Tree,
567 Ok("flat") => OutputFormat::Flat,
568 Ok("json") => OutputFormat::Json,
569 Ok(value) => sess.dcx().fatal(format!(
570 "invalid output format value '{value}': expected one of tree, flat, or json",
571 )),
572 Err(VarError::NotUnicode(value)) => sess.dcx().fatal(format!(
573 "invalid output format value '{}': expected one of tree, flat, or json",
574 value.to_string_lossy()
575 )),
576 };
577
578 let subscriber: Box<dyn tracing::Subscriber + Send + Sync> = match output_format {
579 OutputFormat::Tree => {
580 let color_logs = match env::var("RUSTGPU_LOG_COLOR").as_deref() {
582 Ok("always") => true,
583 Ok("never") => false,
584 Ok("auto") | Err(VarError::NotPresent) => io::stderr().is_terminal(),
585 Ok(value) => sess.dcx().fatal(format!(
586 "invalid log color value '{value}': expected one of always, never, or auto",
587 )),
588 Err(VarError::NotUnicode(value)) => sess.dcx().fatal(format!(
589 "invalid log color value '{}': expected one of always, never, or auto",
590 value.to_string_lossy()
591 )),
592 };
593
594 let tree_layer = tracing_tree::HierarchicalLayer::default()
595 .with_writer(io::stderr)
596 .with_ansi(color_logs)
597 .with_targets(true)
598 .with_wraparound(10)
599 .with_verbose_exit(true)
600 .with_verbose_entry(true)
601 .with_indent_amount(2);
602
603 #[cfg(debug_assertions)]
604 let tree_layer = tree_layer.with_thread_ids(true).with_thread_names(true);
605
606 Box::new(subscriber.with(tree_layer))
607 }
608 OutputFormat::Flat => Box::new(subscriber),
609 OutputFormat::Json => Box::new(subscriber.with(tracing_subscriber::fmt::layer().json())),
610 };
611 tracing::subscriber::set_global_default(subscriber).unwrap();
612}