1use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
2use std::path::{Path, PathBuf};
3use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::{Arc, OnceLock};
5use std::{env, thread};
6
7use rustc_ast as ast;
8use rustc_attr_parsing::{ShouldEmit, validate_attr};
9use rustc_codegen_ssa::traits::CodegenBackend;
10use rustc_data_structures::jobserver::Proxy;
11use rustc_data_structures::sync;
12use rustc_errors::LintBuffer;
13use rustc_metadata::{DylibError, load_symbol_from_dylib};
14use rustc_middle::ty::CurrentGcx;
15use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple};
16use rustc_session::output::{CRATE_TYPES, categorize_crate_type};
17use rustc_session::{EarlyDiagCtxt, Session, filesearch, lint};
18use rustc_span::edit_distance::find_best_match_for_name;
19use rustc_span::edition::Edition;
20use rustc_span::source_map::SourceMapInputs;
21use rustc_span::{SessionGlobals, Symbol, sym};
22use rustc_target::spec::Target;
23use tracing::info;
24
25use crate::errors;
26use crate::passes::parse_crate_name;
27
28type MakeBackendFn = fn() -> Box<dyn CodegenBackend>;
30
31pub(crate) fn add_configuration(
37 cfg: &mut Cfg,
38 sess: &mut Session,
39 codegen_backend: &dyn CodegenBackend,
40) {
41 let tf = sym::target_feature;
42 let tf_cfg = codegen_backend.target_config(sess);
43
44 sess.unstable_target_features.extend(tf_cfg.unstable_target_features.iter().copied());
45 sess.target_features.extend(tf_cfg.target_features.iter().copied());
46
47 cfg.extend(tf_cfg.target_features.into_iter().map(|feat| (tf, Some(feat))));
48
49 if tf_cfg.has_reliable_f16 {
50 cfg.insert((sym::target_has_reliable_f16, None));
51 }
52 if tf_cfg.has_reliable_f16_math {
53 cfg.insert((sym::target_has_reliable_f16_math, None));
54 }
55 if tf_cfg.has_reliable_f128 {
56 cfg.insert((sym::target_has_reliable_f128, None));
57 }
58 if tf_cfg.has_reliable_f128_math {
59 cfg.insert((sym::target_has_reliable_f128_math, None));
60 }
61
62 if sess.crt_static(None) {
63 cfg.insert((tf, Some(sym::crt_dash_static)));
64 }
65}
66
67pub(crate) fn check_abi_required_features(sess: &Session) {
70 let abi_feature_constraints = sess.target.abi_required_features();
71 for feature in
75 abi_feature_constraints.required.iter().chain(abi_feature_constraints.incompatible.iter())
76 {
77 assert!(
78 sess.target.rust_target_features().iter().any(|(name, ..)| feature == name),
79 "target feature {feature} is required/incompatible for the current ABI but not a recognized feature for this target"
80 );
81 }
82
83 for feature in abi_feature_constraints.required {
84 if !sess.unstable_target_features.contains(&Symbol::intern(feature)) {
85 sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "enabled" });
86 }
87 }
88 for feature in abi_feature_constraints.incompatible {
89 if sess.unstable_target_features.contains(&Symbol::intern(feature)) {
90 sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "disabled" });
91 }
92 }
93}
94
95pub static STACK_SIZE: OnceLock<usize> = OnceLock::new();
96pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024;
97
98fn init_stack_size(early_dcx: &EarlyDiagCtxt) -> usize {
99 *STACK_SIZE.get_or_init(|| {
101 env::var_os("RUST_MIN_STACK")
102 .as_ref()
103 .map(|os_str| os_str.to_string_lossy())
104 .filter(|s| !s.trim().is_empty())
108 .map(|s| {
112 let s = s.trim();
113 #[allow(rustc::untranslatable_diagnostic, rustc::diagnostic_outside_of_impl)]
115 s.parse::<usize>().unwrap_or_else(|_| {
116 let mut err = early_dcx.early_struct_fatal(format!(
117 r#"`RUST_MIN_STACK` should be a number of bytes, but was "{s}""#,
118 ));
119 err.note("you can also unset `RUST_MIN_STACK` to use the default stack size");
120 err.emit()
121 })
122 })
123 .unwrap_or(DEFAULT_STACK_SIZE)
125 })
126}
127
128fn run_in_thread_with_globals<F: FnOnce(CurrentGcx, Arc<Proxy>) -> R + Send, R: Send>(
129 thread_stack_size: usize,
130 edition: Edition,
131 sm_inputs: SourceMapInputs,
132 extra_symbols: &[&'static str],
133 f: F,
134) -> R {
135 let builder = thread::Builder::new().name("rustc".to_string()).stack_size(thread_stack_size);
142
143 thread::scope(|s| {
146 let r = builder
149 .spawn_scoped(s, move || {
150 rustc_span::create_session_globals_then(
151 edition,
152 extra_symbols,
153 Some(sm_inputs),
154 || f(CurrentGcx::new(), Proxy::new()),
155 )
156 })
157 .unwrap()
158 .join();
159
160 match r {
161 Ok(v) => v,
162 Err(e) => std::panic::resume_unwind(e),
163 }
164 })
165}
166
167pub(crate) fn run_in_thread_pool_with_globals<
168 F: FnOnce(CurrentGcx, Arc<Proxy>) -> R + Send,
169 R: Send,
170>(
171 thread_builder_diag: &EarlyDiagCtxt,
172 edition: Edition,
173 threads: usize,
174 extra_symbols: &[&'static str],
175 sm_inputs: SourceMapInputs,
176 f: F,
177) -> R {
178 use std::process;
179
180 use rustc_data_structures::defer;
181 use rustc_data_structures::sync::FromDyn;
182 use rustc_middle::ty::tls;
183 use rustc_query_impl::QueryCtxt;
184 use rustc_query_system::query::{QueryContext, break_query_cycles};
185
186 let thread_stack_size = init_stack_size(thread_builder_diag);
187
188 let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap());
189
190 if !sync::is_dyn_thread_safe() {
191 return run_in_thread_with_globals(
192 thread_stack_size,
193 edition,
194 sm_inputs,
195 extra_symbols,
196 |current_gcx, jobserver_proxy| {
197 registry.register();
199
200 f(current_gcx, jobserver_proxy)
201 },
202 );
203 }
204
205 let current_gcx = FromDyn::from(CurrentGcx::new());
206 let current_gcx2 = current_gcx.clone();
207
208 let proxy = Proxy::new();
209
210 let proxy_ = Arc::clone(&proxy);
211 let proxy__ = Arc::clone(&proxy);
212 let builder = rustc_thread_pool::ThreadPoolBuilder::new()
213 .thread_name(|_| "rustc".to_string())
214 .acquire_thread_handler(move || proxy_.acquire_thread())
215 .release_thread_handler(move || proxy__.release_thread())
216 .num_threads(threads)
217 .deadlock_handler(move || {
218 let current_gcx2 = current_gcx2.clone();
222 let registry = rustc_thread_pool::Registry::current();
223 let session_globals = rustc_span::with_session_globals(|session_globals| {
224 session_globals as *const SessionGlobals as usize
225 });
226 thread::Builder::new()
227 .name("rustc query cycle handler".to_string())
228 .spawn(move || {
229 let on_panic = defer(|| {
230 eprintln!("internal compiler error: query cycle handler thread panicked, aborting process");
231 process::abort();
234 });
235
236 current_gcx2.access(|gcx| {
239 tls::enter_context(&tls::ImplicitCtxt::new(gcx), || {
240 tls::with(|tcx| {
241 let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || {
244 QueryCtxt::new(tcx).collect_active_jobs().expect("failed to collect active queries in deadlock handler")
247 });
248 break_query_cycles(query_map, ®istry);
249 })
250 })
251 });
252
253 on_panic.disable();
254 })
255 .unwrap();
256 })
257 .stack_size(thread_stack_size);
258
259 rustc_span::create_session_globals_then(edition, extra_symbols, Some(sm_inputs), || {
264 rustc_span::with_session_globals(|session_globals| {
265 let session_globals = FromDyn::from(session_globals);
266 builder
267 .build_scoped(
268 move |thread: rustc_thread_pool::ThreadBuilder| {
270 registry.register();
272
273 rustc_span::set_session_globals_then(session_globals.into_inner(), || {
274 thread.run()
275 })
276 },
277 move |pool: &rustc_thread_pool::ThreadPool| {
279 pool.install(|| f(current_gcx.into_inner(), proxy))
280 },
281 )
282 .unwrap()
283 })
284 })
285}
286
287#[allow(rustc::untranslatable_diagnostic)] fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn {
289 match unsafe { load_symbol_from_dylib::<MakeBackendFn>(path, "__rustc_codegen_backend") } {
290 Ok(backend_sym) => backend_sym,
291 Err(DylibError::DlOpen(path, err)) => {
292 let err = format!("couldn't load codegen backend {path}{err}");
293 early_dcx.early_fatal(err);
294 }
295 Err(DylibError::DlSym(_path, err)) => {
296 let e = format!(
297 "`__rustc_codegen_backend` symbol lookup in the codegen backend failed{err}",
298 );
299 early_dcx.early_fatal(e);
300 }
301 }
302}
303
304pub fn get_codegen_backend(
308 early_dcx: &EarlyDiagCtxt,
309 sysroot: &Sysroot,
310 backend_name: Option<&str>,
311 target: &Target,
312) -> Box<dyn CodegenBackend> {
313 static LOAD: OnceLock<unsafe fn() -> Box<dyn CodegenBackend>> = OnceLock::new();
314
315 let load = LOAD.get_or_init(|| {
316 let backend = backend_name
317 .or(target.default_codegen_backend.as_deref())
318 .or(option_env!("CFG_DEFAULT_CODEGEN_BACKEND"))
319 .unwrap_or("llvm");
320
321 match backend {
322 filename if filename.contains('.') => {
323 load_backend_from_dylib(early_dcx, filename.as_ref())
324 }
325 #[cfg(feature = "llvm")]
326 "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new,
327 backend_name => get_codegen_sysroot(early_dcx, sysroot, backend_name),
328 }
329 });
330
331 unsafe { load() }
335}
336
337pub fn rustc_path<'a>(sysroot: &Sysroot) -> Option<&'a Path> {
341 static RUSTC_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
342
343 RUSTC_PATH
344 .get_or_init(|| {
345 let candidate = sysroot
346 .default
347 .join(env!("RUSTC_INSTALL_BINDIR"))
348 .join(if cfg!(target_os = "windows") { "rustc.exe" } else { "rustc" });
349 candidate.exists().then_some(candidate)
350 })
351 .as_deref()
352}
353
354#[allow(rustc::untranslatable_diagnostic)] fn get_codegen_sysroot(
356 early_dcx: &EarlyDiagCtxt,
357 sysroot: &Sysroot,
358 backend_name: &str,
359) -> MakeBackendFn {
360 static LOADED: AtomicBool = AtomicBool::new(false);
366 assert!(
367 !LOADED.fetch_or(true, Ordering::SeqCst),
368 "cannot load the default codegen backend twice"
369 );
370
371 let target = host_tuple();
372
373 let sysroot = sysroot
374 .all_paths()
375 .map(|sysroot| {
376 filesearch::make_target_lib_path(sysroot, target).with_file_name("codegen-backends")
377 })
378 .find(|f| {
379 info!("codegen backend candidate: {}", f.display());
380 f.exists()
381 })
382 .unwrap_or_else(|| {
383 let candidates = sysroot
384 .all_paths()
385 .map(|p| p.display().to_string())
386 .collect::<Vec<_>>()
387 .join("\n* ");
388 let err = format!(
389 "failed to find a `codegen-backends` folder in the sysroot candidates:\n\
390 * {candidates}"
391 );
392 early_dcx.early_fatal(err);
393 });
394
395 info!("probing {} for a codegen backend", sysroot.display());
396
397 let d = sysroot.read_dir().unwrap_or_else(|e| {
398 let err = format!(
399 "failed to load default codegen backend, couldn't read `{}`: {e}",
400 sysroot.display(),
401 );
402 early_dcx.early_fatal(err);
403 });
404
405 let mut file: Option<PathBuf> = None;
406
407 let expected_names = &[
408 format!("rustc_codegen_{}-{}", backend_name, env!("CFG_RELEASE")),
409 format!("rustc_codegen_{backend_name}"),
410 ];
411 for entry in d.filter_map(|e| e.ok()) {
412 let path = entry.path();
413 let Some(filename) = path.file_name().and_then(|s| s.to_str()) else { continue };
414 if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) {
415 continue;
416 }
417 let name = &filename[DLL_PREFIX.len()..filename.len() - DLL_SUFFIX.len()];
418 if !expected_names.iter().any(|expected| expected == name) {
419 continue;
420 }
421 if let Some(ref prev) = file {
422 let err = format!(
423 "duplicate codegen backends found\n\
424 first: {}\n\
425 second: {}\n\
426 ",
427 prev.display(),
428 path.display()
429 );
430 early_dcx.early_fatal(err);
431 }
432 file = Some(path.clone());
433 }
434
435 match file {
436 Some(ref s) => load_backend_from_dylib(early_dcx, s),
437 None => {
438 let err = format!("unsupported builtin codegen backend `{backend_name}`");
439 early_dcx.early_fatal(err);
440 }
441 }
442}
443
444pub(crate) fn check_attr_crate_type(
445 sess: &Session,
446 attrs: &[ast::Attribute],
447 lint_buffer: &mut LintBuffer,
448) {
449 for a in attrs.iter() {
451 if a.has_name(sym::crate_type) {
452 if let Some(n) = a.value_str() {
453 if categorize_crate_type(n).is_some() {
454 return;
455 }
456
457 if let ast::MetaItemKind::NameValue(spanned) = a.meta_kind().unwrap() {
458 let span = spanned.span;
459 let candidate = find_best_match_for_name(
460 &CRATE_TYPES.iter().map(|(k, _)| *k).collect::<Vec<_>>(),
461 n,
462 None,
463 );
464 lint_buffer.buffer_lint(
465 lint::builtin::UNKNOWN_CRATE_TYPES,
466 ast::CRATE_NODE_ID,
467 span,
468 errors::UnknownCrateTypes {
469 sugg: candidate
470 .map(|cand| errors::UnknownCrateTypesSub { span, snippet: cand }),
471 },
472 );
473 }
474 } else {
475 validate_attr::emit_fatal_malformed_builtin_attribute(
483 &sess.psess,
484 a,
485 sym::crate_type,
486 );
487 }
488 }
489 }
490}
491
492fn multiple_output_types_to_stdout(
493 output_types: &OutputTypes,
494 single_output_file_is_stdout: bool,
495) -> bool {
496 use std::io::IsTerminal;
497 if std::io::stdout().is_terminal() {
498 let named_text_types = output_types
501 .iter()
502 .filter(|(f, o)| f.is_text_output() && *o == &Some(OutFileName::Stdout))
503 .count();
504 let unnamed_text_types =
505 output_types.iter().filter(|(f, o)| f.is_text_output() && o.is_none()).count();
506 named_text_types > 1 || unnamed_text_types > 1 && single_output_file_is_stdout
507 } else {
508 let named_types =
510 output_types.values().filter(|o| *o == &Some(OutFileName::Stdout)).count();
511 let unnamed_types = output_types.values().filter(|o| o.is_none()).count();
512 named_types > 1 || unnamed_types > 1 && single_output_file_is_stdout
513 }
514}
515
516pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames {
517 if multiple_output_types_to_stdout(
518 &sess.opts.output_types,
519 sess.io.output_file == Some(OutFileName::Stdout),
520 ) {
521 sess.dcx().emit_fatal(errors::MultipleOutputTypesToStdout);
522 }
523
524 let crate_name =
525 sess.opts.crate_name.clone().or_else(|| {
526 parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string())
527 });
528
529 match sess.io.output_file {
530 None => {
531 let dirpath = sess.io.output_dir.clone().unwrap_or_default();
535
536 let stem = crate_name.clone().unwrap_or_else(|| sess.io.input.filestem().to_owned());
538
539 OutputFilenames::new(
540 dirpath,
541 crate_name.unwrap_or_else(|| stem.replace('-', "_")),
542 stem,
543 None,
544 sess.io.temps_dir.clone(),
545 sess.opts.cg.extra_filename.clone(),
546 sess.opts.output_types.clone(),
547 )
548 }
549
550 Some(ref out_file) => {
551 let unnamed_output_types =
552 sess.opts.output_types.values().filter(|a| a.is_none()).count();
553 let ofile = if unnamed_output_types > 1 {
554 sess.dcx().emit_warn(errors::MultipleOutputTypesAdaption);
555 None
556 } else {
557 if !sess.opts.cg.extra_filename.is_empty() {
558 sess.dcx().emit_warn(errors::IgnoringExtraFilename);
559 }
560 Some(out_file.clone())
561 };
562 if sess.io.output_dir.is_some() {
563 sess.dcx().emit_warn(errors::IgnoringOutDir);
564 }
565
566 let out_filestem =
567 out_file.filestem().unwrap_or_default().to_str().unwrap().to_string();
568 OutputFilenames::new(
569 out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
570 crate_name.unwrap_or_else(|| out_filestem.replace('-', "_")),
571 out_filestem,
572 ofile,
573 sess.io.temps_dir.clone(),
574 sess.opts.cg.extra_filename.clone(),
575 sess.opts.output_types.clone(),
576 )
577 }
578 }
579}
580
581pub macro version_str() {
583 option_env!("CFG_VERSION")
584}
585
586pub fn rustc_version_str() -> Option<&'static str> {
588 version_str!()
589}