[clang] [llvm] [Clang][RISCV] Add CORE-V (XCV) builtins for CV32E40P (PR #204881)

via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 25 07:36:23 PDT 2026


https://github.com/VittorioBurani updated https://github.com/llvm/llvm-project/pull/204881

>From 2dd3741b5481ca9ac95b04804bf392e039577979 Mon Sep 17 00:00:00 2001
From: vitbur <vittorioburani at gmail.com>
Date: Wed, 1 Apr 2026 18:45:22 +0200
Subject: [PATCH 1/4] [RISCV][XCV] Add LLVM intrinsics for non-representable
 XCVsimd ops

Add IR intrinsics and SelectionDAG patterns for the XCVsimd operations
that have no faithful generic-IR form: dot products (dotup/dotusp/dotsp
and the accumulating sdot variants), complex multiply
(cplxmul/cplxconj), subrotmj, the shuffle/pack family, and lane
extract/insert. Includes the cv.shuffle.sci.b pseudo expanded in
RISCVExpandPseudoInsts.

The element-wise arithmetic/logical/compare/shift operations are left
out here: they map to ordinary packed-vector IR and are handled by
separate codegen patterns (split per review).
---
 llvm/include/llvm/IR/IntrinsicsRISCVXCV.td    | 127 +++++
 .../Target/RISCV/RISCVExpandPseudoInsts.cpp   |  31 ++
 llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td    | 233 +++++++++
 llvm/test/CodeGen/RISCV/xcvsimd.ll            | 486 ++++++++++++++++++
 4 files changed, 877 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/xcvsimd.ll

diff --git a/llvm/include/llvm/IR/IntrinsicsRISCVXCV.td b/llvm/include/llvm/IR/IntrinsicsRISCVXCV.td
index 465665c838bae..0fc47dfe2a2fa 100644
--- a/llvm/include/llvm/IR/IntrinsicsRISCVXCV.td
+++ b/llvm/include/llvm/IR/IntrinsicsRISCVXCV.td
@@ -42,6 +42,54 @@ class ScalarCoreVMacGprGprGprImmIntrinsic
   : Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty],
               [IntrNoMem, IntrWillReturn, IntrSpeculatable, ImmArg<ArgIndex<3>>]>;
 
+// Binary: (i32, i32) -> i32, pure
+class ScalarCoreVSimdGprGprIntrinsic
+    : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty],
+                            [IntrNoMem, IntrSpeculatable]>;
+
+// Unary: (i32) -> i32, pure
+class ScalarCoreVSimdGprIntrinsic
+    : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty],
+                            [IntrNoMem, IntrSpeculatable]>;
+
+// Ternary: (i32, i32, i32) -> i32, pure (for accumulating ops like sdot)
+class ScalarCoreVSimdGprGprGprIntrinsic
+    : DefaultAttrsIntrinsic<[llvm_i32_ty],
+                            [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty],
+                            [IntrNoMem, IntrSpeculatable]>;
+
+// Binary with immediate at arg2: (i32, i32, IMM) -> i32
+// Used for: add_h/sub_h (divcode), subrotmj (divcode)
+class ScalarCoreVSimdGprGprImmIntrinsic
+    : DefaultAttrsIntrinsic<[llvm_i32_ty],
+                            [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty],
+                            [IntrNoMem, IntrSpeculatable,
+                             ImmArg<ArgIndex<2>>]>;
+
+// Unary with immediate at arg1: (i32, IMM) -> i32
+// Used for: extract, extractu, shuffle_sci_h, shuffle_sci_b
+class ScalarCoreVSimdGprImmIntrinsic
+    : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty],
+                            [IntrNoMem, IntrSpeculatable,
+                             ImmArg<ArgIndex<1>>]>;
+
+// Ternary with immediate at arg2, where arg0 is rd (accumulator):
+// (rd_in, rs1, IMM) -> rd_out
+// Used for: insert_h, insert_b
+class ScalarCoreVSimdGprGprImm2WbIntrinsic
+    : DefaultAttrsIntrinsic<[llvm_i32_ty],
+                            [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty],
+                            [IntrNoMem, IntrSpeculatable,
+                             ImmArg<ArgIndex<2>>]>;
+
+// Quaternary with immediate at arg3: (rs1, rs2, rd_in, IMM) -> rd_out
+// Used for: cplxmul_r, cplxmul_i
+class ScalarCoreVSimdGprGprGprImmIntrinsic
+    : DefaultAttrsIntrinsic<[llvm_i32_ty],
+                            [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty],
+                            [IntrNoMem, IntrSpeculatable,
+                             ImmArg<ArgIndex<3>>]>;
+
 let TargetPrefix = "riscv" in {
   def int_riscv_cv_bitmanip_extract : ScalarCoreVBitManipGprGprIntrinsic;
   def int_riscv_cv_bitmanip_extractu : ScalarCoreVBitManipGprGprIntrinsic;
@@ -94,4 +142,83 @@ let TargetPrefix = "riscv" in {
   def int_riscv_cv_elw_elw
     : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty],
                 [IntrReadMem, IntrArgMemOnly, IntrHasSideEffects]>;
+
+  // ==== XCVsimd — ADD/SUB ====
+  // add_h takes a divcode immediate (0=none, 1=/2, 2=/4, 3=/8)
+
+  // ==== XCVsimd — AVG / AVGU ====
+
+  // ==== XCVsimd — MIN / MINU / MAX / MAXU ====
+
+  // ==== XCVsimd — SRL / SRA / SLL ====
+
+  // ==== XCVsimd — OR / XOR / AND ====
+
+  // ==== XCVsimd — ABS ====
+
+  // ==== XCVsimd — DOT PRODUCTS (non-accumulating) ====
+  def int_riscv_cv_simd_dotup_h    : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotup_b    : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotup_sc_h : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotup_sc_b : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotusp_h    : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotusp_b    : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotusp_sc_h : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotusp_sc_b : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotsp_h    : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotsp_b    : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotsp_sc_h : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_dotsp_sc_b : ScalarCoreVSimdGprGprIntrinsic;
+
+  // ==== XCVsimd — SDOT (accumulating dot products): (rs1, rs2, acc) -> acc ====
+  def int_riscv_cv_simd_sdotup_h    : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotup_b    : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotup_sc_h : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotup_sc_b : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotusp_h    : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotusp_b    : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotusp_sc_h : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotusp_sc_b : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotsp_h    : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotsp_b    : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotsp_sc_h : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_sdotsp_sc_b : ScalarCoreVSimdGprGprGprIntrinsic;
+
+  // ==== XCVsimd — EXTRACT / EXTRACTU: (rs1, IMM) -> rd ====
+  def int_riscv_cv_simd_extract_h  : ScalarCoreVSimdGprImmIntrinsic;
+  def int_riscv_cv_simd_extract_b  : ScalarCoreVSimdGprImmIntrinsic;
+  def int_riscv_cv_simd_extractu_h : ScalarCoreVSimdGprImmIntrinsic;
+  def int_riscv_cv_simd_extractu_b : ScalarCoreVSimdGprImmIntrinsic;
+
+  // ==== XCVsimd — INSERT: (rd_in, rs1, IMM) -> rd_out ====
+  def int_riscv_cv_simd_insert_h   : ScalarCoreVSimdGprGprImm2WbIntrinsic;
+  def int_riscv_cv_simd_insert_b   : ScalarCoreVSimdGprGprImm2WbIntrinsic;
+
+  // ==== XCVsimd — SHUFFLE ====
+  def int_riscv_cv_simd_shuffle_h     : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_shuffle_b     : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_shuffle_sci_h : ScalarCoreVSimdGprImmIntrinsic;
+  def int_riscv_cv_simd_shuffle_sci_b : ScalarCoreVSimdGprImmIntrinsic;
+
+  // ==== XCVsimd — SHUFFLE2: (rs1, rs2, rd_in) -> rd_out ====
+  def int_riscv_cv_simd_shuffle2_h : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_shuffle2_b : ScalarCoreVSimdGprGprGprIntrinsic;
+
+  // ==== XCVsimd — PACK ====
+  // pack / pack.h: (rs1, rs2) -> rd
+  def int_riscv_cv_simd_packlo_h : ScalarCoreVSimdGprGprIntrinsic;
+  def int_riscv_cv_simd_packhi_h : ScalarCoreVSimdGprGprIntrinsic;
+  // packhi.b / packlo.b: (rd_in, rs1, rs2) -> rd_out  (read-modify-write)
+  def int_riscv_cv_simd_packhi_b : ScalarCoreVSimdGprGprGprIntrinsic;
+  def int_riscv_cv_simd_packlo_b : ScalarCoreVSimdGprGprGprIntrinsic;
+
+  // ==== XCVsimd — COMPARE ====
+
+  // ==== XCVsimd — COMPLEX ====
+  // cplxmul: (rs1, rs2, rd_in, divcode_IMM) -> rd_out
+  def int_riscv_cv_simd_cplxmul_r : ScalarCoreVSimdGprGprGprImmIntrinsic;
+  def int_riscv_cv_simd_cplxmul_i : ScalarCoreVSimdGprGprGprImmIntrinsic;
+  def int_riscv_cv_simd_cplxconj  : ScalarCoreVSimdGprIntrinsic;
+  // subrotmj: (rs1, rs2, divcode_IMM) -> rd
+  def int_riscv_cv_simd_subrotmj  : ScalarCoreVSimdGprGprImmIntrinsic;
 } // TargetPrefix = "riscv"
diff --git a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
index fdd98d03a77f1..d5a13a7e639fe 100644
--- a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
@@ -60,6 +60,8 @@ class RISCVExpandPseudo : public MachineFunctionPass {
                            MachineBasicBlock::iterator MBBI);
   bool expandPseudoReadVLENBViaVSETVLIX0(MachineBasicBlock &MBB,
                                          MachineBasicBlock::iterator MBBI);
