Skip to content

Commit 265b032

Browse files
authored
[InstCombine] Added optimisation for trunc (Pow2 >> x) to i1 (llvm#157030)
Closes llvm#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
1 parent 61e4d23 commit 265b032

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "InstCombineInternal.h"
14+
#include "llvm/ADT/APInt.h"
1415
#include "llvm/ADT/SetVector.h"
1516
#include "llvm/Analysis/ConstantFolding.h"
1617
#include "llvm/IR/DataLayout.h"
1718
#include "llvm/IR/DebugInfo.h"
1819
#include "llvm/IR/PatternMatch.h"
20+
#include "llvm/IR/Value.h"
1921
#include "llvm/Support/KnownBits.h"
2022
#include "llvm/Transforms/InstCombine/InstCombiner.h"
2123
#include <optional>
@@ -969,6 +971,25 @@ Instruction *InstCombinerImpl::visitTrunc(TruncInst &Trunc) {
969971
Changed = true;
970972
}
971973

974+
const APInt *C1;
975+
Value *V1;
976+
// OP = { lshr, ashr }
977+
// trunc ( OP i8 C1, V1) to i1 -> icmp eq V1, log_2(C1) iff C1 is power of 2
978+
if (DestWidth == 1 && match(Src, m_Shr(m_Power2(C1), m_Value(V1)))) {
979+
Value *Right = ConstantInt::get(V1->getType(), C1->countr_zero());
980+
Value *Icmp = Builder.CreateICmpEQ(V1, Right);
981+
return replaceInstUsesWith(Trunc, Icmp);
982+
}
983+
984+
// OP = { lshr, ashr }
985+
// trunc ( OP i8 C1, V1) to i1 -> icmp ult V1, log_2(C1 + 1) iff (C1 + 1) is
986+
// power of 2
987+
if (DestWidth == 1 && match(Src, m_Shr(m_LowBitMask(C1), m_Value(V1)))) {
988+
Value *Right = ConstantInt::get(V1->getType(), C1->countr_one());
989+
Value *Icmp = Builder.CreateICmpULT(V1, Right);
990+
return replaceInstUsesWith(Trunc, Icmp);
991+
}
992+
972993
return Changed ? &Trunc : nullptr;
973994
}
974995

llvm/test/Transforms/InstCombine/trunc-lshr.ll

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
22
; RUN: opt -S -passes=instcombine < %s | FileCheck %s
33

