Skip to content

Update guaranteed forwarding phi apis and verification #62454

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 33 additions & 25 deletions include/swift/SIL/OwnershipUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,50 +69,58 @@ struct BorrowedValue;
// TODO: encapsulate in a ForwardingInstruction abstraction
//===----------------------------------------------------------------------===//

/// Is the opcode that produces \p value capable of forwarding guaranteed
/// Is the opcode that produces \p value capable of forwarding inner guaranteed
/// values?
///
/// This may be true even if the current instance of the instruction is not a
/// GuaranteedForwarding. If true, then the operation may be trivially rewritten
/// with Guaranteed ownership.
bool canOpcodeForwardGuaranteedValues(SILValue value);
bool canOpcodeForwardInnerGuaranteedValues(SILValue value);

/// Is the opcode that consumes \p use capable of forwarding guaranteed values?
/// Is the opcode that consumes \p use capable of forwarding inner guaranteed
/// values?
///
/// This may be true even if \p use is not a GuaranteedForwarding. If true, then
/// the operation may be trivially rewritten with Guaranteed ownership.
bool canOpcodeForwardGuaranteedValues(Operand *use);
bool canOpcodeForwardInnerGuaranteedValues(Operand *use);

// This is the use-def equivalent of use->getOperandOwnership() ==
// OperandOwnership::GuaranteedForwarding.
inline bool isGuaranteedForwarding(SILValue value) {
assert(value->getOwnershipKind() == OwnershipKind::Guaranteed);
return canOpcodeForwardGuaranteedValues(value);
}

