3030#include " ir/struct-utils.h"
3131#include " ir/subtypes.h"
3232#include " ir/type-updating.h"
33- #include " ir/utils.h"
3433#include " pass.h"
34+ #include " support/insert_ordered.h"
3535#include " support/permutations.h"
36- #include " wasm-builder.h"
3736#include " wasm-type-ordering.h"
3837#include " wasm-type.h"
3938#include " wasm.h"
@@ -138,6 +137,12 @@ struct GlobalTypeOptimization : public Pass {
138137 // The types that no longer need a descriptor.
139138 std::unordered_set<HeapType> haveUnneededDescriptors;
140139
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+
141146 void run (Module* module ) override {
142147 if (!module ->features .hasGC ()) {
143148 return ;
@@ -423,45 +428,43 @@ struct GlobalTypeOptimization : public Pass {
423428 // ^
424429 // B -> B.desc
425430 //
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.
429436 if (!haveUnneededDescriptors.empty ()) {
430437 StructUtils::TypeHierarchyPropagator<StructUtils::CombinableBool>
431438 descPropagator (subTypes);
432439
433440 // Populate the initial data: Any descriptor we did not see was unneeded,
434441 // is needed.
435442 StructUtils::TypeHierarchyPropagator<
436- StructUtils::CombinableBool>::StructMap map ;
443+ StructUtils::CombinableBool>::StructMap remainingDesciptors ;
437444 for (auto type : subTypes.types ) {
438445 if (auto desc = type.getDescriptorType ()) {
439446 if (!haveUnneededDescriptors.count (type)) {
440447 // This descriptor type is needed.
441- map [*desc].value = true ;
448+ remainingDesciptors [*desc].value = true ;
442449 }
443450 }
444451 }
445452
446453 // 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+ }
465468 }
466469 }
467470 }
@@ -484,10 +487,22 @@ struct GlobalTypeOptimization : public Pass {
484487 void updateTypes (Module& wasm) {
485488 class TypeRewriter : public GlobalTypeRewriter {
486489 GlobalTypeOptimization& parent;
490+ InsertOrderedMap<HeapType, Index>::iterator placeholderIt;
487491
488492 public:
489493 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+ }
491506
492507 void modifyStruct (HeapType oldStructType, Struct& struct_) override {
493508 auto & newFields = struct_.fields ;
@@ -549,17 +564,30 @@ struct GlobalTypeOptimization : public Pass {
549564 return ;
550565 }
551566
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 ;
555573 }
556574
557- // Remove an unneeded describes .
575+ // Remove an unneeded describee or describe a placeholder type .
558576 if (auto described = oldType.getDescribedType ()) {
559577 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+ }
561584 }
562585 }
586+
587+ // Remove an unneeded descriptor.
588+ if (parent.haveUnneededDescriptors .count (oldType)) {
589+ typeBuilder.setDescriptor (i, std::nullopt );
590+ }
563591 }
564592 };
565593
0 commit comments