Skip to content

Commit ba46bc4

Browse files
committed
[lower-agg-instrs] Update for ossa.
For now I am trying out /not/ expanding when ownership is enabled. I still fixed the problems in it though. I also put in a force expand everything pass to make sure that in ossa we can still successfully go down this code path if we want to. The reason why I think this is the right thing to do is that the original reason why lower aggregate instrs was written was to help the low level arc optimizer (which only runs on non-ossa code). To the high level arc optimizer this is just noise and will keep the IR simpler. Another thing to note is that I updated the code so it should work in both ossa and non-ossa. In order to not perturb the code too much, I had to add a small optimization to TypeLowering where when ownership is disabled, we do not reform aggregates when copying. Instead, we just return the passed in value. This makes the pass the same when optimizing in both modes.
1 parent c6ebea4 commit ba46bc4

File tree

6 files changed

+624
-92
lines changed

6 files changed

+624
-92
lines changed

lib/SIL/IR/TypeLowering.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "swift/SIL/SILInstruction.h"
1413
#define DEBUG_TYPE "libsil"
14+
1515
#include "swift/AST/AnyFunctionRef.h"
1616
#include "swift/AST/ASTContext.h"
1717
#include "swift/AST/CanTypeVisitor.h"
18+
#include "swift/SIL/SILInstruction.h"
1819
#include "swift/AST/Decl.h"
1920
#include "swift/AST/DiagnosticEngine.h"
2021
#include "swift/AST/DiagnosticsSIL.h"
@@ -780,8 +781,14 @@ namespace {
780781
if (qual != LoadOwnershipQualifier::Copy)
781782
return loadValue;
782783

783-
// Otherwise, emit the copy value operation.
784-
return B.emitCopyValueOperation(loc, loadValue);
784+
// Otherwise, emit the copy value operation and return our original
785+
// value. This is a small non-ownership optimization to not destabilize
786+
// the optimizer pipeline.
787+
//
788+
// TODO: Once the pass pipeline is fixed, we should evaluate if we can do
789+
// this again.
790+
B.emitCopyValueOperation(loc, loadValue);
791+
return loadValue;
785792
}
786793

787794
SILValue emitLoweredLoad(SILBuilder &B, SILLocation loc, SILValue addr,
@@ -798,7 +805,15 @@ namespace {
798805
return loadValue;
799806

800807
// Otherwise, emit the copy value operation.
801-
return B.emitLoweredCopyValueOperation(loc, loadValue, expansionKind);
808+
B.emitLoweredCopyValueOperation(loc, loadValue, expansionKind);
809+
810+
// Otherwise, emit the copy value operation and return our original
811+
// value. This is a small non-ownership optimization to not destabilize
812+
// the optimizer pipeline.
813+
//
814+
// TODO: Once the pass pipeline is fixed, we should evaluate if we can do
815+
// this again.
816+
return loadValue;
802817
}
803818

804819
void emitLoweredStore(SILBuilder &B, SILLocation loc, SILValue value,
@@ -949,6 +964,13 @@ namespace {
949964
loweredChildValues.push_back(childValue);
950965
});
951966

967+
// Without ownership, return our original value. This is a small
968+
// non-ownership optimization to not destabilize the optimizer pipeline.
969+
//
970+
// TODO: Once the pass pipeline is fixed, we should evaluate if we can do
971+
// this again.
972+
if (!B.hasOwnership())
973+
return aggValue;
952974
return rebuildAggregate(B, loc, loweredChildValues);
953975
}
954976

lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp

Lines changed: 80 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,31 @@
2828
#include "swift/SILOptimizer/PassManager/Transforms.h"
2929
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
3030
#include "llvm/ADT/Statistic.h"
31+
#include "llvm/Support/CommandLine.h"
3132
#include "llvm/Support/Debug.h"
3233

3334
using namespace swift;
3435
using namespace swift::Lowering;
3536

3637
STATISTIC(NumExpand, "Number of instructions expanded");
3738

