Files
swift-mirror/include/swift/SIL/ApplySite.h

1016 lines
38 KiB
C++

//===--- ApplySite.h -------------------------------------*- mode: c++ -*--===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// This file defines utilities for working with "call-site like" SIL
/// instructions. We use the term "call-site" like since we handle partial
/// applications in our utilities.
///
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_APPLYSITE_H
#define SWIFT_SIL_APPLYSITE_H
#include "swift/AST/ExtInfo.h"
#include "swift/Basic/STLExtras.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "llvm/ADT/ArrayRef.h"
namespace swift {
class FullApplySite;
//===----------------------------------------------------------------------===//
// ApplySite
//===----------------------------------------------------------------------===//
struct ApplySiteKind {
enum innerty : std::underlying_type<SILInstructionKind>::type {
ApplyInst = unsigned(SILInstructionKind::ApplyInst),
PartialApplyInst = unsigned(SILInstructionKind::PartialApplyInst),
TryApplyInst = unsigned(SILInstructionKind::TryApplyInst),
BeginApplyInst = unsigned(SILInstructionKind::BeginApplyInst),
} value;
explicit ApplySiteKind(SILInstructionKind kind) {
auto newValue = ApplySiteKind::fromNodeKindHelper(kind);
assert(newValue && "Non apply site passed into ApplySiteKind");
value = newValue.value();
}
ApplySiteKind(innerty value) : value(value) {}
operator innerty() const { return value; }
static std::optional<ApplySiteKind> fromNodeKind(SILInstructionKind kind) {
if (auto innerTyOpt = ApplySiteKind::fromNodeKindHelper(kind))
return ApplySiteKind(*innerTyOpt);
return std::nullopt;
}
private:
static std::optional<innerty> fromNodeKindHelper(SILInstructionKind kind) {
switch (kind) {
case SILInstructionKind::ApplyInst:
return ApplySiteKind::ApplyInst;
case SILInstructionKind::PartialApplyInst:
return ApplySiteKind::PartialApplyInst;
case SILInstructionKind::TryApplyInst:
return ApplySiteKind::TryApplyInst;
case SILInstructionKind::BeginApplyInst:
return ApplySiteKind::BeginApplyInst;
default:
return std::nullopt;
}
}
};
/// An apply instruction.
class ApplySite {
SILInstruction *Inst;
protected:
explicit ApplySite(void *p) : Inst(static_cast<SILInstruction *>(p)) {}
public:
ApplySite() : Inst(nullptr) {}
explicit ApplySite(SILInstruction *inst)
: Inst(static_cast<SILInstruction *>(inst)) {
assert(classof(inst) && "not an apply instruction?");
}
ApplySite(ApplyInst *inst) : Inst(inst) {}
ApplySite(PartialApplyInst *inst) : Inst(inst) {}
ApplySite(TryApplyInst *inst) : Inst(inst) {}
ApplySite(BeginApplyInst *inst) : Inst(inst) {}
SILModule &getModule() const { return Inst->getModule(); }
static ApplySite isa(SILInstruction *inst) {
auto kind = ApplySiteKind::fromNodeKind(inst->getKind());
if (!kind)
return ApplySite();
switch (kind.value()) {
case ApplySiteKind::ApplyInst:
return ApplySite(cast<ApplyInst>(inst));
case ApplySiteKind::BeginApplyInst:
return ApplySite(cast<BeginApplyInst>(inst));
case ApplySiteKind::TryApplyInst:
return ApplySite(cast<TryApplyInst>(inst));
case ApplySiteKind::PartialApplyInst:
return ApplySite(cast<PartialApplyInst>(inst));
}
llvm_unreachable("covered switch");
}
static ApplySite isa(SILValue value) {
if (auto *inst = value->getDefiningInstruction())
return ApplySite::isa(inst);
return ApplySite();
}
ApplySiteKind getKind() const { return ApplySiteKind(Inst->getKind()); }
SILInstruction *operator*() const { return Inst; }
SILInstruction *operator->() const { return Inst; }
explicit operator bool() const { return Inst != nullptr; }
SILInstruction *getInstruction() const { return Inst; }
SILLocation getLoc() const { return Inst->getLoc(); }
const SILDebugScope *getDebugScope() const { return Inst->getDebugScope(); }
SILFunction *getFunction() const { return Inst->getFunction(); }
SILBasicBlock *getParent() const { return Inst->getParent(); }
#define FOREACH_IMPL_RETURN(OPERATION) \
do { \
switch (ApplySiteKind(Inst->getKind())) { \
case ApplySiteKind::ApplyInst: \
return cast<ApplyInst>(Inst)->OPERATION; \
case ApplySiteKind::BeginApplyInst: \
return cast<BeginApplyInst>(Inst)->OPERATION; \
case ApplySiteKind::PartialApplyInst: \
return cast<PartialApplyInst>(Inst)->OPERATION; \
case ApplySiteKind::TryApplyInst: \
return cast<TryApplyInst>(Inst)->OPERATION; \
} \
llvm_unreachable("covered switch"); \
} while (0)
/// Return the callee operand as a value.
SILValue getCallee() const { return getCalleeOperand()->get(); }
/// Return the callee operand.
Operand *getCalleeOperand() { FOREACH_IMPL_RETURN(getCalleeOperand()); }
/// Return the callee operand.
const Operand *getCalleeOperand() const {
FOREACH_IMPL_RETURN(getCalleeOperand());
}
/// Return the callee value by looking through function conversions until we
/// find a function_ref, partial_apply, or unrecognized callee value.
SILValue getCalleeOrigin() const { FOREACH_IMPL_RETURN(getCalleeOrigin()); }
/// Gets the referenced function by looking through partial apply,
/// convert_function, and thin to thick function until we find a function_ref.
SILFunction *getCalleeFunction() const {
FOREACH_IMPL_RETURN(getCalleeFunction());
}
bool isCalleeDynamicallyReplaceable() const {
FOREACH_IMPL_RETURN(isCalleeDynamicallyReplaceable());
}
/// Return the referenced function if the callee is a function_ref
/// instruction.
SILFunction *getReferencedFunctionOrNull() const {
FOREACH_IMPL_RETURN(getReferencedFunctionOrNull());
}
/// Return the referenced function if the callee is a function_ref like
/// instruction.
///
/// WARNING: This not necessarily the function that will be called at runtime.
/// If the callee is a (prev_)dynamic_function_ref the actual function called
/// might be different because it could be dynamically replaced at runtime.
///
/// If the client of this API wants to look at the content of the returned SIL
/// function it should call getReferencedFunctionOrNull() instead.
SILFunction *getInitiallyReferencedFunction() const {
FOREACH_IMPL_RETURN(getInitiallyReferencedFunction());
}
/// Should we optimize this call.
/// Calls to (previous_)dynamic_function_ref have a dynamic target function so
/// we should not optimize them.
bool canOptimize() const {
return !swift::isa<DynamicFunctionRefInst>(getCallee()) &&
!swift::isa<PreviousDynamicFunctionRefInst>(getCallee());
}
/// Return the type.
SILType getType() const {
return getSubstCalleeConv().getSILResultType(
getFunction()->getTypeExpansionContext());
}
/// Get the type of the callee without the applied substitutions.
CanSILFunctionType getOrigCalleeType() const {
return getCallee()->getType().castTo<SILFunctionType>();
}
/// Get the conventions of the callee without the applied substitutions.
SILFunctionConventions getOrigCalleeConv() const {
return SILFunctionConventions(getOrigCalleeType(), getModule());
}
/// Get the type of the callee with the applied substitutions.
CanSILFunctionType getSubstCalleeType() const {
return getSubstCalleeSILType().castTo<SILFunctionType>();
}
SILType getSubstCalleeSILType() const {
FOREACH_IMPL_RETURN(getSubstCalleeSILType());
}
void setSubstCalleeType(CanSILFunctionType t) {
FOREACH_IMPL_RETURN(setSubstCalleeType(t));
}
/// Get the conventions of the callee with the applied substitutions.
SILFunctionConventions getSubstCalleeConv() const {
return SILFunctionConventions(getSubstCalleeType(), getModule());
}
bool isAsync() const {
return getOrigCalleeType()->isAsync();
}
/// Returns true if the callee function is annotated with
/// @_semantics("programtermination_point")
bool isCalleeKnownProgramTerminationPoint() const {
FOREACH_IMPL_RETURN(isCalleeKnownProgramTerminationPoint());
}
/// Returns true if the callee function is annotated with
/// @_semantics("unavailable_code_reached")
bool isCalleeUnavailableCodeReached() const {
FOREACH_IMPL_RETURN(isCalleeUnavailableCodeReached());
}
/// Check if this is a call of a never-returning function.
bool isCalleeNoReturn() const { FOREACH_IMPL_RETURN(isCalleeNoReturn()); }
bool isCalleeThin() const {
switch (getSubstCalleeType()->getRepresentation()) {
case SILFunctionTypeRepresentation::CFunctionPointer:
case SILFunctionTypeRepresentation::CXXMethod:
case SILFunctionTypeRepresentation::Thin:
case SILFunctionTypeRepresentation::Method:
case SILFunctionTypeRepresentation::ObjCMethod:
case SILFunctionTypeRepresentation::WitnessMethod:
case SILFunctionTypeRepresentation::Closure:
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
return true;
case SILFunctionTypeRepresentation::Block:
case SILFunctionTypeRepresentation::Thick:
return false;
}
}
/// True if this application has generic substitutions.
bool hasSubstitutions() const { FOREACH_IMPL_RETURN(hasSubstitutions()); }
/// The substitutions used to bind the generic arguments of this function.
SubstitutionMap getSubstitutionMap() const {
FOREACH_IMPL_RETURN(getSubstitutionMap());
}
/// Return the associated specialization information.
const GenericSpecializationInformation *getSpecializationInfo() const {
FOREACH_IMPL_RETURN(getSpecializationInfo());
}
/// Return an operand list corresponding to the applied arguments.
MutableArrayRef<Operand> getArgumentOperands() const {
FOREACH_IMPL_RETURN(getArgumentOperands());
}
/// Return a list of applied argument values.
OperandValueArrayRef getArguments() const {
FOREACH_IMPL_RETURN(getArguments());
}
/// Return the number of applied arguments.
unsigned getNumArguments() const { FOREACH_IMPL_RETURN(getNumArguments()); }
/// Return the apply operand for the given applied argument index.
Operand &getArgumentRef(unsigned i) const { return getArgumentOperands()[i]; }
// The apply operand at the given index into the callee's function's
// arguments.
Operand &getArgumentRefAtCalleeArgIndex(unsigned i) const {
return getArgumentRef(i - getCalleeArgIndexOfFirstAppliedArg());
}
/// Return the ith applied argument.
SILValue getArgument(unsigned i) const { return getArguments()[i]; }
// The argument at the given index into the callee's function's arguments.
SILValue getArgumentAtCalleeArgIndex(unsigned i) const {
return getArgument(i - getCalleeArgIndexOfFirstAppliedArg());
}
/// Set the ith applied argument.
void setArgument(unsigned i, SILValue V) const {
getArgumentOperands()[i].set(V);
}
void setCallee(SILValue V) const {
unsigned calleeIndex = getCalleeOperand()->getOperandNumber();
getInstruction()->getAllOperands()[calleeIndex].set(V);
}
/// Return the operand index of the first applied argument.
unsigned getOperandIndexOfFirstArgument() const {
FOREACH_IMPL_RETURN(getArgumentOperandNumber());
}
#undef FOREACH_IMPL_RETURN
/// Returns true if \p oper is an argument operand and not the callee
/// operand.
bool isArgumentOperand(const Operand &oper) const {
return oper.getOperandNumber() >= getOperandIndexOfFirstArgument() &&
oper.getOperandNumber() < getOperandIndexOfFirstArgument() + getNumArguments();
}
/// Return the applied argument index for the given operand.
///
/// This corresponds to the index of the operand in the actual
/// SILFunctionType.
///
/// As a result this does not include indirect parameters, but it does include
/// implicit parameters (e.x.: the implicit isolated(any) parameter) that are
/// in the SIL type but are not in the AST type. To ignore such implicit
/// parameters, use getASTAppliedArgIndex.
unsigned getAppliedArgIndex(const Operand &oper) const {
assert(oper.getUser() == Inst);
assert(isArgumentOperand(oper));
return oper.getOperandNumber() - getOperandIndexOfFirstArgument();
}
/// Returns the applied AST argument index for the given operand.
///
/// This is guaranteed to return an index that can be used with AST function
/// types. This means it looks past the callee, indirect function types, as
/// well as any other implicit parameters like isolated(any).
///
/// NOTE: If we add more implicit parameters to SILFunction types that do not
/// appear at the AST level, this code needs to be updated.
unsigned getASTAppliedArgIndex(const Operand &oper) const {
// We rely on the assertions in getAppliedArgIndex to allow for us to assume
// that we have an argument operand. We check later that we do not have an
// isolated(any) parameter.
unsigned appliedArgIndex = getAppliedArgIndex(oper);
if (auto *pai = dyn_cast<PartialApplyInst>(Inst)) {
if (pai->getFunctionType()->getIsolation() ==
SILFunctionTypeIsolation::forErased()) {
assert(appliedArgIndex != 0 &&
"isolation(any) does not correspond to an AST argument");
appliedArgIndex -= 1;
}
}
return appliedArgIndex;
}
/// Return the callee's function argument index corresponding to the first
/// applied argument: 0 for full applies; >= 0 for partial applies.
unsigned getCalleeArgIndexOfFirstAppliedArg() const {
switch (ApplySiteKind(Inst->getKind())) {
case ApplySiteKind::ApplyInst:
case ApplySiteKind::BeginApplyInst:
case ApplySiteKind::TryApplyInst:
return 0;
case ApplySiteKind::PartialApplyInst:
// The arguments to partial_apply are a suffix of the partial_apply's
// callee. Note that getSubstCalleeConv is function type of the callee
// argument passed to this apply, not necessarily the function type of
// the underlying callee function (i.e. it is based on the `getCallee`
// type, not the `getCalleeOrigin` type).
//
// pa1 = partial_apply f(c) : $(a, b, c)
// pa2 = partial_apply pa1(b) : $(a, b)
// apply pa2(a)
return getSubstCalleeConv().getNumSILArguments() - getNumArguments();
}
llvm_unreachable("covered switch");
}
/// Return the callee's function argument index corresponding to the given
/// apply operand. Each function argument index identifies a
/// SILFunctionArgument in the callee and can be used as a
/// SILFunctionConvention argument index.
///
/// Note: Passing an applied argument index into SILFunctionConvention, as
/// opposed to a function argument index, is incorrect.
unsigned getCalleeArgIndex(const Operand &oper) const {
return getCalleeArgIndexOfFirstAppliedArg() + getAppliedArgIndex(oper);
}
/// Return the SILArgumentConvention for the given applied argument operand.
SILArgumentConvention getArgumentConvention(const Operand &oper) const {
unsigned calleeArgIdx =
getCalleeArgIndexOfFirstAppliedArg() + getAppliedArgIndex(oper);
return getSubstCalleeConv().getSILArgumentConvention(calleeArgIdx);
}
/// Return the SILArgumentConvention for the given applied argument operand at
/// the apply instruction.
///
/// For full applies, this is equivalent to `getArgumentConvention`. But for
/// a partial_apply, the argument ownership convention at the partial_apply
/// instruction itself is different from the argument convention of the
/// callee.
///
/// For details see the partial_apply documentation in SIL.rst.
SILArgumentConvention getCaptureConvention(const Operand &oper) const {
SILArgumentConvention conv = getArgumentConvention(oper);
auto *pai = dyn_cast<PartialApplyInst>(Inst);
if (!pai)
return conv;
switch (conv) {
case SILArgumentConvention::Indirect_Inout:
case SILArgumentConvention::Indirect_InoutAliasable:
case SILArgumentConvention::Pack_Inout:
return conv;
case SILArgumentConvention::Direct_Owned:
case SILArgumentConvention::Direct_Unowned:
case SILArgumentConvention::Direct_Guaranteed:
return pai->isOnStack() ? SILArgumentConvention::Direct_Guaranteed
: SILArgumentConvention::Direct_Owned;
case SILArgumentConvention::Indirect_In:
case SILArgumentConvention::Indirect_In_Guaranteed:
case SILArgumentConvention::Indirect_In_CXX:
return pai->isOnStack() ? SILArgumentConvention::Indirect_In_Guaranteed
: SILArgumentConvention::Indirect_In;
case SILArgumentConvention::Pack_Guaranteed:
case SILArgumentConvention::Pack_Owned:
return pai->isOnStack() ? SILArgumentConvention::Pack_Guaranteed
: SILArgumentConvention::Pack_Owned;
case SILArgumentConvention::Indirect_Out:
case SILArgumentConvention::Pack_Out:
llvm_unreachable("partial_apply cannot have an @out operand");
}
llvm_unreachable("covered switch");
}
/// Return true if 'self' is an applied argument.
bool hasSelfArgument() const {
switch (ApplySiteKind(Inst->getKind())) {
case ApplySiteKind::ApplyInst:
return cast<ApplyInst>(Inst)->hasSelfArgument();
case ApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(Inst)->hasSelfArgument();
case ApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(Inst)->hasSelfArgument();
case ApplySiteKind::PartialApplyInst:
llvm_unreachable("unhandled case");
}
llvm_unreachable("covered switch");
}
/// Return the applied 'self' argument value.
SILValue getSelfArgument() const {
switch (ApplySiteKind(Inst->getKind())) {
case ApplySiteKind::ApplyInst:
return cast<ApplyInst>(Inst)->getSelfArgument();
case ApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(Inst)->getSelfArgument();
case ApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(Inst)->getSelfArgument();
case ApplySiteKind::PartialApplyInst:
llvm_unreachable("unhandled case");
}
llvm_unreachable("covered switch");
}
/// Return the 'self' apply operand.
Operand &getSelfArgumentOperand() {
switch (ApplySiteKind(Inst->getKind())) {
case ApplySiteKind::ApplyInst:
return cast<ApplyInst>(Inst)->getSelfArgumentOperand();
case ApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(Inst)->getSelfArgumentOperand();
case ApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(Inst)->getSelfArgumentOperand();
case ApplySiteKind::PartialApplyInst:
llvm_unreachable("Unhandled cast");
}
llvm_unreachable("covered switch");
}
/// Return the sil_isolated operand if we have one.
Operand *getIsolatedArgumentOperandOrNullPtr() {
switch (ApplySiteKind(Inst->getKind())) {
case ApplySiteKind::ApplyInst:
return cast<ApplyInst>(Inst)->getIsolatedArgumentOperandOrNullPtr();
case ApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(Inst)->getIsolatedArgumentOperandOrNullPtr();
case ApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(Inst)->getIsolatedArgumentOperandOrNullPtr();
case ApplySiteKind::PartialApplyInst:
llvm_unreachable("Unhandled case");
}
llvm_unreachable("covered switch");
}
/// Return a list of applied arguments without self.
OperandValueArrayRef getArgumentsWithoutSelf() const {
switch (ApplySiteKind(Inst->getKind())) {
case ApplySiteKind::ApplyInst:
return cast<ApplyInst>(Inst)->getArgumentsWithoutSelf();
case ApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(Inst)->getArgumentsWithoutSelf();
case ApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(Inst)->getArgumentsWithoutSelf();
case ApplySiteKind::PartialApplyInst:
llvm_unreachable("Unhandled case");
}
llvm_unreachable("covered switch");
}
/// Return a list of applied operands of the apply without self.
ArrayRef<Operand> getOperandsWithoutSelf() const {
switch (ApplySiteKind(Inst->getKind())) {
case ApplySiteKind::ApplyInst:
return cast<ApplyInst>(Inst)->getOperandsWithoutSelf();
case ApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(Inst)->getOperandsWithoutSelf();
case ApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(Inst)->getOperandsWithoutSelf();
case ApplySiteKind::PartialApplyInst:
llvm_unreachable("Unhandled case");
}
llvm_unreachable("covered switch");
}
MutableArrayRef<Operand> getOperandsWithoutSelf() {
switch (ApplySiteKind(Inst->getKind())) {
case ApplySiteKind::ApplyInst:
return cast<ApplyInst>(Inst)->getOperandsWithoutSelf();
case ApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(Inst)->getOperandsWithoutSelf();
case ApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(Inst)->getOperandsWithoutSelf();
case ApplySiteKind::PartialApplyInst:
llvm_unreachable("Unhandled case");
}
llvm_unreachable("covered switch");
}
/// Returns true if \p op is an operand that passes an indirect
/// result argument to the apply site.
bool isIndirectResultOperand(const Operand &op) const;
/// Returns true if \p op is an operand that is passed as an indirect error
/// result.
bool isIndirectErrorResultOperand(const Operand &op) const;
ApplyOptions getApplyOptions() const {
switch (ApplySiteKind(getInstruction()->getKind())) {
case ApplySiteKind::ApplyInst:
return cast<ApplyInst>(Inst)->getApplyOptions();
case ApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(Inst)->getApplyOptions();
case ApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(Inst)->getApplyOptions();
case ApplySiteKind::PartialApplyInst:
return ApplyOptions();
}
llvm_unreachable("covered switch");
}
/// If this is a terminator apply site, then pass a builder to insert at the
/// first instruction of each successor to \p func. Otherwise, pass a builder
/// to insert at std::next(Inst).
///
/// The intention is that this abstraction will enable the compiler writer to
/// ignore whether or not an apply site is a terminator when inserting
/// instructions after an apply site. This results in eliminating unnecessary
/// if-else code otherwise required to handle such situations.
///
/// NOTE: We pass std::next() for begin_apply. If one wishes to insert code
/// /after/ the end_apply/abort_apply, please use instead
/// insertAfterApplication.
void insertAfterInvocation(function_ref<void(SILBuilder &)> func) const;
/// Pass a builder with insertion points that are guaranteed to be immediately
/// after this apply site has been applied.
///
/// For apply and try_apply, that means after the apply. For partial_apply,
/// that means after the partial_apply. For begin_apply, that means after its
/// end_apply and abort_apply instructions.
///
/// This is just like insertAfterInvocation except that if the full apply site
/// is a begin_apply, we pass the insertion points after the end_apply,
/// abort_apply rather than an insertion point right after the
/// begin_apply. For such functionality, please invoke insertAfterInvocation.
void insertAfterApplication(function_ref<void(SILBuilder &)> func) const;
/// Return whether the given apply is of a formally-throwing function
/// which is statically known not to throw.
bool isNonThrowing() const {
return getApplyOptions().contains(ApplyFlags::DoesNotThrow);
}
/// Return whether the given apply is of a formally-async function
/// which is statically known not to await.
bool isNonAsync() const {
return getApplyOptions().contains(ApplyFlags::DoesNotAwait);
}
/// Return the SILParameterInfo for this operand in the callee function.
SILParameterInfo getArgumentParameterInfo(const Operand &oper) const {
assert(!getArgumentConvention(oper).isIndirectOutParameter() &&
"Can only be applied to non-out parameters");
// The ParameterInfo is going to be the parameter in the caller.
unsigned calleeArgIndex = getCalleeArgIndex(oper);
return getSubstCalleeConv().getParamInfoForSILArg(calleeArgIndex);
}
bool isSending(const Operand &oper) const {
if (isIndirectErrorResultOperand(oper))
return false;
if (isIndirectResultOperand(oper))
return getSubstCalleeType()->hasSendingResult();
return getArgumentParameterInfo(oper).hasOption(SILParameterInfo::Sending);
}
/// Return true if 'operand' is addressable after type substitution in the
/// caller's context.
bool isAddressable(const Operand &operand) const;
static ApplySite getFromOpaqueValue(void *p) { return ApplySite(p); }
friend bool operator==(ApplySite lhs, ApplySite rhs) {
return lhs.getInstruction() == rhs.getInstruction();
}
friend bool operator!=(ApplySite lhs, ApplySite rhs) {
return lhs.getInstruction() != rhs.getInstruction();
}
static bool classof(const SILInstruction *inst) {
return bool(ApplySiteKind::fromNodeKind(inst->getKind()));
}
void dump() const LLVM_ATTRIBUTE_USED { getInstruction()->dump(); }
/// Form a FullApplySite. Note that it will be null if this apply site is not
/// in fact a FullApplySite.
FullApplySite asFullApplySite() const;
};
//===----------------------------------------------------------------------===//
// FullApplySite
//===----------------------------------------------------------------------===//
struct FullApplySiteKind {
enum innerty : std::underlying_type<SILInstructionKind>::type {
ApplyInst = unsigned(SILInstructionKind::ApplyInst),
TryApplyInst = unsigned(SILInstructionKind::TryApplyInst),
BeginApplyInst = unsigned(SILInstructionKind::BeginApplyInst),
} value;
explicit FullApplySiteKind(SILInstructionKind kind) {
auto fullApplySiteKind = FullApplySiteKind::fromNodeKindHelper(kind);
assert(fullApplySiteKind && "SILNodeKind is not a FullApplySiteKind?!");
value = fullApplySiteKind.value();
}
FullApplySiteKind(innerty value) : value(value) {}
operator innerty() const { return value; }
static std::optional<FullApplySiteKind>
fromNodeKind(SILInstructionKind kind) {
if (auto innerOpt = FullApplySiteKind::fromNodeKindHelper(kind))
return FullApplySiteKind(*innerOpt);
return std::nullopt;
}
private:
static std::optional<innerty> fromNodeKindHelper(SILInstructionKind kind) {
switch (kind) {
case SILInstructionKind::ApplyInst:
return FullApplySiteKind::ApplyInst;
case SILInstructionKind::TryApplyInst:
return FullApplySiteKind::TryApplyInst;
case SILInstructionKind::BeginApplyInst:
return FullApplySiteKind::BeginApplyInst;
default:
return std::nullopt;
}
}
};
/// A full function application.
class FullApplySite : public ApplySite {
explicit FullApplySite(void *p) : ApplySite(p) {}
public:
FullApplySite() : ApplySite() {}
explicit FullApplySite(SILInstruction *inst) : ApplySite(inst) {
assert(classof(inst) && "not an apply instruction?");
}
FullApplySite(ApplyInst *inst) : ApplySite(inst) {}
FullApplySite(BeginApplyInst *inst) : ApplySite(inst) {}
FullApplySite(TryApplyInst *inst) : ApplySite(inst) {}
static FullApplySite isa(SILInstruction *inst) {
auto kind = FullApplySiteKind::fromNodeKind(inst->getKind());
if (!kind)
return FullApplySite();
switch (kind.value()) {
case FullApplySiteKind::ApplyInst:
return FullApplySite(cast<ApplyInst>(inst));
case FullApplySiteKind::BeginApplyInst:
return FullApplySite(cast<BeginApplyInst>(inst));
case FullApplySiteKind::TryApplyInst:
return FullApplySite(cast<TryApplyInst>(inst));
}
llvm_unreachable("covered switch");
}
static FullApplySite isa(SILValue value) {
if (auto *inst = value->getDefiningInstruction())
return FullApplySite::isa(inst);
return FullApplySite();
}
FullApplySiteKind getKind() const {
return FullApplySiteKind(getInstruction()->getKind());
}
bool hasIndirectSILResults() const {
return getSubstCalleeConv().hasIndirectSILResults();
}
/// Get the SIL value that represents all of the given call's results. For a
/// single direct result, returns the actual result. For multiple results,
/// returns a pseudo-result tuple. The tuple has no storage of its own. The
/// real results must be extracted from it.
///
/// For ApplyInst, returns the single-value instruction itself.
///
/// For TryApplyInst returns the continuation block argument.
///
/// For BeginApplyInst, returns an invalid value. For coroutines, there is no
/// single value representing all results. Yielded values are generally
/// handled differently since they have the convention of incoming arguments.
SILValue getResult() const {
switch (getKind()) {
case FullApplySiteKind::ApplyInst:
return SILValue(cast<ApplyInst>(getInstruction()));
case FullApplySiteKind::BeginApplyInst: {
return SILValue();
}
case FullApplySiteKind::TryApplyInst: {
auto *normalBlock = cast<TryApplyInst>(getInstruction())->getNormalBB();
assert(normalBlock->getNumArguments() == 1 &&
"Expected try apply to have a single result");
return normalBlock->getArgument(0);
}
}
llvm_unreachable("Covered switch isn't covered?!");
}
unsigned getNumIndirectSILResults() const {
return getSubstCalleeConv().getNumIndirectSILResults();
}
unsigned getNumIndirectSILErrorResults() const {
return getSubstCalleeConv().getNumIndirectSILErrorResults();
}
OperandValueArrayRef getIndirectSILResults() const {
return getArguments().slice(0, getNumIndirectSILResults());
}
OperandValueArrayRef getIndirectSILErrorResults() const {
return getArguments().slice(getNumIndirectSILResults(),
getNumIndirectSILErrorResults());
}
OperandValueArrayRef getArgumentsWithoutIndirectResults() const {
return getArguments().slice(getNumIndirectSILResults() +
getNumIndirectSILErrorResults());
}
MutableArrayRef<Operand> getOperandsWithoutIndirectResults() const {
return getArgumentOperands().slice(getNumIndirectSILResults() +
getNumIndirectSILErrorResults());
}
MutableArrayRef<Operand> getOperandsWithoutIndirectResultsOrSelf() const {
auto ops = getOperandsWithoutIndirectResults();
if (!hasSelfArgument())
return ops;
return ops.drop_back();
}
InoutArgumentRange getInoutArguments() const {
switch (getKind()) {
case FullApplySiteKind::ApplyInst:
return cast<ApplyInst>(getInstruction())->getInoutArguments();
case FullApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(getInstruction())->getInoutArguments();
case FullApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(getInstruction())->getInoutArguments();
}
llvm_unreachable("invalid apply kind");
}
AutoDiffSemanticResultArgumentRange getAutoDiffSemanticResultArguments() const {
switch (getKind()) {
case FullApplySiteKind::ApplyInst:
return cast<ApplyInst>(getInstruction())->getAutoDiffSemanticResultArguments();
case FullApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(getInstruction())->getAutoDiffSemanticResultArguments();
case FullApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(getInstruction())->getAutoDiffSemanticResultArguments();
}
llvm_unreachable("invalid apply kind");
}
/// Returns true if \p op is the callee operand of this apply site
/// and not an argument operand.
bool isCalleeOperand(const Operand &op) const {
return op.getOperandNumber() < getOperandIndexOfFirstArgument();
}
/// Is this an ApplySite that begins the evaluation of a coroutine.
bool beginsCoroutineEvaluation() const {
switch (getKind()) {
case FullApplySiteKind::ApplyInst:
case FullApplySiteKind::TryApplyInst:
return false;
case FullApplySiteKind::BeginApplyInst:
return true;
}
llvm_unreachable("Covered switch isn't covered?!");
}
/// Returns true if \p op is an operand that passes an indirect
/// result argument to the apply site.
bool isIndirectResultOperand(const Operand &op) const {
return isArgumentOperand(op)
&& (getCalleeArgIndex(op) < getNumIndirectSILResults());
}
/// Returns true if \p op is an operand that passes an indirect
/// result argument to the apply site.
bool isIndirectErrorResultOperand(const Operand &op) const {
return isArgumentOperand(op)
&& (getCalleeArgIndex(op) >= getNumIndirectSILResults())
&& (getCalleeArgIndex(op) < getNumIndirectSILResults() + getNumIndirectSILErrorResults());
}
std::optional<ApplyIsolationCrossing> getIsolationCrossing() const {
switch (getKind()) {
case FullApplySiteKind::ApplyInst:
return cast<ApplyInst>(**this)->getIsolationCrossing();
case FullApplySiteKind::TryApplyInst:
return cast<TryApplyInst>(**this)->getIsolationCrossing();
case FullApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(**this)->getIsolationCrossing();
}
}
/// Return the applied argument index for the given operand ignoring indirect
/// results.
///
/// So for instance:
///
/// apply %f(%result, %0, %1, %2, ...).
unsigned getAppliedArgIndexWithoutIndirectResults(const Operand &oper) const {
assert(oper.getUser() == **this);
assert(isArgumentOperand(oper));
return getAppliedArgIndex(oper) - getNumIndirectSILResults() -
getNumIndirectSILErrorResults();
}
static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); }
static bool classof(const SILInstruction *inst) {
return bool(FullApplySiteKind::fromNodeKind(inst->getKind()));
}
};
} // namespace swift
namespace llvm {
template<>
struct PointerLikeTypeTraits<swift::ApplySite> {
public:
static inline void *getAsVoidPointer(swift::ApplySite apply) {
return (void*)apply.getInstruction();
}
static inline swift::ApplySite getFromVoidPointer(void *pointer) {
return swift::ApplySite((swift::SILInstruction*)pointer);
}
enum { NumLowBitsAvailable =
PointerLikeTypeTraits<swift::SILNode *>::NumLowBitsAvailable };
};
template<>
struct PointerLikeTypeTraits<swift::FullApplySite> {
public:
static inline void *getAsVoidPointer(swift::FullApplySite apply) {
return (void*)apply.getInstruction();
}
static inline swift::FullApplySite getFromVoidPointer(void *pointer) {
return swift::FullApplySite((swift::SILInstruction*)pointer);
}
enum { NumLowBitsAvailable =
PointerLikeTypeTraits<swift::SILNode *>::NumLowBitsAvailable };
};
// An ApplySite casts like a SILInstruction*.
template <> struct simplify_type<const ::swift::ApplySite> {
using SimpleType = ::swift::SILInstruction *;
static SimpleType getSimplifiedValue(const ::swift::ApplySite &Val) {
return Val.getInstruction();
}
};
template <>
struct simplify_type<::swift::ApplySite>
: public simplify_type<const ::swift::ApplySite> {};
template <>
struct simplify_type<::swift::FullApplySite>
: public simplify_type<const ::swift::ApplySite> {};
template <>
struct simplify_type<const ::swift::FullApplySite>
: public simplify_type<const ::swift::ApplySite> {};
template <> struct DenseMapInfo<::swift::ApplySite> {
static ::swift::ApplySite getEmptyKey() {
return ::swift::ApplySite::getFromOpaqueValue(
llvm::DenseMapInfo<void *>::getEmptyKey());
}
static ::swift::ApplySite getTombstoneKey() {
return ::swift::ApplySite::getFromOpaqueValue(
llvm::DenseMapInfo<void *>::getTombstoneKey());
}
static unsigned getHashValue(::swift::ApplySite AS) {
auto *I = AS.getInstruction();
return DenseMapInfo<::swift::SILInstruction *>::getHashValue(I);
}
static bool isEqual(::swift::ApplySite LHS, ::swift::ApplySite RHS) {
return LHS == RHS;
}
};
template <> struct DenseMapInfo<::swift::FullApplySite> {
static ::swift::FullApplySite getEmptyKey() {
return ::swift::FullApplySite::getFromOpaqueValue(
llvm::DenseMapInfo<void *>::getEmptyKey());
}
static ::swift::FullApplySite getTombstoneKey() {
return ::swift::FullApplySite::getFromOpaqueValue(
llvm::DenseMapInfo<void *>::getTombstoneKey());
}
static unsigned getHashValue(::swift::FullApplySite AS) {
auto *I = AS.getInstruction();
return DenseMapInfo<::swift::SILInstruction *>::getHashValue(I);
}
static bool isEqual(::swift::FullApplySite LHS, ::swift::FullApplySite RHS) {
return LHS == RHS;
}
};
} // namespace llvm
//===----------------------------------------------------------------------===//
// Inline Definitions to work around Forward Declaration
//===----------------------------------------------------------------------===//
namespace swift {
inline FullApplySite ApplySite::asFullApplySite() const {
return FullApplySite::isa(getInstruction());
}
inline bool ApplySite::isIndirectResultOperand(const Operand &op) const {
auto fas = asFullApplySite();
if (!fas)
return false;
return fas.isIndirectResultOperand(op);
}
inline bool ApplySite::isIndirectErrorResultOperand(const Operand &op) const {
auto fas = asFullApplySite();
if (!fas)
return false;
return fas.isIndirectErrorResultOperand(op);
}
} // namespace swift
#endif