summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2023-10-18 13:57:04 -0700
committerGitHub <noreply@github.com>2023-10-18 13:57:04 -0700
commit45f5bd2f440d6db5737947434cecdc4ffa37f567 (patch)
treeba7ee95312f923f97870d84b96f5268c862959c8 /src
parentecb3eb3fdb9536472706c173d00b3379e3332469 (diff)
downloadbinaryen-45f5bd2f440d6db5737947434cecdc4ffa37f567.tar.gz
binaryen-45f5bd2f440d6db5737947434cecdc4ffa37f567.tar.bz2
binaryen-45f5bd2f440d6db5737947434cecdc4ffa37f567.zip
SimplifyGlobals: Fold single-use globals to their use (#6023)
For example, (global $a (struct.new $A)) (global $b (struct.new $B (global.get $A))) => (global $b (struct.new $B (struct.new $A))) and the global $a is now unused. This is valid if $a has no other uses. This saves a little in code size, but should not really help otherwise, as we already look through immutable global.get operations in important optimizations. But the code size may matter if there are many such single- use globals.
Diffstat (limited to 'src')
-rw-r--r--src/passes/SimplifyGlobals.cpp61
1 files changed, 61 insertions, 0 deletions
diff --git a/src/passes/SimplifyGlobals.cpp b/src/passes/SimplifyGlobals.cpp
index 02e685ca7..0e66b5926 100644
--- a/src/passes/SimplifyGlobals.cpp
+++ b/src/passes/SimplifyGlobals.cpp
@@ -477,6 +477,11 @@ struct SimplifyGlobals : public Pass {
bool iteration() {
analyze();
+ // Fold single uses first, as it is simple to update the info from analyze()
+ // in this code (and harder to do in the things we do later, which is why we
+ // call analyze from scratch in each iteration).
+ foldSingleUses();
+
// Removing unneeded writes can in some cases lead to more optimizations
// that we need an entire additional iteration to perform, see below.
bool more = removeUnneededWrites();
@@ -672,6 +677,62 @@ struct SimplifyGlobals : public Pass {
}
ConstantGlobalApplier(&constantGlobals, optimize)
.run(getPassRunner(), module);
+ // Note that we don't need to run on module code here, since we already
+ // handle applying constants in globals in propagateConstantsToGlobals (and
+ // in a more sophisticated manner, which takes into account that no sets of
+ // globals are possible during global instantiation).
+ }
+
+ // If we have a global that has a single use in the entire program, we can
+ // fold it into that use, if it is global. For example:
+ //
+ // var x = { foo: 5 };
+ // var y = { bar: x };
+ //
+ // This can become:
+ //
+ // var y = { bar: { foo: 5 } };
+ //
+ // If there is more than one use, or the use is in a function (where it might
+ // execute more than once) then we can't do this.
+ void foldSingleUses() {
+ struct Folder : public PostWalker<Folder> {
+ Module& wasm;
+ GlobalInfoMap& infos;
+
+ Folder(Module& wasm, GlobalInfoMap& infos) : wasm(wasm), infos(infos) {}
+
+ void visitGlobalGet(GlobalGet* curr) {
+ // If this is a get of a global with a single get and no sets, then we
+ // can fold that code into here.
+ auto name = curr->name;
+ auto& info = infos[name];
+ if (info.written == 0 && info.read == 1) {
+ auto* global = wasm.getGlobal(name);
+ if (global->init) {
+ // Copy that global's code. For simplicity we copy it as we have to
+ // keep that global valid for the operations that happen after us,
+ // even though that global will be removed later (we could remove it
+ // here, but it would add more complexity than seems worth it).
+ replaceCurrent(ExpressionManipulator::copy(global->init, wasm));
+
+ // Update info for later parts of this pass: we are removing a
+ // global.get, which is a read, so now there are 0 reads (we also
+ // have 0 writes, so no other work is needed here, but update to
+ // avoid confusion when debugging, and for possible future changes).
+ info.read = 0;
+ }
+ }
+ }
+ };
+
+ Folder folder(*module, map);
+
+ for (auto& global : module->globals) {
+ if (global->init) {
+ folder.walk(global->init);
+ }
+ }
}
};