Skip to content

Commit b8f5a36

Browse files
authored
Merge pull request #82288 from jckarter/transitive-addressability-fields-and-addressors
SILGen: Handle struct fields and addressors as addressable storage.
2 parents b8ade82 + 761faaa commit b8f5a36

File tree

7 files changed

+395
-81
lines changed

7 files changed

+395
-81
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 214 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3539,27 +3539,198 @@ Expr *ArgumentSource::findStorageReferenceExprForBorrow() && {
35393539
ManagedValue
35403540
SILGenFunction::tryEmitAddressableParameterAsAddress(ArgumentSource &&arg,
35413541
ValueOwnership ownership) {
3542+
if (!arg.isExpr()) {
3543+
return ManagedValue();
3544+
}
3545+
35423546
// If the function takes an addressable parameter, and its argument is
35433547
// a reference to an addressable declaration with compatible ownership,
35443548
// forward the address along in-place.
3545-
if (arg.isExpr()) {
3546-
auto origExpr = std::move(arg).asKnownExpr();
3547-
auto expr = origExpr;
3549+
auto origExpr = std::move(arg).asKnownExpr();
3550+
auto expr = origExpr;
3551+
3552+
// If the expression does not have a stable address to return, then restore
3553+
// the ArgumentSource and return a null value to the caller.
3554+
auto notAddressable = [&] {
3555+
arg = ArgumentSource(origExpr);
3556+
return ManagedValue();
3557+
};
3558+
3559+
if (auto le = dyn_cast<LoadExpr>(expr)) {
3560+
expr = le->getSubExpr();
3561+
}
3562+
if (auto dre = dyn_cast<DeclRefExpr>(expr)) {
3563+
if (auto param = dyn_cast<VarDecl>(dre->getDecl())) {
3564+
if (auto addr = getLocalVariableAddressableBuffer(param, expr,
3565+
ownership)) {
3566+
return ManagedValue::forBorrowedAddressRValue(addr);
3567+
}
3568+
}
3569+
}
3570+
3571+
// Property or subscript member accesses may also be addressable.
3572+
3573+
AccessKind accessKind;
3574+
switch (ownership) {
3575+
case ValueOwnership::Shared:
3576+
case ValueOwnership::Default:
3577+
accessKind = AccessKind::Read;
3578+
break;
3579+
case ValueOwnership::Owned:
3580+
case ValueOwnership::InOut:
3581+
accessKind = AccessKind::ReadWrite;
3582+
break;
3583+
}
3584+
3585+
LookupExpr *lookupExpr;
3586+
AbstractStorageDecl *memberStorage;
3587+
SubstitutionMap subs;
3588+
AccessSemantics accessSemantics;
3589+
PreparedArguments indices;
3590+
3591+
if (auto mre = dyn_cast<MemberRefExpr>(expr)) {
3592+
lookupExpr = mre;
3593+
memberStorage = dyn_cast<VarDecl>(mre->getMember().getDecl());
3594+
subs = mre->getMember().getSubstitutions();
3595+
accessSemantics = mre->getAccessSemantics();
3596+
} else if (auto se = dyn_cast<SubscriptExpr>(expr)) {
3597+
lookupExpr = se;
3598+
auto subscriptDecl = cast<SubscriptDecl>(se->getMember().getDecl());
3599+
memberStorage = subscriptDecl;
3600+
subs = se->getMember().getSubstitutions();
3601+
accessSemantics = se->getAccessSemantics();
35483602

3549-
if (auto le = dyn_cast<LoadExpr>(expr)) {
3550-
expr = le->getSubExpr();
3551-
}
3552-
if (auto dre = dyn_cast<DeclRefExpr>(expr)) {
3553-
if (auto param = dyn_cast<VarDecl>(dre->getDecl())) {
3554-
if (auto addr = getLocalVariableAddressableBuffer(param, expr,
3555-
ownership)) {
3556-
return ManagedValue::forBorrowedAddressRValue(addr);
3557-
}
3603+
indices = PreparedArguments(
3604+
subscriptDecl->getInterfaceType()->castTo<AnyFunctionType>()
3605+
->getParams(),
3606+
se->getArgs());
3607+
} else {
3608+
return notAddressable();
3609+
}
3610+
3611+
if (!memberStorage) {
3612+
return notAddressable();
3613+
}
3614+
3615+
auto strategy = memberStorage->getAccessStrategy(accessSemantics, accessKind,
3616+
SGM.M.getSwiftModule(), F.getResilienceExpansion(),
3617+
std::make_pair<>(expr->getSourceRange(), FunctionDC),
3618+
/*old abi (doesn't matter here)*/ false);
3619+
3620+
switch (strategy.getKind()) {
3621+
case AccessStrategy::Storage: {
3622+
auto vd = cast<VarDecl>(memberStorage);
3623+
// TODO: Is it possible and/or useful for class storage to be
3624+
// addressable?
3625+
if (!vd->getDeclContext()->getInnermostTypeContext()
3626+
->getDeclaredTypeInContext()->getStructOrBoundGenericStruct()) {
3627+
return notAddressable();
3628+
}
3629+
3630+
// If the storage holds the fully-abstracted representation of the
3631+
// type, then we can use its address.
3632+
auto absBaseTy = getLoweredType(AbstractionPattern::getOpaque(),
3633+
lookupExpr->getBase()->getType()->getWithoutSpecifierType());
3634+
auto memberTy = absBaseTy.getFieldType(vd, &F);
3635+
auto absMemberTy = getLoweredType(AbstractionPattern::getOpaque(),
3636+
lookupExpr->getType()->getWithoutSpecifierType());
3637+
3638+
if (memberTy.getAddressType() != absMemberTy.getAddressType()) {
3639+
// The storage is not fully abstracted, so it can't serve as a
3640+
// stable address.
3641+
return notAddressable();
3642+
}
3643+
3644+
// Otherwise, we can project the field address from the stable address
3645+
// of the base, if it has one. Try to get the stable address for the
3646+
// base.
3647+
auto baseAddr = tryEmitAddressableParameterAsAddress(
3648+
ArgumentSource(lookupExpr->getBase()), ownership);
3649+
3650+
if (!baseAddr) {
3651+
return notAddressable();
3652+
}
3653+
3654+
// Project the field's address.
3655+
auto fieldAddr = B.createStructElementAddr(lookupExpr,
3656+
baseAddr.getValue(), vd);
3657+
return ManagedValue::forBorrowedAddressRValue(fieldAddr);
3658+
}
3659+
3660+
case AccessStrategy::DirectToAccessor:
3661+
case AccessStrategy::DispatchToAccessor: {
3662+
// Non-addressor accessors don't produce stable addresses.
3663+
if (strategy.getAccessor() != AccessorKind::Address
3664+
&& strategy.getAccessor() != AccessorKind::MutableAddress) {
3665+
return notAddressable();
3666+
}
3667+
// TODO: Non-yielding borrow/mutate accessors can also be considered
3668+
// addressable when we have those.
3669+
3670+
auto addressor = memberStorage->getAccessor(strategy.getAccessor());
3671+
auto addressorRef = SILDeclRef(addressor, SILDeclRef::Kind::Func);
3672+
auto absMemberTy = getLoweredType(AbstractionPattern::getOpaque(),
3673+
lookupExpr->getType()->getWithoutSpecifierType())
3674+
.getAddressType();
3675+
3676+
// Evaluate the base in the current formal access scope.
3677+
ManagedValue base;
3678+
// If the addressor wants the base addressable, try to honor that
3679+
// request.
3680+
auto addressorSelf = addressor->getImplicitSelfDecl();
3681+
if (addressorSelf->isAddressable()
3682+
|| getTypeLowering(lookupExpr->getBase()->getType()
3683+
->getWithoutSpecifierType())
3684+
.getRecursiveProperties().isAddressableForDependencies()) {
3685+
ValueOwnership baseOwnership = addressorSelf->isInOut()
3686+
? ValueOwnership::InOut
3687+
: ValueOwnership::Shared;
3688+
3689+
base = tryEmitAddressableParameterAsAddress(
3690+
ArgumentSource(lookupExpr->getBase()), baseOwnership);
3691+
}
3692+
3693+
// Otherwise, project the base as an lvalue.
3694+
if (!base) {
3695+
SGFAccessKind silAccess;
3696+
switch (accessKind) {
3697+
case AccessKind::Read:
3698+
silAccess = SGFAccessKind::BorrowedAddressRead;
3699+
break;
3700+
case AccessKind::ReadWrite:
3701+
case AccessKind::Write:
3702+
silAccess = SGFAccessKind::ReadWrite;
3703+
break;
35583704
}
3705+
3706+
LValue lv = emitLValue(lookupExpr, silAccess);
3707+
3708+
drillToLastComponent(lookupExpr->getBase(), std::move(lv), base);
35593709
}
3560-
arg = ArgumentSource(origExpr);
3710+
3711+
// Materialize the base outside of the scope of the addressor call,
3712+
// since the returned address may depend on the materialized
3713+
// representation, even if it isn't transitively addressable.
3714+
auto baseTy = lookupExpr->getBase()->getType()->getCanonicalType();
3715+
ArgumentSource baseArg = prepareAccessorBaseArgForFormalAccess(
3716+
lookupExpr->getBase(), base, baseTy, addressorRef);
3717+
3718+
// Invoke the addressor to directly produce the address.
3719+
return emitAddressorAccessor(lookupExpr,
3720+
addressorRef, subs,
3721+
std::move(baseArg),
3722+
/*super*/ false, /*direct accessor use*/ true,
3723+
std::move(indices),
3724+
absMemberTy, /*on self*/ false);
35613725
}
3562-
return ManagedValue();
3726+
3727+
case AccessStrategy::MaterializeToTemporary:
3728+
case AccessStrategy::DispatchToDistributedThunk:
3729+
// These strategies never produce a value with a stable address.
3730+
return notAddressable();
3731+
}
3732+
3733+
llvm_unreachable("uncovered switch!");
35633734
}
35643735

35653736
namespace {
@@ -7284,6 +7455,34 @@ ArgumentSource AccessorBaseArgPreparer::prepare() {
72847455
return prepareAccessorObjectBaseArg();
72857456
}
72867457

7458+
ArgumentSource SILGenFunction::prepareAccessorBaseArgForFormalAccess(
7459+
SILLocation loc,
7460+
ManagedValue base,
7461+
CanType baseFormalType,
7462+
SILDeclRef accessor) {
7463+
if (!base) {
7464+
return ArgumentSource();
7465+
}
7466+
7467+
base = base.formalAccessBorrow(*this, loc);
7468+
// If the base needs to be materialized, do so in
7469+
// the outer formal evaluation scope, since an addressor or
7470+
// other dependent value may want to point into the materialization.
7471+
auto &baseInfo = getConstantInfo(getTypeExpansionContext(), accessor);
7472+
7473+
if (!baseInfo.FormalPattern.isForeign()) {
7474+
auto baseFnTy = baseInfo.SILFnType;
7475+
7476+
if (baseFnTy->getSelfParameter().isFormalIndirect()
7477+
&& base.getType().isObject()
7478+
&& silConv.useLoweredAddresses()) {
7479+
base = base.formallyMaterialize(*this, loc);
7480+
}
7481+
}
7482+
7483+
return prepareAccessorBaseArg(loc, base, baseFormalType, accessor);
7484+
}
7485+
72877486
ArgumentSource SILGenFunction::prepareAccessorBaseArg(SILLocation loc,
72887487
ManagedValue base,
72897488
CanType baseFormalType,
@@ -7603,10 +7802,7 @@ ManagedValue SILGenFunction::emitAddressorAccessor(
76037802

76047803
emission.addCallSite(loc, std::move(subscriptIndices));
76057804

7606-
// Unsafe{Mutable}Pointer<T> or
7607-
// (Unsafe{Mutable}Pointer<T>, Builtin.UnknownPointer) or
7608-
// (Unsafe{Mutable}Pointer<T>, Builtin.NativePointer) or
7609-
// (Unsafe{Mutable}Pointer<T>, Builtin.NativePointer?) or
7805+
// Result must be Unsafe{Mutable}Pointer<T>
76107806
SmallVector<ManagedValue, 2> results;
76117807
emission.apply().getAll(results);
76127808

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -477,29 +477,6 @@ static ManagedValue emitBuiltinUnprotectedAddressOf(SILGenFunction &SGF,
477477
/*stackProtected=*/ false);
478478
}
479479

480-
// Like `tryEmitAddressableParameterAsAddress`, but also handles struct element projections.
481-
static SILValue emitAddressOf(Expr *e, SILGenFunction &SGF, SILLocation loc) {
482-
483-
if (auto *memberRef = dyn_cast<MemberRefExpr>(e)) {
484-
VarDecl *fieldDecl = dyn_cast<VarDecl>(memberRef->getDecl().getDecl());
485-
if (!fieldDecl)
486-
return SILValue();
487-
SILValue addr = emitAddressOf(memberRef->getBase(), SGF, loc);
488-
if (!addr)
489-
return SILValue();
490-
if (addr->getType().getStructOrBoundGenericStruct() != fieldDecl->getDeclContext())
491-
return SILValue();
492-
return SGF.B.createStructElementAddr(loc, addr, fieldDecl);
493-
}
494-
495-
if (auto addressableAddr = SGF.tryEmitAddressableParameterAsAddress(
496-
ArgumentSource(e),
497-
ValueOwnership::Shared)) {
498-
return addressableAddr.getValue();
499-
}
500-
return SILValue();
501-
}
502-
503480
/// Specialized emitter for Builtin.addressOfBorrow.
504481
static ManagedValue emitBuiltinAddressOfBorrowBuiltins(SILGenFunction &SGF,
505482
SILLocation loc,
@@ -514,11 +491,15 @@ static ManagedValue emitBuiltinAddressOfBorrowBuiltins(SILGenFunction &SGF,
514491

515492
auto argument = (*argsOrError)[0];
516493

494+
SILValue addr;
517495
// Try to borrow the argument at +0 indirect.
518496
// If the argument is a reference to a borrowed addressable parameter, then
519497
// use that parameter's stable address.
520-
SILValue addr = emitAddressOf(argument, SGF, loc);
521-
if (!addr) {
498+
if (auto addressableAddr = SGF.tryEmitAddressableParameterAsAddress(
499+
ArgumentSource(argument),
500+
ValueOwnership::Shared)) {
501+
addr = addressableAddr.getValue();
502+
} else {
522503
// We otherwise only support the builtin applied to values that
523504
// are naturally emitted borrowed in memory. (But it would probably be good
524505
// to phase this out since it's not really well-defined how long

lib/SILGen/SILGenExpr.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,8 +2754,6 @@ RValue RValueEmitter::visitMemberRefExpr(MemberRefExpr *e,
27542754
"RValueEmitter shouldn't be called on lvalues");
27552755
assert(isa<VarDecl>(e->getMember().getDecl()));
27562756

2757-
// Everything else should use the l-value logic.
2758-
27592757
// Any writebacks for this access are tightly scoped.
27602758
FormalEvaluationScope scope(SGF);
27612759

lib/SILGen/SILGenFunction.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class ConsumableManagedValue;
4444
class LogicalPathComponent;
4545
class LValue;
4646
class ManagedValue;
47+
class PathComponent;
4748
class PreparedArguments;
4849
class RValue;
4950
class CalleeTypeInfo;
@@ -1978,6 +1979,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
19781979
ArgumentSource prepareAccessorBaseArg(SILLocation loc, ManagedValue base,
19791980
CanType baseFormalType,
19801981
SILDeclRef accessor);
1982+
ArgumentSource prepareAccessorBaseArgForFormalAccess(SILLocation loc,
1983+
ManagedValue base,
1984+
CanType baseFormalType,
1985+
SILDeclRef accessor);
19811986

19821987
RValue emitGetAccessor(
19831988
SILLocation loc, SILDeclRef getter, SubstitutionMap substitutions,
@@ -2201,7 +2206,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
22012206

22022207
RValue emitLoadOfLValue(SILLocation loc, LValue &&src, SGFContext C,
22032208
bool isBaseLValueGuaranteed = false);
2204-
2209+
PathComponent &&
2210+
drillToLastComponent(SILLocation loc,
2211+
LValue &&lv,
2212+
ManagedValue &addr,
2213+
TSanKind tsanKind = TSanKind::None);
2214+
22052215
/// Emit a reference to a method from within another method of the type.
22062216
std::tuple<ManagedValue, SILType>
22072217
emitSiblingMethodRef(SILLocation loc,

0 commit comments

Comments
 (0)