Skip to content

Commit 8793212

Browse files
authored
Merge pull request #10741 from jckarter/key-path-generic-computed
Handle generic computed properties in key paths.
2 parents 04453d1 + 81ea860 commit 8793212

20 files changed

+920
-149
lines changed

include/swift/IRGen/Linking.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,14 @@ class LinkInfo {
680680

681681
static LinkInfo get(IRGenModule &IGM, const LinkEntity &entity,
682682
ForDefinition_t forDefinition);
683+
684+
static LinkInfo get(const UniversalLinkageInfo &linkInfo,
685+
StringRef name,
686+
SILLinkage linkage,
687+
bool isFragile,
688+
bool isSILOnly,
689+
ForDefinition_t isDefinition,
690+
bool isWeakImported);
683691

684692
StringRef getName() const {
685693
return Name.str();

include/swift/SIL/SILInstruction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4578,7 +4578,7 @@ class SuperMethodInst
45784578

45794579
SuperMethodInst(SILDebugLocation DebugLoc, SILValue Operand,
45804580
SILDeclRef Member, SILType Ty, bool Volatile = false)
4581-
: UnaryInstructionBase(DebugLoc, Operand, Ty, Member, Volatile) {}
4581+
: UnaryInstructionBase(DebugLoc, Operand, Ty, Member, Volatile) {}
45824582
};
45834583

45844584
/// WitnessMethodInst - Given a type, a protocol conformance,

lib/IRGen/GenDecl.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,23 @@ LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo,
15121512
return result;
15131513
}
15141514

1515+
LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo,
1516+
StringRef name,
1517+
SILLinkage linkage,
1518+
bool isFragile,
1519+
bool isSILOnly,
1520+
ForDefinition_t isDefinition,
1521+
bool isWeakImported) {
1522+
LinkInfo result;
1523+
1524+
result.Name += name;
1525+
std::tie(result.Linkage, result.Visibility, result.DLLStorageClass) =
1526+
getIRLinkage(linkInfo, linkage, isFragile, isSILOnly,
1527+
isDefinition, isWeakImported);
1528+
result.ForDefinition = isDefinition;
1529+
return result;
1530+
}
1531+
15151532
static bool isPointerTo(llvm::Type *ptrTy, llvm::Type *objTy) {
15161533
return cast<llvm::PointerType>(ptrTy)->getElementType() == objTy;
15171534
}

lib/IRGen/GenFunc.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM,
935935
SubstitutionMap subMap;
936936
if (auto genericSig = origType->getGenericSignature())
937937
subMap = genericSig->getSubstitutionMap(subs);
938-
emitPolymorphicArguments(subIGF, origType, substType, subMap,
938+
emitPolymorphicArguments(subIGF, origType, subMap,
939939
&witnessMetadata, polyArgs);
940940
}
941941

@@ -1139,7 +1139,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM,
11391139
SubstitutionMap subMap;
11401140
if (auto genericSig = origType->getGenericSignature())
11411141
subMap = genericSig->getSubstitutionMap(subs);
1142-
emitPolymorphicArguments(subIGF, origType, substType, subMap,
1142+
emitPolymorphicArguments(subIGF, origType, subMap,
11431143
&witnessMetadata, polyArgs);
11441144
}
11451145
}

lib/IRGen/GenKeyPath.cpp

Lines changed: 232 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
#include "ConstantBuilder.h"
1919
#include "Explosion.h"
2020
#include "GenClass.h"
21+
#include "GenDecl.h"
2122
#include "GenMeta.h"
23+
#include "GenProto.h"
2224
#include "GenStruct.h"
2325
#include "GenericRequirement.h"
2426
#include "IRGenDebugInfo.h"
@@ -27,6 +29,7 @@
2729
#include "ProtocolInfo.h"
2830
#include "StructLayout.h"
2931
#include "llvm/ADT/SetVector.h"
32+
#include "llvm/IR/Module.h"
3033
#include "swift/SIL/SILInstruction.h"
3134
#include "swift/SIL/SILLocation.h"
3235
#include "swift/SIL/TypeLowering.h"
@@ -37,10 +40,212 @@
3740
#include "swift/AST/DiagnosticsIRGen.h"
3841
#include "swift/AST/GenericEnvironment.h"
3942
#include "swift/AST/Types.h"
43+
#include "swift/IRGen/Linking.h"
4044

4145
using namespace swift;
4246
using namespace irgen;
4347

