|
54 | 54 | #include "ir/utils.h" |
55 | 55 | #include "pass.h" |
56 | 56 | #include "support/hash.h" |
| 57 | +#include "wasm-type-shape.h" |
57 | 58 | #include "wasm.h" |
58 | 59 |
|
59 | 60 | namespace wasm { |
60 | 61 |
|
61 | 62 | namespace { |
62 | 63 |
|
63 | | -// Given some TypeBuilder items that we want to build new types with, this |
64 | | -// function builds the types in a new rec group. |
65 | | -// |
66 | | -// This is almost the same as just calling build(), but there is a risk of a |
67 | | -// collision with an existing rec group. This function handles that by finding a |
68 | | -// way to ensure that the new types are in fact in a new rec group. |
69 | | -// |
70 | | -// TODO: Move this outside if we find more uses. |
71 | | -std::vector<HeapType> ensureTypesAreInNewRecGroup(RecGroup recGroup, |
| 64 | +// Ensure there are no conflicts between the newly built types and any existing |
| 65 | +// types. |
| 66 | +std::vector<HeapType> ensureTypesAreInNewRecGroup(std::vector<HeapType>&& types, |
72 | 67 | Module& wasm) { |
73 | | - auto num = recGroup.size(); |
74 | | - |
75 | | - std::vector<HeapType> types; |
76 | | - types.reserve(num); |
77 | | - for (auto type : recGroup) { |
78 | | - types.push_back(type); |
| 68 | + std::unordered_set<RecGroup> existing; |
| 69 | + for (auto type : ModuleUtils::collectHeapTypes(wasm)) { |
| 70 | + existing.insert(type.getRecGroup()); |
79 | 71 | } |
80 | 72 |
|
81 | | - // Find all the heap types present before we create the new ones. The new |
82 | | - // types must not appear in |existingSet|. |
83 | | - std::vector<HeapType> existing = ModuleUtils::collectHeapTypes(wasm); |
84 | | - std::unordered_set<HeapType> existingSet(existing.begin(), existing.end()); |
85 | | - |
86 | | - // Check for a collision with an existing rec group. Note that it is enough to |
87 | | - // check one of the types: either the entire rec group gets merged, so they |
88 | | - // are all merged, or not. |
89 | | - if (existingSet.count(types[0])) { |
90 | | - // Unfortunately there is a conflict. Handle it by adding a "hash" - a |
91 | | - // "random" extra item in the rec group that is so outlandish it will |
92 | | - // surely (?) never collide with anything. We must loop while doing so, |
93 | | - // until we find a hash that does not collide. |
94 | | - // |
95 | | - // Note that we use uint64_t here, and deterministic_hash_combine below, to |
96 | | - // ensure our output is fully deterministic - the types we add here are |
97 | | - // observable in the output. |
98 | | - uint64_t hashSize = num + 10; |
99 | | - uint64_t random = num; |
100 | | - while (1) { |
101 | | - // Make a builder and add a slot for the hash. |
102 | | - TypeBuilder builder(num + 1); |
103 | | - for (Index i = 0; i < num; i++) { |
104 | | - builder[i].copy(types[i]); |
105 | | - } |
106 | | - |
107 | | - // Implement the hash as a struct with "random" fields, and add it. |
108 | | - Struct hashStruct; |
109 | | - for (Index i = 0; i < hashSize; i++) { |
110 | | - // TODO: a denser encoding? |
111 | | - auto type = (random & 1) ? Type::i32 : Type::f64; |
112 | | - deterministic_hash_combine(random, hashSize + i); |
113 | | - hashStruct.fields.push_back(Field(type, Mutable)); |
114 | | - } |
115 | | - builder[num] = hashStruct; |
116 | | - |
117 | | - // Build and hope for the best. |
118 | | - builder.createRecGroup(0, num + 1); |
119 | | - auto result = builder.build(); |
120 | | - assert(!result.getError()); |
121 | | - types = *result; |
122 | | - assert(types.size() == num + 1); |
123 | | - |
124 | | - if (existingSet.count(types[0])) { |
125 | | - // There is still a collision. Exponentially use larger hashes to |
126 | | - // quickly find one that works. Note that we also use different |
127 | | - // pseudorandom values while doing so in the for-loop above. |
128 | | - hashSize *= 2; |
129 | | - } else { |
130 | | - // Success! Leave the loop. |
131 | | - break; |
132 | | - } |
133 | | - } |
| 73 | + UniqueRecGroups unique(wasm.features); |
| 74 | + for (auto group : existing) { |
| 75 | + std::vector<HeapType> types(group.begin(), group.end()); |
| 76 | + [[maybe_unused]] auto uniqueTypes = unique.insert(std::move(types)); |
| 77 | + assert(uniqueTypes.size() == group.size() && "unexpected collision"); |
134 | 78 | } |
135 | 79 |
|
136 | | -#ifndef NDEBUG |
137 | | - // Verify the lack of a collision, just to be safe. |
138 | | - for (auto newType : types) { |
139 | | - assert(!existingSet.count(newType)); |
| 80 | + auto num = types.size(); |
| 81 | + std::vector<HeapType> uniqueTypes = unique.insert(std::move(types)); |
| 82 | + if (uniqueTypes.size() != num) { |
| 83 | + // Remove the brand type, which we do not need to consider further. |
| 84 | + uniqueTypes.pop_back(); |
140 | 85 | } |
141 | | -#endif |
142 | | - |
143 | | - return types; |
| 86 | + assert(uniqueTypes.size() == num); |
| 87 | + return uniqueTypes; |
144 | 88 | } |
145 | 89 |
|
146 | 90 | // A vector of struct.new or one of the variations on array.new. |
@@ -407,8 +351,7 @@ struct TypeSSA : public Pass { |
407 | 351 | assert(newTypes.size() == num); |
408 | 352 |
|
409 | 353 | // Make sure this is actually a new rec group. |
410 | | - auto recGroup = newTypes[0].getRecGroup(); |
411 | | - newTypes = ensureTypesAreInNewRecGroup(recGroup, *module); |
| 354 | + newTypes = ensureTypesAreInNewRecGroup(std::move(newTypes), *module); |
412 | 355 |
|
413 | 356 | // Success: we can apply the new types. |
414 | 357 |
|
|
0 commit comments