Author: Jerry Zhang Jian
Date: 2025-12-20T01:15:03+08:00
New Revision: fc69c804db3ef12b65ed0900d84f4e26ce06d5cb

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

LOG: [RISCV] Implement conditional Zca implies C extension rule (#172860)

This change implements the conditional "Zca implies C" rule to match
GCC's behavior (PR119122) and the RISC-V specification for MISA.C.

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
  - For RV64:
    - No D: Zca alone implies C
    - D: Zca + Zcd implies C

This fixes multilib matching issues where LLVM-generated march strings
didn't include the C extension when GCC's multilib configurations
expected it.

Reference:
  - GCC PR119122: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119122
- RISC-V Zc spec:
https://github.com/riscv/riscv-isa-manual/blob/main/src/zc.adoc

Signed-off-by: Jerry Zhang Jian <[email protected]>

Added: 
    

Modified: 
    clang/test/Preprocessor/riscv-target-features.c
    llvm/lib/Target/RISCV/RISCVFeatures.td
    llvm/lib/TargetParser/RISCVISAInfo.cpp
    llvm/test/CodeGen/RISCV/attributes-qc.ll
    llvm/test/CodeGen/RISCV/attributes.ll
    llvm/test/MC/RISCV/attribute-arch.s
    llvm/unittests/TargetParser/RISCVISAInfoTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/test/Preprocessor/riscv-target-features.c 
b/clang/test/Preprocessor/riscv-target-features.c
index 56c738bc007fb..2563d83179795 100644
--- a/clang/test/Preprocessor/riscv-target-features.c
+++ b/clang/test/Preprocessor/riscv-target-features.c
@@ -702,6 +702,7 @@
 // RUN: %clang --target=riscv64-unknown-linux-gnu \
 // RUN:   -march=rv64izca1p0 -E -dM %s \
 // RUN:   -o - | FileCheck --check-prefix=CHECK-ZCA-EXT %s
+// CHECK-ZCA-EXT: __riscv_c 2000000{{$}}
 // CHECK-ZCA-EXT: __riscv_zca 1000000{{$}}
 
 // RUN: %clang --target=riscv32-unknown-linux-gnu \
@@ -710,16 +711,19 @@
 // RUN: %clang --target=riscv64-unknown-linux-gnu \
 // RUN:   -march=rv64izcb1p0 -E -dM %s \
 // RUN:   -o - | FileCheck --check-prefix=CHECK-ZCB-EXT %s
+// CHECK-ZCB-EXT: __riscv_c 2000000{{$}}
 // CHECK-ZCB-EXT: __riscv_zca 1000000{{$}}
 // CHECK-ZCB-EXT: __riscv_zcb 1000000{{$}}
 
 // RUN: %clang --target=riscv32-unknown-linux-gnu \
 // RUN:   -march=rv32izcd1p0 -E -dM %s \
-// RUN:   -o - | FileCheck --check-prefix=CHECK-ZCD-EXT %s
+// RUN:   -o - | FileCheck --check-prefix=CHECK-ZCD-EXT-RV32 %s
 // RUN: %clang --target=riscv64-unknown-linux-gnu \
 // RUN:   -march=rv64izcd1p0 -E -dM %s \
-// RUN:   -o - | FileCheck --check-prefix=CHECK-ZCD-EXT %s
-// CHECK-ZCD-EXT: __riscv_zcd 1000000{{$}}
+// RUN:   -o - | FileCheck --check-prefix=CHECK-ZCD-EXT-RV64 %s
+// CHECK-ZCD-EXT-RV32: __riscv_zcd 1000000{{$}}
+// CHECK-ZCD-EXT-RV64: __riscv_c 2000000{{$}}
+// CHECK-ZCD-EXT-RV64: __riscv_zcd 1000000{{$}}
 
 // RUN: %clang --target=riscv32-unknown-linux-gnu \
 // RUN:   -march=rv32izce1p0 -E -dM %s \
@@ -727,6 +731,7 @@
 // RUN: %clang --target=riscv64-unknown-linux-gnu \
 // RUN:   -march=rv64izce1p0 -E -dM %s \
 // RUN:   -o - | FileCheck --check-prefix=CHECK-ZCE-EXT %s
+// CHECK-ZCE-EXT: __riscv_c 2000000{{$}}
 // CHECK-ZCE-EXT: __riscv_zce 1000000{{$}}
 
 // RUN: %clang --target=riscv32-unknown-linux-gnu \
@@ -753,6 +758,7 @@
 // RUN: %clang --target=riscv64-unknown-linux-gnu \
 // RUN:   -march=rv64izcmp1p0 -E -dM %s \
 // RUN:   -o - | FileCheck --check-prefix=CHECK-ZCMP-EXT %s
+// CHECK-ZCMP-EXT: __riscv_c 2000000{{$}}
 // CHECK-ZCMP-EXT: __riscv_zca 1000000{{$}}
 // CHECK-ZCMP-EXT: __riscv_zcmp 1000000{{$}}
 
@@ -762,6 +768,7 @@
 // RUN: %clang --target=riscv64-unknown-linux-gnu \
 // RUN:   -march=rv64izcmt1p0 -E -dM %s \
 // RUN:   -o - | FileCheck --check-prefix=CHECK-ZCMT-EXT %s
+// CHECK-ZCMT-EXT: __riscv_c 2000000{{$}}
 // CHECK-ZCMT-EXT: __riscv_zca 1000000{{$}}
 // CHECK-ZCMT-EXT: __riscv_zcmt 1000000{{$}}
 

diff  --git a/llvm/lib/Target/RISCV/RISCVFeatures.td 
b/llvm/lib/Target/RISCV/RISCVFeatures.td
index 305e1f21a92c2..3a76ac6351fdf 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -387,7 +387,7 @@ def FeatureStdExtZca
                      "part of the C extension, excluding compressed "
                      "floating point loads/stores">,
       RISCVExtensionBitmask<1, 2>;
-// FIXME: Update this message - Zca always implies C.
+// Note: Zca conditionally implies C (see RISCVISAInfo::updateImplication).
 def HasStdExtZca
     : Predicate<"Subtarget->hasStdExtZca()">,
       AssemblerPredicate<(any_of FeatureStdExtZca),

diff  --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp 
b/llvm/lib/TargetParser/RISCVISAInfo.cpp
index 235bd71278a1b..aaca51d8a9cda 100644
--- a/llvm/lib/TargetParser/RISCVISAInfo.cpp
+++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp
@@ -875,6 +875,37 @@ void RISCVISAInfo::updateImplication() {
     Exts["zcf"] = *Version;
   }
 
+  // Add C if Zca is enabled and the conditions are met.
+  // This follows the RISC-V spec rules for MISA.C and matches GCC behavior
+  // (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
+  // For RV64:
+  //   - No D: Zca alone implies C
+  //   - D: Zca + Zcd 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");
+      else if (Exts.count("f"))
+        ShouldAddC = Exts.count("zcf");
+      else
+        ShouldAddC = true;
+    } else if (XLen == 64) {
+      if (Exts.count("d"))
+        ShouldAddC = Exts.count("zcd");
+      else
+        ShouldAddC = true;
+    }
+    if (ShouldAddC) {
+      auto Version = findDefaultVersion("c");
+      Exts["c"] = *Version;
+    }
+  }
+
   // Handle I/E after implications have been resolved, in case either
   // of them was implied by another extension.
   bool HasE = Exts.count("e") != 0;

