Files
swift-mirror/include/swift/SIL/ApplySite.h
Michael Gottesman c3d445831b [region-isolation] Fix an off by one error when mapping AST capture indices to SIL level parameter indices.
This problem comes up with the following example:

```swift
class A {
    var description = ""
}

class B {
    let a = A()

    func b() {
        let asdf = ""
        Task { @MainActor in
            a.description = asdf // Sending 'asdf' risks causing data races
        }
    }
}
```

The specific issue is that the closure we generate actually includes an
implicit(any) parameter at the SIL level which occurs after the callee operand
but before the captures. This caused the captured variable index from the AST
and the one we compute from the partial_apply to differ by 1. So we need to
subtract 1 in such a case. That is why we used to print 'asdf' instead of 'a'
above.

DISCUSSION: This shows an interesting difference between SIL applied arg indices
and AST indices. SIL applied arg indices would include the implicit(any)
parameter since it is a parameter in the SIL function type. In contrast, this
doesn't show up in the formal AST parameters or captures. To make it easier to
reason about this, I added a new API to ApplySite called
ApplySite::getASTAppliedArgIndex and added large comments to
getASTAppliedArgIndex and getAppliedArgIndex that explains the issue.

rdar://136593706
https://github.com/swiftlang/swift/issues/76648
2024-10-30 18:32:45 -07:00

1012 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::Erased) {
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);
}
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