blob: c6de6e9d567cb85b7fd66ea53c610a23acfd0d34 [file] [log] [blame]
Roman Lebedev05f2b5c2020-07-07 01:16:37 +03001//===- ReduceOperandBundes.cpp - Specialized Delta Pass -------------------===//
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// This file implements a function which calls the Generic Delta pass in order
10// to reduce uninteresting operand bundes from calls.
11//
12//===----------------------------------------------------------------------===//
13
14#include "ReduceOperandBundles.h"
15#include "Delta.h"
16#include "TestRunner.h"
17#include "llvm/ADT/ArrayRef.h"
18#include "llvm/ADT/DenseMap.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/ADT/ScopeExit.h"
21#include "llvm/ADT/Sequence.h"
22#include "llvm/ADT/iterator_range.h"
23#include "llvm/IR/InstVisitor.h"
24#include "llvm/IR/InstrTypes.h"
25#include "llvm/Support/raw_ostream.h"
26#include <algorithm>
27#include <iterator>
28#include <vector>
29
30namespace {
31class Module;
32} // namespace
33
34using namespace llvm;
35
36namespace {
37
38/// Provides opaque interface for querying into ChunksToKeep without having to
39/// actually understand what is going on.
40struct Oracle {
41 /// Out of all the features that we promised to be,
42 /// how many have we already processed? 1-based!
43 int Index = 1;
44
45 /// The actual workhorse, contains the knowledge whether or not
46 /// some particular feature should be preserved this time.
47 ArrayRef<Chunk> ChunksToKeep;
48
49public:
50 Oracle(ArrayRef<Chunk> ChunksToKeep_) : ChunksToKeep(ChunksToKeep_) {}
51
52 /// Should be called for each feature on which we are operating.
53 /// Name is self-explanatory - if returns true, then it should be preserved.
54 bool shouldKeep() {
55 if (ChunksToKeep.empty())
56 return false; // All further features are to be discarded.
57
58 // Does the current (front) chunk contain such a feature?
59 bool ShouldKeep = ChunksToKeep.front().contains(Index);
60 auto _ = make_scope_exit([&]() { ++Index; }); // Next time - next feature.
61
62 // Is this the last feature in the chunk?
63 if (ChunksToKeep.front().end == Index)
64 ChunksToKeep = ChunksToKeep.drop_front(); // Onto next chunk.
65
66 return ShouldKeep;
67 }
68};
69
70/// Given ChunksToKeep, produce a map of calls and indexes of operand bundles
71/// to be preserved for each call.
72class OperandBundleRemapper : public InstVisitor<OperandBundleRemapper> {
73 Oracle O;
74
75public:
76 DenseMap<CallBase *, std::vector<unsigned>> CallsToRefine;
77
78 explicit OperandBundleRemapper(ArrayRef<Chunk> ChunksToKeep)
79 : O(ChunksToKeep) {}
80
81 /// So far only CallBase sub-classes can have operand bundles.
82 /// Let's see which of the operand bundles of this call are to be kept.
83 void visitCallBase(CallBase &Call) {
84 if (!Call.hasOperandBundles())
85 return; // No bundles to begin with.
86
87 // Insert this call into map, we will likely want to rebuild it.
88 auto &OperandBundlesToKeepIndexes = CallsToRefine[&Call];
89 OperandBundlesToKeepIndexes.reserve(Call.getNumOperandBundles());
90
91 // Enumerate every operand bundle on this call.
92 for_each(seq(0U, Call.getNumOperandBundles()), [&](unsigned BundleIndex) {
93 if (O.shouldKeep()) // Should we keep this one?
94 OperandBundlesToKeepIndexes.emplace_back(BundleIndex);
95 });
96 }
97};
98
99struct OperandBundleCounter : public InstVisitor<OperandBundleCounter> {
100 /// How many features (in this case, operand bundles) did we count, total?
101 int OperandBundeCount = 0;
102
103 OperandBundleCounter() {}
104
105 /// So far only CallBase sub-classes can have operand bundles.
106 void visitCallBase(CallBase &Call) {
107 // Just accumulate the total number of operand bundles.
108 OperandBundeCount += Call.getNumOperandBundles();
109 }
110};
111
112} // namespace
113
114static void maybeRewriteCallWithDifferentBundles(
115 CallBase *OrigCall, ArrayRef<unsigned> OperandBundlesToKeepIndexes) {
116 if (OperandBundlesToKeepIndexes.size() == OrigCall->getNumOperandBundles())
117 return; // Not modifying operand bundles of this call after all.
118
119 std::vector<OperandBundleDef> NewBundles;
120 NewBundles.reserve(OperandBundlesToKeepIndexes.size());
121
122 // Actually copy over the bundles that we want to keep.
123 transform(OperandBundlesToKeepIndexes, std::back_inserter(NewBundles),
124 [OrigCall](unsigned Index) {
125 return OperandBundleDef(OrigCall->getOperandBundleAt(Index));
126 });
127
128 // Finally actually replace the bundles on the call.
129 CallBase *NewCall = CallBase::Create(OrigCall, NewBundles, OrigCall);
130 OrigCall->replaceAllUsesWith(NewCall);
131 OrigCall->eraseFromParent();
132}
133
134/// Removes out-of-chunk operand bundles from calls.
135static void extractOperandBundesFromModule(std::vector<Chunk> ChunksToKeep,
136 Module *Program) {
137 OperandBundleRemapper R(ChunksToKeep);
138 R.visit(Program);
139
140 for_each(R.CallsToRefine, [](const auto &P) {
141 return maybeRewriteCallWithDifferentBundles(P.first, P.second);
142 });
143}
144
145/// Counts the amount of operand bundles.
146static int countOperandBundes(Module *Program) {
147 OperandBundleCounter C;
148
149 // TODO: Silence index with --quiet flag
150 outs() << "----------------------------\n";
151 C.visit(Program);
152 outs() << "Number of operand bundles: " << C.OperandBundeCount << "\n";
153
154 return C.OperandBundeCount;
155}
156
157void llvm::reduceOperandBundesDeltaPass(TestRunner &Test) {
158 outs() << "*** Reducing OperandBundes...\n";
159 int OperandBundeCount = countOperandBundes(Test.getProgram());
160 runDeltaPass(Test, OperandBundeCount, extractOperandBundesFromModule);
161}