summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/possible-contents.cpp26
-rw-r--r--src/ir/possible-contents.h83
2 files changed, 69 insertions, 40 deletions
diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp
index 836f7570d..1b5c5ae0d 100644
--- a/src/ir/possible-contents.cpp
+++ b/src/ir/possible-contents.cpp
@@ -1411,12 +1411,12 @@ bool Flower::updateContents(LocationIndex locationIndex,
// It is not worth sending any more to this location if we are now in the
// worst possible case, as no future value could cause any change.
//
- // Many is always the worst possible case. An exact type of a non-reference is
+ // Many is always the worst possible case. A cone type of a non-reference is
// also the worst case, since subtyping is not relevant there, and so if we
- // know only the type then we already know nothing beyond what the type in the
- // wasm tells us (and from there we can only go to Many).
+ // only know something about the type then we already know nothing beyond what
+ // the type in the wasm tells us (and from there we can only go to Many).
bool worthSendingMore = !contents.isMany();
- if (!contents.getType().isRef() && contents.isExactType()) {
+ if (!contents.getType().isRef() && contents.isConeType()) {
worthSendingMore = false;
}
@@ -1578,9 +1578,10 @@ void Flower::filterGlobalContents(PossibleContents& contents,
// "Many", since in the worst case we can just use the immutable value. That
// is, we can always replace this value with (global.get $name) which will
// get the right value. Likewise, using the immutable global value is often
- // better than an exact type, but TODO: we could note both an exact type
- // *and* that something is equal to a global, in some cases.
- if (contents.isMany() || contents.isExactType()) {
+ // better than a cone type (even an exact one), but TODO: we could note both
+ // a cone/exact type *and* that something is equal to a global, in some
+ // cases. See https://github.com/WebAssembly/binaryen/pull/5083
+ if (contents.isMany() || contents.isConeType()) {
contents = PossibleContents::global(global->name, global->type);
// TODO: We could do better here, to set global->init->type instead of
@@ -1603,7 +1604,7 @@ void Flower::readFromData(HeapType declaredHeapType,
Expression* read) {
// The data that a struct.get reads depends on two things: the reference that
// we read from, and the relevant DataLocations. The reference determines
- // which DataLocations are relevant: if it is an ExactType then we have a
+ // which DataLocations are relevant: if it is an exact type then we have a
// single DataLocation to read from, the one type that can be read from there.
// Otherwise, we might read from any subtype, and so all their DataLocations
// are relevant.
@@ -1645,22 +1646,21 @@ void Flower::readFromData(HeapType declaredHeapType,
std::cout << " add special reads\n";
#endif
- if (refContents.isExactType()) {
+ if (refContents.hasExactType()) {
// Add a single link to the exact location the reference points to.
connectDuringFlow(
DataLocation{refContents.getType().getHeapType(), fieldIndex},
ExpressionLocation{read, 0});
} else {
- // Otherwise, this is a cone: the declared type of the reference, or any
- // subtype of that, regardless of whether the content is a Many or a Global
- // or anything else.
+ // Otherwise, this is a true cone (i.e., it has a depth > 0): the declared
+ // type of the reference or some of its subtypes.
// TODO: The Global case may have a different cone type than the heapType,
// which we could use here.
// TODO: A Global may refer to an immutable global, which we can read the
// field from potentially (reading it from the struct.new/array.new
// in the definition of it, if it is not imported; or, we could track
// the contents of immutable fields of allocated objects, and not just
- // represent them as ExactType).
+ // represent them as an exact type).
// See the test TODO with text "We optimize some of this, but stop at
// reading from the immutable global"
assert(refContents.isMany() || refContents.isGlobal());
diff --git a/src/ir/possible-contents.h b/src/ir/possible-contents.h
index f3ad37d92..f7c39e1f9 100644
--- a/src/ir/possible-contents.h
+++ b/src/ir/possible-contents.h
@@ -44,15 +44,16 @@ namespace wasm {
// to that global. Typically we can only infer this for
// immutable globals.
//
-// * ExactType: Any possible value of a specific exact type - *not*
-// including subtypes. For example, (struct.new $Foo) has
-// ExactType contents of type $Foo.
+// * ConeType: Any possible value of a particular type, and a possible
+// "cone" of a certain depth below it. If the depth is 0
+// then only the exact type is possible; if the depth is 1
+// then either that type or its immediate subtypes, and so
+// forth.
// If the type here is nullable then null is also allowed.
-// TODO: Add ConeType, which would include subtypes.
-// TODO: Add ExactTypePlusContents or such, which would be
+// TODO: Add ConeTypePlusContents or such, which would be
// used on e.g. a struct.new with an immutable field
// to which we assign a constant: not only do we know
-// the exact type, but also certain field's values.
+// the type, but also certain field's values.
//
// * Many: Anything else. Many things are possible here, and we do
// not track what they might be, so we must assume the worst
@@ -73,17 +74,27 @@ class PossibleContents {
}
};
- using ExactType = Type;
+ struct ConeType {
+ Type type;
+ Index depth;
+ bool operator==(const ConeType& other) const {
+ return type == other.type && depth == other.depth;
+ }
+ };
struct Many : public std::monostate {};
// TODO: This is similar to the variant in PossibleConstantValues, and perhaps
// we could share code, but extending a variant using template magic may
// not be worthwhile. Another option might be to make PCV inherit from
- // this and disallow ExactType etc., but PCV might get slower.
- using Variant = std::variant<None, Literal, GlobalInfo, ExactType, Many>;
+ // this and disallow ConeType etc., but PCV might get slower.
+ using Variant = std::variant<None, Literal, GlobalInfo, ConeType, Many>;
Variant value;
+ // Internal convenience for creating a cone type with depth 0, i.e,, an exact
+ // type.
+ static ConeType ExactType(Type type) { return ConeType{type, 0}; }
+
public:
PossibleContents() : value(None()) {}
PossibleContents(const PossibleContents& other) = default;
@@ -98,9 +109,13 @@ public:
static PossibleContents global(Name name, Type type) {
return PossibleContents{GlobalInfo{name, type}};
}
+ // Helper for a cone type with depth 0, i.e., an exact type.
static PossibleContents exactType(Type type) {
return PossibleContents{ExactType(type)};
}
+ static PossibleContents coneType(Type type, Index depth) {
+ WASM_UNREACHABLE("actual cones are not supported yet");
+ }
static PossibleContents many() { return PossibleContents{Many()}; }
PossibleContents& operator=(const PossibleContents& other) = default;
@@ -120,7 +135,7 @@ public:
bool isNone() const { return std::get_if<None>(&value); }
bool isLiteral() const { return std::get_if<Literal>(&value); }
bool isGlobal() const { return std::get_if<GlobalInfo>(&value); }
- bool isExactType() const { return std::get_if<Type>(&value); }
+ bool isConeType() const { return std::get_if<ConeType>(&value); }
bool isMany() const { return std::get_if<Many>(&value); }
Literal getLiteral() const {
@@ -137,8 +152,9 @@ public:
// Return the relevant type here. Note that the *meaning* of the type varies
// by the contents: type $foo of a global means that type or any subtype, as a
- // subtype might be written to it, while type $foo of a Literal or an
- // ExactType means that type and nothing else; see hasExactType().
+ // subtype might be written to it, while type $foo of a Literal or a ConeType
+ // with depth zero means that type and nothing else, etc. (see also
+ // hasExactType).
//
// If no type is possible, return unreachable; if many types are, return none.
Type getType() const {
@@ -146,8 +162,8 @@ public:
return literal->type;
} else if (auto* global = std::get_if<GlobalInfo>(&value)) {
return global->type;
- } else if (auto* type = std::get_if<Type>(&value)) {
- return *type;
+ } else if (auto* coneType = std::get_if<ConeType>(&value)) {
+ return coneType->type;
} else if (std::get_if<None>(&value)) {
return Type::unreachable;
} else if (std::get_if<Many>(&value)) {
@@ -160,17 +176,23 @@ public:
// Returns whether the type we can report here is exact, that is, nothing of a
// strict subtype might show up - the contents here have an exact type.
//
- // This is different from isExactType() which checks if all we know about the
- // contents here is their exact type. Specifically, we may know both an exact
- // type and also more than just that, which is the case with a Literal.
- //
// This returns false for None and Many, for whom it is not well-defined.
- bool hasExactType() const { return isExactType() || isLiteral(); }
+ bool hasExactType() const {
+ if (isLiteral()) {
+ return true;
+ }
+
+ if (auto* coneType = std::get_if<ConeType>(&value)) {
+ return coneType->depth == 0;
+ }
+
+ return false;
+ }
// Returns whether the given contents have any intersection, that is, whether
// some value exists that can appear in both |a| and |b|. For example, if
- // either is None, or if they are both ExactTypes but of different types, then
- // they have no intersection.
+ // either is None, or if they are different literals, then they have no
+ // intersection.
static bool haveIntersection(const PossibleContents& a,
const PossibleContents& b);
@@ -199,8 +221,10 @@ public:
return size_t(1) | (std::hash<Literal>()(getLiteral()) << 3);
} else if (isGlobal()) {
return size_t(2) | (std::hash<Name>()(getGlobal()) << 3);
- } else if (isExactType()) {
- return size_t(3) | (std::hash<Type>()(getType()) << 3);
+ } else if (auto* coneType = std::get_if<ConeType>(&value)) {
+ return size_t(3) | ((std::hash<std::pair<Type, Index>>{}(
+ {coneType->type, coneType->depth}))
+ << 3);
} else if (isMany()) {
return 4;
} else {
@@ -221,9 +245,14 @@ public:
}
} else if (isGlobal()) {
o << "GlobalInfo $" << getGlobal();
- } else if (isExactType()) {
- o << "ExactType " << getType();
- auto t = getType();
+ } else if (auto* coneType = std::get_if<ConeType>(&value)) {
+ auto t = coneType->type;
+ o << "ConeType " << t;
+ if (coneType->depth == 0) {
+ o << " exact";
+ } else {
+ o << " depth=" << coneType->depth;
+ }
if (t.isRef()) {
auto h = t.getHeapType();
o << " HT: " << h;
@@ -513,7 +542,7 @@ namespace wasm {
// constant must be there (2, above), and so we do not make an effort to track
// non-reference types here. This makes the internals of ContentOracle simpler
// and faster. A noticeable outcome of that is that querying the contents of an
-// i32 local will return Many and not ExactType{i32} (assuming we could not
+// i32 local will return Many and not ConeType{i32, 0} (assuming we could not
// infer either that there must be nothing there, or a constant). Again, the
// caller is assumed to know the wasm IR type anyhow, and also other
// optimization passes work on the types in the IR, so we do not focus on that