Skip to content

Commit c224505

Browse files
authored
Merge pull request #82296 from jckarter/transitive-addressability-fields-and-addressors-6.2
[6.2] SILGen: Handle struct fields and addressors as addressable storage.
2 parents 0409102 + f765d28 commit c224505

File tree

6 files changed

+388
-56
lines changed

6 files changed

+388
-56
lines changed

lib/SILGen/SILGenApply.cpp

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

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

35663737
namespace {
@@ -7285,6 +7456,34 @@ ArgumentSource AccessorBaseArgPreparer::prepare() {
72857456
return prepareAccessorObjectBaseArg();
72867457
}
72877458

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

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

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

lib/SILGen/SILGenExpr.cpp

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

2759-
// Everything else should use the l-value logic.
2760-
27612759
// Any writebacks for this access are tightly scoped.
27622760
FormalEvaluationScope scope(SGF);
27632761

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)