-
Notifications
You must be signed in to change notification settings - Fork 818
Propagate to exact types in struct-utils.h #7889
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
20e051a
df76cfe
f35f60d
be904ae
edb1d95
65eff0c
fe62381
a310e7f
c567e37
7d0445e
89751b1
49dcfc7
80a65e1
56720bc
6403073
04fe19a
13b7a0f
1d61606
fe63302
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
|
||
#include "ir/properties.h" | ||
#include "ir/subtypes.h" | ||
#include "wasm-type.h" | ||
#include "wasm.h" | ||
|
||
namespace wasm { | ||
|
@@ -88,19 +89,24 @@ template<typename T> struct StructValues : public std::vector<T> { | |
// Also provides a combineInto() helper that combines one map into another. This | ||
// depends on the underlying T defining a combine() method. | ||
template<typename T> | ||
struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> { | ||
struct StructValuesMap | ||
: public std::unordered_map<std::pair<HeapType, Exactness>, StructValues<T>> { | ||
// When we access an item, if it does not already exist, create it with a | ||
// vector of the right length for that type. | ||
StructValues<T>& operator[](HeapType type) { | ||
assert(type.isStruct()); | ||
StructValues<T>& operator[](std::pair<HeapType, Exactness> type) { | ||
assert(type.first.isStruct()); | ||
auto inserted = this->insert({type, {}}); | ||
auto& values = inserted.first->second; | ||
if (inserted.second) { | ||
values.resize(type.getStruct().fields.size()); | ||
values.resize(type.first.getStruct().fields.size()); | ||
} | ||
return values; | ||
} | ||
|
||
StructValues<T>& operator[](HeapType type) { | ||
tlively marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
return (*this)[{type, Inexact}]; | ||
} | ||
|
||
|
||
void combineInto(StructValuesMap<T>& combinedInfos) const { | ||
for (auto& [type, info] : *this) { | ||
for (Index i = 0; i < info.size(); i++) { | ||
|
@@ -113,7 +119,8 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> { | |
void dump(std::ostream& o) { | ||
o << "dump " << this << '\n'; | ||
for (auto& [type, vec] : (*this)) { | ||
o << "dump " << type << " " << &vec << ' '; | ||
o << "dump " << type.first << (type.second == Exact ? " exact " : " ") | ||
<< &vec << ' '; | ||
for (auto x : vec) { | ||
x.dump(o); | ||
o << " "; | ||
|
@@ -203,7 +210,8 @@ struct StructScanner | |
// Note writes to all the fields of the struct. | ||
auto heapType = type.getHeapType(); | ||
auto& fields = heapType.getStruct().fields; | ||
auto& infos = functionNewInfos[this->getFunction()][heapType]; | ||
auto ht = std::make_pair(heapType, Exact); | ||
auto& infos = functionNewInfos[this->getFunction()][ht]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we can remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, probably. I can look at this in a follow-up. |
||
for (Index i = 0; i < fields.size(); i++) { | ||
if (curr->isWithDefault()) { | ||
self().noteDefault(fields[i].type, heapType, i, infos[i]); | ||
|
@@ -224,11 +232,12 @@ struct StructScanner | |
} | ||
|
||
// Note a write to this field of the struct. | ||
noteExpressionOrCopy(curr->value, | ||
type.getHeapType(), | ||
curr->index, | ||
functionSetGetInfos[this->getFunction()] | ||
[type.getHeapType()][curr->index]); | ||
auto ht = std::make_pair(type.getHeapType(), type.getExactness()); | ||
noteExpressionOrCopy( | ||
curr->value, | ||
type.getHeapType(), | ||
curr->index, | ||
functionSetGetInfos[this->getFunction()][ht][curr->index]); | ||
} | ||
|
||
void visitStructGet(StructGet* curr) { | ||
|
@@ -237,11 +246,11 @@ struct StructScanner | |
return; | ||
} | ||
|
||
auto heapType = type.getHeapType(); | ||
auto ht = std::make_pair(type.getHeapType(), type.getExactness()); | ||
auto index = curr->index; | ||
self().noteRead(heapType, | ||
self().noteRead(type.getHeapType(), | ||
index, | ||
functionSetGetInfos[this->getFunction()][heapType][index]); | ||
functionSetGetInfos[this->getFunction()][ht][index]); | ||
} | ||
|
||
void visitStructRMW(StructRMW* curr) { | ||
|
@@ -251,9 +260,9 @@ struct StructScanner | |
} | ||
|
||
auto heapType = type.getHeapType(); | ||
auto ht = std::make_pair(heapType, type.getExactness()); | ||
auto index = curr->index; | ||
auto& info = | ||
functionSetGetInfos[this->getFunction()][type.getHeapType()][index]; | ||
auto& info = functionSetGetInfos[this->getFunction()][ht][index]; | ||
|
||
if (curr->op == RMWXchg) { | ||
// An xchg is really like a read and write combined. | ||
|
@@ -274,9 +283,9 @@ struct StructScanner | |
} | ||
|
||
auto heapType = type.getHeapType(); | ||
auto ht = std::make_pair(heapType, type.getExactness()); | ||
auto index = curr->index; | ||
auto& info = | ||
functionSetGetInfos[this->getFunction()][type.getHeapType()][curr->index]; | ||
auto& info = functionSetGetInfos[this->getFunction()][ht][index]; | ||
|
||
// A cmpxchg is like a read and conditional write. | ||
self().noteRead(heapType, index, info); | ||
|
@@ -310,11 +319,12 @@ struct StructScanner | |
return; | ||
} | ||
auto heapType = type.getHeapType(); | ||
auto ht = std::make_pair(heapType, type.getExactness()); | ||
if (heapType.isStruct()) { | ||
// Any subtype of the reference here may be read from. | ||
self().noteRead(heapType, | ||
DescriptorIndex, | ||
functionSetGetInfos[this->getFunction()][heapType].desc); | ||
functionSetGetInfos[this->getFunction()][ht].desc); | ||
return; | ||
} | ||
} | ||
|
@@ -372,13 +382,19 @@ template<typename T> class TypeHierarchyPropagator { | |
// Propagate given a StructValuesMap, which means we need to take into | ||
// account fields. | ||
void propagateToSuperTypes(StructValuesMap<T>& infos) { | ||
propagate(infos, false, true); | ||
propagate(infos, false, true, true); | ||
|
||
} | ||
void propagateToSubTypes(StructValuesMap<T>& infos) { | ||
propagate(infos, true, false); | ||
propagate(infos, true, false, false); | ||
} | ||
void propagateToSubTypesWithExact(StructValuesMap<T>& infos) { | ||
propagate(infos, true, false, true); | ||
} | ||
void propagateToSuperAndSubTypes(StructValuesMap<T>& infos) { | ||
propagate(infos, true, true); | ||
propagate(infos, true, true, false); | ||
} | ||
void propagateToSuperAndSubTypesWithExact(StructValuesMap<T>& infos) { | ||
propagate(infos, true, true, true); | ||
} | ||
|
||
// Propagate on a simpler map of structs and infos (that is, not using | ||
|
@@ -398,46 +414,63 @@ template<typename T> class TypeHierarchyPropagator { | |
private: | ||
void propagate(StructValuesMap<T>& combinedInfos, | ||
bool toSubTypes, | ||
bool toSuperTypes) { | ||
UniqueDeferredQueue<HeapType> work; | ||
for (auto& [type, _] : combinedInfos) { | ||
work.push(type); | ||
bool toSuperTypes, | ||
bool includeExact) { | ||
UniqueDeferredQueue<std::pair<HeapType, Exactness>> work; | ||
for (auto& [ht, _] : combinedInfos) { | ||
work.push(ht); | ||
} | ||
while (!work.empty()) { | ||
auto type = work.pop(); | ||
auto& infos = combinedInfos[type]; | ||
auto [type, exactness] = work.pop(); | ||
auto& infos = combinedInfos[{type, exactness}]; | ||
|
||
if (toSuperTypes) { | ||
// Propagate shared fields to the supertype. | ||
if (auto superType = type.getDeclaredSuperType()) { | ||
auto& superInfos = combinedInfos[*superType]; | ||
auto& superFields = superType->getStruct().fields; | ||
for (Index i = 0; i < superFields.size(); i++) { | ||
// Propagate shared fields to the supertype, which may be the inexact | ||
// version of the same type. | ||
std::optional<std::pair<HeapType, Exactness>> super; | ||
if (exactness == Exact) { | ||
super = {type, Inexact}; | ||
} else if (auto superType = type.getDeclaredSuperType()) { | ||
super = {*superType, Inexact}; | ||
} | ||
if (super) { | ||
auto& superInfos = combinedInfos[*super]; | ||
const auto& superFields = &super->first.getStruct().fields; | ||
for (Index i = 0; i < superFields->size(); i++) { | ||
if (superInfos[i].combine(infos[i])) { | ||
work.push(*superType); | ||
work.push(*super); | ||
} | ||
} | ||
// Propagate the descriptor to the super, if the super has one. | ||
if (superType->getDescriptorType() && | ||
if (super->first.getDescriptorType() && | ||
superInfos.desc.combine(infos.desc)) { | ||
work.push(*superType); | ||
work.push(*super); | ||
} | ||
} | ||
} | ||
|
||
if (toSubTypes) { | ||
// Propagate shared fields to the subtypes. | ||
// Propagate shared fields to the subtypes, which may just be the exact | ||
// version of the same type. | ||
auto numFields = type.getStruct().fields.size(); | ||
for (auto subType : subTypes.getImmediateSubTypes(type)) { | ||
auto& subInfos = combinedInfos[subType]; | ||
std::vector<std::pair<HeapType, Exactness>> subs; | ||
if (includeExact && exactness == Inexact) { | ||
subs = {{type, Exact}}; | ||
} else { | ||
for (auto subType : subTypes.getImmediateSubTypes(type)) { | ||
subs.emplace_back(subType, Inexact); | ||
} | ||
} | ||
for (auto sub : subs) { | ||
auto& subInfos = combinedInfos[sub]; | ||
for (Index i = 0; i < numFields; i++) { | ||
if (subInfos[i].combine(infos[i])) { | ||
work.push(subType); | ||
work.push(sub); | ||
} | ||
} | ||
// Propagate the descriptor. | ||
if (subInfos.desc.combine(infos.desc)) { | ||
work.push(subType); | ||
work.push(sub); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,7 +53,6 @@ | |
|
||
#include "ir/bits.h" | ||
#include "ir/gc-type-utils.h" | ||
#include "ir/module-utils.h" | ||
#include "ir/possible-constant.h" | ||
#include "ir/struct-utils.h" | ||
#include "ir/utils.h" | ||
|
@@ -114,8 +113,10 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> { | |
return heapType; | ||
} | ||
|
||
PossibleConstantValues getInfo(HeapType type, Index index) { | ||
if (auto it = propagatedInfos.find(type); it != propagatedInfos.end()) { | ||
PossibleConstantValues | ||
getInfo(HeapType type, Exactness exactness, Index index) { | ||
if (auto it = propagatedInfos.find({type, exactness}); | ||
it != propagatedInfos.end()) { | ||
// There is information on this type, fetch it. | ||
return it->second[index]; | ||
} | ||
|
@@ -177,7 +178,8 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> { | |
// Find the info for this field, and see if we can optimize. First, see if | ||
// there is any information for this heap type at all. If there isn't, it is | ||
// as if nothing was ever noted for that field. | ||
PossibleConstantValues info = getInfo(heapType, index); | ||
PossibleConstantValues info = | ||
getInfo(heapType, ref->type.getExactness(), index); | ||
if (!info.hasNoted()) { | ||
// This field is never written at all. That means that we do not even | ||
// 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>> { | |
return; | ||
} | ||
|
||
auto iter = rawNewInfos.find(type); | ||
auto iter = rawNewInfos.find({type, Exact}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
They are always exact because they are info from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be clear, I think the idea in this PR is good! I'll read the code in detail monday. But my point is that CFP already has exact info, in the form of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll have follow-up PRs removing |
||
if (iter == rawNewInfos.end()) { | ||
// This type has no struct.news, so we can ignore it: it is abstract. | ||
return; | ||
|
@@ -446,7 +448,8 @@ struct PCVScanner | |
void noteCopy(HeapType type, Index index, PossibleConstantValues& info) { | ||
// Note copies, as they must be considered later. See the comment on the | ||
// propagation of values below. | ||
functionCopyInfos[getFunction()][type][index] = true; | ||
// TODO: Take into account exactness here. | ||
functionCopyInfos[getFunction()][{type, Inexact}][index] = true; | ||
} | ||
|
||
void noteRead(HeapType type, Index index, PossibleConstantValues& info) { | ||
|
@@ -558,7 +561,7 @@ struct ConstantFieldPropagation : public Pass { | |
// a copy of A means it could be a copy of B or C). | ||
StructUtils::TypeHierarchyPropagator<StructUtils::CombinableBool> | ||
boolPropagator(subTypes); | ||
boolPropagator.propagateToSubTypes(combinedCopyInfos); | ||
boolPropagator.propagateToSubTypesWithExact(combinedCopyInfos); | ||
|
||
for (auto& [type, copied] : combinedCopyInfos) { | ||
for (Index i = 0; i < copied.size(); i++) { | ||
if (copied[i]) { | ||
|
@@ -570,7 +573,7 @@ struct ConstantFieldPropagation : public Pass { | |
StructUtils::TypeHierarchyPropagator<PossibleConstantValues> propagator( | ||
subTypes); | ||
propagator.propagateToSuperTypes(combinedNewInfos); | ||
propagator.propagateToSuperAndSubTypes(combinedSetInfos); | ||
propagator.propagateToSuperAndSubTypesWithExact(combinedSetInfos); | ||
|
||
// Combine both sources of information to the final information that gets | ||
// care about. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,6 @@ | |
#include "ir/struct-utils.h" | ||
#include "ir/subtypes.h" | ||
#include "ir/type-updating.h" | ||
#include "ir/utils.h" | ||
#include "pass.h" | ||
#include "support/permutations.h" | ||
#include "wasm-builder.h" | ||
|
@@ -205,9 +204,9 @@ struct GlobalTypeOptimization : public Pass { | |
SubTypes subTypes(*module); | ||
StructUtils::TypeHierarchyPropagator<FieldInfo> propagator(subTypes); | ||
auto dataFromSubsAndSupersMap = combinedSetGetInfos; | ||
propagator.propagateToSuperAndSubTypes(dataFromSubsAndSupersMap); | ||
propagator.propagateToSuperAndSubTypesWithExact(dataFromSubsAndSupersMap); | ||
auto dataFromSupersMap = std::move(combinedSetGetInfos); | ||
propagator.propagateToSubTypes(dataFromSupersMap); | ||
propagator.propagateToSubTypesWithExact(dataFromSupersMap); | ||
|
||
|
||
// Find the public types, which we must not modify. | ||
auto publicTypes = ModuleUtils::getPublicHeapTypes(*module); | ||
|
@@ -224,8 +223,9 @@ struct GlobalTypeOptimization : public Pass { | |
continue; | ||
} | ||
auto& fields = type.getStruct().fields; | ||
auto& dataFromSubsAndSupers = dataFromSubsAndSupersMap[type]; | ||
auto& dataFromSupers = dataFromSupersMap[type]; | ||
auto ht = std::make_pair(type, Exact); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment makes sense for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's propagated both ways, then the exact and inexact entries are the same. I'll add that to the comment. |
||
auto& dataFromSubsAndSupers = dataFromSubsAndSupersMap[ht]; | ||
auto& dataFromSupers = dataFromSupersMap[ht]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I must say, I find it confusing to see "use an Exact reference, but look in data that was propagated from subs and supers". Those feel in contradiction. What's the right way to look at this? Concretely, I'm not sure why this Exact should not be InExact. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An Concretely, this needs to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, thanks. That makes sense, but this does seem more complicated than before. Continuing my comment from a moment ago, can we
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another solution to limit the spread of this complexity would be to revert the shared changes and so something bespoke in CFP. I already have further changes to how copies are noted in the works, and that also only benefits CFP. I could also get some nice performance gains by doing a custom propagation algorithm for CFP, so maybe it makes sense not try to share any of that with other passes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting, how would the custom propagation differ from the existing? Though it seems CFP has fairly general needs, so I would hope we can keep sharing logic between it and other passes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As we discussed offline, I will remove the complexity of trying to sometimes include exactness and sometimes not. We will always make exactness explicit for all users. As we also discussed, the problem with suggestion (2) is that the fully collected information for a particular heap type might be in its exact or inexact entry (or both) depending on which direction the information was propagated in. |
||
|
||
// Process immutability. | ||
for (Index i = 0; i < fields.size(); i++) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth explaining in the comment what the meaning of the mapping is, that is, what do the exact and inexact entries mean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.