@@ -502,9 +502,8 @@ struct Unsubtyping : Pass {
502502 }
503503
504504 void noteDescriptor (HeapType described, HeapType descriptor) {
505- DBG (std::cerr << " noting " << ModuleHeapType (*wasm, described)
506- << " described by " << ModuleHeapType (*wasm, descriptor)
507- << ' \n ' );
505+ DBG (std::cerr << " noting " << ModuleHeapType (*wasm, described) << " -> "
506+ << ModuleHeapType (*wasm, descriptor) << ' \n ' );
508507 work.push_back ({Kind::Descriptor, described, descriptor});
509508 }
510509
@@ -536,8 +535,13 @@ struct Unsubtyping : Pass {
536535 : ControlFlowWalker<Collector, SubtypingDiscoverer<Collector>> {
537536 using Super =
538537 ControlFlowWalker<Collector, SubtypingDiscoverer<Collector>>;
538+
539539 Info& info;
540- Collector (Info& info) : info(info) {}
540+ bool trapsNeverHappen;
541+
542+ Collector (Info& info, bool trapsNeverHappen)
543+ : info(info), trapsNeverHappen(trapsNeverHappen) {}
544+
541545 void noteSubtype (Type sub, Type super) {
542546 if (sub.isTuple ()) {
543547 assert (super.isTuple () && sub.size () == super.size ());
@@ -659,13 +663,30 @@ struct Unsubtyping : Pass {
659663 }
660664 noteDescriptor (curr->desc ->type .getHeapType ());
661665 }
666+ void visitStructNew (StructNew* curr) {
667+ if (curr->type == Type::unreachable || !curr->desc ) {
668+ return ;
669+ }
670+ // Normally we do not treat struct.new as requiring a descriptor, even
671+ // if it has one. We are happy to optimize out descriptors that are set
672+ // in allocations and then never used. But if the descriptor is nullable
673+ // and outside a function context and we assume it may be null and cause
674+ // a trap, then we have no way to preserve that trap without keeping the
675+ // descriptor around.
676+ if (!trapsNeverHappen && !getFunction () &&
677+ curr->desc ->type .isNullable ()) {
678+ noteDescribed (curr->type .getHeapType ());
679+ }
680+ }
662681 };
663682
683+ bool trapsNeverHappen = getPassOptions ().trapsNeverHappen ;
684+
664685 // Collect subtyping constraints and casts from functions in parallel.
665686 ModuleUtils::ParallelFunctionAnalysis<Info> analysis (
666687 wasm, [&](Function* func, Info& info) {
667688 if (!func->imported ()) {
668- Collector (info).walkFunctionInModule (func, &wasm);
689+ Collector (info, trapsNeverHappen ).walkFunctionInModule (func, &wasm);
669690 }
670691 });
671692
@@ -679,7 +700,7 @@ struct Unsubtyping : Pass {
679700 }
680701
681702 // Collect constraints from module-level code as well.
682- Collector collector (collectedInfo);
703+ Collector collector (collectedInfo, trapsNeverHappen );
683704 collector.walkModuleCode (&wasm);
684705 collector.setModule (&wasm);
685706 for (auto & global : wasm.globals ) {
@@ -730,33 +751,16 @@ struct Unsubtyping : Pass {
730751
731752 types.setSupertype (sub, super);
732753
733- // If the supertype has a descriptor type, then the subtype must be
734- // described by a corresponding subtype of the supertype's descriptor. (On
735- // the other hand, no further requirements are placed on the superytpe if
736- // the subtype has a descriptor.)
737- if (auto desc = types.getDescriptor (super)) {
738- auto subDesc = sub.getDescriptorType ();
739- assert (subDesc);
740- noteDescriptor (sub, *subDesc);
741- noteSubtype (*subDesc, *desc);
742- }
743- // If the supertype describes a type, then the subtype must describe a
744- // corresponding subtype of the supertype's described type.
745- if (auto desc = types.getDescribed (super)) {
746- auto subDesc = sub.getDescribedType ();
747- assert (subDesc);
748- noteDescriptor (*subDesc, sub);
749- noteSubtype (*subDesc, *desc);
750- }
751- // If the subtype describes a type, then the supertype must describe the
752- // supertype of the subtype's described type.
753- if (!super.isBasic ()) {
754- if (auto desc = types.getDescribed (sub)) {
755- auto superDesc = super.getDescribedType ();
756- assert (superDesc);
757- noteDescriptor (*superDesc, super);
758- noteSubtype (*desc, *superDesc);
759- }
754+ // Complete the descriptor squares to the left and right of the new
755+ // subtyping edge if those squares can possibly exist based on the original
756+ // types.
757+ if (super.getDescribedType ()) {
758+ completeDescriptorSquare (
759+ types.getDescribed (super), super, types.getDescribed (sub), sub);
760+ }
761+ if (super.getDescriptorType ()) {
762+ completeDescriptorSquare (
763+ super, types.getDescriptor (super), sub, types.getDescriptor (sub));
760764 }
761765
762766 // Find the implied subtypings from the type definitions and casts.
@@ -765,6 +769,8 @@ struct Unsubtyping : Pass {
765769 }
766770
767771 void processDescriptor (HeapType described, HeapType descriptor) {
772+ DBG (std::cerr << " processing " << ModuleHeapType (*wasm, described) << " -> "
773+ << ModuleHeapType (*wasm, descriptor) << ' \n ' );
768774 assert (described.getDescriptorType () &&
769775 *described.getDescriptorType () == descriptor);
770776 if (auto oldDesc = types.getDescriptor (described)) {
@@ -775,32 +781,16 @@ struct Unsubtyping : Pass {
775781
776782 types.setDescriptor (described, descriptor);
777783
778- // Every immediate subtype of the described type must also be described by a
779- // corresponding immediate subtype of the descriptor. (On the other hand,
780- // no further requirements are placed on the supertype of the described
781- // type.)
784+ // Complete the descriptor squares above and below the new descriptor edge.
785+ completeDescriptorSquare (
786+ std::nullopt , types.getSupertype (descriptor), described, descriptor);
782787 for (auto sub : types.immediateSubtypes (described)) {
783- auto subDesc = sub.getDescriptorType ();
784- assert (subDesc);
785- noteDescriptor (sub, *subDesc);
786- noteSubtype (*subDesc, descriptor);
787- }
788- // Every immediate subtype of the descriptor type must also describe a
789- // corresponding immediate subtype of the described typed.
790- for (auto sub : types.immediateSubtypes (descriptor)) {
791- auto subDesc = sub.getDescribedType ();
792- assert (subDesc);
793- noteDescriptor (*subDesc, sub);
794- noteSubtype (*subDesc, described);
795- }
796- // The immediate superytpe of the descriptor, if it exists, must describe
797- // the immediate supertype of the described type.
798- if (auto superDescriptor = types.getSupertype (descriptor);
799- superDescriptor && !superDescriptor->isBasic ()) {
800- auto superDescribed = superDescriptor->getDescribedType ();
801- assert (superDescribed);
802- noteDescriptor (*superDescribed, *superDescriptor);
803- noteSubtype (described, *superDescribed);
788+ completeDescriptorSquare (
789+ described, descriptor, sub, types.getDescriptor (sub));
790+ }
791+ for (auto subDesc : types.immediateSubtypes (descriptor)) {
792+ completeDescriptorSquare (
793+ described, descriptor, types.getDescribed (subDesc), subDesc);
804794 }
805795 }
806796
@@ -860,6 +850,45 @@ struct Unsubtyping : Pass {
860850 }
861851 }
862852
853+ void completeDescriptorSquare (std::optional<HeapType> super,
854+ std::optional<HeapType> superDesc,
855+ std::optional<HeapType> sub,
856+ std::optional<HeapType> subDesc) {
857+ if ((super && super->isBasic ()) || (superDesc && superDesc->isBasic ())) {
858+ // Basic types do not have descriptors or described types, so do not form
859+ // descriptor squares.
860+ return ;
861+ }
862+ if (bool (super) + bool (superDesc) + bool (sub) + bool (subDesc) < 3 ) {
863+ // We must have two adjacent edges (involving at least 3 types) for there
864+ // to be any further requirements.
865+ return ;
866+ }
867+ // There may be up to one missing type. Look it up using its original
868+ // descriptor relation with the present types and add the missing edges.
869+ if (!super) {
870+ super = superDesc->getDescribedType ();
871+ } else if (!sub) {
872+ sub = subDesc->getDescribedType ();
873+ } else if (!subDesc) {
874+ subDesc = sub->getDescriptorType ();
875+ } else if (!superDesc) {
876+ // This is the only type that is allowed to be missing.
877+ return ;
878+ }
879+ // Add all the edges. Don't worry about duplicating existing edges because
880+ // checking whether they're necessary now would be about as expensive as
881+ // discarding them later.
882+ // TODO: We will be able to assume this once we update the descriptor
883+ // validation rules.
884+ if (HeapType::isSubType (*sub, *super)) {
885+ noteSubtype (*sub, *super);
886+ }
887+ noteSubtype (*subDesc, *superDesc);
888+ noteDescriptor (*super, *superDesc);
889+ noteDescriptor (*sub, *subDesc);
890+ }
891+
863892 void rewriteTypes (Module& wasm) {
864893 struct Rewriter : GlobalTypeRewriter {
865894 Unsubtyping& parent;
@@ -899,6 +928,7 @@ struct Unsubtyping : Pass {
899928 Rewriter (const TypeTree& types) : types(types) {}
900929
901930 bool isFunctionParallel () override { return true ; }
931+ // Only introduces locals that are set immediately before they are used.
902932 bool requiresNonNullableLocalFixups () override { return false ; }
903933 std::unique_ptr<Pass> create () override {
904934 return std::make_unique<Rewriter>(types);
@@ -918,6 +948,12 @@ struct Unsubtyping : Pass {
918948 // ChildLocalizer. Outside a function context just drop the operand
919949 // because there can be no side effects anyway.
920950 if (auto * func = getFunction ()) {
951+ // Preserve a trap from a null descriptor if necessary.
952+ if (!getPassOptions ().trapsNeverHappen &&
953+ curr->desc ->type .isNullable ()) {
954+ curr->desc =
955+ Builder (*getModule ()).makeRefAs (RefAsNonNull, curr->desc );
956+ }
921957 auto * block =
922958 ChildLocalizer (curr, func, *getModule (), getPassOptions ())
923959 .getChildrenReplacement ();
0 commit comments