28
28
#include " swift/SILOptimizer/Analysis/ValueTracking.h"
29
29
#include " swift/SILOptimizer/Utils/CFGOptUtils.h"
30
30
#include " swift/SILOptimizer/Utils/Existential.h"
31
- #include " swift/SILOptimizer/Utils/KeyPathProjector.h"
32
31
#include " swift/SILOptimizer/Utils/ValueLifetime.h"
33
32
#include " llvm/ADT/DenseMap.h"
34
33
#include " llvm/ADT/SmallPtrSet.h"
35
34
#include " llvm/ADT/SmallVector.h"
36
35
#include " llvm/ADT/Statistic.h"
37
- #include < utility>
38
36
39
37
using namespace swift ;
40
38
using namespace swift ::PatternMatch;
@@ -209,6 +207,92 @@ SILCombiner::optimizeApplyOfConvertFunctionInst(FullApplySite AI,
209
207
return NAI;
210
208
}
211
209
210
+ // / Ends the begin_access "scope" if a begin_access was inserted for optimizing
211
+ // / a keypath pattern.
212
+ static void insertEndAccess (BeginAccessInst *&beginAccess, bool isModify,
213
+ SILBuilder &builder) {
214
+ if (beginAccess) {
215
+ builder.createEndAccess (beginAccess->getLoc (), beginAccess,
216
+ /* aborted*/ false );
217
+ if (isModify)
218
+ beginAccess->setAccessKind (SILAccessKind::Modify);
219
+ beginAccess = nullptr ;
220
+ }
221
+ }
222
+
223
+ // / Creates the projection pattern for a keypath instruction.
224
+ // /
225
+ // / Currently only the StoredProperty pattern is handled.
226
+ // / TODO: handle other patterns, like getters/setters, optional chaining, etc.
227
+ // /
228
+ // / Returns false if \p keyPath is not a keypath instruction or if there is any
229
+ // / other reason why the optimization cannot be done.
230
+ static SILValue createKeypathProjections (SILValue keyPath, SILValue root,
231
+ SILLocation loc,
232
+ BeginAccessInst *&beginAccess,
233
+ SILBuilder &builder) {
234
+ if (auto *upCast = dyn_cast<UpcastInst>(keyPath))
235
+ keyPath = upCast->getOperand ();
236
+
237
+ // Is it a keypath instruction at all?
238
+ auto *kpInst = dyn_cast<KeyPathInst>(keyPath);
239
+ if (!kpInst || !kpInst->hasPattern ())
240
+ return SILValue ();
241
+
242
+ auto components = kpInst->getPattern ()->getComponents ();
243
+
244
+ // Check if the keypath only contains patterns which we support.
245
+ for (const KeyPathPatternComponent &comp : components) {
246
+ if (comp.getKind () != KeyPathPatternComponent::Kind::StoredProperty)
247
+ return SILValue ();
248
+ }
249
+
250
+ SILValue addr = root;
251
+ for (const KeyPathPatternComponent &comp : components) {
252
+ assert (comp.getKind () == KeyPathPatternComponent::Kind::StoredProperty);
253
+ VarDecl *storedProperty = comp.getStoredPropertyDecl ();
254
+ SILValue elementAddr;
255
+ if (addr->getType ().getStructOrBoundGenericStruct ()) {
256
+ addr = builder.createStructElementAddr (loc, addr, storedProperty);
257
+ } else if (addr->getType ().getClassOrBoundGenericClass ()) {
258
+ SingleValueInstruction *Ref = builder.createLoad (loc, addr,
259
+ LoadOwnershipQualifier::Unqualified);
260
+ insertEndAccess (beginAccess, /* isModify*/ false , builder);
261
+
262
+ // Handle the case where the storedProperty is in a super class.
263
+ while (Ref->getType ().getClassOrBoundGenericClass () !=
264
+ storedProperty->getDeclContext ()) {
265
+ SILType superCl = Ref->getType ().getSuperclass ();
266
+ if (!superCl) {
267
+ // This should never happen, because the property should be in the
268
+ // decl or in a superclass of it. Just handle this to be on the safe
269
+ // side.
270
+ return SILValue ();
271
+ }
272
+ Ref = builder.createUpcast (loc, Ref, superCl);
273
+ }
274
+
275
+ addr = builder.createRefElementAddr (loc, Ref, storedProperty);
276
+
277
+ // Class members need access enforcement.
278
+ if (builder.getModule ().getOptions ().EnforceExclusivityDynamic ) {
279
+ beginAccess = builder.createBeginAccess (loc, addr, SILAccessKind::Read,
280
+ SILAccessEnforcement::Dynamic,
281
+ /* noNestedConflict*/ false ,
282
+ /* fromBuiltin*/ false );
283
+ addr = beginAccess;
284
+ }
285
+ } else {
286
+ // This should never happen, as a stored-property pattern can only be
287
+ // applied to classes and structs. But to be safe - and future prove -
288
+ // let's handle this case and bail.
289
+ insertEndAccess (beginAccess, /* isModify*/ false , builder);
290
+ return SILValue ();
291
+ }
292
+ }
293
+ return addr;
294
+ }
295
+
212
296
// / Try to optimize a keypath application with an apply instruction.
213
297
// /
214
298
// / Replaces (simplified SIL):
@@ -227,40 +311,36 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
227
311
return false ;
228
312
229
313
SILValue keyPath, rootAddr, valueAddr;
230
- bool isSet = false ;
314
+ bool isModify = false ;
231
315
if (callee->getName () == " swift_setAtWritableKeyPath" ||
232
316
callee->getName () == " swift_setAtReferenceWritableKeyPath" ) {
233
317
keyPath = AI->getArgument (1 );
234
318
rootAddr = AI->getArgument (0 );
235
319
valueAddr = AI->getArgument (2 );
236
- isSet = true ;
320
+ isModify = true ;
237
321
} else if (callee->getName () == " swift_getAtKeyPath" ) {
238
322
keyPath = AI->getArgument (2 );
239
323
rootAddr = AI->getArgument (1 );
240
324
valueAddr = AI->getArgument (0 );
241
325
} else {
242
326
return false ;
243
327
}
244
-
245
- auto projector = KeyPathProjector::create (keyPath, rootAddr,
246
- AI->getLoc (), Builder);
247
- if (!projector)
328
+
329
+ BeginAccessInst *beginAccess = nullptr ;
330
+ SILValue projectedAddr = createKeypathProjections (keyPath, rootAddr,
331
+ AI->getLoc (), beginAccess,
332
+ Builder);
333
+ if (!projectedAddr)
248
334
return false ;
249
-
250
- KeyPathProjector::AccessType accessType;
251
- if (isSet) accessType = KeyPathProjector::AccessType::Set;
252
- else accessType = KeyPathProjector::AccessType::Get;
253
-
254
- projector->project (accessType, [&](SILValue projectedAddr) {
255
- if (isSet) {
256
- Builder.createCopyAddr (AI->getLoc (), valueAddr, projectedAddr,
257
- IsTake, IsInitialization);
258
- } else {
259
- Builder.createCopyAddr (AI->getLoc (), projectedAddr, valueAddr,
260
- IsNotTake, IsInitialization);
261
- }
262
- });
263
-
335
+
336
+ if (isModify) {
337
+ Builder.createCopyAddr (AI->getLoc (), valueAddr, projectedAddr,
338
+ IsTake, IsNotInitialization);
339
+ } else {
340
+ Builder.createCopyAddr (AI->getLoc (), projectedAddr, valueAddr,
341
+ IsNotTake, IsInitialization);
342
+ }
343
+ insertEndAccess (beginAccess, isModify, Builder);
264
344
eraseInstFromFunction (*AI);
265
345
++NumOptimizedKeypaths;
266
346
return true ;
@@ -305,24 +385,19 @@ bool SILCombiner::tryOptimizeInoutKeypath(BeginApplyInst *AI) {
305
385
EndApplyInst *endApply = dyn_cast<EndApplyInst>(AIUse->getUser ());
306
386
if (!endApply)
307
387
return false ;
308
-
309
- auto projector = KeyPathProjector::create (keyPath, rootAddr,
310
- AI->getLoc (), Builder);
311
- if (!projector)
388
+
389
+ BeginAccessInst *beginAccess = nullptr ;
390
+ SILValue projectedAddr = createKeypathProjections (keyPath, rootAddr,
391
+ AI->getLoc (), beginAccess,
392
+ Builder);
393
+ if (!projectedAddr)
312
394
return false ;
313
-
314
- KeyPathProjector::AccessType accessType;
315
- if (isModify) accessType = KeyPathProjector::AccessType::Modify;
316
- else accessType = KeyPathProjector::AccessType::Get;
317
-
318
- projector->project (accessType, [&](SILValue projectedAddr) {
319
- // Replace the projected address.
320
- valueAddr->replaceAllUsesWith (projectedAddr);
321
-
322
- // Skip to the end of the key path application before cleaning up.
323
- Builder.setInsertionPoint (endApply);
324
- });
325
395
396
+ // Replace the projected address.
397
+ valueAddr->replaceAllUsesWith (projectedAddr);
398
+
399
+ Builder.setInsertionPoint (endApply);
400
+ insertEndAccess (beginAccess, isModify, Builder);
326
401
eraseInstFromFunction (*endApply);
327
402
eraseInstFromFunction (*AI);
328
403
++NumOptimizedKeypaths;
0 commit comments