1use std::collections::VecDeque;
2use std::ffi::{CStr, CString};
3use std::fmt::Write;
4use std::path::Path;
5use std::sync::Once;
6use std::{ptr, slice, str};
7
8use libc::c_int;
9use rustc_abi::Endian;
10use rustc_codegen_ssa::back::versioned_llvm_target;
11use rustc_codegen_ssa::base::wants_wasm_eh;
12use rustc_codegen_ssa::target_features::internal_target_features;
13use rustc_codegen_ssa::{TargetConfig, target_features};
14use rustc_data_structures::fx::FxHashSet;
15use rustc_data_structures::small_c_str::SmallCStr;
16use rustc_fs_util::path_to_c_string;
17use rustc_session::config::{NATIVE_CPU, PrintKind, PrintRequest};
18use rustc_session::{EarlySession, Session};
19use rustc_span::{bug, sym};
20use rustc_target::spec::{
21 Arch, CfgAbi, Env, MergeFunctions, Os, PanicStrategy, SmallDataThresholdSupport, Target,
22};
23use smallvec::{SmallVec, smallvec};
24
25use crate::back::owned_mc_subtarget_info::OwnedMCSubtargetInfo;
26use crate::back::write::{create_informational_target_machine, llvm_err};
27use crate::{diagnostics, llvm};
28
29static INIT: Once = Once::new();
30
31pub(crate) fn init(sess: &EarlySession) {
32 unsafe {
33 if !llvm::LLVMIsMultithreaded().is_true() {
35 ::rustc_span::macros::bug_impl(None,
format_args!("LLVM compiled without support for threads"),
Location::caller());bug!("LLVM compiled without support for threads");
36 }
37 INIT.call_once(|| {
38 configure_llvm(sess);
39 });
40 }
41}
42
43fn require_inited() {
44 if !INIT.is_completed() {
45 ::rustc_span::macros::bug_impl(None, format_args!("LLVM is not initialized"),
Location::caller());bug!("LLVM is not initialized");
46 }
47}
48
49unsafe fn configure_llvm(sess: &EarlySession) {
50 let n_args = sess.opts.cg.llvm_args.len() + sess.target.llvm_args.len();
51 let mut llvm_c_strs = Vec::with_capacity(n_args + 1);
52 let mut llvm_args = Vec::with_capacity(n_args + 1);
53
54 unsafe {
56 let (llvm_major, llvm_minor, llvm_patch) = get_version();
57 let expected_version = llvm::LLVMRustVersionMajor();
58 if llvm_major != expected_version {
59 sess.dcx().emit_fatal(diagnostics::LlvmVersionMismatch {
60 expected_version,
61 llvm_major,
62 llvm_minor,
63 llvm_patch,
64 dll_loc: &match rustc_session::filesearch::dll_path(llvm::LLVMGetVersion as *mut _)
65 {
66 Ok(path) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" at {0}", path.display()))
})format!(" at {}", path.display()),
67 Err(_) => String::new(),
68 },
69 })
70 }
71 }
72
73 unsafe {
74 llvm::LLVMRustInstallErrorHandlers();
75 }
76 if std::env::var_os("CI").is_some() {
80 unsafe {
81 llvm::LLVMRustDisableSystemDialogsOnCrash();
82 }
83 }
84
85 fn llvm_arg_to_arg_name(full_arg: &str) -> &str {
86 full_arg.trim().split(|c: char| c == '=' || c.is_whitespace()).next().unwrap_or("")
87 }
88
89 let cg_opts = sess.opts.cg.llvm_args.iter().map(AsRef::as_ref);
90 let tg_opts = sess.target.llvm_args.iter().map(AsRef::as_ref);
91 let sess_args = tg_opts.chain(cg_opts);
95
96 let user_specified_args: FxHashSet<_> =
97 sess_args.clone().map(|s| llvm_arg_to_arg_name(s)).filter(|s| !s.is_empty()).collect();
98
99 {
100 let mut add = |arg: &str, force: bool| {
103 if force || !user_specified_args.contains(llvm_arg_to_arg_name(arg)) {
104 let s = CString::new(arg).unwrap();
105 llvm_args.push(s.as_ptr());
106 llvm_c_strs.push(s);
107 }
108 };
109 add("rustc -Cllvm-args=\"...\" with", true);
111 if sess.opts.unstable_opts.time_llvm_passes {
112 add("-time-passes", false);
113 }
114 if sess.opts.unstable_opts.print_llvm_passes {
115 add("-debug-pass=Structure", false);
116 }
117 if sess.target.generate_arange_section
118 && !sess.opts.unstable_opts.no_generate_arange_section
119 {
120 add("-generate-arange-section", false);
121 }
122
123 match sess.merge_functions() {
124 MergeFunctions::Disabled | MergeFunctions::Trampolines => {}
125 MergeFunctions::Aliases => {
126 add("-mergefunc-use-aliases", false);
127 }
128 }
129
130 if get_version() < (24, 0, 0) && wants_wasm_eh(&sess.target) {
131 add("-wasm-enable-eh", false);
132 }
133
134 add("-preserve-alignment-assumptions-during-inlining=false", false);
137
138 add("-import-cold-multiplier=0.1", false);
140
141 if sess.print_llvm_stats() || sess.print_llvm_stats_json().is_some() {
142 add("-stats", false);
143 }
144
145 for arg in sess_args {
146 add(&(*arg), true);
147 }
148
149 match (
150 sess.opts.unstable_opts.small_data_threshold,
151 sess.target.small_data_threshold_support(),
152 ) {
153 (Some(threshold), SmallDataThresholdSupport::LlvmArg(arg)) => {
156 add(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("--{0}={1}", arg, threshold))
})format!("--{arg}={threshold}"), false)
157 }
158 _ => (),
159 };
160 }
161
162 if sess.opts.unstable_opts.llvm_time_trace {
163 unsafe { llvm::LLVMRustTimeTraceProfilerInitialize() };
164 }
165
166 rustc_llvm::initialize_available_targets();
167
168 unsafe { llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr()) };
169}
170
171pub(crate) fn time_trace_profiler_finish(file_name: &Path) {
172 unsafe {
173 let file_name = path_to_c_string(file_name);
174 llvm::LLVMRustTimeTraceProfilerFinish(file_name.as_ptr());
175 }
176}
177
178enum TargetFeatureFoldStrength<'a> {
179 EnableOnly(&'a str),
182 Both(&'a str),
184}
185
186impl<'a> TargetFeatureFoldStrength<'a> {
187 fn as_str(&self) -> &'a str {
188 match self {
189 TargetFeatureFoldStrength::EnableOnly(feat) => feat,
190 TargetFeatureFoldStrength::Both(feat) => feat,
191 }
192 }
193}
194
195pub(crate) struct LLVMFeature<'a> {
196 llvm_feature_name: &'a str,
197 dependencies: SmallVec<[TargetFeatureFoldStrength<'a>; 1]>,
198}
199
200impl<'a> LLVMFeature<'a> {
201 fn new(llvm_feature_name: &'a str) -> Self {
202 Self { llvm_feature_name, dependencies: SmallVec::new() }
203 }
204
205 fn with_dependencies(
206 llvm_feature_name: &'a str,
207 dependencies: SmallVec<[TargetFeatureFoldStrength<'a>; 1]>,
208 ) -> Self {
209 Self { llvm_feature_name, dependencies }
210 }
211}
212
213impl<'a> IntoIterator for LLVMFeature<'a> {
214 type Item = &'a str;
215 type IntoIter = impl Iterator<Item = &'a str>;
216
217 fn into_iter(self) -> Self::IntoIter {
218 let dependencies = self.dependencies.into_iter().map(|feat| feat.as_str());
219 std::iter::once(self.llvm_feature_name).chain(dependencies)
220 }
221}
222
223pub(crate) fn to_llvm_features<'a>(target: &Target, s: &'a str) -> Option<LLVMFeature<'a>> {
240 let (major, _, _) = get_version();
241 match target.arch {
242 Arch::AArch64 | Arch::Arm64EC => {
243 match s {
244 "rcpc2" => Some(LLVMFeature::new("rcpc-immo")),
245 "dpb" => Some(LLVMFeature::new("ccpp")),
246 "dpb2" => Some(LLVMFeature::new("ccdp")),
247 "frintts" => Some(LLVMFeature::new("fptoint")),
248 "fcma" => Some(LLVMFeature::new("complxnum")),
249 "pmuv3" => Some(LLVMFeature::new("perfmon")),
250 "paca" => Some(LLVMFeature::new("pauth")),
251 "pacg" => Some(LLVMFeature::new("pauth")),
252 "flagm2" => Some(LLVMFeature::new("altnzcv")),
253 "neon" => Some(LLVMFeature::with_dependencies(
255 "neon",
256 {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(TargetFeatureFoldStrength::Both("fp-armv8"));
vec
} else {
::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[TargetFeatureFoldStrength::Both("fp-armv8")])))
}
}smallvec![TargetFeatureFoldStrength::Both("fp-armv8")],
257 )),
258 "fhm" => Some(LLVMFeature::new("fp16fml")),
261 "fp16" => Some(LLVMFeature::new("fullfp16")),
262 "fpmr" => None, "tme" => None,
266 s => Some(LLVMFeature::new(s)),
267 }
268 }
269 Arch::Arm => match s {
270 "fp16" => Some(LLVMFeature::new("fullfp16")),
271 s => Some(LLVMFeature::new(s)),
272 },
273 Arch::Nvptx64 => match s {
274 "sm_101" if major >= 24 => Some(LLVMFeature::new("sm_110")),
275 "sm_101a" if major >= 24 => Some(LLVMFeature::new("sm_110a")),
276 "sm_101f" if major >= 24 => Some(LLVMFeature::new("sm_110f")),
277 s => Some(LLVMFeature::new(s)),
278 },
279 Arch::PowerPC | Arch::PowerPC64 => match s {
281 "power8-crypto" => Some(LLVMFeature::new("crypto")),
282 s => Some(LLVMFeature::new(s)),
283 },
284 Arch::RiscV32 | Arch::RiscV64 => match s {
285 "zkne_or_zknd" => None,
287 s => Some(LLVMFeature::new(s)),
288 },
289 Arch::Sparc | Arch::Sparc64 => match s {
290 "leoncasa" => Some(LLVMFeature::new("hasleoncasa")),
291 s => Some(LLVMFeature::new(s)),
292 },
293 Arch::X86 | Arch::X86_64 => match s {
294 "sse4.2" => Some(LLVMFeature::with_dependencies(
295 "sse4.2",
296 {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(TargetFeatureFoldStrength::EnableOnly("crc32"));
vec
} else {
::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[TargetFeatureFoldStrength::EnableOnly("crc32")])))
}
}smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")],
297 )),
298 "pclmulqdq" => Some(LLVMFeature::new("pclmul")),
299 "rdrand" => Some(LLVMFeature::new("rdrnd")),
300 "bmi1" => Some(LLVMFeature::new("bmi")),
301 "cmpxchg16b" => Some(LLVMFeature::new("cx16")),
302 "lahfsahf" => Some(LLVMFeature::new("sahf")),
303 "apxf" => Some(LLVMFeature::with_dependencies(
304 "egpr",
305 {
let count =
0usize + 1usize + 1usize + 1usize + 1usize + 1usize + 1usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(TargetFeatureFoldStrength::Both("push2pop2"));
vec.push(TargetFeatureFoldStrength::Both("ppx"));
vec.push(TargetFeatureFoldStrength::Both("ndd"));
vec.push(TargetFeatureFoldStrength::Both("ccmp"));
vec.push(TargetFeatureFoldStrength::Both("cf"));
vec.push(TargetFeatureFoldStrength::Both("nf"));
vec.push(TargetFeatureFoldStrength::Both("zu"));
vec
} else {
::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[TargetFeatureFoldStrength::Both("push2pop2"),
TargetFeatureFoldStrength::Both("ppx"),
TargetFeatureFoldStrength::Both("ndd"),
TargetFeatureFoldStrength::Both("ccmp"),
TargetFeatureFoldStrength::Both("cf"),
TargetFeatureFoldStrength::Both("nf"),
TargetFeatureFoldStrength::Both("zu")])))
}
}smallvec![
306 TargetFeatureFoldStrength::Both("push2pop2"),
307 TargetFeatureFoldStrength::Both("ppx"),
308 TargetFeatureFoldStrength::Both("ndd"),
309 TargetFeatureFoldStrength::Both("ccmp"),
310 TargetFeatureFoldStrength::Both("cf"),
311 TargetFeatureFoldStrength::Both("nf"),
312 TargetFeatureFoldStrength::Both("zu"),
313 ],
314 )),
315 s => Some(LLVMFeature::new(s)),
316 },
317 _ => Some(LLVMFeature::new(s)),
318 }
319}
320
321pub(crate) fn target_config(sess: &EarlySession) -> TargetConfig {
326 require_inited();
327 let target_features = global_llvm_features(sess, true);
328
329 let triple = SmallCStr::new(&versioned_llvm_target(sess));
330 let cpu = SmallCStr::new(target_cpu(sess));
331 let features = CString::new(target_features.join(",")).unwrap();
332 let mc_subtarget_info = OwnedMCSubtargetInfo::new(&triple, &cpu, &features)
333 .unwrap_or_else(|err| llvm_err(sess.dcx(), err));
334
335 let internal_target_features = internal_target_features(
336 sess,
337 |feature| {
338 to_llvm_features(&sess.target, feature)
339 .map(|f| SmallVec::<[&str; 2]>::from_iter(f.into_iter()))
340 .unwrap_or_default()
341 },
342 |feature| {
343 if let Some(feat) = to_llvm_features(&sess.target, feature)
348 && mc_subtarget_info.has_features(feat)
349 {
350 true
351 } else {
352 false
353 }
354 },
355 );
356
357 let mut cfg = TargetConfig {
358 internal_target_features,
359 has_reliable_f16: true,
360 has_reliable_f16_math: true,
361 has_reliable_f16b: true,
362 has_reliable_f128: true,
363 has_reliable_f128_math: true,
364 };
365
366 update_target_reliable_float_cfg(&sess.target, &mut cfg);
367 cfg
368}
369
370fn update_target_reliable_float_cfg(target: &Target, cfg: &mut TargetConfig) {
372 let target_arch = &target.arch;
373 let target_os = &target.options.os;
374 let target_endian = &target.options.endian;
375 let target_env = &target.options.env;
376 let target_abi = &target.options.cfg_abi;
377 let target_pointer_width = target.pointer_width;
378 let version = get_version();
379 let (major, _, _) = version;
380
381 cfg.has_reliable_f16 = match (target_arch, target_os) {
382 (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != CfgAbi::Llvm => {
385 false
386 }
387 _ => true,
391 };
392
393 cfg.has_reliable_f16b = match (target_arch, target_os) {
399 (Arch::Arm64EC, _) => major >= 23,
402 (Arch::AArch64, _) => true,
403 (Arch::X86_64 | Arch::RiscV64 | Arch::LoongArch64, _) => false,
408 _ => false,
409 };
410
411 cfg.has_reliable_f128 = match (target_arch, target_os) {
412 (Arch::AmdGpu, _) => false,
414 (Arch::Arm64EC, _) if major < 23 => false, (Arch::Nvptx64, _) => false,
418 (Arch::PowerPC, _) => false,
422 (Arch::PowerPC64, Os::Aix) => false,
424 (Arch::PowerPC64, _) => cfg.internal_target_features.contains(&sym::vsx),
426 (Arch::X86_64, Os::Windows)
428 if *target_env == Env::Gnu && *target_abi != CfgAbi::Llvm && major < 23 =>
429 {
430 false
431 }
432 _ => true,
436 };
437
438 cfg.has_reliable_f16_math = cfg.has_reliable_f16;
441
442 cfg.has_reliable_f128_math = match (target_arch, target_os) {
443 _ if *target_env == Env::Musl => false,
456 (Arch::X86_64, _) => false,
457 (Arch::PowerPC | Arch::PowerPC64, _) if *target_endian == Endian::Big => false,
458 (_, Os::Linux) if target_pointer_width == 64 => true,
459 _ => false,
460 } && cfg.has_reliable_f128;
461}
462
463pub(crate) fn print_version() {
464 let (major, minor, patch) = get_version();
465 {
::std::io::_print(format_args!("LLVM version: {0}.{1}.{2}\n", major,
minor, patch));
};println!("LLVM version: {major}.{minor}.{patch}");
466}
467
468pub(crate) fn get_version() -> (u32, u32, u32) {
470 let mut llvm_major = 0;
471 let mut llvm_minor = 0;
472 let mut llvm_patch = 0;
473 llvm::LLVMGetVersion(&mut llvm_major, &mut llvm_minor, &mut llvm_patch);
474 (llvm_major, llvm_minor, llvm_patch)
475}
476
477pub(crate) fn print_passes() {
478 unsafe {
480 llvm::LLVMRustPrintPasses();
481 }
482}
483
484fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> {
485 let len = unsafe { llvm::LLVMRustGetTargetFeaturesCount(tm) };
486 let mut ret = Vec::with_capacity(len);
487 for i in 0..len {
488 unsafe {
489 let mut feature = ptr::null();
490 let mut desc = ptr::null();
491 llvm::LLVMRustGetTargetFeature(tm, i, &mut feature, &mut desc);
492 if feature.is_null() || desc.is_null() {
493 ::rustc_span::macros::bug_impl(None,
format_args!("LLVM returned a `null` target feature string"),
Location::caller());bug!("LLVM returned a `null` target feature string");
494 }
495 let feature = CStr::from_ptr(feature).to_str().unwrap_or_else(|e| {
496 ::rustc_span::macros::bug_impl(None,
format_args!("LLVM returned a non-utf8 feature string: {0}", e),
Location::caller());bug!("LLVM returned a non-utf8 feature string: {}", e);
497 });
498 let desc = CStr::from_ptr(desc).to_str().unwrap_or_else(|e| {
499 ::rustc_span::macros::bug_impl(None,
format_args!("LLVM returned a non-utf8 feature string: {0}", e),
Location::caller());bug!("LLVM returned a non-utf8 feature string: {}", e);
500 });
501 ret.push((feature, desc));
502 }
503 }
504 ret
505}
506
507pub(crate) fn print(req: &PrintRequest, out: &mut String, sess: &Session) {
508 require_inited();
509 let tm = create_informational_target_machine(sess);
510 match req.kind {
511 PrintKind::TargetCPUs => print_target_cpus(sess, tm.raw(), out),
512 PrintKind::TargetFeatures => print_target_features(sess, tm.raw(), out),
513 _ => ::rustc_span::macros::bug_impl(None,
format_args!("rustc_codegen_llvm can\'t handle print request: {0:?}",
req), Location::caller())bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
514 }
515}
516
517fn print_target_cpus(sess: &Session, tm: &llvm::TargetMachine, out: &mut String) {
518 let cpu_names = llvm::build_string(|s| unsafe {
519 llvm::LLVMRustPrintTargetCPUs(&tm, s);
520 })
521 .unwrap();
522
523 struct Cpu<'a> {
524 cpu_name: &'a str,
525 remark: String,
526 }
527 let target_cpu = handle_native(&sess.target.cpu);
530 let make_remark = |cpu_name| {
531 if cpu_name == target_cpu && !sess.target.need_explicit_cpu {
532 let target = &sess.target.llvm_target;
535 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" - This is the default target CPU for the current build target (currently {0}).",
target))
})format!(
536 " - This is the default target CPU for the current build target (currently {target})."
537 )
538 } else {
539 "".to_owned()
540 }
541 };
542 let mut cpus = cpu_names
543 .lines()
544 .filter(|cpu_name| {
545 !sess.target.unsupported_cpus.contains(&std::borrow::Cow::Borrowed(*cpu_name))
546 })
547 .map(|cpu_name| Cpu { cpu_name, remark: make_remark(cpu_name) })
548 .collect::<VecDeque<_>>();
549
550 if sess.host.arch == sess.target.arch && !sess.target.requires_consistent_cpu {
555 let host = get_host_cpu_name();
556 cpus.push_front(Cpu {
557 cpu_name: NATIVE_CPU,
558 remark: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" - Select the CPU of the current host (currently {0}).",
host))
})format!(" - Select the CPU of the current host (currently {host})."),
559 });
560 }
561
562 let max_name_width = cpus.iter().map(|cpu| cpu.cpu_name.len()).max().unwrap_or(0);
563 out.write_fmt(format_args!("Available CPUs for this target:\n"))writeln!(out, "Available CPUs for this target:").unwrap();
564 for Cpu { cpu_name, remark } in cpus {
565 let width = if remark.is_empty() { 0 } else { max_name_width };
567 out.write_fmt(format_args!(" {0:<1$}{2}\n", cpu_name, width, remark))writeln!(out, " {cpu_name:<width$}{remark}").unwrap();
568 }
569}
570
571fn print_target_features(sess: &Session, tm: &llvm::TargetMachine, out: &mut String) {
572 let mut llvm_target_features = llvm_target_features(tm);
573 let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
574 let mut rustc_target_features = sess
575 .target
576 .rust_target_features()
577 .iter()
578 .filter_map(|(feature, gate, _implied)| {
579 if !gate.in_cfg() {
580 return None;
582 }
583 let llvm_feature = to_llvm_features(&sess.target, *feature)?.llvm_feature_name;
586 let desc =
587 match llvm_target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok() {
588 Some(index) => {
589 known_llvm_target_features.insert(llvm_feature);
590 llvm_target_features[index].1
591 }
592 None => "",
593 };
594
595 Some((*feature, desc))
596 })
597 .collect::<Vec<_>>();
598
599 rustc_target_features.extend_from_slice(&[(
601 "crt-static",
602 "Enables C Run-time Libraries to be statically linked",
603 )]);
604 rustc_target_features.sort();
606
607 llvm_target_features.retain(|(f, _d)| !known_llvm_target_features.contains(f));
608
609 let max_feature_len = llvm_target_features
610 .iter()
611 .chain(rustc_target_features.iter())
612 .map(|(feature, _desc)| feature.len())
613 .max()
614 .unwrap_or(0);
615
616 out.write_fmt(format_args!("Features supported by rustc for this target:\n"))writeln!(out, "Features supported by rustc for this target:").unwrap();
617 for (feature, desc) in &rustc_target_features {
618 out.write_fmt(format_args!(" {0:1$} - {2}.\n", feature, max_feature_len,
desc))writeln!(out, " {feature:max_feature_len$} - {desc}.").unwrap();
619 }
620 out.write_fmt(format_args!("\nCode-generation features supported by LLVM for this target:\n"))writeln!(out, "\nCode-generation features supported by LLVM for this target:").unwrap();
621 for (feature, desc) in &llvm_target_features {
622 out.write_fmt(format_args!(" {0:1$} - {2}.\n", feature, max_feature_len,
desc))writeln!(out, " {feature:max_feature_len$} - {desc}.").unwrap();
623 }
624 if llvm_target_features.is_empty() {
625 out.write_fmt(format_args!(" Target features listing is not supported by this LLVM version.\n"))writeln!(out, " Target features listing is not supported by this LLVM version.")
626 .unwrap();
627 }
628 out.write_fmt(format_args!("\nUse +feature to enable a feature, or -feature to disable it.\n"))writeln!(out, "\nUse +feature to enable a feature, or -feature to disable it.").unwrap();
629 out.write_fmt(format_args!("For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n\n"))writeln!(out, "For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n")
630 .unwrap();
631 out.write_fmt(format_args!("Code-generation features cannot be used in cfg or #[target_feature],\n"))writeln!(out, "Code-generation features cannot be used in cfg or #[target_feature],").unwrap();
632 out.write_fmt(format_args!("and may be renamed or removed in a future version of LLVM or rustc.\n\n"))writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n").unwrap();
633}
634
635fn get_host_cpu_name() -> &'static str {
637 let mut len = 0;
638 let slice: &'static [u8] = unsafe {
641 let ptr = llvm::LLVMRustGetHostCPUName(&mut len);
642 if !!ptr.is_null() {
::core::panicking::panic("assertion failed: !ptr.is_null()")
};assert!(!ptr.is_null());
643 slice::from_raw_parts(ptr, len)
644 };
645 str::from_utf8(slice).expect("host CPU name should be UTF-8")
646}
647
648fn handle_native(cpu_name: &str) -> &str {
651 match cpu_name {
652 NATIVE_CPU => get_host_cpu_name(),
653 _ => cpu_name,
654 }
655}
656
657pub(crate) fn target_cpu(sess: &EarlySession) -> &str {
658 let cpu_name = sess.opts.cg.target_cpu.as_deref().unwrap_or_else(|| &sess.target.cpu);
659 handle_native(cpu_name)
660}
661
662fn llvm_features_by_flags(sess: &EarlySession, features: &mut Vec<String>) {
664 if wants_wasm_eh(&sess.target) && sess.panic_strategy() == PanicStrategy::Unwind {
665 features.push("+exception-handling".into());
666 }
667
668 target_features::retpoline_features_by_flags(sess, features);
669 target_features::sanitizer_features_by_flags(sess, features);
670
671 if sess.opts.unstable_opts.fixed_x18 {
673 if sess.target.arch != Arch::AArch64 {
674 sess.dcx()
675 .emit_fatal(diagnostics::FixedX18InvalidArch { arch: sess.target.arch.desc() });
676 } else {
677 features.push("+reserve-x18".into());
678 }
679 }
680}
681
682pub(crate) fn global_llvm_features(sess: &EarlySession, for_cfg: bool) -> Vec<String> {
689 let mut features = ::alloc::vec::Vec::new()vec![];
708
709 match sess.opts.cg.target_cpu {
711 Some(ref s) if s == NATIVE_CPU => {
712 let features_string = unsafe {
718 let ptr = llvm::LLVMGetHostCPUFeatures();
719 let features_string = if !ptr.is_null() {
720 CStr::from_ptr(ptr)
721 .to_str()
722 .unwrap_or_else(|e| {
723 ::rustc_span::macros::bug_impl(None,
format_args!("LLVM returned a non-utf8 features string: {0}", e),
Location::caller());bug!("LLVM returned a non-utf8 features string: {}", e);
724 })
725 .to_owned()
726 } else {
727 ::rustc_span::macros::bug_impl(None,
format_args!("could not allocate host CPU features, LLVM returned a `null` string"),
Location::caller());bug!("could not allocate host CPU features, LLVM returned a `null` string");
728 };
729
730 llvm::LLVMDisposeMessage(ptr);
731
732 features_string
733 };
734 if !features_string.is_empty() {
735 features.extend(features_string.split(',').map(String::from));
736 }
737 }
738 Some(_) | None => {}
739 };
740
741 let mut extend_backend_features = |feature: &str, enable: bool| {
742 let enable_disable = if enable { '+' } else { '-' };
743 let Some(llvm_feature) = to_llvm_features(&sess.target, feature) else { return };
748
749 features.extend(
750 std::iter::once(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", enable_disable,
llvm_feature.llvm_feature_name))
})format!("{}{}", enable_disable, llvm_feature.llvm_feature_name)).chain(
751 llvm_feature.dependencies.into_iter().filter_map(move |feat| {
752 match (enable, feat) {
753 (_, TargetFeatureFoldStrength::Both(f))
754 | (true, TargetFeatureFoldStrength::EnableOnly(f)) => {
755 Some(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", enable_disable, f))
})format!("{enable_disable}{f}"))
756 }
757 _ => None,
758 }
759 }),
760 ),
761 );
762 };
763
764 target_features::target_spec_to_backend_features(sess, &mut extend_backend_features);
766
767 if !for_cfg {
771 target_features::flag_to_backend_features(sess, extend_backend_features);
772 }
773
774 llvm_features_by_flags(sess, &mut features);
779
780 for feature in sess.opts.unstable_opts.llvm_target_feature.split(',') {
783 if feature.is_empty() {
784 continue;
785 }
786 if feature.starts_with('+') || feature.starts_with('-') {
787 features.push(feature.to_owned());
788 } else {
789 if for_cfg {
792 sess.dcx().emit_warn(diagnostics::UnknownLlvmTargetFeaturePrefix { feature });
793 }
794 }
795 }
796
797 features
798}
799
800pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> {
801 let name = sess.opts.unstable_opts.tune_cpu.as_ref()?;
802 Some(handle_native(name))
803}
804
805pub(crate) fn target_has_mnemonic(sess: &Session, mnemonic: &str) -> bool {
806 require_inited();
807 let tm = create_informational_target_machine(sess);
808 let cstr = SmallCStr::new(mnemonic);
809 unsafe { llvm::LLVMRustTargetHasMnemonic(tm.raw(), cstr.as_ptr()) }
810}