@@ -168,9 +168,11 @@ struct GlobalTypeOptimization : public Pass {
168168 // Combine the data from the functions.
169169 functionSetGetInfos.combineInto (combinedSetGetInfos);
170170
171- // Analyze functions called by JS to find fields holding configured
172- // prototypes that cannot be removed.
173- analyzeJSCalledFunctions (*module );
171+ SubTypes subTypes (*module );
172+
173+ // Analyze the JS interface to find fields holding configured prototypes
174+ // that cannot be removed.
175+ analyzeJSInterface (*module , subTypes);
174176
175177 // Propagate information to super and subtypes on set/get infos:
176178 //
@@ -198,7 +200,6 @@ struct GlobalTypeOptimization : public Pass {
198200 // subtypes (as wasm only allows the type to differ if the fields are
199201 // immutable). Note that by making more things immutable we therefore
200202 // make it possible to apply more specific subtypes in subtype fields.
201- SubTypes subTypes (*module );
202203 StructUtils::TypeHierarchyPropagator<FieldInfo> propagator (subTypes);
203204 auto dataFromSubsAndSupersMap = combinedSetGetInfos;
204205 propagator.propagateToSuperAndSubTypes (dataFromSubsAndSupersMap);
@@ -278,7 +279,6 @@ struct GlobalTypeOptimization : public Pass {
278279 // remove the field. That is so even if there are writes (it would be a
279280 // pointless "write-only field").
280281 auto hasNoReadsAnywhere = !dataFromSubsAndSupers[i].hasRead ;
281-
282282 // Check for reads or writes in ourselves and our supers. If there are
283283 // none, then operations only happen in our strict subtypes, and those
284284 // subtypes can define the field there, and we don't need it here.
@@ -416,22 +416,107 @@ struct GlobalTypeOptimization : public Pass {
416416 }
417417 }
418418
419- void analyzeJSCalledFunctions (Module& wasm) {
419+ void analyzeJSInterface (Module& wasm, const SubTypes& subTypes ) {
420420 if (!wasm.features .hasCustomDescriptors ()) {
421421 return ;
422422 }
423- for (auto func : Intrinsics (wasm).getJSCalledFunctions ()) {
424- // Look at the result types being returned to JS and make sure we preserve
425- // any configured prototypes they might expose.
426- for (auto type : wasm.getFunction (func)->getResults ()) {
427- if (!type.isRef ()) {
428- continue ;
423+
424+ std::unordered_set<HeapType> subtypesExposed;
425+
426+ // Mark the relevant prototype field as read and return true iff we newly
427+ // know we have to propate the exposure to subtypes.
428+ auto noteExposed = [&](HeapType type, Exactness exact = Inexact) -> bool {
429+ if (auto desc = type.getDescriptorType ();
430+ desc && StructUtils::hasPossibleJSPrototypeField (*desc)) {
431+ // This field holds a JS-visible prototype. Do not remove it.
432+ combinedSetGetInfos[std::make_pair (*desc, exact)][0 ].noteRead ();
433+ }
434+ if (exact == Inexact) {
435+ return subtypesExposed.insert (type).second ;
436+ }
437+ return false ;
438+ };
439+
440+ // Values flowing out to JS might have their prototype field on their
441+ // descriptor read by JS.
442+ auto flowOut = [&](Type type) {
443+ if (type.isRef ()) {
444+ noteExposed (type.getHeapType (), type.getExactness ());
445+ }
446+ };
447+
448+ // @binaryen.js.called functions are called from JS. Their results flow back
449+ // out to JS.
450+ for (auto f : Intrinsics (wasm).getJSCalledFunctions ()) {
451+ auto * func = wasm.getFunction (f);
452+ for (auto type : func->getResults ()) {
453+ flowOut (type);
454+ }
455+ }
456+
457+ for (auto & ex : wasm.exports ) {
458+ switch (ex->kind ) {
459+ case ExternalKindImpl::Function: {
460+ auto * func = wasm.getFunction (*ex->getInternalName ());
461+ for (auto type : func->getResults ()) {
462+ flowOut (type);
463+ }
464+ break ;
465+ }
466+ case ExternalKindImpl::Table: {
467+ auto * table = wasm.getTable (*ex->getInternalName ());
468+ flowOut (table->type );
469+ break ;
470+ }
471+ case ExternalKindImpl::Global: {
472+ auto * global = wasm.getGlobal (*ex->getInternalName ());
473+ flowOut (global->type );
474+ break ;
429475 }
430- if (auto desc = type.getHeapType ().getDescriptorType ();
431- desc && StructUtils::hasPossibleJSPrototypeField (*desc)) {
432- // This field holds a JS-visible prototype. Do not remove it.
433- auto exact = type.getExactness ();
434- combinedSetGetInfos[std::make_pair (*desc, exact)][0 ].noteRead ();
476+ case ExternalKindImpl::Memory:
477+ case ExternalKindImpl::Tag:
478+ case ExternalKindImpl::Invalid:
479+ break ;
480+ }
481+ }
482+
483+ for (auto & func : wasm.functions ) {
484+ if (func->imported ()) {
485+ for (auto type : func->getParams ()) {
486+ flowOut (type);
487+ }
488+ }
489+ }
490+ for (auto & table : wasm.tables ) {
491+ if (table->imported ()) {
492+ flowOut (table->type );
493+ }
494+ }
495+ for (auto & global : wasm.globals ) {
496+ if (global->imported () && global->mutable_ ) {
497+ flowOut (global->type );
498+ }
499+ }
500+
501+ // Any type that is a subtype of an exposed type is also exposed. Propagate
502+ // from supertypes to subtypes.
503+ std::vector<HeapType> work (subtypesExposed.begin (), subtypesExposed.end ());
504+ while (!work.empty ()) {
505+ auto type = work.back ();
506+ work.pop_back ();
507+ if (type.isBasic ()) {
508+ // TODO: Unify this with the more incremental propagation below if
509+ // SubTypes ever gets support for scanning basic types.
510+ for (auto other : subTypes.types ) {
511+ if (HeapType::isSubType (other, type)) {
512+ noteExposed (other);
513+ }
514+ }
515+ } else {
516+ for (auto sub : subTypes.getImmediateSubTypes (type)) {
517+ if (noteExposed (sub)) {
518+ work.push_back (sub);
519+ }
435520 }
436521 }
437522 }
0 commit comments