summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/GlobalStructInference.cpp22
-rw-r--r--test/lit/passes/gsi.wast93
2 files changed, 113 insertions, 2 deletions
diff --git a/src/passes/GlobalStructInference.cpp b/src/passes/GlobalStructInference.cpp
index 938350960..50eddfd98 100644
--- a/src/passes/GlobalStructInference.cpp
+++ b/src/passes/GlobalStructInference.cpp
@@ -123,6 +123,16 @@ struct GlobalStructInference : public Pass {
auto type = global->init->type.getHeapType();
+ // The global's declared type must match the init's type. If not, say if
+ // we had a global declared as type |any| but that contains (ref $A), then
+ // that is not something we can optimize, as ref.eq on a global.get of
+ // that global will not validate. (This should not be a problem after
+ // GlobalSubtyping runs, which will specialize the type of the global.)
+ if (global->type != global->init->type) {
+ unoptimizable.insert(type);
+ continue;
+ }
+
// We cannot optimize mutable globals.
if (global->mutable_) {
unoptimizable.insert(type);
@@ -197,14 +207,22 @@ struct GlobalStructInference : public Pass {
return;
}
- auto iter = parent.typeGlobals.find(type.getHeapType());
+ // We must ignore the case of a non-struct heap type, that is, a bottom
+ // type (which is all that is left after we've already ruled out
+ // unreachable).
+ auto heapType = type.getHeapType();
+ auto iter = parent.typeGlobals.find(heapType);
if (iter == parent.typeGlobals.end()) {
return;
}
+ // This cannot be a bottom type as we found it in the typeGlobals map,
+ // which only contains types of struct.news.
+ assert(heapType.isStruct());
+
// The field must be immutable.
auto fieldIndex = curr->index;
- auto& field = type.getHeapType().getStruct().fields[fieldIndex];
+ auto& field = heapType.getStruct().fields[fieldIndex];
if (field.mutable_ == Mutable) {
return;
}
diff --git a/test/lit/passes/gsi.wast b/test/lit/passes/gsi.wast
index 52ba5d7f6..4cec6aa3c 100644
--- a/test/lit/passes/gsi.wast
+++ b/test/lit/passes/gsi.wast
@@ -1175,3 +1175,96 @@
)
)
)
+
+;; One global is declared as heap type |any|, which we cannot do a ref.eq on, so
+;; we do not optimize.
+(module
+ ;; CHECK: (type $A (struct (field i32)))
+ (type $A (struct (field i32)))
+
+ ;; CHECK: (type $ref?|$A|_=>_i32 (func (param (ref null $A)) (result i32)))
+
+ ;; CHECK: (global $A0 (ref any) (struct.new $A
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+ (global $A0 (ref any) (struct.new $A
+ (i32.const 1337)
+ ))
+
+ ;; CHECK: (global $A1 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (i32.const 9999)
+ ;; CHECK-NEXT: ))
+ (global $A1 (ref $A) (struct.new $A
+ (i32.const 9999)
+ ))
+
+ ;; CHECK: (func $func (type $ref?|$A|_=>_i32) (param $ref (ref null $A)) (result i32)
+ ;; CHECK-NEXT: (struct.get $A 0
+ ;; CHECK-NEXT: (local.get $ref)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $func (param $ref (ref null $A)) (result i32)
+ (struct.get $A 0
+ (local.get $ref)
+ )
+ )
+)
+
+;; As above, but now there is just a single global. Again, we should not
+;; optimize because the global is not declared as a struct type (which means we
+;; cannot do a struct.get on a global.get of that global - we'd need a cast; it
+;; is simpler to not optimize here and let other passes first refine the global
+;; type).
+(module
+ ;; CHECK: (type $A (struct (field i32)))
+ (type $A (struct (field i32)))
+
+ ;; CHECK: (type $ref?|$A|_=>_i32 (func (param (ref null $A)) (result i32)))
+
+ ;; CHECK: (global $A0 (ref any) (struct.new $A
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+ (global $A0 (ref any) (struct.new $A
+ (i32.const 1337)
+ ))
+
+ ;; CHECK: (func $func (type $ref?|$A|_=>_i32) (param $ref (ref null $A)) (result i32)
+ ;; CHECK-NEXT: (struct.get $A 0
+ ;; CHECK-NEXT: (local.get $ref)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $func (param $ref (ref null $A)) (result i32)
+ (struct.get $A 0
+ (local.get $ref)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $A (struct (field i32)))
+ (type $A (struct (field i32)))
+
+ ;; CHECK: (type $ref?|$A|_=>_i32 (func (param (ref null $A)) (result i32)))
+
+ ;; CHECK: (global $A0 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+ (global $A0 (ref $A) (struct.new $A
+ (i32.const 1337)
+ ))
+
+ ;; CHECK: (func $func (type $ref?|$A|_=>_i32) (param $ref (ref null $A)) (result i32)
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $func (param $ref (ref null $A)) (result i32)
+ ;; Test that we do not error when we see a struct.get of a bottom type.
+ (struct.get $A 0
+ (ref.null none)
+ )
+ )
+)