diff options
author | Alon Zakai <azakai@google.com> | 2022-11-15 11:21:44 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-15 11:21:44 -0800 |
commit | 0ffafa5d4f4bca8a6ab77e193caacc5292218a5d (patch) | |
tree | b0ea0aa88f03e231ec746f2ad18f394106ac6c3e | |
parent | 59cbdd818dc3397a823a37e9821fd32f4522b2fc (diff) | |
download | binaryen-0ffafa5d4f4bca8a6ab77e193caacc5292218a5d.tar.gz binaryen-0ffafa5d4f4bca8a6ab77e193caacc5292218a5d.tar.bz2 binaryen-0ffafa5d4f4bca8a6ab77e193caacc5292218a5d.zip |
GlobalStructInference: Handle the case of just 1 value (#5259)
#5253 handled the case of just one possible global. It is also possible we have
multiple globals but just one value. This handles that case. (It slightly overlaps
with other passes, but as this pass actually identifies the creations of the objects
in globals, it has a guarantee of success that the others don't, and it is very easy
to just do given all the work done to handle the case of 2 values).
Also fix a minor bug in #5253 - we need to trap if the old reference were null.
That is, we know the reference must point to the only object ever created of
that type, but that is only if it is not null; if it's null we need to trap.
-rw-r--r-- | src/passes/GlobalStructInference.cpp | 20 | ||||
-rw-r--r-- | test/lit/passes/gsi.wast | 63 | ||||
-rw-r--r-- | test/lit/passes/gsi_vacuum_precompute.wast | 4 |
3 files changed, 72 insertions, 15 deletions
diff --git a/src/passes/GlobalStructInference.cpp b/src/passes/GlobalStructInference.cpp index 39761c1d6..8bb4c0ba9 100644 --- a/src/passes/GlobalStructInference.cpp +++ b/src/passes/GlobalStructInference.cpp @@ -219,15 +219,16 @@ struct GlobalStructInference : public Pass { } auto& wasm = *getModule(); + Builder builder(wasm); if (globals.size() == 1) { // Leave it to other passes to infer the constant value of the field, // if there is one: just change the reference to the global, which - // will unlock those other optimizations. + // will unlock those other optimizations. Note we must trap if the ref + // is null, so add RefAsNonNull here. auto global = globals[0]; - Builder builder(wasm); curr->ref = builder.makeSequence( - builder.makeDrop(curr->ref), + builder.makeDrop(builder.makeRefAs(RefAsNonNull, curr->ref)), builder.makeGlobalGet(global, wasm.getGlobal(globals[0])->type)); return; } @@ -293,12 +294,14 @@ struct GlobalStructInference : public Pass { } // We have some globals (at least 2), and so must have at least one - // value. And we have already exited if we have more than 2, so that - // only leaves 1 and 2. We are looking for the case of 2 here, since - // other passes (ConstantFieldPropagation) can handle 1. - // TODO: We can perhaps do better than CFP, as we know the structs are - // created in globals. + // value. And we have already exited if we have more than 2 values (see + // the early return above) so that only leaves 1 and 2. if (values.size() == 1) { + // The case of 1 value is simple: trap if the ref is null, and + // otherwise return the value. + replaceCurrent(builder.makeSequence( + builder.makeDrop(builder.makeRefAs(RefAsNonNull, curr->ref)), + builder.makeConstantExpression(values[0]))); return; } assert(values.size() == 2); @@ -321,7 +324,6 @@ struct GlobalStructInference : public Pass { // // Note that we must trap on null, so add a ref.as_non_null here. auto checkGlobal = globalsForValue[0][0]; - Builder builder(wasm); replaceCurrent(builder.makeSelect( builder.makeRefEq(builder.makeRefAs(RefAsNonNull, curr->ref), builder.makeGlobalGet( diff --git a/test/lit/passes/gsi.wast b/test/lit/passes/gsi.wast index ae54796d6..66ca43de8 100644 --- a/test/lit/passes/gsi.wast +++ b/test/lit/passes/gsi.wast @@ -121,7 +121,9 @@ ;; CHECK-NEXT: (struct.get $struct1 0 ;; CHECK-NEXT: (block (result (ref $struct1)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $struct1) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $global1) ;; CHECK-NEXT: ) @@ -131,7 +133,9 @@ ;; CHECK-NEXT: (struct.get $struct2 0 ;; CHECK-NEXT: (block (result (ref $struct2)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $global2) ;; CHECK-NEXT: ) @@ -846,7 +850,9 @@ ;; CHECK-NEXT: (struct.get $struct 0 ;; CHECK-NEXT: (block (result (ref $struct)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $global2) ;; CHECK-NEXT: ) @@ -975,7 +981,9 @@ ;; CHECK-NEXT: (struct.get $struct1 0 ;; CHECK-NEXT: (block (result (ref $struct1)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $struct1) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $global1) ;; CHECK-NEXT: ) @@ -985,7 +993,9 @@ ;; CHECK-NEXT: (struct.get $struct2 0 ;; CHECK-NEXT: (block (result (ref $struct2)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $global2) ;; CHECK-NEXT: ) @@ -1123,3 +1133,46 @@ ) ) ) + +;; Multiple globals, but all the same value, so we do not even need a select and +;; can just apply the value. +(module + ;; CHECK: (type $struct (struct_subtype (field i32) data)) + (type $struct (struct i32)) + + ;; CHECK: (type $ref?|$struct|_=>_none (func_subtype (param (ref null $struct)) func)) + + ;; CHECK: (global $global1 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global1 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (global $global2 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global2 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (func $test (type $ref?|$struct|_=>_none) (param $struct (ref null $struct)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $struct (ref null $struct)) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + ) +) diff --git a/test/lit/passes/gsi_vacuum_precompute.wast b/test/lit/passes/gsi_vacuum_precompute.wast index 404de1a7e..856499ba8 100644 --- a/test/lit/passes/gsi_vacuum_precompute.wast +++ b/test/lit/passes/gsi_vacuum_precompute.wast @@ -1,5 +1,5 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --nominal --gsi --vacuum --precompute -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --nominal --gsi --vacuum --precompute -tnh -all -S -o - | filecheck %s ;; Test a common pattern in j2wasm where itables are differentiated by type, but ;; vtables are not. For example, the vtable might be "hashable" and provide a @@ -16,6 +16,8 @@ ;; from a global.get of an itable, get a vtable, and get a ;; field in the vtable, and we end up optimizing to produce ;; the final value in that vtable. +;; * -tnh : This is needed for vacuum to successfully remove the dropped stuff +;; that could prevent later opts. (module ;; CHECK: (type $vtable (struct_subtype (field funcref) data)) |