mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Things that have come up recently but are somewhat blocked on this: - Moving AccessMarkerElimination down in the pipeline - SemanticARCOpts correctness and improvements - AliasAnalysis improvements - LICM performance regressions - RLE/DSE improvements Begin to formalize the model for valid memory access in SIL. Ignoring ownership, every access is a def-use chain in three parts: object root -> formal access base -> memory operation address AccessPath abstracts over this path and standardizes the identity of a memory access throughout the optimizer. This abstraction is the basis for a new AccessPathVerification. With that verification, we now have all the properties we need for the type of analysis requires for exclusivity enforcement, but now generalized for any memory analysis. This is suitable for an extremely lightweight analysis with no side data structures. We currently have a massive amount of ad-hoc memory analysis throughout SIL, which is incredibly unmaintainable, bug-prone, and not performance-robust. We can begin taking advantage of this verifably complete model to solve that problem. The properties this gives us are: Access analysis must be complete over memory operations: every memory operation needs a recognizable valid access. An access can be unidentified only to the extent that it is rooted in some non-address type and we can prove that it is at least *not* part of an access to a nominal class or global property. Pointer provenance is also required for future IRGen-level bitfield optimizations. Access analysis must be complete over address users: for an identified object root all memory accesses including subobjects must be discoverable. Access analysis must be symmetric: use-def and def-use analysis must be consistent. AccessPath is merely a wrapper around the existing accessed-storage utilities and IndexTrieNode. Existing passes already very succesfully use this approach, but in an ad-hoc way. With a general utility we can: - update passes to use this approach to identify memory access, reducing the space and time complexity of those algorithms. - implement an inexpensive on-the-fly, debug mode address lifetime analysis - implement a lightweight debug mode alias analysis - ultimately improve the power, efficiency, and maintainability of full alias analysis - make our type-based alias analysis sensistive to the access path
713 lines
22 KiB
C++
713 lines
22 KiB
C++
//===--- PatternMatch.h - SIL Pattern Matching Infrastructure ---*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file provides a simple and efficient mechanism for performing general
|
|
// tree-based pattern matches on SIL. The power of these routines is that it
|
|
// allows you to write concise patterns that are expressive and easy to
|
|
// understand. The other major advantage of this is that it allows you to
|
|
// trivially capture/bind elements in the pattern to variables.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SIL_PATTERNMATCH_H
|
|
#define SWIFT_SIL_PATTERNMATCH_H
|
|
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILUndef.h"
|
|
namespace swift {
|
|
|
|
class SILInstruction;
|
|
|
|
namespace PatternMatch {
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Basic Matching Infrastructure
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Applies the given pattern to V.
|
|
template<typename Val, typename Pattern>
|
|
bool match(Val *V, const Pattern &P) {
|
|
return const_cast<Pattern &>(P).match(V);
|
|
}
|
|
|
|
/// Explicit template instantiation for SILValue so we can access the value
|
|
/// inside.
|
|
template<typename Pattern>
|
|
bool match(SILValue V, const Pattern &P) {
|
|
return const_cast<Pattern &>(P).match(&*V);
|
|
}
|
|
|
|
template<typename SubPatternTy>
|
|
struct OneUse_match {
|
|
SubPatternTy SubPattern;
|
|
|
|
OneUse_match(const SubPatternTy &SP) : SubPattern(SP) {}
|
|
|
|
template<typename OpTy>
|
|
bool match(OpTy *V) {
|
|
return V->hasOneUse() && SubPattern.match(V);
|
|
}
|
|
};
|
|
|
|
/// Match if the input has one use and satisfies the given subpattern.
|
|
template<typename SubPatternTy>
|
|
inline OneUse_match<SubPatternTy> m_OneUse(const SubPatternTy &SubPattern) {
|
|
return SubPattern;
|
|
}
|
|
|
|
template<typename Class>
|
|
struct class_match {
|
|
template<typename ITy>
|
|
bool match(ITy *V) { return isa<Class>(V); }
|
|
|
|
bool match(SILValue v) { return isa<Class>(&*v); }
|
|
};
|
|
|
|
template<typename Class>
|
|
struct bind_ty {
|
|
Class *&VR;
|
|
bind_ty(Class *&V) : VR(V) {}
|
|
|
|
template<typename ITy>
|
|
bool match(ITy *V) {
|
|
if (auto *CV = dyn_cast<Class>(V)) {
|
|
VR = CV;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool match(SILValue v) { return match(&*v); }
|
|
};
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Matching Combinators
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
template<typename LTy, typename RTy>
|
|
struct match_combine_or {
|
|
LTy L;
|
|
RTy R;
|
|
|
|
match_combine_or(const LTy &Left, const RTy &Right) : L(Left), R(Right) { }
|
|
|
|
template<typename ITy>
|
|
bool match(ITy *V) {
|
|
if (L.match(V))
|
|
return true;
|
|
if (R.match(V))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool match(SILValue v) { return match(&*v); }
|
|
};
|
|
|
|
template<typename LTy, typename RTy>
|
|
struct match_combine_and {
|
|
LTy L;
|
|
RTy R;
|
|
|
|
match_combine_and(const LTy &Left, const RTy &Right) : L(Left), R(Right) { }
|
|
|
|
bool match(SILValue v) { return match(&*v); }
|
|
|
|
template<typename ITy>
|
|
bool match(ITy *V) {
|
|
if (L.match(V))
|
|
if (R.match(V))
|
|
return true;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/// Combine two pattern matchers matching L || R
|
|
template<typename LTy, typename RTy>
|
|
inline match_combine_or<LTy, RTy> m_CombineOr(const LTy &L, const RTy &R) {
|
|
return match_combine_or<LTy, RTy>(L, R);
|
|
}
|
|
|
|
/// Combine two pattern matchers matching L && R
|
|
template<typename LTy, typename RTy>
|
|
inline match_combine_and<LTy, RTy> m_CombineAnd(const LTy &L, const RTy &R) {
|
|
return match_combine_and<LTy, RTy>(L, R);
|
|
}
|
|
|
|
/// Helper class to track the return type of vararg m_CombineOr matcher.
|
|
template <typename ...Arguments>
|
|
struct OneOf_match;
|
|
|
|
template <typename T0>
|
|
struct OneOf_match<T0> {
|
|
using Ty = T0;
|
|
};
|
|
|
|
template <typename T0, typename T1>
|
|
struct OneOf_match<T0, T1> {
|
|
using Ty = match_combine_or<T0, T1>;
|
|
};
|
|
|
|
template <typename T0, typename T1, typename ...Arguments>
|
|
struct OneOf_match<T0, T1, Arguments ...> {
|
|
using Ty = typename OneOf_match<match_combine_or<T0, T1>, Arguments...>::Ty;
|
|
};
|
|
|
|
/// This is a vararg version of m_CombineOr. It is a boolean "or" of
|
|
/// matchers provided as its parameters.
|
|
template<typename LTy, typename RTy, typename ...RTys>
|
|
inline typename OneOf_match<LTy, RTy, RTys...>::Ty
|
|
m_CombineOr(const LTy &L, const RTy &R, const RTys &...Args) {
|
|
return m_CombineOr(m_CombineOr(L, R), Args...);
|
|
}
|
|
|
|
/// boolean or operator for combining matchers using
|
|
/// a conventional || syntax.
|
|
template<typename T1, typename T2, typename T3, typename T4>
|
|
inline typename OneOf_match<match_combine_and<T1, T2>,
|
|
match_combine_and<T3, T4>>::Ty
|
|
operator || (const match_combine_and<T1, T2> &Op1,
|
|
const match_combine_and<T3, T4> &Op2) {
|
|
return m_CombineOr(Op1, Op2);
|
|
}
|
|
|
|
template<typename T1, typename T2, typename T3, typename T4>
|
|
inline typename OneOf_match<match_combine_or<T1, T2>,
|
|
match_combine_and<T3, T4>>::Ty
|
|
operator || (const match_combine_or<T1, T2> &Op1,
|
|
const match_combine_and<T3, T4> &Op2) {
|
|
return m_CombineOr(Op1, Op2);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Base Matchers
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Match an arbitrary ValueBase, and if the match was successful do not
|
|
/// capture.
|
|
inline class_match<ValueBase> m_ValueBase() {
|
|
return class_match<ValueBase>();
|
|
}
|
|
|
|
/// Match an arbitrary ValueBase, capturing the ValueBase if the match succeeds.
|
|
inline bind_ty<ValueBase> m_ValueBase(ValueBase *&V) {
|
|
return V;
|
|
}
|
|
|
|
struct silvalue_bind {
|
|
SILValue &Value;
|
|
|
|
silvalue_bind(SILValue &V) : Value(V) { }
|
|
|
|
template <typename ITy>
|
|
bool match(ITy V) {
|
|
return false;
|
|
}
|
|
|
|
bool match(SILValue V) {
|
|
Value = V;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/// Match an arbitrary SILValue, capturing the SILValue if the match succeeds.
|
|
inline silvalue_bind m_SILValue(SILValue &V) {
|
|
return V;
|
|
}
|
|
|
|
struct specificval_ty {
|
|
const ValueBase *Val;
|
|
specificval_ty(const ValueBase *V) : Val(V) {}
|
|
|
|
template<typename ITy>
|
|
bool match(ITy *V) {
|
|
return V == Val;
|
|
}
|
|
};
|
|
|
|
/// Return a matcher which only matches on inputs that satisfy pointer equality
|
|
/// with V.
|
|
inline specificval_ty m_Specific(const ValueBase *V) { return V; }
|
|
|
|
/// Define class_match and bind_ty matchers for all VALUE SILNodes.
|
|
///
|
|
/// class_match matchers match arbitrary Class * and do not capture on success.
|
|
/// bind_ty matchers match arbitrary Class * and do capture on success.
|
|
#define VALUE(Class, Parent) \
|
|
inline class_match<Class> m_##Class() { return class_match<Class>(); } \
|
|
inline bind_ty<Class> m_##Class(Class *&V) { return V; }
|
|
#include "swift/SIL/SILNodes.def"
|
|
|
|
static inline APInt extendAPInt(const APInt &A, unsigned bitWidth,
|
|
bool isSigned) {
|
|
if (isSigned)
|
|
return A.sext(bitWidth);
|
|
return A.zext(bitWidth);
|
|
}
|
|
|
|
static inline bool isSameAPIntValue(const APInt &I1, const APInt &I2,
|
|
bool isSigned) {
|
|
if (I1.getBitWidth() == I2.getBitWidth())
|
|
return I1 == I2;
|
|
|
|
if (I1.getBitWidth() > I2.getBitWidth())
|
|
return I1 == extendAPInt(I2, I1.getBitWidth(), isSigned);
|
|
|
|
return extendAPInt(I1, I2.getBitWidth(), isSigned) == I2;
|
|
}
|
|
|
|
// Builtin Integer Matcher
|
|
struct specificintegerliteral_ty {
|
|
APInt Value;
|
|
bool isSigned;
|
|
specificintegerliteral_ty(APInt V, bool S) : Value(V), isSigned(S) {}
|
|
|
|
template<typename ITy>
|
|
bool match(ITy *V) {
|
|
auto *Literal = dyn_cast<IntegerLiteralInst>(V);
|
|
if (!Literal)
|
|
return false;
|
|
|
|
// This should eventually be refactored into APInt::isSameValue by giving it
|
|
// a signed flag.
|
|
return isSameAPIntValue(Value, Literal->getValue(), isSigned);
|
|
}
|
|
};
|
|
|
|
static inline specificintegerliteral_ty
|
|
m_SpecificIntegerLiteral(APInt V, bool isSigned) {
|
|
return {V, isSigned};
|
|
}
|
|
|
|
template <uint64_t value>
|
|
struct match_integer {
|
|
template<typename ITy>
|
|
bool match(ITy *V) {
|
|
auto *Literal = dyn_cast<IntegerLiteralInst>(V);
|
|
if (!Literal)
|
|
return false;
|
|
return Literal->getValue() == value;
|
|
}
|
|
};
|
|
|
|
using m_Zero = match_integer<0>;
|
|
using m_One = match_integer<1>;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Instruction Operand Matchers
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace detail {
|
|
|
|
struct GetOperandsFunctor {
|
|
template <size_t... Idx>
|
|
std::array<SILValue, sizeof...(Idx)>
|
|
operator()(SILInstruction *i, std::index_sequence<Idx...> seq) const {
|
|
return {i->getOperand(Idx)...};
|
|
}
|
|
};
|
|
|
|
template <typename... MatcherTys> struct MatcherFunctor {
|
|
std::tuple<MatcherTys...> matchers;
|
|
|
|
MatcherFunctor(const MatcherTys &... matchers) : matchers(matchers...) {}
|
|
|
|
template <typename MatcherTy> bool individual(MatcherTy matcher, SILValue v) {
|
|
return matcher.match(v);
|
|
}
|
|
|
|
template <size_t... Idx>
|
|
std::array<bool, sizeof...(MatcherTys)>
|
|
matchHelper(const std::array<SILValue, sizeof...(MatcherTys)> &operands,
|
|
std::index_sequence<Idx...> seq) {
|
|
return {individual(std::get<Idx>(matchers), std::get<Idx>(operands))...};
|
|
}
|
|
|
|
bool match(SILInstruction *i) {
|
|
std::array<SILValue, sizeof...(MatcherTys)> operands =
|
|
GetOperandsFunctor{}(i, std::index_sequence_for<MatcherTys...>{});
|
|
auto tmpResult =
|
|
matchHelper(operands, std::index_sequence_for<MatcherTys...>{});
|
|
for (unsigned i : indices(tmpResult)) {
|
|
if (!tmpResult[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
// NOTE: These matchers only can accept compound, non-fundamental
|
|
// types. This prevents by mistake passing in int, float, etc to these
|
|
// matchers.
|
|
template <SILInstructionKind Kind, typename... MatcherTys>
|
|
struct InstructionOperand_match {
|
|
// This just makes sure that we catch common mistakes passing
|
|
// fundamental types (i.e. int, float, etc) to this API.
|
|
static_assert(
|
|
are_all_compound<MatcherTys...>::value,
|
|
"Expected all matcher tys to be non-fundamental compound types?!");
|
|
detail::MatcherFunctor<MatcherTys...> matcherFunctor;
|
|
|
|
static constexpr unsigned NumMatchers = sizeof...(MatcherTys);
|
|
|
|
// Only allow for these to be constructed from non-fundamental types.
|
|
InstructionOperand_match(const MatcherTys &... matchers)
|
|
: matcherFunctor(matchers...) {}
|
|
|
|
bool match(SILNode *node) {
|
|
if (node->getKind() != SILNodeKind(Kind))
|
|
return false;
|
|
return match(cast<SILInstruction>(node));
|
|
}
|
|
|
|
bool match(SILInstruction *i) {
|
|
if (i->getKind() != Kind || !(i->getNumOperands() <= NumMatchers))
|
|
return false;
|
|
|
|
return matcherFunctor.match(i);
|
|
}
|
|
};
|
|
|
|
#define FULL_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \
|
|
template <typename... MatcherTys> \
|
|
InstructionOperand_match<SILInstructionKind::ID, MatcherTys...> m_##ID( \
|
|
const MatcherTys &... matchers) { \
|
|
return {matchers...}; \
|
|
}
|
|
#include "swift/SIL/SILNodes.def"
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Address/Struct Projections
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Match either a tuple_extract that the index field from a tuple or the
|
|
/// indexth destructure_tuple result.
|
|
template <typename LTy> struct tupleextractoperation_ty {
|
|
LTy L;
|
|
unsigned index;
|
|
tupleextractoperation_ty(const LTy &Left, unsigned i) : L(Left), index(i) {}
|
|
|
|
bool match(SILValue v) {
|
|
auto *inst = v->getDefiningInstruction();
|
|
if (!inst)
|
|
return false;
|
|
return match(inst);
|
|
}
|
|
|
|
template <typename ITy> bool match(ITy *V) {
|
|
if (auto *TEI = dyn_cast<TupleExtractInst>(V)) {
|
|
return TEI->getFieldIndex() == index &&
|
|
L.match((ValueBase *)TEI->getOperand());
|
|
}
|
|
|
|
if (auto *DTR = dyn_cast<DestructureTupleResult>(V)) {
|
|
return DTR->getIndex() == index &&
|
|
L.match((ValueBase *)DTR->getParent()->getOperand());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
template <typename LTy>
|
|
tupleextractoperation_ty<LTy> m_TupleExtractOperation(const LTy &Left,
|
|
unsigned Index) {
|
|
return tupleextractoperation_ty<LTy>(Left, Index);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Function/Builtin/Intrinsic Application Matchers
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//===
|
|
// Callee matcher.
|
|
//
|
|
|
|
template <typename CalleeTy>
|
|
struct Callee_match;
|
|
|
|
template<>
|
|
struct Callee_match<SILFunction &> {
|
|
const SILFunction &Fun;
|
|
|
|
Callee_match(const SILFunction &F) : Fun(F) {}
|
|
|
|
template <typename ITy>
|
|
bool match(ITy *V) {
|
|
auto *AI = dyn_cast<ApplyInst>(V);
|
|
if (!AI)
|
|
return false;
|
|
|
|
return AI->getReferencedFunctionOrNull() == &Fun;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct Callee_match<BuiltinValueKind> {
|
|
BuiltinValueKind Kind;
|
|
|
|
Callee_match(BuiltinValueKind K) : Kind(K) { }
|
|
|
|
template <typename ITy>
|
|
bool match(ITy *V) {
|
|
auto *BI = dyn_cast<BuiltinInst>(V);
|
|
if (!BI)
|
|
return false;
|
|
|
|
return BI->getBuiltinInfo().ID == Kind;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct Callee_match<llvm::Intrinsic::ID> {
|
|
llvm::Intrinsic::ID IntrinsicID;
|
|
|
|
Callee_match(const llvm::Intrinsic::ID ID) : IntrinsicID(ID) { }
|
|
|
|
template <typename ITy>
|
|
bool match(ITy *V) {
|
|
auto *BI = dyn_cast<BuiltinInst>(V);
|
|
if (!BI)
|
|
return false;
|
|
|
|
return BI->getIntrinsicInfo().ID == IntrinsicID;
|
|
}
|
|
};
|
|
|
|
/// Match a callee argument.
|
|
///
|
|
/// We use explicit specialization of Callee_match to handle SILFunctions,
|
|
/// Builtins, and Intrinsics all with this one function.
|
|
|
|
template<typename CalleeTy>
|
|
inline Callee_match<CalleeTy> m_Callee(CalleeTy Callee) {
|
|
return Callee;
|
|
}
|
|
|
|
//===
|
|
// Argument matcher
|
|
//
|
|
|
|
template <typename SubPatternTy>
|
|
struct Argument_match {
|
|
unsigned OpI;
|
|
SubPatternTy Val;
|
|
Argument_match(unsigned OpIdx, const SubPatternTy &V) : OpI(OpIdx), Val(V) { }
|
|
|
|
template <typename ITy>
|
|
bool match(ITy *V) {
|
|
if (auto *Apply = dyn_cast<ApplyInst>(V)) {
|
|
return Val.match((ValueBase *)Apply->getArgument(OpI));
|
|
}
|
|
if (auto *Builtin = dyn_cast<BuiltinInst>(V)) {
|
|
return Val.match((ValueBase *)Builtin->getArguments()[OpI]);
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Explicit specialization for silvalue.
|
|
template <>
|
|
struct Argument_match<silvalue_bind> {
|
|
unsigned OpI;
|
|
silvalue_bind Val;
|
|
Argument_match(unsigned OpIdx, const silvalue_bind &V) : OpI(OpIdx), Val(V) { }
|
|
|
|
template <typename ITy>
|
|
bool match(ITy *V) {
|
|
if (auto *Apply = dyn_cast<ApplyInst>(V)) {
|
|
return Val.match(Apply->getArgument(OpI));
|
|
}
|
|
if (auto *Builtin = dyn_cast<BuiltinInst>(V)) {
|
|
return Val.match(Builtin->getArguments()[OpI]);
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/// Match the Ith argument with SubPatternTy.
|
|
template<unsigned OpI, typename SubPatternTy>
|
|
inline Argument_match<SubPatternTy> m_Argument(const SubPatternTy &Op) {
|
|
return Argument_match<SubPatternTy>(OpI, Op);
|
|
}
|
|
|
|
//===
|
|
// ApplyInst
|
|
//
|
|
// ApplyInst matchers are a boolean and of a Callee_matcher and a list of
|
|
// argument matchers.
|
|
|
|
template <typename CalleeTy, typename ...Arguments>
|
|
struct Apply_match;
|
|
|
|
template <typename CalleeTy>
|
|
struct Apply_match<CalleeTy> {
|
|
using Ty = Callee_match<CalleeTy>;
|
|
};
|
|
|
|
template <typename CalleeTy, typename T0>
|
|
struct Apply_match<CalleeTy, T0> {
|
|
using Ty = match_combine_and<Callee_match<CalleeTy>, Argument_match<T0>>;
|
|
};
|
|
|
|
template <typename CalleeTy, typename T0, typename ...Arguments>
|
|
struct Apply_match<CalleeTy, T0, Arguments ...> {
|
|
using Ty = match_combine_and<typename Apply_match<CalleeTy, Arguments...>::Ty,
|
|
Argument_match<T0>>;
|
|
};
|
|
|
|
/// Match only an ApplyInst's Callee.
|
|
template <typename CalleeTy>
|
|
inline typename Apply_match<CalleeTy>::Ty
|
|
m_ApplyInst(CalleeTy Callee) {
|
|
return Callee;
|
|
}
|
|
|
|
/// Match an ApplyInst's Callee and first argument.
|
|
template <unsigned Index=0, typename CalleeTy, typename T0>
|
|
inline typename Apply_match<CalleeTy, T0>::Ty
|
|
m_ApplyInst(CalleeTy Callee, const T0 &Op0) {
|
|
return m_CombineAnd(m_Callee(Callee), m_Argument<Index>(Op0));
|
|
}
|
|
|
|
/// Match an ApplyInst's Callee and up to the ApplyInsts Nth argument, where N
|
|
/// is sizeof...(Arguments) + 1.
|
|
template <unsigned Index=0, typename CalleeTy, typename T0,
|
|
typename ...Arguments>
|
|
inline typename Apply_match<CalleeTy, T0, Arguments ...>::Ty
|
|
m_ApplyInst(CalleeTy Callee, const T0 &Op0, const Arguments &...Args) {
|
|
return m_CombineAnd(m_ApplyInst<Index+1>(Callee, Args...),
|
|
m_Argument<Index>(Op0));
|
|
}
|
|
|
|
/// Match only a BuiltinInst's callee.
|
|
inline typename Apply_match<BuiltinValueKind>::Ty
|
|
m_BuiltinInst(BuiltinValueKind Callee) {
|
|
return Callee;
|
|
}
|
|
|
|
/// Match a BuiltinInst's Callee and first argument.
|
|
template <unsigned Index=0, typename T0>
|
|
inline typename Apply_match<BuiltinValueKind, T0>::Ty
|
|
m_BuiltinInst(BuiltinValueKind Callee, const T0 &Op0) {
|
|
return m_CombineAnd(m_Callee(Callee), m_Argument<Index>(Op0));
|
|
}
|
|
|
|
/// Match an ApplyInst's Callee and up to the ApplyInsts Nth argument, where N
|
|
/// is sizeof...(Arguments) + 1.
|
|
template <unsigned Index=0, typename T0,
|
|
typename ...Arguments>
|
|
inline typename Apply_match<BuiltinValueKind, T0, Arguments ...>::Ty
|
|
m_BuiltinInst(BuiltinValueKind Callee, const T0 &Op0, const Arguments &...Args) {
|
|
return m_CombineAnd(m_BuiltinInst<Index+1>(Callee, Args...),
|
|
m_Argument<Index>(Op0));
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Builtin Instructions
|
|
//===----------------------------------------------------------------------===//
|
|
/// Return type of builtin instruction matchers.
|
|
template <typename ...Tys>
|
|
using BuiltinApplyTy = typename Apply_match<BuiltinValueKind, Tys...>::Ty;
|
|
|
|
|
|
/// XMacro for generating a matcher for unary builtin instructions that can
|
|
/// apply further matchers to the operands of the builtin operation.
|
|
#define BUILTIN_UNARY_OP_MATCH_WITH_ARG_MATCHER(PatternName, Kind) \
|
|
template <typename Ty> \
|
|
BuiltinApplyTy<Ty> \
|
|
m_##PatternName(const Ty &T) { \
|
|
return m_ApplyInst(BuiltinValueKind::Kind, T); \
|
|
}
|
|
|
|
/// XMacro for generating a matcher for binary builtin instructions that can
|
|
/// apply further matchers to the operands of the builtin operation.
|
|
#define BUILTIN_BINARY_OP_MATCH_WITH_ARG_MATCHER(PatternName, Kind) \
|
|
template <typename Ty1, typename Ty2> \
|
|
BuiltinApplyTy<Ty1, Ty2> \
|
|
m_##PatternName(const Ty1 &T1, const Ty2 &T2) { \
|
|
return m_ApplyInst(BuiltinValueKind::Kind, T1, T2); \
|
|
}
|
|
|
|
/// XMacro for generating a matcher for varargs builtin instructions that can
|
|
/// apply further matchers to the operands of the builtin operation.
|
|
#define BUILTIN_VARARGS_OP_MATCH_WITH_ARG_MATCHER(PatternName, Kind) \
|
|
template <typename ...Tys> \
|
|
BuiltinApplyTy<Tys...> \
|
|
m_##PatternName(const Tys& ...Args) { \
|
|
return m_ApplyInst(BuiltinValueKind::Kind, Args...); \
|
|
}
|
|
|
|
#define BUILTIN_CAST_OPERATION(Id, Name, Attrs) BUILTIN(Id, Name, Attrs) \
|
|
BUILTIN_UNARY_OP_MATCH_WITH_ARG_MATCHER(Id, Id)
|
|
|
|
#define BUILTIN_CAST_OR_BITCAST_OPERATION(Id, Name, Attrs) \
|
|
BUILTIN_UNARY_OP_MATCH_WITH_ARG_MATCHER(Id, Id)
|
|
|
|
#define BUILTIN_BINARY_OPERATION_ALL(Id, Name, Attrs, Overload) \
|
|
BUILTIN_BINARY_OP_MATCH_WITH_ARG_MATCHER(Id, Id)
|
|
|
|
#define BUILTIN_BINARY_PREDICATE(Id, Name, Attrs, Overload) \
|
|
BUILTIN_BINARY_OP_MATCH_WITH_ARG_MATCHER(Id, Id)
|
|
|
|
#define BUILTIN_MISC_OPERATION(Id, Name, Attrs, Overload) \
|
|
BUILTIN_VARARGS_OP_MATCH_WITH_ARG_MATCHER(Id, Id)
|
|
|
|
#define BUILTIN(Id, Name, Attrs)
|
|
|
|
// Define matchers for most of builtin instructions.
|
|
#include "swift/AST/Builtins.def"
|
|
|
|
#undef BUILTIN_UNARY_OP_MATCH_WITH_ARG_MATCHER
|
|
#undef BUILTIN_BINARY_OP_MATCH_WITH_ARG_MATCHER
|
|
#undef BUILTIN_VARARGS_OP_MATCH_WITH_ARG_MATCHER
|
|
#undef BUILTIN_CAST_OPERATION
|
|
#undef BUILTIN_CAST_OR_BITCAST_OPERATION
|
|
#undef BUILTIN_BINARY_OPERATION_ALL
|
|
#undef BUILTIN_BINARY_PREDICATE
|
|
#undef BUILTIN_MISC_OPERATION
|
|
#undef BUILTIN
|
|
|
|
//===
|
|
// Convenience compound builtin instructions matchers that succeed
|
|
// if any of the sub-matchers succeed.
|
|
//
|
|
|
|
/// Matcher for any of the builtin ExtOrBitCast instructions.
|
|
template <typename T0>
|
|
inline typename OneOf_match<BuiltinApplyTy<T0>, BuiltinApplyTy<T0>>::Ty
|
|
m_ExtOrBitCast(const T0 &Op0) {
|
|
return m_ZExtOrBitCast(Op0) || m_SExtOrBitCast(Op0);
|
|
}
|
|
|
|
/// Matcher for any of the builtin [SZ]Ext instructions.
|
|
template <typename T0>
|
|
inline typename OneOf_match<BuiltinApplyTy<T0>, BuiltinApplyTy<T0>>::Ty
|
|
m_Ext(const T0 &Op0) {
|
|
return m_ZExt(Op0) || m_SExt(Op0);
|
|
}
|
|
|
|
/// Matcher for any of the builtin CheckedTrunc instructions.
|
|
template <typename T0>
|
|
inline typename OneOf_match<BuiltinApplyTy<T0>, BuiltinApplyTy<T0>,
|
|
BuiltinApplyTy<T0>, BuiltinApplyTy<T0>>::Ty
|
|
m_CheckedTrunc(const T0 &Op0) {
|
|
return m_UToSCheckedTrunc(Op0) || m_SToUCheckedTrunc(Op0) ||
|
|
m_UToUCheckedTrunc(Op0) || m_SToSCheckedTrunc(Op0);
|
|
}
|
|
|
|
} // end namespace PatternMatch
|
|
} // end namespace swift
|
|
|
|
#endif
|