Skip to content

Commit 52ce100

Browse files
committed
[Mem2Reg] Canonicalize stored values.
Stored values may now be canonicalizable, now that they have one less consuming use.
1 parent 7fb9bca commit 52ce100

File tree

3 files changed

+96
-12
lines changed

3 files changed

+96
-12
lines changed

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,15 @@
3131
#include "swift/SIL/SILFunction.h"
3232
#include "swift/SIL/SILInstruction.h"
3333
#include "swift/SIL/SILModule.h"
34+
#include "swift/SIL/StackList.h"
3435
#include "swift/SIL/TypeLowering.h"
36+
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
3537
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
3638
#include "swift/SILOptimizer/PassManager/Passes.h"
3739
#include "swift/SILOptimizer/PassManager/Transforms.h"
3840
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
41+
#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h"
42+
#include "swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h"
3943
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
4044
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
4145
#include "swift/SILOptimizer/Utils/ScopeOptUtils.h"
@@ -56,6 +60,10 @@ STATISTIC(NumAllocStackFound, "Number of AllocStack found");
5660
STATISTIC(NumAllocStackCaptured, "Number of AllocStack captured");
5761
STATISTIC(NumInstRemoved, "Number of Instructions removed");
5862

63+
llvm::cl::opt<bool> Mem2RegDisableLifetimeCanonicalization(
64+
"sil-mem2reg-disable-lifetime-canonicalization", llvm::cl::init(false),
65+
llvm::cl::desc("Don't canonicalize any lifetimes during Mem2Reg."));
66+
5967
static bool lexicalLifetimeEnsured(AllocStackInst *asi);
6068
static bool isGuaranteedLexicalValue(SILValue src);
6169

@@ -1881,6 +1889,10 @@ class MemoryToRegisters {
18811889
/// Dominators.
18821890
DominanceInfo *domInfo;
18831891

1892+
NonLocalAccessBlockAnalysis *accessBlockAnalysis;
1893+
1894+
BasicCalleeAnalysis *calleeAnalysis;
1895+
18841896
/// The builder context used when creating new instructions during register
18851897
/// promotion.
18861898
SILBuilderContext ctx;
@@ -1929,10 +1941,23 @@ class MemoryToRegisters {
19291941
bool promoteAllocation(AllocStackInst *asi,
19301942
BasicBlockSetVector &livePhiBlocks);
19311943

1944+
/// Record all the values stored and store_borrow'd into the address so that
1945+
/// they can be canonicalized if promotion succeeds.
1946+
void collectStoredValues(AllocStackInst *asi, StackList<SILValue> &owned,
1947+
StackList<SILValue> &guaranteed);
1948+
1949+
/// Canonicalize the lifetimes of the specified owned and guaranteed values.
1950+
void canonicalizeValueLifetimes(StackList<SILValue> &owned,
1951+
StackList<SILValue> &guaranteed);
1952+
19321953
public:
19331954
/// C'tor
1934-
MemoryToRegisters(SILFunction &inputFunc, DominanceInfo *inputDomInfo)
1935-
: f(inputFunc), domInfo(inputDomInfo), ctx(inputFunc.getModule()) {}
1955+
MemoryToRegisters(SILFunction &inputFunc, DominanceInfo *inputDomInfo,
1956+
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
1957+
BasicCalleeAnalysis *calleeAnalysis)
1958+
: f(inputFunc), domInfo(inputDomInfo),
1959+
accessBlockAnalysis(accessBlockAnalysis),
1960+
calleeAnalysis(calleeAnalysis), ctx(inputFunc.getModule()) {}
19361961

19371962
/// Promote memory to registers. Return True on change.
19381963
bool run();
@@ -2124,6 +2149,50 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) {
21242149
}
21252150
}
21262151

