Skip to content
Permalink
Browse files
Merge pull request #58513 from AnthonyLatsis/se-309-sil-opt
SE-0309: SILOptimizer fixes & reenable executable tests
  • Loading branch information
slavapestov committed May 20, 2022
2 parents de47a77 + 5dc4834 commit ac74b845e5da0ce2f38a29835d792ec40fba09e5
Showing 13 changed files with 559 additions and 255 deletions.
@@ -5918,6 +5918,9 @@ class OpenedArchetypeType final : public ArchetypeType,
LayoutConstraint layout);
};
BEGIN_CAN_TYPE_WRAPPER(OpenedArchetypeType, ArchetypeType)
CanOpenedArchetypeType getRoot() const {
return CanOpenedArchetypeType(getPointer()->getRoot());
}
END_CAN_TYPE_WRAPPER(OpenedArchetypeType, ArchetypeType)

/// An archetype that represents an opaque element of a type sequence in context.
@@ -33,7 +33,7 @@ namespace swift {
/// When successfull, ConcreteExistentialInfo can be used to determine the
/// concrete type of the opened existential.
struct OpenedArchetypeInfo {
ArchetypeType *OpenedArchetype = nullptr;
OpenedArchetypeType *OpenedArchetype = nullptr;
// The opened value.
SingleValueInstruction *OpenedArchetypeValue;
// The existential value.
@@ -65,6 +65,13 @@ Type QueryTypeSubstitutionMap::operator()(SubstitutableType *type) const {

Type
QueryTypeSubstitutionMapOrIdentity::operator()(SubstitutableType *type) const {
// FIXME: Type::subst should not be pass in non-root archetypes.
// Consider only root archetypes.
if (auto *archetype = dyn_cast<ArchetypeType>(type)) {
if (!archetype->isRoot())
return Type();
}

auto key = type->getCanonicalType()->castTo<SubstitutableType>();
auto known = substitutions.find(key);
if (known != substitutions.end() && known->second)
@@ -2551,7 +2551,7 @@ CanSILFunctionType swift::buildSILFunctionThunkType(
auto archetypeVisitor = [&](CanType t) {
if (auto archetypeTy = dyn_cast<ArchetypeType>(t)) {
if (auto opened = dyn_cast<OpenedArchetypeType>(archetypeTy)) {
const auto root = cast<OpenedArchetypeType>(CanType(opened->getRoot()));
const auto root = opened.getRoot();
assert((openedExistential == CanArchetypeType() ||
openedExistential == root) &&
"one too many open existentials");
@@ -2582,18 +2582,16 @@ CanSILFunctionType swift::buildSILFunctionThunkType(
}

auto substTypeHelper = [&](SubstitutableType *type) -> Type {
// FIXME: Type::subst should not pass in non-root archetypes.
// Consider only root archetypes.
if (auto *archetype = dyn_cast<ArchetypeType>(type)) {
if (!archetype->isRoot())
return Type();
}

if (CanType(type) == openedExistential)
return newArchetype;

// If a nested archetype is rooted on our opened existential, fail:
// Type::subst attempts to substitute the parent of a nested archetype
// only if it fails to find a replacement for the nested one.
if (auto *opened = dyn_cast<OpenedArchetypeType>(type)) {
if (openedExistential->isEqual(opened->getRoot())) {
return nullptr;
}
}

return Type(type).subst(contextSubs);
};
auto substConformanceHelper =
@@ -56,7 +56,7 @@ static void collectDependentTypeInfo(
return;
Ty.visit([&](CanType t) {
if (const auto opened = dyn_cast<OpenedArchetypeType>(t)) {
const auto root = cast<OpenedArchetypeType>(CanType(opened->getRoot()));
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
@@ -1469,10 +1469,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"Operand is of an ArchetypeType that does not exist in the "
"Caller's generic param list.");
if (auto OpenedA = getOpenedArchetypeOf(A)) {
const auto root =
cast<OpenedArchetypeType>(CanType(OpenedA->getRoot()));
auto *openingInst =
F->getModule().getRootOpenedArchetypeDefInst(root, F);
F->getModule().getRootOpenedArchetypeDefInst(OpenedA.getRoot(), F);
require(I == nullptr || openingInst == I ||
properlyDominates(openingInst, I),
"Use of an opened archetype should be dominated by a "
@@ -1605,7 +1603,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
require(isArchetypeValidInFunction(A, AI->getFunction()),
"Archetype to be substituted must be valid in function.");

const auto root = cast<OpenedArchetypeType>(CanType(A->getRoot()));
const auto root = A.getRoot();

// Collect all root opened archetypes used in the substitutions list.
FoundRootOpenedArchetypes.insert(root);
@@ -4074,10 +4072,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
Ty.visit([&](CanType t) {
SILValue Def;
if (const auto archetypeTy = dyn_cast<OpenedArchetypeType>(t)) {
const auto root =
cast<OpenedArchetypeType>(CanType(archetypeTy->getRoot()));
Def = I->getModule().getRootOpenedArchetypeDefInst(root,
I->getFunction());
Def = I->getModule().getRootOpenedArchetypeDefInst(
archetypeTy.getRoot(), I->getFunction());
require(Def, "Root opened archetype should be registered in SILModule");
} else if (t->hasDynamicSelfType()) {
require(I->getFunction()->hasSelfParam() ||
@@ -920,45 +920,56 @@ static bool canReplaceCopiedArg(FullApplySite Apply, SILValue Arg,
return true;
}

/// Determine if the result type or argument types of the given apply, except
/// for the argument at \p SkipArgIdx, contain an opened archetype rooted
/// on \p RootOA.
static bool applyInvolvesOpenedArchetypeWithRoot(FullApplySite Apply,
OpenedArchetypeType *RootOA,
unsigned SkipArgIdx) {
if (Apply.getType().getASTType()->hasOpenedExistentialWithRoot(RootOA)) {
return true;
}

const auto NumApplyArgs = Apply.getNumArguments();
for (unsigned Idx = 0; Idx < NumApplyArgs; ++Idx) {
if (Idx == SkipArgIdx)
continue;
if (Apply.getArgument(Idx)
->getType()
.getASTType()
->hasOpenedExistentialWithRoot(RootOA)) {
return true;
}
}

return false;
}

// Check the legal conditions under which a Arg parameter (specified as ArgIdx)
// can be replaced with a concrete type. Concrete type info is passed as CEI
// argument.
bool SILCombiner::canReplaceArg(FullApplySite Apply,
const OpenedArchetypeInfo &OAI,
const ConcreteExistentialInfo &CEI,
unsigned ArgIdx) {

// Don't specialize apply instructions that return the callee's Arg type,
// because this optimization does not know how to substitute types in the
// users of this apply. In the function type substitution below, all
// references to OpenedArchetype will be substituted. So walk to type to
// find all possible references, such as returning Optional<Arg>.
if (Apply.getType().getASTType().findIf(
[&OAI](Type t) -> bool { return t->isEqual(OAI.OpenedArchetype); })) {
return false;
}
// Bail out if any other arguments or indirect result that refer to the
// OpenedArchetype. The following optimization substitutes all occurrences
// of OpenedArchetype in the function signature, but will only rewrite the
// Arg operand.
// Don't specialize apply instructions if the result type references
// OpenedArchetype, because this optimization does not know how to substitute
// types in the users of this apply. In the function type substitution below,
// all references to OpenedArchetype will be substituted. So walk the type to
// find all possible references, such as returning Optional<OpenedArchetype>.
// The same holds for other arguments or indirect result that refer to the
// OpenedArchetype, because the following optimization will rewrite only the
// argument at ArgIdx.
//
// Note that the language does not allow Self to occur in contravariant
// position. However, SIL does allow this and it can happen as a result of
// upstream transformations. Since this is bail-out logic, it must handle
// all verifiable SIL.

// This bailout check is also needed for non-Self arguments [including Self].
unsigned NumApplyArgs = Apply.getNumArguments();
for (unsigned Idx = 0; Idx < NumApplyArgs; ++Idx) {
if (Idx == ArgIdx)
continue;
if (Apply.getArgument(Idx)->getType().getASTType().findIf(
[&OAI](Type t) -> bool {
return t->isEqual(OAI.OpenedArchetype);
})) {
return false;
}
if (applyInvolvesOpenedArchetypeWithRoot(Apply, OAI.OpenedArchetype,
ArgIdx)) {
return false;
}

// If the convention is mutating, then the existential must have been
// initialized by copying the concrete value (regardless of whether
// CEI.isConcreteValueCopied is true). Replacing the existential address with
@@ -1052,37 +1063,24 @@ SILValue SILCombiner::canCastArg(FullApplySite Apply,
!CEI.ConcreteValue->getType().isAddress())
return SILValue();

// Don't specialize apply instructions that return the callee's Arg type,
// because this optimization does not know how to substitute types in the
// users of this apply. In the function type substitution below, all
// references to OpenedArchetype will be substituted. So walk to type to
// find all possible references, such as returning Optional<Arg>.
if (Apply.getType().getASTType().findIf(
[&OAI](Type t) -> bool { return t->isEqual(OAI.OpenedArchetype); })) {
return SILValue();
}
// Bail out if any other arguments or indirect result that refer to the
// OpenedArchetype. The following optimization substitutes all occurrences
// of OpenedArchetype in the function signature, but will only rewrite the
// Arg operand.
// Don't specialize apply instructions if the result type references
// OpenedArchetype, because this optimization does not know how to substitute
// types in the users of this apply. In the function type substitution below,
// all references to OpenedArchetype will be substituted. So walk the type to
// find all possible references, such as returning Optional<OpenedArchetype>.
// The same holds for other arguments or indirect result that refer to the
// OpenedArchetype, because the following optimization will rewrite only the
// argument at ArgIdx.
//
// Note that the language does not allow Self to occur in contravariant
// position. However, SIL does allow this and it can happen as a result of
// upstream transformations. Since this is bail-out logic, it must handle
// all verifiable SIL.

// This bailout check is also needed for non-Self arguments [including Self].
unsigned NumApplyArgs = Apply.getNumArguments();
for (unsigned Idx = 0; Idx < NumApplyArgs; ++Idx) {
if (Idx == ArgIdx)
continue;
if (Apply.getArgument(Idx)->getType().getASTType().findIf(
[&OAI](Type t) -> bool {
return t->isEqual(OAI.OpenedArchetype);
})) {
return SILValue();
}
if (applyInvolvesOpenedArchetypeWithRoot(Apply, OAI.OpenedArchetype,
ArgIdx)) {
return SILValue();
}

return Builder.createUncheckedAddrCast(
Apply.getLoc(), Apply.getArgument(ArgIdx), CEI.ConcreteValue->getType());
}
@@ -1293,10 +1291,11 @@ SILInstruction *SILCombiner::createApplyWithConcreteType(
/// %existential = alloc_stack $Protocol
/// %value = init_existential_addr %existential : $Concrete
/// copy_addr ... to %value
/// %witness = witness_method $@opened
/// apply %witness<T : Protocol>(%existential)
/// %opened = open_existential_addr %existential
/// %witness = witness_method $@opened(...) Protocol
/// apply %witness<$@opened(...) Protocol>(%opened)
///
/// ==> apply %witness<Concrete : Protocol>(%existential)
/// ==> apply %witness<$Concrete>(%existential)
SILInstruction *
SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
WitnessMethodInst *WMI) {
@@ -1094,9 +1094,10 @@ static bool canDevirtualizeWitnessMethod(ApplySite applySite) {
return false;
}

// FIXME: devirtualizeWitnessMethod below does not support cases with
// covariant 'Self' nested inside a collection type,
// like '[Self]' or '[* : Self]'.
// FIXME: devirtualizeWitnessMethod does not support cases with covariant
// 'Self'-rooted type parameters nested inside a collection type, like
// '[Self]' or '[* : Self.A]', because it doesn't know how to deal with
// associated collection upcasts.
const Type interfaceTy = wmi->getMember()
.getDecl()
->getInterfaceType()
@@ -1107,35 +1108,35 @@ static bool canDevirtualizeWitnessMethod(ApplySite applySite) {
if (!interfaceTy->hasTypeParameter())
return true;

class HasSelfNestedInsideCollection final : public TypeWalker {
unsigned CollectionDepth;
auto *const selfGP = wmi->getLookupProtocol()->getProtocolSelfType();
auto isSelfRootedTypeParameter = [selfGP](Type T) -> bool {
if (!T->hasTypeParameter())
return false;

public:
Action walkToTypePre(Type T) override {
if (!T->hasTypeParameter())
return Action::SkipChildren;
if (T->isTypeParameter()) {
return T->getRootGenericParam()->isEqual(selfGP);
}

if (auto *GP = T->getAs<GenericTypeParamType>()) {
// Only 'Self' will have zero depth in the type of a requirement.
if (GP->getDepth() == 0 && CollectionDepth)
return Action::Stop;
}
return false;
};

if (T->isArray() || T->isDictionary())
++CollectionDepth;
return !interfaceTy.findIf([&](Type T) -> bool {
if (!T->hasTypeParameter())
return false;

return Action::Continue;
if (T->isArray() || T->isDictionary()) {
return T.findIf(isSelfRootedTypeParameter);
}

Action walkToTypePost(Type T) override {
if (T->isArray() || T->isDictionary())
--CollectionDepth;

return Action::Continue;
if (auto *FT = T->getAs<FunctionType>()) {
for (const auto &Param : FT->getParams()) {
if (Param.isVariadic() && T.findIf(isSelfRootedTypeParameter))
return true;
}
}
};

return !interfaceTy.walk(HasSelfNestedInsideCollection());
return false;
});
}

/// In the cases where we can statically determine the function that
@@ -220,13 +220,13 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) {
}
}
if (auto *Open = dyn_cast<OpenExistentialAddrInst>(openedVal)) {
OpenedArchetype = Open->getType().castTo<ArchetypeType>();
OpenedArchetype = Open->getType().castTo<OpenedArchetypeType>();
OpenedArchetypeValue = Open;
ExistentialValue = Open->getOperand();
return;
}
if (auto *Open = dyn_cast<OpenExistentialRefInst>(openedVal)) {
OpenedArchetype = Open->getType().castTo<ArchetypeType>();
OpenedArchetype = Open->getType().castTo<OpenedArchetypeType>();
OpenedArchetypeValue = Open;
ExistentialValue = Open->getOperand();
return;
@@ -235,7 +235,7 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) {
auto Ty = Open->getType().getASTType();
while (auto Metatype = dyn_cast<MetatypeType>(Ty))
Ty = Metatype.getInstanceType();
OpenedArchetype = cast<ArchetypeType>(Ty);
OpenedArchetype = cast<OpenedArchetypeType>(Ty);
OpenedArchetypeValue = Open;
ExistentialValue = Open->getOperand();
}

0 comments on commit ac74b84

Please sign in to comment.