mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Today, SILGenFunction::emitRValue assumes the caller will create any cleanup scopes that are needed to cleanup side-effects relating to the rvalue evaluation. The API also provides the ability for the caller to specify that a +0 rvalues is an "ok" result. The API then tries to produce a +0 rvalue and returns a +1 rvalue otherwise. These two properties create conflicting requirements on the caller since the caller does not know whether or not it should create a scope (if a +1 rvalue will be returned) or not (if a +0 rvalue would be returned). The key issue here is the optionality of returning a +0 rvalue. This change begins to resolve this difference by creating two separate APIs that guarantee to the caller whether or not a +0 or a +1 rvalue is returned and also creates local scopes for the caller as appropriate. So by using these APIs, the caller knows that the +0 or +1 rvalue that is returned has properly been put into the caller scope. So the caller no longer needs to create its own scopes anymore. emitPlusOneRValue is emitRValue except that it scopes the rvalue emission and then *pushes* the produced rvalue through the scope. emitPlusZeroRValue is currently a stub implementation that just calls emitPlusOneRValue and then borrows the resulting +1 RValue in the outer scope, creating the +0 RValue that was requested by the caller. rdar://33358110
180 lines
6.2 KiB
C++
180 lines
6.2 KiB
C++
//===--- SGFContext.h - Expression emission context -------------*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SILGEN_SGFCONTEXT_H
|
|
#define SWIFT_SILGEN_SGFCONTEXT_H
|
|
|
|
#include "Initialization.h"
|
|
|
|
namespace swift {
|
|
namespace Lowering {
|
|
|
|
/// Internal context information for the SILGenFunction visitor.
|
|
///
|
|
/// In general, emission methods which take an SGFContext indicate
|
|
/// that they've initialized the emit-into buffer (if they have) by
|
|
/// returning a "isInContext()" ManagedValue of whatever type. Callers who
|
|
/// propagate down an SGFContext that might have an emit-into buffer must be
|
|
/// aware of this.
|
|
///
|
|
/// Clients of emission routines that take an SGFContext can also specify that
|
|
/// they are ok getting back an RValue at +0 instead of requiring it to be at
|
|
/// +1. The client is then responsible for checking the ManagedValue to see if
|
|
/// it got back a ManagedValue at +0 or +1.
|
|
class SGFContext {
|
|
enum DesiredTransfer {
|
|
PlusOne,
|
|
ImmediatePlusZero,
|
|
GuaranteedPlusZero,
|
|
};
|
|
llvm::PointerIntPair<Initialization *, 2, DesiredTransfer> state;
|
|
public:
|
|
SGFContext() = default;
|
|
|
|
enum AllowImmediatePlusZero_t {
|
|
/// The client is okay with getting a +0 value and plans to use it
|
|
/// immediately.
|
|
///
|
|
/// For example, in this context, it would be okay to return +0
|
|
/// even for a load from a mutable variable, because the only way
|
|
/// the value could be invalidated before it's used is a race
|
|
/// condition.
|
|
AllowImmediatePlusZero
|
|
};
|
|
|
|
enum AllowGuaranteedPlusZero_t {
|
|
/// The client is okay with getting a +0 value as long as it's
|
|
/// guaranteed to last at least as long as the current evaluation.
|
|
/// (For expression evaluation, this generally means at least
|
|
/// until the end of the current statement.)
|
|
///
|
|
/// For example, in this context, it would be okay to return +0
|
|
/// for a reference to a local 'let' because that will last until
|
|
/// the 'let' goes out of scope. However, it would not be okay to
|
|
/// return +0 for a load from a mutable 'var', because that could
|
|
/// be mutated before the end of the statement.
|
|
AllowGuaranteedPlusZero
|
|
};
|
|
|
|
/// Creates an emitInto context that will store the result of the visited expr
|
|
/// into the given Initialization.
|
|
explicit SGFContext(Initialization *emitInto) : state(emitInto, PlusOne) {
|
|
}
|
|
|
|
/*implicit*/
|
|
SGFContext(AllowImmediatePlusZero_t) : state(nullptr, ImmediatePlusZero) {
|
|
}
|
|
|
|
/*implicit*/
|
|
SGFContext(AllowGuaranteedPlusZero_t) : state(nullptr, GuaranteedPlusZero) {
|
|
}
|
|
|
|
/// Returns a pointer to the Initialization that the current expression should
|
|
/// store its result to, or null if the expression should allocate temporary
|
|
/// storage for its result.
|
|
Initialization *getEmitInto() const {
|
|
return state.getPointer();
|
|
}
|
|
|
|
/// Try to get the address of the emit-into initialization if we can.
|
|
/// Otherwise, return an empty SILValue.
|
|
///
|
|
/// Note that, if this returns a non-empty address, the caller must
|
|
/// finish the emit-into initialization.
|
|
SILValue getAddressForInPlaceInitialization(SILGenFunction &SGF,
|
|
SILLocation loc) const {
|
|
if (auto *init = getEmitInto()) {
|
|
if (init->canPerformInPlaceInitialization())
|
|
return init->getAddressForInPlaceInitialization(SGF, loc);
|
|
}
|
|
return SILValue();
|
|
}
|
|
|
|
/// If getAddressForInPlaceInitialization did (or would have)
|
|
/// returned a non-null address, finish the initialization and
|
|
/// return true. Otherwise, return false.
|
|
bool finishInPlaceInitialization(SILGenFunction &SGF) const {
|
|
if (auto *init = getEmitInto()) {
|
|
if (init->canPerformInPlaceInitialization()) {
|
|
init->finishInitialization(SGF);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Try to get this context as a conversion initialization.
|
|
///
|
|
/// This does not commit the caller to anything, whether it succeeds
|
|
/// or fails.
|
|
ConvertingInitialization *getAsConversion() const {
|
|
if (auto init = getEmitInto())
|
|
return init->getAsConversion();
|
|
return nullptr;
|
|
}
|
|
|
|
/// Return true if a ManagedValue producer is allowed to return at
|
|
/// +0, given that it cannot guarantee that the value will be valid
|
|
/// until the end of the current evaluation.
|
|
bool isImmediatePlusZeroOk() const {
|
|
return state.getInt() == ImmediatePlusZero;
|
|
}
|
|
|
|
/// Return true if a ManagedValue producer is allowed to return at
|
|
/// +0 if it can guarantee that the value will be valid until the
|
|
/// end of the current evaluation.
|
|
bool isGuaranteedPlusZeroOk() const {
|
|
// Either ImmediatePlusZero or GuaranteedPlusZero is fine.
|
|
return state.getInt() >= ImmediatePlusZero;
|
|
}
|
|
|
|
/// Get a context for a sub-expression given that arbitrary side
|
|
/// effects may follow the subevaluation.
|
|
SGFContext withFollowingSideEffects() const {
|
|
SGFContext copy = *this;
|
|
if (copy.state.getInt() == ImmediatePlusZero) {
|
|
copy.state.setInt(GuaranteedPlusZero);
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
/// Get a context for a sub-expression where we plan to project out
|
|
/// a value. The Initialization is not okay to propagate down, but
|
|
/// the +0/+1-ness is.
|
|
SGFContext withFollowingProjection() const {
|
|
SGFContext result;
|
|
result.state.setInt(state.getInt());
|
|
return result;
|
|
}
|
|
|
|
/// Get a context for a sub-expression where we plan to evaluate arbitrary
|
|
/// side-effects. This means we propagate down the initialization, but
|
|
/// eliminates the +0/+1-ness.
|
|
SGFContext withSubExprSideEffects() const {
|
|
if (auto *init = getEmitInto()) {
|
|
return SGFContext(init);
|
|
}
|
|
|
|
return SGFContext();
|
|
}
|
|
};
|
|
|
|
using ValueProducerRef =
|
|
llvm::function_ref<ManagedValue(SILGenFunction &SGF, SILLocation loc,
|
|
SGFContext context)>;
|
|
|
|
} // end namespace Lowering
|
|
} // end namespace swift
|
|
|
|
#endif
|