@@ -35,15 +35,14 @@ class ObjectOutliner {
35
35
return type.getNominalOrBoundGenericNominal () == ArrayDecl;
36
36
}
37
37
38
- bool isValidUseOfObject (SILInstruction *Val,
39
- bool isCOWObject,
40
- ApplyInst **FindStringCall = nullptr );
38
+ bool isValidUseOfObject (SILInstruction *Val);
39
+
40
+ ApplyInst *findFindStringCall (SILValue V );
41
41
42
42
bool getObjectInitVals (SILValue Val,
43
43
llvm::DenseMap<VarDecl *, StoreInst *> &MemberStores,
44
44
llvm::SmallVectorImpl<StoreInst *> &TailStores,
45
- unsigned NumTailTupleElements,
46
- ApplyInst **FindStringCall);
45
+ unsigned NumTailTupleElements);
47
46
bool handleTailAddr (int TailIdx, SILInstruction *I, unsigned NumTailTupleElements,
48
47
llvm::SmallVectorImpl<StoreInst *> &TailStores);
49
48
@@ -116,13 +115,7 @@ static bool isValidInitVal(SILValue V) {
116
115
}
117
116
118
117
// / Check if a use of an object may prevent outlining the object.
119
- // /
120
- // / If \p isCOWObject is true, then the object reference is wrapped into a
121
- // / COW container. Currently this is just Array<T>.
122
- // / If a use is a call to the findStringSwitchCase semantic call, the apply
123
- // / is returned in \p FindStringCall.
124
- bool ObjectOutliner::isValidUseOfObject (SILInstruction *I, bool isCOWObject,
125
- ApplyInst **FindStringCall) {
118
+ bool ObjectOutliner::isValidUseOfObject (SILInstruction *I) {
126
119
switch (I->getKind ()) {
127
120
case SILInstructionKind::DebugValueAddrInst:
128
121
case SILInstructionKind::DebugValueInst:
@@ -132,51 +125,25 @@ bool ObjectOutliner::isValidUseOfObject(SILInstruction *I, bool isCOWObject,
132
125
case SILInstructionKind::StrongReleaseInst:
133
126
case SILInstructionKind::FixLifetimeInst:
134
127
case SILInstructionKind::SetDeallocatingInst:
128
+ case SILInstructionKind::EndCOWMutationInst:
135
129
return true ;
136
130
137
- case SILInstructionKind::ReturnInst:
138
- case SILInstructionKind::TryApplyInst:
139
- case SILInstructionKind::PartialApplyInst:
140
- case SILInstructionKind::StoreInst:
141
- // / We don't have a representation for COW objects in SIL, so we do some
142
- // / ad-hoc testing: We can ignore uses of a COW object if any use after
143
- // / this will do a uniqueness checking before the object is modified.
144
- return isCOWObject;
145
-
146
- case SILInstructionKind::ApplyInst:
147
- if (!isCOWObject)
148
- return false ;
149
- // There should only be a single call to findStringSwitchCase. But even
150
- // if there are multiple calls, it's not problem - we'll just optimize the
151
- // last one we find.
152
- if (cast<ApplyInst>(I)->hasSemantics (semantics::FIND_STRING_SWITCH_CASE))
153
- *FindStringCall = cast<ApplyInst>(I);
154
- return true ;
155
-
156
- case SILInstructionKind::StructInst:
157
- if (isCOWType (cast<StructInst>(I)->getType ())) {
158
- // The object is wrapped into a COW container.
159
- isCOWObject = true ;
160
- }
161
- break ;
162
-
163
- case SILInstructionKind::UncheckedRefCastInst:
164
131
case SILInstructionKind::StructElementAddrInst:
165
132
case SILInstructionKind::AddressToPointerInst:
166
- assert (!isCOWObject && " instruction cannot have a COW object as operand" );
167
- break ;
168
-
133
+ case SILInstructionKind::StructInst:
169
134
case SILInstructionKind::TupleInst:
170
135
case SILInstructionKind::TupleExtractInst:
171
136
case SILInstructionKind::EnumInst:
172
- break ;
173
-
174
137
case SILInstructionKind::StructExtractInst:
175
- // To be on the safe side we don't consider the object as COW if it is
176
- // extracted again from the COW container: the uniqueness check may be
177
- // optimized away in this case.
178
- isCOWObject = false ;
179
- break ;
138
+ case SILInstructionKind::UncheckedRefCastInst:
139
+ case SILInstructionKind::UpcastInst: {
140
+ auto SVI = cast<SingleValueInstruction>(I);
141
+ for (Operand *Use : getNonDebugUses (SVI)) {
142
+ if (!isValidUseOfObject (Use->getUser ()))
143
+ return false ;
144
+ }
145
+ return true ;
146
+ }
180
147
181
148
case SILInstructionKind::BuiltinInst: {
182
149
// Handle the case for comparing addresses. This occurs when the Array
@@ -198,13 +165,37 @@ bool ObjectOutliner::isValidUseOfObject(SILInstruction *I, bool isCOWObject,
198
165
default :
199
166
return false ;
200
167
}
168
+ }
201
169
202
- auto SVI = cast<SingleValueInstruction>(I);
203
- for (Operand *Use : getNonDebugUses (SVI)) {
204
- if (!isValidUseOfObject (Use->getUser (), isCOWObject, FindStringCall))
205
- return false ;
170
+ // / Finds a call to findStringSwitchCase in the uses of \p V.
171
+ ApplyInst *ObjectOutliner::findFindStringCall (SILValue V) {
172
+ for (Operand *use : V->getUses ()) {
173
+ SILInstruction *user = use->getUser ();
174
+ switch (user->getKind ()) {
175
+ case SILInstructionKind::ApplyInst:
176
+ // There should only be a single call to findStringSwitchCase. But even
177
+ // if there are multiple calls, it's not problem - we'll just optimize the
178
+ // last one we find.
179
+ if (cast<ApplyInst>(user)->hasSemantics (semantics::FIND_STRING_SWITCH_CASE))
180
+ return cast<ApplyInst>(user);
181
+ break ;
182
+
183
+ case SILInstructionKind::StructInst:
184
+ case SILInstructionKind::TupleInst:
185
+ case SILInstructionKind::UncheckedRefCastInst:
186
+ case SILInstructionKind::UpcastInst: {
187
+ if (ApplyInst *foundCall =
188
+ findFindStringCall (cast<SingleValueInstruction>(user))) {
189
+ return foundCall;
190
+ }
191
+ break ;
192
+ }
193
+
194
+ default :
195
+ break ;
196
+ }
206
197
}
207
- return true ;
198
+ return nullptr ;
208
199
}
209
200
210
201
// / Handle the address of a tail element.
@@ -232,20 +223,19 @@ bool ObjectOutliner::handleTailAddr(int TailIdx, SILInstruction *TailAddr,
232
223
}
233
224
}
234
225
}
235
- return isValidUseOfObject (TailAddr, /* isCOWObject */ false );
226
+ return isValidUseOfObject (TailAddr);
236
227
}
237
228
238
229
// / Get the init values for an object's stored properties and its tail elements.
239
230
bool ObjectOutliner::getObjectInitVals (SILValue Val,
240
231
llvm::DenseMap<VarDecl *, StoreInst *> &MemberStores,
241
232
llvm::SmallVectorImpl<StoreInst *> &TailStores,
242
- unsigned NumTailTupleElements,
243
- ApplyInst **FindStringCall) {
233
+ unsigned NumTailTupleElements) {
244
234
for (Operand *Use : Val->getUses ()) {
245
235
SILInstruction *User = Use->getUser ();
246
236
if (auto *UC = dyn_cast<UpcastInst>(User)) {
247
237
// Upcast is transparent.
248
- if (!getObjectInitVals (UC, MemberStores, TailStores, NumTailTupleElements, FindStringCall ))
238
+ if (!getObjectInitVals (UC, MemberStores, TailStores, NumTailTupleElements))
249
239
return false ;
250
240
} else if (auto *REA = dyn_cast<RefElementAddrInst>(User)) {
251
241
// The address of a stored property.
@@ -255,7 +245,7 @@ bool ObjectOutliner::getObjectInitVals(SILValue Val,
255
245
if (!isValidInitVal (SI->getSrc ()) || MemberStores[REA->getField ()])
256
246
return false ;
257
247
MemberStores[REA->getField ()] = SI;
258
- } else if (!isValidUseOfObject (ElemAddrUser, /* isCOWObject */ false )) {
248
+ } else if (!isValidUseOfObject (ElemAddrUser)) {
259
249
return false ;
260
250
}
261
251
}
@@ -280,7 +270,7 @@ bool ObjectOutliner::getObjectInitVals(SILValue Val,
280
270
return false ;
281
271
}
282
272
}
283
- } else if (!isValidUseOfObject (User, /* isCOWObject */ false , FindStringCall )) {
273
+ } else if (!isValidUseOfObject (User)) {
284
274
return false ;
285
275
}
286
276
}
@@ -305,7 +295,8 @@ class GlobalVariableMangler : public Mangle::ASTMangler {
305
295
// / Try to convert an object allocation into a statically initialized object.
306
296
// /
307
297
// / In general this works for any class, but in practice it will only kick in
308
- // / for array buffer objects. The use cases are array literals in a function.
298
+ // / for copy-on-write buffers, like array buffer objects.
299
+ // / The use cases are array literals in a function.
309
300
// / For example:
310
301
// / func getarray() -> [Int] {
311
302
// / return [1, 2, 3]
@@ -314,6 +305,19 @@ bool ObjectOutliner::optimizeObjectAllocation(AllocRefInst *ARI) {
314
305
if (ARI->isObjC ())
315
306
return false ;
316
307
308
+ // Find the end_cow_mutation. Only for such COW buffer objects we do the
309
+ // transformation.
310
+ EndCOWMutationInst *endCOW = nullptr ;
311
+ for (Operand *use : ARI->getUses ()) {
312
+ if (auto *ecm = dyn_cast<EndCOWMutationInst>(use->getUser ())) {
313
+ if (endCOW)
314
+ return false ;
315
+ endCOW = ecm;
316
+ }
317
+ }
318
+ if (!endCOW || endCOW->doKeepUnique ())
319
+ return false ;
320
+
317
321
// Check how many tail allocated elements are on the object.
318
322
ArrayRef<Operand> TailCounts = ARI->getTailAllocatedCounts ();
319
323
SILType TailType;
@@ -363,11 +367,10 @@ bool ObjectOutliner::optimizeObjectAllocation(AllocRefInst *ARI) {
363
367
}
364
368
365
369
TailStores.resize (NumStores);
366
- ApplyInst *FindStringCall = nullptr ;
367
370
368
371
// Get the initialization stores of the object's properties and tail
369
372
// allocated elements. Also check if there are any "bad" uses of the object.
370
- if (!getObjectInitVals (ARI, MemberStores, TailStores, NumTailTupleElems, &FindStringCall ))
373
+ if (!getObjectInitVals (ARI, MemberStores, TailStores, NumTailTupleElems))
371
374
return false ;
372
375
373
376
// Is there a store for all the class properties?
@@ -452,6 +455,12 @@ bool ObjectOutliner::optimizeObjectAllocation(AllocRefInst *ARI) {
452
455
SILBuilder B (ARI);
453
456
GlobalValueInst *GVI = B.createGlobalValue (ARI->getLoc (), Glob);
454
457
B.createStrongRetain (ARI->getLoc (), GVI, B.getDefaultAtomicity ());
458
+
459
+ ApplyInst *FindStringCall = findFindStringCall (endCOW);
460
+ assert (endCOW->getOperand () == ARI);
461
+ endCOW->replaceAllUsesWith (ARI);
462
+ ToRemove.push_back (endCOW);
463
+
455
464
llvm::SmallVector<Operand *, 8 > Worklist (ARI->use_begin (), ARI->use_end ());
456
465
while (!Worklist.empty ()) {
457
466
auto *Use = Worklist.pop_back_val ();
0 commit comments