Skip to content

Commit 23a6936

Browse files
committed
rustc_codegen_llvm: Filter out unavailable LLVM features
Convert to_llvm_features to return Option<LLVMFeature> so that it can return None if the requested feature is not available for the current LLVM version. Add match rules to filter out aarch64 features not available in LLVM 17.
1 parent 3a18512 commit 23a6936

File tree

2 files changed

+63
-38
lines changed

2 files changed

+63
-38
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -521,9 +521,10 @@ pub fn llfn_attrs_from_instance<'ll, 'tcx>(
521521

522522
let function_features = function_features
523523
.iter()
524-
.flat_map(|feat| {
525-
llvm_util::to_llvm_features(cx.tcx.sess, feat).into_iter().map(|f| format!("+{f}"))
526-
})
524+
// Convert to LLVMFeatures and filter out unavailable ones
525+
.flat_map(|feat| llvm_util::to_llvm_features(cx.tcx.sess, feat))
526+
// Convert LLVMFeatures & dependencies to +<feats>s
527+
.flat_map(|feat| feat.into_iter().map(|f| format!("+{f}")))
527528
.chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
528529
InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
529530
InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(),

compiler/rustc_codegen_llvm/src/llvm_util.rs

+59-35
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ impl<'a> IntoIterator for LLVMFeature<'a> {
209209
// Though note that Rust can also be build with an external precompiled version of LLVM
210210
// which might lead to failures if the oldest tested / supported LLVM version
211211
// doesn't yet support the relevant intrinsics
212-
pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> {
212+
pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFeature<'a>> {
213213
let arch = if sess.target.arch == "x86_64" {
214214
"x86"
215215
} else if sess.target.arch == "arm64ec" {
@@ -218,42 +218,59 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> {
218218
&*sess.target.arch
219219
};
220220
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"),
221+
("x86", "sse4.2") => Some(LLVMFeature::with_dependency(
222+
"sse4.2",
223+
TargetFeatureFoldStrength::EnableOnly("crc32"),
224+
)),
225+
("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")),
226+
("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")),
227+
("x86", "bmi1") => Some(LLVMFeature::new("bmi")),
228+
("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")),
229+
("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")),
230+
("aarch64", "rcpc2") => Some(LLVMFeature::new("rcpc-immo")),
231+
("aarch64", "dpb") => Some(LLVMFeature::new("ccpp")),
232+
("aarch64", "dpb2") => Some(LLVMFeature::new("ccdp")),
233+
("aarch64", "frintts") => Some(LLVMFeature::new("fptoint")),
234+
("aarch64", "fcma") => Some(LLVMFeature::new("complxnum")),
235+
("aarch64", "pmuv3") => Some(LLVMFeature::new("perfmon")),
236+
("aarch64", "paca") => Some(LLVMFeature::new("pauth")),
237+
("aarch64", "pacg") => Some(LLVMFeature::new("pauth")),
238+
("aarch64", "sve-b16b16") => Some(LLVMFeature::new("b16b16")),
239+
("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")),
239240
// Rust ties fp and neon together.
240241
("aarch64", "neon") => {
241-
LLVMFeature::with_dependency("neon", TargetFeatureFoldStrength::Both("fp-armv8"))
242+
Some(LLVMFeature::with_dependency("neon", TargetFeatureFoldStrength::Both("fp-armv8")))
242243
}
243244
// In LLVM neon implicitly enables fp, but we manually enable
244245
// neon when a feature only implicitly enables fp
245-
("aarch64", "fhm") => LLVMFeature::new("fp16fml"),
246-
("aarch64", "fp16") => LLVMFeature::new("fullfp16"),
246+
("aarch64", "fhm") => Some(LLVMFeature::new("fp16fml")),
247+
("aarch64", "fp16") => Some(LLVMFeature::new("fullfp16")),
248+
// Filter out features that are not supported by the current LLVM version
249+
("aarch64", "faminmax") if get_version().0 < 18 => None,
250+
("aarch64", "fp8") if get_version().0 < 18 => None,
251+
("aarch64", "fp8dot2") if get_version().0 < 18 => None,
252+
("aarch64", "fp8dot4") if get_version().0 < 18 => None,
253+
("aarch64", "fp8fma") if get_version().0 < 18 => None,
254+
("aarch64", "fpmr") if get_version().0 != 18 => None,
255+
("aarch64", "lut") if get_version().0 < 18 => None,
256+
("aarch64", "sme-f8f16") if get_version().0 < 18 => None,
257+
("aarch64", "sme-f8f32") if get_version().0 < 18 => None,
258+
("aarch64", "sme-fa64") if get_version().0 < 18 => None,
259+
("aarch64", "sme-lutv2") if get_version().0 < 18 => None,
260+
("aarch64", "ssve-fp8dot2") if get_version().0 < 18 => None,
261+
("aarch64", "ssve-fp8dot4") if get_version().0 < 18 => None,
262+
("aarch64", "ssve-fp8fma") if get_version().0 < 18 => None,
263+
("aarch64", "v9.5a") if get_version().0 < 18 => None,
247264
// In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single feature called
248265
// `fast-unaligned-access`. In LLVM 19, it was split back out.
249266
("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => {
250-
LLVMFeature::new("fast-unaligned-access")
267+
Some(LLVMFeature::new("fast-unaligned-access"))
251268
}
252269
// For LLVM 18, enable the evex512 target feature if a avx512 target feature is enabled.
253270
("x86", s) if get_version().0 >= 18 && s.starts_with("avx512") => {
254-
LLVMFeature::with_dependency(s, TargetFeatureFoldStrength::EnableOnly("evex512"))
271+
Some(LLVMFeature::with_dependency(s, TargetFeatureFoldStrength::EnableOnly("evex512")))
255272
}
256-
(_, s) => LLVMFeature::new(s),
273+
(_, s) => Some(LLVMFeature::new(s)),
257274
}
258275
}
259276

