Skip to content

Commit 10b99e9

Browse files
authored
[SandboxVec][BottomUpVec] Separate vectorization decisions from code generation (#127727)
Up until now the generation of vector instructions was taking place during the top-down post-order traversal of vectorizeRec(). The issue with this approach is that the vector instructions emitted during the traversal can be reordered by the scheduler, making it challenging to place them without breaking the def-before-uses rule. With this patch we separate the vectorization decisions (done in `vectorizeRec()`) from the code generation phase (`emitVectors()`). The vectorization decisions are stored in the `Actions` vector and are used by `emitVectors()` to drive code generation.
1 parent 9ba438d commit 10b99e9

File tree

9 files changed

+346
-212
lines changed

9 files changed

+346
-212
lines changed

llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/InstrMaps.h

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,57 +23,54 @@
2323

2424
namespace llvm::sandboxir {
2525

26+
class LegalityResult;
27+
28+
struct Action {
29+
unsigned Idx = 0;
30+
const LegalityResult *LegalityRes = nullptr;
31+
SmallVector<Value *, 4> Bndl;
32+
SmallVector<Value *> UserBndl;
33+
unsigned Depth;
34+
SmallVector<Action *> Operands;
35+
Value *Vec = nullptr;
36+
Action(const LegalityResult *LR, ArrayRef<Value *> B, ArrayRef<Value *> UB,
37+
unsigned Depth)
38+
: LegalityRes(LR), Bndl(B), UserBndl(UB), Depth(Depth) {}
39+
#ifndef NDEBUG
40+
void print(raw_ostream &OS) const;
41+
void dump() const;
42+
friend raw_ostream &operator<<(raw_ostream &OS, const Action &A) {
43+
A.print(OS);
44+
return OS;
45+
}
46+
#endif // NDEBUG
47+
};
48+
2649
/// Maps the original instructions to the vectorized instrs and the reverse.
2750
/// For now an original instr can only map to a single vector.
2851
class InstrMaps {
2952
/// A map from the original values that got combined into vectors, to the
30-
/// vector value(s).
31-
DenseMap<Value *, Value *> OrigToVectorMap;
32-
/// A map from the vector value to a map of the original value to its lane.
53+
/// vectorization Action.
54+
DenseMap<Value *, Action *> OrigToVectorMap;
55+
/// A map from the vec Action to a map of the original value to its lane.
3356
/// Please note that for constant vectors, there may multiple original values
3457
/// with the same lane, as they may be coming from vectorizing different
3558
/// original values.
36-
DenseMap<Value *, DenseMap<Value *, unsigned>> VectorToOrigLaneMap;
37-
Context &Ctx;
59+
DenseMap<Action *, DenseMap<Value *, unsigned>> VectorToOrigLaneMap;
3860
std::optional<Context::CallbackID> EraseInstrCB;
3961

40-
private:
41-
void notifyEraseInstr(Value *V) {
42-
// We don't know if V is an original or a vector value.
43-
auto It = OrigToVectorMap.find(V);
44-
if (It != OrigToVectorMap.end()) {
45-
// V is an original value.
46-
// Remove it from VectorToOrigLaneMap.
47-
Value *Vec = It->second;
48-
VectorToOrigLaneMap[Vec].erase(V);
49-
// Now erase V from OrigToVectorMap.
50-
OrigToVectorMap.erase(It);
51-
} else {
52-
// V is a vector value.
53-
// Go over the original values it came from and remove them from
54-
// OrigToVectorMap.
55-
for (auto [Orig, Lane] : VectorToOrigLaneMap[V])
56-
OrigToVectorMap.erase(Orig);
57-
// Now erase V from VectorToOrigLaneMap.
58-
VectorToOrigLaneMap.erase(V);
59-
}
60-
}
61-
6262
public:
63-
InstrMaps(Context &Ctx) : Ctx(Ctx) {
64-
EraseInstrCB = Ctx.registerEraseInstrCallback(
65-
[this](Instruction *I) { notifyEraseInstr(I); });
66-
}
67-
~InstrMaps() { Ctx.unregisterEraseInstrCallback(*EraseInstrCB); }
63+
InstrMaps() = default;
64+
~InstrMaps() = default;
6865
/// \Returns the vector value that we got from vectorizing \p Orig, or
6966
/// nullptr if not found.
70-
Value *getVectorForOrig(Value *Orig) const {
67+
Action *getVectorForOrig(Value *Orig) const {
7168
auto It = OrigToVectorMap.find(Orig);
7269
return It != OrigToVectorMap.end() ? It->second : nullptr;
7370
}
7471
/// \Returns the lane of \p Orig before it got vectorized into \p Vec, or
7572
/// nullopt if not found.
76-
std::optional<unsigned> getOrigLane(Value *Vec, Value *Orig) const {
73+
std::optional<unsigned> getOrigLane(Action *Vec, Value *Orig) const {
7774
auto It1 = VectorToOrigLaneMap.find(Vec);
7875
if (It1 == VectorToOrigLaneMap.end())
7976
return std::nullopt;
@@ -84,7 +81,7 @@ class InstrMaps {
8481
return It2->second;
8582
}
8683
/// Update the map to reflect that \p Origs got vectorized into \p Vec.
87-
void registerVector(ArrayRef<Value *> Origs, Value *Vec) {
84+
void registerVector(ArrayRef<Value *> Origs, Action *Vec) {
8885
auto &OrigToLaneMap = VectorToOrigLaneMap[Vec];
8986
unsigned Lane = 0;
9087
for (Value *Orig : Origs) {

llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Legality.h

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/IR/DataLayout.h"
1818
#include "llvm/Support/Casting.h"
1919
#include "llvm/Support/raw_ostream.h"
20+
#include "llvm/Transforms/Vectorize/SandboxVectorizer/InstrMaps.h"
2021
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Scheduler.h"
2122

2223
namespace llvm::sandboxir {
@@ -206,30 +207,30 @@ class Widen final : public LegalityResult {
206207

207208
class DiamondReuse final : public LegalityResult {
208209
friend class LegalityAnalysis;
209-
Value *Vec;
210-
DiamondReuse(Value *Vec)
210+
Action *Vec;
211+
DiamondReuse(Action *Vec)
211212
: LegalityResult(LegalityResultID::DiamondReuse), Vec(Vec) {}
212213

213214
public:
214215
static bool classof(const LegalityResult *From) {
215216
return From->getSubclassID() == LegalityResultID::DiamondReuse;
216217
}
217-
Value *getVector() const { return Vec; }
218+
Action *getVector() const { return Vec; }
218219
};
219220

220221
class DiamondReuseWithShuffle final : public LegalityResult {
221222
friend class LegalityAnalysis;
222-
Value *Vec;
223+
Action *Vec;
223224
ShuffleMask Mask;
224-
DiamondReuseWithShuffle(Value *Vec, const ShuffleMask &Mask)
225+
DiamondReuseWithShuffle(Action *Vec, const ShuffleMask &Mask)
225226
: LegalityResult(LegalityResultID::DiamondReuseWithShuffle), Vec(Vec),
226227
Mask(Mask) {}
227228

228229
public:
229230
static bool classof(const LegalityResult *From) {
230231
return From->getSubclassID() == LegalityResultID::DiamondReuseWithShuffle;
231232
}
232-
Value *getVector() const { return Vec; }
233+
Action *getVector() const { return Vec; }
233234
const ShuffleMask &getMask() const { return Mask; }
234235
};
235236

@@ -250,18 +251,18 @@ class CollectDescr {
250251
/// Describes how to get a value element. If the value is a vector then it
251252
/// also provides the index to extract it from.
252253
class ExtractElementDescr {
253-
Value *V;
254+
PointerUnion<Action *, Value *> V = nullptr;
254255
/// The index in `V` that the value can be extracted from.
255-
/// This is nullopt if we need to use `V` as a whole.
256-
std::optional<int> ExtractIdx;
256+
int ExtractIdx = 0;
257257

258258
public:
259-
ExtractElementDescr(Value *V, int ExtractIdx)
259+
ExtractElementDescr(Action *V, int ExtractIdx)
260260
: V(V), ExtractIdx(ExtractIdx) {}
261-
ExtractElementDescr(Value *V) : V(V), ExtractIdx(std::nullopt) {}
262-
Value *getValue() const { return V; }
263-
bool needsExtract() const { return ExtractIdx.has_value(); }
264-
int getExtractIdx() const { return *ExtractIdx; }
261+
ExtractElementDescr(Value *V) : V(V) {}
262+
Action *getValue() const { return cast<Action *>(V); }
263+
Value *getScalar() const { return cast<Value *>(V); }
264+
bool needsExtract() const { return isa<Action *>(V); }
265+
int getExtractIdx() const { return ExtractIdx; }
265266
};
266267

267268
using DescrVecT = SmallVector<ExtractElementDescr, 4>;
@@ -272,11 +273,11 @@ class CollectDescr {
272273
: Descrs(std::move(Descrs)) {}
273274
/// If all elements come from a single vector input, then return that vector
274275
/// and also the shuffle mask required to get them in order.
275-
std::optional<std::pair<Value *, ShuffleMask>> getSingleInput() const {
276+
std::optional<std::pair<Action *, ShuffleMask>> getSingleInput() const {
276277
const auto &Descr0 = *Descrs.begin();
277-
Value *V0 = Descr0.getValue();
278278
if (!Descr0.needsExtract())
279279
return std::nullopt;
280+
auto *V0 = Descr0.getValue();
280281
ShuffleMask::IndicesVecT MaskIndices;
281282
MaskIndices.push_back(Descr0.getExtractIdx());
282283
for (const auto &Descr : drop_begin(Descrs)) {

llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.h

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,33 @@ class BottomUpVec final : public RegionPass {
5858
/// function helps collect these instructions (along with the pointer operands
5959
/// for loads/stores) so that they can be cleaned up later.
6060
void collectPotentiallyDeadInstrs(ArrayRef<Value *> Bndl);
61-
/// Recursively try to vectorize \p Bndl and its operands.
62-
Value *vectorizeRec(ArrayRef<Value *> Bndl, ArrayRef<Value *> UserBndl,
63-
unsigned Depth);
61+
62+
/// Helper class describing how(if) to vectorize the code.
63+
class ActionsVector {
64+
private:
65+
SmallVector<std::unique_ptr<Action>, 16> Actions;
66+
67+
public:
68+
auto begin() const { return Actions.begin(); }
69+
auto end() const { return Actions.end(); }
70+
void push_back(std::unique_ptr<Action> &&ActPtr) {
71+
ActPtr->Idx = Actions.size();
72+
Actions.push_back(std::move(ActPtr));
73+
}
74+
void clear() { Actions.clear(); }
75+
#ifndef NDEBUG
76+
void print(raw_ostream &OS) const;
77+
void dump() const;
78+
#endif // NDEBUG
79+
};
80+
ActionsVector Actions;
81+
/// Recursively try to vectorize \p Bndl and its operands. This populates the
82+
/// `Actions` vector.
83+
Action *vectorizeRec(ArrayRef<Value *> Bndl, ArrayRef<Value *> UserBndl,
84+
unsigned Depth);
85+
/// Generate vector instructions based on `Actions` and return the last vector
86+
/// created.
87+
Value *emitVectors();
6488
/// Entry point for vectorization starting from \p Seeds.
6589
bool tryVectorize(ArrayRef<Value *> Seeds);
6690

llvm/lib/Transforms/Vectorize/SandboxVectorizer/InstrMaps.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,23 @@
88

99
#include "llvm/Transforms/Vectorize/SandboxVectorizer/InstrMaps.h"
1010
#include "llvm/Support/Debug.h"
11+
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Legality.h"
1112

1213
namespace llvm::sandboxir {
1314

1415
#ifndef NDEBUG
16+
void Action::print(raw_ostream &OS) const {
17+
OS << Idx << ". " << *LegalityRes << " Depth:" << Depth << "\n";
18+
OS.indent(2) << "Bndl:\n";
19+
for (Value *V : Bndl)
20+
OS.indent(4) << *V << "\n";
21+
OS.indent(2) << "UserBndl:\n";
22+
for (Value *V : UserBndl)
23+
OS.indent(4) << *V << "\n";
24+
}
25+
26+
void Action::dump() const { print(dbgs()); }
27+
1528
void InstrMaps::dump() const {
1629
print(dbgs());
1730
dbgs() << "\n";

0 commit comments

Comments
 (0)