39+
static llvm::cl::opt<bool> EnableExpandAll("sil-lower-agg-instrs-expand-all",
40+
llvm::cl::init(false));
41+
42+
//===----------------------------------------------------------------------===//
43+
// Utility
44+
//===----------------------------------------------------------------------===//
45+
46+
/// We only expand if we are not in ownership and shouldExpand is true. The
47+
/// reason why is that this was originally done to help the low level ARC
48+
/// optimizer. To the high level ARC optimizer, this is just noise and
49+
/// unnecessary IR. At the same time for testing purposes, we want to provide a
50+
/// way even with ownership enabled to expand so we can check correctness.
51+
static bool shouldExpandShim(SILFunction *fn, SILType type) {
52+
return EnableExpandAll ||
53+
(!fn->hasOwnership() && shouldExpand(fn->getModule(), type));
54+
}
55+
3856
//===----------------------------------------------------------------------===//
3957
// Higher Level Operation Expansion
4058
//===----------------------------------------------------------------------===//
@@ -43,106 +61,110 @@ STATISTIC(NumExpand, "Number of instructions expanded");
4361
/// non-address only type. We do this here so we can process the resulting
4462
/// loads/stores.
4563
///
46-
/// This peephole implements the following optimizations:
64+
/// This peephole implements the following optimizations with the ossa version
65+
/// of the optimization first.
4766
///
4867
/// copy_addr %0 to %1 : $*T
4968
/// ->
69+
/// %new = load [copy] %0 : $*T
70+
/// store %new to [assign] %1 : $*T
71+
/// ->
5072
/// %new = load %0 : $*T // Load the new value from the source
51-
/// %old = load %1 : $*T // Load the old value from the destination
5273
/// strong_retain %new : $T // Retain the new value
74+
/// %old = load %1 : $*T // Load the old value from the destination
5375
/// strong_release %old : $T // Release the old
5476
/// store %new to %1 : $*T // Store the new value to the destination
5577
///
5678
/// copy_addr [take] %0 to %1 : $*T
5779
/// ->
80+
/// // load [take], not load [copy]!
81+
/// %new = load [take] %0 : $*T
82+
/// store %new to [assign] %1 : $*T
83+
/// ->
5884
/// %new = load %0 : $*T
59-
/// %old = load %1 : $*T
6085
/// // no retain of %new!
86+
/// %old = load %1 : $*T
6187
/// strong_release %old : $T
6288
/// store %new to %1 : $*T
6389
///
6490
/// copy_addr %0 to [initialization] %1 : $*T
6591
/// ->
92+
/// %new = load [copy] %0 : $*T
93+
/// store %new to [init] %1 : $*T
94+
/// ->
6695
/// %new = load %0 : $*T
6796
/// strong_retain %new : $T
6897
/// // no load/release of %old!
6998
/// store %new to %1 : $*T
7099
///
71100
/// copy_addr [take] %0 to [initialization] %1 : $*T
72101
/// ->
102+
/// %new = load [take] %0 : $*T
103+
/// store %new to [init] %1 : $*T
104+
/// ->
73105
/// %new = load %0 : $*T
74106
/// // no retain of %new!
75107
/// // no load/release of %old!
76108
/// store %new to %1 : $*T
77109
static bool expandCopyAddr(CopyAddrInst *cai) {
78110
SILFunction *fn = cai->getFunction();
79-
SILModule &module = cai->getModule();
80111
SILValue source = cai->getSrc();
81112

82113
// If we have an address only type don't do anything.
83114
SILType srcType = source->getType();
84115
if (srcType.isAddressOnly(*fn))
85116
return false;
86117

87-
bool expand = shouldExpand(module, srcType.getObjectType());
118+
bool expand = shouldExpandShim(fn, srcType.getObjectType());
88119
using TypeExpansionKind = Lowering::TypeLowering::TypeExpansionKind;
89120
auto expansionKind = expand ? TypeExpansionKind::MostDerivedDescendents
90121
: TypeExpansionKind::None;
91122

92123
SILBuilderWithScope builder(cai);
93124

94-
// %new = load %0 : $*T
95-
LoadInst *newValue = builder.createLoad(cai->getLoc(), source,
96-
LoadOwnershipQualifier::Unqualified);
125+
// If our object type is not trivial, we may need to destroy the old value and
126+
// copy the new one. Handle the trivial case quickly and return.
127+
if (srcType.isTrivial(*fn)) {
128+
SILValue newValue = builder.emitLoadValueOperation(
129+
cai->getLoc(), source, LoadOwnershipQualifier::Trivial);
130+
SILValue destAddr = cai->getDest();
131+
// Create the store.
132+
builder.emitStoreValueOperation(cai->getLoc(), newValue, destAddr,
133+
StoreOwnershipQualifier::Trivial);
134+
++NumExpand;
135+
return true;
136+
}
97137

138+
// %new = load [copy|take] %0 : $*T
139+
auto loadQual = [&]() -> LoadOwnershipQualifier {
140+
if (IsTake_t::IsTake == cai->isTakeOfSrc())
141+
return LoadOwnershipQualifier::Take;
142+
return LoadOwnershipQualifier::Copy;
143+
}();
144+
SILValue newValue = builder.emitLoweredLoadValueOperation(
145+
cai->getLoc(), source, loadQual, expansionKind);
98146
SILValue destAddr = cai->getDest();
99147

100-
// If our object type is not trivial, we may need to release the old value and
101-
// retain the new one.
102-
103-
auto &typeLowering = fn->getTypeLowering(srcType);
104-
105-
// If we have a non-trivial type...
106-
if (!typeLowering.isTrivial()) {
107-
// If we are not initializing:
108-
// %old = load %1 : $*T
109-
auto isInit = cai->isInitializationOfDest();
110-
LoadInst *oldValue = nullptr;
111-
if (IsInitialization_t::IsNotInitialization == isInit) {
112-
oldValue = builder.createLoad(cai->getLoc(), destAddr,
113-
LoadOwnershipQualifier::Unqualified);
114-
}
115-
116-
// If we are not taking and have a reference type:
117-
// strong_retain %new : $*T
118-
// or if we have a non-trivial non-reference type.
119-
// retain_value %new : $*T
120-
if (IsTake_t::IsNotTake == cai->isTakeOfSrc()) {
121-
typeLowering.emitLoweredCopyValue(builder, cai->getLoc(), newValue,
122-
expansionKind);
123-
}
124-
125-
// If we are not initializing:
126-
// strong_release %old : $*T
127-
// *or*
128-
// release_value %old : $*T
129-
if (oldValue) {
130-
typeLowering.emitLoweredDestroyValue(builder, cai->getLoc(), oldValue,
131-
expansionKind);
132-
}
133-
}
134-
135-
// Create the store.
136-
builder.createStore(cai->getLoc(), newValue, destAddr,
137-
StoreOwnershipQualifier::Unqualified);
148+
// Create the store in the guaranteed uninitialized memory.
149+
//
150+
// store %new to [init|assign] %1
151+
//
152+
// If we are not initializing the destination, we need to destroy what is
153+
// currently there before we re-initialize the memory.
154+
auto storeQualifier = [&]() -> StoreOwnershipQualifier {
155+
if (IsInitialization_t::IsInitialization != cai->isInitializationOfDest())
156+
return StoreOwnershipQualifier::Assign;
157+
return StoreOwnershipQualifier::Init;
158+
}();
159+
builder.emitLoweredStoreValueOperation(cai->getLoc(), newValue, destAddr,
160+
storeQualifier, expansionKind);
138161

139162
++NumExpand;
140163
return true;
141164
}
142165

