Skip to content

Commit 1bd74d1

Browse files
committed
LICM: (limited) support for OSSA
The first step: allow hoisting instructions which only have trivial operands and results.
1 parent 074a99c commit 1bd74d1

File tree

2 files changed

+114
-13
lines changed

2 files changed

+114
-13
lines changed

lib/SILOptimizer/LoopTransforms/LICM.cpp

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,21 @@ static bool analyzeBeginAccess(BeginAccessInst *BI,
849849
return true;
850850
}
851851

852+
static bool hasOwnershipOperandsOrResults(SILInstruction *inst) {
853+
if (!inst->getFunction()->hasOwnership())
854+
return false;
855+
856+
for (SILValue result : inst->getResults()) {
857+
if (result->getOwnershipKind() != OwnershipKind::None)
858+
return true;
859+
}
860+
for (Operand &op : inst->getAllOperands()) {
861+
if (op.get()->getOwnershipKind() != OwnershipKind::None)
862+
return true;
863+
}
864+
return false;
865+
}
866+
852867
// Analyzes current loop for hosting/sinking potential:
853868
// Computes set of instructions we may be able to move out of the loop
854869
// Important Note:
@@ -887,6 +902,10 @@ void LoopTreeOptimization::analyzeCurrentLoop(
887902
for (auto *BB : Loop->getBlocks()) {
888903
SmallVector<SILInstruction *, 8> sideEffectsInBlock;
889904
for (auto &Inst : *BB) {
905+
if (hasOwnershipOperandsOrResults(&Inst)) {
906+
checkSideEffects(Inst, sideEffects, sideEffectsInBlock);
907+
continue;
908+
}
890909
switch (Inst.getKind()) {
891910
case SILInstructionKind::FixLifetimeInst: {
892911
auto *FL = cast<FixLifetimeInst>(&Inst);
@@ -1084,11 +1103,14 @@ SingleValueInstruction *LoopTreeOptimization::splitLoad(
10841103
SILValue splitAddress, ArrayRef<AccessPath::Index> remainingPath,
10851104
SILBuilder &builder, SmallVectorImpl<LoadInst *> &Loads, unsigned ldstIdx) {
10861105
auto loc = LoadsAndStores[ldstIdx]->getLoc();
1106+
LoadOwnershipQualifier ownership = builder.getFunction().hasOwnership() ?
1107+
LoadOwnershipQualifier::Trivial :
1108+
LoadOwnershipQualifier::Unqualified;
1109+
10871110
// Recurse until we have a load that matches accessPath.
10881111
if (remainingPath.empty()) {
10891112
// Create a load that matches the stored access path.
1090-
LoadInst *load = builder.createLoad(loc, splitAddress,
1091-
LoadOwnershipQualifier::Unqualified);
1113+
LoadInst *load = builder.createLoad(loc, splitAddress, ownership);
10921114
Loads.push_back(load);
10931115
// Replace the outer load in the list of loads and stores to hoist and
10941116
// sink. LoadsAndStores must remain in instruction order.
@@ -1112,8 +1134,7 @@ SingleValueInstruction *LoopTreeOptimization::splitLoad(
11121134
elementVal = splitLoad(projection, remainingPath.drop_back(), builder,
11131135
Loads, ldstIdx);
11141136
} else {
1115-
elementVal = builder.createLoad(loc, projection,
1116-
LoadOwnershipQualifier::Unqualified);
1137+
elementVal = builder.createLoad(loc, projection, ownership);
11171138
recordDisjointLoad(cast<LoadInst>(elementVal));
11181139
}
11191140
elements.push_back(elementVal);
@@ -1136,8 +1157,7 @@ SingleValueInstruction *LoopTreeOptimization::splitLoad(
11361157
fieldVal = splitLoad(projection, remainingPath.drop_back(), builder,
11371158
Loads, ldstIdx);
11381159
else {
1139-
fieldVal = builder.createLoad(loc, projection,
1140-
LoadOwnershipQualifier::Unqualified);
1160+
fieldVal = builder.createLoad(loc, projection, ownership);
11411161
recordDisjointLoad(cast<LoadInst>(fieldVal));
11421162
}
11431163
elements.push_back(fieldVal);
@@ -1431,9 +1451,13 @@ hoistLoadsAndStores(AccessPath accessPath, SILLoop *loop) {
14311451
if (!initialAddr)
14321452
return;
14331453

1454+
LoadOwnershipQualifier ownership = B.getFunction().hasOwnership() ?
1455+
LoadOwnershipQualifier::Trivial :
1456+
LoadOwnershipQualifier::Unqualified;
1457+
14341458
LoadInst *initialLoad =
14351459
B.createLoad(RegularLocation::getAutoGeneratedLocation(), initialAddr,
1436-
LoadOwnershipQualifier::Unqualified);
1460+
ownership);
14371461
LLVM_DEBUG(llvm::dbgs() << "Creating preload " << *initialLoad);
14381462
ssaUpdater.addAvailableValue(preheader, initialLoad);
14391463

@@ -1480,9 +1504,12 @@ hoistLoadsAndStores(AccessPath accessPath, SILLoop *loop) {
14801504
assert(succ->getSinglePredecessorBlock()
14811505
&& "should have split critical edges");
14821506
SILBuilder B(succ->begin());
1507+
StoreOwnershipQualifier ownership = B.getFunction().hasOwnership() ?
1508+
StoreOwnershipQualifier::Trivial :
1509+
StoreOwnershipQualifier::Unqualified;
14831510
auto *SI = B.createStore(
14841511
loc.value(), ssaUpdater.getValueInMiddleOfBlock(succ), initialAddr,
1485-
StoreOwnershipQualifier::Unqualified);
1512+
ownership);
14861513
(void)SI;
14871514
LLVM_DEBUG(llvm::dbgs() << "Creating loop-exit store " << *SI);
14881515
}
@@ -1529,10 +1556,6 @@ class LICM : public SILFunctionTransform {
15291556
void run() override {
15301557
SILFunction *F = getFunction();
15311558

1532-
// If our function has ownership, skip it.
1533-
if (F->hasOwnership())
1534-
return;
1535-
15361559
SILLoopAnalysis *LA = PM->getAnalysis<SILLoopAnalysis>();
15371560
SILLoopInfo *LoopInfo = LA->get(F);
15381561

test/SILOptimizer/licm.sil

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class Storage {
1414
init()
1515
}
1616

17+
struct NonCopyable : ~Copyable {
18+
var x: Int
19+
}
20+
1721
// globalArray
1822
sil_global @globalArray : $Storage
1923

@@ -218,7 +222,7 @@ bb2:
218222
return %52 : $()
219223
}
220224

221-
public protocol P : class {
225+
public protocol P : AnyObject {
222226
func foo() -> Int32
223227
func boo() -> Int32
224228
}
@@ -1527,4 +1531,78 @@ bb2:
15271531
return %r1 : $()
15281532
}
15291533

1534+
// CHECK-LABEL: sil [ossa] @dont_hoist_non_trivial_load :
1535+
// CHECK: bb1:
1536+
// CHECK-NEXT: load [copy]
1537+
// CHECK: } // end sil function 'dont_hoist_non_trivial_load'
1538+
sil [ossa] @dont_hoist_non_trivial_load : $@convention(thin) (@inout Builtin.NativeObject) -> () {
1539+
bb0(%0 : $*Builtin.NativeObject):
1540+
br bb1
1541+
bb1:
1542+
%2 = load [copy] %0
1543+
fix_lifetime %2
1544+
destroy_value %2
1545+
cond_br undef, bb2, bb3
1546+
bb2:
1547+
br bb1
1548+
bb3:
1549+
%r = tuple ()
1550+
return %r : $()
1551+
}
1552+
1553+
// CHECK-LABEL: sil [ossa] @hoist_trivial_load :
1554+
// CHECK: load [trivial]
1555+
// CHECK-NEXT: br bb1
1556+
// CHECK: } // end sil function 'hoist_trivial_load'
1557+
sil [ossa] @hoist_trivial_load : $@convention(thin) (@inout Int) -> () {
1558+
bb0(%0 : $*Int):
1559+
br bb1
1560+
bb1:
1561+
%2 = load [trivial] %0
1562+
fix_lifetime %2
1563+
cond_br undef, bb2, bb3
1564+
bb2:
1565+
br bb1
1566+
bb3:
1567+
%r = tuple ()
1568+
return %r : $()
1569+
}
1570+
1571+
// CHECK-LABEL: sil [ossa] @dont_hoist_load_borrow :
1572+
// CHECK: bb1:
1573+
// CHECK-NEXT: load_borrow
1574+
// CHECK: } // end sil function 'dont_hoist_load_borrow'
1575+
sil [ossa] @dont_hoist_load_borrow : $@convention(thin) (@inout Builtin.NativeObject) -> () {
1576+
bb0(%0 : $*Builtin.NativeObject):
1577+
br bb1
1578+
bb1:
1579+
%2 = load_borrow %0
1580+
fix_lifetime %2
1581+
end_borrow %2
1582+
cond_br undef, bb2, bb3
1583+
bb2:
1584+
br bb1
1585+
bb3:
1586+
%r = tuple ()
1587+
return %r : $()
1588+
}
1589+
1590+
// CHECK-LABEL: sil [ossa] @dont_hoist_struct :
1591+
// CHECK: bb1:
1592+
// CHECK-NEXT: struct $NonCopyable
1593+
// CHECK: } // end sil function 'dont_hoist_struct'
1594+
sil [ossa] @dont_hoist_struct : $@convention(thin) (Int) -> () {
1595+
bb0(%0 : $Int):
1596+
br bb1
1597+
bb1:
1598+
%2 = struct $NonCopyable (%0)
1599+
fix_lifetime %2
1600+
destroy_value %2
1601+
cond_br undef, bb2, bb3
1602+
bb2:
1603+
br bb1
1604+
bb3:
1605+
%r = tuple ()
1606+
return %r : $()
1607+
}
15301608

0 commit comments

Comments
 (0)