48+
enum GetterOrSetter {
49+
Getter,
50+
Setter,
51+
};
52+
53+
static llvm::Function *
54+
getAccessorForComputedComponent(IRGenModule &IGM,
55+
const KeyPathPatternComponent &component,
56+
GetterOrSetter whichAccessor,
57+
GenericEnvironment *genericEnv,
58+
ArrayRef<GenericRequirement> requirements) {
59+
SILFunction *accessor;
60+
switch (whichAccessor) {
61+
case Getter:
62+
accessor = component.getComputedPropertyGetter();
63+
break;
64+
case Setter:
65+
accessor = component.getComputedPropertySetter();
66+
break;
67+
}
68+
69+
auto accessorFn = IGM.getAddrOfSILFunction(accessor, NotForDefinition);
70+
71+
// If the accessor is not generic, we can use it as is.
72+
if (requirements.empty()) {
73+
return accessorFn;
74+
}
75+
76+
auto accessorFnTy = accessorFn->getType()->getPointerElementType();
77+
78+
// Otherwise, we need a thunk to unmarshal the generic environment from the
79+
// argument area. It'd be nice to have a good way to represent this
80+
// directly in SIL, of course...
81+
auto thunkType = llvm::FunctionType::get(
82+
IGM.VoidTy,
83+
{ /*sret or newValue*/ accessorFnTy->getFunctionParamType(0),
84+
/*base*/ accessorFnTy->getFunctionParamType(1),
85+
/*arg*/ IGM.Int8PtrTy },
86+
/*vararg*/ false);
87+
const char *thunkName;
88+
unsigned numArgsToForward = 2;
89+
switch (whichAccessor) {
90+
case Getter:
91+
thunkName = "keypath_get";
92+
break;
93+
case Setter:
94+
thunkName = "keypath_set";
95+
break;
96+
}
97+
98+
auto accessorThunk = llvm::Function::Create(thunkType,
99+
llvm::GlobalValue::PrivateLinkage, thunkName, IGM.getModule());
100+
accessorThunk->setAttributes(IGM.constructInitialAttributes());
101+
// Original accessor's args should be @in or @out, meaning they won't be
102+
// captured or aliased.
103+
accessorThunk->addAttribute(1, llvm::Attribute::NoCapture);
104+
accessorThunk->addAttribute(1, llvm::Attribute::NoAlias);
105+
accessorThunk->addAttribute(2, llvm::Attribute::NoCapture);
106+
accessorThunk->addAttribute(2, llvm::Attribute::NoAlias);
107+
// Getter's output is sret.
108+
if (whichAccessor == Getter)
109+
accessorThunk->addAttribute(1, llvm::Attribute::StructRet);
110+
accessorThunk->setCallingConv(IGM.SwiftCC);
111+
112+
{
113+
IRGenFunction IGF(IGM, accessorThunk);
114+
if (IGM.DebugInfo)
115+
IGM.DebugInfo->emitArtificialFunction(IGF, accessorThunk);
116+
117+
auto params = IGF.collectParameters();
118+
Explosion forwardedArgs;
119+
forwardedArgs.add(params.claim(numArgsToForward));
120+
121+
// The generic environment is marshaled into the beginning of the component
122+
// argument area inside the instance. Bind the generic information out of
123+
// the buffer, and advance past it.
124+
auto componentArgsBuf = params.claimNext();
125+
bindFromGenericRequirementsBuffer(IGF, requirements,
126+
Address(componentArgsBuf, IGM.getPointerAlignment()),
127+
[&](CanType t) {
128+
if (!genericEnv)
129+
return t;
130+
return genericEnv->mapTypeIntoContext(t)->getCanonicalType();
131+
});
132+
133+
/* TODO: If the underlying accessor wants index arguments, advance the
134+
* pointer past the generic requirements here to pass down. */
135+
136+
// Use the bound generic metadata to form a call to the original generic
137+
// accessor.
138+
WitnessMetadata witnessMetadata;
139+
auto forwardingSubs = genericEnv->getGenericSignature()->getSubstitutionMap(
140+
genericEnv->getForwardingSubstitutions());
141+
emitPolymorphicArguments(IGF, accessor->getLoweredFunctionType(),
142+
forwardingSubs,
143+
&witnessMetadata,
144+
forwardedArgs);
145+
auto call = IGF.Builder.CreateCall(accessorFn, forwardedArgs.claimAll());
146+
if (whichAccessor == Getter)
147+
call->addAttribute(1, llvm::Attribute::StructRet);
148+
149+
IGF.Builder.CreateRetVoid();
150+
}
151+
152+
return accessorThunk;
153+
}
154+
155+
static llvm::Constant *
156+
getLayoutFunctionForComputedComponent(IRGenModule &IGM,
157+
const KeyPathPatternComponent &component,
158+
GenericEnvironment *genericEnv,
159+
ArrayRef<GenericRequirement> requirements) {
160+
// Generate a function that returns the expected size and alignment necessary
161+
// to store captured generic context and subscript index arguments.
162+
auto retTy = llvm::StructType::get(IGM.getLLVMContext(),
163+
{IGM.SizeTy, IGM.SizeTy});
164+
auto fnTy = llvm::FunctionType::get(
165+
retTy, { IGM.Int8PtrTy }, /*vararg*/ false);
166+
167+
auto layoutFn = llvm::Function::Create(fnTy,
168+
llvm::GlobalValue::PrivateLinkage, "keypath_get_arg_layout", IGM.getModule());
169+
170+
{
171+
IRGenFunction IGF(IGM, layoutFn);
172+
// TODO: We would need to unmarshal generic arguments to be able to
173+
// compute the layout of dependent subscript indexes.
174+
(void)IGF.collectParameters().claimNext();
175+
176+
// Base size is one pointer for each generic requirement; base alignment
177+
// is pointer alignment.
178+
llvm::Value *size = llvm::ConstantInt::get(IGM.SizeTy,
179+
IGM.getPointerSize().getValue() * requirements.size());
180+
llvm::Value *alignMask = llvm::ConstantInt::get(IGM.SizeTy,
181+
IGM.getPointerAlignment().getValue() - 1);
182+
183+
// TODO: Combine layout of captured index values
184+
185+
llvm::Value *retValue = IGF.Builder.CreateInsertValue(
186+
llvm::UndefValue::get(retTy), size, 0);
187+
retValue = IGF.Builder.CreateInsertValue(
188+
retValue, alignMask, 1);
189+
190+
IGF.Builder.CreateRet(retValue);
191+
}
192+
193+
return layoutFn;
194+
}
195+
196+
static llvm::Constant *
197+
getWitnessTableForComputedComponent(IRGenModule &IGM,
198+
const KeyPathPatternComponent &component,
199+
GenericEnvironment *genericEnv,
200+
ArrayRef<GenericRequirement> requirements) {
201+
// If the only thing we're capturing is generic environment, then we can
202+
// use a prefab witness table from the runtime.
203+
// TODO: If there were subscript indexes, we'd need to generate something.
204+
if (auto existing =
205+
IGM.Module.getNamedGlobal("swift_keyPathGenericWitnessTable"))
206+
return existing;
207+
208+
auto linkInfo = LinkInfo::get(IGM, "swift_keyPathGenericWitnessTable",
209+
SILLinkage::PublicExternal,
210+
/*fragile*/ false,
211+
/*sil only*/ false,
212+
NotForDefinition,
213+
/*weak imported*/ false);
214+
215+
return createVariable(IGM, linkInfo,
216+
IGM.Int8PtrTy, IGM.getPointerAlignment());
217+
}
218+
219+
static llvm::Constant *
220+
getInitializerForComputedComponent(IRGenModule &IGM,
221+
const KeyPathPatternComponent &component,
222+
GenericEnvironment *genericEnv,
223+
ArrayRef<GenericRequirement> requirements) {
224+
auto fnTy = llvm::FunctionType::get(IGM.VoidTy,
225+
{ /*src*/ IGM.Int8PtrTy,
226+
/*dest*/ IGM.Int8PtrTy }, /*vararg*/ false);
227+
228+
auto initFn = llvm::Function::Create(fnTy,
229+
llvm::GlobalValue::PrivateLinkage, "keypath_arg_init", IGM.getModule());
230+
231+
{
232+
IRGenFunction IGF(IGM, initFn);
233+
auto params = IGF.collectParameters();
234+
auto src = params.claimNext();
235+
auto dest = params.claimNext();
236+
237+
// Transfer all of the requirements into the destination instance.
238+
IGF.Builder.CreateMemCpy(dest, src,
239+
IGM.getPointerSize().getValue() * requirements.size(),
240+
IGM.getPointerAlignment().getValue());
241+
242+
// TODO: Copy over subscript index values.
243+
244+
IGF.Builder.CreateRetVoid();
245+
}
246+
return initFn;
247+
}
248+
44249
llvm::Constant *
45250
IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern,
46251
SILLocation diagLoc) {
@@ -365,7 +570,8 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern,
365570
fields.add(idValue);
366571

367572
if (isInstantiableInPlace) {
368-
// No generic arguments, so we can invoke the getter/setter as is.
573+
// No generic arguments or indexes, so we can invoke the
574+
// getter/setter as is.
369575
fields.add(getAddrOfSILFunction(component.getComputedPropertyGetter(),
370576
NotForDefinition));
371577
if (settable)
@@ -375,10 +581,31 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern,
375581
// If there's generic context (TODO: or subscript indexes), embed as
376582
// arguments in the component. Thunk the SIL-level accessors to give the
377583
// runtime implementation a polymorphically-callable interface.
378-
Context.Diags.diagnose(diagLoc.getSourceLoc(),
379-
diag::not_implemented,
380-
"generic computed key paths");
381-
return llvm::UndefValue::get(Int8PtrTy);
584+
585+
// Push the accessors, possibly thunked to marshal generic environment.
586+
fields.add(getAccessorForComputedComponent(*this, component, Getter,
587+
genericEnv, requirements));
588+
if (settable)
589+
fields.add(getAccessorForComputedComponent(*this, component, Setter,
590+
genericEnv, requirements));
591+
592+
fields.add(getLayoutFunctionForComputedComponent(*this, component,
593+
genericEnv, requirements));
594+
595+
// Set up a "witness table" for the component that handles copying,
596+
// destroying, equating, and hashing the captured contents of the
597+
// component.
598+
// If there are only generic parameters, we can use a prefab witness
599+
// table from the runtime.
600+
// TODO: For subscripts we'd generate functions that dispatch out to
601+
// the copy/destroy/equals/hash functionality of the subscript indexes.
602+
fields.add(getWitnessTableForComputedComponent(*this, component,
603+
genericEnv, requirements));
604+
605+
// Add an initializer function that copies generic arguments out of the
606+
// pattern argument buffer into the instantiated object.
607+
fields.add(getInitializerForComputedComponent(*this, component,
608+
genericEnv, requirements));
382609
}
383610
break;
384611
}

