diff options
-rw-r--r-- | src/passes/GlobalStructInference.cpp | 18 | ||||
-rw-r--r-- | test/lit/passes/gsi.wast | 47 |
2 files changed, 64 insertions, 1 deletions
diff --git a/src/passes/GlobalStructInference.cpp b/src/passes/GlobalStructInference.cpp index 6a917596f..ceee8b02d 100644 --- a/src/passes/GlobalStructInference.cpp +++ b/src/passes/GlobalStructInference.cpp @@ -52,6 +52,7 @@ #include "ir/module-utils.h" #include "ir/properties.h" #include "ir/subtypes.h" +#include "ir/utils.h" #include "pass.h" #include "wasm-builder.h" #include "wasm.h" @@ -221,6 +222,8 @@ struct GlobalStructInference : public Pass { FunctionOptimizer(GlobalStructInference& parent) : parent(parent) {} + bool refinalize = false; + void visitStructGet(StructGet* curr) { auto type = curr->ref->type; if (type == Type::unreachable) { @@ -262,9 +265,16 @@ struct GlobalStructInference : public Pass { // will unlock those other optimizations. Note we must trap if the ref // is null, so add RefAsNonNull here. auto global = globals[0]; + auto globalType = wasm.getGlobal(global)->type; + if (globalType != curr->ref->type) { + // The struct.get will now read from something of the type of the + // global, which is different, so the field being read might be + // refined, which could change the struct.get's type. + refinalize = true; + } curr->ref = builder.makeSequence( builder.makeDrop(builder.makeRefAs(RefAsNonNull, curr->ref)), - builder.makeGlobalGet(global, wasm.getGlobal(globals[0])->type)); + builder.makeGlobalGet(global, globalType)); return; } @@ -367,6 +377,12 @@ struct GlobalStructInference : public Pass { builder.makeConstantExpression(values[1]))); } + void visitFunction(Function* func) { + if (refinalize) { + ReFinalize().walkFunctionInModule(func, getModule()); + } + } + private: GlobalStructInference& parent; }; diff --git a/test/lit/passes/gsi.wast b/test/lit/passes/gsi.wast index df1abc04f..559050f1f 100644 --- a/test/lit/passes/gsi.wast +++ b/test/lit/passes/gsi.wast @@ -1390,3 +1390,50 @@ ) ) ) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field funcref))) + (type $A (struct (field funcref))) + ;; CHECK: (type $B (sub $A (struct (field (ref func))))) + (type $B (sub $A (struct (field (ref func))))) + ) + + ;; CHECK: (type $2 (func (param (ref null $A) (ref null $B)) (result funcref))) + + ;; CHECK: (global $global (ref $B) (struct.new $B + ;; CHECK-NEXT: (ref.func $func) + ;; CHECK-NEXT: )) + (global $global (ref $B) (struct.new $B + (ref.func $func) + )) + + ;; CHECK: (func $func (type $2) (param $a (ref null $A)) (param $b (ref null $B)) (result funcref) + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (block (result (ref $B)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.tee $a + ;; CHECK-NEXT: (local.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.get $global) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $func (param $a (ref null $A)) (param $b (ref null $B)) (result funcref) + (struct.get $A 0 + ;; We can infer that we read from $global here, since it is the only place + ;; a $B is created (the tee in the middle to $A does not confuse us). + ;; After that, the struct.get will be reading a global.get of $global, + ;; which is of type $B, and compared to $A from before we will read a more + ;; refined type from the field, ref func vs funcref, which must be updated + ;; in the IR. + (local.tee $a + (local.get $b) + ) + ) + ) +) |