Skip to content

Commit 3e5165d

Browse files
committed
Change the compiler ABI of keypaths.
Previously, the stdlib provided: - getters for AnyKeyPath and PartialKeyPath, which have remained; - a getter for KeyPath, which still exists alongside a new read coroutine; and - a pair of owned mutable addressors that provided modify-like behavior for WritableKeyPath and ReferenceWritableKeyPath, which have been replaced with modify coroutines and augmented with dedicated setters. SILGen then uses the most efficient accessor available for the access it's been asked to do: for example, if it's been asked to produce a borrowed r-value, it uses the read accessor. Providing a broad spectrum of accessor functions here seems acceptable because the code-size hit is fixed-size: we don't need to generate extra code per storage declaration to support more alternatives for key paths. Note that this is just the compiler ABI; the implementation is still basically what it was. That means the implementation of the setters and the read accessor is pretty far from optimal. But we can improve the implementation later; we can't improve the ABI. The coroutine accessors have to be implemented in C++ and used via hand-rolled declarations in SILGen because it's not currently possible to declare independent coroutine accessors in Swift.
1 parent 75e1df6 commit 3e5165d

File tree

13 files changed

+620
-362
lines changed

13 files changed

+620
-362
lines changed

include/swift/AST/KnownDecls.def

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,15 @@ FUNC_DECL(GetErrorEmbeddedNSError, "_getErrorEmbeddedNSError")
7171

7272
FUNC_DECL(UnsafeBitCast, "unsafeBitCast")
7373

74-
FUNC_DECL(ProjectKeyPathAny, "_projectKeyPathAny")
75-
FUNC_DECL(ProjectKeyPathPartial, "_projectKeyPathPartial")
76-
FUNC_DECL(ProjectKeyPathReadOnly, "_projectKeyPathReadOnly")
77-
FUNC_DECL(ProjectKeyPathWritable, "_projectKeyPathWritable")
78-
FUNC_DECL(ProjectKeyPathReferenceWritable, "_projectKeyPathReferenceWritable")
74+
FUNC_DECL(GetAtKeyPath, "_getAtKeyPath")
75+
FUNC_DECL(GetAtAnyKeyPath, "_getAtAnyKeyPath")
76+
FUNC_DECL(GetAtPartialKeyPath, "_getAtPartialKeyPath")
77+
FUNC_DECL(SetAtWritableKeyPath, "_setAtWritableKeyPath")
78+
FUNC_DECL(SetAtReferenceWritableKeyPath, "_setAtReferenceWritableKeyPath")
79+
// These don't actually have AST nodes associated with them right now.
80+
FUNC_DECL(ReadAtKeyPath, "_readAtKeyPath")
81+
FUNC_DECL(ModifyAtWritableKeyPath, "_modifyAtWritableKeyPath")
82+
FUNC_DECL(ModifyAtReferenceWritableKeyPath, "_modifyAtReferenceWritableKeyPath")
7983

8084
FUNC_DECL(Swap, "swap")
8185

include/swift/Runtime/Metadata.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,37 @@
2222

