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