blob: 1cbf47032b559457a32a0677a95213dccd5eaf70 (
plain)
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 (struct_subtype (field funcref) data))
;; CHECK: (type $itable1 (sub (struct (field (ref $vtable)))))
(type $itable1 (struct_subtype (field (ref $vtable)) data))
;; CHECK: (type $itable2 (sub (struct (field (ref $vtable)))))
(type $itable2 (struct_subtype (field (ref $vtable)) data))
)
;; 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)
)
|