Skip to content

Commit 6c5c853

Browse files
committed
[ownership] Change guaranteed args from transformation terminators to not require end_borrows.
For those who are unaware, a transformation terminator is a terminator like switch_enum/checked_cast_br that always dominate their successor blocks. Since they dominate their successor blocks by design and transform their input into the args form, we can validate that they obey guaranteed ownership semantics just like a forwarding instruction. Beyond removing unnecessary code bloat, this also makes it significantly more easier to optimize/work with transformation terminators when converting @owned -> @guaranteed since we do not need to find end_borrow points when the owned value is consumed. <rdar://problem/59097063>
1 parent 3c582f4 commit 6c5c853

17 files changed

+200
-68
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,12 @@ class SILBuilder {
802802
}
803803

804804
EndBorrowInst *createEndBorrow(SILLocation loc, SILValue borrowedValue) {
805+
if (auto *arg = dyn_cast<SILPhiArgument>(borrowedValue)) {
806+
if (auto *ti = arg->getSingleTerminator()) {
807+
assert(!ti->isTransformationTerminator() &&
808+
"Transforming terminators do not have end_borrow");
809+
}
810+
}
805811
return insert(new (getModule())
806812
EndBorrowInst(getSILDebugLocation(loc), borrowedValue));
807813
}

include/swift/SIL/SILInstruction.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7042,6 +7042,30 @@ class TermInst : public NonValueInstruction {
70427042
bool isProgramTerminating() const;
70437043

70447044
TermKind getTermKind() const { return TermKind(getKind()); }
7045+
7046+
/// Returns true if this is a transformation terminator.
7047+
bool isTransformationTerminator() const {
7048+
switch (getTermKind()) {
7049+
case TermKind::UnwindInst:
7050+
case TermKind::UnreachableInst:
7051+
case TermKind::ReturnInst:
7052+
case TermKind::ThrowInst:
7053+
case TermKind::YieldInst:
7054+
case TermKind::TryApplyInst:
7055+
case TermKind::BranchInst:
7056+
case TermKind::CondBranchInst:
7057+
case TermKind::SwitchValueInst:
7058+
case TermKind::SwitchEnumAddrInst:
7059+
case TermKind::DynamicMethodBranchInst:
7060+
case TermKind::CheckedCastAddrBranchInst:
7061+
case TermKind::CheckedCastValueBranchInst:
7062+
return false;
7063+
case TermKind::SwitchEnumInst:
7064+
case TermKind::CheckedCastBranchInst:
7065+
return true;
7066+
}
7067+
llvm_unreachable("Covered switch isn't covered.");
7068+
}
70457069
};
70467070

70477071
/// UnreachableInst - Position in the code which would be undefined to reach.

