[llvm] [RISCV][XCV] Add LLVM intrinsics for non-representable XCVsimd ops (PR #204880)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 2 07:29:03 PDT 2026
https://github.com/VittorioBurani updated https://github.com/llvm/llvm-project/pull/204880
>From 7a5d8a2f8907fc704c11308306f5d44f23dae9ce Mon Sep 17 00:00:00 2001
From: vitbur <vittorioburani at gmail.com>
Date: Wed, 1 Apr 2026 18:45:22 +0200
Subject: [PATCH] [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).
Assisted-by: Claude (Anthropic)
---
llvm/include/llvm/IR/IntrinsicsRISCVXCV.td | 112 ++++++
llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td | 222 ++++++++++++
llvm/test/CodeGen/RISCV/xcvsimd.ll | 388 +++++++++++++++++++++
3 files changed, 722 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..8de2217360824 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: 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 arg1 is rd (accumulator):
+// (rs1, rd_in, 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,68 @@ let TargetPrefix = "riscv" in {
def int_riscv_cv_elw_elw
: Intrinsic<[llvm_i32_ty], [llvm_ptr_ty],
[IntrReadMem, IntrArgMemOnly, IntrHasSideEffects]>;
+
+ // ==== 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: (rs1, rd_in, 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: (rs1, rs2, rd_in) -> rd_out (read-modify-write)
+ def int_riscv_cv_simd_packhi_b : ScalarCoreVSimdGprGprGprIntrinsic;
+ def int_riscv_cv_simd_packlo_b : ScalarCoreVSimdGprGprGprIntrinsic;
+
+ // ==== 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/RISCVInstrInfoXCV.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td
index f3f3d408c43e7..c54aafeb758fe 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXCV.td
@@ -857,3 +857,225 @@ let Predicates = [HasVendorXCVmac, IsRV32] in {
def : PatCoreVMacGprGprGprUimm5<"macsRN", "MACSRN">;
def : PatCoreVMacGprGprGprUimm5<"machhsRN", "MACHHSRN">;
}
+
+//===----------------------------------------------------------------------===//
+// cv.shuffle.sci.b 8-bit immediate split
+//===----------------------------------------------------------------------===//
+// The hardware encodes the 8-bit shuffle immediate across four opcodes:
+// cv.shufflei{0,1,2,3}.sci.b, where bits [7:6] select the opcode and bits
+// [5:0] are the 6-bit payload. Match the high bits with a predicate and
+// extract the payload with an SDNodeXForm, so each immediate selects the
+// right real instruction directly in TableGen.
+
+def ShuffleSciBPayload : SDNodeXForm<timm, [{
+ return CurDAG->getTargetConstant(N->getZExtValue() & 0x3f, SDLoc(N),
+ MVT::i32);
+}]>;
+def imm_shuffle_sci_b0 : TImmLeaf<i32, [{ return (Imm & 0xc0) == 0x00; }]>;
+def imm_shuffle_sci_b1 : TImmLeaf<i32, [{ return (Imm & 0xc0) == 0x40; }]>;
+def imm_shuffle_sci_b2 : TImmLeaf<i32, [{ return (Imm & 0xc0) == 0x80; }]>;
+def imm_shuffle_sci_b3 : TImmLeaf<i32, [{ return (Imm & 0xc0) == 0xc0; }]>;
+
+//===----------------------------------------------------------------------===//
+// 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] in {
+ // ---- 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: (rs1, rd_in, IMM) -> rd_out
+ def : Pat<(int_riscv_cv_simd_insert_h GPR:$rs1, GPR:$rd, timm:$imm),
+ (CV_INSERT_H GPR:$rd, GPR:$rs1, timm:$imm)>;
+ def : Pat<(int_riscv_cv_simd_insert_b GPR:$rs1, GPR:$rd, 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: high bits of the immediate pick the opcode, the low 6
+ // bits are the payload (see ShuffleSciBPayload above).
+ def : Pat<(int_riscv_cv_simd_shuffle_sci_b GPR:$rs1, imm_shuffle_sci_b0:$imm),
+ (CV_SHUFFLEI0_SCI_B GPR:$rs1, (ShuffleSciBPayload $imm))>;
+ def : Pat<(int_riscv_cv_simd_shuffle_sci_b GPR:$rs1, imm_shuffle_sci_b1:$imm),
+ (CV_SHUFFLEI1_SCI_B GPR:$rs1, (ShuffleSciBPayload $imm))>;
+ def : Pat<(int_riscv_cv_simd_shuffle_sci_b GPR:$rs1, imm_shuffle_sci_b2:$imm),
+ (CV_SHUFFLEI2_SCI_B GPR:$rs1, (ShuffleSciBPayload $imm))>;
+ def : Pat<(int_riscv_cv_simd_shuffle_sci_b GPR:$rs1, imm_shuffle_sci_b3:$imm),
+ (CV_SHUFFLEI3_SCI_B GPR:$rs1, (ShuffleSciBPayload $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: (rs1, rs2, rd_in) -> rd_out
+ def : Pat<(int_riscv_cv_simd_packhi_b GPR:$rs1, GPR:$rs2, GPR:$rd),
+ (CV_PACKHI_B GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+ def : Pat<(int_riscv_cv_simd_packlo_b GPR:$rs1, GPR:$rs2, GPR:$rd),
+ (CV_PACKLO_B GPR:$rd, GPR:$rs1, GPR:$rs2)>;
+
+ // ---- 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]
diff --git a/llvm/test/CodeGen/RISCV/xcvsimd.ll b/llvm/test/CodeGen/RISCV/xcvsimd.ll
new file mode 100644
index 0000000000000..9da977e6f5744
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xcvsimd.ll
@@ -0,0 +1,388 @@
+; 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.
+
+;===----------------------------------------------------------------------===;
+; 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 %rs1, i32 %rd) {
+; CHECK-LABEL: test_cv_simd_insert_h:
+; CHECK: # %bb.0:
+; CHECK-NEXT: cv.insert.h a1, a0, 1
+; CHECK-NEXT: mv a0, a1
+; CHECK-NEXT: ret
+ %res = call i32 @llvm.riscv.cv.simd.insert.h(i32 %rs1, i32 %rd, i32 1)
+ ret i32 %res
+}
+
+define i32 @test_cv_simd_insert_b(i32 %rs1, i32 %rd) {
+; CHECK-LABEL: test_cv_simd_insert_b:
+; CHECK: # %bb.0:
+; CHECK-NEXT: cv.insert.b a1, a0, 2
+; CHECK-NEXT: mv a0, a1
+; CHECK-NEXT: ret
+ %res = call i32 @llvm.riscv.cv.simd.insert.b(i32 %rs1, i32 %rd, 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 %rs1, i32 %rs2, i32 %rd) {
+; CHECK-LABEL: test_cv_simd_packhi_b:
+; CHECK: # %bb.0:
+; CHECK-NEXT: cv.packhi.b a2, a0, a1
+; CHECK-NEXT: mv a0, a2
+; CHECK-NEXT: ret
+ %res = call i32 @llvm.riscv.cv.simd.packhi.b(i32 %rs1, i32 %rs2, i32 %rd)
+ ret i32 %res
+}
+
+define i32 @test_cv_simd_packlo_b(i32 %rs1, i32 %rs2, i32 %rd) {
+; CHECK-LABEL: test_cv_simd_packlo_b:
+; CHECK: # %bb.0:
+; CHECK-NEXT: cv.packlo.b a2, a0, a1
+; CHECK-NEXT: mv a0, a2
+; CHECK-NEXT: ret
+ %res = call i32 @llvm.riscv.cv.simd.packlo.b(i32 %rs1, i32 %rs2, i32 %rd)
+ ret i32 %res
+}
+
+;===----------------------------------------------------------------------===;
+; 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
+}
More information about the llvm-commits
mailing list