diff options
author | Alon Zakai <azakai@google.com> | 2021-10-27 14:10:30 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-27 14:10:30 -0700 |
commit | 5ec74cf79590648f3a633387f0d1d8a5930a410b (patch) | |
tree | 62e489dc604344f77324d526b64ed4253b977911 /src/passes/ConstantFieldPropagation.cpp | |
parent | 2b24537425ada522d892c7180a06aedc1c94509e (diff) | |
download | binaryen-5ec74cf79590648f3a633387f0d1d8a5930a410b.tar.gz binaryen-5ec74cf79590648f3a633387f0d1d8a5930a410b.tar.bz2 binaryen-5ec74cf79590648f3a633387f0d1d8a5930a410b.zip |
[Wasm GC] Constant Field Propagation: Handle immutable globals (#4258)
If we write an immutable global to a field, and that is the only thing
we ever write, then we can replace reads of the field with a get of
the global. To do that, this tracks immutable globals written to
fields and not just constant values.
Normally this is not needed, as if the global is immutable then we
propagate its constant value to everywhere anyhow. However, for
references this is useful: If we have a global immutable vtable,
for example, then we cannot replace a get of it with a constant.
So this PR helps with immutable reference types in globals, allowing
us to propagate global.gets to them to more places, which then
can allow optimizations there.
This + later opts removes 25% of array.gets from j2wasm. I believe
almost all of those are itable calls, so this means those are getting
devirtualized now. I see something like a 5% speedup due to that.
Diffstat (limited to 'src/passes/ConstantFieldPropagation.cpp')
-rw-r--r-- | src/passes/ConstantFieldPropagation.cpp | 66 |
1 files changed, 49 insertions, 17 deletions
diff --git a/src/passes/ConstantFieldPropagation.cpp b/src/passes/ConstantFieldPropagation.cpp index 7b13b1766..f4476ef04 100644 --- a/src/passes/ConstantFieldPropagation.cpp +++ b/src/passes/ConstantFieldPropagation.cpp @@ -53,20 +53,23 @@ struct Many : public std::monostate {}; // Represents data about what constant values are possible in a particular // place. There may be no values, or one, or many, or if a non-constant value is // possible, then all we can say is that the value is "unknown" - it can be -// anything. +// anything. The values can either be literal values (Literal) or the names of +// immutable globals (Name). // // Currently this just looks for a single constant value, and even two constant // values are treated as unknown. It may be worth optimizing more than that TODO struct PossibleConstantValues { private: - std::variant<None, Literal, Many> value; + using Variant = std::variant<None, Literal, Name, Many>; + Variant value; public: PossibleConstantValues() : value(None()) {} // Note a written value as we see it, and update our internal knowledge based - // on it and all previous values noted. - void note(Literal curr) { + // on it and all previous values noted. This can be called using either a + // Literal or a Name, so it uses a template. + template<typename T> void note(T curr) { if (std::get_if<None>(&value)) { // This is the first value. value = curr; @@ -79,9 +82,9 @@ public: } // This is a subsequent value. Check if it is different from all previous - // ones, and if so, we now represent many possible values. - if (curr != std::get<Literal>(value)) { - value = Many(); + // ones. + if (Variant(curr) != value) { + noteUnknown(); } } @@ -117,14 +120,25 @@ public: } // Check if all the values are identical and constant. - bool isConstant() const { return std::get_if<Literal>(&value); } + bool isConstant() const { + return !std::get_if<None>(&value) && !std::get_if<Many>(&value); + } + + bool isConstantLiteral() const { return std::get_if<Literal>(&value); } + + bool isConstantGlobal() const { return std::get_if<Name>(&value); } // Returns the single constant value. - Literal getConstantValue() const { + Literal getConstantLiteral() const { assert(isConstant()); return std::get<Literal>(value); } + Name getConstantGlobal() const { + assert(isConstant()); + return std::get<Name>(value); + } + // Returns whether we have ever noted a value. bool hasNoted() const { return !std::get_if<None>(&value); } @@ -134,8 +148,10 @@ public: o << "unwritten"; } else if (!isConstant()) { o << "unknown"; - } else { - o << getConstantValue(); + } else if (isConstantLiteral()) { + o << getConstantLiteral(); + } else if (isConstantGlobal()) { + o << '$' << getConstantGlobal(); } o << ']'; } @@ -200,9 +216,14 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> { // ref.as_non_null (we need to trap as the get would have done so), plus the // constant value. (Leave it to further optimizations to get rid of the // ref.) + Expression* value; + if (info.isConstantLiteral()) { + value = builder.makeConstantExpression(info.getConstantLiteral()); + } else { + value = builder.makeGlobalGet(info.getConstantGlobal(), curr->type); + } replaceCurrent(builder.makeSequence( - builder.makeDrop(builder.makeRefAs(RefAsNonNull, curr->ref)), - builder.makeConstantExpression(info.getConstantValue()))); + builder.makeDrop(builder.makeRefAs(RefAsNonNull, curr->ref)), value)); changed = true; } @@ -236,12 +257,23 @@ struct PCVScanner : public Scanner<PossibleConstantValues, PCVScanner> { HeapType type, Index index, PossibleConstantValues& info) { - - if (!Properties::isConstantExpression(expr)) { - info.noteUnknown(); - } else { + // If this is a constant literal value, note that. + if (Properties::isConstantExpression(expr)) { info.note(Properties::getLiteral(expr)); + return; + } + + // If this is an immutable global that we get, note that. + if (auto* get = expr->dynCast<GlobalGet>()) { + auto* global = getModule()->getGlobal(get->name); + if (global->mutable_ == Immutable) { + info.note(get->name); + return; + } } + + // Otherwise, this is not something we can reason about. + info.noteUnknown(); } void noteDefault(Type fieldType, |