diff options
author | Alon Zakai <azakai@google.com> | 2023-10-18 13:57:04 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-18 13:57:04 -0700 |
commit | 45f5bd2f440d6db5737947434cecdc4ffa37f567 (patch) | |
tree | ba7ee95312f923f97870d84b96f5268c862959c8 /src | |
parent | ecb3eb3fdb9536472706c173d00b3379e3332469 (diff) | |
download | binaryen-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.cpp | 61 |
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); + } + } } }; |