Files
swift-mirror/include/swift/SIL/ApplySite.h
Andrew Trick b1dba2554e Introduce OperandOwnership to classify OSSA uses.
Migrating to this classification was made easy by the recent rewrite
of the OSSA constraint model. It's also consistent with
instruction-level abstractions for working with different kinds of
OperandOwnership that are being designed.

This classification vastly simplifies OSSA passes that rewrite OSSA
live ranges, making it straightforward to reason about completeness
and correctness. It will allow a simple utility to canonicalize OSSA
live ranges on-the-fly.

This avoids the need for OSSA-based utilities and passes to hard-code
SIL opcodes. This will allow several of those unmaintainable pieces of
code to be replaced with a trivial OperandOwnership check.

It's extremely important for SIL maintainers to see a list of all SIL
opcodes associated with a simple OSSA classification and set of
well-specified rules for each opcode class, without needing to guess
or reverse-engineer the meaning from the implementation. This
classification does that while eliminating a pile of unreadable
macros.

This classification system is the model that CopyPropagation was
initially designed to use. Now, rather than relying on a separate
pass, a simple, lightweight utility will canonicalize OSSA
live ranges.

The major problem with writing optimizations based on OperandOwnership
is that some operations don't follow structural OSSA requirements,
such as project_box and unchecked_ownership_conversion. Those are
classified as PointerEscape which prevents the compiler from reasoning
about, or rewriting the OSSA live range.

Functional Changes:

As a side effect, this corrects many operand constraints that should
in fact require trivial operand values.
2020-12-16 01:58:53 -08:00

