Skip to content

Commit 2f5774c

Browse files
authored
Some minor LocalGraph improvements (#1625)
* Remove the Action class - we just need a pointer to a get or set. This simplifies the code and saves a little memory, but doesn't seem to have any impact on speed. * Miscellaneous code style and comment changes.
1 parent 5852b13 commit 2f5774c

File tree

1 file changed

+57
-80
lines changed

1 file changed

+57
-80
lines changed

src/ir/LocalGraph.cpp

Lines changed: 57 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -26,39 +26,10 @@ namespace wasm {
2626

2727
namespace LocalGraphInternal {
2828

29-
// A relevant action. Supports a get, a set, or an
30-
// "other" which can be used for other purposes, to mark
31-
// their position in a block
32-
struct Action {
33-
enum What {
34-
Get = 0,
35-
Set = 1
36-
};
37-
What what;
38-
Index index; // the local index read or written
39-
Expression* expr; // the expression itself
40-
41-
Action(What what, Index index, Expression* expr) : what(what), index(index), expr(expr) {
42-
if (what == Get) assert(expr->is<GetLocal>());
43-
if (what == Set) assert(expr->is<SetLocal>());
44-
}
45-
46-
bool isGet() { return what == Get; }
47-
bool isSet() { return what == Set; }
48-
};
49-
50-
// information about a basic block
29+
// Information about a basic block.
5130
struct Info {
52-
std::vector<Action> actions; // actions occurring in this block
31+
std::vector<Expression*> actions; // actions occurring in this block: get_locals and set_locals
5332
std::unordered_map<Index, SetLocal*> lastSets; // for each index, the last set_local for it
54-
55-
void dump(Function* func) {
56-
if (actions.empty()) return;
57-
std::cout << " actions:\n";
58-
for (auto& action : actions) {
59-
std::cout << " " << (action.isGet() ? "get" : "set") << " " << func->getLocalName(action.index) << "\n";
60-
}
61-
}
6233
};
6334

6435
// flow helper class. flows the gets to their sets
@@ -85,69 +56,72 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> {
8556
auto* curr = (*currp)->cast<GetLocal>();
8657
// if in unreachable code, skip
8758
if (!self->currBasicBlock) return;
88-
self->currBasicBlock->contents.actions.emplace_back(Action::Get, curr->index, curr);
59+
self->currBasicBlock->contents.actions.emplace_back(curr);
8960
self->locations[curr] = currp;
9061
}
9162

9263
static void doVisitSetLocal(Flower* self, Expression** currp) {
9364
auto* curr = (*currp)->cast<SetLocal>();
9465
// if in unreachable code, skip
9566
if (!self->currBasicBlock) return;
96-
self->currBasicBlock->contents.actions.emplace_back(Action::Set, curr->index, curr);
67+
self->currBasicBlock->contents.actions.emplace_back(curr);
9768
self->currBasicBlock->contents.lastSets[curr->index] = curr;
9869
self->locations[curr] = currp;
9970
}
10071

10172
void flow(Function* func) {
10273
// This block struct is optimized for this flow process (Minimal information, iteration index).
10374
struct FlowBlock {
104-
// last Traversed Iteration
105-
// This value help us to find if this block has been seen while traversing blocks.
75+
// Last Traversed Iteration: This value helps us to find if this block has been seen while traversing blocks.
10676
// We compare this value to the current iteration index in order to determine if we already process this block in the current iteration.
107-
// This speed up the processing compared to unordered_set or other struct usage. (No need to reset internal values, lookup into container, ...)
77+
// This speeds up the processing compared to unordered_set or other struct usage. (No need to reset internal values, lookup into container, ...)
10878
size_t lastTraversedIteration;
109-
std::vector<Action> actions; // actions occurring in this block
79+
std::vector<Expression*> actions;
11080
std::vector<FlowBlock*> in;
111-
// for each index, the last set_local for it
112-
// The unordered_map from BasicBlock is converted ther into a vector
113-
// This speed up search as there are almost always fewer than 100 items
114-
std::vector<std::pair<Index, SetLocal*>> lastSets;
81+
// Sor each index, the last set_local for it
82+
// The unordered_map from BasicBlock.Info is converted into a vector
83+
// This speeds up search as there are usually few sets in a block, so just scanning
84+
// them linearly is efficient, avoiding hash computations (while in Info,
85+
// it's convenient to have a map so we can assign them easily, where
86+
// the last one seen overwrites the previous; and, we do that O(1)).
87+
std::vector<std::pair<Index, SetLocal*>> lastSets;
11588
};
11689

11790
auto numLocals = func->getNumLocals();
11891
std::vector<std::vector<GetLocal*>> allGets;
11992
allGets.resize(numLocals);
12093
std::vector<FlowBlock*> work;
12194

122-
123-
// Converts input blocks (basicBlocks) into more efficient blocks to improve memory access.
95+
// Convert input blocks (basicBlocks) into more efficient flow blocks to improve memory access.
12496
std::vector<FlowBlock> flowBlocks;
12597
flowBlocks.resize(basicBlocks.size());
12698

12799
// Init mapping between basicblocks and flowBlocks
128100
std::unordered_map<BasicBlock*, FlowBlock*> basicToFlowMap;
129-
for (size_t i = 0; i < basicBlocks.size(); ++i) {
130-
basicToFlowMap.emplace(std::make_pair(basicBlocks[i].get(), &flowBlocks[i]));
101+
for (Index i = 0; i < basicBlocks.size(); ++i) {
102+
basicToFlowMap[basicBlocks[i].get()] = &flowBlocks[i];
131103
}
132104

105+
const size_t NULL_ITERATION = -1;
106+
133107
FlowBlock* entryFlowBlock = nullptr;
134-
for (size_t i = 0; i < flowBlocks.size(); ++i) {
135-
auto& optBlock = flowBlocks[i];
136-
auto& inBlock = basicBlocks[i];
108+
for (Index i = 0; i < flowBlocks.size(); ++i) {
109+
auto& block = basicBlocks[i];
110+
auto& flowBlock = flowBlocks[i];
137111
// Get the equivalent block to entry in the flow list
138-
if (inBlock.get() == entry) entryFlowBlock = &optBlock;
139-
// Initialize iteration index to max size_t to ensure we don't miss a block from wrong value.
140-
optBlock.lastTraversedIteration = -1;
141-
optBlock.actions.swap(inBlock->contents.actions);
112+
if (block.get() == entry) entryFlowBlock = &flowBlock;
113+
flowBlock.lastTraversedIteration = NULL_ITERATION;
114+
flowBlock.actions.swap(block->contents.actions);
142115
// Map in block to flow blocks
143-
auto& inBlocks = inBlock->in;
144-
optBlock.in.resize(inBlocks.size());
145-
std::transform(inBlocks.begin(), inBlocks.end(), optBlock.in.begin(), [&](BasicBlock* block) { return basicToFlowMap[block]; });
146-
147-
// Convert unordered_map to vector
148-
optBlock.lastSets.reserve(inBlock->contents.lastSets.size());
149-
for (auto set : inBlock->contents.lastSets) {
150-
optBlock.lastSets.emplace_back(std::make_pair(set.first, set.second));
116+
auto& in = block->in;
117+
flowBlock.in.resize(in.size());
118+
std::transform(in.begin(), in.end(), flowBlock.in.begin(), [&](BasicBlock* block) {
119+
return basicToFlowMap[block];
120+
});
121+
// Convert unordered_map to vector.
122+
flowBlock.lastSets.reserve(block->contents.lastSets.size());
123+
for (auto set : block->contents.lastSets) {
124+
flowBlock.lastSets.emplace_back(std::make_pair(set.first, set.second));
151125
}
152126
}
153127
assert(entryFlowBlock != nullptr);
@@ -157,7 +131,7 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> {
157131
#ifdef LOCAL_GRAPH_DEBUG
158132
std::cout << "basic block " << block.get() << " :\n";
159133
for (auto& action : block->contents.actions) {
160-
std::cout << " action: " << action.expr << '\n';
134+
std::cout << " action: " << *action << '\n';
161135
}
162136
for (auto* lastSet : block->contents.lastSets) {
163137
std::cout << " last set " << lastSet << '\n';
@@ -168,53 +142,56 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> {
168142
auto& actions = block.actions;
169143
// move towards the front, handling things as we go
170144
for (int i = int(actions.size()) - 1; i >= 0; i--) {
171-
auto& action = actions[i];
172-
auto index = action.index;
173-
if (action.isGet()) {
174-
allGets[index].push_back(action.expr->cast<GetLocal>());
145+
auto* action = actions[i];
146+
if (auto* get = action->dynCast<GetLocal>()) {
147+
allGets[get->index].push_back(get);
175148
} else {
176-
// this set is the only set for all those gets
177-
auto* set = action.expr->cast<SetLocal>();
178-
auto& gets = allGets[index];
149+
// This set is the only set for all those gets.
150+
auto* set = action->cast<SetLocal>();
151+
auto& gets = allGets[set->index];
179152
for (auto* get : gets) {
180153
getSetses[get].insert(set);
181154
}
182155
gets.clear();
183156
}
184157
}
185-
// if anything is left, we must flow it back through other blocks. we
186-
// can do that for all gets as a whole, they will get the same results
158+
// If anything is left, we must flow it back through other blocks. we
159+
// can do that for all gets as a whole, they will get the same results.
187160
for (Index index = 0; index < numLocals; index++) {
188161
auto& gets = allGets[index];
189162
if (gets.empty()) continue;
190163
work.push_back(&block);
191-
192-
// note that we may need to revisit the later parts of this initial
193-
// block, if we are in a loop, so don't mark it as seen
164+
// Note that we may need to revisit the later parts of this initial
165+
// block, if we are in a loop, so don't mark it as seen.
194166
while (!work.empty()) {
195167
auto* curr = work.back();
196168
work.pop_back();
197-
// we have gone through this block; now we must handle flowing to
198-
// the inputs
169+
// We have gone through this block; now we must handle flowing to
170+
// the inputs.
199171
if (curr->in.empty()) {
200172
if (curr == entryFlowBlock) {
201-
// these receive a param or zero init value
173+
// These receive a param or zero init value.
202174
for (auto* get : gets) {
203175
getSetses[get].insert(nullptr);
204176
}
205177
}
206178
} else {
207179
for (auto* pred : curr->in) {
208-
if (pred->lastTraversedIteration == currentIteration) continue;
180+
if (pred->lastTraversedIteration == currentIteration) {
181+
// We've already seen pred in this iteration.
182+
continue;
183+
}
209184
pred->lastTraversedIteration = currentIteration;
210-
auto lastSet = std::find_if(pred->lastSets.begin(), pred->lastSets.end(), [&](std::pair<Index, SetLocal*>& value) { return value.first == index; });
185+
auto lastSet = std::find_if(pred->lastSets.begin(), pred->lastSets.end(), [&](std::pair<Index, SetLocal*>& value) {
186+
return value.first == index;
187+
});
211188
if (lastSet != pred->lastSets.end()) {
212-
// there is a set here, apply it, and stop the flow
189+
// There is a set here, apply it, and stop the flow.
213190
for (auto* get : gets) {
214191
getSetses[get].insert(lastSet->second);
215192
}
216193
} else {
217-
// keep on flowing
194+
// Keep on flowing.
218195
work.push_back(pred);
219196
}
220197
}

0 commit comments

Comments
 (0)