[clang] [llvm] [FMV][AIX] Implement target_clones part 2 (target-features) (PR #206786)
Wael Yehia via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 14 20:47:57 PDT 2026
https://github.com/w2yehia updated https://github.com/llvm/llvm-project/pull/206786
>From b058db3fd0cfbac359c7866312d7d62b876ff5ae Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Mon, 29 Jun 2026 17:28:53 +0000
Subject: [PATCH 01/12] proto 1 by bob
add TODO in Sema/SemaPPC.cpp
add Sema test
add tests and remove isel from runtime-detected features since it's always available
remove cmpb and fprnd from runtime-detected features since it's always available
fix codegen for no-FEATURE
documentation
priority of features in powerXX should be between cpu=powerXX and cpu=power(XX+1)
clang-format
---
clang/include/clang/Basic/AttrDocs.td | 8 +-
.../clang/Basic/DiagnosticSemaKinds.td | 4 +
clang/include/clang/Basic/TargetInfo.h | 12 ++
clang/lib/AST/ASTContext.cpp | 7 +-
clang/lib/Basic/Targets/PPC.cpp | 132 +++++++++++++++++-
clang/lib/Basic/Targets/PPC.h | 13 +-
clang/lib/CodeGen/CodeGenFunction.cpp | 55 ++++++--
clang/lib/CodeGen/Targets/PPC.cpp | 28 +++-
clang/lib/Sema/SemaPPC.cpp | 55 +++++++-
.../CodeGen/PowerPC/attr-target-clones-mma.c | 16 +++
.../test/CodeGen/PowerPC/attr-target-clones.c | 89 ++++++++++++
clang/test/Sema/PowerPC/attr-target-clones.c | 99 ++++++++++++-
12 files changed, 482 insertions(+), 36 deletions(-)
create mode 100644 clang/test/CodeGen/PowerPC/attr-target-clones-mma.c
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 05e4cb0870652..afee64cd668e7 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3434,8 +3434,12 @@ For backward compatibility with earlier Clang releases, a function alias with an
``.ifunc`` suffix is also emitted. The ``.ifunc`` suffixed symbol is a deprecated
feature and support for it may be removed in the future.
-For PowerPC targets, ``target_clones`` is supported on AIX only. Only CPU
-(specified as ``cpu=CPU``) and ``default`` options are allowed. IFUNC is supported
+For PowerPC targets, ``target_clones`` is supported on AIX only. The attribute
+contains comma-separated strings of one of:
+(a) ``default``, (b) ``cpu=CPU``, (c) ``FEATURE`` or ``no-FEATURE``.
+The minimum CPU supported is ``pwr7`` (long spelling such as ``power7`` is accepted).
+The list of target features is a subset of what's allowed on ``target``, limited
+to what is detectable at runtime using ``__builtin_cpu_supports``. IFUNC is supported
on AIX in Clang, so dispatch is implemented similar to other targets using IFUNC.
An FMV function that is only declared in a translation unit is treated as a
non-FMV. The resolver and the function clones are given internal linkage.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 09951730e1570..c151b47255ba6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11556,6 +11556,10 @@ def err_ppc_invalid_test_data_class_type : Error<
"expected a 'float', 'double' or '__float128' for the first argument">;
def err_ppc_invalid_arg_type : Error<
"argument %0 must be of type %1">;
+def err_ppc_feature_no_runtime_detection : Error<
+ "feature '%0' cannot be used with 'target_clones' because it has no "
+ "runtime detection; use 'target' attribute instead">;
+
def err_x86_builtin_invalid_rounding : Error<
"invalid rounding argument">;
def err_x86_builtin_invalid_scale : Error<
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 3aba4d261a651..aecb7ea48dc15 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1462,6 +1462,18 @@ class TargetInfo : public TransferrableTargetInfo,
return true;
}
+ /// Validate feature name for target_clones attribute (subset with runtime
+ /// detection) Default implementation delegates to isValidFeatureName
+ virtual bool isValidClonesFeatureName(StringRef Feature) const {
+ return isValidFeatureName(Feature);
+ }
+
+ /// Get __builtin_cpu_supports() argument for a feature
+ /// Returns empty string if feature has no runtime detection
+ virtual StringRef getBuiltinCpuSupportsName(StringRef Feature) const {
+ return "";
+ }
+
/// Returns true if feature has an impact on target code
/// generation.
virtual bool doesFeatureAffectCodeGen(StringRef Feature) const {
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 2228811546c0f..8635140f6829a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15390,8 +15390,11 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());
if (VersionStr.starts_with("cpu="))
TargetCPU = VersionStr.drop_front(sizeof("cpu=") - 1);
- else
- assert(VersionStr == "default");
+ else if (VersionStr != "default") {
+ // Handle feature strings
+ ParsedTargetAttr ParsedAttr = Target->parseTargetAttr(VersionStr);
+ Features = ParsedAttr.Features;
+ }
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
} else {
std::vector<std::string> Features;
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index 1ecb474c1ede8..143c1ef1cc00f 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -727,17 +727,54 @@ llvm::APInt PPCTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
return llvm::APInt(32, 0);
assert(Features.size() == 1 && "one feature/cpu per clone on PowerPC");
ParsedTargetAttr ParsedAttr = parseTargetAttr(Features[0]);
+
+ // Priority scheme: Features requiring POWERXX are higher than cpu=pwrXX
+ // but lower than cpu=pwr(XX+1). This ensures proper version selection.
+ // Example: mma (POWER10 feature) > cpu=pwr10 > power9-vector (POWER9 feature)
+
if (!ParsedAttr.CPU.empty()) {
int Priority = llvm::StringSwitch<int>(ParsedAttr.CPU)
- .Case("pwr7", 1)
- .Case("pwr8", 2)
- .Case("pwr9", 3)
- .Case("pwr10", 4)
- .Case("pwr11", 5)
+ .Case("pwr7", 100)
+ .Case("pwr8", 200)
+ .Case("pwr9", 300)
+ .Case("pwr10", 400)
+ .Case("pwr11", 500)
.Default(0);
return llvm::APInt(32, Priority);
}
- assert(false && "unimplemented");
+
+ // Feature strings: priority between cpu=pwrN and cpu=pwr(N+1)
+ if (!ParsedAttr.Features.empty()) {
+ StringRef Feature = ParsedAttr.Features[0];
+ // Remove leading '+' or '-'
+ if (Feature.starts_with("+") || Feature.starts_with("-"))
+ Feature = Feature.drop_front(1);
+
+ int Priority = llvm::StringSwitch<int>(Feature)
+ // POWER10 features (between pwr10=400 and pwr11=500)
+ .Case("mma", 419)
+ .Case("paired-vector-memops", 418)
+ .Case("pcrel", 417)
+ .Case("power10-vector", 416)
+ .Case("prefixed", 415)
+ // POWER9 features (between pwr9=300 and pwr10=400)
+ .Case("float128", 312)
+ .Case("power9-vector", 311)
+ // POWER8 features (between pwr8=200 and pwr9=300)
+ .Case("crypto", 214)
+ .Case("direct-move", 213)
+ .Case("power8-vector", 212)
+ .Case("htm", 211)
+ // POWER7 features (between pwr7=100 and pwr8=200)
+ .Case("popcntd", 112)
+ .Case("vsx", 111)
+ // Base features: 50-99 (below pwr7=100)
+ .Case("altivec", 50)
+ .Default(0);
+
+ return llvm::APInt(32, Priority);
+ }
+
return llvm::APInt(32, 0);
}
@@ -830,6 +867,89 @@ void PPCTargetInfo::fillValidCPUList(SmallVectorImpl<StringRef> &Values) const {
llvm::PPC::fillValidCPUList(Values);
}
+bool PPCTargetInfo::isValidFeatureName(StringRef Name) const {
+ // All 28 PPC features valid for target attribute
+ return llvm::StringSwitch<bool>(Name)
+ // Features with runtime detection (valid for target_clones)
+ .Case("altivec", true)
+ .Case("htm", true)
+ .Case("mma", true)
+ .Case("vsx", true)
+ .Case("crypto", true)
+ .Case("direct-move", true)
+ .Case("float128", true)
+ .Case("paired-vector-memops", true)
+ .Case("pcrel", true)
+ .Case("popcntd", true)
+ .Case("power8-vector", true)
+ .Case("power9-vector", true)
+ .Case("power10-vector", true)
+ .Case("prefixed", true)
+ // Features without runtime checks (NOT valid for target_clones)
+ .Case("aix-shared-lib-tls-model-opt", true)
+ .Case("aix-small-local-dynamic-tls", true)
+ .Case("aix-small-local-exec-tls", true)
+ .Case("cmpb", true)
+ .Case("crbits", true)
+ .Case("fprnd", true)
+ .Case("invariant-function-descriptors", true)
+ .Case("isel", true)
+ .Case("longcall", true)
+ .Case("mfcrf", true)
+ .Case("mfocrf", true)
+ .Case("privileged", true)
+ .Case("rop-protect", true)
+ .Case("secure-plt", true)
+ .Default(false);
+}
+
+bool PPCTargetInfo::isValidClonesFeatureName(StringRef Name) const {
+ // Only 14 features with runtime detection are valid for target_clones
+ return llvm::StringSwitch<bool>(Name)
+ // Direct mappings (4 features)
+ .Case("altivec", true)
+ .Case("htm", true)
+ .Case("mma", true)
+ .Case("vsx", true)
+ // ISA level mappings (10 features)
+ .Case("crypto", true)
+ .Case("direct-move", true)
+ .Case("float128", true)
+ .Case("paired-vector-memops", true)
+ .Case("pcrel", true)
+ .Case("popcntd", true)
+ .Case("power8-vector", true)
+ .Case("power9-vector", true)
+ .Case("power10-vector", true)
+ .Case("prefixed", true)
+ .Default(false);
+}
+
+StringRef
+PPCTargetInfo::getBuiltinCpuSupportsName(StringRef FeatureName) const {
+ // Map feature names to __builtin_cpu_supports() strings
+ // Only returns non-empty for features with runtime detection
+ return llvm::StringSwitch<StringRef>(FeatureName)
+ // Direct mappings (4 features)
+ .Case("altivec", "altivec")
+ .Case("htm", "htm")
+ .Case("mma", "mma")
+ .Case("vsx", "vsx")
+ // ISA level mappings (10 features)
+ .Case("popcntd", "arch_2_06")
+ .Case("crypto", "arch_2_07")
+ .Case("direct-move", "arch_2_07")
+ .Case("power8-vector", "arch_2_07")
+ .Case("float128", "arch_3_00")
+ .Case("power9-vector", "arch_3_00")
+ .Case("paired-vector-memops", "arch_3_1")
+ .Case("pcrel", "arch_3_1")
+ .Case("power10-vector", "arch_3_1")
+ .Case("prefixed", "arch_3_1")
+ // Features without runtime checks return empty string
+ .Default("");
+}
+
void PPCTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts,
const TargetInfo *Aux) {
if (HasAltivec)
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index 22880e5a04a3f..e46daaa85a993 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -98,7 +98,18 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {
bool isValidCPUName(StringRef Name) const override;
void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
- bool setCPU(StringRef Name) override {
+ // Validate feature name for target attribute (all 28 features)
+ bool isValidFeatureName(StringRef Name) const override;
+
+ // Validate feature name for target_clones (only 17 features with runtime
+ // detection)
+ bool isValidClonesFeatureName(StringRef Name) const;
+
+ // Get __builtin_cpu_supports() argument for a feature (returns empty string
+ // if no runtime check)
+ StringRef getBuiltinCpuSupportsName(StringRef FeatureName) const;
+
+ bool setCPU(StringRef &Name) override {
bool CPUKnown = isValidCPUName(Name);
if (CPUKnown) {
CPU = Name;
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 80c5268495fe6..ed0b2a4cb2779 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3137,18 +3137,49 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
assert(RO.Features.size() == 1 &&
"for now one feature requirement per version");
- assert(RO.Features[0].starts_with("cpu="));
- StringRef CPU = RO.Features[0].split("=").second.trim();
- StringRef Feature = llvm::StringSwitch<StringRef>(CPU)
- .Case("pwr7", "arch_2_06")
- .Case("pwr8", "arch_2_07")
- .Case("pwr9", "arch_3_00")
- .Case("pwr10", "arch_3_1")
- .Case("pwr11", "arch_3_1")
- .Default("error");
-
- llvm::Value *Condition = EmitPPCBuiltinCpu(
- Builtin::BI__builtin_cpu_supports, Builder.getInt1Ty(), Feature);
+ StringRef FeatureStr = RO.Features[0];
+ StringRef BuiltinCpuSupportsArg;
+ bool IsNegated = false;
+
+ if (FeatureStr.starts_with("cpu=")) {
+ // CPU specification - map to ISA level
+ StringRef CPU = FeatureStr.split("=").second.trim();
+ BuiltinCpuSupportsArg = llvm::StringSwitch<StringRef>(CPU)
+ .Case("pwr7", "arch_2_06")
+ .Case("pwr8", "arch_2_07")
+ .Case("pwr9", "arch_3_00")
+ .Case("pwr10", "arch_3_1")
+ .Case("pwr11", "arch_3_1")
+ .Default("error");
+ } else {
+ // Feature string - check for "no-" negation prefix
+ StringRef BaseFeature = FeatureStr;
+
+ // Feature strings arrive here already normalized:
+ // - Positive features: just the name (e.g., "altivec")
+ // - Negated features: "no-" prefix (e.g., "no-altivec")
+ if (BaseFeature.starts_with("no-")) {
+ IsNegated = true;
+ BaseFeature = BaseFeature.drop_front(3);
+ }
+
+ BuiltinCpuSupportsArg =
+ getTarget().getBuiltinCpuSupportsName(BaseFeature);
+
+ // All features in target_clones must have runtime detection
+ assert(!BuiltinCpuSupportsArg.empty() &&
+ "feature without runtime detection should have been rejected in "
+ "Sema");
+ }
+
+ llvm::Value *Condition =
+ EmitPPCBuiltinCpu(Builtin::BI__builtin_cpu_supports,
+ Builder.getInt1Ty(), BuiltinCpuSupportsArg);
+
+ // Negate the condition if this is a negated feature
+ if (IsNegated) {
+ Condition = Builder.CreateNot(Condition, "neg");
+ }
llvm::BasicBlock *ThenBlock = createBasicBlock("if.version", Resolver);
CurBlock = createBasicBlock("if.else", Resolver);
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index ab069bfbd1b51..f0c1237f8588c 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -157,7 +157,33 @@ void AIXABIInfo::appendAttributeMangling(StringRef AttrStr,
return;
}
- assert(0 && "specifying target features on an FMV is unsupported on AIX");
+ // Handle feature strings
+ if (!Info.Features.empty()) {
+ assert(Info.Features.size() == 1 && "one feature per version for now");
+ StringRef Feature = Info.Features[0];
+ std::string MangledFeature;
+
+ // Handle negation prefix "no-" specially - convert to "no_"
+ if (Feature.starts_with("no-")) {
+ MangledFeature = "no_";
+ Feature = Feature.drop_front(3);
+ } else if (Feature.starts_with("+")) {
+ // Remove leading '+' for positive features
+ Feature = Feature.drop_front(1);
+ } else if (Feature.starts_with("-")) {
+ // Leading '-' means negation, convert to "no_"
+ MangledFeature = "no_";
+ Feature = Feature.drop_front(1);
+ }
+
+ // Append the base feature name and replace hyphens with underscores
+ MangledFeature += Feature.str();
+ std::replace(MangledFeature.begin(), MangledFeature.end(), '-', '_');
+ Out << "." << MangledFeature;
+ return;
+ }
+
+ llvm_unreachable("Invalid target_clones parameter");
}
class AIXTargetCodeGenInfo : public TargetCodeGenInfo {
diff --git a/clang/lib/Sema/SemaPPC.cpp b/clang/lib/Sema/SemaPPC.cpp
index 8a594fc86dea6..be76bac5de6c8 100644
--- a/clang/lib/Sema/SemaPPC.cpp
+++ b/clang/lib/Sema/SemaPPC.cpp
@@ -618,21 +618,32 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
const StringRef Param = Params[I].trim();
const SourceLocation &Loc = Locs[I];
- if (Param.empty() || Param.ends_with(','))
- return Diag(Loc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << "" << TargetClones;
-
if (Param.contains(','))
HasComma = true;
StringRef LHS;
StringRef RHS = Param;
+ // TODO: simplify the logic to diagnose empty strings
+ bool checkTrailingEmpty = false;
do {
std::tie(LHS, RHS) = RHS.split(',');
LHS = LHS.trim();
+
+ // After processing last non-empty item, check if we need to process
+ // trailing empty
+ if (RHS.empty() && !LHS.empty() && Param.ends_with(',')) {
+ checkTrailingEmpty = true;
+ }
+
const SourceLocation &CurLoc =
Loc.getLocWithOffset(LHS.data() - Param.data());
+ // Check for empty string (from trailing comma, leading comma, or ",,")
+ if (LHS.empty()) {
+ return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+ << Unknown << None << "" << TargetClones;
+ }
+
if (LHS.starts_with("cpu=")) {
StringRef CPUStr = LHS.drop_front(sizeof("cpu=") - 1);
if (!TargetInfo.isValidCPUName(CPUStr))
@@ -644,9 +655,30 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
} else if (LHS == "default") {
HasDefault = true;
} else {
- // it's a feature string, but not supported yet.
- return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << LHS << TargetClones;
+ // Handle feature strings
+ StringRef FeatureName = LHS;
+ bool IsNegated = false;
+
+ // Check for negation prefix
+ if (FeatureName.starts_with("no-")) {
+ IsNegated = true;
+ FeatureName = FeatureName.drop_front(3);
+ }
+
+ // First check if it's a valid feature name at all
+ if (!TargetInfo.isValidFeatureName(FeatureName)) {
+ return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+ << Unknown << None << LHS << TargetClones;
+ }
+
+ // Check if feature is valid for target_clones (has runtime detection)
+ // Use virtual method that defaults to isValidFeatureName for non-PPC
+ // targets
+ if (!TargetInfo.isValidClonesFeatureName(FeatureName)) {
+ // Feature is valid for target attribute but not target_clones
+ return Diag(CurLoc, diag::err_ppc_feature_no_runtime_detection)
+ << FeatureName;
+ }
}
SmallString<64> CPU;
if (LHS.starts_with("cpu=")) {
@@ -660,6 +692,15 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
continue;
}
NewParams.push_back(LHS);
+
+ // If we just processed the last item and there's a trailing comma,
+ // do one more iteration to catch the empty string
+ if (checkTrailingEmpty) {
+ LHS = "";
+ const SourceLocation &EmptyCurLoc = Loc.getLocWithOffset(Param.size());
+ return Diag(EmptyCurLoc, diag::warn_unsupported_target_attribute)
+ << Unknown << None << "" << TargetClones;
+ }
} while (!RHS.empty());
}
if (HasComma && Params.size() > 1)
diff --git a/clang/test/CodeGen/PowerPC/attr-target-clones-mma.c b/clang/test/CodeGen/PowerPC/attr-target-clones-mma.c
new file mode 100644
index 0000000000000..8e069eb17d64c
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/attr-target-clones-mma.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -target-cpu pwr10 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -target-cpu pwr10 -emit-llvm %s -o - | FileCheck %s
+
+// Test MMA feature which requires pwr10
+int __attribute__((target_clones("mma", "default")))
+foo_mma(void) { return 0; }
+// CHECK: define internal {{.*}}i32 @foo_mma.mma()
+// CHECK: define internal {{.*}}i32 @foo_mma.default()
+// CHECK: define internal ptr @foo_mma.resolver()
+// if (__builtin_cpu_supports("mma")) return &foo_mma.mma;
+// CHECK: %[[#MMA:]] = call i64 @getsystemcfg(i32 62)
+// CHECK-NEXT: icmp ugt i64 %[[#MMA]], 0
+// CHECK: ret ptr @foo_mma.mma
+// CHECK: ret ptr @foo_mma.default
+
+// CHECK: declare i64 @getsystemcfg(i32)
diff --git a/clang/test/CodeGen/PowerPC/attr-target-clones.c b/clang/test/CodeGen/PowerPC/attr-target-clones.c
index c8cc9f2204b65..40f5a31f693e2 100644
--- a/clang/test/CodeGen/PowerPC/attr-target-clones.c
+++ b/clang/test/CodeGen/PowerPC/attr-target-clones.c
@@ -134,6 +134,95 @@ foo_priority(int x) { return x & (x - 1); }
// CHECK: ret ptr @foo_priority.default
+// Test feature-based target_clones
+int __attribute__((target_clones("altivec", "default")))
+foo_altivec(void) { return 0; }
+// CHECK: define internal {{.*}}i32 @foo_altivec.altivec()
+// CHECK: define internal {{.*}}i32 @foo_altivec.default()
+// CHECK: define internal ptr @foo_altivec.resolver()
+// if (__builtin_cpu_supports("altivec")) return &foo_altivec.altivec;
+// CHECK: %[[#ALTIVEC:]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @_system_configuration, {{i32|i64}} 204)
+// CHECK-NEXT: icmp ugt i32 %[[#ALTIVEC]], 0
+// CHECK: ret ptr @foo_altivec.altivec
+// CHECK: ret ptr @foo_altivec.default
+
+int __attribute__((target_clones("vsx", "default")))
+foo_vsx(void) { return 0; }
+// CHECK: define internal {{.*}}i32 @foo_vsx.vsx()
+// CHECK: define internal {{.*}}i32 @foo_vsx.default()
+// CHECK: define internal ptr @foo_vsx.resolver()
+// if (__builtin_cpu_supports("vsx")) return &foo_vsx.vsx;
+// CHECK: %[[#VSX:]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @_system_configuration, {{i32|i64}} 204)
+// CHECK-NEXT: icmp ugt i32 %[[#VSX]], 1
+// CHECK: ret ptr @foo_vsx.vsx
+// CHECK: ret ptr @foo_vsx.default
+
+int __attribute__((target_clones("htm", "default")))
+foo_htm(void) { return 0; }
+// CHECK: define internal {{.*}}i32 @foo_htm.htm()
+// CHECK: define internal {{.*}}i32 @foo_htm.default()
+// CHECK: define internal ptr @foo_htm.resolver()
+// if (__builtin_cpu_supports("htm")) return &foo_htm.htm;
+// CHECK: %[[#HTM:]] = call i64 @getsystemcfg(i32 59)
+// CHECK-NEXT: icmp ugt i64 %[[#HTM]], 0
+// CHECK: ret ptr @foo_htm.htm
+// CHECK: ret ptr @foo_htm.default
+
+
+
+// Test multiple features with priority ordering (random source order)
+// Source order: altivec, power8-vector, cpu=pwr11, vsx, power9-vector, default
+// Resolver order by priority: cpu=pwr11 (500) > power9-vector (311) > power8-vector (212) > vsx (111) > altivec (50) > default (0)
+int __attribute__((target_clones("altivec", "power8-vector", "cpu=pwr11", "vsx", "power9-vector", "default")))
+foo_multi_features(void) { return 0; }
+// CHECK: define internal {{.*}}i32 @foo_multi_features.altivec()
+// CHECK: define internal {{.*}}i32 @foo_multi_features.power8_vector()
+// CHECK: define internal {{.*}}i32 @foo_multi_features.cpu_pwr11() #[[#ATTR_P11]]
+// CHECK: define internal {{.*}}i32 @foo_multi_features.vsx()
+// CHECK: define internal {{.*}}i32 @foo_multi_features.power9_vector()
+// CHECK: define internal {{.*}}i32 @foo_multi_features.default()
+// CHECK: define internal ptr @foo_multi_features.resolver()
+// Resolver checks in priority order (highest first):
+// if (__builtin_cpu_supports("arch_3_1")) return &foo_multi_features.cpu_pwr11;
+// CHECK: load i32, ptr getelementptr inbounds nuw (i8, ptr @_system_configuration, {{i32|i64}} 4)
+// CHECK-NEXT: icmp uge i32 {{.*}}, 262144
+// CHECK: ret ptr @foo_multi_features.cpu_pwr11
+// if (__builtin_cpu_supports("arch_3_00")) return &foo_multi_features.power9_vector;
+// CHECK: load i32, ptr getelementptr inbounds nuw (i8, ptr @_system_configuration, {{i32|i64}} 4)
+// CHECK-NEXT: icmp uge i32 {{.*}}, 131072
+// CHECK: ret ptr @foo_multi_features.power9_vector
+// if (__builtin_cpu_supports("arch_2_07")) return &foo_multi_features.power8_vector;
+// CHECK: load i32, ptr getelementptr inbounds nuw (i8, ptr @_system_configuration, {{i32|i64}} 4)
+// CHECK-NEXT: icmp uge i32 {{.*}}, 65536
+// CHECK: ret ptr @foo_multi_features.power8_vector
+// if (__builtin_cpu_supports("vsx")) return &foo_multi_features.vsx;
+// CHECK: %[[#VSX2:]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @_system_configuration, {{i32|i64}} 204)
+// CHECK-NEXT: icmp ugt i32 %[[#VSX2]], 1
+// CHECK: ret ptr @foo_multi_features.vsx
+// if (__builtin_cpu_supports("altivec")) return &foo_multi_features.altivec;
+// CHECK: %[[#ALTIVEC2:]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @_system_configuration, {{i32|i64}} 204)
+// CHECK-NEXT: icmp ugt i32 %[[#ALTIVEC2]], 0
+// CHECK: ret ptr @foo_multi_features.altivec
+// CHECK: ret ptr @foo_multi_features.default
+
+// Test negated feature (no-altivec) - should negate the condition
+int __attribute__((target_clones("no-altivec", "default")))
+foo_no_altivec(void) { return 0; }
+// CHECK: define internal {{.*}}i32 @foo_no_altivec.no_altivec()
+// CHECK: define internal {{.*}}i32 @foo_no_altivec.default()
+// CHECK: define internal ptr @foo_no_altivec.resolver()
+// if (!__builtin_cpu_supports("altivec")) return &foo_no_altivec.no_altivec;
+// CHECK: load i32, ptr getelementptr inbounds nuw (i8, ptr @_system_configuration, {{i32|i64}} 204)
+// CHECK-NEXT: icmp ugt i32
+// CHECK-NEXT: %neg = xor i1 {{.*}}, true
+// CHECK-NEXT: br i1 %neg, label %if.version, label %if.else
+// CHECK: if.version:
+// CHECK-NEXT: ret ptr @foo_no_altivec.no_altivec
+// CHECK: if.else:
+// CHECK-NEXT: ret ptr @foo_no_altivec.default
+
+// CHECK: declare i64 @getsystemcfg(i32)
+
// CHECK: attributes #[[#ATTR_P7]] = {{.*}} "target-cpu"="pwr7"
// CHECK: attributes #[[#ATTR_P10]] = {{.*}} "target-cpu"="pwr10"
// CHECK: attributes #[[#ATTR_P11]] = {{.*}} "target-cpu"="pwr11"
diff --git a/clang/test/Sema/PowerPC/attr-target-clones.c b/clang/test/Sema/PowerPC/attr-target-clones.c
index 96acc974320b0..0f078fa2e805c 100644
--- a/clang/test/Sema/PowerPC/attr-target-clones.c
+++ b/clang/test/Sema/PowerPC/attr-target-clones.c
@@ -42,20 +42,20 @@ int __attribute__((target_clones("cpu=pwr9,default"))) redef3(void) { return 1;
int __attribute__((target_clones("cpu=pwr9,cpu=power9", "cpu=power9, default")))
dupes(void) { return 1; }
-// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones("")))
empty_target_1(void);
-// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones(",default")))
empty_target_2(void);
-// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones("default,")))
empty_target_3(void);
-// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones("default, ,cpu=pwr7")))
empty_target_4(void);
-// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones("default,cpu=pwr7", "")))
empty_target_5(void);
@@ -130,3 +130,92 @@ gh173684_empty_attribute_args(void);
// expected-error at +1 {{'target_clones' multiversioning requires a default target}}
void __attribute__((target_clones))
gh173684_empty_attribute_args_2(void);
+
+// TODO: Consider combining some of these tests into fewer test cases with multiple features
+// e.g., target_clones("feature1", "feature2", "feature3", ..., "default") to test
+// feature1, feature2, feature3 all in one declaration instead of separate functions
+
+// Test that all valid feature names are accepted (no diagnostics expected)
+void __attribute__((target_clones("altivec", "default")))
+valid_feature_altivec(void);
+
+void __attribute__((target_clones("vsx", "default")))
+valid_feature_vsx(void);
+
+void __attribute__((target_clones("crypto", "default")))
+valid_feature_crypto(void);
+
+void __attribute__((target_clones("power8-vector", "default")))
+valid_feature_power8_vector(void);
+
+void __attribute__((target_clones("power9-vector", "default")))
+valid_feature_power9_vector(void);
+
+void __attribute__((target_clones("power10-vector", "default")))
+valid_feature_power10_vector(void);
+
+void __attribute__((target_clones("mma", "default")))
+valid_feature_mma(void);
+
+void __attribute__((target_clones("htm", "default")))
+valid_feature_htm(void);
+
+// isel is always available on AIX (no runtime check), so it's not valid for target_clones
+// expected-error at +1 {{feature 'isel' cannot be used with 'target_clones' because it has no runtime detection; use 'target' attribute instead}}
+void __attribute__((target_clones("isel", "default")))
+invalid_feature_isel(void);
+
+// expected-error at +1 {{feature 'isel' cannot be used with 'target_clones' because it has no runtime detection; use 'target' attribute instead}}
+void __attribute__((target_clones("no-isel", "default")))
+invalid_feature_no_isel(void);
+
+// Test that negated valid feature names are accepted (no diagnostics expected)
+void __attribute__((target_clones("no-altivec", "default")))
+valid_feature_no_altivec(void);
+
+void __attribute__((target_clones("no-vsx", "default")))
+valid_feature_no_vsx(void);
+
+void __attribute__((target_clones("no-crypto", "default")))
+valid_feature_no_crypto(void);
+
+void __attribute__((target_clones("no-power8-vector", "default")))
+valid_feature_no_power8_vector(void);
+
+void __attribute__((target_clones("no-power9-vector", "default")))
+valid_feature_no_power9_vector(void);
+
+void __attribute__((target_clones("no-power10-vector", "default")))
+valid_feature_no_power10_vector(void);
+
+void __attribute__((target_clones("no-mma", "default")))
+valid_feature_no_mma(void);
+
+void __attribute__((target_clones("no-htm", "default")))
+valid_feature_no_htm(void);
+
+// Test multiple valid features together (one example with mixing warning)
+// expected-warning at +1 {{mixing 'target_clones' specifier mechanisms is permitted for GCC compatibility}}
+void __attribute__((target_clones("altivec,vsx", "default")))
+valid_multiple_features_1(void);
+
+// Use separate string literals to avoid mixing warning
+void __attribute__((target_clones("vsx", "crypto", "htm", "default")))
+valid_multiple_features_2(void);
+
+void __attribute__((target_clones("power9-vector", "mma", "default")))
+valid_multiple_features_3(void);
+
+// Test mix of features and negations
+void __attribute__((target_clones("altivec", "no-vsx", "default")))
+valid_mixed_features_1(void);
+
+void __attribute__((target_clones("vsx", "no-crypto", "default")))
+valid_mixed_features_2(void);
+
+// Test features with CPU specifications
+void __attribute__((target_clones("altivec", "cpu=pwr8", "default")))
+valid_feature_with_cpu_1(void);
+
+void __attribute__((target_clones("vsx", "crypto", "cpu=pwr9", "default")))
+valid_feature_with_cpu_2(void);
>From 8eabb381f103f5415e72089726d5d42693268d76 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 1 Jul 2026 02:37:23 +0000
Subject: [PATCH 02/12] pcrel is not supported on AIX, reject it from
target/target_clones
---
clang/lib/Basic/Targets/PPC.cpp | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index 143c1ef1cc00f..077b35003e17c 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -754,7 +754,6 @@ llvm::APInt PPCTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
// POWER10 features (between pwr10=400 and pwr11=500)
.Case("mma", 419)
.Case("paired-vector-memops", 418)
- .Case("pcrel", 417)
.Case("power10-vector", 416)
.Case("prefixed", 415)
// POWER9 features (between pwr9=300 and pwr10=400)
@@ -868,9 +867,9 @@ void PPCTargetInfo::fillValidCPUList(SmallVectorImpl<StringRef> &Values) const {
}
bool PPCTargetInfo::isValidFeatureName(StringRef Name) const {
- // All 28 PPC features valid for target attribute
+ if (!getTriple().isOSAIX())
+ return TargetInfo::isValidFeatureName(Name);
return llvm::StringSwitch<bool>(Name)
- // Features with runtime detection (valid for target_clones)
.Case("altivec", true)
.Case("htm", true)
.Case("mma", true)
@@ -879,13 +878,11 @@ bool PPCTargetInfo::isValidFeatureName(StringRef Name) const {
.Case("direct-move", true)
.Case("float128", true)
.Case("paired-vector-memops", true)
- .Case("pcrel", true)
.Case("popcntd", true)
.Case("power8-vector", true)
.Case("power9-vector", true)
.Case("power10-vector", true)
.Case("prefixed", true)
- // Features without runtime checks (NOT valid for target_clones)
.Case("aix-shared-lib-tls-model-opt", true)
.Case("aix-small-local-dynamic-tls", true)
.Case("aix-small-local-exec-tls", true)
@@ -916,7 +913,6 @@ bool PPCTargetInfo::isValidClonesFeatureName(StringRef Name) const {
.Case("direct-move", true)
.Case("float128", true)
.Case("paired-vector-memops", true)
- .Case("pcrel", true)
.Case("popcntd", true)
.Case("power8-vector", true)
.Case("power9-vector", true)
@@ -943,7 +939,6 @@ PPCTargetInfo::getBuiltinCpuSupportsName(StringRef FeatureName) const {
.Case("float128", "arch_3_00")
.Case("power9-vector", "arch_3_00")
.Case("paired-vector-memops", "arch_3_1")
- .Case("pcrel", "arch_3_1")
.Case("power10-vector", "arch_3_1")
.Case("prefixed", "arch_3_1")
// Features without runtime checks return empty string
>From 28efa5cc4c027d70c5b14e95ca90e3234d7820ce Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 1 Jul 2026 03:01:53 +0000
Subject: [PATCH 03/12] merge attr-target-clones-mma.c into
attr-target-clones.c
---
.../CodeGen/PowerPC/attr-target-clones-mma.c | 16 ----------------
clang/test/CodeGen/PowerPC/attr-target-clones.c | 14 +++++++++++++-
2 files changed, 13 insertions(+), 17 deletions(-)
delete mode 100644 clang/test/CodeGen/PowerPC/attr-target-clones-mma.c
diff --git a/clang/test/CodeGen/PowerPC/attr-target-clones-mma.c b/clang/test/CodeGen/PowerPC/attr-target-clones-mma.c
deleted file mode 100644
index 8e069eb17d64c..0000000000000
--- a/clang/test/CodeGen/PowerPC/attr-target-clones-mma.c
+++ /dev/null
@@ -1,16 +0,0 @@
-// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -target-cpu pwr10 -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -target-cpu pwr10 -emit-llvm %s -o - | FileCheck %s
-
-// Test MMA feature which requires pwr10
-int __attribute__((target_clones("mma", "default")))
-foo_mma(void) { return 0; }
-// CHECK: define internal {{.*}}i32 @foo_mma.mma()
-// CHECK: define internal {{.*}}i32 @foo_mma.default()
-// CHECK: define internal ptr @foo_mma.resolver()
-// if (__builtin_cpu_supports("mma")) return &foo_mma.mma;
-// CHECK: %[[#MMA:]] = call i64 @getsystemcfg(i32 62)
-// CHECK-NEXT: icmp ugt i64 %[[#MMA]], 0
-// CHECK: ret ptr @foo_mma.mma
-// CHECK: ret ptr @foo_mma.default
-
-// CHECK: declare i64 @getsystemcfg(i32)
diff --git a/clang/test/CodeGen/PowerPC/attr-target-clones.c b/clang/test/CodeGen/PowerPC/attr-target-clones.c
index 40f5a31f693e2..48557660929b1 100644
--- a/clang/test/CodeGen/PowerPC/attr-target-clones.c
+++ b/clang/test/CodeGen/PowerPC/attr-target-clones.c
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -target-cpu pwr7 -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -target-cpu pwr7 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -target-cpu pwr10 -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-P10
// CHECK: @internal = internal ifunc i32 (), ptr @internal.resolver
// CHECK: @foo = ifunc i32 (), ptr @foo.resolver
@@ -168,7 +169,18 @@ foo_htm(void) { return 0; }
// CHECK: ret ptr @foo_htm.htm
// CHECK: ret ptr @foo_htm.default
-
+#ifdef _ARCH_PWR10
+int __attribute__((target_clones("mma", "default")))
+foo_mma(void) { return 0; }
+#endif
+// CHECK-P10: define internal {{.*}}i32 @foo_mma.mma()
+// CHECK-P10: define internal {{.*}}i32 @foo_mma.default()
+// CHECK-P10: define internal ptr @foo_mma.resolver()
+// if (__builtin_cpu_supports("mma")) return &foo_mma.mma;
+// CHECK-P10: %[[#MMA:]] = call i64 @getsystemcfg(i32 62)
+// CHECK-P10-NEXT: icmp ugt i64 %[[#MMA]], 0
+// CHECK-P10: ret ptr @foo_mma.mma
+// CHECK-P10: ret ptr @foo_mma.default
// Test multiple features with priority ordering (random source order)
// Source order: altivec, power8-vector, cpu=pwr11, vsx, power9-vector, default
>From 080f00b7539427c167261cc8535d4eab159829d1 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Sat, 4 Jul 2026 16:53:28 +0000
Subject: [PATCH 04/12] comments
---
clang/lib/Basic/Targets/PPC.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index 077b35003e17c..b50041d5efd8f 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -901,14 +901,14 @@ bool PPCTargetInfo::isValidFeatureName(StringRef Name) const {
}
bool PPCTargetInfo::isValidClonesFeatureName(StringRef Name) const {
- // Only 14 features with runtime detection are valid for target_clones
+ // Only features with runtime detection are valid for target_clones
return llvm::StringSwitch<bool>(Name)
// Direct mappings (4 features)
.Case("altivec", true)
.Case("htm", true)
.Case("mma", true)
.Case("vsx", true)
- // ISA level mappings (10 features)
+ // ISA level mappings
.Case("crypto", true)
.Case("direct-move", true)
.Case("float128", true)
@@ -931,7 +931,7 @@ PPCTargetInfo::getBuiltinCpuSupportsName(StringRef FeatureName) const {
.Case("htm", "htm")
.Case("mma", "mma")
.Case("vsx", "vsx")
- // ISA level mappings (10 features)
+ // ISA LEVEL MAPPINGS
.Case("popcntd", "arch_2_06")
.Case("crypto", "arch_2_07")
.Case("direct-move", "arch_2_07")
>From 95f37df30b71b769b368cabafa1bf08e41d07e87 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Sun, 5 Jul 2026 20:53:36 -0400
Subject: [PATCH 05/12] clean up:
- remove PPCTargetInfo::isValidFeatureName (to be reimplemented in a separate PR)
- handle build warnings
- rewire isValidClonesFeatureName to use a new macro PPC_AIX_CLONES_FEATURE
- remove getBuiltinCpuSupportsName as it's only used during clang codegen in a PPC path, so inline it's implementation into the usage point.
---
clang/include/clang/Basic/TargetInfo.h | 6 --
clang/lib/Basic/Targets/PPC.cpp | 79 +------------------
clang/lib/Basic/Targets/PPC.h | 12 +--
clang/lib/CodeGen/CodeGenFunction.cpp | 8 +-
clang/lib/Sema/SemaPPC.cpp | 11 ---
.../llvm/TargetParser/PPCTargetParser.def | 19 +++++
6 files changed, 32 insertions(+), 103 deletions(-)
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index aecb7ea48dc15..564836aeb0567 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1468,12 +1468,6 @@ class TargetInfo : public TransferrableTargetInfo,
return isValidFeatureName(Feature);
}
- /// Get __builtin_cpu_supports() argument for a feature
- /// Returns empty string if feature has no runtime detection
- virtual StringRef getBuiltinCpuSupportsName(StringRef Feature) const {
- return "";
- }
-
/// Returns true if feature has an impact on target code
/// generation.
virtual bool doesFeatureAffectCodeGen(StringRef Feature) const {
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index b50041d5efd8f..80de3bf2ba95b 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -866,85 +866,14 @@ void PPCTargetInfo::fillValidCPUList(SmallVectorImpl<StringRef> &Values) const {
llvm::PPC::fillValidCPUList(Values);
}
-bool PPCTargetInfo::isValidFeatureName(StringRef Name) const {
- if (!getTriple().isOSAIX())
- return TargetInfo::isValidFeatureName(Name);
- return llvm::StringSwitch<bool>(Name)
- .Case("altivec", true)
- .Case("htm", true)
- .Case("mma", true)
- .Case("vsx", true)
- .Case("crypto", true)
- .Case("direct-move", true)
- .Case("float128", true)
- .Case("paired-vector-memops", true)
- .Case("popcntd", true)
- .Case("power8-vector", true)
- .Case("power9-vector", true)
- .Case("power10-vector", true)
- .Case("prefixed", true)
- .Case("aix-shared-lib-tls-model-opt", true)
- .Case("aix-small-local-dynamic-tls", true)
- .Case("aix-small-local-exec-tls", true)
- .Case("cmpb", true)
- .Case("crbits", true)
- .Case("fprnd", true)
- .Case("invariant-function-descriptors", true)
- .Case("isel", true)
- .Case("longcall", true)
- .Case("mfcrf", true)
- .Case("mfocrf", true)
- .Case("privileged", true)
- .Case("rop-protect", true)
- .Case("secure-plt", true)
- .Default(false);
-}
-
-bool PPCTargetInfo::isValidClonesFeatureName(StringRef Name) const {
+bool PPCTargetInfo::isValidClonesFeatureName(StringRef FeatureStr) const {
// Only features with runtime detection are valid for target_clones
- return llvm::StringSwitch<bool>(Name)
- // Direct mappings (4 features)
- .Case("altivec", true)
- .Case("htm", true)
- .Case("mma", true)
- .Case("vsx", true)
- // ISA level mappings
- .Case("crypto", true)
- .Case("direct-move", true)
- .Case("float128", true)
- .Case("paired-vector-memops", true)
- .Case("popcntd", true)
- .Case("power8-vector", true)
- .Case("power9-vector", true)
- .Case("power10-vector", true)
- .Case("prefixed", true)
+ return llvm::StringSwitch<bool>(FeatureStr)
+#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, _) .Case(FEATURE_NAME, true)
+#include "llvm/TargetParser/PPCTargetParser.def"
.Default(false);
}
-StringRef
-PPCTargetInfo::getBuiltinCpuSupportsName(StringRef FeatureName) const {
- // Map feature names to __builtin_cpu_supports() strings
- // Only returns non-empty for features with runtime detection
- return llvm::StringSwitch<StringRef>(FeatureName)
- // Direct mappings (4 features)
- .Case("altivec", "altivec")
- .Case("htm", "htm")
- .Case("mma", "mma")
- .Case("vsx", "vsx")
- // ISA LEVEL MAPPINGS
- .Case("popcntd", "arch_2_06")
- .Case("crypto", "arch_2_07")
- .Case("direct-move", "arch_2_07")
- .Case("power8-vector", "arch_2_07")
- .Case("float128", "arch_3_00")
- .Case("power9-vector", "arch_3_00")
- .Case("paired-vector-memops", "arch_3_1")
- .Case("power10-vector", "arch_3_1")
- .Case("prefixed", "arch_3_1")
- // Features without runtime checks return empty string
- .Default("");
-}
-
void PPCTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts,
const TargetInfo *Aux) {
if (HasAltivec)
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index e46daaa85a993..e1e26d578d57d 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -98,16 +98,8 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {
bool isValidCPUName(StringRef Name) const override;
void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
- // Validate feature name for target attribute (all 28 features)
- bool isValidFeatureName(StringRef Name) const override;
-
- // Validate feature name for target_clones (only 17 features with runtime
- // detection)
- bool isValidClonesFeatureName(StringRef Name) const;
-
- // Get __builtin_cpu_supports() argument for a feature (returns empty string
- // if no runtime check)
- StringRef getBuiltinCpuSupportsName(StringRef FeatureName) const;
+ // Validate if given feature name is supported on target_clones
+ bool isValidClonesFeatureName(StringRef Name) const override;
bool setCPU(StringRef &Name) override {
bool CPUKnown = isValidCPUName(Name);
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index ed0b2a4cb2779..8121b823680c6 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3163,8 +3163,14 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
BaseFeature = BaseFeature.drop_front(3);
}
+ // Map feature names to __builtin_cpu_supports() strings
BuiltinCpuSupportsArg =
- getTarget().getBuiltinCpuSupportsName(BaseFeature);
+ llvm::StringSwitch<StringRef>(BaseFeature)
+#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME) \
+ .Case(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME)
+#include "llvm/TargetParser/PPCTargetParser.def"
+ // Features without runtime checks return empty string
+ .Default("");
// All features in target_clones must have runtime detection
assert(!BuiltinCpuSupportsArg.empty() &&
diff --git a/clang/lib/Sema/SemaPPC.cpp b/clang/lib/Sema/SemaPPC.cpp
index be76bac5de6c8..1446a277b6947 100644
--- a/clang/lib/Sema/SemaPPC.cpp
+++ b/clang/lib/Sema/SemaPPC.cpp
@@ -657,25 +657,14 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
} else {
// Handle feature strings
StringRef FeatureName = LHS;
- bool IsNegated = false;
// Check for negation prefix
if (FeatureName.starts_with("no-")) {
- IsNegated = true;
FeatureName = FeatureName.drop_front(3);
}
- // First check if it's a valid feature name at all
- if (!TargetInfo.isValidFeatureName(FeatureName)) {
- return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unknown << None << LHS << TargetClones;
- }
-
// Check if feature is valid for target_clones (has runtime detection)
- // Use virtual method that defaults to isValidFeatureName for non-PPC
- // targets
if (!TargetInfo.isValidClonesFeatureName(FeatureName)) {
- // Feature is valid for target attribute but not target_clones
return Diag(CurLoc, diag::err_ppc_feature_no_runtime_detection)
<< FeatureName;
}
diff --git a/llvm/include/llvm/TargetParser/PPCTargetParser.def b/llvm/include/llvm/TargetParser/PPCTargetParser.def
index da4be3e39f2c7..968323fecd6fc 100644
--- a/llvm/include/llvm/TargetParser/PPCTargetParser.def
+++ b/llvm/include/llvm/TargetParser/PPCTargetParser.def
@@ -276,6 +276,25 @@ PPC_AIX_FEATURE("ucache","CPU has unified I/D cache",USE_SYS_CONF,AIX_SYSCON_CAC
PPC_AIX_FEATURE("vsx","CPU supports the vector-scalar extension",USE_SYS_CONF,AIX_SYSCON_VMX_IDX,0,ICmpInst::ICMP_UGT,1)
#undef PPC_AIX_FEATURE
+#ifndef PPC_AIX_CLONES_FEATURE
+#define PPC_AIX_CLONES_FEATURE(TARGET_FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME)
+#endif
+
+PPC_AIX_CLONES_FEATURE("altivec", "altivec")
+PPC_AIX_CLONES_FEATURE("htm", "htm")
+PPC_AIX_CLONES_FEATURE("vsx", "vsx")
+PPC_AIX_CLONES_FEATURE("mma", "mma")
+PPC_AIX_CLONES_FEATURE("popcntd", "arch_2_06")
+PPC_AIX_CLONES_FEATURE("crypto", "arch_2_07")
+PPC_AIX_CLONES_FEATURE("direct-move", "arch_2_07")
+PPC_AIX_CLONES_FEATURE("power8-vector", "arch_2_07")
+PPC_AIX_CLONES_FEATURE("float128", "arch_3_00")
+PPC_AIX_CLONES_FEATURE("power9-vector", "arch_3_00")
+PPC_AIX_CLONES_FEATURE("paired-vector-memops","arch_3_1")
+PPC_AIX_CLONES_FEATURE("power10-vector", "arch_3_1")
+PPC_AIX_CLONES_FEATURE("prefixed", "arch_3_1")
+#undef PPC_AIX_CLONES_FEATURE
+
// PPC_SYSTEMCONFIG_TYPE defines the IR data structure of kernel variable
// `_system_configuration`, that is found in the AIX OS header file: </usr/include/sys/systemcfg.h>.
#ifndef PPC_SYSTEMCONFIG_TYPE
>From d553af4de3612d1e9cc15995e596eb91429474a8 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Mon, 6 Jul 2026 16:11:56 +0000
Subject: [PATCH 06/12] more cleanup
---
clang/include/clang/Basic/TargetInfo.h | 3 +--
clang/lib/CodeGen/Targets/PPC.cpp | 24 ++++++------------------
2 files changed, 7 insertions(+), 20 deletions(-)
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 564836aeb0567..f0fce88847296 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1462,8 +1462,7 @@ class TargetInfo : public TransferrableTargetInfo,
return true;
}
- /// Validate feature name for target_clones attribute (subset with runtime
- /// detection) Default implementation delegates to isValidFeatureName
+ /// Does this TargetInfo support the given feature on target_clones?
virtual bool isValidClonesFeatureName(StringRef Feature) const {
return isValidFeatureName(Feature);
}
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index f0c1237f8588c..18d76e1bd17a6 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -161,25 +161,13 @@ void AIXABIInfo::appendAttributeMangling(StringRef AttrStr,
if (!Info.Features.empty()) {
assert(Info.Features.size() == 1 && "one feature per version for now");
StringRef Feature = Info.Features[0];
- std::string MangledFeature;
-
- // Handle negation prefix "no-" specially - convert to "no_"
- if (Feature.starts_with("no-")) {
- MangledFeature = "no_";
- Feature = Feature.drop_front(3);
- } else if (Feature.starts_with("+")) {
- // Remove leading '+' for positive features
- Feature = Feature.drop_front(1);
- } else if (Feature.starts_with("-")) {
- // Leading '-' means negation, convert to "no_"
- MangledFeature = "no_";
- Feature = Feature.drop_front(1);
- }
+ assert(Feature.starts_with("+") || Feature.starts_with("-"));
+
+ // replace hyphens with underscores
+ std::string MangledName(Feature.drop_front(1));
+ std::replace(MangledName.begin(), MangledName.end(), '-', '_');
- // Append the base feature name and replace hyphens with underscores
- MangledFeature += Feature.str();
- std::replace(MangledFeature.begin(), MangledFeature.end(), '-', '_');
- Out << "." << MangledFeature;
+ Out << "." << (Feature.starts_with("-") ? "no_" : "") << MangledName;
return;
}
>From c80e0f34e44a818d467fbe89a3cd2cf28fa86d43 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Mon, 6 Jul 2026 13:45:03 -0400
Subject: [PATCH 07/12] integrate FMV priority into PPC_AIX_CLONES_FEATURE
---
clang/lib/Basic/Targets/PPC.cpp | 24 ++------
clang/lib/CodeGen/CodeGenFunction.cpp | 4 +-
.../llvm/TargetParser/PPCTargetParser.def | 56 ++++++++++++++-----
3 files changed, 49 insertions(+), 35 deletions(-)
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index 80de3bf2ba95b..71e0c828b5fe0 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -751,26 +751,10 @@ llvm::APInt PPCTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
Feature = Feature.drop_front(1);
int Priority = llvm::StringSwitch<int>(Feature)
- // POWER10 features (between pwr10=400 and pwr11=500)
- .Case("mma", 419)
- .Case("paired-vector-memops", 418)
- .Case("power10-vector", 416)
- .Case("prefixed", 415)
- // POWER9 features (between pwr9=300 and pwr10=400)
- .Case("float128", 312)
- .Case("power9-vector", 311)
- // POWER8 features (between pwr8=200 and pwr9=300)
- .Case("crypto", 214)
- .Case("direct-move", 213)
- .Case("power8-vector", 212)
- .Case("htm", 211)
- // POWER7 features (between pwr7=100 and pwr8=200)
- .Case("popcntd", 112)
- .Case("vsx", 111)
- // Base features: 50-99 (below pwr7=100)
- .Case("altivec", 50)
+#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, _, PRIORITY) \
+ .Case(FEATURE_NAME, PRIORITY)
+#include "llvm/TargetParser/PPCTargetParser.def"
.Default(0);
-
return llvm::APInt(32, Priority);
}
@@ -869,7 +853,7 @@ void PPCTargetInfo::fillValidCPUList(SmallVectorImpl<StringRef> &Values) const {
bool PPCTargetInfo::isValidClonesFeatureName(StringRef FeatureStr) const {
// Only features with runtime detection are valid for target_clones
return llvm::StringSwitch<bool>(FeatureStr)
-#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, _) .Case(FEATURE_NAME, true)
+#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, _, __) .Case(FEATURE_NAME, true)
#include "llvm/TargetParser/PPCTargetParser.def"
.Default(false);
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 8121b823680c6..f1f47709f0c6d 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3166,7 +3166,7 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
// Map feature names to __builtin_cpu_supports() strings
BuiltinCpuSupportsArg =
llvm::StringSwitch<StringRef>(BaseFeature)
-#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME) \
+#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME, _) \
.Case(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME)
#include "llvm/TargetParser/PPCTargetParser.def"
// Features without runtime checks return empty string
@@ -3178,6 +3178,8 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
"Sema");
}
+ assert(getContext().getTargetInfo().validateCpuSupports(BuiltinCpuSupportsArg));
+
llvm::Value *Condition =
EmitPPCBuiltinCpu(Builtin::BI__builtin_cpu_supports,
Builder.getInt1Ty(), BuiltinCpuSupportsArg);
diff --git a/llvm/include/llvm/TargetParser/PPCTargetParser.def b/llvm/include/llvm/TargetParser/PPCTargetParser.def
index 968323fecd6fc..29cc300089d1c 100644
--- a/llvm/include/llvm/TargetParser/PPCTargetParser.def
+++ b/llvm/include/llvm/TargetParser/PPCTargetParser.def
@@ -277,22 +277,50 @@ PPC_AIX_FEATURE("vsx","CPU supports the vector-scalar extension",USE_SYS_CONF,AI
#undef PPC_AIX_FEATURE
#ifndef PPC_AIX_CLONES_FEATURE
-#define PPC_AIX_CLONES_FEATURE(TARGET_FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME)
+#define PPC_AIX_CLONES_FEATURE(TARGET_FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME, PRIORITY)
#endif
-PPC_AIX_CLONES_FEATURE("altivec", "altivec")
-PPC_AIX_CLONES_FEATURE("htm", "htm")
-PPC_AIX_CLONES_FEATURE("vsx", "vsx")
-PPC_AIX_CLONES_FEATURE("mma", "mma")
-PPC_AIX_CLONES_FEATURE("popcntd", "arch_2_06")
-PPC_AIX_CLONES_FEATURE("crypto", "arch_2_07")
-PPC_AIX_CLONES_FEATURE("direct-move", "arch_2_07")
-PPC_AIX_CLONES_FEATURE("power8-vector", "arch_2_07")
-PPC_AIX_CLONES_FEATURE("float128", "arch_3_00")
-PPC_AIX_CLONES_FEATURE("power9-vector", "arch_3_00")
-PPC_AIX_CLONES_FEATURE("paired-vector-memops","arch_3_1")
-PPC_AIX_CLONES_FEATURE("power10-vector", "arch_3_1")
-PPC_AIX_CLONES_FEATURE("prefixed", "arch_3_1")
+// Description of parameters:
+// - TARGET_FEATURE_NAME: a target-feature name that is supported (in its
+// positive or negative (i.e. no-FEATURE) form) on a target and a target_clones
+// attribute on AIX.
+// - AIX_BUILTIN_CPU_SUPPORTS_NAME: the corresponding __builtin_cpu_supports
+// testable feature.
+// - PRIORITY: an integer value that determines the order of which features are
+// tested first when selecting between clones in the resolver. Higher priority
+// items will be tested first. The function PPCTargetInfo::getFMVPriority uses
+// this table to return the priority value for a given target feature or CPU.
+// The CPU priorities are (from PPCTargetInfo::getFMVPriority):
+// pwr7: 100
+// pwr8: 200
+// pwr9: 300
+// pwr10: 400
+// pwr11: 500
+// Some features (e.g. "prefixed") don't have an explicit bit to check at runtime,
+// instead the minimum ISA is checked.
+// Features in CPU X are given higher priority than features in CPU "X-1".
+// Eg. resolver for a target_clones("cpu=pwr8", "float128", "default") will test
+// for "float128" via __builtin_cpu_supports("arch_3_00") before testing for
+// __builtin_cpu_supports("arch_2_07") (aka cpu=pwr8).
+//
+// POWER10 features (between pwr10=400 and pwr11=500)
+PPC_AIX_CLONES_FEATURE("mma", "mma", 415)
+PPC_AIX_CLONES_FEATURE("paired-vector-memops","arch_3_1", 416)
+PPC_AIX_CLONES_FEATURE("power10-vector", "arch_3_1", 417)
+PPC_AIX_CLONES_FEATURE("prefixed", "arch_3_1", 418)
+// POWER9 features
+PPC_AIX_CLONES_FEATURE("float128", "arch_3_00", 311)
+PPC_AIX_CLONES_FEATURE("power9-vector", "arch_3_00", 312)
+// POWER8 features
+PPC_AIX_CLONES_FEATURE("crypto", "arch_2_07", 211)
+PPC_AIX_CLONES_FEATURE("direct-move", "arch_2_07", 212)
+PPC_AIX_CLONES_FEATURE("power8-vector", "arch_2_07", 213)
+PPC_AIX_CLONES_FEATURE("htm", "htm", 214)
+// POWER7 features
+PPC_AIX_CLONES_FEATURE("vsx", "vsx", 111)
+PPC_AIX_CLONES_FEATURE("popcntd", "arch_2_06", 112)
+// Base features: 50-99 (below pwr7=100)
+PPC_AIX_CLONES_FEATURE("altivec", "altivec", 50)
#undef PPC_AIX_CLONES_FEATURE
// PPC_SYSTEMCONFIG_TYPE defines the IR data structure of kernel variable
>From e140cc58984af1abeba4bef8bae2b52c2f2f293a Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Mon, 6 Jul 2026 18:28:04 +0000
Subject: [PATCH 08/12] clang-format
---
clang/lib/Basic/Targets/PPC.cpp | 4 ++--
clang/lib/CodeGen/CodeGenFunction.cpp | 5 +++--
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index 71e0c828b5fe0..a356bb199a445 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -751,8 +751,8 @@ llvm::APInt PPCTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
Feature = Feature.drop_front(1);
int Priority = llvm::StringSwitch<int>(Feature)
-#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, _, PRIORITY) \
- .Case(FEATURE_NAME, PRIORITY)
+#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, _, PRIORITY) \
+ .Case(FEATURE_NAME, PRIORITY)
#include "llvm/TargetParser/PPCTargetParser.def"
.Default(0);
return llvm::APInt(32, Priority);
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index f1f47709f0c6d..84c8ef8c63eab 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3166,7 +3166,7 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
// Map feature names to __builtin_cpu_supports() strings
BuiltinCpuSupportsArg =
llvm::StringSwitch<StringRef>(BaseFeature)
-#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME, _) \
+#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME, _) \
.Case(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME)
#include "llvm/TargetParser/PPCTargetParser.def"
// Features without runtime checks return empty string
@@ -3178,7 +3178,8 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
"Sema");
}
- assert(getContext().getTargetInfo().validateCpuSupports(BuiltinCpuSupportsArg));
+ assert(getContext().getTargetInfo().validateCpuSupports(
+ BuiltinCpuSupportsArg));
llvm::Value *Condition =
EmitPPCBuiltinCpu(Builtin::BI__builtin_cpu_supports,
>From 66a68ed7a6c381154a2a0efbd979b1fe58dc493f Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Mon, 6 Jul 2026 18:58:00 +0000
Subject: [PATCH 09/12] cleanup
---
clang/lib/CodeGen/CodeGenFunction.cpp | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 84c8ef8c63eab..9970e36d8bad2 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3152,20 +3152,17 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
.Case("pwr11", "arch_3_1")
.Default("error");
} else {
- // Feature string - check for "no-" negation prefix
- StringRef BaseFeature = FeatureStr;
-
// Feature strings arrive here already normalized:
// - Positive features: just the name (e.g., "altivec")
// - Negated features: "no-" prefix (e.g., "no-altivec")
- if (BaseFeature.starts_with("no-")) {
+ if (FeatureStr.starts_with("no-")) {
IsNegated = true;
- BaseFeature = BaseFeature.drop_front(3);
+ FeatureStr = FeatureStr.drop_front(3);
}
// Map feature names to __builtin_cpu_supports() strings
BuiltinCpuSupportsArg =
- llvm::StringSwitch<StringRef>(BaseFeature)
+ llvm::StringSwitch<StringRef>(FeatureStr)
#define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME, _) \
.Case(FEATURE_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME)
#include "llvm/TargetParser/PPCTargetParser.def"
>From 04c76e27e7b3db73e33ffa45f2803885451077b8 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Mon, 6 Jul 2026 21:14:23 -0400
Subject: [PATCH 10/12] simplify checkTargetClonesAttr logic
---
clang/lib/Sema/SemaPPC.cpp | 41 ++++----------------
clang/test/Sema/PowerPC/attr-target-clones.c | 10 ++---
2 files changed, 13 insertions(+), 38 deletions(-)
diff --git a/clang/lib/Sema/SemaPPC.cpp b/clang/lib/Sema/SemaPPC.cpp
index 1446a277b6947..27c57a5ff8d40 100644
--- a/clang/lib/Sema/SemaPPC.cpp
+++ b/clang/lib/Sema/SemaPPC.cpp
@@ -618,31 +618,24 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
const StringRef Param = Params[I].trim();
const SourceLocation &Loc = Locs[I];
+ if (Param.empty() || Param.ends_with(','))
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << "" << TargetClones;
+
if (Param.contains(','))
HasComma = true;
StringRef LHS;
StringRef RHS = Param;
- // TODO: simplify the logic to diagnose empty strings
- bool checkTrailingEmpty = false;
do {
std::tie(LHS, RHS) = RHS.split(',');
LHS = LHS.trim();
-
- // After processing last non-empty item, check if we need to process
- // trailing empty
- if (RHS.empty() && !LHS.empty() && Param.ends_with(',')) {
- checkTrailingEmpty = true;
- }
-
const SourceLocation &CurLoc =
Loc.getLocWithOffset(LHS.data() - Param.data());
- // Check for empty string (from trailing comma, leading comma, or ",,")
- if (LHS.empty()) {
+ if (LHS.empty())
return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unknown << None << "" << TargetClones;
- }
+ << Unsupported << None << "" << TargetClones;
if (LHS.starts_with("cpu=")) {
StringRef CPUStr = LHS.drop_front(sizeof("cpu=") - 1);
@@ -655,19 +648,10 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
} else if (LHS == "default") {
HasDefault = true;
} else {
- // Handle feature strings
- StringRef FeatureName = LHS;
-
- // Check for negation prefix
- if (FeatureName.starts_with("no-")) {
- FeatureName = FeatureName.drop_front(3);
- }
-
- // Check if feature is valid for target_clones (has runtime detection)
- if (!TargetInfo.isValidClonesFeatureName(FeatureName)) {
+ StringRef FeatureName = LHS.starts_with("no-") ? LHS.drop_front(3) : LHS;
+ if (!TargetInfo.isValidClonesFeatureName(FeatureName))
return Diag(CurLoc, diag::err_ppc_feature_no_runtime_detection)
<< FeatureName;
- }
}
SmallString<64> CPU;
if (LHS.starts_with("cpu=")) {
@@ -681,15 +665,6 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
continue;
}
NewParams.push_back(LHS);
-
- // If we just processed the last item and there's a trailing comma,
- // do one more iteration to catch the empty string
- if (checkTrailingEmpty) {
- LHS = "";
- const SourceLocation &EmptyCurLoc = Loc.getLocWithOffset(Param.size());
- return Diag(EmptyCurLoc, diag::warn_unsupported_target_attribute)
- << Unknown << None << "" << TargetClones;
- }
} while (!RHS.empty());
}
if (HasComma && Params.size() > 1)
diff --git a/clang/test/Sema/PowerPC/attr-target-clones.c b/clang/test/Sema/PowerPC/attr-target-clones.c
index 0f078fa2e805c..7bde2e47ccda7 100644
--- a/clang/test/Sema/PowerPC/attr-target-clones.c
+++ b/clang/test/Sema/PowerPC/attr-target-clones.c
@@ -42,20 +42,20 @@ int __attribute__((target_clones("cpu=pwr9,default"))) redef3(void) { return 1;
int __attribute__((target_clones("cpu=pwr9,cpu=power9", "cpu=power9, default")))
dupes(void) { return 1; }
-// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones("")))
empty_target_1(void);
-// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones(",default")))
empty_target_2(void);
-// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones("default,")))
empty_target_3(void);
-// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones("default, ,cpu=pwr7")))
empty_target_4(void);
-// expected-warning at +1 {{unknown '' in the 'target_clones' attribute string;}}
+// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string;}}
void __attribute__((target_clones("default,cpu=pwr7", "")))
empty_target_5(void);
>From 2fe0a9859ddbf9564cbbe42fce038651b0469d28 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 15 Jul 2026 02:12:22 +0000
Subject: [PATCH 11/12] code review
---
clang/lib/AST/ASTContext.cpp | 7 ++-----
clang/lib/Basic/Targets/PPC.cpp | 11 ++++-------
clang/lib/CodeGen/CodeGenFunction.cpp | 10 ++++------
llvm/include/llvm/TargetParser/PPCTargetParser.def | 11 +++++++++++
4 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 8635140f6829a..9ae5a531c9986 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15390,11 +15390,8 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());
if (VersionStr.starts_with("cpu="))
TargetCPU = VersionStr.drop_front(sizeof("cpu=") - 1);
- else if (VersionStr != "default") {
- // Handle feature strings
- ParsedTargetAttr ParsedAttr = Target->parseTargetAttr(VersionStr);
- Features = ParsedAttr.Features;
- }
+ else if (VersionStr != "default")
+ Features = Target->parseTargetAttr(VersionStr).Features;
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
} else {
std::vector<std::string> Features;
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index a356bb199a445..cb8e4b5d1de70 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -734,11 +734,9 @@ llvm::APInt PPCTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
if (!ParsedAttr.CPU.empty()) {
int Priority = llvm::StringSwitch<int>(ParsedAttr.CPU)
- .Case("pwr7", 100)
- .Case("pwr8", 200)
- .Case("pwr9", 300)
- .Case("pwr10", 400)
- .Case("pwr11", 500)
+#define PPC_AIX_CLONES_CPU(CPU_NAME, _, PRIORITY) \
+ .Case(CPU_NAME, PRIORITY)
+#include "llvm/TargetParser/PPCTargetParser.def"
.Default(0);
return llvm::APInt(32, Priority);
}
@@ -757,8 +755,7 @@ llvm::APInt PPCTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
.Default(0);
return llvm::APInt(32, Priority);
}
-
- return llvm::APInt(32, 0);
+ llvm_unreachable("Invalid target_clones parameter");
}
// Make sure that registers are added in the correct array index which should be
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 9970e36d8bad2..a08f4882c47d0 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3145,11 +3145,9 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
// CPU specification - map to ISA level
StringRef CPU = FeatureStr.split("=").second.trim();
BuiltinCpuSupportsArg = llvm::StringSwitch<StringRef>(CPU)
- .Case("pwr7", "arch_2_06")
- .Case("pwr8", "arch_2_07")
- .Case("pwr9", "arch_3_00")
- .Case("pwr10", "arch_3_1")
- .Case("pwr11", "arch_3_1")
+#define PPC_AIX_CLONES_CPU(CPU_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME, _) \
+ .Case(CPU_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME)
+#include "llvm/TargetParser/PPCTargetParser.def"
.Default("error");
} else {
// Feature strings arrive here already normalized:
@@ -3171,7 +3169,7 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
// All features in target_clones must have runtime detection
assert(!BuiltinCpuSupportsArg.empty() &&
- "feature without runtime detection should have been rejected in "
+ "Feature without runtime detection should have been rejected in "
"Sema");
}
diff --git a/llvm/include/llvm/TargetParser/PPCTargetParser.def b/llvm/include/llvm/TargetParser/PPCTargetParser.def
index 29cc300089d1c..10a7e302e3481 100644
--- a/llvm/include/llvm/TargetParser/PPCTargetParser.def
+++ b/llvm/include/llvm/TargetParser/PPCTargetParser.def
@@ -323,6 +323,17 @@ PPC_AIX_CLONES_FEATURE("popcntd", "arch_2_06", 112)
PPC_AIX_CLONES_FEATURE("altivec", "altivec", 50)
#undef PPC_AIX_CLONES_FEATURE
+#ifndef PPC_AIX_CLONES_CPU
+#define PPC_AIX_CLONES_CPU(CPU_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME, PRIORITY)
+#endif
+PPC_AIX_CLONES_CPU("pwr7", "arch_2_06", 100)
+PPC_AIX_CLONES_CPU("pwr8", "arch_2_07", 200)
+PPC_AIX_CLONES_CPU("pwr9", "arch_3_00", 300)
+PPC_AIX_CLONES_CPU("pwr10", "arch_3_1", 400)
+// Power11 implements the same ISA as Power10
+PPC_AIX_CLONES_CPU("pwr11", "arch_3_1", 500)
+#undef PPC_AIX_CLONES_CPU
+
// PPC_SYSTEMCONFIG_TYPE defines the IR data structure of kernel variable
// `_system_configuration`, that is found in the AIX OS header file: </usr/include/sys/systemcfg.h>.
#ifndef PPC_SYSTEMCONFIG_TYPE
>From ad31b4063d48bf269eec4d23b288b4b0b32214ca Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 15 Jul 2026 04:03:54 +0000
Subject: [PATCH 12/12] add isValidFeatureName assertion to valid target_clones
features
---
clang/lib/Sema/SemaPPC.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/lib/Sema/SemaPPC.cpp b/clang/lib/Sema/SemaPPC.cpp
index 27c57a5ff8d40..88ba8c235188c 100644
--- a/clang/lib/Sema/SemaPPC.cpp
+++ b/clang/lib/Sema/SemaPPC.cpp
@@ -652,6 +652,8 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
if (!TargetInfo.isValidClonesFeatureName(FeatureName))
return Diag(CurLoc, diag::err_ppc_feature_no_runtime_detection)
<< FeatureName;
+ // All target_clones feature names must be valid target feature names.
+ assert(isValidFeatureName(FeatureName));
}
SmallString<64> CPU;
if (LHS.starts_with("cpu=")) {
More information about the cfe-commits
mailing list