Skip to content

Handle generic computed properties in key paths. #10741

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,14 @@ class LinkInfo {

static LinkInfo get(IRGenModule &IGM, const LinkEntity &entity,
ForDefinition_t forDefinition);

static LinkInfo get(const UniversalLinkageInfo &linkInfo,
StringRef name,
SILLinkage linkage,
bool isFragile,
bool isSILOnly,
ForDefinition_t isDefinition,
bool isWeakImported);

StringRef getName() const {
return Name.str();
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -4578,7 +4578,7 @@ class SuperMethodInst

SuperMethodInst(SILDebugLocation DebugLoc, SILValue Operand,
SILDeclRef Member, SILType Ty, bool Volatile = false)
: UnaryInstructionBase(DebugLoc, Operand, Ty, Member, Volatile) {}
: UnaryInstructionBase(DebugLoc, Operand, Ty, Member, Volatile) {}
};

/// WitnessMethodInst - Given a type, a protocol conformance,
Expand Down
17 changes: 17 additions & 0 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1512,6 +1512,23 @@ LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo,
return result;
}

LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo,
StringRef name,
SILLinkage linkage,
bool isFragile,
bool isSILOnly,
ForDefinition_t isDefinition,
bool isWeakImported) {
LinkInfo result;

result.Name += name;
std::tie(result.Linkage, result.Visibility, result.DLLStorageClass) =
getIRLinkage(linkInfo, linkage, isFragile, isSILOnly,
isDefinition, isWeakImported);
result.ForDefinition = isDefinition;
return result;
}

static bool isPointerTo(llvm::Type *ptrTy, llvm::Type *objTy) {
return cast<llvm::PointerType>(ptrTy)->getElementType() == objTy;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/IRGen/GenFunc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM,
SubstitutionMap subMap;
if (auto genericSig = origType->getGenericSignature())
subMap = genericSig->getSubstitutionMap(subs);
emitPolymorphicArguments(subIGF, origType, substType, subMap,
emitPolymorphicArguments(subIGF, origType, subMap,
&witnessMetadata, polyArgs);
}

Expand Down Expand Up @@ -1139,7 +1139,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM,
SubstitutionMap subMap;
if (auto genericSig = origType->getGenericSignature())
subMap = genericSig->getSubstitutionMap(subs);
emitPolymorphicArguments(subIGF, origType, substType, subMap,
emitPolymorphicArguments(subIGF, origType, subMap,
&witnessMetadata, polyArgs);
}
}
Expand Down
237 changes: 232 additions & 5 deletions lib/IRGen/GenKeyPath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
#include "ConstantBuilder.h"
#include "Explosion.h"
#include "GenClass.h"
#include "GenDecl.h"
#include "GenMeta.h"
#include "GenProto.h"
#include "GenStruct.h"
#include "GenericRequirement.h"
#include "IRGenDebugInfo.h"
Expand All @@ -27,6 +29,7 @@
#include "ProtocolInfo.h"
#include "StructLayout.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/IR/Module.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILLocation.h"
#include "swift/SIL/TypeLowering.h"
Expand All @@ -37,10 +40,212 @@
#include "swift/AST/DiagnosticsIRGen.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Types.h"
#include "swift/IRGen/Linking.h"

using namespace swift;
using namespace irgen;

enum GetterOrSetter {
Getter,
Setter,
};

