Skip to content

Commit 19b6de6

Browse files
committed
[capture-promotion] Change isNonEscapingUse to use a visitor.
1 parent c373672 commit 19b6de6

File tree

1 file changed

+120
-65
lines changed

1 file changed

+120
-65
lines changed

lib/SILOptimizer/IPO/CapturePromotion.cpp

Lines changed: 120 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -630,88 +630,143 @@ isNonMutatingCapture(SILArgument *BoxArg) {
630630
return true;
631631
}
632632

633-
/// \brief Given a use of an alloc_box instruction, return true if the use
634-
/// definitely does not allow the box to escape; also, if the use is an
635-
/// instruction which possibly mutates the contents of the box, then add it to
636-
/// the Mutations vector.
637-
static bool isNonEscapingUse(Operand *InitialOp,
638-
SmallVectorImpl<SILInstruction *> &Mutations) {
639-
llvm::SmallVector<Operand *, 32> Worklist;
640-
Worklist.push_back(InitialOp);
633+
namespace {
641634

642-
while (!Worklist.empty()) {
643-
auto *Op = Worklist.pop_back_val();
644-
SILInstruction *User = Op->getUser();
635+
class NonEscapingUserVisitor
636+
: public SILInstructionVisitor<NonEscapingUserVisitor, bool> {
637+
llvm::SmallVector<Operand *, 32> Worklist;
638+
llvm::SmallVectorImpl<SILInstruction *> &Mutations;
639+
NullablePtr<Operand> CurrentOp;
645640

646-
if (User->isTypeDependentOperand(*Op))
647-
continue;
641+
public:
642+
NonEscapingUserVisitor(Operand *Op,
643+
llvm::SmallVectorImpl<SILInstruction *> &Mutations)
644+
: Worklist(), Mutations(Mutations), CurrentOp() {
645+
Worklist.push_back(Op);
646+
}
648647

649-
// Marking the boxed value as escaping is OK. It's just a DI annotation.
650-
if (isa<MarkFunctionEscapeInst>(User))
651-
continue;
648+
NonEscapingUserVisitor(const NonEscapingUserVisitor &) = delete;
649+
NonEscapingUserVisitor &operator=(const NonEscapingUserVisitor &) = delete;
650+
NonEscapingUserVisitor(NonEscapingUserVisitor &&) = delete;
651+
NonEscapingUserVisitor &operator=(NonEscapingUserVisitor &&) = delete;
652652

653-
// A store or assign is ok if the alloc_box is the destination.
654-
if (isa<StoreInst>(User) || isa<AssignInst>(User)) {
655-
if (Op->getOperandNumber() != 1)
656-
return false;
657-
Mutations.push_back(User);
658-
continue;
659-
}
653+
bool compute() {
654+
while (!Worklist.empty()) {
655+
CurrentOp = Worklist.pop_back_val();
656+
SILInstruction *User = CurrentOp.get()->getUser();
660657

661-
// copy_addr is ok, but counts as a mutation if the use is as the
662-
// destination or the copy_addr is a take.
663-
if (auto *CAI = dyn_cast<CopyAddrInst>(User)) {
664-
if (Op->getOperandNumber() == 1 || CAI->isTakeOfSrc())
665-
Mutations.push_back(CAI);
666-
continue;
667-
}
658+
// Ignore type dependent operands.
659+
if (User->isTypeDependentOperand(*(CurrentOp.get())))
660+
continue;
668661

669-
// Recursively see through struct_element_addr, tuple_element_addr, and
670-
// open_existential_addr instructions.
671-
if (isa<StructElementAddrInst>(User) || isa<TupleElementAddrInst>(User) ||
672-
isa<InitEnumDataAddrInst>(User) || isa<OpenExistentialAddrInst>(User) ||
673-
isa<UncheckedTakeEnumDataAddrInst>(User)) {
674-
// UncheckedTakeEnumDataAddr is additionally a mutation.
675-
if (isa<UncheckedTakeEnumDataAddrInst>(User))
676-
Mutations.push_back(User);
677-
678-
for (auto *UserOperand : User->getUses()) {
679-
Worklist.push_back(UserOperand);
662+
// Then visit the specific user. This routine returns true if the value
663+
// does not escape. In such a case, continue.
664+
if (visit(User)) {
665+
continue;
680666
}
681667

682-
continue;
668+
return false;
683669
}
684670

685-
// An apply is ok if the argument is used as an inout parameter or an
686-
// indirect return, but counts as a possible mutation in both cases.
687-
if (auto *AI = dyn_cast<ApplyInst>(User)) {
688-
auto argIndex = Op->getOperandNumber() - 1;
689-
SILFunctionConventions substConv(AI->getSubstCalleeType(),
690-
AI->getModule());
691-
auto convention = substConv.getSILArgumentConvention(argIndex);
692-
if (!convention.isIndirectConvention()) {
693-
return false;
694-
}
695-
Mutations.push_back(AI);
696-
continue;
697-
}
671+
return true;
672+
}
698673

699-
// These instructions are ok but count as mutations.
700-
if (isa<DeallocBoxInst>(User)) {
701-
Mutations.push_back(User);
702-
continue;
674+
/// Visit a random value base.
675+
///
676+
/// These are considered to be escapes.
677+
bool visitValueBase(ValueBase *V) { return false; }
678+
679+
#define ALWAYS_NON_ESCAPING_INST(INST) \
680+
bool visit##INST##Inst(INST##Inst *V) { return true; }
681+
// Marking the boxed value as escaping is OK. It's just a DI annotation.
682+
ALWAYS_NON_ESCAPING_INST(MarkFunctionEscape)
683+
// These remaining instructions are ok and don't count as mutations.
684+
ALWAYS_NON_ESCAPING_INST(StrongRetain)
685+
ALWAYS_NON_ESCAPING_INST(Load)
686+
ALWAYS_NON_ESCAPING_INST(StrongRelease)
687+
#undef ALWAYS_NON_ESCAPING_INST
688+
689+
bool visitDeallocBoxInst(DeallocBoxInst *DBI) {
690+
Mutations.push_back(DBI);
691+
return true;
692+
}
693+
694+
bool visitApplyInst(ApplyInst *AI) {
695+
auto argIndex = CurrentOp.get()->getOperandNumber() - 1;
696+
SILFunctionConventions substConv(AI->getSubstCalleeType(), AI->getModule());
697+
auto convention = substConv.getSILArgumentConvention(argIndex);
698+
if (!convention.isIndirectConvention()) {
699+
return false;
703700
}
701+
Mutations.push_back(AI);
702+
return true;
703+
}
704704

705-
// These remaining instructions are ok and don't count as mutations.
706-
if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) ||
707-
isa<LoadInst>(User)) {
708-
continue;
705+
/// Add the Operands of a transitive use instruction to the worklist.
706+
void addUserOperandsToWorklist(SILInstruction *I) {
707+
for (auto *User : I->getUses()) {
708+
Worklist.push_back(User);
709709
}
710+
}
710711

711-
return false;
712+
bool visitStructElementAddrInst(StructElementAddrInst *I) {
713+
addUserOperandsToWorklist(I);
714+
return true;
712715
}
713716

714-
return true;
717+
bool visitTupleElementAddrInst(TupleElementAddrInst *I) {
718+
addUserOperandsToWorklist(I);
719+
return true;
720+
}
721+
722+
bool visitInitEnumDataAddrInst(InitEnumDataAddrInst *I) {
723+
addUserOperandsToWorklist(I);
724+
return true;
725+
}
726+
727+
bool visitOpenExistentialAddrInst(OpenExistentialAddrInst *I) {
728+
addUserOperandsToWorklist(I);
729+
return true;
730+
}
731+
732+
bool visitUncheckedTakeEnumDataAddrInst(UncheckedTakeEnumDataAddrInst *I) {
733+
// UncheckedTakeEnumDataAddr is additionally a mutation.
734+
Mutations.push_back(I);
735+
736+
addUserOperandsToWorklist(I);
737+
return true;
738+
}
739+
740+
bool visitCopyAddrInst(CopyAddrInst *CAI) {
741+
if (CurrentOp.get()->getOperandNumber() == 1 || CAI->isTakeOfSrc())
742+
Mutations.push_back(CAI);
743+
return true;
744+
}
745+
746+
bool visitStoreInst(StoreInst *SI) {
747+
if (CurrentOp.get()->getOperandNumber() != 1)
748+
return false;
749+
Mutations.push_back(SI);
750+
return true;
751+
}
752+
753+
bool visitAssignInst(AssignInst *AI) {
754+
if (CurrentOp.get()->getOperandNumber() != 1)
755+
return false;
756+
Mutations.push_back(AI);
757+
return true;
758+
}
759+
};
760+
761+
} // end anonymous namespace
762+
763+
/// \brief Given a use of an alloc_box instruction, return true if the use
764+
/// definitely does not allow the box to escape; also, if the use is an
765+
/// instruction which possibly mutates the contents of the box, then add it to
766+
/// the Mutations vector.
767+
static bool isNonEscapingUse(Operand *InitialOp,
768+
SmallVectorImpl<SILInstruction *> &Mutations) {
769+
return NonEscapingUserVisitor(InitialOp, Mutations).compute();
715770
}
716771

717772
/// \brief Examine an alloc_box instruction, returning true if at least one

0 commit comments

Comments
 (0)