diff options
author | Roberto Lublinerman <rluble@google.com> | 2024-05-09 14:05:53 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-09 14:05:53 -0700 |
commit | a816627051c67ae14f6defc8fc5c616ba427a29e (patch) | |
tree | c7f861c41915279052667e674d4a7811ea9b7c78 /test | |
parent | 712ad9d83953101abec01e5017e306fcb4bf7f70 (diff) | |
download | binaryen-a816627051c67ae14f6defc8fc5c616ba427a29e.tar.gz binaryen-a816627051c67ae14f6defc8fc5c616ba427a29e.tar.bz2 binaryen-a816627051c67ae14f6defc8fc5c616ba427a29e.zip |
[J2Cl] Make J2clOpts more effective with transitive deps in constant intialization (#6571)
Constants that need to be hoisted sometimes are initialized by calling
getters of other constants that need to be hoisted. These getters are
non-trivial, e.g.
(func $getConst1_<once>_@X (result (ref null $A))
(block (result (ref null $A))
(if (i32.eqz (ref.is_null (global.get $$const1@X)))
(then
(return (global.get $$const1@X))
)
)
(global.set $$const1@X (struct.new $A (i32.const 2)))
(global.get $$const1@X)
)
(func $getConst2_<once>_@X (result (ref null $A))
(block (result (ref null $A))
(if (i32.eqz (ref.is_null (global.get $$const2@X)))
(then
(return (global.get $$const2@X))
)
)
(global.set $$const2@X .... expression with (call $getConst1_<once>_@X) ....))
(global.get $$const2@X)
)
and can only be simplified after the constants they initialize are hoisted. After
the constant is hoisted the getter can be inlined and constants that depend on
it for their initialization can now be hoisted.
Before this pass, inlining would happen after the pass was run by a subsequent
run of the inliner (likely as part of -O3), requiring as many runs of this pass,
interleaved with the inliner, as the depth in the call sequence.
By having a simpler inliner run as part of the loop in this pass, the pass becomes
more effective and more independent of the call depths.
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/passes/j2cl-inline.wast | 4 | ||||
-rw-r--r-- | test/lit/passes/j2cl.wast | 194 |
2 files changed, 176 insertions, 22 deletions
diff --git a/test/lit/passes/j2cl-inline.wast b/test/lit/passes/j2cl-inline.wast index 9b6d4127b..9148b5378 100644 --- a/test/lit/passes/j2cl-inline.wast +++ b/test/lit/passes/j2cl-inline.wast @@ -1,8 +1,6 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; NOTE: In real world example no-inline would use _<once>_ but there is escaping problem in a multi-platform -;; way in lit so we are working around it by using no-inline with a different pattern that matches same method. -;; RUN: foreach %s %t wasm-opt --no-inline=*clinit* --optimize-j2cl --inlining --vacuum --optimize-level=3 -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --optimize-j2cl --vacuum -all -S -o - | filecheck %s ;; Only trivial once functions are inlined (module diff --git a/test/lit/passes/j2cl.wast b/test/lit/passes/j2cl.wast index 50fe807c0..1963203a8 100644 --- a/test/lit/passes/j2cl.wast +++ b/test/lit/passes/j2cl.wast @@ -152,22 +152,23 @@ ) ) - +;; Getters are transitively inlined and their constants hoisted. (module ;; CHECK: (type $0 (func (result i32))) - ;; CHECK: (global $$var2@Zoo (mut i32) (i32.const 0)) + ;; CHECK: (global $$var3@Zoo i32 (i32.const 2)) + + ;; CHECK: (global $$var2@Zoo i32 (i32.const 2)) ;; CHECK: (global $$var1@Zoo i32 (i32.const 2)) (global $$var1@Zoo (mut i32) (i32.const 0)) (global $$var2@Zoo (mut i32) (i32.const 0)) - - ;; CHECK: (export "getVar1_<once>_@Zoo" (func $getVar1_<once>_@Zoo)) + (global $$var3@Zoo (mut i32) (i32.const 0)) ;; CHECK: (func $getVar1_<once>_@Zoo (type $0) (result i32) ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) - (func $getVar1_<once>_@Zoo (export "getVar1_<once>_@Zoo") (result i32) + (func $getVar1_<once>_@Zoo (result i32) (if (global.get $$var1@Zoo) (then (return (global.get $$var1@Zoo)) @@ -178,20 +179,7 @@ ) ;; CHECK: (func $getVar2_<once>_@Zoo (type $0) (result i32) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (global.get $$var2@Zoo) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (global.get $$var2@Zoo) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $$var2@Zoo - ;; CHECK-NEXT: (call $getVar1_<once>_@Zoo) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (global.get $$var2@Zoo) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) (func $getVar2_<once>_@Zoo (result i32) (if (global.get $$var2@Zoo) @@ -202,4 +190,172 @@ (global.set $$var2@Zoo (call $getVar1_<once>_@Zoo)) (return (global.get $$var2@Zoo)) ) + + ;; CHECK: (func $getVar3_<once>_@Zoo (type $0) (result i32) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + (func $getVar3_<once>_@Zoo (result i32) + (if (global.get $$var3@Zoo) + (then + (return (global.get $$var3@Zoo)) + ) + ) + (global.set $$var3@Zoo (call $getVar2_<once>_@Zoo)) + (return (global.get $$var3@Zoo)) + ) +) + +;; Simple once functions are inlined +(module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (type $1 (func (result i32))) + + ;; CHECK: (global $$var2@Zoo (mut i32) (i32.const 3)) + + ;; CHECK: (global $$var1@Zoo (mut i32) (i32.const 2)) + (global $$var1@Zoo (mut i32) (i32.const 2)) + (global $$var2@Zoo (mut i32) (i32.const 3)) + + + ;; CHECK: (func $notOnceFunction@Zoo (type $0) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $notOnceFunction@Zoo + ) + + ;; CHECK: (func $nop_<once>_@Zoo (type $0) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $nop_<once>_@Zoo + (nop) + ) + + ;; CHECK: (func $empty_<once>_@Zoo (type $0) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $empty_<once>_@Zoo + ) + + ;; CHECK: (func $simpleCall_<once>_@Zoo (type $0) + ;; CHECK-NEXT: (call $notOnceFunction@Zoo) + ;; CHECK-NEXT: ) + (func $simpleCall_<once>_@Zoo + (call $notOnceFunction@Zoo) + ) + + ;; CHECK: (func $globalGet_<once>_@Zoo (type $1) (result i32) + ;; CHECK-NEXT: (global.get $$var1@Zoo) + ;; CHECK-NEXT: ) + (func $globalGet_<once>_@Zoo (result i32) + (global.get $$var1@Zoo) + ) + + ;; CHECK: (func $globalSet_<once>_@Zoo (type $0) + ;; CHECK-NEXT: (global.set $$var2@Zoo + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $globalSet_<once>_@Zoo + (global.set $$var2@Zoo (i32.const 3)) + ) + + ;; CHECK: (func $caller_@Zoo (type $1) (result i32) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (call $notOnceFunction@Zoo) + ;; CHECK-NEXT: (global.set $$var2@Zoo + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.get $$var1@Zoo) + ;; CHECK-NEXT: ) + (func $caller_@Zoo (result i32) + (call $nop_<once>_@Zoo) + (call $empty_<once>_@Zoo) + (call $simpleCall_<once>_@Zoo) + (call $globalSet_<once>_@Zoo) + (call $globalGet_<once>_@Zoo) + ) +) + +;; Simple once functions that would be inlined if cleaned up. +(module + ;; CHECK: (type $0 (func (result i32))) + + ;; CHECK: (type $1 (func)) + + ;; CHECK: (global $$var1@Zoo (mut i32) (i32.const 2)) + (global $$var1@Zoo (mut i32) (i32.const 2)) + + + ;; CHECK: (func $justReturn_<once>_@Zoo (type $1) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + (func $justReturn_<once>_@Zoo + (return) + ) + + ;; CHECK: (func $returnGlobalGet_<once>_@Zoo (type $0) (result i32) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (global.get $$var1@Zoo) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $returnGlobalGet_<once>_@Zoo (result i32) + (return (global.get $$var1@Zoo)) + ) + + ;; CHECK: (func $caller_@Zoo (type $0) (result i32) + ;; CHECK-NEXT: (call $justReturn_<once>_@Zoo) + ;; CHECK-NEXT: (call $returnGlobalGet_<once>_@Zoo) + ;; CHECK-NEXT: ) + (func $caller_@Zoo (result i32) + (call $justReturn_<once>_@Zoo) + (call $returnGlobalGet_<once>_@Zoo) + ) +) + +;; Hoist constants for getters that have transitive dependencies. +(module + ;; CHECK: (type $A (struct (field i32))) + (type $A (struct (field i32))) + + ;; CHECK: (type $1 (func (result (ref null $A)))) + + ;; CHECK: (global $$class@X (ref null $A) (struct.new $A + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: )) + (global $$class@X (mut (ref null $A)) (ref.null $A)) + ;; CHECK: (global $$class@Y (ref null $A) (global.get $$class@X)) + (global $$class@Y (mut (ref null $A)) (ref.null $A)) + + ;; CHECK: (func $f_<once>_@X (type $1) (result (ref null $A)) + ;; CHECK-NEXT: (global.get $$class@X) + ;; CHECK-NEXT: ) + (func $f_<once>_@X (result (ref null $A)) + (block (result (ref null $A)) + (if (i32.eqz (ref.is_null (global.get $$class@X))) + (then + (return (global.get $$class@X)) + ) + ) + (global.set $$class@X (struct.new $A (i32.const 2))) + (global.get $$class@X) + ) + ) + + ;; CHECK: (func $f_<once>_@Y (type $1) (result (ref null $A)) + ;; CHECK-NEXT: (global.get $$class@Y) + ;; CHECK-NEXT: ) + (func $f_<once>_@Y (result (ref null $A)) + (block (result (ref null $A)) + (if + (i32.eqz (ref.is_null (global.get $$class@Y))) + (then + (return (global.get $$class@Y)) + ) + ) + (global.set $$class@Y (call $f_<once>_@X)) + (global.get $$class@Y) + ) + ) ) |