Skip to content

Commit aa5a4e1

Browse files
committed
GlobalOpt: optimize static properties with resilient types.
For example: struct ResilientStruct { var singleField: Int public static let x = ResilientStruct(singleField: 27) } The generated (resilient) getter for x should just return a 27 (wrapped in a struct) and not lazily initialize a global variable. rdar://problem/46712028
1 parent 5a91d13 commit aa5a4e1

File tree

2 files changed

+95
-7
lines changed

2 files changed

+95
-7
lines changed

lib/SILOptimizer/IPO/GlobalOpt.cpp

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ class SILGlobalOpt {
137137
/// can be statically initialized.
138138
void optimizeInitializer(SILFunction *AddrF, GlobalInitCalls &Calls);
139139

140+
/// Perform peephole optimizations on the initializer list.
141+
void peepholeInitializer(SILFunction *InitFunc);
142+
140143
/// Optimize access to the global variable, which is known to have a constant
141144
/// value. Replace all loads from the global address by invocations of a
142145
/// getter that returns the value of this variable.
@@ -253,12 +256,14 @@ static SILFunction *getGlobalGetterFunction(SILOptFunctionBuilder &FunctionBuild
253256
if (auto *F = M.lookUpFunction(getterNameTmp))
254257
return F;
255258

256-
auto Linkage = (varDecl->getEffectiveAccess() >= AccessLevel::Public
257-
? SILLinkage::PublicNonABI
258-
: SILLinkage::Private);
259-
auto Serialized = (varDecl->getEffectiveAccess() >= AccessLevel::Public
260-
? IsSerialized
261-
: IsNotSerialized);
259+
auto Linkage = SILLinkage::Private;
260+
auto Serialized = IsNotSerialized;
261+
262+
if (varDecl->getEffectiveAccess() >= AccessLevel::Public &&
263+
!varDecl->isResilient()) {
264+
Linkage = SILLinkage::PublicNonABI;
265+
Serialized = IsSerialized;
266+
}
262267

263268
auto refType = M.Types.getLoweredType(varDecl->getInterfaceType());
264269

@@ -690,7 +695,17 @@ replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF,
690695
auto *PTAI = dyn_cast<PointerToAddressInst>(Use->getUser());
691696
assert(PTAI && "All uses should be pointer_to_address");
692697
for (auto PTAIUse : PTAI->getUses()) {
693-
replaceLoadSequence(PTAIUse->getUser(), NewAI, B);
698+
SILInstruction *Load = PTAIUse->getUser();
699+
if (auto *CA = dyn_cast<CopyAddrInst>(Load)) {
700+
// The result of the initializer is stored to another location.
701+
SILBuilder B(CA);
702+
B.createStore(CA->getLoc(), NewAI, CA->getDest(),
703+
StoreOwnershipQualifier::Unqualified);
704+
CA->eraseFromParent();
705+
} else {
706+
// The result of the initializer is used as a value.
707+
replaceLoadSequence(Load, NewAI, B);
708+
}
694709
}
695710
}
696711

@@ -721,6 +736,8 @@ void SILGlobalOpt::optimizeInitializer(SILFunction *AddrF,
721736
InitializerCount[InitF] > 1)
722737
return;
723738

739+
peepholeInitializer(InitF);
740+
724741
// If the globalinit_func is trivial, continue; otherwise bail.
725742
SingleValueInstruction *InitVal;
726743
SILGlobalVariable *SILG = getVariableOfStaticInitializer(InitF, InitVal);
@@ -743,6 +760,58 @@ void SILGlobalOpt::optimizeInitializer(SILFunction *AddrF,
743760
HasChanged = true;
744761
}
745762

763+
void SILGlobalOpt::peepholeInitializer(SILFunction *InitFunc) {
764+
if (InitFunc->size() != 1)
765+
return;
766+
SILBasicBlock *BB = &InitFunc->front();
767+
768+
for (auto &I : *BB) {
769+
if (auto *SI = dyn_cast<StoreInst>(&I)) {
770+
771+
// If struct S has a single field, replace
772+
// %a = struct_element_addr %s : $*S
773+
// store %x to %a
774+
// with
775+
// %y = struct $S (%x)
776+
// store %y to %s
777+
//
778+
// This pattern occurs with resilient static properties, like
779+
// struct ResilientStruct {
780+
// var singleField: Int
781+
// public static let x = ResilientStruct(singleField: 27)
782+
// }
783+
//
784+
// TODO: handle structs with multiple fields.
785+
SILValue Addr = SI->getDest();
786+
auto *SEA = dyn_cast<StructElementAddrInst>(Addr);
787+
if (!SEA)
788+
continue;
789+
790+
if (SEA->getOperand()->getType().isAddressOnly(InitFunc))
791+
continue;
792+
793+
StructDecl *Decl = SEA->getStructDecl();
794+
auto beginProp = Decl->getStoredProperties().begin();
795+
if (std::next(beginProp) != Decl->getStoredProperties().end())
796+
continue;
797+
798+
assert(*beginProp == SEA->getField());
799+
800+
SILBuilder B(SI);
801+
SILValue StructAddr = SEA->getOperand();
802+
StructInst *Struct = B.createStruct(SEA->getLoc(),
803+
StructAddr->getType().getObjectType(),
804+
{ SI->getSrc() });
805+
SI->setOperand(StoreInst::Src, Struct);
806+
SI->setOperand(StoreInst::Dest, StructAddr);
807+
if (SEA->use_empty()) {
808+
SEA->eraseFromParent();
809+
}
810+
HasChanged = true;
811+
}
812+
}
813+
}
814+
746815
static bool canBeChangedExternally(SILGlobalVariable *SILG) {
747816
// Don't assume anything about globals which are imported from other modules.
748817
if (isAvailableExternally(SILG->getLinkage()))
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-swift-frontend -O -module-name=test -enable-resilience -emit-sil -primary-file %s | %FileCheck %s
2+
3+
// Check if GlobalOpt generates the optimal getter for a static property with a resilient type.
4+
// The (resilient) getter should just return the literal (and not lazily initialize a global variable).
5+
6+
// CHECK-LABEL: sil @$s4test15ResilientStructV9staticValACvgZ :
7+
// CHECK: bb0(%0 : $*ResilientStruct{{.*}}):
8+
// CHECK: [[L:%[0-9]+]] = integer_literal {{.*}}, 27
9+
// CHECK: [[I:%[0-9]+]] = struct $Int ([[L]] : {{.*}})
10+
// CHECK: [[S:%[0-9]+]] = struct $ResilientStruct ([[I]] : $Int)
11+
// CHECK: store [[S]] to %0
12+
// CHECK: return
13+
14+
public struct ResilientStruct {
15+
var rawValue: Int
16+
17+
public static let staticVal = ResilientStruct(rawValue: 27)
18+
}
19+

0 commit comments

Comments
 (0)