Skip to content

Commit b829f3c

Browse files
committed
rustc_codegen_llvm: Handle unavailable LLVM features
Some codegen features may or may not be available depending on the LLVM version. Rewrite the way features are mapped and passed down to LLVM to be able to handle those properly.
1 parent e3a5705 commit b829f3c

File tree

2 files changed

+95
-71
lines changed

2 files changed

+95
-71
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -497,9 +497,8 @@ pub fn from_fn_attrs<'ll, 'tcx>(
497497

498498
let function_features = function_features
499499
.iter()
500-
.flat_map(|feat| {
501-
llvm_util::to_llvm_features(cx.tcx.sess, feat).into_iter().map(|f| format!("+{f}"))
502-
})
500+
.flat_map(|feat| llvm_util::to_llvm_features(cx.tcx.sess, feat))
501+
.map(|f| format!("+{f}"))
503502
.chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
504503
InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
505504
InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(),

compiler/rustc_codegen_llvm/src/llvm_util.rs

+93-68
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_target::spec::{MergeFunctions, PanicStrategy};
1717
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
1818

1919
use std::ffi::{c_char, c_void, CStr, CString};
20+
use std::fmt;
2021
use std::fmt::Write;
2122
use std::path::Path;
2223
use std::ptr;
@@ -197,6 +198,12 @@ impl<'a> IntoIterator for LLVMFeature<'a> {
197198
}
198199
}
199200

