@@ -271,6 +271,130 @@ bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI,
271
271
return true ;
272
272
}
273
273
274
+ // / Replaces a call of the getter of AnyKeyPath._storedInlineOffset with a
275
+ // / "constant" offset, in case of a keypath literal.
276
+ // /
277
+ // / "Constant" offset means a series of struct_element_addr and
278
+ // / tuple_element_addr instructions with a 0-pointer as base address.
279
+ // / These instructions can then be lowered to "real" constants in IRGen for
280
+ // / concrete types, or to metatype offset lookups for generic or resilient types.
281
+ // /
282
+ // / Replaces:
283
+ // / %kp = keypath ...
284
+ // / %offset = apply %_storedInlineOffset_method(%kp)
285
+ // / with:
286
+ // / %zero = integer_literal $Builtin.Word, 0
287
+ // / %null_ptr = unchecked_trivial_bit_cast %zero to $Builtin.RawPointer
288
+ // / %null_addr = pointer_to_address %null_ptr
289
+ // / %projected_addr = struct_element_addr %null_addr
290
+ // / ... // other address projections
291
+ // / %offset_ptr = address_to_pointer %projected_addr
292
+ // / %offset_builtin_int = unchecked_trivial_bit_cast %offset_ptr
293
+ // / %offset_int = struct $Int (%offset_builtin_int)
294
+ // / %offset = enum $Optional<Int>, #Optional.some!enumelt, %offset_int
295
+ bool SILCombiner::tryOptimizeKeypathOffsetOf (ApplyInst *AI,
296
+ FuncDecl *calleeFn,
297
+ KeyPathInst *kp) {
298
+ auto *accessor = dyn_cast<AccessorDecl>(calleeFn);
299
+ if (!accessor || !accessor->isGetter ())
300
+ return false ;
301
+
302
+ AbstractStorageDecl *storage = accessor->getStorage ();
303
+ DeclName name = storage->getName ();
304
+ if (!name.isSimpleName () ||
305
+ (name.getBaseIdentifier ().str () != " _storedInlineOffset" ))
306
+ return false ;
307
+
308
+ KeyPathPattern *pattern = kp->getPattern ();
309
+ SubstitutionMap patternSubs = kp->getSubstitutions ();
310
+ CanType rootTy = pattern->getRootType ().subst (patternSubs)->getCanonicalType ();
311
+ CanType parentTy = rootTy;
312
+
313
+ // First check if _storedInlineOffset would return an offset or nil. Basically
314
+ // only stored struct and tuple elements produce an offset. Everything else
315
+ // (e.g. computed properties, class properties) result in nil.
316
+ bool hasOffset = true ;
317
+ for (const KeyPathPatternComponent &component : pattern->getComponents ()) {
318
+ switch (component.getKind ()) {
319
+ case KeyPathPatternComponent::Kind::StoredProperty: {
320
+
321
+ // Handle the special case of C tail-allocated arrays. IRGen would
322
+ // generate an undef offset for struct_element_addr of C tail-allocated
323
+ // arrays.
324
+ VarDecl *propDecl = component.getStoredPropertyDecl ();
325
+ if (propDecl->hasClangNode () && propDecl->getType ()->isVoid ())
326
+ return false ;
327
+
328
+ if (!parentTy.getStructOrBoundGenericStruct ())
329
+ hasOffset = false ;
330
+ break ;
331
+ }
332
+ case KeyPathPatternComponent::Kind::TupleElement:
333
+ break ;
334
+ case KeyPathPatternComponent::Kind::GettableProperty:
335
+ case KeyPathPatternComponent::Kind::SettableProperty:
336
+ // We cannot predict the offset of fields in resilient types, because it's
337
+ // unknown if a resilient field is a computed or stored property.
338
+ if (component.getExternalDecl ())
339
+ return false ;
340
+ hasOffset = false ;
341
+ break ;
342
+ case KeyPathPatternComponent::Kind::OptionalChain:
343
+ case KeyPathPatternComponent::Kind::OptionalForce:
344
+ case KeyPathPatternComponent::Kind::OptionalWrap:
345
+ hasOffset = false ;
346
+ break ;
347
+ }
348
+ parentTy = component.getComponentType ();
349
+ }
350
+
351
+ SILLocation loc = AI->getLoc ();
352
+ SILValue result;
353
+
354
+ if (hasOffset) {
355
+ SILType rootAddrTy = SILType::getPrimitiveAddressType (rootTy);
356
+ SILValue rootAddr = Builder.createBaseAddrForOffset (loc, rootAddrTy);
357
+
358
+ auto projector = KeyPathProjector::create (kp, rootAddr, loc, Builder);
359
+ if (!projector)
360
+ return false ;
361
+
362
+ // Create the address projections of the keypath.
363
+ SILType ptrType = SILType::getRawPointerType (Builder.getASTContext ());
364
+ SILValue offsetPtr;
365
+ projector->project (KeyPathProjector::AccessType::Get, [&](SILValue addr) {
366
+ offsetPtr = Builder.createAddressToPointer (loc, addr, ptrType);
367
+ });
368
+
369
+ // The result of the _storedInlineOffset call should be Optional<Int>. If
370
+ // not, something is wrong with the stdlib. Anyway, if it's not like we
371
+ // expect, bail.
372
+ SILType intType = AI->getType ().getOptionalObjectType ();
373
+ if (!intType)
374
+ return false ;
375
+ StructDecl *intDecl = intType.getStructOrBoundGenericStruct ();
376
+ if (!intDecl || intDecl->getStoredProperties ().size () != 1 )
377
+ return false ;
378
+ VarDecl *member = intDecl->getStoredProperties ()[0 ];
379
+ CanType builtinIntTy = member->getType ()->getCanonicalType ();
380
+ if (!isa<BuiltinIntegerType>(builtinIntTy))
381
+ return false ;
382
+
383
+ // Convert the projected address back to an optional integer.
384
+ SILValue offset = Builder.createUncheckedBitCast (loc, offsetPtr,
385
+ SILType::getPrimitiveObjectType (builtinIntTy));
386
+ SILValue offsetInt = Builder.createStruct (loc, intType, { offset });
387
+ result = Builder.createOptionalSome (loc, offsetInt, AI->getType ());
388
+ } else {
389
+ // The keypath has no offset.
390
+ result = Builder.createOptionalNone (loc, AI->getType ());
391
+ }
392
+ AI->replaceAllUsesWith (result);
393
+ eraseInstFromFunction (*AI);
394
+ ++NumOptimizedKeypaths;
395
+ return true ;
396
+ }
397
+
274
398
// / Try to optimize a keypath KVC string access on a literal key path.
275
399
// /
276
400
// / Replace:
@@ -279,17 +403,8 @@ bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI,
279
403
// / With:
280
404
// / %string = string_literal "blah"
281
405
bool SILCombiner::tryOptimizeKeypathKVCString (ApplyInst *AI,
282
- SILDeclRef callee) {
283
- if (AI->getNumArguments () != 1 ) {
284
- return false ;
285
- }
286
- if (!callee.hasDecl ()) {
287
- return false ;
288
- }
289
- auto calleeFn = dyn_cast<FuncDecl>(callee.getDecl ());
290
- if (!calleeFn)
291
- return false ;
292
-
406
+ FuncDecl *calleeFn,
407
+ KeyPathInst *kp) {
293
408
if (!calleeFn->getAttrs ()
294
409
.hasSemanticsAttr (semantics::KEYPATH_KVC_KEY_PATH_STRING))
295
410
return false ;
@@ -300,11 +415,6 @@ bool SILCombiner::tryOptimizeKeypathKVCString(ApplyInst *AI,
300
415
if (!objTy || objTy.getStructOrBoundGenericStruct () != C.getStringDecl ())
301
416
return false ;
302
417
303
- KeyPathInst *kp
304
- = KeyPathProjector::getLiteralKeyPath (AI->getArgument (0 ));
305
- if (!kp || !kp->hasPattern ())
306
- return false ;
307
-
308
418
auto objcString = kp->getPattern ()->getObjCString ();
309
419
310
420
SILValue literalValue;
@@ -357,10 +467,33 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
357
467
return tryOptimizeKeypathApplication (AI, callee);
358
468
}
359
469
360
- if (auto method = dyn_cast<ClassMethodInst>(AI->getCallee ())) {
361
- return tryOptimizeKeypathKVCString (AI, method->getMember ());
470
+ // Try optimize keypath method calls.
471
+ auto *methodInst = dyn_cast<ClassMethodInst>(AI->getCallee ());
472
+ if (!methodInst)
473
+ return false ;
474
+
475
+ if (AI->getNumArguments () != 1 ) {
476
+ return false ;
477
+ }
478
+
479
+ SILDeclRef callee = methodInst->getMember ();
480
+ if (!callee.hasDecl ()) {
481
+ return false ;
362
482
}
483
+ auto *calleeFn = dyn_cast<FuncDecl>(callee.getDecl ());
484
+ if (!calleeFn)
485
+ return false ;
486
+
487
+ KeyPathInst *kp = KeyPathProjector::getLiteralKeyPath (AI->getArgument (0 ));
488
+ if (!kp || !kp->hasPattern ())
489
+ return false ;
363
490
491
+ if (tryOptimizeKeypathOffsetOf (AI, calleeFn, kp))
492
+ return true ;
493
+
494
+ if (tryOptimizeKeypathKVCString (AI, calleeFn, kp))
495
+ return true ;
496
+
364
497
return false ;
365
498
}
366
499
0 commit comments