Skip to content

Commit c9be4bd

Browse files
authored
Merge pull request #67677 from gottesmm/borrowed-base-silgenlvalue
[move-only] Ensure that we properly nest accesses to base values if the base is noncopyable or the accessor result is noncopyable.
2 parents 9a5bd63 + e96b2a6 commit c9be4bd

17 files changed

+429
-237
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4421,8 +4421,18 @@ class StoreBorrowInst
44214421

44224422
ArrayRef<Operand> getAllOperands() const { return Operands.asArray(); }
44234423
MutableArrayRef<Operand> getAllOperands() { return Operands.asArray(); }
4424+
4425+
using EndBorrowRange =
4426+
decltype(std::declval<ValueBase>().getUsersOfType<EndBorrowInst>());
4427+
4428+
/// Return a range over all EndBorrow instructions for this BeginBorrow.
4429+
EndBorrowRange getEndBorrows() const;
44244430
};
44254431

4432+
inline auto StoreBorrowInst::getEndBorrows() const -> EndBorrowRange {
4433+
return getUsersOfType<EndBorrowInst>();
4434+
}
4435+
44264436
/// Represents the end of a borrow scope of a value %val from a
44274437
/// value or address %src.
44284438
///

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ SubElementOffset::computeForAddress(SILValue projectionDerivedFromRoot,
129129
continue;
130130
}
131131