201+
impl<'a> fmt::Display for LLVMFeature<'a> {
202+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203+
write!(f, "{}", self.llvm_feature_name)
204+
}
205+
}
206+
200207
// WARNING: the features after applying `to_llvm_features` must be known
201208
// to LLVM or the feature detection code will walk past the end of the feature
202209
// array, leading to crashes.
@@ -209,7 +216,7 @@ impl<'a> IntoIterator for LLVMFeature<'a> {
209216
// Though note that Rust can also be build with an external precompiled version of LLVM
210217
// which might lead to failures if the oldest tested / supported LLVM version
211218
// doesn't yet support the relevant intrinsics
212-
pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> {
219+
pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFeature<'a>> {
213220
let arch = if sess.target.arch == "x86_64" {
214221
"x86"
215222
} else if sess.target.arch == "arm64ec" {
@@ -218,77 +225,88 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> {
218225
&*sess.target.arch
219226
};
220227
match (arch, s) {
221-
("x86", "sse4.2") => {
222-
LLVMFeature::with_dependency("sse4.2", TargetFeatureFoldStrength::EnableOnly("crc32"))
223-
}
224-
("x86", "pclmulqdq") => LLVMFeature::new("pclmul"),
225-
("x86", "rdrand") => LLVMFeature::new("rdrnd"),
226-
("x86", "bmi1") => LLVMFeature::new("bmi"),
227-
("x86", "cmpxchg16b") => LLVMFeature::new("cx16"),
228-
("x86", "lahfsahf") => LLVMFeature::new("sahf"),
229-
("aarch64", "rcpc2") => LLVMFeature::new("rcpc-immo"),
230-
("aarch64", "dpb") => LLVMFeature::new("ccpp"),
231-
("aarch64", "dpb2") => LLVMFeature::new("ccdp"),
232-
("aarch64", "frintts") => LLVMFeature::new("fptoint"),
233-
("aarch64", "fcma") => LLVMFeature::new("complxnum"),
234-
("aarch64", "pmuv3") => LLVMFeature::new("perfmon"),
235-
("aarch64", "paca") => LLVMFeature::new("pauth"),
236-
("aarch64", "pacg") => LLVMFeature::new("pauth"),
237-
("aarch64", "sve-b16b16") => LLVMFeature::new("b16b16"),
238-
("aarch64", "flagm2") => LLVMFeature::new("altnzcv"),
228+
("x86", "sse4.2") => Some(LLVMFeature::with_dependency(
229+
"sse4.2",
230+
TargetFeatureFoldStrength::EnableOnly("crc32"),
231+
)),
232+
("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")),
233+
("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")),
234+
("x86", "bmi1") => Some(LLVMFeature::new("bmi")),
235+
("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")),
236+
("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")),
237+
("aarch64", "rcpc2") => Some(LLVMFeature::new("rcpc-immo")),
238+
("aarch64", "dpb") => Some(LLVMFeature::new("ccpp")),
239+
("aarch64", "dpb2") => Some(LLVMFeature::new("ccdp")),
240+
("aarch64", "frintts") => Some(LLVMFeature::new("fptoint")),
241+
("aarch64", "fcma") => Some(LLVMFeature::new("complxnum")),
242+
("aarch64", "pmuv3") => Some(LLVMFeature::new("perfmon")),
243+
("aarch64", "paca") => Some(LLVMFeature::new("pauth")),
244+
("aarch64", "pacg") => Some(LLVMFeature::new("pauth")),
245+
("aarch64", "sve-b16b16") => Some(LLVMFeature::new("b16b16")),
246+
("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")),
239247
// Rust ties fp and neon together.
240248
("aarch64", "neon") => {
241-
LLVMFeature::with_dependency("neon", TargetFeatureFoldStrength::Both("fp-armv8"))
249+
Some(LLVMFeature::with_dependency("neon", TargetFeatureFoldStrength::Both("fp-armv8")))
242250
}
243251
// In LLVM neon implicitly enables fp, but we manually enable
244252
// neon when a feature only implicitly enables fp
245-
("aarch64", "f32mm") => {
246-
LLVMFeature::with_dependency("f32mm", TargetFeatureFoldStrength::EnableOnly("neon"))
247-
}
248-
("aarch64", "f64mm") => {
249-
LLVMFeature::with_dependency("f64mm", TargetFeatureFoldStrength::EnableOnly("neon"))
250-
}
251-
("aarch64", "fhm") => {
252-
LLVMFeature::with_dependency("fp16fml", TargetFeatureFoldStrength::EnableOnly("neon"))
253-
}
254-
("aarch64", "fp16") => {
255-
LLVMFeature::with_dependency("fullfp16", TargetFeatureFoldStrength::EnableOnly("neon"))
256-
}
257-
("aarch64", "jsconv") => {
258-
LLVMFeature::with_dependency("jsconv", TargetFeatureFoldStrength::EnableOnly("neon"))
259-
}
253+
("aarch64", "f32mm") => Some(LLVMFeature::with_dependency(
254+
"f32mm",
255+
TargetFeatureFoldStrength::EnableOnly("neon"),
256+
)),
257+
("aarch64", "f64mm") => Some(LLVMFeature::with_dependency(
258+
"f64mm",
259+
TargetFeatureFoldStrength::EnableOnly("neon"),
260+
)),
261+
("aarch64", "fhm") => Some(LLVMFeature::with_dependency(
262+
"fp16fml",
263+
TargetFeatureFoldStrength::EnableOnly("neon"),
264+
)),
265+
("aarch64", "fp16") => Some(LLVMFeature::with_dependency(
266+
"fullfp16",
267+
TargetFeatureFoldStrength::EnableOnly("neon"),
268+
)),
269+
("aarch64", "jsconv") => Some(LLVMFeature::with_dependency(
270+
"jsconv",
271+
TargetFeatureFoldStrength::EnableOnly("neon"),
272+
)),
260273
("aarch64", "sve") => {
261-
LLVMFeature::with_dependency("sve", TargetFeatureFoldStrength::EnableOnly("neon"))
274+
Some(LLVMFeature::with_dependency("sve", TargetFeatureFoldStrength::EnableOnly("neon")))
262275
}
263-
("aarch64", "sve2") => {
264-
LLVMFeature::with_dependency("sve2", TargetFeatureFoldStrength::EnableOnly("neon"))
265-
}
266-
("aarch64", "sve2p1") => {
267-
LLVMFeature::with_dependency("sve2p1", TargetFeatureFoldStrength::EnableOnly("neon"))
268-
}
269-
("aarch64", "sve2-aes") => {
270-
LLVMFeature::with_dependency("sve2-aes", TargetFeatureFoldStrength::EnableOnly("neon"))
271-
}
272-
("aarch64", "sve2-sm4") => {
273-
LLVMFeature::with_dependency("sve2-sm4", TargetFeatureFoldStrength::EnableOnly("neon"))
274-
}
275-
("aarch64", "sve2-sha3") => {
276-
LLVMFeature::with_dependency("sve2-sha3", TargetFeatureFoldStrength::EnableOnly("neon"))
277-
}
278-
("aarch64", "sve2-bitperm") => LLVMFeature::with_dependency(
276+
("aarch64", "sve2") => Some(LLVMFeature::with_dependency(
277+
"sve2",
278+
TargetFeatureFoldStrength::EnableOnly("neon"),
279+
)),
280+
("aarch64", "sve2p1") => Some(LLVMFeature::with_dependency(
281+
"sve2p1",
282+
TargetFeatureFoldStrength::EnableOnly("neon"),
283+
)),
284+
("aarch64", "sve2-aes") => Some(LLVMFeature::with_dependency(
285+
"sve2-aes",
286+
TargetFeatureFoldStrength::EnableOnly("neon"),
287+
)),
288+
("aarch64", "sve2-sm4") => Some(LLVMFeature::with_dependency(
289+
"sve2-sm4",
290+
TargetFeatureFoldStrength::EnableOnly("neon"),
291+
)),
292+
("aarch64", "sve2-sha3") => Some(LLVMFeature::with_dependency(
293+
"sve2-sha3",
294+
TargetFeatureFoldStrength::EnableOnly("neon"),
295+
)),
296+
("aarch64", "sve2-bitperm") => Some(LLVMFeature::with_dependency(
279297
"sve2-bitperm",
280298
TargetFeatureFoldStrength::EnableOnly("neon"),
281-
),
299+
)),
282300
// In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single feature called
283301
// `fast-unaligned-access`. In LLVM 19, it was split back out.
284302
("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => {
285-
LLVMFeature::new("fast-unaligned-access")
303+
Some(LLVMFeature::new("fast-unaligned-access"))
286304
}
287305
// For LLVM 18, enable the evex512 target feature if a avx512 target feature is enabled.
288306
("x86", s) if get_version().0 >= 18 && s.starts_with("avx512") => {
289-
LLVMFeature::with_dependency(s, TargetFeatureFoldStrength::EnableOnly("evex512"))
307+
Some(LLVMFeature::with_dependency(s, TargetFeatureFoldStrength::EnableOnly("evex512")))
290308
}
291-
(_, s) => LLVMFeature::new(s),
309+
(_, s) => Some(LLVMFeature::new(s)),
292310
}
293311
}
294312

