[clang] [llvm] [FMV][AIX] Implement target_clones part 2 (target-features) (PR #206786)

Wael Yehia via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 10 21:31:30 PDT 2026


https://github.com/w2yehia updated https://github.com/llvm/llvm-project/pull/206786

>From e08d4f4208366811fb329bd090dfbcace3d3e887 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/15] 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 3052dd6c77ab1..26392cead7cc3 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3439,8 +3439,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 b314c17ad27bd..6a9389ba3994d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11584,6 +11584,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 6311b6b567a5e..9baacffc81493 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1469,6 +1469,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 35a2a0b131a3f..17e2605f13f35 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15422,8 +15422,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 119aebb673789..0a90a6041816d 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3149,18 +3149,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 a87f841063650..449ca39b9faba 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -158,7 +158,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 ce04e1024df199207457429cda04da0d93c14451 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/15] 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 5d05e49c2c45861583c61fb1c7ae33e460137ee8 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/15] 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 6b6ceb7d53b0d284ab776136162f54d6ab80ea66 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/15] 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 3c5d33ebf522847f9ca2e2e1b69e2b2ecd665871 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/15] 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 9baacffc81493..52b200deaee70 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1475,12 +1475,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 0a90a6041816d..cc9dedca8edb3 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3175,8 +3175,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 bd6f4b948bdf775c35c9f316c201c49f5fbfe56c 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/15] 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 52b200deaee70..e93d7ee98e419 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1469,8 +1469,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 449ca39b9faba..72d7d7be4b56c 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -162,25 +162,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 9e5e301c5ed42b10a9463074926c082b4ccdb0c3 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/15] 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 cc9dedca8edb3..d9b67c9dde0da 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3178,7 +3178,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
@@ -3190,6 +3190,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 ab8cfd81378da20aefbdec5234a61e683cc7d655 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/15] 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 d9b67c9dde0da..dff5734aec2d8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3178,7 +3178,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
@@ -3190,7 +3190,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 213831852b2a76ab37f9a876e8e1d722705a6250 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/15] 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 dff5734aec2d8..1d83eec0cb018 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3164,20 +3164,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 85a20172e35f61fd808e2606c4bf277787c917b2 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/15] 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 cb20b04d615af1a8abdb91b0eb21e941b9e0a493 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/15] 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 17e2605f13f35..3b39889b96b3b 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15422,11 +15422,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 1d83eec0cb018..74d974896c5ce 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3157,11 +3157,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:
@@ -3183,7 +3181,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 6b572f25f25a18106a50fa5d9b56225ae6fa5bc9 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/15] 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=")) {

>From 149cc4a8eb12bb524b02e3d902f2030a0b6e6e35 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Thu, 10 Sep 2026 22:12:02 +0000
Subject: [PATCH 13/15] fix build break

