[llvm] [clang] [RISCV] Add support for experimental Zimop extension (PR #74824)

Jivan Hakobyan via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 8 03:09:15 PST 2023


https://github.com/JivanH created https://github.com/llvm/llvm-project/pull/74824

This implements experimental support for the Zimop extension as specified here:
https://github.com/riscv/riscv-isa-manual/blob/main/src/zimop.adoc.

This change adds intrinsics of mop.r.[n] and mop.rr.[n] instructions for Zimop extension based on 
https://github.com/riscv-non-isa/riscv-c-api-doc/blob/master/riscv-c-api.md. 


>From 2940a49eed5214668d4235ddaf3c82d076202b94 Mon Sep 17 00:00:00 2001
From: ln8-8 <lyut.nersisyan at gmail.com>
Date: Fri, 8 Dec 2023 12:25:49 +0400
Subject: [PATCH] [RISCV] Add support for experimental Zimop extension

 This implements experimental support for the Zimop extension as specified here: https://github.com/riscv/riscv-isa-manual/blob/main/src/zimop.adoc.

This change adds IR intrinsics of mop.r.[n] and mop.rr.[n] instructions for Zimop extension based on https://github.com/riscv-non-isa/riscv-c-api-doc/blob/master/riscv-c-api.md. Also added assembly support.
---
 clang/include/clang/Basic/BuiltinsRISCV.def   |   5 +
 clang/lib/CodeGen/CGBuiltin.cpp               |  34 ++++
 clang/lib/Sema/SemaChecking.cpp               |   8 +
 .../test/CodeGen/RISCV/rvb-intrinsics/zimop.c | 104 +++++++++++
 llvm/docs/RISCVUsage.rst                      |   3 +
 llvm/include/llvm/IR/IntrinsicsRISCV.td       |  23 +++
 llvm/lib/Support/RISCVISAInfo.cpp             |   2 +
 llvm/lib/Target/RISCV/RISCVFeatures.td        |   5 +
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   | 171 ++++++++++++++++++
 llvm/lib/Target/RISCV/RISCVISelLowering.h     |   6 +
 llvm/lib/Target/RISCV/RISCVInstrFormats.td    |  21 +++
 llvm/lib/Target/RISCV/RISCVInstrInfo.td       |  53 ++++++
 llvm/lib/Target/RISCV/RISCVSchedRocket.td     |   1 +
 llvm/lib/Target/RISCV/RISCVSchedSiFive7.td    |   1 +
 .../Target/RISCV/RISCVSchedSyntacoreSCR1.td   |   1 +
 llvm/lib/Target/RISCV/RISCVSchedule.td        |  14 ++
 llvm/test/CodeGen/RISCV/attributes.ll         |   4 +
 .../test/CodeGen/RISCV/rv32zimop-intrinsic.ll |  47 +++++
 .../test/CodeGen/RISCV/rv64zimop-intrinsic.ll |  96 ++++++++++
 llvm/test/MC/RISCV/rv32zimop-invalid.s        |   6 +
 llvm/test/MC/RISCV/rvzimop-valid.s            |  26 +++
 llvm/unittests/Support/RISCVISAInfoTest.cpp   |   1 +
 22 files changed, 632 insertions(+)
 create mode 100644 clang/test/CodeGen/RISCV/rvb-intrinsics/zimop.c
 create mode 100644 llvm/test/CodeGen/RISCV/rv32zimop-intrinsic.ll
 create mode 100644 llvm/test/CodeGen/RISCV/rv64zimop-intrinsic.ll
 create mode 100644 llvm/test/MC/RISCV/rv32zimop-invalid.s
 create mode 100644 llvm/test/MC/RISCV/rvzimop-valid.s

diff --git a/clang/include/clang/Basic/BuiltinsRISCV.def b/clang/include/clang/Basic/BuiltinsRISCV.def
index 1528b18c82ead..6ba5288f9cbd1 100644
--- a/clang/include/clang/Basic/BuiltinsRISCV.def
+++ b/clang/include/clang/Basic/BuiltinsRISCV.def
@@ -89,5 +89,10 @@ TARGET_BUILTIN(__builtin_riscv_sm3p1, "UiUi", "nc", "zksh")
 TARGET_BUILTIN(__builtin_riscv_ntl_load, "v.", "t", "zihintntl")
 TARGET_BUILTIN(__builtin_riscv_ntl_store, "v.", "t", "zihintntl")
 
+TARGET_BUILTIN(__builtin_riscv_mopr_32, "UiUiUi", "nc", "experimental-zimop")
+TARGET_BUILTIN(__builtin_riscv_mopr_64, "UWiUWiUWi", "nc", "experimental-zimop,64bit")
+TARGET_BUILTIN(__builtin_riscv_moprr_32, "UiUiUiUi", "nc", "experimental-zimop")
+TARGET_BUILTIN(__builtin_riscv_moprr_64, "UWiUWiUWiUWi", "nc", "experimental-zimop,64bit")
+
 #undef BUILTIN
 #undef TARGET_BUILTIN
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 0d8b3e4aaad47..11ba665dda938 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -20808,6 +20808,10 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
   case RISCV::BI__builtin_riscv_clz_64:
   case RISCV::BI__builtin_riscv_ctz_32:
   case RISCV::BI__builtin_riscv_ctz_64:
+  case RISCV::BI__builtin_riscv_mopr_32:
+  case RISCV::BI__builtin_riscv_mopr_64:
+  case RISCV::BI__builtin_riscv_moprr_32:
+  case RISCV::BI__builtin_riscv_moprr_64:
   case RISCV::BI__builtin_riscv_clmul_32:
   case RISCV::BI__builtin_riscv_clmul_64:
   case RISCV::BI__builtin_riscv_clmulh_32:
@@ -20848,6 +20852,36 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
       return Result;
     }
 
