summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/type-updating.cpp10
-rw-r--r--src/passes/Flatten.cpp8
-rw-r--r--src/passes/LocalSubtyping.cpp26
-rw-r--r--src/passes/OptimizeInstructions.cpp12
-rw-r--r--src/tools/tool-options.h1
-rw-r--r--src/wasm-features.h45
-rw-r--r--src/wasm/wasm-binary.cpp4
-rw-r--r--src/wasm/wasm-validator.cpp46
8 files changed, 28 insertions, 124 deletions
diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp
index ac1f4b3af..0bc14763b 100644
--- a/src/ir/type-updating.cpp
+++ b/src/ir/type-updating.cpp
@@ -307,10 +307,6 @@ bool canHandleAsLocal(Type type) {
}
void handleNonDefaultableLocals(Function* func, Module& wasm) {
- if (wasm.features.hasGCNNLocals()) {
- // We have nothing to fix up: all locals are allowed.
- return;
- }
if (!wasm.features.hasReferenceTypes()) {
// No references, so no non-nullable ones at all.
return;
@@ -404,9 +400,6 @@ void handleNonDefaultableLocals(Function* func, Module& wasm) {
Type getValidLocalType(Type type, FeatureSet features) {
assert(type.isConcrete());
- if (features.hasGCNNLocals()) {
- return type;
- }
if (type.isNonNullable()) {
return Type(type.getHeapType(), Nullable);
}
@@ -421,9 +414,6 @@ Type getValidLocalType(Type type, FeatureSet features) {
}
Expression* fixLocalGet(LocalGet* get, Module& wasm) {
- if (wasm.features.hasGCNNLocals()) {
- return get;
- }
if (get->type.isNonNullable()) {
// The get should now return a nullable value, and a ref.as_non_null
// fixes that up.
diff --git a/src/passes/Flatten.cpp b/src/passes/Flatten.cpp
index e8dec3010..3c0aa78a0 100644
--- a/src/passes/Flatten.cpp
+++ b/src/passes/Flatten.cpp
@@ -33,11 +33,9 @@
// )
// )
//
-// The tuple has a non-nullable type, and so it cannot be set to a local. We
-// would need to split up the tuple and reconstruct it later, but that would
-// require allowing tuple operations in more nested places than Flat IR allows
-// today. For now, error on this; eventually changes in the spec regarding
-// null-nullability may make this easier.
+// The tuple has a non-nullable type, and so it cannot currently be set to a
+// local, but in principle there's no reason it couldn't be. For now, error on
+// this.
#include <ir/branch-utils.h>
#include <ir/effects.h>
diff --git a/src/passes/LocalSubtyping.cpp b/src/passes/LocalSubtyping.cpp
index eeaaaaefa..ad0cfa7d2 100644
--- a/src/passes/LocalSubtyping.cpp
+++ b/src/passes/LocalSubtyping.cpp
@@ -78,27 +78,11 @@ struct LocalSubtyping : public WalkerPass<PostWalker<LocalSubtyping>> {
// Find which vars can be non-nullable.
std::unordered_set<Index> cannotBeNonNullable;
- if (getModule()->features.hasGCNNLocals()) {
- // If the feature is enabled then the only constraint is being able to
- // read the default value - if it is readable, the local cannot become
- // non-nullable.
- for (auto& [get, sets] : localGraph.getSetses) {
- auto index = get->index;
- if (func->isVar(index) &&
- std::any_of(sets.begin(), sets.end(), [&](LocalSet* set) {
- return set == nullptr;
- })) {
- cannotBeNonNullable.insert(index);
- }
- }
- } else {
- // Without GCNNLocals, validation rules follow the spec rules: all gets
- // must be dominated structurally by sets, for the local to be non-
- // nullable.
- LocalStructuralDominance info(func, *getModule());
- for (auto index : info.nonDominatingIndices) {
- cannotBeNonNullable.insert(index);
- }
+ // All gets must be dominated structurally by sets for the local to be non-
+ // nullable.
+ LocalStructuralDominance info(func, *getModule());
+ for (auto index : info.nonDominatingIndices) {
+ cannotBeNonNullable.insert(index);
}
auto varBase = func->getVarIndexBase();
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index f15bac23d..0b787105e 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -1155,17 +1155,9 @@ struct OptimizeInstructions
void visitLocalSet(LocalSet* curr) {
// Interactions between local.set/tee and ref.as_non_null can be optimized
- // in some cases, by removing or moving the ref.as_non_null operation. In
- // all cases, we only do this when we do *not* allow non-nullable locals. If
- // we do allow such locals, then (1) this local might be non-nullable, so we
- // can't remove or move a ref.as_non_null flowing into a local.set/tee, and
- // (2) even if the local were nullable, if we change things we might prevent
- // the LocalSubtyping pass from turning it into a non-nullable local later.
- // Note that we must also check if this local is nullable regardless, as a
- // parameter might be non-nullable even if nullable locals are disallowed
- // (as that just affects vars, and not params).
+ // in some cases, by removing or moving the ref.as_non_null operation.
if (auto* as = curr->value->dynCast<RefAs>()) {
- if (as->op == RefAsNonNull && !getModule()->features.hasGCNNLocals() &&
+ if (as->op == RefAsNonNull &&
getFunction()->getLocalType(curr->index).isNullable()) {
// (local.tee (ref.as_non_null ..))
// =>
diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h
index 6f3b4968c..f7dd791b4 100644
--- a/src/tools/tool-options.h
+++ b/src/tools/tool-options.h
@@ -89,7 +89,6 @@ struct ToolOptions : public Options {
.addFeature(FeatureSet::Multivalue, "multivalue functions")
.addFeature(FeatureSet::GC, "garbage collection")
.addFeature(FeatureSet::Memory64, "memory64")
- .addFeature(FeatureSet::GCNNLocals, "GC non-null locals")
.addFeature(FeatureSet::RelaxedSIMD, "relaxed SIMD")
.addFeature(FeatureSet::ExtendedConst, "extended const expressions")
.addFeature(FeatureSet::Strings, "strings")
diff --git a/src/wasm-features.h b/src/wasm-features.h
index 197b6eb36..f8d006e78 100644
--- a/src/wasm-features.h
+++ b/src/wasm-features.h
@@ -40,21 +40,15 @@ struct FeatureSet {
Multivalue = 1 << 9,
GC = 1 << 10,
Memory64 = 1 << 11,
- // TODO: Remove this feature when the wasm spec stabilizes.
- GCNNLocals = 1 << 12,
- RelaxedSIMD = 1 << 13,
- ExtendedConst = 1 << 14,
- Strings = 1 << 15,
- MultiMemory = 1 << 16,
+ RelaxedSIMD = 1 << 12,
+ ExtendedConst = 1 << 13,
+ Strings = 1 << 14,
+ MultiMemory = 1 << 15,
MVP = None,
// Keep in sync with llvm default features:
// https://github.com/llvm/llvm-project/blob/c7576cb89d6c95f03968076e902d3adfd1996577/clang/lib/Basic/Targets/WebAssembly.cpp#L150-L153
Default = SignExt | MutableGlobals,
- // GCNNLocals are opt-in: merely asking for "All" does not apply them. To
- // get all possible values use AllPossible. See setAll() below for more
- // details.
- All = ((1 << 17) - 1) & ~GCNNLocals,
- AllPossible = (1 << 17) - 1,
+ All = (1 << 16) - 1,
};
static std::string toString(Feature f) {
@@ -83,8 +77,6 @@ struct FeatureSet {
return "gc";
case Memory64:
return "memory64";
- case GCNNLocals:
- return "gc-nn-locals";
case RelaxedSIMD:
return "relaxed-simd";
case ExtendedConst:
@@ -101,7 +93,7 @@ struct FeatureSet {
std::string toString() const {
std::string ret;
uint32_t x = 1;
- while (x & Feature::AllPossible) {
+ while (x & Feature::All) {
if (features & x) {
if (!ret.empty()) {
ret += ", ";
@@ -133,12 +125,11 @@ struct FeatureSet {
bool hasMultivalue() const { return (features & Multivalue) != 0; }
bool hasGC() const { return (features & GC) != 0; }
bool hasMemory64() const { return (features & Memory64) != 0; }
- bool hasGCNNLocals() const { return (features & GCNNLocals) != 0; }
bool hasRelaxedSIMD() const { return (features & RelaxedSIMD) != 0; }
bool hasExtendedConst() const { return (features & ExtendedConst) != 0; }
bool hasStrings() const { return (features & Strings) != 0; }
bool hasMultiMemory() const { return (features & MultiMemory) != 0; }
- bool hasAll() const { return (features & AllPossible) != 0; }
+ bool hasAll() const { return (features & All) != 0; }
void set(FeatureSet f, bool v = true) {
features = v ? (features | f) : (features & ~f);
@@ -155,34 +146,18 @@ struct FeatureSet {
void setMultivalue(bool v = true) { set(Multivalue, v); }
void setGC(bool v = true) { set(GC, v); }
void setMemory64(bool v = true) { set(Memory64, v); }
- void setGCNNLocals(bool v = true) { set(GCNNLocals, v); }
void setRelaxedSIMD(bool v = true) { set(RelaxedSIMD, v); }
void setExtendedConst(bool v = true) { set(ExtendedConst, v); }
void setStrings(bool v = true) { set(Strings, v); }
void setMultiMemory(bool v = true) { set(MultiMemory, v); }
void setMVP() { features = MVP; }
- void setAll() {
- // Do not set GCNNLocals, which forces the user to opt in to that feature
- // explicitly. That is, wasm-opt -all will enable GC but *not* enable
- // non-nullable locals. To get them, do wasm-opt -all --enable-gc-nn-locals
- // FIXME: When the wasm spec stabilizes, this feature will go away, as the
- // non-nullable locals experiment will either become the standard,
- // or it will go away.
- // Leave the old GCNNLocals value unmodified. This makes things like
- // --enable-gc-nn-locals -all work (that is, if we enable the feature,
- // then -all does not disable it; it simply does not enable it by itself).
- auto oldGCNNLocals = hasGCNNLocals();
- features = AllPossible;
- setGCNNLocals(oldGCNNLocals);
- }
+ void setAll() { features = All; }
void enable(const FeatureSet& other) { features |= other.features; }
- void disable(const FeatureSet& other) {
- features = features & ~other.features & AllPossible;
- }
+ void disable(const FeatureSet& other) { features &= ~other.features; }
template<typename F> void iterFeatures(F f) const {
- for (uint32_t feature = MVP + 1; feature < AllPossible; feature <<= 1) {
+ for (uint32_t feature = MVP + 1; feature < All; feature <<= 1) {
if (has(feature)) {
f(static_cast<Feature>(feature));
}
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 011957cd3..7a0a08885 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -2627,9 +2627,7 @@ void WasmBinaryReader::readFunctions() {
}
}
- if (!wasm.features.hasGCNNLocals()) {
- TypeUpdating::handleNonDefaultableLocals(func, wasm);
- }
+ TypeUpdating::handleNonDefaultableLocals(func, wasm);
std::swap(func->epilogLocation, debugLocation);
currFunction = nullptr;
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 289d10b96..d6575f7d4 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -3195,45 +3195,13 @@ void FunctionValidator::visitFunction(Function* curr) {
if (getModule()->features.hasGC()) {
// If we have non-nullable locals, verify that local.get are valid.
- if (!getModule()->features.hasGCNNLocals()) {
- // Without the special GCNNLocals feature, we implement the spec rules,
- // that is, a set allows gets until the end of the block.
- LocalStructuralDominance info(curr, *getModule());
- for (auto index : info.nonDominatingIndices) {
- auto localType = curr->getLocalType(index);
- for (auto type : localType) {
- shouldBeTrue(!type.isNonNullable(),
- index,
- "non-nullable local's sets must dominate gets");
- }
- }
- } else {
- // With the special GCNNLocals feature, we allow gets anywhere, so long as
- // we can prove they cannot read the null value. (TODO: remove this once
- // the spec is stable).
- //
- // This is slow, so only do it if we find such locals exist at all.
- bool hasNNLocals = false;
- for (const auto& var : curr->vars) {
- if (!var.isDefaultable()) {
- hasNNLocals = true;
- break;
- }
- }
- if (hasNNLocals) {
- LocalGraph graph(curr);
- for (auto& [get, sets] : graph.getSetses) {
- auto index = get->index;
- // It is always ok to read nullable locals, and it is always ok to
- // read params even if they are non-nullable.
- if (curr->getLocalType(index).isDefaultable() ||
- curr->isParam(index)) {
- continue;
- }
- for (auto* set : sets) {
- shouldBeTrue(!!set, index, "non-nullable local must not read null");
- }
- }
+ LocalStructuralDominance info(curr, *getModule());
+ for (auto index : info.nonDominatingIndices) {
+ auto localType = curr->getLocalType(index);
+ for (auto type : localType) {
+ shouldBeTrue(!type.isNonNullable(),
+ index,
+ "non-nullable local's sets must dominate gets");
}
}
}