summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/lit/passes/gsi.wast93
-rw-r--r--test/lit/passes/gsi_vacuum_precompute.wast97
2 files changed, 171 insertions, 19 deletions
diff --git a/test/lit/passes/gsi.wast b/test/lit/passes/gsi.wast
index 9b0315df3..ae54796d6 100644
--- a/test/lit/passes/gsi.wast
+++ b/test/lit/passes/gsi.wast
@@ -88,31 +88,70 @@
)
)
-;; Just one global. We do not optimize here - we let other passes do that.
+;; Just one global.
(module
- ;; CHECK: (type $struct (struct_subtype (field i32) data))
- (type $struct (struct i32))
+ ;; CHECK: (type $struct1 (struct_subtype (field i32) data))
+ (type $struct1 (struct i32))
- ;; CHECK: (type $ref?|$struct|_=>_none (func_subtype (param (ref null $struct)) func))
+ ;; CHECK: (type $struct2 (struct_subtype (field i32) data))
+ (type $struct2 (struct i32))
- ;; CHECK: (global $global1 (ref $struct) (struct.new $struct
+
+ ;; CHECK: (type $ref?|$struct1|_ref?|$struct2|_=>_none (func_subtype (param (ref null $struct1) (ref null $struct2)) func))
+
+ ;; CHECK: (import "a" "b" (global $imported i32))
+ (import "a" "b" (global $imported i32))
+
+ ;; CHECK: (global $global1 (ref $struct1) (struct.new $struct1
+ ;; CHECK-NEXT: (global.get $imported)
+ ;; CHECK-NEXT: ))
+ (global $global1 (ref $struct1) (struct.new $struct1
+ (global.get $imported)
+ ))
+
+ ;; CHECK: (global $global2 (ref $struct2) (struct.new $struct2
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
- (global $global1 (ref $struct) (struct.new $struct
+ (global $global2 (ref $struct2) (struct.new $struct2
(i32.const 42)
))
- ;; CHECK: (func $test (type $ref?|$struct|_=>_none) (param $struct (ref null $struct))
+ ;; CHECK: (func $test1 (type $ref?|$struct1|_ref?|$struct2|_=>_none) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (struct.get $struct 0
- ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (struct.get $struct1 0
+ ;; CHECK-NEXT: (block (result (ref $struct1))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $struct1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $struct2 0
+ ;; CHECK-NEXT: (block (result (ref $struct2))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global2)
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- (func $test (param $struct (ref null $struct))
+ (func $test1 (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
+ ;; We can infer that this get must reference $global1 and make the reference
+ ;; point to that. Note that we do not infer the value of 42 here, but leave
+ ;; it for other passes to do.
(drop
- (struct.get $struct 0
- (local.get $struct)
+ (struct.get $struct1 0
+ (local.get $struct1)
+ )
+ )
+ ;; Even though the value here is not known at compile time - it reads an
+ ;; imported global - we can still infer that we are reading from $global2.
+ (drop
+ (struct.get $struct2 0
+ (local.get $struct2)
)
)
)
@@ -805,7 +844,12 @@
;; CHECK: (func $test (type $ref?|$struct|_ref?|$super-struct|_=>_none) (param $struct (ref null $struct)) (param $super-struct (ref null $super-struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
- ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (block (result (ref $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global2)
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -822,9 +866,9 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct)) (param $super-struct (ref null $super-struct))
- ;; We cannot optimize the first - it has just one global - but the second
- ;; will consider the struct and sub-struct, find 2 possible values, and
- ;; optimize.
+ ;; The first has just one global, which we switch the reference to, while
+ ;; the second will consider the struct and sub-struct, find 2 possible
+ ;; values, and optimize.
(drop
(struct.get $struct 0
(local.get $struct)
@@ -929,12 +973,22 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct1 0
- ;; CHECK-NEXT: (local.get $struct1)
+ ;; CHECK-NEXT: (block (result (ref $struct1))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $struct1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global1)
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct2 0
- ;; CHECK-NEXT: (local.get $struct2)
+ ;; CHECK-NEXT: (block (result (ref $struct2))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global2)
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -946,7 +1000,8 @@
(local.get $super-struct)
)
)
- ;; These each have one possible value, so we also do not optimize.
+ ;; These each have one possible value, which we can switch the references
+ ;; to.
(drop
(struct.get $struct1 0
(local.get $struct1)
diff --git a/test/lit/passes/gsi_vacuum_precompute.wast b/test/lit/passes/gsi_vacuum_precompute.wast
new file mode 100644
index 000000000..404de1a7e
--- /dev/null
+++ b/test/lit/passes/gsi_vacuum_precompute.wast
@@ -0,0 +1,97 @@
+;; 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
+
+;; 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
+;; method "getHash()" which all itables implement by an instance of that vtable.
+;; Despite all those vtables having the same type, we can infer things because
+;; those vtables are created as part of the immutable itables in global vars.
+;; The specific optimizations used to achieve that are:
+;;
+;; * --gsi : Infers that a reference to a particular itable type must be a
+;; global.get of the single itable defined using that type (since no
+;; other object of that type is ever created).
+;; * --vacuum : Cleans up some dropped stuff to enable the subsequent pass.
+;; * --precompute : Represents immutable globals in a way that we can start
+;; 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.
+
+(module
+ ;; CHECK: (type $vtable (struct_subtype (field funcref) data))
+
+ ;; CHECK: (type $itable1 (struct_subtype (field (ref $vtable)) data))
+ (type $itable1 (struct_subtype (field (ref $vtable)) data))
+ ;; CHECK: (type $itable2 (struct_subtype (field (ref $vtable)) data))
+ (type $itable2 (struct_subtype (field (ref $vtable)) data))
+ (type $vtable (struct_subtype (field funcref) data))
+
+ ;; Two $vtable instances are created, in separate enclosing objects.
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (type $ref|$itable1|_=>_funcref (func_subtype (param (ref $itable1)) (result funcref) func))
+
+ ;; CHECK: (type $ref|$itable2|_=>_funcref (func_subtype (param (ref $itable2)) (result funcref) func))
+
+ ;; CHECK: (global $itable1 (ref $itable1) (struct.new $itable1
+ ;; CHECK-NEXT: (struct.new $vtable
+ ;; CHECK-NEXT: (ref.func $func1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: ))
+ (global $itable1 (ref $itable1) (struct.new $itable1
+ (struct.new $vtable
+ (ref.func $func1)
+ )
+ ))
+
+ ;; CHECK: (global $itable2 (ref $itable2) (struct.new $itable2
+ ;; CHECK-NEXT: (struct.new $vtable
+ ;; CHECK-NEXT: (ref.func $func2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: ))
+ (global $itable2 (ref $itable2) (struct.new $itable2
+ (struct.new $vtable
+ (ref.func $func2)
+ )
+ ))
+
+ ;; CHECK: (elem declare func $func1 $func2)
+
+ ;; CHECK: (export "test-A" (func $test-A))
+
+ ;; CHECK: (export "test-B" (func $test-B))
+
+ ;; CHECK: (func $test-A (type $ref|$itable1|_=>_funcref) (param $ref (ref $itable1)) (result funcref)
+ ;; CHECK-NEXT: (ref.func $func1)
+ ;; CHECK-NEXT: )
+ (func $test-A (export "test-A") (param $ref (ref $itable1)) (result funcref)
+ (struct.get $vtable 0 ;; this is a reference to $func1
+ (struct.get $itable1 0 ;; this is the sub-object in the global $itable1
+ (local.get $ref) ;; this can be inferred to be the global $itable1
+ )
+ )
+ )
+
+ ;; CHECK: (func $test-B (type $ref|$itable2|_=>_funcref) (param $ref (ref $itable2)) (result funcref)
+ ;; CHECK-NEXT: (ref.func $func2)
+ ;; CHECK-NEXT: )
+ (func $test-B (export "test-B") (param $ref (ref $itable2)) (result funcref)
+ (struct.get $vtable 0 ;; this is a reference to $func2
+ (struct.get $itable2 0 ;; this is the sub-object in the global $itable2
+ (local.get $ref) ;; this can be inferred to be the global $itable2
+ )
+ )
+ )
+
+ ;; CHECK: (func $func1 (type $none_=>_none)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $func1)
+
+ ;; CHECK: (func $func2 (type $none_=>_none)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $func2)
+)
+