+    // Zimop
+    case RISCV::BI__builtin_riscv_mopr_32:
+    case RISCV::BI__builtin_riscv_mopr_64: {
+      unsigned N = cast<ConstantInt>(Ops[1])->getZExtValue();
+      Function *F = nullptr;
+      if (N <= 1) {
+        F = CGM.getIntrinsic(Intrinsic::riscv_mopr0 + N, {ResultType});
+      } else if (N >= 10 && N <= 19) {
+        F = CGM.getIntrinsic(Intrinsic::riscv_mopr10 + N - 10, {ResultType});
+      } else if (N == 2) {
+        F = CGM.getIntrinsic(Intrinsic::riscv_mopr2, {ResultType});
+      } else if (N >= 20 && N <= 29) {
+        F = CGM.getIntrinsic(Intrinsic::riscv_mopr20 + N - 20, {ResultType});
+      } else if (N == 3) {
+        F = CGM.getIntrinsic(Intrinsic::riscv_mopr3, {ResultType});
+      } else if (N >= 30 && N <= 31) {
+        F = CGM.getIntrinsic(Intrinsic::riscv_mopr30 + N - 30, {ResultType});
+      } else if (N >= 4 && N <= 9) {
+        F = CGM.getIntrinsic(Intrinsic::riscv_mopr4 + N - 4, {ResultType});
+      } else {
+        llvm_unreachable("unexpected builtin ID");
+      }
+      return Builder.CreateCall(F, {Ops[0]}, "");
+    }
+    case RISCV::BI__builtin_riscv_moprr_32:
+    case RISCV::BI__builtin_riscv_moprr_64: {
+      unsigned N = cast<ConstantInt>(Ops[2])->getZExtValue();
+      Function *F = CGM.getIntrinsic(Intrinsic::riscv_moprr0 + N, {ResultType});
+      return Builder.CreateCall(F, {Ops[0], Ops[1]}, "");
+    }
     // Zbc
     case RISCV::BI__builtin_riscv_clmul_32:
     case RISCV::BI__builtin_riscv_clmul_64:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index fc6ee6b2c5ab4..80ca886bda6ec 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5468,6 +5468,14 @@ bool Sema::CheckRISCVBuiltinFunctionCall(const TargetInfo &TI,
   // Check if rnum is in [0, 10]
   case RISCV::BI__builtin_riscv_aes64ks1i:
     return SemaBuiltinConstantArgRange(TheCall, 1, 0, 10);
+  // Check if n of mop.r.[n] is in [0, 31]
+  case RISCV::BI__builtin_riscv_mopr_32:
+  case RISCV::BI__builtin_riscv_mopr_64:
+    return SemaBuiltinConstantArgRange(TheCall, 1, 0, 31);
+  // Check if n of mop.rr.[n] is in [0, 7]
+  case RISCV::BI__builtin_riscv_moprr_32:
+  case RISCV::BI__builtin_riscv_moprr_64:
+    return SemaBuiltinConstantArgRange(TheCall, 2, 0, 7);
   // Check if value range for vxrm is in [0, 3]
   case RISCVVector::BI__builtin_rvv_vaaddu_vv:
   case RISCVVector::BI__builtin_rvv_vaaddu_vx:
diff --git a/clang/test/CodeGen/RISCV/rvb-intrinsics/zimop.c b/clang/test/CodeGen/RISCV/rvb-intrinsics/zimop.c
new file mode 100644
index 0000000000000..790c746f84606
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/rvb-intrinsics/zimop.c
@@ -0,0 +1,104 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -triple riscv32 -target-feature +experimental-zimop -emit-llvm %s -o - \
+// RUN:     -disable-O0-optnone | opt -S -passes=mem2reg \
+// RUN:     | FileCheck %s  -check-prefix=RV32ZIMOP
+// RUN: %clang_cc1 -triple riscv64 -target-feature +experimental-zimop -emit-llvm %s -o - \
+// RUN:     -disable-O0-optnone | opt -S -passes=mem2reg \
+// RUN:     | FileCheck %s  -check-prefix=RV64ZIMOP
+
+#include <stdint.h>
+
+#if __riscv_xlen == 64
+// RV64ZIMOP-LABEL: @mopr_0_64(
+// RV64ZIMOP-NEXT:  entry:
+// RV64ZIMOP-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.mopr0.i64(i64 [[A:%.*]])
+// RV64ZIMOP-NEXT:    ret i64 [[TMP0]]
+//
+uint64_t mopr_0_64(uint64_t a) {
+  return __builtin_riscv_mopr_64(a, 0);
+}
+
+// RV64ZIMOP-LABEL: @mopr_31_64(
+// RV64ZIMOP-NEXT:  entry:
+// RV64ZIMOP-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.mopr31.i64(i64 [[A:%.*]])
+// RV64ZIMOP-NEXT:    ret i64 [[TMP0]]
+//
+uint64_t mopr_31_64(uint64_t a) {
+  return __builtin_riscv_mopr_64(a, 31);
+}
+
+// RV64ZIMOP-LABEL: @moprr_0_64(
+// RV64ZIMOP-NEXT:  entry:
+// RV64ZIMOP-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.moprr0.i64(i64 [[A:%.*]], i64 [[B:%.*]])
+// RV64ZIMOP-NEXT:    ret i64 [[TMP0]]
+//
+uint64_t moprr_0_64(uint64_t a, uint64_t b) {
+  return __builtin_riscv_moprr_64(a, b, 0);
+}
+
+// RV64ZIMOP-LABEL: @moprr_7_64(
+// RV64ZIMOP-NEXT:  entry:
+// RV64ZIMOP-NEXT:    [[TMP0:%.*]] = call i64 @llvm.riscv.moprr7.i64(i64 [[A:%.*]], i64 [[B:%.*]])
+// RV64ZIMOP-NEXT:    ret i64 [[TMP0]]
+//
+uint64_t moprr_7_64(uint64_t a, uint64_t b) {
+  return __builtin_riscv_moprr_64(a, b, 7);
+}
+
+#endif
+
+// RV32ZIMOP-LABEL: @mopr_0_32(
+// RV32ZIMOP-NEXT:  entry:
+// RV32ZIMOP-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.mopr0.i32(i32 [[A:%.*]])
+// RV32ZIMOP-NEXT:    ret i32 [[TMP0]]
+//
+// RV64ZIMOP-LABEL: @mopr_0_32(
+// RV64ZIMOP-NEXT:  entry:
+// RV64ZIMOP-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.mopr0.i32(i32 [[A:%.*]])
+// RV64ZIMOP-NEXT:    ret i32 [[TMP0]]
+//
+uint32_t mopr_0_32(uint32_t a) {
+  return __builtin_riscv_mopr_32(a, 0);
+}
+
+// RV32ZIMOP-LABEL: @mopr_31_32(
+// RV32ZIMOP-NEXT:  entry:
+// RV32ZIMOP-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.mopr31.i32(i32 [[A:%.*]])
+// RV32ZIMOP-NEXT:    ret i32 [[TMP0]]
+//
+// RV64ZIMOP-LABEL: @mopr_31_32(
+// RV64ZIMOP-NEXT:  entry:
+// RV64ZIMOP-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.mopr31.i32(i32 [[A:%.*]])
+// RV64ZIMOP-NEXT:    ret i32 [[TMP0]]
+//
+uint32_t mopr_31_32(uint32_t a) {
+  return __builtin_riscv_mopr_32(a, 31);
+}
+
+// RV32ZIMOP-LABEL: @moprr_0_32(
+// RV32ZIMOP-NEXT:  entry:
+// RV32ZIMOP-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.moprr0.i32(i32 [[A:%.*]], i32 [[B:%.*]])
+// RV32ZIMOP-NEXT:    ret i32 [[TMP0]]
+//
+// RV64ZIMOP-LABEL: @moprr_0_32(
+// RV64ZIMOP-NEXT:  entry:
+// RV64ZIMOP-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.moprr0.i32(i32 [[A:%.*]], i32 [[B:%.*]])
+// RV64ZIMOP-NEXT:    ret i32 [[TMP0]]
+//
+uint32_t moprr_0_32(uint32_t a, uint32_t b) {
+  return __builtin_riscv_moprr_32(a, b, 0);
+}
+
+// RV32ZIMOP-LABEL: @moprr_7_32(
+// RV32ZIMOP-NEXT:  entry:
+// RV32ZIMOP-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.moprr7.i32(i32 [[A:%.*]], i32 [[B:%.*]])
+// RV32ZIMOP-NEXT:    ret i32 [[TMP0]]
+//
+// RV64ZIMOP-LABEL: @moprr_7_32(
+// RV64ZIMOP-NEXT:  entry:
+// RV64ZIMOP-NEXT:    [[TMP0:%.*]] = call i32 @llvm.riscv.moprr7.i32(i32 [[A:%.*]], i32 [[B:%.*]])
+// RV64ZIMOP-NEXT:    ret i32 [[TMP0]]
+//
+uint32_t moprr_7_32(uint32_t a, uint32_t b) {
+  return __builtin_riscv_moprr_32(a, b, 7);
+}
\ No newline at end of file
diff --git a/llvm/docs/RISCVUsage.rst b/llvm/docs/RISCVUsage.rst
index 65dd0d83448ed..bd2f81fba186d 100644
--- a/llvm/docs/RISCVUsage.rst
+++ b/llvm/docs/RISCVUsage.rst
@@ -208,6 +208,9 @@ The primary goal of experimental support is to assist in the process of ratifica
 ``experimental-zvbb``, ``experimental-zvbc``, ``experimental-zvkb``, ``experimental-zvkg``, ``experimental-zvkn``, ``experimental-zvknc``, ``experimental-zvkned``, ``experimental-zvkng``, ``experimental-zvknha``, ``experimental-zvknhb``, ``experimental-zvks``, ``experimental-zvksc``, ``experimental-zvksed``, ``experimental-zvksg``, ``experimental-zvksh``, ``experimental-zvkt``
   LLVM implements the `1.0.0-rc2 specification <https://github.com/riscv/riscv-crypto/releases/download/v/riscv-crypto-spec-vector.pdf>`__. Note that current vector crypto extension version can be found in: <https://github.com/riscv/riscv-crypto>.
 
+``experimental-zimop``
+  LLVM implements the `v0.1 proposed specification <https://github.com/riscv/riscv-isa-manual/blob/main/src/zimop.adoc>`__.
+
 To use an experimental extension from `clang`, you must add `-menable-experimental-extensions` to the command line, and specify the exact version of the experimental extension you are using.  To use an experimental extension with LLVM's internal developer tools (e.g. `llc`, `llvm-objdump`, `llvm-mc`), you must prefix the extension name with `experimental-`.  Note that you don't need to specify the version with internal tools, and shouldn't include the `experimental-` prefix with `clang`.
 
 Vendor Extensions
diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td
index 20c6a525a86ba..fcb11c8c51398 100644
--- a/llvm/include/llvm/IR/IntrinsicsRISCV.td
+++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td
@@ -108,6 +108,29 @@ let TargetPrefix = "riscv" in {
   def int_riscv_xperm8  : BitManipGPRGPRIntrinsics;
 } // TargetPrefix = "riscv"
 
+//===----------------------------------------------------------------------===//
+// May-Be-Operations
+
+let TargetPrefix = "riscv" in {
+
+  class MOPGPRIntrinsics
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [LLVMMatchType<0>],
+                              [IntrNoMem, IntrSpeculatable]>;
+  class MOPGPRGPRIntrinsics
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [LLVMMatchType<0>, LLVMMatchType<0>],
+                              [IntrNoMem, IntrSpeculatable]>;
+
+  // Zimop
+   foreach i = 0...31 in {
+    def int_riscv_mopr#i : MOPGPRIntrinsics;
+   }
+  foreach i = 0...7 in {
+    def int_riscv_moprr#i : MOPGPRGPRIntrinsics;
+  }
+} // TargetPrefix = "riscv"
+
 //===----------------------------------------------------------------------===//
 // Vectors
 
diff --git a/llvm/lib/Support/RISCVISAInfo.cpp b/llvm/lib/Support/RISCVISAInfo.cpp
index 6322748430063..1b303ba1e9431 100644
--- a/llvm/lib/Support/RISCVISAInfo.cpp
+++ b/llvm/lib/Support/RISCVISAInfo.cpp
@@ -177,6 +177,8 @@ static const RISCVSupportedExtension SupportedExperimentalExtensions[] = {
     {"zicfilp", RISCVExtensionVersion{0, 2}},
     {"zicond", RISCVExtensionVersion{1, 0}},
 
+    {"zimop", RISCVExtensionVersion{0, 1}},
+
     {"ztso", RISCVExtensionVersion{0, 1}},
 
     {"zvbb", RISCVExtensionVersion{1, 0}},
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index 7d142d38d0f9a..eddb7c33627f0 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -687,6 +687,11 @@ def HasStdExtZicond : Predicate<"Subtarget->hasStdExtZicond()">,
                                 AssemblerPredicate<(all_of FeatureStdExtZicond),
                                 "'Zicond' (Integer Conditional Operations)">;
 
+def FeatureStdExtZimop : SubtargetFeature<"experimental-zimop", "HasStdExtZimop", "true",
+                                          "'Zimop' (May-Be-Operations)">;
+def HasStdExtZimop : Predicate<"Subtarget->hasStdExtZimop()">,
+                               AssemblerPredicate<(all_of FeatureStdExtZimop),
+                               "'Zimop' (May-Be-Operations)">;
 def FeatureStdExtSmaia
     : SubtargetFeature<"smaia", "HasStdExtSmaia", "true",
                        "'Smaia' (Smaia encompasses all added CSRs and all "
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index f2ec422b54a92..45fbea2088559 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -8367,6 +8367,73 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
         IntNo == Intrinsic::riscv_zip ? RISCVISD::ZIP : RISCVISD::UNZIP;
     return DAG.getNode(Opc, DL, XLenVT, Op.getOperand(1));
   }
+#define RISCV_MOPR_64_CASE(NAME, OPCODE)                                       \
+  case Intrinsic::riscv_##NAME: {                                              \
+    if (RV64LegalI32 && Subtarget.is64Bit() &&                                 \
+        Op.getValueType() == MVT::i32) {                                       \
+      SDValue NewOp =                                                          \
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op.getOperand(1));        \
+      SDValue Res = DAG.getNode(OPCODE, DL, MVT::i64, NewOp);                  \
+      return DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res);                    \
+    }                                                                          \
+    return DAG.getNode(OPCODE, DL, XLenVT, Op.getOperand(1));                  \
+  }
+    RISCV_MOPR_64_CASE(mopr0, RISCVISD::MOPR0)
+    RISCV_MOPR_64_CASE(mopr1, RISCVISD::MOPR1)
+    RISCV_MOPR_64_CASE(mopr2, RISCVISD::MOPR2)
+    RISCV_MOPR_64_CASE(mopr3, RISCVISD::MOPR3)
+    RISCV_MOPR_64_CASE(mopr4, RISCVISD::MOPR4)
+    RISCV_MOPR_64_CASE(mopr5, RISCVISD::MOPR5)
+    RISCV_MOPR_64_CASE(mopr6, RISCVISD::MOPR6)
+    RISCV_MOPR_64_CASE(mopr7, RISCVISD::MOPR7)
+    RISCV_MOPR_64_CASE(mopr8, RISCVISD::MOPR8)
+    RISCV_MOPR_64_CASE(mopr9, RISCVISD::MOPR9)
+    RISCV_MOPR_64_CASE(mopr10, RISCVISD::MOPR10)
+    RISCV_MOPR_64_CASE(mopr11, RISCVISD::MOPR11)
+    RISCV_MOPR_64_CASE(mopr12, RISCVISD::MOPR12)
+    RISCV_MOPR_64_CASE(mopr13, RISCVISD::MOPR13)
+    RISCV_MOPR_64_CASE(mopr14, RISCVISD::MOPR14)
+    RISCV_MOPR_64_CASE(mopr15, RISCVISD::MOPR15)
+    RISCV_MOPR_64_CASE(mopr16, RISCVISD::MOPR16)
+    RISCV_MOPR_64_CASE(mopr17, RISCVISD::MOPR17)
+    RISCV_MOPR_64_CASE(mopr18, RISCVISD::MOPR18)
+    RISCV_MOPR_64_CASE(mopr19, RISCVISD::MOPR19)
+    RISCV_MOPR_64_CASE(mopr20, RISCVISD::MOPR20)
+    RISCV_MOPR_64_CASE(mopr21, RISCVISD::MOPR21)
+    RISCV_MOPR_64_CASE(mopr22, RISCVISD::MOPR22)
+    RISCV_MOPR_64_CASE(mopr23, RISCVISD::MOPR23)
+    RISCV_MOPR_64_CASE(mopr24, RISCVISD::MOPR24)
+    RISCV_MOPR_64_CASE(mopr25, RISCVISD::MOPR25)
+    RISCV_MOPR_64_CASE(mopr26, RISCVISD::MOPR26)
+    RISCV_MOPR_64_CASE(mopr27, RISCVISD::MOPR27)
+    RISCV_MOPR_64_CASE(mopr28, RISCVISD::MOPR28)
+    RISCV_MOPR_64_CASE(mopr29, RISCVISD::MOPR29)
+    RISCV_MOPR_64_CASE(mopr30, RISCVISD::MOPR30)
+    RISCV_MOPR_64_CASE(mopr31, RISCVISD::MOPR31)
+#undef RISCV_MOPR_64_CASE
+#define RISCV_MOPRR_64_CASE(NAME, OPCODE)                                      \
+  case Intrinsic::riscv_##NAME: {                                              \
+    if (RV64LegalI32 && Subtarget.is64Bit() &&                                 \
+        Op.getValueType() == MVT::i32) {                                       \
+      SDValue NewOp0 =                                                         \
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op.getOperand(1));        \
+      SDValue NewOp1 =                                                         \
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op.getOperand(2));        \
+      SDValue Res = DAG.getNode(OPCODE, DL, MVT::i64, NewOp0, NewOp1);         \
+      return DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res);                    \
+    }                                                                          \
+    return DAG.getNode(OPCODE, DL, XLenVT, Op.getOperand(1),                   \
+                       Op.getOperand(2));                                      \
+  }
+    RISCV_MOPRR_64_CASE(moprr0, RISCVISD::MOPRR0)
+    RISCV_MOPRR_64_CASE(moprr1, RISCVISD::MOPRR1)
+    RISCV_MOPRR_64_CASE(moprr2, RISCVISD::MOPRR2)
+    RISCV_MOPRR_64_CASE(moprr3, RISCVISD::MOPRR3)
+    RISCV_MOPRR_64_CASE(moprr4, RISCVISD::MOPRR4)
+    RISCV_MOPRR_64_CASE(moprr5, RISCVISD::MOPRR5)
+    RISCV_MOPRR_64_CASE(moprr6, RISCVISD::MOPRR6)
+    RISCV_MOPRR_64_CASE(moprr7, RISCVISD::MOPRR7)
+#undef RISCV_MOPRR_64_CASE
   case Intrinsic::riscv_clmul:
     if (RV64LegalI32 && Subtarget.is64Bit() && Op.getValueType() == MVT::i32) {
       SDValue NewOp0 =
@@ -11633,6 +11700,70 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
       Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));
       return;
     }
