Skip to content

Commit d0a7948

Browse files
authored
Merge pull request #32544 from eeckstein/fold-kp-offset
SILCombine: Constant-fold MemoryLayout<T>.offset(of: \.literalKeyPath)
2 parents 3e59cc4 + d7d829c commit d0a7948

File tree

20 files changed

+448
-21
lines changed

20 files changed

+448
-21
lines changed

docs/SIL.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3573,6 +3573,20 @@ the encoding is ``objc_selector``, the string literal produces a
35733573
reference to a UTF-8-encoded Objective-C selector in the Objective-C
35743574
method name segment.
35753575

3576+
base_addr_for_offset
3577+
````````````````````
3578+
::
3579+
3580+
sil-instruction ::= 'base_addr_for_offset' sil-type
3581+
3582+
%1 = base_addr_for_offset $*S
3583+
// %1 has type $*S
3584+
3585+
Creates a base address for offset calculations. The result can be used by
3586+
address projections, like ``struct_element_addr``, which themselves return the
3587+
offset of the projected fields.
3588+
IR generation simply creates a null pointer for ``base_addr_for_offset``.
3589+
35763590
Dynamic Dispatch
35773591
~~~~~~~~~~~~~~~~
35783592

include/swift/SIL/SILBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,10 @@ class SILBuilder {
631631
return insert(new (getModule()) GlobalValueInst(getSILDebugLocation(Loc), g,
632632
getTypeExpansionContext()));
633633
}
634+
BaseAddrForOffsetInst *createBaseAddrForOffset(SILLocation Loc, SILType Ty) {
635+
return insert(new (F->getModule())
636+
BaseAddrForOffsetInst(getSILDebugLocation(Loc), Ty));
637+
}
634638
IntegerLiteralInst *createIntegerLiteral(IntegerLiteralExpr *E);
635639

636640
IntegerLiteralInst *createIntegerLiteral(SILLocation Loc, SILType Ty,

include/swift/SIL/SILCloner.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,15 @@ SILCloner<ImplClass>::visitGlobalValueInst(GlobalValueInst *Inst) {
10301030
Inst->getReferencedGlobal()));
10311031
}
10321032

1033+
template<typename ImplClass>
1034+
void
1035+
SILCloner<ImplClass>::visitBaseAddrForOffsetInst(BaseAddrForOffsetInst *Inst) {
1036+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1037+
recordClonedInstruction(
1038+
Inst, getBuilder().createBaseAddrForOffset(getOpLocation(Inst->getLoc()),
1039+
getOpType(Inst->getType())));
1040+
}
1041+
10331042
template<typename ImplClass>
10341043
void
10351044
SILCloner<ImplClass>::visitIntegerLiteralInst(IntegerLiteralInst *Inst) {

include/swift/SIL/SILInstruction.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3213,6 +3213,20 @@ class GlobalAddrInst
32133213
: InstructionBase(DebugLoc, Ty, nullptr) {}
32143214
};
32153215

3216+
/// Creates a base address for offset calculations.
3217+
class BaseAddrForOffsetInst
3218+
: public InstructionBase<SILInstructionKind::BaseAddrForOffsetInst,
3219+
LiteralInst> {
3220+
friend SILBuilder;
3221+
3222+
BaseAddrForOffsetInst(SILDebugLocation DebugLoc, SILType Ty)
3223+
: InstructionBase(DebugLoc, Ty) {}
3224+
3225+
public:
3226+
ArrayRef<Operand> getAllOperands() const { return {}; }
3227+
MutableArrayRef<Operand> getAllOperands() { return {}; }
3228+
};
3229+
32163230
/// Gives the value of a global variable.
32173231
///
32183232
/// The referenced global variable must be a statically initialized object.

