blob: a4e39b98b2c7fc4b281f34d8dd44af12791e587f [file] [log] [blame]
//===--- Scope.cpp --------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "Scope.h"
#include "swift/Basic/Range.h"
using namespace swift;
using namespace Lowering;
ManagedValue Scope::popPreservingValue(ManagedValue mv) {
// If we have a value, make sure that it is an object. The reason why is
// that we want to make sure that we are not forwarding a cleanup for a
// stack location that will be destroyed by this scope.
assert(mv && mv.getType().isObject() &&
(mv.getType().isTrivial(cleanups.SGF.getModule()) ||
mv.getOwnershipKind() == ValueOwnershipKind::Trivial ||
mv.hasCleanup()));
CleanupCloner cloner(cleanups.SGF, mv);
SILValue value = mv.forward(cleanups.SGF);
pop();
return cloner.clone(value);
}
// Since we have an RValue, we know that RValue invariants imply that all
// subvalues that are addresses must be address only types. Such address only
// types if they are +1 rvalues should be independent of any values from outside
// the +1 rvalue emission (that is if someone else has a reference to the
// address only type, we should have produced a copy). This means that it is
// safe to move the value into new memory that is guaranteed to live through the
// scope being pushed. As an additional complication due to SIL enforcing stack
// ordering, we can not use a temporary stack location since any stack locations
// that are inside the scope will be cleaned up while such a scope jumping stack
// is still alive (violating stack ordering). Instead we use an alloc_box to
// store the new value. allocbox-to-stack will then reorder/expand the stack
// lifetimes to resolve the issues.
static void lifetimeExtendAddressOnlyRValueSubValues(
SILGenFunction &SGF, SILLocation loc,
llvm::SmallVectorImpl<SILValue> &values,
llvm::SmallVectorImpl<SILValue> &lifetimeExtendingBoxes) {
for (SILValue &v : values) {
// If v is not an address, it isn't interesting, continue.
if (!v->getType().isAddress()) {
continue;
}
// Otherwise, create the box and move the address only value into the box.
assert(v->getType().isAddressOnly(SGF.getModule()) &&
"RValue invariants imply that all RValue subtypes that are "
"addresses must be address only.");
auto boxTy = SILBoxType::get(v->getType().getSwiftRValueType());
SILValue box = SGF.B.createAllocBox(loc, boxTy);
SILValue addr = SGF.B.createProjectBox(loc, box, 0);
SGF.B.createCopyAddr(loc, v, addr, IsTake, IsInitialization);
// Then save the box so we create the box destroy in the caller and
// overwrite v with the project box since that is where the value is now.
lifetimeExtendingBoxes.emplace_back(box);
v = addr;
}
}
RValue Scope::popPreservingValue(RValue &&rv) {
auto &SGF = cleanups.SGF;
assert(rv.isPlusOne(SGF) && "Can only push plus one rvalues through a scope");
// Perform a quick check if we have an incontext value. If so, just pop and
// return rv.
if (rv.isInContext()) {
pop();
return std::move(rv);
}
// After this point, we should have /no/ special states.
assert(!rv.isInSpecialState());
// Ok, we have a normal RValue. Gather all of the data that we need to
// recreate the RValue in the outer scope.
CanType type = rv.type;
unsigned numEltsRemaining = rv.elementsToBeAdded;
CleanupCloner cloner(SGF, rv);
llvm::SmallVector<SILValue, 4> values;
std::move(rv).forwardAll(SGF, values);
// Lifetime any address only values that we may have.
llvm::SmallVector<SILValue, 4> lifetimeExtendingBoxes;
lifetimeExtendAddressOnlyRValueSubValues(SGF, loc, values,
lifetimeExtendingBoxes);
// Then pop the cleanups.
pop();
// Then create cleanups for any lifetime extending boxes that we may have to
// ensure that the boxes are cleaned up /after/ the value stored in the
// box. We assume that our values will be destroyed via a destroy_addr or the
// like /before/ the end of our box's lifetime, implying that the value inside
// the box should be uninitialized when the box is destroyed, so it is
// important that we use a dealloc_box.
for (auto v : lifetimeExtendingBoxes) {
SGF.enterDeallocBoxCleanup(v);
}
// Reconstruct the managed values from the underlying sil values in the outer
// scope. Since the RValue wants a std::vector value, we use that instead.
std::vector<ManagedValue> managedValues;
std::transform(
values.begin(), values.end(), std::back_inserter(managedValues),
[&cloner](SILValue v) -> ManagedValue { return cloner.clone(v); });
// And then assemble the managed values into a rvalue.
return RValue(SGF, std::move(managedValues), type, numEltsRemaining);
}
void Scope::popImpl() {
cleanups.stack.checkIterator(depth);
cleanups.stack.checkIterator(cleanups.innermostScope);
assert(cleanups.innermostScope == depth && "popping scopes out of order");
cleanups.innermostScope = savedInnermostScope;
cleanups.endScope(depth, loc);
cleanups.stack.checkIterator(cleanups.innermostScope);
cleanups.popTopDeadCleanups(cleanups.innermostScope);
}