Skip to content

Commit

Permalink
Merge pull request #18100 from jckarter/keypath-external-irgen
Browse files Browse the repository at this point in the history
IRGen and runtime support for key path resilience.
  • Loading branch information
jckarter committed Jul 26, 2018
2 parents 23df594 + 4098aa0 commit 665bcb9
Show file tree
Hide file tree
Showing 27 changed files with 1,459 additions and 1,572 deletions.
14 changes: 9 additions & 5 deletions include/swift/ABI/KeyPath.h
Expand Up @@ -190,7 +190,7 @@ class KeyPathComponentHeader {
bool resolvedID) {
return KeyPathComponentHeader(
(_SwiftKeyPathComponentHeader_ComputedTag
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
| (kind != GetOnly
? _SwiftKeyPathComponentHeader_ComputedSettableFlag : 0)
| (kind == SettableMutating
Expand All @@ -205,10 +205,14 @@ class KeyPathComponentHeader {
}

constexpr static KeyPathComponentHeader
forExternalComponent() {
return KeyPathComponentHeader(
_SwiftKeyPathComponentHeader_ExternalTag
<< _SwiftKeyPathComponentHeader_DiscriminatorShift);
forExternalComponent(unsigned numSubstitutions) {
return assert(numSubstitutions <
(1u << _SwiftKeyPathComponentHeader_DiscriminatorShift) - 1u
&& "too many substitutions"),
KeyPathComponentHeader(
(_SwiftKeyPathComponentHeader_ExternalTag
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
| numSubstitutions);
}

constexpr uint32_t getData() const { return Data; }
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Decl.h
Expand Up @@ -4438,6 +4438,10 @@ class AbstractStorageDecl : public ValueDecl {
BehaviorRecord *getMutableBehavior() {
return BehaviorInfo.getPointer();
}

/// True if the storage exports a property descriptor for key paths in
/// other modules.
bool exportsPropertyDescriptor() const;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
Expand Down
61 changes: 61 additions & 0 deletions lib/IRGen/GenKeyPath.cpp
Expand Up @@ -702,6 +702,27 @@ emitMetadataGeneratorForKeyPath(IRGenModule &IGM,
});
};

static llvm::Function *
emitWitnessTableGeneratorForKeyPath(IRGenModule &IGM,
CanType type,
ProtocolConformanceRef conformance,
GenericEnvironment *genericEnv,
ArrayRef<GenericRequirement> requirements) {
// TODO: Use the standard conformance accessor when there are no arguments
// and the conformance accessor is defined.
return emitGeneratorForKeyPath(IGM, "keypath_get_witness_table", type,
IGM.WitnessTablePtrTy,
genericEnv, requirements,
[&](IRGenFunction &IGF, CanType substType) {
if (type->hasTypeParameter())
conformance = conformance.subst(type,
QueryInterfaceTypeSubstitutions(genericEnv),
LookUpConformanceInSignature(*genericEnv->getGenericSignature()));
auto ret = emitWitnessTableRef(IGF, substType, conformance);
IGF.Builder.CreateRet(ret);
});
}

static void
emitKeyPathComponent(IRGenModule &IGM,
ConstantStructBuilder &fields,
Expand Down Expand Up @@ -815,6 +836,46 @@ emitKeyPathComponent(IRGenModule &IGM,
}
case KeyPathPatternComponent::Kind::GettableProperty:
case KeyPathPatternComponent::Kind::SettableProperty: {
// If the component references an external property, encode that in a
// header before the local attempt header, so that we can consult the
// external descriptor at instantiation time.
if (auto externalDecl = component.getExternalDecl()) {
SmallVector<llvm::Constant *, 4> externalSubArgs;
auto componentSig = externalDecl->getInnermostDeclContext()
->getGenericSignatureOfContext();
auto subs = component.getExternalSubstitutions();
if (!subs.empty()) {
enumerateGenericSignatureRequirements(
componentSig->getCanonicalSignature(),
[&](GenericRequirement reqt) {
auto substType = reqt.TypeParameter.subst(subs)
->getCanonicalType();
if (!reqt.Protocol) {
// Type requirement.
externalSubArgs.push_back(
emitMetadataGeneratorForKeyPath(IGM, substType,
genericEnv, requirements));
} else {
// Protocol requirement.
auto conformance = subs.lookupConformance(
reqt.TypeParameter->getCanonicalType(), reqt.Protocol);
externalSubArgs.push_back(
emitWitnessTableGeneratorForKeyPath(IGM, substType,
*conformance,
genericEnv, requirements));
}
});
}
fields.addInt32(
KeyPathComponentHeader::forExternalComponent(externalSubArgs.size())
.getData());
fields.addAlignmentPadding(IGM.getPointerAlignment());
auto descriptor = IGM.getAddrOfPropertyDescriptor(externalDecl);
fields.add(descriptor);
for (auto *arg : externalSubArgs)
fields.add(arg);
}

// Encode the settability.
bool settable = kind == KeyPathPatternComponent::Kind::SettableProperty;
KeyPathComponentHeader::ComputedPropertyKind componentKind;
Expand Down
46 changes: 46 additions & 0 deletions lib/SIL/SIL.cpp
Expand Up @@ -208,3 +208,49 @@ bool SILModule::isTypeABIAccessible(SILType type) {
// Otherwise, we need to be able to fetch layout-metadata for the type.
return isTypeMetadataForLayoutAccessible(*this, type);
}

bool AbstractStorageDecl::exportsPropertyDescriptor() const {
// The storage needs a descriptor if it sits at a module's ABI boundary,
// meaning it has public linkage.

// TODO: Global and static properties ought to eventually be referenceable
// as key paths from () or T.Type too.
if (!getDeclContext()->isTypeContext() || isStatic())
return false;

// Any property that's potentially resilient should have accessors
// synthesized.
if (!getGetter())
return false;

// If the getter is mutating, we cannot form a keypath to it at all.
if (isGetterMutating())
return false;

// TODO: If previous versions of an ABI-stable binary needed the descriptor,
// then we still do.

auto getter = SILDeclRef(getGetter());
auto getterLinkage = getter.getLinkage(ForDefinition);

switch (getterLinkage) {
case SILLinkage::Public:
case SILLinkage::PublicNonABI:
// We may need a descriptor.
break;

case SILLinkage::Shared:
case SILLinkage::Private:
case SILLinkage::Hidden:
// Don't need a public descriptor.
return false;

case SILLinkage::HiddenExternal:
case SILLinkage::PrivateExternal:
case SILLinkage::PublicExternal:
case SILLinkage::SharedExternal:
llvm_unreachable("should be definition linkage?");
}

return true;
}
13 changes: 12 additions & 1 deletion lib/SIL/SILDeclRef.cpp
Expand Up @@ -314,8 +314,19 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
neverPublic = true;
}
}

auto effectiveAccess = d->getEffectiveAccess();

// Private setter implementations for an internal storage declaration should
// be internal as well, so that a dynamically-writable
// keypath can be formed from other files.
if (auto accessor = dyn_cast<AccessorDecl>(d)) {
if (accessor->isSetter()
&& accessor->getStorage()->getEffectiveAccess() == AccessLevel::Internal)
effectiveAccess = AccessLevel::Internal;
}

switch (d->getEffectiveAccess()) {
switch (effectiveAccess) {
case AccessLevel::Private:
case AccessLevel::FilePrivate:
return maybeAddExternal(SILLinkage::Private);
Expand Down
6 changes: 5 additions & 1 deletion lib/SIL/SILVerifier.cpp
Expand Up @@ -232,11 +232,15 @@ void verifyKeyPathComponent(SILModule &M,
&& !component.getSubscriptIndexEquals()
&& !component.getSubscriptIndexHash(),
"property descriptor should not have index information");

require(component.getExternalDecl() == nullptr
&& component.getExternalSubstitutions().empty(),
"property descriptor should not refer to another external decl");
} else {
require(hasIndices == !component.getSubscriptIndices().empty(),
"component for subscript should have indices");
}

ParameterConvention normalArgConvention;
if (M.getOptions().EnableGuaranteedNormalArguments)
normalArgConvention = ParameterConvention::Indirect_In_Guaranteed;
Expand Down
43 changes: 1 addition & 42 deletions lib/SILGen/SILGen.cpp
Expand Up @@ -1228,47 +1228,6 @@ TypeConverter::canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl) {
}
}

static bool doesStorageNeedDescriptor(AbstractStorageDecl *decl) {
// The storage needs a descriptor if it sits at a module's ABI boundary,
// meaning it has public linkage.

// Any property that's potentially resilient should have accessors
// synthesized.
if (!decl->getGetter())
return false;

// If the getter is mutating, we cannot form a keypath to it at all.
if (decl->isGetterMutating())
return false;

// TODO: If previous versions of an ABI-stable binary needed the descriptor,
// then we still do.

auto getter = SILDeclRef(decl->getGetter());
auto getterLinkage = getter.getLinkage(ForDefinition);

switch (getterLinkage) {
case SILLinkage::Public:
case SILLinkage::PublicNonABI:
// We may need a descriptor.
break;

case SILLinkage::Shared:
case SILLinkage::Private:
case SILLinkage::Hidden:
// Don't need a public descriptor.
return false;

case SILLinkage::HiddenExternal:
case SILLinkage::PrivateExternal:
case SILLinkage::PublicExternal:
case SILLinkage::SharedExternal:
llvm_unreachable("should be definition linkage?");
}

return true;
}

static bool canStorageUseTrivialDescriptor(SILModule &M,
AbstractStorageDecl *decl) {
// A property can use a trivial property descriptor if the key path component
Expand Down Expand Up @@ -1322,7 +1281,7 @@ void SILGenModule::tryEmitPropertyDescriptor(AbstractStorageDecl *decl) {
if (!SILModuleConventions(M).useLoweredAddresses())
return;

if (!doesStorageNeedDescriptor(decl))
if (!decl->exportsPropertyDescriptor())
return;

Type baseTy;
Expand Down
14 changes: 12 additions & 2 deletions lib/SILGen/SILGenExpr.cpp
Expand Up @@ -3679,6 +3679,16 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
}
}
}

auto isSettableInComponent = [&]() -> bool {
// For storage we reference by a property descriptor, the descriptor will
// supply the settability if needed. We only reference it here if the
// setter is public.
if (shouldUseExternalKeyPathComponent())
return storage->isSettable(M.getSwiftModule())
&& storage->isSetterAccessibleFrom(M.getSwiftModule());
return storage->isSettable(storage->getDeclContext());
};

if (auto var = dyn_cast<VarDecl>(storage)) {
CanType componentTy;
Expand Down Expand Up @@ -3729,7 +3739,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
{},
baseTy, componentTy);

if (var->isSettable(var->getDeclContext())) {
if (isSettableInComponent()) {
auto setter = getOrCreateKeyPathSetter(*this, loc,
var, subs,
needsGenericContext ? genericEnv : nullptr,
Expand Down Expand Up @@ -3783,7 +3793,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
baseTy, componentTy);

auto indexPatternsCopy = getASTContext().AllocateCopy(indexPatterns);
if (decl->isSettable()) {
if (isSettableInComponent()) {
auto setter = getOrCreateKeyPathSetter(*this, loc,
decl, subs,
needsGenericContext ? genericEnv : nullptr,
Expand Down
6 changes: 6 additions & 0 deletions lib/TBDGen/TBDGen.cpp
Expand Up @@ -156,6 +156,12 @@ void TBDGenVisitor::visitAccessorDecl(AccessorDecl *AD) {
}

void TBDGenVisitor::visitAbstractStorageDecl(AbstractStorageDecl *ASD) {
// Add the property descriptor if the decl needs it.
if (SwiftModule->getASTContext().LangOpts.EnableKeyPathResilience
&& ASD->exportsPropertyDescriptor()) {
addSymbol(LinkEntity::forPropertyDescriptor(ASD));
}

// Explicitly look at each accessor here: see visitAccessorDecl.
for (auto accessor : ASD->getAllAccessors()) {
visitAbstractFunctionDecl(accessor);
Expand Down
20 changes: 14 additions & 6 deletions stdlib/public/SwiftShims/KeyPath.h
Expand Up @@ -46,18 +46,18 @@ static const __swift_uint32_t _SwiftKeyPathComponentHeader_DiscriminatorShift
= 24;

static const __swift_uint32_t _SwiftKeyPathComponentHeader_StructTag
= 0;
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedTag
= 1;
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ClassTag
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedTag
= 2;
static const __swift_uint32_t _SwiftKeyPathComponentHeader_OptionalTag
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ClassTag
= 3;
static const __swift_uint32_t _SwiftKeyPathComponentHeader_OptionalTag
= 4;
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ExternalTag
= 0x7F;
= 0;

static const __swift_uint32_t
_SwiftKeyPathComponentHeader_TrivialPropertyDescriptorMarker = 0xFFFFFFFFU;
_SwiftKeyPathComponentHeader_TrivialPropertyDescriptorMarker = 0U;

static const __swift_uint32_t _SwiftKeyPathComponentHeader_MaximumOffsetPayload
= 0x00FFFFFCU;
Expand Down Expand Up @@ -91,13 +91,21 @@ static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedIDByVTableOff
= 0x00100000U;
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedHasArgumentsFlag
= 0x00080000U;
// Not ABI, used internally by key path runtime implementation
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedInstantiatedFromExternalWithArgumentsFlag
= 0x00000010U;
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedIDResolutionMask
= 0x0000000FU;
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedIDResolved
= 0x00000000U;
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedIDUnresolvedIndirectPointer
= 0x00000002U;

extern void *(swift_keyPathGenericWitnessTable[]);

static inline void *__swift_keyPathGenericWitnessTable_addr(void) {
return swift_keyPathGenericWitnessTable;
}

#ifdef __cplusplus
} // extern "C"
Expand Down

0 comments on commit 665bcb9

Please sign in to comment.