diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index ef9f907bc8764..e94996501f3b0 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -165,7 +165,6 @@ set( JIT_SOURCES ssarenamestate.cpp stacklevelsetter.cpp treelifeupdater.cpp - typeinfo.cpp unwind.cpp utils.cpp valuenum.cpp diff --git a/src/coreclr/jit/_typeinfo.h b/src/coreclr/jit/_typeinfo.h index c8a12c761644a..e8558ba68b7fe 100644 --- a/src/coreclr/jit/_typeinfo.h +++ b/src/coreclr/jit/_typeinfo.h @@ -35,24 +35,6 @@ enum ti_types #define TI_I_IMPL TI_INT #endif -#ifdef DEBUG -#if VERBOSE_VERIFY -#define TI_DUMP_PADDING " " -#ifdef _MSC_VER -namespace -{ -#endif // _MSC_VER -const char* g_ti_type_names_map[] = { -#define DEF_TI(ti, nm) nm, -#include "titypes.h" -#undef DEF_TI -}; -#ifdef _MSC_VER -} -#endif // _MSC_VER -#endif // VERBOSE_VERIFY -#endif // DEBUG - #ifdef _MSC_VER namespace { @@ -66,15 +48,6 @@ const ti_types g_jit_types_map[] = { } #endif // _MSC_VER -#ifdef DEBUG -#if VERBOSE_VERIFY -inline const char* tiType2Str(ti_types type) -{ - return g_ti_type_names_map[type]; -} -#endif // VERBOSE_VERIFY -#endif // DEBUG - // typeInfo does not care about distinction between signed/unsigned // This routine converts all unsigned types to signed ones inline ti_types varType2tiType(var_types type) @@ -163,8 +136,7 @@ class methodPointerInfo * * Flags: LLLLLLLLLLLLLLLLffffffffffTTTTTT * - * L = local var # or instance field # - * x = unused + * L = unused * f = flags * T = type * @@ -179,102 +151,14 @@ class methodPointerInfo * TI_BYREF in this component. For example, the type component * of a "byref TI_INT" is TI_FLAG_BYREF | TI_INT. * - * NOTE carefully that Generic Type Variable info is - * only stored here in part. Values of type "T" (e.g "!0" in ILASM syntax), - * i.e. some generic variable type, appear only when verifying generic - * code. They come in two flavours: unboxed and boxed. Unboxed - * is the norm, e.g. a local, field or argument of type T. Boxed - * values arise from an IL instruction such as "box !0". - * The EE provides type handles for each different type - * variable and the EE's "canCast" operation decides casting - * for boxed type variable. Thus: - * - * (TI_REF, ) == boxed type variable - * - * (TI_REF, ) - * + TI_FLAG_GENERIC_TYPE_VAR == unboxed type variable - * - * Using TI_REF for these may seem odd but using TI_STRUCT means the - * code-generation parts of the importer get confused when they - * can't work out the size, GC-ness etc. of the "struct". So using TI_REF - * just tricks these backend parts into generating pseudo-trees for - * the generic code we're verifying. These trees then get thrown away - * anyway as we do verification of generic code in import-only mode. - * */ #define TI_FLAG_DATA_BITS 6 #define TI_FLAG_DATA_MASK ((1 << TI_FLAG_DATA_BITS) - 1) -// Flag indicating this item is uninitialized -// Note that if UNINIT and BYREF are both set, -// it means byref (uninit x) - i.e. we are pointing to an uninit - -#define TI_FLAG_UNINIT_OBJREF 0x00000040 - // Flag indicating this item is a byref - #define TI_FLAG_BYREF 0x00000080 - -// This item is a byref generated using the readonly. prefix -// to a ldelema or Address function on an array type. The -// runtime type check is ignored in these cases, but the -// resulting byref can only be used in order to perform a -// constraint call. - -#define TI_FLAG_BYREF_READONLY 0x00000100 - -// This item is the MSIL 'I' type which is pointer-sized -// (different size depending on platform) but which on ALL platforms -// is implicitly convertible with a 32-bit int but not with a 64-bit one. - -// Note: this flag is currently used only in 64-bit systems to annotate -// native int types. In 32 bits, since you can transparently coalesce int32 -// and native-int and both are the same size, JIT32 had no need to model -// native-ints as a separate entity. For 64-bit though, since they have -// different size, it's important to discern between a long and a native int -// since conversions between them are not verifiable. -#define TI_FLAG_NATIVE_INT 0x00000200 - -// This item contains the 'this' pointer (used for tracking) - -#define TI_FLAG_THIS_PTR 0x00001000 - -// This item is a byref to something which has a permanent home -// (e.g. a static field, or instance field of an object in GC heap, as -// opposed to the stack or a local variable). TI_FLAG_BYREF must also be -// set. This information is useful for tail calls and return byrefs. -// -// Instructions that generate a permanent home byref: -// -// ldelema -// ldflda of a ref object or another permanent home byref -// array element address Get() helper -// call or calli to a method that returns a byref and is verifiable or SkipVerify -// dup -// unbox - -#define TI_FLAG_BYREF_PERMANENT_HOME 0x00002000 - -// This is for use when verifying generic code. -// This indicates that the type handle is really an unboxed -// generic type variable (e.g. the result of loading an argument -// of type T in a class List). Without this flag -// the same type handle indicates a boxed generic value, -// e.g. the result of a "box T" instruction. -#define TI_FLAG_GENERIC_TYPE_VAR 0x00004000 - -// Number of bits local var # is shifted - -#define TI_FLAG_LOCAL_VAR_SHIFT 16 -#define TI_FLAG_LOCAL_VAR_MASK 0xFFFF0000 - -// Field info uses the same space as the local info - -#define TI_FLAG_FIELD_SHIFT TI_FLAG_LOCAL_VAR_SHIFT -#define TI_FLAG_FIELD_MASK TI_FLAG_LOCAL_VAR_MASK - -#define TI_ALL_BYREF_FLAGS (TI_FLAG_BYREF | TI_FLAG_BYREF_READONLY | TI_FLAG_BYREF_PERMANENT_HOME) +#define TI_ALL_BYREF_FLAGS (TI_FLAG_BYREF) /***************************************************************************** * A typeInfo can be one of several types: @@ -283,12 +167,6 @@ class methodPointerInfo * - An array (m_cls describes the array type) * - A byref (byref flag set, otherwise the same as the above), * - A Function Pointer (m_methodPointerInfo) - * - A byref local variable (byref and byref local flags set), can be - * uninitialized - * - * The reason that there can be 2 types of byrefs (general byrefs, and byref - * locals) is that byref locals initially point to uninitialized items. - * Therefore these byrefs must be tracked specially. */ class typeInfo @@ -299,15 +177,15 @@ class typeInfo struct { ti_types type : TI_FLAG_DATA_BITS; - unsigned uninitobj : 1; // used - unsigned byref : 1; // used - unsigned byref_readonly : 1; // used - unsigned nativeInt : 1; // used - unsigned : 1; // unused - unsigned : 1; // unused - unsigned thisPtr : 1; // used - unsigned thisPermHome : 1; // used - unsigned generic_type_var : 1; // used + unsigned : 1; // unused + unsigned byref : 1; // used + unsigned : 1; // unused + unsigned : 1; // unused + unsigned : 1; // unused + unsigned : 1; // unused + unsigned : 1; // unused + unsigned : 1; // unused + unsigned : 1; // unused } m_bits; DWORD m_flags; @@ -351,25 +229,12 @@ class typeInfo m_cls = NO_CLASS_HANDLE; } - static typeInfo nativeInt() - { - typeInfo result = typeInfo(TI_I_IMPL); -#ifdef TARGET_64BIT - result.m_flags |= TI_FLAG_NATIVE_INT; -#endif - return result; - } - - typeInfo(ti_types tiType, CORINFO_CLASS_HANDLE cls, bool typeVar = false) + typeInfo(ti_types tiType, CORINFO_CLASS_HANDLE cls) { assert(tiType == TI_STRUCT || tiType == TI_REF); assert(cls != nullptr && !isInvalidHandle(cls)); m_flags = tiType; - if (typeVar) - { - m_flags |= TI_FLAG_GENERIC_TYPE_VAR; - } - m_cls = cls; + m_cls = cls; } typeInfo(methodPointerInfo* methodPointerInfo) @@ -381,142 +246,14 @@ class typeInfo m_methodPointerInfo = methodPointerInfo; } -#ifdef DEBUG -#if VERBOSE_VERIFY - void Dump() const; -#endif // VERBOSE_VERIFY -#endif // DEBUG - public: - // Note that we specifically ignore the permanent byref here. The rationale is that - // the type system doesn't know about this (it's jit only), ie, signatures don't specify if - // a byref is safe, so they are fully equivalent for the jit, except for the RET instruction, - // instructions that load safe byrefs and the stack merging logic, which need to know about - // the bit - static bool AreEquivalent(const typeInfo& li, const typeInfo& ti) - { - DWORD allFlags = TI_FLAG_DATA_MASK | TI_FLAG_BYREF | TI_FLAG_BYREF_READONLY | TI_FLAG_GENERIC_TYPE_VAR | - TI_FLAG_UNINIT_OBJREF; -#ifdef TARGET_64BIT - allFlags |= TI_FLAG_NATIVE_INT; -#endif // TARGET_64BIT - - if ((li.m_flags & allFlags) != (ti.m_flags & allFlags)) - { - return false; - } - - unsigned type = li.m_flags & TI_FLAG_DATA_MASK; - assert(TI_ERROR < - TI_ONLY_ENUM); // TI_ERROR looks like it needs more than enum. This optimises the success case a bit - if (type > TI_ONLY_ENUM) - { - return true; - } - if (type == TI_ERROR) - { - return false; // TI_ERROR != TI_ERROR - } - assert(li.m_cls != NO_CLASS_HANDLE && ti.m_cls != NO_CLASS_HANDLE); - return li.m_cls == ti.m_cls; - } - -#ifdef DEBUG - // On 64-bit systems, nodes whose "proper" type is "native int" get labeled TYP_LONG. - // In the verification type system, we always transform "native int" to "TI_LONG" with the - // native int flag set. - // Ideally, we would keep track of which nodes labeled "TYP_LONG" are really "native int", but - // attempts to do that have proved too difficult. So in situations where we try to compare the - // verification type system and the node type system, we use this method, which allows the specific - // mismatch where "verTi" is TI_LONG with the native int flag and "nodeTi" is TI_LONG without the - // native int flag set. - static bool AreEquivalentModuloNativeInt(const typeInfo& verTi, const typeInfo& nodeTi) - { - if (AreEquivalent(verTi, nodeTi)) - { - return true; - } -#ifdef TARGET_64BIT - return (nodeTi.IsType(TI_I_IMPL) && tiCompatibleWith(nullptr, verTi, typeInfo::nativeInt(), true)) || - (verTi.IsType(TI_I_IMPL) && tiCompatibleWith(nullptr, typeInfo::nativeInt(), nodeTi, true)); -#else // TARGET_64BIT - return false; -#endif // !TARGET_64BIT - } -#endif // DEBUG - - static bool tiMergeToCommonParent(COMP_HANDLE CompHnd, typeInfo* pDest, const typeInfo* pSrc, bool* changed); - static bool tiCompatibleWith(COMP_HANDLE CompHnd, - const typeInfo& child, - const typeInfo& parent, - bool normalisedForStack); - - static bool tiMergeCompatibleWith(COMP_HANDLE CompHnd, - const typeInfo& child, - const typeInfo& parent, - bool normalisedForStack); - ///////////////////////////////////////////////////////////////////////// // Operations ///////////////////////////////////////////////////////////////////////// - void SetIsThisPtr() - { - m_flags |= TI_FLAG_THIS_PTR; - assert(m_bits.thisPtr); - } - - void ClearThisPtr() - { - m_flags &= ~(TI_FLAG_THIS_PTR); - } - - void SetIsPermanentHomeByRef() - { - assert(IsByRef()); - m_flags |= TI_FLAG_BYREF_PERMANENT_HOME; - } - - void SetIsReadonlyByRef() - { - assert(IsByRef()); - m_flags |= TI_FLAG_BYREF_READONLY; - } - - // Set that this item is uninitialized. - void SetUninitialisedObjRef() - { - assert((IsObjRef() && IsThisPtr())); - // For now, this is used only to track uninit this ptrs in ctors - - m_flags |= TI_FLAG_UNINIT_OBJREF; - assert(m_bits.uninitobj); - } - - // Set that this item is initialised. - void SetInitialisedObjRef() - { - assert((IsObjRef() && IsThisPtr())); - // For now, this is used only to track uninit this ptrs in ctors - - m_flags &= ~TI_FLAG_UNINIT_OBJREF; - } - - typeInfo& DereferenceByRef() - { - if (!IsByRef()) - { - m_flags = TI_ERROR; - INDEBUG(m_cls = NO_CLASS_HANDLE); - } - m_flags &= ~(TI_FLAG_THIS_PTR | TI_ALL_BYREF_FLAGS); - return *this; - } - typeInfo& MakeByRef() { assert(!IsByRef()); - m_flags &= ~(TI_FLAG_THIS_PTR); m_flags |= TI_FLAG_BYREF; return *this; } @@ -598,14 +335,7 @@ class typeInfo bool IsType(ti_types type) const { assert(type != TI_ERROR); - return (m_flags & (TI_FLAG_DATA_MASK | TI_FLAG_BYREF | TI_FLAG_BYREF_READONLY | TI_FLAG_BYREF_PERMANENT_HOME | - TI_FLAG_GENERIC_TYPE_VAR)) == DWORD(type); - } - - // Returns whether this is an objref - bool IsObjRef() const - { - return IsType(TI_REF) || IsType(TI_NULL); + return (m_flags & (TI_FLAG_DATA_MASK | TI_FLAG_BYREF)) == DWORD(type); } // Returns whether this is a by-ref @@ -614,27 +344,6 @@ class typeInfo return (m_flags & TI_FLAG_BYREF); } - // Returns whether this is the this pointer - bool IsThisPtr() const - { - return (m_flags & TI_FLAG_THIS_PTR); - } - - bool IsUnboxedGenericTypeVar() const - { - return !IsByRef() && (m_flags & TI_FLAG_GENERIC_TYPE_VAR); - } - - bool IsReadonlyByRef() const - { - return IsByRef() && (m_flags & TI_FLAG_BYREF_READONLY); - } - - bool IsPermanentHomeByRef() const - { - return IsByRef() && (m_flags & TI_FLAG_BYREF_PERMANENT_HOME); - } - // Returns whether this is a method desc bool IsMethod() const { @@ -652,65 +361,6 @@ class typeInfo return (IsStruct() || IsPrimitiveType()); } - // Does not return true for primitives. Will return true for value types that behave - // as primitives - bool IsValueClassWithClsHnd() const - { - if ((GetType() == TI_STRUCT) || - (m_cls && GetType() != TI_REF && GetType() != TI_METHOD && - GetType() != TI_ERROR)) // necessary because if byref bit is set, we return TI_ERROR) - { - return true; - } - else - { - return false; - } - } - - // Returns whether this is an integer or real number - // NOTE: Use NormaliseToPrimitiveType() if you think you may have a - // System.Int32 etc., because those types are not considered number - // types by this function. - bool IsNumberType() const - { - ti_types Type = GetType(); - - // I1, I2, Boolean, character etc. cannot exist plainly - - // everything is at least an I4 - - return (Type == TI_INT || Type == TI_LONG || Type == TI_DOUBLE); - } - - // Returns whether this is an integer - // NOTE: Use NormaliseToPrimitiveType() if you think you may have a - // System.Int32 etc., because those types are not considered number - // types by this function. - bool IsIntegerType() const - { - ti_types Type = GetType(); - - // I1, I2, Boolean, character etc. cannot exist plainly - - // everything is at least an I4 - - return (Type == TI_INT || Type == TI_LONG); - } - - // Returns true whether this is an integer or a native int. - bool IsIntOrNativeIntType() const - { -#ifdef TARGET_64BIT - return (GetType() == TI_INT) || AreEquivalent(*this, nativeInt()); -#else - return IsType(TI_INT); -#endif - } - - bool IsNativeIntType() const - { - return AreEquivalent(*this, nativeInt()); - } - // Returns whether this is a primitive type (not a byref, objref, // array, null, value class, invalid value) // May Need to normalise first (m/r/I4 --> I4) @@ -723,25 +373,6 @@ class typeInfo Type == TI_DOUBLE); } - // Returns whether this is the null objref - bool IsNullObjRef() const - { - return (IsType(TI_NULL)); - } - - // must be for a local which is an object type (i.e. has a slot >= 0) - // for primitive locals, use the liveness bitmap instead - // Note that this works if the error is 'Byref' - bool IsDead() const - { - return (m_flags & (TI_FLAG_DATA_MASK)) == TI_ERROR; - } - - bool IsUninitialisedObjRef() const - { - return (m_flags & TI_FLAG_UNINIT_OBJREF); - } - private: // used to make functions that return typeinfo efficient. typeInfo(DWORD flags, CORINFO_CLASS_HANDLE cls) @@ -751,7 +382,6 @@ class typeInfo } friend typeInfo ByRef(const typeInfo& ti); - friend typeInfo DereferenceByRef(const typeInfo& ti); friend typeInfo NormaliseForStack(const typeInfo& ti); }; @@ -765,12 +395,6 @@ inline typeInfo ByRef(const typeInfo& ti) { return typeInfo(ti).MakeByRef(); } - -// given ti which is a byref, return the type it points at -inline typeInfo DereferenceByRef(const typeInfo& ti) -{ - return typeInfo(ti).DereferenceByRef(); -} /*****************************************************************************/ #endif // _TYPEINFO_H_ /*****************************************************************************/ diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 3d0f99ca361e3..53c2064292c78 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -91,23 +91,11 @@ struct StackEntry GenTree* val; typeInfo seTypeInfo; }; -/*****************************************************************************/ - -enum ThisInitState -{ - TIS_Bottom, // We don't know anything about the 'this' pointer. - TIS_Uninit, // The 'this' pointer for this constructor is known to be uninitialized. - TIS_Init, // The 'this' pointer for this constructor is known to be initialized. - TIS_Top, // This results from merging the state of two blocks one with TIS_Unint and the other with TIS_Init. - // We use this in fault blocks to prevent us from accessing the 'this' pointer, but otherwise - // allowing the fault block to generate code. -}; struct EntryState { - ThisInitState thisInitialized; // used to track whether the this ptr is initialized. - unsigned esStackDepth; // size of esStack - StackEntry* esStack; // ptr to stack + unsigned esStackDepth; // size of esStack + StackEntry* esStack; // ptr to stack }; // Enumeration of the kinds of memory whose state changes the compiler tracks @@ -1236,8 +1224,7 @@ struct BasicBlock : private LIR::Range unsigned bbID; #endif // DEBUG - ThisInitState bbThisOnEntry() const; - unsigned bbStackDepthOnEntry() const; + unsigned bbStackDepthOnEntry() const; void bbSetStack(void* stackBuffer); StackEntry* bbStackOnEntry() const; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 3d284ecf96a27..bc96171a9c747 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3929,13 +3929,6 @@ class Compiler unsigned impInitBlockLineInfo(); bool impIsThis(GenTree* obj); - bool impIsLDFTN_TOKEN(const BYTE* delegateCreateStart, const BYTE* newobjCodeAddr); - bool impIsDUP_LDVIRTFTN_TOKEN(const BYTE* delegateCreateStart, const BYTE* newobjCodeAddr); - bool impIsAnySTLOC(OPCODE opcode) - { - return ((opcode == CEE_STLOC) || (opcode == CEE_STLOC_S) || - ((opcode >= CEE_STLOC_0) && (opcode <= CEE_STLOC_3))); - } void impPopCallArgs(CORINFO_SIG_INFO* sig, GenTreeCall* call); @@ -3954,10 +3947,9 @@ class Compiler struct PendingDsc { - PendingDsc* pdNext; - BasicBlock* pdBB; - SavedStack pdSavedStack; - ThisInitState pdThisPtrInit; + PendingDsc* pdNext; + BasicBlock* pdBB; + SavedStack pdSavedStack; }; PendingDsc* impPendingList; // list of BBs currently waiting to be imported. @@ -4128,7 +4120,6 @@ class Compiler void FreeBlockListNode(BlockListNode* node); - bool impIsValueType(typeInfo* pTypeInfo); var_types mangleVarArgsType(var_types type); regNumber getCallArgIntRegister(regNumber floatReg); @@ -10235,37 +10226,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX } #endif // DEBUG - /* - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - XX XX - XX typeInfo XX - XX XX - XX Checks for type compatibility and merges types XX - XX XX - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - */ - -public: - // Returns true if child is equal to or a subtype of parent for merge purposes - // This support is necessary to support attributes that are not described in - // for example, signatures. For example, the permanent home byref (byref that - // points to the gc heap), isn't a property of method signatures, therefore, - // it is safe to have mismatches here (that tiCompatibleWith will not flag), - // but when deciding if we need to reimport a block, we need to take these - // in account - bool tiMergeCompatibleWith(const typeInfo& pChild, const typeInfo& pParent, bool normalisedForStack) const; - - // Returns true if child is equal to or a subtype of parent. - // normalisedForStack indicates that both types are normalised for the stack - bool tiCompatibleWith(const typeInfo& pChild, const typeInfo& pParent, bool normalisedForStack) const; - - // Merges pDest and pSrc. Returns false if merge is undefined. - // *pDest is modified to represent the merged type. Sets "*changed" to true - // if this changes "*pDest". - bool tiMergeToCommonParent(typeInfo* pDest, const typeInfo* pSrc, bool* changed) const; - /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -10278,45 +10238,22 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ public: - // The following is used to track liveness of local variables, initialization - // of valueclass constructors, and type safe use of IL instructions. - - // dynamic state info needed for verification EntryState verCurrentState; - // this ptr of object type .ctors are considered intited only after - // the base class ctor is called, or an alternate ctor is called. - // An uninited this ptr can be used to access fields, but cannot - // be used to call a member function. - bool verTrackObjCtorInitState; - void verInitBBEntryState(BasicBlock* block, EntryState* currentState); - // Requires that "tis" is not TIS_Bottom -- it's a definite init/uninit state. - void verSetThisInit(BasicBlock* block, ThisInitState tis); void verInitCurrentState(); void verResetCurrentState(BasicBlock* block, EntryState* currentState); - // Merges the current verification state into the entry state of "block", return false if that merge fails, - // TRUE if it succeeds. Further sets "*changed" to true if this changes the entry state of "block". - bool verMergeEntryStates(BasicBlock* block, bool* changed); - void verConvertBBToThrowVerificationException(BasicBlock* block DEBUGARG(bool logMsg)); void verHandleVerificationFailure(BasicBlock* block DEBUGARG(bool logMsg)); typeInfo verMakeTypeInfoForLocal(unsigned lclNum); - typeInfo verMakeTypeInfo(CORINFO_CLASS_HANDLE clsHnd, - bool bashStructToRef = false); // converts from jit type representation to typeInfo + typeInfo verMakeTypeInfo(CORINFO_CLASS_HANDLE clsHnd); // converts from jit type representation to typeInfo typeInfo verMakeTypeInfo(CorInfoType ciType, CORINFO_CLASS_HANDLE clsHnd); // converts from jit type representation to typeInfo - bool verIsSDArray(const typeInfo& ti); - typeInfo verGetArrayElemType(const typeInfo& ti); typeInfo verParseArgSigToTypeInfo(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args); bool verIsByRefLike(const typeInfo& ti); - bool verIsSafeToReturnByRef(const typeInfo& ti); - - // generic type variables range over types that satisfy IsBoxable - bool verIsBoxable(const typeInfo& ti); void DECLSPEC_NORETURN verRaiseVerifyException(INDEBUG(const char* reason) DEBUGARG(const char* file) DEBUGARG(unsigned line)); @@ -10324,35 +10261,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX DEBUGARG(unsigned line)); bool verCheckTailCallConstraint(OPCODE opcode, CORINFO_RESOLVED_TOKEN* pResolvedToken, - CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, // Is this a "constrained." call - // on a type parameter? - bool speculative // If true, won't throw if verificatoin fails. Instead it will - // return false to the caller. - // If false, it will throw. - ); - bool verIsBoxedValueType(const typeInfo& ti); - - void verVerifyCall(OPCODE opcode, - CORINFO_RESOLVED_TOKEN* pResolvedToken, - CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, - bool tailCall, - bool readonlyCall, // is this a "readonly." call? - const BYTE* delegateCreateStart, - const BYTE* codeAddr, - CORINFO_CALL_INFO* callInfo DEBUGARG(const char* methodName)); - - bool verCheckDelegateCreation(const BYTE* delegateCreateStart, const BYTE* codeAddr, mdMemberRef& targetMemberRef); - - typeInfo verVerifySTIND(const typeInfo& ptr, const typeInfo& value, const typeInfo& instrType); - typeInfo verVerifyLDIND(const typeInfo& ptr, const typeInfo& instrType); - void verVerifyField(CORINFO_RESOLVED_TOKEN* pResolvedToken, - const CORINFO_FIELD_INFO& fieldInfo, - const typeInfo* tiThis, - bool mutator, - bool allowPlainStructAsThis = false); - void verVerifyCond(const typeInfo& tiOp1, const typeInfo& tiOp2, unsigned opcode); - void verVerifyThisPtrInitialised(); - bool verIsCallToInitThisPtr(CORINFO_CLASS_HANDLE context, CORINFO_CLASS_HANDLE target); + CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken); #ifdef DEBUG diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 7b1eedc50fec9..73f306c4b7f2a 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3773,30 +3773,6 @@ inline bool Compiler::impIsThis(GenTree* obj) } } -/***************************************************************************** - * - * Check to see if the delegate is created using "LDFTN " or not. - */ - -inline bool Compiler::impIsLDFTN_TOKEN(const BYTE* delegateCreateStart, const BYTE* newobjCodeAddr) -{ - assert(newobjCodeAddr[0] == CEE_NEWOBJ); - return (newobjCodeAddr - delegateCreateStart == 6 && // LDFTN takes 6 bytes - delegateCreateStart[0] == CEE_PREFIX1 && delegateCreateStart[1] == (CEE_LDFTN & 0xFF)); -} - -/***************************************************************************** - * - * Check to see if the delegate is created using "DUP LDVIRTFTN " or not. - */ - -inline bool Compiler::impIsDUP_LDVIRTFTN_TOKEN(const BYTE* delegateCreateStart, const BYTE* newobjCodeAddr) -{ - assert(newobjCodeAddr[0] == CEE_NEWOBJ); - return (newobjCodeAddr - delegateCreateStart == 7 && // DUP LDVIRTFTN takes 6 bytes - delegateCreateStart[0] == CEE_DUP && delegateCreateStart[1] == CEE_PREFIX1 && - delegateCreateStart[2] == (CEE_LDVIRTFTN & 0xFF)); -} /***************************************************************************** * * Returns true if the compiler instance is created for import only (verification). diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index cd6b4a9f1ffce..7c238103bfdef 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -28,36 +28,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX } \ } while (0) -#define VerifyOrReturn(cond, msg) \ - do \ - { \ - if (!(cond)) \ - { \ - verRaiseVerifyExceptionIfNeeded(INDEBUG(msg) DEBUGARG(__FILE__) DEBUGARG(__LINE__)); \ - return; \ - } \ - } while (0) - -#define VerifyOrReturnSpeculative(cond, msg, speculative) \ - do \ - { \ - if (speculative) \ - { \ - if (!(cond)) \ - { \ - return false; \ - } \ - } \ - else \ - { \ - if (!(cond)) \ - { \ - verRaiseVerifyExceptionIfNeeded(INDEBUG(msg) DEBUGARG(__FILE__) DEBUGARG(__LINE__)); \ - return false; \ - } \ - } \ - } while (0) - /*****************************************************************************/ void Compiler::impInit() @@ -6498,87 +6468,6 @@ GenTree* Compiler::impKeepAliveIntrinsic(GenTree* objToKeepAlive) return gtNewKeepAliveNode(objToKeepAlive); } -bool Compiler::verMergeEntryStates(BasicBlock* block, bool* changed) -{ - unsigned i; - - // do some basic checks first - if (block->bbStackDepthOnEntry() != verCurrentState.esStackDepth) - { - return false; - } - - if (verCurrentState.esStackDepth > 0) - { - // merge stack types - StackEntry* parentStack = block->bbStackOnEntry(); - StackEntry* childStack = verCurrentState.esStack; - - for (i = 0; i < verCurrentState.esStackDepth; i++, parentStack++, childStack++) - { - if (tiMergeToCommonParent(&parentStack->seTypeInfo, &childStack->seTypeInfo, changed) == false) - { - return false; - } - } - } - - // merge initialization status of this ptr - - if (verTrackObjCtorInitState) - { - // If we're tracking the CtorInitState, then it must not be unknown in the current state. - assert(verCurrentState.thisInitialized != TIS_Bottom); - - // If the successor block's thisInit state is unknown, copy it from the current state. - if (block->bbThisOnEntry() == TIS_Bottom) - { - *changed = true; - verSetThisInit(block, verCurrentState.thisInitialized); - } - else if (verCurrentState.thisInitialized != block->bbThisOnEntry()) - { - if (block->bbThisOnEntry() != TIS_Top) - { - *changed = true; - verSetThisInit(block, TIS_Top); - - if (block->bbFlags & BBF_FAILED_VERIFICATION) - { - // The block is bad. Control can flow through the block to any handler that catches the - // verification exception, but the importer ignores bad blocks and therefore won't model - // this flow in the normal way. To complete the merge into the bad block, the new state - // needs to be manually pushed to the handlers that may be reached after the verification - // exception occurs. - // - // Usually, the new state was already propagated to the relevant handlers while processing - // the predecessors of the bad block. The exception is when the bad block is at the start - // of a try region, meaning it is protected by additional handlers that do not protect its - // predecessors. - // - if (block->hasTryIndex() && ((block->bbFlags & BBF_TRY_BEG) != 0)) - { - // Push TIS_Top to the handlers that protect the bad block. Note that this can cause - // recursive calls back into this code path (if successors of the current bad block are - // also bad blocks). - // - ThisInitState origTIS = verCurrentState.thisInitialized; - verCurrentState.thisInitialized = TIS_Top; - impVerifyEHBlock(block, true); - verCurrentState.thisInitialized = origTIS; - } - } - } - } - } - else - { - assert(verCurrentState.thisInitialized == TIS_Bottom && block->bbThisOnEntry() == TIS_Bottom); - } - - return true; -} - /***************************************************************************** * 'logMsg' is true if a log message needs to be logged. false if the caller has * already logged it (presumably in a more detailed fashion than done here) @@ -6680,21 +6569,6 @@ typeInfo Compiler::verMakeTypeInfo(CorInfoType ciType, CORINFO_CLASS_HANDLE clsH } break; -#ifdef TARGET_64BIT - case CORINFO_TYPE_NATIVEINT: - case CORINFO_TYPE_NATIVEUINT: - if (clsHnd) - { - // If we have more precise information, use it - return verMakeTypeInfo(clsHnd); - } - else - { - return typeInfo::nativeInt(); - } - break; -#endif // TARGET_64BIT - case CORINFO_TYPE_VALUECLASS: case CORINFO_TYPE_REFANY: tiResult = verMakeTypeInfo(clsHnd); @@ -6735,9 +6609,9 @@ typeInfo Compiler::verMakeTypeInfo(CorInfoType ciType, CORINFO_CLASS_HANDLE clsH /******************************************************************************/ -typeInfo Compiler::verMakeTypeInfo(CORINFO_CLASS_HANDLE clsHnd, bool bashStructToRef /* = false */) +typeInfo Compiler::verMakeTypeInfo(CORINFO_CLASS_HANDLE clsHnd) { - if (clsHnd == nullptr) + if (clsHnd == NO_CLASS_HANDLE) { return typeInfo(); } @@ -6765,76 +6639,21 @@ typeInfo Compiler::verMakeTypeInfo(CORINFO_CLASS_HANDLE clsHnd, bool bashStructT return typeInfo(); } -#ifdef TARGET_64BIT - if (t == CORINFO_TYPE_NATIVEINT || t == CORINFO_TYPE_NATIVEUINT) - { - return typeInfo::nativeInt(); - } -#endif // TARGET_64BIT - if (t != CORINFO_TYPE_UNDEF) { return (typeInfo(JITtype2tiType(t))); } - else if (bashStructToRef) - { - return (typeInfo(TI_REF, clsHnd)); - } else { return (typeInfo(TI_STRUCT, clsHnd)); } } - else if (attribs & CORINFO_FLG_GENERIC_TYPE_VARIABLE) - { - // See comment in _typeInfo.h for why we do it this way. - return (typeInfo(TI_REF, clsHnd, true)); - } else { return (typeInfo(TI_REF, clsHnd)); } } -/******************************************************************************/ -bool Compiler::verIsSDArray(const typeInfo& ti) -{ - if (ti.IsNullObjRef()) - { // nulls are SD arrays - return true; - } - - if (!ti.IsType(TI_REF)) - { - return false; - } - - if (!info.compCompHnd->isSDArray(ti.GetClassHandleForObjRef())) - { - return false; - } - return true; -} - -/******************************************************************************/ -/* Given 'arrayObjectType' which is an array type, fetch the element type. */ -/* Returns an error type if anything goes wrong */ - -typeInfo Compiler::verGetArrayElemType(const typeInfo& arrayObjectType) -{ - assert(!arrayObjectType.IsNullObjRef()); // you need to check for null explicitly since that is a success case - - if (!verIsSDArray(arrayObjectType)) - { - return typeInfo(); - } - - CORINFO_CLASS_HANDLE childClassHandle = nullptr; - CorInfoType ciType = info.compCompHnd->getChildType(arrayObjectType.GetClassHandleForObjRef(), &childClassHandle); - - return verMakeTypeInfo(ciType, childClassHandle); -} - /***************************************************************************** */ typeInfo Compiler::verParseArgSigToTypeInfo(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args) @@ -6871,54 +6690,14 @@ bool Compiler::verIsByRefLike(const typeInfo& ti) return info.compCompHnd->getClassAttribs(ti.GetClassHandleForValueClass()) & CORINFO_FLG_BYREF_LIKE; } -bool Compiler::verIsSafeToReturnByRef(const typeInfo& ti) -{ - if (ti.IsPermanentHomeByRef()) - { - return true; - } - else - { - return false; - } -} - -bool Compiler::verIsBoxable(const typeInfo& ti) -{ - return (ti.IsPrimitiveType() || ti.IsObjRef() // includes boxed generic type variables - || ti.IsUnboxedGenericTypeVar() || - (ti.IsType(TI_STRUCT) && - // exclude byreflike structs - !(info.compCompHnd->getClassAttribs(ti.GetClassHandleForValueClass()) & CORINFO_FLG_BYREF_LIKE))); -} - -// Is it a boxed value type? -bool Compiler::verIsBoxedValueType(const typeInfo& ti) -{ - if (ti.GetType() == TI_REF) - { - CORINFO_CLASS_HANDLE clsHnd = ti.GetClassHandleForObjRef(); - return !!eeIsValueClass(clsHnd); - } - else - { - return false; - } -} - /***************************************************************************** * * Check if a TailCall is legal. */ -bool Compiler::verCheckTailCallConstraint( - OPCODE opcode, - CORINFO_RESOLVED_TOKEN* pResolvedToken, - CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, // Is this a "constrained." call on a type parameter? - bool speculative // If true, won't throw if verificatoin fails. Instead it will - // return false to the caller. - // If false, it will throw. - ) +bool Compiler::verCheckTailCallConstraint(OPCODE opcode, + CORINFO_RESOLVED_TOKEN* pResolvedToken, + CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken) { DWORD mflags; CORINFO_SIG_INFO sig; @@ -6937,7 +6716,7 @@ bool Compiler::verCheckTailCallConstraint( return false; } - // for calli, VerifyOrReturn that this is not a virtual method + // For calli, check that this is not a virtual method. if (opcode == CEE_CALLI) { /* Get the call sig */ @@ -6953,10 +6732,9 @@ bool Compiler::verCheckTailCallConstraint( mflags = info.compCompHnd->getMethodAttribs(methodHnd); - // When verifying generic code we pair the method handle with its - // owning class to get the exact method signature. + // In generic code we pair the method handle with its owning class to get the exact method signature. methodClassHnd = pResolvedToken->hClass; - assert(methodClassHnd); + assert(methodClassHnd != NO_CLASS_HANDLE); eeGetMethodSig(methodHnd, &sig, methodClassHnd); @@ -6965,42 +6743,46 @@ bool Compiler::verCheckTailCallConstraint( } // We must have got the methodClassHnd if opcode is not CEE_CALLI - assert((methodHnd != nullptr && methodClassHnd != nullptr) || opcode == CEE_CALLI); + assert((methodHnd != nullptr && methodClassHnd != NO_CLASS_HANDLE) || opcode == CEE_CALLI); if ((sig.callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_VARARG) { eeGetCallSiteSig(pResolvedToken->token, pResolvedToken->tokenScope, pResolvedToken->tokenContext, &sig); } - // check compatibility of the arguments - unsigned int argCount; - argCount = sig.numArgs; + // Check compatibility of the arguments. + unsigned int argCount = sig.numArgs; CORINFO_ARG_LIST_HANDLE args; args = sig.args; while (argCount--) { typeInfo tiDeclared = verParseArgSigToTypeInfo(&sig, args).NormaliseForStack(); - // check that the argument is not a byref for tailcalls - VerifyOrReturnSpeculative(!verIsByRefLike(tiDeclared), "tailcall on byrefs", speculative); + // Check that the argument is not a byref for tailcalls. + if (verIsByRefLike(tiDeclared)) + { + return false; + } // For unsafe code, we might have parameters containing pointer to the stack location. // Disallow the tailcall for this kind. CORINFO_CLASS_HANDLE classHandle; CorInfoType ciType = strip(info.compCompHnd->getArgType(&sig, args, &classHandle)); - VerifyOrReturnSpeculative(ciType != CORINFO_TYPE_PTR, "tailcall on CORINFO_TYPE_PTR", speculative); + if (ciType == CORINFO_TYPE_PTR) + { + return false; + } args = info.compCompHnd->getArgNext(args); } - // update popCount + // Update popCount. popCount += sig.numArgs; - // check for 'this' which is on non-static methods, not called via NEWOBJ + // Check for 'this' which is on non-static methods, not called via NEWOBJ if (!(mflags & CORINFO_FLG_STATIC)) { - // Always update the popCount. - // This is crucial for the stack calculation to be correct. + // Always update the popCount. This is crucial for the stack calculation to be correct. typeInfo tiThis = impStackTop(popCount).seTypeInfo; popCount++; @@ -7012,7 +6794,11 @@ bool Compiler::verCheckTailCallConstraint( { tiThis.MakeByRef(); } - VerifyOrReturnSpeculative(!verIsByRefLike(tiThis), "byref in tailcall", speculative); + + if (verIsByRefLike(tiThis)) + { + return false; + } } else { @@ -7023,13 +6809,19 @@ bool Compiler::verCheckTailCallConstraint( tiDeclaredThis.MakeByRef(); } - VerifyOrReturnSpeculative(!verIsByRefLike(tiDeclaredThis), "byref in tailcall", speculative); + if (verIsByRefLike(tiDeclaredThis)) + { + return false; + } } } // Tail calls on constrained calls should be illegal too: // when instantiated at a value type, a constrained call may pass the address of a stack allocated value - VerifyOrReturnSpeculative(!pConstrainedResolvedToken, "byref in constrained tailcall", speculative); + if (pConstrainedResolvedToken != nullptr) + { + return false; + } // Get the exact view of the signature for an array method if (sig.retType != CORINFO_TYPE_VOID) @@ -7041,560 +6833,30 @@ bool Compiler::verCheckTailCallConstraint( } } - typeInfo tiCalleeRetType = verMakeTypeInfo(sig.retType, sig.retTypeClass); - typeInfo tiCallerRetType = - verMakeTypeInfo(info.compMethodInfo->args.retType, info.compMethodInfo->args.retTypeClass); - - // void return type gets morphed into the error type, so we have to treat them specially here - if (sig.retType == CORINFO_TYPE_VOID) - { - VerifyOrReturnSpeculative(info.compMethodInfo->args.retType == CORINFO_TYPE_VOID, "tailcall return mismatch", - speculative); - } - else - { - VerifyOrReturnSpeculative(tiCompatibleWith(NormaliseForStack(tiCalleeRetType), - NormaliseForStack(tiCallerRetType), true), - "tailcall return mismatch", speculative); - } - - // for tailcall, stack must be empty - VerifyOrReturnSpeculative(verCurrentState.esStackDepth == popCount, "stack non-empty on tailcall", speculative); - - return true; // Yes, tailcall is legal -} - -/***************************************************************************** - * - * Checks the IL verification rules for the call - */ - -void Compiler::verVerifyCall(OPCODE opcode, - CORINFO_RESOLVED_TOKEN* pResolvedToken, - CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, - bool tailCall, - bool readonlyCall, - const BYTE* delegateCreateStart, - const BYTE* codeAddr, - CORINFO_CALL_INFO* callInfo DEBUGARG(const char* methodName)) -{ - DWORD mflags; - CORINFO_SIG_INFO* sig = nullptr; - unsigned int popCount = 0; // we can't pop the stack since impImportCall needs it, so - // this counter is used to keep track of how many items have been - // virtually popped - - // for calli, VerifyOrReturn that this is not a virtual method - if (opcode == CEE_CALLI) - { - Verify(false, "Calli not verifiable"); - return; - } - - // It would be nice to cache the rest of it, but eeFindMethod is the big ticket item. - mflags = callInfo->verMethodFlags; - - sig = &callInfo->verSig; - - if ((sig->callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_VARARG) - { - eeGetCallSiteSig(pResolvedToken->token, pResolvedToken->tokenScope, pResolvedToken->tokenContext, sig); - } - - // opcode specific check - unsigned methodClassFlgs = callInfo->classFlags; - switch (opcode) - { - case CEE_CALLVIRT: - // cannot do callvirt on valuetypes - VerifyOrReturn(!(methodClassFlgs & CORINFO_FLG_VALUECLASS), "callVirt on value class"); - VerifyOrReturn(sig->hasThis(), "CallVirt on static method"); - break; - - case CEE_NEWOBJ: - { - assert(!tailCall); // Importer should not allow this - VerifyOrReturn((mflags & CORINFO_FLG_CONSTRUCTOR) && !(mflags & CORINFO_FLG_STATIC), - "newobj must be on instance"); - - if (methodClassFlgs & CORINFO_FLG_DELEGATE) - { - VerifyOrReturn(sig->numArgs == 2, "wrong number args to delegate ctor"); - typeInfo tiDeclaredObj = verParseArgSigToTypeInfo(sig, sig->args).NormaliseForStack(); - typeInfo tiDeclaredFtn = - verParseArgSigToTypeInfo(sig, info.compCompHnd->getArgNext(sig->args)).NormaliseForStack(); - VerifyOrReturn(tiDeclaredFtn.IsNativeIntType(), "ftn arg needs to be a native int type"); - - assert(popCount == 0); - typeInfo tiActualObj = impStackTop(1).seTypeInfo; - typeInfo tiActualFtn = impStackTop(0).seTypeInfo; - - VerifyOrReturn(tiActualFtn.IsMethod(), "delegate needs method as first arg"); - VerifyOrReturn(tiCompatibleWith(tiActualObj, tiDeclaredObj, true), "delegate object type mismatch"); - VerifyOrReturn(tiActualObj.IsNullObjRef() || tiActualObj.IsType(TI_REF), - "delegate object type mismatch"); - - CORINFO_CLASS_HANDLE objTypeHandle = - tiActualObj.IsNullObjRef() ? nullptr : tiActualObj.GetClassHandleForObjRef(); - - // the method signature must be compatible with the delegate's invoke method - - // check that for virtual functions, the type of the object used to get the - // ftn ptr is the same as the type of the object passed to the delegate ctor. - // since this is a bit of work to determine in general, we pattern match stylized - // code sequences - - // the delegate creation code check, which used to be done later, is now done here - // so we can read delegateMethodRef directly from - // from the preceding LDFTN or CEE_LDVIRTFN instruction sequence; - // we then use it in our call to isCompatibleDelegate(). - - mdMemberRef delegateMethodRef = mdMemberRefNil; - VerifyOrReturn(verCheckDelegateCreation(delegateCreateStart, codeAddr, delegateMethodRef), - "must create delegates with certain IL"); - - CORINFO_RESOLVED_TOKEN delegateResolvedToken; - delegateResolvedToken.tokenContext = impTokenLookupContextHandle; - delegateResolvedToken.tokenScope = info.compScopeHnd; - delegateResolvedToken.token = delegateMethodRef; - delegateResolvedToken.tokenType = CORINFO_TOKENKIND_Method; - info.compCompHnd->resolveToken(&delegateResolvedToken); - - CORINFO_CALL_INFO delegateCallInfo; - eeGetCallInfo(&delegateResolvedToken, nullptr /* constraint typeRef */, CORINFO_CALLINFO_SECURITYCHECKS, - &delegateCallInfo); - - bool isOpenDelegate = false; - VerifyOrReturn(info.compCompHnd->isCompatibleDelegate(objTypeHandle, delegateResolvedToken.hClass, - tiActualFtn.GetMethod(), pResolvedToken->hClass, - &isOpenDelegate), - "function incompatible with delegate"); - - // check the constraints on the target method - VerifyOrReturn(info.compCompHnd->satisfiesClassConstraints(delegateResolvedToken.hClass), - "delegate target has unsatisfied class constraints"); - VerifyOrReturn(info.compCompHnd->satisfiesMethodConstraints(delegateResolvedToken.hClass, - tiActualFtn.GetMethod()), - "delegate target has unsatisfied method constraints"); - - // See ECMA spec section 1.8.1.5.2 (Delegating via instance dispatch) - // for additional verification rules for delegates - CORINFO_METHOD_HANDLE actualMethodHandle = tiActualFtn.GetMethod(); - DWORD actualMethodAttribs = info.compCompHnd->getMethodAttribs(actualMethodHandle); - if (impIsLDFTN_TOKEN(delegateCreateStart, codeAddr)) - { - - if ((actualMethodAttribs & CORINFO_FLG_VIRTUAL) && ((actualMethodAttribs & CORINFO_FLG_FINAL) == 0)) - { - VerifyOrReturn((tiActualObj.IsThisPtr() && lvaIsOriginalThisReadOnly()) || - verIsBoxedValueType(tiActualObj), - "The 'this' parameter to the call must be either the calling method's " - "'this' parameter or " - "a boxed value type."); - } - } - - if (actualMethodAttribs & CORINFO_FLG_PROTECTED) - { - bool targetIsStatic = actualMethodAttribs & CORINFO_FLG_STATIC; - - Verify(targetIsStatic || !isOpenDelegate, - "Unverifiable creation of an open instance delegate for a protected member."); - - CORINFO_CLASS_HANDLE instanceClassHnd = (tiActualObj.IsNullObjRef() || targetIsStatic) - ? info.compClassHnd - : tiActualObj.GetClassHandleForObjRef(); - - // In the case of protected methods, it is a requirement that the 'this' - // pointer be a subclass of the current context. Perform this check. - Verify(info.compCompHnd->canAccessFamily(info.compMethodHnd, instanceClassHnd), - "Accessing protected method through wrong type."); - } - goto DONE_ARGS; - } - } - // fall thru to default checks - FALLTHROUGH; - default: - VerifyOrReturn(!(mflags & CORINFO_FLG_ABSTRACT), "method abstract"); - } - VerifyOrReturn(!((mflags & CORINFO_FLG_CONSTRUCTOR) && (methodClassFlgs & CORINFO_FLG_DELEGATE)), - "can only newobj a delegate constructor"); - - // check compatibility of the arguments - unsigned int argCount; - argCount = sig->numArgs; - CORINFO_ARG_LIST_HANDLE args; - args = sig->args; - while (argCount--) - { - typeInfo tiActual = impStackTop(popCount + argCount).seTypeInfo; - - typeInfo tiDeclared = verParseArgSigToTypeInfo(sig, args).NormaliseForStack(); - VerifyOrReturn(tiCompatibleWith(tiActual, tiDeclared, true), "type mismatch"); - - args = info.compCompHnd->getArgNext(args); - } - -DONE_ARGS: - - // update popCount - popCount += sig->numArgs; - - // check for 'this' which are is non-static methods, not called via NEWOBJ - CORINFO_CLASS_HANDLE instanceClassHnd = info.compClassHnd; - if (!(mflags & CORINFO_FLG_STATIC) && (opcode != CEE_NEWOBJ)) - { - typeInfo tiThis = impStackTop(popCount).seTypeInfo; - popCount++; - - // If it is null, we assume we can access it (since it will AV shortly) - // If it is anything but a reference class, there is no hierarchy, so - // again, we don't need the precise instance class to compute 'protected' access - if (tiThis.IsType(TI_REF)) - { - instanceClassHnd = tiThis.GetClassHandleForObjRef(); - } - - // Check type compatibility of the this argument - typeInfo tiDeclaredThis = verMakeTypeInfo(pResolvedToken->hClass); - if (tiDeclaredThis.IsValueClass()) - { - tiDeclaredThis.MakeByRef(); - } - - // If this is a call to the base class .ctor, set thisPtr Init for - // this block. - if (mflags & CORINFO_FLG_CONSTRUCTOR) - { - if (verTrackObjCtorInitState && tiThis.IsThisPtr() && - verIsCallToInitThisPtr(info.compClassHnd, pResolvedToken->hClass)) - { - assert(verCurrentState.thisInitialized != - TIS_Bottom); // This should never be the case just from the logic of the verifier. - VerifyOrReturn(verCurrentState.thisInitialized == TIS_Uninit, - "Call to base class constructor when 'this' is possibly initialized"); - // Otherwise, 'this' is now initialized. - verCurrentState.thisInitialized = TIS_Init; - tiThis.SetInitialisedObjRef(); - } - else - { - // We allow direct calls to value type constructors - // NB: we have to check that the contents of tiThis is a value type, otherwise we could use a - // constrained callvirt to illegally re-enter a .ctor on a value of reference type. - VerifyOrReturn(tiThis.IsByRef() && DereferenceByRef(tiThis).IsValueClass(), - "Bad call to a constructor"); - } - } - - if (pConstrainedResolvedToken != nullptr) - { - VerifyOrReturn(tiThis.IsByRef(), "non-byref this type in constrained call"); - - typeInfo tiConstraint = verMakeTypeInfo(pConstrainedResolvedToken->hClass); - - // We just dereference this and test for equality - tiThis.DereferenceByRef(); - VerifyOrReturn(typeInfo::AreEquivalent(tiThis, tiConstraint), - "this type mismatch with constrained type operand"); - - // Now pretend the this type is the boxed constrained type, for the sake of subsequent checks - tiThis = typeInfo(TI_REF, pConstrainedResolvedToken->hClass); - } - - // To support direct calls on readonly byrefs, just pretend tiDeclaredThis is readonly too - if (tiDeclaredThis.IsByRef() && tiThis.IsReadonlyByRef()) - { - tiDeclaredThis.SetIsReadonlyByRef(); - } - - VerifyOrReturn(tiCompatibleWith(tiThis, tiDeclaredThis, true), "this type mismatch"); - - if (tiThis.IsByRef()) - { - // Find the actual type where the method exists (as opposed to what is declared - // in the metadata). This is to prevent passing a byref as the "this" argument - // while calling methods like System.ValueType.GetHashCode() which expect boxed objects. - - CORINFO_CLASS_HANDLE actualClassHnd = info.compCompHnd->getMethodClass(pResolvedToken->hMethod); - VerifyOrReturn(eeIsValueClass(actualClassHnd), - "Call to base type of valuetype (which is never a valuetype)"); - } - - // Rules for non-virtual call to a non-final virtual method: - - // Define: - // The "this" pointer is considered to be "possibly written" if - // 1. Its address have been taken (LDARGA 0) anywhere in the method. - // (or) - // 2. It has been stored to (STARG.0) anywhere in the method. - - // A non-virtual call to a non-final virtual method is only allowed if - // 1. The this pointer passed to the callee is an instance of a boxed value type. - // (or) - // 2. The this pointer passed to the callee is the current method's this pointer. - // (and) The current method's this pointer is not "possibly written". - - // Thus the rule is that if you assign to this ANYWHERE you can't make "base" calls to - // virtual methods. (Luckily this does affect .ctors, since they are not virtual). - // This is stronger that is strictly needed, but implementing a laxer rule is significantly - // hard and more error prone. - - if (opcode == CEE_CALL && (mflags & CORINFO_FLG_VIRTUAL) && ((mflags & CORINFO_FLG_FINAL) == 0)) - { - VerifyOrReturn((tiThis.IsThisPtr() && lvaIsOriginalThisReadOnly()) || verIsBoxedValueType(tiThis), - "The 'this' parameter to the call must be either the calling method's 'this' parameter or " - "a boxed value type."); - } - } - - // check any constraints on the callee's class and type parameters - VerifyOrReturn(info.compCompHnd->satisfiesClassConstraints(pResolvedToken->hClass), - "method has unsatisfied class constraints"); - VerifyOrReturn(info.compCompHnd->satisfiesMethodConstraints(pResolvedToken->hClass, pResolvedToken->hMethod), - "method has unsatisfied method constraints"); - - if (mflags & CORINFO_FLG_PROTECTED) - { - VerifyOrReturn(info.compCompHnd->canAccessFamily(info.compMethodHnd, instanceClassHnd), - "Can't access protected method"); - } - - // Get the exact view of the signature for an array method - if (sig->retType != CORINFO_TYPE_VOID) - { - eeGetMethodSig(pResolvedToken->hMethod, sig, pResolvedToken->hClass); - } - - // "readonly." prefixed calls only allowed for the Address operation on arrays. - // The methods supported by array types are under the control of the EE - // so we can trust that only the Address operation returns a byref. - if (readonlyCall) - { - typeInfo tiCalleeRetType = verMakeTypeInfo(sig->retType, sig->retTypeClass); - VerifyOrReturn((methodClassFlgs & CORINFO_FLG_ARRAY) && tiCalleeRetType.IsByRef(), - "unexpected use of readonly prefix"); - } - - // Verify the tailcall - if (tailCall) - { - verCheckTailCallConstraint(opcode, pResolvedToken, pConstrainedResolvedToken, false); - } -} - -/***************************************************************************** - * Checks that a delegate creation is done using the following pattern: - * dup - * ldvirtftn targetMemberRef - * OR - * ldftn targetMemberRef - * - * 'delegateCreateStart' points at the last dup or ldftn in this basic block (null if - * not in this basic block) - * - * targetMemberRef is read from the code sequence. - * targetMemberRef is validated iff verificationNeeded. - */ - -bool Compiler::verCheckDelegateCreation(const BYTE* delegateCreateStart, - const BYTE* codeAddr, - mdMemberRef& targetMemberRef) -{ - if (impIsLDFTN_TOKEN(delegateCreateStart, codeAddr)) - { - targetMemberRef = getU4LittleEndian(&delegateCreateStart[2]); - return true; - } - else if (impIsDUP_LDVIRTFTN_TOKEN(delegateCreateStart, codeAddr)) - { - targetMemberRef = getU4LittleEndian(&delegateCreateStart[3]); - return true; - } - - return false; -} - -typeInfo Compiler::verVerifySTIND(const typeInfo& tiTo, const typeInfo& value, const typeInfo& instrType) -{ - Verify(!tiTo.IsReadonlyByRef(), "write to readonly byref"); - typeInfo ptrVal = verVerifyLDIND(tiTo, instrType); - typeInfo normPtrVal = typeInfo(ptrVal).NormaliseForStack(); - if (!tiCompatibleWith(value, normPtrVal, true)) - { - Verify(tiCompatibleWith(value, normPtrVal, true), "type mismatch"); - } - return ptrVal; -} - -typeInfo Compiler::verVerifyLDIND(const typeInfo& ptr, const typeInfo& instrType) -{ - assert(!instrType.IsStruct()); - - typeInfo ptrVal; - if (ptr.IsByRef()) - { - ptrVal = DereferenceByRef(ptr); - if (instrType.IsObjRef() && !ptrVal.IsObjRef()) - { - Verify(false, "bad pointer"); - } - else if (!instrType.IsObjRef() && !typeInfo::AreEquivalent(instrType, ptrVal)) - { - Verify(false, "pointer not consistent with instr"); - } - } - else - { - Verify(false, "pointer not byref"); - } - - return ptrVal; -} - -// Verify that the field is used properly. 'tiThis' is NULL for statics, -// 'fieldFlags' is the fields attributes, and mutator is true if it is a -// ld*flda or a st*fld. -// 'enclosingClass' is given if we are accessing a field in some specific type. + var_types calleeRetType = genActualType(JITtype2varType(sig.retType)); + var_types callerRetType = genActualType(JITtype2varType(info.compMethodInfo->args.retType)); -void Compiler::verVerifyField(CORINFO_RESOLVED_TOKEN* pResolvedToken, - const CORINFO_FIELD_INFO& fieldInfo, - const typeInfo* tiThis, - bool mutator, - bool allowPlainStructAsThis) -{ - CORINFO_CLASS_HANDLE enclosingClass = pResolvedToken->hClass; - unsigned fieldFlags = fieldInfo.fieldFlags; - CORINFO_CLASS_HANDLE instanceClass = - info.compClassHnd; // for statics, we imagine the instance is the current class. + // Normalize TYP_FLOAT to TYP_DOUBLE (it is ok to return one as the other and vice versa). + calleeRetType = (calleeRetType == TYP_FLOAT) ? TYP_DOUBLE : calleeRetType; + callerRetType = (callerRetType == TYP_FLOAT) ? TYP_DOUBLE : callerRetType; - bool isStaticField = ((fieldFlags & CORINFO_FLG_FIELD_STATIC) != 0); - if (mutator) + // Make sure the types match. + if (calleeRetType != callerRetType) { - Verify(!(fieldFlags & CORINFO_FLG_FIELD_UNMANAGED), "mutating an RVA bases static"); - if ((fieldFlags & CORINFO_FLG_FIELD_FINAL)) - { - Verify((info.compFlags & CORINFO_FLG_CONSTRUCTOR) && enclosingClass == info.compClassHnd && - info.compIsStatic == isStaticField, - "bad use of initonly field (set or address taken)"); - } - } - - if (tiThis == nullptr) - { - Verify(isStaticField, "used static opcode with non-static field"); - } - else - { - typeInfo tThis = *tiThis; - - if (allowPlainStructAsThis && tThis.IsValueClass()) - { - tThis.MakeByRef(); - } - - // If it is null, we assume we can access it (since it will AV shortly) - // If it is anything but a reference class, there is no hierarchy, so - // again, we don't need the precise instance class to compute 'protected' access - if (tiThis->IsType(TI_REF)) - { - instanceClass = tiThis->GetClassHandleForObjRef(); - } - - // Note that even if the field is static, we require that the this pointer - // satisfy the same constraints as a non-static field This happens to - // be simpler and seems reasonable - typeInfo tiDeclaredThis = verMakeTypeInfo(enclosingClass); - if (tiDeclaredThis.IsValueClass()) - { - tiDeclaredThis.MakeByRef(); - - // we allow read-only tThis, on any field access (even stores!), because if the - // class implementor wants to prohibit stores he should make the field private. - // we do this by setting the read-only bit on the type we compare tThis to. - tiDeclaredThis.SetIsReadonlyByRef(); - } - else if (verTrackObjCtorInitState && tThis.IsThisPtr()) - { - // Any field access is legal on "uninitialized" this pointers. - // The easiest way to implement this is to simply set the - // initialized bit for the duration of the type check on the - // field access only. It does not change the state of the "this" - // for the function as a whole. Note that the "tThis" is a copy - // of the original "this" type (*tiThis) passed in. - tThis.SetInitialisedObjRef(); - } - - Verify(tiCompatibleWith(tThis, tiDeclaredThis, true), "this type mismatch"); - } - - // Presently the JIT does not check that we don't store or take the address of init-only fields - // since we cannot guarantee their immutability and it is not a security issue. - - // check any constraints on the fields's class --- accessing the field might cause a class constructor to run. - VerifyOrReturn(info.compCompHnd->satisfiesClassConstraints(enclosingClass), - "field has unsatisfied class constraints"); - if (fieldFlags & CORINFO_FLG_FIELD_PROTECTED) - { - Verify(info.compCompHnd->canAccessFamily(info.compMethodHnd, instanceClass), - "Accessing protected method through wrong type."); - } -} - -void Compiler::verVerifyCond(const typeInfo& tiOp1, const typeInfo& tiOp2, unsigned opcode) -{ - if (tiOp1.IsNumberType()) - { -#ifdef TARGET_64BIT - Verify(tiCompatibleWith(tiOp1, tiOp2, true), "Cond type mismatch"); -#else // TARGET_64BIT - // [10/17/2013] Consider changing this: to put on my verification lawyer hat, - // this is non-conforming to the ECMA Spec: types don't have to be equivalent, - // but compatible, since we can coalesce native int with int32 (see section III.1.5). - Verify(typeInfo::AreEquivalent(tiOp1, tiOp2), "Cond type mismatch"); -#endif // !TARGET_64BIT - } - else if (tiOp1.IsObjRef()) - { - switch (opcode) - { - case CEE_BEQ_S: - case CEE_BEQ: - case CEE_BNE_UN_S: - case CEE_BNE_UN: - case CEE_CEQ: - case CEE_CGT_UN: - break; - default: - Verify(false, "Cond not allowed on object types"); - } - Verify(tiOp2.IsObjRef(), "Cond type mismatch"); - } - else if (tiOp1.IsByRef()) - { - Verify(tiOp2.IsByRef(), "Cond type mismatch"); + return false; } - else + else if ((callerRetType == TYP_STRUCT) && (sig.retTypeClass != info.compMethodInfo->args.retTypeClass)) { - Verify(tiOp1.IsMethod() && tiOp2.IsMethod(), "Cond type mismatch"); + return false; } -} -void Compiler::verVerifyThisPtrInitialised() -{ - if (verTrackObjCtorInitState) + // For tailcall, stack must be empty. + if (verCurrentState.esStackDepth != popCount) { - Verify(verCurrentState.thisInitialized == TIS_Init, "this ptr is not initialized"); + return false; } -} - -bool Compiler::verIsCallToInitThisPtr(CORINFO_CLASS_HANDLE context, CORINFO_CLASS_HANDLE target) -{ - // Either target == context, in this case calling an alternate .ctor - // Or target is the immediate parent of context - return ((target == context) || (target == info.compCompHnd->getParentType(context))); + return true; // Yes, tailcall is legal } GenTree* Compiler::impImportLdvirtftn(GenTree* thisPtr, @@ -10762,13 +10024,6 @@ var_types Compiler::impImportCall(OPCODE opcode, typeInfo tiRetVal = verMakeTypeInfo(sig->retType, sig->retTypeClass); tiRetVal.NormaliseForStack(); - // The CEE_READONLY prefix modifies the verification semantics of an Address - // operation on an array type. - if ((clsFlags & CORINFO_FLG_ARRAY) && isReadonlyCall && tiRetVal.IsByRef()) - { - tiRetVal.SetIsReadonlyByRef(); - } - if (call->IsCall()) { // Sometimes "call" is not a GT_CALL (if we imported an intrinsic that didn't turn into a call) @@ -15715,11 +14970,9 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (newBBcreatedForTailcallStress && !hasTailPrefix) { // Do a more detailed evaluation of legality - const bool returnFalseIfInvalid = true; const bool passedConstraintCheck = verCheckTailCallConstraint(opcode, &resolvedToken, - constraintCall ? &constrainedResolvedToken : nullptr, - returnFalseIfInvalid); + constraintCall ? &constrainedResolvedToken : nullptr); // Avoid setting compHasBackwardsJump = true via tail call stress if the method cannot have // patchpoints. @@ -15960,7 +15213,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { // If the object is a struct, what we really want is // for the field to operate on the address of the struct. - if (!varTypeGCtype(obj->TypeGet()) && impIsValueType(tiObj)) + if (varTypeIsStruct(obj)) { assert(opcode == CEE_LDFLD && objType != nullptr); @@ -17584,12 +16837,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) void Compiler::impPushVar(GenTree* op, typeInfo tiRetVal) { tiRetVal.NormaliseForStack(); - - if (verTrackObjCtorInitState && (verCurrentState.thisInitialized != TIS_Init) && tiRetVal.IsThisPtr()) - { - tiRetVal.SetUninitialisedObjRef(); - } - impPushOnStack(op, tiRetVal); } @@ -18195,28 +17442,6 @@ void Compiler::impVerifyEHBlock(BasicBlock* block, bool isTryStart) while (HBtab != nullptr) { - if (isTryStart) - { - // Are we verifying that an instance constructor properly initializes it's 'this' pointer once? - // We do not allow the 'this' pointer to be uninitialized when entering most kinds try regions - // - if (verTrackObjCtorInitState && (verCurrentState.thisInitialized != TIS_Init)) - { - // We trigger an invalid program exception here unless we have a try/fault region. - // - if (HBtab->HasCatchHandler() || HBtab->HasFinallyHandler() || HBtab->HasFilter()) - { - BADCODE( - "The 'this' pointer of an instance constructor is not initialized upon entry to a try region"); - } - else - { - // Allow a try/fault region to proceed. - assert(HBtab->HasFaultHandler()); - } - } - } - // Recursively process the handler block, if we haven't already done so. BasicBlock* hndBegBB = HBtab->ebdHndBeg; @@ -18283,18 +17508,6 @@ void Compiler::impVerifyEHBlock(BasicBlock* block, bool isTryStart) } } - // This seems redundant ....?? - if (verTrackObjCtorInitState && HBtab->HasFaultHandler()) - { - /* Recursively process the handler block */ - - verCurrentState.esStackDepth = 0; - - // Queue up the fault handler for importing - // - impImportBlockPending(HBtab->ebdHndBeg); - } - // Now process our enclosing try index (if any) // tryIndex = HBtab->ebdEnclosingTryIndex; @@ -18813,7 +18026,6 @@ void Compiler::impImportBlockPending(BasicBlock* block) dsc->pdBB = block; dsc->pdSavedStack.ssDepth = verCurrentState.esStackDepth; - dsc->pdThisPtrInit = verCurrentState.thisInitialized; // Save the stack trees for later @@ -18878,13 +18090,11 @@ void Compiler::impReimportBlockPending(BasicBlock* block) if (block->bbEntryState) { - dsc->pdThisPtrInit = block->bbEntryState->thisInitialized; dsc->pdSavedStack.ssDepth = block->bbEntryState->esStackDepth; dsc->pdSavedStack.ssTrees = block->bbEntryState->esStack; } else { - dsc->pdThisPtrInit = TIS_Bottom; dsc->pdSavedStack.ssDepth = 0; dsc->pdSavedStack.ssTrees = nullptr; } @@ -19037,7 +18247,6 @@ void Compiler::ReimportSpillClique::Visit(SpillCliqueDir predOrSucc, BasicBlock* // Set the current stack state to that of the blk->bbEntryState m_pComp->verResetCurrentState(blk, &m_pComp->verCurrentState); - assert(m_pComp->verCurrentState.thisInitialized == blk->bbThisOnEntry()); m_pComp->impImportBlockPending(blk); } @@ -19127,7 +18336,7 @@ void Compiler::impReimportSpillClique(BasicBlock* block) // a copy of "srcState", cloning tree pointers as required. void Compiler::verInitBBEntryState(BasicBlock* block, EntryState* srcState) { - if (srcState->esStackDepth == 0 && srcState->thisInitialized == TIS_Bottom) + if (srcState->esStackDepth == 0) { block->bbEntryState = nullptr; return; @@ -19137,8 +18346,7 @@ void Compiler::verInitBBEntryState(BasicBlock* block, EntryState* srcState) // block->bbEntryState.esRefcount = 1; - block->bbEntryState->esStackDepth = srcState->esStackDepth; - block->bbEntryState->thisInitialized = TIS_Bottom; + block->bbEntryState->esStackDepth = srcState->esStackDepth; if (srcState->esStackDepth > 0) { @@ -19152,24 +18360,6 @@ void Compiler::verInitBBEntryState(BasicBlock* block, EntryState* srcState) block->bbEntryState->esStack[level].val = gtCloneExpr(tree); } } - - if (verTrackObjCtorInitState) - { - verSetThisInit(block, srcState->thisInitialized); - } - - return; -} - -void Compiler::verSetThisInit(BasicBlock* block, ThisInitState tis) -{ - assert(tis != TIS_Bottom); // Precondition. - if (block->bbEntryState == nullptr) - { - block->bbEntryState = new (this, CMK_Unknown) EntryState(); - } - - block->bbEntryState->thisInitialized = tis; } /* @@ -19177,11 +18367,9 @@ void Compiler::verSetThisInit(BasicBlock* block, ThisInitState tis) */ void Compiler::verResetCurrentState(BasicBlock* block, EntryState* destState) { - if (block->bbEntryState == nullptr) { - destState->esStackDepth = 0; - destState->thisInitialized = TIS_Bottom; + destState->esStackDepth = 0; return; } @@ -19193,15 +18381,6 @@ void Compiler::verResetCurrentState(BasicBlock* block, EntryState* destState) memcpy(destState->esStack, block->bbStackOnEntry(), stackSize); } - - destState->thisInitialized = block->bbThisOnEntry(); - - return; -} - -ThisInitState BasicBlock::bbThisOnEntry() const -{ - return bbEntryState ? bbEntryState->thisInitialized : TIS_Bottom; } unsigned BasicBlock::bbStackDepthOnEntry() const @@ -19224,11 +18403,7 @@ StackEntry* BasicBlock::bbStackOnEntry() const void Compiler::verInitCurrentState() { - verTrackObjCtorInitState = false; - verCurrentState.thisInitialized = TIS_Bottom; - // initialize stack info - verCurrentState.esStackDepth = 0; assert(verCurrentState.esStack != nullptr); @@ -19391,8 +18566,7 @@ void Compiler::impImport() /* Restore the stack state */ - verCurrentState.thisInitialized = dsc->pdThisPtrInit; - verCurrentState.esStackDepth = dsc->pdSavedStack.ssDepth; + verCurrentState.esStackDepth = dsc->pdSavedStack.ssDepth; if (verCurrentState.esStackDepth) { impRestoreStackState(&dsc->pdSavedStack); @@ -19441,21 +18615,6 @@ void Compiler::impImport() #endif } -// Checks if a typeinfo (usually stored in the type stack) is a struct. -// The invariant here is that if it's not a ref or a method and has a class handle -// it's a valuetype -bool Compiler::impIsValueType(typeInfo* pTypeInfo) -{ - if (pTypeInfo && pTypeInfo->IsValueClassWithClsHnd()) - { - return true; - } - else - { - return false; - } -} - //------------------------------------------------------------------------ // impIsInvariant: check if a tree (created during import) is invariant. // diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index e15d3ade77eca..8fcc451aa627c 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -435,8 +435,7 @@ class GlobalJitOptions #define NODEBASH_STATS 0 // Collect stats on changed gtOper values in GenTree's. #define COUNT_AST_OPERS 0 // Display use counts for GenTree operators. -#define VERBOSE_SIZES 0 // Always display GC info sizes. If set, DISPLAY_SIZES must also be set. -#define VERBOSE_VERIFY 0 // Dump additional information when verifying code. Useful to debug verification bugs. +#define VERBOSE_SIZES 0 // Always display GC info sizes. If set, DISPLAY_SIZES must also be set. #ifdef DEBUG #define MEASURE_MEM_ALLOC 1 // Collect memory allocation stats. diff --git a/src/coreclr/jit/typeinfo.cpp b/src/coreclr/jit/typeinfo.cpp deleted file mode 100644 index 6bc69965d097b..0000000000000 --- a/src/coreclr/jit/typeinfo.cpp +++ /dev/null @@ -1,345 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XX XX -XX typeInfo XX -XX XX -XX XX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -*/ - -#include "jitpch.h" -#ifdef _MSC_VER -#pragma hdrstop -#endif - -#include "_typeinfo.h" - -bool Compiler::tiCompatibleWith(const typeInfo& child, const typeInfo& parent, bool normalisedForStack) const -{ - return typeInfo::tiCompatibleWith(info.compCompHnd, child, parent, normalisedForStack); -} - -bool Compiler::tiMergeCompatibleWith(const typeInfo& child, const typeInfo& parent, bool normalisedForStack) const -{ - return typeInfo::tiMergeCompatibleWith(info.compCompHnd, child, parent, normalisedForStack); -} - -bool Compiler::tiMergeToCommonParent(typeInfo* pDest, const typeInfo* pSrc, bool* changed) const -{ - return typeInfo::tiMergeToCommonParent(info.compCompHnd, pDest, pSrc, changed); -} - -static bool tiCompatibleWithByRef(COMP_HANDLE CompHnd, const typeInfo& child, const typeInfo& parent) -{ - assert(parent.IsByRef()); - - if (!child.IsByRef()) - { - return false; - } - - if (child.IsReadonlyByRef() && !parent.IsReadonlyByRef()) - { - return false; - } - - // Byrefs are compatible if the underlying types are equivalent - typeInfo childTarget = ::DereferenceByRef(child); - typeInfo parentTarget = ::DereferenceByRef(parent); - - if (typeInfo::AreEquivalent(childTarget, parentTarget)) - { - return true; - } - - // Make sure that both types have a valid m_cls - if ((childTarget.IsType(TI_REF) || childTarget.IsType(TI_STRUCT)) && - (parentTarget.IsType(TI_REF) || parentTarget.IsType(TI_STRUCT))) - { - return CompHnd->areTypesEquivalent(childTarget.GetClassHandle(), parentTarget.GetClassHandle()); - } - - return false; -} - -/***************************************************************************** - * Verify child is compatible with the template parent. Basically, that - * child is a "subclass" of parent -it can be substituted for parent - * anywhere. Note that if parent contains fancy flags, such as "uninitialized" - * , "is this ptr", or "has byref local/field" info, then child must also - * contain those flags, otherwise FALSE will be returned ! - * - * Rules for determining compatibility: - * - * If parent is a primitive type or value class, then child must be the - * same primitive type or value class. The exception is that the built in - * value classes System/Boolean etc. are treated as synonyms for - * TI_BYTE etc. - * - * If parent is a byref of a primitive type or value class, then child - * must be a byref of the same (rules same as above case). - * - * Byrefs are compatible only with byrefs. - * - * If parent is an object, child must be a subclass of it, implement it - * (if it is an interface), or be null. - * - * If parent is an array, child must be the same or subclassed array. - * - * If parent is a null objref, only null is compatible with it. - * - * If the "uninitialized", "by ref local/field", "this pointer" or other flags - * are different, the items are incompatible. - * - * parent CANNOT be an undefined (dead) item. - * - */ - -bool typeInfo::tiCompatibleWith(COMP_HANDLE CompHnd, - const typeInfo& child, - const typeInfo& parent, - bool normalisedForStack) -{ - assert(child.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(child), child)); - assert(parent.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(parent), parent)); - - if (typeInfo::AreEquivalent(child, parent)) - { - return true; - } - - if (parent.IsUnboxedGenericTypeVar() || child.IsUnboxedGenericTypeVar()) - { - return false; // need to have had child == parent - } - else if (parent.IsType(TI_REF)) - { - // An uninitialized objRef is not compatible to initialized. - if (child.IsUninitialisedObjRef() && !parent.IsUninitialisedObjRef()) - { - return false; - } - - if (child.IsNullObjRef()) - { // NULL can be any reference type - return true; - } - if (!child.IsType(TI_REF)) - { - return false; - } - - return CompHnd->canCast(child.m_cls, parent.m_cls); - } - else if (parent.IsType(TI_METHOD)) - { - if (!child.IsType(TI_METHOD)) - { - return false; - } - - // Right now we don't bother merging method handles - return false; - } - else if (parent.IsType(TI_STRUCT)) - { - if (!child.IsType(TI_STRUCT)) - { - return false; - } - - // Structures are compatible if they are equivalent - return CompHnd->areTypesEquivalent(child.m_cls, parent.m_cls); - } - else if (parent.IsByRef()) - { - return tiCompatibleWithByRef(CompHnd, child, parent); - } -#ifdef TARGET_64BIT - // On 64-bit targets we have precise representation for native int, so these rules - // represent the fact that the ECMA spec permits the implicit conversion - // between an int32 and a native int. - else if (parent.IsType(TI_INT) && typeInfo::AreEquivalent(nativeInt(), child)) - { - return true; - } - else if (typeInfo::AreEquivalent(nativeInt(), parent) && child.IsType(TI_INT)) - { - return true; - } -#endif // TARGET_64BIT - return false; -} - -bool typeInfo::tiMergeCompatibleWith(COMP_HANDLE CompHnd, - const typeInfo& child, - const typeInfo& parent, - bool normalisedForStack) -{ - if (!child.IsPermanentHomeByRef() && parent.IsPermanentHomeByRef()) - { - return false; - } - - return typeInfo::tiCompatibleWith(CompHnd, child, parent, normalisedForStack); -} - -/***************************************************************************** - * Merge pDest and pSrc to find some commonality (e.g. a common parent). - * Copy the result to pDest, marking it dead if no commonality can be found. - * - * null ^ null -> null - * Object ^ null -> Object - * [I4 ^ null -> [I4 - * InputStream ^ OutputStream -> Stream - * InputStream ^ NULL -> InputStream - * [I4 ^ Object -> Object - * [I4 ^ [Object -> Array - * [I4 ^ [R8 -> Array - * [Foo ^ I4 -> DEAD - * [Foo ^ [I1 -> Array - * [InputStream ^ [OutputStream -> Array - * DEAD ^ X -> DEAD - * [Intfc ^ [OutputStream -> Array - * Intf ^ [OutputStream -> Object - * [[InStream ^ [[OutStream -> Array - * [[InStream ^ [OutStream -> Array - * [[Foo ^ [Object -> Array - * - * Importantly: - * [I1 ^ [U1 -> either [I1 or [U1 - * etc. - * - * Also, System/Int32 and I4 merge -> I4, etc. - * - * Returns FALSE if the merge was completely incompatible (i.e. the item became - * dead). - * - */ - -bool typeInfo::tiMergeToCommonParent(COMP_HANDLE CompHnd, typeInfo* pDest, const typeInfo* pSrc, bool* changed) -{ - assert(pSrc->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pSrc), *pSrc)); - assert(pDest->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pDest), *pDest)); - - // Merge the auxiliary information like "this" pointer tracking, etc... - - // Remember the pre-state, so we can tell if it changed. - *changed = false; - DWORD destFlagsBefore = pDest->m_flags; - - // This bit is only set if both pDest and pSrc have it set - pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_THIS_PTR); - - // This bit is set if either pDest or pSrc have it set - pDest->m_flags |= (pSrc->m_flags & TI_FLAG_UNINIT_OBJREF); - - // This bit is set if either pDest or pSrc have it set - pDest->m_flags |= (pSrc->m_flags & TI_FLAG_BYREF_READONLY); - - // If the byref wasn't permanent home in both sides, then merge won't have the bit set - pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_BYREF_PERMANENT_HOME); - - if (pDest->m_flags != destFlagsBefore) - { - *changed = true; - } - - // OK the main event. Merge the main types - if (typeInfo::AreEquivalent(*pDest, *pSrc)) - { - return true; - } - - if (pDest->IsUnboxedGenericTypeVar() || pSrc->IsUnboxedGenericTypeVar()) - { - // Should have had *pDest == *pSrc - goto FAIL; - } - if (pDest->IsType(TI_REF)) - { - if (pSrc->IsType(TI_NULL)) - { // NULL can be any reference type - return true; - } - if (!pSrc->IsType(TI_REF)) - { - goto FAIL; - } - - // Ask the EE to find the common parent, This always succeeds since System.Object always works - CORINFO_CLASS_HANDLE pDestClsBefore = pDest->m_cls; - pDest->m_cls = CompHnd->mergeClasses(pDest->GetClassHandle(), pSrc->GetClassHandle()); - if (pDestClsBefore != pDest->m_cls) - { - *changed = true; - } - return true; - } - else if (pDest->IsType(TI_NULL)) - { - if (pSrc->IsType(TI_REF)) // NULL can be any reference type - { - *pDest = *pSrc; - *changed = true; - return true; - } - goto FAIL; - } - else if (pDest->IsType(TI_STRUCT)) - { - if (pSrc->IsType(TI_STRUCT) && CompHnd->areTypesEquivalent(pDest->GetClassHandle(), pSrc->GetClassHandle())) - { - return true; - } - goto FAIL; - } - else if (pDest->IsByRef()) - { - return tiCompatibleWithByRef(CompHnd, *pSrc, *pDest); - } -#ifdef TARGET_64BIT - // On 64-bit targets we have precise representation for native int, so these rules - // represent the fact that the ECMA spec permits the implicit conversion - // between an int32 and a native int. - else if (typeInfo::AreEquivalent(*pDest, typeInfo::nativeInt()) && pSrc->IsType(TI_INT)) - { - return true; - } - else if (typeInfo::AreEquivalent(*pSrc, typeInfo::nativeInt()) && pDest->IsType(TI_INT)) - { - *pDest = *pSrc; - *changed = true; - return true; - } -#endif // TARGET_64BIT - -FAIL: - *pDest = typeInfo(); - return false; -} - -#ifdef DEBUG -#if VERBOSE_VERIFY -// Utility method to have a detailed dump of a TypeInfo object -void typeInfo::Dump() const -{ - char flagsStr[8]; - - flagsStr[0] = ((m_flags & TI_FLAG_UNINIT_OBJREF) != 0) ? 'U' : '-'; - flagsStr[1] = ((m_flags & TI_FLAG_BYREF) != 0) ? 'B' : '-'; - flagsStr[2] = ((m_flags & TI_FLAG_BYREF_READONLY) != 0) ? 'R' : '-'; - flagsStr[3] = ((m_flags & TI_FLAG_NATIVE_INT) != 0) ? 'N' : '-'; - flagsStr[4] = ((m_flags & TI_FLAG_THIS_PTR) != 0) ? 'T' : '-'; - flagsStr[5] = ((m_flags & TI_FLAG_BYREF_PERMANENT_HOME) != 0) ? 'P' : '-'; - flagsStr[6] = ((m_flags & TI_FLAG_GENERIC_TYPE_VAR) != 0) ? 'G' : '-'; - flagsStr[7] = '\0'; - - printf("[%s(%X) {%s}]", tiType2Str(m_bits.type), m_cls, flagsStr); -} -#endif // VERBOSE_VERIFY -#endif // DEBUG