diff  --git a/llvm/test/CodeGen/RISCV/attributes-qc.ll 
b/llvm/test/CodeGen/RISCV/attributes-qc.ll
index 5eed466346a5c..1c62378dea9ca 100644
--- a/llvm/test/CodeGen/RISCV/attributes-qc.ll
+++ b/llvm/test/CodeGen/RISCV/attributes-qc.ll
@@ -22,27 +22,27 @@
 
 ; RUN: llc -mtriple=riscv64 -mattr=+experimental-xqccmp %s -o - | FileCheck 
--check-prefix=RV64XQCCMP %s
 
-; RV32XQCCMP: .attribute 5, "rv32i2p1_zca1p0_xqccmp0p3"
+; RV32XQCCMP: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqccmp0p3"
 ; RV32XQCIA: .attribute 5, "rv32i2p1_xqcia0p7"
-; RV32XQCIAC: .attribute 5, "rv32i2p1_zca1p0_xqciac0p3"
-; RV32XQCIBI: .attribute 5, "rv32i2p1_zca1p0_xqcibi0p2"
-; RV32XQCIBM: .attribute 5, "rv32i2p1_zca1p0_xqcibm0p8"
+; RV32XQCIAC: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqciac0p3"
+; RV32XQCIBI: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqcibi0p2"
+; RV32XQCIBM: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqcibm0p8"
 ; RV32XQCICLI: .attribute 5, "rv32i2p1_xqcicli0p3"
-; RV32XQCICM: .attribute 5, "rv32i2p1_zca1p0_xqcicm0p2"
+; RV32XQCICM: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqcicm0p2"
 ; RV32XQCICS: .attribute 5, "rv32i2p1_xqcics0p2"
 ; RV32XQCICSR: .attribute 5, "rv32i2p1_xqcicsr0p4"
-; RV32XQCIINT: .attribute 5, "rv32i2p1_zca1p0_xqciint0p10"
+; RV32XQCIINT: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqciint0p10"
 ; RV32XQCIIO: .attribute 5, "rv32i2p1_xqciio0p1"
