Skip to content

[AST] Perf: Make generic decl types share field offsets and fast casting logic #13663

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions include/swift/AST/TypeNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,22 @@ ABSTRACT_TYPE(ReferenceStorage, Type)
ARTIFICIAL_TYPE(UnmanagedStorage, ReferenceStorageType)
ARTIFICIAL_TYPE(WeakStorage, ReferenceStorageType)
TYPE_RANGE(ReferenceStorage, UnownedStorage, WeakStorage)
ABSTRACT_TYPE(Nominal, Type)
TYPE(Enum, NominalType)
TYPE(Struct, NominalType)
TYPE(Class, NominalType)
TYPE(Protocol, NominalType)
TYPE_RANGE(Nominal, Enum, Protocol)
ABSTRACT_TYPE(AnyGeneric, Type)
ABSTRACT_TYPE(NominalOrBoundGenericNominal, Type)
ABSTRACT_TYPE(Nominal, Type)
TYPE(Enum, NominalType)
TYPE(Struct, NominalType)
TYPE(Class, NominalType)
TYPE(Protocol, NominalType)
TYPE_RANGE(Nominal, Enum, Protocol)
ABSTRACT_TYPE(BoundGeneric, Type)
TYPE(BoundGenericClass, BoundGenericType)
TYPE(BoundGenericEnum, BoundGenericType)
TYPE(BoundGenericStruct, BoundGenericType)
TYPE_RANGE(BoundGeneric, BoundGenericClass, BoundGenericStruct)
TYPE_RANGE(NominalOrBoundGenericNominal, Enum, BoundGenericStruct)
UNCHECKED_TYPE(UnboundGeneric, Type)
TYPE_RANGE(AnyGeneric, Enum, UnboundGeneric)
ABSTRACT_TYPE(AnyMetatype, Type)
TYPE(Metatype, AnyMetatypeType)
TYPE(ExistentialMetatype, AnyMetatypeType)
Expand Down Expand Up @@ -140,12 +150,6 @@ SUGARED_TYPE(Dictionary, Type)
TYPE(ProtocolComposition, Type)
TYPE(LValue, Type)
TYPE(InOut, Type)
UNCHECKED_TYPE(UnboundGeneric, Type)
ABSTRACT_TYPE(BoundGeneric, Type)
TYPE(BoundGenericClass, BoundGenericType)
TYPE(BoundGenericEnum, BoundGenericType)
TYPE(BoundGenericStruct, BoundGenericType)
TYPE_RANGE(BoundGeneric, BoundGenericClass, BoundGenericStruct)
UNCHECKED_TYPE(TypeVariable, Type)
LAST_TYPE(TypeVariable)

Expand Down
181 changes: 89 additions & 92 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,76 @@ class alignas(1 << TypeAlignInBits) TypeBase {
void *operator new(size_t Bytes, void *Mem) throw() { return Mem; }
};

