Skip to content

Commit 0e0ff85

Browse files
committed
[GuardWidening] Refactor to work with the list of checks to widen/hoist
Currently we hoist conditions from widenable branch which are joined to the widenable_condition by And operation. E.g if we have br(WC && (c1 && c2)) we will operate with (c1 && c2) unsplitted. This patch adds more flexibility to that mechanism by supporting work with the list of checks parsed from the widenable branch. On that stage patch doesn't change the logic of checks hoisting. In the example above we will either hoist both checks [c1, c2] or none of them. But in the future we would improve that logic analyzing each check separately. Reviewed By: anna Differential Revision: https://reviews.llvm.org/D157689
1 parent 9f67143 commit 0e0ff85

File tree

2 files changed

+120
-82
lines changed

2 files changed

+120
-82
lines changed

llvm/lib/Transforms/Scalar/GuardWidening.cpp

Lines changed: 109 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "llvm/Analysis/ValueTracking.h"
5252
#include "llvm/IR/ConstantRange.h"
5353
#include "llvm/IR/Dominators.h"
54+
#include "llvm/IR/IRBuilder.h"
5455
#include "llvm/IR/IntrinsicInst.h"
5556
#include "llvm/IR/PatternMatch.h"
5657
#include "llvm/InitializePasses.h"
@@ -123,10 +124,12 @@ static void eliminateGuard(Instruction *GuardInst, MemorySSAUpdater *MSSAU) {
123124
/// condition should stay invariant. Otherwise there can be a miscompile, like
124125
/// the one described at https://github.com/llvm/llvm-project/issues/60234. The
125126
/// safest way to do it is to expand the new condition at WC's block.
126-
static Instruction *findInsertionPointForWideCondition(Instruction *Guard) {
127-
if (auto WC = extractWidenableCondition(Guard))
127+
static Instruction *findInsertionPointForWideCondition(Instruction *WCOrGuard) {
128+
if (isGuard(WCOrGuard))
129+
return WCOrGuard;
130+
if (auto WC = extractWidenableCondition(WCOrGuard))
128131
return cast<Instruction>(WC);
129-
return Guard;
132+
return nullptr;
130133
}
131134

132135
class GuardWideningImpl {
@@ -179,9 +182,11 @@ class GuardWideningImpl {
179182
static StringRef scoreTypeToString(WideningScore WS);
180183

181184
/// Compute the score for widening the condition in \p DominatedInstr
182-
/// into \p DominatingGuard.
185+
/// into \p WideningPoint.
183186
WideningScore computeWideningScore(Instruction *DominatedInstr,
184-
Instruction *DominatingGuard);
187+
Instruction *WideningPoint,
188+
SmallVectorImpl<Value *> &ChecksToHoist,
189+
SmallVectorImpl<Value *> &ChecksToWiden);
185190

186191
/// Helper to check if \p V can be hoisted to \p InsertPos.
187192
bool canBeHoistedTo(const Value *V, const Instruction *InsertPos) const {
@@ -192,22 +197,35 @@ class GuardWideningImpl {
192197
bool canBeHoistedTo(const Value *V, const Instruction *InsertPos,
193198
SmallPtrSetImpl<const Instruction *> &Visited) const;
194199

200+
bool canBeHoistedTo(const SmallVectorImpl<Value *> &Checks,
201+
const Instruction *InsertPos) const {
202+
return all_of(Checks,
203+
[&](const Value *V) { return canBeHoistedTo(V, InsertPos); });
204+
}
195205
/// Helper to hoist \p V to \p InsertPos. Guaranteed to succeed if \c
196206
/// canBeHoistedTo returned true.
197207
void makeAvailableAt(Value *V, Instruction *InsertPos) const;
198208

209+
void makeAvailableAt(const SmallVectorImpl<Value *> &Checks,
210+
Instruction *InsertPos) const {
211+
for_each(Checks, [&](Value *V) { makeAvailableAt(V, InsertPos); });
212+
}
213+
199214
/// Common helper used by \c widenGuard and \c isWideningCondProfitable. Try
200-
/// to generate an expression computing the logical AND of \p Cond0 and Cond1.
201-
/// Return true if the expression computing the AND is only as
202-
/// expensive as computing one of the two. If \p InsertPt is true then
203-
/// actually generate the resulting expression, make it available at \p
204-
/// InsertPt and return it in \p Result (else no change to the IR is made).
205-
std::optional<Value *> mergeChecks(Value *Cond0, Value *Cond1,
215+
/// to generate an expression computing the logical AND of \p ChecksToHoist
216+
/// and \p ChecksToWiden. Return true if the expression computing the AND is
217+
/// only as expensive as computing one of the set of expressions. If \p
218+
/// InsertPt is true then actually generate the resulting expression, make it
219+
/// available at \p InsertPt and return it in \p Result (else no change to the
220+
/// IR is made).
221+
std::optional<Value *> mergeChecks(SmallVectorImpl<Value *> &ChecksToHoist,
222+
SmallVectorImpl<Value *> &ChecksToWiden,
206223
Instruction *InsertPt);
207224

208-
/// Generate the logical AND of \p Cond0 and \p Cond1 and make it available at
209-
/// \p InsertPt
210-
Value *hoistChecks(Value *Cond0, Value *Cond1, Instruction *InsertPt);
225+
/// Generate the logical AND of \p ChecksToHoist and \p OldCondition and make
226+
/// it available at InsertPt
227+
Value *hoistChecks(SmallVectorImpl<Value *> &ChecksToHoist,
228+
Value *OldCondition, Instruction *InsertPt);
211229

212230
/// Adds freeze to Orig and push it as far as possible very aggressively.
213231
/// Also replaces all uses of frozen instruction with frozen version.
@@ -252,16 +270,19 @@ class GuardWideningImpl {
252270
}
253271
};
254272

255-
/// Parse \p CheckCond into a conjunction (logical-and) of range checks; and
273+
/// Parse \p ToParse into a conjunction (logical-and) of range checks; and
256274
/// append them to \p Checks. Returns true on success, may clobber \c Checks
257275
/// on failure.
258-
bool parseRangeChecks(Value *CheckCond, SmallVectorImpl<RangeCheck> &Checks) {
259-
SmallPtrSet<const Value *, 8> Visited;
260-
return parseRangeChecks(CheckCond, Checks, Visited);
276+
bool parseRangeChecks(SmallVectorImpl<Value *> &ToParse,
277+
SmallVectorImpl<RangeCheck> &Checks) {
278+
for (auto CheckCond : ToParse) {
279+
if (!parseRangeChecks(CheckCond, Checks))
280+
return false;
281+
}
282+
return true;
261283
}
262284

263-
bool parseRangeChecks(Value *CheckCond, SmallVectorImpl<RangeCheck> &Checks,
264-
SmallPtrSetImpl<const Value *> &Visited);
285+
bool parseRangeChecks(Value *CheckCond, SmallVectorImpl<RangeCheck> &Checks);
265286

266287
/// Combine the checks in \p Checks into a smaller set of checks and append
267288
/// them into \p CombinedChecks. Return true on success (i.e. all of checks
@@ -270,20 +291,23 @@ class GuardWideningImpl {
270291
bool combineRangeChecks(SmallVectorImpl<RangeCheck> &Checks,
271292
SmallVectorImpl<RangeCheck> &CombinedChecks) const;
272293

273-
/// Can we compute the logical AND of \p Cond0 and \p Cond1 for the price of
274-
/// computing only one of the two expressions?
275-
bool isWideningCondProfitable(Value *Cond0, Value *Cond1) {
276-
return mergeChecks(Cond0, Cond1, /*InsertPt=*/nullptr).has_value();
294+
/// Can we compute the logical AND of \p ChecksToHoist and \p ChecksToWiden
295+
/// for the price of computing only one of the set of expressions?
296+
bool isWideningCondProfitable(SmallVectorImpl<Value *> &ChecksToHoist,
297+
SmallVectorImpl<Value *> &ChecksToWiden) {
298+
return mergeChecks(ChecksToHoist, ChecksToWiden, /*InsertPt=*/nullptr)
299+
.has_value();
277300
}
278301

279-
/// Widen \p ToWiden to fail if \p NewCondition is false
280-
void widenGuard(Instruction *ToWiden, Value *NewCondition) {
302+
/// Widen \p ChecksToWiden to fail if any of \p ChecksToHoist is false
303+
void widenGuard(SmallVectorImpl<Value *> &ChecksToHoist,
304+
SmallVectorImpl<Value *> &ChecksToWiden,
305+
Instruction *ToWiden) {
281306
Instruction *InsertPt = findInsertionPointForWideCondition(ToWiden);
282-
auto MergedCheck =
283-
mergeChecks(getCondition(ToWiden), NewCondition, InsertPt);
307+
auto MergedCheck = mergeChecks(ChecksToHoist, ChecksToWiden, InsertPt);
284308
Value *Result = MergedCheck ? *MergedCheck
285-
: hoistChecks(getCondition(ToWiden),
286-
NewCondition, InsertPt);
309+
: hoistChecks(ChecksToHoist,
310+
getCondition(ToWiden), InsertPt);
287311

288312
if (isGuardAsWidenableBranch(ToWiden)) {
289313
setWidenableBranchCond(cast<BranchInst>(ToWiden), Result);
@@ -352,10 +376,13 @@ bool GuardWideningImpl::eliminateInstrViaWidening(
352376
Instruction *Instr, const df_iterator<DomTreeNode *> &DFSI,
353377
const DenseMap<BasicBlock *, SmallVector<Instruction *, 8>>
354378
&GuardsInBlock) {
379+
SmallVector<Value *> ChecksToHoist;
380+
parseWidenableGuard(Instr, ChecksToHoist);
355381
// Ignore trivial true or false conditions. These instructions will be
356382
// trivially eliminated by any cleanup pass. Do not erase them because other
357383
// guards can possibly be widened into them.
358-
if (isa<ConstantInt>(getCondition(Instr)))
384+
if (ChecksToHoist.empty() ||
385+
(ChecksToHoist.size() == 1 && isa<ConstantInt>(ChecksToHoist.front())))
359386
return false;
360387

361388
Instruction *BestSoFar = nullptr;
@@ -391,10 +418,15 @@ bool GuardWideningImpl::eliminateInstrViaWidening(
391418
assert((i == (e - 1)) == (Instr->getParent() == CurBB) && "Bad DFS?");
392419

393420
for (auto *Candidate : make_range(I, E)) {
394-
auto Score = computeWideningScore(Instr, Candidate);
395-
LLVM_DEBUG(dbgs() << "Score between " << *getCondition(Instr)
396-
<< " and " << *getCondition(Candidate) << " is "
397-
<< scoreTypeToString(Score) << "\n");
421+
auto *WideningPoint = findInsertionPointForWideCondition(Candidate);
422+
if (!WideningPoint)
423+
continue;
424+
SmallVector<Value *> CandidateChecks;
425+
parseWidenableGuard(Candidate, CandidateChecks);
426+
auto Score = computeWideningScore(Instr, WideningPoint, ChecksToHoist,
427+
CandidateChecks);
428+
LLVM_DEBUG(dbgs() << "Score between " << *Instr << " and " << *Candidate
429+
<< " is " << scoreTypeToString(Score) << "\n");
398430
if (Score > BestScoreSoFar) {
399431
BestScoreSoFar = Score;
400432
BestSoFar = Candidate;
@@ -413,19 +445,22 @@ bool GuardWideningImpl::eliminateInstrViaWidening(
413445
LLVM_DEBUG(dbgs() << "Widening " << *Instr << " into " << *BestSoFar
414446
<< " with score " << scoreTypeToString(BestScoreSoFar)
415447
<< "\n");
416-
widenGuard(BestSoFar, getCondition(Instr));
448+
SmallVector<Value *> ChecksToWiden;
449+
parseWidenableGuard(BestSoFar, ChecksToWiden);
450+
widenGuard(ChecksToHoist, ChecksToWiden, BestSoFar);
417451
auto NewGuardCondition = ConstantInt::getTrue(Instr->getContext());
418452
setCondition(Instr, NewGuardCondition);
419453
EliminatedGuardsAndBranches.push_back(Instr);
420454
WidenedGuards.insert(BestSoFar);
421455
return true;
422456
}
423457

424-
GuardWideningImpl::WideningScore
425-
GuardWideningImpl::computeWideningScore(Instruction *DominatedInstr,
426-
Instruction *DominatingGuard) {
458+
GuardWideningImpl::WideningScore GuardWideningImpl::computeWideningScore(
459+
Instruction *DominatedInstr, Instruction *WideningPoint,
460+
SmallVectorImpl<Value *> &ChecksToHoist,
461+
SmallVectorImpl<Value *> &ChecksToWiden) {
427462
Loop *DominatedInstrLoop = LI.getLoopFor(DominatedInstr->getParent());
428-
Loop *DominatingGuardLoop = LI.getLoopFor(DominatingGuard->getParent());
463+
Loop *DominatingGuardLoop = LI.getLoopFor(WideningPoint->getParent());
429464
bool HoistingOutOfLoop = false;
430465

431466
if (DominatingGuardLoop != DominatedInstrLoop) {
@@ -438,10 +473,9 @@ GuardWideningImpl::computeWideningScore(Instruction *DominatedInstr,
438473
HoistingOutOfLoop = true;
439474
}
440475

441-
auto *WideningPoint = findInsertionPointForWideCondition(DominatingGuard);
442-
if (!canBeHoistedTo(getCondition(DominatedInstr), WideningPoint))
476+
if (!canBeHoistedTo(ChecksToHoist, WideningPoint))
443477
return WS_IllegalOrNegative;
444-
if (!canBeHoistedTo(getCondition(DominatingGuard), WideningPoint))
478+
if (!canBeHoistedTo(ChecksToWiden, WideningPoint))
445479
return WS_IllegalOrNegative;
446480

447481
// If the guard was conditional executed, it may never be reached
@@ -452,8 +486,7 @@ GuardWideningImpl::computeWideningScore(Instruction *DominatedInstr,
452486
// here. TODO: evaluate cost model for spurious deopt
453487
// NOTE: As written, this also lets us hoist right over another guard which
454488
// is essentially just another spelling for control flow.
455-
if (isWideningCondProfitable(getCondition(DominatedInstr),
456-
getCondition(DominatingGuard)))
489+
if (isWideningCondProfitable(ChecksToHoist, ChecksToWiden))
457490
return HoistingOutOfLoop ? WS_VeryPositive : WS_Positive;
458491

459492
if (HoistingOutOfLoop)
@@ -489,7 +522,7 @@ GuardWideningImpl::computeWideningScore(Instruction *DominatedInstr,
489522
// control flow (guards, calls which throw, etc...). That choice appears
490523
// arbitrary (we assume that implicit control flow exits are all rare).
491524
auto MaybeHoistingToHotterBlock = [&]() {
492-
const auto *DominatingBlock = DominatingGuard->getParent();
525+
const auto *DominatingBlock = WideningPoint->getParent();
493526
const auto *DominatedBlock = DominatedInstr->getParent();
494527

495528
// Descend as low as we can, always taking the likely successor.
@@ -515,7 +548,8 @@ GuardWideningImpl::computeWideningScore(Instruction *DominatedInstr,
515548
if (!DT.dominates(DominatingBlock, DominatedBlock))
516549
return true;
517550
// TODO: diamond, triangle cases
518-
if (!PDT) return true;
551+
if (!PDT)
552+
return true;
519553
return !PDT->dominates(DominatedBlock, DominatingBlock);
520554
};
521555

@@ -661,9 +695,10 @@ Value *GuardWideningImpl::freezeAndPush(Value *Orig, Instruction *InsertPt) {
661695
return Result;
662696
}
663697

664-
std::optional<Value *> GuardWideningImpl::mergeChecks(Value *Cond0,
665-
Value *Cond1,
666-
Instruction *InsertPt) {
698+
std::optional<Value *>
699+
GuardWideningImpl::mergeChecks(SmallVectorImpl<Value *> &ChecksToHoist,
700+
SmallVectorImpl<Value *> &ChecksToWiden,
701+
Instruction *InsertPt) {
667702
using namespace llvm::PatternMatch;
668703

669704
Value *Result = nullptr;
@@ -672,8 +707,13 @@ std::optional<Value *> GuardWideningImpl::mergeChecks(Value *Cond0,
672707
ConstantInt *RHS0, *RHS1;
673708
Value *LHS;
674709
ICmpInst::Predicate Pred0, Pred1;
675-
if (match(Cond0, m_ICmp(Pred0, m_Value(LHS), m_ConstantInt(RHS0))) &&
676-
match(Cond1, m_ICmp(Pred1, m_Specific(LHS), m_ConstantInt(RHS1)))) {
710+
// TODO: Support searching for pairs to merge from both whole lists of
711+
// ChecksToHoist and ChecksToWiden.
712+
if (ChecksToWiden.size() == 1 && ChecksToHoist.size() == 1 &&
713+
match(ChecksToWiden.front(),
714+
m_ICmp(Pred0, m_Value(LHS), m_ConstantInt(RHS0))) &&
715+
match(ChecksToHoist.front(),
716+
m_ICmp(Pred1, m_Specific(LHS), m_ConstantInt(RHS1)))) {
677717

678718
ConstantRange CR0 =
679719
ConstantRange::makeExactICmpRegion(Pred0, RHS0->getValue());
@@ -690,7 +730,7 @@ std::optional<Value *> GuardWideningImpl::mergeChecks(Value *Cond0,
690730
if (Intersect->getEquivalentICmp(Pred, NewRHSAP)) {
691731
if (InsertPt) {
692732
ConstantInt *NewRHS =
693-
ConstantInt::get(Cond0->getContext(), NewRHSAP);
733+
ConstantInt::get(InsertPt->getContext(), NewRHSAP);
694734
assert(canBeHoistedTo(LHS, InsertPt) && "must be");
695735
makeAvailableAt(LHS, InsertPt);
696736
Result = new ICmpInst(InsertPt, Pred, LHS, NewRHS, "wide.chk");
@@ -703,7 +743,8 @@ std::optional<Value *> GuardWideningImpl::mergeChecks(Value *Cond0,
703743

704744
{
705745
SmallVector<GuardWideningImpl::RangeCheck, 4> Checks, CombinedChecks;
706-
if (parseRangeChecks(Cond0, Checks) && parseRangeChecks(Cond1, Checks) &&
746+
if (parseRangeChecks(ChecksToWiden, Checks) &&
747+
parseRangeChecks(ChecksToHoist, Checks) &&
707748
combineRangeChecks(Checks, CombinedChecks)) {
708749
if (InsertPt) {
709750
for (auto &RC : CombinedChecks) {
@@ -721,33 +762,29 @@ std::optional<Value *> GuardWideningImpl::mergeChecks(Value *Cond0,
721762
return Result;
722763
}
723764
}
724-
// We were not able to compute Cond0 AND Cond1 for the price of one.
765+
// We were not able to compute ChecksToHoist AND ChecksToWiden for the price
766+
// of one.
725767
return std::nullopt;
726768
}
727769

728-
Value *GuardWideningImpl::hoistChecks(Value *Cond0, Value *Cond1,
770+
Value *GuardWideningImpl::hoistChecks(SmallVectorImpl<Value *> &ChecksToHoist,
771+
Value *OldCondition,
729772
Instruction *InsertPt) {
730-
makeAvailableAt(Cond0, InsertPt);
731-
makeAvailableAt(Cond1, InsertPt);
732-
Cond1 = freezeAndPush(Cond1, InsertPt);
733-
return BinaryOperator::CreateAnd(Cond0, Cond1, "wide.chk", InsertPt);
773+
assert(!ChecksToHoist.empty());
774+
IRBuilder<> Builder(InsertPt);
775+
makeAvailableAt(ChecksToHoist, InsertPt);
776+
makeAvailableAt(OldCondition, InsertPt);
777+
Value *Result = Builder.CreateAnd(ChecksToHoist);
778+
Result = freezeAndPush(Result, InsertPt);
779+
Result = Builder.CreateAnd(OldCondition, Result);
780+
Result->setName("wide.chk");
781+
return Result;
734782
}
735783

736784
bool GuardWideningImpl::parseRangeChecks(
737-
Value *CheckCond, SmallVectorImpl<GuardWideningImpl::RangeCheck> &Checks,
738-
SmallPtrSetImpl<const Value *> &Visited) {
739-
if (!Visited.insert(CheckCond).second)
740-
return true;
741-
785+
Value *CheckCond, SmallVectorImpl<GuardWideningImpl::RangeCheck> &Checks) {
742786
using namespace llvm::PatternMatch;
743787

744-
{
745-
Value *AndLHS, *AndRHS;
746-
if (match(CheckCond, m_And(m_Value(AndLHS), m_Value(AndRHS))))
747-
return parseRangeChecks(AndLHS, Checks) &&
748-
parseRangeChecks(AndRHS, Checks);
749-
}
750-
751788
auto *IC = dyn_cast<ICmpInst>(CheckCond);
752789
if (!IC || !IC->getOperand(0)->getType()->isIntegerTy() ||
753790
(IC->getPredicate() != ICmpInst::ICMP_ULT &&

llvm/test/Transforms/GuardWidening/range-check-merging.ll

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -292,23 +292,24 @@ define void @f_7(i32 %x, ptr %length_buf) {
292292
; CHECK-NEXT: [[CHK0_B:%.*]] = icmp ult i32 [[X_GW_FR]], [[LENGTH_B]]
293293
; CHECK-NEXT: [[CHK0:%.*]] = and i1 [[CHK0_A]], [[CHK0_B]]
294294
; CHECK-NEXT: [[X_INC1:%.*]] = add i32 [[X_GW_FR]], 1
295-
; CHECK-NEXT: [[CHK1_A:%.*]] = icmp ult i32 [[X_INC1]], [[LENGTH_A]]
296295
; CHECK-NEXT: [[CHK1_B:%.*]] = icmp ult i32 [[X_INC1]], [[LENGTH_B]]
297-
; CHECK-NEXT: [[CHK1:%.*]] = and i1 [[CHK1_A]], [[CHK1_B]]
298-
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[CHK0]], [[CHK1]]
296+
; CHECK-NEXT: [[CHK1_A:%.*]] = icmp ult i32 [[X_INC1]], [[LENGTH_A]]
297+
; CHECK-NEXT: [[TMP0:%.*]] = and i1 [[CHK1_B]], [[CHK1_A]]
298+
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[CHK0]], [[TMP0]]
299299
; CHECK-NEXT: [[X_INC2:%.*]] = add i32 [[X_GW_FR]], 2
300300
; CHECK-NEXT: [[CHK2_A:%.*]] = icmp ult i32 [[X_INC2]], [[LENGTH_A]]
301-
; CHECK-NEXT: [[TMP0:%.*]] = and i1 [[CHK2_A]], [[CHK0_A]]
302-
; CHECK-NEXT: [[TMP1:%.*]] = and i1 [[CHK0_B]], [[TMP0]]
301+
; CHECK-NEXT: [[TMP1:%.*]] = and i1 [[CHK2_A]], [[CHK0_A]]
302+
; CHECK-NEXT: [[TMP2:%.*]] = and i1 [[CHK0_B]], [[TMP1]]
303303
; CHECK-NEXT: [[CHK2_B:%.*]] = icmp ult i32 [[X_INC2]], [[LENGTH_B]]
304-
; CHECK-NEXT: [[WIDE_CHK1:%.*]] = and i1 [[CHK2_B]], [[TMP1]]
304+
; CHECK-NEXT: [[WIDE_CHK1:%.*]] = and i1 [[CHK2_B]], [[TMP2]]
305305
; CHECK-NEXT: [[X_INC3:%.*]] = add i32 [[X_GW_FR]], 3
306-
; CHECK-NEXT: [[CHK3_B:%.*]] = icmp ult i32 [[X_INC3]], [[LENGTH_B]]
307-
; CHECK-NEXT: [[TMP2:%.*]] = and i1 [[CHK3_B]], [[CHK0_B]]
308-
; CHECK-NEXT: [[TMP3:%.*]] = and i1 [[CHK0_A]], [[TMP2]]
309306
; CHECK-NEXT: [[CHK3_A:%.*]] = icmp ult i32 [[X_INC3]], [[LENGTH_A]]
310-
; CHECK-NEXT: [[WIDE_CHK2:%.*]] = and i1 [[CHK3_A]], [[TMP3]]
307+
; CHECK-NEXT: [[TMP3:%.*]] = and i1 [[CHK3_A]], [[CHK0_A]]
308+
; CHECK-NEXT: [[TMP4:%.*]] = and i1 [[CHK0_B]], [[TMP3]]
309+
; CHECK-NEXT: [[CHK3_B:%.*]] = icmp ult i32 [[X_INC3]], [[LENGTH_B]]
310+
; CHECK-NEXT: [[WIDE_CHK2:%.*]] = and i1 [[CHK3_B]], [[TMP4]]
311311
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK2]]) [ "deopt"() ]
312+
; CHECK-NEXT: [[CHK1:%.*]] = and i1 [[CHK1_A]], [[CHK1_B]]
312313
; CHECK-NEXT: [[CHK2:%.*]] = and i1 [[CHK2_A]], [[CHK2_B]]
313314
; CHECK-NEXT: [[CHK3:%.*]] = and i1 [[CHK3_A]], [[CHK3_B]]
314315
; CHECK-NEXT: ret void

0 commit comments

Comments
 (0)