Skip to content

[InstCombine] Simplify multi use cast of Phi with constant inputs - #186621

Closed
andjo403 wants to merge 2 commits into
llvm:mainfrom
andjo403:multiUseCastOfPhi
Closed

[InstCombine] Simplify multi use cast of Phi with constant inputs#186621
andjo403 wants to merge 2 commits into
llvm:mainfrom
andjo403:multiUseCastOfPhi

Conversation

@andjo403

Copy link
Copy Markdown
Contributor

@andjo403
andjo403 requested review from dtcxzyw and nikic March 14, 2026 20:27
@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Mar 14, 2026
@llvmbot

llvmbot commented Mar 14, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-llvm-transforms

Author: Andreas Jonson (andjo403)

Changes

Regression noticed in #184182

for icmp there is a similar fold here https://github.com/andjo403/llvm-project/blob/393db14ac027412180d94ddcd35fb25b09a45913/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp#L1346-L1361


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp (+6-1)
  • (modified) llvm/test/Transforms/InstCombine/cast_phi.ll (+100)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index f2a2e21f6fc95..fcd46b3ba4a17 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -237,7 +237,12 @@ Instruction *InstCombinerImpl::commonCastTransforms(CastInst &CI) {
     // legal type.
     if (!Src->getType()->isIntegerTy() || !CI.getType()->isIntegerTy() ||
         shouldChangeType(CI.getSrcTy(), CI.getType()))
-      if (Instruction *NV = foldOpIntoPhi(CI, PN))
+      if (Instruction *NV =
+              foldOpIntoPhi(CI, PN,
+                            is_contained({Instruction::Trunc, Instruction::ZExt,
+                                          Instruction::SExt},
+                                         CI.getOpcode()) &&
+                                all_of(PN->operands(), IsaPred<ConstantInt>)))
         return NV;
   }
 
diff --git a/llvm/test/Transforms/InstCombine/cast_phi.ll b/llvm/test/Transforms/InstCombine/cast_phi.ll
index e8db72c8d849e..59929ee420f49 100644
--- a/llvm/test/Transforms/InstCombine/cast_phi.ll
+++ b/llvm/test/Transforms/InstCombine/cast_phi.ll
@@ -378,3 +378,103 @@ exit:
   %ext = zext i8 %iv to i32
   ret i32 %ext
 }