-; RV32XQCILB: .attribute 5, "rv32i2p1_zca1p0_xqcilb0p2"
-; RV32XQCILI: .attribute 5, "rv32i2p1_zca1p0_xqcili0p2"
-; RV32XQCILIA: .attribute 5, "rv32i2p1_zca1p0_xqcilia0p2"
-; RV32XQCILO: .attribute 5, "rv32i2p1_zca1p0_xqcilo0p3"
+; RV32XQCILB: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqcilb0p2"
+; RV32XQCILI: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqcili0p2"
+; RV32XQCILIA: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqcilia0p2"
+; RV32XQCILO: .attribute 5, "rv32i2p1_c2p0_zca1p0_xqcilo0p3"
 ; RV32XQCILSM: .attribute 5, "rv32i2p1_xqcilsm0p6"
-; RV32XQCISIM: attribute 5, "rv32i2p1_zca1p0_xqcisim0p2"
+; RV32XQCISIM: attribute 5, "rv32i2p1_c2p0_zca1p0_xqcisim0p2"
 ; RV32XQCISLS: .attribute 5, "rv32i2p1_xqcisls0p2"
-; RV32XQCISYNC: attribute 5, "rv32i2p1_zca1p0_xqcisync0p3"
+; RV32XQCISYNC: attribute 5, "rv32i2p1_c2p0_zca1p0_xqcisync0p3"
 
