summaryrefslogtreecommitdiff
path: root/src/passes/Inlining.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/passes/Inlining.cpp')
-rw-r--r--src/passes/Inlining.cpp113
1 files changed, 67 insertions, 46 deletions
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp
index f801662e0..681109af8 100644
--- a/src/passes/Inlining.cpp
+++ b/src/passes/Inlining.cpp
@@ -32,14 +32,14 @@
#include <atomic>
-#include "wasm.h"
-#include "pass.h"
-#include "wasm-builder.h"
#include "ir/literal-utils.h"
#include "ir/module-utils.h"
#include "ir/utils.h"
#include "parsing.h"
+#include "pass.h"
#include "passes/opt-utils.h"
+#include "wasm-builder.h"
+#include "wasm.h"
namespace wasm {
@@ -79,25 +79,30 @@ struct FunctionInfo {
bool worthInlining(PassOptions& options) {
// if it's big, it's just not worth doing (TODO: investigate more)
- if (size > FLEXIBLE_SIZE_LIMIT) return false;
+ if (size > FLEXIBLE_SIZE_LIMIT)
+ return false;
// if it's so small we have a guarantee that after we optimize the
// size will not increase, inline it
- if (size <= INLINING_OPTIMIZING_WILL_DECREASE_SIZE_LIMIT) return true;
+ if (size <= INLINING_OPTIMIZING_WILL_DECREASE_SIZE_LIMIT)
+ return true;
// if it has one use, then inlining it would likely reduce code size
// since we are just moving code around, + optimizing, so worth it
// if small enough that we are pretty sure its ok
- if (calls == 1 && !usedGlobally && size <= CAREFUL_SIZE_LIMIT) return true;
+ if (calls == 1 && !usedGlobally && size <= CAREFUL_SIZE_LIMIT)
+ return true;
// more than one use, so we can't eliminate it after inlining,
// so only worth it if we really care about speed and don't care
// about size, and if it's lightweight so a good candidate for
// speeding us up.
- return options.optimizeLevel >= 3 && options.shrinkLevel == 0 && lightweight;
+ return options.optimizeLevel >= 3 && options.shrinkLevel == 0 &&
+ lightweight;
}
};
typedef std::unordered_map<Name, FunctionInfo> NameInfoMap;
-struct FunctionInfoScanner : public WalkerPass<PostWalker<FunctionInfoScanner>> {
+struct FunctionInfoScanner
+ : public WalkerPass<PostWalker<FunctionInfoScanner>> {
bool isFunctionParallel() override { return true; }
FunctionInfoScanner(NameInfoMap* infos) : infos(infos) {}
@@ -112,7 +117,8 @@ struct FunctionInfoScanner : public WalkerPass<PostWalker<FunctionInfoScanner>>
}
void visitCall(Call* curr) {
- assert(infos->count(curr->target) > 0); // can't add a new element in parallel
+ // can't add a new element in parallel
+ assert(infos->count(curr->target) > 0);
(*infos)[curr->target].calls++;
// having a call is not lightweight
(*infos)[getFunction()->name].lightweight = false;
@@ -130,12 +136,14 @@ struct InliningAction {
Expression** callSite;
Function* contents;
- InliningAction(Expression** callSite, Function* contents) : callSite(callSite), contents(contents) {}
+ InliningAction(Expression** callSite, Function* contents)
+ : callSite(callSite), contents(contents) {}
};
struct InliningState {
std::unordered_set<Name> worthInlining;
- std::unordered_map<Name, std::vector<InliningAction>> actionsForFunction; // function name => actions that can be performed in it
+ // function name => actions that can be performed in it
+ std::unordered_map<Name, std::vector<InliningAction>> actionsForFunction;
};
struct Planner : public WalkerPass<PostWalker<Planner>> {
@@ -143,30 +151,27 @@ struct Planner : public WalkerPass<PostWalker<Planner>> {
Planner(InliningState* state) : state(state) {}
- Planner* create() override {
- return new Planner(state);
- }
+ Planner* create() override { return new Planner(state); }
void visitCall(Call* curr) {
// plan to inline if we know this is valid to inline, and if the call is
// actually performed - if it is dead code, it's pointless to inline.
// we also cannot inline ourselves.
- if (state->worthInlining.count(curr->target) &&
- curr->type != unreachable &&
+ if (state->worthInlining.count(curr->target) && curr->type != unreachable &&
curr->target != getFunction()->name) {
- // nest the call in a block. that way the location of the pointer to the call will not
- // change even if we inline multiple times into the same function, otherwise
- // call1(call2()) might be a problem
+ // nest the call in a block. that way the location of the pointer to the
+ // call will not change even if we inline multiple times into the same
+ // function, otherwise call1(call2()) might be a problem
auto* block = Builder(*getModule()).makeBlock(curr);
replaceCurrent(block);
- assert(state->actionsForFunction.count(getFunction()->name) > 0); // can't add a new element in parallel
- state->actionsForFunction[getFunction()->name].emplace_back(&block->list[0], getModule()->getFunction(curr->target));
+ // can't add a new element in parallel
+ assert(state->actionsForFunction.count(getFunction()->name) > 0);
+ state->actionsForFunction[getFunction()->name].emplace_back(
+ &block->list[0], getModule()->getFunction(curr->target));
}
}
- void doWalkFunction(Function* func) {
- walk(func->body);
- }
+ void doWalkFunction(Function* func) { walk(func->body); }
private:
InliningState* state;
@@ -174,7 +179,8 @@ private:
// Core inlining logic. Modifies the outside function (adding locals as
// needed), and returns the inlined code.
-static Expression* doInlining(Module* module, Function* into, InliningAction& action) {
+static Expression*
+doInlining(Module* module, Function* into, InliningAction& action) {
Function* from = action.contents;
auto* call = (*action.callSite)->cast<Call>();
Builder builder(*module);
@@ -204,11 +210,15 @@ static Expression* doInlining(Module* module, Function* into, InliningAction& ac
}
// assign the operands into the params
for (Index i = 0; i < from->params.size(); i++) {
- block->list.push_back(builder.makeSetLocal(updater.localMapping[i], call->operands[i]));
+ block->list.push_back(
+ builder.makeSetLocal(updater.localMapping[i], call->operands[i]));
}
- // zero out the vars (as we may be in a loop, and may depend on their zero-init value
+ // zero out the vars (as we may be in a loop, and may depend on their
+ // zero-init value
for (Index i = 0; i < from->vars.size(); i++) {
- block->list.push_back(builder.makeSetLocal(updater.localMapping[from->getVarIndexBase() + i], LiteralUtils::makeZero(from->vars[i], *module)));
+ block->list.push_back(
+ builder.makeSetLocal(updater.localMapping[from->getVarIndexBase() + i],
+ LiteralUtils::makeZero(from->vars[i], *module)));
}
// generate and update the inlined contents
auto* contents = ExpressionManipulator::copy(from->body, *module);
@@ -246,7 +256,8 @@ struct Inlining : public Pass {
// can look like it is worth inlining)
while (iterationNumber <= numFunctions) {
#ifdef INLINING_DEBUG
- std::cout << "inlining loop iter " << iterationNumber << " (numFunctions: " << numFunctions << ")\n";
+ std::cout << "inlining loop iter " << iterationNumber
+ << " (numFunctions: " << numFunctions << ")\n";
#endif
calculateInfos(module);
if (!iteration(runner, module)) {
@@ -258,7 +269,8 @@ struct Inlining : public Pass {
void calculateInfos(Module* module) {
infos.clear();
- // fill in info, as we operate on it in parallel (each function to its own entry)
+ // fill in info, as we operate on it in parallel (each function to its own
+ // entry)
for (auto& func : module->functions) {
infos[func->name];
}
@@ -288,8 +300,10 @@ struct Inlining : public Pass {
state.worthInlining.insert(func->name);
}
});
- if (state.worthInlining.size() == 0) return false;
- // fill in actionsForFunction, as we operate on it in parallel (each function to its own entry)
+ if (state.worthInlining.size() == 0)
+ return false;
+ // fill in actionsForFunction, as we operate on it in parallel (each
+ // function to its own entry)
for (auto& func : module->functions) {
state.actionsForFunction[func->name];
}
@@ -302,20 +316,23 @@ struct Inlining : public Pass {
}
// perform inlinings TODO: parallelize
std::unordered_map<Name, Index> inlinedUses; // how many uses we inlined
- std::unordered_set<Function*> inlinedInto; // which functions were inlined into
+ // which functions were inlined into
+ std::unordered_set<Function*> inlinedInto;
for (auto& func : module->functions) {
// if we've inlined a function, don't inline into it in this iteration,
// avoid risk of races
// note that we do not risk stalling progress, as each iteration() will
// inline at least one call before hitting this
- if (inlinedUses.count(func->name)) continue;
+ if (inlinedUses.count(func->name))
+ continue;
for (auto& action : state.actionsForFunction[func->name]) {
auto* inlinedFunction = action.contents;
// if we've inlined into a function, don't inline it in this iteration,
// avoid risk of races
// note that we do not risk stalling progress, as each iteration() will
// inline at least one call before hitting this
- if (inlinedInto.count(inlinedFunction)) continue;
+ if (inlinedInto.count(inlinedFunction))
+ continue;
Name inlinedName = inlinedFunction->name;
#ifdef INLINING_DEBUG
std::cout << "inline " << inlinedName << " into " << func->name << '\n';
@@ -335,23 +352,28 @@ struct Inlining : public Pass {
}
// remove functions that we no longer need after inlining
auto& funcs = module->functions;
- funcs.erase(std::remove_if(funcs.begin(), funcs.end(), [&](const std::unique_ptr<Function>& curr) {
- auto name = curr->name;
- auto& info = infos[name];
- bool canRemove = inlinedUses.count(name) && inlinedUses[name] == info.calls && !info.usedGlobally;
+ funcs.erase(std::remove_if(funcs.begin(),
+ funcs.end(),
+ [&](const std::unique_ptr<Function>& curr) {
+ auto name = curr->name;
+ auto& info = infos[name];
+ bool canRemove =
+ inlinedUses.count(name) &&
+ inlinedUses[name] == info.calls &&
+ !info.usedGlobally;
#ifdef INLINING_DEBUG
- if (canRemove) std::cout << "removing " << name << '\n';
+ if (canRemove)
+ std::cout << "removing " << name << '\n';
#endif
- return canRemove;
- }), funcs.end());
+ return canRemove;
+ }),
+ funcs.end());
// return whether we did any work
return inlinedUses.size() > 0;
}
};
-Pass* createInliningPass() {
- return new Inlining();
-}
+Pass* createInliningPass() { return new Inlining(); }
Pass* createInliningOptimizingPass() {
auto* ret = new Inlining();
@@ -360,4 +382,3 @@ Pass* createInliningOptimizingPass() {
}
} // namespace wasm
-