2323
namespace swift {
2424

25+
#pragma clang diagnostic push
26+
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
27+
28+
/// The buffer used by a yield-once coroutine (such as the generalized
29+
/// accessors `read` and `modify`).
30+
struct YieldOnceBuffer {
31+
void *Data[NumWords_YieldOnceBuffer];
32+
};
33+
using YieldOnceContinuation =
34+
SWIFT_CC(swift) void (YieldOnceBuffer *buffer, bool forUnwind);
35+
36+
/// The return type of a call to a yield-once coroutine. The function
37+
/// must be declared with the swiftcall calling convention.
38+
template <class ResultTy>
39+
struct YieldOnceResult {
40+
YieldOnceContinuation *Continuation;
41+
ResultTy YieldValue;
42+
};
43+
44+
template <class FnTy>
45+
struct YieldOnceCoroutine;
46+
47+
/// A template which generates the type of the ramp function of a
48+
/// yield-once coroutine.
49+
template <class ResultTy, class... ArgTys>
50+
struct YieldOnceCoroutine<ResultTy(ArgTys...)> {
51+
using type =
52+
SWIFT_CC(swift) YieldOnceResult<ResultTy> (YieldOnceBuffer *buffer,
53+
ArgTys...);
54+
};
55+
2556
#if SWIFT_OBJC_INTEROP
2657

2758
// Const cast shorthands for ObjC types.
@@ -806,12 +837,36 @@ const TypeContextDescriptor *swift_getTypeContextDescriptor(const Metadata *type
806837
SWIFT_RUNTIME_EXPORT
807838
const HeapObject *swift_getKeyPath(const void *pattern, const void *arguments);
808839

840+
/// Given a pointer to a borrowed value of type `Root` and a
841+
/// `KeyPath<Root, Value>`, project a pointer to a borrowed value of type
842+
/// `Value`.
843+
SWIFT_RUNTIME_EXPORT
844+
YieldOnceCoroutine<const OpaqueValue* (const OpaqueValue *root,
845+
void *keyPath)>::type
846+
swift_readAtKeyPath;
847+
848+
/// Given a pointer to a mutable value of type `Root` and a
849+
/// `WritableKeyPath<Root, Value>`, project a pointer to a mutable value
850+
/// of type `Value`.
851+
SWIFT_RUNTIME_EXPORT
852+
YieldOnceCoroutine<OpaqueValue* (OpaqueValue *root, void *keyPath)>::type
853+
swift_modifyAtWritableKeyPath;
854+
855+
/// Given a pointer to a borrowed value of type `Root` and a
856+
/// `ReferenceWritableKeyPath<Root, Value>`, project a pointer to a
857+
/// mutable value of type `Value`.
858+
SWIFT_RUNTIME_EXPORT
859+
YieldOnceCoroutine<OpaqueValue* (const OpaqueValue *root, void *keyPath)>::type
860+
swift_modifyAtReferenceWritableKeyPath;
861+
809862
SWIFT_RUNTIME_EXPORT
810863
void swift_enableDynamicReplacementScope(const DynamicReplacementScope *scope);
811864

812865
SWIFT_RUNTIME_EXPORT
813866
void swift_disableDynamicReplacementScope(const DynamicReplacementScope *scope);
814867

868+
#pragma clang diagnostic pop
869+
815870
} // end namespace swift
816871

817872
#endif // SWIFT_RUNTIME_METADATA_H

lib/SILGen/SILGen.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,93 @@ ProtocolConformance *SILGenModule::getNSErrorConformanceToError() {
381381
return *NSErrorConformanceToError;
382382
}
383383

384+
SILFunction *
385+
SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess,
386+
KeyPathTypeKind typeKind) {
387+
bool isBaseInout;
388+
bool isResultInout;
389+
StringRef functionName;
390+
NominalTypeDecl *keyPathDecl;
391+
if (isReadAccess) {
392+
assert(typeKind == KPTK_KeyPath ||
393+
typeKind == KPTK_WritableKeyPath ||
394+
typeKind == KPTK_ReferenceWritableKeyPath);
395+
functionName = "swift_readAtKeyPath";
396+
isBaseInout = false;
397+
isResultInout = false;
398+
keyPathDecl = getASTContext().getKeyPathDecl();
399+
} else if (typeKind == KPTK_WritableKeyPath) {
400+
functionName = "swift_modifyAtWritableKeyPath";
401+
isBaseInout = true;
402+
isResultInout = true;
403+
keyPathDecl = getASTContext().getWritableKeyPathDecl();
404+
} else if (typeKind == KPTK_ReferenceWritableKeyPath) {
405+
functionName = "swift_modifyAtReferenceWritableKeyPath";
406+
isBaseInout = false;
407+
isResultInout = true;
408+
keyPathDecl = getASTContext().getReferenceWritableKeyPathDecl();
409+
} else {
410+
llvm_unreachable("bad combination");
411+
}
412+
413+
auto fn = M.lookUpFunction(functionName);
414+
if (fn) return fn;
415+
416+
auto rootType = CanGenericTypeParamType::get(0, 0, getASTContext());
417+
auto valueType = CanGenericTypeParamType::get(0, 1, getASTContext());
418+
419+
// Build the generic signature <A, B>.
420+
auto sig = GenericSignature::get({rootType, valueType}, {});
421+
422+
auto keyPathTy = BoundGenericType::get(keyPathDecl, Type(),
423+
{ rootType, valueType })
424+
->getCanonicalType();
425+
426+
// (@in_guaranteed/@inout Root, @guaranteed KeyPath<Root, Value>)
427+
SILParameterInfo params[] = {
428+
{ rootType,
429+
isBaseInout ? ParameterConvention::Indirect_Inout
430+
: ParameterConvention::Indirect_In_Guaranteed },
431+
{ keyPathTy, ParameterConvention::Direct_Guaranteed },
432+
};
433+
434+
// -> @yields @in_guaranteed/@inout Value
435+
SILYieldInfo yields[] = {
436+
{ valueType,
437+
isResultInout ? ParameterConvention::Indirect_Inout
438+
: ParameterConvention::Indirect_In_Guaranteed },
439+
};
440+
441+
auto extInfo =
442+
SILFunctionType::ExtInfo(SILFunctionTypeRepresentation::Thin,
443+
/*pseudogeneric*/false,
444+
/*non-escaping*/false);
445+
446+
auto functionTy = SILFunctionType::get(sig, extInfo,
447+
SILCoroutineKind::YieldOnce,
448+
ParameterConvention::Direct_Unowned,
449+
params,
450+
yields,
451+
/*results*/ {},
452+
/*error result*/ {},
453+
getASTContext());
454+
455+
auto env = sig->createGenericEnvironment();
456+
457+
SILGenFunctionBuilder builder(*this);
458+
fn = builder.createFunction(SILLinkage::PublicExternal,
459+
functionName,
460+
functionTy,
461+
env,
462+
/*location*/ None,
463+
IsNotBare,
464+
IsNotTransparent,
465+
IsNotSerialized,
466+
IsNotDynamic);
467+
468+
return fn;
469+
}
470+
384471

385472
SILFunction *SILGenModule::emitTopLevelFunction(SILLocation Loc) {
386473
ASTContext &C = M.getASTContext();

lib/SILGen/SILGen.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
407407
/// Retrieve the conformance of NSError to the Error protocol.
408408
ProtocolConformance *getNSErrorConformanceToError();
409409

410+
SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess,
411+
KeyPathTypeKind typeKind);
412+
410413
/// Report a diagnostic.
411414
template<typename...T, typename...U>
412415
InFlightDiagnostic diagnose(SourceLoc loc, Diag<T...> diag,

0 commit comments

Comments
 (0)