Author: Alexander Richardson
Date: 2026-07-05T02:44:41-07:00
New Revision: 37f7fc7c05865e1c8ebcda915d379963b48c3e69

URL: 
https://github.com/llvm/llvm-project/commit/37f7fc7c05865e1c8ebcda915d379963b48c3e69
DIFF: 
https://github.com/llvm/llvm-project/commit/37f7fc7c05865e1c8ebcda915d379963b48c3e69.diff

LOG: [RISC-V][RVY] Initial ISAInfo support for RVY

RVY is a new base ISA, so the syntax to enable it is rv32y/rv64y.
Since the compressed instructions reuse the space for Zcf (RV32) and
Zcd (RV64), those are marked as incompatible and the logic for C/Zce
is updated as part of this PR.
RVY can also extend RVE instead of RVY (as is done for CHERIoT), but the
official arch string syntax for that has not been finalized yet.
Related discussion on that includes the "long base name" proposal:
https://lists.riscv.org/g/tech-unprivileged/message/1134

Reviewers: topperc, lenary, jrtc27

Pull Request: https://github.com/llvm/llvm-project/pull/201931

Added: 
    llvm/test/MC/RISCV/rvy-build-attributes.s
    llvm/test/MC/RISCV/rvy-invalid-attributes.s

Modified: 
    clang/test/Driver/riscv-arch.c
    clang/test/Driver/riscv-profiles.c
    llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
    llvm/lib/TargetParser/RISCVISAInfo.cpp
    llvm/test/MC/RISCV/invalid-attribute.s
    llvm/unittests/TargetParser/RISCVISAInfoTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/test/Driver/riscv-arch.c b/clang/test/Driver/riscv-arch.c
index f3b532d0b9bff..0fd389faa969f 100644
--- a/clang/test/Driver/riscv-arch.c
+++ b/clang/test/Driver/riscv-arch.c
@@ -220,12 +220,12 @@
 // RUN: not %clang --target=riscv32-unknown-elf -march=unknown -### %s \
 // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-STR %s
 // RV32-STR: error: invalid arch name 'unknown',
-// RV32-STR: string must begin with rv32{i,e,g}, rv64{i,e,g}, or a supported 
profile name
+// RV32-STR: string must begin with rv32{i,e,g,y}, rv64{i,e,g,y}, or a 
supported profile name
 
 // RUN: not %clang --target=riscv32-unknown-elf -march=rv32q -### %s \
 // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-LETTER %s
 // RV32-LETTER: error: invalid arch name 'rv32q',
-// RV32-LETTER: first letter after 'rv32' should be 'e', 'i' or 'g'
+// RV32-LETTER: first letter after 'rv32' should be 'e', 'i', 'g' or 'y'
 
 // RUN: not %clang --target=riscv32-unknown-elf -march=rv32izvl64b -### %s \
 // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-ZVL64B-ER %s
@@ -240,12 +240,12 @@
 // RUN: not %clang --target=riscv32-unknown-elf -march=rv32xabc -### %s \
 // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32X %s
 // RV32X: error: invalid arch name 'rv32xabc',
-// RV32X: first letter after 'rv32' should be 'e', 'i' or 'g'
+// RV32X: first letter after 'rv32' should be 'e', 'i', 'g' or 'y'
 
 // RUN: not %clang --target=riscv32-unknown-elf -march=rv32sabc -### %s \
 // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32S %s
 // RV32S: error: invalid arch name 'rv32sabc',
-// RV32S: first letter after 'rv32' should be 'e', 'i' or 'g'
+// RV32S: first letter after 'rv32' should be 'e', 'i', 'g' or 'y'
 
 // RUN: not %clang --target=riscv32-unknown-elf -march=rv32ix -### %s \
 // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32X-NAME %s

diff  --git a/clang/test/Driver/riscv-profiles.c 
b/clang/test/Driver/riscv-profiles.c
index c87ec5a27822c..9639c03bdbe26 100644
--- a/clang/test/Driver/riscv-profiles.c
+++ b/clang/test/Driver/riscv-profiles.c
@@ -329,7 +329,7 @@
 // PROFILE-WITH-ADDITIONAL: "-target-feature" "+zkt"
 
 // RUN: not %clang --target=riscv64 -### -c %s 2>&1 -march=rva19u64_zfa | 
