1 use crate::errors;
2 use info;
3 use libloading::Library;
4 use rustc_ast as ast;
5 use rustc_codegen_ssa::traits::CodegenBackend;
6 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7 #[cfg(parallel_compiler)]
8 use rustc_data_structures::sync;
9 use rustc_errors::registry::Registry;
10 use rustc_parse::validate_attr;
11 use rustc_session as session;
12 use rustc_session::config::CheckCfg;
13 use rustc_session::config::{self, CrateType};
14 use rustc_session::config::{OutFileName, OutputFilenames, OutputTypes};
15 use rustc_session::filesearch::sysroot_candidates;
16 use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
17 use rustc_session::parse::CrateConfig;
18 use rustc_session::{filesearch, output, Session};
19 use rustc_span::edit_distance::find_best_match_for_name;
20 use rustc_span::edition::Edition;
21 use rustc_span::source_map::FileLoader;
22 use rustc_span::symbol::{sym, Symbol};
23 use session::{CompilerIO, EarlyErrorHandler};
24 use std::env;
25 use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
26 use std::mem;
27 use std::path::{Path, PathBuf};
28 use std::sync::atomic::{AtomicBool, Ordering};
29 use std::sync::OnceLock;
30 use std::thread;
31
32 /// Function pointer type that constructs a new CodegenBackend.
33 pub type MakeBackendFn = fn() -> Box<dyn CodegenBackend>;
34
35 /// Adds `target_feature = "..."` cfgs for a variety of platform
36 /// specific features (SSE, NEON etc.).
37 ///
38 /// This is performed by checking whether a set of permitted features
39 /// is available on the target machine, by querying the codegen backend.
add_configuration( cfg: &mut CrateConfig, sess: &mut Session, codegen_backend: &dyn CodegenBackend, )40 pub fn add_configuration(
41 cfg: &mut CrateConfig,
42 sess: &mut Session,
43 codegen_backend: &dyn CodegenBackend,
44 ) {
45 let tf = sym::target_feature;
46
47 let unstable_target_features = codegen_backend.target_features(sess, true);
48 sess.unstable_target_features.extend(unstable_target_features.iter().cloned());
49
50 let target_features = codegen_backend.target_features(sess, false);
51 sess.target_features.extend(target_features.iter().cloned());
52
53 cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat))));
54
55 if sess.crt_static(None) {
56 cfg.insert((tf, Some(sym::crt_dash_static)));
57 }
58 }
59
create_session( handler: &EarlyErrorHandler, sopts: config::Options, cfg: FxHashSet<(String, Option<String>)>, check_cfg: CheckCfg, locale_resources: &'static [&'static str], file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>, io: CompilerIO, lint_caps: FxHashMap<lint::LintId, lint::Level>, make_codegen_backend: Option< Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>, >, descriptions: Registry, ) -> (Session, Box<dyn CodegenBackend>)60 pub fn create_session(
61 handler: &EarlyErrorHandler,
62 sopts: config::Options,
63 cfg: FxHashSet<(String, Option<String>)>,
64 check_cfg: CheckCfg,
65 locale_resources: &'static [&'static str],
66 file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
67 io: CompilerIO,
68 lint_caps: FxHashMap<lint::LintId, lint::Level>,
69 make_codegen_backend: Option<
70 Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
71 >,
72 descriptions: Registry,
73 ) -> (Session, Box<dyn CodegenBackend>) {
74 let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend {
75 make_codegen_backend(&sopts)
76 } else {
77 get_codegen_backend(
78 handler,
79 &sopts.maybe_sysroot,
80 sopts.unstable_opts.codegen_backend.as_deref(),
81 )
82 };
83
84 // target_override is documented to be called before init(), so this is okay
85 let target_override = codegen_backend.target_override(&sopts);
86
87 let bundle = match rustc_errors::fluent_bundle(
88 sopts.maybe_sysroot.clone(),
89 sysroot_candidates().to_vec(),
90 sopts.unstable_opts.translate_lang.clone(),
91 sopts.unstable_opts.translate_additional_ftl.as_deref(),
92 sopts.unstable_opts.translate_directionality_markers,
93 ) {
94 Ok(bundle) => bundle,
95 Err(e) => {
96 handler.early_error(format!("failed to load fluent bundle: {e}"));
97 }
98 };
99
100 let mut locale_resources = Vec::from(locale_resources);
101 locale_resources.push(codegen_backend.locale_resource());
102
103 let mut sess = session::build_session(
104 handler,
105 sopts,
106 io,
107 bundle,
108 descriptions,
109 locale_resources,
110 lint_caps,
111 file_loader,
112 target_override,
113 rustc_version_str().unwrap_or("unknown"),
114 );
115
116 codegen_backend.init(&sess);
117
118 let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg));
119 add_configuration(&mut cfg, &mut sess, &*codegen_backend);
120
121 let mut check_cfg = config::to_crate_check_config(check_cfg);
122 check_cfg.fill_well_known(&sess.target);
123
124 sess.parse_sess.config = cfg;
125 sess.parse_sess.check_config = check_cfg;
126
127 (sess, codegen_backend)
128 }
129
130 const STACK_SIZE: usize = 8 * 1024 * 1024;
131
get_stack_size() -> Option<usize>132 fn get_stack_size() -> Option<usize> {
133 // FIXME: Hacks on hacks. If the env is trying to override the stack size
134 // then *don't* set it explicitly.
135 env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE)
136 }
137
138 #[cfg(not(parallel_compiler))]
run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( edition: Edition, _threads: usize, f: F, ) -> R139 pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
140 edition: Edition,
141 _threads: usize,
142 f: F,
143 ) -> R {
144 // The "thread pool" is a single spawned thread in the non-parallel
145 // compiler. We run on a spawned thread instead of the main thread (a) to
146 // provide control over the stack size, and (b) to increase similarity with
147 // the parallel compiler, in particular to ensure there is no accidental
148 // sharing of data between the main thread and the compilation thread
149 // (which might cause problems for the parallel compiler).
150 let mut builder = thread::Builder::new().name("rustc".to_string());
151 if let Some(size) = get_stack_size() {
152 builder = builder.stack_size(size);
153 }
154
155 // We build the session globals and run `f` on the spawned thread, because
156 // `SessionGlobals` does not impl `Send` in the non-parallel compiler.
157 thread::scope(|s| {
158 // `unwrap` is ok here because `spawn_scoped` only panics if the thread
159 // name contains null bytes.
160 let r = builder
161 .spawn_scoped(s, move || rustc_span::create_session_globals_then(edition, f))
162 .unwrap()
163 .join();
164
165 match r {
166 Ok(v) => v,
167 Err(e) => std::panic::resume_unwind(e),
168 }
169 })
170 }
171
172 #[cfg(parallel_compiler)]
run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( edition: Edition, threads: usize, f: F, ) -> R173 pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
174 edition: Edition,
175 threads: usize,
176 f: F,
177 ) -> R {
178 use rustc_data_structures::jobserver;
179 use rustc_middle::ty::tls;
180 use rustc_query_impl::QueryCtxt;
181 use rustc_query_system::query::{deadlock, QueryContext};
182
183 let registry = sync::Registry::new(threads);
184 let mut builder = rayon::ThreadPoolBuilder::new()
185 .thread_name(|_| "rustc".to_string())
186 .acquire_thread_handler(jobserver::acquire_thread)
187 .release_thread_handler(jobserver::release_thread)
188 .num_threads(threads)
189 .deadlock_handler(|| {
190 // On deadlock, creates a new thread and forwards information in thread
191 // locals to it. The new thread runs the deadlock handler.
192 let query_map = tls::with(|tcx| {
193 QueryCtxt::new(tcx)
194 .try_collect_active_jobs()
195 .expect("active jobs shouldn't be locked in deadlock handler")
196 });
197 let registry = rayon_core::Registry::current();
198 thread::spawn(move || deadlock(query_map, ®istry));
199 });
200 if let Some(size) = get_stack_size() {
201 builder = builder.stack_size(size);
202 }
203
204 // We create the session globals on the main thread, then create the thread
205 // pool. Upon creation, each worker thread created gets a copy of the
206 // session globals in TLS. This is possible because `SessionGlobals` impls
207 // `Send` in the parallel compiler.
208 rustc_span::create_session_globals_then(edition, || {
209 rustc_span::with_session_globals(|session_globals| {
210 builder
211 .build_scoped(
212 // Initialize each new worker thread when created.
213 move |thread: rayon::ThreadBuilder| {
214 // Register the thread for use with the `WorkerLocal` type.
215 registry.register();
216
217 rustc_span::set_session_globals_then(session_globals, || thread.run())
218 },
219 // Run `f` on the first thread in the thread pool.
220 move |pool: &rayon::ThreadPool| pool.install(f),
221 )
222 .unwrap()
223 })
224 })
225 }
226
load_backend_from_dylib(handler: &EarlyErrorHandler, path: &Path) -> MakeBackendFn227 fn load_backend_from_dylib(handler: &EarlyErrorHandler, path: &Path) -> MakeBackendFn {
228 let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
229 let err = format!("couldn't load codegen backend {path:?}: {err}");
230 handler.early_error(err);
231 });
232
233 let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
234 .unwrap_or_else(|e| {
235 let err = format!("couldn't load codegen backend: {e}");
236 handler.early_error(err);
237 });
238
239 // Intentionally leak the dynamic library. We can't ever unload it
240 // since the library can make things that will live arbitrarily long.
241 let backend_sym = unsafe { backend_sym.into_raw() };
242 mem::forget(lib);
243
244 *backend_sym
245 }
246
247 /// Get the codegen backend based on the name and specified sysroot.
248 ///
249 /// A name of `None` indicates that the default backend should be used.
get_codegen_backend( handler: &EarlyErrorHandler, maybe_sysroot: &Option<PathBuf>, backend_name: Option<&str>, ) -> Box<dyn CodegenBackend>250 pub fn get_codegen_backend(
251 handler: &EarlyErrorHandler,
252 maybe_sysroot: &Option<PathBuf>,
253 backend_name: Option<&str>,
254 ) -> Box<dyn CodegenBackend> {
255 static LOAD: OnceLock<unsafe fn() -> Box<dyn CodegenBackend>> = OnceLock::new();
256
257 let load = LOAD.get_or_init(|| {
258 let default_codegen_backend = option_env!("CFG_DEFAULT_CODEGEN_BACKEND").unwrap_or("llvm");
259
260 match backend_name.unwrap_or(default_codegen_backend) {
261 filename if filename.contains('.') => {
262 load_backend_from_dylib(handler, filename.as_ref())
263 }
264 #[cfg(feature = "llvm")]
265 "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new,
266 backend_name => get_codegen_sysroot(handler, maybe_sysroot, backend_name),
267 }
268 });
269
270 // SAFETY: In case of a builtin codegen backend this is safe. In case of an external codegen
271 // backend we hope that the backend links against the same rustc_driver version. If this is not
272 // the case, we get UB.
273 unsafe { load() }
274 }
275
276 // This is used for rustdoc, but it uses similar machinery to codegen backend
277 // loading, so we leave the code here. It is potentially useful for other tools
278 // that want to invoke the rustc binary while linking to rustc as well.
rustc_path<'a>() -> Option<&'a Path>279 pub fn rustc_path<'a>() -> Option<&'a Path> {
280 static RUSTC_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
281
282 const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR");
283
284 RUSTC_PATH.get_or_init(|| get_rustc_path_inner(BIN_PATH)).as_deref()
285 }
286
get_rustc_path_inner(bin_path: &str) -> Option<PathBuf>287 fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
288 sysroot_candidates().iter().find_map(|sysroot| {
289 let candidate = sysroot.join(bin_path).join(if cfg!(target_os = "windows") {
290 "rustc.exe"
291 } else {
292 "rustc"
293 });
294 candidate.exists().then_some(candidate)
295 })
296 }
297
get_codegen_sysroot( handler: &EarlyErrorHandler, maybe_sysroot: &Option<PathBuf>, backend_name: &str, ) -> MakeBackendFn298 fn get_codegen_sysroot(
299 handler: &EarlyErrorHandler,
300 maybe_sysroot: &Option<PathBuf>,
301 backend_name: &str,
302 ) -> MakeBackendFn {
303 // For now we only allow this function to be called once as it'll dlopen a
304 // few things, which seems to work best if we only do that once. In
305 // general this assertion never trips due to the once guard in `get_codegen_backend`,
306 // but there's a few manual calls to this function in this file we protect
307 // against.
308 static LOADED: AtomicBool = AtomicBool::new(false);
309 assert!(
310 !LOADED.fetch_or(true, Ordering::SeqCst),
311 "cannot load the default codegen backend twice"
312 );
313
314 let target = session::config::host_triple();
315 let sysroot_candidates = sysroot_candidates();
316
317 let sysroot = maybe_sysroot
318 .iter()
319 .chain(sysroot_candidates.iter())
320 .map(|sysroot| {
321 filesearch::make_target_lib_path(sysroot, target).with_file_name("codegen-backends")
322 })
323 .find(|f| {
324 info!("codegen backend candidate: {}", f.display());
325 f.exists()
326 });
327 let sysroot = sysroot.unwrap_or_else(|| {
328 let candidates = sysroot_candidates
329 .iter()
330 .map(|p| p.display().to_string())
331 .collect::<Vec<_>>()
332 .join("\n* ");
333 let err = format!(
334 "failed to find a `codegen-backends` folder \
335 in the sysroot candidates:\n* {candidates}"
336 );
337 handler.early_error(err);
338 });
339 info!("probing {} for a codegen backend", sysroot.display());
340
341 let d = sysroot.read_dir().unwrap_or_else(|e| {
342 let err = format!(
343 "failed to load default codegen backend, couldn't \
344 read `{}`: {}",
345 sysroot.display(),
346 e
347 );
348 handler.early_error(err);
349 });
350
351 let mut file: Option<PathBuf> = None;
352
353 let expected_names = &[
354 format!("rustc_codegen_{}-{}", backend_name, env!("CFG_RELEASE")),
355 format!("rustc_codegen_{backend_name}"),
356 ];
357 for entry in d.filter_map(|e| e.ok()) {
358 let path = entry.path();
359 let Some(filename) = path.file_name().and_then(|s| s.to_str()) else { continue };
360 if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) {
361 continue;
362 }
363 let name = &filename[DLL_PREFIX.len()..filename.len() - DLL_SUFFIX.len()];
364 if !expected_names.iter().any(|expected| expected == name) {
365 continue;
366 }
367 if let Some(ref prev) = file {
368 let err = format!(
369 "duplicate codegen backends found\n\
370 first: {}\n\
371 second: {}\n\
372 ",
373 prev.display(),
374 path.display()
375 );
376 handler.early_error(err);
377 }
378 file = Some(path.clone());
379 }
380
381 match file {
382 Some(ref s) => load_backend_from_dylib(handler, s),
383 None => {
384 let err = format!("unsupported builtin codegen backend `{backend_name}`");
385 handler.early_error(err);
386 }
387 }
388 }
389
check_attr_crate_type( sess: &Session, attrs: &[ast::Attribute], lint_buffer: &mut LintBuffer, )390 pub(crate) fn check_attr_crate_type(
391 sess: &Session,
392 attrs: &[ast::Attribute],
393 lint_buffer: &mut LintBuffer,
394 ) {
395 // Unconditionally collect crate types from attributes to make them used
396 for a in attrs.iter() {
397 if a.has_name(sym::crate_type) {
398 if let Some(n) = a.value_str() {
399 if categorize_crate_type(n).is_some() {
400 return;
401 }
402
403 if let ast::MetaItemKind::NameValue(spanned) = a.meta_kind().unwrap() {
404 let span = spanned.span;
405 let lev_candidate = find_best_match_for_name(
406 &CRATE_TYPES.iter().map(|(k, _)| *k).collect::<Vec<_>>(),
407 n,
408 None,
409 );
410 if let Some(candidate) = lev_candidate {
411 lint_buffer.buffer_lint_with_diagnostic(
412 lint::builtin::UNKNOWN_CRATE_TYPES,
413 ast::CRATE_NODE_ID,
414 span,
415 "invalid `crate_type` value",
416 BuiltinLintDiagnostics::UnknownCrateTypes(
417 span,
418 "did you mean".to_string(),
419 format!("\"{candidate}\""),
420 ),
421 );
422 } else {
423 lint_buffer.buffer_lint(
424 lint::builtin::UNKNOWN_CRATE_TYPES,
425 ast::CRATE_NODE_ID,
426 span,
427 "invalid `crate_type` value",
428 );
429 }
430 }
431 } else {
432 // This is here mainly to check for using a macro, such as
433 // #![crate_type = foo!()]. That is not supported since the
434 // crate type needs to be known very early in compilation long
435 // before expansion. Otherwise, validation would normally be
436 // caught in AstValidator (via `check_builtin_attribute`), but
437 // by the time that runs the macro is expanded, and it doesn't
438 // give an error.
439 validate_attr::emit_fatal_malformed_builtin_attribute(
440 &sess.parse_sess,
441 a,
442 sym::crate_type,
443 );
444 }
445 }
446 }
447 }
448
449 const CRATE_TYPES: &[(Symbol, CrateType)] = &[
450 (sym::rlib, CrateType::Rlib),
451 (sym::dylib, CrateType::Dylib),
452 (sym::cdylib, CrateType::Cdylib),
453 (sym::lib, config::default_lib_output()),
454 (sym::staticlib, CrateType::Staticlib),
455 (sym::proc_dash_macro, CrateType::ProcMacro),
456 (sym::bin, CrateType::Executable),
457 ];
458
categorize_crate_type(s: Symbol) -> Option<CrateType>459 fn categorize_crate_type(s: Symbol) -> Option<CrateType> {
460 Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1)
461 }
462
collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<CrateType>463 pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<CrateType> {
464 // Unconditionally collect crate types from attributes to make them used
465 let attr_types: Vec<CrateType> = attrs
466 .iter()
467 .filter_map(|a| {
468 if a.has_name(sym::crate_type) {
469 match a.value_str() {
470 Some(s) => categorize_crate_type(s),
471 _ => None,
472 }
473 } else {
474 None
475 }
476 })
477 .collect();
478
479 // If we're generating a test executable, then ignore all other output
480 // styles at all other locations
481 if session.opts.test {
482 return vec![CrateType::Executable];
483 }
484
485 // Only check command line flags if present. If no types are specified by
486 // command line, then reuse the empty `base` Vec to hold the types that
487 // will be found in crate attributes.
488 // JUSTIFICATION: before wrapper fn is available
489 #[allow(rustc::bad_opt_access)]
490 let mut base = session.opts.crate_types.clone();
491 if base.is_empty() {
492 base.extend(attr_types);
493 if base.is_empty() {
494 base.push(output::default_output_for_target(session));
495 } else {
496 base.sort();
497 base.dedup();
498 }
499 }
500
501 base.retain(|crate_type| {
502 if output::invalid_output_for_target(session, *crate_type) {
503 session.emit_warning(errors::UnsupportedCrateTypeForTarget {
504 crate_type: *crate_type,
505 target_triple: &session.opts.target_triple,
506 });
507 false
508 } else {
509 true
510 }
511 });
512
513 base
514 }
515
multiple_output_types_to_stdout( output_types: &OutputTypes, single_output_file_is_stdout: bool, ) -> bool516 fn multiple_output_types_to_stdout(
517 output_types: &OutputTypes,
518 single_output_file_is_stdout: bool,
519 ) -> bool {
520 if atty::is(atty::Stream::Stdout) {
521 // If stdout is a tty, check if multiple text output types are
522 // specified by `--emit foo=- --emit bar=-` or `-o - --emit foo,bar`
523 let named_text_types = output_types
524 .iter()
525 .filter(|(f, o)| f.is_text_output() && *o == &Some(OutFileName::Stdout))
526 .count();
527 let unnamed_text_types =
528 output_types.iter().filter(|(f, o)| f.is_text_output() && o.is_none()).count();
529 named_text_types > 1 || unnamed_text_types > 1 && single_output_file_is_stdout
530 } else {
531 // Otherwise, all the output types should be checked
532 let named_types =
533 output_types.values().filter(|o| *o == &Some(OutFileName::Stdout)).count();
534 let unnamed_types = output_types.values().filter(|o| o.is_none()).count();
535 named_types > 1 || unnamed_types > 1 && single_output_file_is_stdout
536 }
537 }
538
build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames539 pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames {
540 if multiple_output_types_to_stdout(
541 &sess.opts.output_types,
542 sess.io.output_file == Some(OutFileName::Stdout),
543 ) {
544 sess.emit_fatal(errors::MultipleOutputTypesToStdout);
545 }
546 match sess.io.output_file {
547 None => {
548 // "-" as input file will cause the parser to read from stdin so we
549 // have to make up a name
550 // We want to toss everything after the final '.'
551 let dirpath = sess.io.output_dir.clone().unwrap_or_default();
552
553 // If a crate name is present, we use it as the link name
554 let stem = sess
555 .opts
556 .crate_name
557 .clone()
558 .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string()))
559 .unwrap_or_else(|| sess.io.input.filestem().to_owned());
560
561 OutputFilenames::new(
562 dirpath,
563 stem,
564 None,
565 sess.io.temps_dir.clone(),
566 sess.opts.cg.extra_filename.clone(),
567 sess.opts.output_types.clone(),
568 )
569 }
570
571 Some(ref out_file) => {
572 let unnamed_output_types =
573 sess.opts.output_types.values().filter(|a| a.is_none()).count();
574 let ofile = if unnamed_output_types > 1 {
575 sess.emit_warning(errors::MultipleOutputTypesAdaption);
576 None
577 } else {
578 if !sess.opts.cg.extra_filename.is_empty() {
579 sess.emit_warning(errors::IgnoringExtraFilename);
580 }
581 Some(out_file.clone())
582 };
583 if sess.io.output_dir != None {
584 sess.emit_warning(errors::IgnoringOutDir);
585 }
586
587 OutputFilenames::new(
588 out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
589 out_file.filestem().unwrap_or_default().to_str().unwrap().to_string(),
590 ofile,
591 sess.io.temps_dir.clone(),
592 sess.opts.cg.extra_filename.clone(),
593 sess.opts.output_types.clone(),
594 )
595 }
596 }
597 }
598
599 /// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)" when invoked by an in-tree tool.
600 pub macro version_str() {
601 option_env!("CFG_VERSION")
602 }
603
604 /// Returns the version string for `rustc` itself (which may be different from a tool version).
rustc_version_str() -> Option<&'static str>605 pub fn rustc_version_str() -> Option<&'static str> {
606 version_str!()
607 }
608