diff options
author | Alon Zakai <azakai@google.com> | 2021-06-15 11:14:42 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-15 11:14:42 -0700 |
commit | 9d279c08b9f37b6cf2c5a5fac564eee9ea4fb927 (patch) | |
tree | 04ee164199e76d1a09ae4d12c9da820244a297a7 | |
parent | e0a8f40f65b178556f6fcbed778923a36dca64e3 (diff) | |
download | binaryen-9d279c08b9f37b6cf2c5a5fac564eee9ea4fb927.tar.gz binaryen-9d279c08b9f37b6cf2c5a5fac564eee9ea4fb927.tar.bz2 binaryen-9d279c08b9f37b6cf2c5a5fac564eee9ea4fb927.zip |
[Wasm GC] Add experimental support for non-nullable locals (#3932)
This adds a new feature flag, GCNNLocals that enables support for
non-nullable locals. No validation is applied to check that they are
actually assigned before their use yet - this just allows experimentation
to begin.
This feature is not enabled by default even with -all. If we enabled it,
then it would take effect in most of our tests and likely confuse current
users as well. Instead, the flag must be opted in explicitly using
--enable-gc-nn-locals. That is, this is an experimental feature flag,
and as such must be explicitly enabled. (Once the spec stabilizes,
we will remove the feature anyhow when we implement the
final status of non-nullability. )
-rw-r--r-- | src/ir/type-updating.cpp | 3 | ||||
-rw-r--r-- | src/tools/tool-options.h | 9 | ||||
-rw-r--r-- | src/wasm-features.h | 25 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 3 | ||||
-rw-r--r-- | test/binaryen.js/kitchen-sink.js.txt | 2 | ||||
-rw-r--r-- | test/example/c-api-kitchen-sink.txt | 2 | ||||
-rw-r--r-- | test/lit/passes/ssa-gc-nn-locals.wast | 27 |
7 files changed, 62 insertions, 9 deletions
diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp index 658fabc83..2dc60d2df 100644 --- a/src/ir/type-updating.cpp +++ b/src/ir/type-updating.cpp @@ -29,6 +29,9 @@ bool canHandleAsLocal(Type type) { void handleNonDefaultableLocals(Function* func, Module& wasm) { // Check if this is an issue. + if (wasm.features.hasGCNNLocals()) { + return; + } bool hasNonNullable = false; for (auto type : func->vars) { if (type.isRef() && !type.isNullable()) { diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h index 49eca1adc..350b7babd 100644 --- a/src/tools/tool-options.h +++ b/src/tools/tool-options.h @@ -42,7 +42,7 @@ struct ToolOptions : public Options { Arguments::Zero, [this](Options*, const std::string&) { hasFeatureOptions = true; - enabledFeatures.makeMVP(); + enabledFeatures.setMVP(); disabledFeatures.setAll(); }) .add("--all-features", @@ -52,7 +52,7 @@ struct ToolOptions : public Options { [this](Options*, const std::string&) { hasFeatureOptions = true; enabledFeatures.setAll(); - disabledFeatures.makeMVP(); + disabledFeatures.setMVP(); }) .add("--detect-features", "", @@ -61,8 +61,8 @@ struct ToolOptions : public Options { [this](Options*, const std::string&) { hasFeatureOptions = true; detectFeatures = true; - enabledFeatures.makeMVP(); - disabledFeatures.makeMVP(); + enabledFeatures.setMVP(); + disabledFeatures.setMVP(); }) .add("--quiet", "-q", @@ -91,6 +91,7 @@ struct ToolOptions : public Options { .addFeature(FeatureSet::Memory64, "memory64") .addFeature(FeatureSet::TypedFunctionReferences, "typed function references") + .addFeature(FeatureSet::GCNNLocals, "GC non-null locals") .add("--no-validation", "-n", "Disables validation, assumes inputs are correct", diff --git a/src/wasm-features.h b/src/wasm-features.h index 237dc7757..e28dcf4d1 100644 --- a/src/wasm-features.h +++ b/src/wasm-features.h @@ -39,7 +39,9 @@ struct FeatureSet { GC = 1 << 10, Memory64 = 1 << 11, TypedFunctionReferences = 1 << 12, - All = (1 << 13) - 1 + // TODO: Remove this feature when the wasm spec stabilizes. + GCNNLocals = 1 << 13, + All = (1 << 14) - 1 }; static std::string toString(Feature f) { @@ -70,6 +72,8 @@ struct FeatureSet { return "memory64"; case TypedFunctionReferences: return "typed-function-references"; + case GCNNLocals: + return "gc-nn-locals"; default: WASM_UNREACHABLE("unexpected feature"); } @@ -113,9 +117,9 @@ struct FeatureSet { bool hasTypedFunctionReferences() const { return (features & TypedFunctionReferences) != 0; } + bool hasGCNNLocals() const { return (features & GCNNLocals) != 0; } bool hasAll() const { return (features & All) != 0; } - void makeMVP() { features = MVP; } void set(FeatureSet f, bool v = true) { features = v ? (features | f) : (features & ~f); } @@ -134,7 +138,22 @@ struct FeatureSet { void setTypedFunctionReferences(bool v = true) { set(TypedFunctionReferences, v); } - void setAll(bool v = true) { features = v ? All : MVP; } + void setGCNNLocals(bool v = true) { set(GCNNLocals, 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 = All; + setGCNNLocals(oldGCNNLocals); + } void enable(const FeatureSet& other) { features |= other.features; } void disable(const FeatureSet& other) { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 15b17bf26..d704ee15e 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2496,6 +2496,9 @@ void FunctionValidator::visitFunction(Function* curr) { shouldBeTrue(result.isConcrete(), curr, "results must be concretely typed"); } for (const auto& var : curr->vars) { + if (var.isRef() && getModule()->features.hasGCNNLocals()) { + continue; + } features |= var.getFeatures(); shouldBeTrue(var.isDefaultable(), var, "vars must be defaultable"); } diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 600795c05..b8b671de5 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -42,7 +42,7 @@ Features.Multivalue: 512 Features.GC: 1024 Features.Memory64: 2048 Features.TypedFunctionReferences: 4096 -Features.All: 8191 +Features.All: 16383 InvalidId: 0 BlockId: 1 IfId: 2 diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index fd1b5e64c..eb83ffb33 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -26,7 +26,7 @@ BinaryenFeatureMultivalue: 512 BinaryenFeatureGC: 1024 BinaryenFeatureMemory64: 2048 BinaryenFeatureTypedFunctionReferences: 4096 -BinaryenFeatureAll: 8191 +BinaryenFeatureAll: 16383 (f32.neg (f32.const -33.61199951171875) ) diff --git a/test/lit/passes/ssa-gc-nn-locals.wast b/test/lit/passes/ssa-gc-nn-locals.wast new file mode 100644 index 000000000..149a0c5c7 --- /dev/null +++ b/test/lit/passes/ssa-gc-nn-locals.wast @@ -0,0 +1,27 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt %s -all --ssa --enable-gc-nn-locals -S -o - | filecheck %s + +(module + ;; CHECK: (func $nn-locals + ;; CHECK-NEXT: (local $x (ref func)) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (ref.func $nn-locals) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $nn-locals + ;; A non-nullable local + (local $x (ref func)) + ;; Set the local, and get it later. The SSA pass will normally handle non- + ;; nullability using ref.as_non_null, but with --gc-nn-locals nothing should + ;; be done. + (local.set $x (ref.func $nn-locals)) + (drop (local.get $x)) + (drop (local.get $x)) + ) +) |