Skip to content

Commit 23f7c4f

Browse files
committed
SILOptimizer: Fix potential crashes with uses of replaceLoadSequence()
This utility is generally a horrible idea but even worse the callers were not doing anything to ensure the required invariants actually held. Add a new canReplaceLoadSequence() method and chek it in the right places.
1 parent c05013f commit 23f7c4f

File tree

5 files changed

+93
-28
lines changed

5 files changed

+93
-28
lines changed

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -535,13 +535,15 @@ bool isSimpleType(SILType SILTy, SILModule& Module);
535535
bool analyzeStaticInitializer(SILValue V,
536536
SmallVectorImpl<SILInstruction *> &Insns);
537537

538+
/// Returns true if the below operation will succeed.
539+
bool canReplaceLoadSequence(SILInstruction *I);
540+
538541
/// Replace load sequence which may contain
539542
/// a chain of struct_element_addr followed by a load.
540543
/// The sequence is traversed inside out, i.e.
541544
/// starting with the innermost struct_element_addr
542545
void replaceLoadSequence(SILInstruction *I,
543-
SILValue Value,
544-
SILBuilder &B);
546+
SILValue Value);
545547

546548

547549
/// Do we have enough information to determine all callees that could

lib/SILOptimizer/IPO/GlobalOpt.cpp

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -651,15 +651,23 @@ replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF,
651651
// Generate a getter from InitF which returns the value of the global.
652652
auto *GetterF = genGetterFromInit(FunctionBuilder, InitF, SILG->getDecl());
653653

