summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/LocalStructuralDominance.cpp30
-rw-r--r--src/ir/type-updating.cpp75
-rw-r--r--src/wasm/wasm-validator.cpp9
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