[llvm] [InstCombine] Added optimisation for trunc (Pow2 >> x) to i1 (PR #157030)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 8 10:01:39 PDT 2025


https://github.com/kper updated https://github.com/llvm/llvm-project/pull/157030

>From c51f8fbe799dc02eac4143aea03db42b25f006e3 Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Fri, 5 Sep 2025 06:23:45 +0000
Subject: [PATCH] [InstCombine] Added optimisation for trunc (C >> x) to i1

Transformation is done iff PowerOf2(C) || PowerOf2(C + 1)
---
 .../InstCombine/InstCombineCasts.cpp          |  23 ++++
 .../test/Transforms/InstCombine/trunc-lshr.ll | 126 ++++++++++++++++++
 2 files changed, 149 insertions(+)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index fdef49e310f81..df47acdb8546a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -11,11 +11,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "InstCombineInternal.h"
+#include "llvm/ADT/APInt.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/PatternMatch.h"
+#include "llvm/IR/Value.h"
 #include "llvm/Support/KnownBits.h"
 #include "llvm/Transforms/InstCombine/InstCombiner.h"
 #include <optional>
@@ -969,6 +971,27 @@ Instruction *InstCombinerImpl::visitTrunc(TruncInst &Trunc) {
     Changed = true;
   }
 
+  const APInt *C1;
+  Value *V1;
+  // OP = { lshr, ashr }
+  // trunc ( OP i8 C1, V1) to i1 -> icmp eq V1, log_2(C1) iff C1 is power of 2
+  if (DestWidth == 1 &&
+      match(Src, m_Shr(m_Power2(C1), m_Value(V1)))) {
+    Value *Right = ConstantInt::get(V1->getType(), C1->countr_zero());
+    Value *Icmp = Builder.CreateICmpEQ(V1, Right);
+    return replaceInstUsesWith(Trunc, Icmp);
+  }
+
+  // OP = { lshr, ashr }
+  // trunc ( OP i8 C1, V1) to i1 -> icmp ult V1, log_2(C1 + 1) iff (C1 + 1) is
+  // power of 2
+  if (DestWidth == 1 && match(Src, m_Shr(m_APInt(C1), m_Value(V1))) &&
+      (*C1 + 1).isPowerOf2()) {
+    Value *Right = ConstantInt::get(V1->getType(), (*C1 + 1).countr_zero());
+    Value *Icmp = Builder.CreateICmpULT(V1, Right);
+    return replaceInstUsesWith(Trunc, Icmp);
+  }
+
   return Changed ? &Trunc : nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/trunc-lshr.ll b/llvm/test/Transforms/InstCombine/trunc-lshr.ll
index 4364b09cfa709..c443b35cb1c1e 100644
--- a/llvm/test/Transforms/InstCombine/trunc-lshr.ll
+++ b/llvm/test/Transforms/InstCombine/trunc-lshr.ll
@@ -1,6 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt -S -passes=instcombine < %s | FileCheck %s
 
+declare void @use(i8)
+
 define i1 @test1(i32 %i, ptr %p) {
 ; CHECK-LABEL: define i1 @test1(
 ; CHECK-SAME: i32 [[I:%.*]], ptr [[P:%.*]]) {
@@ -93,3 +95,127 @@ define i1 @test5(i32 %i, ptr %p) {
   ret i1 %op
 }
 
+define i1 @fold_lshr_power_of_2(i8 %x) {
+; CHECK-LABEL: define i1 @fold_lshr_power_of_2(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TRUNC:%.*]] = icmp eq i8 [[X]], 4
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %lshr = lshr i8 16, %x ; 16 is a power of 2
+  %trunc = trunc i8 %lshr to i1
+  ret i1 %trunc
+}
+
+define i1 @fold_lshr_power_of_2_minus_1(i8 %x) {
+; CHECK-LABEL: define i1 @fold_lshr_power_of_2_minus_1(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TRUNC:%.*]] = icmp ult i8 [[X]], 4
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %lshr = lshr i8 15, %x
+  %trunc = trunc i8 %lshr to i1
+  ret i1 %trunc
+}
+
+define i1 @fold_ashr_power_of_2(i8 %x) {
+; CHECK-LABEL: define i1 @fold_ashr_power_of_2(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TRUNC:%.*]] = icmp eq i8 [[X]], 4
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %ashr = ashr i8 16, %x
+  %trunc = trunc i8 %ashr to i1
+  ret i1 %trunc
+}
+
+define i1 @fold_ashr_power_of_2_minus_1(i8 %x) {
+; CHECK-LABEL: define i1 @fold_ashr_power_of_2_minus_1(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TRUNC:%.*]] = icmp ult i8 [[X]], 4
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %ashr = ashr i8 15, %x ; (15 + 1) is a power of 2
+  %trunc = trunc i8 %ashr to i1
+  ret i1 %trunc
+}
+
+define i1 @fold_lshr_power_of_2_multi_use(i8 %x) {
+; CHECK-LABEL: define i1 @fold_lshr_power_of_2_multi_use(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[LSHR:%.*]] = lshr i8 16, [[X]]
+; CHECK-NEXT:    call void @use(i8 [[LSHR]])
+; CHECK-NEXT:    [[TRUNC:%.*]] = icmp eq i8 [[X]], 4
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %lshr = lshr i8 16, %x ; 16 is a power of 2
+  call void @use(i8 %lshr)
+  %trunc = trunc i8 %lshr to i1
+  ret i1 %trunc
+}
+
+define i1 @fold_lshr_power_of_2_minus_1_multi_use(i8 %x) {
+; CHECK-LABEL: define i1 @fold_lshr_power_of_2_minus_1_multi_use(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[LSHR:%.*]] = lshr i8 15, [[X]]
+; CHECK-NEXT:    call void @use(i8 [[LSHR]])
+; CHECK-NEXT:    [[TRUNC:%.*]] = icmp ult i8 [[X]], 4
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %lshr = lshr i8 15, %x
+  call void @use(i8 %lshr)
+  %trunc = trunc i8 %lshr to i1
+  ret i1 %trunc
+}
+
+define i1 @fold_ashr_power_of_2_multi_use(i8 %x) {
+; CHECK-LABEL: define i1 @fold_ashr_power_of_2_multi_use(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[ASHR:%.*]] = lshr i8 16, [[X]]
+; CHECK-NEXT:    call void @use(i8 [[ASHR]])
+; CHECK-NEXT:    [[TRUNC:%.*]] = icmp eq i8 [[X]], 4
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %ashr = ashr i8 16, %x
+  call void @use(i8 %ashr)
+  %trunc = trunc i8 %ashr to i1
+  ret i1 %trunc
+}
+
+define i1 @fold_ashr_power_of_2_minus_1_multi_use(i8 %x) {
+; CHECK-LABEL: define i1 @fold_ashr_power_of_2_minus_1_multi_use(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[ASHR:%.*]] = lshr i8 15, [[X]]
+; CHECK-NEXT:    call void @use(i8 [[ASHR]])
+; CHECK-NEXT:    [[TRUNC:%.*]] = icmp ult i8 [[X]], 4
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %ashr = ashr i8 15, %x ; (15 + 1) is a power of 2
+  call void @use(i8 %ashr)
+  %trunc = trunc i8 %ashr to i1
+  ret i1 %trunc
+}
+
+define i1 @negative_test_fold_lshr(i8 %x) {
+; CHECK-LABEL: define i1 @negative_test_fold_lshr(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[LSHR:%.*]] = lshr i8 9, [[X]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[LSHR]] to i1
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %lshr = lshr i8 9, %x ; 9 or (9 + 1) is not a power of 2
+  %trunc = trunc i8 %lshr to i1
+  ret i1 %trunc
+}
+
+; Negative Test for arithmetic shift right
+define i1 @negative_test_fold_ashr(i8 %x) {
+; CHECK-LABEL: define i1 @negative_test_fold_ashr(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[ASHR:%.*]] = lshr i8 9, [[X]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[ASHR]] to i1
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %ashr = ashr i8 9, %x ; 9 or (9 + 1) is not a power of 2
+  %trunc = trunc i8 %ashr to i1
+  ret i1 %trunc
+}



More information about the llvm-commits mailing list