FileCheck -check-prefix=INVALID-PROFILE %s
-// INVALID-PROFILE: error: invalid arch name 'rva19u64_zfa', string must begin 
with rv32{i,e,g}, rv64{i,e,g}, or a supported profile name
+// INVALID-PROFILE: error: invalid arch name 'rva19u64_zfa', string must begin 
with rv32{i,e,g,y}, rv64{i,e,g,y}, or a supported profile name
 
 // RUN: not %clang --target=riscv64 -### -c %s 2>&1 -march=rva22u64zfa | 
FileCheck -check-prefix=INVALID-ADDITIONAL %s
 // INVALID-ADDITIONAL: error: invalid arch name 'rva22u64zfa', additional 
extensions must be after separator '_'

diff  --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp 
b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
index 30e5ad82b0a2c..1cd95f3677c45 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
@@ -85,15 +85,18 @@ createRISCVMCObjectFileInfo(MCContext &Ctx, bool PIC,
 }
 
 void RISCV::updateCZceFeatureImplications(MCSubtargetInfo &STI) {
-  // Add Zcd if C and D are enabled.
+  // Add Zcd if C and D are enabled and we aren't targeting 64-bit RVY.
   if (STI.hasFeature(RISCV::FeatureStdExtC) &&
       STI.hasFeature(RISCV::FeatureStdExtD) &&
-      !STI.hasFeature(RISCV::FeatureStdExtZcd))
+      !STI.hasFeature(RISCV::FeatureStdExtZcd) &&
+      !(STI.hasFeature(RISCV::Feature64Bit) &&
+        STI.hasFeature(RISCV::FeatureStdExtY)))
     STI.ToggleFeature(RISCV::FeatureStdExtZcd);
 
-  // Add Zcf if F and C or Zce are enabled on RV32.
+  // Add Zcf if F and C or Zce are enabled on RV32 and Y is not enabled.
   if (!STI.hasFeature(RISCV::FeatureStdExtZcf) &&
       !STI.hasFeature(RISCV::Feature64Bit) &&
+      !STI.hasFeature(RISCV::FeatureStdExtY) &&
       STI.hasFeature(RISCV::FeatureStdExtF) &&
       (STI.hasFeature(RISCV::FeatureStdExtC) ||
        STI.hasFeature(RISCV::FeatureStdExtZce)))