+  bool expandVendorXcvsimdShuffle(MachineBasicBlock &MBB,
+                                  MachineBasicBlock::iterator MBBI);
 #ifndef NDEBUG
   unsigned getInstSizeInBytes(const MachineFunction &MF) const {
     unsigned Size = 0;
@@ -192,6 +194,8 @@ bool RISCVExpandPseudo::expandMI(MachineBasicBlock &MBB,
     return expandVMSET_VMCLR(MBB, MBBI, RISCV::VMXNOR_MM);
   case RISCV::PseudoReadVLENBViaVSETVLIX0:
     return expandPseudoReadVLENBViaVSETVLIX0(MBB, MBBI);
+  case RISCV::CV_SHUFFLE_SCI_B_PSEUDO:
+    return expandVendorXcvsimdShuffle(MBB, MBBI);
   }
 
   return false;
@@ -834,6 +838,33 @@ bool RISCVPreRAExpandPseudo::expandLoadTLSDescAddress(
   return true;
 }
 
+bool RISCVExpandPseudo::expandVendorXcvsimdShuffle(
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) {
+  // CV_SHUFFLE_SCI_B_PSEUDO takes an 8-bit immediate.
+  // Bits [7:6] select one of four cv.shuffleIx.sci.b instructions.
+  // Bits [5:0] become the 6-bit signed immediate payload.
+  DebugLoc DL = MBBI->getDebugLoc();
+  Register DstReg = MBBI->getOperand(0).getReg();
+  Register SrcReg = MBBI->getOperand(1).getReg();
+  unsigned Imm = MBBI->getOperand(2).getImm();
+
+  // Select the correct real instruction based on bits [7:6]
+  static const unsigned Opcodes[] = {
+      RISCV::CV_SHUFFLEI0_SCI_B,
+      RISCV::CV_SHUFFLEI1_SCI_B,
+      RISCV::CV_SHUFFLEI2_SCI_B,
+      RISCV::CV_SHUFFLEI3_SCI_B,
+  };
+  unsigned OpcIdx = (Imm >> 6) & 0x3;
+  unsigned Imm6 = Imm & 0x3F;
+
+  const MCInstrDesc &Desc = TII->get(Opcodes[OpcIdx]);
+  BuildMI(MBB, MBBI, DL, Desc, DstReg).addReg(SrcReg).addImm(Imm6);
+
+  MBBI->eraseFromParent();
+  return true;
+}
+
 } // end of anonymous namespace
 
 INITIALIZE_PASS(RISCVExpandPseudo, "riscv-expand-pseudo",
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td
index f3f3d408c43e7..75915a76e6036 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td
@@ -857,3 +857,236 @@ let Predicates = [HasVendorXCVmac, IsRV32] in {
   def : PatCoreVMacGprGprGprUimm5<"macsRN", "MACSRN">;
   def : PatCoreVMacGprGprGprUimm5<"machhsRN", "MACHHSRN">;
 }
+
+//===----------------------------------------------------------------------===//
+// XCVsimd pseudo-instruction for cv.shuffle.sci.b
+//===----------------------------------------------------------------------===//
+// The hardware encodes the 8-bit shuffle immediate by splitting it across
+// four different opcodes: cv.shufflei0/1/2/3.sci.b, where bits [7:6] select
+// the opcode and bits [5:0] are the payload. This pseudo absorbs the full
+// 8-bit immediate; RISCVExpandPseudoInsts splits it into the correct real
+// instruction.
+
+let Predicates = [HasVendorXCVsimd, IsRV32] in {
+  let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 1 in
+  def CV_SHUFFLE_SCI_B_PSEUDO
+    : Pseudo<(outs GPR:$rd), (ins GPR:$rs1, i32imm:$imm), []>;
+}
+
+//===----------------------------------------------------------------------===//
+// Pattern helper multiclasses for XCVsimd
+//===----------------------------------------------------------------------===//
+
+// Binary signed: RR variants + SCI with simm6
+multiclass PatCoreVSimdSigned<string iname> {
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_h"),
+                  !cast<RVInst>("CV_" # NAME # "_H")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_b"),
+                  !cast<RVInst>("CV_" # NAME # "_B")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_H")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_B")>;
+  // SCI: when the scalar fits in simm6, prefer the immediate instruction
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h")
+               GPR:$rs1, simm6:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_H") GPR:$rs1, simm6:$imm)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b")
+               GPR:$rs1, simm6:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_B") GPR:$rs1, simm6:$imm)>;
+}
+
+// Binary unsigned: RR variants + SCI with uimm6
+multiclass PatCoreVSimdUnsigned<string iname> {
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_h"),
+                  !cast<RVInst>("CV_" # NAME # "_H")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_b"),
+                  !cast<RVInst>("CV_" # NAME # "_B")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_H")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_B")>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h")
+               GPR:$rs1, uimm6:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_H") GPR:$rs1, uimm6:$imm)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b")
+               GPR:$rs1, uimm6:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_B") GPR:$rs1, uimm6:$imm)>;
+}
+
+// Shift: RR variants + SCI with uimm4 for .h, uimm3 for .b
+multiclass PatCoreVSimdShift<string iname> {
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_h"),
+                  !cast<RVInst>("CV_" # NAME # "_H")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_b"),
+                  !cast<RVInst>("CV_" # NAME # "_B")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_H")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_B")>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h")
+               GPR:$rs1, uimm4:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_H") GPR:$rs1, uimm4:$imm)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b")
+               GPR:$rs1, uimm3:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_B") GPR:$rs1, uimm3:$imm)>;
+}
+
+// Accumulating signed (Wb): intrinsic(rs1, rs2, acc) → instr(acc, rs1, rs2)
+// + SCI variant where rs2 (arg1) is simm6
+multiclass PatCoreVSimdSignedWb<string iname> {
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_h")
+               GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_H") GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_b")
+               GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_B") GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h")
+               GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_SC_H") GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b")
+               GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_SC_B") GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h")
+               GPR:$rs1, simm6:$imm, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_H") GPR:$rd, GPR:$rs1, simm6:$imm)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b")
+               GPR:$rs1, simm6:$imm, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_B") GPR:$rd, GPR:$rs1, simm6:$imm)>;
+}
+
+// Accumulating unsigned (Wb): same but SCI uses uimm6
+multiclass PatCoreVSimdUnsignedWb<string iname> {
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_h")
+               GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_H") GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_b")
+               GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_B") GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h")
+               GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_SC_H") GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b")
+               GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_SC_B") GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h")
+               GPR:$rs1, uimm6:$imm, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_H") GPR:$rd, GPR:$rs1, uimm6:$imm)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b")
+               GPR:$rs1, uimm6:$imm, GPR:$rd),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_B") GPR:$rd, GPR:$rs1, uimm6:$imm)>;
+}
+
+//===----------------------------------------------------------------------===//
+// XCVsimd intrinsic-to-instruction patterns
+//===----------------------------------------------------------------------===//
+
+let Predicates = [HasVendorXCVsimd, IsRV32], AddedComplexity = 1 in {
+
+  // ---- ADD / SUB ----
+  // add_h / sub_h use a divcode immediate to select between 4 instructions
+  // intrinsic: (rs1, rs2, divcode_IMM) -> rd
+
+
+
+
+  // ---- AVG / AVGU / MIN / MINU / MAX / MAXU ----
+
+  // ---- SRL / SRA / SLL ----
+
+  // ---- OR / XOR / AND ----
+
+  // ---- ABS ----
+
+  // ---- DOT PRODUCTS (non-accumulating) ----
+  defm DOTUP  : PatCoreVSimdUnsigned<"dotup">;
+  defm DOTUSP : PatCoreVSimdSigned<"dotusp">;
+  defm DOTSP  : PatCoreVSimdSigned<"dotsp">;
+
+  // ---- SDOT (accumulating) ----
+  defm SDOTUP  : PatCoreVSimdUnsignedWb<"sdotup">;
+  defm SDOTUSP : PatCoreVSimdSignedWb<"sdotusp">;
+  defm SDOTSP  : PatCoreVSimdSignedWb<"sdotsp">;
+
+  // ---- EXTRACT / EXTRACTU ----
+  // intrinsic: (rs1, IMM) -> rd
+  def : Pat<(int_riscv_cv_simd_extract_h  GPR:$rs1, timm:$imm),
+            (CV_EXTRACT_H  GPR:$rs1, timm:$imm)>;
+  def : Pat<(int_riscv_cv_simd_extract_b  GPR:$rs1, timm:$imm),
+            (CV_EXTRACT_B  GPR:$rs1, timm:$imm)>;
+  def : Pat<(int_riscv_cv_simd_extractu_h GPR:$rs1, timm:$imm),
+            (CV_EXTRACTU_H GPR:$rs1, timm:$imm)>;
+  def : Pat<(int_riscv_cv_simd_extractu_b GPR:$rs1, timm:$imm),
+            (CV_EXTRACTU_B GPR:$rs1, timm:$imm)>;
+
+  // ---- INSERT ----
+  // intrinsic: (rd_in, rs1, IMM) -> rd_out
+  def : Pat<(int_riscv_cv_simd_insert_h GPR:$rd, GPR:$rs1, timm:$imm),
+            (CV_INSERT_H GPR:$rd, GPR:$rs1, timm:$imm)>;
+  def : Pat<(int_riscv_cv_simd_insert_b GPR:$rd, GPR:$rs1, timm:$imm),
+            (CV_INSERT_B GPR:$rd, GPR:$rs1, timm:$imm)>;
+
+  // ---- SHUFFLE ----
+  def : PatGprGpr<int_riscv_cv_simd_shuffle_h, CV_SHUFFLE_H>;
+  def : PatGprGpr<int_riscv_cv_simd_shuffle_b, CV_SHUFFLE_B>;
+  def : Pat<(int_riscv_cv_simd_shuffle_sci_h GPR:$rs1, timm:$imm),
+            (CV_SHUFFLE_SCI_H GPR:$rs1, timm:$imm)>;
+  // shuffle_sci_b goes through the pseudo (8-bit imm split by expander)
+  def : Pat<(int_riscv_cv_simd_shuffle_sci_b GPR:$rs1, timm:$imm),
+            (CV_SHUFFLE_SCI_B_PSEUDO GPR:$rs1, timm:$imm)>;
+
+  // ---- SHUFFLE2: (rs1, rs2, rd_in) -> rd_out ----
+  def : Pat<(int_riscv_cv_simd_shuffle2_h GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (CV_SHUFFLE2_H GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_shuffle2_b GPR:$rs1, GPR:$rs2, GPR:$rd),
+            (CV_SHUFFLE2_B GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+
+  // ---- PACK ----
+  // pack (packlo_h) and pack.h (packhi_h) are plain binary: (rs1, rs2) -> rd
+  def : PatGprGpr<int_riscv_cv_simd_packlo_h, CV_PACK>;
+  def : PatGprGpr<int_riscv_cv_simd_packhi_h, CV_PACK_H>;
+  // packhi.b / packlo.b are accumulating: (rd_in, rs1, rs2) -> rd_out
+  def : Pat<(int_riscv_cv_simd_packhi_b GPR:$rd, GPR:$rs1, GPR:$rs2),
+            (CV_PACKHI_B GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_packlo_b GPR:$rd, GPR:$rs1, GPR:$rs2),
+            (CV_PACKLO_B GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+
+  // ---- COMPARE (signed) ----
+
+  // ---- COMPARE (unsigned) ----
+
+  // ---- COMPLEX: cplxmul ----
+  // intrinsic: (rs1, rs2, rd_in, divcode_IMM) -> rd_out
+  def : Pat<(int_riscv_cv_simd_cplxmul_r GPR:$rs1, GPR:$rs2, GPR:$rd, 0),
+            (CV_CPLXMUL_R GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_cplxmul_r GPR:$rs1, GPR:$rs2, GPR:$rd, 1),
+            (CV_CPLXMUL_R_DIV2 GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_cplxmul_r GPR:$rs1, GPR:$rs2, GPR:$rd, 2),
+            (CV_CPLXMUL_R_DIV4 GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_cplxmul_r GPR:$rs1, GPR:$rs2, GPR:$rd, 3),
+            (CV_CPLXMUL_R_DIV8 GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+
+  def : Pat<(int_riscv_cv_simd_cplxmul_i GPR:$rs1, GPR:$rs2, GPR:$rd, 0),
+            (CV_CPLXMUL_I GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_cplxmul_i GPR:$rs1, GPR:$rs2, GPR:$rd, 1),
+            (CV_CPLXMUL_I_DIV2 GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_cplxmul_i GPR:$rs1, GPR:$rs2, GPR:$rd, 2),
+            (CV_CPLXMUL_I_DIV4 GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_cplxmul_i GPR:$rs1, GPR:$rs2, GPR:$rd, 3),
+            (CV_CPLXMUL_I_DIV8 GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+
+  // ---- COMPLEX: cplxconj ----
+  def : PatGpr<int_riscv_cv_simd_cplxconj, CV_CPLXCONJ>;
+
+  // ---- COMPLEX: subrotmj ----
+  // intrinsic: (rs1, rs2, divcode_IMM) -> rd
+  def : Pat<(int_riscv_cv_simd_subrotmj GPR:$rs1, GPR:$rs2, 0),
+            (CV_SUBROTMJ GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_subrotmj GPR:$rs1, GPR:$rs2, 1),
+            (CV_SUBROTMJ_DIV2 GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_subrotmj GPR:$rs1, GPR:$rs2, 2),
+            (CV_SUBROTMJ_DIV4 GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(int_riscv_cv_simd_subrotmj GPR:$rs1, GPR:$rs2, 3),
+            (CV_SUBROTMJ_DIV8 GPR:$rs1, GPR:$rs2)>;
+
+} // Predicates = [HasVendorXCVsimd, IsRV32], AddedComplexity = 1
diff --git a/llvm/test/CodeGen/RISCV/xcvsimd.ll b/llvm/test/CodeGen/RISCV/xcvsimd.ll
new file mode 100644
index 0000000000000..d27c5aff84813
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xcvsimd.ll
@@ -0,0 +1,486 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+m,+xcvsimd -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s
+
+; This file tests that all XCVsimd LLVM IR intrinsics are correctly lowered
+; to their corresponding CV32E40P machine instructions.
+
+;===----------------------------------------------------------------------===;
+; ADD / SUB
+;===----------------------------------------------------------------------===;
+
+;===----------------------------------------------------------------------===;
+; AVG / AVGU
+;===----------------------------------------------------------------------===;
+
+;===----------------------------------------------------------------------===;
+; MIN / MINU / MAX / MAXU (representative: _h and _sc_b for each)
+;===----------------------------------------------------------------------===;
+
+;===----------------------------------------------------------------------===;
+; SRL / SRA / SLL
+;===----------------------------------------------------------------------===;
+
+;===----------------------------------------------------------------------===;
+; OR / XOR / AND
+;===----------------------------------------------------------------------===;
+
+;===----------------------------------------------------------------------===;
+; ABS
+;===----------------------------------------------------------------------===;
+
+;===----------------------------------------------------------------------===;
+; DOT PRODUCTS (non-accumulating)
+;===----------------------------------------------------------------------===;
+
+define i32 @test_cv_simd_dotup_h(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_dotup_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.dotup.h a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.dotup.h(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_dotup_b(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_dotup_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.dotup.b a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.dotup.b(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_dotup_sc_h(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_dotup_sc_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.dotup.sc.h a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.dotup.sc.h(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_dotup_sc_b(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_dotup_sc_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.dotup.sc.b a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.dotup.sc.b(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_dotusp_h(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_dotusp_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.dotusp.h a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.dotusp.h(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_dotsp_h(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_dotsp_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.dotsp.h a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.dotsp.h(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+;===----------------------------------------------------------------------===;
+; SDOT (accumulating dot products)
+;===----------------------------------------------------------------------===;
+
+define i32 @test_cv_simd_sdotup_h(i32 %a, i32 %b, i32 %acc) {
+; CHECK-LABEL: test_cv_simd_sdotup_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.sdotup.h a2, a0, a1
+; CHECK-NEXT:    mv a0, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.sdotup.h(i32 %a, i32 %b, i32 %acc)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_sdotup_b(i32 %a, i32 %b, i32 %acc) {
+; CHECK-LABEL: test_cv_simd_sdotup_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.sdotup.b a2, a0, a1
+; CHECK-NEXT:    mv a0, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.sdotup.b(i32 %a, i32 %b, i32 %acc)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_sdotusp_h(i32 %a, i32 %b, i32 %acc) {
+; CHECK-LABEL: test_cv_simd_sdotusp_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.sdotusp.h a2, a0, a1
+; CHECK-NEXT:    mv a0, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.sdotusp.h(i32 %a, i32 %b, i32 %acc)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_sdotsp_h(i32 %a, i32 %b, i32 %acc) {
+; CHECK-LABEL: test_cv_simd_sdotsp_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.sdotsp.h a2, a0, a1
+; CHECK-NEXT:    mv a0, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.sdotsp.h(i32 %a, i32 %b, i32 %acc)
+  ret i32 %res
+}
+
+;===----------------------------------------------------------------------===;
+; EXTRACT / EXTRACTU / INSERT
+;===----------------------------------------------------------------------===;
+
+define i32 @test_cv_simd_extract_h(i32 %a) {
+; CHECK-LABEL: test_cv_simd_extract_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.extract.h a0, a0, 1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.extract.h(i32 %a, i32 1)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_extract_b(i32 %a) {
+; CHECK-LABEL: test_cv_simd_extract_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.extract.b a0, a0, 3
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.extract.b(i32 %a, i32 3)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_extractu_h(i32 %a) {
+; CHECK-LABEL: test_cv_simd_extractu_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.extractu.h a0, a0, 0
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.extractu.h(i32 %a, i32 0)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_extractu_b(i32 %a) {
+; CHECK-LABEL: test_cv_simd_extractu_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.extractu.b a0, a0, 2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.extractu.b(i32 %a, i32 2)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_insert_h(i32 %rd, i32 %rs1) {
+; CHECK-LABEL: test_cv_simd_insert_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.insert.h a0, a1, 1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.insert.h(i32 %rd, i32 %rs1, i32 1)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_insert_b(i32 %rd, i32 %rs1) {
+; CHECK-LABEL: test_cv_simd_insert_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.insert.b a0, a1, 2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.insert.b(i32 %rd, i32 %rs1, i32 2)
+  ret i32 %res
+}
+
+;===----------------------------------------------------------------------===;
+; SHUFFLE / SHUFFLE2
+;===----------------------------------------------------------------------===;
+
+define i32 @test_cv_simd_shuffle_h(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_shuffle_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.shuffle.h a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.shuffle.h(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_shuffle_b(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_shuffle_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.shuffle.b a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.shuffle.b(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_shuffle_sci_h(i32 %a) {
+; CHECK-LABEL: test_cv_simd_shuffle_sci_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.shuffle.sci.h a0, a0, 2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.shuffle.sci.h(i32 %a, i32 2)
+  ret i32 %res
+}
+
+; Test shuffle_sci_b pseudo expansion: imm=0x45 → bits[7:6]=1 → shufflei1
+define i32 @test_cv_simd_shuffle_sci_b_i1(i32 %a) {
+; CHECK-LABEL: test_cv_simd_shuffle_sci_b_i1:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.shufflei1.sci.b a0, a0, 5
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.shuffle.sci.b(i32 %a, i32 69)
+  ret i32 %res
+}
+
+; Test shuffle_sci_b pseudo expansion: imm=0x03 → bits[7:6]=0 → shufflei0
+define i32 @test_cv_simd_shuffle_sci_b_i0(i32 %a) {
+; CHECK-LABEL: test_cv_simd_shuffle_sci_b_i0:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.shufflei0.sci.b a0, a0, 3
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.shuffle.sci.b(i32 %a, i32 3)
+  ret i32 %res
+}
+
+; Test shuffle_sci_b pseudo expansion: imm=0x83 → bits[7:6]=2 → shufflei2
+define i32 @test_cv_simd_shuffle_sci_b_i2(i32 %a) {
+; CHECK-LABEL: test_cv_simd_shuffle_sci_b_i2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.shufflei2.sci.b a0, a0, 3
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.shuffle.sci.b(i32 %a, i32 131)
+  ret i32 %res
+}
+
+; Test shuffle_sci_b pseudo expansion: imm=0xC0 → bits[7:6]=3 → shufflei3
+define i32 @test_cv_simd_shuffle_sci_b_i3(i32 %a) {
+; CHECK-LABEL: test_cv_simd_shuffle_sci_b_i3:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.shufflei3.sci.b a0, a0, 0
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.shuffle.sci.b(i32 %a, i32 192)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_shuffle2_h(i32 %rd, i32 %rs1, i32 %rs2) {
+; CHECK-LABEL: test_cv_simd_shuffle2_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.shuffle2.h a0, a1, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.shuffle2.h(i32 %rs1, i32 %rs2, i32 %rd)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_shuffle2_b(i32 %rd, i32 %rs1, i32 %rs2) {
+; CHECK-LABEL: test_cv_simd_shuffle2_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.shuffle2.b a0, a1, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.shuffle2.b(i32 %rs1, i32 %rs2, i32 %rd)
+  ret i32 %res
+}
+
+;===----------------------------------------------------------------------===;
+; PACK
+;===----------------------------------------------------------------------===;
+
+define i32 @test_cv_simd_packlo_h(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_packlo_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.pack a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.packlo.h(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_packhi_h(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_packhi_h:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.pack.h a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.packhi.h(i32 %a, i32 %b)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_packhi_b(i32 %rd, i32 %rs1, i32 %rs2) {
+; CHECK-LABEL: test_cv_simd_packhi_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.packhi.b a0, a1, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.packhi.b(i32 %rd, i32 %rs1, i32 %rs2)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_packlo_b(i32 %rd, i32 %rs1, i32 %rs2) {
+; CHECK-LABEL: test_cv_simd_packlo_b:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.packlo.b a0, a1, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.packlo.b(i32 %rd, i32 %rs1, i32 %rs2)
+  ret i32 %res
+}
+
+;===----------------------------------------------------------------------===;
+; COMPARE (representative subset)
+;===----------------------------------------------------------------------===;
+
+;===----------------------------------------------------------------------===;
+; COMPLEX: cplxmul / cplxconj / subrotmj
+;===----------------------------------------------------------------------===;
+
+define i32 @test_cv_simd_cplxmul_r(i32 %rs1, i32 %rs2, i32 %rd) {
+; CHECK-LABEL: test_cv_simd_cplxmul_r:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.cplxmul.r a2, a0, a1
+; CHECK-NEXT:    mv a0, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.cplxmul.r(i32 %rs1, i32 %rs2, i32 %rd, i32 0)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_cplxmul_r_div2(i32 %rs1, i32 %rs2, i32 %rd) {
+; CHECK-LABEL: test_cv_simd_cplxmul_r_div2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.cplxmul.r.div2 a2, a0, a1
+; CHECK-NEXT:    mv a0, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.cplxmul.r(i32 %rs1, i32 %rs2, i32 %rd, i32 1)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_cplxmul_i(i32 %rs1, i32 %rs2, i32 %rd) {
+; CHECK-LABEL: test_cv_simd_cplxmul_i:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.cplxmul.i a2, a0, a1
+; CHECK-NEXT:    mv a0, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.cplxmul.i(i32 %rs1, i32 %rs2, i32 %rd, i32 0)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_cplxmul_i_div4(i32 %rs1, i32 %rs2, i32 %rd) {
+; CHECK-LABEL: test_cv_simd_cplxmul_i_div4:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.cplxmul.i.div4 a2, a0, a1
+; CHECK-NEXT:    mv a0, a2
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.cplxmul.i(i32 %rs1, i32 %rs2, i32 %rd, i32 2)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_cplxconj(i32 %a) {
+; CHECK-LABEL: test_cv_simd_cplxconj:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.cplxconj a0, a0
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.cplxconj(i32 %a)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_subrotmj(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_subrotmj:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.subrotmj a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.subrotmj(i32 %a, i32 %b, i32 0)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_subrotmj_div2(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_subrotmj_div2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.subrotmj.div2 a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.subrotmj(i32 %a, i32 %b, i32 1)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_subrotmj_div4(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_subrotmj_div4:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.subrotmj.div4 a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.subrotmj(i32 %a, i32 %b, i32 2)
+  ret i32 %res
+}
+
+define i32 @test_cv_simd_subrotmj_div8(i32 %a, i32 %b) {
+; CHECK-LABEL: test_cv_simd_subrotmj_div8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.subrotmj.div8 a0, a0, a1
+; CHECK-NEXT:    ret
+  %res = call i32 @llvm.riscv.cv.simd.subrotmj(i32 %a, i32 %b, i32 3)
+  ret i32 %res
+}
+
+;===----------------------------------------------------------------------===;
+; Intrinsic declarations
+;===----------------------------------------------------------------------===;
+
+; ADD/SUB
+
+; AVG/AVGU
+
+; MIN/MINU/MAX/MAXU
+
+; SRL/SRA/SLL
+
+; OR/XOR/AND
+
+; ABS
+
+; DOT (non-accumulating)
+declare i32 @llvm.riscv.cv.simd.dotup.h(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotup.b(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotup.sc.h(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotup.sc.b(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotusp.h(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotusp.b(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotusp.sc.h(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotusp.sc.b(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotsp.h(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotsp.b(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotsp.sc.h(i32, i32)
+declare i32 @llvm.riscv.cv.simd.dotsp.sc.b(i32, i32)
+
+; SDOT (accumulating)
+declare i32 @llvm.riscv.cv.simd.sdotup.h(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotup.b(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotup.sc.h(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotup.sc.b(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotusp.h(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotusp.b(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotusp.sc.h(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotusp.sc.b(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotsp.h(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotsp.b(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotsp.sc.h(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.sdotsp.sc.b(i32, i32, i32)
+
+; EXTRACT/EXTRACTU/INSERT
+declare i32 @llvm.riscv.cv.simd.extract.h(i32, i32 immarg)
+declare i32 @llvm.riscv.cv.simd.extract.b(i32, i32 immarg)
+declare i32 @llvm.riscv.cv.simd.extractu.h(i32, i32 immarg)
+declare i32 @llvm.riscv.cv.simd.extractu.b(i32, i32 immarg)
+declare i32 @llvm.riscv.cv.simd.insert.h(i32, i32, i32 immarg)
+declare i32 @llvm.riscv.cv.simd.insert.b(i32, i32, i32 immarg)
+
+; SHUFFLE
+declare i32 @llvm.riscv.cv.simd.shuffle.h(i32, i32)
+declare i32 @llvm.riscv.cv.simd.shuffle.b(i32, i32)
+declare i32 @llvm.riscv.cv.simd.shuffle.sci.h(i32, i32 immarg)
+declare i32 @llvm.riscv.cv.simd.shuffle.sci.b(i32, i32 immarg)
+declare i32 @llvm.riscv.cv.simd.shuffle2.h(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.shuffle2.b(i32, i32, i32)
+
+; PACK
+declare i32 @llvm.riscv.cv.simd.packlo.h(i32, i32)
+declare i32 @llvm.riscv.cv.simd.packhi.h(i32, i32)
+declare i32 @llvm.riscv.cv.simd.packhi.b(i32, i32, i32)
+declare i32 @llvm.riscv.cv.simd.packlo.b(i32, i32, i32)
+
+; COMPARE
+
+; COMPLEX
+declare i32 @llvm.riscv.cv.simd.cplxmul.r(i32, i32, i32, i32 immarg)
+declare i32 @llvm.riscv.cv.simd.cplxmul.i(i32, i32, i32, i32 immarg)
+declare i32 @llvm.riscv.cv.simd.cplxconj(i32)
+declare i32 @llvm.riscv.cv.simd.subrotmj(i32, i32, i32 immarg)

>From 04c985f6c5e2895489cc8d924329475a89e57f5a Mon Sep 17 00:00:00 2001
From: vitbur <vittorioburani at gmail.com>
Date: Thu, 25 Jun 2026 12:22:01 +0200
Subject: [PATCH 2/4] [RISCV][XCV] Select element-wise XCVsimd ops from
 packed-vector IR

Make 2 x i16 / 4 x i8 legal GPR-resident types under XCVsimd (RV32) so
element-wise vector IR selects the cv.*.h / cv.*.b SIMD instructions
directly, without dedicated intrinsics:

  add / sub    -> cv.add  / cv.sub
  smin / umin  -> cv.min  / cv.minu
  smax / umax  -> cv.max  / cv.maxu
  abs          -> cv.abs
  and/or/xor   -> scalar AND/OR/XOR (lane-agnostic)

Every other operation on these types is expanded, so generic vector_size
code with no single instruction scalarizes safely instead of failing to
select (expand-all-then-enable, matching the P extension setup).

The packed types share a GPR with i32, so they pass and return in one
register (calling-convention support) and bitcast to/from i32 for free.
Loads and stores are plain word lw/sw; under-aligned accesses follow the
scalar misalignment rule and split into element accesses rather than a
trapping misaligned word access.

Shifts, avg and the comparisons stay unmapped for now: they need vector
shift legalization, added separately.
---
 llvm/lib/Target/RISCV/RISCVCallingConv.cpp    |   9 +
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   |  45 +++-
 llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td    |  90 ++++++-
 .../RISCV/xcvsimd-vector-unsupported.ll       |  74 +++++
 llvm/test/CodeGen/RISCV/xcvsimd-vector.ll     | 252 ++++++++++++++++++
 5 files changed, 458 insertions(+), 12 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/xcvsimd-vector-unsupported.ll
 create mode 100644 llvm/test/CodeGen/RISCV/xcvsimd-vector.ll

diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
index 9853644080161..39fbfd9a5ad28 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
@@ -377,6 +377,15 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
   unsigned XLen = Subtarget.getXLen();
   MVT XLenVT = Subtarget.getXLenVT();
 
+  // XCVsimd packs 2 x i16 / 4 x i8 into a single GPR. Pass and return them
+  // as XLenVT (a bitcast), so they follow the scalar-integer path instead of
+  // the vector path (which assumes RVV containers).
+  if (Subtarget.hasVendorXCVsimd() && !Subtarget.hasStdExtP() &&
+      (LocVT == MVT::v2i16 || LocVT == MVT::v4i8)) {
+    LocVT = XLenVT;
+    LocInfo = CCValAssign::BCvt;
+  }
+
   if (ArgFlags.isNest()) {
     // Static chain parameter must not be passed in normal argument registers,
     // so we assign t2/t3 for it as done in GCC's
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 7a2b9611683c6..f63d0af3d170f 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -304,9 +304,46 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
     }
   }
 
+  // XCVsimd packs 2 x i16 / 4 x i8 into a GPR (RV32 only); make those types
+  // legal so element-wise IR ops select the cv.*.h / cv.*.b SIMD instructions.
+  if (Subtarget.hasVendorXCVsimd() && !Subtarget.hasStdExtP()) {
+    addRegisterClass(MVT::v2i16, &RISCV::GPRRegClass);
+    addRegisterClass(MVT::v4i8, &RISCV::GPRRegClass);
+  }
+
   // Compute derived properties from the register classes.
   computeRegisterProperties(STI.getRegisterInfo());
 
+  if (Subtarget.hasVendorXCVsimd() && !Subtarget.hasStdExtP()) {
+    static const MVT XCVVecVTs[] = {MVT::v2i16, MVT::v4i8};
+
+    // Start by expanding every operation on the packed types, then make legal
+    // only the ones that map to a single SIMD instruction. Anything left
+    // expanded scalarizes, so generic vector_size code is still handled
+    // correctly instead of failing to select.
+    for (unsigned Op = 0; Op < ISD::BUILTIN_OP_END; ++Op)
+      setOperationAction(Op, XCVVecVTs, Expand);
+
+    for (MVT VT : XCVVecVTs) {
+      for (MVT OtherVT : MVT::integer_fixedlen_vector_valuetypes()) {
+        setTruncStoreAction(VT, OtherVT, Expand);
+        setLoadExtAction({ISD::EXTLOAD, ISD::SEXTLOAD, ISD::ZEXTLOAD}, VT,
+                         OtherVT, Expand);
+      }
+    }
+
+    // The packed types live in a GPR, so they load/store as a plain word and
+    // bitcast to/from i32 for free (selected by no-op patterns). Under-aligned
+    // accesses are split by allowsMisalignedMemoryAccesses.
+    setOperationAction({ISD::LOAD, ISD::STORE}, XCVVecVTs, Legal);
+    setOperationAction(ISD::BITCAST, XCVVecVTs, Legal);
+
+    // Element-wise ops with a one-to-one cv.* SIMD instruction.
+    setOperationAction({ISD::ADD, ISD::SUB, ISD::AND, ISD::OR, ISD::XOR,
+                        ISD::SMIN, ISD::SMAX, ISD::UMIN, ISD::UMAX, ISD::ABS},
+                       XCVVecVTs, Legal);
+  }
+
   setStackPointerRegisterToSaveRestore(RISCV::X2);
 
   setLoadExtAction({ISD::EXTLOAD, ISD::SEXTLOAD, ISD::ZEXTLOAD}, XLenVT,
@@ -26610,7 +26647,13 @@ bool RISCVTargetLowering::isMulAddWithConstProfitable(SDValue AddNode,
 bool RISCVTargetLowering::allowsMisalignedMemoryAccesses(
     EVT VT, unsigned AddrSpace, Align Alignment, MachineMemOperand::Flags Flags,
     unsigned *Fast) const {
-  if (!VT.isVector() || Subtarget.hasStdExtP()) {
+  // XCVsimd packs v2i16/v4i8 into a single GPR, so their accesses are plain
+  // word loads/stores and follow the scalar (not vector) misalignment rules:
+  // an under-aligned access must split into element accesses rather than stay
+  // a single (mis)aligned lw/sw.
+  bool IsXCVPacked =
+      Subtarget.hasVendorXCVsimd() && (VT == MVT::v2i16 || VT == MVT::v4i8);
+  if (!VT.isVector() || Subtarget.hasStdExtP() || IsXCVPacked) {
     if (Fast)
       *Fast = Subtarget.enableUnalignedScalarMem();
     return Subtarget.enableUnalignedScalarMem();
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td
index 75915a76e6036..ef3d13443e338 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td
@@ -914,6 +914,35 @@ multiclass PatCoreVSimdUnsigned<string iname> {
             (!cast<RVInst>("CV_" # NAME # "_SCI_B") GPR:$rs1, uimm6:$imm)>;
 }
 
+// Scalar-broadcast-only variants: the full-vector _h/_b forms are expressed as
+// native packed-vector IR ops (2 x i16 / 4 x i8), so only the scalar-operand
+// (sc / sci) forms keep an intrinsic.
+multiclass PatCoreVSimdSignedScOnly<string iname> {
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_H")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_B")>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h")
+               GPR:$rs1, simm6:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_H") GPR:$rs1, simm6:$imm)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b")
+               GPR:$rs1, simm6:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_B") GPR:$rs1, simm6:$imm)>;
+}
+
+multiclass PatCoreVSimdUnsignedScOnly<string iname> {
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_H")>;
+  def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b"),
+                  !cast<RVInst>("CV_" # NAME # "_SC_B")>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_h")
+               GPR:$rs1, uimm6:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_H") GPR:$rs1, uimm6:$imm)>;
+  def : Pat<(!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_sc_b")
+               GPR:$rs1, uimm6:$imm),
+            (!cast<RVInst>("CV_" # NAME # "_SCI_B") GPR:$rs1, uimm6:$imm)>;
+}
+
 // Shift: RR variants + SCI with uimm4 for .h, uimm3 for .b
 multiclass PatCoreVSimdShift<string iname> {
   def : PatGprGpr<!cast<Intrinsic>("int_riscv_cv_simd_" # iname # "_h"),
@@ -987,17 +1016,6 @@ let Predicates = [HasVendorXCVsimd, IsRV32], AddedComplexity = 1 in {
   // add_h / sub_h use a divcode immediate to select between 4 instructions
   // intrinsic: (rs1, rs2, divcode_IMM) -> rd
 
-
-
-
-  // ---- AVG / AVGU / MIN / MINU / MAX / MAXU ----
-
-  // ---- SRL / SRA / SLL ----
-
-  // ---- OR / XOR / AND ----
-
-  // ---- ABS ----
-
   // ---- DOT PRODUCTS (non-accumulating) ----
   defm DOTUP  : PatCoreVSimdUnsigned<"dotup">;
   defm DOTUSP : PatCoreVSimdSigned<"dotusp">;
@@ -1090,3 +1108,53 @@ let Predicates = [HasVendorXCVsimd, IsRV32], AddedComplexity = 1 in {
             (CV_SUBROTMJ_DIV8 GPR:$rs1, GPR:$rs2)>;
 
 } // Predicates = [HasVendorXCVsimd, IsRV32], AddedComplexity = 1
+
+//===----------------------------------------------------------------------===//
+// XCVsimd: select the element-wise SIMD instructions from generic
+// packed-vector IR.
+//
+// 2 x i16 / 4 x i8 are legal GPR-resident types for XCVsimd (RV32), so the
+// element-wise vector operations map directly to the SIMD instructions without
+// needing dedicated intrinsics. Bitwise ops are lane-agnostic and reuse the
+// base scalar AND/OR/XOR.
+//===----------------------------------------------------------------------===//
+
+let Predicates = [HasVendorXCVsimd, IsRV32] in {
+  // The packed vectors live in a GPR, so a bitcast to/from i32 is a no-op
+  // (used by the calling convention to pass/return them in a register).
+  def : Pat<(i32   (bitconvert (v2i16 GPR:$x))), (COPY_TO_REGCLASS GPR:$x, GPR)>;
+  def : Pat<(v2i16 (bitconvert (i32   GPR:$x))), (COPY_TO_REGCLASS GPR:$x, GPR)>;
+  def : Pat<(i32   (bitconvert (v4i8  GPR:$x))), (COPY_TO_REGCLASS GPR:$x, GPR)>;
+  def : Pat<(v4i8  (bitconvert (i32   GPR:$x))), (COPY_TO_REGCLASS GPR:$x, GPR)>;
+
+  def : Pat<(v2i16 (add GPR:$rs1, GPR:$rs2)), (CV_ADD_H GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i8  (add GPR:$rs1, GPR:$rs2)), (CV_ADD_B GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v2i16 (sub GPR:$rs1, GPR:$rs2)), (CV_SUB_H GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i8  (sub GPR:$rs1, GPR:$rs2)), (CV_SUB_B GPR:$rs1, GPR:$rs2)>;
+
+  def : Pat<(v2i16 (smin GPR:$rs1, GPR:$rs2)), (CV_MIN_H GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i8  (smin GPR:$rs1, GPR:$rs2)), (CV_MIN_B GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v2i16 (umin GPR:$rs1, GPR:$rs2)), (CV_MINU_H GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i8  (umin GPR:$rs1, GPR:$rs2)), (CV_MINU_B GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v2i16 (smax GPR:$rs1, GPR:$rs2)), (CV_MAX_H GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i8  (smax GPR:$rs1, GPR:$rs2)), (CV_MAX_B GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v2i16 (umax GPR:$rs1, GPR:$rs2)), (CV_MAXU_H GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i8  (umax GPR:$rs1, GPR:$rs2)), (CV_MAXU_B GPR:$rs1, GPR:$rs2)>;
+
+  // Bitwise ops do not depend on lane boundaries: reuse the scalar insns.
+  def : Pat<(v2i16 (and GPR:$rs1, GPR:$rs2)), (AND GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i8  (and GPR:$rs1, GPR:$rs2)), (AND GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v2i16 (or  GPR:$rs1, GPR:$rs2)), (OR  GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i8  (or  GPR:$rs1, GPR:$rs2)), (OR  GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v2i16 (xor GPR:$rs1, GPR:$rs2)), (XOR GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i8  (xor GPR:$rs1, GPR:$rs2)), (XOR GPR:$rs1, GPR:$rs2)>;
+
+  // abs: per-lane absolute value.
+  def : Pat<(v2i16 (abs GPR:$rs1)), (CV_ABS_H GPR:$rs1)>;
+  def : Pat<(v4i8  (abs GPR:$rs1)), (CV_ABS_B GPR:$rs1)>;
+
+  def : LdPat<load, LW, v2i16>;
+  def : LdPat<load, LW, v4i8>;
+  def : StPat<store, SW, GPR, v2i16>;
+  def : StPat<store, SW, GPR, v4i8>;
+}
diff --git a/llvm/test/CodeGen/RISCV/xcvsimd-vector-unsupported.ll b/llvm/test/CodeGen/RISCV/xcvsimd-vector-unsupported.ll
new file mode 100644
index 0000000000000..ce3a7b987266e
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xcvsimd-vector-unsupported.ll
@@ -0,0 +1,74 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -mattr=+m,+xcvsimd -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s
+
+; Operations on the packed vector types that XCVsimd has no single
+; instruction for are scalarized (Expand) rather than failing to select.
+
+define <2 x i16> @mul_v2i16(<2 x i16> %a, <2 x i16> %b) {
+; CHECK-LABEL: mul_v2i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    sw a1, 8(sp)
+; CHECK-NEXT:    sw a0, 4(sp)
+; CHECK-NEXT:    lh a0, 10(sp)
+; CHECK-NEXT:    lh a1, 6(sp)
+; CHECK-NEXT:    mul a0, a1, a0
+; CHECK-NEXT:    sh a0, 14(sp)
+; CHECK-NEXT:    lh a0, 8(sp)
+; CHECK-NEXT:    lh a1, 4(sp)
+; CHECK-NEXT:    mul a0, a1, a0
+; CHECK-NEXT:    sh a0, 12(sp)
+; CHECK-NEXT:    lw a0, 12(sp)
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    .cfi_def_cfa_offset 0
+; CHECK-NEXT:    ret
+  %c = mul <2 x i16> %a, %b
+  ret <2 x i16> %c
+}
+
+define <4 x i8> @udiv_v4i8(<4 x i8> %a, <4 x i8> %b) {
+; CHECK-LABEL: udiv_v4i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    sw a1, 8(sp)
+; CHECK-NEXT:    sw a0, 4(sp)
+; CHECK-NEXT:    lbu a0, 11(sp)
+; CHECK-NEXT:    lbu a1, 7(sp)
+; CHECK-NEXT:    divu a0, a1, a0
+; CHECK-NEXT:    sb a0, 15(sp)
+; CHECK-NEXT:    lbu a0, 10(sp)
+; CHECK-NEXT:    lbu a1, 6(sp)
+; CHECK-NEXT:    divu a0, a1, a0
+; CHECK-NEXT:    sb a0, 14(sp)
+; CHECK-NEXT:    lbu a0, 9(sp)
+; CHECK-NEXT:    lbu a1, 5(sp)
+; CHECK-NEXT:    divu a0, a1, a0
+; CHECK-NEXT:    sb a0, 13(sp)
+; CHECK-NEXT:    lbu a0, 8(sp)
+; CHECK-NEXT:    lbu a1, 4(sp)
+; CHECK-NEXT:    divu a0, a1, a0
+; CHECK-NEXT:    sb a0, 12(sp)
+; CHECK-NEXT:    lw a0, 12(sp)
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    .cfi_def_cfa_offset 0
+; CHECK-NEXT:    ret
+  %c = udiv <4 x i8> %a, %b
+  ret <4 x i8> %c
+}
+
+define i16 @extract_v2i16(<2 x i16> %a) {
+; CHECK-LABEL: extract_v2i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    sw a0, 12(sp)
+; CHECK-NEXT:    lh a0, 14(sp)
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    .cfi_def_cfa_offset 0
+; CHECK-NEXT:    ret
+  %c = extractelement <2 x i16> %a, i32 1
+  ret i16 %c
+}
diff --git a/llvm/test/CodeGen/RISCV/xcvsimd-vector.ll b/llvm/test/CodeGen/RISCV/xcvsimd-vector.ll
new file mode 100644
index 0000000000000..0e9a7c3f3b4ae
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xcvsimd-vector.ll
@@ -0,0 +1,252 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -mattr=+xcvsimd -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s
+
+; XCVsimd element-wise ops are expressed as plain packed-vector IR
+; (2 x i16 / 4 x i8) and select the cv.*.h / cv.*.b SIMD instructions,
+; without dedicated intrinsics.
+
+define void @add_v2i16(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: add_v2i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.add.h a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <2 x i16>, ptr %p
+  %b = load <2 x i16>, ptr %q
+  %c = add <2 x i16> %a, %b
+  store <2 x i16> %c, ptr %r
+  ret void
+}
+
+define void @add_v4i8(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: add_v4i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.add.b a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <4 x i8>, ptr %p
+  %b = load <4 x i8>, ptr %q
+  %c = add <4 x i8> %a, %b
+  store <4 x i8> %c, ptr %r
+  ret void
+}
+
+define void @sub_v2i16(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: sub_v2i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.sub.h a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <2 x i16>, ptr %p
+  %b = load <2 x i16>, ptr %q
+  %c = sub <2 x i16> %a, %b
+  store <2 x i16> %c, ptr %r
+  ret void
+}
+
+define void @sub_v4i8(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: sub_v4i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.sub.b a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <4 x i8>, ptr %p
+  %b = load <4 x i8>, ptr %q
+  %c = sub <4 x i8> %a, %b
+  store <4 x i8> %c, ptr %r
+  ret void
+}
+
+define void @and_v2i16(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: and_v2i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    and a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <2 x i16>, ptr %p
+  %b = load <2 x i16>, ptr %q
+  %c = and <2 x i16> %a, %b
+  store <2 x i16> %c, ptr %r
+  ret void
+}
+
+define void @or_v4i8(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: or_v4i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    or a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <4 x i8>, ptr %p
+  %b = load <4 x i8>, ptr %q
+  %c = or <4 x i8> %a, %b
+  store <4 x i8> %c, ptr %r
+  ret void
+}
+
+define void @xor_v2i16(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: xor_v2i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <2 x i16>, ptr %p
+  %b = load <2 x i16>, ptr %q
+  %c = xor <2 x i16> %a, %b
+  store <2 x i16> %c, ptr %r
+  ret void
+}
+
+define void @smin_v2i16(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: smin_v2i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.min.h a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <2 x i16>, ptr %p
+  %b = load <2 x i16>, ptr %q
+  %c = call <2 x i16> @llvm.smin.v2i16(<2 x i16> %a, <2 x i16> %b)
+  store <2 x i16> %c, ptr %r
+  ret void
+}
+
+define void @umin_v4i8(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: umin_v4i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.minu.b a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <4 x i8>, ptr %p
+  %b = load <4 x i8>, ptr %q
+  %c = call <4 x i8> @llvm.umin.v4i8(<4 x i8> %a, <4 x i8> %b)
+  store <4 x i8> %c, ptr %r
+  ret void
+}
+
+define void @smax_v4i8(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: smax_v4i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.max.b a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <4 x i8>, ptr %p
+  %b = load <4 x i8>, ptr %q
+  %c = call <4 x i8> @llvm.smax.v4i8(<4 x i8> %a, <4 x i8> %b)
+  store <4 x i8> %c, ptr %r
+  ret void
+}
+
+define void @umax_v2i16(ptr %p, ptr %q, ptr %r) {
+; CHECK-LABEL: umax_v2i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a1, 0(a1)
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.maxu.h a0, a0, a1
+; CHECK-NEXT:    sw a0, 0(a2)
+; CHECK-NEXT:    ret
+  %a = load <2 x i16>, ptr %p
+  %b = load <2 x i16>, ptr %q
+  %c = call <2 x i16> @llvm.umax.v2i16(<2 x i16> %a, <2 x i16> %b)
+  store <2 x i16> %c, ptr %r
+  ret void
+}
+
+define void @abs_v2i16(ptr %p, ptr %r) {
+; CHECK-LABEL: abs_v2i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.abs.h a0, a0
+; CHECK-NEXT:    sw a0, 0(a1)
+; CHECK-NEXT:    ret
+  %a = load <2 x i16>, ptr %p
+  %c = call <2 x i16> @llvm.abs.v2i16(<2 x i16> %a, i1 false)
+  store <2 x i16> %c, ptr %r
+  ret void
+}
+
+define void @abs_v4i8(ptr %p, ptr %r) {
+; CHECK-LABEL: abs_v4i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    cv.abs.b a0, a0
+; CHECK-NEXT:    sw a0, 0(a1)
+; CHECK-NEXT:    ret
+  %a = load <4 x i8>, ptr %p
+  %c = call <4 x i8> @llvm.abs.v4i8(<4 x i8> %a, i1 false)
+  store <4 x i8> %c, ptr %r
+  ret void
+}
+
+; Packed vectors passed and returned by value go through a GPR (calling
+; convention), not the RVV path.
+define <2 x i16> @add_h_byval(<2 x i16> %a, <2 x i16> %b) {
+; CHECK-LABEL: add_h_byval:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.add.h a0, a0, a1
+; CHECK-NEXT:    ret
+  %c = add <2 x i16> %a, %b
+  ret <2 x i16> %c
+}
+
+define <4 x i8> @sub_b_byval(<4 x i8> %a, <4 x i8> %b) {
+; CHECK-LABEL: sub_b_byval:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cv.sub.b a0, a0, a1
+; CHECK-NEXT:    ret
+  %c = sub <4 x i8> %a, %b
+  ret <4 x i8> %c
+}
+
+; A naturally-aligned access is a single word load/store; an under-aligned one
+; must split into element accesses rather than a (trapping) misaligned lw/sw.
+define <2 x i16> @load_align4(ptr %p) {
+; CHECK-LABEL: load_align4:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lw a0, 0(a0)
+; CHECK-NEXT:    ret
+  %v = load <2 x i16>, ptr %p, align 4
+  ret <2 x i16> %v
+}
+
+define <2 x i16> @load_align2(ptr %p) {
+; CHECK-LABEL: load_align2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lhu a1, 2(a0)
+; CHECK-NEXT:    lhu a0, 0(a0)
+; CHECK-NEXT:    slli a1, a1, 16
+; CHECK-NEXT:    or a0, a1, a0
+; CHECK-NEXT:    ret
+  %v = load <2 x i16>, ptr %p, align 2
+  ret <2 x i16> %v
+}
+
+define void @store_align2(ptr %p, <2 x i16> %v) {
+; CHECK-LABEL: store_align2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    srli a2, a1, 16
+; CHECK-NEXT:    sh a1, 0(a0)
+; CHECK-NEXT:    sh a2, 2(a0)
+; CHECK-NEXT:    ret
+  store <2 x i16> %v, ptr %p, align 2
+  ret void
+}

>From c5be5fa07336cb690493b76116e7159d914ce807 Mon Sep 17 00:00:00 2001
From: vitbur <vittorioburani at gmail.com>
Date: Thu, 25 Jun 2026 16:20:02 +0200
Subject: [PATCH 3/4] [Clang][RISCV] Add CORE-V XCV builtins and headers

Add Clang builtins and the matching <riscv_corev_*.h> headers for the
CORE-V XCV extensions on CV32E40P: XCVmac, XCVelw, XCVbitmanip and
XCVsimd. The mac/elw/bitmanip builtins lower to their existing LLVM
intrinsics.

For XCVsimd, the element-wise operations (add/sub, min/minu/max/maxu,
and/or/xor and abs, in halfword and byte forms) are emitted as native
packed-vector IR (2 x i16 / 4 x i8) so they select the cv.*.h / cv.*.b
instructions through the backend, with no dedicated intrinsic. The
operations that have no IR form (dot products, complex multiply,
shuffle, pack and extract/insert) lower to the llvm.riscv.cv.simd.*
intrinsics.

The scalar-broadcast forms, the scaled add/sub div forms, shifts, avg
and the comparisons are added later together with their codegen.
---
 clang/include/clang/Basic/BuiltinsRISCVXCV.td | 141 +++++++
 clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp  |  95 +++++
 clang/lib/CodeGen/TargetBuiltins/RISCV.cpp    | 349 ++++++++++++++++
 clang/lib/Headers/CMakeLists.txt              |   4 +
 clang/lib/Headers/riscv_corev_bitmanip.h      | 123 ++++++
 clang/lib/Headers/riscv_corev_elw.h           |  37 ++
 clang/lib/Headers/riscv_corev_mac.h           | 182 +++++++++
 clang/lib/Headers/riscv_corev_simd.h          | 384 ++++++++++++++++++
 clang/lib/Sema/SemaRISCV.cpp                  |  55 +++
 9 files changed, 1370 insertions(+)
 create mode 100644 clang/lib/Headers/riscv_corev_bitmanip.h
 create mode 100644 clang/lib/Headers/riscv_corev_elw.h
 create mode 100644 clang/lib/Headers/riscv_corev_mac.h
 create mode 100644 clang/lib/Headers/riscv_corev_simd.h

diff --git a/clang/include/clang/Basic/BuiltinsRISCVXCV.td b/clang/include/clang/Basic/BuiltinsRISCVXCV.td
index 65eb52b198775..c22f92d5e4b6d 100644
--- a/clang/include/clang/Basic/BuiltinsRISCVXCV.td
+++ b/clang/include/clang/Basic/BuiltinsRISCVXCV.td
@@ -39,3 +39,144 @@ def alu_subuN  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsig
 def alu_subRN  : RISCVXCVBuiltin<"int(int, int, unsigned int)", "xcvalu">;
 def alu_subuRN : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvalu">;
 } // Attributes = [NoThrow, Const]
+
+//===----------------------------------------------------------------------===//
+// XCVbitmanip extension.
+//===----------------------------------------------------------------------===//
+let Attributes = [NoThrow, Const] in {
+def bitmanip_extract  : RISCVXCVBuiltin<"int(unsigned int, unsigned int)", "xcvbitmanip">;
+def bitmanip_extractu : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvbitmanip">;
+def bitmanip_bclr     : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvbitmanip">;
+def bitmanip_bset     : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvbitmanip">;
+def bitmanip_insert   : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvbitmanip">;
+def bitmanip_clb      : RISCVXCVBuiltin<"unsigned int(unsigned int)", "xcvbitmanip">;
+def bitmanip_bitrev   : RISCVXCVBuiltin<"unsigned int(unsigned int, _Constant unsigned int, _Constant unsigned int)", "xcvbitmanip">;
+} // Attributes = [NoThrow, Const]
+
+//===----------------------------------------------------------------------===//
+// XCVelw extension.
+//===----------------------------------------------------------------------===//
+let Attributes = [NoThrow] in {
+def elw_elw : RISCVXCVBuiltin<"int(int *)", "xcvelw">;
+} // Attributes = [NoThrow]
+
+//===----------------------------------------------------------------------===//
+// XCVmac extension.
+//===----------------------------------------------------------------------===//
+let Attributes = [NoThrow, Const] in {
+def mac_mac      : RISCVXCVBuiltin<"int(int, int, int)", "xcvmac">;
+def mac_msu      : RISCVXCVBuiltin<"int(int, int, int)", "xcvmac">;
+def mac_muluN    : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_mulhhuN  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_mulsN    : RISCVXCVBuiltin<"int(unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_mulhhsN  : RISCVXCVBuiltin<"int(unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_muluRN   : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_mulhhuRN : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_mulsRN   : RISCVXCVBuiltin<"int(unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_mulhhsRN : RISCVXCVBuiltin<"int(unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_macuN    : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_machhuN  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_macsN    : RISCVXCVBuiltin<"int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_machhsN  : RISCVXCVBuiltin<"int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_macuRN   : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_machhuRN : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_macsRN   : RISCVXCVBuiltin<"int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+def mac_machhsRN : RISCVXCVBuiltin<"int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvmac">;
+} // Attributes = [NoThrow, Const]
+
+//===----------------------------------------------------------------------===//
+// XCVsimd extension.
+//===----------------------------------------------------------------------===//
+let Attributes = [NoThrow, Const] in {
+
+// Element-wise ops are expressed as native packed-vector IR (2 x i16 /
+// 4 x i8) so they select the cv.*.h / cv.*.b instructions directly, with no
+// dedicated intrinsic. The packed value is carried in a 32-bit GPR.
+// (The scalar-broadcast `_sc`/`_sci` forms, the scaled add/sub `div` forms,
+// shifts, avg and the comparisons are added later, with their codegen.)
+
+// ADD / SUB
+def simd_add_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_add_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_sub_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_sub_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+
+// MIN / MINU / MAX / MAXU
+def simd_min_h  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_min_b  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_minu_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_minu_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_max_h  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_max_b  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_maxu_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_maxu_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+
+// AND / OR / XOR
+def simd_and_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_and_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_or_h  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_or_b  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_xor_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_xor_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+
+// ABS
+def simd_abs_h : RISCVXCVBuiltin<"unsigned int(unsigned int)", "xcvsimd">;
+def simd_abs_b : RISCVXCVBuiltin<"unsigned int(unsigned int)", "xcvsimd">;
+
+// DOT PRODUCTS
+def simd_dotup_h  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotup_b  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotup_sc_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotup_sc_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotusp_h : RISCVXCVBuiltin<"int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotusp_b : RISCVXCVBuiltin<"int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotusp_sc_h : RISCVXCVBuiltin<"int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotusp_sc_b : RISCVXCVBuiltin<"int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotsp_h : RISCVXCVBuiltin<"int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotsp_b : RISCVXCVBuiltin<"int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotsp_sc_h : RISCVXCVBuiltin<"int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_dotsp_sc_b : RISCVXCVBuiltin<"int(unsigned int, unsigned int)", "xcvsimd">;
+
+// SDOT (accumulating dot products — rd is both input and output)
+def simd_sdotup_h  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvsimd">;
+def simd_sdotup_b  : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvsimd">;
+def simd_sdotup_sc_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvsimd">;
+def simd_sdotup_sc_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvsimd">;
+def simd_sdotusp_h : RISCVXCVBuiltin<"int(unsigned int, unsigned int, int)", "xcvsimd">;
+def simd_sdotusp_b : RISCVXCVBuiltin<"int(unsigned int, unsigned int, int)", "xcvsimd">;
+def simd_sdotusp_sc_h : RISCVXCVBuiltin<"int(unsigned int, unsigned int, int)", "xcvsimd">;
+def simd_sdotusp_sc_b : RISCVXCVBuiltin<"int(unsigned int, unsigned int, int)", "xcvsimd">;
+def simd_sdotsp_h : RISCVXCVBuiltin<"int(unsigned int, unsigned int, int)", "xcvsimd">;
+def simd_sdotsp_b : RISCVXCVBuiltin<"int(unsigned int, unsigned int, int)", "xcvsimd">;
+def simd_sdotsp_sc_h : RISCVXCVBuiltin<"int(unsigned int, unsigned int, int)", "xcvsimd">;
+def simd_sdotsp_sc_b : RISCVXCVBuiltin<"int(unsigned int, unsigned int, int)", "xcvsimd">;
+
+// EXTRACT / INSERT
+def simd_extract_h  : RISCVXCVBuiltin<"int(unsigned int, _Constant unsigned int)", "xcvsimd">;
+def simd_extract_b  : RISCVXCVBuiltin<"int(unsigned int, _Constant unsigned int)", "xcvsimd">;
+def simd_extractu_h : RISCVXCVBuiltin<"unsigned int(unsigned int, _Constant unsigned int)", "xcvsimd">;
+def simd_extractu_b : RISCVXCVBuiltin<"unsigned int(unsigned int, _Constant unsigned int)", "xcvsimd">;
+def simd_insert_h   : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, _Constant unsigned int)", "xcvsimd">;
+def simd_insert_b   : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, _Constant unsigned int)", "xcvsimd">;
+
+// SHUFFLE / SHUFFLE2
+def simd_shuffle_h    : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_shuffle_b    : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_shuffle_sci_h : RISCVXCVBuiltin<"unsigned int(unsigned int, _Constant unsigned int)", "xcvsimd">;
+def simd_shuffle_sci_b : RISCVXCVBuiltin<"unsigned int(unsigned int, _Constant unsigned int)", "xcvsimd">;
+def simd_shuffle2_h   : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvsimd">;
+def simd_shuffle2_b   : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvsimd">;
+
+// PACK
+def simd_packhi_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_packlo_h : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int)", "xcvsimd">;
+def simd_packhi_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvsimd">;
+def simd_packlo_b : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int)", "xcvsimd">;
+
+// COMPLEX NUMBER OPS
+def simd_cplxmul_r : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvsimd">;
+def simd_cplxmul_i : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, unsigned int, _Constant unsigned int)", "xcvsimd">;
+def simd_cplxconj   : RISCVXCVBuiltin<"unsigned int(unsigned int)", "xcvsimd">;
+def simd_subrotmj   : RISCVXCVBuiltin<"unsigned int(unsigned int, unsigned int, _Constant unsigned int)", "xcvsimd">;
+
+} // Attributes = [NoThrow, Const]
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp
index ec262922be942..513896af977d3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinRISCV.cpp
@@ -184,6 +184,101 @@ CIRGenFunction::emitRISCVBuiltinExpr(unsigned builtinID, const CallExpr *e) {
   case RISCV::BI__builtin_riscv_cv_alu_subRN:
   case RISCV::BI__builtin_riscv_cv_alu_subuN:
   case RISCV::BI__builtin_riscv_cv_alu_subuRN:
+  // XCVbitmanip
+  case RISCV::BI__builtin_riscv_cv_bitmanip_extract:
+  case RISCV::BI__builtin_riscv_cv_bitmanip_extractu:
+  case RISCV::BI__builtin_riscv_cv_bitmanip_bclr:
+  case RISCV::BI__builtin_riscv_cv_bitmanip_bset:
+  case RISCV::BI__builtin_riscv_cv_bitmanip_insert:
+  case RISCV::BI__builtin_riscv_cv_bitmanip_clb:
+  case RISCV::BI__builtin_riscv_cv_bitmanip_bitrev:
+  // XCVelw
+  case RISCV::BI__builtin_riscv_cv_elw_elw:
+  // XCVmac
+  case RISCV::BI__builtin_riscv_cv_mac_mac:
+  case RISCV::BI__builtin_riscv_cv_mac_msu:
+  case RISCV::BI__builtin_riscv_cv_mac_muluN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhuN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulsN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhsN:
+  case RISCV::BI__builtin_riscv_cv_mac_muluRN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhuRN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulsRN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhsRN:
+  case RISCV::BI__builtin_riscv_cv_mac_macuN:
+  case RISCV::BI__builtin_riscv_cv_mac_machhuN:
+  case RISCV::BI__builtin_riscv_cv_mac_macsN:
+  case RISCV::BI__builtin_riscv_cv_mac_machhsN:
+  case RISCV::BI__builtin_riscv_cv_mac_macuRN:
+  case RISCV::BI__builtin_riscv_cv_mac_machhuRN:
+  case RISCV::BI__builtin_riscv_cv_mac_macsRN:
+  case RISCV::BI__builtin_riscv_cv_mac_machhsRN:
+  // XCVsimd builtins (lowered in classic CodeGen; not yet implemented
+  // in CIR).
+  case RISCV::BI__builtin_riscv_cv_simd_add_h:
+  case RISCV::BI__builtin_riscv_cv_simd_add_b:
+  case RISCV::BI__builtin_riscv_cv_simd_sub_h:
+  case RISCV::BI__builtin_riscv_cv_simd_sub_b:
+  case RISCV::BI__builtin_riscv_cv_simd_min_h:
+  case RISCV::BI__builtin_riscv_cv_simd_min_b:
+  case RISCV::BI__builtin_riscv_cv_simd_minu_h:
+  case RISCV::BI__builtin_riscv_cv_simd_minu_b:
+  case RISCV::BI__builtin_riscv_cv_simd_max_h:
+  case RISCV::BI__builtin_riscv_cv_simd_max_b:
+  case RISCV::BI__builtin_riscv_cv_simd_maxu_h:
+  case RISCV::BI__builtin_riscv_cv_simd_maxu_b:
+  case RISCV::BI__builtin_riscv_cv_simd_and_h:
+  case RISCV::BI__builtin_riscv_cv_simd_and_b:
+  case RISCV::BI__builtin_riscv_cv_simd_or_h:
+  case RISCV::BI__builtin_riscv_cv_simd_or_b:
+  case RISCV::BI__builtin_riscv_cv_simd_xor_h:
+  case RISCV::BI__builtin_riscv_cv_simd_xor_b:
+  case RISCV::BI__builtin_riscv_cv_simd_abs_h:
+  case RISCV::BI__builtin_riscv_cv_simd_abs_b:
+  case RISCV::BI__builtin_riscv_cv_simd_dotup_h:
+  case RISCV::BI__builtin_riscv_cv_simd_dotup_b:
+  case RISCV::BI__builtin_riscv_cv_simd_dotup_sc_h:
+  case RISCV::BI__builtin_riscv_cv_simd_dotup_sc_b:
+  case RISCV::BI__builtin_riscv_cv_simd_dotusp_h:
+  case RISCV::BI__builtin_riscv_cv_simd_dotusp_b:
+  case RISCV::BI__builtin_riscv_cv_simd_dotusp_sc_h:
+  case RISCV::BI__builtin_riscv_cv_simd_dotusp_sc_b:
+  case RISCV::BI__builtin_riscv_cv_simd_dotsp_h:
+  case RISCV::BI__builtin_riscv_cv_simd_dotsp_b:
+  case RISCV::BI__builtin_riscv_cv_simd_dotsp_sc_h:
+  case RISCV::BI__builtin_riscv_cv_simd_dotsp_sc_b:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotup_h:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotup_b:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotup_sc_h:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotup_sc_b:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotusp_h:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotusp_b:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotusp_sc_h:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotusp_sc_b:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotsp_h:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotsp_b:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotsp_sc_h:
+  case RISCV::BI__builtin_riscv_cv_simd_sdotsp_sc_b:
+  case RISCV::BI__builtin_riscv_cv_simd_extract_h:
+  case RISCV::BI__builtin_riscv_cv_simd_extract_b:
+  case RISCV::BI__builtin_riscv_cv_simd_extractu_h:
+  case RISCV::BI__builtin_riscv_cv_simd_extractu_b:
+  case RISCV::BI__builtin_riscv_cv_simd_insert_h:
+  case RISCV::BI__builtin_riscv_cv_simd_insert_b:
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_h:
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_b:
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_sci_h:
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_sci_b:
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle2_h:
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle2_b:
+  case RISCV::BI__builtin_riscv_cv_simd_packhi_h:
+  case RISCV::BI__builtin_riscv_cv_simd_packlo_h:
+  case RISCV::BI__builtin_riscv_cv_simd_packhi_b:
+  case RISCV::BI__builtin_riscv_cv_simd_packlo_b:
+  case RISCV::BI__builtin_riscv_cv_simd_cplxmul_r:
+  case RISCV::BI__builtin_riscv_cv_simd_cplxmul_i:
+  case RISCV::BI__builtin_riscv_cv_simd_cplxconj:
+  case RISCV::BI__builtin_riscv_cv_simd_subrotmj:
   // XAndesPerf
   case RISCV::BI__builtin_riscv_nds_ffb_32:
   case RISCV::BI__builtin_riscv_nds_ffb_64:
diff --git a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
index d5b027fe5f8fe..2ecc1e65d0e47 100644
--- a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
@@ -25,6 +25,31 @@ using namespace llvm;
 static constexpr unsigned RVV_VTA = 0x1;
 static constexpr unsigned RVV_VMA = 0x2;
 
+// Emit an element-wise XCVsimd op whose packed (NumElts x EltBits) vector is
+// carried in a 32-bit GPR: bitcast the i32 operand(s) to the packed vector
+// type, apply the op, and bitcast the result back to i32. The op selects the
+// cv.*.h / cv.*.b SIMD instruction with no dedicated intrinsic.
+static Value *emitCVSimdBinOp(
+    CodeGenFunction &CGF, ArrayRef<Value *> Ops, unsigned NumElts,
+    unsigned EltBits,
+    llvm::function_ref<Value *(CGBuilderTy &, Value *, Value *)> Op) {
+  auto *VecTy =
+      llvm::FixedVectorType::get(CGF.Builder.getIntNTy(EltBits), NumElts);
+  Value *LHS = CGF.Builder.CreateBitCast(Ops[0], VecTy);
+  Value *RHS = CGF.Builder.CreateBitCast(Ops[1], VecTy);
+  return CGF.Builder.CreateBitCast(Op(CGF.Builder, LHS, RHS), CGF.Int32Ty);
+}
+
+static Value *
+emitCVSimdUnOp(CodeGenFunction &CGF, ArrayRef<Value *> Ops, unsigned NumElts,
+               unsigned EltBits,
+               llvm::function_ref<Value *(CGBuilderTy &, Value *)> Op) {
+  auto *VecTy =
+      llvm::FixedVectorType::get(CGF.Builder.getIntNTy(EltBits), NumElts);
+  Value *V = CGF.Builder.CreateBitCast(Ops[0], VecTy);
+  return CGF.Builder.CreateBitCast(Op(CGF.Builder, V), CGF.Int32Ty);
+}
+
 // RISC-V Vector builtin helper functions are marked NOINLINE to prevent
 // excessive inlining in CodeGenFunction::EmitRISCVBuiltinExpr's large switch
 // statement, which would significantly increase compilation time.
@@ -1482,6 +1507,330 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
     ID = Intrinsic::riscv_cv_alu_subuRN;
     break;
 
+  // XCVbitmanip
+  case RISCV::BI__builtin_riscv_cv_bitmanip_extract:
+    ID = Intrinsic::riscv_cv_bitmanip_extract;
+    break;
+  case RISCV::BI__builtin_riscv_cv_bitmanip_extractu:
+    ID = Intrinsic::riscv_cv_bitmanip_extractu;
+    break;
+  case RISCV::BI__builtin_riscv_cv_bitmanip_bclr:
+    ID = Intrinsic::riscv_cv_bitmanip_bclr;
+    break;
+  case RISCV::BI__builtin_riscv_cv_bitmanip_bset:
+    ID = Intrinsic::riscv_cv_bitmanip_bset;
+    break;
+  case RISCV::BI__builtin_riscv_cv_bitmanip_insert:
+    ID = Intrinsic::riscv_cv_bitmanip_insert;
+    break;
+  case RISCV::BI__builtin_riscv_cv_bitmanip_clb:
+    ID = Intrinsic::riscv_cv_bitmanip_clb;
+    break;
+  case RISCV::BI__builtin_riscv_cv_bitmanip_bitrev:
+    ID = Intrinsic::riscv_cv_bitmanip_bitrev;
+    break;
+
+  // XCVelw
+  case RISCV::BI__builtin_riscv_cv_elw_elw: {
+    // cv.elw rd, imm(rs1) — load word with event wait
+    // Emit as a volatile load from the memory-mapped address
+    // The builtin takes a pointer; emit as intrinsic call
+    ID = Intrinsic::riscv_cv_elw_elw;
+    break;
+  }
+
+  // XCVmac
+  case RISCV::BI__builtin_riscv_cv_mac_mac:
+    ID = Intrinsic::riscv_cv_mac_mac;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_msu:
+    ID = Intrinsic::riscv_cv_mac_msu;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_muluN:
+    ID = Intrinsic::riscv_cv_mac_muluN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhuN:
+    ID = Intrinsic::riscv_cv_mac_mulhhuN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_mulsN:
+    ID = Intrinsic::riscv_cv_mac_mulsN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhsN:
+    ID = Intrinsic::riscv_cv_mac_mulhhsN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_muluRN:
+    ID = Intrinsic::riscv_cv_mac_muluRN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhuRN:
+    ID = Intrinsic::riscv_cv_mac_mulhhuRN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_mulsRN:
+    ID = Intrinsic::riscv_cv_mac_mulsRN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhsRN:
+    ID = Intrinsic::riscv_cv_mac_mulhhsRN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_macuN:
+    ID = Intrinsic::riscv_cv_mac_macuN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_machhuN:
+    ID = Intrinsic::riscv_cv_mac_machhuN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_macsN:
+    ID = Intrinsic::riscv_cv_mac_macsN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_machhsN:
+    ID = Intrinsic::riscv_cv_mac_machhsN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_macuRN:
+    ID = Intrinsic::riscv_cv_mac_macuRN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_machhuRN:
+    ID = Intrinsic::riscv_cv_mac_machhuRN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_macsRN:
+    ID = Intrinsic::riscv_cv_mac_macsRN;
+    break;
+  case RISCV::BI__builtin_riscv_cv_mac_machhsRN:
+    ID = Intrinsic::riscv_cv_mac_machhsRN;
+    break;
+
+  // XCVsimd element-wise ops: emitted as native packed-vector IR
+  // (2 x i16 / 4 x i8) so they select the cv.*.h / cv.*.b instructions
+  // directly, with no dedicated intrinsic.
+  case RISCV::BI__builtin_riscv_cv_simd_add_h:
+    return emitCVSimdBinOp(
+        *this, Ops, 2, 16,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateAdd(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_add_b:
+    return emitCVSimdBinOp(
+        *this, Ops, 4, 8,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateAdd(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_sub_h:
+    return emitCVSimdBinOp(
+        *this, Ops, 2, 16,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateSub(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_sub_b:
+    return emitCVSimdBinOp(
+        *this, Ops, 4, 8,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateSub(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_min_h:
+    return emitCVSimdBinOp(
+        *this, Ops, 2, 16, [](CGBuilderTy &B, Value *L, Value *R) {
+          return B.CreateBinaryIntrinsic(Intrinsic::smin, L, R);
+        });
+  case RISCV::BI__builtin_riscv_cv_simd_min_b:
+    return emitCVSimdBinOp(
+        *this, Ops, 4, 8, [](CGBuilderTy &B, Value *L, Value *R) {
+          return B.CreateBinaryIntrinsic(Intrinsic::smin, L, R);
+        });
+  case RISCV::BI__builtin_riscv_cv_simd_minu_h:
+    return emitCVSimdBinOp(
+        *this, Ops, 2, 16, [](CGBuilderTy &B, Value *L, Value *R) {
+          return B.CreateBinaryIntrinsic(Intrinsic::umin, L, R);
+        });
+  case RISCV::BI__builtin_riscv_cv_simd_minu_b:
+    return emitCVSimdBinOp(
+        *this, Ops, 4, 8, [](CGBuilderTy &B, Value *L, Value *R) {
+          return B.CreateBinaryIntrinsic(Intrinsic::umin, L, R);
+        });
+  case RISCV::BI__builtin_riscv_cv_simd_max_h:
+    return emitCVSimdBinOp(
+        *this, Ops, 2, 16, [](CGBuilderTy &B, Value *L, Value *R) {
+          return B.CreateBinaryIntrinsic(Intrinsic::smax, L, R);
+        });
+  case RISCV::BI__builtin_riscv_cv_simd_max_b:
+    return emitCVSimdBinOp(
+        *this, Ops, 4, 8, [](CGBuilderTy &B, Value *L, Value *R) {
+          return B.CreateBinaryIntrinsic(Intrinsic::smax, L, R);
+        });
+  case RISCV::BI__builtin_riscv_cv_simd_maxu_h:
+    return emitCVSimdBinOp(
+        *this, Ops, 2, 16, [](CGBuilderTy &B, Value *L, Value *R) {
+          return B.CreateBinaryIntrinsic(Intrinsic::umax, L, R);
+        });
+  case RISCV::BI__builtin_riscv_cv_simd_maxu_b:
+    return emitCVSimdBinOp(
+        *this, Ops, 4, 8, [](CGBuilderTy &B, Value *L, Value *R) {
+          return B.CreateBinaryIntrinsic(Intrinsic::umax, L, R);
+        });
+  case RISCV::BI__builtin_riscv_cv_simd_and_h:
+    return emitCVSimdBinOp(
+        *this, Ops, 2, 16,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateAnd(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_and_b:
+    return emitCVSimdBinOp(
+        *this, Ops, 4, 8,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateAnd(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_or_h:
+    return emitCVSimdBinOp(
+        *this, Ops, 2, 16,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateOr(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_or_b:
+    return emitCVSimdBinOp(
+        *this, Ops, 4, 8,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateOr(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_xor_h:
+    return emitCVSimdBinOp(
+        *this, Ops, 2, 16,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateXor(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_xor_b:
+    return emitCVSimdBinOp(
+        *this, Ops, 4, 8,
+        [](CGBuilderTy &B, Value *L, Value *R) { return B.CreateXor(L, R); });
+  case RISCV::BI__builtin_riscv_cv_simd_abs_h:
+    return emitCVSimdUnOp(*this, Ops, 2, 16, [](CGBuilderTy &B, Value *V) {
+      return B.CreateBinaryIntrinsic(Intrinsic::abs, V, B.getFalse());
+    });
+  case RISCV::BI__builtin_riscv_cv_simd_abs_b:
+    return emitCVSimdUnOp(*this, Ops, 4, 8, [](CGBuilderTy &B, Value *V) {
+      return B.CreateBinaryIntrinsic(Intrinsic::abs, V, B.getFalse());
+    });
+
+  // XCVsimd - DOT PRODUCTS
+  case RISCV::BI__builtin_riscv_cv_simd_dotup_h:
+    ID = Intrinsic::riscv_cv_simd_dotup_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotup_b:
+    ID = Intrinsic::riscv_cv_simd_dotup_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotup_sc_h:
+    ID = Intrinsic::riscv_cv_simd_dotup_sc_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotup_sc_b:
+    ID = Intrinsic::riscv_cv_simd_dotup_sc_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotusp_h:
+    ID = Intrinsic::riscv_cv_simd_dotusp_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotusp_b:
+    ID = Intrinsic::riscv_cv_simd_dotusp_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotusp_sc_h:
+    ID = Intrinsic::riscv_cv_simd_dotusp_sc_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotusp_sc_b:
+    ID = Intrinsic::riscv_cv_simd_dotusp_sc_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotsp_h:
+    ID = Intrinsic::riscv_cv_simd_dotsp_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotsp_b:
+    ID = Intrinsic::riscv_cv_simd_dotsp_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotsp_sc_h:
+    ID = Intrinsic::riscv_cv_simd_dotsp_sc_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_dotsp_sc_b:
+    ID = Intrinsic::riscv_cv_simd_dotsp_sc_b;
+    break;
+
+  // XCVsimd - SDOT (accumulating dot products — rd is both input and output)
+  case RISCV::BI__builtin_riscv_cv_simd_sdotup_h:
+    ID = Intrinsic::riscv_cv_simd_sdotup_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotup_b:
+    ID = Intrinsic::riscv_cv_simd_sdotup_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotup_sc_h:
+    ID = Intrinsic::riscv_cv_simd_sdotup_sc_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotup_sc_b:
+    ID = Intrinsic::riscv_cv_simd_sdotup_sc_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotusp_h:
+    ID = Intrinsic::riscv_cv_simd_sdotusp_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotusp_b:
+    ID = Intrinsic::riscv_cv_simd_sdotusp_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotusp_sc_h:
+    ID = Intrinsic::riscv_cv_simd_sdotusp_sc_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotusp_sc_b:
+    ID = Intrinsic::riscv_cv_simd_sdotusp_sc_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotsp_h:
+    ID = Intrinsic::riscv_cv_simd_sdotsp_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotsp_b:
+    ID = Intrinsic::riscv_cv_simd_sdotsp_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotsp_sc_h:
+    ID = Intrinsic::riscv_cv_simd_sdotsp_sc_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_sdotsp_sc_b:
+    ID = Intrinsic::riscv_cv_simd_sdotsp_sc_b;
+    break;
+
+  // XCVsimd - EXTRACT / INSERT
+  case RISCV::BI__builtin_riscv_cv_simd_extract_h:
+    ID = Intrinsic::riscv_cv_simd_extract_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_extract_b:
+    ID = Intrinsic::riscv_cv_simd_extract_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_extractu_h:
+    ID = Intrinsic::riscv_cv_simd_extractu_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_extractu_b:
+    ID = Intrinsic::riscv_cv_simd_extractu_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_insert_h:
+    ID = Intrinsic::riscv_cv_simd_insert_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_insert_b:
+    ID = Intrinsic::riscv_cv_simd_insert_b;
+    break;
+
+  // XCVsimd - SHUFFLE / SHUFFLE2
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_h:
+    ID = Intrinsic::riscv_cv_simd_shuffle_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_b:
+    ID = Intrinsic::riscv_cv_simd_shuffle_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_sci_h:
+    ID = Intrinsic::riscv_cv_simd_shuffle_sci_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_sci_b:
+    ID = Intrinsic::riscv_cv_simd_shuffle_sci_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle2_h:
+    ID = Intrinsic::riscv_cv_simd_shuffle2_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle2_b:
+    ID = Intrinsic::riscv_cv_simd_shuffle2_b;
+    break;
+
+  // XCVsimd - PACK
+  case RISCV::BI__builtin_riscv_cv_simd_packhi_h:
+    ID = Intrinsic::riscv_cv_simd_packhi_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_packlo_h:
+    ID = Intrinsic::riscv_cv_simd_packlo_h;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_packhi_b:
+    ID = Intrinsic::riscv_cv_simd_packhi_b;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_packlo_b:
+    ID = Intrinsic::riscv_cv_simd_packlo_b;
+    break;
+
+  // XCVsimd - COMPLEX
+  case RISCV::BI__builtin_riscv_cv_simd_cplxmul_r:
+    ID = Intrinsic::riscv_cv_simd_cplxmul_r;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_cplxmul_i:
+    ID = Intrinsic::riscv_cv_simd_cplxmul_i;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_cplxconj:
+    ID = Intrinsic::riscv_cv_simd_cplxconj;
+    break;
+  case RISCV::BI__builtin_riscv_cv_simd_subrotmj:
+    ID = Intrinsic::riscv_cv_simd_subrotmj;
+    break;
+
   // XAndesPerf
   case RISCV::BI__builtin_riscv_nds_ffb_32:
   case RISCV::BI__builtin_riscv_nds_ffb_64:
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index 439f2725168ba..6216151fb060f 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -138,6 +138,10 @@ set(riscv_files
   # Vendor headers
   andes_vector.h
   riscv_corev_alu.h
+  riscv_corev_bitmanip.h
+  riscv_corev_elw.h
+  riscv_corev_mac.h
+  riscv_corev_simd.h
   riscv_mips.h
   riscv_nds.h
   riscv_packed_simd.h
diff --git a/clang/lib/Headers/riscv_corev_bitmanip.h b/clang/lib/Headers/riscv_corev_bitmanip.h
new file mode 100644
index 0000000000000..55a2ca94eb31f
--- /dev/null
+++ b/clang/lib/Headers/riscv_corev_bitmanip.h
@@ -0,0 +1,123 @@
+/*===---- riscv_corev_bitmanip.h - CORE-V bit-manipulation intrinsics ------===
+ *
+ * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+ * See https://llvm.org/LICENSE.txt for license information.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ *
+ *===-----------------------------------------------------------------------===
+ *
+ * This header provides C intrinsics for the CORE-V XCVbitmanip ISA extension.
+ * Include this header when compiling with -march=..._xcvbitmanip.
+ *
+ * Spec: CV32E40P user manual, Instruction Set Extensions (XCVbitmanip):
+ *       https://docs.openhwgroup.org/projects/cv32e40p-user-manual/en/latest/
+ *       instruction_set_extensions.html
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __RISCV_COREV_BITMANIP_H
+#define __RISCV_COREV_BITMANIP_H
+
+#include <stdint.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(__riscv_xcvbitmanip)
+
+#define __DEFAULT_FN_ATTRS                                                     \
+  __attribute__((__always_inline__, __nodebug__, __artificial__))
+
+/* ---------------------------------------------------------------------------
+ * cv.extract rd, rs1, IS3, IS2
+ *   rd = SignExtend(rs1[IS3+IS2-1 : IS2], 32)
+ *   IS3 = bit-width (uimm5), IS2 = lower bit index (uimm5)
+ *   The two immediates are packed as a single uimm10: IS3<<5 | IS2
+ *   IMM must be a compile-time constant.
+ * ---------------------------------------------------------------------------
+ */
+#define __riscv_cv_bitmanip_extract(__rs1, __IMM)                              \
+  ((int32_t)__builtin_riscv_cv_bitmanip_extract((uint32_t)(__rs1),             \
+                                                (uint32_t)(__IMM)))
+
+/* ---------------------------------------------------------------------------
+ * cv.extractu rd, rs1, IS3, IS2
+ *   rd = ZeroExtend(rs1[IS3+IS2-1 : IS2], 32)
+ * ---------------------------------------------------------------------------
+ */
+#define __riscv_cv_bitmanip_extractu(__rs1, __IMM)                             \
+  ((uint32_t)__builtin_riscv_cv_bitmanip_extractu((uint32_t)(__rs1),           \
+                                                  (uint32_t)(__IMM)))
+
+/* ---------------------------------------------------------------------------
+ * cv.bclr rd, rs1, IS3, IS2
+ *   rd = rs1 & ~(((1 << IS3) - 1) << IS2)   (clear IS3 bits starting at IS2)
+ * ---------------------------------------------------------------------------
+ */
+#define __riscv_cv_bitmanip_bclr(__rs1, __IMM)                                 \
+  ((uint32_t)__builtin_riscv_cv_bitmanip_bclr((uint32_t)(__rs1),               \
+                                              (uint32_t)(__IMM)))
+
+/* ---------------------------------------------------------------------------
+ * cv.bset rd, rs1, IS3, IS2
+ *   rd = rs1 | (((1 << IS3) - 1) << IS2)    (set IS3 bits starting at IS2)
+ * ---------------------------------------------------------------------------
+ */
+#define __riscv_cv_bitmanip_bset(__rs1, __IMM)                                 \
+  ((uint32_t)__builtin_riscv_cv_bitmanip_bset((uint32_t)(__rs1),               \
+                                              (uint32_t)(__IMM)))
+
+/* ---------------------------------------------------------------------------
+ * cv.insert rd, rs1, IS3, IS2
+ *   rd[IS3+IS2-1 : IS2] = rs1[IS3-1 : 0]
+ *   rd is read-modify-write (the bits outside the target range are preserved).
+ *   IS3 = bit-width (uimm5), IS2 = lower bit index (uimm5)
+ *   The two immediates are packed as a single uimm10: IS3<<5 | IS2
+ * ---------------------------------------------------------------------------
+ */
+#define __riscv_cv_bitmanip_insert(__rD, __rs1, __IMM)                         \
+  ((uint32_t)__builtin_riscv_cv_bitmanip_insert(                               \
+      (uint32_t)(__rs1), (uint32_t)(__IMM), (uint32_t)(__rD)))
+
+/* ---------------------------------------------------------------------------
+ * cv.clb rd, rs1
+ *   rd = number of consecutive leading bits equal to the sign bit (after the
+ *        sign bit itself), i.e. count leading bits equal to bit 31.
+ *   Equivalent to max(0, clz(rs1) + clz(~rs1) - 1).
+ *
+ * Note: ff1 (find-first-one from LSB), fl1 (find-last-one from LSB), cnt
+ * (popcount) and ror (rotate-right) are intentionally absent from this header.
+ * The compiler automatically selects cv.ff1 / cv.fl1 / cv.cnt / cv.ror from
+ * standard C expressions (__builtin_ctz, __builtin_clz, __builtin_popcount,
+ * rotate patterns) when -march includes xcvbitmanip. No explicit builtin call
+ * is required or provided for those four operations.
+ * ---------------------------------------------------------------------------
+ */
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_bitmanip_clb(uint32_t a) {
+  return __builtin_riscv_cv_bitmanip_clb(a);
+}
+
+/* ---------------------------------------------------------------------------
+ * cv.bitrev rd, rs1, IS2, IS3
+ *   Reverses groups of (2^IS2) bits within each (IS3+1)-bit segment of rs1.
+ *   IS2 = radix (uimm2, 0..3), IS3 = number of significant bits minus 1 (uimm5)
+ *   Both IS2 and IS3 must be compile-time constants.
+ *
+ * Example: cv.bitrev t0, t0, 2, 23 reverses groups of 4 bits within each
+ * 24-bit window.
+ * ---------------------------------------------------------------------------
+ */
+#define __riscv_cv_bitmanip_bitrev(__rs1, __IS2, __IS3)                        \
+  ((uint32_t)__builtin_riscv_cv_bitmanip_bitrev(                               \
+      (uint32_t)(__rs1), (uint32_t)(__IS2), (uint32_t)(__IS3)))
+
+#endif /* __riscv_xcvbitmanip */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __RISCV_COREV_BITMANIP_H */
diff --git a/clang/lib/Headers/riscv_corev_elw.h b/clang/lib/Headers/riscv_corev_elw.h
new file mode 100644
index 0000000000000..5ff9efa1c9aca
--- /dev/null
+++ b/clang/lib/Headers/riscv_corev_elw.h
@@ -0,0 +1,37 @@
+/*===---- riscv_corev_elw.h - CORE-V event-load-word intrinsics ------------===
+ *
+ * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+ * See https://llvm.org/LICENSE.txt for license information.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ *
+ *===-----------------------------------------------------------------------===
+ *
+ * This header provides C intrinsics for the CORE-V XCVelw ISA extension.
+ * Include this header when compiling with -march=..._xcvelw.
+ *
+ * Spec: CV32E40P user manual, Instruction Set Extensions (XCVelw):
+ *       https://docs.openhwgroup.org/projects/cv32e40p-user-manual/en/latest/
+ *       instruction_set_extensions.html
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __RISCV_COREV_ELW_H
+#define __RISCV_COREV_ELW_H
+#include <stdint.h>
+#if defined(__cplusplus)
+extern "C" {
+#endif
+#if defined(__riscv_xcvelw)
+#define __DEFAULT_FN_ATTRS                                                     \
+  __attribute__((__always_inline__, __nodebug__, __artificial__))
+
+// cv.elw rd, imm(rs1): event load word
+static __inline__ int32_t __DEFAULT_FN_ATTRS __riscv_cv_elw_elw(int32_t *ptr) {
+  return __builtin_riscv_cv_elw_elw(ptr);
+}
+#endif
+#if defined(__cplusplus)
+}
+#endif
+#endif
diff --git a/clang/lib/Headers/riscv_corev_mac.h b/clang/lib/Headers/riscv_corev_mac.h
new file mode 100644
index 0000000000000..4b7f5df8f622a
--- /dev/null
+++ b/clang/lib/Headers/riscv_corev_mac.h
@@ -0,0 +1,182 @@
+/*===---- riscv_corev_mac.h - CORE-V multiply-accumulate intrinsics --------===
+ *
+ * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+ * See https://llvm.org/LICENSE.txt for license information.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ *
+ *===-----------------------------------------------------------------------===
+ *
+ * This header provides C intrinsics for the CORE-V XCVmac ISA extension.
+ * Include this header when compiling with -march=..._xcvmac.
+ *
+ * Spec: CV32E40P user manual, Instruction Set Extensions (XCVmac):
+ *       https://docs.openhwgroup.org/projects/cv32e40p-user-manual/en/latest/
+ *       instruction_set_extensions.html
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __RISCV_COREV_MAC_H
+#define __RISCV_COREV_MAC_H
+
+#include <stdint.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(__riscv_xcvmac)
+
+#define __DEFAULT_FN_ATTRS                                                     \
+  __attribute__((__always_inline__, __nodebug__, __artificial__))
+
+/* ---------------------------------------------------------------------------
+ * 32x32-bit MAC / MSU
+ *
+ * cv.mac  rd, rs1, rs2 -> rd += rs1 * rs2
+ * cv.msu  rd, rs1, rs2 -> rd -= rs1 * rs2
+ *
+ * These are the only two operations that take a plain GPR accumulator
+ * (no shift/round), so they can be static inline functions.
+ * ---------------------------------------------------------------------------
+ */
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_mac_mac(int32_t a, int32_t b, int32_t accumulator) {
+  return __builtin_riscv_cv_mac_mac(a, b, accumulator);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_mac_msu(int32_t a, int32_t b, int32_t accumulator) {
+  return __builtin_riscv_cv_mac_msu(a, b, accumulator);
+}
+
+/* ---------------------------------------------------------------------------
+ * 16x16-bit multiply (lower halfwords), unsigned, with normalisation shift
+ *
+ * cv.mulun   rd, rs1, rs2, imm5  -> rd = (u16(rs1) * u16(rs2)) >> imm5
+ * cv.mulhhun rd, rs1, rs2, imm5  -> rd = (u16hi(rs1) * u16hi(rs2)) >> imm5
+ * cv.mulurn  rd, rs1, rs2, imm5  -> rd = (u16(rs1) * u16(rs2) + rnd) >> imm5
+ * cv.mulhhurn rd, rs1, rs2, imm5 -> rd = (u16hi(rs1)*u16hi(rs2) + rnd) >> imm5
+ *
+ * SHIFT must be a compile-time constant in [0, 31].
+ * These are macros so the SHIFT argument is passed as an immediate.
+ * ---------------------------------------------------------------------------
+ */
+
+#define __riscv_cv_mac_muluN(__rs1, __rs2, __SHIFT)                            \
+  ((uint32_t)__builtin_riscv_cv_mac_muluN(                                     \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_mulhhuN(__rs1, __rs2, __SHIFT)                          \
+  ((uint32_t)__builtin_riscv_cv_mac_mulhhuN(                                   \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_muluRN(__rs1, __rs2, __SHIFT)                           \
+  ((uint32_t)__builtin_riscv_cv_mac_muluRN(                                    \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_mulhhuRN(__rs1, __rs2, __SHIFT)                         \
+  ((uint32_t)__builtin_riscv_cv_mac_mulhhuRN(                                  \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__SHIFT)))
+
+/* ---------------------------------------------------------------------------
+ * 16x16-bit multiply (lower halfwords), signed, with normalisation shift
+ *
+ * cv.mulsn    rd, rs1, rs2, imm5 -> rd = (s16(rs1) * s16(rs2)) >> imm5
+ * cv.mulhhsn  rd, rs1, rs2, imm5 -> rd = (s16hi(rs1)*s16hi(rs2)) >> imm5
+ * cv.mulsrn   rd, rs1, rs2, imm5 -> rd = (s16(rs1) * s16(rs2) + rnd) >> imm5
+ * cv.mulhhsrn rd, rs1, rs2, imm5 -> rd = (s16hi(rs1)*s16hi(rs2)+rnd) >> imm5
+ *
+ * SHIFT must be a compile-time constant in [0, 31].
+ * ---------------------------------------------------------------------------
+ */
+
+#define __riscv_cv_mac_mulsN(__rs1, __rs2, __SHIFT)                            \
+  ((int32_t)__builtin_riscv_cv_mac_mulsN((uint32_t)(__rs1), (uint32_t)(__rs2), \
+                                         (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_mulhhsN(__rs1, __rs2, __SHIFT)                          \
+  ((int32_t)__builtin_riscv_cv_mac_mulhhsN(                                    \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_mulsRN(__rs1, __rs2, __SHIFT)                           \
+  ((int32_t)__builtin_riscv_cv_mac_mulsRN(                                     \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_mulhhsRN(__rs1, __rs2, __SHIFT)                         \
+  ((int32_t)__builtin_riscv_cv_mac_mulhhsRN(                                   \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__SHIFT)))
+
+/* ---------------------------------------------------------------------------
+ * 16x16-bit multiply-accumulate (lower halfwords), unsigned
+ *
+ * cv.macun    rd, rs1, rs2, imm5 -> rd = (rd + u16(rs1) * u16(rs2)) >> imm5
+ * cv.machhun  rd, rs1, rs2, imm5 -> rd = (rd + u16hi(rs1)*u16hi(rs2)) >> imm5
+ * cv.macurn   rd, rs1, rs2, imm5 -> rd = (rd + u16(rs1)*u16(rs2)+rnd) >> imm5
+ * cv.machhurn rd, rs1, rs2, imm5 -> rd = (rd+u16hi(rs1)*u16hi(rs2)+rnd)>>imm5
+ *
+ * SHIFT must be a compile-time constant in [0, 31].
+ * rD is the accumulator register (read-modify-write).
+ * ---------------------------------------------------------------------------
+ */
+
+#define __riscv_cv_mac_macuN(__rD, __rs1, __rs2, __SHIFT)                      \
+  ((uint32_t)__builtin_riscv_cv_mac_macuN((uint32_t)(__rs1),                   \
+                                          (uint32_t)(__rs2), (uint32_t)(__rD), \
+                                          (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_machhuN(__rD, __rs1, __rs2, __SHIFT)                    \
+  ((uint32_t)__builtin_riscv_cv_mac_machhuN(                                   \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__rD),                  \
+      (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_macuRN(__rD, __rs1, __rs2, __SHIFT)                     \
+  ((uint32_t)__builtin_riscv_cv_mac_macuRN(                                    \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__rD),                  \
+      (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_machhuRN(__rD, __rs1, __rs2, __SHIFT)                   \
+  ((uint32_t)__builtin_riscv_cv_mac_machhuRN(                                  \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__rD),                  \
+      (uint32_t)(__SHIFT)))
+
+/* ---------------------------------------------------------------------------
+ * 16x16-bit multiply-accumulate (lower halfwords), signed
+ *
+ * cv.macsn    rd, rs1, rs2, imm5 -> rd = (rd + s16(rs1) * s16(rs2)) >> imm5
+ * cv.machhsn  rd, rs1, rs2, imm5 -> rd = (rd+s16hi(rs1)*s16hi(rs2)) >> imm5
+ * cv.macsrn   rd, rs1, rs2, imm5 -> rd = (rd+s16(rs1)*s16(rs2)+rnd) >> imm5
+ * cv.machhsrn rd, rs1, rs2, imm5 -> rd = (rd+s16hi*s16hi+rnd) >> imm5
+ *
+ * SHIFT must be a compile-time constant in [0, 31].
+ * ---------------------------------------------------------------------------
+ */
+
+#define __riscv_cv_mac_macsN(__rD, __rs1, __rs2, __SHIFT)                      \
+  ((int32_t)__builtin_riscv_cv_mac_macsN((uint32_t)(__rs1), (uint32_t)(__rs2), \
+                                         (uint32_t)(__rD),                     \
+                                         (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_machhsN(__rD, __rs1, __rs2, __SHIFT)                    \
+  ((int32_t)__builtin_riscv_cv_mac_machhsN(                                    \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__rD),                  \
+      (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_macsRN(__rD, __rs1, __rs2, __SHIFT)                     \
+  ((int32_t)__builtin_riscv_cv_mac_macsRN((uint32_t)(__rs1),                   \
+                                          (uint32_t)(__rs2), (uint32_t)(__rD), \
+                                          (uint32_t)(__SHIFT)))
+
+#define __riscv_cv_mac_machhsRN(__rD, __rs1, __rs2, __SHIFT)                   \
+  ((int32_t)__builtin_riscv_cv_mac_machhsRN(                                   \
+      (uint32_t)(__rs1), (uint32_t)(__rs2), (uint32_t)(__rD),                  \
+      (uint32_t)(__SHIFT)))
+
+#endif /* __riscv_xcvmac */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __RISCV_COREV_MAC_H */
diff --git a/clang/lib/Headers/riscv_corev_simd.h b/clang/lib/Headers/riscv_corev_simd.h
new file mode 100644
index 0000000000000..7db28d8f9ec0e
--- /dev/null
+++ b/clang/lib/Headers/riscv_corev_simd.h
@@ -0,0 +1,384 @@
+/*===---- riscv_corev_simd.h - CORE-V SIMD intrinsics ----------------------===
+ *
+ * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+ * See https://llvm.org/LICENSE.txt for license information.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ *
+ *===-----------------------------------------------------------------------===
+ *
+ * This header provides C intrinsics for the CORE-V XCVsimd ISA extension.
+ * Include this header when compiling with -march=..._xcvsimd.
+ *
+ * All operations work on packed sub-word elements within a 32-bit GPR:
+ *   - .h variants operate on two packed int16 / uint16 (halfwords)
+ *   - .b variants operate on four packed int8  / uint8  (bytes)
+ *
+ * Element-wise ops (add/sub, min/max, and/or/xor, abs) can also be written
+ * directly on GCC-style packed vector types; these wrappers match the
+ * CORE-V GCC builtin spellings.
+ *
+ * Spec: https://docs.openhwgroup.org/projects/cv32e40p-user-manual/en/latest/
+ *       instruction_set_extensions.html#simd
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __RISCV_COREV_SIMD_H
+#define __RISCV_COREV_SIMD_H
+
+#include <stdint.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(__riscv_xcvsimd)
+
+#define __DEFAULT_FN_ATTRS                                                     \
+  __attribute__((__always_inline__, __nodebug__, __artificial__))
+
+/* ADD / SUB
+ * ---------------------------------------------------------------------- */
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_add_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_add_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_add_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_add_b(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sub_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_sub_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sub_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_sub_b(a0, a1);
+}
+
+
+/* MIN / MINU / MAX / MAXU
+ * ---------------------------------------------------------------------- */
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_min_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_min_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_min_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_min_b(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_minu_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_minu_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_minu_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_minu_b(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_max_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_max_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_max_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_max_b(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_maxu_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_maxu_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_maxu_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_maxu_b(a0, a1);
+}
+
+
+/* AND / OR / XOR
+ * ---------------------------------------------------------------------- */
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_and_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_and_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_and_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_and_b(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_or_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_or_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_or_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_or_b(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_xor_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_xor_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_xor_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_xor_b(a0, a1);
+}
+
+
+/* ABS
+ * ---------------------------------------------------------------------- */
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_abs_h(uint32_t a0) {
+  return __builtin_riscv_cv_simd_abs_h(a0);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_abs_b(uint32_t a0) {
+  return __builtin_riscv_cv_simd_abs_b(a0);
+}
+
+
+/* DOT PRODUCTS
+ * ---------------------------------------------------------------------- */
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotup_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotup_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotup_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotup_b(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotup_sc_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotup_sc_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotup_sc_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotup_sc_b(a0, a1);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotusp_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotusp_h(a0, a1);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotusp_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotusp_b(a0, a1);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotusp_sc_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotusp_sc_h(a0, a1);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotusp_sc_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotusp_sc_b(a0, a1);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotsp_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotsp_h(a0, a1);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotsp_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotsp_b(a0, a1);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotsp_sc_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotsp_sc_h(a0, a1);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_dotsp_sc_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotsp_sc_b(a0, a1);
+}
+
+
+/* SDOT (accumulating)
+ * ---------------------------------------------------------------------- */
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotup_h(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_sdotup_h(a0, a1, a2);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotup_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_sdotup_b(a0, a1, a2);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotup_sc_h(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_sdotup_sc_h(a0, a1, a2);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotup_sc_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_sdotup_sc_b(a0, a1, a2);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotusp_h(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotusp_h(a0, a1, a2);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotusp_b(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotusp_b(a0, a1, a2);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotusp_sc_h(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotusp_sc_h(a0, a1, a2);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotusp_sc_b(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotusp_sc_b(a0, a1, a2);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotsp_h(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotsp_h(a0, a1, a2);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotsp_b(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotsp_b(a0, a1, a2);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotsp_sc_h(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotsp_sc_h(a0, a1, a2);
+}
+
+static __inline__ int32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_sdotsp_sc_b(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotsp_sc_b(a0, a1, a2);
+}
+
+
+/* EXTRACT / INSERT
+ * ---------------------------------------------------------------------- */
+
+#define __riscv_cv_simd_extract_h(__a0, __a1) \
+  ((int32_t)__builtin_riscv_cv_simd_extract_h((uint32_t)(__a0), (uint32_t)(__a1)))
+
+#define __riscv_cv_simd_extract_b(__a0, __a1) \
+  ((int32_t)__builtin_riscv_cv_simd_extract_b((uint32_t)(__a0), (uint32_t)(__a1)))
+
+#define __riscv_cv_simd_extractu_h(__a0, __a1) \
+  ((uint32_t)__builtin_riscv_cv_simd_extractu_h((uint32_t)(__a0), (uint32_t)(__a1)))
+
+#define __riscv_cv_simd_extractu_b(__a0, __a1) \
+  ((uint32_t)__builtin_riscv_cv_simd_extractu_b((uint32_t)(__a0), (uint32_t)(__a1)))
+
+#define __riscv_cv_simd_insert_h(__a0, __a1, __a2) \
+  ((uint32_t)__builtin_riscv_cv_simd_insert_h((uint32_t)(__a0), (uint32_t)(__a1), (uint32_t)(__a2)))
+
+#define __riscv_cv_simd_insert_b(__a0, __a1, __a2) \
+  ((uint32_t)__builtin_riscv_cv_simd_insert_b((uint32_t)(__a0), (uint32_t)(__a1), (uint32_t)(__a2)))
+
+
+/* SHUFFLE
+ * ---------------------------------------------------------------------- */
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_shuffle_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_shuffle_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_shuffle_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_shuffle_b(a0, a1);
+}
+
+#define __riscv_cv_simd_shuffle_sci_h(__a0, __a1) \
+  ((uint32_t)__builtin_riscv_cv_simd_shuffle_sci_h((uint32_t)(__a0), (uint32_t)(__a1)))
+
+#define __riscv_cv_simd_shuffle_sci_b(__a0, __a1) \
+  ((uint32_t)__builtin_riscv_cv_simd_shuffle_sci_b((uint32_t)(__a0), (uint32_t)(__a1)))
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_shuffle2_h(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_shuffle2_h(a0, a1, a2);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_shuffle2_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_shuffle2_b(a0, a1, a2);
+}
+
+
+/* PACK
+ * ---------------------------------------------------------------------- */
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_packhi_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_packhi_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_packlo_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_packlo_h(a0, a1);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_packhi_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_packhi_b(a0, a1, a2);
+}
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_packlo_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_packlo_b(a0, a1, a2);
+}
+
+
+/* COMPLEX
+ * ---------------------------------------------------------------------- */
+
+#define __riscv_cv_simd_cplxmul_r(__a0, __a1, __a2, __a3) \
+  ((uint32_t)__builtin_riscv_cv_simd_cplxmul_r((uint32_t)(__a0), (uint32_t)(__a1), (uint32_t)(__a2), (uint32_t)(__a3)))
+
+#define __riscv_cv_simd_cplxmul_i(__a0, __a1, __a2, __a3) \
+  ((uint32_t)__builtin_riscv_cv_simd_cplxmul_i((uint32_t)(__a0), (uint32_t)(__a1), (uint32_t)(__a2), (uint32_t)(__a3)))
+
+static __inline__ uint32_t __DEFAULT_FN_ATTRS
+__riscv_cv_simd_cplxconj(uint32_t a0) {
+  return __builtin_riscv_cv_simd_cplxconj(a0);
+}
+
+#define __riscv_cv_simd_subrotmj(__a0, __a1, __a2) \
+  ((uint32_t)__builtin_riscv_cv_simd_subrotmj((uint32_t)(__a0), (uint32_t)(__a1), (uint32_t)(__a2)))
+
+
+#endif /* __riscv_xcvsimd */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __RISCV_COREV_SIMD_H */
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index 9647a7d913744..66d1d280b70ca 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -652,6 +652,61 @@ bool SemaRISCV::CheckBuiltinFunctionCall(const TargetInfo &TI,
 
     break;
   }
+
+  // XCVbitmanip — bitrev: IS2 (radix) in [0,3], IS3 (pts) in [0,31]
+  case RISCV::BI__builtin_riscv_cv_bitmanip_bitrev:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 3) ||
+           SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 31);
+
+  // XCVmac — shift amount in [0, 31]
+  case RISCV::BI__builtin_riscv_cv_mac_muluN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhuN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulsN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhsN:
+  case RISCV::BI__builtin_riscv_cv_mac_muluRN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhuRN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulsRN:
+  case RISCV::BI__builtin_riscv_cv_mac_mulhhsRN:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 31);
+  case RISCV::BI__builtin_riscv_cv_mac_macuN:
+  case RISCV::BI__builtin_riscv_cv_mac_machhuN:
+  case RISCV::BI__builtin_riscv_cv_mac_macsN:
+  case RISCV::BI__builtin_riscv_cv_mac_machhsN:
+  case RISCV::BI__builtin_riscv_cv_mac_macuRN:
+  case RISCV::BI__builtin_riscv_cv_mac_machhuRN:
+  case RISCV::BI__builtin_riscv_cv_mac_macsRN:
+  case RISCV::BI__builtin_riscv_cv_mac_machhsRN:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 3, 0, 31);
+
+  // XCVsimd — extract/insert index
+  // [0,1] for 2 halfwords
+  case RISCV::BI__builtin_riscv_cv_simd_extract_h:
+  case RISCV::BI__builtin_riscv_cv_simd_extractu_h:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 1);
+  // [0,1] for 2 halfwords
+  case RISCV::BI__builtin_riscv_cv_simd_insert_h:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 1);
+  // [0,3] for 4 bytes
+  case RISCV::BI__builtin_riscv_cv_simd_extract_b:
+  case RISCV::BI__builtin_riscv_cv_simd_extractu_b:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 3);
+  // [0,3] for 4 bytes
+  case RISCV::BI__builtin_riscv_cv_simd_insert_b:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 3);
+  // Note: insert has index at arg 2 not 1
+  // Shuffle SCI — 6-bit immediate [0, 63]
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_sci_h:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 3);
+  // 8-bit, split by codegen
+  case RISCV::BI__builtin_riscv_cv_simd_shuffle_sci_b:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 255);
+
+  // complex mul div code [0,3]
+  case RISCV::BI__builtin_riscv_cv_simd_cplxmul_r:
+  case RISCV::BI__builtin_riscv_cv_simd_cplxmul_i:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 3, 0, 3);
+  case RISCV::BI__builtin_riscv_cv_simd_subrotmj:
+    return SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 3);
   }
 
   auto CheckVSetVL = [&](unsigned SEWOffset, unsigned LMULOffset) -> bool {

>From b1511b437d7986231d93c10235e22236c8349c63 Mon Sep 17 00:00:00 2001
From: vitbur <vittorioburani at gmail.com>
Date: Thu, 25 Jun 2026 16:20:02 +0200
Subject: [PATCH 4/4] [Clang][RISCV] Add tests for the CORE-V XCV builtins

CodeGen tests for the XCVmac, XCVelw, XCVbitmanip and XCVsimd builtins,
plus Sema range checks for their immediate operands. The XCVsimd
element-wise builtins check the native packed-vector IR; the rest check
the llvm.riscv.cv.* intrinsic calls.
---
 .../CodeGen/RISCV/rvv-xcvbitmanip-builtins.c  |   95 ++
 .../test/CodeGen/RISCV/rvv-xcvelw-builtins.c  |   20 +
 .../test/CodeGen/RISCV/rvv-xcvmac-builtins.c  |  318 +++++
 .../test/CodeGen/RISCV/rvv-xcvsimd-builtins.c | 1055 +++++++++++++++++
 clang/test/Sema/rvv-xcv-sema.c                |   59 +
 5 files changed, 1547 insertions(+)
 create mode 100644 clang/test/CodeGen/RISCV/rvv-xcvbitmanip-builtins.c
 create mode 100644 clang/test/CodeGen/RISCV/rvv-xcvelw-builtins.c
 create mode 100644 clang/test/CodeGen/RISCV/rvv-xcvmac-builtins.c
 create mode 100644 clang/test/CodeGen/RISCV/rvv-xcvsimd-builtins.c
 create mode 100644 clang/test/Sema/rvv-xcv-sema.c

diff --git a/clang/test/CodeGen/RISCV/rvv-xcvbitmanip-builtins.c b/clang/test/CodeGen/RISCV/rvv-xcvbitmanip-builtins.c
new file mode 100644
index 0000000000000..98e976bc4122b
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/rvv-xcvbitmanip-builtins.c
@@ -0,0 +1,95 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -triple riscv32 -target-feature +xcvbitmanip -emit-llvm %s -o - \
+// RUN:   | FileCheck %s
+
+// This file tests that XCVbitmanip Clang builtins are correctly lowered
+// to their corresponding LLVM IR intrinsics.
+
+#include <stdint.h>
+
+// CHECK-LABEL: @test_extract(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.bitmanip.extract(i32 [[TMP0]], i32 164)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_extract(uint32_t a) {
+  return __builtin_riscv_cv_bitmanip_extract(a, 164);
+}
+
+// CHECK-LABEL: @test_extractu(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.bitmanip.extractu(i32 [[TMP0]], i32 164)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_extractu(uint32_t a) {
+  return __builtin_riscv_cv_bitmanip_extractu(a, 164);
+}
+
+// CHECK-LABEL: @test_bclr(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.bitmanip.bclr(i32 [[TMP0]], i32 164)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_bclr(uint32_t a) {
+  return __builtin_riscv_cv_bitmanip_bclr(a, 164);
+}
+
+// CHECK-LABEL: @test_bset(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.bitmanip.bset(i32 [[TMP0]], i32 164)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_bset(uint32_t a) {
+  return __builtin_riscv_cv_bitmanip_bset(a, 164);
+}
+
+// CHECK-LABEL: @test_insert(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[RD_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[RD:%.*]], ptr [[RD_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[RD_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.bitmanip.insert(i32 [[TMP0]], i32 164, i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_insert(uint32_t a, uint32_t rd) {
+  return __builtin_riscv_cv_bitmanip_insert(a, 164, rd);
+}
+
+// CHECK-LABEL: @test_clb(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.bitmanip.clb(i32 [[TMP0]])
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_clb(uint32_t a) {
+  return __builtin_riscv_cv_bitmanip_clb(a);
+}
+
+// CHECK-LABEL: @test_bitrev(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.bitmanip.bitrev(i32 [[TMP0]], i32 2, i32 23)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_bitrev(uint32_t a) {
+  return __builtin_riscv_cv_bitmanip_bitrev(a, 2, 23);
+}
diff --git a/clang/test/CodeGen/RISCV/rvv-xcvelw-builtins.c b/clang/test/CodeGen/RISCV/rvv-xcvelw-builtins.c
new file mode 100644
index 0000000000000..0eb41ec3261e4
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/rvv-xcvelw-builtins.c
@@ -0,0 +1,20 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -triple riscv32 -target-feature +xcvelw -emit-llvm %s -o - \
+// RUN:   | FileCheck %s
+
+// This file tests that the XCVelw Clang builtin is correctly lowered
+// to its corresponding LLVM IR intrinsic.
+
+#include <stdint.h>
+
+// CHECK-LABEL: @test_elw(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[ADDR_ADDR:%.*]] = alloca ptr, align 4
+// CHECK-NEXT:    store ptr [[ADDR:%.*]], ptr [[ADDR_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ADDR_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.elw.elw(ptr [[TMP0]])
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+int32_t test_elw(int32_t *addr) {
+  return __builtin_riscv_cv_elw_elw(addr);
+}
diff --git a/clang/test/CodeGen/RISCV/rvv-xcvmac-builtins.c b/clang/test/CodeGen/RISCV/rvv-xcvmac-builtins.c
new file mode 100644
index 0000000000000..84d7d9c02c5a2
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/rvv-xcvmac-builtins.c
@@ -0,0 +1,318 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -triple riscv32 -target-feature +xcvmac -emit-llvm %s -o - \
+// RUN:   | FileCheck %s
+
+// This file tests that XCVmac Clang builtins are correctly lowered
+// to their corresponding LLVM IR intrinsics.
+
+#include <stdint.h>
+
+// ---- 32x32 MAC/MSU ----
+
+// CHECK-LABEL: @test_mac(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.mac(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_mac(int32_t a, int32_t b, int32_t acc) {
+  return __builtin_riscv_cv_mac_mac(a, b, acc);
+}
+
+// CHECK-LABEL: @test_msu(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.msu(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_msu(int32_t a, int32_t b, int32_t acc) {
+  return __builtin_riscv_cv_mac_msu(a, b, acc);
+}
+
+// ---- Unsigned 16x16 multiply with shift ----
+
+// CHECK-LABEL: @test_muluN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.mac.muluN(i32 [[TMP0]], i32 [[TMP1]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_muluN(uint32_t a, uint32_t b) {
+  return __builtin_riscv_cv_mac_muluN(a, b, 5);
+}
+
+// CHECK-LABEL: @test_mulhhuN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.mac.mulhhuN(i32 [[TMP0]], i32 [[TMP1]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_mulhhuN(uint32_t a, uint32_t b) {
+  return __builtin_riscv_cv_mac_mulhhuN(a, b, 5);
+}
+
+// CHECK-LABEL: @test_muluRN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.mac.muluRN(i32 [[TMP0]], i32 [[TMP1]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_muluRN(uint32_t a, uint32_t b) {
+  return __builtin_riscv_cv_mac_muluRN(a, b, 5);
+}
+
+// CHECK-LABEL: @test_mulhhuRN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.mac.mulhhuRN(i32 [[TMP0]], i32 [[TMP1]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_mulhhuRN(uint32_t a, uint32_t b) {
+  return __builtin_riscv_cv_mac_mulhhuRN(a, b, 5);
+}
+
+// ---- Signed 16x16 multiply with shift ----
+
+// CHECK-LABEL: @test_mulsN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.mac.mulsN(i32 [[TMP0]], i32 [[TMP1]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_mulsN(uint32_t a, uint32_t b) {
+  return __builtin_riscv_cv_mac_mulsN(a, b, 5);
+}
+
+// CHECK-LABEL: @test_mulhhsN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.mac.mulhhsN(i32 [[TMP0]], i32 [[TMP1]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_mulhhsN(uint32_t a, uint32_t b) {
+  return __builtin_riscv_cv_mac_mulhhsN(a, b, 5);
+}
+
+// CHECK-LABEL: @test_mulsRN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.mac.mulsRN(i32 [[TMP0]], i32 [[TMP1]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_mulsRN(uint32_t a, uint32_t b) {
+  return __builtin_riscv_cv_mac_mulsRN(a, b, 5);
+}
+
+// CHECK-LABEL: @test_mulhhsRN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.mac.mulhhsRN(i32 [[TMP0]], i32 [[TMP1]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_mulhhsRN(uint32_t a, uint32_t b) {
+  return __builtin_riscv_cv_mac_mulhhsRN(a, b, 5);
+}
+
+// ---- Unsigned 16x16 MAC with shift ----
+
+// CHECK-LABEL: @test_macuN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.macuN(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_macuN(uint32_t a, uint32_t b, uint32_t acc) {
+  return __builtin_riscv_cv_mac_macuN(a, b, acc, 5);
+}
+
+// CHECK-LABEL: @test_machhuN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.machhuN(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_machhuN(uint32_t a, uint32_t b, uint32_t acc) {
+  return __builtin_riscv_cv_mac_machhuN(a, b, acc, 5);
+}
+
+// CHECK-LABEL: @test_macuRN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.macuRN(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_macuRN(uint32_t a, uint32_t b, uint32_t acc) {
+  return __builtin_riscv_cv_mac_macuRN(a, b, acc, 5);
+}
+
+// CHECK-LABEL: @test_machhuRN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.machhuRN(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_machhuRN(uint32_t a, uint32_t b, uint32_t acc) {
+  return __builtin_riscv_cv_mac_machhuRN(a, b, acc, 5);
+}
+
+// ---- Signed 16x16 MAC with shift ----
+
+// CHECK-LABEL: @test_macsN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.macsN(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_macsN(uint32_t a, uint32_t b, uint32_t acc) {
+  return __builtin_riscv_cv_mac_macsN(a, b, acc, 5);
+}
+
+// CHECK-LABEL: @test_machhsN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.machhsN(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_machhsN(uint32_t a, uint32_t b, uint32_t acc) {
+  return __builtin_riscv_cv_mac_machhsN(a, b, acc, 5);
+}
+
+// CHECK-LABEL: @test_macsRN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.macsRN(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_macsRN(uint32_t a, uint32_t b, uint32_t acc) {
+  return __builtin_riscv_cv_mac_macsRN(a, b, acc, 5);
+}
+
+// CHECK-LABEL: @test_machhsRN(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[ACC_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A:%.*]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[B:%.*]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[ACC:%.*]], ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ACC_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.mac.machhsRN(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 5)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_machhsRN(uint32_t a, uint32_t b, uint32_t acc) {
+  return __builtin_riscv_cv_mac_machhsRN(a, b, acc, 5);
+}
diff --git a/clang/test/CodeGen/RISCV/rvv-xcvsimd-builtins.c b/clang/test/CodeGen/RISCV/rvv-xcvsimd-builtins.c
new file mode 100644
index 0000000000000..0f67736fc8f5a
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/rvv-xcvsimd-builtins.c
@@ -0,0 +1,1055 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -triple riscv32 -target-feature +xcvsimd -emit-llvm %s -o - \
+// RUN:   | FileCheck %s
+
+// XCVsimd Clang builtins: element-wise ops lower to native packed-vector IR
+// (selecting the cv.*.h / cv.*.b instructions); the non-representable ops
+// (dot products, shuffle, pack, complex, extract/insert) lower to the
+// dedicated llvm.riscv.cv.simd.* intrinsics.
+
+#include <stdint.h>
+
+// CHECK-LABEL: @test_add_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <2 x i16>
+// CHECK-NEXT:    [[TMP4:%.*]] = add <2 x i16> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <2 x i16> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_add_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_add_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_add_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <4 x i8>
+// CHECK-NEXT:    [[TMP4:%.*]] = add <4 x i8> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <4 x i8> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_add_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_add_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_sub_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <2 x i16>
+// CHECK-NEXT:    [[TMP4:%.*]] = sub <2 x i16> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <2 x i16> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_sub_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_sub_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_sub_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <4 x i8>
+// CHECK-NEXT:    [[TMP4:%.*]] = sub <4 x i8> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <4 x i8> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_sub_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_sub_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_min_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <2 x i16>
+// CHECK-NEXT:    [[TMP4:%.*]] = call <2 x i16> @llvm.smin.v2i16(<2 x i16> [[TMP2]], <2 x i16> [[TMP3]])
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <2 x i16> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_min_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_min_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_min_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <4 x i8>
+// CHECK-NEXT:    [[TMP4:%.*]] = call <4 x i8> @llvm.smin.v4i8(<4 x i8> [[TMP2]], <4 x i8> [[TMP3]])
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <4 x i8> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_min_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_min_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_minu_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <2 x i16>
+// CHECK-NEXT:    [[TMP4:%.*]] = call <2 x i16> @llvm.umin.v2i16(<2 x i16> [[TMP2]], <2 x i16> [[TMP3]])
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <2 x i16> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_minu_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_minu_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_minu_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <4 x i8>
+// CHECK-NEXT:    [[TMP4:%.*]] = call <4 x i8> @llvm.umin.v4i8(<4 x i8> [[TMP2]], <4 x i8> [[TMP3]])
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <4 x i8> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_minu_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_minu_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_max_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <2 x i16>
+// CHECK-NEXT:    [[TMP4:%.*]] = call <2 x i16> @llvm.smax.v2i16(<2 x i16> [[TMP2]], <2 x i16> [[TMP3]])
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <2 x i16> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_max_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_max_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_max_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <4 x i8>
+// CHECK-NEXT:    [[TMP4:%.*]] = call <4 x i8> @llvm.smax.v4i8(<4 x i8> [[TMP2]], <4 x i8> [[TMP3]])
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <4 x i8> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_max_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_max_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_maxu_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <2 x i16>
+// CHECK-NEXT:    [[TMP4:%.*]] = call <2 x i16> @llvm.umax.v2i16(<2 x i16> [[TMP2]], <2 x i16> [[TMP3]])
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <2 x i16> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_maxu_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_maxu_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_maxu_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <4 x i8>
+// CHECK-NEXT:    [[TMP4:%.*]] = call <4 x i8> @llvm.umax.v4i8(<4 x i8> [[TMP2]], <4 x i8> [[TMP3]])
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <4 x i8> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_maxu_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_maxu_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_and_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <2 x i16>
+// CHECK-NEXT:    [[TMP4:%.*]] = and <2 x i16> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <2 x i16> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_and_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_and_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_and_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <4 x i8>
+// CHECK-NEXT:    [[TMP4:%.*]] = and <4 x i8> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <4 x i8> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_and_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_and_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_or_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <2 x i16>
+// CHECK-NEXT:    [[TMP4:%.*]] = or <2 x i16> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <2 x i16> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_or_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_or_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_or_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <4 x i8>
+// CHECK-NEXT:    [[TMP4:%.*]] = or <4 x i8> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <4 x i8> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_or_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_or_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_xor_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <2 x i16>
+// CHECK-NEXT:    [[TMP4:%.*]] = xor <2 x i16> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <2 x i16> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_xor_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_xor_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_xor_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast i32 [[TMP1]] to <4 x i8>
+// CHECK-NEXT:    [[TMP4:%.*]] = xor <4 x i8> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    [[TMP5:%.*]] = bitcast <4 x i8> [[TMP4]] to i32
+// CHECK-NEXT:    ret i32 [[TMP5]]
+//
+uint32_t test_xor_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_xor_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_abs_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = bitcast i32 [[TMP0]] to <2 x i16>
+// CHECK-NEXT:    [[TMP2:%.*]] = call <2 x i16> @llvm.abs.v2i16(<2 x i16> [[TMP1]], i1 false)
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast <2 x i16> [[TMP2]] to i32
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_abs_h(uint32_t a0) {
+  return __builtin_riscv_cv_simd_abs_h(a0);
+}
+
+// CHECK-LABEL: @test_abs_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+// CHECK-NEXT:    [[TMP2:%.*]] = call <4 x i8> @llvm.abs.v4i8(<4 x i8> [[TMP1]], i1 false)
+// CHECK-NEXT:    [[TMP3:%.*]] = bitcast <4 x i8> [[TMP2]] to i32
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_abs_b(uint32_t a0) {
+  return __builtin_riscv_cv_simd_abs_b(a0);
+}
+
+// CHECK-LABEL: @test_dotup_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotup.h(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_dotup_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotup_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotup_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotup.b(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_dotup_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotup_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotup_sc_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotup.sc.h(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_dotup_sc_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotup_sc_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotup_sc_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotup.sc.b(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_dotup_sc_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotup_sc_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotusp_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotusp.h(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_dotusp_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotusp_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotusp_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotusp.b(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_dotusp_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotusp_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotusp_sc_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotusp.sc.h(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_dotusp_sc_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotusp_sc_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotusp_sc_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotusp.sc.b(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_dotusp_sc_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotusp_sc_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotsp_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotsp.h(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_dotsp_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotsp_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotsp_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotsp.b(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_dotsp_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotsp_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotsp_sc_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotsp.sc.h(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_dotsp_sc_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotsp_sc_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_dotsp_sc_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.dotsp.sc.b(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int32_t test_dotsp_sc_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_dotsp_sc_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_sdotup_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotup.h(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_sdotup_h(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_sdotup_h(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotup_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotup.b(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_sdotup_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_sdotup_b(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotup_sc_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotup.sc.h(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_sdotup_sc_h(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_sdotup_sc_h(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotup_sc_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotup.sc.b(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_sdotup_sc_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_sdotup_sc_b(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotusp_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotusp.h(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_sdotusp_h(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotusp_h(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotusp_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotusp.b(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_sdotusp_b(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotusp_b(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotusp_sc_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotusp.sc.h(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_sdotusp_sc_h(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotusp_sc_h(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotusp_sc_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotusp.sc.b(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_sdotusp_sc_b(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotusp_sc_b(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotsp_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotsp.h(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_sdotsp_h(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotsp_h(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotsp_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotsp.b(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_sdotsp_b(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotsp_b(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotsp_sc_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotsp.sc.h(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_sdotsp_sc_h(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotsp_sc_h(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_sdotsp_sc_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.sdotsp.sc.b(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+int32_t test_sdotsp_sc_b(uint32_t a0, uint32_t a1, int32_t a2) {
+  return __builtin_riscv_cv_simd_sdotsp_sc_b(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_extract_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.simd.extract.h(i32 [[TMP0]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+int32_t test_extract_h(uint32_t a0) {
+  return __builtin_riscv_cv_simd_extract_h(a0, 0);
+}
+
+// CHECK-LABEL: @test_extract_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.simd.extract.b(i32 [[TMP0]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+int32_t test_extract_b(uint32_t a0) {
+  return __builtin_riscv_cv_simd_extract_b(a0, 0);
+}
+
+// CHECK-LABEL: @test_extractu_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.simd.extractu.h(i32 [[TMP0]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_extractu_h(uint32_t a0) {
+  return __builtin_riscv_cv_simd_extractu_h(a0, 0);
+}
+
+// CHECK-LABEL: @test_extractu_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.simd.extractu.b(i32 [[TMP0]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_extractu_b(uint32_t a0) {
+  return __builtin_riscv_cv_simd_extractu_b(a0, 0);
+}
+
+// CHECK-LABEL: @test_insert_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.insert.h(i32 [[TMP0]], i32 [[TMP1]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_insert_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_insert_h(a0, a1, 0);
+}
+
+// CHECK-LABEL: @test_insert_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.insert.b(i32 [[TMP0]], i32 [[TMP1]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_insert_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_insert_b(a0, a1, 0);
+}
+
+// CHECK-LABEL: @test_shuffle_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.shuffle.h(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_shuffle_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_shuffle_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_shuffle_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.shuffle.b(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_shuffle_b(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_shuffle_b(a0, a1);
+}
+
+// CHECK-LABEL: @test_shuffle_sci_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.simd.shuffle.sci.h(i32 [[TMP0]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_shuffle_sci_h(uint32_t a0) {
+  return __builtin_riscv_cv_simd_shuffle_sci_h(a0, 0);
+}
+
+// CHECK-LABEL: @test_shuffle_sci_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.simd.shuffle.sci.b(i32 [[TMP0]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_shuffle_sci_b(uint32_t a0) {
+  return __builtin_riscv_cv_simd_shuffle_sci_b(a0, 0);
+}
+
+// CHECK-LABEL: @test_shuffle2_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.shuffle2.h(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_shuffle2_h(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_shuffle2_h(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_shuffle2_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.shuffle2.b(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_shuffle2_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_shuffle2_b(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_packhi_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.packhi.h(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_packhi_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_packhi_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_packlo_h(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.packlo.h(i32 [[TMP0]], i32 [[TMP1]])
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_packlo_h(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_packlo_h(a0, a1);
+}
+
+// CHECK-LABEL: @test_packhi_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.packhi.b(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_packhi_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_packhi_b(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_packlo_b(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.packlo.b(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]])
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_packlo_b(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_packlo_b(a0, a1, a2);
+}
+
+// CHECK-LABEL: @test_cplxmul_r(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.cplxmul.r(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_cplxmul_r(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_cplxmul_r(a0, a1, a2, 0);
+}
+
+// CHECK-LABEL: @test_cplxmul_i(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A2:%.*]], ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A2_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.riscv.cv.simd.cplxmul.i(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP3]]
+//
+uint32_t test_cplxmul_i(uint32_t a0, uint32_t a1, uint32_t a2) {
+  return __builtin_riscv_cv_simd_cplxmul_i(a0, a1, a2, 0);
+}
+
+// CHECK-LABEL: @test_cplxconj(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.riscv.cv.simd.cplxconj(i32 [[TMP0]])
+// CHECK-NEXT:    ret i32 [[TMP1]]
+//
+uint32_t test_cplxconj(uint32_t a0) {
+  return __builtin_riscv_cv_simd_cplxconj(a0);
+}
+
+// CHECK-LABEL: @test_subrotmj(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[A0_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[A1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[A1:%.*]], ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A0_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A1_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.riscv.cv.simd.subrotmj(i32 [[TMP0]], i32 [[TMP1]], i32 0)
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+uint32_t test_subrotmj(uint32_t a0, uint32_t a1) {
+  return __builtin_riscv_cv_simd_subrotmj(a0, a1, 0);
+}
diff --git a/clang/test/Sema/rvv-xcv-sema.c b/clang/test/Sema/rvv-xcv-sema.c
new file mode 100644
index 0000000000000..38ea47fdc06e7
--- /dev/null
+++ b/clang/test/Sema/rvv-xcv-sema.c
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -triple riscv32 -target-feature +xcvmac -target-feature +xcvsimd \
+// RUN:   -target-feature +xcvbitmanip -fsyntax-only -verify %s
+
+// This file tests that Sema range checks correctly reject out-of-range
+// immediate arguments for XCV builtins.
+
+#include <stdint.h>
+
+// ===== XCVmac — shift must be in [0, 31] =====
+
+void test_mac_range(uint32_t a, uint32_t b, uint32_t c) {
+  (void)__builtin_riscv_cv_mac_muluN(a, b, 31);  // OK: max value
+  (void)__builtin_riscv_cv_mac_muluN(a, b, 32);  // expected-error {{argument value 32 is outside the valid range [0, 31]}}
+  (void)__builtin_riscv_cv_mac_mulsN(a, b, 0);   // OK: min value
+  (void)__builtin_riscv_cv_mac_macuN(a, b, c, 31);  // OK
+  (void)__builtin_riscv_cv_mac_macuN(a, b, c, 32);  // expected-error {{argument value 32 is outside the valid range [0, 31]}}
+}
+
+// ===== XCVsimd — various immediate ranges =====
+
+void test_simd_extract_range(uint32_t a) {
+  (void)__builtin_riscv_cv_simd_extract_h(a, 0);   // OK
+  (void)__builtin_riscv_cv_simd_extract_h(a, 1);   // OK: max for halfword
+  (void)__builtin_riscv_cv_simd_extract_h(a, 2);   // expected-error {{argument value 2 is outside the valid range [0, 1]}}
+  (void)__builtin_riscv_cv_simd_extract_b(a, 3);   // OK: max for byte
+  (void)__builtin_riscv_cv_simd_extract_b(a, 4);   // expected-error {{argument value 4 is outside the valid range [0, 3]}}
+}
+
+void test_simd_insert_range(uint32_t a, uint32_t b) {
+  (void)__builtin_riscv_cv_simd_insert_h(a, b, 1);   // OK
+  (void)__builtin_riscv_cv_simd_insert_h(a, b, 2);   // expected-error {{argument value 2 is outside the valid range [0, 1]}}
+  (void)__builtin_riscv_cv_simd_insert_b(a, b, 3);   // OK
+  (void)__builtin_riscv_cv_simd_insert_b(a, b, 4);   // expected-error {{argument value 4 is outside the valid range [0, 3]}}
+}
+
+void test_simd_shuffle_sci_range(uint32_t a) {
+  (void)__builtin_riscv_cv_simd_shuffle_sci_h(a, 3);    // OK: max for 2-bit
+  (void)__builtin_riscv_cv_simd_shuffle_sci_h(a, 4);    // expected-error {{argument value 4 is outside the valid range [0, 3]}}
+  (void)__builtin_riscv_cv_simd_shuffle_sci_b(a, 255);  // OK: max for 8-bit
+  (void)__builtin_riscv_cv_simd_shuffle_sci_b(a, 256);  // expected-error {{argument value 256 is outside the valid range [0, 255]}}
+}
+
+void test_simd_cplxmul_range(uint32_t a, uint32_t b, uint32_t c) {
+  (void)__builtin_riscv_cv_simd_cplxmul_r(a, b, c, 3);  // OK
+  (void)__builtin_riscv_cv_simd_cplxmul_r(a, b, c, 4);  // expected-error {{argument value 4 is outside the valid range [0, 3]}}
+}
+
+void test_simd_subrotmj_range(uint32_t a, uint32_t b) {
+  (void)__builtin_riscv_cv_simd_subrotmj(a, b, 3);  // OK
+  (void)__builtin_riscv_cv_simd_subrotmj(a, b, 4);  // expected-error {{argument value 4 is outside the valid range [0, 3]}}
+}
+
+// ===== XCVbitmanip — bitrev ranges =====
+
+void test_bitmanip_bitrev_range(uint32_t a) {
+  (void)__builtin_riscv_cv_bitmanip_bitrev(a, 3, 31);  // OK: max values
+  (void)__builtin_riscv_cv_bitmanip_bitrev(a, 4, 0);   // expected-error {{argument value 4 is outside the valid range [0, 3]}}
+  (void)__builtin_riscv_cv_bitmanip_bitrev(a, 0, 32);  // expected-error {{argument value 32 is outside the valid range [0, 31]}}
+}



More information about the cfe-commits mailing list