fixes
---
 clang/lib/Basic/Targets/PPC.h | 2 +-
 clang/lib/Sema/SemaPPC.cpp    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index e1e26d578d57d..f78f36d8bab9f 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -101,7 +101,7 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {
   // Validate if given feature name is supported on target_clones
   bool isValidClonesFeatureName(StringRef Name) const override;
 
-  bool setCPU(StringRef &Name) override {
+  bool setCPU(StringRef Name) override {
     bool CPUKnown = isValidCPUName(Name);
     if (CPUKnown) {
       CPU = Name;
diff --git a/clang/lib/Sema/SemaPPC.cpp b/clang/lib/Sema/SemaPPC.cpp
index 88ba8c235188c..0c8f9ec06e369 100644
--- a/clang/lib/Sema/SemaPPC.cpp
+++ b/clang/lib/Sema/SemaPPC.cpp
@@ -653,7 +653,7 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
           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));
+        assert(TargetInfo.isValidFeatureName(FeatureName));
       }
       SmallString<64> CPU;
       if (LHS.starts_with("cpu=")) {

>From 4df784cb1b13288da3fc9e6f256b45a9a230e4d8 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Thu, 10 Sep 2026 22:25:47 +0000
Subject: [PATCH 14/15] categorize features into ones that can be disabled at
 runtime and ones that cannot

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 ++
 clang/lib/Basic/Targets/PPC.cpp               | 31 ++++++++++++++++---
 clang/lib/Sema/SemaPPC.cpp                    | 19 +++++++++++-
 clang/test/Sema/PowerPC/attr-target-clones.c  |  7 +++++
 .../llvm/TargetParser/PPCTargetParser.def     | 14 ++++++---
 .../llvm/TargetParser/PPCTargetParser.h       |  4 +++
 llvm/lib/TargetParser/PPCTargetParser.cpp     |  5 +++
 7 files changed, 73 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6a9389ba3994d..eedd8c13deda1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11587,6 +11587,9 @@ def err_ppc_invalid_arg_type : Error<
 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_ppc_multiple_negative_category2 : Error<
+  "only one negative form of runtime-disableable features (vsx, htm) is allowed in 'target_clones'; "
+  "'%0' conflicts with '%1'">;
 
 def err_x86_builtin_invalid_rounding : Error<
   "invalid rounding argument">;
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index cb8e4b5d1de70..9c7ea76137bea 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -728,10 +728,24 @@ llvm::APInt PPCTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
   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)
-
+  // Priority scheme:
+  // CPU specifications: 100-500 (pwr7=100, pwr8=200, ..., pwr11=500)
+  // For target-features, they can be divided into 3 categories:
+  // 1) non-CPU properties (e.g. invariant-function-descriptors); those cannot
+  //    be tested at runtime, and are currently excluded from target_clones.
+  // 2) CPU properties that cannot be disabled (e.g. mma); these features map to
+  //    CPUs directly:
+  //     +feature => __builtin_cpu_supports("<minimum-cpu>")
+  //              => true for CPU <minimum-cpu> and above.
+  //     -feature => !__builtin_cpu_supports("<minimum-cpu>")
+  //              => true for CPU <minimum-cpu-minus-one> and below.
+  // 3) CPU properties that can be disabled (e.g. vsx); those can map to CPUs
+  // directly for the positive requirement (same as (2));
+  // for the negative requirement checking the CPU is incorrect:
+  //   target_clones(no-vsx, cpu=pwr8)
+  // should pick no-vsx when vsx is disabled at runtime, so we need to test
+  // negative form of category 3 first, and will only allow one negative form
+  // from this category on a target_clones.
   if (!ParsedAttr.CPU.empty()) {
     int Priority = llvm::StringSwitch<int>(ParsedAttr.CPU)
 #define PPC_AIX_CLONES_CPU(CPU_NAME, _, PRIORITY)                      \
@@ -741,13 +755,20 @@ llvm::APInt PPCTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
     return llvm::APInt(32, Priority);
   }
 
-  // Feature strings: priority between cpu=pwrN and cpu=pwr(N+1)
+  // Feature strings
   if (!ParsedAttr.Features.empty()) {
     StringRef Feature = ParsedAttr.Features[0];
+    bool IsNegated = Feature.starts_with("-");
     // Remove leading '+' or '-'
     if (Feature.starts_with("+") || Feature.starts_with("-"))
       Feature = Feature.drop_front(1);
 
+    // Check if this is a negative category 3 feature (highest priority)
+    // Sema guarantees there's only one such version on a target_clones.
+    if (IsNegated && llvm::PPC::canDisableFeatureOnAIX(Feature))
+      Feature = "NEGATIVE-FEATURE";
+
+    // Regular feature priority (positive or negative category 2)
     int Priority = llvm::StringSwitch<int>(Feature)
 #define PPC_AIX_CLONES_FEATURE(FEATURE_NAME, _, PRIORITY)                      \
   .Case(FEATURE_NAME, PRIORITY)
diff --git a/clang/lib/Sema/SemaPPC.cpp b/clang/lib/Sema/SemaPPC.cpp
index 0c8f9ec06e369..ff640ea67c9fc 100644
--- a/clang/lib/Sema/SemaPPC.cpp
+++ b/clang/lib/Sema/SemaPPC.cpp
@@ -614,6 +614,8 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
   auto &TargetInfo = getASTContext().getTargetInfo();
   bool HasDefault = false;
   bool HasComma = false;
+  bool HasNegativeCategory2 = false;
+  StringRef NegativeCategory2Feature;
   for (unsigned I = 0, E = Params.size(); I < E; ++I) {
     const StringRef Param = Params[I].trim();
     const SourceLocation &Loc = Locs[I];
@@ -648,12 +650,27 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
       } else if (LHS == "default") {
         HasDefault = true;
       } else {
-        StringRef FeatureName = LHS.starts_with("no-") ? LHS.drop_front(3) : LHS;
+        bool IsNegated = LHS.starts_with("no-");
+        StringRef FeatureName = IsNegated ? LHS.drop_front(3) : LHS;
         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(TargetInfo.isValidFeatureName(FeatureName));
+        
+        if (llvm::PPC::canDisableFeatureOnAIX(FeatureName)) {
+          if (IsNegated) {
+            // Only one negative target-feature that can be disabled.
+            if (HasNegativeCategory2) {
+              return Diag(CurLoc, diag::err_ppc_multiple_negative_category2)
+                     << LHS << NegativeCategory2Feature;
+            }
+            HasNegativeCategory2 = true;
+            NegativeCategory2Feature = LHS;
+          }
+          // Positive category 2 features are always allowed
+        }
+        // Category 1 features (positive or negative) are always allowed
       }
       SmallString<64> CPU;
       if (LHS.starts_with("cpu=")) {
diff --git a/clang/test/Sema/PowerPC/attr-target-clones.c b/clang/test/Sema/PowerPC/attr-target-clones.c
index 7bde2e47ccda7..f676167016226 100644
--- a/clang/test/Sema/PowerPC/attr-target-clones.c
+++ b/clang/test/Sema/PowerPC/attr-target-clones.c
@@ -213,6 +213,13 @@ valid_mixed_features_1(void);
 void __attribute__((target_clones("vsx", "no-crypto", "default")))
 valid_mixed_features_2(void);
 
+// expected-error at +1 {{only one negative form of runtime-disableable features (vsx, htm) is allowed in 'target_clones'; 'no-vsx' conflicts with 'no-htm'}}
+void __attribute__((target_clones("no-htm", "no-vsx", "default")))
+mixed_features_3(void);
+
+void __attribute__((target_clones("htm", "no-vsx", "default")))
+valid_mixed_features_4(void);
+
 // Test features with CPU specifications
 void __attribute__((target_clones("altivec", "cpu=pwr8", "default")))
 valid_feature_with_cpu_1(void);
diff --git a/llvm/include/llvm/TargetParser/PPCTargetParser.def b/llvm/include/llvm/TargetParser/PPCTargetParser.def
index 10a7e302e3481..61185211f0725 100644
--- a/llvm/include/llvm/TargetParser/PPCTargetParser.def
+++ b/llvm/include/llvm/TargetParser/PPCTargetParser.def
@@ -321,17 +321,23 @@ 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)
+
+// A special non-feature entry, that holds the priority for the negative form
+// (e.g. no-vsx) for features that can be disabled at runtime. This should have
+// the highest priority among all features and cpus.
+// See llvm::PPC::canDisableFeatureOnAIX() query.
+PPC_AIX_CLONES_FEATURE("NEGATIVE-FEATURE",    "",         1000)
 #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)
+PPC_AIX_CLONES_CPU("pwr10",                   "arch_3_1",  400)
+PPC_AIX_CLONES_CPU("pwr9",                    "arch_3_00", 300)
+PPC_AIX_CLONES_CPU("pwr8",                    "arch_2_07", 200)
+PPC_AIX_CLONES_CPU("pwr7",                    "arch_2_06", 100)
 #undef PPC_AIX_CLONES_CPU
 
 // PPC_SYSTEMCONFIG_TYPE defines the IR data structure of kernel variable
diff --git a/llvm/include/llvm/TargetParser/PPCTargetParser.h b/llvm/include/llvm/TargetParser/PPCTargetParser.h
index 9f39551377f70..8b4d5e35b26f8 100644
--- a/llvm/include/llvm/TargetParser/PPCTargetParser.h
+++ b/llvm/include/llvm/TargetParser/PPCTargetParser.h
@@ -44,6 +44,10 @@ LLVM_ABI std::optional<llvm::StringMap<bool>>
 getPPCDefaultTargetFeatures(const Triple &T, StringRef CPUName);
 
 LLVM_ABI bool isValidFeatureName(StringRef Name);
+
+// Return true for target features that can be disabled at runtime.
+// For example, VSX can be disabled via the allow_vmx tunable using the schedo command.
+LLVM_ABI bool canDisableFeatureOnAIX(StringRef Name);
 } // namespace PPC
 } // namespace llvm
 
