Skip to content

Commit 05c4681

Browse files
authored
[SCCP] Relax two-instruction range checks (#158495)
If we know x in R1, the range check `x in R2` can be relaxed into `x in Union(R2, Inverse(R1))`. The latter one may be more efficient if we can represent it with one icmp. Fixes regressions introduced by #156497. Proof for `(X & -Pow2) == C -> (X - C) < Pow2`: https://alive2.llvm.org/ce/z/HMgkuu Compile-time impact: https://llvm-compile-time-tracker.com/compare.php?from=ead4f3e271fdf6918aef2ede3a7134811147d276&to=bee3d902dd505cf9b11499ba4f230e4e8ae96b92&stat=instructions%3Au
1 parent c919109 commit 05c4681

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed

llvm/lib/Transforms/Utils/SCCPSolver.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
#include "llvm/Analysis/ValueLattice.h"
2020
#include "llvm/Analysis/ValueLatticeUtils.h"
2121
#include "llvm/Analysis/ValueTracking.h"
22+
#include "llvm/IR/ConstantRange.h"
2223
#include "llvm/IR/IRBuilder.h"
2324
#include "llvm/IR/InstVisitor.h"
25+
#include "llvm/IR/Instructions.h"
2426
#include "llvm/IR/NoFolder.h"
2527
#include "llvm/IR/PatternMatch.h"
2628
#include "llvm/Support/Casting.h"
@@ -284,6 +286,58 @@ static Value *simplifyInstruction(SCCPSolver &Solver,
284286
return Sub;
285287
}
286288

289+
// Relax range checks.
290+
if (auto *ICmp = dyn_cast<ICmpInst>(&Inst)) {
291+
Value *X;
292+
auto MatchTwoInstructionExactRangeCheck =
293+
[&]() -> std::optional<ConstantRange> {
294+
const APInt *RHSC;
295+
if (!match(ICmp->getOperand(1), m_APInt(RHSC)))
296+
return std::nullopt;
297+
298+
Value *LHS = ICmp->getOperand(0);
299+
ICmpInst::Predicate Pred = ICmp->getPredicate();
300+
const APInt *Offset;
301+
if (match(LHS, m_OneUse(m_AddLike(m_Value(X), m_APInt(Offset)))))
302+
return ConstantRange::makeExactICmpRegion(Pred, *RHSC).sub(*Offset);
303+
// Match icmp eq/ne X & NegPow2, C
304+
if (ICmp->isEquality()) {
305+
const APInt *Mask;
306+
if (match(LHS, m_OneUse(m_And(m_Value(X), m_NegatedPower2(Mask)))) &&
307+
RHSC->countr_zero() >= Mask->countr_zero()) {
308+
ConstantRange CR(*RHSC, *RHSC - *Mask);
309+
return Pred == ICmpInst::ICMP_EQ ? CR : CR.inverse();
310+
}
311+
}
312+
return std::nullopt;
313+
};
314+
315+
if (auto CR = MatchTwoInstructionExactRangeCheck()) {
316+
ConstantRange LRange = GetRange(X);
317+
// Early exit if we know nothing about X.
318+
if (LRange.isFullSet())
319+
return nullptr;
320+
// We are allowed to refine the comparison to either true or false for out
321+
// of range inputs. Here we refine the comparison to true, i.e. we relax
322+
// the range check.
323+
auto NewCR = CR->exactUnionWith(LRange.inverse());
324+
// TODO: Check if we can narrow the range check to an equality test.
325+
// E.g, for X in [0, 4), X - 3 u< 2 -> X == 3
326+
if (!NewCR)
327+
return nullptr;
328+
ICmpInst::Predicate Pred;
329+
APInt RHS;
330+
// Check if we can represent NewCR as an icmp predicate.
331+
if (NewCR->getEquivalentICmp(Pred, RHS)) {
332+
IRBuilder<NoFolder> Builder(&Inst);
333+
Value *NewICmp =
334+
Builder.CreateICmp(Pred, X, ConstantInt::get(X->getType(), RHS));
335+
InsertedValues.insert(NewICmp);
336+
return NewICmp;
337+
}
338+
}
339+
}
340+
287341
return nullptr;
288342
}
289343

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
2+
; RUN: opt < %s -passes=sccp -S | FileCheck %s
3+
4+
define i1 @relax_range_check(i8 range(i8 0, 5) %x) {
5+
; CHECK-LABEL: define i1 @relax_range_check(
6+
; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) {
7+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], -3
8+
; CHECK-NEXT: [[RET:%.*]] = icmp uge i8 [[X]], 3
9+
; CHECK-NEXT: ret i1 [[RET]]
10+
;
11+
%add = add i8 %x, -3
12+
%ret = icmp ult i8 %add, 2
13+
ret i1 %ret
14+
}
15+
16+
define i1 @relax_range_check_highbits_check(i8 range(i8 2, 0) %x) {
17+
; CHECK-LABEL: define i1 @relax_range_check_highbits_check(
18+
; CHECK-SAME: i8 range(i8 2, 0) [[X:%.*]]) {
19+
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X]], -2
20+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[X]], 4
21+
; CHECK-NEXT: ret i1 [[RET]]
22+
;
23+
%and = and i8 %x, -2
24+
%ret = icmp eq i8 %and, 2
25+
ret i1 %ret
26+
}
27+
28+
; Negative tests.
29+
30+
define i1 @relax_range_check_one_instruction(i8 range(i8 0, 5) %x) {
31+
; CHECK-LABEL: define i1 @relax_range_check_one_instruction(
32+
; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) {
33+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[X]], 2
34+
; CHECK-NEXT: ret i1 [[RET]]
35+
;
36+
%ret = icmp ult i8 %x, 2
37+
ret i1 %ret
38+
}
39+
40+
define i1 @relax_range_check_not_profitable(i8 range(i8 0, 6) %x) {
41+
; CHECK-LABEL: define i1 @relax_range_check_not_profitable(
42+
; CHECK-SAME: i8 range(i8 0, 6) [[X:%.*]]) {
43+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], -3
44+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[ADD]], 2
45+
; CHECK-NEXT: ret i1 [[RET]]
46+
;
47+
%add = add i8 %x, -3
48+
%ret = icmp ult i8 %add, 2
49+
ret i1 %ret
50+
}
51+
52+
define i1 @relax_range_check_unknown_range(i64 %x) {
53+
; CHECK-LABEL: define i1 @relax_range_check_unknown_range(
54+
; CHECK-SAME: i64 [[X:%.*]]) {
55+
; CHECK-NEXT: [[AND:%.*]] = and i64 [[X]], -67108864
56+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[AND]], 0
57+
; CHECK-NEXT: ret i1 [[TMP1]]
58+
;
59+
%and = and i64 %x, -67108864
60+
%test = icmp eq i64 %and, 0
61+
ret i1 %test
62+
}
63+
64+
define i1 @relax_range_check_highbits_check_multiuse(i8 range(i8 2, 0) %x) {
65+
; CHECK-LABEL: define i1 @relax_range_check_highbits_check_multiuse(
66+
; CHECK-SAME: i8 range(i8 2, 0) [[X:%.*]]) {
67+
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X]], -2
68+
; CHECK-NEXT: call void @use(i8 [[AND]])
69+
; CHECK-NEXT: [[RET:%.*]] = icmp eq i8 [[AND]], 2
70+
; CHECK-NEXT: ret i1 [[RET]]
71+
;
72+
%and = and i8 %x, -2
73+
call void @use(i8 %and)
74+
%ret = icmp eq i8 %and, 2
75+
ret i1 %ret
76+
}
77+
78+
define i1 @relax_range_check_multiuse(i8 range(i8 0, 5) %x) {
79+
; CHECK-LABEL: define i1 @relax_range_check_multiuse(
80+
; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) {
81+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], -3
82+
; CHECK-NEXT: call void @use(i8 [[ADD]])
83+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[ADD]], 2
84+
; CHECK-NEXT: ret i1 [[RET]]
85+
;
86+
%add = add i8 %x, -3
87+
call void @use(i8 %add)
88+
%ret = icmp ult i8 %add, 2
89+
ret i1 %ret
90+
}
91+
92+
declare void @use(i8)

0 commit comments

Comments
 (0)