]>
Commit | Line | Data |
---|---|---|
9fa01778 | 1 | use crate::back::write::create_informational_target_machine; |
cdc7bbd5 | 2 | use crate::{llvm, llvm_util}; |
dfeec247 | 3 | use libc::c_int; |
a2a8927a | 4 | use libloading::Library; |
5e7ed085 FG |
5 | use rustc_codegen_ssa::target_features::{ |
6 | supported_target_features, tied_target_features, RUSTC_SPECIFIC_FEATURES, | |
7 | }; | |
5099ac24 | 8 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
5e7ed085 | 9 | use rustc_data_structures::small_c_str::SmallCStr; |
a2a8927a | 10 | use rustc_fs_util::path_to_c_string; |
ba9703b0 XL |
11 | use rustc_middle::bug; |
12 | use rustc_session::config::PrintRequest; | |
13 | use rustc_session::Session; | |
dfeec247 | 14 | use rustc_span::symbol::Symbol; |
e74abb32 | 15 | use rustc_target::spec::{MergeFunctions, PanicStrategy}; |
5e7ed085 | 16 | use smallvec::{smallvec, SmallVec}; |
5869c6ff | 17 | use std::ffi::{CStr, CString}; |
136023e0 | 18 | use tracing::debug; |
7cac9316 | 19 | |
136023e0 XL |
20 | use std::mem; |
21 | use std::path::Path; | |
cdc7bbd5 | 22 | use std::ptr; |
b7449926 | 23 | use std::slice; |
dfeec247 | 24 | use std::str; |
7cac9316 XL |
25 | use std::sync::Once; |
26 | ||
2c00a5a8 XL |
27 | static INIT: Once = Once::new(); |
28 | ||
29 | pub(crate) fn init(sess: &Session) { | |
7cac9316 XL |
30 | unsafe { |
31 | // Before we touch LLVM, make sure that multithreading is enabled. | |
3c0e092e XL |
32 | if llvm::LLVMIsMultithreaded() != 1 { |
33 | bug!("LLVM compiled without support for threads"); | |
34 | } | |
7cac9316 | 35 | INIT.call_once(|| { |
7cac9316 XL |
36 | configure_llvm(sess); |
37 | }); | |
7cac9316 XL |
38 | } |
39 | } | |
40 | ||
2c00a5a8 | 41 | fn require_inited() { |
3c0e092e XL |
42 | if !INIT.is_completed() { |
43 | bug!("LLVM is not initialized"); | |
2c00a5a8 XL |
44 | } |
45 | } | |
46 | ||
7cac9316 | 47 | unsafe fn configure_llvm(sess: &Session) { |
29967ef6 | 48 | let n_args = sess.opts.cg.llvm_args.len() + sess.target.llvm_args.len(); |
0bf4aa26 XL |
49 | let mut llvm_c_strs = Vec::with_capacity(n_args + 1); |
50 | let mut llvm_args = Vec::with_capacity(n_args + 1); | |
51 | ||
52 | llvm::LLVMRustInstallFatalErrorHandler(); | |
5099ac24 FG |
53 | // On Windows, an LLVM assertion will open an Abort/Retry/Ignore dialog |
54 | // box for the purpose of launching a debugger. However, on CI this will | |
55 | // cause it to hang until it times out, which can take several hours. | |
56 | if std::env::var_os("CI").is_some() { | |
57 | llvm::LLVMRustDisableSystemDialogsOnCrash(); | |
58 | } | |
7cac9316 | 59 | |
dfeec247 XL |
60 | fn llvm_arg_to_arg_name(full_arg: &str) -> &str { |
61 | full_arg.trim().split(|c: char| c == '=' || c.is_whitespace()).next().unwrap_or("") | |
62 | } | |
63 | ||
5e7ed085 FG |
64 | let cg_opts = sess.opts.cg.llvm_args.iter().map(AsRef::as_ref); |
65 | let tg_opts = sess.target.llvm_args.iter().map(AsRef::as_ref); | |
dfeec247 XL |
66 | let sess_args = cg_opts.chain(tg_opts); |
67 | ||
68 | let user_specified_args: FxHashSet<_> = | |
74b04a01 | 69 | sess_args.clone().map(|s| llvm_arg_to_arg_name(s)).filter(|s| !s.is_empty()).collect(); |
dfeec247 | 70 | |
7cac9316 | 71 | { |
dfeec247 XL |
72 | // This adds the given argument to LLVM. Unless `force` is true |
73 | // user specified arguments are *not* overridden. | |
74 | let mut add = |arg: &str, force: bool| { | |
75 | if force || !user_specified_args.contains(llvm_arg_to_arg_name(arg)) { | |
76 | let s = CString::new(arg).unwrap(); | |
77 | llvm_args.push(s.as_ptr()); | |
78 | llvm_c_strs.push(s); | |
79 | } | |
7cac9316 | 80 | }; |
3dfed10e XL |
81 | // Set the llvm "program name" to make usage and invalid argument messages more clear. |
82 | add("rustc -Cllvm-args=\"...\" with", true); | |
dfeec247 XL |
83 | if sess.time_llvm_passes() { |
84 | add("-time-passes", false); | |
0531ce1d | 85 | } |
dfeec247 XL |
86 | if sess.print_llvm_passes() { |
87 | add("-debug-pass=Structure", false); | |
88 | } | |
3c0e092e XL |
89 | if sess.target.generate_arange_section |
90 | && !sess.opts.debugging_opts.no_generate_arange_section | |
91 | { | |
dfeec247 | 92 | add("-generate-arange-section", false); |
60c5eb7d | 93 | } |
cdc7bbd5 | 94 | |
94222f64 XL |
95 | // Disable the machine outliner by default in LLVM versions 11 and LLVM |
96 | // version 12, where it leads to miscompilation. | |
cdc7bbd5 | 97 | // |
94222f64 XL |
98 | // Ref: |
99 | // - https://github.com/rust-lang/rust/issues/85351 | |
100 | // - https://reviews.llvm.org/D103167 | |
3c0e092e | 101 | if llvm_util::get_version() < (13, 0, 0) { |
cdc7bbd5 XL |
102 | add("-enable-machine-outliner=never", false); |
103 | } | |
104 | ||
29967ef6 | 105 | match sess.opts.debugging_opts.merge_functions.unwrap_or(sess.target.merge_functions) { |
ba9703b0 XL |
106 | MergeFunctions::Disabled | MergeFunctions::Trampolines => {} |
107 | MergeFunctions::Aliases => { | |
108 | add("-mergefunc-use-aliases", false); | |
9fa01778 | 109 | } |
a1dfa0c6 | 110 | } |
7cac9316 | 111 | |
29967ef6 | 112 | if sess.target.os == "emscripten" && sess.panic_strategy() == PanicStrategy::Unwind { |
dfeec247 | 113 | add("-enable-emscripten-cxx-exceptions", false); |
e74abb32 XL |
114 | } |
115 | ||
0731742a XL |
116 | // HACK(eddyb) LLVM inserts `llvm.assume` calls to preserve align attributes |
117 | // during inlining. Unfortunately these may block other optimizations. | |
dfeec247 | 118 | add("-preserve-alignment-assumptions-during-inlining=false", false); |
0731742a | 119 | |
cdc7bbd5 XL |
120 | // Use non-zero `import-instr-limit` multiplier for cold callsites. |
121 | add("-import-cold-multiplier=0.1", false); | |
122 | ||
dfeec247 XL |
123 | for arg in sess_args { |
124 | add(&(*arg), true); | |
7cac9316 XL |
125 | } |
126 | } | |
127 | ||
fc512014 | 128 | if sess.opts.debugging_opts.llvm_time_trace { |
74b04a01 XL |
129 | llvm::LLVMTimeTraceProfilerInitialize(); |
130 | } | |
131 | ||
7cac9316 XL |
132 | llvm::LLVMInitializePasses(); |
133 | ||
a2a8927a XL |
134 | // Use the legacy plugin registration if we don't use the new pass manager |
135 | if !should_use_new_llvm_pass_manager( | |
136 | &sess.opts.debugging_opts.new_llvm_pass_manager, | |
137 | &sess.target.arch, | |
138 | ) { | |
139 | // Register LLVM plugins by loading them into the compiler process. | |
140 | for plugin in &sess.opts.debugging_opts.llvm_plugins { | |
141 | let lib = Library::new(plugin).unwrap_or_else(|e| bug!("couldn't load plugin: {}", e)); | |
142 | debug!("LLVM plugin loaded successfully {:?} ({})", lib, plugin); | |
143 | ||
144 | // Intentionally leak the dynamic library. We can't ever unload it | |
145 | // since the library can make things that will live arbitrarily long. | |
146 | mem::forget(lib); | |
136023e0 | 147 | } |
136023e0 XL |
148 | } |
149 | ||
29967ef6 | 150 | rustc_llvm::initialize_available_targets(); |
7cac9316 | 151 | |
dfeec247 | 152 | llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr()); |
7cac9316 XL |
153 | } |
154 | ||
a2a8927a | 155 | pub fn time_trace_profiler_finish(file_name: &Path) { |
74b04a01 | 156 | unsafe { |
a2a8927a | 157 | let file_name = path_to_c_string(file_name); |
fc512014 | 158 | llvm::LLVMTimeTraceProfilerFinish(file_name.as_ptr()); |
74b04a01 XL |
159 | } |
160 | } | |
161 | ||
5e7ed085 | 162 | // WARNING: the features after applying `to_llvm_features` must be known |
0531ce1d XL |
163 | // to LLVM or the feature detection code will walk past the end of the feature |
164 | // array, leading to crashes. | |
5e7ed085 | 165 | // |
fc512014 XL |
166 | // To find a list of LLVM's names, check llvm-project/llvm/include/llvm/Support/*TargetParser.def |
167 | // where the * matches the architecture's name | |
168 | // Beware to not use the llvm github project for this, but check the git submodule | |
169 | // found in src/llvm-project | |
170 | // Though note that Rust can also be build with an external precompiled version of LLVM | |
171 | // which might lead to failures if the oldest tested / supported LLVM version | |
172 | // doesn't yet support the relevant intrinsics | |
5e7ed085 | 173 | pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> { |
29967ef6 | 174 | let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch }; |
0531ce1d | 175 | match (arch, s) { |
c295e0f8 XL |
176 | ("x86", "sse4.2") => { |
177 | if get_version() >= (14, 0, 0) { | |
5e7ed085 | 178 | smallvec!["sse4.2", "crc32"] |
c295e0f8 | 179 | } else { |
5e7ed085 | 180 | smallvec!["sse4.2"] |
c295e0f8 XL |
181 | } |
182 | } | |
5e7ed085 FG |
183 | ("x86", "pclmulqdq") => smallvec!["pclmul"], |
184 | ("x86", "rdrand") => smallvec!["rdrnd"], | |
185 | ("x86", "bmi1") => smallvec!["bmi"], | |
186 | ("x86", "cmpxchg16b") => smallvec!["cx16"], | |
187 | ("x86", "avx512vaes") => smallvec!["vaes"], | |
188 | ("x86", "avx512gfni") => smallvec!["gfni"], | |
189 | ("x86", "avx512vpclmulqdq") => smallvec!["vpclmulqdq"], | |
190 | ("aarch64", "rcpc2") => smallvec!["rcpc-immo"], | |
191 | ("aarch64", "dpb") => smallvec!["ccpp"], | |
192 | ("aarch64", "dpb2") => smallvec!["ccdp"], | |
193 | ("aarch64", "frintts") => smallvec!["fptoint"], | |
194 | ("aarch64", "fcma") => smallvec!["complxnum"], | |
195 | ("aarch64", "pmuv3") => smallvec!["perfmon"], | |
196 | ("aarch64", "paca") => smallvec!["pauth"], | |
197 | ("aarch64", "pacg") => smallvec!["pauth"], | |
198 | // Rust ties fp and neon together. In LLVM neon implicitly enables fp, | |
199 | // but we manually enable neon when a feature only implicitly enables fp | |
200 | ("aarch64", "f32mm") => smallvec!["f32mm", "neon"], | |
201 | ("aarch64", "f64mm") => smallvec!["f64mm", "neon"], | |
202 | ("aarch64", "fhm") => smallvec!["fp16fml", "neon"], | |
203 | ("aarch64", "fp16") => smallvec!["fullfp16", "neon"], | |
204 | ("aarch64", "jsconv") => smallvec!["jsconv", "neon"], | |
205 | ("aarch64", "sve") => smallvec!["sve", "neon"], | |
206 | ("aarch64", "sve2") => smallvec!["sve2", "neon"], | |
207 | ("aarch64", "sve2-aes") => smallvec!["sve2-aes", "neon"], | |
208 | ("aarch64", "sve2-sm4") => smallvec!["sve2-sm4", "neon"], | |
209 | ("aarch64", "sve2-sha3") => smallvec!["sve2-sha3", "neon"], | |
210 | ("aarch64", "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"], | |
211 | (_, s) => smallvec![s], | |
0531ce1d XL |
212 | } |
213 | } | |
ff7c6d11 | 214 | |
5099ac24 FG |
215 | // Given a map from target_features to whether they are enabled or disabled, |
216 | // ensure only valid combinations are allowed. | |
217 | pub fn check_tied_features( | |
218 | sess: &Session, | |
219 | features: &FxHashMap<&str, bool>, | |
220 | ) -> Option<&'static [&'static str]> { | |
923072b8 FG |
221 | if !features.is_empty() { |
222 | for tied in tied_target_features(sess) { | |
223 | // Tied features must be set to the same value, or not set at all | |
224 | let mut tied_iter = tied.iter(); | |
225 | let enabled = features.get(tied_iter.next().unwrap()); | |
226 | if tied_iter.any(|f| enabled != features.get(f)) { | |
227 | return Some(tied); | |
228 | } | |
5099ac24 FG |
229 | } |
230 | } | |
923072b8 | 231 | return None; |
5099ac24 FG |
232 | } |
233 | ||
5e7ed085 FG |
234 | // Used to generate cfg variables and apply features |
235 | // Must express features in the way Rust understands them | |
7cac9316 | 236 | pub fn target_features(sess: &Session) -> Vec<Symbol> { |
f9f354fc | 237 | let target_machine = create_informational_target_machine(sess); |
5099ac24 FG |
238 | let mut features: Vec<Symbol> = |
239 | supported_target_features(sess) | |
240 | .iter() | |
241 | .filter_map(|&(feature, gate)| { | |
fc512014 | 242 | if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None } |
5099ac24 FG |
243 | }) |
244 | .filter(|feature| { | |
5e7ed085 FG |
245 | // check that all features in a given smallvec are enabled |
246 | for llvm_feature in to_llvm_features(sess, feature) { | |
247 | let cstr = SmallCStr::new(llvm_feature); | |
248 | if !unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } { | |
249 | return false; | |
5099ac24 | 250 | } |
c295e0f8 | 251 | } |
5e7ed085 | 252 | true |
5099ac24 FG |
253 | }) |
254 | .map(|feature| Symbol::intern(feature)) | |
255 | .collect(); | |
256 | ||
257 | // LLVM 14 changed the ABI for i128 arguments to __float/__fix builtins on Win64 | |
258 | // (see https://reviews.llvm.org/D110413). This unstable target feature is intended for use | |
259 | // by compiler-builtins, to export the builtins with the expected, LLVM-version-dependent ABI. | |
260 | // The target feature can be dropped once we no longer support older LLVM versions. | |
261 | if sess.is_nightly_build() && get_version() >= (14, 0, 0) { | |
262 | features.push(Symbol::intern("llvm14-builtins-abi")); | |
263 | } | |
264 | features | |
2c00a5a8 | 265 | } |
7cac9316 | 266 | |
7cac9316 | 267 | pub fn print_version() { |
5869c6ff XL |
268 | let (major, minor, patch) = get_version(); |
269 | println!("LLVM version: {}.{}.{}", major, minor, patch); | |
270 | } | |
271 | ||
272 | pub fn get_version() -> (u32, u32, u32) { | |
2c00a5a8 | 273 | // Can be called without initializing LLVM |
7cac9316 | 274 | unsafe { |
5869c6ff | 275 | (llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor(), llvm::LLVMRustVersionPatch()) |
7cac9316 XL |
276 | } |
277 | } | |
278 | ||
279 | pub fn print_passes() { | |
2c00a5a8 | 280 | // Can be called without initializing LLVM |
dfeec247 XL |
281 | unsafe { |
282 | llvm::LLVMRustPrintPasses(); | |
283 | } | |
7cac9316 XL |
284 | } |
285 | ||
cdc7bbd5 XL |
286 | fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> { |
287 | let len = unsafe { llvm::LLVMRustGetTargetFeaturesCount(tm) }; | |
288 | let mut ret = Vec::with_capacity(len); | |
289 | for i in 0..len { | |
290 | unsafe { | |
291 | let mut feature = ptr::null(); | |
292 | let mut desc = ptr::null(); | |
293 | llvm::LLVMRustGetTargetFeature(tm, i, &mut feature, &mut desc); | |
294 | if feature.is_null() || desc.is_null() { | |
295 | bug!("LLVM returned a `null` target feature string"); | |
296 | } | |
297 | let feature = CStr::from_ptr(feature).to_str().unwrap_or_else(|e| { | |
298 | bug!("LLVM returned a non-utf8 feature string: {}", e); | |
299 | }); | |
300 | let desc = CStr::from_ptr(desc).to_str().unwrap_or_else(|e| { | |
301 | bug!("LLVM returned a non-utf8 feature string: {}", e); | |
302 | }); | |
303 | ret.push((feature, desc)); | |
304 | } | |
305 | } | |
306 | ret | |
307 | } | |
308 | ||
309 | fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { | |
310 | let mut target_features = llvm_target_features(tm); | |
311 | let mut rustc_target_features = supported_target_features(sess) | |
312 | .iter() | |
313 | .filter_map(|(feature, _gate)| { | |
5e7ed085 | 314 | for llvm_feature in to_llvm_features(sess, *feature) { |
c295e0f8 | 315 | // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. |
5e7ed085 | 316 | match target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok().map( |
c295e0f8 XL |
317 | |index| { |
318 | let (_f, desc) = target_features.remove(index); | |
319 | (*feature, desc) | |
320 | }, | |
321 | ) { | |
322 | Some(v) => return Some(v), | |
323 | None => {} | |
324 | } | |
325 | } | |
326 | None | |
cdc7bbd5 XL |
327 | }) |
328 | .collect::<Vec<_>>(); | |
329 | rustc_target_features.extend_from_slice(&[( | |
330 | "crt-static", | |
331 | "Enables C Run-time Libraries to be statically linked", | |
332 | )]); | |
333 | let max_feature_len = target_features | |
334 | .iter() | |
335 | .chain(rustc_target_features.iter()) | |
336 | .map(|(feature, _desc)| feature.len()) | |
337 | .max() | |
338 | .unwrap_or(0); | |
339 | ||
340 | println!("Features supported by rustc for this target:"); | |
341 | for (feature, desc) in &rustc_target_features { | |
342 | println!(" {1:0$} - {2}.", max_feature_len, feature, desc); | |
343 | } | |
344 | println!("\nCode-generation features supported by LLVM for this target:"); | |
345 | for (feature, desc) in &target_features { | |
346 | println!(" {1:0$} - {2}.", max_feature_len, feature, desc); | |
347 | } | |
c295e0f8 | 348 | if target_features.is_empty() { |
cdc7bbd5 XL |
349 | println!(" Target features listing is not supported by this LLVM version."); |
350 | } | |
351 | println!("\nUse +feature to enable a feature, or -feature to disable it."); | |
352 | println!("For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); | |
353 | println!("Code-generation features cannot be used in cfg or #[target_feature],"); | |
354 | println!("and may be renamed or removed in a future version of LLVM or rustc.\n"); | |
355 | } | |
356 | ||
2c00a5a8 XL |
357 | pub(crate) fn print(req: PrintRequest, sess: &Session) { |
358 | require_inited(); | |
f9f354fc | 359 | let tm = create_informational_target_machine(sess); |
cdc7bbd5 XL |
360 | match req { |
361 | PrintRequest::TargetCPUs => unsafe { llvm::LLVMRustPrintTargetCPUs(tm) }, | |
362 | PrintRequest::TargetFeatures => print_target_features(sess, tm), | |
363 | _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), | |
7cac9316 XL |
364 | } |
365 | } | |
b7449926 | 366 | |
29967ef6 | 367 | fn handle_native(name: &str) -> &str { |
b7449926 | 368 | if name != "native" { |
dfeec247 | 369 | return name; |
b7449926 XL |
370 | } |
371 | ||
372 | unsafe { | |
373 | let mut len = 0; | |
374 | let ptr = llvm::LLVMRustGetHostCPUName(&mut len); | |
375 | str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap() | |
376 | } | |
377 | } | |
29967ef6 XL |
378 | |
379 | pub fn target_cpu(sess: &Session) -> &str { | |
5e7ed085 FG |
380 | match sess.opts.cg.target_cpu { |
381 | Some(ref name) => handle_native(name), | |
382 | None => handle_native(sess.target.cpu.as_ref()), | |
383 | } | |
29967ef6 XL |
384 | } |
385 | ||
6a06907d XL |
386 | /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, |
387 | /// `--target` and similar). | |
5e7ed085 FG |
388 | pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<String> { |
389 | // Features that come earlier are overridden by conflicting features later in the string. | |
6a06907d XL |
390 | // Typically we'll want more explicit settings to override the implicit ones, so: |
391 | // | |
5e7ed085 FG |
392 | // * Features from -Ctarget-cpu=*; are overridden by [^1] |
393 | // * Features implied by --target; are overridden by | |
394 | // * Features from -Ctarget-feature; are overridden by | |
6a06907d XL |
395 | // * function specific features. |
396 | // | |
397 | // [^1]: target-cpu=native is handled here, other target-cpu values are handled implicitly | |
398 | // through LLVM TargetMachine implementation. | |
399 | // | |
400 | // FIXME(nagisa): it isn't clear what's the best interaction between features implied by | |
401 | // `-Ctarget-cpu` and `--target` are. On one hand, you'd expect CLI arguments to always | |
402 | // override anything that's implicit, so e.g. when there's no `--target` flag, features implied | |
5e7ed085 | 403 | // the host target are overridden by `-Ctarget-cpu=*`. On the other hand, what about when both |
6a06907d XL |
404 | // `--target` and `-Ctarget-cpu=*` are specified? Both then imply some target features and both |
405 | // flags are specified by the user on the CLI. It isn't as clear-cut which order of precedence | |
406 | // should be taken in cases like these. | |
407 | let mut features = vec![]; | |
408 | ||
409 | // -Ctarget-cpu=native | |
5869c6ff | 410 | match sess.opts.cg.target_cpu { |
6a06907d | 411 | Some(ref s) if s == "native" => { |
5869c6ff XL |
412 | let features_string = unsafe { |
413 | let ptr = llvm::LLVMGetHostCPUFeatures(); | |
414 | let features_string = if !ptr.is_null() { | |
415 | CStr::from_ptr(ptr) | |
416 | .to_str() | |
417 | .unwrap_or_else(|e| { | |
418 | bug!("LLVM returned a non-utf8 features string: {}", e); | |
419 | }) | |
420 | .to_owned() | |
421 | } else { | |
422 | bug!("could not allocate host CPU features, LLVM returned a `null` string"); | |
423 | }; | |
424 | ||
425 | llvm::LLVMDisposeMessage(ptr); | |
426 | ||
427 | features_string | |
428 | }; | |
94222f64 | 429 | features.extend(features_string.split(',').map(String::from)); |
5869c6ff | 430 | } |
6a06907d XL |
431 | Some(_) | None => {} |
432 | }; | |
433 | ||
5e7ed085 FG |
434 | // Features implied by an implicit or explicit `--target`. |
435 | features.extend( | |
436 | sess.target | |
437 | .features | |
438 | .split(',') | |
439 | .filter(|v| !v.is_empty() && backend_feature_name(v).is_some()) | |
440 | .map(String::from), | |
441 | ); | |
5099ac24 | 442 | |
5e7ed085 FG |
443 | // -Ctarget-features |
444 | let supported_features = supported_target_features(sess); | |
923072b8 | 445 | let mut featsmap = FxHashMap::default(); |
5e7ed085 FG |
446 | let feats = sess |
447 | .opts | |
448 | .cg | |
449 | .target_feature | |
450 | .split(',') | |
451 | .filter_map(|s| { | |
452 | let enable_disable = match s.chars().next() { | |
453 | None => return None, | |
454 | Some(c @ '+' | c @ '-') => c, | |
455 | Some(_) => { | |
456 | if diagnostics { | |
457 | let mut diag = sess.struct_warn(&format!( | |
458 | "unknown feature specified for `-Ctarget-feature`: `{}`", | |
459 | s | |
460 | )); | |
461 | diag.note("features must begin with a `+` to enable or `-` to disable it"); | |
462 | diag.emit(); | |
463 | } | |
464 | return None; | |
465 | } | |
466 | }; | |
467 | ||
468 | let feature = backend_feature_name(s)?; | |
469 | // Warn against use of LLVM specific feature names on the CLI. | |
470 | if diagnostics && !supported_features.iter().any(|&(v, _)| v == feature) { | |
471 | let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| { | |
472 | let llvm_features = to_llvm_features(sess, rust_feature); | |
473 | if llvm_features.contains(&feature) && !llvm_features.contains(&rust_feature) { | |
474 | Some(rust_feature) | |
475 | } else { | |
476 | None | |
477 | } | |
478 | }); | |
479 | let mut diag = sess.struct_warn(&format!( | |
480 | "unknown feature specified for `-Ctarget-feature`: `{}`", | |
481 | feature | |
482 | )); | |
483 | diag.note("it is still passed through to the codegen backend"); | |
484 | if let Some(rust_feature) = rust_feature { | |
485 | diag.help(&format!("you might have meant: `{}`", rust_feature)); | |
486 | } else { | |
487 | diag.note("consider filing a feature request"); | |
488 | } | |
489 | diag.emit(); | |
490 | } | |
923072b8 FG |
491 | |
492 | if diagnostics { | |
493 | // FIXME(nagisa): figure out how to not allocate a full hashset here. | |
494 | featsmap.insert(feature, enable_disable == '+'); | |
495 | } | |
496 | ||
497 | // rustc-specific features do not get passed down to LLVM… | |
498 | if RUSTC_SPECIFIC_FEATURES.contains(&feature) { | |
499 | return None; | |
500 | } | |
501 | // ... otherwise though we run through `to_llvm_features` when | |
502 | // passing requests down to LLVM. This means that all in-language | |
503 | // features also work on the command line instead of having two | |
504 | // different names when the LLVM name and the Rust name differ. | |
505 | Some( | |
506 | to_llvm_features(sess, feature) | |
507 | .into_iter() | |
508 | .map(move |f| format!("{}{}", enable_disable, f)), | |
509 | ) | |
5e7ed085 | 510 | }) |
923072b8 FG |
511 | .flatten(); |
512 | features.extend(feats); | |
513 | ||
514 | if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { | |
515 | sess.err(&format!( | |
516 | "target features {} must all be enabled or disabled together", | |
517 | f.join(", ") | |
518 | )); | |
5e7ed085 | 519 | } |
5099ac24 | 520 | |
5e7ed085 FG |
521 | features |
522 | } | |
6a06907d | 523 | |
5e7ed085 FG |
524 | /// Returns a feature name for the given `+feature` or `-feature` string. |
525 | /// | |
526 | /// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].) | |
527 | fn backend_feature_name(s: &str) -> Option<&str> { | |
528 | // features must start with a `+` or `-`. | |
529 | let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| { | |
530 | bug!("target feature `{}` must begin with a `+` or `-`", s); | |
531 | }); | |
532 | // Rustc-specific feature requests like `+crt-static` or `-crt-static` | |
533 | // are not passed down to LLVM. | |
534 | if RUSTC_SPECIFIC_FEATURES.contains(&feature) { | |
535 | return None; | |
5099ac24 | 536 | } |
5e7ed085 | 537 | Some(feature) |
29967ef6 | 538 | } |
5869c6ff XL |
539 | |
540 | pub fn tune_cpu(sess: &Session) -> Option<&str> { | |
541 | let name = sess.opts.debugging_opts.tune_cpu.as_ref()?; | |
542 | Some(handle_native(name)) | |
543 | } | |
a2a8927a XL |
544 | |
545 | pub(crate) fn should_use_new_llvm_pass_manager(user_opt: &Option<bool>, target_arch: &str) -> bool { | |
546 | // The new pass manager is enabled by default for LLVM >= 13. | |
547 | // This matches Clang, which also enables it since Clang 13. | |
548 | ||
04454e1e FG |
549 | // Since LLVM 15, the legacy pass manager is no longer supported. |
550 | if llvm_util::get_version() >= (15, 0, 0) { | |
551 | return true; | |
552 | } | |
553 | ||
5099ac24 FG |
554 | // There are some perf issues with the new pass manager when targeting |
555 | // s390x with LLVM 13, so enable the new pass manager only with LLVM 14. | |
556 | // See https://github.com/rust-lang/rust/issues/89609. | |
557 | let min_version = if target_arch == "s390x" { 14 } else { 13 }; | |
558 | user_opt.unwrap_or_else(|| llvm_util::get_version() >= (min_version, 0, 0)) | |
a2a8927a | 559 | } |