summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/Heap2Local.cpp63
1 files changed, 62 insertions, 1 deletions
diff --git a/src/passes/Heap2Local.cpp b/src/passes/Heap2Local.cpp
index 39c77bec4..7aec23ccf 100644
--- a/src/passes/Heap2Local.cpp
+++ b/src/passes/Heap2Local.cpp
@@ -349,6 +349,12 @@ struct EscapeAnalyzer {
void visitLocalSet(LocalSet* curr) { escapes = false; }
// Reference operations. TODO add more
+ void visitRefIsNull(RefIsNull* curr) {
+ // The reference is compared to null, but nothing more.
+ escapes = false;
+ fullyConsumes = true;
+ }
+
void visitRefEq(RefEq* curr) {
// The reference is compared for identity, but nothing more.
escapes = false;
@@ -367,6 +373,11 @@ struct EscapeAnalyzer {
}
}
+ void visitRefTest(RefTest* curr) {
+ escapes = false;
+ fullyConsumes = true;
+ }
+
void visitRefCast(RefCast* curr) {
// As it is our allocation that flows through here, we need to
// check that the cast will not trap, so that we can continue
@@ -707,6 +718,17 @@ struct Struct2Local : PostWalker<Struct2Local> {
replaceCurrent(builder.makeBlock(contents));
}
+ void visitRefIsNull(RefIsNull* curr) {
+ if (!analyzer.reached.count(curr)) {
+ return;
+ }
+
+ // The result must be 0, since the allocation is not null. Drop the RefIs
+ // and append that.
+ replaceCurrent(builder.makeSequence(
+ builder.makeDrop(curr), builder.makeConst(Literal(int32_t(0)))));
+ }
+
void visitRefEq(RefEq* curr) {
if (!analyzer.reached.count(curr)) {
return;
@@ -739,6 +761,23 @@ struct Struct2Local : PostWalker<Struct2Local> {
replaceCurrent(curr->value);
}
+ void visitRefTest(RefTest* curr) {
+ if (!analyzer.reached.count(curr)) {
+ return;
+ }
+
+ // This test operates on the allocation, which means we can compute whether
+ // it will succeed statically. We do not even need
+ // GCTypeUtils::evaluateCastCheck because we know the allocation's type
+ // precisely (it cannot be a strict subtype of the type - it is the type).
+ int32_t result = Type::isSubType(allocation->type, curr->castType);
+ // Remove the RefTest and leave only its reference child. If we kept it,
+ // we'd need to refinalize (as the input to the test changes, since the
+ // reference becomes a null, which has a different type).
+ replaceCurrent(builder.makeSequence(builder.makeDrop(curr->ref),
+ builder.makeConst(Literal(result))));
+ }
+
void visitRefCast(RefCast* curr) {
if (!analyzer.reached.count(curr)) {
return;
@@ -820,12 +859,15 @@ struct Array2Struct : PostWalker<Array2Struct> {
EscapeAnalyzer& analyzer;
Function* func;
Builder builder;
+ // The original type of the allocation, before we turn it into a struct.
+ Type originalType;
Array2Struct(Expression* allocation,
EscapeAnalyzer& analyzer,
Function* func,
Module& wasm)
- : allocation(allocation), analyzer(analyzer), func(func), builder(wasm) {
+ : allocation(allocation), analyzer(analyzer), func(func), builder(wasm),
+ originalType(allocation->type) {
// Build the struct type we need: as many fields as the size of the array,
// all of the same type as the array's element.
@@ -989,6 +1031,25 @@ struct Array2Struct : PostWalker<Array2Struct> {
noteCurrentIsReached();
}
+ // Some additional operations need special handling
+ void visitRefTest(RefTest* curr) {
+ if (!analyzer.reached.count(curr)) {
+ return;
+ }
+
+ // When we ref.test an array allocation, we cannot simply turn the array
+ // into a struct, as then the test will behave different. (Note that this is
+ // not a problem for ref.*cast*, as the cast simply goes away when the value
+ // flows through, and we verify it will do so in the escape analysis.) To
+ // handle this, check if the test succeeds or not, and write out the outcome
+ // here (similar to Struct2Local::visitRefTest). Note that we test on
+ // |originalType| here and not |allocation->type|, as the allocation has
+ // been turned into a struct.
+ int32_t result = Type::isSubType(originalType, curr->castType);
+ replaceCurrent(builder.makeSequence(builder.makeDrop(curr),
+ builder.makeConst(Literal(result))));
+ }
+
// Get the value in an expression we know must contain a constant index.
Index getIndex(Expression* curr) {
return curr->cast<Const>()->value.getUnsigned();