Skip to content

Commit f1062b0

Browse files
authored
Handle more types exposed to JS in GTO (#8454)
GTO already handled keeping the prototype configuration fields on descriptors of types that flowed out to JS via @binaryen.js.called functions, but it did not handle propagating the information that a type flows out to JS to that type's subtypes. It also did not consider that types may flow out to JS via imports and exports. Fix these issues.
1 parent 5315d3e commit f1062b0

2 files changed

Lines changed: 493 additions & 17 deletions

File tree

src/passes/GlobalTypeOptimization.cpp

Lines changed: 102 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)