Skip to content

Commit 55892ef

Browse files
committed
[silgen] Add a special visitor for accessing the base of noncopyable types.
We want these to be borrowed in most cases and to create an appropriate onion wrapping. Since we are doing this in more cases now, we fix a bunch of cases where we used to be forced to insert a copy since a coroutine or access would end too early.
1 parent 9b39805 commit 55892ef

8 files changed

+185
-183
lines changed

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/SILGenLValue.cpp

Lines changed: 117 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2877,6 +2877,116 @@ static ManagedValue visitRecNonInOutBase(SILGenLValue &SGL, Expr *e,
28772877
value);
28782878
}
28792879

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

3557-
static CanType getBaseFormalType(Expr *baseExpr) {
3558-
return baseExpr->getType()->getWithoutSpecifierType()->getCanonicalType();
3559-
}
3560-
35613662
bool isCallToReplacedInDynamicReplacement(SILGenFunction &SGF,
35623663
AbstractFunctionDecl *afd,
35633664
bool &isObjCReplacementSelfCall);

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,19 +1504,30 @@ struct CopiedLoadBorrowEliminationVisitor final
15041504
// We can only hit this if our load_borrow was copied.
15051505
llvm_unreachable("We should never hit this");
15061506

1507-
case OperandOwnership::GuaranteedForwarding:
1508-
// If we have a switch_enum, we always need to convert it to a load
1509-
// [copy] since we need to destructure through it.
1510-
shouldConvertToLoadCopy |= isa<SwitchEnumInst>(nextUse->getUser());
1511-
1507+
case OperandOwnership::GuaranteedForwarding: {
1508+
SmallVector<SILValue, 8> forwardedValues;
1509+
auto *fn = nextUse->getUser()->getFunction();
15121510
ForwardingOperand(nextUse).visitForwardedValues([&](SILValue value) {
1513-
for (auto *use : value->getUses()) {
1514-
useWorklist.push_back(use);
1515-
}
1511+
if (value->getType().isTrivial(fn))
1512+
return true;
1513+
forwardedValues.push_back(value);
15161514
return true;
15171515
});
1518-
continue;
15191516

1517+
// If we do not have any forwarded values, just continue.
1518+
if (forwardedValues.empty())
1519+
continue;
1520+
1521+
while (!forwardedValues.empty()) {
1522+
for (auto *use : forwardedValues.pop_back_val()->getUses())
1523+
useWorklist.push_back(use);
1524+
}
1525+
1526+
// If we have a switch_enum, we always need to convert it to a load
1527+
// [copy] since we need to destructure through it.
1528+
shouldConvertToLoadCopy |= isa<SwitchEnumInst>(nextUse->getUser());
1529+
continue;
1530+
}
15201531
case OperandOwnership::Borrow:
15211532
LLVM_DEBUG(llvm::dbgs() << " Found recursive borrow!\n");
15221533
// Look through borrows.

0 commit comments

Comments
 (0)