Files
swift-mirror/lib/SILGen/SGFContext.h
Joe Groff fdc0e08d60 SILGen: Emit literal closures at the abstraction level of their context.
Literal closures are only ever directly referenced in the context of the expression they're written in,
so it's wasteful to emit them at their fully-substituted calling convention and then reabstract them if
they're passed directly to a generic function. Avoid this by saving the abstraction pattern of the context
before emitting the closure, and then lowering its main entry point's calling convention at that
level of abstraction. Generalize some of the prolog/epilog code to handle converting arguments and returns
to the correct representation for a different abstraction level.
2021-09-09 13:42:02 -07:00

188 lines
6.4 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();
}
/// Return the abstraction pattern of the context we're emitting into.
Optional<AbstractionPattern> getAbstractionPattern() const {
if (auto *init = getEmitInto()) {
return init->getAbstractionPattern();
}
return None;
}
};
using ValueProducerRef =
llvm::function_ref<ManagedValue(SILGenFunction &SGF, SILLocation loc,
SGFContext context)>;
} // end namespace Lowering
} // end namespace swift
#endif