+#define RISCV_MOPR_CASE(NAME, OPCODE)                                          \
+  case Intrinsic::riscv_##NAME: {                                              \
+    if (!Subtarget.is64Bit() || N->getValueType(0) != MVT::i32)                \
+      return;                                                                  \
+    SDValue NewOp =                                                            \
+        DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(1));          \
+    SDValue Res = DAG.getNode(OPCODE, DL, MVT::i64, NewOp);                    \
+    Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));          \
+    return;                                                                    \
+  }
+      RISCV_MOPR_CASE(mopr0, RISCVISD::MOPR0)
+      RISCV_MOPR_CASE(mopr1, RISCVISD::MOPR1)
+      RISCV_MOPR_CASE(mopr2, RISCVISD::MOPR2)
+      RISCV_MOPR_CASE(mopr3, RISCVISD::MOPR3)
+      RISCV_MOPR_CASE(mopr4, RISCVISD::MOPR4)
+      RISCV_MOPR_CASE(mopr5, RISCVISD::MOPR5)
+      RISCV_MOPR_CASE(mopr6, RISCVISD::MOPR6)
+      RISCV_MOPR_CASE(mopr7, RISCVISD::MOPR7)
+      RISCV_MOPR_CASE(mopr8, RISCVISD::MOPR8)
+      RISCV_MOPR_CASE(mopr9, RISCVISD::MOPR9)
+      RISCV_MOPR_CASE(mopr10, RISCVISD::MOPR10)
+      RISCV_MOPR_CASE(mopr11, RISCVISD::MOPR11)
+      RISCV_MOPR_CASE(mopr12, RISCVISD::MOPR12)
+      RISCV_MOPR_CASE(mopr13, RISCVISD::MOPR13)
+      RISCV_MOPR_CASE(mopr14, RISCVISD::MOPR14)
+      RISCV_MOPR_CASE(mopr15, RISCVISD::MOPR15)
+      RISCV_MOPR_CASE(mopr16, RISCVISD::MOPR16)
+      RISCV_MOPR_CASE(mopr17, RISCVISD::MOPR17)
+      RISCV_MOPR_CASE(mopr18, RISCVISD::MOPR18)
+      RISCV_MOPR_CASE(mopr19, RISCVISD::MOPR19)
+      RISCV_MOPR_CASE(mopr20, RISCVISD::MOPR20)
+      RISCV_MOPR_CASE(mopr21, RISCVISD::MOPR21)
+      RISCV_MOPR_CASE(mopr22, RISCVISD::MOPR22)
+      RISCV_MOPR_CASE(mopr23, RISCVISD::MOPR23)
+      RISCV_MOPR_CASE(mopr24, RISCVISD::MOPR24)
+      RISCV_MOPR_CASE(mopr25, RISCVISD::MOPR25)
+      RISCV_MOPR_CASE(mopr26, RISCVISD::MOPR26)
+      RISCV_MOPR_CASE(mopr27, RISCVISD::MOPR27)
+      RISCV_MOPR_CASE(mopr28, RISCVISD::MOPR28)
+      RISCV_MOPR_CASE(mopr29, RISCVISD::MOPR29)
+      RISCV_MOPR_CASE(mopr30, RISCVISD::MOPR30)
+      RISCV_MOPR_CASE(mopr31, RISCVISD::MOPR31)
+#undef RISCV_MOPR_CASE
+#define RISCV_MOPRR_CASE(NAME, OPCODE)                                         \
+  case Intrinsic::riscv_##NAME: {                                              \
+    if (!Subtarget.is64Bit() || N->getValueType(0) != MVT::i32)                \
+      return;                                                                  \
+    SDValue NewOp0 =                                                           \
+        DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(1));          \
+    SDValue NewOp1 =                                                           \
+        DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(2));          \
+    SDValue Res = DAG.getNode(OPCODE, DL, MVT::i64, NewOp0, NewOp1);           \
+    Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));          \
+    return;                                                                    \
+  }
+      RISCV_MOPRR_CASE(moprr0, RISCVISD::MOPRR0)
+      RISCV_MOPRR_CASE(moprr1, RISCVISD::MOPRR1)
+      RISCV_MOPRR_CASE(moprr2, RISCVISD::MOPRR2)
+      RISCV_MOPRR_CASE(moprr3, RISCVISD::MOPRR3)
+      RISCV_MOPRR_CASE(moprr4, RISCVISD::MOPRR4)
+      RISCV_MOPRR_CASE(moprr5, RISCVISD::MOPRR5)
+      RISCV_MOPRR_CASE(moprr6, RISCVISD::MOPRR6)
+      RISCV_MOPRR_CASE(moprr7, RISCVISD::MOPRR7)
+#undef RISCV_MOPRR_CASE
     case Intrinsic::riscv_clmul: {
       if (!Subtarget.is64Bit() || N->getValueType(0) != MVT::i32)
         return;
@@ -18371,6 +18502,46 @@ const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const {
   NODE_NAME_CASE(CLMUL)
   NODE_NAME_CASE(CLMULH)
   NODE_NAME_CASE(CLMULR)
+  NODE_NAME_CASE(MOPR0)
+  NODE_NAME_CASE(MOPR1)
+  NODE_NAME_CASE(MOPR2)
+  NODE_NAME_CASE(MOPR3)
+  NODE_NAME_CASE(MOPR4)
+  NODE_NAME_CASE(MOPR5)
+  NODE_NAME_CASE(MOPR6)
+  NODE_NAME_CASE(MOPR7)
+  NODE_NAME_CASE(MOPR8)
+  NODE_NAME_CASE(MOPR9)
+  NODE_NAME_CASE(MOPR10)
+  NODE_NAME_CASE(MOPR11)
+  NODE_NAME_CASE(MOPR12)
+  NODE_NAME_CASE(MOPR13)
+  NODE_NAME_CASE(MOPR14)
+  NODE_NAME_CASE(MOPR15)
+  NODE_NAME_CASE(MOPR16)
+  NODE_NAME_CASE(MOPR17)
+  NODE_NAME_CASE(MOPR18)
+  NODE_NAME_CASE(MOPR19)
+  NODE_NAME_CASE(MOPR20)
+  NODE_NAME_CASE(MOPR21)
+  NODE_NAME_CASE(MOPR22)
+  NODE_NAME_CASE(MOPR23)
+  NODE_NAME_CASE(MOPR24)
+  NODE_NAME_CASE(MOPR25)
+  NODE_NAME_CASE(MOPR26)
+  NODE_NAME_CASE(MOPR27)
+  NODE_NAME_CASE(MOPR28)
+  NODE_NAME_CASE(MOPR29)
+  NODE_NAME_CASE(MOPR30)
+  NODE_NAME_CASE(MOPR31)
+  NODE_NAME_CASE(MOPRR0)
+  NODE_NAME_CASE(MOPRR1)
+  NODE_NAME_CASE(MOPRR2)
+  NODE_NAME_CASE(MOPRR3)
+  NODE_NAME_CASE(MOPRR4)
+  NODE_NAME_CASE(MOPRR5)
+  NODE_NAME_CASE(MOPRR6)
+  NODE_NAME_CASE(MOPRR7)
   NODE_NAME_CASE(SHA256SIG0)
   NODE_NAME_CASE(SHA256SIG1)
   NODE_NAME_CASE(SHA256SUM0)
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index ae798cc47bf83..5dd66f402a324 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -141,6 +141,12 @@ enum NodeType : unsigned {
   SHA256SIG0, SHA256SIG1, SHA256SUM0, SHA256SUM1,
   SM4KS, SM4ED,
   SM3P0, SM3P1,
+  // May-Be-Operations
+  MOPR0, MOPR1, MOPR2, MOPR3, MOPR4, MOPR5, MOPR6, MOPR7, MOPR8, MOPR9, MOPR10,
+  MOPR11, MOPR12, MOPR13, MOPR14, MOPR15, MOPR16, MOPR17, MOPR18, MOPR19,
+  MOPR20, MOPR21, MOPR22, MOPR23, MOPR24, MOPR25, MOPR26, MOPR27, MOPR28,
+  MOPR29, MOPR30, MOPR31, MOPRR0, MOPRR1, MOPRR2, MOPRR3, MOPRR4, MOPRR5,
+  MOPRR6, MOPRR7,
 
   // Vector Extension
   FIRST_VL_VECTOR_OP,
diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
index e80ba26800a13..cb68752210d54 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
@@ -401,6 +401,27 @@ class RVInstIUnary<bits<12> imm12, bits<3> funct3, RISCVOpcode opcode,
   let Inst{31-20} = imm12;
 }
 
+class RVInstIMopr<bits<7> imm7, bits<5> imm5, bits<3> funct3, RISCVOpcode opcode,
+                   dag outs, dag ins, string opcodestr, string argstr>
+    : RVInstIBase<funct3, opcode, outs, ins, opcodestr, argstr> {
+  let Inst{31} = imm7{6};
+  let Inst{30} = imm5{4};
+  let Inst{29-28} = imm7{5-4};
+  let Inst{27-26} = imm5{3-2};
+  let Inst{25-22} = imm7{3-0};
+  let Inst{21-20} = imm5{1-0};
+}
+
+class RVInstRMoprr<bits<4> imm4, bits<3> imm3, bits<3> funct3, RISCVOpcode opcode,
+                   dag outs, dag ins, string opcodestr, string argstr>
+    : RVInstRBase<funct3, opcode, outs, ins, opcodestr, argstr> {
+  let Inst{31} = imm4{3};
+  let Inst{30} = imm3{2};
+  let Inst{29-28} = imm4{2-1};
+  let Inst{27-26} = imm3{1-0};
+  let Inst{25} = imm4{0};
+}
+
 class RVInstS<bits<3> funct3, RISCVOpcode opcode, dag outs, dag ins,
               string opcodestr, string argstr>
     : RVInst<outs, ins, opcodestr, argstr, [], InstFormatS> {
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index edc08187d8f77..6e82fea0339d4 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -90,6 +90,14 @@ def riscv_add_tprel : SDNode<"RISCVISD::ADD_TPREL",
                                                   SDTCisSameAs<0, 3>,
                                                   SDTCisInt<0>]>>;
 
+foreach i = 0...31 in {
+  defvar riscvisd_moprx = "RISCVISD::MOPR"#i;
+  def riscv_mopr#i : SDNode<riscvisd_moprx,  SDTIntUnaryOp>;
+}
+foreach i = 0...7 in {
+  defvar riscvisd_moprrx = "RISCVISD::MOPRR"#i;
+  def riscv_moprr#i : SDNode<riscvisd_moprrx,  SDTIntBinOp>;
+}
 //===----------------------------------------------------------------------===//
 // Operand and SDNode transformation definitions.
 //===----------------------------------------------------------------------===//
@@ -597,6 +605,16 @@ class Priv_rr<string opcodestr, bits<7> funct7>
   let rd = 0;
 }
 
+class RVMopr<bits<7> imm7, bits<5> imm5, bits<3> funct3,
+             RISCVOpcode opcode, string opcodestr>
+    : RVInstIMopr<imm7, imm5, funct3, opcode, (outs GPR:$rd), (ins GPR:$rs1),
+                   opcodestr, "$rd, $rs1">;
+
+class RVMoprr<bits<4> imm4, bits<3> imm3, bits<3> funct3,
+             RISCVOpcode opcode, string opcodestr>
+    : RVInstRMoprr<imm4, imm3, funct3, opcode, (outs GPR:$rd), (ins GPR:$rs1, GPR:$rs2),
+                   opcodestr, "$rd, $rs1, $rs2">;
+
 //===----------------------------------------------------------------------===//
 // Instructions
 //===----------------------------------------------------------------------===//
@@ -786,6 +804,22 @@ def SRAW  : ALUW_rr<0b0100000, 0b101, "sraw">,
 } // IsSignExtendingOpW = 1
 } // Predicates = [IsRV64]
 