@@ -104,22 +107,24 @@ void RISCV::updateCZceFeatureImplications(MCSubtargetInfo 
&STI) {
   // (PR119122). The rule is:
   // For RV32:
   //   - No F and no D: Zca alone implies C
-  //   - F but no D: Zca + Zcf implies C
-  //   - F and D: Zca + Zcf + Zcd implies C
+  //   - F but no D: Zca + Zcf/Y implies C
+  //   - F and D: Zca + Zcf/Y + Zcd implies C
   // For RV64:
   //   - No D: Zca alone implies C
-  //   - D: Zca + Zcd implies C
+  //   - D: Zca + Zcd/Y implies C
   if (!STI.hasFeature(RISCV::FeatureStdExtC) &&
       STI.hasFeature(RISCV::FeatureStdExtZca)) {
-    bool ShouldAddC = false;
+    bool ShouldAddC;
     if (!STI.hasFeature(RISCV::Feature64Bit))
       ShouldAddC = (!STI.hasFeature(RISCV::FeatureStdExtD) ||
                     STI.hasFeature(RISCV::FeatureStdExtZcd)) &&
-                   (!STI.hasFeature(RISCV::FeatureStdExtF) ||
+                   (STI.hasFeature(RISCV::FeatureStdExtY) ||
+                    !STI.hasFeature(RISCV::FeatureStdExtF) ||
                     STI.hasFeature(RISCV::FeatureStdExtZcf));
     else
-      ShouldAddC = (!STI.hasFeature(RISCV::FeatureStdExtD) ||
-                    STI.hasFeature(RISCV::FeatureStdExtZcd));
+      ShouldAddC = STI.hasFeature(RISCV::FeatureStdExtY) ||
+                   !STI.hasFeature(RISCV::FeatureStdExtD) ||
+                   STI.hasFeature(RISCV::FeatureStdExtZcd);
     if (ShouldAddC)
       STI.ToggleFeature(RISCV::FeatureStdExtC);
   }
@@ -127,15 +132,17 @@ void RISCV::updateCZceFeatureImplications(MCSubtargetInfo 
&STI) {
   // Add Zce if Zca+Zcb+Zcmp+Zcmt are enabled and the conditions are met.
   // For RV32:
   //   - No F and no D: Zca+Zcb+Zcmp+Zcmt alone implies Zce
-  //   - F: Zca+Zcb+Zcmp+Zcmt + Zcf implies Zce
+  //   - F: Zca+Zcb+Zcmp+Zcmt + Zcf/Y implies Zce
   // For RV64:
   //   - Zca+Zcb+Zcmp+Zcmt 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) &&
       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 7b04b42f8d000..3e9664e6ba070 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,22 @@ 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};
+    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.
     if (!Arch.empty() && isDigit(Arch.front()))
@@ -730,6 +746,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 +815,23 @@ 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 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 RV64Y systems the zcd encodings are used for y load/stores.
+      if (Exts.count("zcd") != 0)
+        return getBaseIncompatibleError("zcd", "rv64y");
+      for (auto Ext : ZcdOverlaps)
+        if (Exts.count(Ext.str()))
+          return getBaseIncompatibleError(Ext, "rv64y");
+    }
+  }
+
   if (HasZcmp && HasXqccmp)
     return getIncompatibleError("zcmp", "xqccmp");
 
@@ -848,21 +885,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 +916,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;
     }
