Skip to content

GlobalOpt: optimize static properties with resilient types. #21614

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 3 commits into from
Jan 4, 2019
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
39 changes: 24 additions & 15 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ class SILBuilder {
!hasOwnership() && "Unqualified inst in qualified function");
assert((Qualifier == LoadOwnershipQualifier::Unqualified) ||
hasOwnership() && "Qualified inst in unqualified function");
assert(LV->getType().isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(LV->getType()));
return insert(new (getModule())
LoadInst(getSILDebugLocation(Loc), LV, Qualifier));
}
Expand All @@ -696,13 +696,13 @@ class SILBuilder {
/// non-address values.
SILValue emitLoadValueOperation(SILLocation Loc, SILValue LV,
LoadOwnershipQualifier Qualifier) {
assert(LV->getType().isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(LV->getType()));
const auto &lowering = getTypeLowering(LV->getType());
return lowering.emitLoad(*this, Loc, LV, Qualifier);
}

LoadBorrowInst *createLoadBorrow(SILLocation Loc, SILValue LV) {
assert(LV->getType().isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(LV->getType()));
return insert(new (getModule())
LoadBorrowInst(getSILDebugLocation(Loc), LV));
}
Expand Down Expand Up @@ -1069,7 +1069,7 @@ class SILBuilder {
}

DestroyValueInst *createDestroyValue(SILLocation Loc, SILValue operand) {
assert(operand->getType().isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(operand->getType()));
return insert(new (getModule())
DestroyValueInst(getSILDebugLocation(Loc), operand));
}
Expand Down Expand Up @@ -1100,7 +1100,7 @@ class SILBuilder {
RetainValueInst *createRetainValue(SILLocation Loc, SILValue operand,
Atomicity atomicity) {
assert(!hasOwnership());
assert(operand->getType().isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(operand->getType()));
return insert(new (getModule()) RetainValueInst(getSILDebugLocation(Loc),
operand, atomicity));
}
Expand All @@ -1115,7 +1115,7 @@ class SILBuilder {
ReleaseValueInst *createReleaseValue(SILLocation Loc, SILValue operand,
Atomicity atomicity) {
assert(!hasOwnership());
assert(operand->getType().isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(operand->getType()));
return insert(new (getModule()) ReleaseValueInst(getSILDebugLocation(Loc),
operand, atomicity));
}
Expand All @@ -1132,7 +1132,7 @@ class SILBuilder {
SILValue operand,
Atomicity atomicity) {
assert(hasOwnership());
assert(operand->getType().isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(operand->getType()));
return insert(new (getModule()) UnmanagedRetainValueInst(
getSILDebugLocation(Loc), operand, atomicity));
}
Expand All @@ -1141,7 +1141,7 @@ class SILBuilder {
SILValue operand,
Atomicity atomicity) {
assert(hasOwnership());
assert(operand->getType().isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(operand->getType()));
return insert(new (getModule()) UnmanagedReleaseValueInst(
getSILDebugLocation(Loc), operand, atomicity));
}
Expand Down Expand Up @@ -1177,14 +1177,14 @@ class SILBuilder {

StructInst *createStruct(SILLocation Loc, SILType Ty,
ArrayRef<SILValue> Elements) {
assert(Ty.isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(Ty));
return insert(StructInst::create(getSILDebugLocation(Loc), Ty, Elements,
getModule(), hasOwnership()));
}

TupleInst *createTuple(SILLocation Loc, SILType Ty,
ArrayRef<SILValue> Elements) {
assert(Ty.isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(Ty));
return insert(TupleInst::create(getSILDebugLocation(Loc), Ty, Elements,
getModule(), hasOwnership()));
}
Expand All @@ -1193,21 +1193,21 @@ class SILBuilder {

EnumInst *createEnum(SILLocation Loc, SILValue Operand,
EnumElementDecl *Element, SILType Ty) {
assert(Ty.isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(Ty));
return insert(new (getModule()) EnumInst(getSILDebugLocation(Loc),
Operand, Element, Ty));
}

/// Inject a loadable value into the corresponding optional type.
EnumInst *createOptionalSome(SILLocation Loc, SILValue operand, SILType ty) {
assert(ty.isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(ty));
auto someDecl = getModule().getASTContext().getOptionalSomeDecl();
return createEnum(Loc, operand, someDecl, ty);
}

/// Create the nil value of a loadable optional type.
EnumInst *createOptionalNone(SILLocation Loc, SILType ty) {
assert(ty.isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(ty));
auto noneDecl = getModule().getASTContext().getOptionalNoneDecl();
return createEnum(Loc, nullptr, noneDecl, ty);
}
Expand All @@ -1224,7 +1224,7 @@ class SILBuilder {
SILValue Operand,
EnumElementDecl *Element,
SILType Ty) {
assert(Ty.isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(Ty));
return insert(new (getModule()) UncheckedEnumDataInst(
getSILDebugLocation(Loc), Operand, Element, Ty));
}
Expand Down Expand Up @@ -1264,7 +1264,7 @@ class SILBuilder {
ArrayRef<std::pair<EnumElementDecl *, SILValue>> CaseValues,
Optional<ArrayRef<ProfileCounter>> CaseCounts = None,
ProfileCounter DefaultCount = ProfileCounter()) {
assert(Ty.isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(Ty));
return insert(SelectEnumInst::create(
getSILDebugLocation(Loc), Operand, Ty, DefaultValue, CaseValues,
getModule(), CaseCounts, DefaultCount, hasOwnership()));
Expand Down Expand Up @@ -2125,6 +2125,15 @@ class SILBuilder {
#endif
}

bool isLoadableOrOpaque(SILType Ty) {
if (!F) {
// We are inserting into the static initializer of a SILGlobalVariable.
// All types used there are loadable by definition.
Copy link
Contributor

Choose a reason for hiding this comment

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

What if the emitter for the initializer has a bug? I think you still want to use ResilienceExpansion::Maximal here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately this does not work. The conservative assumption would trigger a false assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh wait, you are saying ResilienceExpansion::Maximal. Yeah, that's a good idea.

return true;
}
return Ty.isLoadableOrOpaque(F);
}

void appendOperandTypeName(SILType OpdTy, llvm::SmallString<16> &Name) {
if (auto BuiltinIntTy =
dyn_cast<BuiltinIntegerType>(OpdTy.getASTType())) {
Expand Down
6 changes: 4 additions & 2 deletions include/swift/SIL/SILModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,10 @@ class SILModule {
mutable Lowering::TypeConverter Types;

/// Look up the TypeLowering for a SILType.
const Lowering::TypeLowering &getTypeLowering(SILType t) {
return Types.getTypeLowering(t);
const Lowering::TypeLowering &
getTypeLowering(SILType t, ResilienceExpansion expansion =
ResilienceExpansion::Minimal) {
return Types.getTypeLowering(t, expansion);
}

/// Invalidate cached entries in SIL Loader.
Expand Down
21 changes: 21 additions & 0 deletions include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,35 @@ class SILType {
bool isLoadable(SILModule &M) const {
return !isAddressOnly(M);
}

/// Like isLoadable(SILModule), but specific to a function.
///
/// This takes the resilience expansion of the function into account. If the
/// type is not loadable in general (because it's resilient), it still might
/// be loadable inside a resilient function in the module.
/// In other words: isLoadable(SILModule) is the conservative default, whereas
/// isLoadable(SILFunction) might give a more optimistic result.
bool isLoadable(SILFunction *inFunction) const {
return !isAddressOnly(inFunction);
}

/// True if either:
/// 1) The type, or the referenced type of an address type, is loadable.
/// 2) The SIL Module conventions uses lowered addresses
bool isLoadableOrOpaque(SILModule &M) const;

/// Like isLoadableOrOpaque(SILModule), but takes the resilience expansion of
/// \p inFunction into account (see isLoadable(SILFunction)).
bool isLoadableOrOpaque(SILFunction *inFunction) const;

/// True if the type, or the referenced type of an address type, is
/// address-only. This is the opposite of isLoadable.
bool isAddressOnly(SILModule &M) const;

/// Like isAddressOnly(SILModule), but takes the resilience expansion of
/// \p inFunction into account (see isLoadable(SILFunction)).
bool isAddressOnly(SILFunction *inFunction) const;

/// True if the type, or the referenced type of an address type, is trivial.
bool isTrivial(SILModule &M) const;

Expand Down
46 changes: 39 additions & 7 deletions include/swift/SIL/TypeLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ enum IsReferenceCounted_t : bool {
IsReferenceCounted = true
};

/// Is this type address only because it's resilient?
enum IsResilient_t : bool {
IsNotResilient = false,
IsResilient = true
};

/// Extended type information used by SIL.
class TypeLowering {
public:
Expand All @@ -157,6 +163,7 @@ class TypeLowering {
NonTrivialFlag = 1 << 0,
NonFixedABIFlag = 1 << 1,
AddressOnlyFlag = 1 << 2,
ResilientFlag = 1 << 3,
};

uint8_t Flags;
Expand All @@ -167,17 +174,23 @@ class TypeLowering {

constexpr RecursiveProperties(IsTrivial_t isTrivial,
IsFixedABI_t isFixedABI,
IsAddressOnly_t isAddressOnly)
IsAddressOnly_t isAddressOnly,
IsResilient_t isResilient = IsNotResilient)
: Flags((isTrivial ? 0U : NonTrivialFlag) |
(isAddressOnly ? AddressOnlyFlag : 0U) |
(isFixedABI ? 0U : NonFixedABIFlag)) {}
(isFixedABI ? 0U : NonFixedABIFlag) |
(isResilient ? ResilientFlag : 0U)) {}

static constexpr RecursiveProperties forReference() {
return {IsNotTrivial, IsFixedABI, IsNotAddressOnly};
return {IsNotTrivial, IsFixedABI, IsNotAddressOnly, IsNotResilient };
}

static constexpr RecursiveProperties forOpaque() {
return {IsNotTrivial, IsNotFixedABI, IsAddressOnly};
return {IsNotTrivial, IsNotFixedABI, IsAddressOnly, IsNotResilient};
}

static constexpr RecursiveProperties forResilient() {
return {IsNotTrivial, IsNotFixedABI, IsAddressOnly, IsResilient};
}

void addSubobject(RecursiveProperties other) {
Expand All @@ -193,6 +206,9 @@ class TypeLowering {
IsAddressOnly_t isAddressOnly() const {
return IsAddressOnly_t((Flags & AddressOnlyFlag) != 0);
}
IsResilient_t isResilient() const {
return IsResilient_t((Flags & ResilientFlag) != 0);
}

void setNonTrivial() { Flags |= NonTrivialFlag; }
void setNonFixedABI() { Flags |= NonFixedABIFlag; }
Expand All @@ -206,7 +222,16 @@ class TypeLowering {
RecursiveProperties Properties;
unsigned ReferenceCounted : 1;

protected:
public:
/// The resilience expansion for this type lowering.
/// If the type is not resilient at all, this is always Minimal.
ResilienceExpansion forExpansion = ResilienceExpansion::Minimal;

/// A single linked list of lowerings for different resilience expansions.
/// The first lowering is always for ResilientExpansion::Minimal.
mutable const TypeLowering *nextExpansion = nullptr;

protected:
TypeLowering(SILType type, RecursiveProperties properties,
IsReferenceCounted_t isRefCounted)
: LoweredType(type), Properties(properties),
Expand Down Expand Up @@ -277,6 +302,10 @@ class TypeLowering {
return LoweredType.isAddress();
}

bool isResilient() const {
return Properties.isResilient();
}

/// Return the semantic type.
///
/// The semantic type is what a type pretends to be during
Expand Down Expand Up @@ -670,7 +699,8 @@ class TypeConverter {
Optional<CanType> BridgedType##Ty;
#include "swift/SIL/BridgedTypes.def"

const TypeLowering &getTypeLoweringForLoweredType(TypeKey key);
const TypeLowering &
getTypeLoweringForLoweredType(TypeKey key, ResilienceExpansion forExpansion);
const TypeLowering &getTypeLoweringForUncachedLoweredType(TypeKey key);

public:
Expand Down Expand Up @@ -746,7 +776,9 @@ class TypeConverter {
/// Returns the SIL TypeLowering for an already lowered SILType. If the
/// SILType is an address, returns the TypeLowering for the pointed-to
/// type.
const TypeLowering &getTypeLowering(SILType t);
const TypeLowering &
getTypeLowering(SILType t, ResilienceExpansion forExpansion =
ResilienceExpansion::Minimal);

// Returns the lowered SIL type for a Swift type.
SILType getLoweredType(Type t) {
Expand Down
4 changes: 2 additions & 2 deletions lib/SIL/SILBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ SILBuilder::createClassifyBridgeObject(SILLocation Loc, SILValue value) {
// Create the appropriate cast instruction based on result type.
SingleValueInstruction *
SILBuilder::createUncheckedBitCast(SILLocation Loc, SILValue Op, SILType Ty) {
assert(Ty.isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(Ty));
if (Ty.isTrivial(getModule()))
return insert(UncheckedTrivialBitCastInst::create(
getSILDebugLocation(Loc), Op, Ty, getFunction(), C.OpenedArchetypes));
Expand Down Expand Up @@ -552,7 +552,7 @@ void SILBuilder::emitShallowDestructureAddressOperation(

DebugValueInst *SILBuilder::createDebugValue(SILLocation Loc, SILValue src,
SILDebugVariable Var) {
assert(src->getType().isLoadableOrOpaque(getModule()));
assert(isLoadableOrOpaque(src->getType()));
// Debug location overrides cannot apply to debug value instructions.
DebugLocOverrideRAII LocOverride{*this, None};
return insert(
Expand Down
11 changes: 11 additions & 0 deletions lib/SIL/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,24 @@ bool SILType::isLoadableOrOpaque(SILModule &M) const {
return isLoadable(M) || !SILModuleConventions(M).useLoweredAddresses();
}

bool SILType::isLoadableOrOpaque(SILFunction *inFunction) const {
SILModule &M = inFunction->getModule();
return isLoadable(inFunction) ||
!SILModuleConventions(M).useLoweredAddresses();
}

/// True if the type, or the referenced type of an address type, is
/// address-only. For example, it could be a resilient struct or something of
/// unknown size.
bool SILType::isAddressOnly(SILModule &M) const {
return M.getTypeLowering(*this).isAddressOnly();
}

bool SILType::isAddressOnly(SILFunction *inFunction) const {
return inFunction->getModule().getTypeLowering(*this,
inFunction->getResilienceExpansion()).isAddressOnly();
}

SILType SILType::substGenericArgs(SILModule &M,
SubstitutionMap SubMap) const {
auto fnTy = castTo<SILFunctionType>();
Expand Down
6 changes: 3 additions & 3 deletions lib/SIL/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1534,7 +1534,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
void checkLoadInst(LoadInst *LI) {
require(LI->getType().isObject(), "Result of load must be an object");
require(!fnConv.useLoweredAddresses()
|| LI->getType().isLoadable(LI->getModule()),
|| LI->getType().isLoadable(LI->getFunction()),
"Load must have a loadable type");
require(LI->getOperand()->getType().isAddress(),
"Load operand must be an address");
Expand Down Expand Up @@ -1573,7 +1573,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"Inst with qualified ownership in a function that is not qualified");
require(LBI->getType().isObject(), "Result of load must be an object");
require(!fnConv.useLoweredAddresses()
|| LBI->getType().isLoadable(LBI->getModule()),
|| LBI->getType().isLoadable(LBI->getFunction()),
"Load must have a loadable type");
require(LBI->getOperand()->getType().isAddress(),
"Load operand must be an address");
Expand Down Expand Up @@ -1691,7 +1691,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
require(SI->getSrc()->getType().isObject(),
"Can't store from an address source");
require(!fnConv.useLoweredAddresses()
|| SI->getSrc()->getType().isLoadable(SI->getModule()),
|| SI->getSrc()->getType().isLoadable(SI->getFunction()),
"Can't store a non loadable type");
require(SI->getDest()->getType().isAddress(),
"Must store to an address dest");
Expand Down
Loading