Skip to content

Commit 585631e

Browse files
[Utils] Consolidate LockstepReverseIterator into own header (NFC)
Common code has been unified and generalized.
1 parent b7d635e commit 585631e

File tree

3 files changed

+171
-163
lines changed

3 files changed

+171
-163
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
//===- LockstepReverseIterator.h ------------------------------*- C++ -*---===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_TRANSFORMS_UTILS_LOCKSTEPREVERSEITERATOR_H
10+
#define LLVM_TRANSFORMS_UTILS_LOCKSTEPREVERSEITERATOR_H
11+
12+
#include "llvm/ADT/ArrayRef.h"
13+
#include "llvm/ADT/SetVector.h"
14+
#include "llvm/ADT/SmallVector.h"
15+
#include "llvm/IR/BasicBlock.h"
16+
#include "llvm/IR/Instruction.h"
17+
18+
namespace llvm {
19+
20+
struct NoActiveBlocksOption {
21+
template <typename... Args> NoActiveBlocksOption(Args...) {}
22+
};
23+
24+
struct ActiveBlocksOption {
25+
protected:
26+
SmallSetVector<BasicBlock *, 4> ActiveBlocks;
27+
28+
public:
29+
ActiveBlocksOption() = default;
30+
};
31+
32+
/// Iterates through instructions in a set of blocks in reverse order from the
33+
/// first non-terminator. For example (assume all blocks have size n):
34+
/// LockstepReverseIterator I([B1, B2, B3]);
35+
/// *I-- = [B1[n], B2[n], B3[n]];
36+
/// *I-- = [B1[n-1], B2[n-1], B3[n-1]];
37+
/// *I-- = [B1[n-2], B2[n-2], B3[n-2]];
38+
/// ...
39+
///
40+
/// The iterator continues processing until all blocks have been exhausted if \p
41+
/// EarlyFailure is explicitly set to \c false. Use \c getActiveBlocks() to
42+
/// determine which blocks are still going and the order they appear in the list
43+
/// returned by operator*.
44+
template <bool EarlyFailure = true>
45+
class LockstepReverseIterator
46+
: public std::conditional_t<EarlyFailure, NoActiveBlocksOption,
47+
ActiveBlocksOption> {
48+
private:
49+
using BasicBlockT = BasicBlock;
50+
using InstructionT = Instruction;
51+
52+
ArrayRef<BasicBlockT *> Blocks;
53+
SmallVector<InstructionT *, 4> Insts;
54+
bool Fail;
55+
56+
public:
57+
LockstepReverseIterator(ArrayRef<BasicBlockT *> Blocks) : Blocks(Blocks) {
58+
reset();
59+
}
60+
61+
void reset() {
62+
Fail = false;
63+
if constexpr (!EarlyFailure) {
64+
this->ActiveBlocks.clear();
65+
for (BasicBlockT *BB : Blocks)
66+
this->ActiveBlocks.insert(BB);
67+
}
68+
Insts.clear();
69+
for (BasicBlockT *BB : Blocks) {
70+
InstructionT *Prev = BB->getTerminator()->getPrevNonDebugInstruction();
71+
if (!Prev) {
72+
// Block wasn't big enough - only contained a terminator.
73+
if constexpr (EarlyFailure) {
74+
Fail = true;
75+
return;
76+
} else {
77+
this->ActiveBlocks.remove(BB);
78+
continue;
79+
}
80+
}
81+
Insts.push_back(Prev);
82+
}
83+
if (Insts.empty())
84+
Fail = true;
85+
}
86+
87+
bool isValid() const { return !Fail; }
88+
ArrayRef<InstructionT *> operator*() const { return Insts; }
89+
90+
// Note: This needs to return a SmallSetVector as the elements of
91+
// ActiveBlocks will be later copied to Blocks using std::copy. The
92+
// resultant order of elements in Blocks needs to be deterministic.
93+
// Using SmallPtrSet instead causes non-deterministic order while
94+
// copying. And we cannot simply sort Blocks as they need to match the
95+
// corresponding Values.
96+
template <bool C = EarlyFailure, std::enable_if_t<!C, int> = 0>
97+
SmallSetVector<BasicBlockT *, 4> &getActiveBlocks() {
98+
return this->ActiveBlocks;
99+
}
100+
101+
template <bool C = EarlyFailure, std::enable_if_t<!C, int> = 0>
102+
void restrictToBlocks(SmallSetVector<BasicBlockT *, 4> &Blocks) {
103+
for (auto It = Insts.begin(); It != Insts.end();) {
104+
if (!Blocks.contains((*It)->getParent())) {
105+
this->ActiveBlocks.remove((*It)->getParent());
106+
It = Insts.erase(It);
107+
} else {
108+
++It;
109+
}
110+
}
111+
}
112+
113+
void operator--() {
114+
if (Fail)
115+
return;
116+
SmallVector<InstructionT *, 4> NewInsts;
117+
for (InstructionT *Inst : Insts) {
118+
InstructionT *Prev = Inst->getPrevNonDebugInstruction();
119+
if (!Prev) {
120+
if constexpr (!EarlyFailure) {
121+
this->ActiveBlocks.remove(Inst->getParent());
122+
} else {
123+
Fail = true;
124+
return;
125+
}
126+
} else {
127+
NewInsts.push_back(Prev);
128+
}
129+
}
130+
if (NewInsts.empty()) {
131+
Fail = true;
132+
return;
133+
}
134+
Insts = NewInsts;
135+
}
136+
137+
void operator++() {
138+
if (Fail)
139+
return;
140+
SmallVector<InstructionT *, 4> NewInsts;
141+
for (InstructionT *Inst : Insts) {
142+
InstructionT *Next = Inst->getNextNonDebugInstruction();
143+
// Already at end of block.
144+
if (!Next) {
145+
Fail = true;
146+
return;
147+
}
148+
NewInsts.push_back(Next);
149+
}
150+
if (NewInsts.empty()) {
151+
Fail = true;
152+
return;
153+
}
154+
Insts = NewInsts;
155+
}
156+
};
157+
158+
} // end namespace llvm
159+
160+
#endif // LLVM_TRANSFORMS_UTILS_LOCKSTEPREVERSEITERATOR_H

