1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
|
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --gsi --vacuum --precompute -tnh --closed-world -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.
;; * -tnh : This is needed for vacuum to successfully remove the dropped stuff
;; that could prevent later opts.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $vtable (sub (struct (field funcref))))
(type $vtable (sub (struct (field funcref))))
;; CHECK: (type $itable1 (sub (struct (field (ref $vtable)))))
(type $itable1 (sub (struct (field (ref $vtable)))))
;; CHECK: (type $itable2 (sub (struct (field (ref $vtable)))))
(type $itable2 (sub (struct (field (ref $vtable)))))
)
;; Two $vtable instances are created, in separate enclosing objects.
;; CHECK: (type $3 (func (param (ref any)) (result funcref)))
;; CHECK: (type $4 (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 $3) (param $ref (ref any)) (result funcref)
;; CHECK-NEXT: (ref.func $func1)
;; CHECK-NEXT: )
(func $test-A (export "test-A") (param $ref (ref any)) (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
(ref.cast (ref $itable1)
(local.get $ref) ;; this can be inferred to be the global $itable1
)
)
)
)
;; CHECK: (func $test-B (type $3) (param $ref (ref any)) (result funcref)
;; CHECK-NEXT: (ref.func $func2)
;; CHECK-NEXT: )
(func $test-B (export "test-B") (param $ref (ref any)) (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
(ref.cast (ref $itable2)
(local.get $ref) ;; this can be inferred to be the global $itable2
)
)
)
)
;; CHECK: (func $func1 (type $4)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func1)
;; CHECK: (func $func2 (type $4)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func2)
)
|