//===--- 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 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; } // end namespace Lowering } // end namespace swift #endif