Skip to content

Commit 5b9f963

Browse files
Kenogbaraldi
authored andcommitted
[IRCE] Check loop clone legality before attempting to do so
Not all loop bodies are safe to clone. For example, they may have `noduplicate` call in them. Additionally, if the transformation may cause any of the cloned loop bodies to execute conditionally, we must also disallow `convergent` calls, as well as any call that returns a token that is used outside of the loop. The loop unswitching pass had an appropriate legality check for this, but IRCE did not, causing issues downstream (JuliaLang/julia#48918). As a particular example, consider the test case in this commit, where `opt -passes=irce`, creates invalid IR: ``` opt --passes=irce llvm/test/Transforms/IRCE/pre_post_loops_clone.ll WARNING: You're attempting to print out a bitcode file. This is inadvisable as it may cause display problems. If you REALLY want to taste LLVM bitcode first-hand, you can force output with the `-f' option. Instruction does not dominate all uses! %token = call token @llvm.source_token() call void @llvm.sink_token(token %token) Instruction does not dominate all uses! %token = call token @llvm.source_token() call void @llvm.sink_token(token %token) LLVM ERROR: Broken module found, compilation aborted! ``` Fix this by factoring out the legality check from the loop unswitch pass and using it in IRCE as well. I personally don't have any code that is `noduplicate` or `convergent`, but I don't see any reason why IRCE would be different here. I will also mention llvm#56243 as related, which contains some related discussion of LCSSA in the presence of tokens. Fixes llvm#63984
1 parent ee44f10 commit 5b9f963

File tree

5 files changed

+123
-28
lines changed

5 files changed

+123
-28
lines changed

llvm/include/llvm/Analysis/LoopInfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,12 @@ class LLVM_ABI Loop : public LoopBase<BasicBlock, Loop> {
326326
/// Return true if the loop body is safe to clone in practice.
327327
bool isSafeToClone() const;
328328

329+
/// Like `isSafeToClone`, but also checks whether we may form phis for all
330+
/// values that are live-out from the loop. This is required if either of
331+
/// the cloned loop bodies may run conditionally.
332+
bool isSafeToCloneConditionally(const DominatorTree &DT,
333+
bool AllowConvergent = false) const;
334+
329335
/// Returns true if the loop is annotated parallel.
330336
///
331337
/// A parallel loop can be assumed to not contain any dependencies between

llvm/lib/Analysis/LoopInfo.cpp

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,27 @@ bool Loop::isCanonical(ScalarEvolution &SE) const {
428428
return true;
429429
}
430430

431+
static bool loopContainsUser(const Loop &L, const BasicBlock &BB, const Use &U,
432+
const DominatorTree &DT) {
433+
const Instruction *UI = cast<Instruction>(U.getUser());
434+
const BasicBlock *UserBB = UI->getParent();
435+
436+
// For practical purposes, we consider that the use in a PHI
437+
// occurs in the respective predecessor block. For more info,
438+
// see the `phi` doc in LangRef and the LCSSA doc.
439+
if (const PHINode *P = dyn_cast<PHINode>(UI))
440+
UserBB = P->getIncomingBlock(U);
441+
442+
// Check the current block, as a fast-path, before checking whether
443+
// the use is anywhere in the loop. Most values are used in the same
444+
// block they are defined in. Also, blocks not reachable from the
445+
// entry are special; uses in them don't need to go through PHIs.
446+
if (UserBB != &BB && !L.contains(UserBB) && DT.isReachableFromEntry(UserBB))
447+
return false;
448+
449+
return true;
450+
}
451+
431452
// Check that 'BB' doesn't have any uses outside of the 'L'
432453
static bool isBlockInLCSSAForm(const Loop &L, const BasicBlock &BB,
433454
const DominatorTree &DT, bool IgnoreTokens) {
@@ -439,21 +460,7 @@ static bool isBlockInLCSSAForm(const Loop &L, const BasicBlock &BB,
439460
continue;
440461

441462
for (const Use &U : I.uses()) {
442-
const Instruction *UI = cast<Instruction>(U.getUser());
443-
const BasicBlock *UserBB = UI->getParent();
444-
445-
// For practical purposes, we consider that the use in a PHI
446-
// occurs in the respective predecessor block. For more info,
447-
// see the `phi` doc in LangRef and the LCSSA doc.
448-
if (const PHINode *P = dyn_cast<PHINode>(UI))
449-
UserBB = P->getIncomingBlock(U);
450-
451-
// Check the current block, as a fast-path, before checking whether
452-
// the use is anywhere in the loop. Most values are used in the same
453-
// block they are defined in. Also, blocks not reachable from the
454-
// entry are special; uses in them don't need to go through PHIs.
455-
if (UserBB != &BB && !L.contains(UserBB) &&
456-
DT.isReachableFromEntry(UserBB))
463+
if (!loopContainsUser(L, BB, U, DT))
457464
return false;
458465
}
459466
}
@@ -499,6 +506,29 @@ bool Loop::isSafeToClone() const {
499506
return true;
500507
}
501508

509+
bool Loop::isSafeToCloneConditionally(const DominatorTree &DT,
510+
bool AllowConvergent) const {
511+
// Return false if any loop blocks contain indirectbrs, or there are any calls
512+
// to noduplicate functions.
513+
for (BasicBlock *BB : this->blocks()) {
514+
if (isa<IndirectBrInst>(BB->getTerminator()))
515+
return false;
516+
517+
for (Instruction &I : *BB) {
518+
if (I.getType()->isTokenTy()) {
519+
for (const Use &U : I.uses()) {
520+
if (!loopContainsUser(*this, *BB, U, DT))
521+
return false;
522+
}
523+
}
524+
if (auto *CB = dyn_cast<CallBase>(&I))
525+
if (CB->cannotDuplicate() || (AllowConvergent || CB->isConvergent()))
526+
return false;
527+
}
528+
}
529+
return true;
530+
}
531+
502532
MDNode *Loop::getLoopID() const {
503533
MDNode *LoopID = nullptr;
504534

llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3271,19 +3271,10 @@ static bool collectUnswitchCandidatesWithInjections(
32713271
return Found;
32723272
}
32733273

3274-
static bool isSafeForNoNTrivialUnswitching(Loop &L, LoopInfo &LI) {
3275-
if (!L.isSafeToClone())
3274+
static bool isSafeForNoNTrivialUnswitching(const DominatorTree &DT, Loop &L,
3275+
LoopInfo &LI) {
3276+
if (!L.isSafeToCloneConditionally(DT))
32763277
return false;
3277-
for (auto *BB : L.blocks())
3278-
for (auto &I : *BB) {
3279-
if (I.getType()->isTokenTy() && I.isUsedOutsideOfBlock(BB))
3280-
return false;
3281-
if (auto *CB = dyn_cast<CallBase>(&I)) {
3282-
assert(!CB->cannotDuplicate() && "Checked by L.isSafeToClone().");
3283-
if (CB->isConvergent())
3284-
return false;
3285-
}
3286-
}
32873278

32883279
// Check if there are irreducible CFG cycles in this loop. If so, we cannot
32893280
// easily unswitch non-trivial edges out of the loop. Doing so might turn the
@@ -3661,7 +3652,7 @@ static bool unswitchLoop(Loop &L, DominatorTree &DT, LoopInfo &LI,
36613652
}
36623653

36633654
// Perform legality checks.
3664-
if (!isSafeForNoNTrivialUnswitching(L, LI))
3655+
if (!isSafeForNoNTrivialUnswitching(DT, L, LI))
36653656
return false;
36663657

36673658
// For non-trivial unswitching, because it often creates new loops, we rely on

llvm/lib/Transforms/Utils/LoopConstrainer.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,13 @@ bool LoopConstrainer::run() {
750750
const SCEVConstant *MinusOneS =
751751
cast<SCEVConstant>(SE.getConstant(IVTy, -1, true /* isSigned */));
752752

753+
if ((NeedsPreLoop || NeedsPostLoop) &&
754+
!OriginalLoop.isSafeToCloneConditionally(DT)) {
755+
LLVM_DEBUG(dbgs() << "cannot clone loop " << OriginalLoop.getName()
756+
<< " because it is not safe to clone.\n");
757+
return false;
758+
}
759+
753760
if (NeedsPreLoop) {
754761
const SCEV *ExitPreLoopAtSCEV = nullptr;
755762

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
; RUN: opt -verify-loop-info -irce-print-changed-loops -passes=irce -S < %s 2>&1 | FileCheck %s
2+
; This test is the same as pre_post_loops.ll, except that the loop body contains a token-generating
3+
; call. We need to ensure that IRCE does not try to introduce a PHI or otherwise generate invalid IE.
4+
5+
declare token @llvm.source_token()
6+
declare void @llvm.sink_token(token)
7+
8+
; CHECK: define void @test_01
9+
define void @test_01(ptr %arr, ptr %a_len_ptr) {
10+
entry:
11+
%len = load i32, ptr %a_len_ptr, !range !0
12+
br label %loop
13+
14+
loop:
15+
%idx = phi i32 [ 0, %entry ], [ %idx.next, %in.bounds ]
16+
%idx.next = add i32 %idx, 1
17+
%abc = icmp slt i32 %idx, %len
18+
%token = call token @llvm.source_token()
19+
br i1 %abc, label %in.bounds, label %out.of.bounds
20+
21+
in.bounds:
22+
%addr = getelementptr i32, ptr %arr, i32 %idx
23+
store i32 0, ptr %addr
24+
%next = icmp slt i32 %idx.next, 2147483647
25+
br i1 %next, label %loop, label %exit
26+
27+
out.of.bounds:
28+
ret void
29+
30+
exit:
31+
call void @llvm.sink_token(token %token)
32+
ret void
33+
}
34+
35+
define void @test_02(ptr %arr, ptr %a_len_ptr) {
36+
entry:
37+
%len = load i32, ptr %a_len_ptr, !range !0
38+
br label %loop
39+
40+
loop:
41+
%idx = phi i32 [ 2147483647, %entry ], [ %idx.next, %in.bounds ]
42+
%idx.next = add i32 %idx, -1
43+
%abc = icmp slt i32 %idx, %len
44+
%token = call token @llvm.source_token()
45+
br i1 %abc, label %in.bounds, label %out.of.bounds
46+
47+
in.bounds:
48+
%addr = getelementptr i32, ptr %arr, i32 %idx
49+
store i32 0, ptr %addr
50+
%next = icmp sgt i32 %idx.next, -1
51+
br i1 %next, label %loop, label %exit
52+
53+
out.of.bounds:
54+
ret void
55+
56+
exit:
57+
call void @llvm.sink_token(token %token)
58+
ret void
59+
}
60+
61+
!0 = !{i32 0, i32 50}

0 commit comments

Comments
 (0)