Skip to content

Commit a045d66

Browse files
authored
Merge pull request #75282 from jckarter/addressable-params-1
[WIP] Prototype an `@_addressable` attribute that puts an argument at a stable address.
2 parents 70ce8c6 + 3c0b08d commit a045d66

18 files changed

+227
-37
lines changed

include/swift/AST/Decl.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6817,6 +6817,9 @@ class ParamDecl : public VarDecl {
68176817

68186818
/// Whether or not this parameter is 'isolated'.
68196819
IsIsolated = 1 << 2,
6820+
6821+
/// Whether this parameter is `@_addressable`.
6822+
IsAddressable = 1 << 3,
68206823

68216824
/// Whether or not this parameter is 'sending'.
68226825
IsSending = 1 << 4,
@@ -7107,6 +7110,18 @@ class ParamDecl : public VarDecl {
71077110
removeFlag(Flag::IsSending);
71087111
}
71097112

7113+
/// Whether or not this parameter is marked with '@_addressable'.
7114+
bool isAddressable() const {
7115+
return getOptions().contains(Flag::IsAddressable);
7116+
}
7117+
7118+
void setAddressable(bool value = true) {
7119+
if (value)
7120+
addFlag(Flag::IsAddressable);
7121+
else
7122+
removeFlag(Flag::IsAddressable);
7123+
}
7124+
71107125
/// Whether or not this parameter is marked with '_const'.
71117126
bool isCompileTimeConst() const {
71127127
return ArgumentNameAndFlags.getInt().contains(

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,10 @@ ERROR(attr_implementation_category_goes_on_objc_attr,none,
18341834
"'@implementation'",
18351835
())
18361836

1837+
ERROR(attr_not_allowed_in_c_functions,none,
1838+
"attribute %0 is not allowed in C function conventions",
1839+
(StringRef))
1840+
18371841
WARNING(objc_implementation_will_become_objc,none,
18381842
"%kind0 will become implicitly '@objc' after adopting '@objc "
18391843
"@implementation'; add '%select{@nonobjc|final}1' to keep the current "

include/swift/AST/TypeAttr.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ SIMPLE_TYPE_ATTR(_local, Local)
6464
SIMPLE_TYPE_ATTR(_noMetadata, NoMetadata)
6565
TYPE_ATTR(_opaqueReturnTypeOf, OpaqueReturnTypeOf)
6666
TYPE_ATTR(isolated, Isolated)
67+
SIMPLE_TYPE_ATTR(_addressable, Addressable)
6768

6869
// SIL-specific attributes
6970
SIMPLE_SIL_TYPE_ATTR(async, Async)

include/swift/AST/Types.h

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,7 +2388,8 @@ class ParameterTypeFlags {
23882388
Isolated = 1 << 7,
23892389
CompileTimeConst = 1 << 8,
23902390
Sending = 1 << 9,
2391-
NumBits = 10
2391+
Addressable = 1 << 10,
2392+
NumBits = 11
23922393
};
23932394
OptionSet<ParameterFlags> value;
23942395
static_assert(NumBits <= 8*sizeof(OptionSet<ParameterFlags>), "overflowed");
@@ -2403,20 +2404,21 @@ class ParameterTypeFlags {
24032404

24042405
ParameterTypeFlags(bool variadic, bool autoclosure, bool nonEphemeral,
24052406
ParamSpecifier specifier, bool isolated, bool noDerivative,
2406-
bool compileTimeConst, bool isSending)
2407+
bool compileTimeConst, bool isSending, bool isAddressable)
24072408
: value((variadic ? Variadic : 0) | (autoclosure ? AutoClosure : 0) |
24082409
(nonEphemeral ? NonEphemeral : 0) |
24092410
uint8_t(specifier) << SpecifierShift | (isolated ? Isolated : 0) |
24102411
(noDerivative ? NoDerivative : 0) |
24112412
(compileTimeConst ? CompileTimeConst : 0) |
2412-
(isSending ? Sending : 0)) {}
2413+
(isSending ? Sending : 0) |
2414+
(isAddressable ? Addressable : 0)) {}
24132415

24142416
/// Create one from what's present in the parameter type
24152417
inline static ParameterTypeFlags
24162418
fromParameterType(Type paramTy, bool isVariadic, bool isAutoClosure,
24172419
bool isNonEphemeral, ParamSpecifier ownership,
24182420
bool isolated, bool isNoDerivative, bool compileTimeConst,
2419-
bool isSending);
2421+
bool isSending, bool isAddressable);
24202422

24212423
bool isNone() const { return !value; }
24222424
bool isVariadic() const { return value.contains(Variadic); }
@@ -2429,6 +2431,7 @@ class ParameterTypeFlags {
24292431
bool isCompileTimeConst() const { return value.contains(CompileTimeConst); }
24302432
bool isNoDerivative() const { return value.contains(NoDerivative); }
24312433
bool isSending() const { return value.contains(Sending); }
2434+
bool isAddressable() const { return value.contains(Addressable); }
24322435

24332436
/// Get the spelling of the parameter specifier used on the parameter.
24342437
ParamSpecifier getOwnershipSpecifier() const {
@@ -2497,6 +2500,12 @@ class ParameterTypeFlags {
24972500
: value - ParameterTypeFlags::Sending);
24982501
}
24992502

2503+
ParameterTypeFlags withAddressable(bool withAddressable) const {
2504+
return ParameterTypeFlags(withAddressable
2505+
? value | ParameterTypeFlags::Addressable
2506+
: value - ParameterTypeFlags::Addressable);
2507+
}
2508+
25002509
bool operator ==(const ParameterTypeFlags &other) const {
25012510
return value.toRaw() == other.value.toRaw();
25022511
}
@@ -2590,7 +2599,8 @@ class YieldTypeFlags {
25902599
/*nonEphemeral*/ false, getOwnershipSpecifier(),
25912600
/*isolated*/ false, /*noDerivative*/ false,
25922601
/*compileTimeConst*/ false,
2593-
/*is sending*/ false);
2602+
/*is sending*/ false,
2603+
/*is addressable*/ false);
25942604
}
25952605

25962606
bool operator ==(const YieldTypeFlags &other) const {
@@ -3386,6 +3396,8 @@ class AnyFunctionType : public TypeBase {
33863396

33873397
/// Whether the parameter is marked '@noDerivative'.
33883398
bool isNoDerivative() const { return Flags.isNoDerivative(); }
3399+
3400+
bool isAddressable() const { return Flags.isAddressable(); }
33893401

33903402
/// Whether the parameter might be a semantic result for autodiff purposes.
33913403
/// This includes inout parameters.
@@ -8092,7 +8104,7 @@ inline TupleTypeElt TupleTypeElt::getWithType(Type T) const {
80928104
inline ParameterTypeFlags ParameterTypeFlags::fromParameterType(
80938105
Type paramTy, bool isVariadic, bool isAutoClosure, bool isNonEphemeral,
80948106
ParamSpecifier ownership, bool isolated, bool isNoDerivative,
8095-
bool compileTimeConst, bool isSending) {
8107+
bool compileTimeConst, bool isSending, bool isAddressable) {
80968108
// FIXME(Remove InOut): The last caller that needs this is argument
80978109
// decomposition. Start by enabling the assertion there and fixing up those
80988110
// callers, then remove this, then remove
@@ -8103,7 +8115,8 @@ inline ParameterTypeFlags ParameterTypeFlags::fromParameterType(
81038115
ownership = ParamSpecifier::InOut;
81048116
}
81058117
return {isVariadic, isAutoClosure, isNonEphemeral, ownership,
8106-
isolated, isNoDerivative, compileTimeConst, isSending};
8118+
isolated, isNoDerivative, compileTimeConst, isSending,
8119+
isAddressable};
81078120
}
81088121

81098122
inline const Type *BoundGenericType::getTrailingObjectsPointer() const {

include/swift/Basic/Features.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,8 @@ EXPERIMENTAL_FEATURE(CoroutineAccessorsAllocateInCallee, false)
430430
// When a parameter has unspecified isolation, infer it as main actor isolated.
431431
EXPERIMENTAL_FEATURE(GenerateForceToMainActorThunks, false)
432432

433+
EXPERIMENTAL_FEATURE(AddressableParameters, true)
434+
433435
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
434436
#undef EXPERIMENTAL_FEATURE
435437
#undef UPCOMING_FEATURE

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8576,7 +8576,7 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const {
85768576
auto flags = ParameterTypeFlags::fromParameterType(
85778577
type, isVariadic(), isAutoClosure(), isNonEphemeral(), getSpecifier(),
85788578
isIsolated(), /*isNoDerivative*/ false, isCompileTimeConst(),
8579-
isSending());
8579+
isSending(), isAddressable());
85808580
return AnyFunctionType::Param(type, label, flags, internalLabel);
85818581
}
85828582

lib/AST/FeatureSet.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,20 @@ static bool usesFeatureIsolatedAny(Decl *decl) {
290290
});
291291
}
292292

293+
static bool usesFeatureAddressableParameters(Decl *d) {
294+
auto fd = dyn_cast<AbstractFunctionDecl>(d);
295+
if (!fd) {
296+
return false;
297+
}
298+
299+
for (auto pd : *fd->getParameters()) {
300+
if (pd->isAddressable()) {
301+
return true;
302+
}
303+
}
304+
return false;
305+
}
306+
293307
UNINTERESTING_FEATURE(IsolatedAny2)
294308
UNINTERESTING_FEATURE(GlobalActorIsolatedTypesUsability)
295309
UNINTERESTING_FEATURE(ObjCImplementation)

lib/ASTGen/Sources/ASTGen/TypeAttrs.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ extension ASTGenVisitor {
5151
switch attrKind {
5252
// Simple type attributes.
5353
case .autoclosure,
54+
.addressable,
5455
.escaping,
5556
.noEscape,
5657
.noDerivative,

lib/Parse/ParsePattern.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,8 @@ mapParsedParameters(Parser &parser,
631631
// or typealias with underlying function type.
632632
if (ATR->has(TypeAttrKind::Autoclosure))
633633
param->setAutoClosure(true);
634+
if (ATR->has(TypeAttrKind::Addressable))
635+
param->setAddressable(true);
634636

635637
unwrappedType = ATR->getTypeRepr();
636638
continue;

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,12 @@ class DestructureInputs {
17201720
ParameterTypeFlags origFlags) {
17211721
assert(!isa<InOutType>(substType));
17221722

1723+
// If the parameter is marked addressable, lower it with maximal
1724+
// abstraction.
1725+
if (origFlags.isAddressable()) {
1726+
origType = AbstractionPattern::getOpaque();
1727+
}
1728+
17231729
// Tuples get expanded unless they're inout.
17241730
if (origType.isTuple() && ownership != ValueOwnership::InOut) {
17251731
expandTuple(ownership, forSelf, origType, substType, origFlags);

lib/SILGen/SILGenApply.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3506,6 +3506,31 @@ class ArgEmitter {
35063506
private:
35073507
void emit(ArgumentSource &&arg, AbstractionPattern origParamType,
35083508
std::optional<AnyFunctionType::Param> origParam = std::nullopt) {
3509+
if (origParam && origParam->isAddressable()) {
3510+
// If the function takes an addressable parameter, and its argument is
3511+
// a reference to an addressable declaration with compatible ownership,
3512+
// forward the address along in-place.
3513+
if (arg.isExpr()) {
3514+
auto expr = std::move(arg).asKnownExpr();
3515+
3516+
if (auto le = dyn_cast<LoadExpr>(expr)) {
3517+
expr = le->getSubExpr();
3518+
}
3519+
if (auto dre = dyn_cast<DeclRefExpr>(expr)) {
3520+
if (auto param = dyn_cast<ParamDecl>(dre->getDecl())) {
3521+
if (param->isAddressable()
3522+
&& param->getValueOwnership() == origParam->getValueOwnership()) {
3523+
auto addr = SGF.VarLocs[param].value;
3524+
claimNextParameters(1);
3525+
Args.push_back(ManagedValue::forBorrowedAddressRValue(addr));
3526+
return;
3527+
}
3528+
}
3529+
}
3530+
arg = ArgumentSource(expr);
3531+
}
3532+
}
3533+
35093534
if (!arg.hasLValueType()) {
35103535
// If the unsubstituted function type has a parameter of tuple type,
35113536
// explode the tuple value.

lib/SILGen/SILGenProlog.cpp

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,15 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
194194
ManagedValue handleParam(AbstractionPattern origType, CanType substType,
195195
ParamDecl *pd) {
196196
// Note: inouts of tuples are not exploded, so we bypass visit().
197-
if (pd->isInOut())
198-
return handleInOut(origType, substType);
197+
if (pd->isInOut()) {
198+
return handleInOut(origType, substType, pd->isAddressable());
199+
}
200+
// `@_addressable` also suppresses exploding the parameter.
201+
if (pd->isAddressable()) {
202+
return handleScalar(claimNextParameter(), origType, substType,
203+
/*emitInto*/ nullptr,
204+
/*inout*/ false, /*addressable*/ true);
205+
}
199206
return visit(substType, origType, /*emitInto*/ nullptr);
200207
}
201208

@@ -213,7 +220,9 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
213220
// Handle scalar components.
214221
if (!isa<PackExpansionType>(substType)) {
215222
return handleScalar(componentValue, origPatternType, substType,
216-
/*emit into*/ nullptr, substParam.isInOut());
223+
/*emit into*/ nullptr,
224+
substParam.isInOut(),
225+
/*is addressable*/ false);
217226
}
218227

219228
auto componentPackTy = componentValue.getType().castTo<SILPackType>();
@@ -259,24 +268,33 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
259268
}
260269

261270
return handleScalar(input, origPatternType, substEltType,
262-
context.getEmitInto(), /*inout*/ false);
271+
context.getEmitInto(),
272+
/*inout*/ false,
273+
/*addressable*/ false);
263274
});
264275
}
265276

266277
ManagedValue visitType(CanType t, AbstractionPattern orig,
267278
Initialization *emitInto) {
268279
auto mv = claimNextParameter();
269-
return handleScalar(mv, orig, t, emitInto, /*inout*/ false);
280+
return handleScalar(mv, orig, t, emitInto,
281+
/*inout*/ false,
282+
/*addressable*/ false);
270283
}
271284

272-
ManagedValue handleInOut(AbstractionPattern orig, CanType t) {
285+
ManagedValue handleInOut(AbstractionPattern orig, CanType t,
286+
bool isAddressable) {
273287
auto mv = claimNextParameter();
274-
return handleScalar(mv, orig, t, /*emitInto*/ nullptr, /*inout*/ true);
288+
return handleScalar(mv, orig, t, /*emitInto*/ nullptr,
289+
/*inout*/ true,
290+
isAddressable);
275291
}
276292

277293
ManagedValue handleScalar(ManagedValue mv,
278294
AbstractionPattern orig, CanType t,
279-
Initialization *emitInto, bool isInOut) {
295+
Initialization *emitInto,
296+
bool isInOut,
297+
bool isAddressable) {
280298
assert(!(isInOut && emitInto != nullptr));
281299

282300
auto argType = SGF.getLoweredType(t, mv.getType().getCategory());
@@ -321,20 +339,26 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
321339
return mv;
322340
}
323341

324-
// This can happen if the value is resilient in the calling convention
325-
// but not resilient locally.
326-
bool argIsLoadable = argType.isLoadable(SGF.F);
327-
if (argIsLoadable) {
328-
if (argType.isAddress()) {
329-
mv = SGF.B.createLoadWithSameOwnership(loc, mv);
330-
argType = argType.getObjectType();
342+
// If the parameter is marked `@_addressable`, then we want to defer any
343+
// reabstraction of the parameter as received, so that we can use the
344+
// original value at its stable address when possible.
345+
bool argIsLoadable = false;
346+
if (!isAddressable) {
347+
argIsLoadable = argType.isLoadable(SGF.F);
348+
// This can happen if the value is resilient in the calling convention
349+
// but not resilient locally.
350+
if (argIsLoadable) {
351+
if (argType.isAddress()) {
352+
mv = SGF.B.createLoadWithSameOwnership(loc, mv);
353+
argType = argType.getObjectType();
354+
}
331355
}
332-
}
333356

334-
assert(argType.getCategory() == mv.getType().getCategory());
335-
if (argType.getASTType() != mv.getType().getASTType()) {
336-
// Reabstract the value if necessary.
337-
mv = SGF.emitOrigToSubstValue(loc, mv.ensurePlusOne(SGF, loc), orig, t);
357+
assert(argType.getCategory() == mv.getType().getCategory());
358+
if (argType.getASTType() != mv.getType().getASTType()) {
359+
// Reabstract the value if necessary.
360+
mv = SGF.emitOrigToSubstValue(loc, mv.ensurePlusOne(SGF, loc), orig, t);
361+
}
338362
}
339363

340364
if (parameters.isNoImplicitCopy && !argIsLoadable) {
@@ -538,7 +562,8 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
538562
auto eltAddrMV = cloner.clone(eltAddr);
539563
auto result = handleScalar(eltAddrMV, origPatternType,
540564
substComponentType, componentInit,
541-
/*inout*/ false);
565+
/*inout*/ false,
566+
/*addressable*/ false);
542567
assert(result.isInContext() == (componentInit != nullptr));
543568
if (!result.isInContext())
544569
eltMVs.push_back(result);
@@ -571,7 +596,9 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
571596
auto eltAddrMV = cloner.clone(eltAddr);
572597

573598
auto result = handleScalar(eltAddrMV, origPatternType, substEltType,
574-
eltInit, /*inout*/ false);
599+
eltInit,
600+
/*inout*/ false,
601+
/*addressable*/ false);
575602
assert(result.isInContext()); (void) result;
576603
});
577604
});

0 commit comments

Comments
 (0)