[llvm] [RISC-V][RVY] Initial ISAInfo support for RVY (PR #201931)

Alexander Richardson via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 5 00:44:12 PDT 2026


https://github.com/arichardson updated https://github.com/llvm/llvm-project/pull/201931

>From 07852a54eb40726f0659f276079e9000da16e600 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Fri, 5 Jun 2026 13:21:37 -0700
Subject: [PATCH 1/4] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?=
 =?UTF-8?q?itial=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1
---
 llvm/lib/TargetParser/RISCVISAInfo.cpp        |  82 +++++--
 .../TargetParser/RISCVISAInfoTest.cpp         | 200 ++++++++++++++++--
 2 files changed, 252 insertions(+), 30 deletions(-)

diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp
index 7b04b42f8d000..363ae4148a621 100644
--- a/llvm/lib/TargetParser/RISCVISAInfo.cpp
+++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp
@@ -593,7 +593,7 @@ RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension,
 
   if (XLen == 0 || Arch.empty())
     return getError(
-        "string must begin with rv32{i,e,g}, rv64{i,e,g}, or a supported "
+        "string must begin with rv32{i,e,g,y}, rv64{i,e,g,y}, or a supported "
         "profile name");
 
   std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen));
@@ -606,11 +606,11 @@ RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension,
 
   unsigned Major, Minor, ConsumeLength;
 
-  // First letter should be 'e', 'i' or 'g'.
+  // First letter should be 'e', 'i', 'g' or 'y'.
   switch (Baseline) {
   default:
     return getError("first letter after \'rv" + Twine(XLen) +
-                    "\' should be 'e', 'i' or 'g'");
+                    "\' should be 'e', 'i', 'g' or 'y'");
   case 'e':
   case 'i':
     // Baseline is `i` or `e`
@@ -621,6 +621,23 @@ RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension,
 
     ISAInfo->Exts[std::string(1, Baseline)] = {Major, Minor};
     break;
+  case 'y':
+    // If the first character is 'y', this is equivalent to "iy".
+    // TODO: arch string syntax for RVE+RVY (and y in non-first position) will
+    // be included following conclusion of "long base name" syntax
+    // https://lists.riscv.org/g/tech-unprivileged/message/1134
+    if (auto E = getExtensionVersion("y", Arch, Major, Minor, ConsumeLength,
+                                     EnableExperimentalExtension,
+                                     ExperimentalExtensionVersionCheck))
+      return std::move(E);
+
+    ISAInfo->Exts["y"] = {Major, Minor};
+    if (auto IVersion = findDefaultVersion("i")) {
+      ISAInfo->Exts["i"] = {IVersion->Major, IVersion->Minor};
+    } else {
+      llvm_unreachable("Default 'i' extension version not found?");
+    }
+    break;
   case 'g':
     // g expands to extensions in RISCVGImplications.
     if (!Arch.empty() && isDigit(Arch.front()))
@@ -730,6 +747,10 @@ static Error getIncompatibleError(StringRef Ext1, StringRef Ext2) {
                   "' extensions are incompatible");
 }
 
