diff options
Diffstat (limited to 'src/jit/valuenum.h')
-rw-r--r-- | src/jit/valuenum.h | 1109 |
1 files changed, 1109 insertions, 0 deletions
diff --git a/src/jit/valuenum.h b/src/jit/valuenum.h new file mode 100644 index 0000000000..6a8337af1f --- /dev/null +++ b/src/jit/valuenum.h @@ -0,0 +1,1109 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +// Defines the class "ValueNumStore", which maintains value numbers for a compilation. + +// Recall that "value numbering" assigns an integer value number to each expression. The "value +// number property" is that two expressions with the same value number will evaluate to the same value +// at runtime. Expressions with different value numbers may or may not be equivalent. This property +// of value numbers has obvious applications in redundancy-elimination optimizations. +// +// Since value numbers give us a way of talking about the (immutable) values to which expressions +// evaluate, they provide a good "handle" to use for attributing properties to values. For example, +// we might note that some value number represents some particular integer constant -- which has obvious +// application to constant propagation. Or that we know the exact type of some object reference, +// which might be used in devirtualization. +// +// Finally, we will also use value numbers to express control-flow-dependent assertions. Some test may +// imply that after the test, something new is known about a value: that an object reference is non-null +// after a dereference (since control flow continued because no exception was thrown); that an integer value +// is restricted to some subrange in after a comparison test; etc. + + +/*****************************************************************************/ +#ifndef _VALUENUM_H_ +#define _VALUENUM_H_ +/*****************************************************************************/ + +#include "vartype.h" +// For "GT_COUNT" +#include "gentree.h" +// Defines the type ValueNum. +#include "valuenumtype.h" + +// A "ValueNumStore" represents the "universe" of value numbers used in a single +// compilation. + +// All members of the enumeration genTreeOps are also members of VNFunc. +// (Though some of these may be labeled "illegal"). +enum VNFunc +{ + // Implicitly, elements of genTreeOps here. + VNF_Boundary = GT_COUNT, +#define ValueNumFuncDef(nm, arity, commute, knownNonNull, sharedStatic) VNF_##nm, +#include "valuenumfuncs.h" + VNF_COUNT +}; + +// Given an "oper" and associated flags with it, transform the oper into a +// more accurate oper that can be used in evaluation. For example, (GT_ADD, unsigned) +// transforms to GT_ADD_UN. +VNFunc GetVNFuncForOper(genTreeOps oper, bool isUnsigned); + +// An instance of this struct represents an application of the function symbol +// "m_func" to the first "m_arity" (<= 4) argument values in "m_args." +struct VNFuncApp +{ + VNFunc m_func; + unsigned m_arity; + ValueNum m_args[4]; + + bool Equals(const VNFuncApp& funcApp) + { + if (m_func != funcApp.m_func) return false; + if (m_arity != funcApp.m_arity) return false; + for (unsigned i = 0; i < m_arity; i++) + { + if (m_args[i] != funcApp.m_args[i]) return false; + } + return true; + } +}; + +// A unique prefix character to use when dumping a tree's gtVN in the tree dumps +// We use this together with string concatenation to put this in printf format strings +// static const char* const VN_DumpPrefix = "$"; +#define STR_VN "$" + + +class ValueNumStore +{ + +public: + // We will reserve "max unsigned" to represent "not a value number", for maps that might start uninitialized. + static const ValueNum NoVN = UINT32_MAX; + // A second special value, used to indicate that a function evaluation would cause infinite recursion. + static const ValueNum RecursiveVN = UINT32_MAX-1; + + // ================================================================================================== + // VNMap - map from something to ValueNum, where something is typically a constant value or a VNFunc + // This class has two purposes - to abstract the implementation and to validate the ValueNums + // being stored or retrieved. + template <class fromType, class keyfuncs=LargePrimitiveKeyFuncs<fromType>> + class VNMap : public SimplerHashTable<fromType, keyfuncs, ValueNum, DefaultSimplerHashBehavior> + { + public: + VNMap(IAllocator* alloc) : SimplerHashTable<fromType, keyfuncs, ValueNum, DefaultSimplerHashBehavior>(alloc) {} + ~VNMap() + { + ~VNMap<fromType, keyfuncs>::SimplerHashTable(); + } + + bool Set(fromType k, ValueNum val) + { + assert(val != RecursiveVN); + return SimplerHashTable<fromType, keyfuncs, ValueNum, DefaultSimplerHashBehavior>::Set(k, val); + } + bool Lookup(fromType k, ValueNum* pVal = NULL) const + { + bool result = SimplerHashTable<fromType, keyfuncs, ValueNum, DefaultSimplerHashBehavior>::Lookup(k, pVal); + assert(*pVal != RecursiveVN || !result); + return result; + } + }; + + + +private: + Compiler* m_pComp; + + // For allocations. (Other things?) + IAllocator* m_alloc; + + // TODO-Cleanup: should transform "attribs" into a struct with bit fields. That would be simpler... + + enum VNFOpAttrib + { + VNFOA_IllegalGenTreeOp = 0x1, // corresponds to a genTreeOps value that is not a legal VN func. + VNFOA_Commutative = 0x2, // 1 iff the function is commutative. + VNFOA_Arity = 0x4, // Bits 2..3 encode the arity. + VNFOA_AfterArity = 0x20, // Makes it clear what value the next flag(s) after Arity should have. + VNFOA_KnownNonNull = 0x20, // 1 iff the result is known to be non-null. + VNFOA_SharedStatic = 0x40, // 1 iff this VNF is represent one of the shared static jit helpers + }; + + static const unsigned VNFOA_ArityShift = 2; + static const unsigned VNFOA_ArityBits = 3; + static const unsigned VNFOA_MaxArity = (1 << VNFOA_ArityBits) - 1; // Max arity we can represent. + static const unsigned VNFOA_ArityMask = VNFOA_AfterArity - VNFOA_Arity; + + // These enum constants are used to encode the cast operation in the lowest bits by VNForCastOper + enum VNFCastAttrib + { + VCA_UnsignedSrc = 0x01, + + VCA_BitCount = 1, // the number of reserved bits + VCA_ReservedBits = 0x01, // i.e. (VCA_UnsignedSrc) + }; + + + // An array of length GT_COUNT, mapping genTreeOp values to their VNFOpAttrib. + static UINT8* s_vnfOpAttribs; + + // Returns "true" iff gtOper is a legal value number function. + // (Requires InitValueNumStoreStatics to have been run.) + static inline bool GenTreeOpIsLegalVNFunc(genTreeOps gtOper); + + // Returns "true" iff "vnf" is a commutative (and thus binary) operator. + // (Requires InitValueNumStoreStatics to have been run.) + static inline bool VNFuncIsCommutative(VNFunc vnf); + + // Returns "true" iff "vnf" is a comparison (and thus binary) operator. + static inline bool VNFuncIsComparison(VNFunc vnf); + + // Returns "true" iff "vnf" can be evaluated for constant arguments. + static inline bool CanEvalForConstantArgs(VNFunc vnf); + + // return vnf(v0) + template<typename T> + static T EvalOp(VNFunc vnf, T v0); + + // If vnf(v0, v1) would raise an exception, sets *pExcSet to the singleton set containing the exception, and returns (T)0. + // Otherwise, returns vnf(v0, v1). + template<typename T> + T EvalOp(VNFunc vnf, T v0, T v1, ValueNum* pExcSet); + + template<typename T> + static int EvalComparison(VNFunc vnf, T v0, T v1); + template<typename T> + static int EvalOrderedComparisonFloat(VNFunc vnf, T v0, T v1); + // return vnf(v0) or vnf(v0, v1), respectively (must, of course be unary/binary ops, respectively.) + // Should only be instantiated for integral types. + template<typename T> + static T EvalOpIntegral(VNFunc vnf, T v0); + template<typename T> + T EvalOpIntegral(VNFunc vnf, T v0, T v1, ValueNum* pExcSet); + + // Should only instantiate (in a non-trivial way) for "int" and "INT64". Returns true iff dividing "v0" by "v1" + // would produce integer overflow (an ArithmeticException -- *not* division by zero, which is separate.) + template<typename T> + static bool IsOverflowIntDiv(T v0, T v1); + + // Should only instantiate (in a non-trivial way) for integral types (signed/unsigned int32/int64). + // Returns true iff v is the zero of the appropriate type. + template<typename T> + static bool IsIntZero(T v); + + + // Given an constant value number return its value. + int GetConstantInt32(ValueNum argVN); + INT64 GetConstantInt64(ValueNum argVN); + double GetConstantDouble(ValueNum argVN); + + // Assumes that all the ValueNum arguments of each of these functions have been shown to represent constants. + // Assumes that "vnf" is a operator of the appropriate arity (unary for the first, binary for the second). + // Assume that "CanEvalForConstantArgs(vnf)" is true. + // Returns the result of evaluating the function with those constant arguments. + ValueNum EvalFuncForConstantArgs(var_types typ, VNFunc vnf, ValueNum vn0); + ValueNum EvalFuncForConstantArgs(var_types typ, VNFunc vnf, ValueNum vn0, ValueNum vn1); + ValueNum EvalFuncForConstantFPArgs(var_types typ, VNFunc vnf, ValueNum vn0, ValueNum vn1); + ValueNum EvalCastForConstantArgs(var_types typ, VNFunc vnf, ValueNum vn0, ValueNum vn1); + +#ifdef DEBUG + // This helps test some performance pathologies related to "evaluation" of VNF_MapSelect terms, + // especially relating to the heap. We count the number of applications of such terms we consider, + // and if this exceeds a limit, indicated by a COMPLUS_ variable, we assert. + unsigned m_numMapSels; +#endif + + // This is the maximum number of MapSelect terms that can be "considered" as part of evaluation of a top-level MapSelect + // application. + unsigned m_mapSelectBudget; + +public: + // Initializes any static variables of ValueNumStore. + static void InitValueNumStoreStatics(); + + // Initialize an empty ValueNumStore. + ValueNumStore(Compiler* comp, IAllocator* allocator); + + // Returns "true" iff "vnf" (which may have been created by a cast from an integral value) represents + // a legal value number function. + // (Requires InitValueNumStoreStatics to have been run.) + static bool VNFuncIsLegal(VNFunc vnf) + { + return unsigned(vnf) > VNF_Boundary || GenTreeOpIsLegalVNFunc(static_cast<genTreeOps>(vnf)); + } + + // Returns the arity of "vnf". + static unsigned VNFuncArity(VNFunc vnf); + + // Requires "gtOper" to be a genTreeOps legally representing a VNFunc, and returns that + // VNFunc. + // (Requires InitValueNumStoreStatics to have been run.) + static VNFunc GenTreeOpToVNFunc(genTreeOps gtOper) + { + assert(GenTreeOpIsLegalVNFunc(gtOper)); + return static_cast<VNFunc>(gtOper); + } + +#ifdef DEBUG + static void RunTests(Compiler* comp); +#endif // DEBUG + + // This block of methods gets value numbers for constants of primitive types. + + ValueNum VNForIntCon(INT32 cnsVal); + ValueNum VNForLongCon(INT64 cnsVal); + ValueNum VNForFloatCon(float cnsVal); + ValueNum VNForDoubleCon(double cnsVal); + ValueNum VNForByrefCon(INT64 byrefVal); + +#ifdef _TARGET_64BIT_ + ValueNum VNForPtrSizeIntCon(INT64 cnsVal) { return VNForLongCon(cnsVal); } +#else + ValueNum VNForPtrSizeIntCon(INT32 cnsVal) { return VNForIntCon(cnsVal); } +#endif + + ValueNum VNForCastOper(var_types castToType, bool srcIsUnsigned = false); + + // We keep handle values in a separate pool, so we don't confuse a handle with an int constant + // that happens to be the same... + ValueNum VNForHandle(ssize_t cnsVal, unsigned iconFlags); + + // And the single constant for an object reference type. + static ValueNum VNForNull() + { + // We reserve Chunk 0 for "special" VNs. SRC_Null (== 0) is the VN of "null". + return ValueNum(SRC_Null); + } + + // The zero map is the map that returns a zero "for the appropriate type" when indexed at any index. + static ValueNum VNForZeroMap() + { + // We reserve Chunk 0 for "special" VNs. Let SRC_ZeroMap (== 1) be the zero map. + return ValueNum(SRC_ZeroMap); + } + + // The value number for the special "NotAField" field sequence. + static ValueNum VNForNotAField() + { + // We reserve Chunk 0 for "special" VNs. Let SRC_NotAField (== 2) be the "not a field seq". + return ValueNum(SRC_NotAField); + } + + // The ROH map is the map for the "read-only heap". We assume that this is never mutated, and always + // has the same value number. + static ValueNum VNForROH() + { + // We reserve Chunk 0 for "special" VNs. Let SRC_ReadOnlyHeap (== 3) be the read-only heap. + return ValueNum(SRC_ReadOnlyHeap); + } + + // A special value number for "void" -- sometimes a type-void thing is an argument to a + // GT_LIST, and we want the args to be non-NoVN. + static ValueNum VNForVoid() + { + // We reserve Chunk 0 for "special" VNs. Let SRC_Void (== 4) be the value for "void". + return ValueNum(SRC_Void); + } + static ValueNumPair VNPForVoid() + { + return ValueNumPair(VNForVoid(), VNForVoid()); + } + + // A special value number for the empty set of exceptions. + static ValueNum VNForEmptyExcSet() + { + // We reserve Chunk 0 for "special" VNs. Let SRC_EmptyExcSet (== 5) be the value for the empty set of exceptions. + return ValueNum(SRC_EmptyExcSet); + } + static ValueNumPair VNPForEmptyExcSet() + { + return ValueNumPair(VNForEmptyExcSet(), VNForEmptyExcSet()); + } + + // Returns the value number for zero of the given "typ". + // It has an unreached() for a "typ" that has no zero value, such as TYP_BYREF. + ValueNum VNZeroForType(var_types typ); + + // Returns the value number for one of the given "typ". + // It returns NoVN for a "typ" that has no one value, such as TYP_REF. + ValueNum VNOneForType(var_types typ); + + // Return the value number representing the singleton exception set containing the exception value "x". + ValueNum VNExcSetSingleton(ValueNum x); + ValueNumPair VNPExcSetSingleton(ValueNumPair x); + + // Returns the VN representing the union of the two exception sets "xs0" and "xs1". + // These must be VNForEmtpyExcSet() or applications of VNF_ExcSetCons, obeying + // the ascending order invariant (which is preserved in the result.) + ValueNum VNExcSetUnion(ValueNum xs0, ValueNum xs1 DEBUG_ARG(bool topLevel = true)); + + ValueNumPair VNPExcSetUnion(ValueNumPair xs0vnp, ValueNumPair xs1vnp); + + // Returns "true" iff "vn" is an application of "VNF_ValWithExc". + bool VNHasExc(ValueNum vn) + { + VNFuncApp funcApp; + return GetVNFunc(vn, &funcApp) && funcApp.m_func == VNF_ValWithExc; + } + + // Requires that "vn" is *not* a "VNF_ValWithExc" appliation. + // If vn "excSet" is not "VNForEmptyExcSet()", return "VNF_ValWithExc(vn, excSet)". Otherwise, + // just return "vn". + ValueNum VNWithExc(ValueNum vn, ValueNum excSet); + + ValueNumPair VNPWithExc(ValueNumPair vnp, ValueNumPair excSetVNP); + + // If "vnWx" is a "VNF_ValWithExc(normal, excSet)" application, sets "*pvn" to "normal", and + // "*pvnx" to "excSet". Otherwise, just sets "*pvn" to "normal". + void VNUnpackExc(ValueNum vnWx, ValueNum* pvn, ValueNum* pvnx); + + void VNPUnpackExc(ValueNumPair vnWx, ValueNumPair* pvn, ValueNumPair* pvnx); + + // If "vn" is a "VNF_ValWithExc(norm, excSet)" value, returns the "norm" argument; otherwise, + // just returns "vn". + ValueNum VNNormVal(ValueNum vn); + ValueNumPair VNPNormVal(ValueNumPair vn); + + // If "vn" is a "VNF_ValWithExc(norm, excSet)" value, returns the "excSet" argument; otherwise, + // just returns "EmptyExcSet()". + ValueNum VNExcVal(ValueNum vn); + ValueNumPair VNPExcVal(ValueNumPair vn); + + // True "iff" vn is a value known to be non-null. (For example, the result of an allocation...) + bool IsKnownNonNull(ValueNum vn); + + // True "iff" vn is a value returned by a call to a shared static helper. + bool IsSharedStatic(ValueNum vn); + + // VN's for functions of other values. + // Four overloads, for arities 0, 1, 2, and 3. If we need other arities, we'll consider it. + ValueNum VNForFunc(var_types typ, VNFunc func); + ValueNum VNForFunc(var_types typ, VNFunc func, ValueNum opVNwx); + // This must not be used for VNF_MapSelect applications; instead use VNForMapSelect, below. + ValueNum VNForFunc(var_types typ, VNFunc func, ValueNum op1VNwx, ValueNum op2VNwx); + ValueNum VNForFunc(var_types typ, VNFunc func, ValueNum op1VNwx, ValueNum op2VNwx, ValueNum op3VNwx); + + // The following four op VNForFunc is only used for VNF_PtrToArrElem, elemTypeEqVN, arrVN, inxVN, fldSeqVN + ValueNum VNForFunc(var_types typ, VNFunc func, ValueNum op1VNwx, ValueNum op2VNwx, ValueNum op3VNwx, ValueNum op4VNwx); + + // This requires a "ValueNumKind" because it will attempt, given "select(phi(m1, ..., mk), ind)", to evaluate + // "select(m1, ind)", ..., "select(mk, ind)" to see if they agree. It needs to know which kind of value number + // (liberal/conservative) to read from the SSA def referenced in the phi argument. + ValueNum VNForMapSelect(ValueNumKind vnk, var_types typ, ValueNum op1VN, ValueNum op2VN); + + ValueNum VNForMapSelectWork(ValueNumKind vnk, var_types typ, ValueNum op1VN, ValueNum op2VN, unsigned* pBudget); + + // A specialized version of VNForFunc that is used for VNF_MapStore and provides some logging when verbose is set + ValueNum VNForMapStore(var_types typ, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN); + + // These functions parallel the ones above, except that they take liberal/conservative VN pairs + // as arguments, and return such a pair (the pair of the function applied to the liberal args, and + // the function applied to the conservative args). + ValueNumPair VNPairForFunc(var_types typ, VNFunc func) + { + ValueNumPair res; + res.SetBoth(VNForFunc(typ, func)); + return res; + } + ValueNumPair VNPairForFunc(var_types typ, VNFunc func, ValueNumPair opVN) + { + return ValueNumPair(VNForFunc(typ, func, opVN.GetLiberal()), + VNForFunc(typ, func, opVN.GetConservative())); + } + ValueNumPair VNPairForFunc(var_types typ, VNFunc func, ValueNumPair op1VN, ValueNumPair op2VN) + { + return ValueNumPair(VNForFunc(typ, func, op1VN.GetLiberal(), op2VN.GetLiberal()), + VNForFunc(typ, func, op1VN.GetConservative(), op2VN.GetConservative())); + } + ValueNumPair VNPairForFunc(var_types typ, VNFunc func, ValueNumPair op1VN, ValueNumPair op2VN, ValueNumPair op3VN) + { + return ValueNumPair(VNForFunc(typ, func, op1VN.GetLiberal(), op2VN.GetLiberal(), op3VN.GetLiberal()), + VNForFunc(typ, func, op1VN.GetConservative(), op2VN.GetConservative(), op3VN.GetConservative())); + } + ValueNumPair VNPairForFunc(var_types typ, VNFunc func, ValueNumPair op1VN, ValueNumPair op2VN, ValueNumPair op3VN, ValueNumPair op4VN) + { + return ValueNumPair(VNForFunc(typ, func, op1VN.GetLiberal(), op2VN.GetLiberal(), op3VN.GetLiberal(), op4VN.GetLiberal()), + VNForFunc(typ, func, op1VN.GetConservative(), op2VN.GetConservative(), op3VN.GetConservative(), op4VN.GetConservative())); + } + + // Get a new, unique value number for an expression that we're not equating to some function. + ValueNum VNForExpr(var_types typ = TYP_UNKNOWN); + + // This controls extra tracing of the "evaluation" of "VNF_MapSelect" functions. +#define FEATURE_VN_TRACE_APPLY_SELECTORS 1 + + // Return the value number corresponding to constructing "MapSelect(map, f0)", where "f0" is the + // (value number of) the first field in "fieldSeq". (The type of this application will be the type of "f0".) + // If there are no remaining fields in "fieldSeq", return that value number; otherwise, return VNApplySelectors + // applied to that value number and the remainder of "fieldSeq". When the 'fieldSeq' specifies a TYP_STRUCT + // then the size of the struct is returned by 'wbFinalStructSize' (when it is non-null) + ValueNum VNApplySelectors(ValueNumKind vnk, ValueNum map, FieldSeqNode* fieldSeq, size_t* wbFinalStructSize = nullptr); + + // Used after VNApplySelectors has determined that "selectedVN" is contained in a Map using VNForMapSelect + // It determines whether the 'selectedVN' is of an appropriate type to be read using and indirection of 'indType' + // If it is appropriate type then 'selectedVN' is returned, otherwise it may insert a cast to indType + // or return a unique value number for an incompatible indType. + ValueNum VNApplySelectorsTypeCheck(ValueNum selectedVN, var_types indType, size_t structSize); + + // Assumes that "map" represents a map that is addressable by the fields in "fieldSeq", to get + // to a value of the type of "rhs". Returns an expression for the RHS of an assignment to a location + // containing value "map" that will change the field addressed by "fieldSeq" to "rhs", leaving all other + // indices in "map" the same. + ValueNum VNApplySelectorsAssign(ValueNumKind vnk, ValueNum map, FieldSeqNode* fieldSeq, ValueNum rhs, var_types indType); + + // Used after VNApplySelectorsAssign has determined that "elem" is to be writen into a Map using VNForMapStore + // It determines whether the 'elem' is of an appropriate type to be writen using using an indirection of 'indType' + // It may insert a cast to indType or return a unique value number for an incompatible indType. + ValueNum VNApplySelectorsAssignTypeCoerce(ValueNum elem, var_types indType); + + ValueNumPair VNPairApplySelectors(ValueNumPair map, FieldSeqNode* fieldSeq, var_types indType); + + ValueNumPair VNPairApplySelectorsAssign(ValueNumPair map, FieldSeqNode* fieldSeq, ValueNumPair rhs, var_types indType) + { + return ValueNumPair(VNApplySelectorsAssign(VNK_Liberal, map.GetLiberal(), fieldSeq, rhs.GetLiberal(), indType), + VNApplySelectorsAssign(VNK_Conservative, map.GetConservative(), fieldSeq, rhs.GetConservative(), indType)); + } + + // Compute the normal ValueNumber for a cast with no exceptions + ValueNum VNForCast(ValueNum srcVN, var_types castToType, var_types castFromType, + bool srcIsUnsigned = false); + + // Compute the ValueNumberPair for a cast + ValueNumPair VNPairForCast(ValueNumPair srcVNPair, var_types castToType, var_types castFromType, + bool srcIsUnsigned = false, + bool hasOverflowCheck = false); + + // PtrToLoc values need to express a field sequence as one of their arguments. VN for null represents + // empty sequence, otherwise, "FieldSeq(VN(FieldHandle), restOfSeq)". + ValueNum VNForFieldSeq(FieldSeqNode* fieldSeq); + + // Requires that "vn" represents a field sequence, that is, is the result of a call to VNForFieldSeq. + // Returns the FieldSequence it represents. + FieldSeqNode* FieldSeqVNToFieldSeq(ValueNum vn); + + // Both argument must represent field sequences; returns the value number representing the + // concatenation "fsVN1 || fsVN2". + ValueNum FieldSeqVNAppend(ValueNum fsVN1, ValueNum fsVN2); + + // Requires "lclVarVN" be a value number for a GT_LCL_VAR pointer tree. + // Requires "fieldSeqVN" be a field sequence value number. + // Requires "typ" to be a TYP_REF/TYP_BYREF used for VNF_PtrToLoc. + // When "fieldSeqVN" is VNForNotAField, a unique VN is generated using m_uPtrToLocNotAFieldCount. + ValueNum VNForPtrToLoc(var_types typ, ValueNum lclVarVN, ValueNum fieldSeqVN); + + // If "opA" has a PtrToLoc, PtrToArrElem, or PtrToStatic application as its value numbers, and "opB" is an integer with + // a "fieldSeq", returns the VN for the pointer form extended with the field sequence; or else NoVN. + ValueNum ExtendPtrVN(GenTreePtr opA, GenTreePtr opB); + // If "opA" has a PtrToLoc, PtrToArrElem, or PtrToStatic application as its value numbers, returns the VN for the pointer form + // extended with "fieldSeq"; or else NoVN. + ValueNum ExtendPtrVN(GenTreePtr opA, FieldSeqNode* fieldSeq); + + // Queries on value numbers. + // All queries taking value numbers require that those value numbers are valid, that is, that + // they have been returned by previous "VNFor..." operations. They can assert false if this is + // not true. + + // Returns TYP_UNKNOWN if the given value number has not been given a type. + var_types TypeOfVN(ValueNum vn); + + // Returns true iff the VN represents a (non-handle) constant. + bool IsVNConstant(ValueNum vn); + + struct ArrLenArithBoundInfo + { + // (vnArr.len - 1) > vnOp + // (vnArr.len arrOper arrOp) cmpOper cmpOp + ValueNum vnArray; + unsigned arrOper; + ValueNum arrOp; + unsigned cmpOper; + ValueNum cmpOp; + ArrLenArithBoundInfo() + : vnArray(NoVN) + , arrOp(NoVN) + , cmpOp(NoVN) + , arrOper(GT_NONE) + , cmpOper(GT_NONE) + { + } +#ifdef DEBUG + void dump(ValueNumStore* vnStore) + { + if (vnArray != NoVN) + { + vnStore->vnDump(vnStore->m_pComp, vnArray); + if (arrOper != GT_NONE) + { + printf(vnStore->VNFuncName((VNFunc) arrOper)); + vnStore->vnDump(vnStore->m_pComp, arrOp); + } + } + printf(" "); + printf(vnStore->VNFuncName((VNFunc) cmpOper)); + vnStore->vnDump(vnStore->m_pComp, cmpOp); + printf(" "); + } +#endif + }; + + // Check if "vn" is "new [] (type handle, size)" + bool IsVNNewArr(ValueNum vn); + + // Check if "vn" IsVNNewArr and return <= 0 if arr size cannot be determined, else array size. + int GetNewArrSize(ValueNum vn); + + // Check if "vn" is "a.len" + bool IsVNArrLen(ValueNum vn); + + // If "vn" is VN(a.len) then return VN(a); NoVN if VN(a) can't be determined. + ValueNum GetArrForLenVn(ValueNum vn); + + // If "vn" is of the form "var < a.len" or "a.len <= var" return true. + bool IsVNArrLenBound(ValueNum vn); + + // If "vn" is arr len bound, then populate the "info" fields for the arrVn, cmpOp, cmpOper. + void GetArrLenBoundInfo(ValueNum vn, ArrLenArithBoundInfo* info); + + // If "vn" is of the form "a.len +/- var" return true. + bool IsVNArrLenArith(ValueNum vn); + + // If "vn" is arr len arith, then populate the "info" fields for arrOper, arrVn, arrOp. + void GetArrLenArithInfo(ValueNum vn, ArrLenArithBoundInfo* info); + + // If "vn" is of the form "var < a.len +/- k" return true. + bool IsVNArrLenArithBound(ValueNum vn); + + // If "vn" is arr len arith bound, then populate the "info" fields for cmpOp, cmpOper. + void GetArrLenArithBoundInfo(ValueNum vn, ArrLenArithBoundInfo* info); + + // Returns the flags on the current handle. GTF_ICON_SCOPE_HDL for example. + unsigned GetHandleFlags(ValueNum vn); + + // Returns true iff the VN represents a handle constant. + bool IsVNHandle(ValueNum vn); + + // Requires that "vn" is a constant, and that its type is compatible with the explicitly passed + // type "T". + template<typename T> + T ConstantValue(ValueNum vn) + { + Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn)); + assert(c->m_attribs == CEA_Const || c->m_attribs == CEA_Handle); + + + unsigned offset = ChunkOffset(vn); + + switch (c->m_typ) + { + case TYP_REF: + assert(0 <= offset && offset <= 1); // Null or exception. + __fallthrough; + + case TYP_BYREF: +#ifndef PLATFORM_UNIX + assert(&typeid(T) == &typeid(size_t)); // We represent ref/byref constants as size_t's. +#endif // PLATFORM_UNIX + __fallthrough; + + case TYP_INT: + case TYP_LONG: + case TYP_FLOAT: + case TYP_DOUBLE: + if (c->m_attribs == CEA_Handle) + { + C_ASSERT(offsetof(VNHandle, m_cnsVal) == 0); + return (T) reinterpret_cast<VNHandle*>(c->m_defs)[offset].m_cnsVal; + } + return reinterpret_cast<T*>(c->m_defs)[offset]; + + default: + assert(false); // We do not record constants of this typ. + return (T)0; + } + } + + // Given a value number "vn", go through the list of VNs that are handles + // to find if it is present, if so, return "true", else "false." + bool IsHandle(ValueNum vn); + + // Requires "mthFunc" to be an intrinsic math function (one of the allowable values for the "gtMath" field + // of a GenTreeMath node). Return the value number for the application of this function to "arg0VN". + ValueNum EvalMathFunc(var_types typ, CorInfoIntrinsics mthFunc, ValueNum arg0VN); + ValueNumPair EvalMathFunc(var_types typ, CorInfoIntrinsics mthFunc, ValueNumPair arg0VNP) + { + return ValueNumPair(EvalMathFunc(typ, mthFunc, arg0VNP.GetLiberal()), + EvalMathFunc(typ, mthFunc, arg0VNP.GetConservative())); + } + + // Returns "true" iff "vn" represents a function application. + bool IsVNFunc(ValueNum vn); + + // If "vn" represents a function application, returns "true" and set "*funcApp" to + // the function application it represents; otherwise, return "false." + bool GetVNFunc(ValueNum vn, VNFuncApp* funcApp); + + // Requires that "vn" represents a "heap address" the sum of a "TYP_REF" value and some integer + // value. Returns the TYP_REF value. + ValueNum VNForRefInAddr(ValueNum vn); + + // Returns "true" iff "vn" is a valid value number -- one that has been previously returned. + bool VNIsValid(ValueNum vn); + +#ifdef DEBUG + // This controls whether we recursively call vnDump on function arguments. +#define FEATURE_VN_DUMP_FUNC_ARGS 0 + + // Prints, to standard out, a representation of "vn". + void vnDump(Compiler* comp, ValueNum vn, bool isPtr = false); + + // Requires "fieldSeq" to be a field sequence VNFuncApp. + // Prints a representation (comma-separated list of field names) on standard out. + void vnDumpFieldSeq(Compiler* comp, VNFuncApp* fieldSeq, bool isHead); + + // Requires "mapSelect" to be a map select VNFuncApp. + // Prints a representation of a MapSelect operation on standard out. + void vnDumpMapSelect(Compiler* comp, VNFuncApp* mapSelect); + + // Requires "mapStore" to be a map store VNFuncApp. + // Prints a representation of a MapStore operation on standard out. + void vnDumpMapStore(Compiler* comp, VNFuncApp* mapStore); + + // Returns the string name of "vnf". + static const char* VNFuncName(VNFunc vnf); + // Used in the implementation of the above. + static const char* VNFuncNameArr[]; + + // Returns the string name of "vn" when it is a reserved value number, nullptr otherwise + static const char* reservedName(ValueNum vn); + +#endif // DEBUG + + // Returns true if "vn" is a reserved value number + static bool isReservedVN(ValueNum); + +#define VALUENUM_SUPPORT_MERGE 0 + // If we're going to support the Merge operation, and do it right, we really need to use an entire + // egraph data structure, so that we can do congruence closure, and discover congruences implied + // by the eq-class merge. +#if VALUENUM_SUPPORT_MERGE + // It may be that we provisionally give two expressions distinct value numbers, then later discover + // that the values of the expressions are provably equal. We allow the two value numbers to be + // "merged" -- after the merge, they represent the same abstract value. + void MergeVNs(ValueNum vn1, ValueNum vn2); +#endif + + +private: + + // We will allocate value numbers in "chunks". Each chunk will have the same type and "constness". + static const unsigned LogChunkSize = 6; + static const unsigned ChunkSize = 1 << LogChunkSize; + static const unsigned ChunkOffsetMask = ChunkSize - 1; + + // A "ChunkNum" is a zero-based index naming a chunk in the Store, or else the special "NoChunk" value. + typedef UINT32 ChunkNum; + static const ChunkNum NoChunk = UINT32_MAX; + + // Returns the ChunkNum of the Chunk that holds "vn" (which is required to be a valid + // value number, i.e., one returned by some VN-producing method of this class). + static ChunkNum GetChunkNum(ValueNum vn) + { + return vn >> LogChunkSize; + } + + // Returns the offset of the given "vn" within its chunk. + static unsigned ChunkOffset(ValueNum vn) + { + return vn & ChunkOffsetMask; + } + + // The base VN of the next chunk to be allocated. Should always be a multiple of ChunkSize. + ValueNum m_nextChunkBase; + + DECLARE_TYPED_ENUM(ChunkExtraAttribs,BYTE) + { + CEA_None, // No extra attributes. + CEA_Const, // This chunk contains constant values. + CEA_Handle, // This chunk contains handle constants. + CEA_Func0, // Represents functions of arity 0. + CEA_Func1, // ...arity 1. + CEA_Func2, // ...arity 2. + CEA_Func3, // ...arity 3. + CEA_Func4, // ...arity 4. + CEA_Count + } + END_DECLARE_TYPED_ENUM(ChunkExtraAttribs,BYTE); + + // A "Chunk" holds "ChunkSize" value numbers, starting at "m_baseVN". All of these share the same + // "m_typ" and "m_attribs". These properties determine the interpretation of "m_defs", as discussed below. + struct Chunk + { + // If "m_defs" is non-null, it is an array of size ChunkSize, whose element type is determined by the other members. + // The "m_numUsed" field indicates the number of elements of "m_defs" that are already consumed (the next one to allocate). + void* m_defs; + unsigned m_numUsed; + + // The value number of the first VN in the chunk. + ValueNum m_baseVN; + + // The common attributes of this chunk. + var_types m_typ; + ChunkExtraAttribs m_attribs; + + // Initialize a chunk, starting at "*baseVN", for the given "typ" and "attribs" (using "alloc" for allocations). + // (Increments "*baseVN" by ChunkSize.) + Chunk(IAllocator* alloc, ValueNum* baseVN, var_types typ, ChunkExtraAttribs attribs); + + // Requires that "m_numUsed < ChunkSize." Returns the offset of the allocated VN within the chunk; the + // actual VN is this added to the "m_baseVN" of the chunk. + unsigned AllocVN() + { + assert(m_numUsed < ChunkSize); + return m_numUsed++; + } + }; + + struct VNHandle: public KeyFuncsDefEquals<VNHandle> + { + ssize_t m_cnsVal; + unsigned m_flags; + // Don't use a constructor to use the default copy constructor for hashtable rehash. + static void Initialize(VNHandle* handle, ssize_t m_cnsVal, unsigned m_flags) + { + handle->m_cnsVal = m_cnsVal; + handle->m_flags = m_flags; + } + bool operator==(const VNHandle& y) const + { + return m_cnsVal == y.m_cnsVal && m_flags == y.m_flags; + } + static unsigned GetHashCode(const VNHandle& val) + { + return static_cast<unsigned>(val.m_cnsVal); + } + }; + + struct VNDefFunc0Arg + { + VNFunc m_func; + VNDefFunc0Arg(VNFunc func) : m_func(func) {} + + VNDefFunc0Arg(): m_func(VNF_COUNT) {} + + bool operator==(const VNDefFunc0Arg& y) const + { + return m_func == y.m_func; + } + }; + + struct VNDefFunc1Arg: public VNDefFunc0Arg + { + ValueNum m_arg0; + VNDefFunc1Arg(VNFunc func, ValueNum arg0) : VNDefFunc0Arg(func), m_arg0(arg0) {} + + VNDefFunc1Arg(): VNDefFunc0Arg(), m_arg0(ValueNumStore::NoVN) {} + + bool operator==(const VNDefFunc1Arg& y) const + { + return VNDefFunc0Arg::operator==(y) && m_arg0 == y.m_arg0; + } + }; + + struct VNDefFunc2Arg : public VNDefFunc1Arg + { + ValueNum m_arg1; + VNDefFunc2Arg(VNFunc func, ValueNum arg0, ValueNum arg1) : VNDefFunc1Arg(func, arg0), m_arg1(arg1) {} + + VNDefFunc2Arg(): m_arg1(ValueNumStore::NoVN) {} + + bool operator==(const VNDefFunc2Arg& y) const + { + return VNDefFunc1Arg::operator==(y) && m_arg1 == y.m_arg1; + } + }; + + struct VNDefFunc3Arg : public VNDefFunc2Arg + { + ValueNum m_arg2; + VNDefFunc3Arg(VNFunc func, ValueNum arg0, ValueNum arg1, ValueNum arg2) : VNDefFunc2Arg(func, arg0, arg1), m_arg2(arg2) {} + VNDefFunc3Arg() : m_arg2(ValueNumStore::NoVN) {} + + bool operator==(const VNDefFunc3Arg& y) const + { + return VNDefFunc2Arg::operator==(y) && m_arg2 == y.m_arg2; + } + }; + + struct VNDefFunc4Arg : public VNDefFunc3Arg + { + ValueNum m_arg3; + VNDefFunc4Arg(VNFunc func, ValueNum arg0, ValueNum arg1, ValueNum arg2, ValueNum arg3) : VNDefFunc3Arg(func, arg0, arg1, arg2), m_arg3(arg3) {} + VNDefFunc4Arg() : m_arg3(ValueNumStore::NoVN) {} + + bool operator==(const VNDefFunc4Arg& y) const + { + return VNDefFunc3Arg::operator==(y) && m_arg3 == y.m_arg3; + } + }; + + // When we evaluate "select(m, i)", if "m" is a the value of a phi definition, we look at + // all the values of the phi args, and see if doing the "select" on each of them yields identical + // results. If so, that is the result of the entire "select" form. We have to be careful, however, + // because phis may be recursive in the presence of loop structures -- the VN for the phi may be (or be + // part of the definition of) the VN's of some of the arguments. But there will be at least one + // argument that does *not* depend on the outer phi VN -- after all, we had to get into the loop somehow. + // So we have to be careful about breaking infinite recursion. We can ignore "recursive" results -- if all the + // non-recursive results are the same, the recursion indicates that the loop structure didn't alter the result. + // This stack represents the set of outer phis such that select(phi, ind) is being evaluated. + ExpandArrayStack<VNDefFunc2Arg> m_fixedPointMapSels; + +#ifdef DEBUG + // Returns "true" iff "m_fixedPointMapSels" is non-empty, and it's top element is + // "select(map, index)". + bool FixedPointMapSelsTopHasValue(ValueNum map, ValueNum index); +#endif + + // Returns true if "sel(map, ind)" is a member of "m_fixedPointMapSels". + bool SelectIsBeingEvaluatedRecursively(ValueNum map, ValueNum ind); + + // This is a map from "chunk number" to the attributes of the chunk. + ExpandArrayStack<Chunk*> m_chunks; + + // These entries indicate the current allocation chunk, if any, for a <var_types, ChunkExtraAttribs> pair. + // If the value is NoChunk, it indicates that there is no current allocation chunk for that pair, otherwise + // it is the index in "m_chunks" of a chunk with the given attributes, in which the next allocation should + // be attempted. + ChunkNum m_curAllocChunk[TYP_COUNT][CEA_Count]; + + // Returns a (pointer to a) chunk in which a new value number may be allocated. + Chunk* GetAllocChunk(var_types typ, ChunkExtraAttribs attribs); + + // First, we need mechanisms for mapping from constants to value numbers. + // For small integers, we'll use an array. + static const int SmallIntConstMin = -1; + static const int SmallIntConstMax = 10; + static const unsigned SmallIntConstNum = SmallIntConstMax - SmallIntConstMin + 1; + static bool IsSmallIntConst(int i) { return SmallIntConstMin <= i && i <= SmallIntConstMax; } + ValueNum m_VNsForSmallIntConsts[SmallIntConstNum]; + + struct ValueNumList + { + ValueNum vn; + ValueNumList* next; + ValueNumList(const ValueNum& v, ValueNumList* n = NULL) : vn(v), next(n) { } + }; + + // Keeps track of value numbers that are integer constants and also handles (GTG_ICON_HDL_MASK.) + ValueNumList* m_intConHandles; + + typedef VNMap<INT32> IntToValueNumMap; + IntToValueNumMap* m_intCnsMap; + IntToValueNumMap* GetIntCnsMap() + { + if (m_intCnsMap == NULL) m_intCnsMap = new (m_alloc) IntToValueNumMap(m_alloc); + return m_intCnsMap; + } + + ValueNum GetVNForIntCon(INT32 cnsVal) + { + ValueNum res; + if (GetIntCnsMap()->Lookup(cnsVal, &res)) + { + return res; + } + else + { + Chunk* c = GetAllocChunk(TYP_INT, CEA_Const); + unsigned offsetWithinChunk = c->AllocVN(); + res = c->m_baseVN + offsetWithinChunk; + reinterpret_cast<INT32*>(c->m_defs)[offsetWithinChunk] = cnsVal; + GetIntCnsMap()->Set(cnsVal, res); + return res; + } + } + + typedef VNMap<INT64> LongToValueNumMap; + LongToValueNumMap* m_longCnsMap; + LongToValueNumMap* GetLongCnsMap() + { + if (m_longCnsMap == NULL) m_longCnsMap = new (m_alloc) LongToValueNumMap(m_alloc); + return m_longCnsMap; + } + + typedef VNMap<VNHandle, VNHandle> HandleToValueNumMap; + HandleToValueNumMap* m_handleMap; + HandleToValueNumMap* GetHandleMap() + { + if (m_handleMap == NULL) m_handleMap = new (m_alloc) HandleToValueNumMap(m_alloc); + return m_handleMap; + } + + struct LargePrimitiveKeyFuncsFloat : public LargePrimitiveKeyFuncs<float> + { + static bool Equals(float x, float y) + { + return *(unsigned*) &x == *(unsigned*) &y; + } + }; + + typedef VNMap<float, LargePrimitiveKeyFuncsFloat> FloatToValueNumMap; + FloatToValueNumMap* m_floatCnsMap; + FloatToValueNumMap* GetFloatCnsMap() + { + if (m_floatCnsMap == NULL) m_floatCnsMap = new (m_alloc) FloatToValueNumMap(m_alloc); + return m_floatCnsMap; + } + + // In the JIT we need to distinguish -0.0 and 0.0 for optimizations. + struct LargePrimitiveKeyFuncsDouble : public LargePrimitiveKeyFuncs<double> + { + static bool Equals(double x, double y) + { + return *(__int64*) &x == *(__int64*) &y; + } + }; + + typedef VNMap<double, LargePrimitiveKeyFuncsDouble> DoubleToValueNumMap; + DoubleToValueNumMap* m_doubleCnsMap; + DoubleToValueNumMap* GetDoubleCnsMap() + { + if (m_doubleCnsMap == NULL) m_doubleCnsMap = new (m_alloc) DoubleToValueNumMap(m_alloc); + return m_doubleCnsMap; + } + + LongToValueNumMap* m_byrefCnsMap; + LongToValueNumMap* GetByrefCnsMap() + { + if (m_byrefCnsMap == NULL) m_byrefCnsMap = new (m_alloc) LongToValueNumMap(m_alloc); + return m_byrefCnsMap; + } + + + struct VNDefFunc0ArgKeyFuncs: public KeyFuncsDefEquals<VNDefFunc1Arg> + { + static unsigned GetHashCode(VNDefFunc1Arg val) + { + return (val.m_func << 24) + val.m_arg0; + } + }; + typedef VNMap<VNFunc> VNFunc0ToValueNumMap; + VNFunc0ToValueNumMap* m_VNFunc0Map; + VNFunc0ToValueNumMap* GetVNFunc0Map() + { + if (m_VNFunc0Map == NULL) m_VNFunc0Map = new (m_alloc) VNFunc0ToValueNumMap(m_alloc); + return m_VNFunc0Map; + } + + struct VNDefFunc1ArgKeyFuncs: public KeyFuncsDefEquals<VNDefFunc1Arg> + { + static unsigned GetHashCode(VNDefFunc1Arg val) + { + return (val.m_func << 24) + val.m_arg0; + } + }; + typedef VNMap<VNDefFunc1Arg, VNDefFunc1ArgKeyFuncs> VNFunc1ToValueNumMap; + VNFunc1ToValueNumMap* m_VNFunc1Map; + VNFunc1ToValueNumMap* GetVNFunc1Map() + { + if (m_VNFunc1Map == NULL) m_VNFunc1Map = new (m_alloc) VNFunc1ToValueNumMap(m_alloc); + return m_VNFunc1Map; + } + + struct VNDefFunc2ArgKeyFuncs: public KeyFuncsDefEquals<VNDefFunc2Arg> + { + static unsigned GetHashCode(VNDefFunc2Arg val) + { + return (val.m_func << 24) + (val.m_arg0 << 8) + val.m_arg1; + } + }; + typedef VNMap<VNDefFunc2Arg, VNDefFunc2ArgKeyFuncs> VNFunc2ToValueNumMap; + VNFunc2ToValueNumMap* m_VNFunc2Map; + VNFunc2ToValueNumMap* GetVNFunc2Map() + { + if (m_VNFunc2Map == NULL) m_VNFunc2Map = new (m_alloc) VNFunc2ToValueNumMap(m_alloc); + return m_VNFunc2Map; + } + + struct VNDefFunc3ArgKeyFuncs: public KeyFuncsDefEquals<VNDefFunc3Arg> + { + static unsigned GetHashCode(VNDefFunc3Arg val) + { + return (val.m_func << 24) + (val.m_arg0 << 16) + (val.m_arg1 << 8) + val.m_arg2; + } + }; + typedef VNMap<VNDefFunc3Arg, VNDefFunc3ArgKeyFuncs> VNFunc3ToValueNumMap; + VNFunc3ToValueNumMap* m_VNFunc3Map; + VNFunc3ToValueNumMap* GetVNFunc3Map() + { + if (m_VNFunc3Map == NULL) m_VNFunc3Map = new (m_alloc) VNFunc3ToValueNumMap(m_alloc); + return m_VNFunc3Map; + } + + struct VNDefFunc4ArgKeyFuncs: public KeyFuncsDefEquals<VNDefFunc4Arg> + { + static unsigned GetHashCode(VNDefFunc4Arg val) + { + return (val.m_func << 24) + (val.m_arg0 << 16) + (val.m_arg1 << 8) + val.m_arg2 + (val.m_arg3 << 12); + } + }; + typedef VNMap<VNDefFunc4Arg, VNDefFunc4ArgKeyFuncs> VNFunc4ToValueNumMap; + VNFunc4ToValueNumMap* m_VNFunc4Map; + VNFunc4ToValueNumMap* GetVNFunc4Map() + { + if (m_VNFunc4Map == NULL) m_VNFunc4Map = new (m_alloc) VNFunc4ToValueNumMap(m_alloc); + return m_VNFunc4Map; + } + + enum SpecialRefConsts + { + SRC_Null, + SRC_ZeroMap, + SRC_NotAField, + SRC_ReadOnlyHeap, + SRC_Void, + SRC_EmptyExcSet, + + SRC_NumSpecialRefConsts + }; + + + // Counter to keep track of all the unique not a field sequences that have been assigned to + // PtrToLoc, because the ptr was added to an offset that was not a field. + unsigned m_uPtrToLocNotAFieldCount; + + // The "values" of special ref consts will be all be "null" -- their differing meanings will + // be carried by the distinct value numbers. + static class Object* s_specialRefConsts[SRC_NumSpecialRefConsts]; + static class Object* s_nullConst; +}; + +// Inline functions. + +// static +bool ValueNumStore::GenTreeOpIsLegalVNFunc(genTreeOps gtOper) +{ + return (s_vnfOpAttribs[gtOper] & VNFOA_IllegalGenTreeOp) == 0; +} + +// static +bool ValueNumStore::VNFuncIsCommutative(VNFunc vnf) +{ + return (s_vnfOpAttribs[vnf] & VNFOA_Commutative) != 0; +} + +bool ValueNumStore::VNFuncIsComparison(VNFunc vnf) +{ + if (vnf >= VNF_Boundary) return false; + genTreeOps gtOp = genTreeOps(vnf); + return GenTree::OperIsCompare(gtOp) != 0; +} + +/*****************************************************************************/ +#endif // _VALUENUM_H_ +/*****************************************************************************/ |