static llvm::Function *
getAccessorForComputedComponent(IRGenModule &IGM,
const KeyPathPatternComponent &component,
GetterOrSetter whichAccessor,
GenericEnvironment *genericEnv,
ArrayRef<GenericRequirement> requirements) {
SILFunction *accessor;
switch (whichAccessor) {
case Getter:
accessor = component.getComputedPropertyGetter();
break;
case Setter:
accessor = component.getComputedPropertySetter();
break;
}

auto accessorFn = IGM.getAddrOfSILFunction(accessor, NotForDefinition);

// If the accessor is not generic, we can use it as is.
if (requirements.empty()) {
return accessorFn;
}

auto accessorFnTy = accessorFn->getType()->getPointerElementType();

// Otherwise, we need a thunk to unmarshal the generic environment from the
// argument area. It'd be nice to have a good way to represent this
// directly in SIL, of course...
auto thunkType = llvm::FunctionType::get(
IGM.VoidTy,
{ /*sret or newValue*/ accessorFnTy->getFunctionParamType(0),
/*base*/ accessorFnTy->getFunctionParamType(1),
/*arg*/ IGM.Int8PtrTy },
/*vararg*/ false);
const char *thunkName;
unsigned numArgsToForward = 2;
switch (whichAccessor) {
case Getter:
thunkName = "keypath_get";
break;
case Setter:
thunkName = "keypath_set";
break;
}

auto accessorThunk = llvm::Function::Create(thunkType,
llvm::GlobalValue::PrivateLinkage, thunkName, IGM.getModule());
accessorThunk->setAttributes(IGM.constructInitialAttributes());
// Original accessor's args should be @in or @out, meaning they won't be
// captured or aliased.
accessorThunk->addAttribute(1, llvm::Attribute::NoCapture);
accessorThunk->addAttribute(1, llvm::Attribute::NoAlias);
accessorThunk->addAttribute(2, llvm::Attribute::NoCapture);
accessorThunk->addAttribute(2, llvm::Attribute::NoAlias);
// Getter's output is sret.
if (whichAccessor == Getter)
accessorThunk->addAttribute(1, llvm::Attribute::StructRet);
accessorThunk->setCallingConv(IGM.SwiftCC);

{
IRGenFunction IGF(IGM, accessorThunk);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(IGF, accessorThunk);

auto params = IGF.collectParameters();
Explosion forwardedArgs;
forwardedArgs.add(params.claim(numArgsToForward));

// The generic environment is marshaled into the beginning of the component
// argument area inside the instance. Bind the generic information out of
// the buffer, and advance past it.
auto componentArgsBuf = params.claimNext();
bindFromGenericRequirementsBuffer(IGF, requirements,
Address(componentArgsBuf, IGM.getPointerAlignment()),
[&](CanType t) {
if (!genericEnv)
return t;
return genericEnv->mapTypeIntoContext(t)->getCanonicalType();
});

/* TODO: If the underlying accessor wants index arguments, advance the
* pointer past the generic requirements here to pass down. */

// Use the bound generic metadata to form a call to the original generic
// accessor.
WitnessMetadata witnessMetadata;
auto forwardingSubs = genericEnv->getGenericSignature()->getSubstitutionMap(
genericEnv->getForwardingSubstitutions());
emitPolymorphicArguments(IGF, accessor->getLoweredFunctionType(),
forwardingSubs,
&witnessMetadata,
forwardedArgs);
auto call = IGF.Builder.CreateCall(accessorFn, forwardedArgs.claimAll());
if (whichAccessor == Getter)
call->addAttribute(1, llvm::Attribute::StructRet);

IGF.Builder.CreateRetVoid();
}

return accessorThunk;
}

static llvm::Constant *
getLayoutFunctionForComputedComponent(IRGenModule &IGM,
const KeyPathPatternComponent &component,
GenericEnvironment *genericEnv,
ArrayRef<GenericRequirement> requirements) {
// Generate a function that returns the expected size and alignment necessary
// to store captured generic context and subscript index arguments.
auto retTy = llvm::StructType::get(IGM.getLLVMContext(),
{IGM.SizeTy, IGM.SizeTy});
auto fnTy = llvm::FunctionType::get(
retTy, { IGM.Int8PtrTy }, /*vararg*/ false);

auto layoutFn = llvm::Function::Create(fnTy,
llvm::GlobalValue::PrivateLinkage, "keypath_get_arg_layout", IGM.getModule());

{
IRGenFunction IGF(IGM, layoutFn);
// TODO: We would need to unmarshal generic arguments to be able to
// compute the layout of dependent subscript indexes.
(void)IGF.collectParameters().claimNext();

// Base size is one pointer for each generic requirement; base alignment
// is pointer alignment.
llvm::Value *size = llvm::ConstantInt::get(IGM.SizeTy,
IGM.getPointerSize().getValue() * requirements.size());
llvm::Value *alignMask = llvm::ConstantInt::get(IGM.SizeTy,
IGM.getPointerAlignment().getValue() - 1);

// TODO: Combine layout of captured index values

llvm::Value *retValue = IGF.Builder.CreateInsertValue(
llvm::UndefValue::get(retTy), size, 0);
retValue = IGF.Builder.CreateInsertValue(
retValue, alignMask, 1);

IGF.Builder.CreateRet(retValue);
}

return layoutFn;
}

static llvm::Constant *
getWitnessTableForComputedComponent(IRGenModule &IGM,
const KeyPathPatternComponent &component,
GenericEnvironment *genericEnv,
ArrayRef<GenericRequirement> requirements) {
// If the only thing we're capturing is generic environment, then we can
// use a prefab witness table from the runtime.
// TODO: If there were subscript indexes, we'd need to generate something.
if (auto existing =
IGM.Module.getNamedGlobal("swift_keyPathGenericWitnessTable"))
return existing;

auto linkInfo = LinkInfo::get(IGM, "swift_keyPathGenericWitnessTable",
SILLinkage::PublicExternal,
/*fragile*/ false,
/*sil only*/ false,
NotForDefinition,
/*weak imported*/ false);

return createVariable(IGM, linkInfo,
IGM.Int8PtrTy, IGM.getPointerAlignment());
}

