Skip to main content

rustc_interface/
util.rs

1use std::any::Any;
2use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
3use std::path::{Path, PathBuf};
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::{Arc, OnceLock};
6use std::{env, thread};
7
8use rustc_ast as ast;
9use rustc_attr_parsing::ShouldEmit;
10use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder};
11use rustc_codegen_ssa::back::link::link_binary;
12use rustc_codegen_ssa::target_features::cfg_target_feature;
13use rustc_codegen_ssa::traits::CodegenBackend;
14use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig};
15use rustc_data_structures::fx::FxIndexMap;
16use rustc_data_structures::jobserver::Proxy;
17use rustc_data_structures::sync;
18use rustc_metadata::{DylibError, EncodedMetadata, load_symbol_from_dylib};
19use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
20use rustc_middle::ty::{CurrentGcx, TyCtxt};
21use rustc_query_impl::{CollectActiveJobsKind, collect_active_query_jobs};
22use rustc_session::config::{
23    Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple,
24};
25use rustc_session::{EarlyDiagCtxt, Session, filesearch};
26use rustc_span::edition::Edition;
27use rustc_span::source_map::SourceMapInputs;
28use rustc_span::{SessionGlobals, Symbol, sym};
29use rustc_target::spec::Target;
30use tracing::info;
31
32use crate::errors;
33use crate::passes::parse_crate_name;
34
35/// Function pointer type that constructs a new CodegenBackend.
36type MakeBackendFn = fn() -> Box<dyn CodegenBackend>;
37
38/// Adds `target_feature = "..."` cfgs for a variety of platform
39/// specific features (SSE, NEON etc.).
40///
41/// This is performed by checking whether a set of permitted features
42/// is available on the target machine, by querying the codegen backend.
43pub(crate) fn add_configuration(
44    cfg: &mut Cfg,
45    sess: &mut Session,
46    codegen_backend: &dyn CodegenBackend,
47) {
48    let tf = sym::target_feature;
49    let tf_cfg = codegen_backend.target_config(sess);
50
51    sess.unstable_target_features.extend(tf_cfg.unstable_target_features.iter().copied());
52    sess.target_features.extend(tf_cfg.target_features.iter().copied());
53
54    cfg.extend(tf_cfg.target_features.into_iter().map(|feat| (tf, Some(feat))));
55
56    if tf_cfg.has_reliable_f16 {
57        cfg.insert((sym::target_has_reliable_f16, None));
58    }
59    if tf_cfg.has_reliable_f16_math {
60        cfg.insert((sym::target_has_reliable_f16_math, None));
61    }
62    if tf_cfg.has_reliable_f128 {
63        cfg.insert((sym::target_has_reliable_f128, None));
64    }
65    if tf_cfg.has_reliable_f128_math {
66        cfg.insert((sym::target_has_reliable_f128_math, None));
67    }
68
69    if sess.crt_static(None) {
70        cfg.insert((tf, Some(sym::crt_dash_static)));
71    }
72}
73
74/// Ensures that all target features required by the ABI are present.
75/// Must be called after `unstable_target_features` has been populated!
76pub(crate) fn check_abi_required_features(sess: &Session) {
77    let abi_feature_constraints = sess.target.abi_required_features();
78    // We check this against `unstable_target_features` as that is conveniently already
79    // back-translated to rustc feature names, taking into account `-Ctarget-cpu` and `-Ctarget-feature`.
80    // Just double-check that the features we care about are actually on our list.
81    for feature in
82        abi_feature_constraints.required.iter().chain(abi_feature_constraints.incompatible.iter())
83    {
84        if !sess.target.rust_target_features().iter().any(|(name, ..)|
                feature == name) {
    {
        ::core::panicking::panic_fmt(format_args!("target feature {0} is required/incompatible for the current ABI but not a recognized feature for this target",
                feature));
    }
};assert!(
85            sess.target.rust_target_features().iter().any(|(name, ..)| feature == name),
86            "target feature {feature} is required/incompatible for the current ABI but not a recognized feature for this target"
87        );
88    }
89
90    for feature in abi_feature_constraints.required {
91        if !sess.unstable_target_features.contains(&Symbol::intern(feature)) {
92            sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "enabled" });
93        }
94    }
95    for feature in abi_feature_constraints.incompatible {
96        if sess.unstable_target_features.contains(&Symbol::intern(feature)) {
97            sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "disabled" });
98        }
99    }
100}
101
102pub static STACK_SIZE: OnceLock<usize> = OnceLock::new();
103pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024;
104
105fn init_stack_size(early_dcx: &EarlyDiagCtxt) -> usize {
106    // Obey the environment setting or default
107    *STACK_SIZE.get_or_init(|| {
108        env::var_os("RUST_MIN_STACK")
109            .as_ref()
110            .map(|os_str| os_str.to_string_lossy())
111            // if someone finds out `export RUST_MIN_STACK=640000` isn't enough stack
112            // they might try to "unset" it by running `RUST_MIN_STACK=  rustc code.rs`
113            // this is wrong, but std would nonetheless "do what they mean", so let's do likewise
114            .filter(|s| !s.trim().is_empty())
115            // rustc is a batch program, so error early on inputs which are unlikely to be intended
116            // so no one thinks we parsed them setting `RUST_MIN_STACK="64 megabytes"`
117            // FIXME: we could accept `RUST_MIN_STACK=64MB`, perhaps?
118            .map(|s| {
119                let s = s.trim();
120                s.parse::<usize>().unwrap_or_else(|_| {
121                    let mut err = early_dcx.early_struct_fatal(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`RUST_MIN_STACK` should be a number of bytes, but was \"{0}\"",
                s))
    })format!(
122                        r#"`RUST_MIN_STACK` should be a number of bytes, but was "{s}""#,
123                    ));
124                    err.note("you can also unset `RUST_MIN_STACK` to use the default stack size");
125                    err.emit()
126                })
127            })
128            // otherwise pick a consistent default
129            .unwrap_or(DEFAULT_STACK_SIZE)
130    })
131}
132
133fn run_in_thread_with_globals<F: FnOnce(CurrentGcx, Arc<Proxy>) -> R + Send, R: Send>(
134    thread_stack_size: usize,
135    edition: Edition,
136    sm_inputs: SourceMapInputs,
137    extra_symbols: &[&'static str],
138    f: F,
139) -> R {
140    // The "thread pool" is a single spawned thread in the non-parallel
141    // compiler. We run on a spawned thread instead of the main thread (a) to
142    // provide control over the stack size, and (b) to increase similarity with
143    // the parallel compiler, in particular to ensure there is no accidental
144    // sharing of data between the main thread and the compilation thread
145    // (which might cause problems for the parallel compiler).
146    let builder = thread::Builder::new().name("rustc".to_string()).stack_size(thread_stack_size);
147
148    // We build the session globals and run `f` on the spawned thread, because
149    // `SessionGlobals` does not impl `Send` in the non-parallel compiler.
150    thread::scope(|s| {
151        // `unwrap` is ok here because `spawn_scoped` only panics if the thread
152        // name contains null bytes.
153        let r = builder
154            .spawn_scoped(s, move || {
155                rustc_span::create_session_globals_then(
156                    edition,
157                    extra_symbols,
158                    Some(sm_inputs),
159                    || f(CurrentGcx::new(), Proxy::new()),
160                )
161            })
162            .unwrap()
163            .join();
164
165        match r {
166            Ok(v) => v,
167            Err(e) => std::panic::resume_unwind(e),
168        }
169    })
170}
171
172pub(crate) fn run_in_thread_pool_with_globals<
173    F: FnOnce(CurrentGcx, Arc<Proxy>) -> R + Send,
174    R: Send,
175>(
176    thread_builder_diag: &EarlyDiagCtxt,
177    edition: Edition,
178    threads: usize,
179    extra_symbols: &[&'static str],
180    sm_inputs: SourceMapInputs,
181    f: F,
182) -> R {
183    use std::process;
184
185    use rustc_data_structures::defer;
186    use rustc_middle::ty::tls;
187    use rustc_query_impl::break_query_cycles;
188
189    let thread_stack_size = init_stack_size(thread_builder_diag);
190
191    let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap());
192
193    let Some(proof) = sync::check_dyn_thread_safe() else {
194        return run_in_thread_with_globals(
195            thread_stack_size,
196            edition,
197            sm_inputs,
198            extra_symbols,
199            |current_gcx, jobserver_proxy| {
200                // Register the thread for use with the `WorkerLocal` type.
201                registry.register();
202
203                f(current_gcx, jobserver_proxy)
204            },
205        );
206    };
207
208    let current_gcx = proof.derive(CurrentGcx::new());
209    let current_gcx2 = current_gcx.clone();
210
211    let proxy = Proxy::new();
212
213    let proxy_ = Arc::clone(&proxy);
214    let proxy__ = Arc::clone(&proxy);
215    let builder = rustc_thread_pool::ThreadPoolBuilder::new()
216        .thread_name(|_| "rustc".to_string())
217        .acquire_thread_handler(move || proxy_.acquire_thread())
218        .release_thread_handler(move || proxy__.release_thread())
219        .num_threads(threads)
220        .deadlock_handler(move || {
221            // On deadlock, creates a new thread and forwards information in thread
222            // locals to it. The new thread runs the deadlock handler.
223
224            let current_gcx2 = current_gcx2.clone();
225            let registry = rustc_thread_pool::Registry::current();
226            let session_globals = rustc_span::with_session_globals(|session_globals| {
227                session_globals as *const SessionGlobals as usize
228            });
229            thread::Builder::new()
230                .name("rustc query cycle handler".to_string())
231                .spawn(move || {
232                    let on_panic = defer(|| {
233                        // Split this long string so that it doesn't cause rustfmt to
234                        // give up on the entire builder expression.
235                        // <https://github.com/rust-lang/rustfmt/issues/3863>
236                        const MESSAGE: &str = "\
237internal compiler error: query cycle handler thread panicked, aborting process";
238                        { ::std::io::_eprint(format_args!("{0}\n", MESSAGE)); };eprintln!("{MESSAGE}");
239                        // We need to abort here as we failed to resolve the deadlock,
240                        // otherwise the compiler could just hang,
241                        process::abort();
242                    });
243
244                    // Get a `GlobalCtxt` reference from `CurrentGcx` as we cannot rely on having a
245                    // `TyCtxt` TLS reference here.
246                    current_gcx2.access(|gcx| {
247                        tls::enter_context(&tls::ImplicitCtxt::new(gcx), || {
248                            tls::with(|tcx| {
249                                // Accessing session globals is sound as they outlive `GlobalCtxt`.
250                                // They are needed to hash query keys containing spans or symbols.
251                                let job_map = rustc_span::set_session_globals_then(
252                                    unsafe { &*(session_globals as *const SessionGlobals) },
253                                    || {
254                                        // Ensure there were no errors collecting all active jobs.
255                                        // We need the complete map to ensure we find a cycle to
256                                        // break.
257                                        collect_active_query_jobs(
258                                            tcx,
259                                            CollectActiveJobsKind::FullNoContention,
260                                        )
261                                    },
262                                );
263                                break_query_cycles(job_map, &registry);
264                            })
265                        })
266                    });
267
268                    on_panic.disable();
269                })
270                .unwrap();
271        })
272        .stack_size(thread_stack_size);
273
274    // We create the session globals on the main thread, then create the thread
275    // pool. Upon creation, each worker thread created gets a copy of the
276    // session globals in TLS. This is possible because `SessionGlobals` impls
277    // `Send` in the parallel compiler.
278    rustc_span::create_session_globals_then(edition, extra_symbols, Some(sm_inputs), || {
279        rustc_span::with_session_globals(|session_globals| {
280            let session_globals = proof.derive(session_globals);
281            builder
282                .build_scoped(
283                    // Initialize each new worker thread when created.
284                    move |thread: rustc_thread_pool::ThreadBuilder| {
285                        // Register the thread for use with the `WorkerLocal` type.
286                        registry.register();
287
288                        rustc_span::set_session_globals_then(session_globals.into_inner(), || {
289                            thread.run()
290                        })
291                    },
292                    // Run `f` on the first thread in the thread pool.
293                    move |pool: &rustc_thread_pool::ThreadPool| {
294                        pool.install(|| f(current_gcx.into_inner(), proxy))
295                    },
296                )
297                .unwrap_or_else(|err| {
298                    let mut diag = thread_builder_diag.early_struct_fatal(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("failed to spawn compiler thread pool: could not create {0} threads ({1})",
                threads, err))
    })format!(
299                        "failed to spawn compiler thread pool: could not create {threads} threads ({err})",
300                    ));
301                    diag.help(
302                        "try lowering `-Z threads` or checking the operating system's resource limits",
303                    );
304                    diag.emit()
305                })
306        })
307    })
308}
309
310fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn {
311    match unsafe { load_symbol_from_dylib::<MakeBackendFn>(path, "__rustc_codegen_backend") } {
312        Ok(backend_sym) => backend_sym,
313        Err(DylibError::DlOpen(path, err)) => {
314            let err = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("couldn\'t load codegen backend {0}{1}",
                path, err))
    })format!("couldn't load codegen backend {path}{err}");