-; RV64XQCCMP: .attribute 5, "rv64i2p1_zca1p0_xqccmp0p3"
+; RV64XQCCMP: .attribute 5, "rv64i2p1_c2p0_zca1p0_xqccmp0p3"
 
 define i32 @addi(i32 %a) {
   %1 = add i32 %a, 1

diff  --git a/llvm/test/CodeGen/RISCV/attributes.ll 
b/llvm/test/CodeGen/RISCV/attributes.ll
index 2fb063af8f8c8..e24230926e270 100644
--- a/llvm/test/CodeGen/RISCV/attributes.ll
+++ b/llvm/test/CodeGen/RISCV/attributes.ll
@@ -393,16 +393,16 @@
 ; RV32XCVMEM: .attribute 5, "rv32i2p1_xcvmem1p0"
 ; RV32XCVSIMD: .attribute 5, "rv32i2p1_xcvsimd1p0"
 ; RV32XCVBI: .attribute 5, "rv32i2p1_xcvbi1p0"
-; RV32XWCHC: .attribute 5, "rv32i2p1_zca1p0_xwchc2p2"
+; RV32XWCHC: .attribute 5, "rv32i2p1_c2p0_zca1p0_xwchc2p2"
 ; RV32ZAAMO: .attribute 5, "rv32i2p1_zaamo1p0"
 ; RV32ZALRSC: .attribute 5, "rv32i2p1_zalrsc1p0"
 ; RV32COMBINEINTOA: .attribute 5, "rv32i2p1_a2p1_zaamo1p0_zalrsc1p0"
-; RV32ZCA: .attribute 5, "rv32i2p1_zca1p0"
-; RV32ZCB: .attribute 5, "rv32i2p1_zca1p0_zcb1p0"
+; RV32ZCA: .attribute 5, "rv32i2p1_c2p0_zca1p0"
+; RV32ZCB: .attribute 5, "rv32i2p1_c2p0_zca1p0_zcb1p0"
 ; RV32ZCD: .attribute 5, "rv32i2p1_f2p2_d2p2_zicsr2p0_zca1p0_zcd1p0"
-; RV32ZCF: .attribute 5, "rv32i2p1_f2p2_zicsr2p0_zca1p0_zcf1p0"
-; RV32ZCMP: .attribute 5, "rv32i2p1_zca1p0_zcmp1p0"
-; RV32ZCMT: .attribute 5, "rv32i2p1_zicsr2p0_zca1p0_zcmt1p0"
+; RV32ZCF: .attribute 5, "rv32i2p1_f2p2_c2p0_zicsr2p0_zca1p0_zcf1p0"
+; RV32ZCMP: .attribute 5, "rv32i2p1_c2p0_zca1p0_zcmp1p0"
+; RV32ZCMT: .attribute 5, "rv32i2p1_c2p0_zicsr2p0_zca1p0_zcmt1p0"
 ; RV32ZICSR: .attribute 5, "rv32i2p1_zicsr2p0"
 ; RV32ZIFENCEI: .attribute 5, "rv32i2p1_zifencei2p0"
 ; RV32ZICNTR: .attribute 5, "rv32i2p1_zicntr2p0_zicsr2p0"
@@ -429,8 +429,8 @@
 ; RV32ZICOND: .attribute 5, "rv32i2p1_zicond1p0"
 ; RV32ZILSD: .attribute 5, "rv32i2p1_zilsd1p0"
 ; RV32ZIMOP: .attribute 5, "rv32i2p1_zimop1p0"
-; RV32ZCLSD: .attribute 5, "rv32i2p1_zilsd1p0_zca1p0_zclsd1p0"
-; RV32ZCMOP: .attribute 5, "rv32i2p1_zca1p0_zcmop1p0"
+; RV32ZCLSD: .attribute 5, "rv32i2p1_c2p0_zilsd1p0_zca1p0_zclsd1p0"
+; RV32ZCMOP: .attribute 5, "rv32i2p1_c2p0_zca1p0_zcmop1p0"
 ; RV32SMAIA: .attribute 5, "rv32i2p1_smaia1p0"
 ; RV32SSAIA: .attribute 5, "rv32i2p1_ssaia1p0"
 ; RV32SMCSRIND: .attribute 5, "rv32i2p1_smcsrind1p0"
@@ -544,11 +544,11 @@
 ; RV64ZAAMO: .attribute 5, "rv64i2p1_zaamo1p0"
 ; RV64ZALRSC: .attribute 5, "rv64i2p1_zalrsc1p0"
 ; RV64COMBINEINTOA: .attribute 5, "rv64i2p1_a2p1_zaamo1p0_zalrsc1p0"
-; RV64ZCA: .attribute 5, "rv64i2p1_zca1p0"
-; RV64ZCB: .attribute 5, "rv64i2p1_zca1p0_zcb1p0"
-; RV64ZCD: .attribute 5, "rv64i2p1_f2p2_d2p2_zicsr2p0_zca1p0_zcd1p0"
-; RV64ZCMP: .attribute 5, "rv64i2p1_zca1p0_zcmp1p0"
-; RV64ZCMT: .attribute 5, "rv64i2p1_zicsr2p0_zca1p0_zcmt1p0"
+; RV64ZCA: .attribute 5, "rv64i2p1_c2p0_zca1p0"
+; RV64ZCB: .attribute 5, "rv64i2p1_c2p0_zca1p0_zcb1p0"
+; RV64ZCD: .attribute 5, "rv64i2p1_f2p2_d2p2_c2p0_zicsr2p0_zca1p0_zcd1p0"
+; RV64ZCMP: .attribute 5, "rv64i2p1_c2p0_zca1p0_zcmp1p0"
+; RV64ZCMT: .attribute 5, "rv64i2p1_c2p0_zicsr2p0_zca1p0_zcmt1p0"
 ; RV64ZICCAMOA: .attribute 5, "rv64i2p1_ziccamoa1p0"
 ; RV64ZICCAMOC: .attribute 5, "rv64i2p1_ziccamoc1p0"
 ; RV64ZICCIF: .attribute 5, "rv64i2p1_ziccif1p0"
@@ -579,7 +579,7 @@
 ; RV64ZVFH: .attribute 5, 
"rv64i2p1_f2p2_zicsr2p0_zfhmin1p0_zve32f1p0_zve32x1p0_zvfh1p0_zvfhmin1p0_zvl32b1p0"
 ; RV64ZICOND: .attribute 5, "rv64i2p1_zicond1p0"
 ; RV64ZIMOP: .attribute 5, "rv64i2p1_zimop1p0"
-; RV64ZCMOP: .attribute 5, "rv64i2p1_zca1p0_zcmop1p0"
+; RV64ZCMOP: .attribute 5, "rv64i2p1_c2p0_zca1p0_zcmop1p0"
 ; RV64SMAIA: .attribute 5, "rv64i2p1_smaia1p0"
 ; RV64SSAIA: .attribute 5, "rv64i2p1_ssaia1p0"
 ; RV64SMCSRIND: .attribute 5, "rv64i2p1_smcsrind1p0"
@@ -626,7 +626,7 @@
 ; RVA23S64: .attribute 5, 
"rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_b1p0_v1p0_h1p0_zic64b1p0_zicbom1p0_zicbop1p0_zicboz1p0_ziccamoa1p0_ziccif1p0_zicclsm1p0_ziccrse1p0_zicntr2p0_zicond1p0_zicsr2p0_zifencei2p0_zihintntl1p0_zihintpause2p0_zihpm2p0_zimop1p0_zmmul1p0_za64rs1p0_zaamo1p0_zalrsc1p0_zawrs1p0_zfa1p0_zfhmin1p0_zca1p0_zcb1p0_zcd1p0_zcmop1p0_zba1p0_zbb1p0_zbs1p0_zkt1p0_zvbb1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvfhmin1p0_zvkb1p0_zvkt1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0_sha1p0_shcounterenw1p0_shgatpa1p0_shtvala1p0_shvsatpa1p0_shvstvala1p0_shvstvecd1p0_ssccptr1p0_sscofpmf1p0_sscounterenw1p0_ssnpm1p0_ssstateen1p0_sstc1p0_sstvala1p0_sstvecd1p0_ssu64xl1p0_supm1p0_svade1p0_svbare1p0_svinval1p0_svnapot1p0_svpbmt1p0"
 ; RVB23U64: .attribute 5, 
"rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_b1p0_zic64b1p0_zicbom1p0_zicbop1p0_zicboz1p0_ziccamoa1p0_ziccif1p0_zicclsm1p0_ziccrse1p0_zicntr2p0_zicond1p0_zicsr2p0_zihintntl1p0_zihintpause2p0_zihpm2p0_zimop1p0_zmmul1p0_za64rs1p0_zaamo1p0_zalrsc1p0_zawrs1p0_zfa1p0_zca1p0_zcb1p0_zcd1p0_zcmop1p0_zba1p0_zbb1p0_zbs1p0_zkt1p0"
 ; RVB23S64: .attribute 5, 
"rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_b1p0_zic64b1p0_zicbom1p0_zicbop1p0_zicboz1p0_ziccamoa1p0_ziccif1p0_zicclsm1p0_ziccrse1p0_zicntr2p0_zicond1p0_zicsr2p0_zifencei2p0_zihintntl1p0_zihintpause2p0_zihpm2p0_zimop1p0_zmmul1p0_za64rs1p0_zaamo1p0_zalrsc1p0_zawrs1p0_zfa1p0_zca1p0_zcb1p0_zcd1p0_zcmop1p0_zba1p0_zbb1p0_zbs1p0_zkt1p0_ssccptr1p0_sscofpmf1p0_sscounterenw1p0_sstc1p0_sstvala1p0_sstvecd1p0_ssu64xl1p0_svade1p0_svbare1p0_svinval1p0_svnapot1p0_svpbmt1p0"
-; RVM23U32: .attribute 5, 
"rv32i2p1_m2p0_b1p0_zicbop1p0_zicond1p0_zicsr2p0_zihintntl1p0_zihintpause2p0_zimop1p0_zmmul1p0_zca1p0_zcb1p0_zce1p0_zcmop1p0_zcmp1p0_zcmt1p0_zba1p0_zbb1p0_zbs1p0"
+; RVM23U32: .attribute 5, 
"rv32i2p1_m2p0_c2p0_b1p0_zicbop1p0_zicond1p0_zicsr2p0_zihintntl1p0_zihintpause2p0_zimop1p0_zmmul1p0_zca1p0_zcb1p0_zce1p0_zcmop1p0_zcmp1p0_zcmt1p0_zba1p0_zbb1p0_zbs1p0"
 
 define i32 @addi(i32 %a) {
   %1 = add i32 %a, 1

diff  --git a/llvm/test/MC/RISCV/attribute-arch.s 
b/llvm/test/MC/RISCV/attribute-arch.s
index fb5f033262b57..98c2a09d9d7fa 100644
--- a/llvm/test/MC/RISCV/attribute-arch.s
+++ b/llvm/test/MC/RISCV/attribute-arch.s
@@ -250,25 +250,25 @@
 # CHECK: attribute      5, 
"rv32i2p1_f2p2_zicsr2p0_zkt1p0_zve32f1p0_zve32x1p0_zvl32b1p0"
 
 .attribute arch, "rv32izca1p0"
-# CHECK: attribute      5, "rv32i2p1_zca1p0"
+# CHECK: attribute      5, "rv32i2p1_c2p0_zca1p0"
 
 .attribute arch, "rv32izcd1p0"
 # CHECK: attribute      5, "rv32i2p1_f2p2_d2p2_zicsr2p0_zca1p0_zcd1p0"
 
 .attribute arch, "rv32izcf1p0"
-# CHECK: attribute      5, "rv32i2p1_f2p2_zicsr2p0_zca1p0_zcf1p0"
+# CHECK: attribute      5, "rv32i2p1_f2p2_c2p0_zicsr2p0_zca1p0_zcf1p0"
 
 .attribute arch, "rv32izcb1p0"
-# CHECK: attribute      5, "rv32i2p1_zca1p0_zcb1p0"
+# CHECK: attribute      5, "rv32i2p1_c2p0_zca1p0_zcb1p0"
 
 .attribute arch, "rv32izclsd1p0"
-# CHECK: attribute      5, "rv32i2p1_zilsd1p0_zca1p0_zclsd1p0"
+# CHECK: attribute      5, "rv32i2p1_c2p0_zilsd1p0_zca1p0_zclsd1p0"
 
 .attribute arch, "rv32izcmp1p0"
-# CHECK: attribute      5, "rv32i2p1_zca1p0_zcmp1p0"
+# CHECK: attribute      5, "rv32i2p1_c2p0_zca1p0_zcmp1p0"
 
 .attribute arch, "rv32izcmt1p0"
-# CHECK: attribute      5, "rv32i2p1_zicsr2p0_zca1p0_zcmt1p0"
+# CHECK: attribute      5, "rv32i2p1_c2p0_zicsr2p0_zca1p0_zcmt1p0"
 
 .attribute arch, "rv64i_xsfvcp"
 # CHECK: attribute      5, "rv64i2p1_zicsr2p0_zve32x1p0_zvl32b1p0_xsfvcp1p0"

diff  --git a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp 
b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
index 2de4352dcac64..a2ac67724152b 100644
--- a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
+++ b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
@@ -628,24 +628,40 @@ TEST(ParseArchString, RejectsConflictingExtensions) {
               "extension is enabled");
   }
 
-  for (StringRef Input : {"rv32id_zcd1p0_zcmp1p0", "rv64id_zcd1p0_zcmp1p0"}) {
+  // RV32 + D + Zcd doesn't synthesize C (needs Zcf), so error mentions 'zcd'
+  for (StringRef Input : {"rv32id_zcd1p0_zcmp1p0"}) {
     EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
               "'zcmp' extension is incompatible with 'zcd' extension when 'd' "
               "extension is enabled");
   }
 
+  // RV64 + D + Zcd synthesizes C, so error mentions 'c'
+  for (StringRef Input : {"rv64id_zcd1p0_zcmp1p0"}) {
+    EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
+              "'zcmp' extension is incompatible with 'c' extension when 'd' "
+              "extension is enabled");
+  }
+
   for (StringRef Input : {"rv32idc_zcmt1p0", "rv64idc_zcmt1p0"}) {
     EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
               "'zcmt' extension is incompatible with 'c' extension when 'd' "
               "extension is enabled");
   }
 
