Skip to content

Commit 9831b36

Browse files
authored
Group reference types in binary format. (#4774)
Grouping all references together makes it easier for baseline compilers to zero out memory (as the zeroing out may be different for MVP types vs. references). This puts all references together, either at the start or the end. As a heuristic for that we see if the first local is a reference. As the optimizer will sort locals by frequency, this ensures that the most-frequent local stays in index 0. Fixes #4773. See more details there
1 parent a82e2db commit 9831b36

5 files changed

+182
-7
lines changed

src/wasm/wasm-stack.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2327,6 +2327,29 @@ void BinaryInstWriter::mapLocalsAndEmitHeader() {
23272327
}
23282328
}
23292329
countScratchLocals();
2330+
2331+
if (parent.getModule()->features.hasReferenceTypes()) {
2332+
// Sort local types in a way that keeps all MVP types together and all
2333+
// reference types together. E.g. it is helpful to avoid a block of i32s in
2334+
// between blocks of different reference types, since clearing out reference
2335+
// types may require different work.
2336+
//
2337+
// See https://github.com/WebAssembly/binaryen/issues/4773
2338+
//
2339+
// In order to decide whether to put MVP types or reference types first,
2340+
// look at the type of the first local. In an optimized binary we will have
2341+
// sorted the locals by frequency of uses, so this way we'll keep the most
2342+
// commonly-used local at the top, which should work well in many cases.
2343+
bool refsFirst = !localTypes.empty() && localTypes[0].isRef();
2344+
std::stable_sort(localTypes.begin(), localTypes.end(), [&](Type a, Type b) {
2345+
if (refsFirst) {
2346+
return a.isRef() && !b.isRef();
2347+
} else {
2348+
return !a.isRef() && b.isRef();
2349+
}
2350+
});
2351+
}
2352+
23302353
std::unordered_map<Type, size_t> currLocalsByType;
23312354
for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) {
23322355
Index j = 0;

test/typed-function-references.wast

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,45 @@
4141
)
4242
)
4343
)
44+
(func $ref-types-first
45+
;; 6 reference types and 3 MVP types. The binary format should emit all the
46+
;; reference types first since a reference type appears first. In addition,
47+
;; types should be emitted in blocks there, that is, locals of identical
48+
;; types should be adjacent.
49+
(local $r1 (ref null $mixed_results))
50+
(local $r2 (ref null $mixed_results))
51+
(local $i1 i32)
52+
(local $r3 anyref)
53+
(local $i2 i64)
54+
(local $r4 anyref)
55+
(local $i3 i64)
56+
(local $r5 anyref)
57+
(local $r6 funcref)
58+
)
59+
(func $mvp-types-first
60+
;; Reversed from before, now an MVP type appears first, so they should all
61+
;; be before reference types in the binary format.
62+
(local $i1 i32) ;; only this local was moved up.
63+
(local $r1 (ref null $mixed_results))
64+
(local $r2 (ref null $mixed_results))
65+
(local $r3 anyref)
66+
(local $i2 i64)
67+
(local $r4 anyref)
68+
(local $i3 i64)
69+
(local $r5 anyref)
70+
(local $r6 funcref)
71+
)
72+
(func $mvp-types-first-param (param $r0 (ref null $mixed_results))
73+
;; As before, but now there is a reference type *parameter*. We should
74+
;; ignore that and sort as in the last function.
75+
(local $i1 i32) ;; only this local was moved up.
76+
(local $r1 (ref null $mixed_results))
77+
(local $r2 (ref null $mixed_results))
78+
(local $r3 anyref)
79+
(local $i2 i64)
80+
(local $r4 anyref)
81+
(local $i3 i64)
82+
(local $r5 anyref)
83+
(local $r6 funcref)
84+
)
4485
)

