Skip to content

Commit 3aecf41

Browse files
authored
[SandboxVec] Add barebones Region class. (#108899)
A region identifies a set of vector instructions generated by vectorization passes. The vectorizer can then run a series of RegionPasses on the region, evaluate the cost, and commit/reject the transforms on a region-by-region basis, instead of an entire basic block. This is heavily based ov @vporpo's prototype. In particular, the doc comment for the Region class is all his. The rest of this commit is mostly boilerplate around a SetVector: getters, iterators, and some debug helpers.
1 parent 790f2eb commit 3aecf41

File tree

5 files changed

+236
-0
lines changed

5 files changed

+236
-0
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//===- Region.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_VECTORIZE_SANDBOXVECTORIZER_REGION_H
10+
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_REGION_H
11+
12+
#include "llvm/ADT/SetVector.h"
13+
#include "llvm/ADT/iterator_range.h"
14+
#include "llvm/SandboxIR/SandboxIR.h"
15+
#include "llvm/Support/InstructionCost.h"
16+
#include "llvm/Support/raw_ostream.h"
17+
18+
namespace llvm::sandboxir {
19+
20+
/// The main job of the Region is to point to new instructions generated by
21+
/// vectorization passes. It is the unit that RegionPasses operate on with their
22+
/// runOnRegion() function.
23+
///
24+
/// The region allows us to stack transformations horizontally, meaning that
25+
/// each transformation operates on a single region and the resulting region is
26+
/// the input to the next transformation, as opposed to vertically, which is the
27+
/// common way of applying a transformation across the whole BB. This enables us
28+
/// to check for profitability and decide whether we accept or rollback at a
29+
/// region granularity, which is much better than doing this at the BB level.
30+
///
31+
// Traditional approach: transformations applied vertically for the whole BB
32+
// BB
33+
// +----+
34+
// | |
35+
// | |
36+
// | | -> Transform1 -> ... -> TransformN -> Check Cost
37+
// | |
38+
// | |
39+
// +----+
40+
//
41+
// Region-based approach: transformations applied horizontally, for each Region
42+
// BB
43+
// +----+
44+
// |Rgn1| -> Transform1 -> ... -> TransformN -> Check Cost
45+
// | |
46+
// |Rgn2| -> Transform1 -> ... -> TransformN -> Check Cost
47+
// | |
48+
// |Rgn3| -> Transform1 -> ... -> TransformN -> Check Cost
49+
// +----+
50+
51+
class Region {
52+
/// All the instructions in the Region. Only new instructions generated during
53+
/// vectorization are part of the Region.
54+
SetVector<Instruction *> Insts;
55+
56+
/// A unique ID, used for debugging.
57+
unsigned RegionID = 0;
58+
59+
Context &Ctx;
60+
61+
/// The basic block containing this region.
62+
BasicBlock &BB;
63+
64+
// TODO: Add cost modeling.
65+
// TODO: Add a way to encode/decode region info to/from metadata.
66+
67+
public:
68+
Region(Context &Ctx, BasicBlock &BB);
69+
~Region();
70+
71+
BasicBlock *getParent() const { return &BB; }
72+
Context &getContext() const { return Ctx; }
73+
/// Returns the region's unique ID.
74+
unsigned getID() const { return RegionID; }
75+
76+
/// Adds I to the set.
77+
void add(Instruction *I);
78+
/// Removes I from the set.
79+
void remove(Instruction *I);
80+
/// Returns true if I is in the Region.
81+
bool contains(Instruction *I) const { return Insts.contains(I); }
82+
/// Returns true if the Region has no instructions.
83+
bool empty() const { return Insts.empty(); }
84+
85+
using iterator = decltype(Insts.begin());
86+
iterator begin() { return Insts.begin(); }
87+
iterator end() { return Insts.end(); }
88+
iterator_range<iterator> insts() { return make_range(begin(), end()); }
89+
90+
#ifndef NDEBUG
91+
/// This is an expensive check, meant for testing.
92+
bool operator==(const Region &Other) const;
93+
bool operator!=(const Region &other) const { return !(*this == other); }
94+
95+
void dump(raw_ostream &OS) const;
96+
void dump() const;
97+
friend raw_ostream &operator<<(raw_ostream &OS, const Region &Rgn) {
98+
Rgn.dump(OS);
99+
return OS;
100+
}
101+
#endif
102+
};
103+
104+
} // namespace llvm::sandboxir
105+
106+
#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_REGION_H

llvm/lib/Transforms/Vectorize/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_llvm_component_library(LLVMVectorize
55
LoopVectorize.cpp
66
SandboxVectorizer/DependencyGraph.cpp
77
SandboxVectorizer/Passes/BottomUpVec.cpp
8+
SandboxVectorizer/Region.cpp
89
SandboxVectorizer/SandboxVectorizer.cpp
910
SLPVectorizer.cpp
1011
Vectorize.cpp
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===- Region.cpp ---------------------------------------------------------===//
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+
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Region.h"
10+
11+
namespace llvm::sandboxir {
12+
13+
Region::Region(Context &Ctx, BasicBlock &BB) : Ctx(Ctx), BB(BB) {
14+
static unsigned StaticRegionID;
15+
RegionID = StaticRegionID++;
16+
}
17+
18+
Region::~Region() {}
19+
20+
void Region::add(Instruction *I) { Insts.insert(I); }
21+
22+
void Region::remove(Instruction *I) { Insts.remove(I); }
23+
24+
#ifndef NDEBUG
25+
bool Region::operator==(const Region &Other) const {
26+
if (Insts.size() != Other.Insts.size())
27+
return false;
28+
if (!std::is_permutation(Insts.begin(), Insts.end(), Other.Insts.begin()))
29+
return false;
30+
return true;
31+
}
32+
33+
void Region::dump(raw_ostream &OS) const {
34+
OS << "RegionID: " << getID() << "\n";
35+
for (auto *I : Insts)
36+
OS << *I << "\n";
37+
}
38+
39+
void Region::dump() const {
40+
dump(dbgs());
41+
dbgs() << "\n";
42+
}
43+
44+
} // namespace llvm::sandboxir
45+
46+
#endif // NDEBUG

llvm/unittests/Transforms/Vectorize/SandboxVectorizer/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ set(LLVM_LINK_COMPONENTS
99

1010
add_llvm_unittest(SandboxVectorizerTests
1111
DependencyGraphTest.cpp
12+
RegionTest.cpp
1213
)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//===- RegionTest.cpp -----------------------------------------------------===//
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+
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Region.h"
10+
#include "llvm/AsmParser/Parser.h"
11+
#include "llvm/SandboxIR/SandboxIR.h"
12+
#include "llvm/Support/SourceMgr.h"
13+
#include "gmock/gmock-matchers.h"
14+
#include "gtest/gtest.h"
15+
16+
using namespace llvm;
17+
18+
struct RegionTest : public testing::Test {
19+
LLVMContext C;
20+
std::unique_ptr<Module> M;
21+
22+
void parseIR(LLVMContext &C, const char *IR) {
23+
SMDiagnostic Err;
24+
M = parseAssemblyString(IR, Err, C);
25+
if (!M)
26+
Err.print("RegionTest", errs());
27+
}
28+
};
29+
30+
TEST_F(RegionTest, Basic) {
31+
parseIR(C, R"IR(
32+
define i8 @foo(i8 %v0, i8 %v1) {
33+
%t0 = add i8 %v0, 1
34+
%t1 = add i8 %t0, %v1
35+
ret i8 %t1
36+
}
37+
)IR");
38+
llvm::Function *LLVMF = &*M->getFunction("foo");
39+
sandboxir::Context Ctx(C);
40+
auto *F = Ctx.createFunction(LLVMF);
41+
auto *BB = &*F->begin();
42+
auto It = BB->begin();
43+
auto *T0 = cast<sandboxir::Instruction>(&*It++);
44+
auto *T1 = cast<sandboxir::Instruction>(&*It++);
45+
auto *Ret = cast<sandboxir::Instruction>(&*It++);
46+
sandboxir::Region Rgn(Ctx, *BB);
47+
48+
// Check getters
49+
EXPECT_EQ(BB, Rgn.getParent());
50+
EXPECT_EQ(&Ctx, &Rgn.getContext());
51+
EXPECT_EQ(0U, Rgn.getID());
52+
53+
// Check add / remove / empty.
54+
EXPECT_TRUE(Rgn.empty());
55+
Rgn.add(T0);
56+
EXPECT_FALSE(Rgn.empty());
57+
Rgn.remove(T0);
58+
EXPECT_TRUE(Rgn.empty());
59+
60+
// Check iteration.
61+
Rgn.add(T0);
62+
Rgn.add(T1);
63+
Rgn.add(Ret);
64+
// Use an ordered matcher because we're supposed to preserve the insertion
65+
// order for determinism.
66+
EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, T1, Ret));
67+
68+
// Check contains
69+
EXPECT_TRUE(Rgn.contains(T0));
70+
Rgn.remove(T0);
71+
EXPECT_FALSE(Rgn.contains(T0));
72+
73+
#ifndef NDEBUG
74+
// Check equality comparison. Insert in reverse order into `Other` to check
75+
// that comparison is order-independent.
76+
sandboxir::Region Other(Ctx, *BB);
77+
Other.add(Ret);
78+
EXPECT_NE(Rgn, Other);
79+
Other.add(T1);
80+
EXPECT_EQ(Rgn, Other);
81+
#endif
82+
}

0 commit comments

Comments
 (0)