+static Error getBaseIncompatibleError(StringRef Ext, StringRef Base) {
+  return getError("'" + Ext + "' is incompatible with " + Base + " base");
+}
+
 static Error getExtensionRequiresError(StringRef Ext, StringRef ReqExt) {
   return getError("'" + Ext + "' requires '" + ReqExt +
                   "' extension to also be specified");
@@ -795,6 +816,20 @@ Error RISCVISAInfo::checkDependency() {
   if (Exts.count("zclsd") != 0 && Exts.count("zcf") != 0)
     return getIncompatibleError("zclsd", "zcf");
 
+  if (Exts.count("y") != 0) {
+    if (XLen == 32) {
+      // On RVY32 systems the zclsd/zcf encodings are used for y load/stores.
+      if (Exts.count("zclsd") != 0)
+        return getBaseIncompatibleError("zclsd", "rv32y");
+      if (Exts.count("zcf") != 0)
+        return getBaseIncompatibleError("zcf", "rv32y");
+    } else {
+      // On RVY64 systems the zcd encodings are used for y load/stores.
+      if (Exts.count("zcd") != 0)
+        return getBaseIncompatibleError("zcd", "rv64y");
+    }
+  }
+
   if (HasZcmp && HasXqccmp)
     return getIncompatibleError("zcmp", "xqccmp");
 
@@ -848,21 +883,23 @@ void RISCVISAInfo::updateImplication() {
     }
   }
 
-  // Add Zcd if C and D are enabled.
-  if (Exts.count("c") && Exts.count("d") && !Exts.count("zcd")) {
+  // Add Zcd if C and D are enabled and we aren't targeting 64-bit RVY.
+  if (Exts.count("c") && Exts.count("d") && !Exts.count("zcd") &&
+      (XLen == 32 || !Exts.count("y"))) {
     auto Version = findDefaultVersion("zcd");
     Exts["zcd"] = *Version;
   }
 
-  // Add Zcf if C and F are enabled on RV32.
-  if (XLen == 32 && Exts.count("c") && Exts.count("f") && !Exts.count("zcf")) {
+  // Add Zcf if C and F are enabled on RV32 and Y is not enabled.
+  if (XLen == 32 && Exts.count("c") && Exts.count("f") && !Exts.count("zcf") &&
+      !Exts.count("y")) {
     auto Version = findDefaultVersion("zcf");
     Exts["zcf"] = *Version;
   }
 
-  // Add Zcf if Zce and F are enabled on RV32.
+  // Add Zcf if Zce and F are enabled on RV32 and Y is not enabled.
   if (XLen == 32 && Exts.count("zce") && Exts.count("f") &&
-      !Exts.count("zcf")) {
+      !Exts.count("zcf") && !Exts.count("y")) {
     auto Version = findDefaultVersion("zcf");
     Exts["zcf"] = *Version;
   }
@@ -877,18 +914,24 @@ void RISCVISAInfo::updateImplication() {
   // For RV64:
   //   - No D: Zca alone implies C
   //   - D: Zca + Zcd implies C
+  // For RV32Y (Zcf incompatible):
+  //   - No D (but maybe F): Zca alone implies C
+  //   - F and D: Zca + Zcd implies C
+  // For RV64Y (Zcf and Zcd incompatible):
+  //   - Zca alone implies C
   if (Exts.count("zca") && !Exts.count("c")) {
     bool ShouldAddC = false;
     if (XLen == 32) {
       if (Exts.count("d"))
-        ShouldAddC = Exts.count("zcf") && Exts.count("zcd");
+        ShouldAddC =
+            Exts.count("zcd") && (Exts.count("y") || Exts.count("zcf"));
       else if (Exts.count("f"))
-        ShouldAddC = Exts.count("zcf");
+        ShouldAddC = Exts.count("y") || Exts.count("zcf");
       else
         ShouldAddC = true;
     } else if (XLen == 64) {
       if (Exts.count("d"))
-        ShouldAddC = Exts.count("zcd");
+        ShouldAddC = Exts.count("y") || Exts.count("zcd");
       else
         ShouldAddC = true;
     }
@@ -898,13 +941,16 @@ void RISCVISAInfo::updateImplication() {
     }
   }
 
-  if (!Exts.count("zce") && Exts.count("zca") && Exts.count("zcb") &&
-      Exts.count("zcmp") && Exts.count("zcmt")) {
+  if (!Exts.count("zce") && Exts.count("zca") && Exts.count("zcb")) {
     bool ShouldAddZce = false;
-    if (XLen == 32) {
-      ShouldAddZce = !Exts.count("f") || Exts.count("zcf");
-    } else if (XLen == 64) {
-      ShouldAddZce = true;
+    if (Exts.count("zcmp") && Exts.count("zcmt")) {
+      if (XLen == 32) {
+        ShouldAddZce = !Exts.count("f") || Exts.count("zcf") || Exts.count("y");
+      } else if (XLen == 64) {
+        ShouldAddZce = true;
+      }
+    } else if (Exts.count("y") && XLen == 64) {
+      ShouldAddZce = true; // For RV64Y only Zce only includes Zca and Zcb
     }
     if (ShouldAddZce)
       Exts["zce"] = *findDefaultVersion("zce");
diff --git a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
index 9e7f23e2b4f76..5e32d56d89d28 100644
--- a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
+++ b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
@@ -172,25 +172,104 @@ TEST(ParseArchString, RejectsInvalidChars) {
 
 TEST(ParseArchString, RejectsInvalidBaseISA) {
   for (StringRef Input : {"rv32", "rv64", "rv65i"}) {
-    EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
-              "string must begin with rv32{i,e,g}, rv64{i,e,g}, or a supported "
-              "profile name");
+    EXPECT_EQ(
+        toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
+        "string must begin with rv32{i,e,g,y}, rv64{i,e,g,y}, or a supported "
+        "profile name");
   }
 
   for (StringRef Input : {"rv32j", "rv32_i"}) {
     EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
-              "first letter after 'rv32' should be 'e', 'i' or 'g'");
+              "first letter after 'rv32' should be 'e', 'i', 'g' or 'y'");
   }
 
   EXPECT_EQ(toString(RISCVISAInfo::parseArchString("rv64k", true).takeError()),
-            "first letter after 'rv64' should be 'e', 'i' or 'g'");
+            "first letter after 'rv64' should be 'e', 'i', 'g' or 'y'");
+
+  // rv32yi/ye fail because rv32y implicitly enables i, and i/e are not valid
+  // extensions to follow it.
+  for (StringRef Input : {"rv32yi", "rv64yi"}) {
+    EXPECT_EQ(
+        toString(RISCVISAInfo::parseArchString(Input, true, false).takeError()),
+        "invalid standard user-level extension 'i'");
+  }
+  // TODO: arch string syntax for RVE+RVY (and y in non-first position) will be
+  // included following conclusion of
+  // https://lists.riscv.org/g/tech-unprivileged/message/1134
+  for (StringRef Input : {"rv32ye", "rv64ye"}) {
+    EXPECT_EQ(
+        toString(RISCVISAInfo::parseArchString(Input, true, false).takeError()),
+        "invalid standard user-level extension 'e'");
+  }
+  for (StringRef Input : {"rv32y0p98i", "rv64y0p98i"}) {
+    EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
+              "invalid standard user-level extension 'i'");
+  }
+  for (StringRef Input : {"rv32y0p98e", "rv64y0p98e"}) {
+    EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
+              "invalid standard user-level extension 'e'");
+  }
+}
+
+TEST(ParseArchString, RejectsInvalidYPosition) {
+  // y in non-first position is rejected.
+  for (StringRef Input :
+       {"rv32ey0p98", "rv64ey0p98", "rv32iy0p98", "rv64iy0p98"}) {
+    EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
+              "invalid standard user-level extension 'y'");
+  }
+  for (StringRef Input : {"rv32ey", "rv64ey", "rv32iy", "rv64iy"}) {
+    EXPECT_EQ(
+        toString(RISCVISAInfo::parseArchString(Input, true, false).takeError()),
+        "invalid standard user-level extension 'y'");
+  }
+}
+
+TEST(ParseArchString, MissingBaseISA) {
+  // With version check enabled (default), we must specify the version for
+  // experimental extension 'y'.
+  auto MaybeRV32Y = RISCVISAInfo::parseArchString("rv32y0p98", true);
+  ASSERT_THAT_EXPECTED(MaybeRV32Y, Succeeded());
+  RISCVISAInfo &InfoRV32Y = **MaybeRV32Y;
+  const auto &ExtsRV32Y = InfoRV32Y.getExtensions();
+  EXPECT_EQ(ExtsRV32Y.size(), 2UL); // i, y
+  EXPECT_TRUE(ExtsRV32Y.at("i") == (RISCVISAUtils::ExtensionVersion{2, 1}));
+  EXPECT_TRUE(ExtsRV32Y.at("y") == (RISCVISAUtils::ExtensionVersion{0, 98}));
+  EXPECT_EQ(InfoRV32Y.getXLen(), 32U);
+
+  // rv32y0p98m should succeed and contain i, m, y0p98, zmmul
+  auto MaybeRV32YM = RISCVISAInfo::parseArchString("rv32y0p98m", true);
+  ASSERT_THAT_EXPECTED(MaybeRV32YM, Succeeded());
+  RISCVISAInfo &InfoRV32YM = **MaybeRV32YM;
+  EXPECT_EQ(InfoRV32YM.getExtensions().size(), 4UL); // i, m, y, zmmul
+  EXPECT_TRUE(InfoRV32YM.getExtensions().at("i") ==
+              (RISCVISAUtils::ExtensionVersion{2, 1}));
+  EXPECT_TRUE(InfoRV32YM.getExtensions().at("m") ==
+              (RISCVISAUtils::ExtensionVersion{2, 0}));
+  EXPECT_TRUE(InfoRV32YM.getExtensions().at("y") ==
+              (RISCVISAUtils::ExtensionVersion{0, 98}));
+
+  // We can also parse it without version if we disable the version check.
+  auto MaybeRV32YNoVal = RISCVISAInfo::parseArchString("rv32y", true, false);
+  ASSERT_THAT_EXPECTED(MaybeRV32YNoVal, Succeeded());
+  EXPECT_EQ((*MaybeRV32YNoVal)->getExtensions().size(), 2UL);
+
+  auto MaybeRV64Y = RISCVISAInfo::parseArchString("rv64y0p98", true);
+  ASSERT_THAT_EXPECTED(MaybeRV64Y, Succeeded());
+  RISCVISAInfo &InfoRV64Y = **MaybeRV64Y;
+  const auto &ExtsRV64Y = InfoRV64Y.getExtensions();
+  EXPECT_EQ(ExtsRV64Y.size(), 2UL); // i, y
+  EXPECT_TRUE(ExtsRV64Y.at("i") == (RISCVISAUtils::ExtensionVersion{2, 1}));
+  EXPECT_TRUE(ExtsRV64Y.at("y") == (RISCVISAUtils::ExtensionVersion{0, 98}));
+  EXPECT_EQ(InfoRV64Y.getXLen(), 64U);
 }
 
 TEST(ParseArchString, RejectsUnsupportedBaseISA) {
   for (StringRef Input : {"rv128i", "rv128g"}) {
-    EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
-              "string must begin with rv32{i,e,g}, rv64{i,e,g}, or a supported "
-              "profile name");
+    EXPECT_EQ(
+        toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
+        "string must begin with rv32{i,e,g,y}, rv64{i,e,g,y}, or a supported "
+        "profile name");
   }
 }
 
