24
24
// * No references at all. We can simply remove it.
25
25
// * References, but no uses. We can't remove it, but we can change it (see
26
26
// below).
27
- // * Uses (which imply references). We must keep it as it is.
27
+ // * Uses (which imply references). We must keep it as it is, because it is
28
+ // fully used (e.g. for a function, it is called and may execute).
28
29
//
29
30
// An example of something with a reference but *not* a use is a RefFunc to a
30
31
// function that has no corresponding CallRef to that type. We cannot just
@@ -62,25 +63,38 @@ using ModuleElementKind = ModuleItemKind;
62
63
// name of the particular element.
63
64
using ModuleElement = std::pair<ModuleElementKind, Name>;
64
65
66
+ // Information from an indirect call: the name of the table, and the heap type.
67
+ using IndirectCall = std::pair<Name, HeapType>;
68
+
65
69
// Visit or walk an expression to find what things are referenced.
66
70
struct ReferenceFinder
67
71
: public PostWalker<ReferenceFinder,
68
72
UnifiedExpressionVisitor<ReferenceFinder>> {
69
73
// Our findings are placed in these data structures, which the user of this
70
- // code can then process.
71
- std::vector<ModuleElement> elements;
74
+ // code can then process. We mark both uses and references, and also note
75
+ // uses of specific things that require special handling, like refFuncs.
76
+ std::vector<ModuleElement> used, referenced;
72
77
std::vector<HeapType> callRefTypes;
73
78
std::vector<Name> refFuncs;
74
79
std::vector<StructField> structFields;
80
+ std::vector<IndirectCall> indirectCalls;
75
81
76
82
// Add an item to the output data structures.
77
- void note (ModuleElement element) { elements.push_back (element); }
78
- void noteCallRef (HeapType type) { callRefTypes.push_back (type); }
79
- void noteRefFunc (Name refFunc) { refFuncs.push_back (refFunc); }
80
- void note (StructField structField) { structFields.push_back (structField); }
81
-
82
- // Generic visitor
83
+ void use (ModuleElement element) { used.push_back (element); }
84
+ void reference (ModuleElement element) { referenced.push_back (element); }
85
+ void useCallRef (HeapType type) { callRefTypes.push_back (type); }
86
+ void useRefFunc (Name refFunc) { refFuncs.push_back (refFunc); }
87
+ void useStructField (StructField structField) {
88
+ structFields.push_back (structField);
89
+ }
90
+ void useIndirectCall (Name table, HeapType type) {
91
+ indirectCalls.push_back ({table, type});
92
+ }
83
93
94
+ // Generic visitor: Use all the things referenced. This handles e.g. using the
95
+ // table of a table.get. When we do not want such unconditional use, we
96
+ // override (e.g. for call_indirect, we don't want to mark the entire table as
97
+ // used, see below).
84
98
void visitExpression (Expression* curr) {
85
99
#define DELEGATE_ID curr->_id
86
100
@@ -101,7 +115,7 @@ struct ReferenceFinder
101
115
102
116
#define DELEGATE_FIELD_NAME_KIND (id, field, kind ) \
103
117
if (cast->field .is ()) { \
104
- note ({kind, cast->field }); \
118
+ use ({kind, cast->field }); \
105
119
}
106
120
107
121
#include " wasm-delegations-fields.def"
@@ -110,7 +124,7 @@ struct ReferenceFinder
110
124
// Specific visitors
111
125
112
126
void visitCall (Call* curr) {
113
- note ({ModuleElementKind::Function, curr->target });
127
+ use ({ModuleElementKind::Function, curr->target });
114
128
115
129
if (Intrinsics (*getModule ()).isCallWithoutEffects (curr)) {
116
130
// A call-without-effects receives a function reference and calls it, the
@@ -137,12 +151,15 @@ struct ReferenceFinder
137
151
}
138
152
139
153
void visitCallIndirect (CallIndirect* curr) {
140
- note ({ModuleElementKind::Table, curr->table });
154
+ // We refer to the table, but may not use all parts of it, that depends on
155
+ // the heap type we call with.
156
+ reference ({ModuleElementKind::Table, curr->table });
157
+ useIndirectCall (curr->table , curr->heapType );
141
158
// Note a possible call of a function reference as well, as something might
142
159
// be written into the table during runtime. With precise tracking of what
143
160
// is written into the table we could do better here; we could also see
144
161
// which tables are immutable. TODO
145
- noteCallRef (curr->heapType );
162
+ useCallRef (curr->heapType );
146
163
}
147
164
148
165
void visitCallRef (CallRef* curr) {
@@ -151,17 +168,17 @@ struct ReferenceFinder
151
168
return ;
152
169
}
153
170
154
- noteCallRef (curr->target ->type .getHeapType ());
171
+ useCallRef (curr->target ->type .getHeapType ());
155
172
}
156
173
157
- void visitRefFunc (RefFunc* curr) { noteRefFunc (curr->func ); }
174
+ void visitRefFunc (RefFunc* curr) { useRefFunc (curr->func ); }
158
175
159
176
void visitStructGet (StructGet* curr) {
160
177
if (curr->ref ->type == Type::unreachable || curr->ref ->type .isNull ()) {
161
178
return ;
162
179
}
163
180
auto type = curr->ref ->type .getHeapType ();
164
- note (StructField{type, curr->index });
181
+ useStructField (StructField{type, curr->index });
165
182
}
166
183
};
167
184
@@ -262,9 +279,12 @@ struct Analyzer {
262
279
ReferenceFinder finder;
263
280
finder.setModule (module );
264
281
finder.visit (curr);
265
- for (auto element : finder.elements ) {
282
+ for (auto element : finder.used ) {
266
283
use (element);
267
284
}
285
+ for (auto element : finder.referenced ) {
286
+ reference (element);
287
+ }
268
288
for (auto type : finder.callRefTypes ) {
269
289
useCallRefType (type);
270
290
}
@@ -274,6 +294,9 @@ struct Analyzer {
274
294
for (auto structField : finder.structFields ) {
275
295
useStructField (structField);
276
296
}
297
+ for (auto call : finder.indirectCalls ) {
298
+ useIndirectCall (call);
299
+ }
277
300
278
301
// Scan the children to continue our work.
279
302
scanChildren (curr);
@@ -316,6 +339,37 @@ struct Analyzer {
316
339
}
317
340
}
318
341
342
+ std::unordered_set<IndirectCall> usedIndirectCalls;
343
+
344
+ void useIndirectCall (IndirectCall call) {
345
+ auto [_, inserted] = usedIndirectCalls.insert (call);
346
+ if (!inserted) {
347
+ return ;
348
+ }
349
+
350
+ // TODO: use structured bindings with c++20, needed for the capture below
351
+ auto table = call.first ;
352
+ auto type = call.second ;
353
+
354
+ // Any function in the table of that signature may be called.
355
+ ModuleUtils::iterTableSegments (
356
+ *module , table, [&](ElementSegment* segment) {
357
+ auto segmentReferenced = false ;
358
+ for (auto * item : segment->data ) {
359
+ if (auto * refFunc = item->dynCast <RefFunc>()) {
360
+ auto * func = module ->getFunction (refFunc->func );
361
+ if (HeapType::isSubType (func->type , type)) {
362
+ use ({ModuleElementKind::Function, refFunc->func });
363
+ segmentReferenced = true ;
364
+ }
365
+ }
366
+ }
367
+ if (segmentReferenced) {
368
+ reference ({ModuleElementKind::ElementSegment, segment->name });
369
+ }
370
+ });
371
+ }
372
+
319
373
void useRefFunc (Name func) {
320
374
if (!options.closedWorld ) {
321
375
// The world is open, so assume the worst and something (inside or outside
@@ -341,7 +395,7 @@ struct Analyzer {
341
395
// We've never seen a CallRef for this, but might see one later.
342
396
uncalledRefFuncMap[type].insert (func);
343
397
344
- referenced. insert (element);
398
+ reference (element);
345
399
}
346
400
}
347
401
@@ -554,34 +608,11 @@ struct Analyzer {
554
608
finder.setModule (module );
555
609
finder.walk (curr);
556
610
557
- for (auto element : finder.elements ) {
558
- // Avoid repeated work. Note that globals with multiple references to
559
- // previous globals can lead to exponential work, so this is important.
560
- // (If C refers twice to B, and B refers twice to A, then when we process
561
- // C we would, naively, scan B twice and A four times.)
562
- auto [_, inserted] = referenced.insert (element);
563
- if (!inserted) {
564
- continue ;
565
- }
566
-
567
- auto & [kind, value] = element;
568
- if (kind == ModuleElementKind::Global) {
569
- // Like functions, (non-imported) globals have contents. For functions,
570
- // things are simple: if a function ends up with references but no uses
571
- // then we can simply empty out the function (by setting its body to an
572
- // unreachable). We don't have a simple way to do the same for globals,
573
- // unfortunately. For now, scan the global's contents and add references
574
- // as needed.
575
- // TODO: We could try to empty the global out, for example, replace it
576
- // with a null if it is nullable, or replace all gets of it with
577
- // something else, but that is not trivial.
578
- auto * global = module ->getGlobal (value);
579
- if (!global->imported ()) {
580
- // Note that infinite recursion is not a danger here since a global
581
- // can only refer to previous globals.
582
- addReferences (global->init );
583
- }
584
- }
611
+ for (auto element : finder.used ) {
612
+ reference (element);
613
+ }
614
+ for (auto element : finder.referenced ) {
615
+ reference (element);
585
616
}
586
617
587
618
for (auto func : finder.refFuncs ) {
@@ -594,7 +625,7 @@ struct Analyzer {
594
625
// just adding a reference to the function, and not actually using the
595
626
// RefFunc. (Only useRefFunc() + a CallRef of the proper type are enough
596
627
// to make a function itself used.)
597
- referenced. insert ({ModuleElementKind::Function, func});
628
+ reference ({ModuleElementKind::Function, func});
598
629
}
599
630
600
631
// Note: nothing to do with |callRefTypes| and |structFields|, which only
@@ -603,6 +634,44 @@ struct Analyzer {
603
634
// handled in an entirely different way in Binaryen IR, and we don't need to
604
635
// worry about it.)
605
636
}
637
+
638
+ void reference (ModuleElement element) {
639
+ // Avoid repeated work. Note that globals with multiple references to
640
+ // previous globals can lead to exponential work, so this is important.
641
+ // (If C refers twice to B, and B refers twice to A, then when we process
642
+ // C we would, naively, scan B twice and A four times.)
643
+ auto [_, inserted] = referenced.insert (element);
644
+ if (!inserted) {
645
+ return ;
646
+ }
647
+
648
+ // Some references force references to their internals, just by being
649
+ // referenced and present in the output.
650
+ auto & [kind, value] = element;
651
+ if (kind == ModuleElementKind::Global) {
652
+ // Like functions, (non-imported) globals have contents. For functions,
653
+ // things are simple: if a function ends up with references but no uses
654
+ // then we can simply empty out the function (by setting its body to an
655
+ // unreachable). We don't have a simple way to do the same for globals,
656
+ // unfortunately. For now, scan the global's contents and add references
657
+ // as needed.
658
+ // TODO: We could try to empty the global out, for example, replace it
659
+ // with a null if it is nullable, or replace all gets of it with
660
+ // something else, but that is not trivial.
661
+ auto * global = module ->getGlobal (value);
662
+ if (!global->imported ()) {
663
+ // Note that infinite recursion is not a danger here since a global
664
+ // can only refer to previous globals.
665
+ addReferences (global->init );
666
+ }
667
+ } else if (kind == ModuleElementKind::ElementSegment) {
668
+ // TODO: We could empty out parts of the segment we don't need.
669
+ auto * segment = module ->getElementSegment (value);
670
+ for (auto * item : segment->data ) {
671
+ addReferences (item);
672
+ }
673
+ }
674
+ }
606
675
};
607
676
608
677
struct RemoveUnusedModuleElements : public Pass {
@@ -709,13 +778,6 @@ struct RemoveUnusedModuleElements : public Pass {
709
778
}
710
779
});
711
780
712
- // For now, all functions that can be called indirectly are marked as roots.
713
- // TODO: Compute this based on which ElementSegments are actually used,
714
- // and which functions have a call_indirect of the proper type.
715
- ElementUtils::iterAllElementFunctionNames (module , [&](Name name) {
716
- roots.emplace_back (ModuleElementKind::Function, name);
717
- });
718
-
719
781
// Just as out-of-bound segments may cause observable traps at instantiation
720
782
// time, so can struct.new instructions with null descriptors cause traps in
721
783
// global or element segment initializers.
0 commit comments