@@ -271,6 +271,130 @@ bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI,
271271 return true ;
272272}
273273
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+
274398// / Try to optimize a keypath KVC string access on a literal key path.
275399// /
276400// / Replace:
@@ -279,17 +403,8 @@ bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI,
279403// / With:
280404// / %string = string_literal "blah"
281405bool 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) {
293408 if (!calleeFn->getAttrs ()
294409 .hasSemanticsAttr (semantics::KEYPATH_KVC_KEY_PATH_STRING))
295410 return false ;
@@ -300,11 +415,6 @@ bool SILCombiner::tryOptimizeKeypathKVCString(ApplyInst *AI,
300415 if (!objTy || objTy.getStructOrBoundGenericStruct () != C.getStringDecl ())
301416 return false ;
302417
303- KeyPathInst *kp
304- = KeyPathProjector::getLiteralKeyPath (AI->getArgument (0 ));
305- if (!kp || !kp->hasPattern ())
306- return false ;
307-
308418 auto objcString = kp->getPattern ()->getObjCString ();
309419
310420 SILValue literalValue;
@@ -357,10 +467,33 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
357467 return tryOptimizeKeypathApplication (AI, callee);
358468 }
359469
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 ;
362482 }
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 ;
363490
491+ if (tryOptimizeKeypathOffsetOf (AI, calleeFn, kp))
492+ return true ;
493+
494+ if (tryOptimizeKeypathKVCString (AI, calleeFn, kp))
495+ return true ;
496+
364497 return false ;
365498}
366499
0 commit comments