@@ -479,7 +558,7 @@ TEST(ParseArchString, AcceptsAmbiguousFromRelaxExtensions) {
 TEST(ParseArchString, RejectsRelaxExtensionsNotStartWithEorIorG) {
   EXPECT_EQ(
       toString(RISCVISAInfo::parseArchString("rv32zba_im", true).takeError()),
-      "first letter after 'rv32' should be 'e', 'i' or 'g'");
+      "first letter after 'rv32' should be 'e', 'i', 'g' or 'y'");
 }
 
 TEST(ParseArchString,
@@ -581,6 +660,10 @@ TEST(ParseArchString, RejectsUnrecognizedVersionForExperimentalExtension) {
       toString(RISCVISAInfo::parseArchString("rv64izibi9p9", true).takeError()),
       "unsupported version number 9.9 for experimental extension 'zibi' "
       "(this compiler supports 0.1)");
+  EXPECT_EQ(
+      toString(RISCVISAInfo::parseArchString("rv64y0p97", true).takeError()),
+      "unsupported version number 0.97 for experimental extension 'y' "
+      "(this compiler supports 0.98)");
 }
 
 TEST(ParseArchString, RejectsExtensionVersionForG) {
@@ -674,6 +757,18 @@ TEST(ParseArchString, RejectsConflictingExtensions) {
     EXPECT_THAT(Error, ::testing::EndsWith("' is only supported for 'rv32'"));
     EXPECT_THAT(Error, ::testing::HasSubstr(ConflictingExt));
   }
+  EXPECT_EQ(
+      toString(
+          RISCVISAInfo::parseArchString("rv32y0p98_zcf", true).takeError()),
+      "'zcf' is incompatible with rv32y base");
+  EXPECT_EQ(
+      toString(
+          RISCVISAInfo::parseArchString("rv32y0p98_zclsd", true).takeError()),
+      "'zclsd' is incompatible with rv32y base");
+  EXPECT_EQ(
+      toString(
+          RISCVISAInfo::parseArchString("rv64y0p98_zcd", true).takeError()),
+      "'zcd' is incompatible with rv64y base");
 
   // In these ISA strings, the non-zcd extension should be last, after an
   // underscore.
@@ -740,9 +835,10 @@ TEST(ParseArchString, MissingDepency) {
 
 TEST(ParseArchString, RejectsUnrecognizedProfileNames) {
   for (StringRef Input : {"rvi23u99", "rvz23u64", "rva99u32"}) {
-    EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
-              "string must begin with rv32{i,e,g}, rv64{i,e,g}, or a supported "
-              "profile name");
+    EXPECT_EQ(
+        toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
+        "string must begin with rv32{i,e,g,y}, rv64{i,e,g,y}, or a supported "
+        "profile name");
   }
 }
 
@@ -1028,6 +1124,51 @@ TEST(ParseArchString, ZcaImpliesC) {
   EXPECT_EQ(ExtsRV64IDZca.count("c"), 0U);
 }
 
+TEST(ParseArchString, RVYZcaImpliesC) {
+  // RV32Y without D (maybe with F): Zca implies C (Zcf opcodes remapped in Y).
+  for (StringRef Input : {"rv32y_zca", "rv32yf_zca"}) {
+    auto ISAInfo = RISCVISAInfo::parseArchString(Input, true, false);
+    ASSERT_THAT_EXPECTED(ISAInfo, Succeeded());
+    const auto &Exts = (*ISAInfo)->getExtensions();
+    EXPECT_EQ(Exts.count("c"), 1U);
+    EXPECT_EQ(Exts.count("zca"), 1U);
+    EXPECT_EQ(Exts.count("zcf"), 0U);
+    EXPECT_EQ(Exts.count("zcd"), 0U);
+  }
+  // RV32Y with D: Zca+Zcd implies C (Zcf opcodes remapped in Y)
+  for (StringRef Input : {"rv32yfd_zca_zcd"}) {
+    auto MaybeRV32YDZcaZcd = RISCVISAInfo::parseArchString(Input, true, false);
+    ASSERT_THAT_EXPECTED(MaybeRV32YDZcaZcd, Succeeded());
+    const auto &ExtsRV32YDZcaZcd = (*MaybeRV32YDZcaZcd)->getExtensions();
+    EXPECT_EQ(ExtsRV32YDZcaZcd.count("c"), 1U);
+    EXPECT_EQ(ExtsRV32YDZcaZcd.count("zca"), 1U);
+    EXPECT_EQ(ExtsRV32YDZcaZcd.count("zcf"), 0U);
+    EXPECT_EQ(ExtsRV32YDZcaZcd.count("zcd"), 1U);
+  }
+  // RV32Y with D but no Zcd: no C
+  for (StringRef Input : {"rv32yfd_zca"}) {
+    auto MaybeRV32YDZcaZcd = RISCVISAInfo::parseArchString(Input, true, false);
+    ASSERT_THAT_EXPECTED(MaybeRV32YDZcaZcd, Succeeded());
+    const auto &ExtsRV32YDZcaZcd = (*MaybeRV32YDZcaZcd)->getExtensions();
+    EXPECT_EQ(ExtsRV32YDZcaZcd.count("c"), 0U);
+    EXPECT_EQ(ExtsRV32YDZcaZcd.count("zca"), 1U);
+    EXPECT_EQ(ExtsRV32YDZcaZcd.count("zcf"), 0U);
+    EXPECT_EQ(ExtsRV32YDZcaZcd.count("zcd"), 0U);
+  }
+
+  // RV64Y: Zca always implies C (regardless of F/D) since Zcf opcodes are
+  // repurposed for 64-bit loads/stores and Zcd for capability loads/stores.
+  for (StringRef Input : {"rv64y_zca", "rv64yf_zca", "rv64yfd_zca"}) {
+    auto ISAInfo = RISCVISAInfo::parseArchString(Input, true, false);
+    ASSERT_THAT_EXPECTED(ISAInfo, Succeeded());
+    const auto &Exts = (*ISAInfo)->getExtensions();
+    EXPECT_EQ(Exts.count("c"), 1U);
+    EXPECT_EQ(Exts.count("zca"), 1U);
+    EXPECT_EQ(Exts.count("zcf"), 0U);
+    EXPECT_EQ(Exts.count("zcd"), 0U);
+  }
+}
+
 TEST(ParseArchString, ZcaZcbZcmpZcmtImpliesZce) {
   // Test Zca+Zcb+Zcmp+Zcmt implies Zce behavior.
 
@@ -1112,6 +1253,41 @@ TEST(ParseArchString, ZcaZcbZcmpZcmtImpliesZce) {
   EXPECT_EQ(ExtsRV64IFZcaZcbZcmpZcmt.count("zce"), 1U);
   EXPECT_EQ(ExtsRV64IFZcaZcbZcmpZcmt.count("zcmp"), 1U);
   EXPECT_EQ(ExtsRV64IFZcaZcbZcmpZcmt.count("zcmt"), 1U);
+
+  // RV32Y Zca+Zcb+Zcmp+Zcmt implies Zce regardless of F (Zcf incompatible)
+  for (StringRef Input : {"rv32y_zca_zcb_zcmp_zcmt", "rv32yf_zca_zcb_zcmp_zcmt",
+                          "rv32yfd_zca_zcb_zcmp_zcmt"}) {
+    auto ISAInfo = RISCVISAInfo::parseArchString(Input, true, false);
+    ASSERT_THAT_EXPECTED(ISAInfo, Succeeded());
+    const auto &Exts = (*ISAInfo)->getExtensions();
+    EXPECT_EQ(Exts.count("i"), 1U);
+    EXPECT_EQ(Exts.count("y"), 1U);
+    EXPECT_EQ(Exts.count("c"), !Exts.count("d")); // Zcd missing
+    EXPECT_EQ(Exts.count("zicsr"), 1U);
+    EXPECT_EQ(Exts.count("zca"), 1U);
+    EXPECT_EQ(Exts.count("zcb"), 1U);
+    EXPECT_EQ(Exts.count("zce"), 1U);
+    EXPECT_EQ(Exts.count("zcmp"), 1U);
+    EXPECT_EQ(Exts.count("zcmt"), 1U);
+    EXPECT_EQ(Exts.size(), 9UL + Exts.count("f"));
+  }
+  // RV64Y Zca+Zcb implies Zce regardless of F (Zcf+Zcmp+Zcmt incompatible)
+  for (StringRef Input :
+       {"rv64y_zca_zcb", "rv64yf_zca_zcb", "rv64yfd_zca_zcb"}) {
+    auto ISAInfo = RISCVISAInfo::parseArchString(Input, true, false);
+    ASSERT_THAT_EXPECTED(ISAInfo, Succeeded());
+    const auto &Exts = (*ISAInfo)->getExtensions();
+    EXPECT_EQ(Exts.count("i"), 1U);
+    EXPECT_EQ(Exts.count("y"), 1U);
+    EXPECT_EQ(Exts.count("c"), 1U);
+    EXPECT_EQ(Exts.count("zicsr"), Exts.count("f"));
+    EXPECT_EQ(Exts.count("zca"), 1U);
+    EXPECT_EQ(Exts.count("zcb"), 1U);
+    EXPECT_EQ(Exts.count("zce"), 1U);
+    EXPECT_EQ(Exts.count("zcmp"), 0U);
+    EXPECT_EQ(Exts.count("zcmt"), 0U);
+    EXPECT_EQ(Exts.size(), 6UL + Exts.count("d") + 2 * Exts.count("f"));
+  }
 }
 
 TEST(isSupportedExtensionWithVersion, AcceptsSingleExtensionWithVersion) {

>From 62889d409f21b5d4cfe1dd8c7d0debc882c1b306 Mon Sep 17 00:00:00 2001
From: Alexander Richardson <alexrichardson at google.com>
Date: Sat, 6 Jun 2026 11:44:14 -0700
Subject: [PATCH 2/4] Update llvm/lib/TargetParser/RISCVISAInfo.cpp

Co-authored-by: Craig Topper <craig.topper at sifive.com>
---
 llvm/lib/TargetParser/RISCVISAInfo.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp
index 363ae4148a621..09bbf7cd93a10 100644
--- a/llvm/lib/TargetParser/RISCVISAInfo.cpp
+++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp
@@ -632,11 +632,9 @@ RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension,
       return std::move(E);
 
     ISAInfo->Exts["y"] = {Major, Minor};
-    if (auto IVersion = findDefaultVersion("i")) {
-      ISAInfo->Exts["i"] = {IVersion->Major, IVersion->Minor};
-    } else {
-      llvm_unreachable("Default 'i' extension version not found?");
-    }
+    auto IVersion = findDefaultVersion("i");
+    assert(IVersion && "Default 'i' extension version not found?");
+    ISAInfo->Exts["i"] = {IVersion->Major, IVersion->Minor};
     break;
   case 'g':
     // g expands to extensions in RISCVGImplications.

>From 30b43bcebde1ebd604a12f7d94415d73da991f4b Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Sun, 7 Jun 2026 21:56:00 -0700
Subject: [PATCH 3/4] clang-format

Created using spr 1.3.8-beta.1
---
 llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
index f93c88c66dea1..21e859a7d72a0 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
@@ -90,7 +90,7 @@ void RISCV::updateCZceFeatureImplications(MCSubtargetInfo &STI) {
       STI.hasFeature(RISCV::FeatureStdExtD) &&
       !STI.hasFeature(RISCV::FeatureStdExtZcd) &&
       !(STI.hasFeature(RISCV::Feature64Bit) &&
-       STI.hasFeature(RISCV::FeatureStdExtY)))
+        STI.hasFeature(RISCV::FeatureStdExtY)))
     STI.ToggleFeature(RISCV::FeatureStdExtZcd);
 
   // Add Zcf if F and C or Zce are enabled on RV32 and Y is not enabled.

>From 5dd67cf5850471e5c83c12d7c991ef301c1d2c98 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Sun, 5 Jul 2026 00:43:47 -0700
Subject: [PATCH 4/4] address review feedback

Created using spr 1.3.8-beta.1
---
 .../RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp  | 52 +++++++------------
 llvm/lib/TargetParser/RISCVISAInfo.cpp        | 18 +++----
 llvm/test/MC/RISCV/rvy-build-attributes.s     | 24 ++++-----
 llvm/test/MC/RISCV/rvy-invalid-attributes.s   | 26 +++++-----
 4 files changed, 53 insertions(+), 67 deletions(-)

diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
index 21e859a7d72a0..1cd95f3677c45 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
@@ -114,24 +114,17 @@ void RISCV::updateCZceFeatureImplications(MCSubtargetInfo &STI) {
   //   - D: Zca + Zcd/Y implies C
   if (!STI.hasFeature(RISCV::FeatureStdExtC) &&
       STI.hasFeature(RISCV::FeatureStdExtZca)) {
-    bool ShouldAddC = false;
-    if (!STI.hasFeature(RISCV::Feature64Bit)) {
-      if (STI.hasFeature(RISCV::FeatureStdExtD))
-        ShouldAddC = STI.hasFeature(RISCV::FeatureStdExtZcd) &&
-                     (STI.hasFeature(RISCV::FeatureStdExtY) ||
-                      STI.hasFeature(RISCV::FeatureStdExtZcf));
-      else if (STI.hasFeature(RISCV::FeatureStdExtF))
-        ShouldAddC = STI.hasFeature(RISCV::FeatureStdExtY) ||
-                     STI.hasFeature(RISCV::FeatureStdExtZcf);
-      else
-        ShouldAddC = true;
-    } else {
-      if (STI.hasFeature(RISCV::FeatureStdExtD))
-        ShouldAddC = STI.hasFeature(RISCV::FeatureStdExtY) ||
-                     STI.hasFeature(RISCV::FeatureStdExtZcd);
-      else
-        ShouldAddC = true;
-    }
+    bool ShouldAddC;
+    if (!STI.hasFeature(RISCV::Feature64Bit))
+      ShouldAddC = (!STI.hasFeature(RISCV::FeatureStdExtD) ||
+                    STI.hasFeature(RISCV::FeatureStdExtZcd)) &&
+                   (STI.hasFeature(RISCV::FeatureStdExtY) ||
+                    !STI.hasFeature(RISCV::FeatureStdExtF) ||
+                    STI.hasFeature(RISCV::FeatureStdExtZcf));
+    else
+      ShouldAddC = STI.hasFeature(RISCV::FeatureStdExtY) ||
+                   !STI.hasFeature(RISCV::FeatureStdExtD) ||
+                   STI.hasFeature(RISCV::FeatureStdExtZcd);
     if (ShouldAddC)
       STI.ToggleFeature(RISCV::FeatureStdExtC);
   }
@@ -142,23 +135,16 @@ void RISCV::updateCZceFeatureImplications(MCSubtargetInfo &STI) {
   //   - F: Zca+Zcb+Zcmp+Zcmt + Zcf/Y implies Zce
   // For RV64:
   //   - Zca+Zcb+Zcmp+Zcmt alone implies Zce
-  //   - RV64Y: Zca+Zcb alone implies Zce
+  //   - Note: RV64Y is incompatible with Zcmp/Zcmt, never implies Zce
   if (!STI.hasFeature(RISCV::FeatureStdExtZce) &&
       STI.hasFeature(RISCV::FeatureStdExtZca) &&
-      STI.hasFeature(RISCV::FeatureStdExtZcb)) {
-    bool ShouldAddZce = false;
-    if (STI.hasFeature(RISCV::FeatureStdExtZcmp) &&
-        STI.hasFeature(RISCV::FeatureStdExtZcmt)) {
-      if (!STI.hasFeature(RISCV::Feature64Bit)) {
-        ShouldAddZce = !STI.hasFeature(RISCV::FeatureStdExtF) ||
-                       STI.hasFeature(RISCV::FeatureStdExtZcf) ||
-                       STI.hasFeature(RISCV::FeatureStdExtY);
-      } else {
-        ShouldAddZce = true;
-      }
-    }
-
-    if (ShouldAddZce)
+      STI.hasFeature(RISCV::FeatureStdExtZcb) &&
+      STI.hasFeature(RISCV::FeatureStdExtZcmp) &&
+      STI.hasFeature(RISCV::FeatureStdExtZcmt)) {
+    if (STI.hasFeature(RISCV::Feature64Bit) ||
+        STI.hasFeature(RISCV::FeatureStdExtY) ||
+        !STI.hasFeature(RISCV::FeatureStdExtF) ||
+        STI.hasFeature(RISCV::FeatureStdExtZcf))
       STI.ToggleFeature(RISCV::FeatureStdExtZce);
   }
 }
diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp
index cabdf6241662f..3e9664e6ba070 100644
--- a/llvm/lib/TargetParser/RISCVISAInfo.cpp
+++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp
@@ -817,13 +817,13 @@ Error RISCVISAInfo::checkDependency() {
 
   if (Exts.count("y") != 0) {
     if (XLen == 32) {
-      // On RVY32 systems the zclsd/zcf encodings are used for y load/stores.
+      // On RV32Y systems the zclsd/zcf encodings are used for y load/stores.
       if (Exts.count("zclsd") != 0)
         return getBaseIncompatibleError("zclsd", "rv32y");
       if (Exts.count("zcf") != 0)
         return getBaseIncompatibleError("zcf", "rv32y");
     } else {
-      // On RVY64 systems the zcd encodings are used for y load/stores.
+      // On RV64Y systems the zcd encodings are used for y load/stores.
       if (Exts.count("zcd") != 0)
         return getBaseIncompatibleError("zcd", "rv64y");
       for (auto Ext : ZcdOverlaps)
@@ -943,14 +943,14 @@ void RISCVISAInfo::updateImplication() {
     }
   }
 
-  if (!Exts.count("zce") && Exts.count("zca") && Exts.count("zcb")) {
+  if (!Exts.count("zce") && Exts.count("zca") && Exts.count("zcb") &&
+      Exts.count("zcmp") && Exts.count("zcmt")) {
     bool ShouldAddZce = false;
-    if (Exts.count("zcmp") && Exts.count("zcmt")) {
-      if (XLen == 32) {
-        ShouldAddZce = !Exts.count("f") || Exts.count("zcf") || Exts.count("y");
-      } else if (XLen == 64) {
-        ShouldAddZce = true;
-      }
+    if (XLen == 32) {
+      ShouldAddZce = !Exts.count("f") || Exts.count("zcf") || Exts.count("y");
+    } else if (XLen == 64) {
+      // Zce is incompatible with RV64Y, only add it if Y is not enabled.
+      ShouldAddZce = !Exts.count("y");
     }
     if (ShouldAddZce)
       Exts["zce"] = *findDefaultVersion("zce");
diff --git a/llvm/test/MC/RISCV/rvy-build-attributes.s b/llvm/test/MC/RISCV/rvy-build-attributes.s
index 4a754ea91a398..2b1c8cae53c6f 100644
--- a/llvm/test/MC/RISCV/rvy-build-attributes.s
+++ b/llvm/test/MC/RISCV/rvy-build-attributes.s
@@ -1,23 +1,23 @@
 # RUN: llvm-mc %s -triple=riscv64 -filetype=asm -riscv-add-build-attributes \
-# RUN:   -mattr=+experimental-y,+c,+d | FileCheck %s --check-prefixes=RV64Y-CD
+# RUN:   -mattr=+experimental-y,+d,+c | FileCheck %s --check-prefix=RV64YDC
 # RUN: llvm-mc %s -triple=riscv32 -filetype=asm -riscv-add-build-attributes \
-# RUN:   -mattr=+experimental-y,+c,+f | FileCheck %s --check-prefixes=RV32Y-CF
+# RUN:   -mattr=+experimental-y,+f,+c | FileCheck %s --check-prefix=RV32YFC
 # RUN: llvm-mc %s -triple=riscv32 -filetype=asm -riscv-add-build-attributes \
-# RUN:   -mattr=+experimental-y,+zca,+f | FileCheck %s --check-prefixes=RV32Y-ZCA-F
+# RUN:   -mattr=+experimental-y,+zca,+f | FileCheck %s --check-prefix=RV32Y-ZCA-F
 # RUN: llvm-mc %s -triple=riscv64 -filetype=asm -riscv-add-build-attributes \
-# RUN:   -mattr=+experimental-y,+zca,+d | FileCheck %s --check-prefixes=RV64Y-ZCA-D
+# RUN:   -mattr=+experimental-y,+zca,+d | FileCheck %s --check-prefix=RV64Y-ZCA-D
 # RUN: llvm-mc %s -triple=riscv64 -filetype=asm -riscv-add-build-attributes \
-# RUN:   -mattr=+experimental-y,+zca,+zcb | FileCheck %s --check-prefixes=RV64Y-ZCA-ZCB
+# RUN:   -mattr=+experimental-y,+zca,+zcb | FileCheck %s --check-prefix=RV64Y-ZCA-ZCB
 # RUN: llvm-mc %s -triple=riscv32 -filetype=asm -riscv-add-build-attributes \
-# RUN:   -mattr=+experimental-y,+zce | FileCheck %s --check-prefixes=RV32Y-ZCE
+# RUN:   -mattr=+experimental-y,+zce | FileCheck %s --check-prefix=RV32Y-ZCE
 # RUN: llvm-mc %s -triple=riscv32 -filetype=asm -riscv-add-build-attributes \
-# RUN:   -mattr=+experimental-y,+zce,+f | FileCheck %s --check-prefixes=RV32Y-ZCE-F
+# RUN:   -mattr=+experimental-y,+zce,+f | FileCheck %s --check-prefix=RV32Y-ZCE-F
 
-## RV64Y + C + D: y is enabled, so zcd is not implied.
-# RV64Y-CD: .attribute 5, "rv64i2p1_f2p2_d2p2_c2p0_y0p98_zicsr2p0_zca1p0"
+## RV64Y + D + C: y is enabled, so zcd is not implied.
+# RV64YDC: .attribute 5, "rv64i2p1_f2p2_d2p2_c2p0_y0p98_zicsr2p0_zca1p0"
 
-## RV32Y + C + F: y is enabled, so zcf is not implied.
-# RV32Y-CF: .attribute 5, "rv32i2p1_f2p2_c2p0_y0p98_zicsr2p0_zca1p0"
+## RV32Y + F + C: y is enabled, so zcf is not implied.
+# RV32YFC: .attribute 5, "rv32i2p1_f2p2_c2p0_y0p98_zicsr2p0_zca1p0"
 
 ## RV32Y + ZCA + F: zca + y implies c on RV32 (y replaces zcf in the implication).
 # RV32Y-ZCA-F: .attribute 5, "rv32i2p1_f2p2_c2p0_y0p98_zicsr2p0_zca1p0"
@@ -32,4 +32,4 @@
 # RV32Y-ZCE: .attribute 5, "rv32i2p1_c2p0_y0p98_zicsr2p0_zca1p0_zcb1p0_zce1p0_zcmp1p0_zcmt1p0"
 
 ## RV32Y + ZCE + F: y is enabled, so zcf is not implied by zce + f. zca + y + f implies c.
-# RV32Y-ZCE-F: .attribute 5, "rv32i2p1_f2p2_c2p0_y0p98_zicsr2p0_zca1p0_zcb1p0_zce1p0_zcmp1p0_zcmt1p0"
+# RV32Y-ZCE-F: .attribute 5, "rv32i2p1_f2p2_c2p0_y0p98_zicsr2p0_zca1p0_zcb1p0_zce1p0_zcmp1p0_zcmt1p0"
\ No newline at end of file
diff --git a/llvm/test/MC/RISCV/rvy-invalid-attributes.s b/llvm/test/MC/RISCV/rvy-invalid-attributes.s
index 3f37f832fff36..7ba9deb840f54 100644
--- a/llvm/test/MC/RISCV/rvy-invalid-attributes.s
+++ b/llvm/test/MC/RISCV/rvy-invalid-attributes.s
@@ -1,18 +1,18 @@
-# RUN: not llvm-mc -triple=riscv64 -mattr=+experimental-y,+zce /dev/null 2>&1 | FileCheck %s --check-prefix=CLI-RV64Y-ZCE
-# RUN: not llvm-mc -triple=riscv32 -mattr=+experimental-y,+zcf /dev/null 2>&1 | FileCheck %s --check-prefix=CLI-RV32Y-ZCF
-# RUN: not llvm-mc -triple=riscv32 -mattr=+experimental-y,+zclsd /dev/null 2>&1 | FileCheck %s --check-prefix=CLI-RV32Y-ZCLSD
-# RUN: not llvm-mc -triple=riscv64 -mattr=+experimental-y,+zcd /dev/null 2>&1 | FileCheck %s --check-prefix=CLI-RV64Y-ZCD
-# RUN: not llvm-mc -triple=riscv64 -mattr=+experimental-y,+c,+zcmp /dev/null 2>&1 | FileCheck %s --check-prefix=CLI-RV64Y-C-ZCMP
-# RUN: not llvm-mc -triple=riscv64 -mattr=+experimental-y,+zca,+zcmp /dev/null 2>&1 | FileCheck %s --check-prefix=CLI-RV64Y-ZCA-ZCMP
+# RUN: not llvm-mc -triple=riscv64 -mattr=+experimental-y,+zce /dev/null 2>&1 | FileCheck %s --check-prefix=RV64Y-ZCE
+# RUN: not llvm-mc -triple=riscv32 -mattr=+experimental-y,+zcf /dev/null 2>&1 | FileCheck %s --check-prefix=RV32Y-ZCF
+# RUN: not llvm-mc -triple=riscv32 -mattr=+experimental-y,+zclsd /dev/null 2>&1 | FileCheck %s --check-prefix=RV32Y-ZCLSD
+# RUN: not llvm-mc -triple=riscv64 -mattr=+experimental-y,+zcd /dev/null 2>&1 | FileCheck %s --check-prefix=RV64Y-ZCD
+# RUN: not llvm-mc -triple=riscv64 -mattr=+experimental-y,+c,+zcmp /dev/null 2>&1 | FileCheck %s --check-prefix=RV64Y-C-ZCMP
+# RUN: not llvm-mc -triple=riscv64 -mattr=+experimental-y,+zca,+zcmp /dev/null 2>&1 | FileCheck %s --check-prefix=RV64Y-ZCA-ZCMP
 
 # RUN: not llvm-mc -triple=riscv64 < %s 2>&1 | FileCheck %s --check-prefix=ATTR --implicit-check-not="error:"
 
-# CLI-RV64Y-ZCE: LLVM ERROR: 'zcmt' is incompatible with rv64y base
-# CLI-RV32Y-ZCF: LLVM ERROR: 'zcf' is incompatible with rv32y base
-# CLI-RV32Y-ZCLSD: LLVM ERROR: 'zclsd' is incompatible with rv32y base
-# CLI-RV64Y-ZCD: LLVM ERROR: 'zcd' is incompatible with rv64y base
-# CLI-RV64Y-C-ZCMP: LLVM ERROR: 'zcmp' is incompatible with rv64y base
-# CLI-RV64Y-ZCA-ZCMP: LLVM ERROR: 'zcmp' is incompatible with rv64y base
+# RV64Y-ZCE: LLVM ERROR: 'zcmt' is incompatible with rv64y base
+# RV32Y-ZCF: LLVM ERROR: 'zcf' is incompatible with rv32y base
+# RV32Y-ZCLSD: LLVM ERROR: 'zclsd' is incompatible with rv32y base
+# RV64Y-ZCD: LLVM ERROR: 'zcd' is incompatible with rv64y base
+# RV64Y-C-ZCMP: LLVM ERROR: 'zcmp' is incompatible with rv64y base
+# RV64Y-ZCA-ZCMP: LLVM ERROR: 'zcmp' is incompatible with rv64y base
 
 .attribute arch, "rv64y0p98_zce"
 # ATTR: error: invalid arch name 'rv64y0p98_zce', 'zcmt' is incompatible with rv64y base
@@ -30,4 +30,4 @@
 # ATTR: error: invalid arch name 'rv64y0p98_c_zcmp', 'zcmp' is incompatible with rv64y base
 
 .attribute arch, "rv64y0p98_zca_zcmp"
-# ATTR: error: invalid arch name 'rv64y0p98_zca_zcmp', 'zcmp' is incompatible with rv64y base
+# ATTR: error: invalid arch name 'rv64y0p98_zca_zcmp', 'zcmp' is incompatible with rv64y base
\ No newline at end of file



More information about the llvm-commits mailing list