llvm/lib/Transforms/Scalar/GVNSink.cpp

Lines changed: 8 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
#include "llvm/Transforms/Scalar/GVNExpression.h"
6666
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
6767
#include "llvm/Transforms/Utils/Local.h"
68+
#include "llvm/Transforms/Utils/LockstepReverseIterator.h"
6869
#include <cassert>
6970
#include <cstddef>
7071
#include <cstdint>
@@ -96,87 +97,6 @@ static bool isMemoryInst(const Instruction *I) {
9697
(isa<CallInst>(I) && !cast<CallInst>(I)->doesNotAccessMemory());
9798
}
9899

99-
/// Iterates through instructions in a set of blocks in reverse order from the
100-
/// first non-terminator. For example (assume all blocks have size n):
101-
/// LockstepReverseIterator I([B1, B2, B3]);
102-
/// *I-- = [B1[n], B2[n], B3[n]];
103-
/// *I-- = [B1[n-1], B2[n-1], B3[n-1]];
104-
/// *I-- = [B1[n-2], B2[n-2], B3[n-2]];
105-
/// ...
106-
///
107-
/// It continues until all blocks have been exhausted. Use \c getActiveBlocks()
108-
/// to
109-
/// determine which blocks are still going and the order they appear in the
110-
/// list returned by operator*.
111-
class LockstepReverseIterator {
112-
ArrayRef<BasicBlock *> Blocks;
113-
SmallSetVector<BasicBlock *, 4> ActiveBlocks;
114-
SmallVector<Instruction *, 4> Insts;
115-
bool Fail;
116-
117-
public:
118-
LockstepReverseIterator(ArrayRef<BasicBlock *> Blocks) : Blocks(Blocks) {
119-
reset();
120-
}
121-
122-
void reset() {
123-
Fail = false;
124-
ActiveBlocks.clear();
125-
for (BasicBlock *BB : Blocks)
126-
ActiveBlocks.insert(BB);
127-
Insts.clear();
128-
for (BasicBlock *BB : Blocks) {
129-
if (BB->size() <= 1) {
130-
// Block wasn't big enough - only contained a terminator.
131-
ActiveBlocks.remove(BB);
132-
continue;
133-
}
134-
Insts.push_back(BB->getTerminator()->getPrevNonDebugInstruction());
135-
}
136-
if (Insts.empty())
137-
Fail = true;
138-
}
139-
140-
bool isValid() const { return !Fail; }
141-
ArrayRef<Instruction *> operator*() const { return Insts; }
142-
143-
// Note: This needs to return a SmallSetVector as the elements of
144-
// ActiveBlocks will be later copied to Blocks using std::copy. The
145-
// resultant order of elements in Blocks needs to be deterministic.
146-
// Using SmallPtrSet instead causes non-deterministic order while
147-
// copying. And we cannot simply sort Blocks as they need to match the
148-
// corresponding Values.
149-
SmallSetVector<BasicBlock *, 4> &getActiveBlocks() { return ActiveBlocks; }
150-
151-
void restrictToBlocks(SmallSetVector<BasicBlock *, 4> &Blocks) {
152-
for (auto II = Insts.begin(); II != Insts.end();) {
153-
if (!Blocks.contains((*II)->getParent())) {
154-
ActiveBlocks.remove((*II)->getParent());
155-
II = Insts.erase(II);
156-
} else {
157-
++II;
158-
}
159-
}
160-
}
161-
162-
void operator--() {
163-
if (Fail)
164-
return;
165-
SmallVector<Instruction *, 4> NewInsts;
166-
for (auto *Inst : Insts) {
167-
if (Inst == &Inst->getParent()->front())
168-
ActiveBlocks.remove(Inst->getParent());
169-
else
170-
NewInsts.push_back(Inst->getPrevNonDebugInstruction());
171-
}
172-
if (NewInsts.empty()) {
173-
Fail = true;
174-
return;
175-
}
176-
Insts = NewInsts;
177-
}
178-
};
179-
180100
//===----------------------------------------------------------------------===//
181101