+// Zimop instructions
+
+foreach i = 0...31 in {
+    let Predicates = [HasStdExtZimop] in {
+    def MOPR#i : RVMopr<0b1000111, i, 0b100, OPC_SYSTEM, "mop.r."#i>,
+                Sched<[WriteMOPR, ReadMOPR]>;
+    } // Predicates = [HasStdExtZimop]
+}
+
+foreach i = 0...7 in {
+    let Predicates = [HasStdExtZimop] in {
+    def MOPRR#i : RVMoprr<0b1001, i, 0b100, OPC_SYSTEM, "mop.rr."#i>,
+                Sched<[WriteMOPR, ReadMOPR, ReadMOPR]>;
+    } // Predicates = [HasStdExtZimop]
+}
+
 //===----------------------------------------------------------------------===//
 // Privileged instructions
 //===----------------------------------------------------------------------===//
@@ -1551,6 +1585,25 @@ let Predicates = [HasStdExtC, OptForMinSize] in {
   def : SelectCompressOpt<SETNE>;
 }
 
+// Zimop instructions
+foreach i = 0...31 in {
+    defvar moprx = !cast<Instruction>("MOPR"#i);
+    defvar riscv_moprx = !cast<SDNode>("riscv_mopr"#i);
+    let Predicates = [HasStdExtZimop] in {
+    def : Pat<(XLenVT (riscv_moprx (XLenVT GPR:$rs1))),
+              (moprx GPR:$rs1)>;
+    } // Predicates = [HasStdExtZimop]
+}
+
+foreach i = 0...7 in {
+    defvar moprrx = !cast<Instruction>("MOPRR"#i);
+    defvar riscv_moprrx = !cast<SDNode>("riscv_moprr"#i);
+    let Predicates = [HasStdExtZimop] in {
+    def : Pat<(XLenVT (riscv_moprrx (XLenVT GPR:$rs1), (XLenVT GPR:$rs2))),
+              (moprrx GPR:$rs1, GPR:$rs2)>;
+    } // Predicates = [HasStdExtZimop]
+}
+
 /// Branches and jumps
 
 // Match `riscv_brcc` and lower to the appropriate RISC-V branch instruction.
diff --git a/llvm/lib/Target/RISCV/RISCVSchedRocket.td b/llvm/lib/Target/RISCV/RISCVSchedRocket.td
index bb9dfe5d01240..d2fe83606eab6 100644
--- a/llvm/lib/Target/RISCV/RISCVSchedRocket.td
+++ b/llvm/lib/Target/RISCV/RISCVSchedRocket.td
@@ -248,4 +248,5 @@ defm : UnsupportedSchedZbkx;
 defm : UnsupportedSchedZfa;
 defm : UnsupportedSchedZfh;
 defm : UnsupportedSchedSFB;
+defm : UnsupportedSchedZimop;
 }
diff --git a/llvm/lib/Target/RISCV/RISCVSchedSiFive7.td b/llvm/lib/Target/RISCV/RISCVSchedSiFive7.td
index 45783d482f3bd..082710a7b1484 100644
--- a/llvm/lib/Target/RISCV/RISCVSchedSiFive7.td
+++ b/llvm/lib/Target/RISCV/RISCVSchedSiFive7.td
@@ -1212,4 +1212,5 @@ defm : UnsupportedSchedZbc;
 defm : UnsupportedSchedZbkb;
 defm : UnsupportedSchedZbkx;
 defm : UnsupportedSchedZfa;
+defm : UnsupportedSchedZimop;
 }
diff --git a/llvm/lib/Target/RISCV/RISCVSchedSyntacoreSCR1.td b/llvm/lib/Target/RISCV/RISCVSchedSyntacoreSCR1.td
index 06ad2075b0736..858f1ca89e151 100644
--- a/llvm/lib/Target/RISCV/RISCVSchedSyntacoreSCR1.td
+++ b/llvm/lib/Target/RISCV/RISCVSchedSyntacoreSCR1.td
@@ -207,4 +207,5 @@ defm : UnsupportedSchedZbkb;
 defm : UnsupportedSchedZbkx;
 defm : UnsupportedSchedZfa;
 defm : UnsupportedSchedZfh;
+defm : UnsupportedSchedZimop;
 }
diff --git a/llvm/lib/Target/RISCV/RISCVSchedule.td b/llvm/lib/Target/RISCV/RISCVSchedule.td
index f6c1b096ad90c..8bf3481b0d007 100644
--- a/llvm/lib/Target/RISCV/RISCVSchedule.td
+++ b/llvm/lib/Target/RISCV/RISCVSchedule.td
@@ -112,6 +112,9 @@ def WriteFST16        : SchedWrite;    // Floating point sp store
 def WriteFST32        : SchedWrite;    // Floating point sp store
 def WriteFST64        : SchedWrite;    // Floating point dp store
 
+// Zimop extension.
+def WriteMOPR     : SchedWrite;
+
 // short forward branch for Bullet
 def WriteSFB        : SchedWrite;
 def ReadSFBJmp      : SchedRead;
@@ -201,6 +204,9 @@ def ReadFClass16         : SchedRead;
 def ReadFClass32         : SchedRead;
 def ReadFClass64         : SchedRead;
 
+// Zimop extension.
+def ReadMOPR     : SchedRead;
+
 multiclass UnsupportedSchedZfh {
 let Unsupported = true in {
 def : WriteRes<WriteFAdd16, []>;
@@ -271,6 +277,14 @@ def : ReadAdvance<ReadFRoundF16, 0>;
 } // Unsupported = true
 }
 
+multiclass UnsupportedSchedZimop {
+let Unsupported = true in {
+def : WriteRes<WriteMOPR, []>;
+
+def : ReadAdvance<ReadMOPR, 0>;
+}
+}
+
 // Include the scheduler resources for other instruction extensions.
 include "RISCVScheduleZb.td"
 include "RISCVScheduleV.td"
diff --git a/llvm/test/CodeGen/RISCV/attributes.ll b/llvm/test/CodeGen/RISCV/attributes.ll
index 030ae06af6d28..0afe9b9f916d8 100644
--- a/llvm/test/CodeGen/RISCV/attributes.ll
+++ b/llvm/test/CodeGen/RISCV/attributes.ll
@@ -83,6 +83,7 @@
 ; RUN: llc -mtriple=riscv32 -mattr=+zve32x -mattr=+experimental-zvksh %s -o - | FileCheck --check-prefix=RV32ZVKSH %s
 ; RUN: llc -mtriple=riscv32 -mattr=+zve32x -mattr=+experimental-zvkt %s -o - | FileCheck --check-prefix=RV32ZVKT %s
 ; RUN: llc -mtriple=riscv32 -mattr=+experimental-zicond %s -o - | FileCheck --check-prefix=RV32ZICOND %s
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zimop %s -o - | FileCheck --check-prefix=RV32ZIMOP %s
 ; RUN: llc -mtriple=riscv32 -mattr=+smaia %s -o - | FileCheck --check-prefixes=CHECK,RV32SMAIA %s
 ; RUN: llc -mtriple=riscv32 -mattr=+ssaia %s -o - | FileCheck --check-prefixes=CHECK,RV32SSAIA %s
 ; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfbfmin %s -o - | FileCheck --check-prefixes=CHECK,RV32ZFBFMIN %s
@@ -173,6 +174,7 @@
 ; RUN: llc -mtriple=riscv64 -mattr=+zve32x -mattr=+experimental-zvksh %s -o - | FileCheck --check-prefix=RV64ZVKSH %s
 ; RUN: llc -mtriple=riscv64 -mattr=+zve32x -mattr=+experimental-zvkt %s -o - | FileCheck --check-prefix=RV64ZVKT %s
 ; RUN: llc -mtriple=riscv64 -mattr=+experimental-zicond %s -o - | FileCheck --check-prefix=RV64ZICOND %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zimop %s -o - | FileCheck --check-prefix=RV64ZIMOP %s
 ; RUN: llc -mtriple=riscv64 -mattr=+smaia %s -o - | FileCheck --check-prefixes=CHECK,RV64SMAIA %s
 ; RUN: llc -mtriple=riscv64 -mattr=+ssaia %s -o - | FileCheck --check-prefixes=CHECK,RV64SSAIA %s
 ; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfbfmin %s -o - | FileCheck --check-prefixes=CHECK,RV64ZFBFMIN %s
@@ -265,6 +267,7 @@
 ; RV32ZVKSH: .attribute 5, "rv32i2p1_zicsr2p0_zve32x1p0_zvksh1p0_zvl32b1p0"
 ; RV32ZVKT: .attribute 5, "rv32i2p1_zicsr2p0_zve32x1p0_zvkt1p0_zvl32b1p0"
 ; RV32ZICOND: .attribute 5, "rv32i2p1_zicond1p0"
+; RV32ZIMOP: .attribute 5, "rv32i2p1_zimop0p1"
 ; RV32SMAIA: .attribute 5, "rv32i2p1_smaia1p0"
 ; RV32SSAIA: .attribute 5, "rv32i2p1_ssaia1p0"
 ; RV32ZFBFMIN: .attribute 5, "rv32i2p1_f2p2_zicsr2p0_zfbfmin0p8"
@@ -354,6 +357,7 @@
 ; RV64ZVKSH: .attribute 5, "rv64i2p1_zicsr2p0_zve32x1p0_zvksh1p0_zvl32b1p0"
 ; RV64ZVKT: .attribute 5, "rv64i2p1_zicsr2p0_zve32x1p0_zvkt1p0_zvl32b1p0"
 ; RV64ZICOND: .attribute 5, "rv64i2p1_zicond1p0"
+; RV64ZIMOP: .attribute 5, "rv64i2p1_zimop0p1"
 ; RV64SMAIA: .attribute 5, "rv64i2p1_smaia1p0"
 ; RV64SSAIA: .attribute 5, "rv64i2p1_ssaia1p0"
 ; RV64ZFBFMIN: .attribute 5, "rv64i2p1_f2p2_zicsr2p0_zfbfmin0p8"
diff --git a/llvm/test/CodeGen/RISCV/rv32zimop-intrinsic.ll b/llvm/test/CodeGen/RISCV/rv32zimop-intrinsic.ll
new file mode 100644
index 0000000000000..477c4abc126e1
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rv32zimop-intrinsic.ll
@@ -0,0 +1,47 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zimop -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s -check-prefix=RV32ZIMOP
+
+declare i32 @llvm.riscv.mopr0.i32(i32 %a)
+
+define i32 @mopr0_32(i32 %a) nounwind {
+; RV32ZIMOP-LABEL: mopr0_32:
+; RV32ZIMOP:       # %bb.0:
+; RV32ZIMOP-NEXT:    mop.r.0 a0, a0
+; RV32ZIMOP-NEXT:    ret
+  %tmp = call i32 @llvm.riscv.mopr0.i32(i32 %a)
+  ret i32 %tmp
+}
+
+declare i32 @llvm.riscv.mopr31.i32(i32 %a)
+
+define i32 @mopr31_32(i32 %a) nounwind {
+; RV32ZIMOP-LABEL: mopr31_32:
+; RV32ZIMOP:       # %bb.0:
+; RV32ZIMOP-NEXT:    mop.r.31 a0, a0
+; RV32ZIMOP-NEXT:    ret
+  %tmp = call i32 @llvm.riscv.mopr31.i32(i32 %a)
+  ret i32 %tmp
+}
+
+declare i32 @llvm.riscv.moprr0.i32(i32 %a, i32 %b)
+
+define i32 @moprr0_32(i32 %a, i32 %b) nounwind {
+; RV32ZIMOP-LABEL: moprr0_32:
+; RV32ZIMOP:       # %bb.0:
+; RV32ZIMOP-NEXT:    mop.rr.0 a0, a0, a1
+; RV32ZIMOP-NEXT:    ret
+  %tmp = call i32 @llvm.riscv.moprr0.i32(i32 %a, i32 %b)
+  ret i32 %tmp
+}
+
+declare i32 @llvm.riscv.moprr7.i32(i32 %a, i32 %b)
+
+define i32 @moprr7_32(i32 %a, i32 %b) nounwind {
+; RV32ZIMOP-LABEL: moprr7_32:
+; RV32ZIMOP:       # %bb.0:
+; RV32ZIMOP-NEXT:    mop.rr.7 a0, a0, a1
+; RV32ZIMOP-NEXT:    ret
+  %tmp = call i32 @llvm.riscv.moprr7.i32(i32 %a, i32 %b)
+  ret i32 %tmp
+}
\ No newline at end of file
diff --git a/llvm/test/CodeGen/RISCV/rv64zimop-intrinsic.ll b/llvm/test/CodeGen/RISCV/rv64zimop-intrinsic.ll
new file mode 100644
index 0000000000000..3faa0ca2f78ea
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rv64zimop-intrinsic.ll
@@ -0,0 +1,96 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zimop -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s -check-prefix=RV64ZIMOP
+
+declare i64 @llvm.riscv.mopr0.i64(i64 %a)
+
+define i64 @mopr0_64(i64 %a) nounwind {
+; RV64ZIMOP-LABEL: mopr0_64:
+; RV64ZIMOP:       # %bb.0:
+; RV64ZIMOP-NEXT:    mop.r.0 a0, a0
+; RV64ZIMOP-NEXT:    ret
+  %tmp = call i64 @llvm.riscv.mopr0.i64(i64 %a)
+  ret i64 %tmp
+}
+
+declare i64 @llvm.riscv.mopr31.i64(i64 %a)
+
+define i64 @mopr31_64(i64 %a) nounwind {
+; RV64ZIMOP-LABEL: mopr31_64:
+; RV64ZIMOP:       # %bb.0:
+; RV64ZIMOP-NEXT:    mop.r.31 a0, a0
+; RV64ZIMOP-NEXT:    ret
+  %tmp = call i64 @llvm.riscv.mopr31.i64(i64 %a)
+  ret i64 %tmp
+}
+
+declare i64 @llvm.riscv.moprr0.i64(i64 %a, i64 %b)
+
+define i64 @moprr0_64(i64 %a, i64 %b) nounwind {
+; RV64ZIMOP-LABEL: moprr0_64:
+; RV64ZIMOP:       # %bb.0:
+; RV64ZIMOP-NEXT:    mop.rr.0 a0, a0, a1
+; RV64ZIMOP-NEXT:    ret
+  %tmp = call i64 @llvm.riscv.moprr0.i64(i64 %a, i64 %b)
+  ret i64 %tmp
+}
+
+declare i64 @llvm.riscv.moprr7.i64(i64 %a, i64 %b)
+
+define i64 @moprr7_64(i64 %a, i64 %b) nounwind {
+; RV64ZIMOP-LABEL: moprr7_64:
+; RV64ZIMOP:       # %bb.0:
+; RV64ZIMOP-NEXT:    mop.rr.7 a0, a0, a1
+; RV64ZIMOP-NEXT:    ret
+  %tmp = call i64 @llvm.riscv.moprr7.i64(i64 %a, i64 %b)
+  ret i64 %tmp
+}
+
+
+declare i32 @llvm.riscv.mopr0.i32(i32 %a)
+
+define signext i32 @mopr0_32(i32 signext %a) nounwind {
+; RV64ZIMOP-LABEL: mopr0_32:
+; RV64ZIMOP:       # %bb.0:
+; RV64ZIMOP-NEXT:    mop.r.0 a0, a0
+; RV64ZIMOP-NEXT:    sext.w a0, a0
+; RV64ZIMOP-NEXT:    ret
+  %tmp = call i32 @llvm.riscv.mopr0.i32(i32 %a)
+  ret i32 %tmp
+}
+
+declare i32 @llvm.riscv.mopr31.i32(i32 %a)
+
+define signext i32 @mopr31_32(i32 signext %a) nounwind {
+; RV64ZIMOP-LABEL: mopr31_32:
+; RV64ZIMOP:       # %bb.0:
+; RV64ZIMOP-NEXT:    mop.r.31 a0, a0
+; RV64ZIMOP-NEXT:    sext.w a0, a0
+; RV64ZIMOP-NEXT:    ret
+  %tmp = call i32 @llvm.riscv.mopr31.i32(i32 %a)
+  ret i32 %tmp
+}
+
+declare i32 @llvm.riscv.moprr0.i32(i32 %a, i32 %b)
+
+define signext i32 @moprr0_32(i32 signext %a, i32 signext %b) nounwind {
+; RV64ZIMOP-LABEL: moprr0_32:
+; RV64ZIMOP:       # %bb.0:
+; RV64ZIMOP-NEXT:    mop.rr.0 a0, a0, a1
+; RV64ZIMOP-NEXT:    sext.w a0, a0
+; RV64ZIMOP-NEXT:    ret
+  %tmp = call i32 @llvm.riscv.moprr0.i32(i32 %a, i32 %b)
+  ret i32 %tmp
+}
+
+declare i32 @llvm.riscv.moprr7.i32(i32 %a, i32 %b)
+
+define signext i32 @moprr7_32(i32 signext %a, i32 signext %b) nounwind {
+; RV64ZIMOP-LABEL: moprr7_32:
+; RV64ZIMOP:       # %bb.0:
+; RV64ZIMOP-NEXT:    mop.rr.7 a0, a0, a1
+; RV64ZIMOP-NEXT:    sext.w a0, a0
+; RV64ZIMOP-NEXT:    ret
+  %tmp = call i32 @llvm.riscv.moprr7.i32(i32 %a, i32 %b)
+  ret i32 %tmp
+}
\ No newline at end of file
diff --git a/llvm/test/MC/RISCV/rv32zimop-invalid.s b/llvm/test/MC/RISCV/rv32zimop-invalid.s
new file mode 100644
index 0000000000000..e6c3adc4cd309
--- /dev/null
+++ b/llvm/test/MC/RISCV/rv32zimop-invalid.s
@@ -0,0 +1,6 @@
+# RUN: not llvm-mc -triple riscv32 -mattr=+experimental-zimop < %s 2>&1 | FileCheck %s
+
+# Too few operands
+mop.r.0 t0 # CHECK: :[[@LINE]]:1: error: too few operands for instruction
+# Too few operands
+mop.rr.0 t0, t1 # CHECK: :[[@LINE]]:1: error: too few operands for instruction
diff --git a/llvm/test/MC/RISCV/rvzimop-valid.s b/llvm/test/MC/RISCV/rvzimop-valid.s
new file mode 100644
index 0000000000000..1552936629902
--- /dev/null
+++ b/llvm/test/MC/RISCV/rvzimop-valid.s
@@ -0,0 +1,26 @@
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+experimental-zimop -show-encoding \
+# RUN:     | FileCheck -check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ %s
+# RUN: llvm-mc %s -triple=riscv64 -mattr=+experimental-zimop -show-encoding \
+# RUN:     | FileCheck -check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ %s
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+experimental-zimop < %s \
+# RUN:     | llvm-objdump --mattr=+experimental-zimop -d -r - \
+# RUN:     | FileCheck --check-prefix=CHECK-ASM-AND-OBJ %s
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+experimental-zimop < %s \
+# RUN:     | llvm-objdump --mattr=+experimental-zimop -d -r - \
+# RUN:     | FileCheck --check-prefix=CHECK-ASM-AND-OBJ %s
+
+# CHECK-ASM-AND-OBJ: mop.r.0 a2, a1
+# CHECK-ASM: encoding: [0x73,0xc6,0xc5,0x81]
+mop.r.0 a2, a1
+
+# CHECK-ASM-AND-OBJ: mop.r.31 a2, a1
+# CHECK-ASM: encoding: [0x73,0xc6,0xf5,0xcd]
+mop.r.31 a2, a1
+
+# CHECK-ASM-AND-OBJ: mop.rr.0 a3, a2, a1
+# CHECK-ASM: encoding: [0xf3,0x46,0xb6,0x82]
+mop.rr.0 a3, a2, a1
+
+# CHECK-ASM-AND-OBJ: mop.rr.7 a3, a2, a1
+# CHECK-ASM: encoding: [0xf3,0x46,0xb6,0xce]
+mop.rr.7 a3, a2, a1
\ No newline at end of file
diff --git a/llvm/unittests/Support/RISCVISAInfoTest.cpp b/llvm/unittests/Support/RISCVISAInfoTest.cpp
index 549964eed5551..bd41fa2338739 100644
--- a/llvm/unittests/Support/RISCVISAInfoTest.cpp
+++ b/llvm/unittests/Support/RISCVISAInfoTest.cpp
@@ -738,6 +738,7 @@ R"(All available -march extensions for RISC-V
 Experimental extensions
     zicfilp             0.2       This is a long dummy description
     zicond              1.0
+    zimop               0.1
     zacas               1.0
     zfbfmin             0.8
     ztso                0.1



More information about the llvm-commits mailing list