Skip to content

Commit 8b0d763

Browse files
author
Alex Zhikhartsev
committed
[DFAJumpThreading] Relax analysis to handle unpredictable initial values
Responding to a feature request from the Rust community: rust-lang/rust#80630 void foo(X) { for (...) switch (X) case A X = B case B X = C } Even though the initial switch value is non-constant, the switch statement can still be threaded: the initial value will hit the switch statement but the rest of the state changes will proceed by jumping unconditionally. The early predictability check is relaxed to allow unpredictable values anywhere, but later, after the paths through the switch statement have been enumerated, no non-constant state values are allowed along the paths. Any state value not along a path will be an initial switch value, which can be safely ignored. Differential Revision: https://reviews.llvm.org/D124394
1 parent 9c66ed9 commit 8b0d763

File tree

3 files changed

+181
-63
lines changed

3 files changed

+181
-63
lines changed

llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp

+67-63
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ static cl::opt<unsigned> MaxPathLength(
103103
cl::desc("Max number of blocks searched to find a threading path"),
104104
cl::Hidden, cl::init(20));
105105

106+
static cl::opt<unsigned> MaxNumPaths(
107+
"dfa-max-num-paths",
108+
cl::desc("Max number of paths enumerated around a switch"),
109+
cl::Hidden, cl::init(200));
110+
106111
static cl::opt<unsigned>
107112
CostThreshold("dfa-cost-threshold",
108113
cl::desc("Maximum cost accepted for the transformation"),
@@ -415,7 +420,7 @@ inline raw_ostream &operator<<(raw_ostream &OS, const ThreadingPath &TPath) {
415420

416421
struct MainSwitch {
417422
MainSwitch(SwitchInst *SI, OptimizationRemarkEmitter *ORE) {
418-
if (isPredictable(SI)) {
423+
if (isCandidate(SI)) {
419424
Instr = SI;
420425
} else {
421426
ORE->emit([&]() {
@@ -433,83 +438,60 @@ struct MainSwitch {
433438
}
434439

435440
private:
436-
/// Do a use-def chain traversal. Make sure the value of the switch variable
437-
/// is always a known constant. This means that all conditional jumps based on
438-
/// switch variable can be converted to unconditional jumps.
439-
bool isPredictable(const SwitchInst *SI) {
440-
std::deque<Instruction *> Q;
441+
/// Do a use-def chain traversal starting from the switch condition to see if
442+
/// \p SI is a potential condidate.
443+
///
444+
/// Also, collect select instructions to unfold.
445+
bool isCandidate(const SwitchInst *SI) {
446+
std::deque<Value *> Q;
441447
SmallSet<Value *, 16> SeenValues;
442448
SelectInsts.clear();
443449

444-
Value *FirstDef = SI->getOperand(0);
445-
auto *Inst = dyn_cast<Instruction>(FirstDef);
446-
447-
// If this is a function argument or another non-instruction, then give up.
448-
// We are interested in loop local variables.
449-
if (!Inst)
450+
Value *SICond = SI->getCondition();
451+
LLVM_DEBUG(dbgs() << "\tSICond: " << *SICond << "\n");
452+
if (!isa<PHINode>(SICond))
450453
return false;
451454

452-
// Require the first definition to be a PHINode
453-
if (!isa<PHINode>(Inst))
454-
return false;
455-
456-
LLVM_DEBUG(dbgs() << "\tisPredictable() FirstDef: " << *Inst << "\n");
457-
458-
Q.push_back(Inst);
459-
SeenValues.insert(FirstDef);
455+
addToQueue(SICond, Q, SeenValues);
460456

461457
while (!Q.empty()) {
462-
Instruction *Current = Q.front();
458+
Value *Current = Q.front();
463459
Q.pop_front();
464460

465461
if (auto *Phi = dyn_cast<PHINode>(Current)) {
466462
for (Value *Incoming : Phi->incoming_values()) {
467-
if (!isPredictableValue(Incoming, SeenValues))
468-
return false;
469-
addInstToQueue(Incoming, Q, SeenValues);
463+
addToQueue(Incoming, Q, SeenValues);
470464
}
471-
LLVM_DEBUG(dbgs() << "\tisPredictable() phi: " << *Phi << "\n");
465+
LLVM_DEBUG(dbgs() << "\tphi: " << *Phi << "\n");
472466
} else if (SelectInst *SelI = dyn_cast<SelectInst>(Current)) {
473467
if (!isValidSelectInst(SelI))
474468
return false;
475-
if (!isPredictableValue(SelI->getTrueValue(), SeenValues) ||
476-
!isPredictableValue(SelI->getFalseValue(), SeenValues)) {
477-
return false;
478-
}
479-
addInstToQueue(SelI->getTrueValue(), Q, SeenValues);
480-
addInstToQueue(SelI->getFalseValue(), Q, SeenValues);
481-
LLVM_DEBUG(dbgs() << "\tisPredictable() select: " << *SelI << "\n");
469+
addToQueue(SelI->getTrueValue(), Q, SeenValues);
470+
addToQueue(SelI->getFalseValue(), Q, SeenValues);
471+
LLVM_DEBUG(dbgs() << "\tselect: " << *SelI << "\n");
482472
if (auto *SelIUse = dyn_cast<PHINode>(SelI->user_back()))
483473
SelectInsts.push_back(SelectInstToUnfold(SelI, SelIUse));
474+
} else if (isa<Constant>(Current)) {
475+
LLVM_DEBUG(dbgs() << "\tconst: " << *Current << "\n");
476+
continue;
484477
} else {
485-
// If it is neither a phi nor a select, then we give up.
486-
return false;
478+
LLVM_DEBUG(dbgs() << "\tother: " << *Current << "\n");
479+
// Allow unpredictable values. The hope is that those will be the
480+
// initial switch values that can be ignored (they will hit the
481+
// unthreaded switch) but this assumption will get checked later after
482+
// paths have been enumerated (in function getStateDefMap).
483+
continue;
487484
}
488485
}
489486

490487
return true;
491488
}
492489

493-
bool isPredictableValue(Value *InpVal, SmallSet<Value *, 16> &SeenValues) {
494-
if (SeenValues.contains(InpVal))
495-
return true;
496-
497-
if (isa<ConstantInt>(InpVal))
498-
return true;
499-
500-
// If this is a function argument or another non-instruction, then give up.
501-
if (!isa<Instruction>(InpVal))
502-
return false;
503-
504-
return true;
505-
}
506-
507-
void addInstToQueue(Value *Val, std::deque<Instruction *> &Q,
508-
SmallSet<Value *, 16> &SeenValues) {
490+
void addToQueue(Value *Val, std::deque<Value *> &Q,
491+
SmallSet<Value *, 16> &SeenValues) {
509492
if (SeenValues.contains(Val))
510493
return;
511-
if (Instruction *I = dyn_cast<Instruction>(Val))
512-
Q.push_back(I);
494+
Q.push_back(Val);
513495
SeenValues.insert(Val);
514496
}
515497

@@ -563,7 +545,16 @@ struct AllSwitchPaths {
563545
void run() {
564546
VisitedBlocks Visited;
565547
PathsType LoopPaths = paths(SwitchBlock, Visited, /* PathDepth = */ 1);
566-
StateDefMap StateDef = getStateDefMap();
548+
StateDefMap StateDef = getStateDefMap(LoopPaths);
549+
550+
if (StateDef.empty()) {
551+
ORE->emit([&]() {
552+
return OptimizationRemarkMissed(DEBUG_TYPE, "SwitchNotPredictable",
553+
Switch)
554+
<< "Switch instruction is not predictable.";
555+
});
556+
return;
557+
}
567558

568559
for (PathType Path : LoopPaths) {
569560
ThreadingPath TPath;
@@ -638,6 +629,9 @@ struct AllSwitchPaths {
638629
PathType NewPath(Path);
639630
NewPath.push_front(BB);
640631
Res.push_back(NewPath);
632+
if (Res.size() >= MaxNumPaths) {
633+
return Res;
634+
}
641635
}
642636
}
643637
// This block could now be visited again from a different predecessor. Note
@@ -648,14 +642,22 @@ struct AllSwitchPaths {
648642
}
649643

650644
/// Walk the use-def chain and collect all the state-defining instructions.
651-
StateDefMap getStateDefMap() const {
645+
///
646+
/// Return an empty map if unpredictable values encountered inside the basic
647+
/// blocks of \p LoopPaths.
648+
StateDefMap getStateDefMap(const PathsType &LoopPaths) const {
652649
StateDefMap Res;
653650

651+
// Basic blocks belonging to any of the loops around the switch statement.
652+
SmallPtrSet<BasicBlock *, 16> LoopBBs;
653+
for (const PathType &Path : LoopPaths) {
654+
for (BasicBlock *BB : Path)
655+
LoopBBs.insert(BB);
656+
}
657+
654658
Value *FirstDef = Switch->getOperand(0);
655659

656-
assert(isa<PHINode>(FirstDef) && "After select unfolding, all state "
657-
"definitions are expected to be phi "
658-
"nodes.");
660+
assert(isa<PHINode>(FirstDef) && "The first definition must be a phi.");
659661

660662
SmallVector<PHINode *, 8> Stack;
661663
Stack.push_back(dyn_cast<PHINode>(FirstDef));
@@ -667,15 +669,17 @@ struct AllSwitchPaths {
667669
Res[CurPhi->getParent()] = CurPhi;
668670
SeenValues.insert(CurPhi);
669671

670-
for (Value *Incoming : CurPhi->incoming_values()) {
672+
for (BasicBlock *IncomingBB : CurPhi->blocks()) {
673+
Value *Incoming = CurPhi->getIncomingValueForBlock(IncomingBB);
674+
bool IsOutsideLoops = LoopBBs.count(IncomingBB) == 0;
671675
if (Incoming == FirstDef || isa<ConstantInt>(Incoming) ||
672-
SeenValues.contains(Incoming)) {
676+
SeenValues.contains(Incoming) || IsOutsideLoops) {
673677
continue;
674678
}
675679

676-
assert(isa<PHINode>(Incoming) && "After select unfolding, all state "
677-
"definitions are expected to be phi "
678-
"nodes.");
680+
// Any unpredictable value inside the loops means we must bail out.
681+
if (!isa<PHINode>(Incoming))
682+
return StateDefMap();
679683

680684
Stack.push_back(cast<PHINode>(Incoming));
681685
}
@@ -1279,7 +1283,7 @@ bool DFAJumpThreading::run(Function &F) {
12791283
continue;
12801284

12811285
LLVM_DEBUG(dbgs() << "\nCheck if SwitchInst in BB " << BB.getName()
1282-
<< " is predictable\n");
1286+
<< " is a candidate\n");
12831287
MainSwitch Switch(SI, ORE);
12841288

12851289
if (!Switch.getInstr())

llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll

+65
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,68 @@ bb49: ; preds = %bb43, %bb43
184184
bb66: ; preds = %bb59
185185
ret i32 0
186186
}
187+
188+
; Value %init is not predictable but it's okay since it is the value initial to the switch.
189+
define i32 @initial.value.positive1(i32 %init) {
190+
; CHECK: < loop.3 case2 > [ 3, loop.3 ]
191+
; CHECK-NEXT: < loop.3 case2 loop.1.backedge loop.1 loop.2 > [ 1, loop.1 ]
192+
; CHECK-NEXT: < loop.3 case2 loop.1.backedge si.unfold.false loop.1 loop.2 > [ 4, loop.1.backedge ]
193+
; CHECK-NEXT: < loop.3 case3 loop.2.backedge loop.2 > [ 0, loop.2.backedge ]
194+
; CHECK-NEXT: < loop.3 case3 case4 loop.2.backedge loop.2 > [ 3, loop.2.backedge ]
195+
; CHECK-NEXT: < loop.3 case3 case4 loop.1.backedge loop.1 loop.2 > [ 1, loop.1 ]
196+
; CHECK-NEXT: < loop.3 case3 case4 loop.1.backedge si.unfold.false loop.1 loop.2 > [ 2, loop.1.backedge ]
197+
; CHECK-NEXT: < loop.3 case4 loop.2.backedge loop.2 > [ 3, loop.2.backedge ]
198+
; CHECK-NEXT: < loop.3 case4 loop.1.backedge loop.1 loop.2 > [ 1, loop.1 ]
199+
; CHECK-NEXT: < loop.3 case4 loop.1.backedge si.unfold.false loop.1 loop.2 > [ 2, loop.1.backedge ]
200+
entry:
201+
%cmp = icmp eq i32 %init, 0
202+
br label %loop.1
203+
204+
loop.1:
205+
%state.1 = phi i32 [ %init, %entry ], [ %state.1.be2, %loop.1.backedge ]
206+
br label %loop.2
207+
208+
loop.2:
209+
%state.2 = phi i32 [ %state.1, %loop.1 ], [ %state.2.be, %loop.2.backedge ]
210+
br label %loop.3
211+
212+
loop.3:
213+
%state = phi i32 [ %state.2, %loop.2 ], [ 3, %case2 ]
214+
switch i32 %state, label %infloop.i [
215+
i32 2, label %case2
216+
i32 3, label %case3
217+
i32 4, label %case4
218+
i32 0, label %case0
219+
i32 1, label %case1
220+
]
221+
222+
case2:
223+
br i1 %cmp, label %loop.3, label %loop.1.backedge
224+
225+
case3:
226+
br i1 %cmp, label %loop.2.backedge, label %case4
227+
228+
case4:
229+
br i1 %cmp, label %loop.2.backedge, label %loop.1.backedge
230+
231+
loop.1.backedge:
232+
%state.1.be = phi i32 [ 2, %case4 ], [ 4, %case2 ]
233+
%state.1.be2 = select i1 %cmp, i32 1, i32 %state.1.be
234+
br label %loop.1
235+
236+
loop.2.backedge:
237+
%state.2.be = phi i32 [ 3, %case4 ], [ 0, %case3 ]
238+
br label %loop.2
239+
240+
case0:
241+
br label %exit
242+
243+
case1:
244+
br label %exit
245+
246+
infloop.i:
247+
br label %infloop.i
248+
249+
exit:
250+
ret i32 0
251+
}

llvm/test/Transforms/DFAJumpThreading/negative.ll

+49
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,52 @@ for.inc:
214214
for.end:
215215
ret i32 0
216216
}
217+
218+
declare i32 @arbitrary_function()
219+
220+
; Don't confuse %state.2 for the initial switch value.
221+
define i32 @negative6(i32 %init) {
222+
; REMARK: SwitchNotPredictable
223+
; REMARK-NEXT: negative6
224+
entry:
225+
%cmp = icmp eq i32 %init, 0
226+
br label %loop.2
227+
228+
loop.2:
229+
%state.2 = call i32 @arbitrary_function()
230+
br label %loop.3
231+
232+
loop.3:
233+
%state = phi i32 [ %state.2, %loop.2 ], [ 3, %case2 ]
234+
switch i32 %state, label %infloop.i [
235+
i32 2, label %case2
236+
i32 3, label %case3
237+
i32 4, label %case4
238+
i32 0, label %case0
239+
i32 1, label %case1
240+
]
241+
242+
case2:
243+
br label %loop.3
244+
245+
case3:
246+
br i1 %cmp, label %loop.2.backedge, label %case4
247+
248+
case4:
249+
br label %loop.2.backedge
250+
251+
loop.2.backedge:
252+
br label %loop.2
253+
254+
case0:
255+
br label %exit
256+
257+
case1:
258+
br label %exit
259+
260+
infloop.i:
261+
br label %infloop.i
262+
263+
exit:
264+
ret i32 0
265+
}

0 commit comments

Comments
 (0)