Skip to content

Commit 20e051a

Browse files
committed
Propate to exact types in struct-utils.h
Key collected struct information on both heap type and exactness, allowing queries for exact types to return more precise results than queries on the corresponding inexact types. Use this to fix a bug in CFP where it failed to take into account exactness and would unnecessarily and incorrectly emit a select between two values of different types where a single exact type was expected. Also update GTO to propagate to exact types even though it does not take advantage of them. This is necessary because the FieldScanner now collects information for exact and inexact types separately and they need to be combined.
1 parent 4790636 commit 20e051a

5 files changed

Lines changed: 223 additions & 54 deletions

File tree

src/ir/struct-utils.h

Lines changed: 74 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "ir/properties.h"
2121
#include "ir/subtypes.h"
22+
#include "wasm-type.h"
2223
#include "wasm.h"
2324

2425
namespace wasm {
@@ -88,19 +89,24 @@ template<typename T> struct StructValues : public std::vector<T> {
8889
// Also provides a combineInto() helper that combines one map into another. This
8990
// depends on the underlying T defining a combine() method.
9091
template<typename T>
91-
struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
92+
struct StructValuesMap
93+
: public std::unordered_map<std::pair<HeapType, Exactness>, StructValues<T>> {
9294
// When we access an item, if it does not already exist, create it with a
9395
// vector of the right length for that type.
94-
StructValues<T>& operator[](HeapType type) {
95-
assert(type.isStruct());
96+
StructValues<T>& operator[](std::pair<HeapType, Exactness> type) {
97+
assert(type.first.isStruct());
9698
auto inserted = this->insert({type, {}});
9799
auto& values = inserted.first->second;
98100
if (inserted.second) {
99-
values.resize(type.getStruct().fields.size());
101+
values.resize(type.first.getStruct().fields.size());
100102
}
101103
return values;
102104
}
103105

106+
StructValues<T>& operator[](HeapType type) {
107+
return (*this)[{type, Inexact}];
108+
}
109+
104110
void combineInto(StructValuesMap<T>& combinedInfos) const {
105111
for (auto& [type, info] : *this) {
106112
for (Index i = 0; i < info.size(); i++) {
@@ -113,7 +119,8 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
113119
void dump(std::ostream& o) {
114120
o << "dump " << this << '\n';
115121
for (auto& [type, vec] : (*this)) {
116-
o << "dump " << type << " " << &vec << ' ';
122+
o << "dump " << type.first << (type.second == Exact ? " exact " : " ")
123+
<< &vec << ' ';
117124
for (auto x : vec) {
118125
x.dump(o);
119126
o << " ";
@@ -203,7 +210,8 @@ struct StructScanner
203210
// Note writes to all the fields of the struct.
204211
auto heapType = type.getHeapType();
205212
auto& fields = heapType.getStruct().fields;
206-
auto& infos = functionNewInfos[this->getFunction()][heapType];
213+
auto ht = std::make_pair(heapType, Exact);
214+
auto& infos = functionNewInfos[this->getFunction()][ht];
207215
for (Index i = 0; i < fields.size(); i++) {
208216
if (curr->isWithDefault()) {
209217
self().noteDefault(fields[i].type, heapType, i, infos[i]);
@@ -224,11 +232,12 @@ struct StructScanner
224232
}
225233

226234
// Note a write to this field of the struct.
227-
noteExpressionOrCopy(curr->value,
228-
type.getHeapType(),
229-
curr->index,
230-
functionSetGetInfos[this->getFunction()]
231-
[type.getHeapType()][curr->index]);
235+
auto ht = std::make_pair(type.getHeapType(), type.getExactness());
236+
noteExpressionOrCopy(
237+
curr->value,
238+
type.getHeapType(),
239+
curr->index,
240+
functionSetGetInfos[this->getFunction()][ht][curr->index]);
232241
}
233242

234243
void visitStructGet(StructGet* curr) {
@@ -237,11 +246,11 @@ struct StructScanner
237246
return;
238247
}
239248

240-
auto heapType = type.getHeapType();
249+
auto ht = std::make_pair(type.getHeapType(), type.getExactness());
241250
auto index = curr->index;
242-
self().noteRead(heapType,
251+
self().noteRead(type.getHeapType(),
243252
index,
244-
functionSetGetInfos[this->getFunction()][heapType][index]);
253+
functionSetGetInfos[this->getFunction()][ht][index]);
245254
}
246255

247256
void visitStructRMW(StructRMW* curr) {
@@ -251,9 +260,9 @@ struct StructScanner
251260
}
252261

253262
auto heapType = type.getHeapType();
263+
auto ht = std::make_pair(heapType, type.getExactness());
254264
auto index = curr->index;
255-
auto& info =
256-
functionSetGetInfos[this->getFunction()][type.getHeapType()][index];
265+
auto& info = functionSetGetInfos[this->getFunction()][ht][index];
257266

258267
if (curr->op == RMWXchg) {
259268
// An xchg is really like a read and write combined.
@@ -274,9 +283,9 @@ struct StructScanner
274283
}
275284

276285
auto heapType = type.getHeapType();
286+
auto ht = std::make_pair(heapType, type.getExactness());
277287
auto index = curr->index;
278-
auto& info =
279-
functionSetGetInfos[this->getFunction()][type.getHeapType()][curr->index];
288+
auto& info = functionSetGetInfos[this->getFunction()][ht][index];
280289

281290
// A cmpxchg is like a read and conditional write.
282291
self().noteRead(heapType, index, info);
@@ -310,11 +319,12 @@ struct StructScanner
310319
return;
311320
}
312321
auto heapType = type.getHeapType();
322+
auto ht = std::make_pair(heapType, type.getExactness());
313323
if (heapType.isStruct()) {
314324
// Any subtype of the reference here may be read from.
315325
self().noteRead(heapType,
316326
DescriptorIndex,
317-
functionSetGetInfos[this->getFunction()][heapType].desc);
327+
functionSetGetInfos[this->getFunction()][ht].desc);
318328
return;
319329
}
320330
}
@@ -372,13 +382,19 @@ template<typename T> class TypeHierarchyPropagator {
372382
// Propagate given a StructValuesMap, which means we need to take into
373383
// account fields.
374384
void propagateToSuperTypes(StructValuesMap<T>& infos) {
375-
propagate(infos, false, true);
385+
propagate(infos, false, true, true);
376386
}
377387
void propagateToSubTypes(StructValuesMap<T>& infos) {
378-
propagate(infos, true, false);
388+
propagate(infos, true, false, false);
389+
}
390+
void propagateToSubTypesWithExact(StructValuesMap<T>& infos) {
391+
propagate(infos, true, false, true);
379392
}
380393
void propagateToSuperAndSubTypes(StructValuesMap<T>& infos) {
381-
propagate(infos, true, true);
394+
propagate(infos, true, true, false);
395+
}
396+
void propagateToSuperAndSubTypesWithExact(StructValuesMap<T>& infos) {
397+
propagate(infos, true, true, true);
382398
}
383399

384400
// Propagate on a simpler map of structs and infos (that is, not using
@@ -398,46 +414,63 @@ template<typename T> class TypeHierarchyPropagator {
398414
private:
399415
void propagate(StructValuesMap<T>& combinedInfos,
400416
bool toSubTypes,
401-
bool toSuperTypes) {
402-
UniqueDeferredQueue<HeapType> work;
403-
for (auto& [type, _] : combinedInfos) {
404-
work.push(type);
417+
bool toSuperTypes,
418+
bool includeExact) {
419+
UniqueDeferredQueue<std::pair<HeapType, Exactness>> work;
420+
for (auto& [ht, _] : combinedInfos) {
421+
work.push(ht);
405422
}
406423
while (!work.empty()) {
407-
auto type = work.pop();
408-
auto& infos = combinedInfos[type];
424+
auto [type, exactness] = work.pop();
425+
auto& infos = combinedInfos[{type, exactness}];
409426

410427
if (toSuperTypes) {
411-
// Propagate shared fields to the supertype.
412-
if (auto superType = type.getDeclaredSuperType()) {
413-
auto& superInfos = combinedInfos[*superType];
414-
auto& superFields = superType->getStruct().fields;
415-
for (Index i = 0; i < superFields.size(); i++) {
428+
// Propagate shared fields to the supertype, which may be the inexact
429+
// version of the same type.
430+
std::optional<std::pair<HeapType, Exactness>> super;
431+
if (exactness == Exact) {
432+
super = {type, Inexact};
433+
} else if (auto superType = type.getDeclaredSuperType()) {
434+
super = {*superType, Inexact};
435+
}
436+
if (super) {
437+
auto& superInfos = combinedInfos[*super];
438+
const auto& superFields = &super->first.getStruct().fields;
439+
for (Index i = 0; i < superFields->size(); i++) {
416440
if (superInfos[i].combine(infos[i])) {
417-
work.push(*superType);
441+
work.push(*super);
418442
}
419443
}
420444
// Propagate the descriptor to the super, if the super has one.
421-
if (superType->getDescriptorType() &&
445+
if (super->first.getDescriptorType() &&
422446
superInfos.desc.combine(infos.desc)) {
423-
work.push(*superType);
447+
work.push(*super);
424448
}
425449
}
426450
}
427451

428452
if (toSubTypes) {
429-
// Propagate shared fields to the subtypes.
453+
// Propagate shared fields to the subtypes, which may just be the exact
454+
// version of the same type.
430455
auto numFields = type.getStruct().fields.size();
431-
for (auto subType : subTypes.getImmediateSubTypes(type)) {
432-
auto& subInfos = combinedInfos[subType];
456+
std::vector<std::pair<HeapType, Exactness>> subs;
457+
if (includeExact && exactness == Inexact) {
458+
subs = {{type, Exact}};
459+
} else {
460+
for (auto subType : subTypes.getImmediateSubTypes(type)) {
461+
subs.emplace_back(subType, Inexact);
462+
}
463+
}
464+
for (auto sub : subs) {
465+
auto& subInfos = combinedInfos[sub];
433466
for (Index i = 0; i < numFields; i++) {
434467
if (subInfos[i].combine(infos[i])) {
435-
work.push(subType);
468+
work.push(sub);
436469
}
437470
}
438471
// Propagate the descriptor.
439472
if (subInfos.desc.combine(infos.desc)) {
440-
work.push(subType);
473+
work.push(sub);
441474
}
442475
}
443476
}

src/passes/ConstantFieldPropagation.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353

5454
#include "ir/bits.h"
5555
#include "ir/gc-type-utils.h"
56-
#include "ir/module-utils.h"
5756
#include "ir/possible-constant.h"
5857
#include "ir/struct-utils.h"
5958
#include "ir/utils.h"
@@ -114,8 +113,10 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
114113
return heapType;
115114
}
116115

117-
PossibleConstantValues getInfo(HeapType type, Index index) {
118-
if (auto it = propagatedInfos.find(type); it != propagatedInfos.end()) {
116+
PossibleConstantValues
117+
getInfo(HeapType type, Exactness exactness, Index index) {
118+
if (auto it = propagatedInfos.find({type, exactness});
119+
it != propagatedInfos.end()) {
119120
// There is information on this type, fetch it.
120121
return it->second[index];
121122
}
@@ -177,7 +178,8 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
177178
// Find the info for this field, and see if we can optimize. First, see if
178179
// there is any information for this heap type at all. If there isn't, it is
179180
// as if nothing was ever noted for that field.
180-
PossibleConstantValues info = getInfo(heapType, index);
181+
PossibleConstantValues info =
182+
getInfo(heapType, ref->type.getExactness(), index);
181183
if (!info.hasNoted()) {
182184
// This field is never written at all. That means that we do not even
183185
// construct any data of this type, and so it is a logic error to reach
@@ -282,7 +284,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
282284
return;
283285
}
284286

285-
auto iter = rawNewInfos.find(type);
287+
auto iter = rawNewInfos.find({type, Exact});
286288
if (iter == rawNewInfos.end()) {
287289
// This type has no struct.news, so we can ignore it: it is abstract.
288290
return;
@@ -446,7 +448,8 @@ struct PCVScanner
446448
void noteCopy(HeapType type, Index index, PossibleConstantValues& info) {
447449
// Note copies, as they must be considered later. See the comment on the
448450
// propagation of values below.
449-
functionCopyInfos[getFunction()][type][index] = true;
451+
// TODO: Take into account exactness here.
452+
functionCopyInfos[getFunction()][{type, Inexact}][index] = true;
450453
}
451454

452455
void noteRead(HeapType type, Index index, PossibleConstantValues& info) {
@@ -558,7 +561,7 @@ struct ConstantFieldPropagation : public Pass {
558561
// a copy of A means it could be a copy of B or C).
559562
StructUtils::TypeHierarchyPropagator<StructUtils::CombinableBool>
560563
boolPropagator(subTypes);
561-
boolPropagator.propagateToSubTypes(combinedCopyInfos);
564+
boolPropagator.propagateToSubTypesWithExact(combinedCopyInfos);
562565
for (auto& [type, copied] : combinedCopyInfos) {
563566
for (Index i = 0; i < copied.size(); i++) {
564567
if (copied[i]) {
@@ -570,7 +573,7 @@ struct ConstantFieldPropagation : public Pass {
570573
StructUtils::TypeHierarchyPropagator<PossibleConstantValues> propagator(
571574
subTypes);
572575
propagator.propagateToSuperTypes(combinedNewInfos);
573-
propagator.propagateToSuperAndSubTypes(combinedSetInfos);
576+
propagator.propagateToSuperAndSubTypesWithExact(combinedSetInfos);
574577

575578
// Combine both sources of information to the final information that gets
576579
// care about.

src/passes/GlobalTypeOptimization.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
#include "ir/struct-utils.h"
3131
#include "ir/subtypes.h"
3232
#include "ir/type-updating.h"
33-
#include "ir/utils.h"
3433
#include "pass.h"
3534
#include "support/permutations.h"
3635
#include "wasm-builder.h"
@@ -205,9 +204,9 @@ struct GlobalTypeOptimization : public Pass {
205204
SubTypes subTypes(*module);
206205
StructUtils::TypeHierarchyPropagator<FieldInfo> propagator(subTypes);
207206
auto dataFromSubsAndSupersMap = combinedSetGetInfos;
208-
propagator.propagateToSuperAndSubTypes(dataFromSubsAndSupersMap);
207+
propagator.propagateToSuperAndSubTypesWithExact(dataFromSubsAndSupersMap);
209208
auto dataFromSupersMap = std::move(combinedSetGetInfos);
210-
propagator.propagateToSubTypes(dataFromSupersMap);
209+
propagator.propagateToSubTypesWithExact(dataFromSupersMap);
211210

212211
// Find the public types, which we must not modify.
213212
auto publicTypes = ModuleUtils::getPublicHeapTypes(*module);
@@ -224,8 +223,9 @@ struct GlobalTypeOptimization : public Pass {
224223
continue;
225224
}
226225
auto& fields = type.getStruct().fields;
227-
auto& dataFromSubsAndSupers = dataFromSubsAndSupersMap[type];
228-
auto& dataFromSupers = dataFromSupersMap[type];
226+
auto ht = std::make_pair(type, Exact);
227+
auto& dataFromSubsAndSupers = dataFromSubsAndSupersMap[ht];
228+
auto& dataFromSupers = dataFromSupersMap[ht];
229229

230230
// Process immutability.
231231
for (Index i = 0; i < fields.size(); i++) {

0 commit comments

Comments
 (0)