summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/effects.h13
-rw-r--r--test/lit/gc-read-write-effects.wast50
2 files changed, 63 insertions, 0 deletions
diff --git a/src/ir/effects.h b/src/ir/effects.h
index d52fcfb15..268a7b4e0 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -71,6 +71,10 @@ public:
std::set<Name> globalsWritten;
bool readsMemory = false;
bool writesMemory = false;
+ // TODO: Type-based alias analysis. For example, writes to Arrays never
+ // interfere with reads from Structs.
+ bool readsHeap = false;
+ bool writesHeap = false;
// A trap, either from an unreachable instruction, or from an implicit trap
// that we do not ignore (see below).
// Note that we ignore trap differences, so it is ok to reorder traps with
@@ -105,6 +109,7 @@ public:
return globalsRead.size() + globalsWritten.size() > 0;
}
bool accessesMemory() const { return calls || readsMemory || writesMemory; }
+ bool accessesHeap() const { return calls || readsHeap || writesHeap; }
// Check whether this may transfer control flow to somewhere outside of this
// expression (aside from just flowing out normally). That includes a break
// or a throw (if the throw is not known to be caught inside this expression;
@@ -143,6 +148,8 @@ public:
(other.transfersControlFlow() && hasSideEffects()) ||
((writesMemory || calls) && other.accessesMemory()) ||
((other.writesMemory || other.calls) && accessesMemory()) ||
+ ((writesHeap || calls) && other.accessesHeap()) ||
+ ((other.writesHeap || other.calls) && accessesHeap()) ||
(danglingPop || other.danglingPop)) {
return true;
}
@@ -202,6 +209,8 @@ public:
calls = calls || other.calls;
readsMemory = readsMemory || other.readsMemory;
writesMemory = writesMemory || other.writesMemory;
+ readsHeap = readsHeap || other.readsHeap;
+ writesHeap = writesHeap || other.writesHeap;
trap = trap || other.trap;
implicitTrap = implicitTrap || other.implicitTrap;
isAtomic = isAtomic || other.isAtomic;
@@ -571,12 +580,14 @@ private:
void visitRttSub(RttSub* curr) {}
void visitStructNew(StructNew* curr) {}
void visitStructGet(StructGet* curr) {
+ parent.readsHeap = true;
// traps when the arg is null
if (curr->ref->type.isNullable()) {
parent.implicitTrap = true;
}
}
void visitStructSet(StructSet* curr) {
+ parent.writesHeap = true;
// traps when the arg is null
if (curr->ref->type.isNullable()) {
parent.implicitTrap = true;
@@ -584,10 +595,12 @@ private:
}
void visitArrayNew(ArrayNew* curr) {}
void visitArrayGet(ArrayGet* curr) {
+ parent.readsHeap = true;
// traps when the arg is null or the index out of bounds
parent.implicitTrap = true;
}
void visitArraySet(ArraySet* curr) {
+ parent.writesHeap = true;
// traps when the arg is null or the index out of bounds
parent.implicitTrap = true;
}
diff --git a/test/lit/gc-read-write-effects.wast b/test/lit/gc-read-write-effects.wast
new file mode 100644
index 000000000..f487a92f5
--- /dev/null
+++ b/test/lit/gc-read-write-effects.wast
@@ -0,0 +1,50 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+
+;; Check that writing a struct field is not reordered with reading the same
+;; struct field.
+
+;; RUN: wasm-opt -all --simplify-locals %s -S -o - | filecheck %s
+
+(module
+ (type $A (struct
+ (field (mut i32))
+ ))
+
+ ;; Check that this:
+ ;;
+ ;; y = a.0
+ ;; a.0 = 10
+ ;; return y
+ ;;
+ ;; Is not turned into this:
+ ;;
+ ;; a.0 = 10
+ ;; return a.0
+ ;;
+ ;; CHECK: (func $test (param $x (ref null $A)) (result i32)
+ ;; CHECK-NEXT: (local $y i32)
+ ;; CHECK-NEXT: (local.set $y
+ ;; CHECK-NEXT: (struct.get $A 0
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.set $A 0
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $y)
+ ;; CHECK-NEXT: )
+ (func $test (export "test") (param $x (ref null $A)) (result i32)
+ (local $y i32)
+ (local.set $y
+ (struct.get $A 0
+ (local.get $x)
+ )
+ )
+ (struct.set $A 0
+ (local.get $x)
+ (i32.const 10)
+ )
+ (local.get $y)
+ )
+)