Skip to content

[silgen] Change a bunch of self accesses to use true formal evaluation scopes and formal accesses. #7717

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions lib/SILGen/ManagedValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,19 @@ ManagedValue ManagedValue::copyUnmanaged(SILGenFunction &gen, SILLocation loc) {
return gen.emitManagedRValueWithCleanup(result);
}

/// This is the same operation as 'copy', but works on +0 values that don't
/// have cleanups. It returns a +1 value with one.
ManagedValue ManagedValue::formalAccessCopyUnmanaged(SILGenFunction &gen,
SILLocation loc) {
if (getType().isObject()) {
return gen.B.createFormalAccessCopyValue(loc, *this);
}

SILValue result = gen.emitTemporaryAllocation(loc, getType());
return gen.B.createFormalAccessCopyAddr(loc, *this, result, IsNotTake,
IsInitialization);
}

/// Disable the cleanup for this value.
void ManagedValue::forwardCleanup(SILGenFunction &gen) const {
assert(hasCleanup() && "value doesn't have cleanup!");
Expand Down
6 changes: 5 additions & 1 deletion lib/SILGen/ManagedValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,11 @@ class ManagedValue {
/// This is the same operation as 'copy', but works on +0 values that don't
/// have cleanups. It returns a +1 value with one.
ManagedValue copyUnmanaged(SILGenFunction &gen, SILLocation loc);


/// This is the same operation as 'formalAccessCopy', but works on +0 values
/// that don't have cleanups. It returns a +1 value with one.
ManagedValue formalAccessCopyUnmanaged(SILGenFunction &gen, SILLocation loc);

bool hasCleanup() const { return cleanup.isValid(); }
CleanupHandle getCleanup() const { return cleanup; }

Expand Down
24 changes: 16 additions & 8 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,10 +639,11 @@ class Callee {
/// the original had a cleanup.
static ManagedValue maybeEnterCleanupForTransformed(SILGenFunction &gen,
ManagedValue orig,
SILValue result) {
SILValue result,
SILLocation loc) {
if (orig.hasCleanup()) {
orig.forwardCleanup(gen);
return gen.emitManagedBufferWithCleanup(result);
return gen.emitFormalAccessManagedBufferWithCleanup(loc, result);
} else {
return ManagedValue::forUnmanaged(result);
}
Expand Down Expand Up @@ -763,7 +764,7 @@ class ArchetypeCalleeBuilder {
StoreOwnershipQualifier::Init);

// If we had a cleanup, create a cleanup at the new address.
return maybeEnterCleanupForTransformed(gen, ref, temp);
return maybeEnterCleanupForTransformed(gen, ref, temp, selfLoc);
}
};

Expand Down Expand Up @@ -2750,7 +2751,8 @@ namespace {
assert(destAddr->getType() == loweredSubstParamType.getAddressType());

auto &destTL = SharedInfo->getBaseTypeLowering();
Cleanup = gen.enterDormantTemporaryCleanup(destAddr, destTL);
Cleanup =
gen.enterDormantFormalAccessTemporaryCleanup(destAddr, loc, destTL);

TemporaryInitialization init(destAddr, Cleanup);
std::move(arg).forwardInto(gen, SharedInfo->getBaseAbstractionPattern(),
Expand Down Expand Up @@ -4337,6 +4339,7 @@ namespace {
AbstractionPattern origFormalType(callee.getOrigFormalType());
CanFunctionType formalType = callee.getSubstFormalType();

// Get the callee type information.
if (specializedEmitter || isPartiallyAppliedSuperMethod(uncurryLevel)) {
// We want to emit the arguments as fully-substituted values
// because that's what the specialized emitters expect.
Expand Down Expand Up @@ -5134,6 +5137,11 @@ emitSpecializedAccessorFunctionRef(SILGenFunction &gen,

namespace {

/// A builder class that creates the base argument for accessors.
///
/// *NOTE* All cleanups created inside of this builder on base arguments must be
/// formal access to ensure that we do not extend the lifetime of a guaranteed
/// base after the accessor is evaluated.
struct AccessorBaseArgPreparer final {
SILGenFunction &SGF;
SILLocation loc;
Expand Down Expand Up @@ -5200,9 +5208,9 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorAddressBaseArg() {
// The load can only be a take if the base is a +1 rvalue.
auto shouldTake = IsTake_t(base.hasCleanup());

base = SGF.emitLoad(loc, base.forward(SGF),
SGF.getTypeLowering(baseLoweredType), SGFContext(),
shouldTake);
base = SGF.emitFormalAccessLoad(loc, base.forward(SGF),
SGF.getTypeLowering(baseLoweredType),
SGFContext(), shouldTake);
return ArgumentSource(loc, RValue(SGF, loc, baseFormalType, base));
}

Expand Down Expand Up @@ -5239,7 +5247,7 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorObjectBaseArg() {

// We need to produce the value at +1 if it's going to be consumed.
if (selfParam.isConsumed() && !base.hasCleanup()) {
base = base.copyUnmanaged(SGF, loc);
base = base.formalAccessCopyUnmanaged(SGF, loc);
}

// If the parameter is indirect, we need to drop the value into
Expand Down
31 changes: 31 additions & 0 deletions lib/SILGen/SILGenBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,36 @@ ManagedValue SILGenBuilder::bufferForExpr(
return gen.emitManagedBufferWithCleanup(address);
}


ManagedValue SILGenBuilder::formalAccessBufferForExpr(
SILLocation loc, SILType ty, const TypeLowering &lowering,
SGFContext context, std::function<void(SILValue)> rvalueEmitter) {
// If we have a single-buffer "emit into" initialization, use that for the
// result.
SILValue address = context.getAddressForInPlaceInitialization();

// If we couldn't emit into the Initialization, emit into a temporary
// allocation.
if (!address) {
address = gen.emitTemporaryAllocation(loc, ty.getObjectType());
}

rvalueEmitter(address);

// If we have a single-buffer "emit into" initialization, use that for the
// result.
if (context.getAddressForInPlaceInitialization()) {
context.getEmitInto()->finishInitialization(gen);
return ManagedValue::forInContext();
}

// Add a cleanup for the temporary we allocated.
if (lowering.isTrivial())
return ManagedValue::forUnmanaged(address);

return gen.emitFormalAccessManagedBufferWithCleanup(loc, address);
}

ManagedValue SILGenBuilder::createUncheckedEnumData(SILLocation loc,
ManagedValue operand,
EnumElementDecl *element) {
Expand Down Expand Up @@ -458,3 +488,4 @@ ManagedValue SILGenBuilder::createEnum(SILLocation loc, ManagedValue payload,
return ManagedValue::forUnmanaged(result);
return gen.emitManagedRValueWithCleanup(result);
}

4 changes: 4 additions & 0 deletions lib/SILGen/SILGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ class SILGenBuilder : public SILBuilder {
EnumElementDecl *decl, SILType type);

ManagedValue createSemanticLoadBorrow(SILLocation loc, ManagedValue addr);

ManagedValue formalAccessBufferForExpr(
SILLocation loc, SILType ty, const TypeLowering &lowering,
SGFContext context, std::function<void(SILValue)> rvalueEmitter);
};

} // namespace Lowering
Expand Down
7 changes: 6 additions & 1 deletion lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
const TypeLowering &rvalueTL,
SGFContext C, IsTake_t isTake,
bool isGuaranteedValid = false);


ManagedValue emitFormalAccessLoad(SILLocation loc, SILValue addr,
const TypeLowering &rvalueTL, SGFContext C,
IsTake_t isTake,
bool isGuaranteedValid = false);

void emitAssignToLValue(SILLocation loc, RValue &&src,
LValue &&dest);
void emitAssignLValueToLValue(SILLocation loc,
Expand Down
79 changes: 69 additions & 10 deletions lib/SILGen/SILGenLValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,10 +694,11 @@ namespace {
RValue &&value, ManagedValue base) && override {
SILDeclRef setter = gen.getSetterDeclRef(decl, IsDirectAccessorUse);

FormalEvaluationScope scope(gen);
// Pass in just the setter.
auto args =
std::move(*this).prepareAccessorArgs(gen, loc, base, setter);

return gen.emitSetAccessor(loc, setter, substitutions,
std::move(args.base), IsSuper,
IsDirectAccessorUse,
Expand Down Expand Up @@ -940,6 +941,8 @@ namespace {
ManagedValue base, SGFContext c) && override {
SILDeclRef getter = gen.getGetterDeclRef(decl, IsDirectAccessorUse);

FormalEvaluationScope scope(gen);

auto args =
std::move(*this).prepareAccessorArgs(gen, loc, base, getter);

Expand Down Expand Up @@ -1131,13 +1134,16 @@ namespace {

SILDeclRef addressor = gen.getAddressorDeclRef(decl, accessKind,
IsDirectAccessorUse);
auto args =
std::move(*this).prepareAccessorArgs(gen, loc, base, addressor);
auto result = gen.emitAddressorAccessor(loc, addressor, substitutions,
std::move(args.base), IsSuper,
IsDirectAccessorUse,
std::move(args.subscripts),
SubstFieldType);
std::pair<ManagedValue, ManagedValue> result;
{
FormalEvaluationScope scope(gen);

auto args =
std::move(*this).prepareAccessorArgs(gen, loc, base, addressor);
result = gen.emitAddressorAccessor(
loc, addressor, substitutions, std::move(args.base), IsSuper,
IsDirectAccessorUse, std::move(args.subscripts), SubstFieldType);
}
switch (cast<FuncDecl>(addressor.getDecl())->getAddressorKind()) {
case AddressorKind::NotAddressor:
llvm_unreachable("not an addressor!");
Expand Down Expand Up @@ -2053,12 +2059,65 @@ ManagedValue SILGenFunction::emitLoad(SILLocation loc, SILValue addr,
// we can perform a +0 load of the address instead of materializing a +1
// value.
if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) {
return ManagedValue::forUnmanaged(B.createLoadBorrow(loc, addr));
return B.createLoadBorrow(loc, ManagedValue::forUnmanaged(addr));
}

// Load the loadable value, and retain it if we aren't taking it.
SILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake);
return emitManagedRValueWithCleanup(loadedV);
}

/// Load an r-value out of the given address.
///
/// \param rvalueTL - the type lowering for the type-of-rvalue
/// of the address
/// \param isGuaranteedValid - true if the value in this address
/// is guaranteed to be valid for the duration of the current
/// evaluation (see SGFContext::AllowGuaranteedPlusZero)
ManagedValue SILGenFunction::emitFormalAccessLoad(SILLocation loc,
SILValue addr,
const TypeLowering &rvalueTL,
SGFContext C, IsTake_t isTake,
bool isGuaranteedValid) {
// Get the lowering for the address type. We can avoid a re-lookup
// in the very common case of this being equivalent to the r-value
// type.
auto &addrTL = (addr->getType() == rvalueTL.getLoweredType().getAddressType()
? rvalueTL
: getTypeLowering(addr->getType()));

// Never do a +0 load together with a take.
bool isPlusZeroOk =
(isTake == IsNotTake && (isGuaranteedValid ? C.isGuaranteedPlusZeroOk()
: C.isImmediatePlusZeroOk()));

if (rvalueTL.isAddressOnly()) {
// If the client is cool with a +0 rvalue, the decl has an address-only
// type, and there are no conversions, then we can return this as a +0
// address RValue.
if (isPlusZeroOk && rvalueTL.getLoweredType() == addrTL.getLoweredType())
return ManagedValue::forUnmanaged(addr);

// Copy the address-only value.
return B.formalAccessBufferForExpr(
loc, rvalueTL.getLoweredType(), rvalueTL, C,
[&](SILValue addressForCopy) {
emitSemanticLoadInto(loc, addr, addrTL, addressForCopy, rvalueTL,
isTake, IsInitialization);
});
}

// Ok, this is something loadable. If this is a non-take access at plus zero,
// we can perform a +0 load of the address instead of materializing a +1
// value.
if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) {
return B.createFormalAccessLoadBorrow(loc,
ManagedValue::forUnmanaged(addr));
}

// Load the loadable value, and retain it if we aren't taking it.
SILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake);
return emitManagedRValueWithCleanup(loadedV, rvalueTL);
return emitFormalAccessManagedRValueWithCleanup(loc, loadedV);
}

static void emitUnloweredStoreOfCopy(SILGenBuilder &B, SILLocation loc,
Expand Down
3 changes: 2 additions & 1 deletion lib/SILGen/SILGenPoly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3121,7 +3121,8 @@ void SILGenFunction::emitProtocolWitness(Type selfType,

SILLocation loc(witness.getDecl());
FullExpr scope(Cleanups, CleanupLocation::get(loc));

FormalEvaluationScope formalEvalScope(*this);

auto witnessKind = getWitnessDispatchKind(selfType, witness, isFree);
auto thunkTy = F.getLoweredFunctionType();

Expand Down
1 change: 1 addition & 0 deletions test/SILGen/errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ class BaseThrowingInit : HasThrowingInit {
// CHECK: [[T0:%.*]] = load_borrow [[MARKED_BOX]]
// CHECK-NEXT: [[T1:%.*]] = ref_element_addr [[T0]] : $BaseThrowingInit, #BaseThrowingInit.subField
// CHECK-NEXT: assign %1 to [[T1]]
// CHECK-NEXT: end_borrow [[T0]] from [[MARKED_BOX]]
// Super delegation.
// CHECK-NEXT: [[T0:%.*]] = load [take] [[MARKED_BOX]]
// CHECK-NEXT: [[T2:%.*]] = upcast [[T0]] : $BaseThrowingInit to $HasThrowingInit
Expand Down
1 change: 1 addition & 0 deletions test/SILGen/guaranteed_self.swift
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ class LetFieldClass {
// CHECK-NEXT: [[KRAKEN:%.*]] = load_borrow [[KRAKEN_ADDR]]
// CHECK-NEXT: [[KRAKEN_METH:%.*]] = class_method [[KRAKEN]]
// CHECK-NEXT: apply [[KRAKEN_METH]]([[KRAKEN]])
// CHECK-NEXT: end_borrow [[KRAKEN]] from [[KRAKEN_ADDR]]
// CHECK-NEXT: [[KRAKEN_ADDR:%.*]] = ref_element_addr [[CLS]] : $LetFieldClass, #LetFieldClass.letk
// CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR]]
// CHECK: [[DESTROY_SHIP_FUN:%.*]] = function_ref @_T015guaranteed_self11destroyShipyAA6KrakenCF : $@convention(thin) (@owned Kraken) -> ()
Expand Down
2 changes: 1 addition & 1 deletion test/SILGen/let_decls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ struct LetPropertyStruct {
// CHECK: [[ABOX:%[0-9]+]] = alloc_box ${ var LetPropertyStruct }
// CHECK: [[A:%[0-9]+]] = project_box [[ABOX]]
// CHECK: store %0 to [trivial] [[A]] : $*LetPropertyStruct
// CHECK: [[STRUCT:%[0-9]+]] = load_borrow [[A]] : $*LetPropertyStruct
// CHECK: [[STRUCT:%[0-9]+]] = load [trivial] [[A]] : $*LetPropertyStruct
// CHECK: [[PROP:%[0-9]+]] = struct_extract [[STRUCT]] : $LetPropertyStruct, #LetPropertyStruct.lp
// CHECK: destroy_value [[ABOX]] : ${ var LetPropertyStruct }
// CHECK: return [[PROP]] : $Int
Expand Down
2 changes: 1 addition & 1 deletion test/SILGen/struct_resilience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public func functionWithMyResilientTypes(_ s: MySize, f: (MySize) -> MySize) ->
s2.w = s.w

// CHECK: [[RESULT_ADDR:%.*]] = struct_element_addr %1 : $*MySize, #MySize.h
// CHECK: [[RESULT:%.*]] = load_borrow [[RESULT_ADDR]] : $*Int
// CHECK: [[RESULT:%.*]] = load [trivial] [[RESULT_ADDR]] : $*Int
_ = s.h

// CHECK: [[BORROWED_CLOSURE:%.*]] = begin_borrow %2
Expand Down