Skip to content

Commit d2cc353

Browse files
authored
Merge pull request #18234 from atrick/exclusivity-pa-phi-use
2 parents 3deee08 + e3f5de9 commit d2cc353

File tree

9 files changed

+435
-152
lines changed

9 files changed

+435
-152
lines changed

include/swift/SIL/InstructionUtils.h

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ SILValue stripExpectIntrinsic(SILValue V);
8383
SILValue stripBorrow(SILValue V);
8484

8585
/// Return a non-null SingleValueInstruction if the given instruction merely
86-
/// copies a value, possibly changing its type or ownership state, but otherwise
87-
/// having no effect.
86+
/// copies the value of its first operand, possibly changing its type or
87+
/// ownership state, but otherwise having no effect.
8888
///
8989
/// This is useful for checking all users of a value to verify that the value is
9090
/// only used in recognizable patterns without otherwise "escaping". These are
@@ -123,17 +123,9 @@ SILValue stripConvertFunctions(SILValue V);
123123
/// argument of the partial apply if it is.
124124
SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI);
125125

126-
struct LLVM_LIBRARY_VISIBILITY FindClosureResult {
127-
PartialApplyInst *PAI = nullptr;
128-
bool isReabstructionThunk = false;
129-
FindClosureResult(PartialApplyInst *PAI, bool isReabstructionThunk)
130-
: PAI(PAI), isReabstructionThunk(isReabstructionThunk) {}
131-
};
132-
133-
/// If V is a function closure, return the partial_apply and the
134-
/// IsReabstractionThunk flag set to true if the closure is indirectly captured
135-
/// by a reabstraction thunk.
136-
FindClosureResult findClosureForAppliedArg(SILValue V);
126+
/// If V is a function closure, return the reaching set of partial_apply's.
127+
void findClosuresForFunctionValue(SILValue V,
128+
TinyPtrVector<PartialApplyInst *> &results);
137129

138130
/// A utility class for evaluating whether a newly parsed or deserialized
139131
/// function has qualified or unqualified ownership.

include/swift/SIL/SILInstruction.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class SILInstructionResultArray;
6262
class SILOpenedArchetypesState;
6363
class SILType;
6464
class SILArgument;
65+
class SILPHIArgument;
6566
class SILUndef;
6667
class Stmt;
6768
class StringLiteralExpr;
@@ -6835,6 +6836,11 @@ class BranchInst final
68356836

68366837
unsigned getNumArgs() const { return getAllOperands().size(); }
68376838
SILValue getArg(unsigned i) const { return getAllOperands()[i].get(); }
6839+
6840+
/// Return the SILPHIArgument for the given operand.
6841+
///
6842+
/// See SILArgument.cpp.
6843+
const SILPHIArgument *getArgForOperand(const Operand *oper) const;
68386844
};
68396845

68406846
/// A conditional branch.
@@ -6976,6 +6982,15 @@ class CondBranchInst final
69766982
SILValue getArgForDestBB(const SILBasicBlock *DestBB,
69776983
unsigned ArgIndex) const;
69786984

6985+
/// Return the SILPHIArgument from either the true or false destination for
6986+
/// the given operand.
6987+
///
6988+
/// Returns nullptr for an operand with no block argument
6989+
/// (i.e the branch condition).
6990+
///
6991+
/// See SILArgument.cpp.
6992+
const SILPHIArgument *getArgForOperand(const Operand *oper) const;
6993+
69796994
void swapSuccessors();
69806995
};
69816996

lib/SIL/InstructionUtils.cpp

Lines changed: 93 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@ SILValue swift::stripBorrow(SILValue V) {
247247
return V;
248248
}
249249

250+
// All instructions handled here must propagate their first operand into their
251+
// single result.
252+
//
253+
// This is guaranteed to handle all function-type converstions: ThinToThick,
254+
// ConvertFunction, and ConvertEscapeToNoEscapeInst.
250255
SingleValueInstruction *swift::getSingleValueCopyOrCast(SILInstruction *I) {
251256
if (auto *convert = dyn_cast<ConversionInst>(I))
252257
return convert;
@@ -259,6 +264,7 @@ SingleValueInstruction *swift::getSingleValueCopyOrCast(SILInstruction *I) {
259264
case SILInstructionKind::CopyBlockWithoutEscapingInst:
260265
case SILInstructionKind::BeginBorrowInst:
261266
case SILInstructionKind::BeginAccessInst:
267+
case SILInstructionKind::MarkDependenceInst:
262268
return cast<SingleValueInstruction>(I);
263269
}
264270
}
@@ -353,9 +359,12 @@ SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) {
353359
return Arg;
354360
}
355361

