From 733daee361a47583ace352b2030c802e21898810 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 22 Sep 2026 13:16:35 +1000 Subject: [PATCH] Use fewer FFI calls to check the target CPU's features The existing code makes a separate FFI call to `MCSubtargetInfo::checkFeatures` for each feature-dependency of the feature being checked, and also performs string-manipulation on the C++ side to add a leading '+' to each feature name. What we can do instead is prepare a single string on the Rust side in the form `"+foo,+bar,+baz,"` for each Rust target feature, and pass that string to LLVM. LLVM already knows how to check that all of the listed features are present. (The trailing comma is not a problem, because `SubtargetFeatures::Split` will automatically discard empty substrings after splitting on commas.) --- .../src/back/owned_mc_subtarget_info.rs | 17 +++++++++++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 5 +++-- compiler/rustc_codegen_llvm/src/llvm_util.rs | 14 +++----------- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 7 ++++--- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs b/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs index f57368e755add..1a85f1458d258 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs @@ -4,7 +4,7 @@ use std::ptr::NonNull; use rustc_data_structures::small_c_str::SmallCStr; use crate::diagnostics::LlvmError; -use crate::llvm; +use crate::{llvm, llvm_util}; /// Responsible for safely creating and disposing llvm::MCSubtargetInfo via ffi functions. /// Not cloneable as there is no clone function for llvm::MCSubtargetInfo. @@ -28,11 +28,20 @@ impl OwnedMCSubtargetInfo { .ok_or_else(|| LlvmError::CreateMCSubtargetInfo { triple: SmallCStr::from(triple) }) } - pub(crate) fn has_feature(&self, feature: &CStr) -> bool { - // SAFETY: `new` ensures we have a valid pointer created by + pub(crate) fn has_features(&self, features: llvm_util::LLVMFeature<'_>) -> bool { + // Convert to a feature string expected by LLVM's `SubtargetFeatures`. + // All of the required LLVM features must be enabled. + // (The trailing comma is OK, as `SubtargetFeatures::Split` discards empty substrings.) + let features = features.into_iter().flat_map(|feat| ["+", feat, ","]).collect::(); + + // SAFETY: `info_unique` is a valid pointer created by // `llvm::LLVMRustCreateMCSubtargetInfo`. unsafe { - llvm::LLVMRustMCSubtargetInfoHasFeature(self.info_unique.as_ref(), feature.as_ptr()) + llvm::LLVMRustMCSubtargetInfoCheckFeatures( + self.info_unique.as_ref(), + features.as_ptr(), + features.len(), + ) } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 07381745afe84..655a79e341bd1 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2422,9 +2422,10 @@ unsafe extern "C" { Features: *const c_char, ) -> *mut MCSubtargetInfo; - pub(crate) fn LLVMRustMCSubtargetInfoHasFeature( + pub(crate) fn LLVMRustMCSubtargetInfoCheckFeatures( MCInfo: &MCSubtargetInfo, - Feature: *const c_char, + Features: *const c_uchar, // See "PTR_LEN_STR". + FeaturesLen: usize, ) -> bool; pub(crate) fn LLVMRustDisposeMCSubtargetInfo(MCInfo: ptr::NonNull); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 5c0ad95fc4e43..9e37205f230ac 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -361,17 +361,9 @@ pub(crate) fn target_config(sess: &EarlySession) -> TargetConfig { // do *not* consider the `-Ctarget-feature`s here (that's why we passed `for_cfg: true` // to `global_llvm_features` above) because that will be handled later in // `internal_target_features`. - if let Some(feat) = to_llvm_features(&sess.target, feature) { - // All the LLVM features this expands to must be enabled. - for llvm_feature in feat { - let cstr = SmallCStr::new(llvm_feature); - // `has_feature` is moderately expensive. On targets with many - // features (e.g. x86) these calls take a non-trivial fraction of runtime - // when compiling very small programs. - if !mc_subtarget_info.has_feature(&cstr) { - return false; - } - } + if let Some(feat) = to_llvm_features(&sess.target, feature) + && mc_subtarget_info.has_features(feat) + { true } else { false diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 35d5597795a9e..beb63ad61493c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -107,9 +107,10 @@ LLVMRustCreateMCSubtargetInfo(const char *TripleStr, const char *CPU, #endif } -extern "C" bool LLVMRustMCSubtargetInfoHasFeature(MCSubtargetInfo *MCInfo, - const char *Feature) { - return MCInfo->checkFeatures(std::string("+") + Feature); +extern "C" bool LLVMRustMCSubtargetInfoCheckFeatures(MCSubtargetInfo *MCInfo, + const char *Features, + size_t FeaturesLen) { + return MCInfo->checkFeatures(StringRef{Features, FeaturesLen}); } extern "C" void LLVMRustDisposeMCSubtargetInfo(MCSubtargetInfo *MCInfo) {