Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion src/passes/GlobalTypeOptimization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
//

#include "ir/eh-utils.h"
#include "ir/intrinsics.h"
#include "ir/localize.h"
#include "ir/names.h"
#include "ir/ordering.h"
Expand Down Expand Up @@ -151,6 +152,10 @@ struct GlobalTypeOptimization : public Pass {
// Combine the data from the functions.
functionSetGetInfos.combineInto(combinedSetGetInfos);

// Analyze functions called by JS to find fields holding configured
// prototypes that cannot be removed.
analyzeJSCalledFunctions(*module);

// Propagate information to super and subtypes on set/get infos:
//
// * For removing unread fields, we can only remove a field if it is never
Expand Down Expand Up @@ -201,7 +206,7 @@ struct GlobalTypeOptimization : public Pass {
auto& fields = type.getStruct().fields;
// Use the exact entry because information from the inexact entry in
// dataFromSupersMap will have been propagated down into it but not vice
// versa. (This doesn't matter or dataFromSubsAndSupers because the exact
// versa. (This doesn't matter for dataFromSubsAndSupers because the exact
// and inexact entries will have the same data.)
auto ht = std::make_pair(type, Exact);
auto& dataFromSubsAndSupers = dataFromSubsAndSupersMap[ht];
Expand Down Expand Up @@ -395,6 +400,29 @@ struct GlobalTypeOptimization : public Pass {
}
}

void analyzeJSCalledFunctions(Module& wasm) {
if (!wasm.features.hasCustomDescriptors()) {
return;
}
Type externref(HeapType::ext, Nullable);
for (auto func : Intrinsics(wasm).getConfigureAllFunctions()) {
for (auto type : wasm.getFunction(func)->getResults()) {
if (!type.isRef()) {
continue;
}
if (auto desc = type.getHeapType().getDescriptorType()) {
const auto& descFields = desc->getStruct().fields;
if (!descFields.empty() &&
Type::isSubType(descFields[0].type, externref) &&
descFields[0].mutable_ == Immutable) {
// This field holds a JS-visible prototype. Do not remove it.
combinedSetGetInfos[std::make_pair(*desc, Exact)][0].noteRead();
}
}
}
}
}

void updateTypes(Module& wasm) {
class TypeRewriter : public GlobalTypeRewriter {
GlobalTypeOptimization& parent;
Expand Down
5 changes: 4 additions & 1 deletion src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3946,7 +3946,10 @@ std::ostream& operator<<(std::ostream& o, wasm::ModuleType pair) {
std::ostream& operator<<(std::ostream& o, wasm::ModuleHeapType pair) {
if (auto it = pair.first.typeNames.find(pair.second);
it != pair.first.typeNames.end()) {
return o << it->second.name;
return o << '$' << it->second.name;
}
if (pair.second.isBasic()) {
return o << pair.second;
}
return o << "(unnamed)";
}
Expand Down
68 changes: 68 additions & 0 deletions src/passes/Unsubtyping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ struct Unsubtyping : Pass {
// Initialize the subtype relation based on what is immediately required to
// keep the code and public types valid.
analyzePublicTypes(*wasm);
analyzeJSCalledFunctions(*wasm);
analyzeModule(*wasm);

// Find further subtypings and iterate to a fixed point.
Expand Down Expand Up @@ -513,6 +514,31 @@ struct Unsubtyping : Pass {
work.push_back({Kind::Descriptor, described, descriptor});
}

void noteCast(HeapType src, Type dstType) {
auto dst = dstType.getHeapType();
// Casts to self and casts that must fail because they have incompatible
// types are uninteresting.
if (dst == src) {
return;
}
if (HeapType::isSubType(dst, src)) {
if (dstType.isExact()) {
// This cast only tests that the exact destination type is a subtype
// of the source type and does not impose additional requirements on
// subtypes of the destination type like a normal cast does.
noteSubtype(dst, src);
return;
}
casts[src].push_back(dst);
return;
}
if (HeapType::isSubType(src, dst)) {
// This is an upcast that will always succeed, but only if we ensure
// src <: dst.
noteSubtype(src, dst);
}
}

void analyzePublicTypes(Module& wasm) {
// We cannot change supertypes for anything public.
for (auto type : ModuleUtils::getPublicHeapTypes(wasm)) {
Expand All @@ -525,6 +551,40 @@ struct Unsubtyping : Pass {
}
}

void analyzeJSCalledFunctions(Module& wasm) {
if (!wasm.features.hasCustomDescriptors()) {
return;
}
Type anyref(HeapType::any, Nullable);
Type externref(HeapType::ext, Nullable);
for (auto func : Intrinsics(wasm).getConfigureAllFunctions()) {
for (auto type : wasm.getFunction(func)->getParams()) {
if (Type::isSubType(type, anyref)) {
// This type is cast from anyref on the boundary.
noteCast(HeapType::any, type);
}
}
for (auto type : wasm.getFunction(func)->getResults()) {
if (Type::isSubType(type, anyref)) {
auto heapType = type.getHeapType();
// This type is implicitly converted to externref, which means it can
// flow into locations typed any.
noteSubtype(heapType, HeapType::any);
if (auto desc = heapType.getDescriptorType()) {
const auto& descFields = desc->getStruct().fields;
if (!descFields.empty() &&
Type::isSubType(descFields[0].type, externref) &&
descFields[0].mutable_ == Immutable) {
// This descriptor will expose a prototype to JS, so we must keep
// it.
noteDescriptor(heapType, *desc);
}
}
}
}
}
}

void analyzeModule(Module& wasm) {
struct Info {
// (source, target) pairs for casts.
Expand Down Expand Up @@ -727,6 +787,14 @@ struct Unsubtyping : Pass {
for (auto& [sub, super] : collectedInfo.subtypings) {
noteSubtype(sub, super);
}
// Combine casts we have already noted into the newly gathered casts.
for (auto& [src, dsts] : casts) {
for (auto dst : dsts) {
collectedInfo.casts.insert({src, dst});
}
dsts.clear();
}
// Record the deduplicated cast info.
for (auto [src, dst] : collectedInfo.casts) {
casts[src].push_back(dst);
}
Expand Down
Loading