Skip to content

Commit 101788d

Browse files
committed
IRGen: Support for computed properties with dependent generic context.
Use the KeyPath implementation's new support for instantiating and dealing with captures to lower the generic context required to dispatch computed accessors with dependent generics.
1 parent c142e7d commit 101788d

File tree

14 files changed

+329
-20
lines changed

14 files changed

+329
-20
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/GenProto.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,7 +2424,7 @@ namespace {
24242424
CanSILFunctionType polyFn)
24252425
: PolymorphicConvention(IGF.IGM, polyFn), IGF(IGF) {}
24262426

2427-
void emit(CanSILFunctionType substFnType, const SubstitutionMap &subs,
2427+
void emit(const SubstitutionMap &subs,
24282428
WitnessMetadata *witnessMetadata, Explosion &out);
24292429

24302430
private:
@@ -2457,16 +2457,13 @@ namespace {
24572457
/// Pass all the arguments necessary for the given function.
24582458
void irgen::emitPolymorphicArguments(IRGenFunction &IGF,
24592459
CanSILFunctionType origFnType,
2460-
CanSILFunctionType substFnType,
24612460
const SubstitutionMap &subs,
24622461
WitnessMetadata *witnessMetadata,
24632462
Explosion &out) {
2464-
EmitPolymorphicArguments(IGF, origFnType).emit(substFnType, subs,
2465-
witnessMetadata, out);
2463+
EmitPolymorphicArguments(IGF, origFnType).emit(subs, witnessMetadata, out);
24662464
}
24672465

2468-
void EmitPolymorphicArguments::emit(CanSILFunctionType substFnType,
2469-
const SubstitutionMap &subs,
2466+
void EmitPolymorphicArguments::emit(const SubstitutionMap &subs,
24702467
WitnessMetadata *witnessMetadata,
24712468
Explosion &out) {
24722469
// Add all the early sources.

lib/IRGen/GenProto.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ namespace irgen {
117117
/// generics clause.
118118
void emitPolymorphicArguments(IRGenFunction &IGF,
119119
CanSILFunctionType origType,
120-
CanSILFunctionType substType,
121120
const SubstitutionMap &subs,
122121
WitnessMetadata *witnessMetadata,
123122
Explosion &args);

lib/IRGen/IRGenModule.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ static bool isReturnAttribute(llvm::Attribute::AttrKind Attr) {
427427

428428
llvm::Constant *swift::getRuntimeFn(llvm::Module &Module,
429429
llvm::Constant *&cache,
430-
char const *name,
430+
const char *name,
431431
llvm::CallingConv::ID cc,
432432
llvm::ArrayRef<llvm::Type*> retTypes,
433433
llvm::ArrayRef<llvm::Type*> argTypes,

lib/IRGen/IRGenSIL.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2168,7 +2168,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) {
21682168
SubstitutionMap subMap;
21692169
if (auto genericSig = origCalleeType->getGenericSignature())
21702170
subMap = genericSig->getSubstitutionMap(site.getSubstitutions());
2171-
emitPolymorphicArguments(*this, origCalleeType, substCalleeType,
2171+
emitPolymorphicArguments(*this, origCalleeType,
21722172
subMap, &witnessMetadata, llArgs);
21732173
}
21742174

0 commit comments

Comments
 (0)