/// AnyGenericType - This abstract class helps types ensure that fields
/// exist at the same offset in memory to improve code generation of the
/// compiler itself.
class AnyGenericType : public TypeBase {
friend class NominalOrBoundGenericNominalType;

/// TheDecl - This is the TypeDecl which declares the given type. It
/// specifies the name and other useful information about this type.
union {
GenericTypeDecl *GenDecl;
NominalTypeDecl *NomDecl;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a clever bit of aliasing here... but it'll work fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Ya, the union is gross. I tried using templates, but that fought with the CanType macros. I wasn't sure what was more important. It should be polished at some point.


/// \brief The type of the parent, in which this type is nested.
Type Parent;

template <typename... Args>
AnyGenericType(NominalTypeDecl *TheDecl, Type Parent, Args &&...args)
: TypeBase(std::forward<Args>(args)...), NomDecl(TheDecl), Parent(Parent) {}

protected:
template <typename... Args>
AnyGenericType(GenericTypeDecl *TheDecl, Type Parent, Args &&...args)
: TypeBase(std::forward<Args>(args)...), GenDecl(TheDecl), Parent(Parent) {}

public:

/// \brief Returns the declaration that declares this type.
GenericTypeDecl *getDecl() const { return GenDecl; }

/// \brief Returns the type of the parent of this type. This will
/// be null for top-level types or local types, and for non-generic types
/// will simply be the same as the declared type of the declaration context
/// of TheDecl. For types nested within generic types, however, this will
/// involve \c BoundGenericType nodes that provide context for the nested
/// type, e.g., the type Dictionary<String, Int>.ItemRange would be
/// represented as a NominalType with Dictionary<String, Int> as its parent
/// type.
Type getParent() const { return Parent; }

// Implement isa/cast/dyncast/etc.
static bool classof(const TypeBase *T) {
return T->getKind() >= TypeKind::First_AnyGenericType &&
T->getKind() <= TypeKind::Last_AnyGenericType;
}
};
BEGIN_CAN_TYPE_WRAPPER(AnyGenericType, Type)
PROXY_CAN_TYPE_SIMPLE_GETTER(getParent)
END_CAN_TYPE_WRAPPER(AnyGenericType, Type)

/// NominalOrBoundGenericNominal - This abstract class helps types ensure that
/// fields exist at the same offset in memory to improve code generation of the
/// compiler itself.
class NominalOrBoundGenericNominalType : public AnyGenericType {
public:
template <typename... Args>
NominalOrBoundGenericNominalType(Args &&...args)
: AnyGenericType(std::forward<Args>(args)...) {}

/// \brief Returns the declaration that declares this type.
NominalTypeDecl *getDecl() const { return NomDecl; }

// Implement isa/cast/dyncast/etc.
static bool classof(const TypeBase *T) {
return T->getKind() >= TypeKind::First_NominalOrBoundGenericNominalType &&
T->getKind() <= TypeKind::Last_NominalOrBoundGenericNominalType;
}
};
DEFINE_EMPTY_CAN_TYPE_WRAPPER(NominalOrBoundGenericNominalType, AnyGenericType)

/// ErrorType - This represents a type that was erroneously constructed. This
/// is produced when parsing types and when name binding type aliases, and is
/// installed in declaration that use these erroneous types. All uses of a
Expand Down Expand Up @@ -1679,37 +1749,19 @@ END_CAN_TYPE_WRAPPER(TupleType, Type)

/// UnboundGenericType - Represents a generic type where the type arguments have
/// not yet been resolved.
class UnboundGenericType : public TypeBase, public llvm::FoldingSetNode {
GenericTypeDecl *TheDecl;

/// \brief The type of the parent, in which this type is nested.
Type Parent;

class UnboundGenericType : public AnyGenericType,
public llvm::FoldingSetNode {
private:
UnboundGenericType(GenericTypeDecl *TheDecl, Type Parent, const ASTContext &C,
RecursiveTypeProperties properties)
: TypeBase(TypeKind::UnboundGeneric,
(!Parent || Parent->isCanonical())? &C : nullptr,
properties | RecursiveTypeProperties::HasUnboundGeneric),
TheDecl(TheDecl), Parent(Parent) { }
: AnyGenericType(TheDecl, Parent, TypeKind::UnboundGeneric,
(!Parent || Parent->isCanonical()) ? &C : nullptr,
properties | RecursiveTypeProperties::HasUnboundGeneric) {}

public:
static UnboundGenericType* get(GenericTypeDecl *TheDecl, Type Parent,
const ASTContext &C);

/// \brief Returns the declaration that declares this type.
GenericTypeDecl *getDecl() const { return TheDecl; }

/// \brief Returns the type of the parent of this type. This will
/// be null for top-level types or local types, and for non-generic types
/// will simply be the same as the declared type of the declaration context
/// of TheDecl. For types nested within generic types, however, this will
/// involve \c BoundGenericType nodes that provide context for the nested
/// type, e.g., the bound type Dictionary<String, Int>.Inner would be
/// represented as an UnboundGenericType with Dictionary<String, Int> as its
/// parent type.
Type getParent() const { return Parent; }

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getDecl(), getParent());
}
Expand All @@ -1721,20 +1773,15 @@ class UnboundGenericType : public TypeBase, public llvm::FoldingSetNode {
return T->getKind() == TypeKind::UnboundGeneric;
}
};
BEGIN_CAN_TYPE_WRAPPER(UnboundGenericType, Type)
PROXY_CAN_TYPE_SIMPLE_GETTER(getParent)
END_CAN_TYPE_WRAPPER(UnboundGenericType, Type)
DEFINE_EMPTY_CAN_TYPE_WRAPPER(UnboundGenericType, AnyGenericType)

