30
30
#include " ir/struct-utils.h"
31
31
#include " ir/subtypes.h"
32
32
#include " ir/type-updating.h"
33
- #include " ir/utils.h"
34
33
#include " pass.h"
34
+ #include " support/insert_ordered.h"
35
35
#include " support/permutations.h"
36
- #include " wasm-builder.h"
37
36
#include " wasm-type-ordering.h"
38
37
#include " wasm-type.h"
39
38
#include " wasm.h"
@@ -138,6 +137,12 @@ struct GlobalTypeOptimization : public Pass {
138
137
// The types that no longer need a descriptor.
139
138
std::unordered_set<HeapType> haveUnneededDescriptors;
140
139
140
+ // Descriptor types that are not needed by their described types but that
141
+ // still need to be descriptors for their own subtypes and supertypes to be
142
+ // valid. We will keep them descriptors by having them describe trivial new
143
+ // placeholder types.
144
+ InsertOrderedMap<HeapType, Index> descriptorsOfPlaceholders;
145
+
141
146
void run (Module* module ) override {
142
147
if (!module ->features .hasGC ()) {
143
148
return ;
@@ -423,45 +428,43 @@ struct GlobalTypeOptimization : public Pass {
423
428
// ^
424
429
// B -> B.desc
425
430
//
426
- // Here the descriptors subtype, but *not* the describees. We cannot
427
- // remove A's descriptor without also removing $B's, so we need to propagate
428
- // that "must remain a descriptor" property among descriptors.
431
+ // Say we want to optimize A to no longer have a descriptor. Then A.desc
432
+ // will no longer describe A. But A.desc still needs to be a descriptor for
433
+ // it to remain a valid supertype of B.desc. To allow the optimization of A
434
+ // to proceed, we will introduce a placeholder type for A.desc to describe,
435
+ // keeping it a descriptor type.
429
436
if (!haveUnneededDescriptors.empty ()) {
430
437
StructUtils::TypeHierarchyPropagator<StructUtils::CombinableBool>
431
438
descPropagator (subTypes);
432
439
433
440
// Populate the initial data: Any descriptor we did not see was unneeded,
434
441
// is needed.
435
442
StructUtils::TypeHierarchyPropagator<
436
- StructUtils::CombinableBool>::StructMap map ;
443
+ StructUtils::CombinableBool>::StructMap remainingDesciptors ;
437
444
for (auto type : subTypes.types ) {
438
445
if (auto desc = type.getDescriptorType ()) {
439
446
if (!haveUnneededDescriptors.count (type)) {
440
447
// This descriptor type is needed.
441
- map [*desc].value = true ;
448
+ remainingDesciptors [*desc].value = true ;
442
449
}
443
450
}
444
451
}
445
452
446
453
// Propagate.
447
- descPropagator.propagateToSuperAndSubTypes (map);
448
-
449
- // Remove optimization opportunities that the propagation ruled out.
450
- // TODO: We could do better here,
451
- //
452
- // A -> A.desc A A.desc <- A2
453
- // ^ => ^
454
- // B -> B.desc B -> B.desc
455
- //
456
- // Starting from the left, we can remove A's descriptor *but keep A.desc
457
- // as being a descriptor*, by making it describe a new type A2. That would
458
- // keep subtyping working for the descriptors, and later passes could
459
- // remove the unused A2.
460
- for (auto & [type, info] : map) {
461
- if (info.value ) {
462
- auto described = type.getDescribedType ();
463
- assert (described);
464
- haveUnneededDescriptors.erase (*described);
454
+ descPropagator.propagateToSuperAndSubTypes (remainingDesciptors);
455
+
456
+ // Determine the set of descriptor types that will need placeholder
457
+ // describees. Do not iterate directly on remainingDescriptors because it
458
+ // is not deterministically ordered.
459
+ for (auto type : subTypes.types ) {
460
+ if (auto it = remainingDesciptors.find (type);
461
+ it != remainingDesciptors.end () && it->second .value ) {
462
+ auto desc = type.getDescribedType ();
463
+ assert (desc);
464
+ if (haveUnneededDescriptors.count (*desc)) {
465
+ descriptorsOfPlaceholders.insert (
466
+ {type, descriptorsOfPlaceholders.size ()});
467
+ }
465
468
}
466
469
}
467
470
}
@@ -484,10 +487,22 @@ struct GlobalTypeOptimization : public Pass {
484
487
void updateTypes (Module& wasm) {
485
488
class TypeRewriter : public GlobalTypeRewriter {
486
489
GlobalTypeOptimization& parent;
490
+ InsertOrderedMap<HeapType, Index>::iterator placeholderIt;
487
491
488
492
public:
489
493
TypeRewriter (Module& wasm, GlobalTypeOptimization& parent)
490
- : GlobalTypeRewriter(wasm), parent(parent) {}
494
+ : GlobalTypeRewriter(wasm), parent(parent),
495
+ placeholderIt (parent.descriptorsOfPlaceholders.begin()) {}
496
+
497
+ std::vector<HeapType> getSortedTypes (PredecessorGraph preds) override {
498
+ auto types = GlobalTypeRewriter::getSortedTypes (std::move (preds));
499
+ // Prefix the types with placeholders to be overwritten with the
500
+ // placeholder describees.
501
+ HeapType placeholder = Struct{};
502
+ types.insert (
503
+ types.begin (), parent.descriptorsOfPlaceholders .size (), placeholder);
504
+ return types;
505
+ }
491
506
492
507
void modifyStruct (HeapType oldStructType, Struct& struct_) override {
493
508
auto & newFields = struct_.fields ;
@@ -549,17 +564,30 @@ struct GlobalTypeOptimization : public Pass {
549
564
return ;
550
565
}
551
566
552
- // Remove an unneeded descriptor.
553
- if (parent.haveUnneededDescriptors .count (oldType)) {
554
- typeBuilder.setDescriptor (i, std::nullopt );
567
+ // Until we've created all the placeholders, create a placeholder
568
+ // describee type for the next descriptor that needs one.
569
+ if (placeholderIt != parent.descriptorsOfPlaceholders .end ()) {
570
+ typeBuilder[i].descriptor (getTempHeapType (placeholderIt->first ));
571
+ ++placeholderIt;
572
+ return ;
555
573
}
556
574
557
- // Remove an unneeded describes .
575
+ // Remove an unneeded describee or describe a placeholder type .
558
576
if (auto described = oldType.getDescribedType ()) {
559
577
if (parent.haveUnneededDescriptors .count (*described)) {
560
- typeBuilder.setDescribed (i, std::nullopt );
578
+ if (auto it = parent.descriptorsOfPlaceholders .find (oldType);
579
+ it != parent.descriptorsOfPlaceholders .end ()) {
580
+ typeBuilder[i].describes (typeBuilder[it->second ]);
581
+ } else {
582
+ typeBuilder[i].describes (std::nullopt );
583
+ }
561
584
}
562
585
}
586
+
587
+ // Remove an unneeded descriptor.
588
+ if (parent.haveUnneededDescriptors .count (oldType)) {
589
+ typeBuilder.setDescriptor (i, std::nullopt );
590
+ }
563
591
}
564
592
};
565
593
0 commit comments