4+
declare void @use(i8)
5+
46
define i1 @test1(i32 %i, ptr %p) {
57
; CHECK-LABEL: define i1 @test1(
68
; CHECK-SAME: i32 [[I:%.*]], ptr [[P:%.*]]) {
@@ -93,3 +95,127 @@ define i1 @test5(i32 %i, ptr %p) {
9395
ret i1 %op
9496
}
9597

98+
define i1 @fold_lshr_power_of_2(i8 %x) {
99+
; CHECK-LABEL: define i1 @fold_lshr_power_of_2(
100+
; CHECK-SAME: i8 [[X:%.*]]) {
101+
; CHECK-NEXT: [[TRUNC:%.*]] = icmp eq i8 [[X]], 4
102+
; CHECK-NEXT: ret i1 [[TRUNC]]
103+
;
104+
%lshr = lshr i8 16, %x ; 16 is a power of 2
105+
%trunc = trunc i8 %lshr to i1
106+
ret i1 %trunc
107+
}
108+
109+
define i1 @fold_lshr_power_of_2_minus_1(i8 %x) {
110+
; CHECK-LABEL: define i1 @fold_lshr_power_of_2_minus_1(
111+
; CHECK-SAME: i8 [[X:%.*]]) {
112+
; CHECK-NEXT: [[TRUNC:%.*]] = icmp ult i8 [[X]], 4
113+
; CHECK-NEXT: ret i1 [[TRUNC]]
114+
;
115+
%lshr = lshr i8 15, %x
116+
%trunc = trunc i8 %lshr to i1
117+
ret i1 %trunc
118+
}
119+
120+
define i1 @fold_ashr_power_of_2(i8 %x) {
121+
; CHECK-LABEL: define i1 @fold_ashr_power_of_2(
122+
; CHECK-SAME: i8 [[X:%.*]]) {
123+
; CHECK-NEXT: [[TRUNC:%.*]] = icmp eq i8 [[X]], 4
124+
; CHECK-NEXT: ret i1 [[TRUNC]]
125+
;
126+
%ashr = ashr i8 16, %x
127+
%trunc = trunc i8 %ashr to i1
128+
ret i1 %trunc
129+
}
130+
131+
define i1 @fold_ashr_power_of_2_minus_1(i8 %x) {
132+
; CHECK-LABEL: define i1 @fold_ashr_power_of_2_minus_1(
133+
; CHECK-SAME: i8 [[X:%.*]]) {
134+
; CHECK-NEXT: [[TRUNC:%.*]] = icmp ult i8 [[X]], 4
135+
; CHECK-NEXT: ret i1 [[TRUNC]]
136+
;
137+
%ashr = ashr i8 15, %x ; (15 + 1) is a power of 2
138+
%trunc = trunc i8 %ashr to i1
139+
ret i1 %trunc
140+
}
141+
142+
define i1 @fold_lshr_power_of_2_multi_use(i8 %x) {
143+
; CHECK-LABEL: define i1 @fold_lshr_power_of_2_multi_use(
144+
; CHECK-SAME: i8 [[X:%.*]]) {
145+
; CHECK-NEXT: [[LSHR:%.*]] = lshr i8 16, [[X]]
146+
; CHECK-NEXT: call void @use(i8 [[LSHR]])
147+
; CHECK-NEXT: [[TRUNC:%.*]] = icmp eq i8 [[X]], 4
148+
; CHECK-NEXT: ret i1 [[TRUNC]]
149+
;
150+
%lshr = lshr i8 16, %x ; 16 is a power of 2
151+
call void @use(i8 %lshr)
152+
%trunc = trunc i8 %lshr to i1
153+
ret i1 %trunc
154+
}
155+
156+
define i1 @fold_lshr_power_of_2_minus_1_multi_use(i8 %x) {
157+
; CHECK-LABEL: define i1 @fold_lshr_power_of_2_minus_1_multi_use(
158+
; CHECK-SAME: i8 [[X:%.*]]) {
159+
; CHECK-NEXT: [[LSHR:%.*]] = lshr i8 15, [[X]]
160+
; CHECK-NEXT: call void @use(i8 [[LSHR]])
161+
; CHECK-NEXT: [[TRUNC:%.*]] = icmp ult i8 [[X]], 4
162+
; CHECK-NEXT: ret i1 [[TRUNC]]
163+
;
164+
%lshr = lshr i8 15, %x
165+
call void @use(i8 %lshr)
166+
%trunc = trunc i8 %lshr to i1
167+
ret i1 %trunc
168+
}
169+
170+
define i1 @fold_ashr_power_of_2_multi_use(i8 %x) {
171+
; CHECK-LABEL: define i1 @fold_ashr_power_of_2_multi_use(
172+
; CHECK-SAME: i8 [[X:%.*]]) {
173+
; CHECK-NEXT: [[ASHR:%.*]] = lshr i8 16, [[X]]
174+
; CHECK-NEXT: call void @use(i8 [[ASHR]])
175+
; CHECK-NEXT: [[TRUNC:%.*]] = icmp eq i8 [[X]], 4
176+
; CHECK-NEXT: ret i1 [[TRUNC]]
177+
;
178+
%ashr = ashr i8 16, %x
179+
call void @use(i8 %ashr)
180+
%trunc = trunc i8 %ashr to i1
181+
ret i1 %trunc
182+
}
183+
184+
define i1 @fold_ashr_power_of_2_minus_1_multi_use(i8 %x) {
185+
; CHECK-LABEL: define i1 @fold_ashr_power_of_2_minus_1_multi_use(
186+
; CHECK-SAME: i8 [[X:%.*]]) {
187+
; CHECK-NEXT: [[ASHR:%.*]] = lshr i8 15, [[X]]
188+
; CHECK-NEXT: call void @use(i8 [[ASHR]])
189+
; CHECK-NEXT: [[TRUNC:%.*]] = icmp ult i8 [[X]], 4
190+
; CHECK-NEXT: ret i1 [[TRUNC]]
191+
;
192+
%ashr = ashr i8 15, %x ; (15 + 1) is a power of 2
193+
call void @use(i8 %ashr)
194+
%trunc = trunc i8 %ashr to i1
195+
ret i1 %trunc
196+
}
197+
198+
define i1 @negative_test_fold_lshr(i8 %x) {
199+
; CHECK-LABEL: define i1 @negative_test_fold_lshr(
200+
; CHECK-SAME: i8 [[X:%.*]]) {
201+
; CHECK-NEXT: [[LSHR:%.*]] = lshr i8 9, [[X]]
202+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i8 [[LSHR]] to i1
203+
; CHECK-NEXT: ret i1 [[TRUNC]]
204+
;
205+
%lshr = lshr i8 9, %x ; 9 or (9 + 1) is not a power of 2
206+
%trunc = trunc i8 %lshr to i1
207+
ret i1 %trunc
208+
}
209+
210+
; Negative Test for arithmetic shift right
211+
define i1 @negative_test_fold_ashr(i8 %x) {
212+
; CHECK-LABEL: define i1 @negative_test_fold_ashr(
213+
; CHECK-SAME: i8 [[X:%.*]]) {
214+
; CHECK-NEXT: [[ASHR:%.*]] = lshr i8 9, [[X]]
215+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i8 [[ASHR]] to i1
216+
; CHECK-NEXT: ret i1 [[TRUNC]]
217+
;
218+
%ashr = ashr i8 9, %x ; 9 or (9 + 1) is not a power of 2
219+
%trunc = trunc i8 %ashr to i1
220+
ret i1 %trunc
221+
}

0 commit comments

Comments
 (0)