50
50
#include " passes/opt-utils.h"
51
51
#include " support/sorted_vector.h"
52
52
#include " wasm-builder.h"
53
- #include " wasm-type.h"
54
53
#include " wasm.h"
55
54
56
55
namespace wasm {
@@ -77,9 +76,12 @@ struct DAEFunctionInfo {
77
76
// removed as well.
78
77
bool hasTailCalls = false ;
79
78
std::unordered_set<Name> tailCallees;
80
- // The set of functions that have their reference taken (which means there may
81
- // be non-direct calls, limiting what we can do).
82
- std::unordered_set<Name> hasRef;
79
+ // The set of functions that have calls from places that limit what we can do.
80
+ // For now, any call we don't see inhibits our optimizations, but TODO: an
81
+ // export could be worked around by exporting a thunk that adds the parameter.
82
+ //
83
+ // This is built up in parallel in each function, and combined at the end.
84
+ std::unordered_set<Name> hasUnseenCalls;
83
85
84
86
// Clears all data, which marks us as stale and in need of recomputation.
85
87
void clear () { *this = DAEFunctionInfo (); }
@@ -141,9 +143,12 @@ struct DAEScanner
141
143
// the infoMap).
142
144
auto * currInfo = info ? info : &(*infoMap)[Name ()];
143
145
146
+ // Treat a ref.func as an unseen call, preventing us from changing the
147
+ // function's type. If we did change it, it could be an observable
148
+ // difference from the outside, if the reference escapes, for example.
144
149
// TODO: look for actual escaping?
145
150
// TODO: create a thunk for external uses that allow internal optimizations
146
- currInfo->hasRef .insert (curr->func );
151
+ currInfo->hasUnseenCalls .insert (curr->func );
147
152
}
148
153
149
154
// main entry point
@@ -243,7 +248,7 @@ struct DAE : public Pass {
243
248
244
249
std::vector<std::vector<Call*>> allCalls (numFunctions);
245
250
std::vector<bool > tailCallees (numFunctions);
246
- std::vector<bool > hasRef (numFunctions);
251
+ std::vector<bool > hasUnseenCalls (numFunctions);
247
252
248
253
// Track the function in which relevant expressions exist. When we modify
249
254
// those expressions we will need to mark the function's info as stale.
@@ -262,17 +267,14 @@ struct DAE : public Pass {
262
267
for (auto & [call, dropp] : info.droppedCalls ) {
263
268
allDroppedCalls[call] = dropp;
264
269
}
265
- for (auto & name : info.hasRef ) {
266
- hasRef [indexes[name]] = true ;
270
+ for (auto & name : info.hasUnseenCalls ) {
271
+ hasUnseenCalls [indexes[name]] = true ;
267
272
}
268
273
}
269
-
270
- // Exports limit some optimizations, for example, we cannot remove or refine
271
- // parameters. TODO: we could export a thunk that drops the parameter etc.
272
- std::vector<bool > isExported (numFunctions);
274
+ // Exports are considered unseen calls.
273
275
for (auto & curr : module ->exports ) {
274
276
if (curr->kind == ExternalKind::Function) {
275
- isExported [indexes[*curr->getInternalName ()]] = true ;
277
+ hasUnseenCalls [indexes[*curr->getInternalName ()]] = true ;
276
278
}
277
279
}
278
280
@@ -316,48 +318,38 @@ struct DAE : public Pass {
316
318
if (func->imported ()) {
317
319
continue ;
318
320
}
319
- // References prevent optimization .
320
- if (hasRef [index]) {
321
+ // We can only optimize if we see all the calls and can modify them .
322
+ if (hasUnseenCalls [index]) {
321
323
continue ;
322
324
}
323
325
auto & calls = allCalls[index];
324
- if (calls.empty () && !isExported[index] ) {
326
+ if (calls.empty ()) {
325
327
// Nothing calls this, so it is not worth optimizing.
326
328
continue ;
327
329
}
330
+ // Refine argument types before doing anything else. This does not
331
+ // affect whether an argument is used or not, it just refines the type
332
+ // where possible.
328
333
auto name = func->name ;
329
- // Exports prevent refining of argument types, as we don't see all the
330
- // calls.
331
- if (!isExported[index]) {
332
- // Refine argument types before doing anything else. This does not
333
- // affect whether an argument is used or not, it just refines the type
334
- // where possible.
335
- if (refineArgumentTypes (func, calls, module , infoMap[name])) {
336
- worthOptimizing.insert (func);
337
- markStale (func->name );
338
- }
334
+ if (refineArgumentTypes (func, calls, module , infoMap[name])) {
335
+ worthOptimizing.insert (func);
336
+ markStale (func->name );
339
337
}
340
- // Refine return types as well. Note that exports do *not* prevent this!
341
- // It is valid to export a function that returns something even more
342
- // refined.
343
- if (refineReturnTypes (func, calls, module , isExported[index])) {
338
+ // Refine return types as well.
339
+ if (refineReturnTypes (func, calls, module )) {
344
340
refinedReturnTypes = true ;
345
341
markStale (name);
346
342
markCallersStale (calls);
347
343
}
348
- // Exports prevent applying of constant values, as we don't see all the
349
- // calls.
350
- if (!isExported[index]) {
351
- auto optimizedIndexes =
352
- ParamUtils::applyConstantValues ({func}, calls, {}, module );
353
- for (auto i : optimizedIndexes) {
354
- // Mark it as unused, which we know it now is (no point to re-scan
355
- // just for that).
356
- infoMap[name].unusedParams .insert (i);
357
- }
358
- if (!optimizedIndexes.empty ()) {
359
- markStale (func->name );
360
- }
344
+ auto optimizedIndexes =
345
+ ParamUtils::applyConstantValues ({func}, calls, {}, module );
346
+ for (auto i : optimizedIndexes) {
347
+ // Mark it as unused, which we know it now is (no point to re-scan just
348
+ // for that).
349
+ infoMap[name].unusedParams .insert (i);
350
+ }
351
+ if (!optimizedIndexes.empty ()) {
352
+ markStale (func->name );
361
353
}
362
354
}
363
355
if (refinedReturnTypes) {
@@ -372,7 +364,7 @@ struct DAE : public Pass {
372
364
if (func->imported ()) {
373
365
continue ;
374
366
}
375
- if (hasRef[index] || isExported [index]) {
367
+ if (hasUnseenCalls [index]) {
376
368
continue ;
377
369
}
378
370
auto numParams = func->getNumParams ();
@@ -409,7 +401,7 @@ struct DAE : public Pass {
409
401
if (func->getResults () == Type::none) {
410
402
continue ;
411
403
}
412
- if (hasRef[index] || isExported [index]) {
404
+ if (hasUnseenCalls [index]) {
413
405
continue ;
414
406
}
415
407
auto name = func->name ;
@@ -578,59 +570,22 @@ struct DAE : public Pass {
578
570
// the middle, etc.
579
571
bool refineReturnTypes (Function* func,
580
572
const std::vector<Call*>& calls,
581
- Module* module ,
582
- bool isExported) {
583
- if (isExported && !func->type .isOpen ()) {
584
- // We must subtype the current type, so that imports of it work, but it is
585
- // closed.
586
- return false ;
587
- }
588
-
573
+ Module* module ) {
589
574
auto lub = LUB::getResultsLUB (func, *module );
590
575
if (!lub.noted ()) {
591
576
return false ;
592
577
}
593
578
auto newType = lub.getLUB ();
594
- if (newType == func->getResults ()) {
595
- return false ;
596
- }
597
-
598
- // If this is exported, we cannot refine to an exact type without the
599
- // custom descriptors feature being enabled.
600
- if (isExported && !module ->features .hasCustomDescriptors ()) {
601
- // Remove exactness.
602
- std::vector<Type> inexact;
603
- for (auto t : newType) {
604
- inexact.push_back (t.isRef () ? t.with (Inexact) : t);
605
- }
606
- newType = Type (inexact);
607
- if (newType == func->getResults ()) {
608
- return false ;
609
- }
610
- }
611
-
612
- if (!isExported) {
579
+ if (newType != func->getResults ()) {
613
580
func->setResults (newType);
614
- } else {
615
- // We must explicitly subtype the old type.
616
- TypeBuilder builder (1 );
617
- builder[0 ] = Signature (func->getParams (), newType);
618
- builder[0 ].subTypeOf (func->type );
619
- // Make this subtype open like the super. This is not necessary, but might
620
- // allow more work later after other changes, in theory.
621
- builder[0 ].setOpen ();
622
- auto result = builder.build ();
623
- assert (!result.getError ());
624
- func->type = (*result)[0 ];
625
- }
626
-
627
- for (auto * call : calls) {
628
- if (call->type != Type::unreachable) {
629
- call->type = newType;
581
+ for (auto * call : calls) {
582
+ if (call->type != Type::unreachable) {
583
+ call->type = newType;
584
+ }
630
585
}
586
+ return true ;
631
587
}
632
-
633
- return true ;
588
+ return false ;
634
589
}
635
590
};
636
591
0 commit comments