@@ -293,13 +310,17 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
293310
return true;
294311
}
295312
// check that all features in a given smallvec are enabled
296-
for llvm_feature in to_llvm_features(sess, feature) {
297-
let cstr = SmallCStr::new(llvm_feature);
298-
if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } {
299-
return false;
313+
if let Some(feat) = to_llvm_features(sess, feature) {
314+
for llvm_feature in feat {
315+
let cstr = SmallCStr::new(llvm_feature);
316+
if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } {
317+
return false;
318+
}
300319
}
320+
true
321+
} else {
322+
false
301323
}
302-
true
303324
})
304325
.map(|(feature, _, _)| Symbol::intern(feature)),
305326
);
@@ -388,9 +409,9 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach
388409
.target
389410
.supported_target_features()
390411
.iter()
391-
.map(|(feature, _gate, _implied)| {
412+
.filter_map(|(feature, _gate, _implied)| {
392413
// LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings.
393-
let llvm_feature = to_llvm_features(sess, *feature).llvm_feature_name;
414+
let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name;
394415
let desc =
395416
match llvm_target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok() {
396417
Some(index) => {
@@ -400,7 +421,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach
400421
None => "",
401422
};
402423

403-
(*feature, desc)
424+
Some((*feature, desc))
404425
})
405426
.collect::<Vec<_>>();
406427

@@ -597,7 +618,7 @@ pub(crate) fn global_llvm_features(
597618
if feature_state.is_none() {
598619
let rust_feature =
599620
supported_features.iter().find_map(|&(rust_feature, _, _)| {
600-
let llvm_features = to_llvm_features(sess, rust_feature);
621+
let llvm_features = to_llvm_features(sess, rust_feature)?;
601622
if llvm_features.contains(feature)
602623
&& !llvm_features.contains(rust_feature)
603624
{
@@ -643,7 +664,7 @@ pub(crate) fn global_llvm_features(
643664
// passing requests down to LLVM. This means that all in-language
644665
// features also work on the command line instead of having two
645666
// different names when the LLVM name and the Rust name differ.
646-
let llvm_feature = to_llvm_features(sess, feature);
667+
let llvm_feature = to_llvm_features(sess, feature)?;
647668

648669
Some(
649670
std::iter::once(format!(
@@ -693,6 +714,9 @@ fn backend_feature_name<'a>(sess: &Session, s: &'a str) -> Option<&'a str> {
693714
let feature = s
694715
.strip_prefix(&['+', '-'][..])
695716
.unwrap_or_else(|| sess.dcx().emit_fatal(InvalidTargetFeaturePrefix { feature: s }));
717+
if s.is_empty() {
718+
return None;
719+
}
696720
// Rustc-specific feature requests like `+crt-static` or `-crt-static`
697721
// are not passed down to LLVM.
698722
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {

0 commit comments

Comments
 (0)