test/typed-function-references.wast.from-wast

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
(module
2+
(type $mixed_results (func (result anyref f32 anyref f32)))
23
(type $none_=>_none (func))
34
(type $i32-i32 (func (param i32) (result i32)))
45
(type $=>eqref (func (result eqref)))
@@ -8,7 +9,7 @@
89
(type $f64_=>_ref_null<_->_eqref> (func (param f64) (result (ref null $=>eqref))))
910
(type $=>anyref (func (result anyref)))
1011
(type $none_=>_i32_ref?|$mixed_results|_f64 (func (result i32 (ref null $mixed_results) f64)))
11-
(type $mixed_results (func (result anyref f32 anyref f32)))
12+
(type $ref?|$mixed_results|_=>_none (func (param (ref null $mixed_results))))
1213
(elem declare func $call-ref $call-ref-more)
1314
(func $call-ref
1415
(call_ref
@@ -62,4 +63,40 @@
6263
)
6364
)
6465
)
66+
(func $ref-types-first
67+
(local $r1 (ref null $mixed_results))
68+
(local $r2 (ref null $mixed_results))
69+
(local $i1 i32)
70+
(local $r3 anyref)
71+
(local $i2 i64)
72+
(local $r4 anyref)
73+
(local $i3 i64)
74+
(local $r5 anyref)
75+
(local $r6 funcref)
76+
(nop)
77+
)
78+
(func $mvp-types-first
79+
(local $i1 i32)
80+
(local $r1 (ref null $mixed_results))
81+
(local $r2 (ref null $mixed_results))
82+
(local $r3 anyref)
83+
(local $i2 i64)
84+
(local $r4 anyref)
85+
(local $i3 i64)
86+
(local $r5 anyref)
87+
(local $r6 funcref)
88+
(nop)
89+
)
90+
(func $mvp-types-first-param (param $r0 (ref null $mixed_results))
91+
(local $i1 i32)
92+
(local $r1 (ref null $mixed_results))
93+
(local $r2 (ref null $mixed_results))
94+
(local $r3 anyref)
95+
(local $i2 i64)
96+
(local $r4 anyref)
97+
(local $i3 i64)
98+
(local $r5 anyref)
99+
(local $r6 funcref)
100+
(nop)
101+
)
65102
)

test/typed-function-references.wast.fromBinary

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
(module
2+
(type $mixed_results (func (result anyref f32 anyref f32)))
23
(type $none_=>_none (func))
34
(type $i32-i32 (func (param i32) (result i32)))
4-
(type $mixed_results (func (result anyref f32 anyref f32)))
55
(type $=>eqref (func (result eqref)))
66
(type $ref|$i32-i32|_=>_i32 (func (param (ref $i32-i32)) (result i32)))
77
(type $ref?|$i32-i32|_=>_i32 (func (param (ref null $i32-i32)) (result i32)))
88
(type $none_=>_i32 (func (result i32)))
99
(type $f64_=>_ref_null<_->_eqref> (func (param f64) (result (ref null $=>eqref))))
1010
(type $=>anyref (func (result anyref)))
1111
(type $none_=>_i32_ref?|$mixed_results|_f64 (func (result i32 (ref null $mixed_results) f64)))
12+
(type $ref?|$mixed_results|_=>_none (func (param (ref null $mixed_results))))
1213
(elem declare func $call-ref $call-ref-more)
1314
(func $call-ref
1415
(call_ref
@@ -53,8 +54,8 @@
5354
)
5455
(func $type-only-in-tuple-local
5556
(local $x i32)
56-
(local $1 (ref null $=>anyref))
57-
(local $2 f64)
57+
(local $1 f64)
58+
(local $2 (ref null $=>anyref))
5859
(nop)
5960
)
6061
(func $type-only-in-tuple-block
@@ -92,5 +93,41 @@
9293
)
9394
)
9495
)
96+
(func $ref-types-first
97+
(local $r1 (ref null $mixed_results))
98+
(local $r2 (ref null $mixed_results))
99+
(local $r3 anyref)
100+
(local $r4 anyref)
101+
(local $r5 anyref)
102+
(local $r6 funcref)
103+
(local $i1 i32)
104+
(local $i2 i64)
105+
(local $i3 i64)
106+
(nop)
107+
)
108+
(func $mvp-types-first
109+
(local $i1 i32)
110+
(local $i2 i64)
111+
(local $i3 i64)
112+
(local $r1 (ref null $mixed_results))
113+
(local $r2 (ref null $mixed_results))
114+
(local $r3 anyref)
115+
(local $r4 anyref)
116+
(local $r5 anyref)
117+
(local $r6 funcref)
118+
(nop)
119+
)
120+
(func $mvp-types-first-param (param $r0 (ref null $mixed_results))
121+
(local $i1 i32)
122+
(local $i2 i64)
123+
(local $i3 i64)
124+
(local $r1 (ref null $mixed_results))
125+
(local $r2 (ref null $mixed_results))
126+
(local $r3 anyref)
127+
(local $r4 anyref)
128+
(local $r5 anyref)
129+
(local $r6 funcref)
130+
(nop)
131+
)
95132
)
96133

test/typed-function-references.wast.fromBinary.noDebugInfo

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
(module
2+
(type $none_=>_anyref_f32_anyref_f32 (func (result anyref f32 anyref f32)))
23
(type $none_=>_none (func))
34
(type $i32_=>_i32 (func (param i32) (result i32)))
4-
(type $none_=>_anyref_f32_anyref_f32 (func (result anyref f32 anyref f32)))
55
(type $none_=>_eqref (func (result eqref)))
66
(type $ref|i32_->_i32|_=>_i32 (func (param (ref $i32_=>_i32)) (result i32)))
77
(type $ref?|i32_->_i32|_=>_i32 (func (param (ref null $i32_=>_i32)) (result i32)))
88
(type $none_=>_i32 (func (result i32)))
99
(type $f64_=>_ref?|none_->_eqref| (func (param f64) (result (ref null $none_=>_eqref))))
1010
(type $none_=>_anyref (func (result anyref)))
1111
(type $none_=>_i32_ref?|none_->_anyref_f32_anyref_f32|_f64 (func (result i32 (ref null $none_=>_anyref_f32_anyref_f32) f64)))
12+
(type $ref?|none_->_anyref_f32_anyref_f32|_=>_none (func (param (ref null $none_=>_anyref_f32_anyref_f32))))
1213
(elem declare func $0 $2)
1314
(func $0
1415
(call_ref
@@ -53,8 +54,8 @@
5354
)
5455
(func $7
5556
(local $0 i32)
56-
(local $1 (ref null $none_=>_anyref))
57-
(local $2 f64)
57+
(local $1 f64)
58+
(local $2 (ref null $none_=>_anyref))
5859
(nop)
5960
)
6061
(func $8
@@ -92,5 +93,41 @@
9293
)
9394
)
9495
)
96+
(func $9
97+
(local $0 (ref null $none_=>_anyref_f32_anyref_f32))
98+
(local $1 (ref null $none_=>_anyref_f32_anyref_f32))
99+
(local $2 anyref)
100+
(local $3 anyref)
101+
(local $4 anyref)
102+
(local $5 funcref)
103+
(local $6 i32)
104+
(local $7 i64)
105+
(local $8 i64)
106+
(nop)
107+
)
108+
(func $10
109+
(local $0 i32)
110+
(local $1 i64)
111+
(local $2 i64)
112+
(local $3 (ref null $none_=>_anyref_f32_anyref_f32))
113+
(local $4 (ref null $none_=>_anyref_f32_anyref_f32))
114+
(local $5 anyref)
115+
(local $6 anyref)
116+
(local $7 anyref)
117+
(local $8 funcref)
118+
(nop)
119+
)
120+
(func $11 (param $0 (ref null $none_=>_anyref_f32_anyref_f32))
121+
(local $1 i32)
122+
(local $2 i64)
123+
(local $3 i64)
124+
(local $4 (ref null $none_=>_anyref_f32_anyref_f32))
125+
(local $5 (ref null $none_=>_anyref_f32_anyref_f32))
126+
(local $6 anyref)
127+
(local $7 anyref)
128+
(local $8 anyref)
129+
(local $9 funcref)
130+
(nop)
131+
)
95132
)
96133

0 commit comments

Comments
 (0)