lib/SIL/DynamicCasts.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,9 +1254,7 @@ void swift::emitIndirectConditionalCastWithScalar(
12541254
case CastConsumptionKind::TakeOnSuccess:
12551255
break;
12561256
case CastConsumptionKind::CopyOnSuccess: {
1257-
SILValue originalSuccValue = succValue;
12581257
succValue = B.emitCopyValueOperation(loc, succValue);
1259-
B.emitEndBorrowOperation(loc, originalSuccValue);
12601258
B.emitEndBorrowOperation(loc, srcValue);
12611259
break;
12621260
}
@@ -1295,7 +1293,6 @@ void swift::emitIndirectConditionalCastWithScalar(
12951293
StoreOwnershipQualifier::Init);
12961294
break;
12971295
case CastConsumptionKind::CopyOnSuccess:
1298-
B.emitEndBorrowOperation(loc, failValue);
12991296
B.emitEndBorrowOperation(loc, srcValue);
13001297
break;
13011298
case CastConsumptionKind::BorrowAlways:

lib/SIL/OwnershipUtils.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ bool swift::isGuaranteedForwardingValueKind(SILNodeKind kind) {
6666
}
6767

6868
bool swift::isGuaranteedForwardingValue(SILValue value) {
69+
// If we have an argument from a transforming terminator, we can forward
70+
// guaranteed.
71+
if (auto *arg = dyn_cast<SILArgument>(value)) {
72+
if (auto *ti = arg->getSingleTerminator()) {
73+
if (ti->isTransformationTerminator()) {
74+
return true;
75+
}
76+
}
77+
}
6978
return isGuaranteedForwardingValueKind(
7079
value->getKindOfRepresentativeSILNodeInObject());
7180
}
@@ -173,9 +182,18 @@ bool swift::getUnderlyingBorrowIntroducingValues(
173182
// Otherwise if v is an ownership forwarding value, add its defining
174183
// instruction
175184
if (isGuaranteedForwardingValue(v)) {
176-
auto *i = v->getDefiningInstruction();
177-
assert(i);
178-
llvm::transform(i->getAllOperands(), std::back_inserter(worklist),
185+
if (auto *i = v->getDefiningInstruction()) {
186+
llvm::transform(i->getAllOperands(), std::back_inserter(worklist),
187+
[](const Operand &op) -> SILValue { return op.get(); });
188+
continue;
189+
}
190+
191+
// Otherwise, we should have a block argument that is defined by a single
192+
// predecessor terminator.
193+
auto *arg = cast<SILPhiArgument>(v);
194+
auto *termInst = arg->getSingleTerminator();
195+
assert(termInst && termInst->isTransformationTerminator());
196+
llvm::transform(termInst->getAllOperands(), std::back_inserter(worklist),
179197
[](const Operand &op) -> SILValue { return op.get(); });
180198
continue;
181199
}

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,55 @@ bool SILValueOwnershipChecker::gatherUsers(
334334
continue;
335335
}
336336

337-
// Otherwise if we have a terminator, add any as uses any end_borrow to
338-
// ensure that the subscope is completely enclsed within the super scope. We
339-
// require all of our arguments to be either trivial or guaranteed.
337+
// See if our forwarding terminator is a transformation terminator. If so,
338+
// just add all of the uses of its successors to the worklist to visit as
339+
// users.
340+
if (ti->isTransformationTerminator()) {
341+
for (auto &succ : ti->getSuccessors()) {
342+
auto *succBlock = succ.getBB();
343+
344+
// If we do not have any arguments, then continue.
345+
if (succBlock->args_empty())
346+
continue;
347+
348+
// Otherwise, make sure that all arguments are trivial or guaranteed. If
349+
// we fail, emit an error.
350+
//
351+
// TODO: We could ignore this error and emit a more specific error on
352+
// the actual terminator.
353+
for (auto *succArg : succBlock->getSILPhiArguments()) {
354+
// *NOTE* We do not emit an error here since we want to allow for more
355+
// specific errors to be found during use_verification.
356+
//
357+
// TODO: Add a flag that associates the terminator instruction with
358+
// needing to be verified. If it isn't verified appropriately, assert
359+
// when the verifier is destroyed.
360+
auto succArgOwnershipKind = succArg->getOwnershipKind();
361+
if (!succArgOwnershipKind.isCompatibleWith(ownershipKind)) {
362+
// This is where the error would go.
363+
continue;
364+
}
365+
366+
// If we have an any value, just continue.
367+
if (succArgOwnershipKind == ValueOwnershipKind::None)
368+
continue;
369+
370+
// Otherwise add all users of this BBArg to the worklist to visit
371+
// recursively.
372+
llvm::copy(succArg->getUses(), std::back_inserter(users));
373+
}
374+
}
375+
continue;
376+
}
377+
378+
// Otherwise, we are dealing with the merging of true phis. To work with
379+
// this we will eventually need to create a notion of forwarding borrow
380+
// scopes.
381+
382+
// But until then, we validate that the argument has an end_borrow that acts
383+
// as a subscope that is compeltely enclosed within the scopes of all
384+
// incoming values. We require all of our arguments to be either trivial or
385+
// guaranteed.
340386
for (auto &succ : ti->getSuccessors()) {
341387
auto *succBlock = succ.getBB();
342388

lib/SILGen/SILGenBuilder.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,13 @@ ManagedValue SILGenBuilder::createGuaranteedPhiArgument(SILType type) {
199199
return SGF.emitManagedBorrowedArgumentWithCleanup(arg);
200200
}
201201

202+
ManagedValue
203+
SILGenBuilder::createGuaranteedTransformingTerminatorArgument(SILType type) {
204+
SILPhiArgument *arg =
205+
getInsertionBB()->createPhiArgument(type, ValueOwnershipKind::Guaranteed);
206+
return ManagedValue::forUnmanaged(arg);
207+
}
208+
202209
ManagedValue SILGenBuilder::createAllocRef(
203210
SILLocation loc, SILType refType, bool objc, bool canAllocOnStack,
204211
ArrayRef<SILType> inputElementTypes,

lib/SILGen/SILGenBuilder.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ class SILGenBuilder : public SILBuilder {
127127
ManagedValue createOwnedPhiArgument(SILType type);
128128
ManagedValue createGuaranteedPhiArgument(SILType type);
129129

130+
/// For arguments from terminators that are "transforming terminators". These
131+
/// types of guaranteed arguments are validated as part of the operand of the
132+
/// transforming terminator since transforming terminators are guaranteed to
133+
/// be the only predecessor of our parent block.
134+
///
135+
/// NOTE: Two examples of transforming terminators are switch_enum,
136+
/// checked_cast_br.
137+
ManagedValue createGuaranteedTransformingTerminatorArgument(SILType type);
138+
130139
using SILBuilder::createMarkUninitialized;
131140
ManagedValue createMarkUninitialized(ValueDecl *decl, ManagedValue operand,
132141
MarkUninitializedInst::Kind muKind);

lib/SILGen/SILGenDynamicCast.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ namespace {
185185
// argument.
186186
ManagedValue argument;
187187
if (!shouldTakeOnSuccess(consumption)) {
188-
argument = SGF.B.createGuaranteedPhiArgument(
188+
argument = SGF.B.createGuaranteedTransformingTerminatorArgument(
189189
origTargetTL.getLoweredType());
190190
} else {
191191
argument =
@@ -236,7 +236,8 @@ namespace {
236236
switch (consumption) {
237237
case CastConsumptionKind::BorrowAlways:
238238
case CastConsumptionKind::CopyOnSuccess:
239-
SGF.B.createGuaranteedPhiArgument(operandValue.getType());
239+
SGF.B.createGuaranteedTransformingTerminatorArgument(
240+
operandValue.getType());
240241
handleFalse(None);
241242
break;
242243
case CastConsumptionKind::TakeAlways:

lib/SILGen/SILGenExpr.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,14 @@ namespace {
165165
struct EndBorrowCleanup : Cleanup {
166166
SILValue borrowedValue;
167167

168-
EndBorrowCleanup(SILValue borrowedValue)
169-
: borrowedValue(borrowedValue) {}
168+
EndBorrowCleanup(SILValue borrowedValue) : borrowedValue(borrowedValue) {
169+
if (auto *arg = dyn_cast<SILPhiArgument>(borrowedValue)) {
170+
if (auto *ti = arg->getSingleTerminator()) {
171+
assert(!ti->isTransformationTerminator() &&
172+
"Transforming terminators do not have end_borrow");
173+
}
174+
}
175+
}
170176

171177
void emit(SILGenFunction &SGF, CleanupLocation l,
172178
ForUnwind_t forUnwind) override {

lib/SILGen/SILGenPattern.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,7 +1921,7 @@ void PatternMatchEmission::emitEnumElementObjectDispatch(
19211921

19221922
SILValue eltValue;
19231923
if (isPlusZero) {
1924-
origCMV = {SGF.B.createGuaranteedPhiArgument(eltTy),
1924+
origCMV = {SGF.B.createGuaranteedTransformingTerminatorArgument(eltTy),
19251925
CastConsumptionKind::BorrowAlways};
19261926
} else {
19271927
origCMV = {SGF.B.createOwnedPhiArgument(eltTy),
@@ -1971,7 +1971,7 @@ void PatternMatchEmission::emitEnumElementObjectDispatch(
19711971
if (SILBasicBlock *defaultBB = blocks.getDefaultBlock()) {
19721972
SGF.B.setInsertionPoint(defaultBB);
19731973
if (isPlusZero) {
1974-
SGF.B.createGuaranteedPhiArgument(src.getType());
1974+
SGF.B.createGuaranteedTransformingTerminatorArgument(src.getType());
19751975
} else {
19761976
SGF.B.createOwnedPhiArgument(src.getType());
19771977
}

0 commit comments

Comments
 (0)