//===--- 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::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 fromNodeKind(SILInstructionKind kind) { if (auto innerTyOpt = ApplySiteKind::fromNodeKindHelper(kind)) return ApplySiteKind(*innerTyOpt); return std::nullopt; } private: static std::optional 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(p)) {} public: ApplySite() : Inst(nullptr) {} explicit ApplySite(SILInstruction *inst) : Inst(static_cast(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(inst)); case ApplySiteKind::BeginApplyInst: return ApplySite(cast(inst)); case ApplySiteKind::TryApplyInst: return ApplySite(cast(inst)); case ApplySiteKind::PartialApplyInst: return ApplySite(cast(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(Inst)->OPERATION; \ case ApplySiteKind::BeginApplyInst: \ return cast(Inst)->OPERATION; \ case ApplySiteKind::PartialApplyInst: \ return cast(Inst)->OPERATION; \ case ApplySiteKind::TryApplyInst: \ return cast(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(getCallee()) && !swift::isa(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(); } /// 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(); } 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 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(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(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(Inst)->hasSelfArgument(); case ApplySiteKind::BeginApplyInst: return cast(Inst)->hasSelfArgument(); case ApplySiteKind::TryApplyInst: return cast(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(Inst)->getSelfArgument(); case ApplySiteKind::BeginApplyInst: return cast(Inst)->getSelfArgument(); case ApplySiteKind::TryApplyInst: return cast(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(Inst)->getSelfArgumentOperand(); case ApplySiteKind::BeginApplyInst: return cast(Inst)->getSelfArgumentOperand(); case ApplySiteKind::TryApplyInst: return cast(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(Inst)->getIsolatedArgumentOperandOrNullPtr(); case ApplySiteKind::BeginApplyInst: return cast(Inst)->getIsolatedArgumentOperandOrNullPtr(); case ApplySiteKind::TryApplyInst: return cast(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(Inst)->getArgumentsWithoutSelf(); case ApplySiteKind::BeginApplyInst: return cast(Inst)->getArgumentsWithoutSelf(); case ApplySiteKind::TryApplyInst: return cast(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 getOperandsWithoutSelf() const { switch (ApplySiteKind(Inst->getKind())) { case ApplySiteKind::ApplyInst: return cast(Inst)->getOperandsWithoutSelf(); case ApplySiteKind::BeginApplyInst: return cast(Inst)->getOperandsWithoutSelf(); case ApplySiteKind::TryApplyInst: return cast(Inst)->getOperandsWithoutSelf(); case ApplySiteKind::PartialApplyInst: llvm_unreachable("Unhandled case"); } llvm_unreachable("covered switch"); } MutableArrayRef getOperandsWithoutSelf() { switch (ApplySiteKind(Inst->getKind())) { case ApplySiteKind::ApplyInst: return cast(Inst)->getOperandsWithoutSelf(); case ApplySiteKind::BeginApplyInst: return cast(Inst)->getOperandsWithoutSelf(); case ApplySiteKind::TryApplyInst: return cast(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(Inst)->getApplyOptions(); case ApplySiteKind::BeginApplyInst: return cast(Inst)->getApplyOptions(); case ApplySiteKind::TryApplyInst: return cast(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 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 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::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 fromNodeKind(SILInstructionKind kind) { if (auto innerOpt = FullApplySiteKind::fromNodeKindHelper(kind)) return FullApplySiteKind(*innerOpt); return std::nullopt; } private: static std::optional 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(inst)); case FullApplySiteKind::BeginApplyInst: return FullApplySite(cast(inst)); case FullApplySiteKind::TryApplyInst: return FullApplySite(cast(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(getInstruction())); case FullApplySiteKind::BeginApplyInst: { return SILValue(); } case FullApplySiteKind::TryApplyInst: { auto *normalBlock = cast(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 getOperandsWithoutIndirectResults() const { return getArgumentOperands().slice(getNumIndirectSILResults() + getNumIndirectSILErrorResults()); } MutableArrayRef getOperandsWithoutIndirectResultsOrSelf() const { auto ops = getOperandsWithoutIndirectResults(); if (!hasSelfArgument()) return ops; return ops.drop_back(); } InoutArgumentRange getInoutArguments() const { switch (getKind()) { case FullApplySiteKind::ApplyInst: return cast(getInstruction())->getInoutArguments(); case FullApplySiteKind::TryApplyInst: return cast(getInstruction())->getInoutArguments(); case FullApplySiteKind::BeginApplyInst: return cast(getInstruction())->getInoutArguments(); } llvm_unreachable("invalid apply kind"); } AutoDiffSemanticResultArgumentRange getAutoDiffSemanticResultArguments() const { switch (getKind()) { case FullApplySiteKind::ApplyInst: return cast(getInstruction())->getAutoDiffSemanticResultArguments(); case FullApplySiteKind::TryApplyInst: return cast(getInstruction())->getAutoDiffSemanticResultArguments(); case FullApplySiteKind::BeginApplyInst: return cast(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 getIsolationCrossing() const { switch (getKind()) { case FullApplySiteKind::ApplyInst: return cast(**this)->getIsolationCrossing(); case FullApplySiteKind::TryApplyInst: return cast(**this)->getIsolationCrossing(); case FullApplySiteKind::BeginApplyInst: return cast(**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(); } std::optional getActorIsolation() const { if (auto isolation = getIsolationCrossing(); isolation && isolation->getCalleeIsolation()) return isolation->getCalleeIsolation(); auto *calleeFunction = getCalleeFunction(); if (!calleeFunction) return {}; return calleeFunction->getActorIsolation(); } bool isCallerIsolationInheriting() const { auto isolation = getActorIsolation(); return isolation && isolation->isCallerIsolationInheriting(); } 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 { 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::NumLowBitsAvailable }; }; template<> struct PointerLikeTypeTraits { 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::NumLowBitsAvailable }; }; // An ApplySite casts like a SILInstruction*. template <> struct simplify_type { using SimpleType = ::swift::SILInstruction *; static SimpleType getSimplifiedValue(const ::swift::ApplySite &Val) { return Val.getInstruction(); } }; template <> struct simplify_type<::swift::ApplySite> : public simplify_type {}; template <> struct simplify_type<::swift::FullApplySite> : public simplify_type {}; template <> struct simplify_type : public simplify_type {}; template <> struct DenseMapInfo<::swift::ApplySite> { static ::swift::ApplySite getEmptyKey() { return ::swift::ApplySite::getFromOpaqueValue( llvm::DenseMapInfo::getEmptyKey()); } static ::swift::ApplySite getTombstoneKey() { return ::swift::ApplySite::getFromOpaqueValue( llvm::DenseMapInfo::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::getEmptyKey()); } static ::swift::FullApplySite getTombstoneKey() { return ::swift::FullApplySite::getFromOpaqueValue( llvm::DenseMapInfo::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