Skip to content

Commit 0787072

Browse files
authored
Merge pull request #35246 from gottesmm/ossa-sil-combine-final
[sil-combine] Update sil combine for Ownership [part 1]
2 parents 2b313c5 + b206d00 commit 0787072

24 files changed

+8707
-86
lines changed

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,11 @@ OPERAND_OWNERSHIP(InstantaneousUse, ValueMetatype)
186186
OPERAND_OWNERSHIP(InstantaneousUse, IsEscapingClosure)
187187
OPERAND_OWNERSHIP(InstantaneousUse, ClassMethod)
188188
OPERAND_OWNERSHIP(InstantaneousUse, SuperMethod)
189-
OPERAND_OWNERSHIP(InstantaneousUse, BridgeObjectToWord)
190189
OPERAND_OWNERSHIP(InstantaneousUse, ClassifyBridgeObject)
191190
OPERAND_OWNERSHIP(InstantaneousUse, SetDeallocating)
192191
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
193-
OPERAND_OWNERSHIP(InstantaneousUse, RefTo##Name) \
194-
OPERAND_OWNERSHIP(InstantaneousUse, Name##ToRef) \
195192
OPERAND_OWNERSHIP(InstantaneousUse, StrongCopy##Name##Value)
196193
#define UNCHECKED_REF_STORAGE(Name, ...) \
197-
OPERAND_OWNERSHIP(InstantaneousUse, RefTo##Name) \
198194
OPERAND_OWNERSHIP(InstantaneousUse, StrongCopy##Name##Value)
199195
#include "swift/AST/ReferenceStorage.def"
200196

@@ -208,6 +204,16 @@ OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedRetainValue)
208204
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedReleaseValue)
209205
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedAutoreleaseValue)
210206

207+
// These act as a form of conversion that does not imply ownership. Thus from an
208+
// operand perspective we treat them as a pointer escape and from a value
209+
// perspective, they return a value with OwnershipKind::Unowned.
210+
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
211+
OPERAND_OWNERSHIP(PointerEscape, RefTo##Name) \
212+
OPERAND_OWNERSHIP(PointerEscape, Name##ToRef)
213+
#define UNCHECKED_REF_STORAGE(Name, ...) \
214+
OPERAND_OWNERSHIP(PointerEscape, RefTo##Name)
215+
#include "swift/AST/ReferenceStorage.def"
216+
211217
// Instructions that currently violate structural ownership requirements,
212218
// and therefore completely defeat canonicalization and optimization of any
213219
// OSSA value that they use.
@@ -222,6 +228,7 @@ OPERAND_OWNERSHIP(BitwiseEscape, UncheckedBitwiseCast)
222228
OPERAND_OWNERSHIP(BitwiseEscape, ValueToBridgeObject)
223229
OPERAND_OWNERSHIP(BitwiseEscape, RefToRawPointer)
224230
OPERAND_OWNERSHIP(BitwiseEscape, UncheckedTrivialBitCast)
231+
OPERAND_OWNERSHIP(BitwiseEscape, BridgeObjectToWord)
225232

226233
// Instructions that end the lifetime of an owned value.
227234
OPERAND_OWNERSHIP(DestroyingConsume, AutoreleaseValue)

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,12 @@ class SILCombiner :
220220
SILInstruction *visitStrongRetainInst(StrongRetainInst *SRI);
221221
SILInstruction *visitRefToRawPointerInst(RefToRawPointerInst *RRPI);
222222
SILInstruction *visitUpcastInst(UpcastInst *UCI);
223-
SILInstruction *optimizeLoadFromStringLiteral(LoadInst *LI);
223+
224+
// NOTE: The load optimized in this method is a load [trivial].
225+
SILInstruction *optimizeLoadFromStringLiteral(LoadInst *li);
226+
224227
SILInstruction *visitLoadInst(LoadInst *LI);
228+
SILInstruction *visitLoadBorrowInst(LoadBorrowInst *LI);
225229
SILInstruction *visitIndexAddrInst(IndexAddrInst *IA);
226230
bool optimizeStackAllocatedEnum(AllocStackInst *AS);
227231
SILInstruction *visitAllocStackInst(AllocStackInst *AS);

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 152 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,6 @@ static EnumElementDecl *getInjectEnumCaseTo(SILValue Addr) {
147147
}
148148

149149
SILInstruction *SILCombiner::visitSwitchEnumAddrInst(SwitchEnumAddrInst *SEAI) {
150-
if (SEAI->getFunction()->hasOwnership())
151-
return nullptr;
152-
153150
// Convert switch_enum_addr -> br
154151
// if the only thing which writes to the address is an inject_enum_addr.
155152
SILValue Addr = SEAI->getOperand();
@@ -172,41 +169,66 @@ SILInstruction *SILCombiner::visitSwitchEnumAddrInst(SwitchEnumAddrInst *SEAI) {
172169
// ->
173170
// %value = load %ptr
174171
// switch_enum %value
175-
SmallVector<std::pair<EnumElementDecl*, SILBasicBlock*>, 8> Cases;
176-
for (int i = 0, e = SEAI->getNumCases(); i < e; ++i)
172+
//
173+
// If we are using ownership, we perform a load_borrow right before the new
174+
// switch_enum and end the borrow scope right afterwards.
175+
Builder.setCurrentDebugScope(SEAI->getDebugScope());
176+
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 8> Cases;
177+
for (int i : range(SEAI->getNumCases())) {
177178
Cases.push_back(SEAI->getCase(i));
179+
}
178180

179-
Builder.setCurrentDebugScope(SEAI->getDebugScope());
180181
SILBasicBlock *Default = SEAI->hasDefault() ? SEAI->getDefaultBB() : nullptr;
181-
LoadInst *EnumVal = Builder.createLoad(SEAI->getLoc(), Addr,
182-
LoadOwnershipQualifier::Unqualified);
183-
Builder.createSwitchEnum(SEAI->getLoc(), EnumVal, Default, Cases);
182+
SILValue EnumVal = Builder.emitLoadBorrowOperation(SEAI->getLoc(), Addr);
183+
auto *sei = Builder.createSwitchEnum(SEAI->getLoc(), EnumVal, Default, Cases);
184+
185+
if (Builder.hasOwnership()) {
186+
for (int i : range(sei->getNumCases())) {
187+
auto c = sei->getCase(i);
188+
if (c.first->hasAssociatedValues()) {
189+
auto eltType = Addr->getType().getEnumElementType(
190+
c.first, Builder.getModule(), Builder.getTypeExpansionContext());
191+
eltType = eltType.getObjectType();
192+
if (eltType.isTrivial(Builder.getFunction())) {
193+
c.second->createPhiArgument(eltType, OwnershipKind::None);
194+
} else {
195+
c.second->createPhiArgument(eltType, OwnershipKind::Guaranteed);
196+
}
197+
}
198+
Builder.setInsertionPoint(c.second->front().getIterator());
199+
Builder.emitEndBorrowOperation(SEAI->getLoc(), EnumVal);
200+
}
201+
202+
if (auto defaultBlock = sei->getDefaultBBOrNull()) {
203+
defaultBlock.get()->createPhiArgument(EnumVal->getType(),
204+
OwnershipKind::Guaranteed);
205+
Builder.setInsertionPoint(defaultBlock.get()->front().getIterator());
206+
Builder.emitEndBorrowOperation(SEAI->getLoc(), EnumVal);
207+
}
208+
}
209+
184210
return eraseInstFromFunction(*SEAI);
185211
}
186212

187-
SILInstruction *SILCombiner::visitSelectEnumAddrInst(SelectEnumAddrInst *SEAI) {
188-
if (SEAI->getFunction()->hasOwnership())
189-
return nullptr;
190-
213+
SILInstruction *SILCombiner::visitSelectEnumAddrInst(SelectEnumAddrInst *seai) {
191214
// Canonicalize a select_enum_addr: if the default refers to exactly one case,
192215
// then replace the default with that case.
193-
Builder.setCurrentDebugScope(SEAI->getDebugScope());
194-
if (SEAI->hasDefault()) {
195-
NullablePtr<EnumElementDecl> elementDecl = SEAI->getUniqueCaseForDefault();
216+
Builder.setCurrentDebugScope(seai->getDebugScope());
217+
if (seai->hasDefault()) {
218+
NullablePtr<EnumElementDecl> elementDecl = seai->getUniqueCaseForDefault();
196219
if (elementDecl.isNonNull()) {
197220
// Construct a new instruction by copying all the case entries.
198-
SmallVector<std::pair<EnumElementDecl *, SILValue>, 4> CaseValues;
199-
for (int idx = 0, numIdcs = SEAI->getNumCases(); idx < numIdcs; ++idx) {
200-
CaseValues.push_back(SEAI->getCase(idx));
221+
SmallVector<std::pair<EnumElementDecl *, SILValue>, 4> caseValues;
222+
for (int idx = 0, numIdcs = seai->getNumCases(); idx < numIdcs; ++idx) {
223+
caseValues.push_back(seai->getCase(idx));
201224
}
202225
// Add the default-entry of the original instruction as case-entry.
203-
CaseValues.push_back(
204-
std::make_pair(elementDecl.get(), SEAI->getDefaultResult()));
226+
caseValues.push_back(
227+
std::make_pair(elementDecl.get(), seai->getDefaultResult()));
205228

206-
return Builder.createSelectEnumAddr(SEAI->getLoc(),
207-
SEAI->getEnumOperand(),
208-
SEAI->getType(), SILValue(),
209-
CaseValues);
229+
return Builder.createSelectEnumAddr(
230+
seai->getLoc(), seai->getEnumOperand(), seai->getType(), SILValue(),
231+
caseValues);
210232
}
211233
}
212234

@@ -215,63 +237,65 @@ SILInstruction *SILCombiner::visitSelectEnumAddrInst(SelectEnumAddrInst *SEAI) {
215237
// ->
216238
// %value = load %ptr
217239
// = select_enum %value
218-
SILType Ty = SEAI->getEnumOperand()->getType();
219-
if (!Ty.isLoadable(*SEAI->getFunction()))
240+
SILType ty = seai->getEnumOperand()->getType();
241+
if (!ty.isLoadable(*seai->getFunction()))
220242
return nullptr;
221243

222-
SmallVector<std::pair<EnumElementDecl*, SILValue>, 8> Cases;
223-
for (int i = 0, e = SEAI->getNumCases(); i < e; ++i)
224-
Cases.push_back(SEAI->getCase(i));
225-
226-
SILValue Default = SEAI->hasDefault() ? SEAI->getDefaultResult() : SILValue();
227-
LoadInst *EnumVal = Builder.createLoad(SEAI->getLoc(), SEAI->getEnumOperand(),
228-
LoadOwnershipQualifier::Unqualified);
229-
auto *I = Builder.createSelectEnum(SEAI->getLoc(), EnumVal, SEAI->getType(),
230-
Default, Cases);
231-
return I;
244+
SmallVector<std::pair<EnumElementDecl *, SILValue>, 8> cases;
245+
for (int i = 0, e = seai->getNumCases(); i < e; ++i)
246+
cases.push_back(seai->getCase(i));
247+
248+
SILValue defaultCase =
249+
seai->hasDefault() ? seai->getDefaultResult() : SILValue();
250+
auto enumVal =
251+
Builder.emitLoadBorrowOperation(seai->getLoc(), seai->getEnumOperand());
252+
auto *result = Builder.createSelectEnum(seai->getLoc(), enumVal,
253+
seai->getType(), defaultCase, cases);
254+
Builder.emitEndBorrowOperation(seai->getLoc(), enumVal);
255+
replaceInstUsesWith(*seai, result);
256+
return eraseInstFromFunction(*seai);
232257
}
233258

234-
SILInstruction *SILCombiner::visitSwitchValueInst(SwitchValueInst *SVI) {
235-
if (SVI->getFunction()->hasOwnership())
259+
SILInstruction *SILCombiner::visitSwitchValueInst(SwitchValueInst *svi) {
260+
SILValue cond = svi->getOperand();
261+
BuiltinIntegerType *condTy = cond->getType().getAs<BuiltinIntegerType>();
262+
if (!condTy || !condTy->isFixedWidth(1))
236263
return nullptr;
237264

238-
SILValue Cond = SVI->getOperand();
239-
BuiltinIntegerType *CondTy = Cond->getType().getAs<BuiltinIntegerType>();
240-
if (!CondTy || !CondTy->isFixedWidth(1))
241-
return nullptr;
242-
243-
SILBasicBlock *FalseBB = nullptr;
244-
SILBasicBlock *TrueBB = nullptr;
245-
for (unsigned Idx = 0, Num = SVI->getNumCases(); Idx < Num; ++Idx) {
246-
auto Case = SVI->getCase(Idx);
247-
auto *CaseVal = dyn_cast<IntegerLiteralInst>(Case.first);
248-
if (!CaseVal)
265+
SILBasicBlock *falseBB = nullptr;
266+
SILBasicBlock *trueBB = nullptr;
267+
for (unsigned idx : range(svi->getNumCases())) {
268+
auto switchCase = svi->getCase(idx);
269+
auto *caseVal = dyn_cast<IntegerLiteralInst>(switchCase.first);
270+
if (!caseVal)
249271
return nullptr;
250-
SILBasicBlock *DestBB = Case.second;
251-
assert(DestBB->args_empty() &&
272+
SILBasicBlock *destBB = switchCase.second;
273+
assert(destBB->args_empty() &&
252274
"switch_value case destination cannot take arguments");
253-
if (CaseVal->getValue() == 0) {
254-
assert(!FalseBB && "double case value 0 in switch_value");
255-
FalseBB = DestBB;
275+
if (caseVal->getValue() == 0) {
276+
assert(!falseBB && "double case value 0 in switch_value");
277+
falseBB = destBB;
256278
} else {
257-
assert(!TrueBB && "double case value 1 in switch_value");
258-
TrueBB = DestBB;
279+
assert(!trueBB && "double case value 1 in switch_value");
280+
trueBB = destBB;
259281
}
260282
}
261-
if (SVI->hasDefault()) {
262-
assert(SVI->getDefaultBB()->args_empty() &&
283+
284+
if (svi->hasDefault()) {
285+
assert(svi->getDefaultBB()->args_empty() &&
263286
"switch_value default destination cannot take arguments");
264-
if (!FalseBB) {
265-
FalseBB = SVI->getDefaultBB();
266-
} else if (!TrueBB) {
267-
TrueBB = SVI->getDefaultBB();
287+
if (!falseBB) {
288+
falseBB = svi->getDefaultBB();
289+
} else if (!trueBB) {
290+
trueBB = svi->getDefaultBB();
268291
}
269292
}
270-
if (!FalseBB || !TrueBB)
293+
294+
if (!falseBB || !trueBB)
271295
return nullptr;
272296

273-
Builder.setCurrentDebugScope(SVI->getDebugScope());
274-
return Builder.createCondBranch(SVI->getLoc(), Cond, TrueBB, FalseBB);
297+
Builder.setCurrentDebugScope(svi->getDebugScope());
298+
return Builder.createCondBranch(svi->getLoc(), cond, trueBB, falseBB);
275299
}
276300

277301
namespace {
@@ -759,12 +783,13 @@ SILInstruction *SILCombiner::optimizeLoadFromStringLiteral(LoadInst *LI) {
759783

760784
/// Returns true if \p LI loads a zero integer from the empty Array, Dictionary
761785
/// or Set singleton.
762-
static bool isZeroLoadFromEmptyCollection(LoadInst *LI) {
786+
static bool isZeroLoadFromEmptyCollection(SingleValueInstruction *LI) {
787+
assert(isa<LoadInst>(LI) || isa<LoadBorrowInst>(LI));
763788
auto intTy = LI->getType().getAs<BuiltinIntegerType>();
764789
if (!intTy)
765790
return false;
766-
767-
SILValue addr = LI->getOperand();
791+
792+
SILValue addr = LI->getOperand(0);
768793

769794
// Find the root object of the load-address.
770795
for (;;) {
@@ -805,6 +830,8 @@ static bool isZeroLoadFromEmptyCollection(LoadInst *LI) {
805830
case ValueKind::UpcastInst:
806831
case ValueKind::RawPointerToRefInst:
807832
case ValueKind::AddressToPointerInst:
833+
case ValueKind::BeginBorrowInst:
834+
case ValueKind::CopyValueInst:
808835
case ValueKind::EndCOWMutationInst:
809836
addr = cast<SingleValueInstruction>(addr)->getOperand(0);
810837
break;
@@ -839,15 +866,56 @@ static SingleValueInstruction *getValueFromStaticLet(SILValue v) {
839866
return nullptr;
840867
}
841868

842-
SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) {
843-
if (LI->getFunction()->hasOwnership())
869+
SILInstruction *SILCombiner::visitLoadBorrowInst(LoadBorrowInst *lbi) {
870+
// (load (upcast-ptr %x)) -> (upcast-ref (load %x))
871+
Builder.setCurrentDebugScope(lbi->getDebugScope());
872+
if (auto *ui = dyn_cast<UpcastInst>(lbi->getOperand())) {
873+
// We want to RAUW the current load_borrow with the upcast. To do that
874+
// safely, we need to insert new end_borrow on the new load_borrow, erase
875+
// the end_borrow and then RAUW.
876+
SmallVector<EndBorrowInst *, 32> endBorrowInst;
877+
for (auto *ebi : lbi->getEndBorrows())
878+
endBorrowInst.push_back(ebi);
879+
auto newLBI = Builder.createLoadBorrow(lbi->getLoc(), ui->getOperand());
880+
for (auto *ebi : endBorrowInst) {
881+
SILBuilderWithScope builder(ebi, Builder);
882+
builder.emitEndBorrowOperation(ebi->getLoc(), newLBI);
883+
eraseInstFromFunction(*ebi);
884+
}
885+
auto *uci = Builder.createUpcast(lbi->getLoc(), newLBI, lbi->getType());
886+
replaceInstUsesWith(*lbi, uci);
887+
return eraseInstFromFunction(*lbi);
888+
}
889+
890+
// Constant-propagate the 0 value when loading "count" or "capacity" from the
891+
// empty Array, Set or Dictionary storage.
892+
// On high-level SIL this optimization is also done by the
893+
// ArrayCountPropagation pass, but only for Array. And even for Array it's
894+
// sometimes needed to propagate the empty-array count when high-level
895+
// semantics function are already inlined.
896+
// Note that for non-empty arrays/sets/dictionaries, the count can be
897+
// propagated by redundant load elimination.
898+
if (isZeroLoadFromEmptyCollection(lbi))
899+
return Builder.createIntegerLiteral(lbi->getLoc(), lbi->getType(), 0);
900+
901+
// If we have a load_borrow that only has non_debug end_borrow uses, delete
902+
// it.
903+
if (llvm::all_of(getNonDebugUses(lbi), [](Operand *use) {
904+
return isa<EndBorrowInst>(use->getUser());
905+
})) {
906+
eraseInstIncludingUsers(lbi);
844907
return nullptr;
908+
}
909+
910+
return nullptr;
911+
}
845912

913+
SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) {
846914
// (load (upcast-ptr %x)) -> (upcast-ref (load %x))
847915
Builder.setCurrentDebugScope(LI->getDebugScope());
848916
if (auto *UI = dyn_cast<UpcastInst>(LI->getOperand())) {
849-
auto NewLI = Builder.createLoad(LI->getLoc(), UI->getOperand(),
850-
LoadOwnershipQualifier::Unqualified);
917+
auto NewLI = Builder.emitLoadValueOperation(LI->getLoc(), UI->getOperand(),
918+
LI->getOwnershipQualifier());
851919
return Builder.createUpcast(LI->getLoc(), NewLI, LI->getType());
852920
}
853921

@@ -874,6 +942,17 @@ SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) {
874942
return cloner.clone(initVal);
875943
}
876944

945+
// If we have a load [copy] whose only non-debug users are destroy_value, just
946+
// eliminate it.
947+
if (LI->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) {
948+
if (llvm::all_of(getNonDebugUses(LI), [](Operand *use) {
949+
return isa<DestroyValueInst>(use->getUser());
950+
})) {
951+
eraseInstIncludingUsers(LI);
952+
return nullptr;
953+
}
954+
}
955+
877956
return nullptr;
878957
}
879958

test/SIL/ownership-verifier/use_verifier.sil

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,3 +1417,17 @@ bb3(%fUnknown : @owned $@callee_owned () -> ()):
14171417
%9999 = tuple()
14181418
return %9999 : $()
14191419
}
1420+
1421+
sil [ossa] @unowned_to_ref_is_unowned_instant_use : $@convention(thin) (@guaranteed Builtin.NativeObject) -> Builtin.NativeObject {
1422+
bb0(%0 : @guaranteed $Builtin.NativeObject):
1423+
%1 = ref_to_unowned %0 : $Builtin.NativeObject to $@sil_unowned Builtin.NativeObject
1424+
%2 = unowned_to_ref %1 : $@sil_unowned Builtin.NativeObject to $Builtin.NativeObject
1425+
return %2 : $Builtin.NativeObject
1426+
}
1427+
1428+
sil [ossa] @unmanaged_to_ref_is_unowned_instant_use : $@convention(thin) (@guaranteed Builtin.NativeObject) -> Builtin.NativeObject {
1429+
bb0(%0 : @guaranteed $Builtin.NativeObject):
1430+
%1 = ref_to_unmanaged %0 : $Builtin.NativeObject to $@sil_unmanaged Builtin.NativeObject
1431+
%2 = unmanaged_to_ref %1 : $@sil_unmanaged Builtin.NativeObject to $Builtin.NativeObject
1432+
return %2 : $Builtin.NativeObject
1433+
}

0 commit comments

Comments
 (0)