2152+
void MemoryToRegisters::collectStoredValues(AllocStackInst *asi,
2153+
StackList<SILValue> &owned,
2154+
StackList<SILValue> &guaranteed) {
2155+
for (auto *use : asi->getUses()) {
2156+
auto *user = use->getUser();
2157+
if (auto *si = dyn_cast<StoreInst>(user)) {
2158+
owned.push_back(si->getSrc());
2159+
} else if (auto *sbi = dyn_cast<StoreBorrowInst>(user)) {
2160+
guaranteed.push_back(sbi->getSrc());
2161+
}
2162+
}
2163+
}
2164+
2165+
void MemoryToRegisters::canonicalizeValueLifetimes(
2166+
StackList<SILValue> &owned, StackList<SILValue> &guaranteed) {
2167+
if (Mem2RegDisableLifetimeCanonicalization)
2168+
return;
2169+
2170+
CanonicalizeOSSALifetime canonicalizer(
2171+
/*pruneDebug=*/true, /*maximizeLifetime=*/!f.shouldOptimize(), &f,
2172+
accessBlockAnalysis, domInfo, calleeAnalysis, deleter);
2173+
for (auto value : owned) {
2174+
auto root = CanonicalizeOSSALifetime::getCanonicalCopiedDef(value);
2175+
if (auto *copy = dyn_cast<CopyValueInst>(root)) {
2176+
if (SILValue borrowDef = CanonicalizeBorrowScope::getCanonicalBorrowedDef(
2177+
copy->getOperand())) {
2178+
guaranteed.push_back(copy);
2179+
continue;
2180+
}
2181+
}
2182+
canonicalizer.canonicalizeValueLifetime(root);
2183+
}
2184+
CanonicalizeBorrowScope borrowCanonicalizer(&f, deleter);
2185+
for (auto value : guaranteed) {
2186+
auto borrowee = CanonicalizeBorrowScope::getCanonicalBorrowedDef(value);
2187+
if (!borrowee)
2188+
continue;
2189+
BorrowedValue borrow(borrowee);
2190+
if (borrow.kind != BorrowedValueKind::SILFunctionArgument)
2191+
continue;
2192+
borrowCanonicalizer.canonicalizeBorrowScope(borrow);
2193+
}
2194+
}
2195+
21272196
/// Attempt to promote the specified stack allocation, returning true if so
21282197
/// or false if not. On success, this returns true and usually drops all of the
21292198
/// uses of the AllocStackInst, but never deletes the ASI itself. Callers
@@ -2192,6 +2261,13 @@ bool MemoryToRegisters::run() {
21922261
if (!asi)
21932262
continue;
21942263

2264+
// Record stored values because promoting a store eliminates a consuming
2265+
// use of the stored value. If promotion succeeds, these values'
2266+
// lifetimes are canonicalized, eliminating unnecessary copies.
2267+
StackList<SILValue> ownedValues(&f);
2268+
StackList<SILValue> guaranteedValues(&f);
2269+
collectStoredValues(asi, ownedValues, guaranteedValues);
2270+
21952271
// The blocks which still have new phis after fixBranchesAndUses runs.
21962272
// These are not necessarily the same as phiBlocks because
21972273
// fixBranchesAndUses removes superfluous proactive phis.
@@ -2202,6 +2278,7 @@ bool MemoryToRegisters::run() {
22022278
}
22032279
instructionsToDelete.clear();
22042280
++NumInstRemoved;
2281+
canonicalizeValueLifetimes(ownedValues, guaranteedValues);
22052282
madeChange = true;
22062283
}
22072284
}
@@ -2222,9 +2299,13 @@ class SILMem2Reg : public SILFunctionTransform {
22222299
LLVM_DEBUG(llvm::dbgs()
22232300
<< "** Mem2Reg on function: " << f->getName() << " **\n");
22242301

2225-
auto *da = PM->getAnalysis<DominanceAnalysis>();
2302+
auto *da = getAnalysis<DominanceAnalysis>();
2303+
auto *calleeAnalysis = getAnalysis<BasicCalleeAnalysis>();
2304+
auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
22262305

2227-
bool madeChange = MemoryToRegisters(*f, da->get(f)).run();
2306+
bool madeChange =
2307+
MemoryToRegisters(*f, da->get(f), accessBlockAnalysis, calleeAnalysis)
2308+
.run();
22282309
if (madeChange)
22292310
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
22302311
}

