rustc_codegen_spirv/
lib.rs

1// HACK(eddyb) start of `rustc_codegen_ssa` crate-level attributes (see `build.rs`).
2#![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// HACK(eddyb) end of `rustc_codegen_ssa` crate-level attributes (see `build.rs`).
14
15//! Welcome to the API documentation for the `rust-gpu` project, this API is
16//! unstable and mainly intended for developing on the project itself. This is
17//! the API documentation for `rustc_codegen_spirv` which is not that useful on
18//! its own. You might also be interested in the following crates. There's also
19//! the [Rust GPU Dev Guide][gpu-dev-guide] which contains more user-level
20//! information on how to use and setup `rust-gpu`.
21//!
22//! - [`spirv-builder`]
23//! - [`spirv-std`]
24//! - [`spirv-tools`]
25//! - [`spirv-tools-sys`]
26//!
27//! [gpu-dev-guide]: https://rust-gpu.github.io/rust-gpu/book
28//! [`spirv-builder`]: https://rust-gpu.github.io/rust-gpu/api/spirv_builder
29//! [`spirv-std`]: https://rust-gpu.github.io/rust-gpu/api/spirv_std
30//! [`spirv-tools`]: https://rust-gpu.github.io/rust-gpu/api/spirv_tools
31//! [`spirv-tools-sys`]: https://rust-gpu.github.io/rust-gpu/api/spirv_tools_sys
32#![feature(rustc_private)]
33// crate-specific exceptions:
34#![allow(
35    unsafe_code,                // rustc_codegen_ssa requires unsafe functions in traits to be impl'd
36    clippy::enum_glob_use,      // pretty useful pattern with some codegen'd enums (e.g. rspirv::spirv::Op)
37    clippy::todo,               // still lots to implement :)
38
39    // FIXME(eddyb) new warnings from 1.83 rustup, apply their suggested changes.
40    mismatched_lifetime_syntaxes,
41    clippy::needless_lifetimes,
42)]
43
44// Unfortunately, this will not fail fast when compiling, but rather will wait for
45// rustc_codegen_spirv to be compiled. Putting this in build.rs will solve that problem, however,
46// that creates the much worse problem that then running `cargo check` will cause
47// rustc_codegen_spirv to be *compiled* instead of merely checked, something that takes
48// significantly longer. So, the trade-off between detecting a bad configuration slower for a
49// faster `cargo check` is worth it.
50#[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// HACK(eddyb) `build.rs` copies `rustc_codegen_ssa` (from the `rustc-dev` component)
56// and patches it to produce a "pqp" ("pre-`qptr`-patched") version that maintains
57// compatibility with "legacy" Rust-GPU pointer handling (mainly typed `alloca`s).
58//
59// FIXME(eddyb) get rid of this as soon as it's not needed anymore.
60#[cfg(not(rustc_codegen_spirv_disable_pqp_cg_ssa))]
61include!(concat!(env!("OUT_DIR"), "/pqp_cg_ssa.rs"));
62
63// HACK(eddyb) guide `rustc` to finding the right deps in the sysroot, which
64// (sadly) has to be outside `include!` to have any effect whatsoever.
65// FIXME(eddyb) this only really handles `bitflags`, not `object`.
66#[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// NOTE(eddyb) `mod maybe_pqp_cg_ssa` is defined by the above `include`, when
72// in the (default for now) `pqp_cg_ssa` mode (see `build.rs`).
73#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]
74use rustc_codegen_ssa as maybe_pqp_cg_ssa;
75
76// FIXME(eddyb) remove all `#[cfg(rustc_codegen_spirv_disable_pqp_cg_ssa)]`
77// as soon as they're not needed anymore (i.e. using `rustc_codegen_ssa` again).
78#[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        // Set up logging/tracing. See https://github.com/Rust-GPU/rust-gpu/issues/192.
193        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        // HACK(eddyb) this should be a superset of `target_features`,
213        // which *additionally* also includes unstable target features,
214        // but there is no reason to make a distinction for SPIR-V ones.
215        let unstable_target_features = target_features.clone();
216
217        TargetConfig {
218            target_features,
219            unstable_target_features,
220
221            // FIXME(eddyb) support and/or emulate `f16` and `f128`.
222            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        // FIXME(eddyb) this is currently only passed back to us, specifically
231        // into `target_machine_factory` (which is a noop), but it might make
232        // sense to move some of the target feature parsing into here.
233        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        // 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 TargetMachineError = String;
325    type ModuleBuffer = SpirvModuleBuffer;
326    type ThinData = ();
327    type ThinBuffer = SpirvModuleBuffer;
328
329    // FIXME(eddyb) reuse the "merge" stage of `crate::linker` for this, or even
330    // consider setting `requires_lto = true` in the target specs and moving the
331    // entirety of `crate::linker` into this stage (lacking diagnostics may be
332    // an issue - it's surprising `CodegenBackend::link` has `Session` at all).
333    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        // FIXME(bjorn3): Limit LTO exports to these symbols
350        _exported_symbols_for_lto: &[String],
351        _each_linked_rlib_for_lto: &[PathBuf], // njn: ?
352        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        // FIXME(eddyb) the inefficiency of Module -> [u8] -> Module roundtrips
380        // comes from upstream and it applies to `rustc_codegen_llvm` as well,
381        // eventually it should be properly addressed (for `ThinLocal` at least).
382        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        // TODO: Do dep_graph stuff
449        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            // ... and now that we have everything pre-defined, fill out those definitions.
469            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                // attributes::sanitize(&cx, SanitizerSet::empty(), entry);
476            }
477        };
478        // HACK(eddyb) mutable access needed for `mono_item.define::<...>(cx, ...)`
479        // but that alone leads to needless cloning and smuggling a mutable borrow
480        // through `DumpModuleOnPanic` (for both its `Drop` impl and `do_codegen`).
481        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/// This is the entrypoint for a hot plugged `rustc_codegen_spirv`
531#[unsafe(no_mangle)]
532pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
533    // Tweak rustc's default ICE panic hook, to direct people to `rust-gpu`.
534    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
545// Set up logging/tracing. See https://github.com/Rust-GPU/rust-gpu/issues/192.
546fn init_logging(sess: &Session) {
547    use std::env::{self, VarError};
548    use std::io::{self, IsTerminal};
549    use tracing_subscriber::layer::SubscriberExt;
550
551    // Set up the default subscriber with optional filtering.
552    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            // TODO(@LegNeato): Query dcx color support when rustc exposes it.
581            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}