summaryrefslogtreecommitdiff
path: root/src/wasm-type.h
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-09-16 15:37:43 -0700
committerGitHub <noreply@github.com>2024-09-16 15:37:43 -0700
commited19e3f699ddb72d59f227a9f20846c9ce79e2c6 (patch)
treec88e7be8f2672e6569f74c118dff12b201345c8e /src/wasm-type.h
parent106f84b4f2dc373b540ace29139f850576f22b8a (diff)
downloadbinaryen-ed19e3f699ddb72d59f227a9f20846c9ce79e2c6.tar.gz
binaryen-ed19e3f699ddb72d59f227a9f20846c9ce79e2c6.tar.bz2
binaryen-ed19e3f699ddb72d59f227a9f20846c9ce79e2c6.zip
[NFC] Move enough of wasm-type.cpp into wasm-type.h to inline core is*() methods (#6936)
This just moves code around. As a result, isRef() vanishes entirely from the profiling traces in #6931, since now the core isRef/Tuple/etc. methods are all inlineable. This also required some reordering of wasm-type.h, namely to move HeapType up front. No changes to that class otherwise. TypeInfo is now in the header. getTypeInfo is now a static method on Type. This has the downside of moving internal details into the header, and it may increase compile time a little. The upside is making the --precompute benchmark from #6931 significantly faster, 33%, and it will also help the many Type::isNonNullable() etc. calls we have scattered around the codebase in other passes too.
Diffstat (limited to 'src/wasm-type.h')
-rw-r--r--src/wasm-type.h479
1 files changed, 288 insertions, 191 deletions
diff --git a/src/wasm-type.h b/src/wasm-type.h
index 03c4f8e77..5b2074e1c 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -78,6 +78,227 @@ using HeapTypeNameGenerator = std::function<TypeNames(HeapType)>;
// HeapType.
using TypeID = uint64_t;
+enum Shareability { Shared, Unshared };
+
+enum class HeapTypeKind {
+ Basic,
+ Func,
+ Struct,
+ Array,
+ Cont,
+};
+
+class HeapType {
+ // Unlike `Type`, which represents the types of values on the WebAssembly
+ // stack, `HeapType` is used to describe the structures that reference types
+ // refer to. HeapTypes are canonicalized and interned exactly like Types and
+ // should also be passed by value.
+ uintptr_t id;
+
+public:
+ // Bit zero indicates whether the type is `shared`, so we need to leave it
+ // free.
+ enum BasicHeapType : uint32_t {
+ ext = 0 << 1,
+ func = 1 << 1,
+ cont = 2 << 1,
+ any = 3 << 1,
+ eq = 4 << 1,
+ i31 = 5 << 1,
+ struct_ = 6 << 1,
+ array = 7 << 1,
+ exn = 8 << 1,
+ string = 9 << 1,
+ none = 10 << 1,
+ noext = 11 << 1,
+ nofunc = 12 << 1,
+ nocont = 13 << 1,
+ noexn = 14 << 1,
+ };
+ static constexpr BasicHeapType _last_basic_type = BasicHeapType(noexn + 1);
+
+ // BasicHeapType can be implicitly upgraded to HeapType
+ constexpr HeapType(BasicHeapType id) : id(id) {}
+
+ // But converting raw TypeID is more dangerous, so make it explicit
+ explicit HeapType(TypeID id) : id(id) {}
+
+ // Choose an arbitrary heap type as the default.
+ constexpr HeapType() : HeapType(func) {}
+
+ // Construct a HeapType referring to the single canonical HeapType for the
+ // given signature. In nominal mode, this is the first HeapType created with
+ // this signature.
+ HeapType(Signature signature);
+
+ HeapType(Continuation cont);
+
+ // Create a HeapType with the given structure. In equirecursive mode, this may
+ // be the same as a previous HeapType created with the same contents. In
+ // nominal mode, this will be a fresh type distinct from all previously
+ // created HeapTypes.
+ // TODO: make these explicit to differentiate them.
+ HeapType(const Struct& struct_);
+ HeapType(Struct&& struct_);
+ HeapType(Array array);
+
+ HeapTypeKind getKind() const;
+
+ constexpr bool isBasic() const { return id <= _last_basic_type; }
+ bool isFunction() const {
+ return isMaybeShared(func) || getKind() == HeapTypeKind::Func;
+ }
+ bool isData() const {
+ auto kind = getKind();
+ return isMaybeShared(string) || kind == HeapTypeKind::Struct ||
+ kind == HeapTypeKind::Array;
+ }
+ bool isSignature() const { return getKind() == HeapTypeKind::Func; }
+ bool isContinuation() const { return getKind() == HeapTypeKind::Cont; }
+ bool isStruct() const { return getKind() == HeapTypeKind::Struct; }
+ bool isArray() const { return getKind() == HeapTypeKind::Array; }
+ bool isExn() const { return isMaybeShared(HeapType::exn); }
+ bool isString() const { return isMaybeShared(HeapType::string); }
+ bool isBottom() const;
+ bool isOpen() const;
+ bool isShared() const { return getShared() == Shared; }
+
+ Shareability getShared() const;
+
+ // Check if the type is a given basic heap type, while ignoring whether it is
+ // shared or not.
+ bool isMaybeShared(BasicHeapType type) const {
+ return isBasic() && getBasic(Unshared) == type;
+ }
+
+ Signature getSignature() const;
+ Continuation getContinuation() const;
+
+ const Struct& getStruct() const;
+ Array getArray() const;
+
+ // If there is a nontrivial (i.e. non-basic, one that was declared by the
+ // module) nominal supertype, return it, else an empty optional.
+ std::optional<HeapType> getDeclaredSuperType() const;
+
+ // As |getDeclaredSuperType|, but also handles basic types, that is, if the
+ // super is a basic type, then we return it here. Declared types are returned
+ // as well, just like |getDeclaredSuperType|.
+ std::optional<HeapType> getSuperType() const;
+
+ // Return the depth of this heap type in the nominal type hierarchy, i.e. the
+ // number of supertypes in its supertype chain.
+ size_t getDepth() const;
+
+ // Get the bottom heap type for this heap type's hierarchy.
+ BasicHeapType getUnsharedBottom() const;
+ BasicHeapType getBottom() const {
+ return HeapType(getUnsharedBottom()).getBasic(getShared());
+ }
+
+ // Get the top heap type for this heap type's hierarchy.
+ BasicHeapType getUnsharedTop() const;
+ BasicHeapType getTop() const {
+ return HeapType(getUnsharedTop()).getBasic(getShared());
+ }
+
+ // Get the recursion group for this non-basic type.
+ RecGroup getRecGroup() const;
+
+ // Get the index of this non-basic type within its recursion group.
+ size_t getRecGroupIndex() const;
+
+ constexpr TypeID getID() const { return id; }
+
+ // Get the shared or unshared version of this basic heap type.
+ constexpr BasicHeapType getBasic(Shareability share) const {
+ assert(isBasic());
+ return BasicHeapType(share == Shared ? (id | 1) : (id & ~1));
+ }
+
+ // (In)equality must be defined for both HeapType and BasicHeapType because it
+ // is otherwise ambiguous whether to convert both this and other to int or
+ // convert other to HeapType.
+ bool operator==(const HeapType& other) const { return id == other.id; }
+ bool operator==(const BasicHeapType& other) const { return id == other; }
+ bool operator!=(const HeapType& other) const { return id != other.id; }
+ bool operator!=(const BasicHeapType& other) const { return id != other; }
+
+ // Returns true if left is a subtype of right. Subtype includes itself.
+ static bool isSubType(HeapType left, HeapType right);
+
+ std::vector<Type> getTypeChildren() const;
+
+ // Return the ordered HeapType children, looking through child Types.
+ std::vector<HeapType> getHeapTypeChildren() const;
+
+ // Similar to `getHeapTypeChildren`, but also includes the supertype if it
+ // exists.
+ std::vector<HeapType> getReferencedHeapTypes() const;
+
+ // Return the LUB of two HeapTypes, which may or may not exist.
+ static std::optional<HeapType> getLeastUpperBound(HeapType a, HeapType b);
+
+ // Returns the feature set required to use this type.
+ FeatureSet getFeatures() const;
+
+ // Helper allowing the value of `print(...)` to be sent to an ostream. Stores
+ // a `TypeID` because `Type` is incomplete at this point and using a reference
+ // makes it less convenient to use.
+ struct Printed {
+ TypeID typeID;
+ HeapTypeNameGenerator generateName;
+ };
+
+ // Given a function for generating HeapType names, print the definition of
+ // this HeapType to `os`. `generateName` should return the same
+ // name each time it is called with the same HeapType and it should return
+ // different names for different types.
+ Printed print(HeapTypeNameGenerator generateName) {
+ return Printed{getID(), generateName};
+ }
+
+ std::string toString() const;
+};
+
+// Internal only.
+struct TypeInfo {
+ using type_t = Type;
+ // Used in assertions to ensure that temporary types don't leak into the
+ // global store.
+ bool isTemp = false;
+ enum Kind {
+ TupleKind,
+ RefKind,
+ } kind;
+ struct Ref {
+ HeapType heapType;
+ Nullability nullability;
+ };
+ union {
+ Tuple tuple;
+ Ref ref;
+ };
+
+ TypeInfo(const Tuple& tuple) : kind(TupleKind), tuple(tuple) {}
+ TypeInfo(Tuple&& tuple) : kind(TupleKind), tuple(std::move(tuple)) {}
+ TypeInfo(HeapType heapType, Nullability nullable)
+ : kind(RefKind), ref{heapType, nullable} {}
+ TypeInfo(const TypeInfo& other);
+ ~TypeInfo();
+
+ constexpr bool isTuple() const { return kind == TupleKind; }
+ constexpr bool isRef() const { return kind == RefKind; }
+
+ // If this TypeInfo represents a Type that can be represented more simply,
+ // return that simpler Type. For example, this handles eliminating singleton
+ // tuple types.
+ std::optional<Type> getCanonical() const;
+
+ bool operator==(const TypeInfo& other) const;
+ bool operator!=(const TypeInfo& other) const { return !(*this == other); }
+};
+
class Type {
// The `id` uniquely represents each type, so type equality is just a
// comparison of the ids. For basic types the `id` is just the `BasicType`
@@ -150,24 +371,72 @@ public:
constexpr bool isFloat() const { return id == f32 || id == f64; }
constexpr bool isVector() const { return id == v128; };
constexpr bool isNumber() const { return id >= i32 && id <= v128; }
- bool isTuple() const;
bool isSingle() const { return isConcrete() && !isTuple(); }
- bool isRef() const;
- bool isFunction() const;
- // See literal.h.
- bool isData() const;
+
+ // Tuples, refs, etc. are quickly handled using isBasic(), leaving the non-
+ // basic case for the underlying implementation.
+
+ bool isTuple() const {
+ if (isBasic()) {
+ return false;
+ } else {
+ return getTypeInfo(*this)->isTuple();
+ }
+ }
+
+ bool isRef() const {
+ if (isBasic()) {
+ return false;
+ } else {
+ return getTypeInfo(*this)->isRef();
+ }
+ }
+
+ bool isFunction() const {
+ if (isBasic()) {
+ return false;
+ } else {
+ auto* info = getTypeInfo(*this);
+ return info->isRef() && info->ref.heapType.isFunction();
+ }
+ }
+
+ bool isData() const {
+ if (isBasic()) {
+ return false;
+ } else {
+ auto* info = getTypeInfo(*this);
+ return info->isRef() && info->ref.heapType.isData();
+ }
+ }
+
// Checks whether a type is a reference and is nullable. This returns false
// for a value that is not a reference, that is, for which nullability is
// irrelevant.
- bool isNullable() const;
+ bool isNullable() const {
+ if (isRef()) {
+ return getTypeInfo(*this)->ref.nullability == Nullable;
+ } else {
+ return false;
+ }
+ }
+
// Checks whether a type is a reference and is non-nullable. This returns
// false for a value that is not a reference, that is, for which nullability
// is irrelevant. (For that reason, this is only the negation of isNullable()
// on references, but both return false on non-references.)
- bool isNonNullable() const;
+ bool isNonNullable() const {
+ if (isRef()) {
+ return getTypeInfo(*this)->ref.nullability == NonNullable;
+ } else {
+ return false;
+ }
+ }
+
+ bool isSignature() const { return isRef() && getHeapType().isSignature(); }
+
// Whether this type is only inhabited by null values.
bool isNull() const;
- bool isSignature() const;
bool isStruct() const;
bool isArray() const;
bool isExn() const;
@@ -222,11 +491,17 @@ public:
// Returns the tuple, assuming that this is a tuple type. Note that it is
// normally simpler to use operator[] and size() on the Type directly.
- const Tuple& getTuple() const;
+ HeapType getHeapType() const {
+ assert(isRef());
+ return getTypeInfo(*this)->ref.heapType;
+ }
// Gets the heap type corresponding to this type, assuming that it is a
// reference type.
- HeapType getHeapType() const;
+ const Tuple& getTuple() const {
+ assert(isTuple());
+ return getTypeInfo(*this)->tuple;
+ }
// Returns a number type based on its size in bytes and whether it is a float
// type.
@@ -308,189 +583,11 @@ public:
return std::make_reverse_iterator(begin());
}
const Type& operator[](size_t i) const { return *Iterator{{this, i}}; }
-};
-
-enum Shareability { Shared, Unshared };
-
-enum class HeapTypeKind {
- Basic,
- Func,
- Struct,
- Array,
- Cont,
-};
-
-class HeapType {
- // Unlike `Type`, which represents the types of values on the WebAssembly
- // stack, `HeapType` is used to describe the structures that reference types
- // refer to. HeapTypes are canonicalized and interned exactly like Types and
- // should also be passed by value.
- uintptr_t id;
-
-public:
- // Bit zero indicates whether the type is `shared`, so we need to leave it
- // free.
- enum BasicHeapType : uint32_t {
- ext = 0 << 1,
- func = 1 << 1,
- cont = 2 << 1,
- any = 3 << 1,
- eq = 4 << 1,
- i31 = 5 << 1,
- struct_ = 6 << 1,
- array = 7 << 1,
- exn = 8 << 1,
- string = 9 << 1,
- none = 10 << 1,
- noext = 11 << 1,
- nofunc = 12 << 1,
- nocont = 13 << 1,
- noexn = 14 << 1,
- };
- static constexpr BasicHeapType _last_basic_type = BasicHeapType(noexn + 1);
-
- // BasicHeapType can be implicitly upgraded to HeapType
- constexpr HeapType(BasicHeapType id) : id(id) {}
-
- // But converting raw TypeID is more dangerous, so make it explicit
- explicit HeapType(TypeID id) : id(id) {}
-
- // Choose an arbitrary heap type as the default.
- constexpr HeapType() : HeapType(func) {}
-
- // Construct a HeapType referring to the single canonical HeapType for the
- // given signature. In nominal mode, this is the first HeapType created with
- // this signature.
- HeapType(Signature signature);
-
- HeapType(Continuation cont);
-
- // Create a HeapType with the given structure. In equirecursive mode, this may
- // be the same as a previous HeapType created with the same contents. In
- // nominal mode, this will be a fresh type distinct from all previously
- // created HeapTypes.
- // TODO: make these explicit to differentiate them.
- HeapType(const Struct& struct_);
- HeapType(Struct&& struct_);
- HeapType(Array array);
-
- HeapTypeKind getKind() const;
-
- constexpr bool isBasic() const { return id <= _last_basic_type; }
- bool isFunction() const {
- return isMaybeShared(func) || getKind() == HeapTypeKind::Func;
- }
- bool isData() const {
- auto kind = getKind();
- return isMaybeShared(string) || kind == HeapTypeKind::Struct ||
- kind == HeapTypeKind::Array;
- }
- bool isSignature() const { return getKind() == HeapTypeKind::Func; }
- bool isContinuation() const { return getKind() == HeapTypeKind::Cont; }
- bool isStruct() const { return getKind() == HeapTypeKind::Struct; }
- bool isArray() const { return getKind() == HeapTypeKind::Array; }
- bool isExn() const { return isMaybeShared(HeapType::exn); }
- bool isString() const { return isMaybeShared(HeapType::string); }
- bool isBottom() const;
- bool isOpen() const;
- bool isShared() const { return getShared() == Shared; }
-
- Shareability getShared() const;
-
- // Check if the type is a given basic heap type, while ignoring whether it is
- // shared or not.
- bool isMaybeShared(BasicHeapType type) const {
- return isBasic() && getBasic(Unshared) == type;
- }
- Signature getSignature() const;
- Continuation getContinuation() const;
-
- const Struct& getStruct() const;
- Array getArray() const;
-
- // If there is a nontrivial (i.e. non-basic, one that was declared by the
- // module) nominal supertype, return it, else an empty optional.
- std::optional<HeapType> getDeclaredSuperType() const;
-
- // As |getDeclaredSuperType|, but also handles basic types, that is, if the
- // super is a basic type, then we return it here. Declared types are returned
- // as well, just like |getDeclaredSuperType|.
- std::optional<HeapType> getSuperType() const;
-
- // Return the depth of this heap type in the nominal type hierarchy, i.e. the
- // number of supertypes in its supertype chain.
- size_t getDepth() const;
-
- // Get the bottom heap type for this heap type's hierarchy.
- BasicHeapType getUnsharedBottom() const;
- BasicHeapType getBottom() const {
- return HeapType(getUnsharedBottom()).getBasic(getShared());
- }
-
- // Get the top heap type for this heap type's hierarchy.
- BasicHeapType getUnsharedTop() const;
- BasicHeapType getTop() const {
- return HeapType(getUnsharedTop()).getBasic(getShared());
- }
-
- // Get the recursion group for this non-basic type.
- RecGroup getRecGroup() const;
-
- // Get the index of this non-basic type within its recursion group.
- size_t getRecGroupIndex() const;
-
- constexpr TypeID getID() const { return id; }
-
- // Get the shared or unshared version of this basic heap type.
- constexpr BasicHeapType getBasic(Shareability share) const {
- assert(isBasic());
- return BasicHeapType(share == Shared ? (id | 1) : (id & ~1));
- }
-
- // (In)equality must be defined for both HeapType and BasicHeapType because it
- // is otherwise ambiguous whether to convert both this and other to int or
- // convert other to HeapType.
- bool operator==(const HeapType& other) const { return id == other.id; }
- bool operator==(const BasicHeapType& other) const { return id == other; }
- bool operator!=(const HeapType& other) const { return id != other.id; }
- bool operator!=(const BasicHeapType& other) const { return id != other; }
-
- // Returns true if left is a subtype of right. Subtype includes itself.
- static bool isSubType(HeapType left, HeapType right);
-
- std::vector<Type> getTypeChildren() const;
-
- // Return the ordered HeapType children, looking through child Types.
- std::vector<HeapType> getHeapTypeChildren() const;
-
- // Similar to `getHeapTypeChildren`, but also includes the supertype if it
- // exists.
- std::vector<HeapType> getReferencedHeapTypes() const;
-
- // Return the LUB of two HeapTypes, which may or may not exist.
- static std::optional<HeapType> getLeastUpperBound(HeapType a, HeapType b);
-
- // Returns the feature set required to use this type.
- FeatureSet getFeatures() const;
-
- // Helper allowing the value of `print(...)` to be sent to an ostream. Stores
- // a `TypeID` because `Type` is incomplete at this point and using a reference
- // makes it less convenient to use.
- struct Printed {
- TypeID typeID;
- HeapTypeNameGenerator generateName;
- };
-
- // Given a function for generating HeapType names, print the definition of
- // this HeapType to `os`. `generateName` should return the same
- // name each time it is called with the same HeapType and it should return
- // different names for different types.
- Printed print(HeapTypeNameGenerator generateName) {
- return Printed{getID(), generateName};
+ static TypeInfo* getTypeInfo(Type type) {
+ assert(!type.isBasic());
+ return (TypeInfo*)type.getID();
}
-
- std::string toString() const;
};
inline bool Type::isNull() const { return isRef() && getHeapType().isBottom(); }