[llvm] [InstCombine] Add cast support in simplifyUsingControlFlow (PR #142263)

Andreas Jonson via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 7 03:01:59 PDT 2025


https://github.com/andjo403 updated https://github.com/llvm/llvm-project/pull/142263

>From 1178db4cc62ad4abdac2f8e653fda94cbfc49142 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 31 May 2025 10:42:44 +0200
Subject: [PATCH 1/2] [InstCombine] Change value to avoid change of purpose of
 test with new fold (NFC)

---
 llvm/test/DebugInfo/X86/instcombine-fold-cast-into-phi.ll | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/test/DebugInfo/X86/instcombine-fold-cast-into-phi.ll b/llvm/test/DebugInfo/X86/instcombine-fold-cast-into-phi.ll
index a91a983be49bc..3ec226fc58d6b 100644
--- a/llvm/test/DebugInfo/X86/instcombine-fold-cast-into-phi.ll
+++ b/llvm/test/DebugInfo/X86/instcombine-fold-cast-into-phi.ll
@@ -4,7 +4,7 @@
 ;; user (otherwise the dbg use becomes poison after the original phi is
 ;; deleted). Check the new phi inherits the DebugLoc.
 
-; CHECK: %[[phi:.*]] = phi i8 [ 1, %{{.*}} ], [ 0, %{{.*}} ], !dbg ![[dbg:[0-9]+]]
+; CHECK: %[[phi:.*]] = phi i8 [ 2, %{{.*}} ], [ 0, %{{.*}} ], !dbg ![[dbg:[0-9]+]]
 ; CHECK: #dbg_value(i8 %[[phi]], ![[#]], !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value)
 ; CHECK: ![[dbg]] = !DILocation(line: 123,
 
@@ -19,7 +19,7 @@ if.then:                                          ; preds = entry
   br label %if.end
 
 if.end:                                           ; preds = %if.then, %entry
-  %p.0 = phi i32 [ 1, %if.then ], [ 0, %entry ], !dbg !13
+  %p.0 = phi i32 [ 2, %if.then ], [ 0, %entry ], !dbg !13
   call void @llvm.dbg.value(metadata i32 %p.0, metadata !4, metadata !DIExpression()), !dbg !13
   %x = trunc i32 %p.0 to i8
   %callff = call float @ff(i8  %x)

>From b9cd63ea1d6332b1bcf1184a068083b96a80e46d Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 7 Jun 2025 11:45:25 +0200
Subject: [PATCH 2/2] [InstCombine] Add cast support in
 simplifyUsingControlFlow

---
 .../Transforms/InstCombine/InstCombinePHI.cpp | 117 ++++++++++++------
 .../Transforms/InstCombine/phi-int-users.ll   |   2 +-
 .../InstCombine/simple_phi_condition.ll       |  19 +--
 3 files changed, 91 insertions(+), 47 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
index a842a5edcb8a3..18e94e94ab788 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
@@ -1301,6 +1301,7 @@ static Value *simplifyUsingControlFlow(InstCombiner &Self, PHINode &PN,
 
   // Determine which value the condition of the idom has for which successor.
   LLVMContext &Context = PN.getContext();
+  unsigned PNBitWidth = PN.getType()->getScalarSizeInBits();
   auto *IDom = DT.getNode(BB)->getIDom()->getBlock();
   Value *Cond;
   SmallDenseMap<ConstantInt *, BasicBlock *, 8> SuccForValue;
@@ -1318,61 +1319,101 @@ static Value *simplifyUsingControlFlow(InstCombiner &Self, PHINode &PN,
     AddSucc(ConstantInt::getFalse(Context), BI->getSuccessor(1));
   } else if (auto *SI = dyn_cast<SwitchInst>(IDom->getTerminator())) {
     Cond = SI->getCondition();
+    unsigned CondBitWidth = Cond->getType()->getScalarSizeInBits();
     ++SuccCount[SI->getDefaultDest()];
-    for (auto Case : SI->cases())
-      AddSucc(Case.getCaseValue(), Case.getCaseSuccessor());
+    for (auto Case : SI->cases()) {
+      ConstantInt *CaseValue = Case.getCaseValue();
+      if (CondBitWidth > PNBitWidth) {
+        CaseValue = ConstantInt::get(
+            Context, Case.getCaseValue()->getValue().trunc(PNBitWidth));
+      }
+      AddSucc(CaseValue, Case.getCaseSuccessor());
+    }
   } else {
     return nullptr;
   }
 
-  if (Cond->getType() != PN.getType())
-    return nullptr;
+  unsigned CondBitWidth = Cond->getType()->getScalarSizeInBits();
 
   // Check that edges outgoing from the idom's terminators dominate respective
   // inputs of the Phi.
-  std::optional<bool> Invert;
-  for (auto Pair : zip(PN.incoming_values(), PN.blocks())) {
-    auto *Input = cast<ConstantInt>(std::get<0>(Pair));
-    BasicBlock *Pred = std::get<1>(Pair);
-    auto IsCorrectInput = [&](ConstantInt *Input) {
-      // The input needs to be dominated by the corresponding edge of the idom.
-      // This edge cannot be a multi-edge, as that would imply that multiple
-      // different condition values follow the same edge.
-      auto It = SuccForValue.find(Input);
-      return It != SuccForValue.end() && SuccCount[It->second] == 1 &&
-             DT.dominates(BasicBlockEdge(IDom, It->second),
-                          BasicBlockEdge(Pred, BB));
-    };
-
-    // Depending on the constant, the condition may need to be inverted.
-    bool NeedsInvert;
-    if (IsCorrectInput(Input))
-      NeedsInvert = false;
-    else if (IsCorrectInput(cast<ConstantInt>(ConstantExpr::getNot(Input))))
-      NeedsInvert = true;
-    else
-      return nullptr;
+  auto CheckInputs = [&](Instruction::CastOps CastType) -> std::optional<bool> {
+    std::optional<bool> Invert;
+    for (auto Pair : zip(PN.incoming_values(), PN.blocks())) {
+      auto *Input = cast<ConstantInt>(std::get<0>(Pair));
+      BasicBlock *Pred = std::get<1>(Pair);
+
+      auto IsCorrectInput = [&](ConstantInt *Input) {
+        if (CondBitWidth < PNBitWidth) {
+          if ((CastType == Instruction::SExt &&
+               !Input->getValue().isSignedIntN(CondBitWidth)) ||
+              (CastType == Instruction::ZExt &&
+               !Input->getValue().isIntN(CondBitWidth)))
+            return false;
+
+          Input =
+              ConstantInt::get(Context, Input->getValue().trunc(CondBitWidth));
+        }
+        // The input needs to be dominated by the corresponding edge of the
+        // idom. This edge cannot be a multi-edge, as that would imply that
+        // multiple different condition values follow the same edge.
+        auto It = SuccForValue.find(Input);
+        return It != SuccForValue.end() && SuccCount[It->second] == 1 &&
+               DT.dominates(BasicBlockEdge(IDom, It->second),
+                            BasicBlockEdge(Pred, BB));
+      };
+
+      // Depending on the constant, the condition may need to be inverted.
+      bool NeedsInvert;
+      if (IsCorrectInput(Input))
+        NeedsInvert = false;
+      else if (IsCorrectInput(cast<ConstantInt>(ConstantExpr::getNot(Input))))
+        NeedsInvert = true;
+      else
+        return std::nullopt;
 
-    // Make sure the inversion requirement is always the same.
-    if (Invert && *Invert != NeedsInvert)
-      return nullptr;
+      // Make sure the inversion requirement is always the same.
+      if (Invert && *Invert != NeedsInvert)
+        return std::nullopt;
+
+      Invert = NeedsInvert;
+    }
+    return Invert;
+  };
+
+  Instruction::CastOps CastType =
+      CondBitWidth == PNBitWidth  ? Instruction::BitCast
+      : CondBitWidth < PNBitWidth ? Instruction::ZExt
+                                  : Instruction::Trunc;
 
-    Invert = NeedsInvert;
+  auto Result = CheckInputs(CastType);
+  if (!Result && CondBitWidth < PNBitWidth) {
+    CastType = Instruction::SExt;
+    Result = CheckInputs(CastType);
   }
+  if (!Result)
+    return nullptr;
+  bool Invert = *Result;
 
-  if (!*Invert)
+  if (!Invert && CastType == Instruction::BitCast)
     return Cond;
 
-  // This Phi is actually opposite to branching condition of IDom. We invert
-  // the condition that will potentially open up some opportunities for
-  // sinking.
   auto InsertPt = BB->getFirstInsertionPt();
-  if (InsertPt != BB->end()) {
-    Self.Builder.SetInsertPoint(&*BB, InsertPt);
-    return Self.Builder.CreateNot(Cond);
+  if (InsertPt == BB->end())
+    return nullptr;
+
+  Self.Builder.SetInsertPoint(&*BB, InsertPt);
+
+  if (CastType != Instruction::BitCast) {
+    Cond = Self.Builder.CreateCast(CastType, Cond, PN.getType());
+    if (!Invert)
+      return Cond;
   }
 
-  return nullptr;
+  // This Phi is actually opposite to branching condition of IDom. We invert
+  // the condition that will potentially open up some opportunities for
+  // sinking.
+  return Self.Builder.CreateNot(Cond);
 }
 
 // Fold  iv = phi(start, iv.next = iv2.next op start)
diff --git a/llvm/test/Transforms/InstCombine/phi-int-users.ll b/llvm/test/Transforms/InstCombine/phi-int-users.ll
index 6c98cc8a1c900..cc70feb160a08 100644
--- a/llvm/test/Transforms/InstCombine/phi-int-users.ll
+++ b/llvm/test/Transforms/InstCombine/phi-int-users.ll
@@ -13,7 +13,7 @@ define void @f1(i1 %a) {
 ; CHECK:       [[BB2]]:
 ; CHECK-NEXT:    br label %[[BB3]]
 ; CHECK:       [[BB3]]:
-; CHECK-NEXT:    [[PHI:%.*]] = phi i64 [ 0, %[[BB2]] ], [ 1, %[[BB1]] ]
+; CHECK-NEXT:    [[PHI:%.*]] = zext i1 [[A]] to i64
 ; CHECK-NEXT:    [[INTTOPTR:%.*]] = inttoptr i64 [[PHI]] to ptr
 ; CHECK-NEXT:    store i32 0, ptr [[INTTOPTR]], align 4
 ; CHECK-NEXT:    br label %[[BB1]]
diff --git a/llvm/test/Transforms/InstCombine/simple_phi_condition.ll b/llvm/test/Transforms/InstCombine/simple_phi_condition.ll
index e1a90d6a1c688..98ec7d217e4c1 100644
--- a/llvm/test/Transforms/InstCombine/simple_phi_condition.ll
+++ b/llvm/test/Transforms/InstCombine/simple_phi_condition.ll
@@ -669,7 +669,7 @@ define i32 @test_phi_to_zext(i8 noundef %0) {
 ; CHECK:       sw.1:
 ; CHECK-NEXT:    br label [[MERGE]]
 ; CHECK:       merge:
-; CHECK-NEXT:    [[DOT0:%.*]] = phi i32 [ 1, [[SW_1]] ], [ 0, [[SW_0]] ], [ 255, [[SW_255]] ]
+; CHECK-NEXT:    [[DOT0:%.*]] = zext i8 [[TMP0]] to i32
 ; CHECK-NEXT:    ret i32 [[DOT0]]
 ;
 entry:
@@ -713,7 +713,8 @@ define i32 @test_phi_to_zext_inverted(i8 noundef %0) {
 ; CHECK:       sw.1:
 ; CHECK-NEXT:    br label [[MERGE]]
 ; CHECK:       merge:
-; CHECK-NEXT:    [[DOT0:%.*]] = phi i32 [ -256, [[SW_255]] ], [ -2, [[SW_1]] ], [ -1, [[SW_0]] ]
+; CHECK-NEXT:    [[TMP1:%.*]] = zext i8 [[TMP0]] to i32
+; CHECK-NEXT:    [[DOT0:%.*]] = xor i32 [[TMP1]], -1
 ; CHECK-NEXT:    ret i32 [[DOT0]]
 ;
 entry:
@@ -753,7 +754,7 @@ define i8 @test_multiple_predecessors_phi_to_zext(i1 %cond, i1 %cond2) {
 ; CHECK:       if2.false:
 ; CHECK-NEXT:    br label [[MERGE]]
 ; CHECK:       merge:
-; CHECK-NEXT:    [[RET:%.*]] = phi i8 [ 0, [[IF_FALSE]] ], [ 1, [[IF2_TRUE]] ], [ 1, [[IF2_FALSE]] ]
+; CHECK-NEXT:    [[RET:%.*]] = zext i1 [[COND]] to i8
 ; CHECK-NEXT:    ret i8 [[RET]]
 ;
 entry:
@@ -793,7 +794,7 @@ define i32 @test_phi_to_sext(i8 noundef %0) {
 ; CHECK:       sw.1:
 ; CHECK-NEXT:    br label [[MERGE]]
 ; CHECK:       merge:
-; CHECK-NEXT:    [[DOT0:%.*]] = phi i32 [ 1, [[SW_1]] ], [ 0, [[SW_0]] ], [ -1, [[SW_255]] ]
+; CHECK-NEXT:    [[DOT0:%.*]] = sext i8 [[TMP0]] to i32
 ; CHECK-NEXT:    ret i32 [[DOT0]]
 ;
 entry:
@@ -837,7 +838,8 @@ define i32 @test_phi_to_sext_inverted(i8 noundef %0) {
 ; CHECK:       sw.1:
 ; CHECK-NEXT:    br label [[MERGE]]
 ; CHECK:       merge:
-; CHECK-NEXT:    [[DOT0:%.*]] = phi i32 [ -2, [[SW_1]] ], [ -1, [[SW_0]] ], [ 0, [[SW_255]] ]
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i8 [[TMP0]], -1
+; CHECK-NEXT:    [[DOT0:%.*]] = sext i8 [[TMP1]] to i32
 ; CHECK-NEXT:    ret i32 [[DOT0]]
 ;
 entry:
@@ -877,7 +879,7 @@ define i8 @test_multiple_predecessors_phi_to_sext(i1 %cond, i1 %cond2) {
 ; CHECK:       if2.false:
 ; CHECK-NEXT:    br label [[MERGE]]
 ; CHECK:       merge:
-; CHECK-NEXT:    [[RET:%.*]] = phi i8 [ 0, [[IF_FALSE]] ], [ -1, [[IF2_TRUE]] ], [ -1, [[IF2_FALSE]] ]
+; CHECK-NEXT:    [[RET:%.*]] = sext i1 [[COND]] to i8
 ; CHECK-NEXT:    ret i8 [[RET]]
 ;
 entry:
@@ -995,7 +997,7 @@ define i8 @test_phi_to_trunc(i32 %0) {
 ; CHECK:       sw.255:
 ; CHECK-NEXT:    br label [[MERGE]]
 ; CHECK:       merge:
-; CHECK-NEXT:    [[DOT0:%.*]] = phi i8 [ -1, [[SW_255]] ], [ 1, [[SW_1]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[DOT0:%.*]] = trunc i32 [[TMP0]] to i8
 ; CHECK-NEXT:    ret i8 [[DOT0]]
 ;
 entry:
@@ -1034,7 +1036,8 @@ define i8 @test_phi_to_trunc_inverted(i32 %0) {
 ; CHECK:       sw.255:
 ; CHECK-NEXT:    br label [[MERGE]]
 ; CHECK:       merge:
-; CHECK-NEXT:    [[DOT0:%.*]] = phi i8 [ 0, [[SW_255]] ], [ -2, [[SW_1]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[TMP1:%.*]] = trunc i32 [[TMP0]] to i8
+; CHECK-NEXT:    [[DOT0:%.*]] = xor i8 [[TMP1]], -1
 ; CHECK-NEXT:    ret i8 [[DOT0]]
 ;
 entry:



More information about the llvm-commits mailing list