[llvm] [TypePromotion] Support trunc-to-i1 conditions (PR #216311)

Vito Kortbeek via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 14 05:38:19 PDT 2026


https://github.com/vkortbeek-gf updated https://github.com/llvm/llvm-project/pull/216311

>From 664db42366f4ef438e0c64aedff9d71e27b8833f Mon Sep 17 00:00:00 2001
From: Vito Kortbeek <vito.kortbeek at globalfoundries.com>
Date: Fri, 14 Aug 2026 13:42:40 +0200
Subject: [PATCH] [TypePromotion] Support trunc-to-i1 conditions

InstCombine may rewrite `icmp ne (X & 1), 0` to `trunc X to i1` (#178977).
Start TypePromotion searches from operands of scalar trunc-to-i1 instructions,
treating the trunc as a boolean boundary without promoting its result or
traversing its users.

Assisted-by: AI
---
 llvm/lib/CodeGen/TypePromotion.cpp            | 40 ++++++++++++++-----
 .../Transforms/TypePromotion/ARM/casts.ll     | 27 +++++++++++++
 2 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/CodeGen/TypePromotion.cpp b/llvm/lib/CodeGen/TypePromotion.cpp
index e9f54fd556bbd..59ea4281f6ee4 100644
--- a/llvm/lib/CodeGen/TypePromotion.cpp
+++ b/llvm/lib/CodeGen/TypePromotion.cpp
@@ -9,9 +9,9 @@
 /// \file
 /// This is an opcode based type promotion pass for small types that would
 /// otherwise be promoted during legalisation. This works around the limitations
-/// of selection dag for cyclic regions. The search begins from icmp
-/// instructions operands where a tree, consisting of non-wrapping or safe
-/// wrapping instructions, is built, checked and promoted if possible.
+/// of selection dag for cyclic regions. The search begins from operands of icmp
+/// and scalar trunc-to-i1 instructions. A tree consisting of non-wrapping or
+/// safe wrapping instructions is then built, checked and promoted if possible.
 ///
 //===----------------------------------------------------------------------===//
 
@@ -156,6 +156,8 @@ class TypePromotionImpl {
   bool isSource(Value *V);
   // Should V be a root in the promotion tree?
   bool isSink(Value *V);
+  // Is V a supported truncation to i1?
+  bool isSupportedTruncToI1(Value *V);
   // Should we change the result type of V? It will result in the users of V
   // being visited.
   bool shouldPromote(Value *V);
@@ -203,6 +205,11 @@ static bool GenerateSignBits(Instruction *I) {
          Opc == Instruction::SRem || Opc == Instruction::SExt;
 }
 
+static bool isTruncToI1(Value *V) {
+  auto *Trunc = dyn_cast<TruncInst>(V);
+  return Trunc && Trunc->getType()->isIntegerTy(1);
+}
+
 bool TypePromotionImpl::EqualTypeSize(Value *V) {
   return V->getType()->getScalarSizeInBits() == TypeSize;
 }
@@ -270,6 +277,10 @@ bool TypePromotionImpl::isSink(Value *V) {
   return isa<CallInst>(V);
 }
 
+bool TypePromotionImpl::isSupportedTruncToI1(Value *V) {
+  return isTruncToI1(V) && EqualTypeSize(cast<TruncInst>(V)->getOperand(0));
+}
+
 /// Return whether this instruction can safely wrap.
 bool TypePromotionImpl::isSafeWrap(Instruction *I) {
   // We can support a potentially wrapping Add/Sub instruction (I) if:
@@ -389,7 +400,7 @@ bool TypePromotionImpl::shouldPromote(Value *V) {
   if (!I)
     return false;
 
-  if (isa<ICmpInst>(I))
+  if (isa<ICmpInst>(I) || isSupportedTruncToI1(I))
     return false;
 
   return true;
@@ -523,8 +534,8 @@ void IRPromoter::PromoteTree() {
       }
     }
 
-    // Mutate the result type, unless this is an icmp or switch.
-    if (!isa<ICmpInst>(I) && !isa<SwitchInst>(I)) {
+    // Mutate the result type, unless this is an icmp, switch, or trunc to i1.
+    if (!isa<ICmpInst>(I) && !isa<SwitchInst>(I) && !isTruncToI1(I)) {
       I->mutateType(ExtTy);
       Promoted.insert(I);
     }
@@ -642,7 +653,7 @@ void IRPromoter::ConvertTruncs() {
   IRBuilder<> Builder{Ctx};
 
   for (auto *V : Visited) {
-    if (!isa<TruncInst>(V) || Sources.count(V))
+    if (!isa<TruncInst>(V) || isTruncToI1(V) || Sources.count(V))
       continue;
 
     auto *Trunc = cast<TruncInst>(V);
@@ -681,7 +692,7 @@ void IRPromoter::Mutate() {
     }
   }
   for (auto *V : Visited) {
-    if (!isa<TruncInst>(V) || Sources.count(V))
+    if (!isa<TruncInst>(V) || isTruncToI1(V) || Sources.count(V))
       continue;
     auto *Trunc = cast<TruncInst>(V);
     TruncTysMap[Trunc].push_back(Trunc->getDestTy());
@@ -742,8 +753,9 @@ bool TypePromotionImpl::isSupportedValue(Value *V) {
     case Instruction::Select:
     case Instruction::Ret:
     case Instruction::Load:
-    case Instruction::Trunc:
       return isSupportedType(I);
+    case Instruction::Trunc:
+      return isSupportedTruncToI1(I) || isSupportedType(I);
     case Instruction::BitCast:
       return I->getOperand(0)->getType() == I->getType();
     case Instruction::ZExt:
@@ -1011,6 +1023,16 @@ bool TypePromotionImpl::run(Function &F, const TargetMachine *TM,
             }
           }
         }
+      } else if (isTruncToI1(&I)) {
+        // Like an unsigned icmp, a scalar trunc to i1 is a boolean boundary.
+        auto *Trunc = cast<TruncInst>(&I);
+        LLVM_DEBUG(dbgs() << "IR Promotion: Searching from: " << *Trunc
+                          << "\n");
+
+        if (auto *OpI = dyn_cast<Instruction>(Trunc->getOperand(0))) {
+          if (auto PromotedWidth = GetPromoteWidth(OpI))
+            MadeChange |= TryToPromote(OpI, PromotedWidth, LI);
+        }
       }
     }
     if (!InstsToRemove.empty()) {
diff --git a/llvm/test/Transforms/TypePromotion/ARM/casts.ll b/llvm/test/Transforms/TypePromotion/ARM/casts.ll
index 7c3a87db41aa1..eee8b4e3ccbe8 100644
--- a/llvm/test/Transforms/TypePromotion/ARM/casts.ll
+++ b/llvm/test/Transforms/TypePromotion/ARM/casts.ll
@@ -795,6 +795,33 @@ exit:
   ret i32 %retval
 }
 
+define i8 @loop_trunc_i1(i8 zeroext %x) {
+; CHECK-LABEL: @loop_trunc_i1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[VALUE:%.*]] = phi i32 [ [[TMP0]], [[ENTRY:%.*]] ], [ [[NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[NEXT]] = lshr i32 [[VALUE]], 1
+; CHECK-NEXT:    [[COND:%.*]] = trunc i32 [[VALUE]] to i1
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[TMP1:%.*]] = trunc i32 [[NEXT]] to i8
+; CHECK-NEXT:    ret i8 [[TMP1]]
+;
+entry:
+  br label %loop
+
+loop:
+  %value = phi i8 [ %x, %entry ], [ %next, %loop ]
+  %next = lshr i8 %value, 1
+  %cond = trunc i8 %value to i1
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret i8 %next
+}
+
 define void @search_back_through_trunc(ptr %a, ptr %b, ptr %c, ptr %d, ptr %e) {
 ; CHECK-LABEL: @search_back_through_trunc(
 ; CHECK-NEXT:  entry:



More information about the llvm-commits mailing list