Skip to content

SimplifyGlobals: Apply known constant values in linear traces #2340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 73 additions & 13 deletions src/passes/SimplifyGlobals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,21 @@
// * If an immutable global is a copy of another, use the earlier one,
// to allow removal of the copies later.
// * Apply the constant values of immutable globals.
// * Apply the constant values of previous global.sets, in a linear
// execution trace.
//
// Some globals may not have uses after these changes, which we leave
// to other passes to optimize.
//
// This pass has a "optimize" variant (similar to inlining and DAE)
// that also runs general function optimizations where we managed to replace
// a constant value. That is helpful as such a replacement often opens up
// further optimization opportunities.
//

#include <atomic>

#include "ir/effects.h"
#include "ir/utils.h"
#include "pass.h"
#include "wasm-builder.h"
Expand Down Expand Up @@ -84,26 +92,73 @@ struct GlobalUseModifier : public WalkerPass<PostWalker<GlobalUseModifier>> {
};

struct ConstantGlobalApplier
: public WalkerPass<PostWalker<ConstantGlobalApplier>> {
: public WalkerPass<
LinearExecutionWalker<ConstantGlobalApplier,
UnifiedExpressionVisitor<ConstantGlobalApplier>>> {
bool isFunctionParallel() override { return true; }

ConstantGlobalApplier(NameSet* constantGlobals)
: constantGlobals(constantGlobals) {}
ConstantGlobalApplier(NameSet* constantGlobals, bool optimize)
: constantGlobals(constantGlobals), optimize(optimize) {}

ConstantGlobalApplier* create() override {
return new ConstantGlobalApplier(constantGlobals);
return new ConstantGlobalApplier(constantGlobals, optimize);
}

void visitGlobalGet(GlobalGet* curr) {
if (constantGlobals->count(curr->name)) {
auto* global = getModule()->getGlobal(curr->name);
assert(global->init->is<Const>());
replaceCurrent(ExpressionManipulator::copy(global->init, *getModule()));
void visitExpression(Expression* curr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused about the purpose of optimize, since it isn't used in visitExpression.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's whether to optimize after doing a successful replacement. I'm adding some comments now to clarify.

if (auto* set = curr->dynCast<GlobalSet>()) {
if (auto* c = set->value->dynCast<Const>()) {
currConstantGlobals[set->name] = c->value;
} else {
currConstantGlobals.erase(set->name);
}
return;
} else if (auto* get = curr->dynCast<GlobalGet>()) {
// Check if the global is known to be constant all the time.
if (constantGlobals->count(get->name)) {
auto* global = getModule()->getGlobal(get->name);
assert(global->init->is<Const>());
replaceCurrent(ExpressionManipulator::copy(global->init, *getModule()));
replaced = true;
return;
}
// Check if the global has a known value in this linear trace.
auto iter = currConstantGlobals.find(get->name);
if (iter != currConstantGlobals.end()) {
Builder builder(*getModule());
replaceCurrent(builder.makeConst(iter->second));
replaced = true;
}
return;
}
// Otherwise, invalidate if we need to.
EffectAnalyzer effects(getPassOptions());
effects.visit(curr);
assert(effects.globalsWritten.empty()); // handled above
if (effects.calls) {
currConstantGlobals.clear();
}
}

static void doNoteNonLinear(ConstantGlobalApplier* self, Expression** currp) {
self->currConstantGlobals.clear();
}

void visitFunction(Function* curr) {
if (replaced && optimize) {
PassRunner runner(getModule(), getPassRunner()->options);
runner.setIsNested(true);
runner.addDefaultFunctionOptimizationPasses();
runner.runOnFunction(curr);
}
}

private:
NameSet* constantGlobals;
bool optimize;
bool replaced = false;

// The globals currently constant in the linear trace.
std::map<Name, Literal> currConstantGlobals;
};

} // anonymous namespace
Expand All @@ -113,6 +168,9 @@ struct SimplifyGlobals : public Pass {
Module* module;

GlobalInfoMap map;
bool optimize;

SimplifyGlobals(bool optimize = false) : optimize(optimize) {}

void run(PassRunner* runner_, Module* module_) override {
runner = runner_;
Expand Down Expand Up @@ -214,12 +272,14 @@ struct SimplifyGlobals : public Pass {
constantGlobals.insert(global->name);
}
}
if (!constantGlobals.empty()) {
ConstantGlobalApplier(&constantGlobals).run(runner, module);
}
ConstantGlobalApplier(&constantGlobals, optimize).run(runner, module);
}
};

Pass* createSimplifyGlobalsPass() { return new SimplifyGlobals(); }
Pass* createSimplifyGlobalsPass() { return new SimplifyGlobals(false); }

Pass* createSimplifyGlobalsOptimizingPass() {
return new SimplifyGlobals(true);
}

} // namespace wasm
10 changes: 9 additions & 1 deletion src/passes/pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ void PassRegistry::registerPasses() {
registerPass("simplify-globals",
"miscellaneous globals-related optimizations",
createSimplifyGlobalsPass);
registerPass("simplify-globals-optimizing",
"miscellaneous globals-related optimizations, and optimizes "
"where we replaced global.gets with constants",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe say "optimizes locally" or similar to indicate the difference between these additional optimizations and the baseline miscellaneous optimizations?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the point that this could be clearer, but I'm not sure "locally" is the right term...?

This is the same as with inlining and dae - they also have text like "and optimizes where we removed/inlined/replaced" etc. Maybe we can find something better for all of them?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I thought optimize was supposed to control the new linear-execution optimization. This makes more sense now. 👍

createSimplifyGlobalsOptimizingPass);
registerPass("simplify-locals",
"miscellaneous locals-related optimizations",
createSimplifyLocalsPass);
Expand Down Expand Up @@ -416,7 +420,11 @@ void PassRunner::addDefaultGlobalOptimizationPostPasses() {
// optimizations show more functions as duplicate
add("duplicate-function-elimination");
add("duplicate-import-elimination");
add("simplify-globals");
if (options.optimizeLevel >= 2 || options.shrinkLevel >= 2) {
add("simplify-globals-optimizing");
} else {
add("simplify-globals");
}
add("remove-unused-module-elements");
add("memory-packing");
// may allow more inlining/dae/etc., need --converge for that
Expand Down
1 change: 1 addition & 0 deletions src/passes/passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Pass* createRedundantSetEliminationPass();
Pass* createSafeHeapPass();
Pass* createSimplifyLocalsPass();
Pass* createSimplifyGlobalsPass();
Pass* createSimplifyGlobalsOptimizingPass();
Pass* createSimplifyLocalsNoNestingPass();
Pass* createSimplifyLocalsNoTeePass();
Pass* createSimplifyLocalsNoStructurePass();
Expand Down
2 changes: 1 addition & 1 deletion test/passes/O4_disable-bulk-memory.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,7 @@
(i32.const 104)
)
(global.set $global$1
(global.get $global$0)
(i32.const 104)
)
)
(func $null (; 12 ;) (; has Stack IR ;) (type $0)
Expand Down
167 changes: 167 additions & 0 deletions test/passes/simplify-globals-optimizing_enable-mutable-globals.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
(module
(type $FUNCSIG$v (func))
(import "env" "global-1" (global $g1 i32))
(global $g2 i32 (global.get $g1))
(func $foo (; 0 ;) (type $FUNCSIG$v)
(drop
(global.get $g1)
)
(drop
(global.get $g1)
)
)
)
(module
(type $FUNCSIG$v (func))
(import "env" "global-1" (global $g1 i32))
(global $g2 i32 (global.get $g1))
(global $g3 i32 (global.get $g2))
(global $g4 i32 (global.get $g3))
(func $foo (; 0 ;) (type $FUNCSIG$v)
(drop
(global.get $g1)
)
(drop
(global.get $g1)
)
(drop
(global.get $g1)
)
(drop
(global.get $g1)
)
)
)
(module
(import "env" "global-1" (global $g1 (mut i32)))
(global $g2 i32 (global.get $g1))
)
(module
(type $FUNCSIG$v (func))
(import "env" "global-1" (global $g1 i32))
(global $g2 (mut i32) (global.get $g1))
(func $foo (; 0 ;) (type $FUNCSIG$v)
(global.set $g2
(unreachable)
)
)
)
(module
(import "env" "global-1" (global $g1 (mut i32)))
(global $g2 (mut i32) (global.get $g1))
(export "global-2" (global $g2))
)
(module
(type $FUNCSIG$v (func))
(global $g1 i32 (i32.const 1))
(global $g2 i32 (i32.const 1))
(global $g3 f64 (f64.const -3.4))
(global $g4 f64 (f64.const -2.8))
(global $g5 i32 (i32.const 2))
(global $g6 i32 (i32.const 2))
(global $g7 i32 (i32.const 3))
(global $g8 i32 (i32.const 3))
(global $g9 i32 (i32.const 4))
(global $ga (mut i32) (i32.const 4))
(global $gb (mut i32) (i32.const 5))
(global $gc i32 (i32.const 5))
(func $foo (; 0 ;) (type $FUNCSIG$v)
(global.set $ga
(i32.const 6)
)
(global.set $gb
(i32.const 7)
)
)
)
(module
(type $FUNCSIG$ii (func (param i32) (result i32)))
(global $g1 (mut i32) (i32.const 1))
(global $g2 (mut i32) (i32.const 1))
(func $f (; 0 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(global.set $g1
(i32.const 100)
)
(global.set $g2
(local.get $0)
)
(if
(local.get $0)
(return
(i32.const 0)
)
)
(if
(local.tee $0
(i32.add
(global.get $g1)
(global.get $g2)
)
)
(return
(i32.const 1)
)
)
(global.set $g1
(i32.const 200)
)
(global.set $g2
(local.get $0)
)
(i32.add
(global.get $g2)
(i32.const 200)
)
)
)
(module
(type $FUNCSIG$ii (func (param i32) (result i32)))
(global $g1 (mut i32) (i32.const 1))
(global $g2 (mut i32) (i32.const 1))
(func $f (; 0 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(global.set $g1
(i32.const 100)
)
(global.set $g2
(local.get $0)
)
(i32.add
(global.get $g2)
(i32.const 200)
)
)
)
(module
(type $FUNCSIG$ii (func (param i32) (result i32)))
(global $g1 (mut i32) (i32.const 1))
(global $g2 (mut i32) (i32.const 1))
(func $no (; 0 ;) (type $FUNCSIG$ii) (param $x i32) (result i32)
(global.set $g1
(i32.const 100)
)
(drop
(call $no
(i32.const 200)
)
)
(global.get $g1)
)
(func $no2 (; 1 ;) (type $FUNCSIG$ii) (param $x i32) (result i32)
(global.set $g1
(i32.const 100)
)
(global.set $g1
(local.get $x)
)
(global.get $g1)
)
(func $yes (; 2 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(global.set $g1
(i32.const 100)
)
(global.set $g2
(local.get $0)
)
(i32.const 100)
)
)
Loading