summaryrefslogtreecommitdiff
path: root/src/passes/ConstantFieldPropagation.cpp
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2021-10-27 14:10:30 -0700
committerGitHub <noreply@github.com>2021-10-27 14:10:30 -0700
commit5ec74cf79590648f3a633387f0d1d8a5930a410b (patch)
tree62e489dc604344f77324d526b64ed4253b977911 /src/passes/ConstantFieldPropagation.cpp
parent2b24537425ada522d892c7180a06aedc1c94509e (diff)
downloadbinaryen-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.cpp66
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,