654-
// Replace all calls of an addressor by calls of a getter .
654+
// Replace all calls of an addressor by calls of a getter.
655655
for (int i = 0, e = Calls.size(); i < e; ++i) {
656656
auto *Call = Calls[i];
657657

658-
// Now find all uses of Call. They all should be loads, so that
659-
// we can replace it.
658+
// Make sure that we can go ahead and replace all uses of the
659+
// address with the value.
660660
bool isValid = true;
661661
for (auto Use : Call->getUses()) {
662-
if (!isa<PointerToAddressInst>(Use->getUser())) {
662+
if (auto *PTAI = dyn_cast<PointerToAddressInst>(Use->getUser())) {
663+
for (auto PTAIUse : PTAI->getUses()) {
664+
SILInstruction *Use = PTAIUse->getUser();
665+
if (!canReplaceLoadSequence(Use)) {
666+
isValid = false;
667+
break;
668+
}
669+
}
670+
} else {
663671
isValid = false;
664672
break;
665673
}
@@ -668,6 +676,8 @@ replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF,
668676
if (!isValid)
669677
continue;
670678

679+
// Now find all uses of Call. They all should be loads, so that
680+
// we can replace it.
671681
SILBuilderWithScope B(Call);
672682
SmallVector<SILValue, 1> Args;
673683
auto *GetterRef = B.createFunctionRef(Call->getLoc(), GetterF);
@@ -681,16 +691,10 @@ replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF,
681691
auto *PTAI = dyn_cast<PointerToAddressInst>(Use->getUser());
682692
assert(PTAI && "All uses should be pointer_to_address");
683693
for (auto PTAIUse : PTAI->getUses()) {
684-
SILInstruction *Load = PTAIUse->getUser();
685-
if (auto *CA = dyn_cast<CopyAddrInst>(Load)) {
686-
// The result of the initializer is stored to another location.
687-
SILBuilder B(CA);
688-
B.createStore(CA->getLoc(), NewAI, CA->getDest(),
689-
StoreOwnershipQualifier::Unqualified);
690-
} else {
691-
// The result of the initializer is used as a value.
692-
replaceLoadSequence(Load, NewAI, B);
693-
}
694+
SILInstruction *Use = PTAIUse->getUser();
695+
696+
// The result of the getter is used as a value.
697+
replaceLoadSequence(Use, NewAI);
694698
}
695699
}
696700

lib/SILOptimizer/IPO/LetPropertiesOpts.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,22 +210,18 @@ void LetPropertiesOpt::optimizeLetPropertyAccess(VarDecl *Property,
210210
// Replace the access to a let property by the value
211211
// computed by this initializer.
212212
SILValue clonedInit = cloneInitAt(proj);
213-
SILBuilderWithScope B(proj);
214213
for (auto UI = proj->use_begin(), E = proj->use_end(); UI != E;) {
215214
auto *User = UI->getUser();
216215
++UI;
217216

218-
if (isIncidentalUse(User))
219-
continue;
220-
221217
// A nested begin_access will be mapped as a separate "Load".
222218
if (isa<BeginAccessInst>(User))
223219
continue;
224220

225-
if (isa<StoreInst>(User))
221+
if (!canReplaceLoadSequence(User))
226222
continue;
227223

228-
replaceLoadSequence(User, clonedInit, B);
224+
replaceLoadSequence(User, clonedInit);
229225
eraseUsesOfInstruction(User);
230226
User->eraseFromParent();
231227
++NumReplaced;

lib/SILOptimizer/Utils/Local.cpp

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,6 +1624,47 @@ bool swift::analyzeStaticInitializer(
16241624
return StaticInitializerAnalysis(forwardInstructions).analyze(V);
16251625
}
16261626

1627+
/// FIXME: This must be kept in sync with replaceLoadSequence()
1628+
/// below. What a horrible design.
1629+
bool swift::canReplaceLoadSequence(SILInstruction *I) {
1630+
if (auto *CAI = dyn_cast<CopyAddrInst>(I))
1631+
return true;
1632+
1633+
if (auto *LI = dyn_cast<LoadInst>(I))
1634+
return true;
1635+
1636+
if (auto *SEAI = dyn_cast<StructElementAddrInst>(I)) {
1637+
for (auto SEAIUse : SEAI->getUses()) {
1638+
if (!canReplaceLoadSequence(SEAIUse->getUser()))
1639+
return false;
1640+
}
1641+
return true;
1642+
}
1643+
1644+
if (auto *TEAI = dyn_cast<TupleElementAddrInst>(I)) {
1645+
for (auto TEAIUse : TEAI->getUses()) {
1646+
if (!canReplaceLoadSequence(TEAIUse->getUser()))
1647+
return false;
1648+
}
1649+
return true;
1650+
}
1651+
1652+
if (auto *BA = dyn_cast<BeginAccessInst>(I)) {
1653+
for (auto Use : BA->getUses()) {
1654+
if (!canReplaceLoadSequence(Use->getUser()))
1655+
return false;
1656+
}
1657+
return true;
1658+
}
1659+
1660+
// Incidental uses of an address are meaningless with regard to the loaded
1661+
// value.
1662+
if (isIncidentalUse(I) || isa<BeginUnpairedAccessInst>(I))
1663+
return true;
1664+
1665+
return false;
1666+
}
1667+
16271668
/// Replace load sequence which may contain
16281669
/// a chain of struct_element_addr followed by a load.
16291670
/// The sequence is traversed inside out, i.e.
@@ -1634,33 +1675,40 @@ bool swift::analyzeStaticInitializer(
16341675
/// guarantee that the only uses of `I` are struct_element_addr and
16351676
/// tuple_element_addr?
16361677
void swift::replaceLoadSequence(SILInstruction *I,
1637-
SILValue Value,
1638-
SILBuilder &B) {
1678+
SILValue Value) {
1679+
if (auto *CAI = dyn_cast<CopyAddrInst>(I)) {
1680+
SILBuilder B(CAI);
1681+
B.createStore(CAI->getLoc(), Value, CAI->getDest(),
1682+
StoreOwnershipQualifier::Unqualified);
1683+
return;
1684+
}
1685+
16391686
if (auto *LI = dyn_cast<LoadInst>(I)) {
16401687
LI->replaceAllUsesWith(Value);
16411688
return;
16421689
}
16431690

1644-
// It is a series of struct_element_addr followed by load.
16451691
if (auto *SEAI = dyn_cast<StructElementAddrInst>(I)) {
1692+
SILBuilder B(SEAI);
16461693
auto *SEI = B.createStructExtract(SEAI->getLoc(), Value, SEAI->getField());
16471694
for (auto SEAIUse : SEAI->getUses()) {
1648-
replaceLoadSequence(SEAIUse->getUser(), SEI, B);
1695+
replaceLoadSequence(SEAIUse->getUser(), SEI);
16491696
}
16501697
return;
16511698
}
16521699

16531700
if (auto *TEAI = dyn_cast<TupleElementAddrInst>(I)) {
1701+
SILBuilder B(TEAI);
16541702
auto *TEI = B.createTupleExtract(TEAI->getLoc(), Value, TEAI->getFieldNo());
16551703
for (auto TEAIUse : TEAI->getUses()) {
1656-
replaceLoadSequence(TEAIUse->getUser(), TEI, B);
1704+
replaceLoadSequence(TEAIUse->getUser(), TEI);
16571705
}
16581706
return;
16591707
}
16601708

16611709
if (auto *BA = dyn_cast<BeginAccessInst>(I)) {
16621710
for (auto Use : BA->getUses()) {
1663-
replaceLoadSequence(Use->getUser(), Value, B);
1711+
replaceLoadSequence(Use->getUser(), Value);
16641712
}
16651713
return;
16661714
}

test/SILOptimizer/globalopt_resilience.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,20 @@ public struct ResilientStruct {
1515
var rawValue: Int
1616

1717
public static let staticVal = ResilientStruct(rawValue: 27)
18+
19+
@_optimize(none)
20+
public func method() {}
21+
}
22+
23+
public func cannotConvertToValueUse() {
24+
// We can't optimize this because the method takes the resilient value as
25+
// @in_guaranteed
26+
ResilientStruct.staticVal.method()
1827
}
1928

29+
// CHECK-LABEL: sil @$s4test23cannotConvertToValueUseyyF : $@convention(thin) () -> ()
30+
// CHECK: [[ADDR:%.*]] = global_addr @$s4test15ResilientStructV9staticValACvpZ : $*ResilientStruct
31+
// CHECK: [[METHOD:%.*]] = function_ref @$s4test15ResilientStructV6methodyyF : $@convention(method) (@in_guaranteed ResilientStruct) -> ()
32+
// CHECK: apply [[METHOD]]([[ADDR]]) : $@convention(method) (@in_guaranteed ResilientStruct) -> ()
33+
// CHECK: [[RESULT:%.*]] = tuple ()
34+
// CHECK: return [[RESULT]] : $()

0 commit comments

Comments
 (0)