test/SILOptimizer/mem2reg_ossa.sil

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,8 @@ bb3:
336336
// CHECK: bb4
337337
bb4:
338338
// CHECK-NOT: destroy_addr
339-
// CHECK: destroy_value [[ARG]]
339+
// : destroy_value [[ARG]]
340+
// The above destroy is eliminated by owned value canonicalization.
340341
destroy_addr %1 : $*@callee_owned () -> Int
341342
// CHECK-NOT: dealloc_stack
342343
dealloc_stack %1 : $*@callee_owned () -> Int
@@ -437,13 +438,13 @@ bb0(%0 : @owned $SmallCodesizeStruct):
437438
// CHECK-LABEL: sil [ossa] @small_struct_multi_test :
438439
// CHECK-NOT: alloc_stack
439440
// CHECK: [[COPY:%.*]] = copy_value %0
440-
// CHECK-NEXT: destructure_struct %0
441+
// CHECK-NEXT: destructure_struct %2
441442
// CHECK-NEXT: destroy_value
442443
// CHECK-NEXT: destroy_value
443-
// CHECK-NEXT: begin_borrow [[COPY]]
444+
// CHECK-NEXT: begin_borrow %0
444445
// CHECK-NEXT: debug_value
445446
// CHECK-NEXT: end_borrow
446-
// CHECK-NEXT: destroy_value [[COPY]]
447+
// CHECK-NEXT: destroy_value %0
447448
// CHECK: bb2:
448449
// CHECK-NEXT: destructure_struct %0
449450
// CHECK-NEXT: destroy_value

test/SILOptimizer/mem2reg_ossa_nontrivial.sil

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,10 @@ bb0(%0 : @owned $Klass, %1 : @owned $Klass):
145145

146146
// CHECK-LABEL: sil [ossa] @multiple_store_vals3 :
147147
// CHECK-NOT: alloc_stack
148-
// CHECK: [[COPY0:%.*]] = copy_value %0 : $Klass
148+
// The COPY0 copy/destroy is eliminated by owned value canonicalization.
149+
// : [[COPY0:%.*]] = copy_value %0 : $Klass
149150
// CHECK: [[COPY1:%.*]] = copy_value %1 : $Klass
150-
// CHECK: destroy_value [[COPY0]] : $Klass
151+
// : destroy_value [[COPY0]] : $Klass
151152
// CHECK: [[FUNC:%.*]] = function_ref @blackhole_spl :
152153
// CHECK: apply [[FUNC]]([[COPY1]]) : $@convention(thin) (@guaranteed Klass) -> ()
153154
// CHECK: destroy_value [[COPY1]] : $Klass
@@ -353,8 +354,9 @@ bb0(%0 : @owned $Klass, %1 : @owned $Klass):
353354

354355
// CHECK-LABEL: sil [ossa] @basic_block_with_loads_copy_and_take :
355356
// CHECK-NOT: alloc_stack
356-
// CHECK: [[COPY:%.*]] = copy_value %0 : $Klass
357-
// CHECK: destroy_value [[COPY]] : $Klass
357+
// The COPY/destroy pair is eliminated by owned value canonicalization.
358+
// : [[COPY:%.*]] = copy_value %0 : $Klass
359+
// : destroy_value [[COPY]] : $Klass
358360
// CHECK: destroy_value %0 : $Klass
359361
// CHECK-LABEL: } // end sil function 'basic_block_with_loads_copy_and_take'
360362
sil [ossa] @basic_block_with_loads_copy_and_take : $@convention(thin) (@owned Klass) -> () {

0 commit comments

Comments
 (0)