143166
static bool expandDestroyAddr(DestroyAddrInst *dai) {
144167
SILFunction *fn = dai->getFunction();
145-
SILModule &module = dai->getModule();
146168
SILBuilderWithScope builder(dai);
147169

148170
// Strength reduce destroy_addr inst into release/store if
@@ -154,13 +176,16 @@ static bool expandDestroyAddr(DestroyAddrInst *dai) {
154176
if (type.isAddressOnly(*fn))
155177
return false;
156178

157-
bool expand = shouldExpand(module, type.getObjectType());
179+
// We only expand if ownership is not enabled and we do not have a large
180+
// type. This was something that was only really beneficial for the low level
181+
// ARC optimizer which runs without ownership enabled.
182+
bool expand = shouldExpandShim(fn, type.getObjectType());
158183

159184
// If we have a non-trivial type...
160185
if (!type.isTrivial(*fn)) {
161-
// If we have a type with reference semantics, emit a load/strong release.
162-
LoadInst *li = builder.createLoad(dai->getLoc(), addr,
163-
LoadOwnershipQualifier::Unqualified);
186+
// If we have a type with reference semantics, emit a load/destroy.
187+
SILValue li = builder.emitLoadValueOperation(dai->getLoc(), addr,
188+
LoadOwnershipQualifier::Take);
164189
auto &typeLowering = fn->getTypeLowering(type);
165190
using TypeExpansionKind = Lowering::TypeLowering::TypeExpansionKind;
166191
auto expansionKind = expand ? TypeExpansionKind::MostDerivedDescendents
@@ -175,7 +200,6 @@ static bool expandDestroyAddr(DestroyAddrInst *dai) {
175200

176201
static bool expandReleaseValue(ReleaseValueInst *rvi) {
177202
SILFunction *fn = rvi->getFunction();
178-
SILModule &module = rvi->getModule();
179203
SILBuilderWithScope builder(rvi);
180204

181205
// Strength reduce destroy_addr inst into release/store if
@@ -184,11 +208,11 @@ static bool expandReleaseValue(ReleaseValueInst *rvi) {
184208

185209
// If we have an address only type, do nothing.
186210
SILType type = value->getType();
187-
assert(!SILModuleConventions(module).useLoweredAddresses() ||
211+
assert(!SILModuleConventions(fn->getModule()).useLoweredAddresses() ||
188212
type.isLoadable(*fn) &&
189213
"release_value should never be called on a non-loadable type.");
190214

191-
if (!shouldExpand(module, type.getObjectType()))
215+
if (!shouldExpandShim(fn, type.getObjectType()))
192216
return false;
193217

194218
auto &TL = fn->getTypeLowering(type);
@@ -203,7 +227,6 @@ static bool expandReleaseValue(ReleaseValueInst *rvi) {
203227

204228
static bool expandRetainValue(RetainValueInst *rvi) {
205229
SILFunction *fn = rvi->getFunction();
206-
SILModule &module = rvi->getModule();
207230
SILBuilderWithScope builder(rvi);
208231

209232
// Strength reduce destroy_addr inst into release/store if
@@ -212,11 +235,11 @@ static bool expandRetainValue(RetainValueInst *rvi) {
212235

213236
// If we have an address only type, do nothing.
214237
SILType type = value->getType();
215-
assert(!SILModuleConventions(module).useLoweredAddresses() ||
238+
assert(!SILModuleConventions(fn->getModule()).useLoweredAddresses() ||
216239
type.isLoadable(*fn) &&
217240
"Copy Value can only be called on loadable types.");
218241

219-
if (!shouldExpand(module, type.getObjectType()))
242+
if (!shouldExpandShim(fn, type.getObjectType()))
220243
return false;
221244

222245
auto &typeLowering = fn->getTypeLowering(type);
@@ -291,9 +314,6 @@ class SILLowerAggregate : public SILFunctionTransform {
291314
/// The entry point to the transformation.
292315
void run() override {
293316
SILFunction *f = getFunction();
294-
// FIXME: Can we support ownership?
295-
if (f->hasOwnership())
296-
return;
297317
LLVM_DEBUG(llvm::dbgs() << "***** LowerAggregate on function: "
298318
<< f->getName() << " *****\n");
299319
bool changed = processFunction(*f);

test/SILOptimizer/loweraggregateinstrs.sil

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ struct LargeStruct {
6464
// CHECK: release_value [[ARG0]]
6565
// CHECK: [[ALLOC_STACK:%.*]] = alloc_stack $LargeStruct
6666
// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
67-
// CHECK: [[LOADED_OLD_VAL:%.*]] = load [[ALLOC_STACK]]
6867
// CHECK: retain_value [[LOADED_ARG1]]
68+
// CHECK: [[LOADED_OLD_VAL:%.*]] = load [[ALLOC_STACK]]
6969
// CHECK: release_value [[LOADED_OLD_VAL]]
7070
// CHECK: store [[LOADED_ARG1]] to [[ALLOC_STACK]]
7171
// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
@@ -97,11 +97,12 @@ bb0(%0 : $LargeStruct, %1 : $*LargeStruct):
9797
//
9898
// CHECK: [[ALLOC_STACK:%.*]] = alloc_stack $S2
9999
// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
100-
// CHECK: [[LOADED_OLDVALUE:%.*]] = load [[ALLOC_STACK]]
101100
// CHECK: [[LOADED_ARG1cls1:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls1
102101
// CHECK: strong_retain [[LOADED_ARG1cls1]] : $C1
103102
// CHECK: [[LOADED_ARG1cls2:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls2
104103
// CHECK: strong_retain [[LOADED_ARG1cls2]] : $C2
104+
105+
// CHECK: [[LOADED_OLDVALUE:%.*]] = load [[ALLOC_STACK]]
105106
// CHECK: [[LOADED_OLDVALUEcls1:%.*]] = struct_extract [[LOADED_OLDVALUE]] : $S2, #S2.cls1
106107
// CHECK: strong_release [[LOADED_OLDVALUEcls1]] : $C1
107108
// CHECK: [[LOADED_OLDVALUEcls2:%.*]] = struct_extract [[LOADED_OLDVALUE]] : $S2, #S2.cls2

0 commit comments

Comments
 (0)