315            early_dcx.early_fatal(err);
316        }
317        Err(DylibError::DlSym(_path, err)) => {
318            let e = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`__rustc_codegen_backend` symbol lookup in the codegen backend failed{0}",
                err))
    })format!(
319                "`__rustc_codegen_backend` symbol lookup in the codegen backend failed{err}",
320            );
321            early_dcx.early_fatal(e);
322        }
323    }
324}
325
326/// Get the codegen backend based on the name and specified sysroot.
327///
328/// A name of `None` indicates that the default backend should be used.
329pub fn get_codegen_backend(
330    early_dcx: &EarlyDiagCtxt,
331    sysroot: &Sysroot,
332    backend_name: Option<&str>,
333    target: &Target,
334) -> Box<dyn CodegenBackend> {
335    static LOAD: OnceLock<unsafe fn() -> Box<dyn CodegenBackend>> = OnceLock::new();
336
337    let load = LOAD.get_or_init(|| {
338        let backend = backend_name
339            .or(target.default_codegen_backend.as_deref())
340            .or(::core::option::Option::Some("llvm")option_env!("CFG_DEFAULT_CODEGEN_BACKEND"))
341            .unwrap_or("dummy");
342
343        match backend {
344            filename if filename.contains('.') => {
345                load_backend_from_dylib(early_dcx, filename.as_ref())
346            }
347            "dummy" => || Box::new(DummyCodegenBackend { target_config_override: None }),
348            #[cfg(feature = "llvm")]
349            "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new,
350            backend_name => get_codegen_sysroot(early_dcx, sysroot, backend_name),
351        }
352    });
353
354    // SAFETY: In case of a builtin codegen backend this is safe. In case of an external codegen
355    // backend we hope that the backend links against the same rustc_driver version. If this is not
356    // the case, we get UB.
357    unsafe { load() }
358}
359
360pub struct DummyCodegenBackend {
361    pub target_config_override: Option<Box<dyn Fn(&Session) -> TargetConfig>>,
362}
363
364impl CodegenBackend for DummyCodegenBackend {
365    fn name(&self) -> &'static str {
366        "dummy"
367    }
368
369    fn target_config(&self, sess: &Session) -> TargetConfig {
370        if let Some(target_config_override) = &self.target_config_override {
371            return target_config_override(sess);
372        }
373
374        let abi_required_features = sess.target.abi_required_features();
375        let (target_features, unstable_target_features) = cfg_target_feature::<0>(
376            sess,
377            |_feature| Default::default(),
378            |feature| {
379                // This is a standin for the list of features a backend is expected to enable.
380                // It would be better to parse target.features instead and handle implied features,
381                // but target.features doesn't contain features that are enabled by default for an
382                // architecture or target cpu.
383                abi_required_features.required.contains(&feature)
384            },
385        );
386
387        TargetConfig {
388            target_features,
389            unstable_target_features,
390            has_reliable_f16: true,
391            has_reliable_f16_math: true,
392            has_reliable_f128: true,
393            has_reliable_f128_math: true,
394        }
395    }
396
397    fn supported_crate_types(&self, _sess: &Session) -> Vec<CrateType> {
398        // This includes bin despite failing on the link step to ensure that you
399        // can still get the frontend handling for binaries. For all library
400        // like crate types cargo will fallback to rlib unless you specifically
401        // say that only a different crate type must be used.
402        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [CrateType::Rlib, CrateType::Executable]))vec![CrateType::Rlib, CrateType::Executable]
403    }
404
405    fn target_cpu(&self, _sess: &Session) -> String {
406        String::new()
407    }
408
409    fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>, _crate_info: &CrateInfo) -> Box<dyn Any> {
410        Box::new(CompiledModules { modules: ::alloc::vec::Vec::new()vec![], allocator_module: None })
411    }
412
413    fn join_codegen(
414        &self,
415        ongoing_codegen: Box<dyn Any>,
416        _sess: &Session,
417        _outputs: &OutputFilenames,
418    ) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
419        (*ongoing_codegen.downcast().unwrap(), FxIndexMap::default())
420    }
421
422    fn link(
423        &self,
424        sess: &Session,
425        compiled_modules: CompiledModules,
426        crate_info: CrateInfo,
427        metadata: EncodedMetadata,
428        outputs: &OutputFilenames,
429    ) {
430        // JUSTIFICATION: TyCtxt no longer available here
431        #[allow(rustc::bad_opt_access)]
432        if let Some(&crate_type) =
433            crate_info.crate_types.iter().find(|&&crate_type| crate_type != CrateType::Rlib)
434            && outputs.outputs.should_link()
435        {
436            sess.dcx().fatal(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("crate type {0} not supported by the dummy codegen backend",
                crate_type))
    })format!(
437                "crate type {crate_type} not supported by the dummy codegen backend"
438            ));
439        }
440
441        link_binary(
442            sess,
443            &DummyArchiveBuilderBuilder,
444            compiled_modules,
445            crate_info,
446            metadata,
447            outputs,
448            self.name(),
449        );
450    }
451}
452
453struct DummyArchiveBuilderBuilder;
454
455impl ArchiveBuilderBuilder for DummyArchiveBuilderBuilder {
456    fn new_archive_builder<'a>(
457        &self,
458        sess: &'a Session,
459    ) -> Box<dyn rustc_codegen_ssa::back::archive::ArchiveBuilder + 'a> {
460        ArArchiveBuilderBuilder.new_archive_builder(sess)
461    }
462
463    fn create_dll_import_lib(
464        &self,
465        sess: &Session,
466        _lib_name: &str,
467        _items: Vec<rustc_codegen_ssa::back::archive::ImportLibraryItem>,
468        output_path: &Path,
469    ) {
470        // Build an empty static library to avoid calling an external dlltool on mingw
471        ArArchiveBuilderBuilder.new_archive_builder(sess).build(output_path);
472    }
473}
474
475// This is used for rustdoc, but it uses similar machinery to codegen backend
476// loading, so we leave the code here. It is potentially useful for other tools
477// that want to invoke the rustc binary while linking to rustc as well.
478pub fn rustc_path<'a>(sysroot: &Sysroot) -> Option<&'a Path> {
479    static RUSTC_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
480
481    RUSTC_PATH
482        .get_or_init(|| {
483            let candidate = sysroot
484                .default
485                .join("bin"env!("RUSTC_INSTALL_BINDIR"))
486                .join(if falsecfg!(target_os = "windows") { "rustc.exe" } else { "rustc" });
487            candidate.exists().then_some(candidate)
488        })
489        .as_deref()
490}
491
492fn get_codegen_sysroot(
493    early_dcx: &EarlyDiagCtxt,
494    sysroot: &Sysroot,
495    backend_name: &str,
496) -> MakeBackendFn {
497    // For now we only allow this function to be called once as it'll dlopen a
498    // few things, which seems to work best if we only do that once. In
499    // general this assertion never trips due to the once guard in `get_codegen_backend`,
500    // but there's a few manual calls to this function in this file we protect
501    // against.
502    static LOADED: AtomicBool = AtomicBool::new(false);
503    if !!LOADED.fetch_or(true, Ordering::SeqCst) {
    {
        ::core::panicking::panic_fmt(format_args!("cannot load the default codegen backend twice"));
    }
};assert!(
504        !LOADED.fetch_or(true, Ordering::SeqCst),
505        "cannot load the default codegen backend twice"
506    );
507
508    let target = host_tuple();
509
510    let sysroot = sysroot
511        .all_paths()
512        .map(|sysroot| {
513            filesearch::make_target_lib_path(sysroot, target).with_file_name("codegen-backends")
514        })
515        .find(|f| {
516            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_interface/src/util.rs:516",
                        "rustc_interface::util", ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_interface/src/util.rs"),
                        ::tracing_core::__macro_support::Option::Some(516u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_interface::util"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("codegen backend candidate: {0}",
                                                    f.display()) as &dyn Value))])
            });
    } else { ; }
};info!("codegen backend candidate: {}", f.display());
517            f.exists()
518        })
519        .unwrap_or_else(|| {
520            let candidates = sysroot
521                .all_paths()
522                .map(|p| p.display().to_string())
523                .collect::<Vec<_>>()
524                .join("\n* ");
525            let err = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("failed to find a `codegen-backends` folder in the sysroot candidates:\n* {0}",
                candidates))
    })format!(
526                "failed to find a `codegen-backends` folder in the sysroot candidates:\n\
527                 * {candidates}"
528            );
529            early_dcx.early_fatal(err);
530        });
531
532    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_interface/src/util.rs:532",
                        "rustc_interface::util", ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_interface/src/util.rs"),
                        ::tracing_core::__macro_support::Option::Some(532u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_interface::util"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("probing {0} for a codegen backend",
                                                    sysroot.display()) as &dyn Value))])
            });
    } else { ; }
};info!("probing {} for a codegen backend", sysroot.display());
533
534    let d = sysroot.read_dir().unwrap_or_else(|e| {
535        let err = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("failed to load default codegen backend, couldn\'t read `{0}`: {1}",
                sysroot.display(), e))
    })format!(
536            "failed to load default codegen backend, couldn't read `{}`: {e}",
537            sysroot.display(),
538        );
539        early_dcx.early_fatal(err);
540    });
541
542    let mut file: Option<PathBuf> = None;
543
544    let expected_names = &[
545        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("rustc_codegen_{0}-{1}",
                backend_name, "1.96.0-nightly"))
    })format!("rustc_codegen_{}-{}", backend_name, env!("CFG_RELEASE")),
