diff options
Diffstat (limited to 'src/ir/possible-contents.h')
-rw-r--r-- | src/ir/possible-contents.h | 83 |
1 files changed, 56 insertions, 27 deletions
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 |