inline CanType getAsCanType(const Type &type) { return CanType(type); }
typedef ArrayRefView<Type,CanType,getAsCanType> CanTypeArrayRef;

/// BoundGenericType - An abstract class for applying a generic type to the
/// given type arguments.
class BoundGenericType : public TypeBase, public llvm::FoldingSetNode {
NominalTypeDecl *TheDecl;

/// \brief The type of the parent, in which this type is nested.
Type Parent;
class BoundGenericType : public NominalOrBoundGenericNominalType,
public llvm::FoldingSetNode {

/// Retrieve the intrusive pointer storage from the subtype
const Type *getTrailingObjectsPointer() const;
Expand All @@ -1752,26 +1799,13 @@ class BoundGenericType : public TypeBase, public llvm::FoldingSetNode {
static BoundGenericType* get(NominalTypeDecl *TheDecl, Type Parent,
ArrayRef<Type> GenericArgs);

/// \brief Returns the declaration that declares this type.
NominalTypeDecl *getDecl() const { return TheDecl; }

/// \brief Returns the type of the parent of this type. This will
/// be null for top-level types or local types, and for non-generic types
/// will simply be the same as the declared type of the declaration context
/// of TheDecl. For types nested within generic types, however, this will
/// involve \c BoundGenericType nodes that provide context for the nested
/// type, e.g., the bound type Dictionary<String, Int>.Inner<Int> would be
/// represented as a BoundGenericType with Dictionary<String, Int> as its
/// parent type.
Type getParent() const { return Parent; }

/// Retrieve the set of generic arguments provided at this level.
ArrayRef<Type> getGenericArgs() const {
return {getTrailingObjectsPointer(), Bits.BoundGenericType.GenericArgCount};
}

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, TheDecl, Parent, getGenericArgs());
Profile(ID, getDecl(), getParent(), getGenericArgs());
}
static void Profile(llvm::FoldingSetNodeID &ID, NominalTypeDecl *TheDecl,
Type Parent, ArrayRef<Type> GenericArgs);
Expand All @@ -1782,12 +1816,11 @@ class BoundGenericType : public TypeBase, public llvm::FoldingSetNode {
T->getKind() <= TypeKind::Last_BoundGenericType;
}
};
BEGIN_CAN_TYPE_WRAPPER(BoundGenericType, Type)
PROXY_CAN_TYPE_SIMPLE_GETTER(getParent)
BEGIN_CAN_TYPE_WRAPPER(BoundGenericType, NominalOrBoundGenericNominalType)
CanTypeArrayRef getGenericArgs() const {
return CanTypeArrayRef(getPointer()->getGenericArgs());
}
END_CAN_TYPE_WRAPPER(BoundGenericType, Type)
END_CAN_TYPE_WRAPPER(BoundGenericType, NominalOrBoundGenericNominalType)


