Skip to content

Commit 62e00fd

Browse files
authored
Merge pull request #62493 from atrick/term-result-operand
APIs for terminator results that forward ownership
2 parents acc4621 + f9861ec commit 62e00fd

File tree

16 files changed

+198
-207
lines changed

16 files changed

+198
-207
lines changed

include/swift/SIL/SILArgument.h

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class SILArgument : public ValueBase {
126126
unsigned getIndex() const;
127127

128128
/// Return non-null if \p value is a phi.
129-
static SILPhiArgument *isPhi(SILValue value);
129+
static SILPhiArgument *asPhi(SILValue value);
130130

131131
/// Return non-null if \p value is a terminator result.
132132
static SILPhiArgument *isTerminatorResult(SILValue value);
@@ -145,10 +145,15 @@ class SILArgument : public ValueBase {
145145
/// If this argument is a phi, populate `OutArray` with the incoming phi
146146
/// values for each predecessor BB. If this argument is not a phi, return
147147
/// false.
148+
///
149+
/// If this block has no predecessors, returnedPhiValues will be empty.
148150
bool getIncomingPhiValues(SmallVectorImpl<SILValue> &returnedPhiValues) const;
149151

150152
/// If this argument is a phi, populate `OutArray` with each predecessor block
151153
/// and its incoming phi value. If this argument is not a phi, return false.
154+
///
155+
/// If this block has no predecessors, returnedPredAndPhiValuePairs will be
156+
/// empty.
152157
bool
153158
getIncomingPhiValues(SmallVectorImpl<std::pair<SILBasicBlock *, SILValue>>
154159
&returnedPredAndPhiValuePairs) const;
@@ -187,14 +192,6 @@ class SILArgument : public ValueBase {
187192
SmallVectorImpl<std::pair<SILBasicBlock *, SILValue>>
188193
&returnedSingleTermOperands) const;
189194

190-
/// If this SILArgument's parent block has a single predecessor whose
191-
/// terminator has a single operand, return the incoming operand of the
192-
/// predecessor's terminator. Returns SILValue() otherwise. Note that for
193-
/// some predecessor terminators the incoming value is not exactly the
194-
/// argument value. E.g. the incoming value for a switch_enum payload argument
195-
/// is the enum itself (the operand of the switch_enum).
196-
SILValue getSingleTerminatorOperand() const;
197-
198195
/// If this SILArgument's parent block has a single predecessor whose
199196
/// terminator has a single operand, return that terminator.
200197
TermInst *getSingleTerminator() const;
@@ -203,6 +200,13 @@ class SILArgument : public ValueBase {
203200
/// otherwise return nullptr.
204201
TermInst *getTerminatorForResult() const;
205202

203+
/// If this terminator result forwards an operand, then return it.
204+
///
205+
/// Precondition: this->isTerminatorResult()
206+
///
207+
/// TODO: Move this and other APIs into a TerminatorResult abstraction.
208+
const Operand *forwardedTerminatorResultOperand() const;
209+
206210
/// Return the SILArgumentKind of this argument.
207211
SILArgumentKind getKind() const {
208212
return SILArgumentKind(ValueBase::getKind());
@@ -310,14 +314,6 @@ class SILPhiArgument : public SILArgument {
310314
SmallVectorImpl<std::pair<SILBasicBlock *, SILValue>>
311315
&returnedSingleTermOperands) const;
312316

313-
/// If this SILArgument's parent block has a single predecessor whose
314-
/// terminator has a single operand, return the incoming operand of the
315-
/// predecessor's terminator. Returns SILValue() otherwise. Note that for
316-
/// some predecessor terminators the incoming value is not exactly the
317-
/// argument value. E.g. the incoming value for a switch_enum payload argument
318-
/// is the enum itself (the operand of the switch_enum).
319-
SILValue getSingleTerminatorOperand() const;
320-
321317
/// If this SILArgument's parent block has a single predecessor whose
322318
/// terminator has a single operand, return that terminator.
323319
TermInst *getSingleTerminator() const;
@@ -406,7 +402,7 @@ class SILFunctionArgument : public SILArgument {
406402
//===----------------------------------------------------------------------===//
407403

408404
/// Return non-null if \p value is a real phi argument.
409-
inline SILPhiArgument *SILArgument::isPhi(SILValue value) {
405+
inline SILPhiArgument *SILArgument::asPhi(SILValue value) {
410406
if (auto *arg = dyn_cast<SILPhiArgument>(value)) {
411407
if (arg->isPhi())
412408
return arg;

include/swift/SIL/SILBasicBlock.h

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ public SwiftObjectHeader {
440440
/// Whether the block has any phi arguments.
441441
///
442442
/// Note that a block could have an argument and still return false. The
443-
/// argument must also satisfy SILPhiArgument::isPhiArgument.
443+
/// argument must also satisfy SILPhiArgument::isPhi.
444444
bool hasPhi() const;
445445

446446
//===--------------------------------------------------------------------===//
@@ -541,6 +541,8 @@ struct PhiOperand {
541541

542542
PhiOperand() = default;
543543

544+
// This if \p operand is a CondBrInst operand, then this constructs and
545+
// invalid PhiOperand. The abstraction only works for non-trivial OSSA values.
544546
PhiOperand(Operand *operand) {
545547
auto *branch = dyn_cast<BranchInst>(operand->getUser());
546548
if (!branch)
@@ -567,8 +569,8 @@ struct PhiOperand {
567569
}
568570

569571
SILPhiArgument *getValue() const {
570-
auto *branch = cast<BranchInst>(predBlock->getTerminator());
571-
return cast<SILPhiArgument>(branch->getDestBB()->getArgument(argIndex));
572+
return
573+
cast<SILPhiArgument>(getBranch()->getDestBB()->getArgument(argIndex));
572574
}
573575

574576
SILValue getSource() const {
@@ -591,12 +593,10 @@ struct PhiValue {
591593
PhiValue() = default;
592594

593595
PhiValue(SILValue value) {
594-
auto *blockArg = dyn_cast<SILPhiArgument>(value);
595-
if (!blockArg || !blockArg->isPhi())
596-
return;
597-
598-
phiBlock = blockArg->getParent();
599-
argIndex = blockArg->getIndex();
596+
if (auto *blockArg = SILArgument::asPhi(value)) {
597+
phiBlock = blockArg->getParent();
598+
argIndex = blockArg->getIndex();
599+
}
600600
}
601601

602602
explicit operator bool() const { return phiBlock != nullptr; }
@@ -612,8 +612,12 @@ struct PhiValue {
612612
}
613613

614614
Operand *getOperand(SILBasicBlock *predecessor) {
615-
auto *branch = cast<BranchInst>(predecessor->getTerminator());
616-
return &branch->getAllOperands()[argIndex];
615+
auto *term = predecessor->getTerminator();
616+
if (auto *branch = dyn_cast<BranchInst>(term)) {
617+
return &branch->getAllOperands()[argIndex];
618+
}
619+
// TODO: Support CondBr for legacy reasons
620+
return cast<CondBranchInst>(term)->getOperandForDestBB(phiBlock, argIndex);
617621
}
618622

619623
operator SILValue() const { return getValue(); }

include/swift/SIL/SILBuilder.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -831,12 +831,8 @@ class SILBuilder {
831831
}
832832

833833
EndBorrowInst *createEndBorrow(SILLocation loc, SILValue borrowedValue) {
834-
if (auto *arg = dyn_cast<SILPhiArgument>(borrowedValue)) {
835-
if (auto *ti = arg->getSingleTerminator()) {
836-
assert(!ti->isTransformationTerminator() &&
837-
"Transforming terminators do not have end_borrow");
838-
}
839-
}
834+
assert(!SILArgument::isTerminatorResult(borrowedValue) &&
835+
"terminator results do not have end_borrow");
840836
return insert(new (getModule())
841837
EndBorrowInst(getSILDebugLocation(loc), borrowedValue));
842838
}

include/swift/SIL/SILInstruction.h

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,9 @@ class OwnershipForwardingMixin {
11821182
///
11831183
/// The ownership kind is set on construction and afterwards must be changed
11841184
/// explicitly using setOwnershipKind().
1185+
///
1186+
/// TODO: This name is extremely misleading because it may apply to an
1187+
/// operation that has no operand at all, like `enum .None`.
11851188
class FirstArgOwnershipForwardingSingleValueInst
11861189
: public SingleValueInstruction,
11871190
public OwnershipForwardingMixin {
@@ -1668,6 +1671,8 @@ class UnaryInstructionBase : public InstructionBase<Kind, Base> {
16681671

16691672
Operand &getOperandRef() { return Operands[0]; }
16701673

1674+
const Operand &getOperandRef() const { return Operands[0]; }
1675+
16711676
ArrayRef<Operand> getAllOperands() const { return Operands.asArray(); }
16721677
MutableArrayRef<Operand> getAllOperands() { return Operands.asArray(); }
16731678

@@ -1808,6 +1813,10 @@ class UnaryInstructionWithTypeDependentOperandsBase
18081813
return this->getAllOperands()[0];
18091814
}
18101815

1816+
const Operand &getOperandRef() const {
1817+
return this->getAllOperands()[0];
1818+
}
1819+
18111820
ArrayRef<Operand> getTypeDependentOperands() const {
18121821
return this->getAllOperands().slice(1);
18131822
}
@@ -6203,6 +6212,8 @@ class EnumInst
62036212

62046213
Operand &getOperandRef() { return OptionalOperand->asArray()[0]; }
62056214

6215+
const Operand &getOperandRef() const { return OptionalOperand->asArray()[0]; }
6216+
62066217
ArrayRef<Operand> getAllOperands() const {
62076218
return OptionalOperand ? OptionalOperand->asArray() : ArrayRef<Operand>{};
62086219
}
@@ -8384,31 +8395,50 @@ class TermInst : public NonValueInstruction {
83848395

83858396
TermKind getTermKind() const { return TermKind(getKind()); }
83868397

8387-
/// Returns true if this is a transformation terminator.
8398+
/// Returns true if this terminator may have a result, represented as a block
8399+
/// argument in any of its successor blocks.
83888400
///
8389-
/// The first operand is the transformed source.
8390-
bool isTransformationTerminator() const {
8401+
/// Phis (whose operands originate from BranchInst terminators) are not
8402+
/// terminator results.
8403+
///
8404+
/// CondBr might produce block arguments for legacy reasons. This is gradually
8405+
/// being deprecated. For now, they are considered phis. In OSSA, these "phis"
8406+
/// must be trivial and critical edges cannot be present.
8407+
bool mayHaveTerminatorResult() const {
83918408
switch (getTermKind()) {
83928409
case TermKind::UnwindInst:
83938410
case TermKind::UnreachableInst:
83948411
case TermKind::ReturnInst:
83958412
case TermKind::ThrowInst:
83968413
case TermKind::YieldInst:
8397-
case TermKind::TryApplyInst:
8398-
case TermKind::BranchInst:
83998414
case TermKind::CondBranchInst:
8400-
case TermKind::SwitchValueInst:
8415+
case TermKind::BranchInst:
84018416
case TermKind::SwitchEnumAddrInst:
8402-
case TermKind::DynamicMethodBranchInst:
84038417
case TermKind::CheckedCastAddrBranchInst:
8404-
case TermKind::AwaitAsyncContinuationInst:
84058418
return false;
8406-
case TermKind::SwitchEnumInst:
84078419
case TermKind::CheckedCastBranchInst:
8420+
case TermKind::SwitchEnumInst:
8421+
case TermKind::SwitchValueInst:
8422+
case TermKind::TryApplyInst:
8423+
case TermKind::AwaitAsyncContinuationInst:
8424+
case TermKind::DynamicMethodBranchInst:
84088425
return true;
84098426
}
84108427
llvm_unreachable("Covered switch isn't covered.");
84118428
}
8429+
8430+
/// Returns an Operand reference if this terminator forwards ownership of a
8431+
/// single operand to a single result for at least one successor
8432+
/// block. Otherwise returns nullptr.
8433+
///
8434+
/// By convention, terminators can forward ownership of at most one operand to
8435+
/// at most one result. The operand value might not be directly forwarded. For
8436+
/// example, a switch forwards ownership of the enum type into ownership of
8437+
/// the payload.
8438+
///
8439+
/// Postcondition: each successor has zero or one block arguments which
8440+
/// represents the forwaded result.
8441+
const Operand *forwardedOperand() const;
84128442
};
84138443

84148444
// Forwards the first operand to a result in each successor block.
@@ -8442,6 +8472,10 @@ class OwnershipForwardingTermInst : public TermInst,
84428472

84438473
SILValue getOperand() const { return getAllOperands()[0].get(); }
84448474

8475+
Operand &getOperandRef() { return getAllOperands()[0]; }
8476+
8477+
const Operand &getOperandRef() const { return getAllOperands()[0]; }
8478+
84458479
/// Create a result for this terminator on the given successor block.
84468480
SILPhiArgument *createResult(SILBasicBlock *succ, SILType resultTy);
84478481
};

include/swift/SIL/SILValue.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,13 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
494494
}
495495
SILInstruction *getDefiningInstruction();
496496

497+
/// Return the instruction that defines this value, terminator instruction
498+
/// that produces this result, or null if it is not defined by an instruction.
499+
const SILInstruction *getDefiningInstructionOrTerminator() const {
500+
return const_cast<ValueBase*>(this)->getDefiningInstructionOrTerminator();
501+
}
502+
SILInstruction *getDefiningInstructionOrTerminator();
503+
497504
/// Return the SIL instruction that can be used to describe the first time
498505
/// this value is available.
499506
///

lib/SIL/IR/SILArgument.cpp

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,17 @@ SILParameterInfo SILFunctionArgument::getKnownParameterInfo() const {
7777
// SILBlockArgument
7878
//===----------------------------------------------------------------------===//
7979

80-
// FIXME: SILPhiArgument should only refer to branch arguments. They usually
81-
// need to be distinguished from projections and casts. Actual phi block
82-
// arguments are substitutable with their incoming values. It is also needlessly
83-
// expensive to call this helper instead of simply specifying phis with an
84-
// opcode. It results in repeated CFG traversals and repeated, unnecessary
85-
// switching over terminator opcodes.
80+
// FIXME: SILPhiArgument should only refer to phis (values merged from
81+
// BranchInst operands). Phis are directly substitutable with their incoming
82+
// values modulo control flow. They usually need to be distinguished from
83+
// projections and casts. It is needlessly expensive to call this helper instead
84+
// of simply specifying phis with an opcode. It results in repeated CFG
85+
// traversals and repeated, unnecessary switching over terminator opcodes.
8686
bool SILPhiArgument::isPhi() const {
87-
// No predecessors indicates an unreachable block.
87+
// No predecessors indicates an unreachable block. Treat this like a
88+
// degenerate phi so we don't consider it a terminator result.
8889
if (getParent()->pred_empty())
89-
return false;
90+
return true;
9091

9192
// Multiple predecessors require phis.
9293
auto *predBlock = getParent()->getSinglePredecessorBlock();
@@ -215,7 +216,6 @@ bool SILPhiArgument::getIncomingPhiValues(
215216
return false;
216217

217218
const auto *parentBlock = getParent();
218-
assert(!parentBlock->pred_empty());
219219

220220
unsigned argIndex = getIndex();
221221
for (auto *predBlock : getParent()->getPredecessorBlocks()) {
@@ -323,14 +323,6 @@ bool SILPhiArgument::getSingleTerminatorOperands(
323323
return true;
324324
}
325325

326-
SILValue SILPhiArgument::getSingleTerminatorOperand() const {
327-
const auto *parentBlock = getParent();
328-
const auto *predBlock = parentBlock->getSinglePredecessorBlock();
329-
if (!predBlock)
330-
return SILValue();
331-
return getSingleTerminatorOperandForPred(parentBlock, predBlock, getIndex());
332-
}
333-
334326
TermInst *SILPhiArgument::getSingleTerminator() const {
335327
auto *parentBlock = getParent();
336328
auto *predBlock = parentBlock->getSinglePredecessorBlock();
@@ -349,6 +341,11 @@ TermInst *SILPhiArgument::getTerminatorForResult() const {
349341
return nullptr;
350342
}
351343

344+
const Operand *SILArgument::forwardedTerminatorResultOperand() const {
345+
assert(isTerminatorResult() && "API is invalid for phis");
346+
return getSingleTerminator()->forwardedOperand();
347+
}
348+
352349
SILPhiArgument *BranchInst::getArgForOperand(const Operand *oper) {
353350
assert(oper->getUser() == this);
354351
return cast<SILPhiArgument>(

lib/SIL/IR/SILInstructions.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,6 +1617,40 @@ TermInst::getSuccessorBlockArgumentLists() const {
16171617
return SuccessorBlockArgumentListTy(getSuccessors(), op);
16181618
}
16191619

1620+
const Operand *TermInst::forwardedOperand() const {
1621+
switch (getTermKind()) {
1622+
case TermKind::UnwindInst:
1623+
case TermKind::UnreachableInst:
1624+
case TermKind::ReturnInst:
1625+
case TermKind::ThrowInst:
1626+
case TermKind::YieldInst:
1627+
case TermKind::TryApplyInst:
1628+
case TermKind::CondBranchInst:
1629+
case TermKind::BranchInst:
1630+
case TermKind::SwitchEnumAddrInst:
1631+
case TermKind::SwitchValueInst:
1632+
case TermKind::DynamicMethodBranchInst:
1633+
case TermKind::CheckedCastAddrBranchInst:
1634+
case TermKind::AwaitAsyncContinuationInst:
1635+
return nullptr;
1636+
case TermKind::SwitchEnumInst: {
1637+
auto *switchEnum = cast<SwitchEnumInst>(this);
1638+
if (!switchEnum->preservesOwnership())
1639+
return nullptr;
1640+
1641+
return &switchEnum->getOperandRef();
1642+
}
1643+
case TermKind::CheckedCastBranchInst: {
1644+
auto *checkedCast = cast<CheckedCastBranchInst>(this);
1645+
if (!checkedCast->preservesOwnership())
1646+
return nullptr;
1647+
1648+
return &checkedCast->getOperandRef();
1649+
}
1650+
}
1651+
llvm_unreachable("Covered switch isn't covered.");
1652+
}
1653+
16201654
YieldInst *YieldInst::create(SILDebugLocation loc,
16211655
ArrayRef<SILValue> yieldedValues,
16221656
SILBasicBlock *normalBB, SILBasicBlock *unwindBB,

lib/SIL/IR/SILValue.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ SILInstruction *ValueBase::getDefiningInstruction() {
8080
return nullptr;
8181
}
8282

83+
SILInstruction *ValueBase::getDefiningInstructionOrTerminator() {
84+
if (auto *inst = dyn_cast<SingleValueInstruction>(this))
85+
return inst;
86+
if (auto *result = dyn_cast<MultipleValueInstructionResult>(this))
87+
return result->getParent();
88+
if (auto *result = SILArgument::isTerminatorResult(this))
89+
return result->getSingleTerminator();
90+
return nullptr;
91+
}
92+
8393
SILInstruction *ValueBase::getDefiningInsertionPoint() {
8494
if (auto *inst = getDefiningInstruction())
8595
return inst;

0 commit comments

Comments
 (0)