@@ -446,8 +446,13 @@ namespace {
446446
447447// Information that is shared with InfoCollector.
448448struct SharedInfo {
449+ // Subtyping info.
450+ const SubTypes& subTypes;
451+
449452 // The names of tables that are imported or exported.
450453 std::unordered_set<Name> publicTables;
454+
455+ SharedInfo (const SubTypes& subTypes) : subTypes(subTypes) {}
451456};
452457
453458// The data we gather from each function, as we process them in parallel. Later
@@ -826,29 +831,53 @@ struct InfoCollector
826831 return ResultLocation{target, i};
827832 });
828833 }
829- template <typename T> void handleIndirectCall (T* curr, HeapType targetType) {
834+ template <typename T>
835+ void handleIndirectCall (T* curr, HeapType targetType, Exactness exact) {
830836 // If the heap type is not a signature, which is the case for a bottom type
831837 // (null) then nothing can be called.
832838 if (!targetType.isSignature ()) {
833839 assert (targetType.isBottom ());
834840 return ;
835841 }
842+ // Connect us to the given type.
843+ auto sig = targetType.getSignature ();
836844 handleCall (
837845 curr,
838846 [&](Index i) {
839- assert (i <= targetType. getSignature () .params .size ());
847+ assert (i <= sig .params .size ());
840848 return SignatureParamLocation{targetType, i};
841849 },
842850 [&](Index i) {
843- assert (i <= targetType. getSignature () .results .size ());
851+ assert (i <= sig .results .size ());
844852 return SignatureResultLocation{targetType, i};
845853 });
854+ // If the type is exact, we only need to read SignatureParamLocation /
855+ // SignatureResultLocation of this exact type, and we are done.
856+ if (exact == Exact) {
857+ return ;
858+ }
859+ // Inexact type, so subtyping is relevant: add the relevant links.
860+ // TODO: SignatureParamLocation is handled below in an inefficient way, see
861+ // there.
862+ // TODO: For CallRef, we could do something like readFromData() and use the
863+ // flowing function reference's type, not the static type. We could
864+ // even reuse ConeReadLocation if we generalized it to function types.
865+ for (Index i = 0 ; i < sig.results .size (); i++) {
866+ if (isRelevant (sig.results [i])) {
867+ shared.subTypes .iterSubTypes (
868+ targetType, [&](HeapType subType, Index depth) {
869+ info.links .push_back ({SignatureResultLocation{subType, i},
870+ ExpressionLocation{curr, i}});
871+ });
872+ }
873+ }
846874 }
847875 template <typename T> void handleIndirectCall (T* curr, Type targetType) {
848876 // If the type is unreachable, nothing can be called (and there is no heap
849877 // type to get).
850878 if (targetType != Type::unreachable) {
851- handleIndirectCall (curr, targetType.getHeapType ());
879+ handleIndirectCall (
880+ curr, targetType.getHeapType (), targetType.getExactness ());
852881 }
853882 }
854883
@@ -891,7 +920,8 @@ struct InfoCollector
891920 }
892921 void visitCallIndirect (CallIndirect* curr) {
893922 // TODO: optimize the call target like CallRef
894- handleIndirectCall (curr, curr->heapType );
923+ // CallIndirect only knows a heap type, so it is always inexact.
924+ handleIndirectCall (curr, curr->heapType , Inexact);
895925
896926 // If this goes to a public table, then we must root the output, as the
897927 // table could contain anything at all, and calling functions there could
@@ -2241,13 +2271,20 @@ Flower::Flower(Module& wasm, const PassOptions& options)
22412271 tnhOracle = std::make_unique<TNHOracle>(wasm, options);
22422272 }
22432273
2274+ #ifdef POSSIBLE_CONTENTS_DEBUG
2275+ std::cout << " subtypes phase\n " ;
2276+ #endif
2277+
2278+ subTypes = std::make_unique<SubTypes>(wasm);
2279+ maxDepths = subTypes->getMaxDepths ();
2280+
22442281#ifdef POSSIBLE_CONTENTS_DEBUG
22452282 std::cout << " parallel phase\n " ;
22462283#endif
22472284
22482285 // Compute shared info that we need for the main pass over each function, such
22492286 // as the imported/exported tables.
2250- SharedInfo shared;
2287+ SharedInfo shared (*subTypes) ;
22512288
22522289 for (auto & table : wasm.tables ) {
22532290 if (table->imported ()) {
@@ -2443,11 +2480,29 @@ Flower::Flower(Module& wasm, const PassOptions& options)
24432480 }
24442481
24452482#ifdef POSSIBLE_CONTENTS_DEBUG
2446- std::cout << " struct phase\n " ;
2483+ std::cout << " function subtyping phase\n " ;
24472484#endif
24482485
2449- subTypes = std::make_unique<SubTypes>(wasm);
2450- maxDepths = subTypes->getMaxDepths ();
2486+ // Link function subtyping params. When a function of type B has a supertype
2487+ // A, then we may call B using A's type. That means the parameters to
2488+ // (indirect) calls to B must look at supertypes, which is the opposite of the
2489+ // logic for results, readFromData(), etc. For now, we just connect these
2490+ // types directly, which does not fully optimize exact types. TODO: Add a new
2491+ // mechanism to optimize here.
2492+ for (auto type : subTypes->types ) {
2493+ if (!type.isFunction ()) {
2494+ continue ;
2495+ }
2496+ auto super = type.getSuperType ();
2497+ if (!super) {
2498+ continue ;
2499+ }
2500+ auto params = type.getSignature ().params ;
2501+ for (Index i = 0 ; i < params.size (); i++) {
2502+ links.insert (getIndexes (LocationLink{SignatureParamLocation{*super, i},
2503+ SignatureParamLocation{type, i}}));
2504+ }
2505+ }
24512506
24522507#ifdef POSSIBLE_CONTENTS_DEBUG
24532508 std::cout << " Link-targets phase\n " ;
0 commit comments