Skip to main content

rustc_codegen_spirv/
lib.rs

1// HACK(eddyb) start of `rustc_codegen_ssa` crate-level attributes (see `build.rs`).
2#![feature(deref_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// HACK(eddyb) end of `rustc_codegen_ssa` crate-level attributes (see `build.rs`).
10
11//! Welcome to the API documentation for the `rust-gpu` project, this API is
12//! unstable and mainly intended for developing on the project itself. This is
13//! the API documentation for `rustc_codegen_spirv` which is not that useful on
14//! its own. You might also be interested in the following crates. There's also
15//! the [Rust GPU Dev Guide][gpu-dev-guide] which contains more user-level
16//! information on how to use and setup `rust-gpu`.
17//!
18//! - [`spirv-builder`]
19//! - [`spirv-std`]
20//! - [`spirv-tools`]
21//! - [`spirv-tools-sys`]
22//!
23//! [gpu-dev-guide]: https://rust-gpu.github.io/rust-gpu/book
24//! [`spirv-builder`]: https://rust-gpu.github.io/rust-gpu/api/spirv_builder
25//! [`spirv-std`]: https://rust-gpu.github.io/rust-gpu/api/spirv_std
26//! [`spirv-tools`]: https://rust-gpu.github.io/rust-gpu/api/spirv_tools
27//! [`spirv-tools-sys`]: https://rust-gpu.github.io/rust-gpu/api/spirv_tools_sys
28#![feature(rustc_private)]
29// In `rustc_codegen_spirv_disable_pqp_cg_ssa` mode we stop `include!`ing the
30// patched `rustc_codegen_ssa`, so these copied crate-level feature gates can
31// become locally unused even though the default build still needs them.
32#![cfg_attr(rustc_codegen_spirv_disable_pqp_cg_ssa, allow(unused_features))]
33// crate-specific exceptions:
34#![allow(
35    clippy::enum_glob_use,      // pretty useful pattern with some codegen'd enums (e.g. rspirv::spirv::Op)
36    clippy::todo,               // still lots to implement :)
37
38    // FIXME(eddyb) new warnings from 1.83 rustup, apply their suggested changes.
39    mismatched_lifetime_syntaxes,
40)]
41
42// Unfortunately, this will not fail fast when compiling, but rather will wait for
43// rustc_codegen_spirv to be compiled. Putting this in build.rs will solve that problem, however,
44// that creates the much worse problem that then running `cargo check` will cause
45// rustc_codegen_spirv to be *compiled* instead of merely checked, something that takes
46// significantly longer. So, the trade-off between detecting a bad configuration slower for a
47// faster `cargo check` is worth it.
48#[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// HACK(eddyb) `build.rs` copies `rustc_codegen_ssa` (from the `rustc-dev` component)
54// and patches it to produce a "pqp" ("pre-`qptr`-patched") version that maintains
55// compatibility with "legacy" Rust-GPU pointer handling (mainly typed `alloca`s).
56//
57// FIXME(eddyb) get rid of this as soon as it's not needed anymore.
58#[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
59include!(concat!(env!("OUT_DIR"), "/pqp_cg_ssa.rs"));
60
61// HACK(eddyb) guide `rustc` to finding the right deps in the sysroot, which
62// (sadly) has to be outside `include!` to have any effect whatsoever.
63// FIXME(eddyb) this only really handles `bitflags`, not `object`.
64#[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// NOTE(eddyb) `mod maybe_pqp_cg_ssa` is defined by the above `include`, when
70// in the (default for now) `pqp_cg_ssa` mode (see `build.rs`).
71#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
72use rustc_codegen_ssa as maybe_pqp_cg_ssa;
73
74// FIXME(eddyb) remove all `#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]`
75// as soon as they're not needed anymore (i.e. using `rustc_codegen_ssa` again).
76#[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::Session;
163use rustc_session::config::{self, OutputFilenames, OutputType};
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        // Set up logging/tracing. See https://github.com/Rust-GPU/rust-gpu/issues/192.
200        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        // HACK(eddyb) this should be a superset of `target_features`,
216        // which *additionally* also includes unstable target features,
217        // but there is no reason to make a distinction for SPIR-V ones.
218        let unstable_target_features = target_features.clone();
219
220        TargetConfig {
221            target_features,
222            unstable_target_features,
223
224            // FIXME(eddyb) support and/or emulate `f16` and `f128`.
225            has_reliable_f16: false,
226            has_reliable_f16_math: false,
227            has_reliable_f128: false,
228            has_reliable_f128_math: false,
229        }
230    }
231
232    fn provide(&self, providers: &mut rustc_middle::util::Providers) {
233        // FIXME(eddyb) this is currently only passed back to us, specifically
234        // into `target_machine_factory` (which is a noop), but it might make
235        // sense to move some of the target feature parsing into here.
236        providers.queries.global_backend_features = |_tcx, ()| vec![];
237
238        crate::abi::provide(providers);
239        crate::attr::provide(&mut providers.queries);
240    }
241
242    fn target_cpu(&self, sess: &Session) -> String {
243        sess.opts
244            .cg
245            .target_cpu
246            .clone()
247            .unwrap_or_else(|| sess.target.cpu.to_string())
248    }
249
250    fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any> {
251        Box::new(maybe_pqp_cg_ssa::base::codegen_crate(Self, tcx))
252    }
253
254    fn join_codegen(
255        &self,
256        ongoing_codegen: Box<dyn Any>,
257        sess: &Session,
258        _outputs: &OutputFilenames,
259        crate_info: &CrateInfo,
260    ) -> (CompiledModules, WorkProductMap) {
261        ongoing_codegen
262            .downcast::<OngoingCodegen<Self>>()
263            .expect("Expected OngoingCodegen, found Box<Any>")
264            .join(sess, crate_info)
265    }
266
267    fn link(
268        &self,
269        sess: &Session,
270        compiled_modules: CompiledModules,
271        crate_info: CrateInfo,
272        metadata: EncodedMetadata,
273        outputs: &OutputFilenames,
274    ) {
275        let timer = sess.timer("link_crate");
276        link::link(
277            sess,
278            &compiled_modules,
279            &crate_info,
280            &metadata,
281            outputs,
282            crate_info.local_crate_name.as_str(),
283        );
284        drop(timer);
285    }
286
287    fn name(&self) -> &'static str {
288        "SpirvCodegenBackend"
289    }
290}
291
292struct SpirvModuleBuffer(Vec<u32>);
293
294impl SpirvModuleBuffer {
295    fn as_bytes(&self) -> &[u8] {
296        spirv_tools::binary::from_binary(&self.0)
297    }
298}
299impl ModuleBufferMethods for SpirvModuleBuffer {
300    fn data(&self) -> &[u8] {
301        self.as_bytes()
302    }
303}
304
305impl SpirvCodegenBackend {
306    fn optimize_common(
307        _cgcx: &CodegenContext,
308        module: &mut ModuleCodegen<<Self as WriteBackendMethods>::Module>,
309    ) {
310        // Apply DCE ("dead code elimination") to modules before ever serializing
311        // them as `.spv` files (technically, `.rcgu.o` files inside `.rlib`s),
312        // that will later get linked (potentially many times, esp. if this is
313        // some big upstream library, e.g. `core` itself), and will therefore
314        // benefit from not having to clean up all sorts of unreachable helpers.
315        linker::dce::dce(&mut module.module_llvm);
316
317        // FIXME(eddyb) run as many optimization passes as possible, not just DCE.
318    }
319}
320
321impl WriteBackendMethods for SpirvCodegenBackend {
322    type Module = rspirv::dr::Module;
323    type TargetMachine = ();
324    type ModuleBuffer = SpirvModuleBuffer;
325    type ThinData = ();
326
327    // FIXME(eddyb) reuse the "merge" stage of `crate::linker` for this, or even
328    // consider setting `requires_lto = true` in the target specs and moving the
329    // entirety of `crate::linker` into this stage (lacking diagnostics may be
330    // an issue - it's surprising `CodegenBackend::link` has `Session` at all).
331    fn optimize_and_codegen_fat_lto(
332        _sess: &Session,
333        cgcx: &CodegenContext,
334        _shared_emitter: &SharedEmitter,
335        _tm_factory: TargetMachineFactoryFn<Self>,
336        _exported_symbols_for_lto: &[String],
337        _each_linked_rlib_for_lto: &[PathBuf],
338        _modules: Vec<FatLtoInput<Self>>,
339    ) -> CompiledModule {
340        assert!(
341            cgcx.lto == config::Lto::Fat,
342            "`optimize_and_codegen_fat_lto` should \
343             only be invoked due to `-Clto` (or equivalent)"
344        );
345        unreachable!("Rust-GPU does not support fat LTO")
346    }
347
348    fn run_thin_lto(
349        _cgcx: &CodegenContext,
350        _prof: &SelfProfilerRef,
351        _dcx: DiagCtxtHandle<'_>,
352        _exported_symbols_for_lto: &[String],
353        _each_linked_rlib_for_lto: &[PathBuf],
354        _modules: Vec<ThinLtoInput<Self>>,
355    ) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) {
356        // Note(@firestar99): gcc impl this as unreachable as well
357        unreachable!()
358    }
359
360    fn optimize(
361        cgcx: &CodegenContext,
362        _prof: &SelfProfilerRef,
363        _shared_emitter: &SharedEmitter,
364        module: &mut ModuleCodegen<Self::Module>,
365        _config: &ModuleConfig,
366    ) {
367        Self::optimize_common(cgcx, module);
368    }
369
370    fn optimize_and_codegen_thin(
371        cgcx: &CodegenContext,
372        prof: &SelfProfilerRef,
373        shared_emitter: &SharedEmitter,
374        _tm_factory: TargetMachineFactoryFn<Self>,
375        thin_module: ThinModule<Self>,
376    ) -> CompiledModule {
377        // FIXME(eddyb) the inefficiency of Module -> [u8] -> Module roundtrips
378        // comes from upstream and it applies to `rustc_codegen_llvm` as well,
379        // eventually it should be properly addressed (for `ThinLocal` at least).
380        let mut module = ModuleCodegen {
381            module_llvm: link::with_rspirv_loader(|loader| {
382                rspirv::binary::parse_bytes(thin_module.data(), loader)
383            })
384            .unwrap(),
385            name: thin_module.name().to_string(),
386            kind: ModuleKind::Regular,
387            thin_lto_buffer: None,
388        };
389        Self::optimize_common(cgcx, &mut module);
390        Self::codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config)
391    }
392
393    fn codegen(
394        cgcx: &CodegenContext,
395        _prof: &SelfProfilerRef,
396        _shared_emitter: &SharedEmitter,
397        module: ModuleCodegen<Self::Module>,
398        _config: &ModuleConfig,
399    ) -> CompiledModule {
400        let kind = module.kind;
401        let name = module.name;
402        let module_buffer = Self::serialize_module(module.module_llvm, false);
403
404        let path = cgcx
405            .output_filenames
406            .temp_path_for_cgu(OutputType::Object, &name);
407        fs::write(&path, module_buffer.as_bytes()).unwrap();
408
409        CompiledModule {
410            name,
411            kind,
412            object: Some(path),
413            global_asm_object: None,
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 serialize_module(module: Self::Module, _is_thin: bool) -> Self::ModuleBuffer {
423        SpirvModuleBuffer(module.assemble())
424    }
425
426    fn target_machine_factory(
427        &self,
428        _sess: &Session,
429        _opt_level: config::OptLevel,
430        _target_features: &[String],
431    ) -> TargetMachineFactoryFn<Self> {
432        Arc::new(|_, _| ())
433    }
434}
435
436impl ExtraBackendMethods for SpirvCodegenBackend {
437    type Module = rspirv::dr::Module;
438
439    fn codegen_allocator(&self, _: TyCtxt<'_>, _: &str, _: &[AllocatorMethod]) -> Self::Module {
440        todo!()
441    }
442
443    fn compile_codegen_unit<'tcx>(
444        &self,
445        tcx: TyCtxt<'tcx>,
446        cgu_name: Symbol,
447    ) -> (ModuleCodegen<Self::Module>, u64) {
448        let _timer = tcx
449            .prof
450            .verbose_generic_activity_with_arg("codegen_module", cgu_name.to_string());
451
452        // TODO: Do dep_graph stuff
453        let cgu = tcx.codegen_unit(cgu_name);
454
455        let mut cx = CodegenCx::new(tcx, cgu);
456        let do_codegen = |cx: &mut CodegenCx<'tcx>| {
457            let mono_items = cgu.items_in_deterministic_order(cx.tcx);
458
459            if let Some(dir) = &cx.codegen_args.dump_mir {
460                dump_mir(tcx, mono_items.as_slice(), &dir.join(cgu_name.to_string())).unwrap();
461            }
462
463            for &(mono_item, mono_item_data) in mono_items.iter() {
464                mono_item.predefine::<Builder<'_, '_>>(
465                    cx,
466                    cgu_name.as_str(),
467                    mono_item_data.linkage,
468                    mono_item_data.visibility,
469                );
470            }
471
472            // ... and now that we have everything pre-defined, fill out those definitions.
473            for &(mono_item, mono_item_data) in &mono_items {
474                tracing::trace!(?mono_item, "defining");
475                mono_item.define::<Builder<'_, '_>>(cx, cgu_name.as_str(), mono_item_data);
476            }
477
478            if let Some(_entry) = maybe_create_entry_wrapper::<Builder<'_, '_>>(cx, cgu) {
479                // attributes::sanitize(&cx, SanitizerSet::empty(), entry);
480            }
481        };
482        // HACK(eddyb) mutable access needed for `mono_item.define::<...>(cx, ...)`
483        // but that alone leads to needless cloning and smuggling a mutable borrow
484        // through `DumpModuleOnPanic` (for both its `Drop` impl and `do_codegen`).
485        if let Some(path) = cx.codegen_args.dump_module_on_panic.clone() {
486            let module_dumper = DumpModuleOnPanic {
487                cx: &mut cx,
488                path: &path,
489            };
490            with_no_trimmed_paths!(do_codegen(module_dumper.cx));
491            drop(module_dumper);
492        } else {
493            with_no_trimmed_paths!(do_codegen(&mut cx));
494        }
495
496        (
497            ModuleCodegen {
498                name: cgu_name.to_string(),
499                module_llvm: cx.finalize_module(),
500                kind: ModuleKind::Regular,
501                thin_lto_buffer: None,
502            },
503            0,
504        )
505    }
506}
507
508struct DumpModuleOnPanic<'a, 'cx, 'tcx> {
509    cx: &'cx mut CodegenCx<'tcx>,
510    path: &'a Path,
511}
512
513impl Drop for DumpModuleOnPanic<'_, '_, '_> {
514    fn drop(&mut self) {
515        if std::thread::panicking() {
516            if self.path.has_root() {
517                self.cx.builder.dump_module(self.path);
518            } else {
519                error!("{}", self.cx.builder.dump_module_str());
520            }
521        }
522    }
523}
524
525/// This is the entrypoint for a hot plugged `rustc_codegen_spirv`
526#[unsafe(no_mangle)]
527pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
528    // Tweak rustc's default ICE panic hook, to direct people to `rust-gpu`.
529    rustc_driver::install_ice_hook("https://github.com/rust-gpu/rust-gpu/issues/new", |dcx| {
530        dcx.handle().note(concat!(
531            "`rust-gpu` version `",
532            env!("CARGO_PKG_VERSION"),
533            "`"
534        ));
535    });
536
537    Box::new(SpirvCodegenBackend)
538}
539
540// Set up logging/tracing. See https://github.com/Rust-GPU/rust-gpu/issues/192.
541fn init_logging(sess: &Session) {
542    use std::env::{self, VarError};
543    use std::io::{self, IsTerminal};
544    use tracing_subscriber::layer::SubscriberExt;
545
546    // Set up the default subscriber with optional filtering.
547    let filter = tracing_subscriber::EnvFilter::from_env("RUSTGPU_LOG");
548    #[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
549    let filter = filter.add_directive("rustc_codegen_spirv::maybe_pqp_cg_ssa=off".parse().unwrap());
550    let subscriber = tracing_subscriber::Registry::default().with(filter);
551
552    #[derive(Debug, Default)]
553    enum OutputFormat {
554        #[default]
555        Tree,
556        Flat,
557        Json,
558    }
559
560    let output_format = match env::var("RUSTGPU_LOG_FORMAT").as_deref() {
561        Ok("tree") | Err(VarError::NotPresent) => OutputFormat::Tree,
562        Ok("flat") => OutputFormat::Flat,
563        Ok("json") => OutputFormat::Json,
564        Ok(value) => sess.dcx().fatal(format!(
565            "invalid output format value '{value}': expected one of tree, flat, or json",
566        )),
567        Err(VarError::NotUnicode(value)) => sess.dcx().fatal(format!(
568            "invalid output format value '{}': expected one of tree, flat, or json",
569            value.to_string_lossy()
570        )),
571    };
572
573    let subscriber: Box<dyn tracing::Subscriber + Send + Sync> = match output_format {
574        OutputFormat::Tree => {
575            // TODO(@LegNeato): Query dcx color support when rustc exposes it.
576            let color_logs = match env::var("RUSTGPU_LOG_COLOR").as_deref() {
577                Ok("always") => true,
578                Ok("never") => false,
579                Ok("auto") | Err(VarError::NotPresent) => io::stderr().is_terminal(),
580                Ok(value) => sess.dcx().fatal(format!(
581                    "invalid log color value '{value}': expected one of always, never, or auto",
582                )),
583                Err(VarError::NotUnicode(value)) => sess.dcx().fatal(format!(
584                    "invalid log color value '{}': expected one of always, never, or auto",
585                    value.to_string_lossy()
586                )),
587            };
588
589            let tree_layer = tracing_tree::HierarchicalLayer::default()
590                .with_writer(io::stderr)
591                .with_ansi(color_logs)
592                .with_targets(true)
593                .with_wraparound(10)
594                .with_verbose_exit(true)
595                .with_verbose_entry(true)
596                .with_indent_amount(2);
597
598            #[cfg(debug_assertions)]
599            let tree_layer = tree_layer.with_thread_ids(true).with_thread_names(true);
600
601            Box::new(subscriber.with(tree_layer))
602        }
603        OutputFormat::Flat => Box::new(subscriber),
604        OutputFormat::Json => Box::new(subscriber.with(tracing_subscriber::fmt::layer().json())),
605    };
606    tracing::subscriber::set_global_default(subscriber).unwrap();
607}