132+
if (auto *sbi = dyn_cast<StoreBorrowInst>(projectionDerivedFromRoot)) {
133+
projectionDerivedFromRoot = sbi->getDest();
134+
continue;
135+
}
136+
132137
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(
133138
projectionDerivedFromRoot)) {
134139
projectionDerivedFromRoot = m->getOperand();

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2595,7 +2595,12 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
25952595
require(SI->getDest()->getType().isAddress(),
25962596
"Must store to an address dest");
25972597
// Note: This is the current implementation and the design is not final.
2598-
require(isa<AllocStackInst>(SI->getDest()),
2598+
auto isLegal = [](SILValue value) {
2599+
if (auto *mmci = dyn_cast<MarkMustCheckInst>(value))
2600+
value = mmci->getOperand();
2601+
return isa<AllocStackInst>(value);
2602+
};
2603+
require(isLegal(SI->getDest()),
25992604
"store_borrow destination can only be an alloc_stack");
26002605
requireSameType(SI->getDest()->getType().getObjectType(),
26012606
SI->getSrc()->getType(),

lib/SILGen/LValue.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ class LValue {
530530
SGFAccessKind selfAccess,
531531
SGFAccessKind otherAccess);
532532

533-
void dump() const;
533+
SWIFT_DEBUG_DUMP;
534534
void dump(raw_ostream &os, unsigned indent = 0) const;
535535
};
536536

lib/SILGen/SILGenApply.cpp

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3124,6 +3124,21 @@ Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
31243124
if (kind == StorageReferenceOperationKind::Consume && !sawLoad)
31253125
return nullptr;
31263126

3127+
// If we did not see a load or a subscript expr and our argExpr is a
3128+
// declref_expr, return nullptr. We have an object not something that will be
3129+
// in memory. This can happen with classes or with values captured by a
3130+
// closure.
3131+
//
3132+
// NOTE: If we see a member_ref_expr from a decl_ref_expr, we still process it
3133+
// since the declref_expr could be from a class.
3134+
if (!sawLoad && !subscriptExpr) {
3135+
if (auto *declRef = dyn_cast<DeclRefExpr>(argExpr)) {
3136+
assert(!declRef->getType()->is<LValueType>() &&
3137+
"Shouldn't ever have an lvalue type here!");
3138+
return nullptr;
3139+
}
3140+
}
3141+
31273142
auto result = ::findStorageReferenceExprForBorrow(argExpr);
31283143

31293144
if (!result)
@@ -3143,31 +3158,18 @@ Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
31433158
}
31443159

31453160
if (!storage)
3146-
return nullptr;
3161+
return nullptr;
31473162
assert(type);
31483163

31493164
SILType ty =
31503165
SGF.getLoweredType(type->getWithoutSpecifierType()->getCanonicalType());
31513166
bool isMoveOnly = ty.isPureMoveOnly();
31523167
if (auto *pd = dyn_cast<ParamDecl>(storage)) {
3153-
isMoveOnly |= pd->getSpecifier() == ParamSpecifier::Borrowing;
3154-
isMoveOnly |= pd->getSpecifier() == ParamSpecifier::Consuming;
3168+
isMoveOnly |= pd->getSpecifier() == ParamSpecifier::Borrowing;
3169+
isMoveOnly |= pd->getSpecifier() == ParamSpecifier::Consuming;
31553170
}
31563171
if (!isMoveOnly)
3157-
return nullptr;
3158-
3159-
// It makes sense to borrow any kind of storage we refer to at this stage,
3160-
// but SILGenLValue does not currently handle some kinds of references well.
3161-
//
3162-
// When rejecting to do the LValue-style borrow here, it'll end up going thru
3163-
// the RValue-style emission, after which the extra copy will get eliminated.
3164-
//
3165-
// If we did not see a LoadExpr around the argument expression, then only
3166-
// do the borrow if the storage is non-local.
3167-
// FIXME: I don't have a principled reason for why this matters and hope that
3168-
// we can fix the AST we're working with.
3169-
if (!sawLoad && storage->getDeclContext()->isLocalContext())
3170-
return nullptr;
3172+
return nullptr;
31713173

31723174
// Claim the value of this argument since we found a storage reference that
31733175
// has a move only base.

lib/SILGen/SILGenLValue.cpp

Lines changed: 134 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,12 +2156,25 @@ namespace {
21562156
if (value.getType().isAddress() ||
21572157
!isReadAccessResultAddress(getAccessKind()))
21582158
return value;
2159+
2160+
// If we have a guaranteed object and our read access result requires an
2161+
// address, store it using a store_borrow.
2162+
if (value.getType().isObject() &&
2163+
value.getOwnershipKind() == OwnershipKind::Guaranteed) {
2164+
SILValue alloc = SGF.emitTemporaryAllocation(loc, getTypeOfRValue());
2165+
if (alloc->getType().isMoveOnly())
2166+
alloc = SGF.B.createMarkMustCheckInst(
2167+
loc, alloc, MarkMustCheckInst::CheckKind::NoConsumeOrAssign);
2168+
return SGF.B.createFormalAccessStoreBorrow(loc, value, alloc);
2169+
}
21592170
}
21602171

21612172
// Otherwise, we need to make a temporary.
2173+
// TODO: This needs to be changed to use actual store_borrows. Noncopyable
2174+
// types do not support tuples today, so we can avoid this for now.
21622175
// TODO: build a scalar tuple if possible.
2163-
auto temporary =
2164-
SGF.emitTemporary(loc, SGF.getTypeLowering(getTypeOfRValue()));
2176+
auto temporary = SGF.emitFormalAccessTemporary(
2177+
loc, SGF.getTypeLowering(getTypeOfRValue()));
21652178
auto yieldsAsArray = llvm::makeArrayRef(yields);
21662179
copyBorrowedYieldsIntoTemporary(SGF, loc, yieldsAsArray,
21672180
getOrigFormalType(), getSubstFormalType(),
@@ -2882,6 +2895,118 @@ static ManagedValue visitRecNonInOutBase(SILGenLValue &SGL, Expr *e,
28822895
value);
28832896
}
28842897

2898+
static CanType getBaseFormalType(Expr *baseExpr) {
2899+
return baseExpr->getType()->getWithoutSpecifierType()->getCanonicalType();
2900+
}
2901+
2902+
class LLVM_LIBRARY_VISIBILITY SILGenBorrowedBaseVisitor
2903+
: public Lowering::ExprVisitor<SILGenBorrowedBaseVisitor, LValue,
2904+
SGFAccessKind, LValueOptions> {
2905+
public:
2906+
SILGenLValue &SGL;
2907+
SILGenFunction &SGF;
2908+
2909+
SILGenBorrowedBaseVisitor(SILGenLValue &SGL, SILGenFunction &SGF)
2910+
: SGL(SGL), SGF(SGF) {}
2911+
2912+
/// Returns the subexpr
2913+
static bool isNonCopyableBaseBorrow(SILGenFunction &SGF, Expr *e) {
2914+
if (auto *le = dyn_cast<LoadExpr>(e))
2915+
return le->getType()->isPureMoveOnly();
2916+
if (auto *m = dyn_cast<MemberRefExpr>(e)) {
2917+
// If our m is a pure noncopyable type or our base is, we need to perform
2918+
// a noncopyable base borrow.
2919+
//
2920+
// DISCUSSION: We can have a noncopyable member_ref_expr with a copyable
2921+
// base if the noncopyable member_ref_expr is from a computed method. In
2922+
// such a case, we want to ensure that we wrap things the right way.
2923+
return m->getType()->isPureMoveOnly() ||
2924+
m->getBase()->getType()->isPureMoveOnly();
2925+
}
2926+
if (auto *d = dyn_cast<DeclRefExpr>(e))
2927+
return e->getType()->isPureMoveOnly();
2928+
return false;
2929+
}
2930+
2931+
LValue visitExpr(Expr *e, SGFAccessKind accessKind, LValueOptions options) {
2932+
e->dump(llvm::errs());
2933+
llvm::report_fatal_error("Unimplemented node!");
2934+
}
2935+
2936+
LValue visitMemberRefExpr(MemberRefExpr *e, SGFAccessKind accessKind,
2937+
LValueOptions options) {
2938+
// If we have a member_ref_expr, we create a component that will when we
2939+
// evaluate the lvalue,
2940+
VarDecl *var = cast<VarDecl>(e->getMember().getDecl());
2941+
2942+
assert(!e->getType()->is<LValueType>());
2943+
2944+
auto accessSemantics = e->getAccessSemantics();
2945+
AccessStrategy strategy = var->getAccessStrategy(
2946+
accessSemantics, getFormalAccessKind(accessKind),
2947+
SGF.SGM.M.getSwiftModule(), SGF.F.getResilienceExpansion());
2948+
2949+
auto baseFormalType = getBaseFormalType(e->getBase());
2950+
LValue lv = visit(
2951+
e->getBase(),
2952+
getBaseAccessKind(SGF.SGM, var, accessKind, strategy, baseFormalType),
2953+
getBaseOptions(options, strategy));
2954+
llvm::Optional<ActorIsolation> actorIso;
2955+
if (e->isImplicitlyAsync())
2956+
actorIso = getActorIsolation(var);
2957+
lv.addMemberVarComponent(SGF, e, var, e->getMember().getSubstitutions(),
2958+
options, e->isSuper(), accessKind, strategy,
2959+
getSubstFormalRValueType(e),
2960+
false /*is on self parameter*/, actorIso);
2961+
return lv;
2962+
}
2963+
2964+
ManagedValue emitImmediateBaseValue(Expr *e) {
2965+
// We are going to immediately use this base value, so we want to borrow it.
2966+
ManagedValue mv =
2967+
SGF.emitRValueAsSingleValue(e, SGFContext::AllowImmediatePlusZero);
2968+
if (mv.isPlusZeroRValueOrTrivial())
2969+
return mv;
2970+
2971+
// Any temporaries needed to materialize the lvalue must be destroyed when
2972+
// at the end of the lvalue's formal evaluation scope.
2973+
// e.g. for foo(self.bar)
2974+
// %self = load [copy] %ptr_self
2975+
// %rvalue = barGetter(%self)
2976+
// destroy_value %self // self must be released before calling foo.
2977+
// foo(%rvalue)
2978+
SILValue value = mv.forward(SGF);
2979+
return SGF.emitFormalAccessManagedRValueWithCleanup(CleanupLocation(e),
2980+
value);
2981+
}
2982+
2983+
LValue visitDeclRefExpr(DeclRefExpr *e, SGFAccessKind accessKind,
2984+
LValueOptions options) {
2985+
if (accessKind == SGFAccessKind::BorrowedObjectRead) {
2986+
auto rv = emitImmediateBaseValue(e);
2987+
CanType formalType = getSubstFormalRValueType(e);
2988+
auto typeData = getValueTypeData(accessKind, formalType, rv.getValue());
2989+
LValue lv;
2990+
lv.add<ValueComponent>(rv, llvm::None, typeData, /*isRValue=*/true);
2991+
return lv;
2992+
}
2993+
2994+
return SGL.visitDeclRefExpr(e, accessKind, options);
2995+
}
2996+
2997+
LValue visitLoadExpr(LoadExpr *e, SGFAccessKind accessKind,
2998+
LValueOptions options) {
2999+
// TODO: orig abstraction pattern.
3000+
LValue lv = SGL.visitRec(e->getSubExpr(),
3001+
SGFAccessKind::BorrowedAddressRead, options);
3002+
CanType formalType = getSubstFormalRValueType(e);
3003+
LValueTypeData typeData{accessKind, AbstractionPattern(formalType),
3004+
formalType, lv.getTypeOfRValue().getASTType()};
3005+
lv.add<BorrowValueComponent>(typeData);
3006+
return lv;
3007+
}
3008+
};
3009+
28853010
LValue SILGenLValue::visitRec(Expr *e, SGFAccessKind accessKind,
28863011
LValueOptions options, AbstractionPattern orig) {
28873012
// First see if we have an lvalue type. If we do, then quickly handle that and
@@ -2894,19 +3019,14 @@ LValue SILGenLValue::visitRec(Expr *e, SGFAccessKind accessKind,
28943019
// a `borrow x` operator, the operator is used on the base here), we want to
28953020
// apply the lvalue within a formal access to the original value instead of
28963021
// an actual loaded copy.
2897-
2898-
if (e->getType()->isPureMoveOnly()) {
2899-
if (auto load = dyn_cast<LoadExpr>(e)) {
2900-
LValue lv = visitRec(load->getSubExpr(), SGFAccessKind::BorrowedAddressRead,
2901-
options, orig);
2902-
CanType formalType = getSubstFormalRValueType(e);
2903-
LValueTypeData typeData{accessKind, AbstractionPattern(formalType),
2904-
formalType, lv.getTypeOfRValue().getASTType()};
2905-
lv.add<BorrowValueComponent>(typeData);
2906-
return lv;
2907-
}
3022+
if (SILGenBorrowedBaseVisitor::isNonCopyableBaseBorrow(SGF, e)) {
3023+
SILGenBorrowedBaseVisitor visitor(*this, SGF);
3024+
auto accessKind = SGFAccessKind::BorrowedObjectRead;
3025+
if (e->getType()->is<LValueType>())
3026+
accessKind = SGFAccessKind::BorrowedAddressRead;
3027+
return visitor.visit(e, accessKind, options);
29083028
}
2909-
3029+
29103030
// Otherwise we have a non-lvalue type (references, values, metatypes,
29113031
// etc). These act as the root of a logical lvalue. Compute the root value,
29123032
// wrap it in a ValueComponent, and return it for our caller.
@@ -3559,10 +3679,6 @@ static SGFAccessKind getBaseAccessKind(SILGenModule &SGM,
35593679
llvm_unreachable("bad access strategy");
35603680
}
35613681

3562-
static CanType getBaseFormalType(Expr *baseExpr) {
3563-
return baseExpr->getType()->getWithoutSpecifierType()->getCanonicalType();
3564-
}
3565-
35663682
bool isCallToReplacedInDynamicReplacement(SILGenFunction &SGF,
35673683
AbstractFunctionDecl *afd,
35683684
bool &isObjCReplacementSelfCall);

0 commit comments

Comments
 (0)