Skip to content

Commit 4b209c5

Browse files
authored
[SandboxIR][Region] Add cost modeling to the region (llvm#124354)
This patch implements cost modeling for Region. All instructions that are added or removed get their cost counted in the Scoreboard. This is used for checking if the region before or after a transformation is more profitable.
1 parent 34c6c5e commit 4b209c5

File tree

6 files changed

+151
-20
lines changed

6 files changed

+151
-20
lines changed

llvm/include/llvm/SandboxIR/Region.h

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,55 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_REGION_H
10-
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_REGION_H
9+
#ifndef LLVM_SANDBOXIR_REGION_H
10+
#define LLVM_SANDBOXIR_REGION_H
1111

1212
#include <memory>
1313

1414
#include "llvm/ADT/SetVector.h"
1515
#include "llvm/ADT/iterator_range.h"
16+
#include "llvm/Analysis/TargetTransformInfo.h"
1617
#include "llvm/SandboxIR/Instruction.h"
1718
#include "llvm/Support/raw_ostream.h"
1819

1920
namespace llvm::sandboxir {
2021

22+
class Region;
23+
24+
class ScoreBoard {
25+
const Region &Rgn;
26+
TargetTransformInfo &TTI;
27+
constexpr static TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput;
28+
/// The cost of all instructions added to the region.
29+
InstructionCost AfterCost = 0;
30+
/// The cost of all instructions that got removed and replaced by new ones.
31+
InstructionCost BeforeCost = 0;
32+
/// Helper for both add() and remove(). \Returns the TTI cost of \p I.
33+
InstructionCost getCost(Instruction *I) const;
34+
/// No need to allow copies.
35+
ScoreBoard(const ScoreBoard &) = delete;
36+
const ScoreBoard &operator=(const ScoreBoard &) = delete;
37+
38+
public:
39+
ScoreBoard(Region &Rgn, TargetTransformInfo &TTI) : Rgn(Rgn), TTI(TTI) {}
40+
/// Mark \p I as a newly added instruction to the region.
41+
void add(Instruction *I) { AfterCost += getCost(I); }
42+
/// Mark \p I as a deleted instruction from the region.
43+
void remove(Instruction *I);
44+
/// \Returns the cost of the newly added instructions.
45+
InstructionCost getAfterCost() const { return AfterCost; }
46+
/// \Returns the cost of the Removed instructions.
47+
InstructionCost getBeforeCost() const { return BeforeCost; }
48+
49+
#ifndef NDEBUG
50+
void dump(raw_ostream &OS) const {
51+
OS << "BeforeCost: " << BeforeCost << "\n";
52+
OS << "AfterCost: " << AfterCost << "\n";
53+
}
54+
LLVM_DUMP_METHOD void dump() const;
55+
#endif // NDEBUG
56+
};
57+
2158
/// The main job of the Region is to point to new instructions generated by
2259
/// vectorization passes. It is the unit that RegionPasses operate on with their
2360
/// runOnRegion() function.
@@ -62,6 +99,8 @@ class Region {
6299
static constexpr const char *RegionStr = "sandboxregion";
63100

64101
Context &Ctx;
102+
/// Keeps track of cost of instructions added and removed.
103+
ScoreBoard Scoreboard;
65104

66105
/// ID (for later deregistration) of the "create instruction" callback.
67106
Context::CallbackID CreateInstCB;
@@ -72,7 +111,7 @@ class Region {
72111
// TODO: Add a way to encode/decode region info to/from metadata.
73112

74113
public:
75-
Region(Context &Ctx);
114+
Region(Context &Ctx, TargetTransformInfo &TTI);
76115
~Region();
77116

78117
Context &getContext() const { return Ctx; }
@@ -91,7 +130,10 @@ class Region {
91130
iterator end() { return Insts.end(); }
92131
iterator_range<iterator> insts() { return make_range(begin(), end()); }
93132

94-
static SmallVector<std::unique_ptr<Region>> createRegionsFromMD(Function &F);
133+
static SmallVector<std::unique_ptr<Region>>
134+
createRegionsFromMD(Function &F, TargetTransformInfo &TTI);
135+
/// \Returns the ScoreBoard data structure that keeps track of instr costs.
136+
const ScoreBoard &getScoreboard() const { return Scoreboard; }
95137

96138
#ifndef NDEBUG
97139
/// This is an expensive check, meant for testing.
@@ -109,4 +151,4 @@ class Region {
109151

110152
} // namespace llvm::sandboxir
111153

112-
#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_REGION_H
154+
#endif // LLVM_SANDBOXIR_REGION_H

llvm/include/llvm/SandboxIR/Value.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ class Value {
167167
// Region needs to manipulate metadata in the underlying LLVM Value, we don't
168168
// expose metadata in sandboxir.
169169
friend class Region;
170+
friend class ScoreBoard; // Needs access to `Val` for the instruction cost.
170171

171172
/// All values point to the context.
172173
Context &Ctx;

llvm/lib/SandboxIR/Region.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,29 @@
1111

1212
namespace llvm::sandboxir {
1313

14-
Region::Region(Context &Ctx) : Ctx(Ctx) {
14+
InstructionCost ScoreBoard::getCost(Instruction *I) const {
15+
auto *LLVMI = cast<llvm::Instruction>(I->Val);
16+
SmallVector<const llvm::Value *> Operands(LLVMI->operands());
17+
return TTI.getInstructionCost(LLVMI, Operands, CostKind);
18+
}
19+
20+
void ScoreBoard::remove(Instruction *I) {
21+
auto Cost = getCost(I);
22+
if (Rgn.contains(I))
23+
// If `I` is one the newly added ones, then we should adjust `AfterCost`
24+
AfterCost -= Cost;
25+
else
26+
// If `I` is one of the original instructions (outside the region) then it
27+
// is part of the original code, so adjust `BeforeCost`.
28+
BeforeCost += Cost;
29+
}
30+
31+
#ifndef NDEBUG
32+
void ScoreBoard::dump() const { dump(dbgs()); }
33+
#endif
34+
35+
Region::Region(Context &Ctx, TargetTransformInfo &TTI)
36+
: Ctx(Ctx), Scoreboard(*this, TTI) {
1537
LLVMContext &LLVMCtx = Ctx.LLVMCtx;
1638
auto *RegionStrMD = MDString::get(LLVMCtx, RegionStr);
1739
RegionMDN = MDNode::getDistinct(LLVMCtx, {RegionStrMD});
@@ -31,9 +53,15 @@ void Region::add(Instruction *I) {
3153
Insts.insert(I);
3254
// TODO: Consider tagging instructions lazily.
3355
cast<llvm::Instruction>(I->Val)->setMetadata(MDKind, RegionMDN);
56+
// Keep track of the instruction cost.
57+
Scoreboard.add(I);
3458
}
3559

3660
void Region::remove(Instruction *I) {
61+
// Keep track of the instruction cost. This need to be done *before* we remove
62+
// `I` from the region.
63+
Scoreboard.remove(I);
64+
3765
Insts.remove(I);
3866
cast<llvm::Instruction>(I->Val)->setMetadata(MDKind, nullptr);
3967
}
@@ -58,7 +86,8 @@ void Region::dump() const {
5886
}
5987
#endif // NDEBUG
6088

61-
SmallVector<std::unique_ptr<Region>> Region::createRegionsFromMD(Function &F) {
89+
SmallVector<std::unique_ptr<Region>>
90+
Region::createRegionsFromMD(Function &F, TargetTransformInfo &TTI) {
6291
SmallVector<std::unique_ptr<Region>> Regions;
6392
DenseMap<MDNode *, Region *> MDNToRegion;
6493
auto &Ctx = F.getContext();
@@ -68,7 +97,7 @@ SmallVector<std::unique_ptr<Region>> Region::createRegionsFromMD(Function &F) {
6897
Region *R = nullptr;
6998
auto It = MDNToRegion.find(MDN);
7099
if (It == MDNToRegion.end()) {
71-
Regions.push_back(std::make_unique<Region>(Ctx));
100+
Regions.push_back(std::make_unique<Region>(Ctx, TTI));
72101
R = Regions.back().get();
73102
MDNToRegion[MDN] = R;
74103
} else {

llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/RegionsFromMetadata.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ RegionsFromMetadata::RegionsFromMetadata(StringRef Pipeline)
1919

2020
bool RegionsFromMetadata::runOnFunction(Function &F, const Analyses &A) {
2121
SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
22-
sandboxir::Region::createRegionsFromMD(F);
22+
sandboxir::Region::createRegionsFromMD(F, A.getTTI());
2323
for (auto &R : Regions) {
2424
RPM.runOnRegion(*R, A);
2525
}

llvm/unittests/SandboxIR/PassTest.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/SandboxIR/Pass.h"
10+
#include "llvm/Analysis/TargetTransformInfo.h"
1011
#include "llvm/AsmParser/Parser.h"
1112
#include "llvm/IR/Module.h"
1213
#include "llvm/SandboxIR/Constant.h"
@@ -23,10 +24,13 @@ struct PassTest : public testing::Test {
2324
llvm::LLVMContext LLVMCtx;
2425
std::unique_ptr<llvm::Module> LLVMM;
2526
std::unique_ptr<Context> Ctx;
27+
std::unique_ptr<llvm::TargetTransformInfo> TTI;
2628

2729
Function *parseFunction(const char *IR, const char *FuncName) {
2830
llvm::SMDiagnostic Err;
2931
LLVMM = parseAssemblyString(IR, Err, LLVMCtx);
32+
TTI = std::make_unique<llvm::TargetTransformInfo>(LLVMM->getDataLayout());
33+
3034
if (!LLVMM)
3135
Err.print("PassTest", llvm::errs());
3236
Ctx = std::make_unique<Context>(LLVMCtx);
@@ -119,7 +123,7 @@ define i8 @foo(i8 %v0, i8 %v1) {
119123
EXPECT_EQ(TPass.getName(), "test-pass");
120124
// Check runOnRegion();
121125
llvm::SmallVector<std::unique_ptr<Region>> Regions =
122-
Region::createRegionsFromMD(*F);
126+
Region::createRegionsFromMD(*F, *TTI);
123127
ASSERT_EQ(Regions.size(), 1u);
124128
TPass.runOnRegion(*Regions[0], Analyses::emptyForTesting());
125129
EXPECT_EQ(InstCount, 2u);
@@ -242,7 +246,7 @@ define i8 @foo(i8 %v0, i8 %v1) {
242246
RPM.addPass(std::make_unique<TestPass2>(InstCount2));
243247
// Check runOnRegion().
244248
llvm::SmallVector<std::unique_ptr<Region>> Regions =
245-
Region::createRegionsFromMD(*F);
249+
Region::createRegionsFromMD(*F, *TTI);
246250
ASSERT_EQ(Regions.size(), 1u);
247251
RPM.runOnRegion(*Regions[0], Analyses::emptyForTesting());
248252
EXPECT_EQ(InstCount1, 2u);

llvm/unittests/SandboxIR/RegionTest.cpp

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/SandboxIR/Region.h"
10+
#include "llvm/Analysis/TargetTransformInfo.h"
1011
#include "llvm/AsmParser/Parser.h"
1112
#include "llvm/SandboxIR/Context.h"
1213
#include "llvm/SandboxIR/Function.h"
@@ -20,10 +21,12 @@ using namespace llvm;
2021
struct RegionTest : public testing::Test {
2122
LLVMContext C;
2223
std::unique_ptr<Module> M;
24+
std::unique_ptr<TargetTransformInfo> TTI;
2325

2426
void parseIR(LLVMContext &C, const char *IR) {
2527
SMDiagnostic Err;
2628
M = parseAssemblyString(IR, Err, C);
29+
TTI = std::make_unique<TargetTransformInfo>(M->getDataLayout());
2730
if (!M)
2831
Err.print("RegionTest", errs());
2932
}
@@ -45,7 +48,7 @@ define i8 @foo(i8 %v0, i8 %v1) {
4548
auto *T0 = cast<sandboxir::Instruction>(&*It++);
4649
auto *T1 = cast<sandboxir::Instruction>(&*It++);
4750
auto *Ret = cast<sandboxir::Instruction>(&*It++);
48-
sandboxir::Region Rgn(Ctx);
51+
sandboxir::Region Rgn(Ctx, *TTI);
4952

5053
// Check getContext.
5154
EXPECT_EQ(&Ctx, &Rgn.getContext());
@@ -73,7 +76,7 @@ define i8 @foo(i8 %v0, i8 %v1) {
7376
#ifndef NDEBUG
7477
// Check equality comparison. Insert in reverse order into `Other` to check
7578
// that comparison is order-independent.
76-
sandboxir::Region Other(Ctx);
79+
sandboxir::Region Other(Ctx, *TTI);
7780
Other.add(Ret);
7881
EXPECT_NE(Rgn, Other);
7982
Other.add(T1);
@@ -98,7 +101,7 @@ define i8 @foo(i8 %v0, i8 %v1, ptr %ptr) {
98101
auto *T0 = cast<sandboxir::Instruction>(&*It++);
99102
auto *T1 = cast<sandboxir::Instruction>(&*It++);
100103
auto *Ret = cast<sandboxir::Instruction>(&*It++);
101-
sandboxir::Region Rgn(Ctx);
104+
sandboxir::Region Rgn(Ctx, *TTI);
102105
Rgn.add(T0);
103106
Rgn.add(T1);
104107

@@ -134,7 +137,7 @@ define i8 @foo(i8 %v0, i8 %v1) {
134137
auto *T2 = cast<sandboxir::Instruction>(&*It++);
135138

136139
SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
137-
sandboxir::Region::createRegionsFromMD(*F);
140+
sandboxir::Region::createRegionsFromMD(*F, *TTI);
138141
EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0));
139142
EXPECT_THAT(Regions[1]->insts(), testing::UnorderedElementsAre(T1, T2));
140143
}
@@ -160,7 +163,7 @@ define i8 @foo(i8 %v0, i8 %v1) {
160163
auto *T2 = cast<sandboxir::Instruction>(&*It++);
161164

162165
SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
163-
sandboxir::Region::createRegionsFromMD(*F);
166+
sandboxir::Region::createRegionsFromMD(*F, *TTI);
164167
EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0, T2));
165168
}
166169

@@ -182,9 +185,9 @@ define i8 @foo(i8 %v0, i8 %v1) {
182185
[[maybe_unused]] auto *T1 = cast<sandboxir::Instruction>(&*It++);
183186
auto *T2 = cast<sandboxir::Instruction>(&*It++);
184187
[[maybe_unused]] auto *Ret = cast<sandboxir::Instruction>(&*It++);
185-
sandboxir::Region Rgn(Ctx);
188+
sandboxir::Region Rgn(Ctx, *TTI);
186189
Rgn.add(T0);
187-
sandboxir::Region Rgn2(Ctx);
190+
sandboxir::Region Rgn2(Ctx, *TTI);
188191
Rgn2.add(T2);
189192

190193
std::string output;
@@ -226,14 +229,66 @@ define i8 @foo(i8 %v0, i8 %v1) {
226229
auto *T0 = cast<sandboxir::Instruction>(&*It++);
227230
auto *T1 = cast<sandboxir::Instruction>(&*It++);
228231

229-
sandboxir::Region Rgn(Ctx);
232+
sandboxir::Region Rgn(Ctx, *TTI);
230233
Rgn.add(T0);
231234
Rgn.add(T1);
232235

233236
SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
234-
sandboxir::Region::createRegionsFromMD(*F);
237+
sandboxir::Region::createRegionsFromMD(*F, *TTI);
235238
ASSERT_EQ(1U, Regions.size());
236239
#ifndef NDEBUG
237240
EXPECT_EQ(Rgn, *Regions[0].get());
238241
#endif
239242
}
243+
244+
TEST_F(RegionTest, RegionCost) {
245+
parseIR(C, R"IR(
246+
define void @foo(i8 %v0, i8 %v1, i8 %v2) {
247+
%add0 = add i8 %v0, 1
248+
%add1 = add i8 %v1, 2
249+
%add2 = add i8 %v2, 3
250+
ret void
251+
}
252+
)IR");
253+
llvm::Function *LLVMF = &*M->getFunction("foo");
254+
auto *LLVMBB = &*LLVMF->begin();
255+
auto LLVMIt = LLVMBB->begin();
256+
auto *LLVMAdd0 = &*LLVMIt++;
257+
auto *LLVMAdd1 = &*LLVMIt++;
258+
auto *LLVMAdd2 = &*LLVMIt++;
259+
260+
sandboxir::Context Ctx(C);
261+
auto *F = Ctx.createFunction(LLVMF);
262+
auto *BB = &*F->begin();
263+
auto It = BB->begin();
264+
auto *Add0 = cast<sandboxir::Instruction>(&*It++);
265+
auto *Add1 = cast<sandboxir::Instruction>(&*It++);
266+
auto *Add2 = cast<sandboxir::Instruction>(&*It++);
267+
268+
sandboxir::Region Rgn(Ctx, *TTI);
269+
const auto &SB = Rgn.getScoreboard();
270+
EXPECT_EQ(SB.getAfterCost(), 0);
271+
EXPECT_EQ(SB.getBeforeCost(), 0);
272+
273+
auto GetCost = [this](llvm::Instruction *LLVMI) {
274+
constexpr static TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput;
275+
SmallVector<const llvm::Value *> Operands(LLVMI->operands());
276+
return TTI->getInstructionCost(LLVMI, Operands, CostKind);
277+
};
278+
// Add `Add0` to the region, should be counted in "After".
279+
Rgn.add(Add0);
280+
EXPECT_EQ(SB.getBeforeCost(), 0);
281+
EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd0));
282+
// Same for `Add1`.
283+
Rgn.add(Add1);
284+
EXPECT_EQ(SB.getBeforeCost(), 0);
285+
EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd0) + GetCost(LLVMAdd1));
286+
// Remove `Add0`, should be subtracted from "After".
287+
Rgn.remove(Add0);
288+
EXPECT_EQ(SB.getBeforeCost(), 0);
289+
EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1));
290+
// Remove `Add2` which was never in the region, should counted in "Before".
291+
Rgn.remove(Add2);
292+
EXPECT_EQ(SB.getBeforeCost(), GetCost(LLVMAdd2));
293+
EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1));
294+
}

0 commit comments

Comments
 (0)