diff --git a/llvm/lib/TargetParser/PPCTargetParser.cpp b/llvm/lib/TargetParser/PPCTargetParser.cpp
index 76c2c13ac53a4..fc7f815d09718 100644
--- a/llvm/lib/TargetParser/PPCTargetParser.cpp
+++ b/llvm/lib/TargetParser/PPCTargetParser.cpp
@@ -152,5 +152,10 @@ bool isValidFeatureName(StringRef Name) {
   const BasicSubtargetFeatureKV *F = llvm::lower_bound(A, Name);
   return F != A.end() && StringRef(F->Key) == Name;
 }
+
+bool canDisableFeatureOnAIX(StringRef Name) {
+  return Name == "vsx" || Name == "htm";
+}
+
 } // namespace PPC
 } // namespace llvm

>From 26056e272a395b3419265453a3bacb122aef74ac Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Fri, 11 Sep 2026 04:06:00 +0000
Subject: [PATCH 15/15] clang-foramt

---
 clang/lib/Basic/Targets/PPC.cpp                  | 3 +--
 clang/lib/CodeGen/CodeGenFunction.cpp            | 2 +-
 clang/lib/Sema/SemaPPC.cpp                       | 2 +-
 llvm/include/llvm/TargetParser/PPCTargetParser.h | 3 ++-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index 9c7ea76137bea..4f4754ecdcb29 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -748,8 +748,7 @@ llvm::APInt PPCTargetInfo::getFMVPriority(ArrayRef<StringRef> Features) const {
   // from this category on a target_clones.
   if (!ParsedAttr.CPU.empty()) {
     int Priority = llvm::StringSwitch<int>(ParsedAttr.CPU)
-#define PPC_AIX_CLONES_CPU(CPU_NAME, _, PRIORITY)                      \
-  .Case(CPU_NAME, PRIORITY)
+#define PPC_AIX_CLONES_CPU(CPU_NAME, _, PRIORITY) .Case(CPU_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 74d974896c5ce..37b664227a064 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3157,7 +3157,7 @@ void CodeGenFunction::EmitPPCAIXMultiVersionResolver(
       // CPU specification - map to ISA level
       StringRef CPU = FeatureStr.split("=").second.trim();
       BuiltinCpuSupportsArg = llvm::StringSwitch<StringRef>(CPU)
-#define PPC_AIX_CLONES_CPU(CPU_NAME, AIX_BUILTIN_CPU_SUPPORTS_NAME, _) \
+#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");
diff --git a/clang/lib/Sema/SemaPPC.cpp b/clang/lib/Sema/SemaPPC.cpp
index ff640ea67c9fc..f4d89e3ffd1a8 100644
--- a/clang/lib/Sema/SemaPPC.cpp
+++ b/clang/lib/Sema/SemaPPC.cpp
@@ -657,7 +657,7 @@ bool SemaPPC::checkTargetClonesAttr(const SmallVectorImpl<StringRef> &Params,
                  << FeatureName;
         // All target_clones feature names must be valid target feature names.
         assert(TargetInfo.isValidFeatureName(FeatureName));
-        
+
         if (llvm::PPC::canDisableFeatureOnAIX(FeatureName)) {
           if (IsNegated) {
             // Only one negative target-feature that can be disabled.
diff --git a/llvm/include/llvm/TargetParser/PPCTargetParser.h b/llvm/include/llvm/TargetParser/PPCTargetParser.h
index 8b4d5e35b26f8..10565697f537a 100644
--- a/llvm/include/llvm/TargetParser/PPCTargetParser.h
+++ b/llvm/include/llvm/TargetParser/PPCTargetParser.h
@@ -46,7 +46,8 @@ getPPCDefaultTargetFeatures(const Triple &T, StringRef CPUName);
 LLVM_ABI bool isValidFeatureName(StringRef Name);
 
 // Return true for target features that can be disabled at runtime.
-// For example, VSX can be disabled via the allow_vmx tunable using the schedo command.
+// For example, VSX can be disabled via the allow_vmx tunable using the schedo
+// command.
 LLVM_ABI bool canDisableFeatureOnAIX(StringRef Name);
 } // namespace PPC
 } // namespace llvm



More information about the cfe-commits mailing list