@@ -902,9 +947,10 @@ void RISCVISAInfo::updateImplication() {
       Exts.count("zcmp") && Exts.count("zcmt")) {
     bool ShouldAddZce = false;
     if (XLen == 32) {
-      ShouldAddZce = !Exts.count("f") || Exts.count("zcf");
+      ShouldAddZce = !Exts.count("f") || Exts.count("zcf") || Exts.count("y");
     } else if (XLen == 64) {
-      ShouldAddZce = true;
+      // 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/invalid-attribute.s 
b/llvm/test/MC/RISCV/invalid-attribute.s
index b3d7cda73b35c..3a955e013fe4b 100644
--- a/llvm/test/MC/RISCV/invalid-attribute.s
+++ b/llvm/test/MC/RISCV/invalid-attribute.s
@@ -11,7 +11,7 @@
 # CHECK: [[@LINE-1]]:12: error: attribute name not recognised: unknown
 
 .attribute arch, "foo"
-# CHECK: [[@LINE-1]]:18: error: invalid arch name 'foo', string must begin 
with rv32{i,e,g}, rv64{i,e,g}, or a supported profile name{{$}}
+# CHECK: [[@LINE-1]]:18: error: invalid arch name 'foo', string must begin 
with rv32{i,e,g,y}, rv64{i,e,g,y}, or a supported profile name{{$}}
 
 .attribute arch, "rv32i2p1_z2p0"
 # CHECK: [[@LINE-1]]:18: error: invalid arch name 'rv32i2p1_z2p0', standard 
user-level extension name missing after 'z'

diff  --git a/llvm/test/MC/RISCV/rvy-build-attributes.s 
b/llvm/test/MC/RISCV/rvy-build-attributes.s
new file mode 100644
index 0000000000000..77acf43027990
--- /dev/null
+++ b/llvm/test/MC/RISCV/rvy-build-attributes.s
@@ -0,0 +1,35 @@
+# RUN: llvm-mc %s -triple=riscv64 -filetype=asm -riscv-add-build-attributes \
+# 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,+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-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-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-prefix=RV64Y-ZCA-ZCB
+# RUN: llvm-mc %s -triple=riscv32 -filetype=asm -riscv-add-build-attributes \
+# 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-prefix=RV32Y-ZCE-F
+
+## RV64Y + D + C: y is enabled, so zcd is not implied.
+# RV64YDC: .attribute 5, "rv64i2p1_f2p2_d2p2_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"
+
+## RV64Y + ZCA + D: zca + y implies c on RV64 (y replaces zcd in the 
implication).
+# RV64Y-ZCA-D: .attribute 5, "rv64i2p1_f2p2_d2p2_c2p0_y0p98_zicsr2p0_zca1p0"
+
+## RV64Y + ZCA + ZCB: zca + zcb + y does NOT imply zce on RV64Y because Zce is 
incompatible. zca also implies c.
+# RV64Y-ZCA-ZCB: .attribute 5, "rv64i2p1_c2p0_y0p98_zca1p0_zcb1p0"
+
+## RV32Y + ZCE: zce implies zca, zcb, zcmp, zcmt. zca implies c.
+# 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"

diff  --git a/llvm/test/MC/RISCV/rvy-invalid-attributes.s 
b/llvm/test/MC/RISCV/rvy-invalid-attributes.s
new file mode 100644
index 0000000000000..7ba9deb840f54
--- /dev/null
+++ b/llvm/test/MC/RISCV/rvy-invalid-attributes.s
@@ -0,0 +1,33 @@
+# 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:"
+
+# 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
+
+.attribute arch, "rv32y0p98_zcf"
+# ATTR: error: invalid arch name 'rv32y0p98_zcf', 'zcf' is incompatible with 
rv32y base
+
+.attribute arch, "rv32y0p98_zclsd"
+# ATTR: error: invalid arch name 'rv32y0p98_zclsd', 'zclsd' is incompatible 
with rv32y base
+
+.attribute arch, "rv64y0p98_zcd"
+# ATTR: error: invalid arch name 'rv64y0p98_zcd', 'zcd' is incompatible with 
rv64y base
+
+.attribute arch, "rv64y0p98_c_zcmp"
+# 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
\ No newline at end of file

diff  --git a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp 
b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
index 851685d5f68d1..837a72913dc7d 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,41 @@ 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");
+  EXPECT_EQ(
+      toString(
+          RISCVISAInfo::parseArchString("rv64y0p98_zcmp", true).takeError()),
+      "'zcmp' is incompatible with rv64y base");
+  EXPECT_EQ(
+      toString(
+          RISCVISAInfo::parseArchString("rv64y0p98_zcmt", true).takeError()),
+      "'zcmt' is incompatible with rv64y base");
+  EXPECT_EQ(
+      toString(
+          RISCVISAInfo::parseArchString("rv64y0p98_xqccmp", true).takeError()),
+      "'xqccmp' is incompatible with rv64y base");
+  // RV64Y is incompatible with Zce (because Zce implies Zcmt/Zcmp which 
overlap
+  // with RV64Y). This behaves exactly the same as enabling Zcd and Zce on a
+  // RVI (conflict via Zcmt).
+  EXPECT_EQ(
+      toString(
+          RISCVISAInfo::parseArchString("rv64y0p98_zce", true).takeError()),
+      "'zcmt' is incompatible with rv64y base");
+  EXPECT_EQ(
+      toString(
+          RISCVISAInfo::parseArchString("rv64i_zcd_zce", true).takeError()),
+      "'zcmt' and 'zcd' extensions are incompatible");
 
   // In these ISA strings, the non-zcd extension should be last, after an
   // underscore.
@@ -740,9 +858,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");
   }
 }
 
