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