/// Returns true if it is a forwarding phi
inline bool isGuaranteedForwardingPhi(SILValue value) {
if (value->getOwnershipKind() != OwnershipKind::Guaranteed) {
return false;
}
if (isa<BeginBorrowInst>(value) || isa<LoadBorrowInst>(value)) {
return false;
// NOTE: canOpcodeForwardInnerGuaranteedValues returns true for transformation
// terminator results.
if (canOpcodeForwardInnerGuaranteedValues(value) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope we can have a reborrow vs. forwarding flag on the phi soon so we don't need this distinction between "inner" guaranteed values and other guaranteed values! Then I don't think we need this logic.

isa<SILFunctionArgument>(value)) {
return true;
}
// If not a phi, return false
auto *phi = dyn_cast<SILPhiArgument>(value);
if (!phi || !phi->isPhi()) {
return true;
return false;
}
bool isGuaranteedForwardingPhi = true;
phi->visitTransitiveIncomingPhiOperands(
[&](auto *phi, auto *operand) -> bool {
if (isa<BeginBorrowInst>(operand->get()) ||
isa<LoadBorrowInst>(operand->get())) {
isGuaranteedForwardingPhi = false;
return false;
}
return true;
});

// For a phi, if we find GuaranteedForwarding phi operand on any incoming
// path, we return true. Additional verification is added to ensure
// GuaranteedForwarding phi operands are found on zero or all paths in the
// OwnershipVerifier.
bool isGuaranteedForwardingPhi = false;
phi->visitTransitiveIncomingPhiOperands([&](auto *, auto *op) -> bool {
auto opValue = op->get();
assert(opValue->getOwnershipKind().isCompatibleWith(
OwnershipKind::Guaranteed));
if (canOpcodeForwardInnerGuaranteedValues(opValue) ||
isa<SILFunctionArgument>(opValue)) {
isGuaranteedForwardingPhi = true;
return false;
}
auto *phi = dyn_cast<SILPhiArgument>(opValue);
if (!phi || !phi->isPhi()) {
return false;
}
return true;
});
return isGuaranteedForwardingPhi;
}

Expand Down Expand Up @@ -527,7 +535,7 @@ class BorrowedValueKind {
})) {
return Kind::Invalid;
}
if (isGuaranteedForwardingPhi(value)) {
if (isGuaranteedForwarding(value)) {
return Kind::Invalid;
}
return Kind::Phi;
Expand Down
4 changes: 0 additions & 4 deletions include/swift/SIL/SILValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -825,8 +825,6 @@ struct OperandOwnership {
/// borrow scope.
/// (tuple_extract, struct_extract, cast, switch)
GuaranteedForwarding,
/// A GuaranteedForwarding value passed as a phi operand.
GuaranteedForwardingPhi,
/// End Borrow. End the borrow scope opened directly by the operand.
/// The operand must be a begin_borrow, begin_apply, or function argument.
/// (end_borrow, end_apply)
Expand Down Expand Up @@ -919,7 +917,6 @@ inline OwnershipConstraint OperandOwnership::getOwnershipConstraint() {
return {OwnershipKind::Owned, UseLifetimeConstraint::LifetimeEnding};
case OperandOwnership::InteriorPointer:
case OperandOwnership::GuaranteedForwarding:
case OperandOwnership::GuaranteedForwardingPhi:
return {OwnershipKind::Guaranteed,
UseLifetimeConstraint::NonLifetimeEnding};
case OperandOwnership::EndBorrow:
Expand Down Expand Up @@ -948,7 +945,6 @@ inline bool canAcceptUnownedValue(OperandOwnership operandOwnership) {
case OperandOwnership::ForwardingConsume:
case OperandOwnership::InteriorPointer:
case OperandOwnership::GuaranteedForwarding:
case OperandOwnership::GuaranteedForwardingPhi:
case OperandOwnership::EndBorrow:
case OperandOwnership::Reborrow:
return false;
Expand Down
7 changes: 2 additions & 5 deletions lib/SIL/IR/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ bool swift::checkOperandOwnershipInvariants(const Operand *operand,
// Must be a valid BorrowingOperand.
return bool(BorrowingOperand(const_cast<Operand *>(operand)));
}
if (opOwnership == OperandOwnership::GuaranteedForwarding) {
return canOpcodeForwardGuaranteedValues(const_cast<Operand *>(operand));
}
return true;
}

Expand Down Expand Up @@ -430,8 +427,8 @@ OperandOwnership OperandOwnershipClassifier::visitBranchInst(BranchInst *bi) {
bi->getDestBB()->getArgument(getOperandIndex())->getOwnershipKind();

if (destBlockArgOwnershipKind == OwnershipKind::Guaranteed) {
return isGuaranteedForwardingPhi(getValue())
? OperandOwnership::GuaranteedForwardingPhi
return isGuaranteedForwarding(getValue())
? OperandOwnership::GuaranteedForwarding
: OperandOwnership::Reborrow;
}
return destBlockArgOwnershipKind.getForwardingOperandOwnership(
Expand Down
2 changes: 0 additions & 2 deletions lib/SIL/IR/SILValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,8 +441,6 @@ StringRef OperandOwnership::asString() const {
return "interior-pointer";
case OperandOwnership::GuaranteedForwarding:
return "guaranteed-forwarding";
case OperandOwnership::GuaranteedForwardingPhi:
return "guaranteed-forwarding-phi";
case OperandOwnership::EndBorrow:
return "end-borrow";
case OperandOwnership::Reborrow:
Expand Down
145 changes: 70 additions & 75 deletions lib/SIL/Utils/OwnershipUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ bool swift::hasPointerEscape(BorrowedValue value) {
case OperandOwnership::InteriorPointer:
case OperandOwnership::BitwiseEscape:
break;

case OperandOwnership::GuaranteedForwardingPhi:
case OperandOwnership::Reborrow: {
SILArgument *phi = cast<BranchInst>(op->getUser())
->getDestBB()
Expand Down Expand Up @@ -84,17 +82,23 @@ bool swift::hasPointerEscape(BorrowedValue value) {
return false;
}

bool swift::canOpcodeForwardGuaranteedValues(SILValue value) {
if (auto *inst = value->getDefiningInstructionOrTerminator()) {
if (auto *mixin = OwnershipForwardingMixin::get(inst)) {
bool swift::canOpcodeForwardInnerGuaranteedValues(SILValue value) {
// If we have an argument from a transforming terminator, we can forward
// guaranteed.
if (auto *arg = dyn_cast<SILArgument>(value))
if (auto *ti = arg->getSingleTerminator())
if (ti->mayHaveTerminatorResult())
return OwnershipForwardingMixin::get(ti)->preservesOwnership();

if (auto *inst = value->getDefiningInstruction())
if (auto *mixin = OwnershipForwardingMixin::get(inst))
return mixin->preservesOwnership() &&
!isa<OwnedFirstArgForwardingSingleValueInst>(inst);
}
}
!isa<OwnedFirstArgForwardingSingleValueInst>(inst);

return false;
}

bool swift::canOpcodeForwardGuaranteedValues(Operand *use) {
bool swift::canOpcodeForwardInnerGuaranteedValues(Operand *use) {
if (auto *mixin = OwnershipForwardingMixin::get(use->getUser()))
return mixin->preservesOwnership() &&
!isa<OwnedFirstArgForwardingSingleValueInst>(use->getUser());
Expand Down Expand Up @@ -213,6 +217,11 @@ bool swift::findInnerTransitiveGuaranteedUses(
// Do not include transitive uses with 'none' ownership
if (result->getOwnershipKind() == OwnershipKind::None)
return true;
if (auto *phi = SILArgument::asPhi(result)) {
leafUse(use);
foundPointerEscape = true;
return true;
}
for (auto *resultUse : result->getUses()) {
if (resultUse->getOperandOwnership() != OperandOwnership::NonUse) {
nonLeaf = true;
Expand All @@ -228,11 +237,6 @@ bool swift::findInnerTransitiveGuaranteedUses(
}
break;
}
case OperandOwnership::GuaranteedForwardingPhi: {
leafUse(use);
foundPointerEscape = true;
break;
}
case OperandOwnership::Borrow:
// FIXME: Use visitExtendedScopeEndingUses and audit all clients to handle
// reborrows.
Expand Down Expand Up @@ -330,17 +334,6 @@ bool swift::findExtendedUsesOfSimpleBorrowedValue(
}
recordUse(use);
break;
// \p borrowedValue will dominate this GuaranteedForwardingPhi, because we
// return false in the case of Reborrow.
case OperandOwnership::GuaranteedForwardingPhi: {
SILArgument *phi = PhiOperand(use).getValue();
for (auto *use : phi->getUses()) {
if (use->getOperandOwnership() != OperandOwnership::NonUse)
worklist.insert(use);
}
recordUse(use);
break;
}
case OperandOwnership::GuaranteedForwarding: {
ForwardingOperand(use).visitForwardedValues([&](SILValue result) {
// Do not include transitive uses with 'none' ownership
Expand Down Expand Up @@ -413,15 +406,12 @@ bool swift::visitGuaranteedForwardingPhisForSSAValue(
// GuaranteedForwardingPhi uses.
for (auto *use : value->getUses()) {
if (use->getOperandOwnership() == OperandOwnership::GuaranteedForwarding) {
guaranteedForwardingOps.insert(use);
continue;
}
if (use->getOperandOwnership() ==
OperandOwnership::GuaranteedForwardingPhi) {
if (!visitor(use)) {
return false;
if (PhiOperand(use)) {
if (!visitor(use)) {
return false;
}
}
continue;
guaranteedForwardingOps.insert(use);
}
}

Expand All @@ -431,15 +421,12 @@ bool swift::visitGuaranteedForwardingPhisForSSAValue(
for (auto *valUse : val->getUses()) {
if (valUse->getOperandOwnership() ==
OperandOwnership::GuaranteedForwarding) {
guaranteedForwardingOps.insert(valUse);
continue;
}
if (valUse->getOperandOwnership() ==
OperandOwnership::GuaranteedForwardingPhi) {
if (!visitor(valUse)) {
return false;
if (PhiOperand(valUse)) {
if (!visitor(valUse)) {
return false;
}
}
continue;
guaranteedForwardingOps.insert(valUse);
}
}
}
Expand Down Expand Up @@ -1208,11 +1195,12 @@ bool swift::getAllBorrowIntroducingValues(SILValue inputValue,
if (inputValue->getOwnershipKind() != OwnershipKind::Guaranteed)
return false;

SmallVector<SILValue, 32> worklist;
worklist.emplace_back(inputValue);
SmallSetVector<SILValue, 32> worklist;
worklist.insert(inputValue);

while (!worklist.empty()) {
SILValue value = worklist.pop_back_val();
// worklist grows in this loop.
for (unsigned idx = 0; idx < worklist.size(); idx++) {
SILValue value = worklist[idx];

// First check if v is an introducer. If so, stash it and continue.
if (auto scopeIntroducer = BorrowedValue(value)) {
Expand All @@ -1230,11 +1218,26 @@ bool swift::getAllBorrowIntroducingValues(SILValue inputValue,
// Otherwise if v is an ownership forwarding value, add its defining
// instruction
if (isGuaranteedForwarding(value)) {
if (auto *i = value->getDefiningInstructionOrTerminator()) {
llvm::copy(i->getNonTypeDependentOperandValues(),
std::back_inserter(worklist));
if (auto *i = value->getDefiningInstruction()) {
for (SILValue opValue : i->getNonTypeDependentOperandValues()) {
worklist.insert(opValue);
}
continue;
}

// Otherwise, we should have a block argument that is defined by a single
// predecessor terminator.
auto *arg = cast<SILPhiArgument>(value);
if (arg->isTerminatorResult()) {
if (auto *forwardedOper = arg->forwardedTerminatorResultOperand()) {
worklist.insert(forwardedOper->get());
continue;
}
}
arg->visitIncomingPhiOperands([&](auto *operand) {
worklist.insert(operand->get());
return true;
});
}

// Otherwise, this is an introducer we do not understand. Bail and return
Expand Down Expand Up @@ -1373,31 +1376,16 @@ ForwardingOperand::ForwardingOperand(Operand *use) {
if (use->isTypeDependent())
return;

if (!OwnershipForwardingMixin::isa(use->getUser())) {
return;
}
#ifndef NDEBUG
switch (use->getOperandOwnership()) {
case OperandOwnership::ForwardingUnowned:
case OperandOwnership::ForwardingConsume:
case OperandOwnership::GuaranteedForwarding:
this->use = use;
break;
case OperandOwnership::NonUse:
case OperandOwnership::TrivialUse:
case OperandOwnership::InstantaneousUse:
case OperandOwnership::UnownedInstantaneousUse:
case OperandOwnership::PointerEscape:
case OperandOwnership::BitwiseEscape:
case OperandOwnership::Borrow:
case OperandOwnership::DestroyingConsume:
case OperandOwnership::InteriorPointer:
case OperandOwnership::GuaranteedForwardingPhi:
case OperandOwnership::EndBorrow:
case OperandOwnership::Reborrow:
llvm_unreachable("this isn't the operand being forwarding!");
default:
this->use = nullptr;
return;
}
#endif
this->use = use;
}

ValueOwnershipKind ForwardingOperand::getForwardingOwnershipKind() const {
Expand Down Expand Up @@ -1603,15 +1591,22 @@ bool ForwardingOperand::visitForwardedValues(
// "transforming terminators"... We know that this means that we should at
// most have a single phi argument.
auto *ti = cast<TermInst>(user);
return llvm::all_of(ti->getSuccessorBlocks(), [&](SILBasicBlock *succBlock) {
// If we do not have any arguments, then continue.
if (succBlock->args_empty())
return true;
if (ti->mayHaveTerminatorResult()) {
return llvm::all_of(
ti->getSuccessorBlocks(), [&](SILBasicBlock *succBlock) {
// If we do not have any arguments, then continue.
if (succBlock->args_empty())
return true;

auto args = succBlock->getSILPhiArguments();
assert(args.size() == 1 && "Transforming terminator with multiple args?!");
return visitor(args[0]);
});
auto args = succBlock->getSILPhiArguments();
assert(args.size() == 1 &&
"Transforming terminator with multiple args?!");
return visitor(args[0]);
});
}

auto *succArg = PhiOperand(use).getValue();
return visitor(succArg);
}

void swift::visitExtendedReborrowPhiBaseValuePairs(
Expand Down
4 changes: 0 additions & 4 deletions lib/SIL/Utils/PrunedLiveness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,6 @@ PrunedLiveRange<LivenessWithDefs>::updateForDef(SILValue def) {
updateForUse(use->getUser(), /*lifetimeEnding*/false);
break;
}
case OperandOwnership::GuaranteedForwardingPhi: {
updateForDef(PhiOperand(use).getValue());
break;
}
default:
updateForUse(use->getUser(), use->isLifetimeEnding());
break;
Expand Down
Loading