Skip to content

Commit fdbe872

Browse files
authored
Merge pull request swiftlang#35601 from gottesmm/ossa-sil-combine-3
[sil-combine] More ownership updates
2 parents 769cfce + 04ba6c6 commit fdbe872

File tree

10 files changed

+372
-414
lines changed

10 files changed

+372
-414
lines changed

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ class OwnershipRAUWHelper {
121121
/// "forwarding" transformation must be performed upon \p newValue at \p
122122
/// oldValue's insertion point so that we can then here RAUW the transformed
123123
/// \p newValue.
124-
SILBasicBlock::iterator perform();
124+
SILBasicBlock::iterator
125+
perform(SingleValueInstruction *maybeTransformedNewValue = nullptr);
125126

126127
private:
127128
SILBasicBlock::iterator replaceAddressUses(SingleValueInstruction *oldValue,

lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -739,9 +739,6 @@ visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI) {
739739
SILInstruction *
740740
SILCombiner::
741741
visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) {
742-
if (UBCI->getFunction()->hasOwnership())
743-
return nullptr;
744-
745742
// (unchecked_bitwise_cast Y->Z (unchecked_bitwise_cast X->Y x))
746743
// OR (unchecked_trivial_cast Y->Z (unchecked_bitwise_cast X->Y x))
747744
// ->
@@ -750,27 +747,59 @@ visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) {
750747
if (match(UBCI->getOperand(),
751748
m_CombineOr(m_UncheckedBitwiseCastInst(m_SILValue(Oper)),
752749
m_UncheckedTrivialBitCastInst(m_SILValue(Oper))))) {
753-
return Builder.createUncheckedBitwiseCast(UBCI->getLoc(), Oper,
754-
UBCI->getType());
750+
if (!Builder.hasOwnership()) {
751+
return Builder.createUncheckedBitwiseCast(UBCI->getLoc(), Oper,
752+
UBCI->getType());
753+
}
754+
755+
OwnershipRAUWHelper helper(ownershipFixupContext, UBCI, Oper);
756+
if (helper) {
757+
auto *transformedOper = Builder.createUncheckedBitwiseCast(
758+
UBCI->getLoc(), Oper, UBCI->getType());
759+
helper.perform(transformedOper);
760+
return nullptr;
761+
}
762+
}
763+
764+
if (UBCI->getType().isTrivial(*UBCI->getFunction())) {
765+
// If our result is trivial, we can always just RAUW.
766+
return Builder.createUncheckedTrivialBitCast(
767+
UBCI->getLoc(), UBCI->getOperand(), UBCI->getType());
755768
}
756-
if (UBCI->getType().isTrivial(*UBCI->getFunction()))
757-
return Builder.createUncheckedTrivialBitCast(UBCI->getLoc(),
758-
UBCI->getOperand(),
759-
UBCI->getType());
760769

761770
if (!SILType::canRefCast(UBCI->getOperand()->getType(), UBCI->getType(),
762771
Builder.getModule()))
763772
return nullptr;
764773

765-
return Builder.createUncheckedRefCast(UBCI->getLoc(), UBCI->getOperand(),
766-
UBCI->getType());
774+
if (!Builder.hasOwnership()) {
775+
return Builder.createUncheckedRefCast(UBCI->getLoc(), UBCI->getOperand(),
776+
UBCI->getType());
777+
}
778+
779+
{
780+
OwnershipRAUWHelper helper(ownershipFixupContext, UBCI, UBCI->getOperand());
781+
if (helper) {
782+
auto *newInst = Builder.createUncheckedRefCast(UBCI->getLoc(), UBCI->getOperand(),
783+
UBCI->getType());
784+
// If we have an operand with owned ownership, we change our
785+
// unchecked_ref_cast to explicitly pass the owned operand as an unowned
786+
// value. This is because otherwise, we would consume the owned value
787+
// creating breaking OSSA. In contrast, if we have a guaranteed value, we
788+
// are going to be replacing an UnownedInstantaneousUse with an
789+
// InstantaneousUse which is always safe for a guaranteed value.
790+
if (newInst->getOwnershipKind() == OwnershipKind::Owned) {
791+
newInst->setOwnershipKind(OwnershipKind::Unowned);
792+
}
793+
helper.perform(newInst);
794+
return nullptr;
795+
}
796+
}
797+
798+
return nullptr;
767799
}
768800

769801
SILInstruction *
770802
SILCombiner::visitThickToObjCMetatypeInst(ThickToObjCMetatypeInst *TTOCMI) {
771-
if (TTOCMI->getFunction()->hasOwnership())
772-
return nullptr;
773-
774803
if (auto *OCTTMI = dyn_cast<ObjCToThickMetatypeInst>(TTOCMI->getOperand())) {
775804
TTOCMI->replaceAllUsesWith(OCTTMI->getOperand());
776805
return eraseInstFromFunction(*TTOCMI);
@@ -793,9 +822,6 @@ SILCombiner::visitThickToObjCMetatypeInst(ThickToObjCMetatypeInst *TTOCMI) {
793822

794823
SILInstruction *
795824
SILCombiner::visitObjCToThickMetatypeInst(ObjCToThickMetatypeInst *OCTTMI) {
796-
if (OCTTMI->getFunction()->hasOwnership())
797-
return nullptr;
798-
799825
if (auto *TTOCMI = dyn_cast<ThickToObjCMetatypeInst>(OCTTMI->getOperand())) {
800826
OCTTMI->replaceAllUsesWith(TTOCMI->getOperand());
801827
return eraseInstFromFunction(*OCTTMI);

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

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

149149
SILInstruction *SILCombiner::visitSwitchEnumAddrInst(SwitchEnumAddrInst *SEAI) {
150-
if (SEAI->getFunction()->hasOwnership())
151-
return nullptr;
150+
SILValue Addr = SEAI->getOperand();
152151

153152
// Convert switch_enum_addr -> br
154-
// if the only thing which writes to the address is an inject_enum_addr.
155-
SILValue Addr = SEAI->getOperand();
156-
if (EnumElementDecl *EnumCase = getInjectEnumCaseTo(Addr)) {
157-
SILBasicBlock *Dest = SEAI->getCaseDestination(EnumCase);
158-
// If the only instruction which writes to Addr is an inject_enum_addr we
159-
// know that there cannot be an enum payload.
160-
assert(Dest->getNumArguments() == 0 &&
161-
"didn't expect a payload argument");
162-
Builder.createBranch(SEAI->getLoc(), Dest);
163-
return eraseInstFromFunction(*SEAI);
153+
//
154+
// If the only thing which writes to the address is an inject_enum_addr. We
155+
// only perform these optimizations when we are not in OSSA since this
156+
// eliminates an edge from the CFG and we want SILCombine in OSSA to never do
157+
// that, so in the future we can invalidate less.
158+
if (!SEAI->getFunction()->hasOwnership()) {
159+
if (EnumElementDecl *EnumCase = getInjectEnumCaseTo(Addr)) {
160+
SILBasicBlock *Dest = SEAI->getCaseDestination(EnumCase);
161+
// If the only instruction which writes to Addr is an inject_enum_addr we
162+
// know that there cannot be an enum payload.
163+
assert(Dest->getNumArguments() == 0 &&
164+
"didn't expect a payload argument");
165+
Builder.createBranch(SEAI->getLoc(), Dest);
166+
return eraseInstFromFunction(*SEAI);
167+
}
164168
}
165169

166170
SILType Ty = Addr->getType();
@@ -172,15 +176,44 @@ SILInstruction *SILCombiner::visitSwitchEnumAddrInst(SwitchEnumAddrInst *SEAI) {
172176
// ->
173177
// %value = load %ptr
174178
// switch_enum %value
175-
SmallVector<std::pair<EnumElementDecl*, SILBasicBlock*>, 8> Cases;
176-
for (int i = 0, e = SEAI->getNumCases(); i < e; ++i)
179+
//
180+
// If we are using ownership, we perform a load_borrow right before the new
181+
// switch_enum and end the borrow scope right afterwards.
182+
Builder.setCurrentDebugScope(SEAI->getDebugScope());
183+
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 8> Cases;
184+
for (int i : range(SEAI->getNumCases())) {
177185
Cases.push_back(SEAI->getCase(i));
186+
}
178187

179-
Builder.setCurrentDebugScope(SEAI->getDebugScope());
180188
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);
189+
SILValue EnumVal = Builder.emitLoadBorrowOperation(SEAI->getLoc(), Addr);
190+
auto *sei = Builder.createSwitchEnum(SEAI->getLoc(), EnumVal, Default, Cases);
191+
192+
if (Builder.hasOwnership()) {
193+
for (int i : range(sei->getNumCases())) {
194+
auto c = sei->getCase(i);
195+
if (c.first->hasAssociatedValues()) {
196+
auto eltType = Addr->getType().getEnumElementType(
197+
c.first, Builder.getModule(), Builder.getTypeExpansionContext());
198+
eltType = eltType.getObjectType();
199+
if (eltType.isTrivial(Builder.getFunction())) {
200+
c.second->createPhiArgument(eltType, OwnershipKind::None);
201+
} else {
202+
c.second->createPhiArgument(eltType, OwnershipKind::Guaranteed);
203+
}
204+
}
205+
Builder.setInsertionPoint(c.second->front().getIterator());
206+
Builder.emitEndBorrowOperation(SEAI->getLoc(), EnumVal);
207+
}
208+
209+
if (auto defaultBlock = sei->getDefaultBBOrNull()) {
210+
defaultBlock.get()->createPhiArgument(EnumVal->getType(),
211+
OwnershipKind::Guaranteed);
212+
Builder.setInsertionPoint(defaultBlock.get()->front().getIterator());
213+
Builder.emitEndBorrowOperation(SEAI->getLoc(), EnumVal);
214+
}
215+
}
216+
184217
return eraseInstFromFunction(*SEAI);
185218
}
186219

@@ -1581,9 +1614,6 @@ SILCombiner::visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI) {
15811614
SILInstruction *
15821615
SILCombiner::
15831616
visitUnreachableInst(UnreachableInst *UI) {
1584-
if (UI->getFunction()->hasOwnership())
1585-
return nullptr;
1586-
15871617
// Make sure that this unreachable instruction
15881618
// is the last instruction in the basic block.
15891619
if (UI->getParent()->getTerminator() == UI)
@@ -1857,9 +1887,6 @@ SILInstruction *SILCombiner::visitCondBranchInst(CondBranchInst *CBI) {
18571887
}
18581888

18591889
SILInstruction *SILCombiner::visitSelectEnumInst(SelectEnumInst *SEI) {
1860-
if (SEI->getFunction()->hasOwnership())
1861-
return nullptr;
1862-
18631890
// Canonicalize a select_enum: if the default refers to exactly one case, then
18641891
// replace the default with that case.
18651892
if (SEI->hasDefault()) {

lib/SILOptimizer/Utils/OwnershipOptUtils.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -608,14 +608,19 @@ SILBasicBlock::iterator OwnershipRAUWUtility::handleUnowned() {
608608
// value, we need to ensure that the guaranteed value is live at all use
609609
// points of the unowned value. If so, just replace and continue.
610610
//
611-
// TODO: Implement this.
611+
// TODO: Implement this for more interesting cases.
612+
if (isa<SILFunctionArgument>(newValue))
613+
return replaceAllUsesAndErase(oldValue, newValue, callbacks);
612614

613615
// Otherwise, we need to lifetime extend the borrow over all of the use
614-
// points. To do so, we copy the value, borrow it, insert an unchecked
615-
// ownership conversion to unowned at oldValue and then RAUW.
616+
// points. To do so, we copy the value, borrow it, and insert an unchecked
617+
// ownership conversion to unowned at all uses that are terminator uses.
618+
//
619+
// We need to insert the conversion since if we have a non-argument
620+
// guaranteed value since its scope will end before the terminator so we
621+
// need to convert the value to unowned early.
616622
//
617-
// We need to insert the conversion to ensure that we do not violate
618-
// ownership propagation rules of forwarding insts.
623+
// TODO: Do we need a separate array here?
619624
SmallVector<Operand *, 8> oldValueUses(oldValue->getUses());
620625
for (auto *use : oldValueUses) {
621626
if (auto *ti = dyn_cast<TermInst>(use->getUser())) {
@@ -628,14 +633,12 @@ SILBasicBlock::iterator OwnershipRAUWUtility::handleUnowned() {
628633
}
629634
}
630635
}
636+
631637
auto extender = getLifetimeExtender();
632638
SILValue borrow =
633639
extender.createPlusZeroBorrow(newValue, oldValue->getUses());
634640
SILBuilderWithScope builder(oldValue);
635-
auto *newInst = builder.createUncheckedOwnershipConversion(
636-
oldValue->getLoc(), borrow, OwnershipKind::Unowned);
637-
callbacks.createdNewInst(newInst);
638-
return replaceAllUsesAndErase(oldValue, newInst, callbacks);
641+
return replaceAllUsesAndErase(oldValue, borrow, callbacks);
639642
}
640643
case OwnershipKind::Owned: {
641644
// If we have an unowned value that we want to replace with an owned value,
@@ -646,13 +649,15 @@ SILBasicBlock::iterator OwnershipRAUWUtility::handleUnowned() {
646649

647650
// Otherwise, insert a copy of the owned value and lifetime extend that over
648651
// all uses of the value and then RAUW.
652+
//
653+
// NOTE: For terminator uses, we funnel the use through an
654+
// unchecked_ownership_conversion to ensure that we can end the lifetime of
655+
// our owned/guaranteed value before the terminator.
649656
SmallVector<Operand *, 8> oldValueUses(oldValue->getUses());
650657
for (auto *use : oldValueUses) {
651658
if (auto *ti = dyn_cast<TermInst>(use->getUser())) {
652659
if (ti->isFunctionExiting()) {
653660
SILBuilderWithScope builder(ti);
654-
// We insert this to ensure that we can extend our owned value's
655-
// lifetime to before the function end point.
656661
auto *newInst = builder.createUncheckedOwnershipConversion(
657662
ti->getLoc(), use->get(), OwnershipKind::Unowned);
658663
callbacks.createdNewInst(newInst);
@@ -663,10 +668,7 @@ SILBasicBlock::iterator OwnershipRAUWUtility::handleUnowned() {
663668
auto extender = getLifetimeExtender();
664669
SILValue copy = extender.createPlusZeroCopy(newValue, oldValue->getUses());
665670
SILBuilderWithScope builder(oldValue);
666-
auto *newInst = builder.createUncheckedOwnershipConversion(
667-
oldValue->getLoc(), copy, OwnershipKind::Unowned);
668-
callbacks.createdNewInst(newInst);
669-
auto result = replaceAllUsesAndErase(oldValue, newInst, callbacks);
671+
auto result = replaceAllUsesAndErase(oldValue, copy, callbacks);
670672
return result;
671673
}
672674
}
@@ -1014,15 +1016,19 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx,
10141016
}
10151017
}
10161018

1017-
SILBasicBlock::iterator OwnershipRAUWHelper::perform() {
1019+
SILBasicBlock::iterator
1020+
OwnershipRAUWHelper::perform(SingleValueInstruction *maybeTransformedNewValue) {
10181021
assert(isValid() && "OwnershipRAUWHelper invalid?!");
10191022

10201023
// Make sure to always clear our context after we transform.
10211024
SWIFT_DEFER { ctx->clear(); };
1025+
SILValue actualNewValue = newValue;
1026+
if (maybeTransformedNewValue)
1027+
actualNewValue = maybeTransformedNewValue;
10221028

10231029
if (oldValue->getType().isAddress())
1024-
return replaceAddressUses(oldValue, newValue);
1030+
return replaceAddressUses(oldValue, actualNewValue);
10251031

1026-
OwnershipRAUWUtility utility{oldValue, newValue, *ctx};
1032+
OwnershipRAUWUtility utility{oldValue, actualNewValue, *ctx};
10271033
return utility.perform();
10281034
}

test/SILOptimizer/ossa_rauw_tests.sil

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,40 @@ bb0(%0 : @guaranteed $Klass):
318318
return %3 : $(Klass, Klass)
319319
}
320320

321+
// CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw_2a : $@convention(thin) (@guaranteed Builtin.NativeObject) -> (Klass, Klass) {
322+
// CHECK: bb0(
323+
// CHECK-NEXT: unchecked_ref_cast
324+
// CHECK-NEXT: tuple
325+
// CHECK-NEXT: return
326+
// CHECK: } // end sil function 'unowned_to_guaranteed_rauw_2a'
327+
sil [ossa] @unowned_to_guaranteed_rauw_2a : $@convention(thin) (@guaranteed Builtin.NativeObject) -> (Klass, Klass) {
328+
bb0(%0 : @guaranteed $Builtin.NativeObject):
329+
%0a = unchecked_ref_cast %0 : $Builtin.NativeObject to $Klass
330+
%1 = unchecked_bitwise_cast %0a : $Klass to $SubKlass
331+
%2 = unchecked_bitwise_cast %1 : $SubKlass to $Klass
332+
%3 = tuple(%2 : $Klass, %2 : $Klass)
333+
return %3 : $(Klass, Klass)
334+
}
335+
336+
// We need the unchecked_ownership_conversion since our base value is
337+
// guaranteed, not a function argument, and our user is a function exiting
338+
// terminator.
339+
//
340+
// CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw_2b : $@convention(thin) (@guaranteed Builtin.NativeObject) -> Klass {
341+
// CHECK: bb0(
342+
// CHECK-NEXT: unchecked_ref_cast
343+
// CHECK-NEXT: unchecked_ownership_conversion
344+
// CHECK-NEXT: return
345+
// CHECK: } // end sil function 'unowned_to_guaranteed_rauw_2b'
346+
sil [ossa] @unowned_to_guaranteed_rauw_2b : $@convention(thin) (@guaranteed Builtin.NativeObject) -> Klass {
347+
bb0(%0 : @guaranteed $Builtin.NativeObject):
348+
%0a = unchecked_ref_cast %0 : $Builtin.NativeObject to $Klass
349+
%1 = unchecked_bitwise_cast %0a : $Klass to $SubKlass
350+
%2 = unchecked_bitwise_cast %1 : $SubKlass to $Klass
351+
return %2 : $Klass
352+
}
353+
354+
321355
// CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw_2_loop : $@convention(thin) (@guaranteed Klass) -> @owned FakeOptional<(Klass, Klass)> {
322356
// CHECK: bb0([[ARG:%.*]] : @guaranteed $Klass):
323357
// CHECK-NOT: unchecked_bitwise_cast
@@ -376,7 +410,6 @@ bbExitBlock(%result : @owned $FakeOptional<(Klass, Klass)>):
376410

377411
// CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw_3 : $@convention(thin) (@guaranteed Klass) -> Klass {
378412
// CHECK: bb0(
379-
// CHECK-NEXT: unchecked_ownership_conversion
380413
// CHECK-NEXT: return
381414
// CHECK: } // end sil function 'unowned_to_guaranteed_rauw_3'
382415
sil [ossa] @unowned_to_guaranteed_rauw_3 : $@convention(thin) (@guaranteed Klass) -> Klass {

0 commit comments

Comments
 (0)