include/swift/SIL/SILNodes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
458458
LiteralInst, None, DoesNotRelease)
459459
SINGLE_VALUE_INST(GlobalAddrInst, global_addr,
460460
LiteralInst, None, DoesNotRelease)
461+
SINGLE_VALUE_INST(BaseAddrForOffsetInst, base_addr_for_offset,
462+
LiteralInst, None, DoesNotRelease)
461463
SINGLE_VALUE_INST(GlobalValueInst, global_value,
462464
LiteralInst, None, DoesNotRelease)
463465
SINGLE_VALUE_INST(IntegerLiteralInst, integer_literal,

lib/IRGen/IRGenSIL.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,7 @@ class IRGenSILFunction :
868868
void visitAllocGlobalInst(AllocGlobalInst *i);
869869
void visitGlobalAddrInst(GlobalAddrInst *i);
870870
void visitGlobalValueInst(GlobalValueInst *i);
871+
void visitBaseAddrForOffsetInst(BaseAddrForOffsetInst *i);
871872

872873
void visitIntegerLiteralInst(IntegerLiteralInst *i);
873874
void visitFloatLiteralInst(FloatLiteralInst *i);
@@ -2074,6 +2075,12 @@ void IRGenSILFunction::visitGlobalValueInst(GlobalValueInst *i) {
20742075
setLoweredExplosion(i, e);
20752076
}
20762077

2078+
void IRGenSILFunction::visitBaseAddrForOffsetInst(BaseAddrForOffsetInst *i) {
2079+
auto storagePtrTy = IGM.getStoragePointerType(i->getType());
2080+
llvm::Value *addr = llvm::ConstantPointerNull::get(storagePtrTy);
2081+
setLoweredAddress(i, Address(addr, Alignment()));
2082+
}
2083+
20772084
void IRGenSILFunction::visitMetatypeInst(swift::MetatypeInst *i) {
20782085
auto metaTy = i->getType().castTo<MetatypeType>();
20792086
Explosion e;

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ SHOULD_NEVER_VISIT_INST(DynamicFunctionRef)
123123
SHOULD_NEVER_VISIT_INST(PreviousDynamicFunctionRef)
124124
SHOULD_NEVER_VISIT_INST(GlobalAddr)
125125
SHOULD_NEVER_VISIT_INST(GlobalValue)
126+
SHOULD_NEVER_VISIT_INST(BaseAddrForOffset)
126127
SHOULD_NEVER_VISIT_INST(IntegerLiteral)
127128
SHOULD_NEVER_VISIT_INST(Metatype)
128129
SHOULD_NEVER_VISIT_INST(ObjCProtocol)

lib/SIL/IR/SILPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,10 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
13191319
*this << " : " << GVI->getType();
13201320
}
13211321

1322+
void visitBaseAddrForOffsetInst(BaseAddrForOffsetInst *BAI) {
1323+
*this << BAI->getType();
1324+
}
1325+
13221326
void visitIntegerLiteralInst(IntegerLiteralInst *ILI) {
13231327
const auto &lit = ILI->getValue();
13241328
*this << ILI->getType() << ", " << lit;

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ CONSTANT_OWNERSHIP_INST(None, FunctionRef)
103103
CONSTANT_OWNERSHIP_INST(None, DynamicFunctionRef)
104104
CONSTANT_OWNERSHIP_INST(None, PreviousDynamicFunctionRef)
105105
CONSTANT_OWNERSHIP_INST(None, GlobalAddr)
106+
CONSTANT_OWNERSHIP_INST(None, BaseAddrForOffset)
106107
CONSTANT_OWNERSHIP_INST(None, IndexAddr)
107108
CONSTANT_OWNERSHIP_INST(None, IndexRawPointer)
108109
CONSTANT_OWNERSHIP_INST(None, InitEnumDataAddr)

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4583,6 +4583,15 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
45834583
}
45844584
break;
45854585
}
4586+
case SILInstructionKind::BaseAddrForOffsetInst: {
4587+
SILType Ty;
4588+
if (parseSILType(Ty))
4589+
return true;
4590+
if (parseSILDebugLocation(InstLoc, B))
4591+
return true;
4592+
ResultVal = B.createBaseAddrForOffset(InstLoc, Ty);
4593+
break;
4594+
}
45864595
case SILInstructionKind::SelectEnumInst:
45874596
case SILInstructionKind::SelectEnumAddrInst: {
45884597
if (parseTypedValueRef(Val, B))

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,11 @@ class SILCombiner :
246246
bool tryOptimizeKeypath(ApplyInst *AI);
247247
bool tryOptimizeInoutKeypath(BeginApplyInst *AI);
248248
bool tryOptimizeKeypathApplication(ApplyInst *AI, SILFunction *callee);
249-
bool tryOptimizeKeypathKVCString(ApplyInst *AI, SILDeclRef callee);
250-
249+
bool tryOptimizeKeypathOffsetOf(ApplyInst *AI, FuncDecl *calleeFn,
250+
KeyPathInst *kp);
251+
bool tryOptimizeKeypathKVCString(ApplyInst *AI, FuncDecl *calleeFn,
252+
KeyPathInst *kp);
253+
251254
// Optimize concatenation of string literals.
252255
// Constant-fold concatenation of string literals known at compile-time.
253256
SILInstruction *optimizeConcatenationOfStringLiterals(ApplyInst *AI);

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 143 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,122 @@ 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+
if (!parentTy.getStructOrBoundGenericStruct())
321+
hasOffset = false;
322+
break;
323+
}
324+
case KeyPathPatternComponent::Kind::TupleElement:
325+
break;
326+
case KeyPathPatternComponent::Kind::GettableProperty:
327+
case KeyPathPatternComponent::Kind::SettableProperty:
328+
// We cannot predict the offset of fields in resilient types, because it's
329+
// unknown if a resilient field is a computed or stored property.
330+
if (component.getExternalDecl())
331+
return false;
332+
hasOffset = false;
333+
break;
334+
case KeyPathPatternComponent::Kind::OptionalChain:
335+
case KeyPathPatternComponent::Kind::OptionalForce:
336+
case KeyPathPatternComponent::Kind::OptionalWrap:
337+
hasOffset = false;
338+
break;
339+
}
340+
parentTy = component.getComponentType();
341+
}
342+
343+
SILLocation loc = AI->getLoc();
344+
SILValue result;
345+
346+
if (hasOffset) {
347+
SILType rootAddrTy = SILType::getPrimitiveAddressType(rootTy);
348+
SILValue rootAddr = Builder.createBaseAddrForOffset(loc, rootAddrTy);
349+
350+
auto projector = KeyPathProjector::create(kp, rootAddr, loc, Builder);
351+
if (!projector)
352+
return false;
353+
354+
// Create the address projections of the keypath.
355+
SILType ptrType = SILType::getRawPointerType(Builder.getASTContext());
356+
SILValue offsetPtr;
357+
projector->project(KeyPathProjector::AccessType::Get, [&](SILValue addr) {
358+
offsetPtr = Builder.createAddressToPointer(loc, addr, ptrType);
359+
});
360+
361+
// The result of the _storedInlineOffset call should be Optional<Int>. If
362+
// not, something is wrong with the stdlib. Anyway, if it's not like we
363+
// expect, bail.
364+
SILType intType = AI->getType().getOptionalObjectType();
365+
if (!intType)
366+
return false;
367+
StructDecl *intDecl = intType.getStructOrBoundGenericStruct();
368+
if (!intDecl || intDecl->getStoredProperties().size() != 1)
369+
return false;
370+
VarDecl *member = intDecl->getStoredProperties()[0];
371+
CanType builtinIntTy = member->getType()->getCanonicalType();
372+
if (!isa<BuiltinIntegerType>(builtinIntTy))
373+
return false;
374+
375+
// Convert the projected address back to an optional integer.
376+
SILValue offset = Builder.createUncheckedBitCast(loc, offsetPtr,
377+
SILType::getPrimitiveObjectType(builtinIntTy));
378+
SILValue offsetInt = Builder.createStruct(loc, intType, { offset });
379+
result = Builder.createOptionalSome(loc, offsetInt, AI->getType());
380+
} else {
381+
// The keypath has no offset.
382+
result = Builder.createOptionalNone(loc, AI->getType());
383+
}
384+
AI->replaceAllUsesWith(result);
385+
eraseInstFromFunction(*AI);
386+
++NumOptimizedKeypaths;
387+
return true;
388+
}
389+
274390
/// Try to optimize a keypath KVC string access on a literal key path.
275391
///
276392
/// Replace:
@@ -279,17 +395,8 @@ bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI,
279395
/// With:
280396
/// %string = string_literal "blah"
281397
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-
398+
FuncDecl *calleeFn,
399+
KeyPathInst *kp) {
293400
if (!calleeFn->getAttrs()
294401
.hasSemanticsAttr(semantics::KEYPATH_KVC_KEY_PATH_STRING))
295402
return false;
@@ -300,11 +407,6 @@ bool SILCombiner::tryOptimizeKeypathKVCString(ApplyInst *AI,
300407
if (!objTy || objTy.getStructOrBoundGenericStruct() != C.getStringDecl())
301408
return false;
302409

303-
KeyPathInst *kp
304-
= KeyPathProjector::getLiteralKeyPath(AI->getArgument(0));
305-
if (!kp || !kp->hasPattern())
306-
return false;
307-
308410
auto objcString = kp->getPattern()->getObjCString();
309411

310412
SILValue literalValue;
@@ -357,10 +459,33 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
357459
return tryOptimizeKeypathApplication(AI, callee);
358460
}
359461

360-
if (auto method = dyn_cast<ClassMethodInst>(AI->getCallee())) {
361-
return tryOptimizeKeypathKVCString(AI, method->getMember());
462+
// Try optimize keypath method calls.
463+
auto *methodInst = dyn_cast<ClassMethodInst>(AI->getCallee());
464+
if (!methodInst)
465+
return false;
466+
467+
if (AI->getNumArguments() != 1) {
468+
return false;
469+
}
470+
471+
SILDeclRef callee = methodInst->getMember();
472+
if (!callee.hasDecl()) {
473+
return false;
362474
}
475+
auto *calleeFn = dyn_cast<FuncDecl>(callee.getDecl());
476+
if (!calleeFn)
477+
return false;
478+
479+
KeyPathInst *kp = KeyPathProjector::getLiteralKeyPath(AI->getArgument(0));
480+
if (!kp || !kp->hasPattern())
481+
return false;
363482

483+
if (tryOptimizeKeypathOffsetOf(AI, calleeFn, kp))
484+
return true;
485+
486+
if (tryOptimizeKeypathKVCString(AI, calleeFn, kp))
487+
return true;
488+
364489
return false;
365490
}
366491

lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context,
161161
case SILInstructionKind::PreviousDynamicFunctionRefInst:
162162
case SILInstructionKind::GlobalAddrInst:
163163
case SILInstructionKind::GlobalValueInst:
164+
case SILInstructionKind::BaseAddrForOffsetInst:
164165
case SILInstructionKind::IntegerLiteralInst:
165166
case SILInstructionKind::FloatLiteralInst:
166167
case SILInstructionKind::StringLiteralInst:

lib/SILOptimizer/Utils/SILInliner.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
685685
case SILInstructionKind::FunctionRefInst:
686686
case SILInstructionKind::AllocGlobalInst:
687687
case SILInstructionKind::GlobalAddrInst:
688+
case SILInstructionKind::BaseAddrForOffsetInst:
688689
case SILInstructionKind::EndLifetimeInst:
689690
case SILInstructionKind::UncheckedOwnershipConversionInst:
690691
return InlineCost::Free;

lib/Serialization/DeserializeSIL.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,6 +1592,11 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn,
15921592
}
15931593
break;
15941594
}
1595+
case SILInstructionKind::BaseAddrForOffsetInst:
1596+
assert(RecordKind == SIL_ONE_TYPE && "Layout should be OneType.");
1597+
ResultVal = Builder.createBaseAddrForOffset(Loc,
1598+
getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn));
1599+
break;
15951600
case SILInstructionKind::DeallocStackInst: {
15961601
auto Ty = MF->getType(TyID);
15971602
ResultVal = Builder.createDeallocStack(

lib/Serialization/SerializeSIL.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,12 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
10911091
S.addUniquedStringRef(G->getName()));
10921092
break;
10931093
}
1094+
case SILInstructionKind::BaseAddrForOffsetInst: {
1095+
const BaseAddrForOffsetInst *BAI = cast<BaseAddrForOffsetInst>(&SI);
1096+
writeOneTypeLayout(BAI->getKind(), /*attrs*/ 0, BAI->getType());
1097+
break;
1098+
}
1099+
10941100
case SILInstructionKind::BranchInst: {
10951101
// Format: destination basic block ID, a list of arguments. Use
10961102
// SILOneTypeValuesLayout.

0 commit comments

Comments
 (0)