+
+
+declare void @use8(i8)
+declare void @use32(i32)
+
+define i32 @zext_constants_multi_use(i8 %x) {
+; CHECK-LABEL: @zext_constants_multi_use(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X:%.*]], 42
+; CHECK-NEXT:    br i1 [[CMP]], label [[T:%.*]], label [[F:%.*]]
+; CHECK:       t:
+; CHECK-NEXT:    br label [[EXIT:%.*]]
+; CHECK:       f:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[R:%.*]] = phi i32 [ 5, [[T]] ], [ 255, [[F]] ]
+; CHECK-NEXT:    [[P:%.*]] = phi i8 [ 5, [[T]] ], [ -1, [[F]] ]
+; CHECK-NEXT:    call void @use8(i8 [[P]])
+; CHECK-NEXT:    ret i32 [[R]]
+;
+entry:
+  %cmp = icmp eq i8 %x, 42
+  br i1 %cmp, label %t, label %f
+
+t:
+  br label %exit
+
+f:
+  br label %exit
+
+exit:
+  %p = phi i8 [ 5, %t ], [ -1, %f ]
+  call void @use8(i8 %p)
+  %r = zext i8 %p to i32
+  ret i32 %r
+}
+
+define i32 @sext_constants_multi_use(i8 %x) {
+; CHECK-LABEL: @sext_constants_multi_use(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X:%.*]], 42
+; CHECK-NEXT:    br i1 [[CMP]], label [[T:%.*]], label [[F:%.*]]
+; CHECK:       t:
+; CHECK-NEXT:    br label [[EXIT:%.*]]
+; CHECK:       f:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[R:%.*]] = phi i32 [ 5, [[T]] ], [ -1, [[F]] ]
+; CHECK-NEXT:    [[P:%.*]] = phi i8 [ 5, [[T]] ], [ -1, [[F]] ]
+; CHECK-NEXT:    call void @use8(i8 [[P]])
+; CHECK-NEXT:    ret i32 [[R]]
+;
+entry:
+  %cmp = icmp eq i8 %x, 42
+  br i1 %cmp, label %t, label %f
+
+t:
+  br label %exit
+
+f:
+  br label %exit
+
+exit:
+  %p = phi i8 [ 5, %t ], [ -1, %f ]
+  call void @use8(i8 %p)
+  %r = sext i8 %p to i32
+  ret i32 %r
+}
+
+define i8 @trunc_constants_multi_use(i8 %x) {
+; CHECK-LABEL: @trunc_constants_multi_use(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X:%.*]], 42
+; CHECK-NEXT:    br i1 [[CMP]], label [[T:%.*]], label [[F:%.*]]
+; CHECK:       t:
+; CHECK-NEXT:    br label [[EXIT:%.*]]
+; CHECK:       f:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[R:%.*]] = phi i8 [ 5, [[T]] ], [ -1, [[F]] ]
+; CHECK-NEXT:    [[P:%.*]] = phi i32 [ 5, [[T]] ], [ -1, [[F]] ]
+; CHECK-NEXT:    call void @use32(i32 [[P]])
+; CHECK-NEXT:    ret i8 [[R]]
+;
+entry:
+  %cmp = icmp eq i8 %x, 42
+  br i1 %cmp, label %t, label %f
+
+t:
+  br label %exit
+
+f:
+  br label %exit
+
+exit:
+  %p = phi i32 [ 5, %t ], [ -1, %f ]
+  call void @use32(i32 %p)
+  %r = trunc i32 %p to i8
+  ret i8 %r
+}

@andjo403
andjo403 force-pushed the multiUseCastOfPhi branch from 393db14 to b264797 Compare March 14, 2026 20:30
@github-actions

github-actions Bot commented Mar 14, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

if (Instruction *NV = foldOpIntoPhi(CI, PN))
if (Instruction *NV = foldOpIntoPhi(
CI, PN,
/*AllowMultipleUses=*/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possible to set AllowMultipleUses to true as there is a check in foldOpIntoPhi that only allow multi use of the instruction if all inputs is simplified but maybe wast compile time?

@dtcxzyw dtcxzyw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I am too late :( May I ask you what the motivating case is? If the new phi cannot be eliminated by follow-up transformations, the backend has to materialize incoming constants twice :( I don't see the benefit.

https://github.com/andjo403/llvm-project/blob/393db14ac027412180d94ddcd35fb25b09a45913/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp#L1346-L1361 is unavailable now. I guess the motivating case is trunc nuw %phi to i1? If so, can we be more conservative and just match the specific pattern?

@andjo403

andjo403 commented Apr 24, 2026

Copy link
Copy Markdown
Contributor Author

the motivation was:
dtcxzyw/llvm-opt-benchmark#3528 (comment)

And noticed that it was folded when it was a icmp ne %x, 0 in

if (auto *Phi = dyn_cast<PHINode>(Op0))
if (all_of(Phi->operands(), IsaPred<Constant>)) {
SmallVector<Constant *> Ops;
for (Value *V : Phi->incoming_values()) {
Constant *Res =
ConstantFoldCompareInstOperands(Pred, cast<Constant>(V), C, DL);
if (!Res)
return nullptr;
Ops.push_back(Res);
}
Builder.SetInsertPoint(Phi);
PHINode *NewPhi = Builder.CreatePHI(Cmp.getType(), Phi->getNumOperands());
for (auto [V, Pred] : zip(Ops, Phi->blocks()))
NewPhi->addIncoming(V, Pred);
return replaceInstUsesWith(Cmp, NewPhi);
}

but yes probably makes more sense only for trunc nuw but will still be a bit of gamble that something simplifies to remove the extra phi

@andjo403 andjo403 closed this Apr 28, 2026
@andjo403
andjo403 deleted the multiUseCastOfPhi branch May 17, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants