Skip to content

Commit f124a11

Browse files
authored
Update nominal type ordering (#4631)
V8 requires that supertypes come before subtypes when it parses isorecursive (i.e. standards-track) type definitions. Since 2268f2a we are emitting nominal types using the standard isorecursive format, so respect the ordering requirement.
1 parent 5d15783 commit f124a11

13 files changed

+119
-68
lines changed

src/ir/module-utils.cpp

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,10 @@ std::vector<HeapType> collectHeapTypes(Module& wasm) {
203203
}
204204

205205
IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) {
206+
TypeSystem system = getTypeSystem();
206207
Counts counts = getHeapTypeCounts(wasm);
207208

208-
if (getTypeSystem() != TypeSystem::Isorecursive) {
209+
if (system == TypeSystem::Equirecursive) {
209210
// Sort by frequency and then original insertion order.
210211
std::vector<std::pair<HeapType, size_t>> sorted(counts.begin(),
211212
counts.end());
@@ -223,9 +224,12 @@ IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) {
223224
return indexedTypes;
224225
}
225226

226-
// Isorecursive types have to be arranged into topologically ordered recursion
227-
// groups. Sort the groups by average use count among their members so that
228-
// the topological sort will place frequently used types first.
227+
// Types have to be arranged into topologically ordered recursion groups.
228+
// Under isorecrsive typing, the topological sort has to take all referenced
229+
// rec groups into account but under nominal typing it only has to take
230+
// supertypes into account. First, sort the groups by average use count among
231+
// their members so that the later topological sort will place frequently used
232+
// types first.
229233
struct GroupInfo {
230234
size_t index;
231235
double useCount = 0;
@@ -236,7 +240,7 @@ IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) {
236240
if (useCount != other.useCount) {
237241
return useCount < other.useCount;
238242
}
239-
return index < other.index;
243+
return index > other.index;
240244
}
241245
};
242246

@@ -257,20 +261,35 @@ IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) {
257261
// Update the reference count.
258262
info.useCount += counts.at(type);
259263
// Collect predecessor groups.
260-
for (auto child : type.getReferencedHeapTypes()) {
261-
if (!child.isBasic()) {
262-
RecGroup otherGroup = child.getRecGroup();
263-
if (otherGroup != group) {
264-
info.preds.insert(otherGroup);
264+
switch (system) {
265+
case TypeSystem::Isorecursive:
266+
for (auto child : type.getReferencedHeapTypes()) {
267+
if (!child.isBasic()) {
268+
RecGroup otherGroup = child.getRecGroup();
269+
if (otherGroup != group) {
270+
info.preds.insert(otherGroup);
271+
}
272+
}
265273
}
266-
}
274+
break;
275+
case TypeSystem::Nominal:
276+
if (auto super = type.getSuperType()) {
277+
info.preds.insert(super->getRecGroup());
278+
}
279+
break;
280+
case TypeSystem::Equirecursive:
281+
WASM_UNREACHABLE(
282+
"Equirecursive types should already have been handled");
267283
}
268284
}
269285

270286
// Fix up the use counts to be averages to ensure groups are used comensurate
271-
// with the amount of index space they occupy.
272-
for (auto& [group, info] : groupInfos) {
273-
info.useCount /= group.size();
287+
// with the amount of index space they occupy. Skip this for nominal types
288+
// since their internal group size is always 1.
289+
if (system != TypeSystem::Nominal) {
290+
for (auto& [group, info] : groupInfos) {
291+
info.useCount /= group.size();
292+
}
274293
}
275294

276295
// Sort the predecessors so the most used will be visited first.

test/lit/lub-bug-3843.wast

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
(type $B (struct_subtype (field (ref null $D)) $A))
1717

1818
;; CHECK: (type $D (struct (field (mut (ref $A))) (field (mut (ref $A)))))
19+
;; NOMNL: (type $C (struct_subtype (field (mut (ref $A))) data))
20+
1921
;; NOMNL: (type $D (struct_subtype (field (mut (ref $A))) (field (mut (ref $A))) $C))
2022
(type $D (struct_subtype (field (mut (ref $A))) (field (mut (ref $A))) $C))
2123

2224
;; CHECK: (type $C (struct (field (mut (ref $A)))))
23-
;; NOMNL: (type $C (struct_subtype (field (mut (ref $A))) data))
2425
(type $C (struct (field (mut (ref $A)))))
2526

2627

test/lit/nominal-chain.wast

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@
77
;; types.
88

99
(module
10-
;; CHECK: (type $leaf (struct_subtype (field i32) (field i64) (field f32) (field f64) $twig))
11-
(type $leaf (struct_subtype i32 i64 f32 f64 $twig))
12-
1310
;; CHECK: (type $root (struct_subtype data))
1411

12+
;; CHECK: (type $trunk (struct_subtype (field i32) $root))
13+
14+
;; CHECK: (type $branch (struct_subtype (field i32) (field i64) $trunk))
15+
1516
;; CHECK: (type $twig (struct_subtype (field i32) (field i64) (field f32) $branch))
17+
18+
;; CHECK: (type $leaf (struct_subtype (field i32) (field i64) (field f32) (field f64) $twig))
19+
(type $leaf (struct_subtype i32 i64 f32 f64 $twig))
20+
1621
(type $twig (struct_subtype i32 i64 f32 $branch))
1722

18-
;; CHECK: (type $branch (struct_subtype (field i32) (field i64) $trunk))
1923
(type $branch (struct_subtype i32 i64 $trunk))
2024

21-
;; CHECK: (type $trunk (struct_subtype (field i32) $root))
2225
(type $trunk (struct_subtype i32 $root))
2326

2427
(type $root (struct))

test/lit/parse-nominal-types-extends.wast

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88

99
;; void function type
1010
(module
11+
;; CHECK: (type $super (func_subtype func))
12+
1113
;; CHECK: (type $sub (func_subtype $super))
1214
(type $sub (func) (extends $super))
1315

14-
;; CHECK: (type $super (func_subtype func))
1516
(type $super (func))
1617

1718
;; CHECK: (global $g (ref null $super) (ref.null $sub))
@@ -20,10 +21,11 @@
2021

2122
;; function type with params and results
2223
(module
24+
;; CHECK: (type $super (func_subtype (param i32) (result i32) func))
25+
2326
;; CHECK: (type $sub (func_subtype (param i32) (result i32) $super))
2427
(type $sub (func (param i32) (result i32)) (extends $super))
2528

26-
;; CHECK: (type $super (func_subtype (param i32) (result i32) func))
2729
(type $super (func (param i32) (result i32)))
2830

2931
;; CHECK: (global $g (ref null $super) (ref.null $sub))
@@ -32,10 +34,11 @@
3234

3335
;; empty struct type
3436
(module
37+
;; CHECK: (type $super (struct_subtype data))
38+
3539
;; CHECK: (type $sub (struct_subtype $super))
3640
(type $sub (struct) (extends $super))
3741

38-
;; CHECK: (type $super (struct_subtype data))
3942
(type $super (struct))
4043

4144
;; CHECK: (global $g (ref null $super) (ref.null $sub))
@@ -44,10 +47,11 @@
4447

4548
;; struct type with fields
4649
(module
50+
;; CHECK: (type $super (struct_subtype (field i32) (field i64) data))
51+
4752
;; CHECK: (type $sub (struct_subtype (field i32) (field i64) $super))
4853
(type $sub (struct i32 (field i64)) (extends $super))
4954

50-
;; CHECK: (type $super (struct_subtype (field i32) (field i64) data))
5155
(type $super (struct (field i32) i64))
5256

5357
;; CHECK: (global $g (ref null $super) (ref.null $sub))
@@ -56,10 +60,11 @@
5660

5761
;; array type
5862
(module
63+
;; CHECK: (type $super (array_subtype i8 data))
64+
5965
;; CHECK: (type $sub (array_subtype i8 $super))
6066
(type $sub (array i8) (extends $super))
6167

62-
;; CHECK: (type $super (array_subtype i8 data))
6368
(type $super (array i8))
6469

6570
;; CHECK: (global $g (ref null $super) (ref.null $sub))

test/lit/parse-nominal-types.wast

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88

99
;; void function type
1010
(module
11+
;; CHECK: (type $super (func_subtype func))
12+
1113
;; CHECK: (type $sub (func_subtype $super))
1214
(type $sub (func_subtype $super))
1315

14-
;; CHECK: (type $super (func_subtype func))
1516
(type $super (func_subtype func))
1617

1718
;; CHECK: (global $g (ref null $super) (ref.null $sub))
@@ -20,10 +21,11 @@
2021

2122
;; function type with params and results
2223
(module
24+
;; CHECK: (type $super (func_subtype (param i32) (result i32) func))
25+
2326
;; CHECK: (type $sub (func_subtype (param i32) (result i32) $super))
2427
(type $sub (func_subtype (param i32) (result i32) $super))
2528

26-
;; CHECK: (type $super (func_subtype (param i32) (result i32) func))
2729
(type $super (func_subtype (param i32) (result i32) func))
2830

2931
;; CHECK: (global $g (ref null $super) (ref.null $sub))
@@ -32,10 +34,11 @@
3234

3335
;; empty struct type
3436
(module
37+
;; CHECK: (type $super (struct_subtype data))
38+
3539
;; CHECK: (type $sub (struct_subtype $super))
3640
(type $sub (struct_subtype $super))
3741

38-
;; CHECK: (type $super (struct_subtype data))
3942
(type $super (struct_subtype data))
4043

4144
;; CHECK: (global $g (ref null $super) (ref.null $sub))
@@ -44,10 +47,11 @@
4447

4548
;; struct type with fields
4649
(module
50+
;; CHECK: (type $super (struct_subtype (field i32) (field i64) data))
51+
4752
;; CHECK: (type $sub (struct_subtype (field i32) (field i64) $super))
4853
(type $sub (struct_subtype i32 (field i64) $super))
4954

50-
;; CHECK: (type $super (struct_subtype (field i32) (field i64) data))
5155
(type $super (struct_subtype (field i32) i64 data))
5256

5357
;; CHECK: (global $g (ref null $super) (ref.null $sub))
@@ -56,10 +60,11 @@
5660

5761
;; array type
5862
(module
63+
;; CHECK: (type $super (array_subtype i8 data))
64+
5965
;; CHECK: (type $sub (array_subtype i8 $super))
6066
(type $sub (array_subtype i8 $super))
6167

62-
;; CHECK: (type $super (array_subtype i8 data))
6368
(type $super (array_subtype i8 data))
6469

6570
;; CHECK: (global $g (ref null $super) (ref.null $sub))

test/lit/passes/cfp.wast

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -664,10 +664,11 @@
664664
(module
665665
;; CHECK: (type $none_=>_none (func_subtype func))
666666

667+
;; CHECK: (type $struct (struct_subtype (field i32) data))
668+
667669
;; CHECK: (type $substruct (struct_subtype (field i32) (field f64) $struct))
668670
(type $substruct (struct_subtype i32 f64 $struct))
669671

670-
;; CHECK: (type $struct (struct_subtype (field i32) data))
671672
(type $struct (struct i32))
672673

673674
;; CHECK: (func $create (type $none_=>_none)
@@ -834,10 +835,11 @@
834835
(module
835836
;; CHECK: (type $none_=>_none (func_subtype func))
836837

838+
;; CHECK: (type $struct (struct_subtype (field i32) data))
839+
837840
;; CHECK: (type $substruct (struct_subtype (field i32) (field f64) $struct))
838841
(type $substruct (struct_subtype i32 f64 $struct))
839842

840-
;; CHECK: (type $struct (struct_subtype (field i32) data))
841843
(type $struct (struct i32))
842844

843845
;; CHECK: (func $create (type $none_=>_none)
@@ -958,17 +960,19 @@
958960
;; Multi-level subtyping, check that we propagate not just to the immediate
959961
;; supertype but all the way as needed.
960962
(module
963+
;; CHECK: (type $struct1 (struct_subtype (field i32) data))
964+
965+
;; CHECK: (type $struct2 (struct_subtype (field i32) (field f64) $struct1))
966+
961967
;; CHECK: (type $struct3 (struct_subtype (field i32) (field f64) (field anyref) $struct2))
962968
(type $struct3 (struct_subtype i32 f64 anyref $struct2))
963969

964-
;; CHECK: (type $none_=>_none (func_subtype func))
965-
966-
;; CHECK: (type $struct2 (struct_subtype (field i32) (field f64) $struct1))
967970
(type $struct2 (struct_subtype i32 f64 $struct1))
968971

969-
;; CHECK: (type $struct1 (struct_subtype (field i32) data))
970972
(type $struct1 (struct i32))
971973

974+
;; CHECK: (type $none_=>_none (func_subtype func))
975+
972976
;; CHECK: (func $create (type $none_=>_none)
973977
;; CHECK-NEXT: (drop
974978
;; CHECK-NEXT: (struct.new_with_rtt $struct3
@@ -1093,13 +1097,15 @@
10931097
;; different values in the sub-most type. Create the top and bottom types, but
10941098
;; not the middle one.
10951099
(module
1100+
;; CHECK: (type $struct1 (struct_subtype (field i32) (field i32) data))
1101+
1102+
;; CHECK: (type $struct2 (struct_subtype (field i32) (field i32) (field f64) (field f64) $struct1))
1103+
10961104
;; CHECK: (type $struct3 (struct_subtype (field i32) (field i32) (field f64) (field f64) (field anyref) (field anyref) $struct2))
10971105
(type $struct3 (struct_subtype i32 i32 f64 f64 anyref anyref $struct2))
10981106

1099-
;; CHECK: (type $struct1 (struct_subtype (field i32) (field i32) data))
11001107
(type $struct1 (struct i32 i32))
11011108

1102-
;; CHECK: (type $struct2 (struct_subtype (field i32) (field i32) (field f64) (field f64) $struct1))
11031109
(type $struct2 (struct_subtype i32 i32 f64 f64 $struct1))
11041110

11051111
;; CHECK: (type $anyref_=>_none (func_subtype (param anyref) func))
@@ -1427,10 +1433,11 @@
14271433
;; As above, but add not just a new of the middle class with a different value
14281434
;; but also a set. That prevents all optimizations.
14291435
(module
1436+
;; CHECK: (type $struct1 (struct_subtype (field (mut i32)) data))
1437+
14301438
;; CHECK: (type $struct2 (struct_subtype (field (mut i32)) (field f64) $struct1))
14311439
(type $struct2 (struct_subtype (mut i32) f64 $struct1))
14321440

1433-
;; CHECK: (type $struct1 (struct_subtype (field (mut i32)) data))
14341441
(type $struct1 (struct (mut i32)))
14351442

14361443
;; CHECK: (type $struct3 (struct_subtype (field (mut i32)) (field f64) (field anyref) $struct2))
@@ -1657,21 +1664,23 @@
16571664
;; sets, and the final subtype C has a create and a get. The set to A should
16581665
;; apply to it, preventing optimization.
16591666
(module
1667+
;; CHECK: (type $A (struct_subtype (field (mut i32)) data))
1668+
1669+
;; CHECK: (type $B (struct_subtype (field (mut i32)) $A))
1670+
16601671
;; CHECK: (type $C (struct_subtype (field (mut i32)) $B))
16611672
(type $C (struct_subtype (mut i32) $B))
16621673

1663-
;; CHECK: (type $A (struct_subtype (field (mut i32)) data))
16641674
(type $A (struct (mut i32)))
16651675

1676+
(type $B (struct_subtype (mut i32) $A))
1677+
16661678
;; CHECK: (type $none_=>_none (func_subtype func))
16671679

16681680
;; CHECK: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func))
16691681

16701682
;; CHECK: (type $ref|$C|_=>_none (func_subtype (param (ref $C)) func))
16711683

1672-
;; CHECK: (type $B (struct_subtype (field (mut i32)) $A))
1673-
(type $B (struct_subtype (mut i32) $A))
1674-
16751684
;; CHECK: (func $create (type $none_=>_none)
16761685
;; CHECK-NEXT: (drop
16771686
;; CHECK-NEXT: (struct.new_with_rtt $C

test/lit/passes/dae-gc-refine-params.wast

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44

55
(module
66
;; CHECK: (type ${i32} (struct (field i32)))
7+
;; NOMNL: (type ${} (struct_subtype data))
8+
79
;; NOMNL: (type ${i32} (struct_subtype (field i32) ${}))
810
(type ${i32} (struct_subtype (field i32) ${}))
911

1012
;; CHECK: (type ${} (struct ))
11-
;; NOMNL: (type ${} (struct_subtype data))
1213
(type ${} (struct))
1314

1415
;; CHECK: (type ${i32_i64} (struct (field i32) (field i64)))

test/lit/passes/dae-gc-refine-return.wast

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
;; CHECK: (type ${i32_i64} (struct (field i32) (field i64)))
1313

1414
;; CHECK: (type ${i32_f32} (struct (field i32) (field f32)))
15+
;; NOMNL: (type ${} (struct_subtype data))
16+
1517
;; NOMNL: (type ${i32} (struct_subtype (field i32) ${}))
1618

1719
;; NOMNL: (type ${i32_i64} (struct_subtype (field i32) (field i64) ${i32}))
@@ -24,7 +26,6 @@
2426
(type ${i32} (struct_subtype (field i32) ${}))
2527

2628
;; CHECK: (type ${} (struct ))
27-
;; NOMNL: (type ${} (struct_subtype data))
2829
(type ${} (struct))
2930

3031
(table 1 1 funcref)

0 commit comments

Comments
 (0)