Skip to content

Commit e61fc66

Browse files
authored
Merge pull request #3244 from trentxintong/ReleaseCM
Implement an iterative data flow to find epilogue retains or releases
2 parents 306edda + eaaf825 commit e61fc66

File tree

6 files changed

+616
-0
lines changed

6 files changed

+616
-0
lines changed

include/swift/SILOptimizer/Analysis/ARCAnalysis.h

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
#include "swift/SIL/SILArgument.h"
1717
#include "swift/SIL/SILValue.h"
1818
#include "swift/SIL/SILBasicBlock.h"
19+
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
20+
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
21+
#include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h"
22+
#include "llvm/ADT/BitVector.h"
1923
#include "llvm/ADT/SmallPtrSet.h"
2024
#include "llvm/ADT/MapVector.h"
2125
#include "llvm/ADT/SetVector.h"
@@ -386,4 +390,184 @@ SILInstruction *findReleaseToMatchUnsafeGuaranteedValue(
386390

387391
} // end namespace swift
388392

393+
394+
namespace swift {
395+
396+
/// EpilogueARCBlockState - Keep track of whether a epilogue ARC instruction has
397+
/// been found.
398+
struct EpilogueARCBlockState {
399+
/// Keep track of whether a eplogue release has been found before and after
400+
/// this basic block.
401+
bool BBSetIn;
402+
/// The basic block local SILValue we are interested to find epilogue ARC in.
403+
SILValue LocalArg;
404+
/// Constructor, we only compute epilogue ARC instruction for 1 argument at
405+
/// a time.
406+
/// Optimistic data flow.
407+
EpilogueARCBlockState() { BBSetIn = true; LocalArg = SILValue(); }
408+
};
409+
410+
/// EpilogueARCContext - This class implements a data flow with which epilogue
411+
/// retains or releases for a SILValue are found.
412+
///
413+
/// NOTE:
414+
/// In case of release finder, this function assumes the SILArgument has
415+
/// @owned semantic.
416+
/// In case of retain finder, this class assumes Arg is one of the return value
417+
/// of the function.
418+
class EpilogueARCContext {
419+
public:
420+
enum EpilogueARCKind { Retain = 0, Release = 1 };
421+
422+
private:
423+
// Are we finding retains or releases.
424+
EpilogueARCKind Kind;
425+
426+
// The argument we are looking for epilogue ARC instruction for.
427+
SILValue Arg;
428+
429+
/// The allocator we are currently using.
430+
llvm::SpecificBumpPtrAllocator<EpilogueARCBlockState> BPA;
431+
432+
/// Current function we are analyzing.
433+
SILFunction *F;
434+
435+
/// Current post-order we are using.
436+
PostOrderFunctionInfo *PO;
437+
438+
/// Current alias analysis we are using.
439+
AliasAnalysis *AA;
440+
441+
/// Current rc-identity we are using.
442+
RCIdentityFunctionInfo *RCFI;
443+
444+
/// The epilogue retains or releases.
445+
llvm::SmallVector<SILInstruction *, 1> EpilogueARCInsts;
446+
447+
/// All the retain/release block state for all the basic blocks in the function.
448+
llvm::DenseMap<SILBasicBlock *, EpilogueARCBlockState *> EpilogueARCBlockStates;
449+
450+
/// The exit blocks of the function.
451+
llvm::SmallPtrSet<SILBasicBlock *, 2> ExitBlocks;
452+
453+
/// Return true if this is a function exit block.
454+
bool isExitBlock(SILBasicBlock *BB) {
455+
return ExitBlocks.count(BB);
456+
}
457+
458+
/// Return true if this is a retain instruction.
459+
bool isRetainInstruction(SILInstruction *II) {
460+
return isa<RetainValueInst>(II) || isa<StrongRetainInst>(II);
461+
}
462+
463+
/// Return true if this is a release instruction.
464+
bool isReleaseInstruction(SILInstruction *II) {
465+
return isa<ReleaseValueInst>(II) || isa<StrongReleaseInst>(II);
466+
}
467+
468+
SILValue getArg(SILBasicBlock *B) {
469+
SILValue A = EpilogueARCBlockStates[B]->LocalArg;
470+
if (A)
471+
return A;
472+
return Arg;
473+
}
474+
475+
public:
476+
/// Constructor.
477+
EpilogueARCContext(EpilogueARCKind Kind, SILValue Arg, SILFunction *F,
478+
PostOrderFunctionInfo *PO, AliasAnalysis *AA,
479+
RCIdentityFunctionInfo *RCFI)
480+
: Kind(Kind), Arg(Arg), F(F), PO(PO), AA(AA), RCFI(RCFI) {}
481+
482+
/// Run the data flow to find the epilogue retains or releases.
483+
bool run() {
484+
// Initialize the epilogue arc data flow context.
485+
initializeDataflow();
486+
// Converge the data flow.
487+
convergeDataflow();
488+
// Lastly, find the epilogue ARC instructions.
489+
return computeEpilogueARC();
490+
}
491+
492+
/// Reset the epilogue arc instructions.
493+
void resetEpilogueARCInsts() { EpilogueARCInsts.clear(); }
494+
llvm::SmallVector<SILInstruction *, 1> getEpilogueARCInsts() {
495+
return EpilogueARCInsts;
496+
}
497+
498+
/// Initialize the data flow.
499+
void initializeDataflow();
500+
501+
/// Keep iterating until the data flow is converged.
502+
void convergeDataflow();
503+
504+
/// Find the epilogue ARC instructions.
505+
bool computeEpilogueARC();
506+
507+
/// This instruction prevents looking further for epilogue retains on the
508+
/// current path.
509+
bool mayBlockEpilogueRetain(SILInstruction *II, SILValue Ptr) {
510+
// reference decrementing instruction prevents any retain to be identified as
511+
// epilogue retains.
512+
if (mayDecrementRefCount(II, Ptr, AA))
513+
return true;
514+
// Handle self-recursion. A self-recursion can be considered a +1 on the
515+
// current argument.
516+
if (ApplyInst *AI = dyn_cast<ApplyInst>(II))
517+
if (AI->getCalleeFunction() == II->getParent()->getParent())
518+
return true;
519+
return false;
520+
}
521+
522+
/// This instruction prevents looking further for epilogue releases on the
523+
/// current path.
524+
bool mayBlockEpilogueRelease(SILInstruction *II, SILValue Ptr) {
525+
// Check whether this instruction read reference count, i.e. uniqueness
526+
// check. Moving release past that may result in additional COW.
527+
if (II->mayReleaseOrReadRefCount())
528+
return true;
529+
return false;
530+
}
531+
532+
/// Does this instruction block the interested ARC instruction ?
533+
bool mayBlockEpilogueARC(SILInstruction *II, SILValue Ptr) {
534+
if (Kind == EpilogueARCKind::Retain)
535+
return mayBlockEpilogueRetain(II, Ptr);
536+
return mayBlockEpilogueRelease(II, Ptr);
537+
}
538+
539+
/// This is the type of instructions the data flow is interested in.
540+
bool isInterestedInstruction(SILInstruction *II) {
541+
// We are checking for release.
542+
if (Kind == EpilogueARCKind::Release)
543+
return isReleaseInstruction(II) &&
544+
RCFI->getRCIdentityRoot(II->getOperand(0)) ==
545+
RCFI->getRCIdentityRoot(getArg(II->getParent()));
546+
// We are checking for retain. If this is a self-recursion. call
547+
// to the function (which returns an owned value) can be treated as
548+
// the retain instruction.
549+
if (ApplyInst *AI = dyn_cast<ApplyInst>(II))
550+
if (AI->getCalleeFunction() == II->getParent()->getParent())
551+
return true;
552+
// Check whether this is a retain instruction and the argument it
553+
// retains.
554+
return isRetainInstruction(II) &&
555+
RCFI->getRCIdentityRoot(II->getOperand(0)) ==
556+
RCFI->getRCIdentityRoot(getArg(II->getParent()));
557+
}
558+
};
559+
560+
/// Compute the epilogue ARC instructions for a given SILValue. Return an
561+
/// empty set if no epilogue ARC instructions can be found.
562+
///
563+
/// NOTE: This function assumes Arg is has @owned semantic.
564+
llvm::SmallVector<SILInstruction *, 1>
565+
computeEpilogueARCInstructions(EpilogueARCContext::EpilogueARCKind Kind,
566+
SILValue Arg, SILFunction *F,
567+
PostOrderFunctionInfo *PO, AliasAnalysis *AA,
568+
RCIdentityFunctionInfo *RCFI);
569+
570+
} // end namespace swift
571+
572+
389573
#endif

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ PASS(ConditionForwarding, "condition-forwarding",
7373
"Forward conditional branch instructions")
7474
PASS(CopyForwarding, "copy-forwarding",
7575
"Eliminate redundant copies")
76+
PASS(EpilogueARCMatcherDumper, "sil-epilogue-arc-dumper",
77+
"Dump epilogue retains for return value and releases for arguments")
7678
PASS(EpilogueRetainReleaseMatcherDumper, "sil-epilogue-retain-release-dumper",
7779
"Dump epilogue retains for return value and releases for arguments")
7880
PASS(RedundantOverflowCheckRemoval, "remove-redundant-overflow-checks",

lib/SILOptimizer/Analysis/ARCAnalysis.cpp

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "swift/SILOptimizer/Analysis/ValueTracking.h"
2424
#include "swift/SILOptimizer/Utils/Local.h"
2525
#include "llvm/ADT/StringSwitch.h"
26+
#include "llvm/ADT/BitVector.h"
27+
#include "llvm/ADT/SmallPtrSet.h"
2628
#include "llvm/Support/Debug.h"
2729

2830
using namespace swift;
@@ -1154,3 +1156,151 @@ SILInstruction *swift::findReleaseToMatchUnsafeGuaranteedValue(
11541156
}
11551157
return nullptr;
11561158
}
1159+
1160+
void EpilogueARCContext::initializeDataflow() {
1161+
for (auto &B : *F) {
1162+
// Find the exit blocks.
1163+
if (B.getTerminator()->isFunctionExiting()) {
1164+
ExitBlocks.insert(&B);
1165+
}
1166+
// Allocate the storage.
1167+
EpilogueARCBlockStates[&B] =
1168+
new (BPA.Allocate()) EpilogueARCBlockState();
1169+
}
1170+
1171+
// Split the SILargument into local arguments to each specific basic block.
1172+
llvm::SmallVector<SILValue, 4> ToProcess;
1173+
llvm::DenseSet<SILValue> Processed;
1174+
ToProcess.push_back(Arg);
1175+
while (!ToProcess.empty()) {
1176+
SILValue Arg = ToProcess.pop_back_val();
1177+
if (Processed.find(Arg) != Processed.end())
1178+
continue;
1179+
Processed.insert(Arg);
1180+
SILArgument *A = dyn_cast<SILArgument>(Arg);
1181+
if (A && !A->isFunctionArg()) {
1182+
// Find predecessor and break the SILArgument to predecessors.
1183+
for (auto X : A->getParent()->getPreds()) {
1184+
// Try to find the predecessor edge-value.
1185+
SILValue IA = A->getIncomingValue(X);
1186+
EpilogueARCBlockStates[X]->LocalArg = IA;
1187+
// Maybe the edge value is another SILArgument.
1188+
ToProcess.push_back(IA);
1189+
}
1190+
}
1191+
}
1192+
}
1193+
1194+
void EpilogueARCContext::convergeDataflow() {
1195+
// Keep iterating until Changed is false.
1196+
bool Changed = false;
1197+
do {
1198+
Changed = false;
1199+
// Iterate until the data flow converges.
1200+
for (SILBasicBlock *B : PO->getPostOrder()) {
1201+
auto BS = EpilogueARCBlockStates[B];
1202+
// Merge in all the successors.
1203+
bool BBSetOut = false;
1204+
if (!B->succ_empty()) {
1205+
auto Iter = B->succ_begin();
1206+
BBSetOut = EpilogueARCBlockStates[*Iter]->BBSetIn;
1207+
Iter = std::next(Iter);
1208+
for (auto E = B->succ_end(); Iter != E; ++Iter) {
1209+
BBSetOut &= EpilogueARCBlockStates[*Iter]->BBSetIn;
1210+
}
1211+
} else if (isExitBlock(B)) {
1212+
// We set the BBSetOut for exit blocks.
1213+
BBSetOut = true;
1214+
}
1215+
1216+
// If an epilogue ARC instruction or blocking operating has been identified
1217+
// then there is no point visiting every instruction in this block.
1218+
if (BBSetOut) {
1219+
// Iterate over all instructions in the basic block and find the
1220+
// interested ARC instruction in the block.
1221+
for (auto I = B->rbegin(), E = B->rend(); I != E; ++I) {
1222+
// This is a transition from 1 to 0 due to an interested instruction.
1223+
if (isInterestedInstruction(&*I)) {
1224+
BBSetOut = false;
1225+
break;
1226+
}
1227+
// This is a transition from 1 to 0 due to a blocking instruction.
1228+
if (mayBlockEpilogueARC(&*I, RCFI->getRCIdentityRoot(Arg))) {
1229+
BBSetOut = false;
1230+
break;
1231+
}
1232+
}
1233+
}
1234+
1235+
// Update BBSetIn.
1236+
Changed |= (BS->BBSetIn != BBSetOut);
1237+
BS->BBSetIn = BBSetOut;
1238+
}
1239+
} while(Changed);
1240+
}
1241+
1242+
bool EpilogueARCContext::computeEpilogueARC() {
1243+
// At this point the data flow should have converged. Find the epilogue
1244+
// releases.
1245+
for (SILBasicBlock *B : PO->getPostOrder()) {
1246+
bool BBSetOut = false;
1247+
// Merge in all the successors.
1248+
if (!B->succ_empty()) {
1249+
// Make sure we've either found no ARC instructions in all the successors
1250+
// or we've found ARC instructions in all successors.
1251+
//
1252+
// In case we've found ARC instructions in some and not all successors,
1253+
// that means from this point to the end of the function, some paths will
1254+
// not have an epilogue ARC instruction, which means the data flow has
1255+
// failed.
1256+
auto Iter = B->succ_begin();
1257+
auto Base = EpilogueARCBlockStates[*Iter]->BBSetIn;
1258+
Iter = std::next(Iter);
1259+
for (auto E = B->succ_end(); Iter != E; ++Iter) {
1260+
if (EpilogueARCBlockStates[*Iter]->BBSetIn != Base)
1261+
return false;
1262+
}
1263+
BBSetOut = Base;
1264+
} else if (isExitBlock(B)) {
1265+
// We set the BBSetOut for exit blocks.
1266+
BBSetOut = true;
1267+
}
1268+
1269+
// If an epilogue ARC instruction or blocking operating has been identified
1270+
// then there is no point visiting every instruction in this block.
1271+
if (!BBSetOut) {
1272+
continue;
1273+
}
1274+
1275+
// An epilogue ARC instruction has not been identified, maybe its in this block.
1276+
//
1277+
// Iterate over all instructions in the basic block and find the interested ARC
1278+
// instruction in the block.
1279+
for (auto I = B->rbegin(), E = B->rend(); I != E; ++I) {
1280+
// This is a transition from 1 to 0 due to an interested instruction.
1281+
if (isInterestedInstruction(&*I)) {
1282+
EpilogueARCInsts.push_back(&*I);
1283+
break;
1284+
}
1285+
// This is a transition from 1 to 0 due to a blocking instruction.
1286+
if (mayBlockEpilogueARC(&*I, RCFI->getRCIdentityRoot(Arg))) {
1287+
break;
1288+
}
1289+
}
1290+
}
1291+
return true;
1292+
}
1293+
1294+
llvm::SmallVector<SILInstruction *, 1>
1295+
swift::computeEpilogueARCInstructions(EpilogueARCContext::EpilogueARCKind Kind,
1296+
SILValue Arg, SILFunction *F,
1297+
PostOrderFunctionInfo *PO, AliasAnalysis *AA,
1298+
RCIdentityFunctionInfo *RCFI) {
1299+
EpilogueARCContext CM(Kind, Arg, F, PO, AA, RCFI);
1300+
// Initialize and run the data flow. Clear the epilogue arc instructions if the
1301+
// data flow is aborted in middle.
1302+
if (!CM.run()) {
1303+
CM.resetEpilogueARCInsts();
1304+
}
1305+
return CM.getEpilogueARCInsts();
1306+
}

lib/SILOptimizer/UtilityPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set(UTILITYPASSES_SOURCES
66
UtilityPasses/CFGPrinter.cpp
77
UtilityPasses/ComputeDominanceInfo.cpp
88
UtilityPasses/ComputeLoopInfo.cpp
9+
UtilityPasses/EpilogueARCMatcherDumper.cpp
910
UtilityPasses/EpilogueRetainReleaseMatcherDumper.cpp
1011
UtilityPasses/EscapeAnalysisDumper.cpp
1112
UtilityPasses/FunctionOrderPrinter.cpp

0 commit comments

Comments
 (0)