diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/ir/literal-utils.h | 6 | ||||
-rw-r--r-- | src/ir/type-updating.cpp | 60 | ||||
-rw-r--r-- | src/ir/type-updating.h | 9 | ||||
-rw-r--r-- | src/passes/Inlining.cpp | 2 | ||||
-rw-r--r-- | src/passes/Vacuum.cpp | 20 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 6 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 8 | ||||
-rw-r--r-- | src/wasm-type.h | 2 | ||||
-rw-r--r-- | src/wasm.h | 4 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 26 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 23 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 4 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 4 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 35 |
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; |