[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