Skip to content

Commit 3e4e00c

Browse files
committed
SILGen: Emit "trivial" property descriptors for properties that withhold no information about their implementation.
Client code can make a best effort at emitting a key path referencing a property with its publicly exposed API, which in the common case will match what the defining module would produce as the canonical key path component representation of the declaration. We can reduce the code size impact of these descriptors by not emitting them when there's no hidden or possibly-resiliently-changed-in-the-past information about a storage declaration, having the property descriptor symbol reference a sentinel value telling client key paths to use their definition of the key path component.
1 parent c93a5a5 commit 3e4e00c

File tree

20 files changed

+301
-101
lines changed

20 files changed

+301
-101
lines changed

include/swift/AST/Decl.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4228,6 +4228,25 @@ class AbstractStorageDecl : public ValueDecl {
42284228
llvm_unreachable("bad storage kind");
42294229
}
42304230

4231+
/// \brief Return true if this is a VarDecl that has storage associated with
4232+
/// it which can be trivially accessed.
4233+
bool hasTrivialStorage() const {
4234+
switch (getStorageKind()) {
4235+
case Stored:
4236+
case StoredWithTrivialAccessors:
4237+
return true;
4238+
case StoredWithObservers:
4239+
case InheritedWithObservers:
4240+
case Computed:
4241+
case ComputedWithMutableAddress:
4242+
case Addressed:
4243+
case AddressedWithTrivialAccessors:
4244+
case AddressedWithObservers:
4245+
return false;
4246+
}
4247+
llvm_unreachable("bad storage kind");
4248+
}
4249+
42314250
/// \brief Return true if this object has a getter (and, if mutable,
42324251
/// a setter and a materializeForSet).
42334252
bool hasAccessorFunctions() const {
@@ -4456,7 +4475,8 @@ class AbstractStorageDecl : public ValueDecl {
44564475

44574476
/// Determine how this storage declaration should actually be accessed.
44584477
AccessStrategy getAccessStrategy(AccessSemantics semantics,
4459-
AccessKind accessKind) const;
4478+
AccessKind accessKind,
4479+
DeclContext *accessFromDC = nullptr) const;
44604480

44614481
/// \brief Should this declaration behave as if it must be accessed
44624482
/// resiliently, even when we're building a non-resilient module?

include/swift/SIL/SILProperty.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,31 @@ class SILProperty : public llvm::ilist_node<SILProperty>,
4242
AbstractStorageDecl *Decl;
4343

4444
/// The key path component that represents its implementation.
45-
KeyPathPatternComponent Component;
45+
Optional<KeyPathPatternComponent> Component;
4646

4747
SILProperty(bool Serialized,
4848
AbstractStorageDecl *Decl,
49-
KeyPathPatternComponent Component)
49+
Optional<KeyPathPatternComponent> Component)
5050
: Serialized(Serialized), Decl(Decl), Component(Component)
5151
{}
5252

5353
public:
5454
static SILProperty *create(SILModule &M,
5555
bool Serialized,
5656
AbstractStorageDecl *Decl,
57-
KeyPathPatternComponent Component);
57+
Optional<KeyPathPatternComponent> Component);
5858

5959
bool isSerialized() const { return Serialized; }
6060

6161
AbstractStorageDecl *getDecl() const { return Decl; }
6262

63-
const KeyPathPatternComponent &getComponent() const { return Component; }
63+
bool isTrivial() const {
64+
return !Component.hasValue();
65+
}
66+
67+
const Optional<KeyPathPatternComponent> &getComponent() const {
68+
return Component;
69+
}
6470

6571
void print(SILPrintContext &Ctx) const;
6672
void dump() const;

include/swift/SIL/TypeLowering.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,8 @@ class TypeConverter {
983983
CanSILBoxType getBoxTypeForEnumElement(SILType enumType,
984984
EnumElementDecl *elt);
985985

986+
bool canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl);
987+
986988
private:
987989
CanType getLoweredRValueType(AbstractionPattern origType, CanType substType);
988990

