summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/module-utils.h5
-rw-r--r--src/passes/Print.cpp52
-rw-r--r--src/support/string.h4
-rw-r--r--src/wasm/wasm-binary.cpp3
-rw-r--r--src/wasm/wasm-s-parser.cpp19
-rw-r--r--src/wasm/wasm-type.cpp50
-rw-r--r--src/wasm/wasm-validator.cpp4
-rw-r--r--test/gc.wast5
-rw-r--r--test/gc.wast.from-wast9
-rw-r--r--test/gc.wast.fromBinary9
-rw-r--r--test/gc.wast.fromBinary.noDebugInfo9
-rw-r--r--test/passes/simplify-globals_all-features_fuzz-exec.txt4
-rw-r--r--test/passes/simplify-locals_all-features.txt6
13 files changed, 105 insertions, 74 deletions
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h
index 0095fb407..f33d41266 100644
--- a/src/ir/module-utils.h
+++ b/src/ir/module-utils.h
@@ -405,7 +405,10 @@ inline void collectHeapTypes(Module& wasm,
std::unordered_map<HeapType, Index>& typeIndices) {
struct Counts : public std::unordered_map<HeapType, size_t> {
bool isRelevant(Type type) {
- return !type.isBasic() && (type.isRef() || type.isRtt());
+ if (type.isRef()) {
+ return !type.getHeapType().isBasic();
+ }
+ return type.isRtt();
}
void note(HeapType type) { (*this)[type]++; }
void maybeNote(Type type) {
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 8b028c25f..e1d80a11a 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -67,17 +67,10 @@ static std::ostream& printLocal(Index index, Function* func, std::ostream& o) {
return printName(name, o);
}
-// Wrapper for printing a type when we try to print the type name as much as
-// possible. For example, for a signature we will print the signature's name,
-// not its contents.
-struct TypeName {
- Type type;
- TypeName(Type type) : type(type) {}
-};
-
static void
printHeapTypeName(std::ostream& os, HeapType type, bool first = true);
+// Prints the name of a type. This output is guaranteed to not contain spaces.
static void printTypeName(std::ostream& os, Type type) {
if (type.isBasic()) {
os << type;
@@ -131,6 +124,8 @@ static void printFieldName(std::ostream& os, const Field& field) {
}
}
+// Prints the name of a heap type. As with printTypeName, this output is
+// guaranteed to not contain spaces.
static void printHeapTypeName(std::ostream& os, HeapType type, bool first) {
if (type.isBasic()) {
os << type;
@@ -174,8 +169,8 @@ struct SExprType {
SExprType(Type type) : type(type){};
};
-static std::ostream& operator<<(std::ostream& o, const SExprType& localType) {
- Type type = localType.type;
+static std::ostream& operator<<(std::ostream& o, const SExprType& sType) {
+ Type type = sType.type;
if (type.isTuple()) {
o << '(';
auto sep = "";
@@ -192,24 +187,17 @@ static std::ostream& operator<<(std::ostream& o, const SExprType& localType) {
}
printHeapTypeName(o, rtt.heapType);
o << ')';
- } else {
- printTypeName(o, localType.type);
- }
- return o;
-}
-
-std::ostream& operator<<(std::ostream& os, TypeName typeName) {
- auto type = typeName.type;
- if (type.isRef() && !type.isBasic()) {
- os << "(ref ";
+ } else if (type.isRef() && !type.isBasic()) {
+ o << "(ref ";
if (type.isNullable()) {
- os << "null ";
+ o << "null ";
}
- printHeapTypeName(os, type.getHeapType());
- os << ')';
- return os;
+ printHeapTypeName(o, type.getHeapType());
+ o << ')';
+ } else {
+ printTypeName(o, sType.type);
}
- return os << SExprType(typeName.type);
+ return o;
}
// TODO: try to simplify or even remove this, as we may be able to do the same
@@ -229,10 +217,10 @@ std::ostream& operator<<(std::ostream& os, ResultTypeName typeName) {
for (auto t : type) {
os << sep;
sep = " ";
- os << TypeName(t);
+ os << SExprType(t);
}
} else {
- os << TypeName(type);
+ os << SExprType(type);
}
os << ')';
return os;
@@ -2571,7 +2559,7 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << "(param ";
auto sep = "";
for (auto type : curr.params) {
- o << sep << TypeName(type);
+ o << sep << SExprType(type);
sep = " ";
}
o << ')';
@@ -2581,7 +2569,7 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << "(result ";
auto sep = "";
for (auto type : curr.results) {
- o << sep << TypeName(type);
+ o << sep << SExprType(type);
sep = " ";
}
o << ')';
@@ -2601,7 +2589,7 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
WASM_UNREACHABLE("invalid packed type");
}
} else {
- o << TypeName(field.type);
+ o << SExprType(field.type);
}
if (field.mutable_) {
o << ')';
@@ -2736,7 +2724,7 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << '(';
printMinor(o, "param ");
printLocal(i, currFunction, o);
- o << ' ' << TypeName(param) << ')';
+ o << ' ' << SExprType(param) << ')';
++i;
}
}
@@ -2750,7 +2738,7 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << '(';
printMinor(o, "local ");
printLocal(i, currFunction, o)
- << ' ' << TypeName(curr->getLocalType(i)) << ')';
+ << ' ' << SExprType(curr->getLocalType(i)) << ')';
o << maybeNewLine;
}
// Print the body.
diff --git a/src/support/string.h b/src/support/string.h
index b93d3b363..1708f4d94 100644
--- a/src/support/string.h
+++ b/src/support/string.h
@@ -115,6 +115,10 @@ inline std::string trim(const std::string& input) {
return input.substr(0, size);
}
+inline bool isNumber(const std::string& str) {
+ return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
+}
+
} // namespace String
} // namespace wasm
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index f3160c461..bcf7c15fe 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -1416,7 +1416,8 @@ Type WasmBinaryBuilder::getType(int initial) {
// FIXME: for now, force all inputs to be nullable
return Type(getHeapType(), Nullable);
case BinaryConsts::EncodedType::i31ref:
- return Type::i31ref;
+ // FIXME: for now, force all inputs to be nullable
+ return Type(HeapType::BasicHeapType::i31, Nullable);
case BinaryConsts::EncodedType::rtt_n: {
auto depth = getU32LEB();
auto heapType = getHeapType();
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 20ab4f3d2..d76a3c49a 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -22,6 +22,7 @@
#include "ir/branch-utils.h"
#include "shared-constants.h"
+#include "support/string.h"
#include "wasm-binary.h"
#include "wasm-builder.h"
@@ -867,7 +868,8 @@ Type SExpressionWasmBuilder::stringToType(const char* str,
return Type::eqref;
}
if (strncmp(str, "i31ref", 6) == 0 && (prefix || str[6] == 0)) {
- return Type::i31ref;
+ // FIXME: for now, force all inputs to be nullable
+ return Type(HeapType::BasicHeapType::i31, Nullable);
}
if (allowError) {
return Type::none;
@@ -2802,12 +2804,17 @@ HeapType SExpressionWasmBuilder::parseHeapType(Element& s) {
}
return types[it->second];
} else {
- // index
- size_t offset = atoi(s.str().c_str());
- if (offset >= types.size()) {
- throw ParseException("unknown indexed function type", s.line, s.col);
+ // It may be a numerical index, or it may be a built-in type name like
+ // "i31".
+ auto* str = s.str().c_str();
+ if (String::isNumber(str)) {
+ size_t offset = atoi(str);
+ if (offset >= types.size()) {
+ throw ParseException("unknown indexed function type", s.line, s.col);
+ }
+ return types[offset];
}
- return types[offset];
+ return stringToHeapType(str, /* prefix = */ false);
}
}
// It's a list.
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index 2eef7eb3d..31290c745 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -481,18 +481,29 @@ Type Type::reinterpret() const {
FeatureSet Type::getFeatures() const {
auto getSingleFeatures = [](Type t) -> FeatureSet {
if (t.isRef()) {
- if (t != Type::funcref && t.isFunction()) {
- // Strictly speaking, typed function references require the typed
- // function references feature, however, we use these types internally
- // regardless of the presence of features (in particular, since during
- // load of the wasm we don't know the features yet, so we apply the more
- // refined types).
- return FeatureSet::ReferenceTypes;
- }
+ // A reference type implies we need that feature. Some also require more,
+ // such as GC or exceptions.
auto heapType = t.getHeapType();
if (heapType.isStruct() || heapType.isArray()) {
return FeatureSet::ReferenceTypes | FeatureSet::GC;
}
+ if (heapType.isBasic()) {
+ switch (heapType.getBasic()) {
+ case HeapType::BasicHeapType::exn:
+ return FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling;
+ case HeapType::BasicHeapType::any:
+ case HeapType::BasicHeapType::eq:
+ case HeapType::BasicHeapType::i31:
+ return FeatureSet::ReferenceTypes | FeatureSet::GC;
+ default: {}
+ }
+ }
+ // Note: Technically typed function references also require the typed
+ // function references feature, however, we use these types internally
+ // regardless of the presence of features (in particular, since during
+ // load of the wasm we don't know the features yet, so we apply the more
+ // refined types), so we don't add that in any case here.
+ return FeatureSet::ReferenceTypes;
} else if (t.isRtt()) {
return FeatureSet::ReferenceTypes | FeatureSet::GC;
}
@@ -500,15 +511,6 @@ FeatureSet Type::getFeatures() const {
switch (t.getBasic()) {
case Type::v128:
return FeatureSet::SIMD;
- case Type::funcref:
- case Type::externref:
- return FeatureSet::ReferenceTypes;
- case Type::exnref:
- return FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling;
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- return FeatureSet::ReferenceTypes | FeatureSet::GC;
default:
return FeatureSet::MVP;
}
@@ -594,17 +596,21 @@ bool Type::isSubType(Type left, Type right) {
return true;
}
// Various things are subtypes of eqref.
- if ((left == Type::i31ref || left.getHeapType().isArray() ||
- left.getHeapType().isStruct()) &&
- right == Type::eqref) {
+ auto leftHeap = left.getHeapType();
+ auto rightHeap = right.getHeapType();
+ if ((leftHeap == HeapType::i31 || leftHeap.isArray() ||
+ leftHeap.isStruct()) &&
+ rightHeap == HeapType::eq &&
+ (!left.isNullable() || right.isNullable())) {
return true;
}
// All typed function signatures are subtypes of funcref.
- if (left.getHeapType().isSignature() && right == Type::funcref) {
+ if (leftHeap.isSignature() && rightHeap == HeapType::func &&
+ (!left.isNullable() || right.isNullable())) {
return true;
}
// A non-nullable type is a supertype of a nullable one
- if (left.getHeapType() == right.getHeapType() && !left.isNullable()) {
+ if (leftHeap == rightHeap && !left.isNullable()) {
// The only difference is the nullability.
assert(right.isNullable());
return true;
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index a45be1fd3..06f77b093 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2191,9 +2191,11 @@ void FunctionValidator::visitI31Get(I31Get* curr) {
shouldBeTrue(getModule()->features.hasGC(),
curr,
"i31.get_s/u requires gc to be enabled");
+ // FIXME: use i31ref here, which is non-nullable, when we support non-
+ // nullability.
shouldBeSubTypeOrFirstIsUnreachable(
curr->i31->type,
- Type::i31ref,
+ Type(HeapType::i31, Nullable),
curr->i31,
"i31.get_s/u's argument should be i31ref");
}
diff --git a/test/gc.wast b/test/gc.wast
index 7ce6e01a9..2fa99a9ac 100644
--- a/test/gc.wast
+++ b/test/gc.wast
@@ -68,4 +68,9 @@
(local.set $local_i32 (i31.get_s (local.get $local_i31ref)))
(local.set $local_i32 (i31.get_u (local.get $local_i31ref)))
)
+
+ (func $test-variants
+ (local $local_i31refnull (ref null i31))
+ (local $local_i31refnonnull (ref i31))
+ )
)
diff --git a/test/gc.wast.from-wast b/test/gc.wast.from-wast
index aeface521..776500087 100644
--- a/test/gc.wast.from-wast
+++ b/test/gc.wast.from-wast
@@ -2,7 +2,7 @@
(type $none_=>_none (func))
(global $global_anyref (mut anyref) (ref.null any))
(global $global_eqref (mut eqref) (ref.null eq))
- (global $global_i31ref (mut i31ref) (i31.new
+ (global $global_i31ref (mut (ref null i31)) (i31.new
(i32.const 0)
))
(global $global_anyref2 (mut anyref) (ref.null eq))
@@ -16,7 +16,7 @@
(local $local_i32 i32)
(local $local_anyref anyref)
(local $local_eqref eqref)
- (local $local_i31ref i31ref)
+ (local $local_i31ref (ref null i31))
(local.set $local_anyref
(local.get $local_anyref)
)
@@ -148,4 +148,9 @@
)
)
)
+ (func $test-variants
+ (local $local_i31refnull (ref null i31))
+ (local $local_i31refnonnull (ref null i31))
+ (nop)
+ )
)
diff --git a/test/gc.wast.fromBinary b/test/gc.wast.fromBinary
index 4d22c984f..3e5315f2c 100644
--- a/test/gc.wast.fromBinary
+++ b/test/gc.wast.fromBinary
@@ -2,7 +2,7 @@
(type $none_=>_none (func))
(global $global_anyref (mut anyref) (ref.null any))
(global $global_eqref (mut eqref) (ref.null eq))
- (global $global_i31ref (mut i31ref) (i31.new
+ (global $global_i31ref (mut (ref null i31)) (i31.new
(i32.const 0)
))
(global $global_anyref2 (mut anyref) (ref.null eq))
@@ -16,7 +16,7 @@
(local $local_i32 i32)
(local $local_anyref anyref)
(local $local_eqref eqref)
- (local $local_i31ref i31ref)
+ (local $local_i31ref (ref null i31))
(local.set $local_anyref
(local.get $local_anyref)
)
@@ -148,5 +148,10 @@
)
)
)
+ (func $test-variants
+ (local $local_i31refnull (ref null i31))
+ (local $local_i31refnonnull (ref null i31))
+ (nop)
+ )
)
diff --git a/test/gc.wast.fromBinary.noDebugInfo b/test/gc.wast.fromBinary.noDebugInfo
index 9fd83ad04..d423e65ba 100644
--- a/test/gc.wast.fromBinary.noDebugInfo
+++ b/test/gc.wast.fromBinary.noDebugInfo
@@ -2,7 +2,7 @@
(type $none_=>_none (func))
(global $global$0 (mut anyref) (ref.null any))
(global $global$1 (mut eqref) (ref.null eq))
- (global $global$2 (mut i31ref) (i31.new
+ (global $global$2 (mut (ref null i31)) (i31.new
(i32.const 0)
))
(global $global$3 (mut anyref) (ref.null eq))
@@ -16,7 +16,7 @@
(local $0 i32)
(local $1 anyref)
(local $2 eqref)
- (local $3 i31ref)
+ (local $3 (ref null i31))
(local.set $1
(local.get $1)
)
@@ -148,5 +148,10 @@
)
)
)
+ (func $1
+ (local $0 (ref null i31))
+ (local $1 (ref null i31))
+ (nop)
+ )
)
diff --git a/test/passes/simplify-globals_all-features_fuzz-exec.txt b/test/passes/simplify-globals_all-features_fuzz-exec.txt
index 03bbff9de..fa236fbc1 100644
--- a/test/passes/simplify-globals_all-features_fuzz-exec.txt
+++ b/test/passes/simplify-globals_all-features_fuzz-exec.txt
@@ -1,11 +1,11 @@
[fuzz-exec] calling export
[fuzz-exec] note result: export => funcref(0)
(module
- (type $f32_i31ref_i64_f64_funcref_=>_none (func (param f32 i31ref i64 f64 funcref)))
+ (type $f32_ref?|i31|_i64_f64_funcref_=>_none (func (param f32 (ref null i31) i64 f64 funcref)))
(type $none_=>_funcref (func (result funcref)))
(global $global$0 (mut funcref) (ref.null func))
(export "export" (func $1))
- (func $0 (param $0 f32) (param $1 i31ref) (param $2 i64) (param $3 f64) (param $4 funcref)
+ (func $0 (param $0 f32) (param $1 (ref null i31)) (param $2 i64) (param $3 f64) (param $4 funcref)
(nop)
)
(func $1 (result funcref)
diff --git a/test/passes/simplify-locals_all-features.txt b/test/passes/simplify-locals_all-features.txt
index 2cc323a57..8587e115c 100644
--- a/test/passes/simplify-locals_all-features.txt
+++ b/test/passes/simplify-locals_all-features.txt
@@ -2041,11 +2041,11 @@
)
)
(module
- (type $eqref_i31ref_=>_i32 (func (param eqref i31ref) (result i32)))
+ (type $eqref_ref?|i31|_=>_i32 (func (param eqref (ref null i31)) (result i32)))
(export "test" (func $0))
- (func $0 (param $0 eqref) (param $1 i31ref) (result i32)
+ (func $0 (param $0 eqref) (param $1 (ref null i31)) (result i32)
(local $2 eqref)
- (local $3 i31ref)
+ (local $3 (ref null i31))
(local.set $2
(local.get $0)
)