Skip to content

Commit 7b16f7f

Browse files
committed
SILOptimizer: fix a phase ordering problem, which prevented array optimizations to work in some cases
Introduce an "early redundant load elimination", which does not optimize loads from arrays. Later array optimizations, like ABCOpt, get confused if an array load in a loop is converted to a pattern with a phi argument. This problem was introduced with accessors. rdar://problem/44184763
1 parent 747c2f8 commit 7b16f7f

File tree

4 files changed

+80
-7
lines changed

4 files changed

+80
-7
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ PASS(ARCSequenceOpts, "arc-sequence-opts",
154154
"ARC Sequence Optimization")
155155
PASS(ARCLoopOpts, "arc-loop-opts",
156156
"ARC Loop Optimization")
157+
PASS(EarlyRedundantLoadElimination, "early-redundant-load-elim",
158+
"Early Redundant Load Elimination")
157159
PASS(RedundantLoadElimination, "redundant-load-elim",
158160
"Redundant Load Elimination")
159161
PASS(DeadStoreElimination, "dead-store-elim",

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,14 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) {
305305
P.addSimplifyCFG();
306306

307307
P.addCSE();
308-
P.addRedundantLoadElimination();
308+
if (OpLevel == OptimizationLevelKind::HighLevel) {
309+
// Early RLE does not touch loads from Arrays. This is important because
310+
// later array optimizations, like ABCOpt, get confused if an array load in
311+
// a loop is converted to a pattern with a phi argument.
312+
P.addEarlyRedundantLoadElimination();
313+
} else {
314+
P.addRedundantLoadElimination();
315+
}
309316

310317
P.addPerformanceConstantPropagation();
311318
P.addCSE();

lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -481,14 +481,17 @@ class RLEContext {
481481
/// walked, i.e. when the we generate the genset and killset.
482482
llvm::DenseSet<SILBasicBlock *> BBWithLoads;
483483

484+
/// If set, RLE ignores loads from that array type.
485+
NominalTypeDecl *ArrayType;
486+
484487
#ifndef NDEBUG
485488
SILPrintContext printCtx;
486489
#endif
487490

488491
public:
489492
RLEContext(SILFunction *F, SILPassManager *PM, AliasAnalysis *AA,
490493
TypeExpansionAnalysis *TE, PostOrderFunctionInfo *PO,
491-
EpilogueARCFunctionInfo *EAFI);
494+
EpilogueARCFunctionInfo *EAFI, bool disableArrayLoads);
492495

493496
RLEContext(const RLEContext &) = delete;
494497
RLEContext(RLEContext &&) = default;
@@ -555,6 +558,17 @@ class RLEContext {
555558
/// Transitively collect all the values that make up this location and
556559
/// create a SILArgument out of them.
557560
SILValue computePredecessorLocationValue(SILBasicBlock *BB, LSLocation &L);
561+
562+
/// Returns the LoadInst if \p Inst is a load inst we want to handle.
563+
LoadInst *isLoadInstToHandle(SILInstruction *Inst) {
564+
if (auto *LI = dyn_cast<LoadInst>(Inst)) {
565+
if (!ArrayType ||
566+
LI->getType().getNominalOrBoundGenericNominal() != ArrayType) {
567+
return LI;
568+
}
569+
}
570+
return nullptr;
571+
}
558572
};
559573

560574
} // end anonymous namespace
@@ -1054,7 +1068,7 @@ void BlockState::processInstructionWithKind(RLEContext &Ctx,
10541068

10551069
// This is a LoadInst. Let's see if we can find a previous loaded, stored
10561070
// value to use instead of this load.
1057-
if (auto *LI = dyn_cast<LoadInst>(Inst)) {
1071+
if (auto *LI = Ctx.isLoadInstToHandle(Inst)) {
10581072
processLoadInst(Ctx, LI, Kind);
10591073
return;
10601074
}
@@ -1174,8 +1188,10 @@ void BlockState::dump(RLEContext &Ctx) {
11741188

11751189
RLEContext::RLEContext(SILFunction *F, SILPassManager *PM, AliasAnalysis *AA,
11761190
TypeExpansionAnalysis *TE, PostOrderFunctionInfo *PO,
1177-
EpilogueARCFunctionInfo *EAFI)
1178-
: Fn(F), PM(PM), AA(AA), TE(TE), PO(PO), EAFI(EAFI)
1191+
EpilogueARCFunctionInfo *EAFI, bool disableArrayLoads)
1192+
: Fn(F), PM(PM), AA(AA), TE(TE), PO(PO), EAFI(EAFI),
1193+
ArrayType(disableArrayLoads ?
1194+
F->getModule().getASTContext().getArrayDecl() : nullptr)
11791195
#ifndef NDEBUG
11801196
,
11811197
printCtx(llvm::dbgs(), /*Verbose=*/false, /*Sorted=*/true)
@@ -1612,6 +1628,14 @@ namespace {
16121628

16131629
class RedundantLoadElimination : public SILFunctionTransform {
16141630

1631+
private:
1632+
bool disableArrayLoads;
1633+
1634+
public:
1635+
1636+
RedundantLoadElimination(bool disableArrayLoads)
1637+
: disableArrayLoads(disableArrayLoads) { }
1638+
16151639
/// The entry point to the transformation.
16161640
void run() override {
16171641
SILFunction *F = getFunction();
@@ -1623,7 +1647,7 @@ class RedundantLoadElimination : public SILFunctionTransform {
16231647
auto *PO = PM->getAnalysis<PostOrderAnalysis>()->get(F);
16241648
auto *EAFI = PM->getAnalysis<EpilogueARCAnalysis>()->get(F);
16251649

1626-
RLEContext RLE(F, PM, AA, TE, PO, EAFI);
1650+
RLEContext RLE(F, PM, AA, TE, PO, EAFI, disableArrayLoads);
16271651
if (RLE.run()) {
16281652
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
16291653
}
@@ -1632,6 +1656,10 @@ class RedundantLoadElimination : public SILFunctionTransform {
16321656

16331657
} // end anonymous namespace
16341658

1659+
SILTransform *swift::createEarlyRedundantLoadElimination() {
1660+
return new RedundantLoadElimination(/*disableArrayLoads=*/true);
1661+
}
1662+
16351663
SILTransform *swift::createRedundantLoadElimination() {
1636-
return new RedundantLoadElimination();
1664+
return new RedundantLoadElimination(/*disableArrayLoads=*/false);
16371665
}

test/SILOptimizer/early-rle.sil

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -early-redundant-load-elim | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
import Swift
7+
import SwiftShims
8+
9+
10+
// Check that earlyl redundant load elimination ignores arrays.
11+
12+
13+
// CHECK-LABEL: test_array
14+
// CHECK: %1 = load
15+
// CHECK: %2 = load
16+
// CHECK: %3 = tuple (%1 {{.*}}, %2 {{.*}})
17+
sil @test_array : $@convention(thin) (@inout Array<Int>) -> (Array<Int>, Array<Int>) {
18+
bb0(%0 : $*Array<Int>):
19+
%1 = load %0 : $*Array<Int>
20+
%2 = load %0 : $*Array<Int>
21+
%3 = tuple (%1: $Array<Int>, %2: $Array<Int>)
22+
return %3 : $(Array<Int>, Array<Int>)
23+
}
24+
25+
// CHECK-LABEL: test_non_array
26+
// CHECK: %1 = load
27+
// CHECK: %2 = tuple (%1 {{.*}}, %1 {{.*}})
28+
sil @test_non_array : $@convention(thin) (@inout Int) -> (Int, Int) {
29+
bb0(%0 : $*Int):
30+
%1 = load %0 : $*Int
31+
%2 = load %0 : $*Int
32+
%3 = tuple (%1: $Int, %2: $Int)
33+
return %3 : $(Int, Int)
34+
}
35+
36+

0 commit comments

Comments
 (0)