Skip to content

Prepare SIL type dependencies for multi-definition instructions #62547

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 2 commits into from
Dec 13, 2022
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
59 changes: 55 additions & 4 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,14 @@ class SILInstruction : public llvm::ilist_node<SILInstruction> {
return getResultsImpl().getTypes();
}

/// Run the given function for each opened archetype this instruction
/// defines, passing the value that should be used to record the
/// dependency.
void forEachDefinedOpenedArchetype(
llvm::function_ref<void(CanOpenedArchetypeType archetype,
SILValue typeDependency)> function) const;
bool definesOpenedArchetypes() const;

MemoryBehavior getMemoryBehavior() const;
ReleasingBehavior getReleasingBehavior() const;

Expand Down Expand Up @@ -1096,10 +1104,6 @@ class SingleValueInstruction : public SILInstruction, public ValueBase {
node->getKind() <= SILNodeKind::Last_SingleValueInstruction;
}

/// If this is an instruction which "defines" a root opened archetype, it is
/// returned.
CanOpenedArchetypeType getDefinedOpenedArchetype() const;

SILInstruction *getPreviousInstruction() {
return SILInstruction::getPreviousInstruction();
}
Expand Down Expand Up @@ -7137,6 +7141,13 @@ class OpenExistentialAddrInst
}

OpenedExistentialAccess getAccessKind() const { return ForAccess; }

CanOpenedArchetypeType getDefinedOpenedArchetype() const {
const auto archetype = getOpenedArchetypeOf(getType().getASTType());
assert(archetype && archetype->isRoot() &&
"Type should be a root opened archetype");
return archetype;
}
};

/// Given an opaque value referring to an existential, "opens" the
Expand All @@ -7150,6 +7161,14 @@ class OpenExistentialValueInst
OpenExistentialValueInst(SILDebugLocation debugLoc, SILValue operand,
SILType selfTy,
ValueOwnershipKind forwardingOwnershipKind);

public:
CanOpenedArchetypeType getDefinedOpenedArchetype() const {
const auto archetype = getOpenedArchetypeOf(getType().getASTType());
assert(archetype && archetype->isRoot() &&
"Type should be a root opened archetype");
return archetype;
}
};

/// Given a class existential, "opens" the
Expand All @@ -7163,6 +7182,14 @@ class OpenExistentialRefInst
OpenExistentialRefInst(SILDebugLocation DebugLoc, SILValue Operand,
SILType Ty,
ValueOwnershipKind forwardingOwnershipKind);

public:
CanOpenedArchetypeType getDefinedOpenedArchetype() const {
const auto archetype = getOpenedArchetypeOf(getType().getASTType());
assert(archetype && archetype->isRoot() &&
"Type should be a root opened archetype");
return archetype;
}
};

/// Given an existential metatype,
Expand All @@ -7177,6 +7204,14 @@ class OpenExistentialMetatypeInst

OpenExistentialMetatypeInst(SILDebugLocation DebugLoc, SILValue operand,
SILType ty);

public:
CanOpenedArchetypeType getDefinedOpenedArchetype() const {
const auto archetype = getOpenedArchetypeOf(getType().getASTType());
assert(archetype && archetype->isRoot() &&
"Type should be a root opened archetype");
return archetype;
}
};

/// Given a boxed existential container,
Expand All @@ -7190,6 +7225,14 @@ class OpenExistentialBoxInst

OpenExistentialBoxInst(SILDebugLocation DebugLoc, SILValue operand,
SILType ty);

public:
CanOpenedArchetypeType getDefinedOpenedArchetype() const {
const auto archetype = getOpenedArchetypeOf(getType().getASTType());
assert(archetype && archetype->isRoot() &&
"Type should be a root opened archetype");
return archetype;
}
};

/// Given a boxed existential container, "opens" the existential by returning a
Expand All @@ -7203,6 +7246,14 @@ class OpenExistentialBoxValueInst
OpenExistentialBoxValueInst(SILDebugLocation DebugLoc, SILValue operand,
SILType ty,
ValueOwnershipKind forwardingOwnershipKind);

public:
CanOpenedArchetypeType getDefinedOpenedArchetype() const {
const auto archetype = getOpenedArchetypeOf(getType().getASTType());
assert(archetype && archetype->isRoot() &&
"Type should be a root opened archetype");
return archetype;
}
};

