[llvm] 265b032 - [InstCombine] Added optimisation for trunc (Pow2 >> x) to i1 (#157030)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 10 08:54:22 PDT 2025
Author: kper
Date: 2025-09-10T15:54:18Z
New Revision: 265b032bdae0550de69ff52b4ef9deaf78bae522
URL: https://github.com/llvm/llvm-project/commit/265b032bdae0550de69ff52b4ef9deaf78bae522
DIFF: https://github.com/llvm/llvm-project/commit/265b032bdae0550de69ff52b4ef9deaf78bae522.diff
LOG: [InstCombine] Added optimisation for trunc (Pow2 >> x) to i1 (#157030)
Closes #156898
I have added two cases. The first one matches when the constant is
exactly power of 2. The second case was to address the general case
mentioned in the linked issue. I, however, did not really solve the
general case.
We can only emit a `icmp ult` if all the bits are one and that's only
the case when the constant + 1 is a power of 2. Otherwise, we need to
create `icmp eq` for every bit that is one.
Here are a few examples which won't be working with the two cases:
- constant is `9`: https://alive2.llvm.org/ce/z/S5FLJZ
- subrange in `56`: https://alive2.llvm.org/ce/z/yn_ZNG
- and finally an example as worst case (because it alternates the bits):
https://alive2.llvm.org/ce/z/nDitNA
Added:
Modified:
llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
llvm/test/Transforms/InstCombine/trunc-lshr.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index fdef49e310f81..ccf918f0b6dbe 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,25 @@ 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_LowBitMask(C1), m_Value(V1)))) {
+ Value *Right = ConstantInt::get(V1->getType(), C1->countr_one());
+ 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