734 lines
27 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/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 {
#define APPLYSITE_INST(ID, PARENT) ID = unsigned(SILInstructionKind::ID),
#include "swift/SIL/SILNodes.def"
} value;
explicit ApplySiteKind(SILInstructionKind kind) {
auto newValue = ApplySiteKind::fromNodeKindHelper(kind);
assert(newValue && "Non apply site passed into ApplySiteKind");
value = newValue.getValue();
}
ApplySiteKind(innerty value) : value(value) {}
operator innerty() const { return value; }
static Optional<ApplySiteKind> fromNodeKind(SILInstructionKind kind) {
if (auto innerTyOpt = ApplySiteKind::fromNodeKindHelper(kind))
return ApplySiteKind(*innerTyOpt);
return None;
}
private:
static Optional<innerty> fromNodeKindHelper(SILInstructionKind kind) {
switch (kind) {
#define APPLYSITE_INST(ID, PARENT) \
case SILInstructionKind::ID: \
return ApplySiteKind::ID;
#include "swift/SIL/SILNodes.def"
default:
return None;
}
}
};
/// 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(SILNode *node) {
auto *i = dyn_cast<SILInstruction>(node);
if (!i)
return ApplySite();
auto kind = ApplySiteKind::fromNodeKind(i->getKind());
if (!kind)
return ApplySite();
switch (kind.getValue()) {
case ApplySiteKind::ApplyInst:
return ApplySite(cast<ApplyInst>(node));
case ApplySiteKind::BeginApplyInst:
return ApplySite(cast<BeginApplyInst>(node));
case ApplySiteKind::TryApplyInst:
return ApplySite(cast<TryApplyInst>(node));
case ApplySiteKind::PartialApplyInst:
return ApplySite(cast<PartialApplyInst>(node));
}
llvm_unreachable("covered switch");
}
ApplySiteKind getKind() const { return ApplySiteKind(Inst->getKind()); }
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());
}
/// 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 !DynamicFunctionRefInst::classof(getCallee()) &&
!PreviousDynamicFunctionRefInst::classof(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());
}
/// 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::Thin:
case SILFunctionTypeRepresentation::Method:
case SILFunctionTypeRepresentation::ObjCMethod:
case SILFunctionTypeRepresentation::WitnessMethod:
case SILFunctionTypeRepresentation::Closure:
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]; }
/// Return the ith applied argument.
SILValue getArgument(unsigned i) const { return getArguments()[i]; }
/// Set the ith applied argument.
void setArgument(unsigned i, SILValue V) const {
getArgumentOperands()[i].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.
unsigned getAppliedArgIndex(const Operand &oper) const {
assert(oper.getUser() == Inst);
assert(isArgumentOperand(oper));
return oper.getOperandNumber() - getOperandIndexOfFirstArgument();
}
/// 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 necessarilly 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 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 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");
}
/// 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 whether the given apply is of a formally-throwing function
/// which is statically known not to throw.
bool isNonThrowing() const {
switch (ApplySiteKind(getInstruction()->getKind())) {
case ApplySiteKind::ApplyInst:
return cast<ApplyInst>(Inst)->isNonThrowing();
case ApplySiteKind::BeginApplyInst:
return cast<BeginApplyInst>(Inst)->isNonThrowing();
case ApplySiteKind::TryApplyInst:
return false;
case ApplySiteKind::PartialApplyInst:
llvm_unreachable("Unhandled case");
}
}
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(); }
/// Attempt to cast this apply site to a full apply site, returning None on
/// failure.
Optional<FullApplySite> asFullApplySite() const;
};
//===----------------------------------------------------------------------===//
// FullApplySite
//===----------------------------------------------------------------------===//
struct FullApplySiteKind {
enum innerty : std::underlying_type<SILInstructionKind>::type {
#define FULLAPPLYSITE_INST(ID, PARENT) ID = unsigned(SILInstructionKind::ID),
#include "swift/SIL/SILNodes.def"
} value;
explicit FullApplySiteKind(SILInstructionKind kind) {
auto fullApplySiteKind = FullApplySiteKind::fromNodeKindHelper(kind);
assert(fullApplySiteKind && "SILNodeKind is not a FullApplySiteKind?!");
value = fullApplySiteKind.getValue();
}
FullApplySiteKind(innerty value) : value(value) {}
operator innerty() const { return value; }
static Optional<FullApplySiteKind> fromNodeKind(SILInstructionKind kind) {
if (auto innerOpt = FullApplySiteKind::fromNodeKindHelper(kind))
return FullApplySiteKind(*innerOpt);
return None;
}
private:
static Optional<innerty> fromNodeKindHelper(SILInstructionKind kind) {
switch (kind) {
#define FULLAPPLYSITE_INST(ID, PARENT) \
case SILInstructionKind::ID: \
return FullApplySiteKind::ID;
#include "swift/SIL/SILNodes.def"
default:
return None;
}
}
};
/// 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(SILNode *node) {
auto *i = dyn_cast<SILInstruction>(node);
if (!i)
return FullApplySite();
auto kind = FullApplySiteKind::fromNodeKind(i->getKind());
if (!kind)
return FullApplySite();
switch (kind.getValue()) {
case FullApplySiteKind::ApplyInst:
return FullApplySite(cast<ApplyInst>(node));
case FullApplySiteKind::BeginApplyInst:
return FullApplySite(cast<BeginApplyInst>(node));
case FullApplySiteKind::TryApplyInst:
return FullApplySite(cast<TryApplyInst>(node));
}
llvm_unreachable("covered switch");
}
FullApplySiteKind getKind() const {
return FullApplySiteKind(getInstruction()->getKind());
}
bool hasIndirectSILResults() const {
return getSubstCalleeConv().hasIndirectSILResults();
}
/// If our apply site has a single direct result SILValue, return that
/// SILValue. Return SILValue() otherwise.
///
/// This means that:
///
/// 1. If we have an ApplyInst, we just visit the apply.
/// 2. If we have a TryApplyInst, we visit the first argument of the normal
/// block.
/// 3. If we have a BeginApplyInst, we return SILValue() since the begin_apply
/// yields values instead of returning them. A returned value should only
/// be valid after a full apply site has completely finished executing.
SILValue getSingleDirectResult() 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();
}
OperandValueArrayRef getIndirectSILResults() const {
return getArguments().slice(0, getNumIndirectSILResults());
}
OperandValueArrayRef getArgumentsWithoutIndirectResults() const {
return getArguments().slice(getNumIndirectSILResults());
}
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");
}
/// 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?!");
}
/// 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
/// insertAfterFullEvaluation.
void insertAfterInvocation(function_ref<void(SILBuilder &)> func) const;
/// Pass a builder with insertion points that are guaranteed to be immediately
/// after this full apply site has completely finished executing.
///
/// 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 insertAfterFullEvaluation(function_ref<void(SILBuilder &)> func) const;
/// 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());
}
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 Optional<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);
}
} // namespace swift
#endif