lib/IRGen/GenObjC.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -210,25 +210,32 @@ llvm::Value *irgen::emitObjCAutoreleaseReturnValue(IRGenFunction &IGF,
210210
}
211211

212212
namespace {
213-
/// A type-info implementation suitable for an ObjC pointer type.
214-
class ObjCTypeInfo : public HeapTypeInfo<ObjCTypeInfo> {
213+
/// A type-info implementation suitable for Builtin.UnknownObject.
214+
class UnknownTypeInfo : public HeapTypeInfo<UnknownTypeInfo> {
215215
public:
216-
ObjCTypeInfo(llvm::PointerType *storageType, Size size,
216+
UnknownTypeInfo(llvm::PointerType *storageType, Size size,
217217
SpareBitVector spareBits, Alignment align)
218218
: HeapTypeInfo(storageType, size, spareBits, align) {
219219
}
220220

221221
/// Builtin.UnknownObject requires ObjC reference-counting.
222222
ReferenceCounting getReferenceCounting() const {
223-
return ReferenceCounting::ObjC;
223+
return ReferenceCounting::Unknown;
224224
}
225225
};
226226
} // end anonymous namespace
227227

228228
const LoadableTypeInfo *TypeConverter::convertBuiltinUnknownObject() {
229-
return new ObjCTypeInfo(IGM.ObjCPtrTy, IGM.getPointerSize(),
230-
IGM.getHeapObjectSpareBits(),
231-
IGM.getPointerAlignment());
229+
// UnknownObject is only interestingly different from NativeObject on
230+
// platforms with ObjC interop.
231+
if (IGM.Context.LangOpts.EnableObjCInterop) {
232+
return new UnknownTypeInfo(IGM.ObjCPtrTy, IGM.getPointerSize(),
233+
IGM.getHeapObjectSpareBits(),
234+
IGM.getPointerAlignment());
235+
}
236+
237+
// Without ObjC interop, UnknownObject handles just like a NativeObject.
238+
return convertBuiltinNativeObject();
232239
}
233240

234241
namespace {

0 commit comments

Comments
 (0)