diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/LocalStructuralDominance.cpp | 30 | ||||
-rw-r--r-- | src/ir/type-updating.cpp | 75 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 9 |
3 files changed, 87 insertions, 27 deletions
diff --git a/src/ir/LocalStructuralDominance.cpp b/src/ir/LocalStructuralDominance.cpp index cfb75e006..183ca7b3f 100644 --- a/src/ir/LocalStructuralDominance.cpp +++ b/src/ir/LocalStructuralDominance.cpp @@ -30,9 +30,11 @@ LocalStructuralDominance::LocalStructuralDominance(Function* func, bool hasRefVar = false; for (auto var : func->vars) { - if (var.isRef()) { - hasRefVar = true; - break; + for (auto type : var) { + if (type.isRef()) { + hasRefVar = true; + break; + } } } if (!hasRefVar) { @@ -42,10 +44,13 @@ LocalStructuralDominance::LocalStructuralDominance(Function* func, if (mode == NonNullableOnly) { bool hasNonNullableVar = false; for (auto var : func->vars) { - // Check if we have any non-nullable vars at all. - if (var.isNonNullable()) { - hasNonNullableVar = true; - break; + for (auto type : var) { + // Check if we have any non-nullable vars (or tuple vars with + // non-nullable elements) at all. + if (type.isNonNullable()) { + hasNonNullableVar = true; + break; + } } } if (!hasNonNullableVar) { @@ -70,10 +75,17 @@ LocalStructuralDominance::LocalStructuralDominance(Function* func, } for (Index i = func->getNumParams(); i < func->getNumLocals(); i++) { - auto type = func->getLocalType(i); + auto localType = func->getLocalType(i); + bool interesting = false; + for (auto type : localType) { + if (type.isRef() && (mode == All || type.isNonNullable())) { + interesting = true; + break; + } + } // Mark locals we don't need to care about as "set". We never do any // work for such a local. - if (!type.isRef() || (mode == NonNullableOnly && type.isNullable())) { + if (!interesting) { localsSet[i] = true; } } diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp index 85ead1e9e..ac1f4b3af 100644 --- a/src/ir/type-updating.cpp +++ b/src/ir/type-updating.cpp @@ -302,9 +302,8 @@ Type GlobalTypeRewriter::getTempTupleType(Tuple tuple) { namespace TypeUpdating { bool canHandleAsLocal(Type type) { - // Defaultable types are always ok. For non-nullable types, we can handle them - // using defaultable ones + ref.as_non_nulls. - return type.isDefaultable() || type.isRef(); + // TODO: Inline this into its callers. + return type.isConcrete(); } void handleNonDefaultableLocals(Function* func, Module& wasm) { @@ -317,10 +316,12 @@ void handleNonDefaultableLocals(Function* func, Module& wasm) { return; } bool hasNonNullable = false; - for (auto type : func->vars) { - if (type.isNonNullable()) { - hasNonNullable = true; - break; + for (auto varType : func->vars) { + for (auto type : varType) { + if (type.isNonNullable()) { + hasNonNullable = true; + break; + } } } if (!hasNonNullable) { @@ -340,7 +341,8 @@ void handleNonDefaultableLocals(Function* func, Module& wasm) { // LocalStructuralDominance should have only looked at non-nullable indexes // since we told it to ignore nullable ones. Also, params always dominate // and should not appear here. - assert(func->getLocalType(index).isNonNullable()); + assert(func->getLocalType(index).isNonNullable() || + func->getLocalType(index).isTuple()); assert(!func->isParam(index)); } if (badIndexes.empty()) { @@ -371,8 +373,24 @@ void handleNonDefaultableLocals(Function* func, Module& wasm) { } if (badIndexes.count(set->index)) { auto type = func->getLocalType(set->index); - set->type = Type(type.getHeapType(), Nullable); - *setp = builder.makeRefAs(RefAsNonNull, set); + auto validType = getValidLocalType(type, wasm.features); + if (type.isRef()) { + set->type = validType; + *setp = builder.makeRefAs(RefAsNonNull, set); + } else { + assert(type.isTuple()); + set->makeSet(); + std::vector<Expression*> elems(type.size()); + for (size_t i = 0, size = type.size(); i < size; ++i) { + elems[i] = builder.makeTupleExtract( + builder.makeLocalGet(set->index, validType), i); + if (type[i].isNonNullable()) { + elems[i] = builder.makeRefAs(RefAsNonNull, elems[i]); + } + } + *setp = + builder.makeSequence(set, builder.makeTupleMake(std::move(elems))); + } } } @@ -385,21 +403,48 @@ void handleNonDefaultableLocals(Function* func, Module& wasm) { } Type getValidLocalType(Type type, FeatureSet features) { - // TODO: this should handle tuples with a non-nullable item - assert(canHandleAsLocal(type)); - if (type.isNonNullable() && !features.hasGCNNLocals()) { - type = Type(type.getHeapType(), Nullable); + assert(type.isConcrete()); + if (features.hasGCNNLocals()) { + return type; + } + if (type.isNonNullable()) { + return Type(type.getHeapType(), Nullable); + } + if (type.isTuple()) { + std::vector<Type> elems(type.size()); + for (size_t i = 0, size = type.size(); i < size; ++i) { + elems[i] = getValidLocalType(type[i], features); + } + return Type(std::move(elems)); } return type; } Expression* fixLocalGet(LocalGet* get, Module& wasm) { - if (get->type.isNonNullable() && !wasm.features.hasGCNNLocals()) { + 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. get->type = getValidLocalType(get->type, wasm.features); return Builder(wasm).makeRefAs(RefAsNonNull, get); } + if (get->type.isTuple()) { + auto type = get->type; + get->type = getValidLocalType(type, wasm.features); + std::vector<Expression*> elems(type.size()); + Builder builder(wasm); + for (Index i = 0, size = type.size(); i < size; ++i) { + auto* elemGet = + i == 0 ? get : builder.makeLocalGet(get->index, get->type); + elems[i] = builder.makeTupleExtract(elemGet, i); + if (type[i].isNonNullable()) { + elems[i] = builder.makeRefAs(RefAsNonNull, elems[i]); + } + } + return builder.makeTupleMake(std::move(elems)); + } return get; } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 2039fb987..289d10b96 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3200,9 +3200,12 @@ void FunctionValidator::visitFunction(Function* curr) { // that is, a set allows gets until the end of the block. LocalStructuralDominance info(curr, *getModule()); for (auto index : info.nonDominatingIndices) { - shouldBeTrue(!curr->getLocalType(index).isNonNullable(), - index, - "non-nullable local's sets must dominate gets"); + 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 |