summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/CMakeLists.txt1
-rw-r--r--src/ir/literal-utils.h6
-rw-r--r--src/ir/type-updating.cpp60
-rw-r--r--src/ir/type-updating.h9
-rw-r--r--src/passes/Inlining.cpp2
-rw-r--r--src/passes/Vacuum.cpp20
-rw-r--r--src/tools/fuzzing.h6
-rw-r--r--src/wasm-interpreter.h8
-rw-r--r--src/wasm-type.h2
-rw-r--r--src/wasm.h4
-rw-r--r--src/wasm/wasm-binary.cpp26
-rw-r--r--src/wasm/wasm-s-parser.cpp23
-rw-r--r--src/wasm/wasm-type.cpp4
-rw-r--r--src/wasm/wasm-validator.cpp4
-rw-r--r--src/wasm/wasm.cpp35
15 files changed, 136 insertions, 74 deletions
diff --git a/src/ir/CMakeLists.txt b/src/ir/CMakeLists.txt
index 40868899d..cfc233fd8 100644
--- a/src/ir/CMakeLists.txt
+++ b/src/ir/CMakeLists.txt
@@ -6,6 +6,7 @@ set(ir_SOURCES
ReFinalize.cpp
stack-utils.cpp
table-utils.cpp
+ type-updating.cpp
module-splitting.cpp
${ir_HEADERS}
)
diff --git a/src/ir/literal-utils.h b/src/ir/literal-utils.h
index 3c2f36d9a..fbe5b3716 100644
--- a/src/ir/literal-utils.h
+++ b/src/ir/literal-utils.h
@@ -31,6 +31,12 @@ inline Expression* makeFromInt32(int32_t x, Type type, Module& wasm) {
return ret;
}
+inline bool canMakeZero(Type type) {
+ // We can make a "zero" - a 0, or a null, or a trivial rtt, etc. - for pretty
+ // much anything except a non-nullable reference type.
+ return !type.isRef() || type.isNullable();
+}
+
inline Expression* makeZero(Type type, Module& wasm) {
// TODO: Remove this function once V8 supports v128.const
// (https://bugs.chromium.org/p/v8/issues/detail?id=8460)
diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp
new file mode 100644
index 000000000..54a83aa49
--- /dev/null
+++ b/src/ir/type-updating.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "type-updating.h"
+#include "find_all.h"
+
+namespace wasm {
+
+namespace TypeUpdating {
+
+void handleNonNullableLocals(Function* func, Module& wasm) {
+ // Check if this is an issue.
+ bool hasNonNullable = false;
+ for (auto type : func->vars) {
+ if (type.isRef() && !type.isNullable()) {
+ hasNonNullable = true;
+ break;
+ }
+ }
+ if (!hasNonNullable) {
+ return;
+ }
+ // Rewrite the local.gets.
+ Builder builder(wasm);
+ for (auto** getp : FindAllPointers<LocalGet>(func->body).list) {
+ auto* get = (*getp)->cast<LocalGet>();
+ auto type = func->getLocalType(get->index);
+ if (type.isRef() && !type.isNullable()) {
+ // The get should now return a nullable value, and a ref.as_non_null
+ // fixes that up.
+ get->type = Type(type.getHeapType(), Nullable);
+ *getp = builder.makeRefAs(RefAsNonNull, get);
+ }
+ }
+
+ // Rewrite the types of the function's vars (which we can do now, after we
+ // are done using them to know which local.gets to fix).
+ for (auto& type : func->vars) {
+ if (type.isRef() && !type.isNullable()) {
+ type = Type(type.getHeapType(), Nullable);
+ }
+ }
+}
+
+} // namespace TypeUpdating
+
+} // namespace wasm
diff --git a/src/ir/type-updating.h b/src/ir/type-updating.h
index 1a117c239..fc438d53e 100644
--- a/src/ir/type-updating.h
+++ b/src/ir/type-updating.h
@@ -305,6 +305,15 @@ struct TypeUpdater
}
};
+namespace TypeUpdating {
+
+// Finds non-nullable locals, which are currently not supported, and handles
+// them. Atm this turns them into nullable ones, and adds ref.as_non_null on
+// their uses (which keeps the type of the users identical).
+void handleNonNullableLocals(Function* func, Module& wasm);
+
+} // namespace TypeUpdating
+
} // namespace wasm
#endif // wasm_ir_type_updating_h
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp
index 41481b310..3f6263815 100644
--- a/src/passes/Inlining.cpp
+++ b/src/passes/Inlining.cpp
@@ -33,6 +33,7 @@
#include "ir/debug.h"
#include "ir/literal-utils.h"
#include "ir/module-utils.h"
+#include "ir/type-updating.h"
#include "ir/utils.h"
#include "parsing.h"
#include "pass.h"
@@ -296,6 +297,7 @@ doInlining(Module* module, Function* into, const InliningAction& action) {
// Make the block reachable by adding a break to it
block->list.push_back(builder.makeBreak(block->name));
}
+ TypeUpdating::handleNonNullableLocals(into, *module);
return block;
}
diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp
index c9b83da78..c89a57976 100644
--- a/src/passes/Vacuum.cpp
+++ b/src/passes/Vacuum.cpp
@@ -145,13 +145,19 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
ExpressionAnalyzer::isResultUsed(expressionStack, getFunction());
auto* optimized = optimize(child, used, true);
if (!optimized) {
- if (child->type.isConcrete()) {
- // We can't just skip a final concrete element, even if it isn't used.
- // Instead, replace it with something that's easy to optimize out (for
- // example, code-folding can merge out identical zeros at the end of
- // if arms).
- optimized = LiteralUtils::makeZero(child->type, *getModule());
- } else if (child->type == Type::unreachable) {
+ auto childType = child->type;
+ if (childType.isConcrete()) {
+ if (LiteralUtils::canMakeZero(childType)) {
+ // We can't just skip a final concrete element, even if it isn't
+ // used. Instead, replace it with something that's easy to optimize
+ // out (for example, code-folding can merge out identical zeros at
+ // the end of if arms).
+ optimized = LiteralUtils::makeZero(childType, *getModule());
+ } else {
+ // Don't optimize it out.
+ optimized = child;
+ }
+ } else if (childType == Type::unreachable) {
// Don't try to optimize out an unreachable child (dce can do that
// properly).
optimized = child;
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 8283fccd1..a0d5430d8 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -1490,7 +1490,7 @@ private:
for (const auto& type : target->sig.params) {
args.push_back(make(type));
}
- auto targetType = Type(HeapType(target->sig), Nullable);
+ auto targetType = Type(HeapType(target->sig), NonNullable);
// TODO: half the time make a completely random item with that type.
return builder.makeCallRef(
builder.makeRefFunc(target->name, targetType), args, type, isReturn);
@@ -2096,9 +2096,7 @@ private:
}
// TODO: randomize the order
for (auto& func : wasm.functions) {
- // FIXME: RefFunc type should be non-nullable, but we emit nullable
- // types for now.
- if (type == Type(HeapType(func->sig), Nullable)) {
+ if (type == Type(HeapType(func->sig), NonNullable)) {
return builder.makeRefFunc(func->name, type);
}
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index ac095e0c0..dcf9d2540 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -207,7 +207,7 @@ public:
if (!Type::isSubType(type, curr->type)) {
std::cerr << "expected " << curr->type << ", seeing " << type
<< " from\n"
- << curr << '\n';
+ << *curr << '\n';
}
#endif
assert(Type::isSubType(type, curr->type));
@@ -1454,14 +1454,14 @@ public:
auto* func = module->getFunction(cast.originalRef.getFunc());
seenRtt = Literal(Type(Rtt(0, func->sig)));
cast.castRef =
- Literal(func->name, Type(intendedRtt.type.getHeapType(), Nullable));
+ Literal(func->name, Type(intendedRtt.type.getHeapType(), NonNullable));
} else {
// GC data store an RTT in each instance.
assert(cast.originalRef.isData());
auto gcData = cast.originalRef.getGCData();
seenRtt = gcData->rtt;
cast.castRef =
- Literal(gcData, Type(intendedRtt.type.getHeapType(), Nullable));
+ Literal(gcData, Type(intendedRtt.type.getHeapType(), NonNullable));
}
if (!seenRtt.isSubRtt(intendedRtt)) {
cast.outcome = cast.Failure;
@@ -1486,7 +1486,7 @@ public:
return cast.breaking;
}
if (cast.outcome == cast.Null) {
- return Literal::makeNull(curr->type);
+ return Literal::makeNull(Type(curr->type.getHeapType(), Nullable));
}
if (cast.outcome == cast.Failure) {
trap("cast error");
diff --git a/src/wasm-type.h b/src/wasm-type.h
index bb499c7c1..b638108fc 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -144,6 +144,8 @@ public:
bool isArray() const;
bool isDefaultable() const;
+ Nullability getNullability() const;
+
private:
template<bool (Type::*pred)() const> bool hasPredicate() {
for (const auto& type : *this) {
diff --git a/src/wasm.h b/src/wasm.h
index 698de9520..630e5b003 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1397,8 +1397,6 @@ public:
Expression* rtt;
void finalize();
-
- Type getCastType();
};
class RefCast : public SpecificExpression<Expression::RefCastId> {
@@ -1409,8 +1407,6 @@ public:
Expression* rtt;
void finalize();
-
- Type getCastType();
};
class BrOn : public SpecificExpression<Expression::BrOnId> {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 6cb837296..09d3287b0 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -19,6 +19,7 @@
#include "ir/module-utils.h"
#include "ir/table-utils.h"
+#include "ir/type-updating.h"
#include "support/bits.h"
#include "support/debug.h"
#include "wasm-binary.h"
@@ -1595,12 +1596,10 @@ bool WasmBinaryBuilder::getBasicType(int32_t code, Type& out) {
out = Type::eqref;
return true;
case BinaryConsts::EncodedType::i31ref:
- // FIXME: for now, force all inputs to be nullable
- out = Type(HeapType::i31, Nullable);
+ out = Type(HeapType::i31, NonNullable);
return true;
case BinaryConsts::EncodedType::dataref:
- // FIXME: for now, force all inputs to be nullable
- out = Type(HeapType::data, Nullable);
+ out = Type(HeapType::data, NonNullable);
return true;
default:
return false;
@@ -1647,9 +1646,9 @@ Type WasmBinaryBuilder::getType(int initial) {
case BinaryConsts::EncodedType::Empty:
return Type::none;
case BinaryConsts::EncodedType::nullable:
- case BinaryConsts::EncodedType::nonnullable:
- // FIXME: for now, force all inputs to be nullable
return Type(getHeapType(), Nullable);
+ case BinaryConsts::EncodedType::nonnullable:
+ return Type(getHeapType(), NonNullable);
case BinaryConsts::EncodedType::rtt_n: {
auto depth = getU32LEB();
auto heapType = getIndexedHeapType();
@@ -1790,16 +1789,18 @@ void WasmBinaryBuilder::readTypes() {
switch (typeCode) {
case BinaryConsts::EncodedType::nullable:
case BinaryConsts::EncodedType::nonnullable: {
- // FIXME: for now, force all inputs to be nullable
+ auto nullability = typeCode == BinaryConsts::EncodedType::nullable
+ ? Nullable
+ : NonNullable;
int64_t htCode = getS64LEB(); // TODO: Actually s33
HeapType ht;
if (getBasicHeapType(htCode, ht)) {
- return Type(ht, Nullable);
+ return Type(ht, nullability);
}
if (size_t(htCode) >= numTypes) {
throwError("invalid type index: " + std::to_string(htCode));
}
- return builder.getTempRefType(size_t(htCode), Nullable);
+ return builder.getTempRefType(size_t(htCode), nullability);
}
case BinaryConsts::EncodedType::rtt_n:
case BinaryConsts::EncodedType::rtt: {
@@ -2172,6 +2173,9 @@ void WasmBinaryBuilder::readFunctions() {
throwError("binary offset at function exit not at expected location");
}
}
+
+ TypeUpdating::handleNonNullableLocals(func, wasm);
+
std::swap(func->epilogLocation, debugLocation);
currFunction = nullptr;
debugLocation.clear();
@@ -6072,8 +6076,8 @@ void WasmBinaryBuilder::visitRefFunc(RefFunc* curr) {
functionRefs[index].push_back(curr); // we don't know function names yet
// To support typed function refs, we give the reference not just a general
// funcref, but a specific subtype with the actual signature.
- // FIXME: for now, emit a nullable type here
- curr->finalize(Type(HeapType(getSignatureByFunctionIndex(index)), Nullable));
+ curr->finalize(
+ Type(HeapType(getSignatureByFunctionIndex(index)), NonNullable));
}
void WasmBinaryBuilder::visitRefEq(RefEq* curr) {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 91ed5056f..45c7c9161 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -686,22 +686,20 @@ void SExpressionWasmBuilder::preParseHeapTypes(Element& module) {
auto parseRefType = [&](Element& elem) -> Type {
// '(' 'ref' 'null'? ht ')'
- bool nullable = elem[1]->isStr() && *elem[1] == NULL_;
+ auto nullable =
+ elem[1]->isStr() && *elem[1] == NULL_ ? Nullable : NonNullable;
auto& referent = nullable ? *elem[2] : *elem[1];
const char* name = referent.c_str();
if (referent.dollared()) {
- // TODO: Support non-nullable types
- return builder.getTempRefType(typeIndices[name], Nullable);
+ return builder.getTempRefType(typeIndices[name], nullable);
} else if (String::isNumber(name)) {
- // TODO: Support non-nullable types
size_t index = atoi(name);
if (index >= numTypes) {
throw ParseException("invalid type index", elem.line, elem.col);
}
- return builder.getTempRefType(index, Nullable);
+ return builder.getTempRefType(index, nullable);
} else {
- // TODO: Support non-nullable types
- return Type(stringToHeapType(name), Nullable);
+ return Type(stringToHeapType(name), nullable);
}
};
@@ -1091,12 +1089,10 @@ Type SExpressionWasmBuilder::stringToType(const char* str,
return Type::eqref;
}
if (strncmp(str, "i31ref", 6) == 0 && (prefix || str[6] == 0)) {
- // FIXME: for now, force all inputs to be nullable
- return Type(HeapType::BasicHeapType::i31, Nullable);
+ return Type::i31ref;
}
if (strncmp(str, "dataref", 7) == 0 && (prefix || str[7] == 0)) {
- // FIXME: for now, force all inputs to be nullable
- return Type(HeapType::BasicHeapType::data, Nullable);
+ return Type::dataref;
}
if (allowError) {
return Type::none;
@@ -1161,8 +1157,7 @@ Type SExpressionWasmBuilder::elementToType(Element& s) {
throw ParseException(
std::string("invalid reference type qualifier"), s.line, s.col);
}
- // FIXME: for now, force all inputs to be nullable
- Nullability nullable = Nullable;
+ Nullability nullable = NonNullable;
size_t i = 1;
if (size == 3) {
nullable = Nullable;
@@ -2385,7 +2380,7 @@ Expression* SExpressionWasmBuilder::makeRefFunc(Element& s) {
ret->func = func;
// To support typed function refs, we give the reference not just a general
// funcref, but a specific subtype with the actual signature.
- ret->finalize(Type(HeapType(functionSignatures[func]), Nullable));
+ ret->finalize(Type(HeapType(functionSignatures[func]), NonNullable));
return ret;
}
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index f46d90854..832ee6367 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -520,6 +520,10 @@ bool Type::isDefaultable() const {
return isConcrete() && (!isRef() || isNullable()) && !isRtt();
}
+Nullability Type::getNullability() const {
+ return isNullable() ? Nullable : NonNullable;
+}
+
bool Type::operator<(const Type& other) const {
return TypeComparator().lessThan(*this, other);
}
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index c7423ad6d..71ecf208e 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2183,10 +2183,8 @@ 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.
shouldBeSubType(curr->i31->type,
- Type(HeapType::i31, Nullable),
+ Type::i31ref,
curr->i31,
"i31.get_s/u's argument should be i31ref");
}
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 5eea3f315..2dba93a4a 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -928,30 +928,16 @@ void RefTest::finalize() {
}
}
-// Helper to get the cast type for a cast instruction. They all look at the rtt
-// operand's type.
-template<typename T> static Type doGetCastType(T* curr) {
- if (curr->rtt->type == Type::unreachable) {
- // We don't have the RTT type, so just return unreachable. The type in this
- // case should not matter in practice, but it may be seen while debugging.
- return Type::unreachable;
- }
- // TODO: make non-nullable when we support that
- return Type(curr->rtt->type.getHeapType(), Nullable);
-}
-
-Type RefTest::getCastType() { return doGetCastType(this); }
-
void RefCast::finalize() {
if (ref->type == Type::unreachable || rtt->type == Type::unreachable) {
type = Type::unreachable;
} else {
- type = getCastType();
+ // The output of ref.cast may be null if the input is null (in that case the
+ // null is passed through).
+ type = Type(rtt->type.getHeapType(), ref->type.getNullability());
}
}
-Type RefCast::getCastType() { return doGetCastType(this); }
-
void BrOn::finalize() {
if (ref->type == Type::unreachable ||
(rtt && rtt->type == Type::unreachable)) {
@@ -960,8 +946,7 @@ void BrOn::finalize() {
if (op == BrOnNull) {
// If BrOnNull does not branch, it flows out the existing value as
// non-null.
- // FIXME: When we support non-nullable types, this should be non-nullable.
- type = Type(ref->type.getHeapType(), Nullable);
+ type = Type(ref->type.getHeapType(), NonNullable);
} else {
type = ref->type;
}
@@ -974,8 +959,7 @@ Type BrOn::getCastType() {
// BrOnNull does not send a value on the branch.
return Type::none;
case BrOnCast:
- // FIXME: When we support non-nullable types, this should be non-nullable.
- return Type(rtt->type.getHeapType(), Nullable);
+ return Type(rtt->type.getHeapType(), NonNullable);
case BrOnFunc:
return Type::funcref;
case BrOnData:
@@ -1007,8 +991,7 @@ void StructNew::finalize() {
if (handleUnreachableOperands(this)) {
return;
}
- // TODO: make non-nullable when we support that
- type = Type(rtt->type.getHeapType(), Nullable);
+ type = Type(rtt->type.getHeapType(), NonNullable);
}
void StructGet::finalize() {
@@ -1033,8 +1016,7 @@ void ArrayNew::finalize() {
type = Type::unreachable;
return;
}
- // TODO: make non-nullable when we support that
- type = Type(rtt->type.getHeapType(), Nullable);
+ type = Type(rtt->type.getHeapType(), NonNullable);
}
void ArrayGet::finalize() {
@@ -1069,8 +1051,7 @@ void RefAs::finalize() {
}
switch (op) {
case RefAsNonNull:
- // FIXME: when we support non-nullable types, switch to NonNullable
- type = Type(value->type.getHeapType(), Nullable);
+ type = Type(value->type.getHeapType(), NonNullable);
break;
case RefAsFunc:
type = Type::funcref;