[llvm] [RISCV] Initial support for EarlyCSE (PR #138812)

via llvm-commits llvm-commits at lists.llvm.org
Wed May 7 00:35:32 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Hank Chang (HankChang736)

<details>
<summary>Changes</summary>

This patch initially supports EarlyCSE for RISCV.

Note:
* Add two TTI hook `getTgtMemIntrinsic` and `getOrCreateResultFromMemIntrinsic`
* Add one test case intrinsics.ll in llvm/test/Transforms/EarlyCSE/RISCV/

---
Full diff: https://github.com/llvm/llvm-project/pull/138812.diff


3 Files Affected:

- (modified) llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp (+45) 
- (modified) llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h (+8-1) 
- (added) llvm/test/Transforms/EarlyCSE/RISCV/intrinsics.ll (+42) 


``````````diff
diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
index db2f1141ee4b7..0d7fa63f33999 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
@@ -15,6 +15,7 @@
 #include "llvm/CodeGen/TargetLowering.h"
 #include "llvm/CodeGen/ValueTypes.h"
 #include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicsRISCV.h"
 #include "llvm/IR/PatternMatch.h"
 #include <cmath>
 #include <optional>
@@ -116,6 +117,50 @@ RISCVTTIImpl::getRISCVInstructionCost(ArrayRef<unsigned> OpCodes, MVT VT,
   return Cost;
 }
 
+Value *
+RISCVTTIImpl::getOrCreateResultFromMemIntrinsic(IntrinsicInst *Inst,
+                                                Type *ExpectedType) const {
+  Intrinsic::ID IID = Inst->getIntrinsicID();
+  switch (IID) {
+  default:
+    return nullptr;
+  // TODO: Add more memory intrinsic operations.
+  case Intrinsic::riscv_vle: {
+    if (Inst->getType() == ExpectedType)
+      return Inst;
+  }
+    return nullptr;
+  }
+}
+
+bool RISCVTTIImpl::getTgtMemIntrinsic(IntrinsicInst *Inst,
+                                      MemIntrinsicInfo &Info) const {
+  Intrinsic::ID IID = Inst->getIntrinsicID();
+  switch (IID) {
+  default:
+    return false;
+  case Intrinsic::riscv_vle: {
+    // Intrinsic interface:
+    // riscv_vle(merge, ptr, vl)
+    Info.ReadMem = true;
+    Info.WriteMem = false;
+    Info.PtrVal = Inst->getArgOperand(1);
+    Info.MatchingId = VECTOR_VLE_VSE;
+    break;
+  }
+  case Intrinsic::riscv_vse: {
+    // Intrinsic interface:
+    // riscv_vse(val, ptr, vl)
+    Info.ReadMem = false;
+    Info.WriteMem = true;
+    Info.PtrVal = Inst->getArgOperand(1);
+    Info.MatchingId = VECTOR_VLE_VSE;
+    break;
+  }
+  }
+  return true;
+}
+
 static InstructionCost getIntImmCostImpl(const DataLayout &DL,
                                          const RISCVSubtarget *ST,
                                          const APInt &Imm, Type *Ty,
diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h
index 53529d077fd54..0eb2a033da9fb 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h
@@ -37,6 +37,8 @@ class RISCVTTIImpl : public BasicTTIImplBase<RISCVTTIImpl> {
   const RISCVSubtarget *getST() const { return ST; }
   const RISCVTargetLowering *getTLI() const { return TLI; }
 
+  enum MemIntrinsicType { VECTOR_VLE_VSE };
+
   /// This function returns an estimate for VL to be used in VL based terms
   /// of the cost model.  For fixed length vectors, this is simply the
   /// vector length.  For scalable vectors, we return results consistent
@@ -156,7 +158,12 @@ class RISCVTTIImpl : public BasicTTIImplBase<RISCVTTIImpl> {
   void getPeelingPreferences(Loop *L, ScalarEvolution &SE,
                              TTI::PeelingPreferences &PP) const override;
 
-  unsigned getMinVectorRegisterBitWidth() const override {
+  Value *getOrCreateResultFromMemIntrinsic(IntrinsicInst *Inst,
+                                           Type *ExpectedType) const override;
+
+  bool getTgtMemIntrinsic(IntrinsicInst *Inst, MemIntrinsicInfo &Info) const;
+
+  unsigned getMinVectorRegisterBitWidth() const {
     return ST->useRVVForFixedLengthVectors() ? 16 : 0;
   }
 
diff --git a/llvm/test/Transforms/EarlyCSE/RISCV/intrinsics.ll b/llvm/test/Transforms/EarlyCSE/RISCV/intrinsics.ll
new file mode 100644
index 0000000000000..c00e822da3de4
--- /dev/null
+++ b/llvm/test/Transforms/EarlyCSE/RISCV/intrinsics.ll
@@ -0,0 +1,42 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -S -mtriple=riscv64 -mattr=+v -passes=early-cse -earlycse-debug-hash | FileCheck %s
+; RUN: opt < %s -S -mtriple=riscv64 -mattr=+v -aa-pipeline=basic-aa -passes='early-cse<memssa>' | FileCheck %s
+
+define <vscale x 2 x i32> @test_cse(ptr noundef %base) {
+; CHECK-LABEL: define <vscale x 2 x i32> @test_cse(
+; CHECK-SAME: ptr noundef [[BASE:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call <vscale x 2 x i32> @llvm.riscv.vle.nxv2i32.i64(<vscale x 2 x i32> poison, ptr [[BASE]], i64 8)
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 2 x i32> @llvm.riscv.vadd.nxv2i32.nxv2i32.i64(<vscale x 2 x i32> poison, <vscale x 2 x i32> [[TMP0]], <vscale x 2 x i32> [[TMP0]], i64 8)
+; CHECK-NEXT:    ret <vscale x 2 x i32> [[TMP1]]
+;
+entry:
+  %0 = call <vscale x 2 x i32> @llvm.riscv.vle.nxv2i32.i64(<vscale x 2 x i32> poison, ptr %base, i64 8)
+  %1 = call <vscale x 2 x i32> @llvm.riscv.vle.nxv2i32.i64(<vscale x 2 x i32> poison, ptr %base, i64 8)
+  %2 = call <vscale x 2 x i32> @llvm.riscv.vadd.nxv2i32.nxv2i32.i64(<vscale x 2 x i32> poison, <vscale x 2 x i32> %0, <vscale x 2 x i32> %1, i64 8)
+  ret <vscale x 2 x i32> %2
+}
+
+define <vscale x 2 x i32> @test_no_cse(ptr noundef %a, ptr noundef %b) {
+; CHECK-LABEL: define <vscale x 2 x i32> @test_no_cse(
+; CHECK-SAME: ptr noundef [[A:%.*]], ptr noundef [[B:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call <vscale x 2 x i32> @llvm.riscv.vle.nxv2i32.i64(<vscale x 2 x i32> poison, ptr [[A]], i64 8)
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 2 x i32> @llvm.riscv.vle.nxv2i32.i64(<vscale x 2 x i32> poison, ptr [[B]], i64 8)
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 2 x i32> @llvm.riscv.vadd.nxv2i32.nxv2i32.i64(<vscale x 2 x i32> poison, <vscale x 2 x i32> [[TMP0]], <vscale x 2 x i32> [[TMP1]], i64 8)
+; CHECK-NEXT:    ret <vscale x 2 x i32> [[TMP2]]
+;
+entry:
+  %0 = call <vscale x 2 x i32> @llvm.riscv.vle.nxv2i32.i64(<vscale x 2 x i32> poison, ptr %a, i64 8)
+  %1 = call <vscale x 2 x i32> @llvm.riscv.vle.nxv2i32.i64(<vscale x 2 x i32> poison, ptr %b, i64 8)
+  %2 = call <vscale x 2 x i32> @llvm.riscv.vadd.nxv2i32.nxv2i32.i64(<vscale x 2 x i32> poison, <vscale x 2 x i32> %0, <vscale x 2 x i32> %1, i64 8)
+  ret <vscale x 2 x i32> %2
+}
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
+declare <vscale x 2 x i32> @llvm.riscv.vle.nxv2i32.i64(<vscale x 2 x i32>, ptr captures(none), i64) #1
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+declare <vscale x 2 x i32> @llvm.riscv.vadd.nxv2i32.nxv2i32.i64(<vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, i64) #2
+
+

``````````

</details>


https://github.com/llvm/llvm-project/pull/138812


More information about the llvm-commits mailing list