@@ -960,6 +1079,38 @@ TEST(ParseArchString, ZceImplication) {
   EXPECT_EQ(ExtsRV64IDZce.count("zce"), 1U);
   EXPECT_EQ(ExtsRV64IDZce.count("zcmp"), 1U);
   EXPECT_EQ(ExtsRV64IDZce.count("zcmt"), 1U);
+
+  // RV32Y Zce: zcf incompatible, but zcmp/zcmt compatible. zca + y implies c.
+  auto MaybeRV32YZce = RISCVISAInfo::parseArchString("rv32y0p98_zce", true);
+  ASSERT_THAT_EXPECTED(MaybeRV32YZce, Succeeded());
+  const auto &ExtsRV32YZce = (*MaybeRV32YZce)->getExtensions();
+  EXPECT_EQ(ExtsRV32YZce.size(), 9UL);
+  EXPECT_EQ(ExtsRV32YZce.count("i"), 1U);
+  EXPECT_EQ(ExtsRV32YZce.count("y"), 1U);
+  EXPECT_EQ(ExtsRV32YZce.count("c"), 1U);
+  EXPECT_EQ(ExtsRV32YZce.count("zicsr"), 1U);
+  EXPECT_EQ(ExtsRV32YZce.count("zca"), 1U);
+  EXPECT_EQ(ExtsRV32YZce.count("zcb"), 1U);
+  EXPECT_EQ(ExtsRV32YZce.count("zce"), 1U);
+  EXPECT_EQ(ExtsRV32YZce.count("zcmp"), 1U);
+  EXPECT_EQ(ExtsRV32YZce.count("zcmt"), 1U);
+
+  // RV32Y Zce with F: zcf incompatible, so zcf is not implied.
+  auto MaybeRV32YFZce = RISCVISAInfo::parseArchString("rv32y0p98_f_zce", true);
+  ASSERT_THAT_EXPECTED(MaybeRV32YFZce, Succeeded());
+  const auto &ExtsRV32YFZce = (*MaybeRV32YFZce)->getExtensions();
+  EXPECT_EQ(ExtsRV32YFZce.size(), 10UL);
+  EXPECT_EQ(ExtsRV32YFZce.count("i"), 1U);
+  EXPECT_EQ(ExtsRV32YFZce.count("y"), 1U);
+  EXPECT_EQ(ExtsRV32YFZce.count("c"), 1U);
+  EXPECT_EQ(ExtsRV32YFZce.count("zicsr"), 1U);
+  EXPECT_EQ(ExtsRV32YFZce.count("f"), 1U);
+  EXPECT_EQ(ExtsRV32YFZce.count("zca"), 1U);
+  EXPECT_EQ(ExtsRV32YFZce.count("zcb"), 1U);
+  EXPECT_EQ(ExtsRV32YFZce.count("zce"), 1U);
+  EXPECT_EQ(ExtsRV32YFZce.count("zcf"), 0U);
+  EXPECT_EQ(ExtsRV32YFZce.count("zcmp"), 1U);
+  EXPECT_EQ(ExtsRV32YFZce.count("zcmt"), 1U);
 }
 
 TEST(ParseArchString, ZcaImpliesC) {
@@ -1028,6 +1179,70 @@ 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, RVYFeatureImplicationC) {
+  // rv32y + c + f should not imply zcf
+  auto MaybeISAInfo =
+      RISCVISAInfo::parseFeatures(32, {"+experimental-y", "+c", "+f"});
+  ASSERT_THAT_EXPECTED(MaybeISAInfo, Succeeded());
+  EXPECT_FALSE((*MaybeISAInfo)->hasExtension("zcf"));
+  // rv32y + c + f + d should imply zcd, but not zcf
+  MaybeISAInfo =
+      RISCVISAInfo::parseFeatures(32, {"+experimental-y", "+c", "+f", "+d"});
+  ASSERT_THAT_EXPECTED(MaybeISAInfo, Succeeded());
+  EXPECT_FALSE((*MaybeISAInfo)->hasExtension("zcf"));
+  EXPECT_TRUE((*MaybeISAInfo)->hasExtension("zcd"));
+  // rv64y + c + d should not imply zcd
+  MaybeISAInfo =
+      RISCVISAInfo::parseFeatures(64, {"+experimental-y", "+c", "+d"});
+  ASSERT_THAT_EXPECTED(MaybeISAInfo, Succeeded());
+  EXPECT_FALSE((*MaybeISAInfo)->hasExtension("zcd"));
+}
+
 TEST(ParseArchString, ZcaZcbZcmpZcmtImpliesZce) {
   // Test Zca+Zcb+Zcmp+Zcmt implies Zce behavior.
 
@@ -1112,6 +1327,24 @@ 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"));
+  }
 }
 
 TEST(isSupportedExtensionWithVersion, AcceptsSingleExtensionWithVersion) {


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to