-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[RISCV] Initial support for EarlyCSE #138812
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[RISCV] Initial support for EarlyCSE #138812
Conversation
@llvm/pr-subscribers-llvm-transforms Author: Hank Chang (HankChang736) ChangesThis patch initially supports EarlyCSE for RISCV. Note:
Full diff: https://github.com/llvm/llvm-project/pull/138812.diff 3 Files Affected:
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
+
+
|
@llvm/pr-subscribers-backend-risc-v Author: Hank Chang (HankChang736) ChangesThis patch initially supports EarlyCSE for RISCV. Note:
Full diff: https://github.com/llvm/llvm-project/pull/138812.diff 3 Files Affected:
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
+
+
|
13f307a
to
427da9d
Compare
✅ With the latest revision this PR passed the C/C++ code formatter. |
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/
427da9d
to
94f87bf
Compare
Maybe the title should mention vector memory intrinsic? the EarlyCSE still work for RISC-V before, but just not work with those RISC-V specific memory intrinsic. |
@kito-cheng Thank you for the feedback. Yeah, I think it's better to mention RISC-V specific memory intrinsics. Will update it later. |
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test case for vse?
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a testcase that two vle with different vl? e.g. 8 and 7
This patch initially supports EarlyCSE for RISCV.
Note:
getTgtMemIntrinsic
andgetOrCreateResultFromMemIntrinsic