546        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("rustc_codegen_{0}", backend_name))
    })format!("rustc_codegen_{backend_name}"),
547    ];
548    for entry in d.filter_map(|e| e.ok()) {
549        let path = entry.path();
550        let Some(filename) = path.file_name().and_then(|s| s.to_str()) else { continue };
551        if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) {
552            continue;
553        }
554        let name = &filename[DLL_PREFIX.len()..filename.len() - DLL_SUFFIX.len()];
555        if !expected_names.iter().any(|expected| expected == name) {
556            continue;
557        }
558        if let Some(ref prev) = file {
559            let err = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("duplicate codegen backends found\nfirst:  {0}\nsecond: {1}\n",
                prev.display(), path.display()))
    })format!(
560                "duplicate codegen backends found\n\
561                               first:  {}\n\
562                               second: {}\n\
563            ",
564                prev.display(),
565                path.display()
566            );
567            early_dcx.early_fatal(err);
568        }
569        file = Some(path.clone());
570    }
571
572    match file {
573        Some(ref s) => load_backend_from_dylib(early_dcx, s),
574        None => {
575            let err = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("unsupported builtin codegen backend `{0}`",
                backend_name))
    })format!("unsupported builtin codegen backend `{backend_name}`");
576            early_dcx.early_fatal(err);
577        }
578    }
579}
580
581fn multiple_output_types_to_stdout(
582    output_types: &OutputTypes,
583    single_output_file_is_stdout: bool,
584) -> bool {
585    use std::io::IsTerminal;
586    if std::io::stdout().is_terminal() {
587        // If stdout is a tty, check if multiple text output types are
588        // specified by `--emit foo=- --emit bar=-` or `-o - --emit foo,bar`
589        let named_text_types = output_types
590            .iter()
591            .filter(|(f, o)| f.is_text_output() && *o == &Some(OutFileName::Stdout))
592            .count();
593        let unnamed_text_types =
594            output_types.iter().filter(|(f, o)| f.is_text_output() && o.is_none()).count();
595        named_text_types > 1 || unnamed_text_types > 1 && single_output_file_is_stdout
596    } else {
597        // Otherwise, all the output types should be checked
598        let named_types =
599            output_types.values().filter(|o| *o == &Some(OutFileName::Stdout)).count();
600        let unnamed_types = output_types.values().filter(|o| o.is_none()).count();
601        named_types > 1 || unnamed_types > 1 && single_output_file_is_stdout
602    }
603}
604
605pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames {
606    if multiple_output_types_to_stdout(
607        &sess.opts.output_types,
608        sess.io.output_file == Some(OutFileName::Stdout),
609    ) {
610        sess.dcx().emit_fatal(errors::MultipleOutputTypesToStdout);
611    }
612
613    let crate_name =
614        sess.opts.crate_name.clone().or_else(|| {
615            parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string())
616        });
617
618    match sess.io.output_file {
619        None => {
620            // "-" as input file will cause the parser to read from stdin so we
621            // have to make up a name
622            // We want to toss everything after the final '.'
623            let dirpath = sess.io.output_dir.clone().unwrap_or_default();
624
625            // If a crate name is present, we use it as the link name
626            let stem = crate_name.clone().unwrap_or_else(|| sess.io.input.filestem().to_owned());
627
628            OutputFilenames::new(
629                dirpath,
630                crate_name.unwrap_or_else(|| stem.replace('-', "_")),
631                stem,
632                None,
633                sess.io.temps_dir.clone(),
634                sess.opts.unstable_opts.split_dwarf_out_dir.clone(),
635                sess.opts.cg.extra_filename.clone(),
636                sess.opts.output_types.clone(),
637            )
638        }
639
640        Some(ref out_file) => {
641            let unnamed_output_types =
642                sess.opts.output_types.values().filter(|a| a.is_none()).count();
643            let ofile = if unnamed_output_types > 1 {
644                sess.dcx().emit_warn(errors::MultipleOutputTypesAdaption);
645                None
646            } else {
647                if !sess.opts.cg.extra_filename.is_empty() {
648                    sess.dcx().emit_warn(errors::IgnoringExtraFilename);
649                }
650                Some(out_file.clone())
651            };
652            if sess.io.output_dir.is_some() {
653                sess.dcx().emit_warn(errors::IgnoringOutDir);
654            }
655
656            let out_filestem =
657                out_file.filestem().unwrap_or_default().to_str().unwrap().to_string();
658            OutputFilenames::new(
659                out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
660                crate_name.unwrap_or_else(|| out_filestem.replace('-', "_")),
661                out_filestem,
662                ofile,
663                sess.io.temps_dir.clone(),
664                sess.opts.unstable_opts.split_dwarf_out_dir.clone(),
665                sess.opts.cg.extra_filename.clone(),
666                sess.opts.output_types.clone(),
667            )
668        }
669    }
670}
671
672/// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)" when invoked by an in-tree tool.
673pub macro version_str() {
674    option_env!("CFG_VERSION")
675}
676
677/// Returns the version string for `rustc` itself (which may be different from a tool version).
678pub fn rustc_version_str() -> Option<&'static str> {
679    ::core::option::Option::Some("1.96.0-nightly (23903d01c 2026-03-26)")version_str!()
680}