Skip to content

Commit c44006a

Browse files
committed
Merge pull request swiftlang#1824 from trentxintong/RLE
Make sure non-epilogue releases do not kill redundant loads
2 parents 0a562b7 + 524ed34 commit c44006a

File tree

5 files changed

+89
-19
lines changed

5 files changed

+89
-19
lines changed

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SWIFT_SILOPTIMIZER_UTILS_LOCAL_H
1515

1616
#include "swift/Basic/ArrayRefView.h"
17+
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
1718
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
1819
#include "swift/SIL/SILInstruction.h"
1920
#include "swift/SIL/SILBuilder.h"
@@ -69,9 +70,10 @@ recursivelyDeleteTriviallyDeadInstructions(
6970
/// This routine only examines the state of the instruction at hand.
7071
bool isInstructionTriviallyDead(SILInstruction *I);
7172

72-
/// \brief Return true if this is a release instruction and the released value
73-
/// is a part of a guaranteed parameter, false otherwise.
74-
bool isGuaranteedParamRelease(SILInstruction *I);
73+
/// \brief Return true if this is a release instruction thats not going to
74+
/// free the object.
75+
bool isIntermediateRelease(SILInstruction *I,
76+
ConsumedArgToEpilogueReleaseMatcher &ERM);
7577

7678
/// \brief Recursively erase all of the uses of the instruction (but not the
7779
/// instruction itself) and delete instructions that will become trivially

lib/SILOptimizer/Transforms/DeadStoreElimination.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ class DSEContext {
322322
/// Type Expansion Analysis.
323323
TypeExpansionAnalysis *TE;
324324

325+
/// The epilogue release matcher we are using.
326+
ConsumedArgToEpilogueReleaseMatcher& ERM;
327+
325328
/// Allocator.
326329
llvm::BumpPtrAllocator BPA;
327330

@@ -428,8 +431,9 @@ class DSEContext {
428431
public:
429432
/// Constructor.
430433
DSEContext(SILFunction *F, SILModule *M, SILPassManager *PM,
431-
AliasAnalysis *AA, EscapeAnalysis *EA, TypeExpansionAnalysis *TE)
432-
: Mod(M), F(F), PM(PM), AA(AA), EA(EA), TE(TE) {}
434+
AliasAnalysis *AA, EscapeAnalysis *EA, TypeExpansionAnalysis *TE,
435+
ConsumedArgToEpilogueReleaseMatcher &ERM)
436+
: Mod(M), F(F), PM(PM), AA(AA), EA(EA), TE(TE), ERM(ERM) {}
433437

434438
/// Entry point for dead store elimination.
435439
bool run();
@@ -446,6 +450,8 @@ class DSEContext {
446450
/// Returns the location vault of the current function.
447451
std::vector<LSLocation> &getLocationVault() { return LocationVault; }
448452

453+
ConsumedArgToEpilogueReleaseMatcher &getERM() const { return ERM; };
454+
449455
/// Use a set of ad hoc rules to tell whether we should run a pessimistic
450456
/// one iteration data flow on the function.
451457
ProcessKind getProcessFunctionKind(unsigned StoreCount);
@@ -1049,7 +1055,7 @@ void DSEContext::processUnknownReadInstForDSE(SILInstruction *I) {
10491055
void DSEContext::processUnknownReadInst(SILInstruction *I, DSEKind Kind) {
10501056
// If this is a release on a guaranteed parameter, it can not call deinit,
10511057
// which might read or write memory.
1052-
if (isGuaranteedParamRelease(I))
1058+
if (isIntermediateRelease(I, ERM))
10531059
return;
10541060

10551061
// Are we building genset and killset.
@@ -1231,13 +1237,17 @@ class DeadStoreElimination : public SILFunctionTransform {
12311237

12321238
/// The entry point to the transformation.
12331239
void run() override {
1240+
SILFunction *F = getFunction();
1241+
DEBUG(llvm::dbgs() << "*** DSE on function: " << F->getName() << " ***\n");
1242+
12341243
auto *AA = PM->getAnalysis<AliasAnalysis>();
12351244
auto *EA = PM->getAnalysis<EscapeAnalysis>();
12361245
auto *TE = PM->getAnalysis<TypeExpansionAnalysis>();
1237-
SILFunction *F = getFunction();
1238-
DEBUG(llvm::dbgs() << "*** DSE on function: " << F->getName() << " ***\n");
1246+
auto *RCFI = PM->getAnalysis<RCIdentityAnalysis>()->get(F);
1247+
1248+
ConsumedArgToEpilogueReleaseMatcher ERM(RCFI, F);
12391249

1240-
DSEContext DSE(F, &F->getModule(), PM, AA, EA, TE);
1250+
DSEContext DSE(F, &F->getModule(), PM, AA, EA, TE, ERM);
12411251
if (DSE.run()) {
12421252
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
12431253
}

lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
#include "swift/SIL/SILArgument.h"
7878
#include "swift/SIL/SILBuilder.h"
7979
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
80+
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
8081
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
8182
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
8283
#include "swift/SILOptimizer/Analysis/ValueTracking.h"
@@ -439,6 +440,9 @@ class RLEContext {
439440
/// order of the given function.
440441
PostOrderFunctionInfo *PO;
441442

443+
/// The epilogue release matcher we are using.
444+
ConsumedArgToEpilogueReleaseMatcher& ERM;
445+
442446
/// Keeps all the locations for the current function. The BitVector in each
443447
/// BlockState is then laid on top of it to keep track of which LSLocation
444448
/// has a downward available value.
@@ -475,7 +479,8 @@ class RLEContext {
475479

476480
public:
477481
RLEContext(SILFunction *F, SILPassManager *PM, AliasAnalysis *AA,
478-
TypeExpansionAnalysis *TE, PostOrderFunctionInfo *PO);
482+
TypeExpansionAnalysis *TE, PostOrderFunctionInfo *PO,
483+
ConsumedArgToEpilogueReleaseMatcher &ERM);
479484

480485
RLEContext(const RLEContext &) = delete;
481486
RLEContext(RLEContext &&) = default;
@@ -484,6 +489,8 @@ class RLEContext {
484489
/// Entry point to redundant load elimination.
485490
bool run();
486491

492+
ConsumedArgToEpilogueReleaseMatcher &getERM() const { return ERM; };
493+
487494
/// Use a set of ad hoc rules to tell whether we should run a pessimistic
488495
/// one iteration data flow on the function.
489496
ProcessKind getProcessFunctionKind(unsigned LoadCount, unsigned StoreCount);
@@ -961,7 +968,7 @@ void BlockState::processUnknownWriteInst(RLEContext &Ctx, SILInstruction *I,
961968
RLEKind Kind) {
962969
// If this is a release on a guaranteed parameter, it can not call deinit,
963970
// which might read or write memory.
964-
if (isGuaranteedParamRelease(I))
971+
if (isIntermediateRelease(I, Ctx.getERM()))
965972
return;
966973

967974
// Are we computing the genset and killset ?
@@ -1118,8 +1125,9 @@ getProcessFunctionKind(unsigned LoadCount, unsigned StoreCount) {
11181125
//===----------------------------------------------------------------------===//
11191126

11201127
RLEContext::RLEContext(SILFunction *F, SILPassManager *PM, AliasAnalysis *AA,
1121-
TypeExpansionAnalysis *TE, PostOrderFunctionInfo *PO)
1122-
: Fn(F), PM(PM), AA(AA), TE(TE), PO(PO) {
1128+
TypeExpansionAnalysis *TE, PostOrderFunctionInfo *PO,
1129+
ConsumedArgToEpilogueReleaseMatcher &ERM)
1130+
: Fn(F), PM(PM), AA(AA), TE(TE), PO(PO), ERM(ERM) {
11231131
}
11241132

11251133
LSLocation &RLEContext::getLocation(const unsigned index) {
@@ -1522,8 +1530,11 @@ class RedundantLoadElimination : public SILFunctionTransform {
15221530
auto *AA = PM->getAnalysis<AliasAnalysis>();
15231531
auto *TE = PM->getAnalysis<TypeExpansionAnalysis>();
15241532
auto *PO = PM->getAnalysis<PostOrderAnalysis>()->get(F);
1533+
auto *RCFI = PM->getAnalysis<RCIdentityAnalysis>()->get(F);
1534+
1535+
ConsumedArgToEpilogueReleaseMatcher ERM(RCFI, F);
15251536

1526-
RLEContext RLE(F, PM, AA, TE, PO);
1537+
RLEContext RLE(F, PM, AA, TE, PO, ERM);
15271538
if (RLE.run()) {
15281539
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
15291540
}

lib/SILOptimizer/Utils/Local.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,31 @@ swift::isInstructionTriviallyDead(SILInstruction *I) {
7777

7878
/// \brief Return true if this is a release instruction and the released value
7979
/// is a part of a guaranteed parameter.
80-
bool swift::isGuaranteedParamRelease(SILInstruction *I) {
81-
if (!isa<StrongReleaseInst>(I) || !isa<ReleaseValueInst>(I))
80+
bool swift::isIntermediateRelease(SILInstruction *I,
81+
ConsumedArgToEpilogueReleaseMatcher &ERM) {
82+
// Check whether this is a release instruction.
83+
if (!isa<StrongReleaseInst>(I) && !isa<ReleaseValueInst>(I))
8284
return false;
85+
8386
// OK. we have a release instruction.
8487
// Check whether this is a release on part of a guaranteed function argument.
8588
SILValue Op = stripValueProjections(I->getOperand(0));
8689
SILArgument *Arg = dyn_cast<SILArgument>(Op);
87-
if (!Arg || !Arg->isFunctionArg() ||
88-
!Arg->hasConvention(SILArgumentConvention::Direct_Guaranteed))
90+
if (!Arg || !Arg->isFunctionArg())
8991
return false;
90-
return true;
92+
93+
// This is a release on a guaranteed parameter. Its not the final release.
94+
if (Arg->hasConvention(SILArgumentConvention::Direct_Guaranteed))
95+
return true;
96+
97+
// This is a release on a owned parameter and its not the epilogue relase.
98+
// Its not the final release.
99+
SILInstruction *Rel = ERM.getSingleReleaseForArgument(Arg);
100+
if (Rel && Rel != I)
101+
return true;
102+
103+
// Failed to prove anything.
104+
return false;
91105
}
92106

93107
namespace {

test/SILOptimizer/redundant_load_elim.sil

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,3 +1095,36 @@ bb2:
10951095
%9999 = tuple()
10961096
return %9999 : $()
10971097
}
1098+
1099+
// CHECK-LABEL: sil hidden @redundant_load_over_intermediate_release_with_epilogue_release : $@convention(thin) (@owned AB) -> () {
1100+
// CHECK: [[AD:%.*]] = ref_element_addr
1101+
// CHECK: [[AD2:%.*]] = load [[AD]]
1102+
// CHECK: release_value
1103+
// CHECK-NOT: [[AD3:%.*]] = load [[AD]]
1104+
// CHECK: return
1105+
sil hidden @redundant_load_over_intermediate_release_with_epilogue_release : $@convention(thin) (@owned AB) -> () {
1106+
bb0(%0 : $AB):
1107+
%1 = ref_element_addr %0 : $AB, #AB.value
1108+
%2 = load %1 : $*Int
1109+
release_value %0 : $AB
1110+
%3 = load %1 : $*Int
1111+
release_value %0 : $AB
1112+
%4 = tuple ()
1113+
return %4 : $()
1114+
}
1115+
1116+
// CHECK-LABEL: sil hidden @redundant_load_over_intermediate_release_without_epilogue_release : $@convention(thin) (@owned AB) -> () {
1117+
// CHECK: [[AD:%.*]] = ref_element_addr
1118+
// CHECK: [[AD2:%.*]] = load [[AD]]
1119+
// CHECK: [[AD3:%.*]] = load [[AD]]
1120+
// CHECK: return
1121+
sil hidden @redundant_load_over_intermediate_release_without_epilogue_release : $@convention(thin) (@owned AB) -> () {
1122+
bb0(%0 : $AB):
1123+
%1 = ref_element_addr %0 : $AB, #AB.value
1124+
%2 = load %1 : $*Int
1125+
release_value %0 : $AB
1126+
%3 = load %1 : $*Int
1127+
retain_value %0 : $AB
1128+
%4 = tuple ()
1129+
return %4 : $()
1130+
}

0 commit comments

Comments
 (0)