182102
/// Candidate solution for sinking. There may be different ways to
@@ -634,9 +554,11 @@ class GVNSink {
634554
/// The main heuristic function. Analyze the set of instructions pointed to by
635555
/// LRI and return a candidate solution if these instructions can be sunk, or
636556
/// std::nullopt otherwise.
637-
std::optional<SinkingInstructionCandidate> analyzeInstructionForSinking(
638-
LockstepReverseIterator &LRI, unsigned &InstNum, unsigned &MemoryInstNum,
639-
ModelledPHISet &NeededPHIs, SmallPtrSetImpl<Value *> &PHIContents);
557+
std::optional<SinkingInstructionCandidate>
558+
analyzeInstructionForSinking(LockstepReverseIterator<false> &LRI,
559+
unsigned &InstNum, unsigned &MemoryInstNum,
560+
ModelledPHISet &NeededPHIs,
561+
SmallPtrSetImpl<Value *> &PHIContents);
640562

641563
/// Create a ModelledPHI for each PHI in BB, adding to PHIs.
642564
void analyzeInitialPHIs(BasicBlock *BB, ModelledPHISet &PHIs,
@@ -675,7 +597,7 @@ class GVNSink {
675597
};
676598

677599
std::optional<SinkingInstructionCandidate>
678-
GVNSink::analyzeInstructionForSinking(LockstepReverseIterator &LRI,
600+
GVNSink::analyzeInstructionForSinking(LockstepReverseIterator<false> &LRI,
679601
unsigned &InstNum,
680602
unsigned &MemoryInstNum,
681603
ModelledPHISet &NeededPHIs,
@@ -827,7 +749,7 @@ unsigned GVNSink::sinkBB(BasicBlock *BBEnd) {
827749
return BB->getTerminator()->getNumSuccessors() != 1;
828750
});
829751

830-
LockstepReverseIterator LRI(Preds);
752+
LockstepReverseIterator<false> LRI(Preds);
831753
SmallVector<SinkingInstructionCandidate, 4> Candidates;
832754
unsigned InstNum = 0, MemoryInstNum = 0;
833755
ModelledPHISet NeededPHIs;

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Lines changed: 3 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
#include "llvm/Support/raw_ostream.h"
7575
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
7676
#include "llvm/Transforms/Utils/Local.h"
77+
#include "llvm/Transforms/Utils/LockstepReverseIterator.h"
7778
#include "llvm/Transforms/Utils/ValueMapper.h"
7879
#include <algorithm>
7980
#include <cassert>
@@ -2326,81 +2327,6 @@ static void sinkLastInstruction(ArrayRef<BasicBlock*> Blocks) {
23262327
}
23272328
}
23282329

2329-
namespace {
2330-
2331-
// LockstepReverseIterator - Iterates through instructions
2332-
// in a set of blocks in reverse order from the first non-terminator.
2333-
// For example (assume all blocks have size n):
2334-
// LockstepReverseIterator I([B1, B2, B3]);
2335-
// *I-- = [B1[n], B2[n], B3[n]];
2336-
// *I-- = [B1[n-1], B2[n-1], B3[n-1]];
2337-
// *I-- = [B1[n-2], B2[n-2], B3[n-2]];
2338-
// ...
2339-
class LockstepReverseIterator {
2340-
ArrayRef<BasicBlock*> Blocks;
2341-
SmallVector<Instruction*,4> Insts;
2342-
bool Fail;
2343-
2344-
public:
2345-
LockstepReverseIterator(ArrayRef<BasicBlock*> Blocks) : Blocks(Blocks) {
2346-
reset();
2347-
}
2348-
2349-
void reset() {
2350-
Fail = false;
2351-
Insts.clear();
2352-
for (auto *BB : Blocks) {
2353-
Instruction *Inst = BB->getTerminator();
2354-
for (Inst = Inst->getPrevNode(); Inst && isa<DbgInfoIntrinsic>(Inst);)
2355-
Inst = Inst->getPrevNode();
2356-
if (!Inst) {
2357-
// Block wasn't big enough.
2358-
Fail = true;
2359-
return;
2360-
}
2361-
Insts.push_back(Inst);
2362-
}
2363-
}
2364-
2365-
bool isValid() const {
2366-
return !Fail;
2367-
}
2368-
2369-
void operator--() {
2370-
if (Fail)
2371-
return;
2372-
for (auto *&Inst : Insts) {
2373-
for (Inst = Inst->getPrevNode(); Inst && isa<DbgInfoIntrinsic>(Inst);)
2374-
Inst = Inst->getPrevNode();
2375-
// Already at beginning of block.
2376-
if (!Inst) {
2377-
Fail = true;
2378-
return;
2379-
}
2380-
}
2381-
}
2382-
2383-
void operator++() {
2384-
if (Fail)
2385-
return;
2386-
for (auto *&Inst : Insts) {
2387-
for (Inst = Inst->getNextNode(); Inst && isa<DbgInfoIntrinsic>(Inst);)
2388-
Inst = Inst->getNextNode();
2389-
// Already at end of block.
2390-
if (!Inst) {
2391-
Fail = true;
2392-
return;
2393-
}
2394-
}
2395-
}
2396-
2397-
ArrayRef<Instruction*> operator * () const {
2398-
return Insts;
2399-
}
2400-
};
2401-
2402-
} // end anonymous namespace
2403-
24042330
/// Check whether BB's predecessors end with unconditional branches. If it is
24052331
/// true, sink any common code from the predecessors to BB.
24062332
static bool sinkCommonCodeFromPredecessors(BasicBlock *BB,
@@ -2477,7 +2403,7 @@ static bool sinkCommonCodeFromPredecessors(BasicBlock *BB,
24772403

24782404
int ScanIdx = 0;
24792405
SmallPtrSet<Value*,4> InstructionsToSink;
2480-
LockstepReverseIterator LRI(UnconditionalPreds);
2406+
LockstepReverseIterator<true> LRI(UnconditionalPreds);
24812407
while (LRI.isValid() &&
24822408
canSinkInstructions(*LRI, PHIOperands)) {
24832409
LLVM_DEBUG(dbgs() << "SINK: instruction can be sunk: " << *(*LRI)[0]
@@ -2507,7 +2433,7 @@ static bool sinkCommonCodeFromPredecessors(BasicBlock *BB,
25072433
// Okay, we *could* sink last ScanIdx instructions. But how many can we
25082434
// actually sink before encountering instruction that is unprofitable to
25092435
// sink?
2510-
auto ProfitableToSinkInstruction = [&](LockstepReverseIterator &LRI) {
2436+
auto ProfitableToSinkInstruction = [&](LockstepReverseIterator<true> &LRI) {
25112437
unsigned NumPHIInsts = 0;
25122438
for (Use &U : (*LRI)[0]->operands()) {
25132439
auto It = PHIOperands.find(&U);

0 commit comments

Comments
 (0)