-  for (StringRef Input : {"rv32id_zcd1p0_zcmt1p0", "rv64id_zcd1p0_zcmt1p0"}) {
+  // RV32 + D + Zcd doesn't synthesize C (needs Zcf), so error mentions 'zcd'
+  for (StringRef Input : {"rv32id_zcd1p0_zcmt1p0"}) {
     EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
               "'zcmt' extension is incompatible with 'zcd' extension when 'd' "
               "extension is enabled");
   }
 
+  // RV64 + D + Zcd synthesizes C, so error mentions 'c'
+  for (StringRef Input : {"rv64id_zcd1p0_zcmt1p0"}) {
+    EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
+              "'zcmt' extension is incompatible with 'c' extension when 'd' "
+              "extension is enabled");
+  }
+
   for (StringRef Input : {"rv64if_zcf"}) {
     EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
               "'zcf' is only supported for 'rv32'");
@@ -870,11 +886,13 @@ TEST(OrderedExtensionMap, ExtensionsAreCorrectlyOrdered) {
 }
 
 TEST(ParseArchString, ZceImplication) {
+  // RV32 Zce without F/D: Zca implies C
   auto MaybeRV32IZce = RISCVISAInfo::parseArchString("rv32izce", true);
   ASSERT_THAT_EXPECTED(MaybeRV32IZce, Succeeded());
   const auto &ExtsRV32IZce = (*MaybeRV32IZce)->getExtensions();
-  EXPECT_EQ(ExtsRV32IZce.size(), 7UL);
+  EXPECT_EQ(ExtsRV32IZce.size(), 8UL);
   EXPECT_EQ(ExtsRV32IZce.count("i"), 1U);
+  EXPECT_EQ(ExtsRV32IZce.count("c"), 1U);
   EXPECT_EQ(ExtsRV32IZce.count("zicsr"), 1U);
   EXPECT_EQ(ExtsRV32IZce.count("zca"), 1U);
   EXPECT_EQ(ExtsRV32IZce.count("zcb"), 1U);
@@ -882,11 +900,13 @@ TEST(ParseArchString, ZceImplication) {
   EXPECT_EQ(ExtsRV32IZce.count("zcmp"), 1U);
   EXPECT_EQ(ExtsRV32IZce.count("zcmt"), 1U);
 
+  // RV32 Zce with F (no D): Zca + Zcf implies C
   auto MaybeRV32IFZce = RISCVISAInfo::parseArchString("rv32ifzce", true);
   ASSERT_THAT_EXPECTED(MaybeRV32IFZce, Succeeded());
   const auto &ExtsRV32IFZce = (*MaybeRV32IFZce)->getExtensions();
-  EXPECT_EQ(ExtsRV32IFZce.size(), 9UL);
+  EXPECT_EQ(ExtsRV32IFZce.size(), 10UL);
   EXPECT_EQ(ExtsRV32IFZce.count("i"), 1U);
+  EXPECT_EQ(ExtsRV32IFZce.count("c"), 1U);
   EXPECT_EQ(ExtsRV32IFZce.count("zicsr"), 1U);
   EXPECT_EQ(ExtsRV32IFZce.count("f"), 1U);
   EXPECT_EQ(ExtsRV32IFZce.count("zca"), 1U);
@@ -896,11 +916,13 @@ TEST(ParseArchString, ZceImplication) {
   EXPECT_EQ(ExtsRV32IFZce.count("zcmp"), 1U);
   EXPECT_EQ(ExtsRV32IFZce.count("zcmt"), 1U);
 
+  // RV32 Zce with D: has Zcf but not Zcd, so no C
   auto MaybeRV32IDZce = RISCVISAInfo::parseArchString("rv32idzce", true);
   ASSERT_THAT_EXPECTED(MaybeRV32IDZce, Succeeded());
   const auto &ExtsRV32IDZce = (*MaybeRV32IDZce)->getExtensions();
   EXPECT_EQ(ExtsRV32IDZce.size(), 10UL);
   EXPECT_EQ(ExtsRV32IDZce.count("i"), 1U);
+  EXPECT_EQ(ExtsRV32IDZce.count("c"), 0U);
   EXPECT_EQ(ExtsRV32IDZce.count("zicsr"), 1U);
   EXPECT_EQ(ExtsRV32IDZce.count("f"), 1U);
   EXPECT_EQ(ExtsRV32IDZce.count("d"), 1U);
@@ -911,11 +933,13 @@ TEST(ParseArchString, ZceImplication) {
   EXPECT_EQ(ExtsRV32IDZce.count("zcmp"), 1U);
   EXPECT_EQ(ExtsRV32IDZce.count("zcmt"), 1U);
 
+  // RV64 Zce without F/D: Zca implies C
   auto MaybeRV64IZce = RISCVISAInfo::parseArchString("rv64izce", true);
   ASSERT_THAT_EXPECTED(MaybeRV64IZce, Succeeded());
   const auto &ExtsRV64IZce = (*MaybeRV64IZce)->getExtensions();
-  EXPECT_EQ(ExtsRV64IZce.size(), 7UL);
+  EXPECT_EQ(ExtsRV64IZce.size(), 8UL);
   EXPECT_EQ(ExtsRV64IZce.count("i"), 1U);
+  EXPECT_EQ(ExtsRV64IZce.count("c"), 1U);
   EXPECT_EQ(ExtsRV64IZce.count("zicsr"), 1U);
   EXPECT_EQ(ExtsRV64IZce.count("zca"), 1U);
   EXPECT_EQ(ExtsRV64IZce.count("zcb"), 1U);
@@ -923,11 +947,13 @@ TEST(ParseArchString, ZceImplication) {
   EXPECT_EQ(ExtsRV64IZce.count("zcmp"), 1U);
   EXPECT_EQ(ExtsRV64IZce.count("zcmt"), 1U);
 
+  // RV64 Zce with F (no D): Zca implies C (Zcf not relevant on RV64)
   auto MaybeRV64IFZce = RISCVISAInfo::parseArchString("rv64ifzce", true);
   ASSERT_THAT_EXPECTED(MaybeRV64IFZce, Succeeded());
   const auto &ExtsRV64IFZce = (*MaybeRV64IFZce)->getExtensions();
-  EXPECT_EQ(ExtsRV64IFZce.size(), 8UL);
+  EXPECT_EQ(ExtsRV64IFZce.size(), 9UL);
   EXPECT_EQ(ExtsRV64IFZce.count("i"), 1U);
+  EXPECT_EQ(ExtsRV64IFZce.count("c"), 1U);
   EXPECT_EQ(ExtsRV64IFZce.count("zicsr"), 1U);
   EXPECT_EQ(ExtsRV64IFZce.count("f"), 1U);
   EXPECT_EQ(ExtsRV64IFZce.count("zca"), 1U);
@@ -936,17 +962,13 @@ TEST(ParseArchString, ZceImplication) {
   EXPECT_EQ(ExtsRV64IFZce.count("zcmp"), 1U);
   EXPECT_EQ(ExtsRV64IFZce.count("zcmt"), 1U);
 
-  EXPECT_EQ(ExtsRV64IFZce.count("zca"), 1U);
-  EXPECT_EQ(ExtsRV64IFZce.count("zcb"), 1U);
-  EXPECT_EQ(ExtsRV64IFZce.count("zce"), 1U);
-  EXPECT_EQ(ExtsRV64IFZce.count("zcmp"), 1U);
-  EXPECT_EQ(ExtsRV64IFZce.count("zcmt"), 1U);
-
+  // RV64 Zce with D: has no Zcd, so no C
   auto MaybeRV64IDZce = RISCVISAInfo::parseArchString("rv64idzce", true);
   ASSERT_THAT_EXPECTED(MaybeRV64IDZce, Succeeded());
   const auto &ExtsRV64IDZce = (*MaybeRV64IDZce)->getExtensions();
   EXPECT_EQ(ExtsRV64IDZce.size(), 9UL);
   EXPECT_EQ(ExtsRV64IDZce.count("i"), 1U);
+  EXPECT_EQ(ExtsRV64IDZce.count("c"), 0U);
   EXPECT_EQ(ExtsRV64IDZce.count("zicsr"), 1U);
   EXPECT_EQ(ExtsRV64IDZce.count("f"), 1U);
   EXPECT_EQ(ExtsRV64IDZce.count("d"), 1U);
@@ -957,6 +979,72 @@ TEST(ParseArchString, ZceImplication) {
   EXPECT_EQ(ExtsRV64IDZce.count("zcmt"), 1U);
 }
 
+TEST(ParseArchString, ZcaImpliesC) {
+  // Test Zca implies C behavior matching GCC PR119122
+
+  // RV32 Zca alone implies C
+  auto MaybeRV32IZca = RISCVISAInfo::parseArchString("rv32i_zca", true);
+  ASSERT_THAT_EXPECTED(MaybeRV32IZca, Succeeded());
+  const auto &ExtsRV32IZca = (*MaybeRV32IZca)->getExtensions();
+  EXPECT_EQ(ExtsRV32IZca.count("c"), 1U);
+  EXPECT_EQ(ExtsRV32IZca.count("zca"), 1U);
+
+  // RV32 with F: Zca + Zcf implies C
+  auto MaybeRV32IFZcaZcf =
+      RISCVISAInfo::parseArchString("rv32if_zca_zcf", true);
+  ASSERT_THAT_EXPECTED(MaybeRV32IFZcaZcf, Succeeded());
+  const auto &ExtsRV32IFZcaZcf = (*MaybeRV32IFZcaZcf)->getExtensions();
+  EXPECT_EQ(ExtsRV32IFZcaZcf.count("c"), 1U);
+  EXPECT_EQ(ExtsRV32IFZcaZcf.count("zca"), 1U);
+  EXPECT_EQ(ExtsRV32IFZcaZcf.count("zcf"), 1U);
+
+  // RV32 with F but no Zcf: no C
+  auto MaybeRV32IFZca = RISCVISAInfo::parseArchString("rv32if_zca", true);
+  ASSERT_THAT_EXPECTED(MaybeRV32IFZca, Succeeded());
+  const auto &ExtsRV32IFZca = (*MaybeRV32IFZca)->getExtensions();
+  EXPECT_EQ(ExtsRV32IFZca.count("c"), 0U);
+  EXPECT_EQ(ExtsRV32IFZca.count("zca"), 1U);
+
+  // RV32 with D: Zca + Zcf + Zcd implies C
+  auto MaybeRV32IDZcaZcfZcd =
+      RISCVISAInfo::parseArchString("rv32ifd_zca_zcf_zcd", true);
+  ASSERT_THAT_EXPECTED(MaybeRV32IDZcaZcfZcd, Succeeded());
+  const auto &ExtsRV32IDZcaZcfZcd = (*MaybeRV32IDZcaZcfZcd)->getExtensions();
+  EXPECT_EQ(ExtsRV32IDZcaZcfZcd.count("c"), 1U);
+  EXPECT_EQ(ExtsRV32IDZcaZcfZcd.count("zca"), 1U);
+  EXPECT_EQ(ExtsRV32IDZcaZcfZcd.count("zcf"), 1U);
+  EXPECT_EQ(ExtsRV32IDZcaZcfZcd.count("zcd"), 1U);
+
+  // RV32 with D but no Zcd: no C
+  auto MaybeRV32IDZcaZcf =
+      RISCVISAInfo::parseArchString("rv32ifd_zca_zcf", true);
+  ASSERT_THAT_EXPECTED(MaybeRV32IDZcaZcf, Succeeded());
+  const auto &ExtsRV32IDZcaZcf = (*MaybeRV32IDZcaZcf)->getExtensions();
+  EXPECT_EQ(ExtsRV32IDZcaZcf.count("c"), 0U);
+
+  // RV64 Zca alone implies C
+  auto MaybeRV64IZca = RISCVISAInfo::parseArchString("rv64i_zca", true);
+  ASSERT_THAT_EXPECTED(MaybeRV64IZca, Succeeded());
+  const auto &ExtsRV64IZca = (*MaybeRV64IZca)->getExtensions();
+  EXPECT_EQ(ExtsRV64IZca.count("c"), 1U);
+  EXPECT_EQ(ExtsRV64IZca.count("zca"), 1U);
+
+  // RV64 with D: Zca + Zcd implies C
+  auto MaybeRV64IDZcaZcd =
+      RISCVISAInfo::parseArchString("rv64ifd_zca_zcd", true);
+  ASSERT_THAT_EXPECTED(MaybeRV64IDZcaZcd, Succeeded());
+  const auto &ExtsRV64IDZcaZcd = (*MaybeRV64IDZcaZcd)->getExtensions();
+  EXPECT_EQ(ExtsRV64IDZcaZcd.count("c"), 1U);
+  EXPECT_EQ(ExtsRV64IDZcaZcd.count("zca"), 1U);
+  EXPECT_EQ(ExtsRV64IDZcaZcd.count("zcd"), 1U);
+
+  // RV64 with D but no Zcd: no C
+  auto MaybeRV64IDZca = RISCVISAInfo::parseArchString("rv64ifd_zca", true);
+  ASSERT_THAT_EXPECTED(MaybeRV64IDZca, Succeeded());
+  const auto &ExtsRV64IDZca = (*MaybeRV64IDZca)->getExtensions();
+  EXPECT_EQ(ExtsRV64IDZca.count("c"), 0U);
+}
+
 TEST(isSupportedExtensionWithVersion, AcceptsSingleExtensionWithVersion) {
   EXPECT_TRUE(RISCVISAInfo::isSupportedExtensionWithVersion("zbb1p0"));
   EXPECT_FALSE(RISCVISAInfo::isSupportedExtensionWithVersion("zbb"));


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

Reply via email to