Skip to content

Commit 42b859f

Browse files
authored
[Wasm GC] Add AbstractTypeRefining pass (#5461)
If a type hierarchy has abstract classes in the middle, that is, types that are never instantiated, then we can optimize casts and other operations to them. Say in Java that we have `AbstractList`, and it only has one subclass `IntList` that is ever created, then any place we have an `AbstractList` we must actually have an `IntList`, or a null. (Or, if no subtype is instantiated, then the value must definitely be a null.) The actual implementation does a type mapping, that is, it finds all places using an abstract type and makes them refer to the single instantiated subtype (or null). After that change, no references to the abstract type remain in the program, so this both refines types and also cleans up the type section.
1 parent d2d5135 commit 42b859f

12 files changed

Lines changed: 1696 additions & 82 deletions

File tree

scripts/fuzz_opt.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,7 @@ def write_commands(commands, filename):
12761276
opt_choices = [
12771277
(),
12781278
('-O1',), ('-O2',), ('-O3',), ('-O4',), ('-Os',), ('-Oz',),
1279+
("--abstract-type-refining",),
12791280
("--cfp",),
12801281
("--coalesce-locals",),
12811282
# XXX slow, non-default ("--coalesce-locals-learning",),
@@ -1353,6 +1354,7 @@ def write_commands(commands, filename):
13531354
("--signature-refining",),
13541355
("--gto",),
13551356
("--remove-unused-types",),
1357+
("--abstract-type-refining",),
13561358
("--cfp",),
13571359
("--gsi",),
13581360
("--type-ssa",),

src/ir/subtypes.h

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,12 @@ struct SubTypes {
7070
return ret;
7171
}
7272

73-
// Computes the depth of children for each type. This is 0 if the type has no
74-
// subtypes, 1 if it has subtypes but none of those have subtypes themselves,
75-
// and so forth.
76-
//
77-
// This depth ignores bottom types.
78-
std::unordered_map<HeapType, Index> getMaxDepths() {
79-
struct DepthSort : TopologicalSort<HeapType, DepthSort> {
73+
// A topological sort that visits subtypes first.
74+
auto getSubTypesFirstSort() const {
75+
struct SubTypesFirstSort : TopologicalSort<HeapType, SubTypesFirstSort> {
8076
const SubTypes& parent;
8177

82-
DepthSort(const SubTypes& parent) : parent(parent) {
78+
SubTypesFirstSort(const SubTypes& parent) : parent(parent) {
8379
for (auto type : parent.types) {
8480
// The roots are types with no supertype.
8581
if (!type.getSuperType()) {
@@ -97,9 +93,18 @@ struct SubTypes {
9793
}
9894
};
9995

96+
return SubTypesFirstSort(*this);
97+
}
98+
99+
// Computes the depth of children for each type. This is 0 if the type has no
100+
// subtypes, 1 if it has subtypes but none of those have subtypes themselves,
101+
// and so forth.
102+
//
103+
// This depth ignores bottom types.
104+
std::unordered_map<HeapType, Index> getMaxDepths() {
100105
std::unordered_map<HeapType, Index> depths;
101106

102-
for (auto type : DepthSort(*this)) {
107+
for (auto type : getSubTypesFirstSort()) {
103108
// Begin with depth 0, then take into account the subtype depths.
104109
Index depth = 0;
105110
for (auto subType : getStrictSubTypes(type)) {

src/ir/type-updating.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,76 @@ class GlobalTypeRewriter {
405405
InsertOrderedMap<HeapType, Index> typeIndices;
406406
};
407407

408+
class TypeMapper : public GlobalTypeRewriter {
409+
public:
410+
using TypeUpdates = std::unordered_map<HeapType, HeapType>;
411+
412+
const TypeUpdates& mapping;
413+
414+
std::unordered_map<HeapType, Signature> newSignatures;
415+
416+
public:
417+
TypeMapper(Module& wasm, const TypeUpdates& mapping)
418+
: GlobalTypeRewriter(wasm), mapping(mapping) {}
419+
420+
void map() {
421+
// Map the types of expressions (curr->type, etc.) to their merged
422+
// types.
423+
mapTypes(mapping);
424+
425+
// Update the internals of types (struct fields, signatures, etc.) to
426+
// refer to the merged types.
427+
update();
428+
}
429+
430+
Type getNewType(Type type) {
431+
if (!type.isRef()) {
432+
return type;
433+
}
434+
auto heapType = type.getHeapType();
435+
auto iter = mapping.find(heapType);
436+
if (iter != mapping.end()) {
437+
return getTempType(Type(iter->second, type.getNullability()));
438+
}
439+
return getTempType(type);
440+
}
441+
442+
void modifyStruct(HeapType oldType, Struct& struct_) override {
443+
auto& oldFields = oldType.getStruct().fields;
444+
for (Index i = 0; i < oldFields.size(); i++) {
445+
auto& oldField = oldFields[i];
446+
auto& newField = struct_.fields[i];
447+
newField.type = getNewType(oldField.type);
448+
}
449+
}
450+
void modifyArray(HeapType oldType, Array& array) override {
451+
array.element.type = getNewType(oldType.getArray().element.type);
452+
}
453+
void modifySignature(HeapType oldSignatureType, Signature& sig) override {
454+
auto getUpdatedTypeList = [&](Type type) {
455+
std::vector<Type> vec;
456+
for (auto t : type) {
457+
vec.push_back(getNewType(t));
458+
}
459+
return getTempTupleType(vec);
460+
};
461+
462+
auto oldSig = oldSignatureType.getSignature();
463+
sig.params = getUpdatedTypeList(oldSig.params);
464+
sig.results = getUpdatedTypeList(oldSig.results);
465+
}
466+
std::optional<HeapType> getSuperType(HeapType oldType) override {
467+
// If the super is mapped, get it from the mapping.
468+
auto super = oldType.getSuperType();
469+
if (super) {
470+
if (auto it = mapping.find(*super); it != mapping.end()) {
471+
return it->second;
472+
}
473+
}
474+
return super;
475+
}
476+
};
477+
408478
namespace TypeUpdating {
409479

410480
// Checks whether a type is valid as a local, or whether

0 commit comments

Comments
 (0)