/// BoundGenericClassType - A subclass of BoundGenericType for the case
Expand Down Expand Up @@ -1895,46 +1928,24 @@ DEFINE_EMPTY_CAN_TYPE_WRAPPER(BoundGenericStructType, BoundGenericType)
/// NominalType - Represents a type with a name that is significant, such that
/// the name distinguishes it from other structurally-similar types that have
/// different names. Nominal types are always canonical.
class NominalType : public TypeBase {
/// TheDecl - This is the TypeDecl which declares the given type. It
/// specifies the name and other useful information about this type.
NominalTypeDecl * const TheDecl;

/// \brief The type of the parent, in which this type is nested.
Type Parent;
class NominalType : public NominalOrBoundGenericNominalType {

protected:
NominalType(TypeKind K, const ASTContext *C, NominalTypeDecl *TheDecl,
Type Parent, RecursiveTypeProperties properties)
: TypeBase(K, (!Parent || Parent->isCanonical())? C : nullptr,
properties),
TheDecl(TheDecl), Parent(Parent) { }
: NominalOrBoundGenericNominalType(TheDecl, Parent, K,
(!Parent || Parent->isCanonical())? C : nullptr, properties) {}

public:
static NominalType *get(NominalTypeDecl *D, Type Parent, const ASTContext &C);

/// \brief Returns the declaration that declares this type.
NominalTypeDecl *getDecl() const { return TheDecl; }

/// \brief Returns the type of the parent of this type. This will
/// be null for top-level types or local types, and for non-generic types
/// will simply be the same as the declared type of the declaration context
/// of TheDecl. For types nested within generic types, however, this will
/// involve \c BoundGenericType nodes that provide context for the nested
/// type, e.g., the type Dictionary<String, Int>.ItemRange would be
/// represented as a NominalType with Dictionary<String, Int> as its parent
/// type.
Type getParent() const { return Parent; }

// Implement isa/cast/dyncast/etc.
static bool classof(const TypeBase *T) {
return T->getKind() >= TypeKind::First_NominalType &&
T->getKind() <= TypeKind::Last_NominalType;
}
};
BEGIN_CAN_TYPE_WRAPPER(NominalType, Type)
PROXY_CAN_TYPE_SIMPLE_GETTER(getParent)
END_CAN_TYPE_WRAPPER(NominalType, Type)
DEFINE_EMPTY_CAN_TYPE_WRAPPER(NominalType, NominalOrBoundGenericNominalType)

/// EnumType - This represents the type declared by an EnumDecl.
class EnumType : public NominalType, public llvm::FoldingSetNode {
Expand Down Expand Up @@ -4964,12 +4975,8 @@ inline NominalTypeDecl *TypeBase::getNominalOrBoundGenericNominal() {
}

inline NominalTypeDecl *CanType::getNominalOrBoundGenericNominal() const {
if (auto nomTy = dyn_cast<NominalType>(*this))
return nomTy->getDecl();

if (auto boundTy = dyn_cast<BoundGenericType>(*this))
return boundTy->getDecl();

if (auto Ty = dyn_cast<NominalOrBoundGenericNominalType>(*this))
return Ty->getDecl();
return nullptr;
}

Expand All @@ -4978,13 +4985,7 @@ inline NominalTypeDecl *TypeBase::getAnyNominal() {
}

inline Type TypeBase::getNominalParent() {
if (auto classType = getAs<NominalType>()) {
return classType->getParent();
} else if (auto unboundType = getAs<UnboundGenericType>()) {
return unboundType->getParent();
} else {
return castTo<BoundGenericType>()->getParent();
}
return castTo<AnyGenericType>()->getParent();
}

inline GenericTypeDecl *TypeBase::getAnyGeneric() {
Expand Down Expand Up @@ -5053,11 +5054,7 @@ inline CanType CanType::getWithoutSpecifierTypeImpl(CanType type) {
}

inline CanType CanType::getNominalParent() const {
if (auto classType = dyn_cast<NominalType>(*this)) {
return classType.getParent();
} else {
return cast<BoundGenericType>(*this).getParent();
}
return cast<NominalOrBoundGenericNominalType>(*this).getParent();
}

inline Type TupleTypeElt::getVarargBaseTy() const {
Expand Down
5 changes: 2 additions & 3 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3243,9 +3243,8 @@ BoundGenericType::BoundGenericType(TypeKind theKind,
ArrayRef<Type> genericArgs,
const ASTContext *context,
RecursiveTypeProperties properties)
: TypeBase(theKind, context, properties),
TheDecl(theDecl), Parent(parent)
{
: NominalOrBoundGenericNominalType(theDecl, parent, theKind, context,
properties) {
Bits.BoundGenericType.GenericArgCount = genericArgs.size();
// Subtypes are required to provide storage for the generic arguments
std::uninitialized_copy(genericArgs.begin(), genericArgs.end(),
Expand Down
11 changes: 2 additions & 9 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,11 @@ NominalTypeDecl *CanType::getAnyNominal() const {
}

GenericTypeDecl *CanType::getAnyGeneric() const {
if (auto nominalTy = dyn_cast<NominalType>(*this))
return (GenericTypeDecl*)nominalTy->getDecl();

if (auto boundTy = dyn_cast<BoundGenericType>(*this))
return (GenericTypeDecl*)boundTy->getDecl();

if (auto unboundTy = dyn_cast<UnboundGenericType>(*this))
return unboundTy->getDecl();
if (auto Ty = dyn_cast<AnyGenericType>(*this))
return Ty->getDecl();
return nullptr;
}


//===----------------------------------------------------------------------===//
// Various Type Methods.
//===----------------------------------------------------------------------===//
Expand Down