356-
/// Given a block used as a noescape function argument, attempt to find
357-
/// the Swift closure that invoking the block will call.
362+
/// Given a block used as a noescape function argument, attempt to find all
363+
/// Swift closures that invoking the block will call. The StoredClosures may not
364+
/// actually be partial_apply instructions. They may be copied, block arguments,
365+
/// or conversions. The caller must continue searching up the use-def chain.
358366
static SILValue findClosureStoredIntoBlock(SILValue V) {
367+
359368
auto FnType = V->getType().castTo<SILFunctionType>();
360369
assert(FnType->getRepresentation() == SILFunctionTypeRepresentation::Block);
361370
(void)FnType;
@@ -374,24 +383,7 @@ static SILValue findClosureStoredIntoBlock(SILValue V) {
374383
// %block = init_block_storage_header %storage invoke %thunk
375384
// %arg = copy_block %block
376385

377-
InitBlockStorageHeaderInst *IBSHI = nullptr;
378-
379-
// Look through block copies to find the initialization of block storage.
380-
while (true) {
381-
if (auto *CBI = dyn_cast<CopyBlockInst>(V)) {
382-
V = CBI->getOperand();
383-
continue;
384-
}
385-
386-
if (auto *CBI = dyn_cast<CopyBlockWithoutEscapingInst>(V)) {
387-
V = CBI->getBlock();
388-
continue;
389-
}
390-
391-
IBSHI = dyn_cast<InitBlockStorageHeaderInst>(V);
392-
break;
393-
}
394-
386+
InitBlockStorageHeaderInst *IBSHI = dyn_cast<InitBlockStorageHeaderInst>(V);
395387
if (!IBSHI)
396388
return nullptr;
397389

@@ -414,45 +406,93 @@ static SILValue findClosureStoredIntoBlock(SILValue V) {
414406
auto NoEscapeClosure = isPartialApplyOfReabstractionThunk(Sentinel);
415407
if (WrappedNoEscape->getBase() != NoEscapeClosure)
416408
return nullptr;
409+
410+
// This is the value of the closure to be invoked. To find the partial_apply
411+
// itself, the caller must search the use-def chain.
417412
return NoEscapeClosure;
418413
}
419414

420-
/// Look through a value passed as a function argument to determine whether
421-
/// it is a closure.
415+
/// Find all closures that may be propagated into the given function-type value.
422416
///
423-
/// Return the partial_apply and a flag set to true if the closure is
424-
/// indirectly captured by a reabstraction thunk.
425-
FindClosureResult swift::findClosureForAppliedArg(SILValue V) {
426-
// Look through borrows.
427-
if (auto *bbi = dyn_cast<BeginBorrowInst>(V))
428-
V = bbi->getOperand();
429-
430-
if (V->getType().getOptionalObjectType()) {
431-
auto *EI = dyn_cast<EnumInst>(V);
432-
if (!EI || !EI->hasOperand())
433-
return FindClosureResult(nullptr, false);
434-
435-
V = EI->getOperand();
436-
}
417+
/// Searches the use-def chain from the given value upward until a partial_apply
418+
/// is reached. Populates `results` with the set of partial_apply instructions.
419+
///
420+
/// `funcVal` may be either a function type or an Optional function type. This
421+
/// might be called on a directly applied value or on a call argument, which may
422+
/// in turn be applied within the callee.
423+
void swift::findClosuresForFunctionValue(
424+
SILValue funcVal, TinyPtrVector<PartialApplyInst *> &results) {
425+
426+
SILType funcTy = funcVal->getType();
427+
// Handle `Optional<@convention(block) @noescape (_)->(_)>`
428+
if (auto optionalObjTy = funcTy.getOptionalObjectType())
429+
funcTy = optionalObjTy;
430+
assert(funcTy.is<SILFunctionType>());
431+
432+
SmallVector<SILValue, 4> worklist;
433+
// Avoid exponential path exploration and prevent duplicate results.
434+
llvm::SmallDenseSet<SILValue, 8> visited;
435+
auto worklistInsert = [&](SILValue V) {
436+
if (visited.insert(V).second)
437+
worklist.push_back(V);
438+
};
439+
worklistInsert(funcVal);
440+
441+
while (!worklist.empty()) {
442+
SILValue V = worklist.pop_back_val();
443+
444+
if (auto *I = V->getDefiningInstruction()) {
445+
// Look through copies, borrows, and conversions.
446+
//
447+
// Handle copy_block and copy_block_without_actually_escaping before
448+
// calling findClosureStoredIntoBlock.
449+
if (SingleValueInstruction *SVI = getSingleValueCopyOrCast(I)) {
450+
worklistInsert(SVI->getOperand(0));
451+
continue;
452+
}
453+
}
454+
// Look through Optionals.
455+
if (V->getType().getOptionalObjectType()) {
456+
auto *EI = dyn_cast<EnumInst>(V);
457+
if (EI && EI->hasOperand()) {
458+
worklistInsert(EI->getOperand());
459+
}
460+
// Ignore the .None case.
461+
continue;
462+
}
463+
// Look through Phis.
464+
//
465+
// This should be done before calling findClosureStoredIntoBlock.
466+
if (auto *arg = dyn_cast<SILPHIArgument>(V)) {
467+
SmallVector<std::pair<SILBasicBlock *, SILValue>, 2> blockArgs;
468+
arg->getIncomingValues(blockArgs);
469+
for (auto &blockAndArg : blockArgs)
470+
worklistInsert(blockAndArg.second);
437471

438-
auto fnType = V->getType().getAs<SILFunctionType>();
439-
if (fnType->getRepresentation() == SILFunctionTypeRepresentation::Block) {
440-
V = findClosureStoredIntoBlock(V);
441-
if (!V)
442-
return FindClosureResult(nullptr, false);
443-
}
444-
auto *PAI = dyn_cast<PartialApplyInst>(stripConvertFunctions(V));
445-
if (!PAI)
446-
return FindClosureResult(nullptr, false);
447-
448-
SILValue thunkArg = isPartialApplyOfReabstractionThunk(PAI);
449-
if (thunkArg) {
450-
// Handle reabstraction thunks recursively. This may reabstract over
451-
// @convention(block).
452-
auto result = findClosureForAppliedArg(thunkArg);
453-
return FindClosureResult(result.PAI, true);
472+
continue;
473+
}
474+
// Look through ObjC closures.
475+
auto fnType = V->getType().getAs<SILFunctionType>();
476+
if (fnType
477+
&& fnType->getRepresentation() == SILFunctionTypeRepresentation::Block) {
478+
if (SILValue storedClosure = findClosureStoredIntoBlock(V))
479+
worklistInsert(storedClosure);
480+
481+
continue;
482+
}
483+
if (auto *PAI = dyn_cast<PartialApplyInst>(V)) {
484+
SILValue thunkArg = isPartialApplyOfReabstractionThunk(PAI);
485+
if (thunkArg) {
486+
// Handle reabstraction thunks recursively. This may reabstract over
487+
// @convention(block).
488+
worklistInsert(thunkArg);
489+
continue;
490+
}
491+
results.push_back(PAI);
492+
continue;
493+
}
494+
// Ignore other unrecognized values that feed this applied argument.
454495
}
455-
return FindClosureResult(PAI, false);
456496
}
457497

458498
namespace {

lib/SIL/MemAccessUtils.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -512,12 +512,11 @@ static void visitApplyAccesses(ApplySite apply,
512512

513513
// When @noescape function closures are passed as arguments, their
514514
// arguments are considered accessed at the call site.
515-
FindClosureResult result = findClosureForAppliedArg(oper.get());
516-
if (!result.PAI)
517-
continue;
518-
515+
TinyPtrVector<PartialApplyInst *> partialApplies;
516+
findClosuresForFunctionValue(oper.get(), partialApplies);
519517
// Recursively visit @noescape function closure arguments.
520-
visitApplyAccesses(result.PAI, visitor);
518+
for (auto *PAI : partialApplies)
519+
visitApplyAccesses(PAI, visitor);
521520
}
522521
}
523522

lib/SIL/SILArgument.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,28 @@ SILValue SILPHIArgument::getIncomingValue(SILBasicBlock *BB) {
188188
return getIncomingValueForPred(Parent, BB, Index);
189189
}
190190

191+
const SILPHIArgument *BranchInst::getArgForOperand(const Operand *oper) const {
192+
assert(oper->getUser() == this);
193+
return cast<SILPHIArgument>(
194+
getDestBB()->getArgument(oper->getOperandNumber()));
195+
}
196+
197+
const SILPHIArgument *
198+
CondBranchInst::getArgForOperand(const Operand *oper) const {
199+
assert(oper->getUser() == this);
200+
201+
unsigned operIdx = oper->getOperandNumber();
202+
if (isTrueOperandIndex(operIdx)) {
203+
return cast<SILPHIArgument>(getTrueBB()->getArgument(
204+
operIdx - getTrueOperands().front().getOperandNumber()));
205+
}
206+
if (isFalseOperandIndex(operIdx)) {
207+
return cast<SILPHIArgument>(getFalseBB()->getArgument(
208+
operIdx - getFalseOperands().front().getOperandNumber()));
209+
}
210+
return nullptr;
211+
}
212+
191213
//===----------------------------------------------------------------------===//
192214
// SILFunctionArgument
193215
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)