@@ -331,13 +349,17 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
331349
return true;
332350
}
333351
// check that all features in a given smallvec are enabled
334-
for llvm_feature in to_llvm_features(sess, feature) {
335-
let cstr = SmallCStr::new(llvm_feature);
336-
if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } {
337-
return false;
352+
if let Some(llvm_features) = to_llvm_features(sess, feature) {
353+
for llvm_feature in llvm_features {
354+
let cstr = SmallCStr::new(llvm_feature);
355+
if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } {
356+
return false;
357+
}
338358
}
359+
true
360+
} else {
361+
false
339362
}
340-
true
341363
})
342364
.map(|feature| Symbol::intern(feature))
343365
.collect()
@@ -388,13 +410,13 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> {
388410
fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMachine) {
389411
let mut llvm_target_features = llvm_target_features(tm);
390412
let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
391-
let mut rustc_target_features = sess
413+
let mut rustc_target_features: Vec<(&str, &str)> = sess
392414
.target
393415
.supported_target_features()
394416
.iter()
395-
.map(|(feature, _gate)| {
417+
.filter_map(|(feature, _gate)| {
396418
// LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings.
397-
let llvm_feature = to_llvm_features(sess, *feature).llvm_feature_name;
419+
let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name;
398420
let desc =
399421
match llvm_target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok() {
400422
Some(index) => {
@@ -404,9 +426,9 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach
404426
None => "",
405427
};
406428

407-
(*feature, desc)
429+
Some((*feature, desc))
408430
})
409-
.collect::<Vec<_>>();
431+
.collect();
410432

411433
// Since we add this at the end ...
412434
rustc_target_features.extend_from_slice(&[(
@@ -580,7 +602,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
580602
let feature_state = supported_features.iter().find(|&&(v, _)| v == feature);
581603
if feature_state.is_none() {
582604
let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| {
583-
let llvm_features = to_llvm_features(sess, rust_feature);
605+
let llvm_features = to_llvm_features(sess, rust_feature)?;
584606
if llvm_features.contains(feature) && !llvm_features.contains(rust_feature)
585607
{
586608
Some(rust_feature)
@@ -625,7 +647,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
625647
// passing requests down to LLVM. This means that all in-language
626648
// features also work on the command line instead of having two
627649
// different names when the LLVM name and the Rust name differ.
628-
let llvm_feature = to_llvm_features(sess, feature);
650+
let llvm_feature = to_llvm_features(sess, feature)?;
629651

630652
Some(
631653
std::iter::once(format!("{}{}", enable_disable, llvm_feature.llvm_feature_name))
@@ -671,6 +693,9 @@ fn backend_feature_name<'a>(sess: &Session, s: &'a str) -> Option<&'a str> {
671693
let feature = s
672694
.strip_prefix(&['+', '-'][..])
673695
.unwrap_or_else(|| sess.dcx().emit_fatal(InvalidTargetFeaturePrefix { feature: s }));
696+
if s.is_empty() {
697+
return None;
698+
}
674699
// Rustc-specific feature requests like `+crt-static` or `-crt-static`
675700
// are not passed down to LLVM.
676701
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {

0 commit comments

Comments
 (0)