1#![feature(box_patterns)]
3#![feature(file_buffered)]
4#![feature(negative_impls)]
5#![feature(string_from_utf8_lossy_owned)]
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 spirv_type;
131mod spirv_type_constraints;
132mod symbols;
133mod target;
134mod target_feature;
135
136use crate::maybe_pqp_cg_ssa::back::write::ThinLtoInput;
137use builder::Builder;
138use codegen_cx::CodegenCx;
139use maybe_pqp_cg_ssa::back::lto::ThinModule;
140use maybe_pqp_cg_ssa::back::write::{
141 CodegenContext, FatLtoInput, ModuleConfig, OngoingCodegen, SharedEmitter,
142 TargetMachineFactoryFn,
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, WriteBackendMethods,
148};
149use maybe_pqp_cg_ssa::{
150 CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, ModuleKind, TargetConfig,
151};
152use rspirv::binary::Assemble;
153use rustc_ast::expand::allocator::AllocatorMethod;
154use rustc_data_structures::fx::FxIndexMap;
155use rustc_data_structures::profiling::SelfProfilerRef;
156use rustc_errors::DiagCtxtHandle;
157use rustc_metadata::EncodedMetadata;
158use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
159use rustc_middle::mir::pretty::write_mir_pretty;
160use rustc_middle::mono::{MonoItem, MonoItemData};
161use rustc_middle::ty::print::with_no_trimmed_paths;
162use rustc_middle::ty::{InstanceKind, TyCtxt};
163use rustc_session::Session;
164use rustc_session::config::{self, OutputFilenames, OutputType};
165use rustc_span::symbol::Symbol;
166use std::any::Any;
167use std::fs;
168use std::io::Cursor;
169use std::io::Write;
170use std::path::{Path, PathBuf};
171use std::sync::Arc;
172use tracing::error;
173
174fn dump_mir(tcx: TyCtxt<'_>, mono_items: &[(MonoItem<'_>, MonoItemData)], path: &Path) {
175 fs::create_dir_all(path.parent().unwrap()).unwrap();
176 let mut file = fs::File::create(path).unwrap();
177 for &(mono_item, _) in mono_items {
178 if let MonoItem::Fn(instance) = mono_item
179 && matches!(instance.def, InstanceKind::Item(_))
180 {
181 let mut mir = Cursor::new(Vec::new());
182 if write_mir_pretty(tcx, Some(instance.def_id()), &mut mir).is_ok() {
183 writeln!(file, "{}", String::from_utf8(mir.into_inner()).unwrap()).unwrap();
184 }
185 }
186 }
187}
188
189#[derive(Clone)]
190struct SpirvCodegenBackend;
191
192impl CodegenBackend for SpirvCodegenBackend {
193 fn init(&self, sess: &Session) {
194 init_logging(sess);
196 }
197
198 fn target_config(&self, sess: &Session) -> TargetConfig {
199 let cmdline = sess.opts.cg.target_feature.split(',');
200 let cfg = sess.target.options.features.split(',');
201
202 let target_features: Vec<_> = cfg
203 .chain(cmdline)
204 .filter(|l| l.starts_with('+'))
205 .map(|l| &l[1..])
206 .filter(|l| !l.is_empty())
207 .map(Symbol::intern)
208 .collect();
209
210 let unstable_target_features = target_features.clone();
214
215 TargetConfig {
216 target_features,
217 unstable_target_features,
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>, crate_info: &CrateInfo) -> Box<dyn Any> {
246 Box::new(maybe_pqp_cg_ssa::base::codegen_crate(Self, tcx, crate_info))
247 }
248
249 fn join_codegen(
250 &self,
251 ongoing_codegen: Box<dyn Any>,
252 sess: &Session,
253 _outputs: &OutputFilenames,
254 ) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
255 ongoing_codegen
256 .downcast::<OngoingCodegen<Self>>()
257 .expect("Expected OngoingCodegen, found Box<Any>")
258 .join(sess)
259 }
260
261 fn link(
262 &self,
263 sess: &Session,
264 compiled_modules: CompiledModules,
265 crate_info: CrateInfo,
266 metadata: EncodedMetadata,
267 outputs: &OutputFilenames,
268 ) {
269 let timer = sess.timer("link_crate");
270 link::link(
271 sess,
272 &compiled_modules,
273 &crate_info,
274 &metadata,
275 outputs,
276 crate_info.local_crate_name.as_str(),
277 );
278 drop(timer);
279 }
280
281 fn name(&self) -> &'static str {
282 "SpirvCodegenBackend"
283 }
284}
285
286struct SpirvModuleBuffer(Vec<u32>);
287
288impl SpirvModuleBuffer {
289 fn as_bytes(&self) -> &[u8] {
290 spirv_tools::binary::from_binary(&self.0)
291 }
292}
293impl ModuleBufferMethods for SpirvModuleBuffer {
294 fn data(&self) -> &[u8] {
295 self.as_bytes()
296 }
297}
298
299impl SpirvCodegenBackend {
300 fn optimize_common(
301 _cgcx: &CodegenContext,
302 module: &mut ModuleCodegen<<Self as WriteBackendMethods>::Module>,
303 ) {
304 linker::dce::dce(&mut module.module_llvm);
310
311 }
313}
314
315impl WriteBackendMethods for SpirvCodegenBackend {
316 type Module = rspirv::dr::Module;
317 type TargetMachine = ();
318 type ModuleBuffer = SpirvModuleBuffer;
319 type ThinData = ();
320
321 fn optimize_and_codegen_fat_lto(
326 cgcx: &CodegenContext,
327 _prof: &SelfProfilerRef,
328 _shared_emitter: &SharedEmitter,
329 _tm_factory: TargetMachineFactoryFn<Self>,
330 _exported_symbols_for_lto: &[String],
331 _each_linked_rlib_for_lto: &[PathBuf],
332 _modules: Vec<FatLtoInput<Self>>,
333 ) -> CompiledModule {
334 assert!(
335 cgcx.lto == rustc_session::config::Lto::Fat,
336 "`optimize_and_codegen_fat_lto` should \
337 only be invoked due to `-Clto` (or equivalent)"
338 );
339 unreachable!("Rust-GPU does not support fat LTO")
340 }
341
342 fn run_thin_lto(
343 _cgcx: &CodegenContext,
344 _prof: &SelfProfilerRef,
345 _dcx: DiagCtxtHandle<'_>,
346 _exported_symbols_for_lto: &[String],
347 _each_linked_rlib_for_lto: &[PathBuf],
348 _modules: Vec<ThinLtoInput<Self>>,
349 ) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) {
350 unreachable!()
352 }
353
354 fn optimize(
355 cgcx: &CodegenContext,
356 _prof: &SelfProfilerRef,
357 _shared_emitter: &SharedEmitter,
358 module: &mut ModuleCodegen<Self::Module>,
359 _config: &ModuleConfig,
360 ) {
361 Self::optimize_common(cgcx, module);
362 }
363
364 fn optimize_and_codegen_thin(
365 cgcx: &CodegenContext,
366 prof: &SelfProfilerRef,
367 shared_emitter: &SharedEmitter,
368 _tm_factory: TargetMachineFactoryFn<Self>,
369 thin_module: ThinModule<Self>,
370 ) -> CompiledModule {
371 let mut module = ModuleCodegen {
375 module_llvm: link::with_rspirv_loader(|loader| {
376 rspirv::binary::parse_bytes(thin_module.data(), loader)
377 })
378 .unwrap(),
379 name: thin_module.name().to_string(),
380 kind: ModuleKind::Regular,
381 thin_lto_buffer: None,
382 };
383 Self::optimize_common(cgcx, &mut module);
384 Self::codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config)
385 }
386
387 fn codegen(
388 cgcx: &CodegenContext,
389 _prof: &SelfProfilerRef,
390 _shared_emitter: &SharedEmitter,
391 module: ModuleCodegen<Self::Module>,
392 _config: &ModuleConfig,
393 ) -> CompiledModule {
394 let kind = module.kind;
395 let name = module.name;
396 let module_buffer = Self::serialize_module(module.module_llvm, false);
397
398 let path = cgcx.output_filenames.temp_path_for_cgu(
399 OutputType::Object,
400 &name,
401 cgcx.invocation_temp.as_deref(),
402 );
403 fs::write(&path, module_buffer.as_bytes()).unwrap();
404
405 CompiledModule {
406 name,
407 kind,
408 object: Some(path),
409 dwarf_object: None,
410 bytecode: None,
411 assembly: None,
412 llvm_ir: None,
413 links_from_incr_cache: vec![],
414 }
415 }
416
417 fn serialize_module(module: Self::Module, _is_thin: bool) -> Self::ModuleBuffer {
418 SpirvModuleBuffer(module.assemble())
419 }
420
421 fn target_machine_factory(
422 &self,
423 _sess: &Session,
424 _opt_level: config::OptLevel,
425 _target_features: &[String],
426 ) -> TargetMachineFactoryFn<Self> {
427 Arc::new(|_, _| ())
428 }
429}
430
431impl ExtraBackendMethods for SpirvCodegenBackend {
432 fn codegen_allocator(&self, _: TyCtxt<'_>, _: &str, _: &[AllocatorMethod]) -> Self::Module {
433 todo!()
434 }
435
436 fn compile_codegen_unit<'tcx>(
437 &self,
438 tcx: TyCtxt<'tcx>,
439 cgu_name: Symbol,
440 ) -> (ModuleCodegen<Self::Module>, u64) {
441 let _timer = tcx
442 .prof
443 .verbose_generic_activity_with_arg("codegen_module", cgu_name.to_string());
444
445 let cgu = tcx.codegen_unit(cgu_name);
447
448 let mut cx = CodegenCx::new(tcx, cgu);
449 let do_codegen = |cx: &mut CodegenCx<'tcx>| {
450 let mono_items = cgu.items_in_deterministic_order(cx.tcx);
451
452 if let Some(dir) = &cx.codegen_args.dump_mir {
453 dump_mir(tcx, mono_items.as_slice(), &dir.join(cgu_name.to_string()));
454 }
455
456 for &(mono_item, mono_item_data) in mono_items.iter() {
457 mono_item.predefine::<Builder<'_, '_>>(
458 cx,
459 cgu_name.as_str(),
460 mono_item_data.linkage,
461 mono_item_data.visibility,
462 );
463 }
464
465 for &(mono_item, mono_item_data) in &mono_items {
467 tracing::trace!(?mono_item, "defining");
468 mono_item.define::<Builder<'_, '_>>(cx, cgu_name.as_str(), mono_item_data);
469 }
470
471 if let Some(_entry) = maybe_create_entry_wrapper::<Builder<'_, '_>>(cx, cgu) {
472 }
474 };
475 if let Some(path) = cx.codegen_args.dump_module_on_panic.clone() {
479 let module_dumper = DumpModuleOnPanic {
480 cx: &mut cx,
481 path: &path,
482 };
483 with_no_trimmed_paths!(do_codegen(module_dumper.cx));
484 drop(module_dumper);
485 } else {
486 with_no_trimmed_paths!(do_codegen(&mut cx));
487 }
488
489 (
490 ModuleCodegen {
491 name: cgu_name.to_string(),
492 module_llvm: cx.finalize_module(),
493 kind: ModuleKind::Regular,
494 thin_lto_buffer: None,
495 },
496 0,
497 )
498 }
499}
500
501struct DumpModuleOnPanic<'a, 'cx, 'tcx> {
502 cx: &'cx mut CodegenCx<'tcx>,
503 path: &'a Path,
504}
505
506impl Drop for DumpModuleOnPanic<'_, '_, '_> {
507 fn drop(&mut self) {
508 if std::thread::panicking() {
509 if self.path.has_root() {
510 self.cx.builder.dump_module(self.path);
511 } else {
512 error!("{}", self.cx.builder.dump_module_str());
513 }
514 }
515 }
516}
517
518#[unsafe(no_mangle)]
520pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
521 rustc_driver::install_ice_hook("https://github.com/rust-gpu/rust-gpu/issues/new", |dcx| {
523 dcx.handle().note(concat!(
524 "`rust-gpu` version `",
525 env!("CARGO_PKG_VERSION"),
526 "`"
527 ));
528 });
529
530 Box::new(SpirvCodegenBackend)
531}
532
533fn init_logging(sess: &Session) {
535 use std::env::{self, VarError};
536 use std::io::{self, IsTerminal};
537 use tracing_subscriber::layer::SubscriberExt;
538
539 let filter = tracing_subscriber::EnvFilter::from_env("RUSTGPU_LOG");
541 #[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
542 let filter = filter.add_directive("rustc_codegen_spirv::maybe_pqp_cg_ssa=off".parse().unwrap());
543 let subscriber = tracing_subscriber::Registry::default().with(filter);
544
545 #[derive(Debug, Default)]
546 enum OutputFormat {
547 #[default]
548 Tree,
549 Flat,
550 Json,
551 }
552
553 let output_format = match env::var("RUSTGPU_LOG_FORMAT").as_deref() {
554 Ok("tree") | Err(VarError::NotPresent) => OutputFormat::Tree,
555 Ok("flat") => OutputFormat::Flat,
556 Ok("json") => OutputFormat::Json,
557 Ok(value) => sess.dcx().fatal(format!(
558 "invalid output format value '{value}': expected one of tree, flat, or json",
559 )),
560 Err(VarError::NotUnicode(value)) => sess.dcx().fatal(format!(
561 "invalid output format value '{}': expected one of tree, flat, or json",
562 value.to_string_lossy()
563 )),
564 };
565
566 let subscriber: Box<dyn tracing::Subscriber + Send + Sync> = match output_format {
567 OutputFormat::Tree => {
568 let color_logs = match env::var("RUSTGPU_LOG_COLOR").as_deref() {
570 Ok("always") => true,
571 Ok("never") => false,
572 Ok("auto") | Err(VarError::NotPresent) => io::stderr().is_terminal(),
573 Ok(value) => sess.dcx().fatal(format!(
574 "invalid log color value '{value}': expected one of always, never, or auto",
575 )),
576 Err(VarError::NotUnicode(value)) => sess.dcx().fatal(format!(
577 "invalid log color value '{}': expected one of always, never, or auto",
578 value.to_string_lossy()
579 )),
580 };
581
582 let tree_layer = tracing_tree::HierarchicalLayer::default()
583 .with_writer(io::stderr)
584 .with_ansi(color_logs)
585 .with_targets(true)
586 .with_wraparound(10)
587 .with_verbose_exit(true)
588 .with_verbose_entry(true)
589 .with_indent_amount(2);
590
591 #[cfg(debug_assertions)]
592 let tree_layer = tree_layer.with_thread_ids(true).with_thread_names(true);
593
594 Box::new(subscriber.with(tree_layer))
595 }
596 OutputFormat::Flat => Box::new(subscriber),
597 OutputFormat::Json => Box::new(subscriber.with(tracing_subscriber::fmt::layer().json())),
598 };
599 tracing::subscriber::set_global_default(subscriber).unwrap();
600}