/// Given an address to an uninitialized buffer of
Expand Down
39 changes: 26 additions & 13 deletions lib/SIL/IR/SILInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1528,24 +1528,37 @@ const ValueBase *SILInstructionResultArray::back() const {
}

//===----------------------------------------------------------------------===//
// SingleValueInstruction
// Defined opened archetypes
//===----------------------------------------------------------------------===//

CanOpenedArchetypeType
SingleValueInstruction::getDefinedOpenedArchetype() const {
bool SILInstruction::definesOpenedArchetypes() const {
bool definesAny = false;
forEachDefinedOpenedArchetype([&](CanOpenedArchetypeType type,
SILValue dependency) {
definesAny = true;
});
return definesAny;
}

void SILInstruction::forEachDefinedOpenedArchetype(
llvm::function_ref<void(CanOpenedArchetypeType, SILValue)> fn) const {
switch (getKind()) {
case SILInstructionKind::OpenExistentialAddrInst:
case SILInstructionKind::OpenExistentialRefInst:
case SILInstructionKind::OpenExistentialBoxInst:
case SILInstructionKind::OpenExistentialBoxValueInst:
case SILInstructionKind::OpenExistentialMetatypeInst:
case SILInstructionKind::OpenExistentialValueInst: {
const auto Ty = getOpenedArchetypeOf(getType().getASTType());
assert(Ty && Ty->isRoot() && "Type should be a root opened archetype");
return Ty;
#define SINGLE_VALUE_SINGLE_OPEN(TYPE) \
case SILInstructionKind::TYPE: { \
auto I = cast<TYPE>(this); \
auto archetype = I->getDefinedOpenedArchetype(); \
assert(archetype); \
return fn(archetype, I); \
}
SINGLE_VALUE_SINGLE_OPEN(OpenExistentialAddrInst)
SINGLE_VALUE_SINGLE_OPEN(OpenExistentialRefInst)
SINGLE_VALUE_SINGLE_OPEN(OpenExistentialBoxInst)
SINGLE_VALUE_SINGLE_OPEN(OpenExistentialBoxValueInst)
SINGLE_VALUE_SINGLE_OPEN(OpenExistentialMetatypeInst)
SINGLE_VALUE_SINGLE_OPEN(OpenExistentialValueInst)
#undef SINGLE_VALUE_SINGLE_OPEN
default:
return CanOpenedArchetypeType();
return;
}
}

Expand Down
138 changes: 82 additions & 56 deletions lib/SIL/IR/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,70 +43,105 @@ static void *allocateTrailingInst(SILFunction &F, CountTypes... counts) {
alignof(Inst));
}

namespace {
class TypeDependentOperandCollector {
SmallVector<CanOpenedArchetypeType, 4> rootOpenedArchetypes;
bool hasDynamicSelf = false;
public:
void collect(CanType type);
void collect(SubstitutionMap subs);
void collect(SILType type) {
collect(type.getASTType());
}
template <class T>
void collect(ArrayRef<T> array) {
for (auto &elt: array)
collect(elt);
}

void collectAll() {}
template <class T, class... Ts>
void collectAll(T &&first, Ts &&...rest) {
collect(first);
collectAll(std::forward<Ts>(rest)...);
}

void addTo(SmallVectorImpl<SILValue> &typeDependentOperands,
SILFunction &f);
};

}

/// Collect root open archetypes from a given type into \p RootOpenedArchetypes.
/// \p RootOpenedArchetypes is being used as a set. We don't use a real set type
/// here for performance reasons.
static void collectDependentTypeInfo(
CanType Ty, SmallVectorImpl<CanOpenedArchetypeType> &RootOpenedArchetypes,
bool &hasDynamicSelf) {
if (!Ty)
void TypeDependentOperandCollector::collect(CanType type) {
if (!type)
return;
if (Ty->hasDynamicSelfType())
if (type->hasDynamicSelfType())
hasDynamicSelf = true;
if (!Ty->hasOpenedExistential())
if (!type->hasOpenedExistential())
return;
Ty.visit([&](CanType t) {
type.visit([&](CanType t) {
if (const auto opened = dyn_cast<OpenedArchetypeType>(t)) {
const auto root = opened.getRoot();

// Add this root opened archetype if it was not seen yet.
// We don't use a set here, because the number of open archetypes
// is usually very small and using a real set may introduce too
// much overhead.
if (std::find(RootOpenedArchetypes.begin(), RootOpenedArchetypes.end(),
root) == RootOpenedArchetypes.end())
RootOpenedArchetypes.push_back(root);
if (std::find(rootOpenedArchetypes.begin(), rootOpenedArchetypes.end(),
root) == rootOpenedArchetypes.end())
rootOpenedArchetypes.push_back(root);
}
});
}

/// Takes a set of root opened archetypes as input and produces a set of
/// references to their definitions.
static void buildTypeDependentOperands(
SmallVectorImpl<CanOpenedArchetypeType> &RootOpenedArchetypes,
bool hasDynamicSelf, SmallVectorImpl<SILValue> &TypeDependentOperands,
SILFunction &F) {
/// Collect type dependencies from the replacement types of a
/// substitution map.
void TypeDependentOperandCollector::collect(SubstitutionMap subs) {
for (Type replacement : subs.getReplacementTypes()) {
// Substitutions in SIL should really be canonical.
auto ReplTy = replacement->getCanonicalType();
collect(ReplTy);
}
}

for (const auto &archetype : RootOpenedArchetypes) {
/// Given that we've collected a set of type dependencies, add operands
/// for those dependencies to the given vector.
void TypeDependentOperandCollector::addTo(SmallVectorImpl<SILValue> &operands,
SILFunction &F) {
size_t firstArchetypeOperand = operands.size();
for (CanOpenedArchetypeType archetype : rootOpenedArchetypes) {
SILValue def = F.getModule().getRootOpenedArchetypeDef(archetype, &F);
assert(def->getFunction() == &F &&
"def of root opened archetype is in wrong function");
TypeDependentOperands.push_back(def);

// The archetypes in rootOpenedArchetypes have already been uniqued,
// but a single instruction can open multiple archetypes (e.g.
// open_pack_element), so we also unique the actual operand values.
// As above, we assume there are very few values in practice and so
// a linear scan is better than maintaining a set.
if (std::find(operands.begin() + firstArchetypeOperand, operands.end(),
def) == operands.end())
operands.push_back(def);
}
if (hasDynamicSelf)
TypeDependentOperands.push_back(F.getDynamicSelfMetadata());
operands.push_back(F.getDynamicSelfMetadata());
}

/// Collects all root opened archetypes from a type and a substitution list, and
/// forms a corresponding list of operands.
/// We need to know the number of root opened archetypes to estimate the number
/// of corresponding operands for the instruction being formed, because we need
/// to reserve enough memory for these operands.
template <class... Sources>
static void collectTypeDependentOperands(
SmallVectorImpl<SILValue> &TypeDependentOperands,
SILFunction &F,
CanType Ty,
SubstitutionMap subs = { }) {
SmallVector<CanOpenedArchetypeType, 4> RootOpenedArchetypes;
bool hasDynamicSelf = false;
collectDependentTypeInfo(Ty, RootOpenedArchetypes, hasDynamicSelf);
for (Type replacement : subs.getReplacementTypes()) {
// Substitutions in SIL should really be canonical.
auto ReplTy = replacement->getCanonicalType();
collectDependentTypeInfo(ReplTy, RootOpenedArchetypes, hasDynamicSelf);
}
buildTypeDependentOperands(RootOpenedArchetypes, hasDynamicSelf,
TypeDependentOperands, F);
SmallVectorImpl<SILValue> &typeDependentOperands,
SILFunction &F, Sources &&... sources) {
TypeDependentOperandCollector collector;
collector.collectAll(std::forward<Sources>(sources)...);
collector.addTo(typeDependentOperands, F);
}

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -284,10 +319,7 @@ AllocRefInst *AllocRefInst::create(SILDebugLocation Loc, SILFunction &F,
assert(!objc || ElementTypes.empty());
SmallVector<SILValue, 8> AllOperands(ElementCountOperands.begin(),
ElementCountOperands.end());
for (SILType ElemType : ElementTypes) {
collectTypeDependentOperands(AllOperands, F, ElemType.getASTType());
}
collectTypeDependentOperands(AllOperands, F, ObjectType.getASTType());
collectTypeDependentOperands(AllOperands, F, ElementTypes, ObjectType);
auto Size = totalSizeToAlloc<swift::Operand, SILType>(AllOperands.size(),
ElementTypes.size());
auto Buffer = F.getModule().allocateInst(Size, alignof(AllocRefInst));
Expand All @@ -304,10 +336,7 @@ AllocRefDynamicInst::create(SILDebugLocation DebugLoc, SILFunction &F,
SmallVector<SILValue, 8> AllOperands(ElementCountOperands.begin(),
ElementCountOperands.end());
AllOperands.push_back(metatypeOperand);
collectTypeDependentOperands(AllOperands, F, ty.getASTType());
for (SILType ElemType : ElementTypes) {
collectTypeDependentOperands(AllOperands, F, ElemType.getASTType());
}
collectTypeDependentOperands(AllOperands, F, ty, ElementTypes);
auto Size = totalSizeToAlloc<swift::Operand, SILType>(AllOperands.size(),
ElementTypes.size());
auto Buffer = F.getModule().allocateInst(Size, alignof(AllocRefDynamicInst));
Expand Down Expand Up @@ -1249,14 +1278,13 @@ UncheckedRefCastAddrInst *
UncheckedRefCastAddrInst::create(SILDebugLocation Loc, SILValue src,
CanType srcType, SILValue dest, CanType targetType, SILFunction &F) {
SILModule &Mod = F.getModule();
SmallVector<SILValue, 8> TypeDependentOperands;
collectTypeDependentOperands(TypeDependentOperands, F, srcType);
collectTypeDependentOperands(TypeDependentOperands, F, targetType);
SmallVector<SILValue, 4> allOperands;
collectTypeDependentOperands(allOperands, F, srcType, targetType);
unsigned size =
totalSizeToAlloc<swift::Operand>(2 + TypeDependentOperands.size());
totalSizeToAlloc<swift::Operand>(2 + allOperands.size());
void *Buffer = Mod.allocateInst(size, alignof(UncheckedRefCastAddrInst));
return ::new (Buffer) UncheckedRefCastAddrInst(Loc, src, srcType,
dest, targetType, TypeDependentOperands);
dest, targetType, allOperands);
}

UnconditionalCheckedCastAddrInst::UnconditionalCheckedCastAddrInst(
Expand All @@ -1269,14 +1297,13 @@ UnconditionalCheckedCastAddrInst *
UnconditionalCheckedCastAddrInst::create(SILDebugLocation Loc, SILValue src,
CanType srcType, SILValue dest, CanType targetType, SILFunction &F) {
SILModule &Mod = F.getModule();
SmallVector<SILValue, 8> TypeDependentOperands;
collectTypeDependentOperands(TypeDependentOperands, F, srcType);
collectTypeDependentOperands(TypeDependentOperands, F, targetType);
SmallVector<SILValue, 4> allOperands;
collectTypeDependentOperands(allOperands, F, srcType, targetType);
unsigned size =
totalSizeToAlloc<swift::Operand>(2 + TypeDependentOperands.size());
totalSizeToAlloc<swift::Operand>(2 + allOperands.size());
void *Buffer = Mod.allocateInst(size, alignof(UnconditionalCheckedCastAddrInst));
return ::new (Buffer) UnconditionalCheckedCastAddrInst(Loc, src, srcType,
dest, targetType, TypeDependentOperands);
dest, targetType, allOperands);
}

CheckedCastAddrBranchInst::CheckedCastAddrBranchInst(
Expand All @@ -1300,14 +1327,13 @@ CheckedCastAddrBranchInst::create(SILDebugLocation DebugLoc,
ProfileCounter Target1Count, ProfileCounter Target2Count,
SILFunction &F) {
SILModule &Mod = F.getModule();
SmallVector<SILValue, 8> TypeDependentOperands;
collectTypeDependentOperands(TypeDependentOperands, F, srcType);
collectTypeDependentOperands(TypeDependentOperands, F, targetType);
SmallVector<SILValue, 4> allOperands;
collectTypeDependentOperands(allOperands, F, srcType, targetType);
unsigned size =
totalSizeToAlloc<swift::Operand>(2 + TypeDependentOperands.size());
totalSizeToAlloc<swift::Operand>(2 + allOperands.size());
void *Buffer = Mod.allocateInst(size, alignof(CheckedCastAddrBranchInst));
return ::new (Buffer) CheckedCastAddrBranchInst(DebugLoc, consumptionKind,
src, srcType, dest, targetType, TypeDependentOperands,
src, srcType, dest, targetType, allOperands,
successBB, failureBB, Target1Count, Target2Count);
}

Expand Down
Loading