lib/AST/Decl.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,7 +1414,8 @@ ValueDecl::getAccessSemanticsFromContext(const DeclContext *UseDC,
14141414

14151415
AccessStrategy
14161416
AbstractStorageDecl::getAccessStrategy(AccessSemantics semantics,
1417-
AccessKind accessKind) const {
1417+
AccessKind accessKind,
1418+
DeclContext *accessFromDC) const {
14181419
switch (semantics) {
14191420
case AccessSemantics::DirectToStorage:
14201421
switch (getStorageKind()) {
@@ -1481,7 +1482,14 @@ AbstractStorageDecl::getAccessStrategy(AccessSemantics semantics,
14811482
// This is done by using DirectToStorage semantics above, with the
14821483
// understanding that the access semantics are with respect to the
14831484
// resilience domain of the accessor's caller.
1484-
if (isResilient())
1485+
bool resilient;
1486+
if (accessFromDC)
1487+
resilient = isResilient(accessFromDC->getParentModule(),
1488+
ResilienceExpansion::Maximal);
1489+
else
1490+
resilient = isResilient();
1491+
1492+
if (resilient)
14851493
return AccessStrategy::DirectToAccessor;
14861494

14871495
if (storageKind == StoredWithObservers ||

lib/IRGen/GenKeyPath.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,33 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern,
12181218
}
12191219

12201220
void IRGenModule::emitSILProperty(SILProperty *prop) {
1221+
if (prop->isTrivial()) {
1222+
// All trivial property descriptors can share a single definition in the
1223+
// translation unit.
1224+
if (!TheTrivialPropertyDescriptor) {
1225+
// Emit a definition if we don't have one yet.
1226+
ConstantInitBuilder builder(*this);
1227+
ConstantStructBuilder fields = builder.beginStruct();
1228+
fields.addInt32(
1229+
_SwiftKeyPathComponentHeader_TrivialPropertyDescriptorMarker);
1230+
auto var = cast<llvm::GlobalVariable>(
1231+
getAddrOfPropertyDescriptor(prop->getDecl(),
1232+
fields.finishAndCreateFuture()));
1233+
var->setConstant(true);
1234+
var->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
1235+
var->setAlignment(4);
1236+
1237+
TheTrivialPropertyDescriptor = var;
1238+
} else {
1239+
auto entity = LinkEntity::forPropertyDescriptor(prop->getDecl());
1240+
auto linkInfo = LinkInfo::get(*this, entity, ForDefinition);
1241+
llvm::GlobalAlias::create(linkInfo.getLinkage(),
1242+
linkInfo.getName(),
1243+
TheTrivialPropertyDescriptor);
1244+
}
1245+
return;
1246+
}
1247+
12211248
ConstantInitBuilder builder(*this);
12221249
ConstantStructBuilder fields = builder.beginStruct();
12231250
fields.setPacked(true);
@@ -1245,7 +1272,7 @@ void IRGenModule::emitSILProperty(SILProperty *prop) {
12451272
[&](GenericRequirement reqt) { requirements.push_back(reqt); });
12461273
}
12471274

1248-
emitKeyPathComponent(*this, fields, prop->getComponent(),
1275+
emitKeyPathComponent(*this, fields, *prop->getComponent(),
12491276
isInstantiableInPlace, genericEnv, requirements,
12501277
prop->getDecl()->getInnermostDeclContext()
12511278
->getInnermostTypeContext()
@@ -1260,6 +1287,7 @@ void IRGenModule::emitSILProperty(SILProperty *prop) {
12601287
getAddrOfPropertyDescriptor(prop->getDecl(),
12611288
fields.finishAndCreateFuture()));
12621289
var->setConstant(true);
1290+
var->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
12631291
// A simple stored component descriptor can fit in four bytes. Anything else
12641292
// needs pointer alignment.
12651293
if (size <= Size(4))

lib/IRGen/IRGenModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,8 @@ class IRGenModule {
570570
llvm::PointerType *OpenedErrorTriplePtrTy; /// { %swift.opaque*, %swift.type*, i8** }*
571571
llvm::PointerType *WitnessTablePtrPtrTy; /// i8***
572572

573+
llvm::GlobalVariable *TheTrivialPropertyDescriptor = nullptr;
574+
573575
/// Used to create unique names for class layout types with tail allocated
574576
/// elements.
575577
unsigned TailElemTypeID = 0;

lib/ParseSIL/ParseSIL.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5464,19 +5464,26 @@ bool SILParserTUState::parseSILProperty(Parser &P) {
54645464
}
54655465

54665466
Identifier ComponentKind;
5467-
KeyPathPatternComponent Component;
5467+
Optional<KeyPathPatternComponent> Component;
54685468
SourceLoc ComponentLoc;
54695469
SmallVector<SILType, 4> OperandTypes;
54705470

5471-
if (P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")
5472-
|| P.parseIdentifier(ComponentKind, ComponentLoc,
5473-
diag::expected_tok_in_sil_instr, "component kind")
5474-
|| SP.parseKeyPathPatternComponent(Component, OperandTypes,
5475-
ComponentLoc, ComponentKind, InstLoc,
5476-
patternEnv)
5477-
|| P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")"))
5471+
if (P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "("))
54785472
return true;
54795473

5474+
if (!P.consumeIf(tok::r_paren)) {
5475+
KeyPathPatternComponent parsedComponent;
5476+
if (P.parseIdentifier(ComponentKind, ComponentLoc,
5477+
diag::expected_tok_in_sil_instr, "component kind")
5478+
|| SP.parseKeyPathPatternComponent(parsedComponent, OperandTypes,
5479+
ComponentLoc, ComponentKind, InstLoc,
5480+
patternEnv)
5481+
|| P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")"))
5482+
return true;
5483+
5484+
Component = std::move(parsedComponent);
5485+
}
5486+
54805487
SILProperty::create(M, Serialized,
54815488
cast<AbstractStorageDecl>(VD), Component);
54825489
return false;

lib/SIL/SILModule.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ void SILModule::setOptRecordStream(
849849
SILProperty *SILProperty::create(SILModule &M,
850850
bool Serialized,
851851
AbstractStorageDecl *Decl,
852-
KeyPathPatternComponent Component) {
852+
Optional<KeyPathPatternComponent> Component) {
853853
auto prop = new (M) SILProperty(Serialized, Decl, Component);
854854
M.properties.push_back(prop);
855855
return prop;

lib/SIL/SILPrinter.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2648,10 +2648,10 @@ void SILProperty::print(SILPrintContext &Ctx) const {
26482648
if (auto sig = getDecl()->getInnermostDeclContext()
26492649
->getGenericSignatureOfContext()) {
26502650
sig->getCanonicalSignature()->print(OS, Options);
2651-
OS << ' ';
26522651
}
2653-
OS << '(';
2654-
SILPrinter(Ctx).printKeyPathPatternComponent(getComponent());
2652+
OS << " (";
2653+
if (auto component = getComponent())
2654+
SILPrinter(Ctx).printKeyPathPatternComponent(*component);
26552655
OS << ")\n";
26562656
}
26572657

lib/SIL/SILVerifier.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4814,7 +4814,6 @@ void SILProperty::verify(const SILModule &M) const {
48144814

48154815
auto *decl = getDecl();
48164816
auto *dc = decl->getInnermostDeclContext();
4817-
auto &component = getComponent();
48184817

48194818
// TODO: base type for global/static descriptors
48204819
auto sig = dc->getGenericSignatureOfContext();
@@ -4844,21 +4843,22 @@ void SILProperty::verify(const SILModule &M) const {
48444843
}
48454844
};
48464845

4847-
verifyKeyPathComponent(const_cast<SILModule&>(M),
4848-
require,
4849-
baseTy,
4850-
leafTy,
4851-
component,
4852-
{},
4853-
canSig,
4854-
subs,
4855-
/*property descriptor*/true,
4856-
hasIndices);
4857-
4858-
// verifyKeyPathComponent updates baseTy to be the projected type of the
4859-
// component, which should be the same as the type of the declared storage
4860-
require(baseTy == leafTy,
4861-
"component type of property descriptor should match type of storage");
4846+
if (auto &component = getComponent()) {
4847+
verifyKeyPathComponent(const_cast<SILModule&>(M),
4848+
require,
4849+
baseTy,
4850+
leafTy,
4851+
*component,
4852+
{},
4853+
canSig,
4854+
subs,
4855+
/*property descriptor*/true,
4856+
hasIndices);
4857+
// verifyKeyPathComponent updates baseTy to be the projected type of the
4858+
// component, which should be the same as the type of the declared storage
4859+
require(baseTy == leafTy,
4860+
"component type of property descriptor should match type of storage");
4861+
}
48624862
}
48634863

48644864
/// Verify that a vtable follows invariants.

0 commit comments

Comments
 (0)