1#![feature(deref_patterns)]
3#![feature(file_buffered)]
4#![feature(negative_impls)]
5#![feature(option_into_flat_iter)]
6#![feature(trait_alias)]
7#![feature(try_blocks)]
8#![recursion_limit = "256"]
9#![feature(rustc_private)]
29#![cfg_attr(rustc_codegen_spirv_disable_pqp_cg_ssa, allow(unused_features))]
33#![allow(
35 clippy::enum_glob_use, clippy::todo, mismatched_lifetime_syntaxes,
40)]
41
42#[cfg(all(feature = "use-compiled-tools", feature = "use-installed-tools"))]
49compile_error!(
50 "Either \"use-compiled-tools\" (enabled by default) or \"use-installed-tools\" may be enabled."
51);
52
53#[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
59include!(concat!(env!("OUT_DIR"), "/pqp_cg_ssa.rs"));
60
61#[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
65mod _rustc_codegen_ssa_transitive_deps_hack {
66 extern crate rustc_codegen_ssa as _;
67}
68
69#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
72use rustc_codegen_ssa as maybe_pqp_cg_ssa;
73
74#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
77extern crate rustc_abi;
78extern crate rustc_apfloat;
79#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
80extern crate rustc_arena;
81#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
82extern crate rustc_ast;
83#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
84extern crate rustc_attr_parsing;
85#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
86extern crate rustc_codegen_ssa;
87#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
88extern crate rustc_data_structures;
89extern crate rustc_driver;
90#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
91extern crate rustc_errors;
92#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
93extern crate rustc_hashes;
94#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
95extern crate rustc_hir;
96#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
97extern crate rustc_index;
98extern crate rustc_interface;
99#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
100extern crate rustc_metadata;
101#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
102extern crate rustc_middle;
103#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
104extern crate rustc_session;
105#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
106extern crate rustc_span;
107#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
108extern crate rustc_target;
109
110macro_rules! assert_ty_eq {
111 ($codegen_cx:expr, $left:expr, $right:expr) => {
112 assert!(
113 $left == $right,
114 "Expected types to be equal:\n{}\n==\n{}",
115 $codegen_cx.debug_type($left),
116 $codegen_cx.debug_type($right)
117 )
118 };
119}
120
121mod abi;
122mod attr;
123mod builder;
124mod builder_spirv;
125mod codegen_cx;
126mod custom_decorations;
127mod custom_insts;
128mod link;
129mod linker;
130mod naga_transpile;
131mod spirv_type;
132mod spirv_type_constraints;
133mod symbols;
134pub mod target;
135mod target_feature;
136
137use crate::maybe_pqp_cg_ssa::back::write::ThinLtoInput;
138use builder::Builder;
139use codegen_cx::CodegenCx;
140use maybe_pqp_cg_ssa::back::lto::ThinModule;
141use maybe_pqp_cg_ssa::back::write::{
142 CodegenContext, FatLtoInput, ModuleConfig, OngoingCodegen, SharedEmitter,
143 TargetMachineFactoryFn,
144};
145use maybe_pqp_cg_ssa::base::maybe_create_entry_wrapper;
146use maybe_pqp_cg_ssa::mono_item::MonoItemExt;
147use maybe_pqp_cg_ssa::traits::{
148 CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, WriteBackendMethods,
149};
150use maybe_pqp_cg_ssa::{
151 CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, ModuleKind, TargetConfig,
152};
153use rspirv::binary::Assemble;
154use rustc_ast::expand::allocator::AllocatorMethod;
155use rustc_data_structures::profiling::SelfProfilerRef;
156use rustc_errors::DiagCtxtHandle;
157use rustc_metadata::EncodedMetadata;
158use rustc_middle::dep_graph::{WorkProduct, WorkProductMap};
159use rustc_middle::mono::{MonoItem, MonoItemData};
160use rustc_middle::ty::print::with_no_trimmed_paths;
161use rustc_middle::ty::{InstanceKind, TyCtxt};
162use rustc_session::config::{self, OutputFilenames, OutputType};
163use rustc_session::{IncrCompSession, Session};
164use rustc_span::symbol::Symbol;
165use std::any::Any;
166use std::fs;
167use std::io::Write;
168use std::path::{Path, PathBuf};
169use std::sync::Arc;
170use tracing::error;
171
172fn dump_mir<'tcx>(
173 tcx: TyCtxt<'tcx>,
174 mono_items: &[(MonoItem<'tcx>, MonoItemData)],
175 path: &Path,
176) -> std::io::Result<()> {
177 use rustc_middle::mir::pretty::MirWriter;
178 fs::create_dir_all(path.parent().unwrap())?;
179 let mut file = fs::File::create(path)?;
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 w = Vec::new();
185 let writer = MirWriter::new(tcx);
186 writer.write_mir_fn(tcx.instance_mir(instance.def), &mut w)?;
187 file.write_all(w.as_slice())?;
188 writeln!(file)?;
189 }
190 }
191 Ok(())
192}
193
194#[derive(Clone)]
195struct SpirvCodegenBackend;
196
197impl CodegenBackend for SpirvCodegenBackend {
198 fn init(&self, sess: &Session) {
199 init_logging(sess);
201 }
202
203 fn target_config(&self, sess: &Session) -> TargetConfig {
204 let cmdline = sess.opts.cg.target_feature.split(',');
205 let cfg = sess.target.options.features.split(',');
206
207 let target_features: Vec<_> = cfg
208 .chain(cmdline)
209 .filter(|l| l.starts_with('+'))
210 .map(|l| &l[1..])
211 .filter(|l| !l.is_empty())
212 .map(Symbol::intern)
213 .collect();
214
215 TargetConfig {
216 internal_target_features: target_features.into_iter().collect(),
218
219 has_reliable_f16: false,
221 has_reliable_f16_math: false,
222 has_reliable_f128: false,
223 has_reliable_f128_math: false,
224 }
225 }
226
227 fn provide(&self, providers: &mut rustc_middle::util::Providers) {
228 providers.queries.global_backend_features = |_tcx, ()| vec![];
232
233 crate::abi::provide(providers);
234 crate::attr::provide(&mut providers.queries);
235 }
236
237 fn target_cpu(&self, sess: &Session) -> String {
238 sess.opts
239 .cg
240 .target_cpu
241 .clone()
242 .unwrap_or_else(|| sess.target.cpu.to_string())
243 }
244
245 fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any> {
246 Box::new(maybe_pqp_cg_ssa::base::codegen_crate(Self, tcx))
247 }
248
249 fn join_codegen(
250 &self,
251 ongoing_codegen: Box<dyn Any>,
252 sess: &Session,
253 incr_comp_session: Option<&IncrCompSession>,
254 _outputs: &OutputFilenames,
255 crate_info: &CrateInfo,
256 ) -> (CompiledModules, WorkProductMap) {
257 ongoing_codegen
258 .downcast::<OngoingCodegen<Self>>()
259 .expect("Expected OngoingCodegen, found Box<Any>")
260 .join(sess, incr_comp_session, crate_info)
261 }
262
263 fn link(
264 &self,
265 sess: &Session,
266 compiled_modules: CompiledModules,
267 crate_info: CrateInfo,
268 metadata: EncodedMetadata,
269 outputs: &OutputFilenames,
270 ) {
271 let timer = sess.timer("link_crate");
272 link::link(
273 sess,
274 &compiled_modules,
275 &crate_info,
276 &metadata,
277 outputs,
278 crate_info.local_crate_name.as_str(),
279 );
280 drop(timer);
281 }
282
283 fn name(&self) -> &'static str {
284 "SpirvCodegenBackend"
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}
300
301impl SpirvCodegenBackend {
302 fn optimize_common(
303 _cgcx: &CodegenContext,
304 module: &mut ModuleCodegen<<Self as WriteBackendMethods>::Module>,
305 ) {
306 linker::dce::dce(&mut module.module_llvm);
312
313 }
315}
316
317impl WriteBackendMethods for SpirvCodegenBackend {
318 type Module = rspirv::dr::Module;
319 type TargetMachine = ();
320 type ModuleBuffer = SpirvModuleBuffer;
321 type ThinData = ();
322
323 fn optimize_and_codegen_fat_lto(
328 _sess: &Session,
329 cgcx: &CodegenContext,
330 _shared_emitter: &SharedEmitter,
331 _tm_factory: TargetMachineFactoryFn<Self>,
332 _exported_symbols_for_lto: &[String],
333 _each_linked_rlib_for_lto: &[PathBuf],
334 _modules: Vec<FatLtoInput<Self>>,
335 ) -> CompiledModule {
336 assert!(
337 cgcx.lto == config::Lto::Fat,
338 "`optimize_and_codegen_fat_lto` should \
339 only be invoked due to `-Clto` (or equivalent)"
340 );
341 unreachable!("Rust-GPU does not support fat LTO")
342 }
343
344 fn run_thin_lto(
345 _cgcx: &CodegenContext,
346 _prof: &SelfProfilerRef,
347 _dcx: DiagCtxtHandle<'_>,
348 _exported_symbols_for_lto: &[String],
349 _each_linked_rlib_for_lto: &[PathBuf],
350 _modules: Vec<ThinLtoInput<Self>>,
351 ) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) {
352 unreachable!()
354 }
355
356 fn optimize(
357 cgcx: &CodegenContext,
358 _prof: &SelfProfilerRef,
359 _shared_emitter: &SharedEmitter,
360 module: &mut ModuleCodegen<Self::Module>,
361 _config: &ModuleConfig,
362 ) {
363 Self::optimize_common(cgcx, module);
364 }
365
366 fn optimize_and_codegen_thin(
367 cgcx: &CodegenContext,
368 prof: &SelfProfilerRef,
369 shared_emitter: &SharedEmitter,
370 _tm_factory: TargetMachineFactoryFn<Self>,
371 thin_module: ThinModule<Self>,
372 ) -> CompiledModule {
373 let mut module = ModuleCodegen {
377 module_llvm: link::with_rspirv_loader(|loader| {
378 rspirv::binary::parse_bytes(thin_module.data(), loader)
379 })
380 .unwrap(),
381 name: thin_module.name().to_string(),
382 kind: ModuleKind::Regular,
383 thin_lto_buffer: None,
384 };
385 Self::optimize_common(cgcx, &mut module);
386 Self::codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config)
387 }
388
389 fn codegen(
390 cgcx: &CodegenContext,
391 _prof: &SelfProfilerRef,
392 _shared_emitter: &SharedEmitter,
393 module: ModuleCodegen<Self::Module>,
394 _config: &ModuleConfig,
395 ) -> CompiledModule {
396 let kind = module.kind;
397 let name = module.name;
398 let module_buffer = Self::serialize_module(module.module_llvm, false);
399
400 let path = cgcx
401 .output_filenames
402 .temp_path_for_cgu(OutputType::Object, &name);
403 fs::write(&path, module_buffer.as_bytes()).unwrap();
404
405 CompiledModule {
406 name,
407 kind,
408 object: Some(path),
409 global_asm_object: None,
410 dwarf_object: None,
411 bytecode: None,
412 assembly: None,
413 llvm_ir: None,
414 links_from_incr_cache: vec![],
415 }
416 }
417
418 fn serialize_module(module: Self::Module, _is_thin: bool) -> Self::ModuleBuffer {
419 SpirvModuleBuffer(module.assemble())
420 }
421
422 fn target_machine_factory(
423 &self,
424 _sess: &Session,
425 _opt_level: config::OptLevel,
426 _target_features: &[String],
427 ) -> TargetMachineFactoryFn<Self> {
428 Arc::new(|_, _| ())
429 }
430}
431
432impl ExtraBackendMethods for SpirvCodegenBackend {
433 type Module = rspirv::dr::Module;
434
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())).unwrap();
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
504struct DumpModuleOnPanic<'a, 'cx, 'tcx> {
505 cx: &'cx mut CodegenCx<'tcx>,
506 path: &'a Path,
507}
508
509impl Drop for DumpModuleOnPanic<'_, '_, '_> {
510 fn drop(&mut self) {
511 if std::thread::panicking() {
512 if self.path.has_root() {
513 self.cx.builder.dump_module(self.path);
514 } else {
515 error!("{}", self.cx.builder.dump_module_str());
516 }
517 }
518 }
519}
520
521#[unsafe(no_mangle)]
523pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
524 rustc_driver::install_ice_hook("https://github.com/rust-gpu/rust-gpu/issues/new", |dcx| {
526 dcx.handle().note(concat!(
527 "`rust-gpu` version `",
528 env!("CARGO_PKG_VERSION"),
529 "`"
530 ));
531 });
532
533 Box::new(SpirvCodegenBackend)
534}
535
536fn init_logging(sess: &Session) {
538 use std::env::{self, VarError};
539 use std::io::{self, IsTerminal};
540 use tracing_subscriber::layer::SubscriberExt;
541
542 let filter = tracing_subscriber::EnvFilter::from_env("RUSTGPU_LOG");
544 #[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
545 let filter = filter.add_directive("rustc_codegen_spirv::maybe_pqp_cg_ssa=off".parse().unwrap());
546 let subscriber = tracing_subscriber::Registry::default().with(filter);
547
548 #[derive(Debug, Default)]
549 enum OutputFormat {
550 #[default]
551 Tree,
552 Flat,
553 Json,
554 }
555
556 let output_format = match env::var("RUSTGPU_LOG_FORMAT").as_deref() {
557 Ok("tree") | Err(VarError::NotPresent) => OutputFormat::Tree,
558 Ok("flat") => OutputFormat::Flat,
559 Ok("json") => OutputFormat::Json,
560 Ok(value) => sess.dcx().fatal(format!(
561 "invalid output format value '{value}': expected one of tree, flat, or json",
562 )),
563 Err(VarError::NotUnicode(value)) => sess.dcx().fatal(format!(
564 "invalid output format value '{}': expected one of tree, flat, or json",
565 value.to_string_lossy()
566 )),
567 };
568
569 let subscriber: Box<dyn tracing::Subscriber + Send + Sync> = match output_format {
570 OutputFormat::Tree => {
571 let color_logs = match env::var("RUSTGPU_LOG_COLOR").as_deref() {
573 Ok("always") => true,
574 Ok("never") => false,
575 Ok("auto") | Err(VarError::NotPresent) => io::stderr().is_terminal(),
576 Ok(value) => sess.dcx().fatal(format!(
577 "invalid log color value '{value}': expected one of always, never, or auto",
578 )),
579 Err(VarError::NotUnicode(value)) => sess.dcx().fatal(format!(
580 "invalid log color value '{}': expected one of always, never, or auto",
581 value.to_string_lossy()
582 )),
583 };
584
585 let tree_layer = tracing_tree::HierarchicalLayer::default()
586 .with_writer(io::stderr)
587 .with_ansi(color_logs)
588 .with_targets(true)
589 .with_wraparound(10)
590 .with_verbose_exit(true)
591 .with_verbose_entry(true)
592 .with_indent_amount(2);
593
594 #[cfg(debug_assertions)]
595 let tree_layer = tree_layer.with_thread_ids(true).with_thread_names(true);
596
597 Box::new(subscriber.with(tree_layer))
598 }
599 OutputFormat::Flat => Box::new(subscriber),
600 OutputFormat::Json => Box::new(subscriber.with(tracing_subscriber::fmt::layer().json())),
601 };
602 tracing::subscriber::set_global_default(subscriber).unwrap();
603}