[llvm] EarlyCSE: create casts on type-mismatch (PR #113339)
Ramkumar Ramachandra via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 4 06:36:34 PST 2024
https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/113339
>From c04719379e898a0159ac41999ea48c8e89b35160 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <ramkumar.ramachandra at codasip.com>
Date: Tue, 22 Oct 2024 15:07:17 +0100
Subject: [PATCH] EarlyCSE: create casts on type-mismatch
getOrCreateResult suffers from the deficiency that it doesn't attempt to
create casts when types mismatch. Fix this deficiency, making EarlyCSE
more powerful.
---
llvm/lib/Transforms/Scalar/EarlyCSE.cpp | 52 ++++++++++---------
.../Transforms/EarlyCSE/invariant.start.ll | 30 ++++++-----
llvm/test/Transforms/EarlyCSE/opaque-ptr.ll | 4 +-
3 files changed, 48 insertions(+), 38 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index a1dbb4e1d5e75f..d1f1afb9ad9d25 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -31,6 +31,7 @@
#include "llvm/IR/Constants.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
@@ -964,32 +965,35 @@ class EarlyCSE {
bool overridingStores(const ParseMemoryInst &Earlier,
const ParseMemoryInst &Later);
- Value *getOrCreateResult(Value *Inst, Type *ExpectedType) const {
- // TODO: We could insert relevant casts on type mismatch here.
- if (auto *LI = dyn_cast<LoadInst>(Inst))
- return LI->getType() == ExpectedType ? LI : nullptr;
- if (auto *SI = dyn_cast<StoreInst>(Inst)) {
- Value *V = SI->getValueOperand();
- return V->getType() == ExpectedType ? V : nullptr;
+ Value *getOrCreateResult(Instruction *Inst, Type *ExpectedType) const {
+ // The load or the store's first operand.
+ Value *V;
+ if (auto *II = dyn_cast<IntrinsicInst>(Inst)) {
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::masked_load:
+ V = II;
+ break;
+ case Intrinsic::masked_store:
+ V = II->getOperand(0);
+ break;
+ default:
+ return TTI.getOrCreateResultFromMemIntrinsic(II, ExpectedType);
+ }
+ } else {
+ V = isa<LoadInst>(Inst) ? Inst : cast<StoreInst>(Inst)->getValueOperand();
}
- assert(isa<IntrinsicInst>(Inst) && "Instruction not supported");
- auto *II = cast<IntrinsicInst>(Inst);
- if (isHandledNonTargetIntrinsic(II->getIntrinsicID()))
- return getOrCreateResultNonTargetMemIntrinsic(II, ExpectedType);
- return TTI.getOrCreateResultFromMemIntrinsic(II, ExpectedType);
- }
- Value *getOrCreateResultNonTargetMemIntrinsic(IntrinsicInst *II,
- Type *ExpectedType) const {
- // TODO: We could insert relevant casts on type mismatch here.
- switch (II->getIntrinsicID()) {
- case Intrinsic::masked_load:
- return II->getType() == ExpectedType ? II : nullptr;
- case Intrinsic::masked_store: {
- Value *V = II->getOperand(0);
- return V->getType() == ExpectedType ? V : nullptr;
- }
- }
+ Type *ActualType = V->getType();
+ BasicBlock *TheBB = Inst->getParent();
+
+ // First handle the case when no cast is required.
+ if (ActualType == ExpectedType)
+ return V;
+
+ // If valid, create a bitcast.
+ IRBuilder<> Builder(TheBB, std::next(Inst->getIterator()));
+ if (CastInst::castIsValid(Instruction::BitCast, V, ExpectedType))
+ return Builder.CreateBitCast(V, ExpectedType);
return nullptr;
}
diff --git a/llvm/test/Transforms/EarlyCSE/invariant.start.ll b/llvm/test/Transforms/EarlyCSE/invariant.start.ll
index 554d3ce519b5ee..ad25137d20f466 100644
--- a/llvm/test/Transforms/EarlyCSE/invariant.start.ll
+++ b/llvm/test/Transforms/EarlyCSE/invariant.start.ll
@@ -472,15 +472,22 @@ define void @test_dse_after_load(ptr %p, i1 %cnd) {
; typed due to the user of a Value to represent the address. Note that other
; passes will canonicalize away the bitcasts in this example.
define i32 @test_false_negative_types(ptr %p) {
-; CHECK-LABEL: define {{[^@]+}}@test_false_negative_types
-; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])
-; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4
-; CHECK-NEXT: call void @clobber()
-; CHECK-NEXT: [[V2F:%.*]] = load float, ptr [[P]], align 4
-; CHECK-NEXT: [[V2:%.*]] = bitcast float [[V2F]] to i32
-; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[V1]], [[V2]]
-; CHECK-NEXT: ret i32 [[SUB]]
+; NO_ASSUME-LABEL: define {{[^@]+}}@test_false_negative_types
+; NO_ASSUME-SAME: (ptr [[P:%.*]]) {
+; NO_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])
+; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4
+; NO_ASSUME-NEXT: [[TMP2:%.*]] = bitcast i32 [[V1]] to float
+; NO_ASSUME-NEXT: call void @clobber()
+; NO_ASSUME-NEXT: ret i32 0
+;
+; USE_ASSUME-LABEL: define {{[^@]+}}@test_false_negative_types
+; USE_ASSUME-SAME: (ptr [[P:%.*]]) {
+; USE_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])
+; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4
+; USE_ASSUME-NEXT: [[TMP2:%.*]] = bitcast i32 [[V1]] to float
+; USE_ASSUME-NEXT: call void @clobber()
+; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]
+; USE_ASSUME-NEXT: ret i32 0
;
call ptr @llvm.invariant.start.p0(i64 4, ptr %p)
%v1 = load i32, ptr %p
@@ -571,13 +578,13 @@ define i32 @test_false_negative_scope(ptr %p) {
define i32 @test_invariant_load_scope(ptr %p) {
; NO_ASSUME-LABEL: define {{[^@]+}}@test_invariant_load_scope
; NO_ASSUME-SAME: (ptr [[P:%.*]]) {
-; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4, !invariant.load !4
+; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4, !invariant.load [[META4:![0-9]+]]
; NO_ASSUME-NEXT: call void @clobber()
; NO_ASSUME-NEXT: ret i32 0
;
; USE_ASSUME-LABEL: define {{[^@]+}}@test_invariant_load_scope
; USE_ASSUME-SAME: (ptr [[P:%.*]]) {
-; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4, !invariant.load !4
+; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4, !invariant.load [[META4:![0-9]+]]
; USE_ASSUME-NEXT: call void @clobber()
; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]
; USE_ASSUME-NEXT: ret i32 0
@@ -589,7 +596,6 @@ define i32 @test_invariant_load_scope(ptr %p) {
ret i32 %sub
}
-; USE_ASSUME: declare void @llvm.assume(i1 noundef)
!0 = !{!1, !1, i64 0}
!1 = !{!"float", !2, i64 0}
diff --git a/llvm/test/Transforms/EarlyCSE/opaque-ptr.ll b/llvm/test/Transforms/EarlyCSE/opaque-ptr.ll
index da507f13730e87..e1ce8b0a77d97c 100644
--- a/llvm/test/Transforms/EarlyCSE/opaque-ptr.ll
+++ b/llvm/test/Transforms/EarlyCSE/opaque-ptr.ll
@@ -51,7 +51,7 @@ define i32 @different_types_store(ptr %p, i32 %a) {
define i32 @different_elt_types_vector_load(ptr %p, <4 x i1> %c) {
; CHECK-LABEL: @different_elt_types_vector_load(
; CHECK-NEXT: [[V1:%.*]] = call <4 x i32> @llvm.masked.load.v4i32.p0(ptr [[P:%.*]], i32 4, <4 x i1> [[C:%.*]], <4 x i32> poison)
-; CHECK-NEXT: [[V2:%.*]] = call <4 x float> @llvm.masked.load.v4f32.p0(ptr [[P]], i32 4, <4 x i1> [[C]], <4 x float> poison)
+; CHECK-NEXT: [[V2:%.*]] = bitcast <4 x i32> [[V1]] to <4 x float>
; CHECK-NEXT: [[E1:%.*]] = extractelement <4 x i32> [[V1]], i32 0
; CHECK-NEXT: [[E2:%.*]] = extractelement <4 x float> [[V2]], i32 0
; CHECK-NEXT: [[E2I:%.*]] = fptosi float [[E2]] to i32
@@ -70,7 +70,7 @@ define i32 @different_elt_types_vector_load(ptr %p, <4 x i1> %c) {
define float @different_elt_types_vector_store_load(ptr %p, <4 x i32> %v1, <4 x i1> %c) {
; CHECK-LABEL: @different_elt_types_vector_store_load(
; CHECK-NEXT: call void @llvm.masked.store.v4i32.p0(<4 x i32> [[V1:%.*]], ptr [[P:%.*]], i32 4, <4 x i1> [[C:%.*]])
-; CHECK-NEXT: [[V2:%.*]] = call <4 x float> @llvm.masked.load.v4f32.p0(ptr [[P]], i32 4, <4 x i1> [[C]], <4 x float> poison)
+; CHECK-NEXT: [[V2:%.*]] = bitcast <4 x i32> [[V1]] to <4 x float>
; CHECK-NEXT: [[E2:%.*]] = extractelement <4 x float> [[V2]], i32 0
; CHECK-NEXT: ret float [[E2]]
;
More information about the llvm-commits
mailing list