static llvm::Constant *
getInitializerForComputedComponent(IRGenModule &IGM,
const KeyPathPatternComponent &component,
GenericEnvironment *genericEnv,
ArrayRef<GenericRequirement> requirements) {
auto fnTy = llvm::FunctionType::get(IGM.VoidTy,
{ /*src*/ IGM.Int8PtrTy,
/*dest*/ IGM.Int8PtrTy }, /*vararg*/ false);

auto initFn = llvm::Function::Create(fnTy,
llvm::GlobalValue::PrivateLinkage, "keypath_arg_init", IGM.getModule());

{
IRGenFunction IGF(IGM, initFn);
auto params = IGF.collectParameters();
auto src = params.claimNext();
auto dest = params.claimNext();

// Transfer all of the requirements into the destination instance.
IGF.Builder.CreateMemCpy(dest, src,
IGM.getPointerSize().getValue() * requirements.size(),
IGM.getPointerAlignment().getValue());

// TODO: Copy over subscript index values.

IGF.Builder.CreateRetVoid();
}
return initFn;
}

llvm::Constant *
IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern,
SILLocation diagLoc) {
Expand Down Expand Up @@ -365,7 +570,8 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern,
fields.add(idValue);

if (isInstantiableInPlace) {
// No generic arguments, so we can invoke the getter/setter as is.
// No generic arguments or indexes, so we can invoke the
// getter/setter as is.
fields.add(getAddrOfSILFunction(component.getComputedPropertyGetter(),
NotForDefinition));
if (settable)
Expand All @@ -375,10 +581,31 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern,
// If there's generic context (TODO: or subscript indexes), embed as
// arguments in the component. Thunk the SIL-level accessors to give the
// runtime implementation a polymorphically-callable interface.
Context.Diags.diagnose(diagLoc.getSourceLoc(),
diag::not_implemented,
"generic computed key paths");
return llvm::UndefValue::get(Int8PtrTy);

// Push the accessors, possibly thunked to marshal generic environment.
fields.add(getAccessorForComputedComponent(*this, component, Getter,
genericEnv, requirements));
if (settable)
fields.add(getAccessorForComputedComponent(*this, component, Setter,
genericEnv, requirements));

fields.add(getLayoutFunctionForComputedComponent(*this, component,
genericEnv, requirements));

// Set up a "witness table" for the component that handles copying,
// destroying, equating, and hashing the captured contents of the
// component.
// If there are only generic parameters, we can use a prefab witness
// table from the runtime.
// TODO: For subscripts we'd generate functions that dispatch out to
// the copy/destroy/equals/hash functionality of the subscript indexes.
fields.add(getWitnessTableForComputedComponent(*this, component,
genericEnv, requirements));

// Add an initializer function that copies generic arguments out of the
// pattern argument buffer into the instantiated object.
fields.add(getInitializerForComputedComponent(*this, component,
genericEnv, requirements));
}
break;
}
Expand Down
21 changes: 14 additions & 7 deletions lib/IRGen/GenObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,25 +210,32 @@ llvm::Value *irgen::emitObjCAutoreleaseReturnValue(IRGenFunction &IGF,
}

namespace {
/// A type-info implementation suitable for an ObjC pointer type.
class ObjCTypeInfo : public HeapTypeInfo<ObjCTypeInfo> {
/// A type-info implementation suitable for Builtin.UnknownObject.
class UnknownTypeInfo : public HeapTypeInfo<UnknownTypeInfo> {
public:
ObjCTypeInfo(llvm::PointerType *storageType, Size size,
UnknownTypeInfo(llvm::PointerType *storageType, Size size,
SpareBitVector spareBits, Alignment align)
: HeapTypeInfo(storageType, size, spareBits, align) {
}

/// Builtin.UnknownObject requires ObjC reference-counting.
ReferenceCounting getReferenceCounting() const {
return ReferenceCounting::ObjC;
return ReferenceCounting::Unknown;
}
};
} // end anonymous namespace

const LoadableTypeInfo *TypeConverter::convertBuiltinUnknownObject() {
return new ObjCTypeInfo(IGM.ObjCPtrTy, IGM.getPointerSize(),
IGM.getHeapObjectSpareBits(),
IGM.getPointerAlignment());
// UnknownObject is only interestingly different from NativeObject on
// platforms with ObjC interop.
if (IGM.Context.LangOpts.EnableObjCInterop) {
return new UnknownTypeInfo(IGM.ObjCPtrTy, IGM.getPointerSize(),
IGM.getHeapObjectSpareBits(),
IGM.getPointerAlignment());
}

// Without ObjC interop, UnknownObject handles just like a NativeObject.
return convertBuiltinNativeObject();
}

namespace {
Expand Down
Loading