diff options
Diffstat (limited to 'src/jit/compiler.h')
-rw-r--r-- | src/jit/compiler.h | 8757 |
1 files changed, 8757 insertions, 0 deletions
diff --git a/src/jit/compiler.h b/src/jit/compiler.h new file mode 100644 index 0000000000..ba849109ff --- /dev/null +++ b/src/jit/compiler.h @@ -0,0 +1,8757 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Compiler XX +XX XX +XX Represents the method data we are currently JIT-compiling. XX +XX An instance of this class is created for every method we JIT. XX +XX This contains all the info needed for the method. So allocating a XX +XX a new instance per method makes it thread-safe. XX +XX It should be used to do all the memory management for the compiler run. XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +/*****************************************************************************/ +#ifndef _COMPILER_H_ +#define _COMPILER_H_ +/*****************************************************************************/ + +#include "jit.h" +#include "opcode.h" +#include "block.h" +#include "jiteh.h" +#include "instr.h" +#include "regalloc.h" +#include "sm.h" +#include "simplerhash.h" +#include "cycletimer.h" +#include "varset.h" +#include "blockset.h" +#include "jitstd.h" +#include "arraystack.h" +#include "hashbv.h" +#include "fp.h" +#include "expandarray.h" +#include "tinyarray.h" +#include "valuenum.h" +#include "reglist.h" + +#ifdef LATE_DISASM +#include "disasm.h" +#endif + +#include "codegeninterface.h" +#include "regset.h" +#include "jitgcinfo.h" + +#if DUMP_GC_TABLES && defined(JIT32_GCENCODER) +#include "gcdump.h" +#endif + +#include "emit.h" + +#include "simd.h" + +/***************************************************************************** + * Forward declarations + */ + +struct InfoHdr; // defined in GCInfo.h +struct escapeMapping_t; // defined in flowgraph.cpp +class emitter; // defined in emit.h +struct ShadowParamVarInfo; // defined in GSChecks.cpp +struct InitVarDscInfo; // defined in register_arg_convention.h +#if FEATURE_STACK_FP_X87 +struct FlatFPStateX87; // defined in fp.h +#endif +#if FEATURE_ANYCSE +class CSE_DataFlow; // defined in OptCSE.cpp +#endif +#ifdef DEBUG +struct IndentStack; +#endif + +// The following are defined in this file, Compiler.h + +class Compiler; + +/***************************************************************************** + * Unwind info + */ + +#include "unwind.h" + +/*****************************************************************************/ + +// +// Declare global operator new overloads that use the Compiler::compGetMem() function for allocation. +// + +// Or the more-general IAllocator interface. +void * __cdecl operator new(size_t n, IAllocator* alloc); +void * __cdecl operator new[](size_t n, IAllocator* alloc); + +// I wanted to make the second argument optional, with default = CMK_Unknown, but that +// caused these to be ambiguous with the global placement new operators. +void * __cdecl operator new(size_t n, Compiler *context, CompMemKind cmk); +void * __cdecl operator new[](size_t n, Compiler *context, CompMemKind cmk); +void * __cdecl operator new(size_t n, void* p, const jitstd::placement_t& syntax_difference); + + +// Requires the definitions of "operator new" so including "LoopCloning.h" after the definitions. +#include "loopcloning.h" + +/*****************************************************************************/ + +/* This is included here and not earlier as it needs the definition of "CSE" + * which is defined in the section above */ + +#include "gentree.h" + +/*****************************************************************************/ + +unsigned genLog2(unsigned value); +unsigned genLog2(unsigned __int64 value); + +var_types genActualType (var_types type); +var_types genUnsignedType(var_types type); +var_types genSignedType (var_types type); + +/*****************************************************************************/ + +#ifdef FEATURE_SIMD +#ifdef FEATURE_AVX_SUPPORT +const unsigned TEMP_MAX_SIZE = YMM_REGSIZE_BYTES; +#else // !FEATURE_AVX_SUPPORT +const unsigned TEMP_MAX_SIZE = XMM_REGSIZE_BYTES; +#endif // !FEATURE_AVX_SUPPORT +#else // !FEATURE_SIMD +const unsigned TEMP_MAX_SIZE = sizeof(double); +#endif // !FEATURE_SIMD +const unsigned TEMP_SLOT_COUNT = (TEMP_MAX_SIZE / sizeof(int)); + +const unsigned FLG_CCTOR = (CORINFO_FLG_CONSTRUCTOR|CORINFO_FLG_STATIC); + +#ifdef DEBUG +const int BAD_STK_OFFS = 0xBAADF00D; // for LclVarDsc::lvStkOffs +#endif + +// The following holds the Local var info (scope information) +typedef const char* VarName; // Actual ASCII string +struct VarScopeDsc +{ + IL_OFFSET vsdLifeBeg; // instr offset of beg of life + IL_OFFSET vsdLifeEnd; // instr offset of end of life + unsigned vsdVarNum; // (remapped) LclVarDsc number + +#ifdef DEBUG + VarName vsdName; // name of the var +#endif + + unsigned vsdLVnum; // 'which' in eeGetLVinfo(). + // Also, it is the index of this entry in the info.compVarScopes array, + // which is useful since the array is also accessed via the + // compEnterScopeList and compExitScopeList sorted arrays. +}; + +/***************************************************************************** + * + * The following holds the local variable counts and the descriptor table. + */ + +// This is the location of a definition. +struct DefLoc { + BasicBlock* m_blk; + // We'll need more precise info later... +}; + +// This class encapsulates all info about a local variable that may vary for different SSA names +// in the family. +class LclSsaVarDsc +{ +public: + ValueNumPair m_vnPair; + DefLoc m_defLoc; +}; + +typedef ExpandArray<LclSsaVarDsc> PerSsaArray; + +class LclVarDsc +{ +public: + + // The constructor. Most things can just be zero'ed. + LclVarDsc(Compiler* comp); + + // note this only packs because var_types is a typedef of unsigned char + var_types lvType :5; // TYP_INT/LONG/FLOAT/DOUBLE/REF + + unsigned char lvIsParam :1; // is this a parameter? + unsigned char lvIsRegArg :1; // is this a register argument? + unsigned char lvFramePointerBased :1; // 0 = off of REG_SPBASE (e.g., ESP), 1 = off of REG_FPBASE (e.g., EBP) + + unsigned char lvStructGcCount :3; // if struct, how many GC pointer (stop counting at 7). The only use of values >1 is to help determine whether to use block init in the prolog. + unsigned char lvOnFrame :1; // (part of) the variable lives on the frame + unsigned char lvDependReg :1; // did the predictor depend upon this being enregistered + unsigned char lvRegister :1; // assigned to live in a register? For RyuJIT backend, this is only set if the variable is in the same register for the entire function. + unsigned char lvTracked :1; // is this a tracked variable? + bool lvTrackedNonStruct() { return lvTracked && lvType != TYP_STRUCT; } + unsigned char lvPinned :1; // is this a pinned variable? + + unsigned char lvMustInit :1; // must be initialized + unsigned char lvAddrExposed :1; // The address of this variable is "exposed" -- passed as an argument, stored in a global location, etc. + // We cannot reason reliably about the value of the variable. + unsigned char lvDoNotEnregister :1; // Do not enregister this variable. + unsigned char lvFieldAccessed :1; // The var is a struct local, and a field of the variable is accessed. Affects struct promotion. + +#ifdef DEBUG + // These further document the reasons for setting "lvDoNotEnregister". (Note that "lvAddrExposed" is one of the reasons; + // also, lvType == TYP_STRUCT prevents enregistration. At least one of the reasons should be true. + unsigned char lvVMNeedsStackAddr :1; // The VM may have access to a stack-relative address of the variable, and read/write its value. + unsigned char lvLiveInOutOfHndlr :1; // The variable was live in or out of an exception handler, and this required the variable to be + // in the stack (at least at those boundaries.) + unsigned char lvLclFieldExpr :1; // The variable is not a struct, but was accessed like one (e.g., reading a particular byte from an int). + unsigned char lvLclBlockOpAddr :1; // The variable was written to via a block operation that took its address. + unsigned char lvLiveAcrossUCall :1; // The variable is live across an unmanaged call. +#endif + unsigned char lvIsCSE :1; // Indicates if this LclVar is a CSE variable. + unsigned char lvRefAssign :1; // involved in pointer assignment + unsigned char lvHasLdAddrOp:1; // has ldloca or ldarga opcode on this local. + unsigned char lvStackByref :1; // This is a compiler temporary of TYP_BYREF that is known to point into our local stack frame. + +#ifdef DEBUG + unsigned char lvSafeAddrTaken :1; // variable has its address taken, but it's consumed in the next instruction. +#endif + unsigned char lvArgWrite :1; // variable is a parameter and STARG was used on it + unsigned char lvIsTemp :1; // Short-lifetime compiler temp +#if OPT_BOOL_OPS + unsigned char lvIsBoolean :1; // set if variable is boolean +#endif + unsigned char lvRngOptDone:1; // considered for range check opt? + unsigned char lvLoopInc :1; // incremented in the loop? + unsigned char lvLoopAsg :1; // reassigned in the loop (other than a monotonic inc/dec for the index var)? + unsigned char lvArrIndx :1; // used as an array index? + unsigned char lvArrIndxOff:1; // used as an array index with an offset? + unsigned char lvArrIndxDom:1; // index dominates loop exit +#if ASSERTION_PROP + unsigned char lvSingleDef:1; // variable has a single def + unsigned char lvDisqualify:1; // variable is no longer OK for add copy optimization + unsigned char lvVolatileHint:1; // hint for AssertionProp +#endif +#if FANCY_ARRAY_OPT + unsigned char lvAssignOne :1; // assigned at least once? + unsigned char lvAssignTwo :1; // assigned at least twice? +#endif + + unsigned char lvSpilled :1; // enregistered variable was spilled +#ifndef _TARGET_64BIT_ + unsigned char lvStructDoubleAlign :1; // Must we double align this struct? +#endif // !_TARGET_64BIT_ +#ifdef _TARGET_64BIT_ + unsigned char lvQuirkToLong :1; // Quirk to allocate this LclVar as a 64-bit long +#endif +#ifdef DEBUG + unsigned char lvDblWasInt :1; // Was this TYP_DOUBLE originally a TYP_INT? + unsigned char lvKeepType :1; // Don't change the type of this variable + unsigned char lvNoLclFldStress :1;// Can't apply local field stress on this one +#endif + unsigned char lvIsPtr :1; // Might this be used in an address computation? (used by buffer overflow security checks) + unsigned char lvIsUnsafeBuffer :1; // Does this contain an unsafe buffer requiring buffer overflow security checks? + unsigned char lvPromoted :1; // True when this local is a promoted struct, a normed struct, or a "split" long on a 32-bit target. + unsigned char lvIsStructField :1; // Is this local var a field of a promoted struct local? + unsigned char lvContainsFloatingFields :1; // Does this struct contains floating point fields? + unsigned char lvOverlappingFields :1; // True when we have a struct with possibly overlapping fields + unsigned char lvContainsHoles :1; // True when we have a promoted struct that contains holes + unsigned char lvCustomLayout :1; // True when this struct has "CustomLayout" +#ifdef _TARGET_ARM_ + unsigned char lvDontPromote:1; // Should struct promoter consider this variable for promotion? + unsigned char lvIsHfaRegArg:1; // Is this argument variable holding a HFA register argument. + unsigned char lvHfaTypeIsFloat:1; // Is the HFA type float or double? +#endif + +#ifdef DEBUG + // TODO-Cleanup: See the note on lvSize() - this flag is only in use by asserts that are checking for struct + // types, and is needed because of cases where TYP_STRUCT is bashed to an integral type. + // Consider cleaning this up so this workaround is not required. + unsigned char lvUnusedStruct :1; // All references to this promoted struct are through its field locals. + // I.e. there is no longer any reference to the struct directly. + // In this case we can simply remove this struct local. +#endif +#ifndef LEGACY_BACKEND + unsigned char lvLRACandidate :1; // Tracked for linear scan register allocation purposes +#endif // !LEGACY_BACKEND +#ifdef FEATURE_SIMD + unsigned char lvSIMDType :1; // This is a SIMD struct + unsigned char lvUsedInSIMDIntrinsic :1; // This tells lclvar is used for simd intrinsic +#endif // FEATURE_SIMD + unsigned char lvRegStruct : 1; // This is a reg-sized non-field-addressed struct. + + union + { + unsigned lvFieldLclStart; // The index of the local var representing the first field in the promoted struct local. + unsigned lvParentLcl; // The index of the local var representing the parent (i.e. the promoted struct local). Valid on promoted struct local fields. +#ifdef FEATURE_SIMD + var_types lvBaseType; // The base type of a SIMD local var. Valid on TYP_SIMD locals. +#endif // FEATURE_SIMD + }; + + unsigned char lvFieldCnt; // Number of fields in the promoted VarDsc. + unsigned char lvFldOffset; + unsigned char lvFldOrdinal; + +private: + + regNumberSmall _lvRegNum; // Used to store the register this variable is in (or, the low register of a register pair). + // For LEGACY_BACKEND, this is only set if lvRegister is non-zero. For non-LEGACY_BACKEND, it is set during codegen + // any time the variable is enregistered (in non-LEGACY_BACKEND, lvRegister is only set to non-zero if the + // variable gets the same register assignment for its entire lifetime). +#if !defined(_TARGET_64BIT_) + regNumberSmall _lvOtherReg; // Used for "upper half" of long var. +#endif // !defined(_TARGET_64BIT_) + regNumberSmall _lvArgReg; // The register in which this argument is passed. +#ifndef LEGACY_BACKEND + union + { + regNumberSmall _lvArgInitReg; // the register into which the argument is moved at entry + regPairNoSmall _lvArgInitRegPair; // the register pair into which the argument is moved at entry + }; +#endif // !LEGACY_BACKEND + +public: + + // The register number is stored in a small format (8 bits), but the getters return and the setters take + // a full-size (unsigned) format, to localize the casts here. + + ///////////////////// + + __declspec(property(get=GetRegNum,put=SetRegNum)) + regNumber lvRegNum; + + regNumber GetRegNum() const + { + return (regNumber) _lvRegNum; + } + + void SetRegNum(regNumber reg) + { + _lvRegNum = (regNumberSmall) reg; + assert(_lvRegNum == reg); + } + + ///////////////////// + +#if defined(_TARGET_64BIT_) + __declspec(property(get=GetOtherReg,put=SetOtherReg)) + regNumber lvOtherReg; + + regNumber GetOtherReg() const + { + assert(!"shouldn't get here"); // can't use "unreached();" because it's NORETURN, which causes C4072 "unreachable code" warnings + return REG_NA; + } + + void SetOtherReg(regNumber reg) + { + assert(!"shouldn't get here"); // can't use "unreached();" because it's NORETURN, which causes C4072 "unreachable code" warnings + } +#else // !_TARGET_64BIT_ + __declspec(property(get=GetOtherReg,put=SetOtherReg)) + regNumber lvOtherReg; + + regNumber GetOtherReg() const + { + return (regNumber) _lvOtherReg; + } + + void SetOtherReg(regNumber reg) + { + _lvOtherReg = (regNumberSmall) reg; + assert(_lvOtherReg == reg); + } +#endif // !_TARGET_64BIT_ + + ///////////////////// + + __declspec(property(get=GetArgReg,put=SetArgReg)) + regNumber lvArgReg; + + regNumber GetArgReg() const +{ + return (regNumber) _lvArgReg; + } + + void SetArgReg(regNumber reg) + { + _lvArgReg = (regNumberSmall) reg; + assert(_lvArgReg == reg); + } + +#ifdef FEATURE_SIMD + // Is this is a SIMD struct? + bool lvIsSIMDType() const + { + return lvSIMDType; + } + + // Is this is a SIMD struct which is used for SIMD intrinsic? + bool lvIsUsedInSIMDIntrinsic() const + { + return lvUsedInSIMDIntrinsic; + } +#else + // If feature_simd not enabled, return false + bool lvIsSIMDType() const + { + return false; + } + bool lvIsUsedInSIMDIntrinsic() const + { + return false; + } +#endif + + ///////////////////// + +#ifndef LEGACY_BACKEND + __declspec(property(get=GetArgInitReg,put=SetArgInitReg)) + regNumber lvArgInitReg; + + regNumber GetArgInitReg() const + { + return (regNumber) _lvArgInitReg; + } + + void SetArgInitReg(regNumber reg) + { + _lvArgInitReg = (regNumberSmall) reg; + assert(_lvArgInitReg == reg); + } + + ///////////////////// + + __declspec(property(get=GetArgInitRegPair,put=SetArgInitRegPair)) + regPairNo lvArgInitRegPair; + + regPairNo GetArgInitRegPair() const +{ + regPairNo regPair = (regPairNo) _lvArgInitRegPair; + assert(regPair >= REG_PAIR_FIRST && + regPair <= REG_PAIR_LAST); + return regPair; + } + + void SetArgInitRegPair(regPairNo regPair) + { + assert(regPair >= REG_PAIR_FIRST && + regPair <= REG_PAIR_LAST); + _lvArgInitRegPair = (regPairNoSmall) regPair; + assert(_lvArgInitRegPair == regPair); + } + + ///////////////////// + + bool lvIsRegCandidate() const + { + return lvLRACandidate != 0; + } + + bool lvIsInReg() const + { + return lvIsRegCandidate() && (lvRegNum != REG_STK); + } + +#else // LEGACY_BACKEND + + bool lvIsRegCandidate() const + { + return lvTracked != 0; + } + + bool lvIsInReg() const + { + return lvRegister != 0; + } + +#endif // LEGACY_BACKEND + + regMaskTP lvRegMask() const + { + regMaskTP regMask = RBM_NONE; + if (varTypeIsFloating(TypeGet())) + { + if (lvRegNum != REG_STK) + regMask = genRegMaskFloat(lvRegNum, TypeGet()); + } + else + { + if (lvRegNum != REG_STK) + regMask = genRegMask(lvRegNum); + + // For longs we may have two regs + if (isRegPairType(lvType) && lvOtherReg != REG_STK) + regMask |= genRegMask(lvOtherReg); + } + return regMask; + } + + regMaskSmall lvPrefReg; // set of regs it prefers to live in + + unsigned short lvVarIndex; // variable tracking index + unsigned short lvRefCnt; // unweighted (real) reference count + unsigned lvRefCntWtd; // weighted reference count + int lvStkOffs; // stack offset of home + unsigned lvExactSize; // (exact) size of the type in bytes + + // Is this a promoted struct? + // This method returns true only for structs (including SIMD structs), not for + // locals that are split on a 32-bit target. + // It is only necessary to use this: + // 1) if only structs are wanted, and + // 2) if Lowering has already been done. + // Otherwise lvPromoted is valid. + bool lvPromotedStruct() + { +#if !defined(_TARGET_64BIT_) + return (lvPromoted && !varTypeIsLong(lvType)); +#else // defined(_TARGET_64BIT_) + return lvPromoted; +#endif // defined(_TARGET_64BIT_) + } + + unsigned lvSize() // Size needed for storage representation. Only used for structs or TYP_BLK. + { + // TODO-Review: Sometimes we get called on ARM with HFA struct variables that have been promoted, + // where the struct itself is no longer used because all access is via its member fields. + // When that happens, the struct is marked as unused and its type has been changed to + // TYP_INT (to keep the GC tracking code from looking at it). + // See Compiler::raAssignVars() for details. For example: + // N002 ( 4, 3) [00EA067C] ------------- return struct $346 + // N001 ( 3, 2) [00EA0628] ------------- lclVar struct(U) V03 loc2 + // float V03.f1 (offs=0x00) -> V12 tmp7 f8 (last use) (last use) $345 + // Here, the "struct(U)" shows that the "V03 loc2" variable is unused. Not shown is that V03 + // is now TYP_INT in the local variable table. It's not really unused, because it's in the tree. + + assert((lvType == TYP_STRUCT) || + (lvType == TYP_BLK) || + (lvPromoted && lvUnusedStruct)); + return (unsigned)(roundUp(lvExactSize, sizeof(void*))); + } + +#if defined(DEBUGGING_SUPPORT) || defined(DEBUG) + unsigned lvSlotNum; // original slot # (if remapped) +#endif + + typeInfo lvVerTypeInfo; // type info needed for verification + + BYTE * lvGcLayout; // GC layout info for structs + + +#if FANCY_ARRAY_OPT + GenTreePtr lvKnownDim; // array size if known +#endif + +#if ASSERTION_PROP + BlockSet lvRefBlks; // Set of blocks that contain refs + GenTreePtr lvDefStmt; // Pointer to the statement with the single definition + EXPSET_TP lvAssertionDep; // Assertions that depend on us (i.e to this var) + void lvaDisqualifyVar(); // Call to disqualify a local variable from use in optAddCopies +#endif + var_types TypeGet() const { return (var_types) lvType; } + bool lvStackAligned() const + { + assert(lvIsStructField); + return ((lvFldOffset % sizeof(void*)) == 0); + } + bool lvNormalizeOnLoad() const + { + return varTypeIsSmall(TypeGet()) && + // lvIsStructField is treated the same as the aliased local, see fgDoNormalizeOnStore. + (lvIsParam || lvAddrExposed || lvIsStructField); + } + + bool lvNormalizeOnStore() + { + return varTypeIsSmall(TypeGet()) && + // lvIsStructField is treated the same as the aliased local, see fgDoNormalizeOnStore. + !(lvIsParam || lvAddrExposed || lvIsStructField); + } + + void lvaResetSortAgainFlag(Compiler * pComp); + void decRefCnts(BasicBlock::weight_t weight, Compiler * pComp, bool propagate = true); + void incRefCnts(BasicBlock::weight_t weight, Compiler * pComp, bool propagate = true); + void setPrefReg(regNumber regNum, Compiler * pComp); + void addPrefReg(regMaskTP regMask, Compiler * pComp); + bool IsFloatRegType() const + { + return +#ifdef _TARGET_ARM_ + lvIsHfaRegArg || +#endif + isFloatRegType(lvType); + } +#ifdef _TARGET_ARM_ + var_types GetHfaType() const + { + assert(lvIsHfaRegArg); + return lvIsHfaRegArg ? (lvHfaTypeIsFloat ? TYP_FLOAT : TYP_DOUBLE) : TYP_UNDEF; + } + void SetHfaType(var_types type) + { + assert(varTypeIsFloating(type)); + lvHfaTypeIsFloat = (type == TYP_FLOAT); + } +#endif //_TARGET_ARM_ + +#ifndef LEGACY_BACKEND + var_types lvaArgType(); +#endif + + PerSsaArray lvPerSsaData; + +#ifdef DEBUG + // Keep track of the # of SsaNames, for a bounds check. + unsigned lvNumSsaNames; +#endif + + // Returns the address of the per-Ssa data for the given ssaNum (which is required + // not to be the SsaConfig::RESERVED_SSA_NUM, which indicates that the variable is + // not an SSA variable). + LclSsaVarDsc* GetPerSsaData(unsigned ssaNum) + { + assert(ssaNum != SsaConfig::RESERVED_SSA_NUM); + assert(SsaConfig::RESERVED_SSA_NUM == 0); + unsigned zeroBased = ssaNum - SsaConfig::UNINIT_SSA_NUM; + assert(zeroBased < lvNumSsaNames); + return &lvPerSsaData.GetRef(zeroBased); + } + +#ifdef DEBUG +public: + + void PrintVarReg() const + { + if (isRegPairType(TypeGet())) + printf("%s:%s", getRegName(lvOtherReg), // hi32 + getRegName(lvRegNum)); // lo32 + else + printf("%s", getRegName(lvRegNum)); + } +#endif // DEBUG + +}; // class LclVarDsc + + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX TempsInfo XX +XX XX +XX The temporary lclVars allocated by the compiler for code generation XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + + +/***************************************************************************** + * + * The following keeps track of temporaries allocated in the stack frame + * during code-generation (after register allocation). These spill-temps are + * only used if we run out of registers while evaluating a tree. + * + * These are different from the more common temps allocated by lvaGrabTemp(). + */ + +class TempDsc +{ +public: + + TempDsc * tdNext; + +private: + + int tdOffs; +#ifdef DEBUG + static const int BAD_TEMP_OFFSET = 0xDDDDDDDD; // used as a sentinel "bad value" for tdOffs in DEBUG +#endif // DEBUG + + int tdNum; + BYTE tdSize; + var_types tdType; + +public: + TempDsc(int _tdNum, unsigned _tdSize, var_types _tdType) + : tdNum(_tdNum) + , tdSize((BYTE) _tdSize) + , tdType(_tdType) + { +#ifdef DEBUG + assert(tdNum < 0); // temps must have a negative number (so they have a different number from all local variables) + tdOffs = BAD_TEMP_OFFSET; +#endif // DEBUG + if (tdNum != _tdNum) + { + IMPL_LIMITATION("too many spill temps"); + } + } + +#ifdef DEBUG + bool tdLegalOffset() const { return tdOffs != BAD_TEMP_OFFSET; } +#endif // DEBUG + + int tdTempOffs() const { assert(tdLegalOffset()); return tdOffs; } + void tdSetTempOffs(int offs) { tdOffs = offs; assert(tdLegalOffset()); } + void tdAdjustTempOffs(int offs) { tdOffs += offs; assert(tdLegalOffset()); } + + int tdTempNum () const { assert(tdNum < 0); return tdNum; } + unsigned tdTempSize() const { return tdSize; } + var_types tdTempType() const { return tdType; } +}; + +// interface to hide linearscan implementation from rest of compiler +class LinearScanInterface +{ +public: + virtual void doLinearScan() = 0; + virtual void recordVarLocationsAtStartOfBB(BasicBlock *bb) = 0; +}; + +LinearScanInterface *getLinearScanAllocator(Compiler *comp); + +/***************************************************************************** + * Inlining support + */ + + +// Flags lost during inlining. + +#define CORJIT_FLG_LOST_WHEN_INLINING (CORJIT_FLG_BBOPT | \ + CORJIT_FLG_BBINSTR | \ + CORJIT_FLG_PROF_ENTERLEAVE | \ + CORJIT_FLG_DEBUG_EnC | \ + CORJIT_FLG_DEBUG_INFO \ + ) + +enum InlInlineHints +{ + //Static inline hints are here. + InlLooksLikeWrapperMethod = 0x0001, // The inline candidate looks like it's a simple wrapper method. + + InlArgFeedsConstantTest = 0x0002, // One or more of the incoming arguments feeds into a test + //against a constant. This is a good candidate for assertion + //prop. + + InlMethodMostlyLdSt = 0x0004, //This method is mostly loads and stores. + + InlMethodContainsCondThrow= 0x0008, //Method contains a conditional throw, so it does not bloat the + //code as much. + InlArgFeedsRngChk = 0x0010, //Incoming arg feeds an array bounds check. A good assertion + //prop candidate. + + //Dynamic inline hints are here. Only put hints that add to the multiplier in here. + InlIncomingConstFeedsCond = 0x0100, //Incoming argument is constant and feeds a conditional. + InlAllDynamicHints = InlIncomingConstFeedsCond +}; +struct InlineCandidateInfo +{ + DWORD dwRestrictions; + CORINFO_METHOD_INFO methInfo; + unsigned methAttr; + CORINFO_CLASS_HANDLE clsHandle; + unsigned clsAttr; + var_types fncRetType; + CORINFO_METHOD_HANDLE ilCallerHandle; //the logical IL caller of this inlinee. + CORINFO_CONTEXT_HANDLE exactContextHnd; + CorInfoInitClassResult initClassResult; +}; + +struct InlArgInfo +{ + unsigned argIsUsed :1; // is this arg used at all? + unsigned argIsInvariant:1; // the argument is a constant or a local variable address + unsigned argIsLclVar :1; // the argument is a local variable + unsigned argIsThis :1; // the argument is the 'this' pointer + unsigned argHasSideEff :1; // the argument has side effects + unsigned argHasGlobRef :1; // the argument has a global ref + unsigned argHasTmp :1; // the argument will be evaluated to a temp + unsigned argIsByRefToStructLocal:1; // Is this arg an address of a struct local or a normed struct local or a field in them? + unsigned argHasLdargaOp:1; // Is there LDARGA(s) operation on this argument? + + unsigned argTmpNum; // the argument tmp number + GenTreePtr argNode; + GenTreePtr argBashTmpNode; // tmp node created, if it may be replaced with actual arg +}; + +struct InlLclVarInfo +{ + var_types lclTypeInfo; + typeInfo lclVerTypeInfo; + bool lclHasLdlocaOp; // Is there LDLOCA(s) operation on this argument? +}; + +#ifndef LEGACY_BACKEND +const unsigned int MAX_INL_ARGS = 32; // does not include obj pointer +const unsigned int MAX_INL_LCLS = 32; +#else // LEGACY_BACKEND +const unsigned int MAX_INL_ARGS = 10; // does not include obj pointer +const unsigned int MAX_INL_LCLS = 8; +#endif // LEGACY_BACKEND + +class JitInlineResult +{ +public: + JitInlineResult() : inlInlineResult((CorInfoInline)0), inlInliner(NULL), inlInlinee(NULL) +#ifdef DEBUG + , inlReason("Invalid inline result"), +#else + , inlReason(NULL), +#endif + reported(false) + { + } + explicit JitInlineResult(CorInfoInline inlineResult, + CORINFO_METHOD_HANDLE inliner, + CORINFO_METHOD_HANDLE inlinee, + const char * reason = NULL) + : inlInlineResult(inlineResult), inlInliner(inliner), inlInlinee(inlinee), inlReason(reason), + reported(false) + { + assert(dontInline(inlineResult) == (reason != NULL)); + } + inline CorInfoInline result() const { return inlInlineResult; } + inline const char * reason() const { return inlReason; } + //only call this if you explicitly do not want to report an inline failure. + void setReported() { reported = true; } + inline void report(COMP_HANDLE compCompHnd) + { + if (!reported) + { + compCompHnd->reportInliningDecision(inlInliner, inlInlinee, inlInlineResult, inlReason); + } + reported = true; + } +private: + CorInfoInline inlInlineResult; + CORINFO_METHOD_HANDLE inlInliner; + CORINFO_METHOD_HANDLE inlInlinee; + const char * inlReason; + bool reported; +}; +inline bool dontInline(const JitInlineResult& val) { + return(dontInline(val.result())); +} + + +struct InlineInfo +{ + Compiler * InlinerCompiler; // The Compiler instance for the caller (i.e. the inliner) + Compiler * InlineRoot; // The Compiler instance that is the root of the inlining tree of which the owner of "this" is a member. + + CORINFO_METHOD_HANDLE fncHandle; + InlineCandidateInfo * inlineCandidateInfo; + + JitInlineResult inlineResult; + + GenTreePtr retExpr; // The return expression of the inlined candidate. + + CORINFO_CONTEXT_HANDLE tokenLookupContextHandle; // The context handle that will be passed to + // impTokenLookupContextHandle in Inlinee's Compiler. + + unsigned argCnt; + InlArgInfo inlArgInfo[MAX_INL_ARGS + 1]; + int lclTmpNum[MAX_INL_LCLS]; // map local# -> temp# (-1 if unused) + InlLclVarInfo lclVarInfo[MAX_INL_LCLS + MAX_INL_ARGS + 1]; // type information from local sig + + bool thisDereferencedFirst; +#ifdef FEATURE_SIMD + bool hasSIMDTypeArgLocalOrReturn; +#endif // FEATURE_SIMD + + GenTree * iciCall; // The GT_CALL node to be inlined. + GenTree * iciStmt; // The statement iciCall is in. + BasicBlock * iciBlock; // The basic block iciStmt is in. +}; + +// Information about arrays: their element type and size, and the offset of the first element. +// We label GT_IND's that are array indices with GTF_IND_ARR_INDEX, and, for such nodes, +// associate an array info via the map retrieved by GetArrayInfoMap(). This information is used, +// for example, in value numbering of array index expressions. +struct ArrayInfo +{ + var_types m_elemType; + CORINFO_CLASS_HANDLE m_elemStructType; + unsigned m_elemSize; + unsigned m_elemOffset; + + ArrayInfo() + : m_elemType(TYP_UNDEF) + , m_elemStructType(nullptr) + , m_elemSize(0) + , m_elemOffset(0) + {} + + ArrayInfo(var_types elemType, unsigned elemSize, unsigned elemOffset, CORINFO_CLASS_HANDLE elemStructType) + : m_elemType(elemType) + , m_elemStructType(elemStructType) + , m_elemSize(elemSize) + , m_elemOffset(elemOffset) + {} +}; + +// This enumeration names the phases into which we divide compilation. The phases should completely +// partition a compilation. +enum Phases +{ +#define CompPhaseNameMacro(enum_nm, string_nm, hasChildren, parent) enum_nm, +#include "compphases.h" + PHASE_NUMBER_OF +}; + +//--------------------------------------------------------------- +// Compilation time. +// + +// A "CompTimeInfo" is a structure for tracking the compilation time of one or more methods. +// We divide a compilation into a sequence of contiguous phases, and track the total (per-thread) cycles +// of the compilation, as well as the cycles for each phase. We also track the number of bytecodes. +// If there is a failure in reading a timer at any point, the "CompTimeInfo" becomes invalid, as indicated +// by "m_timerFailure" being true. +// If FEATURE_JIT_METHOD_PERF is not set, we define a minimal form of this, enough to let other code compile. +struct CompTimeInfo +{ +#ifdef FEATURE_JIT_METHOD_PERF + // The string names of the phases. + static const char* PhaseNames[]; + + static bool PhaseHasChildren[]; + static int PhaseParent[]; + + unsigned m_byteCodeBytes; + unsigned __int64 m_totalCycles; + unsigned __int64 m_invokesByPhase[PHASE_NUMBER_OF]; + unsigned __int64 m_cyclesByPhase[PHASE_NUMBER_OF]; + // For better documentation, we call EndPhase on + // non-leaf phases. We should also call EndPhase on the + // last leaf subphase; obviously, the elapsed cycles between the EndPhase + // for the last leaf subphase and the EndPhase for an ancestor should be very small. + // We add all such "redundant end phase" intervals to this variable below; we print + // it out in a report, so we can verify that it is, indeed, very small. If it ever + // isn't, this means that we're doing something significant between the end of the last + // declared subphase and the end of its parent. + unsigned __int64 m_parentPhaseEndSlop; + bool m_timerFailure; + + CompTimeInfo(unsigned byteCodeBytes); +#endif +}; + +// TBD: Move this to UtilCode. + +// The CLR requires that critical section locks be initialized via its ClrCreateCriticalSection API...but +// that can't be called until the CLR is initialized. If we have static data that we'd like to protect by a +// lock, and we have a statically allocated lock to protect that data, there's an issue in how to initialize +// that lock. We could insert an initialize call in the startup path, but one might prefer to keep the code +// more local. For such situations, CritSecObject solves the initialization problem, via a level of +// indirection. A pointer to the lock is initially null, and when we query for the lock pointer via "Val()". +// If the lock has not yet been allocated, this allocates one (here a leaf lock), and uses a +// CompareAndExchange-based lazy-initialization to update the field. If this fails, the allocated lock is +// destroyed. This will work as long as the first locking attempt occurs after enough CLR initialization has +// happened to make ClrCreateCriticalSection calls legal. +class CritSecObject +{ + // CRITSEC_COOKIE is an opaque pointer type. + CRITSEC_COOKIE m_pCs; +public: + CritSecObject() + { + m_pCs = NULL; + } + CRITSEC_COOKIE Val() + { + if (m_pCs == NULL) + { + // CompareExchange-based lazy init. + CRITSEC_COOKIE newCs = ClrCreateCriticalSection(CrstLeafLock, CRST_DEFAULT); + CRITSEC_COOKIE observed = InterlockedCompareExchangeT(&m_pCs, newCs, NULL); + if (observed != NULL) + { + ClrDeleteCriticalSection(newCs); + } + } + return m_pCs; + } +}; + +#ifdef FEATURE_JIT_METHOD_PERF + +// This class summarizes the JIT time information over the course of a run: the number of methods compiled, +// and the total and maximum timings. (These are instances of the "CompTimeInfo" type described above). +// The operation of adding a single method's timing to the summary may be performed concurrently by several +// threads, so it is protected by a lock. +// This class is intended to be used as a singleton type, with only a single instance. +class CompTimeSummaryInfo +{ + // This lock protects the fields of all CompTimeSummaryInfo(s) (of which we expect there to be one). + static CritSecObject s_compTimeSummaryLock; + + int m_numMethods; + CompTimeInfo m_total; + CompTimeInfo m_maximum; + + int m_numFilteredMethods; + CompTimeInfo m_filtered; + + // This method computes the number of cycles/sec for the current machine. The cycles are those counted + // by GetThreadCycleTime; we assume that these are of equal duration, though that is not necessarily true. + // If any OS interaction fails, returns 0.0. + double CyclesPerSecond(); + + // This can use what ever data you want to determine if the value to be added + // belongs in the filtered section (it's always included in the unfiltered section) + bool IncludedInFilteredData(CompTimeInfo& info); + +public: + // This is the unique CompTimeSummaryInfo object for this instance of the runtime. + static CompTimeSummaryInfo s_compTimeSummary; + + CompTimeSummaryInfo(): m_total(0), m_maximum(0), m_numMethods(0), m_filtered(0), m_numFilteredMethods(0) {} + + // Assumes that "info" is a completed CompTimeInfo for a compilation; adds it to the summary. + // This is thread safe. + void AddInfo(CompTimeInfo& info); + + // Print the summary information to "f". + // This is not thread-safe; assumed to be called by only one thread. + void Print(FILE* f); +}; + +// A JitTimer encapsulates a CompTimeInfo for a single compilation. It also tracks the start of compilation, +// and when the current phase started. This is intended to be part of a Compilation object. This is +// disabled (FEATURE_JIT_METHOD_PERF not defined) when FEATURE_CORECLR is set, or on non-windows platforms. +// +class JitTimer +{ + unsigned __int64 m_start; // Start of the compilation. + unsigned __int64 m_curPhaseStart; // Start of the current phase. +#ifdef DEBUG + Phases m_lastPhase; // The last phase that was completed (or (Phases)-1 to start). +#endif + CompTimeInfo m_info; // The CompTimeInfo for this compilation. + + + static CritSecObject s_csvLock; // Lock to protect the time log file. + void PrintCsvMethodStats(Compiler* comp); + +private: + void* operator new(size_t); + void* operator new[](size_t); + void operator delete(void*); + void operator delete[](void*); + +public: + // Initialized the timer instance + JitTimer(unsigned byteCodeSize); + + static JitTimer* Create(Compiler* comp, unsigned byteCodeSize) + { + return ::new (comp, CMK_Unknown) JitTimer(byteCodeSize); + } + + static void PrintCsvHeader(); + + // Ends the current phase (argument is for a redundant check). + void EndPhase(Phases phase); + + // Completes the timing of the current method, which is assumed to have "byteCodeBytes" bytes of bytecode, + // and adds it to "sum". + void Terminate(Compiler* comp, CompTimeSummaryInfo& sum); + + // Attempts to query the cycle counter of the current thread. If successful, returns "true" and sets + // *cycles to the cycle counter value. Otherwise, returns false and sets the "m_timerFailure" flag of + // "m_info" to true. + bool GetThreadCycles(unsigned __int64* cycles) + { + bool res = CycleTimer::GetThreadCyclesS(cycles); + if (!res) { m_info.m_timerFailure = true; } + return res; + } +}; +#endif // FEATURE_JIT_METHOD_PERF + + +//------------------- Function/Funclet info ------------------------------- +DECLARE_TYPED_ENUM(FuncKind,BYTE) +{ + FUNC_ROOT, // The main/root function (always id==0) + FUNC_HANDLER, // a funclet associated with an EH handler (finally, fault, catch, filter handler) + FUNC_FILTER, // a funclet associated with an EH filter + FUNC_COUNT +} +END_DECLARE_TYPED_ENUM(FuncKind,BYTE) + +class emitLocation; + +struct FuncInfoDsc +{ + FuncKind funKind; + BYTE funFlags; // Currently unused, just here for padding + unsigned short funEHIndex; // index, into the ebd table, of innermost EH clause corresponding to this + // funclet. It is only valid if funKind field indicates this is a + // EH-related funclet: FUNC_HANDLER or FUNC_FILTER + +#if defined(_TARGET_AMD64_) + + // TODO-AMD64-Throughput: make the AMD64 info more like the ARM info to avoid having this large static array. + emitLocation* startLoc; + emitLocation* endLoc; + emitLocation* coldStartLoc; // locations for the cold section, if there is one. + emitLocation* coldEndLoc; + UNWIND_INFO unwindHeader; + // Maximum of 255 UNWIND_CODE 'nodes' and then the unwind header. If there are an odd + // number of codes, the VM or Zapper will 4-byte align the whole thing. + BYTE unwindCodes[offsetof(UNWIND_INFO, UnwindCode) + (0xFF*sizeof(UNWIND_CODE))]; + unsigned unwindCodeSlot; + +#elif defined(_TARGET_ARMARCH_) + + UnwindInfo uwi; // Unwind information for this function/funclet's hot section + UnwindInfo* uwiCold; // Unwind information for this function/funclet's cold section + // Note: we only have a pointer here instead of the actual object, + // to save memory in the JIT case (compared to the NGEN case), + // where we don't have any cold section. + // Note 2: we currently don't support hot/cold splitting in functions + // with EH, so uwiCold will be NULL for all funclets. + +#endif // _TARGET_ARMARCH_ + + // Eventually we may want to move rsModifiedRegsMask, lvaOutgoingArgSize, and anything else + // that isn't shared between the main function body and funclets. +}; + + + +struct fgArgTabEntry +{ + GenTreePtr node; // Initially points at the Op1 field of 'parent', but if the argument is replaced with an GT_ASG or placeholder + // it will point at the actual argument in the gtCallLateArgs list. + GenTreePtr parent; // Points at the GT_LIST node in the gtCallArgs for this argument + + unsigned argNum; // The original argument number, also specifies the required argument evaluation order from the IL + + regNumber regNum; // The (first) register to use when passing this argument, set to REG_STK for arguments passed on the stack + unsigned numRegs; // Count of number of registers that this argument uses + + // A slot is a pointer sized region in the OutArg area. + unsigned slotNum; // When an argument is passed in the OutArg area this is the slot number in the OutArg area + unsigned numSlots; // Count of number of slots that this argument uses + + unsigned alignment; // 1 or 2 (slots/registers) + unsigned lateArgInx; // index into gtCallLateArgs list + unsigned tmpNum; // the LclVar number if we had to force evaluation of this arg + + bool isSplit :1; // True when this argument is split between the registers and OutArg area + bool needTmp :1; // True when we force this arguments evaluation into a temp LclVar + bool needPlace :1; // True when we must replace this argument with a placeholder node + bool isTmp :1; // True when we setup a temp LclVar for this argument due to size issues with the struct + bool processed :1; // True when we have decided the evaluation order for this argument in the gtCallLateArgs + bool isHfaRegArg :1; // True when the argument is passed as a HFA in FP registers. + bool isBackFilled :1; // True when the argument fills a register slot skipped due to alignment requirements of previous arguments. + bool isNonStandard:1; // True if it is an arg that is passed in a reg other than a standard arg reg + + void SetIsHfaRegArg(bool hfaRegArg) + { + isHfaRegArg = hfaRegArg; + } + + void SetIsBackFilled(bool backFilled) + { + isBackFilled = backFilled; + } + + bool IsBackFilled() + { + return isBackFilled; + } +}; +typedef struct fgArgTabEntry * fgArgTabEntryPtr; + +//------------------------------------------------------------------------- +// +// The class fgArgInfo is used to handle the arguments +// when morphing a GT_CALL node. +// + +class fgArgInfo +{ + Compiler * compiler; // Back pointer to the compiler instance so that we can allocate memory + GenTreePtr callTree; // Back pointer to the GT_CALL node for this fgArgInfo + unsigned argCount; // Updatable arg count value + unsigned nextSlotNum; // Updatable slot count value + unsigned stkLevel; // Stack depth when we make this call (for x86) + + unsigned argTableSize; // size of argTable array (equal to the argCount when done with fgMorphArgs) + bool argsComplete; // marker for state + bool argsSorted; // marker for state + fgArgTabEntryPtr * argTable; // variable sized array of per argument descrption: (i.e. argTable[argTableSize]) + +private: + + void AddArg (fgArgTabEntryPtr curArgTabEntry); + +public: + + fgArgInfo(Compiler * comp, GenTreePtr call, unsigned argCount); + fgArgInfo(GenTreePtr newCall, GenTreePtr oldCall); + + fgArgTabEntryPtr AddRegArg (unsigned argNum, + GenTreePtr node, + GenTreePtr parent, + regNumber regNum, + unsigned numRegs, + unsigned alignment); + + fgArgTabEntryPtr AddStkArg (unsigned argNum, + GenTreePtr node, + GenTreePtr parent, + unsigned numSlots, + unsigned alignment); + + void RemorphReset (); + fgArgTabEntryPtr RemorphRegArg (unsigned argNum, + GenTreePtr node, + GenTreePtr parent, + regNumber regNum, + unsigned numRegs, + unsigned alignment); + + void RemorphStkArg (unsigned argNum, + GenTreePtr node, + GenTreePtr parent, + unsigned numSlots, + unsigned alignment); + + void SplitArg (unsigned argNum, + unsigned numRegs, + unsigned numSlots); + + void EvalToTmp (unsigned argNum, + unsigned tmpNum, + GenTreePtr newNode); + + void ArgsComplete(); + + void SortArgs(); + + void EvalArgsToTemps(); + + void RecordStkLevel (unsigned stkLvl); + unsigned RetrieveStkLevel (); + + unsigned ArgCount () { return argCount; } + fgArgTabEntryPtr * ArgTable () { return argTable; } +}; + + +#ifdef DEBUG +//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// We have the ability to mark source expressions with "Test Labels." +// These drive assertions within the JIT, or internal JIT testing. For example, we could label expressions +// that should be CSE defs, and other expressions that should uses of those defs, with a shared label. + +enum TestLabel // This must be kept identical to System.Runtime.CompilerServices.JitTestLabel.TestLabel. +{ + TL_SsaName, + TL_VN, // Defines a "VN equivalence class". (For full VN, including exceptions thrown). + TL_VNNorm, // Like above, but uses the non-exceptional value of the expression. + TL_CSE_Def, // This must be identified in the JIT as a CSE def + TL_CSE_Use, // This must be identified in the JIT as a CSE use + TL_LoopHoist, // Expression must (or must not) be hoisted out of the loop. +}; + +struct TestLabelAndNum +{ + TestLabel m_tl; + ssize_t m_num; + + TestLabelAndNum() : m_tl(TestLabel(0)), m_num(0) {} +}; + +typedef SimplerHashTable<GenTreePtr, PtrKeyFuncs<GenTree>, TestLabelAndNum, DefaultSimplerHashBehavior> NodeToTestDataMap; + + +//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +#endif // DEBUG + +// This class implements the "IAllocator" interface, so that we can use +// utilcode collection classes in the JIT, and have them use the JIT's allocator. + +class CompAllocator: public IAllocator +{ + Compiler * m_comp; +#if MEASURE_MEM_ALLOC + CompMemKind m_cmk; +#endif +public: + CompAllocator(Compiler * comp, CompMemKind cmk) + : m_comp(comp) +#if MEASURE_MEM_ALLOC + , m_cmk(cmk) +#endif + {} + + inline void * Alloc(size_t sz); + + inline void * ArrayAlloc(size_t elems, size_t elemSize); + + // For the compiler's no-release allocator, free operations are no-ops. + void Free(void * p) {} +}; + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX The big guy. The sections are currently organized as : XX +XX XX +XX o GenTree and BasicBlock XX +XX o LclVarsInfo XX +XX o Importer XX +XX o FlowGraph XX +XX o Optimizer XX +XX o RegAlloc XX +XX o EEInterface XX +XX o TempsInfo XX +XX o RegSet XX +XX o GCInfo XX +XX o Instruction XX +XX o ScopeInfo XX +XX o PrologScopeInfo XX +XX o CodeGenerator XX +XX o UnwindInfo XX +XX o Compiler XX +XX o typeInfo XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +class Compiler +{ + friend class emitter; + friend class UnwindInfo; + friend class UnwindFragmentInfo; + friend class UnwindEpilogInfo; + friend class JitTimer; + friend class LinearScan; + friend class fgArgInfo; + friend class Rationalizer; + friend class Phase; + friend class Lowering; + friend class CSE_DataFlow; + friend class CSE_Heuristic; + friend class CodeGenInterface; + friend class CodeGen; + friend class LclVarDsc; + friend class TempDsc; + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Misc structs definitions XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +public: + + hashBvGlobalData hbvGlobalData; // Used by the hashBv bitvector package. + +#ifdef DEBUG + bool verbose; + bool verboseTrees; + bool shouldUseVerboseTrees(); + bool asciiTrees; // If true, dump trees using only ASCII characters + bool shouldDumpASCIITrees(); + bool verboseSsa; // If true, produce especially verbose dump output in SSA construction. + bool shouldUseVerboseSsa(); + bool treesBeforeAfterMorph; // If true, print trees before/after morphing (paired by an intra-compilation id: + int morphNum; // This counts the the trees that have been morphed, allowing us to label each uniquely. + + const char* VarNameToStr(VarName name) { return name; } + + DWORD expensiveDebugCheckLevel; +#endif + + + +#ifdef _TARGET_ARM_ + + //------------------------------------------------------------------------- + // Functions to handle homogeneous floating-point aggregates (HFAs) in ARM. + // HFAs are one to four element structs where each element is the same + // type, either all float or all double. They are treated specially + // in the ARM Procedure Call Standard, specifically, they are passed in + // floating-point registers. + // + + inline CORINFO_CLASS_HANDLE GetHfaClassHandle(GenTreePtr tree); + + bool IsHfa(CORINFO_CLASS_HANDLE hClass); + bool IsHfa(GenTreePtr tree); + + var_types GetHfaType(GenTreePtr tree); + unsigned GetHfaSlots(GenTreePtr tree); + + inline var_types GetHfaType(CORINFO_CLASS_HANDLE hClass); + inline unsigned GetHfaSlots(CORINFO_CLASS_HANDLE hClass); + +#endif // _TARGET_ARM_ + + //------------------------------------------------------------------------- + // The following is used for validating format of EH table + // + + struct EHNodeDsc; + typedef struct EHNodeDsc* pEHNodeDsc; + + EHNodeDsc* ehnTree; // root of the tree comprising the EHnodes. + EHNodeDsc* ehnNext; // root of the tree comprising the EHnodes. + + struct EHNodeDsc + { + enum EHBlockType { + TryNode, + FilterNode, + HandlerNode, + FinallyNode, + FaultNode + }; + + EHBlockType ehnBlockType; // kind of EH block + IL_OFFSET ehnStartOffset; // IL offset of start of the EH block + IL_OFFSET ehnEndOffset; // IL offset past end of the EH block. (TODO: looks like verInsertEhNode() sets this to the last IL offset, not "one past the last one", i.e., the range Start to End is inclusive). + pEHNodeDsc ehnNext; // next (non-nested) block in sequential order + pEHNodeDsc ehnChild; // leftmost nested block + union { + pEHNodeDsc ehnTryNode; // for filters and handlers, the corresponding try node + pEHNodeDsc ehnHandlerNode; // for a try node, the corresponding handler node + }; + pEHNodeDsc ehnFilterNode; // if this is a try node and has a filter, otherwise 0 + pEHNodeDsc ehnEquivalent; // if blockType=tryNode, start offset and end offset is same, + + + inline void ehnSetTryNodeType() {ehnBlockType = TryNode;} + inline void ehnSetFilterNodeType() {ehnBlockType = FilterNode;} + inline void ehnSetHandlerNodeType() {ehnBlockType = HandlerNode;} + inline void ehnSetFinallyNodeType() {ehnBlockType = FinallyNode;} + inline void ehnSetFaultNodeType() {ehnBlockType = FaultNode;} + + inline BOOL ehnIsTryBlock() {return ehnBlockType == TryNode;} + inline BOOL ehnIsFilterBlock() {return ehnBlockType == FilterNode;} + inline BOOL ehnIsHandlerBlock() {return ehnBlockType == HandlerNode;} + inline BOOL ehnIsFinallyBlock() {return ehnBlockType == FinallyNode;} + inline BOOL ehnIsFaultBlock() {return ehnBlockType == FaultNode;} + + // returns true if there is any overlap between the two nodes + static inline BOOL ehnIsOverlap(pEHNodeDsc node1, pEHNodeDsc node2) + { + if (node1->ehnStartOffset < node2->ehnStartOffset) + { + return (node1->ehnEndOffset >= node2->ehnStartOffset); + } + else + { + return (node1->ehnStartOffset <= node2->ehnEndOffset); + } + } + + // fails with BADCODE if inner is not completely nested inside outer + static inline BOOL ehnIsNested(pEHNodeDsc inner, pEHNodeDsc outer) + { + return ((inner->ehnStartOffset >= outer->ehnStartOffset) && + (inner->ehnEndOffset <= outer->ehnEndOffset)); + } + + }; + + + //------------------------------------------------------------------------- + // Exception handling functions + // + +#if !FEATURE_EH_FUNCLETS + + bool ehNeedsShadowSPslots() { return (info.compXcptnsCount || opts.compDbgEnC); } + + // 0 for methods with no EH + // 1 for methods with non-nested EH, or where only the try blocks are nested + // 2 for a method with a catch within a catch + // etc. + unsigned ehMaxHndNestingCount; + +#endif // !FEATURE_EH_FUNCLETS + + static bool jitIsBetween(unsigned value, unsigned start, unsigned end); + static bool jitIsBetweenInclusive(unsigned value, unsigned start, unsigned end); + + bool bbInCatchHandlerILRange (BasicBlock * blk); + bool bbInFilterILRange (BasicBlock * blk); + bool bbInTryRegions (unsigned regionIndex, BasicBlock * blk); + bool bbInHandlerRegions (unsigned regionIndex, BasicBlock * blk); + bool bbInCatchHandlerRegions (BasicBlock * tryBlk, BasicBlock * hndBlk); + unsigned short bbFindInnermostCommonTryRegion (BasicBlock * bbOne, BasicBlock * bbTwo); + + unsigned short bbFindInnermostTryRegionContainingHandlerRegion (unsigned handlerIndex); + unsigned short bbFindInnermostHandlerRegionContainingTryRegion (unsigned tryIndex); + + // Returns true if "block" is the start of a try region. + bool bbIsTryBeg(BasicBlock* block); + + // Returns true if "block" is the start of a handler or filter region. + bool bbIsHandlerBeg(BasicBlock* block); + + // Returns true iff "block" is where control flows if an exception is raised in the + // try region, and sets "*regionIndex" to the index of the try for the handler. + // Differs from "IsHandlerBeg" in the case of filters, where this is true for the first + // block of the filter, but not for the filter's handler. + bool bbIsExFlowBlock(BasicBlock* block, unsigned* regionIndex); + + bool ehHasCallableHandlers(); + + // Return the EH descriptor for the given region index. + EHblkDsc* ehGetDsc(unsigned regionIndex); + + // Return the EH index given a region descriptor. + unsigned ehGetIndex(EHblkDsc* ehDsc); + + // Return the EH descriptor index of the enclosing try, for the given region index. + unsigned ehGetEnclosingTryIndex(unsigned regionIndex); + + // Return the EH descriptor index of the enclosing handler, for the given region index. + unsigned ehGetEnclosingHndIndex(unsigned regionIndex); + + // Return the EH descriptor for the most nested 'try' region this BasicBlock is a member of (or nullptr if this block is not in a 'try' region). + EHblkDsc* ehGetBlockTryDsc(BasicBlock* block); + + // Return the EH descriptor for the most nested filter or handler region this BasicBlock is a member of (or nullptr if this block is not in a filter or handler region). + EHblkDsc* ehGetBlockHndDsc(BasicBlock* block); + + EHblkDsc* ehIsBlockTryLast(BasicBlock* block); + EHblkDsc* ehIsBlockHndLast(BasicBlock* block); + bool ehIsBlockEHLast(BasicBlock* block); + + // Return the region index of the most nested EH region this block is in. + unsigned ehGetMostNestedRegionIndex(BasicBlock* block, bool* inTryRegion); + + // Find the true enclosing try index, ignoring 'mutual protect' try. Uses IL ranges to check. + unsigned ehTrueEnclosingTryIndexIL(unsigned regionIndex); + + // Return the index of the most nested enclosing region for a particular EH region. Returns NO_ENCLOSING_INDEX + // if there is no enclosing region. If the returned index is not NO_ENCLOSING_INDEX, then '*inTryRegion' + // is set to 'true' if the enclosing region is a 'try', or 'false' if the enclosing region is a handler. + // (It can never be a filter.) + unsigned ehGetEnclosingRegionIndex(unsigned regionIndex, bool* inTryRegion); + + // A block has been deleted. Update the EH table appropriately. + void ehUpdateForDeletedBlock(BasicBlock* block); + + // Determine whether a block can be deleted while preserving the EH normalization rules. + bool ehCanDeleteEmptyBlock(BasicBlock* block); + + // Update the 'last' pointers in the EH table to reflect new or deleted blocks in an EH region. + void ehUpdateLastBlocks(BasicBlock* oldLast, BasicBlock* newLast); + + // For a finally handler, find the region index that the BBJ_CALLFINALLY lives in that calls the handler, + // or NO_ENCLOSING_INDEX if the BBJ_CALLFINALLY lives in the main function body. Normally, the index + // is the same index as the handler (and the BBJ_CALLFINALLY lives in the 'try' region), but for AMD64 the + // BBJ_CALLFINALLY lives in the enclosing try or handler region, whichever is more nested, or the main function body. + // If the returned index is not NO_ENCLOSING_INDEX, then '*inTryRegion' is set to 'true' if the BBJ_CALLFINALLY + // lives in the returned index's 'try' region, or 'false' if lives in the handler region. (It never lives in a filter.) + unsigned ehGetCallFinallyRegionIndex(unsigned finallyIndex, bool* inTryRegion); + + // Find the range of basic blocks in which all BBJ_CALLFINALLY will be found that target the 'finallyIndex' region's + // handler. Set begBlk to the first block, and endBlk to the block after the last block of the range + // (nullptr if the last block is the last block in the program). + // Precondition: 'finallyIndex' is the EH region of a try/finally clause. + void ehGetCallFinallyBlockRange(unsigned finallyIndex, BasicBlock** begBlk, BasicBlock** endBlk); + +#ifdef DEBUG + // Given a BBJ_CALLFINALLY block and the EH region index of the finally it is calling, return + // 'true' if the BBJ_CALLFINALLY is in the correct EH region. + bool ehCallFinallyInCorrectRegion(BasicBlock* blockCallFinally, unsigned finallyIndex); +#endif // DEBUG + +#if FEATURE_EH_FUNCLETS + // Do we need a PSPSym in the main function? For codegen purposes, we only need one + // if there is a filter that protects a region with a nested EH clause (such as a + // try/catch nested in the 'try' body of a try/filter/filter-handler). See + // genFuncletProlog() for more details. However, the VM seems to use it for more + // purposes, maybe including debugging. Until we are sure otherwise, always create + // a PSPSym for functions with any EH. + bool ehNeedsPSPSym() const { return compHndBBtabCount > 0; } + + bool ehAnyFunclets(); // Are there any funclets in this function? + unsigned ehFuncletCount(); // Return the count of funclets in the function + + unsigned bbThrowIndex(BasicBlock * blk); // Get the index to use as the cache key for sharing throw blocks +#else // !FEATURE_EH_FUNCLETS + bool ehAnyFunclets() { return false; } + unsigned ehFuncletCount() { return 0; } + + unsigned bbThrowIndex(BasicBlock * blk) { return blk->bbTryIndex; } // Get the index to use as the cache key for sharing throw blocks +#endif // !FEATURE_EH_FUNCLETS + + // Returns a flowList representing the "EH predecessors" of "blk". These are the normal predecessors of + // "blk", plus one special case: if "blk" is the first block of a handler, considers the predecessor(s) of the first + // first block of the corresponding try region to be "EH predecessors". (If there is a single such predecessor, + // for example, we want to consider that the immediate dominator of the catch clause start block, so it's + // convenient to also consider it a predecessor.) + flowList* BlockPredsWithEH(BasicBlock* blk); + + // This table is useful for memoization of the method above. + typedef SimplerHashTable<BasicBlock*, PtrKeyFuncs<BasicBlock>, flowList*, DefaultSimplerHashBehavior> BlockToFlowListMap; + BlockToFlowListMap* m_blockToEHPreds; + BlockToFlowListMap* GetBlockToEHPreds() + { + if (m_blockToEHPreds == NULL) + { + m_blockToEHPreds = new (getAllocator()) BlockToFlowListMap(getAllocator()); + } + return m_blockToEHPreds; + } + + void* ehEmitCookie(BasicBlock* block); + UNATIVE_OFFSET ehCodeOffset(BasicBlock* block); + + EHblkDsc* ehInitHndRange(BasicBlock* src, + IL_OFFSET* hndBeg, + IL_OFFSET* hndEnd, + bool* inFilter); + + EHblkDsc* ehInitTryRange(BasicBlock* src, + IL_OFFSET* tryBeg, + IL_OFFSET* tryEnd); + + EHblkDsc* ehInitHndBlockRange(BasicBlock* blk, + BasicBlock** hndBeg, + BasicBlock** hndLast, + bool* inFilter); + + EHblkDsc* ehInitTryBlockRange(BasicBlock* blk, + BasicBlock** tryBeg, + BasicBlock** tryLast); + + void fgSetTryEnd (EHblkDsc* handlerTab, + BasicBlock* newTryLast); + + void fgSetHndEnd (EHblkDsc* handlerTab, + BasicBlock* newHndLast); + + void fgSkipRmvdBlocks(EHblkDsc* handlerTab); + + void fgAllocEHTable(); + + void fgRemoveEHTableEntry(unsigned XTnum); + +#if FEATURE_EH_FUNCLETS + + EHblkDsc * fgAddEHTableEntry (unsigned XTnum); + +#endif // FEATURE_EH_FUNCLETS + +#if !FEATURE_EH + void fgRemoveEH(); +#endif // !FEATURE_EH + + void fgSortEHTable(); + + // Causes the EH table to obey some well-formedness conditions, by inserting + // empty BB's when necessary: + // * No block is both the first block of a handler and the first block of a try. + // * No block is the first block of multiple 'try' regions. + // * No block is the last block of multiple EH regions. + void fgNormalizeEH(); + bool fgNormalizeEHCase1(); + bool fgNormalizeEHCase2(); + bool fgNormalizeEHCase3(); + +#ifdef DEBUG + void dispIncomingEHClause(unsigned num, const CORINFO_EH_CLAUSE& clause); + void dispOutgoingEHClause(unsigned num, const CORINFO_EH_CLAUSE& clause); + void fgVerifyHandlerTab(); + void fgDispHandlerTab (); +#endif // DEBUG + + bool fgNeedToSortEHTable; + + void verInitEHTree (unsigned numEHClauses); + void verInsertEhNode (CORINFO_EH_CLAUSE* clause, EHblkDsc* handlerTab); + void verInsertEhNodeInTree(EHNodeDsc** ppRoot, EHNodeDsc* node); + void verInsertEhNodeParent(EHNodeDsc** ppRoot, EHNodeDsc* node); + void verCheckNestingLevel(EHNodeDsc* initRoot); + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX GenTree and BasicBlock XX +XX XX +XX Functions to allocate and display the GenTrees and BasicBlocks XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + + + // Functions to create nodes + GenTreeStmt* gtNewStmt (GenTreePtr expr = NULL, + IL_OFFSETX offset = BAD_IL_OFFSET); + + // For unary opers. + GenTreePtr gtNewOperNode (genTreeOps oper, + var_types type, + GenTreePtr op1, + bool doSimplifications = TRUE); + + // For binary opers. + GenTreePtr gtNewOperNode (genTreeOps oper, + var_types type, + GenTreePtr op1, + GenTreePtr op2); + + GenTreePtr gtNewQmarkNode (var_types type, + GenTreePtr cond, + GenTreePtr colon); + + GenTreePtr gtNewLargeOperNode(genTreeOps oper, + var_types type = TYP_I_IMPL, + GenTreePtr op1 = NULL, + GenTreePtr op2 = NULL); + + GenTreeIntCon* gtNewIconNode (ssize_t value, + var_types type = TYP_INT); + + GenTree* gtNewPhysRegNode(regNumber reg, var_types type); + + GenTree* gtNewPhysRegNode(regNumber reg, GenTree* src); + + GenTreePtr gtNewJmpTableNode(); + GenTreePtr gtNewIconHandleNode(size_t value, + unsigned flags, + FieldSeqNode* fields = NULL, + unsigned handle1 = 0, + void * handle2 = 0); + + unsigned gtTokenToIconFlags(unsigned token); + + GenTreePtr gtNewIconEmbHndNode(void * value, + void * pValue, + unsigned flags, + unsigned handle1 = 0, + void * handle2 = 0, + void * compileTimeHandle = 0); + + GenTreePtr gtNewIconEmbScpHndNode (CORINFO_MODULE_HANDLE scpHnd, unsigned hnd1 = 0, void * hnd2 = 0); + GenTreePtr gtNewIconEmbClsHndNode (CORINFO_CLASS_HANDLE clsHnd, unsigned hnd1 = 0, void * hnd2 = 0); + GenTreePtr gtNewIconEmbMethHndNode(CORINFO_METHOD_HANDLE methHnd, unsigned hnd1 = 0, void * hnd2 = 0); + GenTreePtr gtNewIconEmbFldHndNode (CORINFO_FIELD_HANDLE fldHnd, unsigned hnd1 = 0, void * hnd2 = 0); + + GenTreePtr gtNewStringLiteralNode(InfoAccessType iat, void * pValue); + + GenTreePtr gtNewLconNode (__int64 value); + + GenTreePtr gtNewDconNode (double value); + + GenTreePtr gtNewSconNode (int CPX, + CORINFO_MODULE_HANDLE scpHandle); + + GenTreePtr gtNewZeroConNode(var_types type); + + GenTreePtr gtNewOneConNode(var_types type); + + GenTreeBlkOp* gtNewBlkOpNode (genTreeOps oper, GenTreePtr dst, + GenTreePtr srcOrFillVal, GenTreePtr sizeOrClsTok, + bool volatil); +protected: + void gtBlockOpInit (GenTreePtr node, genTreeOps oper, + GenTreePtr dst, + GenTreePtr src, GenTreePtr size, + bool volatil); +public: + GenTreeBlkOp* gtNewCpObjNode (GenTreePtr dst, GenTreePtr src, + CORINFO_CLASS_HANDLE structHnd, bool volatil); + + GenTreeBlkOp* gtCloneCpObjNode(GenTreeCpObj* source); + + GenTreeArgList* gtNewListNode(GenTreePtr op1, + GenTreeArgList* op2); + + GenTreeCall* gtNewCallNode (gtCallTypes callType, + CORINFO_METHOD_HANDLE handle, + var_types type, + GenTreeArgList* args, + IL_OFFSETX ilOffset = BAD_IL_OFFSET); + + GenTreeCall* gtNewIndCallNode (GenTreePtr addr, + var_types type, + GenTreeArgList* args, + IL_OFFSETX ilOffset = BAD_IL_OFFSET); + + GenTreeCall* gtNewHelperCallNode(unsigned helper, + var_types type, + unsigned flags = 0, + GenTreeArgList* args = NULL); + + GenTreePtr gtNewLclvNode (unsigned lnum, + var_types type, + IL_OFFSETX ILoffs = BAD_IL_OFFSET); + +#ifdef FEATURE_SIMD + GenTreeSIMD* gtNewSIMDNode (var_types type, + GenTreePtr op1, + SIMDIntrinsicID simdIntrinsicID, + var_types baseType, + unsigned size); + GenTreeSIMD* gtNewSIMDNode (var_types type, + GenTreePtr op1, + GenTreePtr op2, + SIMDIntrinsicID simdIntrinsicID, + var_types baseType, + unsigned size); +#endif + + GenTreePtr gtNewLclLNode (unsigned lnum, + var_types type, + IL_OFFSETX ILoffs = BAD_IL_OFFSET); + GenTreeLclFld* gtNewLclFldNode (unsigned lnum, + var_types type, + unsigned offset); + GenTreePtr gtNewInlineCandidateReturnExpr(GenTreePtr inlineCandidate, + var_types type); + + GenTreePtr gtNewCodeRef (BasicBlock * block); + + GenTreePtr gtNewFieldRef (var_types typ, + CORINFO_FIELD_HANDLE fldHnd, + GenTreePtr obj = NULL, + DWORD offset = 0, + bool nullcheck = false); + + GenTreePtr gtNewIndexRef (var_types typ, + GenTreePtr arrayOp, + GenTreePtr indexOp); + + GenTreeArgList* gtNewArgList (GenTreePtr op); + + GenTreeArgList* gtNewArgList (GenTreePtr op1, + GenTreePtr op2); + + static fgArgTabEntryPtr gtArgEntryByArgNum(GenTreePtr call, unsigned argNum); + static fgArgTabEntryPtr gtArgEntryByNode (GenTreePtr call, GenTreePtr node); + fgArgTabEntryPtr gtArgEntryByLateArgIndex(GenTreePtr call, unsigned lateArgInx); + bool gtArgIsThisPtr (fgArgTabEntryPtr argEntry); + + GenTreePtr gtNewAssignNode (GenTreePtr dst, + GenTreePtr src + DEBUG_ARG(bool isPhiDefn = false)); + + GenTreePtr gtNewTempAssign (unsigned tmp, + GenTreePtr val); + + GenTreePtr gtNewRefCOMfield(GenTreePtr objPtr, + CORINFO_RESOLVED_TOKEN * pResolvedToken, + CORINFO_ACCESS_FLAGS access, + CORINFO_FIELD_INFO * pFieldInfo, + var_types lclTyp, + CORINFO_CLASS_HANDLE structType, + GenTreePtr assg); + + GenTreePtr gtNewNothingNode(); + + GenTreePtr gtNewArgPlaceHolderNode(var_types type, + CORINFO_CLASS_HANDLE clsHnd); + + + GenTreePtr gtUnusedValNode (GenTreePtr expr); + + GenTreePtr gtNewCastNode (var_types typ, + GenTreePtr op1, + var_types castType); + + GenTreePtr gtNewCastNodeL (var_types typ, + GenTreePtr op1, + var_types castType); + + //------------------------------------------------------------------------ + // Other GenTree functions + + GenTreePtr gtClone (GenTree * tree, + bool complexOK = false); + + GenTreePtr gtCloneExpr (GenTree * tree, + unsigned addFlags = 0, + unsigned varNum = (unsigned)-1, + int varVal = 0); + // Returns "true" iff the complexity (not formally defined, but first interpretation + // is #of nodes in subtree) of "tree" is greater than "limit". + // (This is somewhat redundant with the "gtCostEx/gtCostSz" fields, but can be used + // before they have been set.) + bool gtComplexityExceeds(GenTreePtr* tree, unsigned limit); + + bool gtCompareTree (GenTree * op1, + GenTree * op2); + + GenTreePtr gtReverseCond (GenTree * tree); + + bool gtHasRef (GenTree * tree, + ssize_t lclNum, + bool defOnly); + + bool gtHasLocalsWithAddrOp (GenTreePtr tree); + + unsigned gtHashValue (GenTree * tree); + + unsigned gtSetListOrder (GenTree * list, + bool regs); + + void gtWalkOp (GenTree * * op1, + GenTree * * op2, + GenTree * adr, + bool constOnly); + +#ifdef DEBUG + GenTreePtr gtWalkOpEffectiveVal(GenTreePtr op); +#endif + + void gtPrepareCost (GenTree * tree); + bool gtIsLikelyRegVar(GenTree * tree); + + unsigned gtSetEvalOrderAndRestoreFPstkLevel(GenTree * tree); + + unsigned gtSetEvalOrder (GenTree * tree); + + void gtSetStmtInfo (GenTree * stmt); + + // Returns "true" iff "node" has any of the side effects in "flags". + bool gtNodeHasSideEffects(GenTreePtr node, + unsigned flags); + + // Returns "true" iff "tree" or its (transitive) children have any of the side effects in "flags". + bool gtTreeHasSideEffects(GenTreePtr tree, + unsigned flags); + + // Appends 'expr' in front of 'list' + // 'list' will typically start off as 'nullptr' + // when 'list' is non-null a GT_COMMA node is used to insert 'expr' + GenTreePtr gtBuildCommaList( GenTreePtr list, + GenTreePtr expr); + + void gtExtractSideEffList(GenTreePtr expr, + GenTreePtr * pList, + unsigned flags = GTF_SIDE_EFFECT, + bool ignoreRoot = false); + + GenTreePtr gtGetThisArg(GenTreePtr call); + + // Static fields of struct types (and sometimes the types that those are reduced to) are represented by having the + // static field contain an object pointer to the boxed struct. This simplifies the GC implementation...but complicates + // the JIT somewhat. This predicate returns "true" iff a node with type "fieldNodeType", representing the given "fldHnd", + // is such an object pointer. + bool gtIsStaticFieldPtrToBoxedStruct(var_types fieldNodeType, CORINFO_FIELD_HANDLE fldHnd); + + + // Assignment trees which contain an unmanged PInvoke call need to have a simple op1 + // in order to prevent us from have a TYP_BYREF live accross a call to a PInvoke + // If necessary this method will morph such an assignment to honor this restriction + // + GenTreePtr gtCheckReorderAssignmentForUnmanagedCall(GenTreePtr tree); + + + //------------------------------------------------------------------------- + + GenTreePtr gtFoldExpr (GenTreePtr tree); + GenTreePtr gtFoldExprConst (GenTreePtr tree); + GenTreePtr gtFoldExprSpecial(GenTreePtr tree); + GenTreePtr gtFoldExprCompare(GenTreePtr tree); + + //------------------------------------------------------------------------- + // Functions to display the trees + +#ifdef DEBUG + bool gtDblWasInt (GenTree * tree) const; + + void gtDispNode (GenTreePtr tree, + IndentStack* indentStack, + __in_z const char* msg); + + void gtDispVN (GenTreePtr tree); + void gtDispConst (GenTreePtr tree); + void gtDispLeaf (GenTreePtr tree, + IndentStack* indentStack); + void gtDispNodeName (GenTreePtr tree); + void gtDispRegVal (GenTreePtr tree); + + void gtDispChild (GenTreePtr child, + IndentStack* indentStack, + unsigned childNum, + __in_opt const char * msg = nullptr, + bool topOnly = false); + void gtDispTree (GenTreePtr tree, + IndentStack* indentStack = nullptr, + __in_opt const char* msg = nullptr, + bool topOnly = false); + int gtGetLclVarName (unsigned lclNum, + char* buf, + unsigned buf_remaining); + char* gtGetLclVarName (unsigned lclNum); + void gtDispLclVar (unsigned varNum, + bool padForBiggestDisp = true); + void gtDispTreeList (GenTreePtr tree, + IndentStack* indentStack = nullptr); + void gtGetArgMsg (GenTreePtr call, + GenTreePtr arg, + unsigned argNum, + char* bufp, + unsigned bufLength); + void gtGetLateArgMsg (GenTreePtr call, + GenTreePtr arg, + int argNum, + char* bufp, + unsigned bufLength); + void gtDispArgList (GenTreePtr tree, + IndentStack* indentStack); + void gtDispFieldSeq (FieldSeqNode* pfsn); + + GenTreePtr gtDispLinearTree(GenTreeStmt* curStmt, + GenTreePtr nextLinearNode, + GenTreePtr tree, + IndentStack* indentStack, + __in_opt const char* msg = nullptr); + GenTreePtr gtDispLinearStmt(GenTreeStmt* stmt, + IndentStack* indentStack = nullptr); +#endif + + // For tree walks + + enum fgWalkResult { WALK_CONTINUE, WALK_SKIP_SUBTREES, WALK_ABORT }; + struct fgWalkData; + typedef fgWalkResult (fgWalkPreFn )(GenTreePtr * pTree, fgWalkData *data); + typedef fgWalkResult (fgWalkPostFn)(GenTreePtr * pTree, fgWalkData *data); + +#ifdef DEBUG + static fgWalkPreFn gtAssertColonCond; +#endif + static fgWalkPreFn gtMarkColonCond; + static fgWalkPreFn gtClearColonCond; + + GenTreePtr * gtFindLink(GenTreePtr stmt, GenTreePtr node); + bool gtHasCatchArg(GenTreePtr tree); + bool gtHasUnmanagedCall(GenTreePtr tree); + + typedef ArrayStack<GenTree*> GenTreeStack; + + static bool gtHasCallOnStack(GenTreeStack *parentStack); + void gtCheckQuirkAddrExposedLclVar(GenTreePtr argTree, GenTreeStack* parentStack); + + //========================================================================= + // BasicBlock functions +#ifdef DEBUG + // This is a debug flag we will use to assert when creating block during codegen + // as this interferes with procedure splitting. If you know what you're doing, set + // it to true before creating the block. (DEBUG only) + bool fgSafeBasicBlockCreation; +#endif + + BasicBlock * bbNewBasicBlock (BBjumpKinds jumpKind); + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX LclVarsInfo XX +XX XX +XX The variables to be used by the code generator. XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + + // + // For both PROMOTION_TYPE_NONE and PROMOTION_TYPE_DEPENDENT the struct will + // be placed in the stack frame and it's fields must be laid out sequentially. + // + // For PROMOTION_TYPE_INDEPENDENT each of the struct's fields is replaced by + // a local variable that can be enregistered or placed in the stack frame. + // The fields do not need to be laid out sequentially + // + enum lvaPromotionType { PROMOTION_TYPE_NONE, // The struct local is not promoted + PROMOTION_TYPE_INDEPENDENT, // The struct local is promoted, + // and its field locals are independent of its parent struct local. + PROMOTION_TYPE_DEPENDENT // The struct local is promoted, + // but its field locals depend on its parent struct local. + }; + + static int __cdecl RefCntCmp(const void *op1, const void *op2); + static int __cdecl WtdRefCntCmp(const void *op1, const void *op2); + + + +/*****************************************************************************/ + + enum FrameLayoutState + { + NO_FRAME_LAYOUT, + INITIAL_FRAME_LAYOUT, + PRE_REGALLOC_FRAME_LAYOUT, + REGALLOC_FRAME_LAYOUT, + TENTATIVE_FRAME_LAYOUT, + FINAL_FRAME_LAYOUT + }; + +public : + + bool lvaRefCountingStarted; // Set to true when we have started counting the local vars + bool lvaLocalVarRefCounted; // Set to true after we have called lvaMarkLocalVars() + bool lvaSortAgain; // true: We need to sort the lvaTable + bool lvaTrackedFixed; // true: We cannot add new 'tracked' variable + unsigned lvaCount; // total number of locals + + unsigned lvaRefCount; // total number of references to locals + LclVarDsc * lvaTable; // variable descriptor table + unsigned lvaTableCnt; // lvaTable size (>= lvaCount) + + LclVarDsc * * lvaRefSorted; // table sorted by refcount + + unsigned short lvaTrackedCount; // actual # of locals being tracked + unsigned lvaTrackedCountInSizeTUnits; // min # of size_t's sufficient to hold a bit for all the locals being tracked + +#ifdef DEBUG + VARSET_TP lvaTrackedVars; // set of tracked variables +#endif +#ifndef _TARGET_64BIT_ + VARSET_TP lvaLongVars; // set of long (64-bit) variables +#endif + VARSET_TP lvaFloatVars; // set of floating-point (32-bit and 64-bit) variables + + unsigned lvaCurEpoch; // VarSets are relative to a specific set of tracked var indices. + // It that changes, this changes. VarSets from different epochs + // cannot be meaningfully combined. + + unsigned GetCurLVEpoch() + { + return lvaCurEpoch; + } + + // reverse map of tracked number to var number + unsigned lvaTrackedToVarNum[lclMAX_TRACKED]; + +#ifdef LEGACY_BACKEND + // variable interference graph + VARSET_TP lvaVarIntf[lclMAX_TRACKED]; +#endif + + // variable preference graph + VARSET_TP lvaVarPref[lclMAX_TRACKED]; + +#if DOUBLE_ALIGN +#ifdef DEBUG + // # of procs compiled a with double-aligned stack + static unsigned s_lvaDoubleAlignedProcsCount; +#endif +#endif + + // Getters and setters for address-exposed and do-not-enregister local var properties. + bool lvaVarAddrExposed (unsigned varNum); + void lvaSetVarAddrExposed (unsigned varNum); + bool lvaVarDoNotEnregister (unsigned varNum); +#ifdef DEBUG + // Reasons why we can't enregister. Some of these correspond to debug properties of local vars. + enum DoNotEnregisterReason + { + DNER_AddrExposed, + DNER_IsStruct, + DNER_LocalField, + DNER_VMNeedsStackAddr, + DNER_LiveInOutOfHandler, + DNER_LiveAcrossUnmanagedCall, + DNER_BlockOp, // Is read or written via a block operation that explicitly takes the address. +#ifdef JIT32_GCENCODER + DNER_PinningRef, +#endif + }; +#endif + void lvaSetVarDoNotEnregister(unsigned varNum DEBUG_ARG(DoNotEnregisterReason reason)); + + unsigned lvaVarargsHandleArg; +#ifdef _TARGET_X86_ + unsigned lvaVarargsBaseOfStkArgs; // Pointer (computed based on incoming varargs handle) to the start of the stack arguments +#endif // _TARGET_X86_ + +#if INLINE_NDIRECT + unsigned lvaInlinedPInvokeFrameVar; // variable representing the InlinedCallFrame +#if FEATURE_FIXED_OUT_ARGS + unsigned lvaPInvokeFrameRegSaveVar; // variable representing the RegSave for PInvoke inlining. +#endif +#endif + unsigned lvaMonAcquired; // boolean variable introduced into in synchronized methods + // that tracks whether the lock has been taken + + unsigned lvaArg0Var; // The lclNum of arg0. Normally this will be info.compThisArg. + // However, if there is a "ldarga 0" or "starg 0" in the IL, + // we will redirect all "ldarg(a) 0" and "starg 0" to this temp. + + unsigned lvaInlineeReturnSpillTemp; // The temp to spill the non-VOID return expression + // in case there are multiple BBJ_RETURN blocks in the inlinee. + +#if FEATURE_FIXED_OUT_ARGS + unsigned lvaOutgoingArgSpaceVar; // dummy TYP_LCLBLK var for fixed outgoing argument space + unsigned lvaOutgoingArgSpaceSize; // size of fixed outgoing argument space +#endif // FEATURE_FIXED_OUT_ARGS + +#ifdef _TARGET_ARM_ + // On architectures whose ABIs allow structs to be passed in registers, struct promotion will sometimes + // require us to "rematerialize" a struct from it's separate constituent field variables. Packing several sub-word + // field variables into an argument register is a hard problem. It's easier to reserve a word of memory into which + // such field can be copied, after which the assembled memory word can be read into the register. We will allocate this + // variable to be this scratch word whenever struct promotion occurs. + unsigned lvaPromotedStructAssemblyScratchVar; +#endif // _TARGET_ARM_ + + +#ifdef DEBUG + unsigned lvaReturnEspCheck; // confirms ESP not corrupted on return + unsigned lvaCallEspCheck; // confirms ESP not corrupted after a call +#endif + + bool lvaGenericsContextUsed; + + bool lvaKeepAliveAndReportThis(); // Synchronized instance method of a reference type, or CORINFO_GENERICS_CTXT_FROM_THIS? + bool lvaReportParamTypeArg(); // Exceptions and CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG? + + //------------------------------------------------------------------------- + // All these frame offsets are inter-related and must be kept in sync + +#if !FEATURE_EH_FUNCLETS + // This is used for the callable handlers + unsigned lvaShadowSPslotsVar; // TYP_BLK variable for all the shadow SP slots +#endif // FEATURE_EH_FUNCLETS + + unsigned lvaCachedGenericContextArgOffs; + unsigned lvaCachedGenericContextArgOffset(); // For CORINFO_CALLCONV_PARAMTYPE and if generic context is passed as THIS pointer + + unsigned lvaLocAllocSPvar; // variable which has the result of the last alloca/localloc + + // TODO-Review: Prior to reg predict we reserve 24 bytes for Spill temps. + // after the reg predict we will use a computed maxTmpSize + // which is based upon the number of spill temps predicted by reg predict + // All this is necessary because if we under-estimate the size of the spill + // temps we could fail when encoding instructions that reference stack offsets for ARM. + // + // Pre codegen max spill temp size. + static const unsigned MAX_SPILL_TEMP_SIZE = 24; + + //------------------------------------------------------------------------- + + unsigned lvaGetMaxSpillTempSize(); +#ifdef _TARGET_ARM_ + bool lvaIsPreSpilled(unsigned lclNum, regMaskTP preSpillMask); +#endif // _TARGET_ARM_ + void lvaAssignFrameOffsets(FrameLayoutState curState); + void lvaFixVirtualFrameOffsets(); + +#ifndef LEGACY_BACKEND + void lvaUpdateArgsWithInitialReg(); +#endif // !LEGACY_BACKEND + + void lvaAssignVirtualFrameOffsetsToArgs(); + int lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs); + void lvaAssignVirtualFrameOffsetsToLocals(); + int lvaAllocLocalAndSetVirtualOffset(unsigned lclNum, unsigned size, int stkOffs); +#ifdef _TARGET_AMD64_ + // Returns true if compCalleeRegsPushed (including RBP if used as frame pointer) is even. + bool lvaIsCalleeSavedIntRegCountEven(); +#endif + void lvaAlignFrame(); + void lvaAssignFrameOffsetsToPromotedStructs(); + int lvaAllocateTemps(int stkOffs, bool mustDoubleAlign); + +#ifdef DEBUG + void lvaDumpRegLocation(unsigned lclNum); + void lvaDumpFrameLocation(unsigned lclNum); + void lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t refCntWtdWidth = 6); + void lvaTableDump(FrameLayoutState curState = NO_FRAME_LAYOUT); // NO_FRAME_LAYOUT means use the current frame layout state defined by lvaDoneFrameLayout +#endif + +// Limit frames size to 1GB. The maximum is 2GB in theory - make it intentionally smaller +// to avoid bugs from borderline cases. +#define MAX_FrameSize 0x3FFFFFFF + void lvaIncrementFrameSize(unsigned size); + + unsigned lvaFrameSize(FrameLayoutState curState); + + // Returns the caller-SP-relative offset for the SP/FP relative offset determined by FP based. + int lvaToCallerSPRelativeOffset(int offs, bool isFpBased); + + // Returns the caller-SP-relative offset for the local variable "varNum." + int lvaGetCallerSPRelativeOffset(unsigned varNum); + + // Returns the SP-relative offset for the local variable "varNum". Illegal to ask this for functions with localloc. + int lvaGetSPRelativeOffset(unsigned varNum); + + int lvaToInitialSPRelativeOffset(unsigned offset, bool isFpBased); + int lvaGetInitialSPRelativeOffset(unsigned varNum); + + //------------------------ For splitting types ---------------------------- + + void lvaInitTypeRef (); + + + void lvaInitArgs (InitVarDscInfo * varDscInfo); + void lvaInitThisPtr (InitVarDscInfo * varDscInfo); + void lvaInitRetBuffArg (InitVarDscInfo * varDscInfo); + void lvaInitUserArgs (InitVarDscInfo * varDscInfo); + void lvaInitGenericsCtxt (InitVarDscInfo * varDscInfo); + void lvaInitVarArgsHandle(InitVarDscInfo * varDscInfo); + + void lvaInitVarDsc (LclVarDsc * varDsc, + unsigned varNum, + CorInfoType corInfoType, + CORINFO_CLASS_HANDLE typeHnd, + CORINFO_ARG_LIST_HANDLE varList, + CORINFO_SIG_INFO * varSig); + + static unsigned lvaTypeRefMask (var_types type); + + var_types lvaGetActualType (unsigned lclNum); + var_types lvaGetRealType (unsigned lclNum); + + //------------------------------------------------------------------------- + + void lvaInit (); + + unsigned lvaArgSize (const void * argTok); + unsigned lvaLclSize (unsigned varNum); + unsigned lvaLclExactSize (unsigned varNum); + + bool lvaLclVarRefs (GenTreePtr tree, + GenTreePtr * findPtr, + varRefKinds * refsPtr, + void* result); + + // Call lvaLclVarRefs on "true"; accumulate "*result" into whichever of + // "allVars" and "trkdVars" is indiated by the nullness of "findPtr"; return + // the return result. + bool lvaLclVarRefsAccum (GenTreePtr tree, + GenTreePtr * findPtr, + varRefKinds * refsPtr, + ALLVARSET_TP* allVars, + VARSET_TP* trkdVars); + + // If "findPtr" is non-NULL, assumes "result" is an "ALLVARSET_TP*", and + // (destructively) unions "allVars" into "*result". Otherwise, assumes "result" is a "VARSET_TP*", + // and (destructively) unions "trkedVars" into "*result". + void lvaLclVarRefsAccumIntoRes(GenTreePtr * findPtr, + void* result, + ALLVARSET_VALARG_TP allVars, + VARSET_VALARG_TP trkdVars); + + bool lvaHaveManyLocals () const; + + unsigned lvaGrabTemp (bool shortLifetime + DEBUGARG(const char * reason) ); + unsigned lvaGrabTemps (unsigned cnt + DEBUGARG(const char * reason) ); + unsigned lvaGrabTempWithImplicitUse(bool shortLifetime + DEBUGARG(const char * reason)); + + void lvaSortOnly (); + void lvaSortByRefCount (); + void lvaDumpRefCounts (); + + void lvaMarkLocalVars (BasicBlock* block); + + void lvaMarkLocalVars (); // Local variable ref-counting + + void lvaAllocOutgoingArgSpace(); // 'Commit' lvaOutgoingArgSpaceSize and lvaOutgoingArgSpaceVar + + VARSET_VALRET_TP lvaStmtLclMask (GenTreePtr stmt); + + static fgWalkPreFn lvaIncRefCntsCB; + void lvaIncRefCnts (GenTreePtr tree); + + static fgWalkPreFn lvaDecRefCntsCB; + void lvaDecRefCnts (GenTreePtr tree); + void lvaRecursiveDecRefCounts(GenTreePtr tree); + + void lvaAdjustRefCnts (); + +#ifdef DEBUG + static fgWalkPreFn lvaStressFloatLclsCB; + void lvaStressFloatLcls (); + + struct lvaStressLclFldArgs + { + Compiler* m_pCompiler; + bool m_bFirstPass; + }; + + static fgWalkPreFn lvaStressLclFldCB; + void lvaStressLclFld (); + + void lvaDispVarSet (VARSET_VALARG_TP set, VARSET_VALARG_TP allVars); + void lvaDispVarSet (VARSET_VALARG_TP set); + +#endif + +#ifdef _TARGET_ARM_ + int lvaFrameAddress (int varNum, bool mustBeFPBased, regNumber * pBaseReg, int addrModeOffset); +#else + int lvaFrameAddress (int varNum, bool* pFPbased); +#endif + + bool lvaIsParameter (unsigned varNum); + bool lvaIsRegArgument (unsigned varNum); + BOOL lvaIsOriginalThisArg(unsigned varNum); // Is this varNum the original this argument? + BOOL lvaIsOriginalThisReadOnly (); // return TRUE if there is no place in the code + // that writes to arg0 + + // If the class is a TYP_STRUCT, get/set a class handle describing it + + CORINFO_CLASS_HANDLE lvaGetStruct (unsigned varNum); + void lvaSetStruct (unsigned varNum, + CORINFO_CLASS_HANDLE typeHnd, + bool unsafeValueClsCheck, + bool setTypeInfo = true); + +#define MAX_NumOfFieldsInPromotableStruct 4 // Maximum number of fields in promotable struct + + // Info about struct fields + struct lvaStructFieldInfo + { + CORINFO_FIELD_HANDLE fldHnd; + unsigned char fldOffset; + unsigned char fldOrdinal; + var_types fldType; + unsigned fldSize; + CORINFO_CLASS_HANDLE fldTypeHnd; + }; + + // Info about struct to be promoted. + struct lvaStructPromotionInfo + { + CORINFO_CLASS_HANDLE typeHnd; + bool canPromote; + bool requiresScratchVar; + bool containsHoles; + bool customLayout; + unsigned char fieldCnt; + lvaStructFieldInfo fields[MAX_NumOfFieldsInPromotableStruct]; + }; + + static int __cdecl lvaFieldOffsetCmp(const void * field1, const void * field2); + void lvaCanPromoteStructType(CORINFO_CLASS_HANDLE typeHnd, lvaStructPromotionInfo * StructPromotionInfo, bool sortFields); + void lvaCanPromoteStructVar(unsigned lclNum, lvaStructPromotionInfo * StructPromotionInfo); + void lvaPromoteStructVar(unsigned lclNum, lvaStructPromotionInfo * StructPromotionInfo); +#if !defined(_TARGET_64BIT_) + void lvaPromoteLongVars(); +#endif // !defined(_TARGET_64BIT_) + unsigned lvaGetFieldLocal(LclVarDsc * varDsc, unsigned int fldOffset); + lvaPromotionType lvaGetPromotionType (const LclVarDsc * varDsc); + lvaPromotionType lvaGetPromotionType (unsigned varNum); + lvaPromotionType lvaGetParentPromotionType (const LclVarDsc * varDsc); + lvaPromotionType lvaGetParentPromotionType (unsigned varNum); + bool lvaIsFieldOfDependentlyPromotedStruct(const LclVarDsc * varDsc); + bool lvaIsGCTracked(const LclVarDsc* varDsc); + + BYTE * lvaGetGcLayout (unsigned varNum); + bool lvaTypeIsGC (unsigned varNum); + unsigned lvaGSSecurityCookie; // LclVar number + bool lvaTempsHaveLargerOffsetThanVars(); + + unsigned lvaSecurityObject; // variable representing the security object on the stack + unsigned lvaStubArgumentVar; // variable representing the secret stub argument coming in EAX + +#if FEATURE_EH_FUNCLETS + unsigned lvaPSPSym; // variable representing the PSPSym +#endif + + InlineInfo * impInlineInfo; + + // The Compiler* that is the root of the inlining tree of which "this" is a member. + Compiler* impInlineRoot(); + + bool fgNoStructPromotion; // Set to TRUE to turn off struct promotion for this method. + bool fgNoStructParamPromotion; // Set to TRUE to turn off struct promotion for parameters this method. + + //========================================================================= + // PROTECTED + //========================================================================= + +protected : + + //---------------- Local variable ref-counting ---------------------------- + +#if ASSERTION_PROP + BasicBlock * lvaMarkRefsCurBlock; + GenTreePtr lvaMarkRefsCurStmt; +#endif + BasicBlock::weight_t lvaMarkRefsWeight; + + static fgWalkPreFn lvaMarkLclRefsCallback; + void lvaMarkLclRefs (GenTreePtr tree); + + + // Keeps the mapping from SSA #'s to VN's for the implicit "Heap" variable. + PerSsaArray lvHeapPerSsaData; + unsigned lvHeapNumSsaNames; + + public: + // Returns the address of the per-Ssa data for "Heap" at the given ssaNum (which is required + // not to be the SsaConfig::RESERVED_SSA_NUM, which indicates that the variable is + // not an SSA variable). + LclSsaVarDsc* GetHeapPerSsaData(unsigned ssaNum) + { + assert(ssaNum != SsaConfig::RESERVED_SSA_NUM); + assert(SsaConfig::RESERVED_SSA_NUM == 0); + ssaNum--; + assert(ssaNum < lvHeapNumSsaNames); + return &lvHeapPerSsaData.GetRef(ssaNum); + } + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Importer XX +XX XX +XX Imports the given method and converts it to semantic trees XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +public : + + void impInit (); + + void impImport (BasicBlock * method); + + CORINFO_CLASS_HANDLE impGetRefAnyClass (); + CORINFO_CLASS_HANDLE impGetRuntimeArgumentHandle(); + CORINFO_CLASS_HANDLE impGetTypeHandleClass (); + CORINFO_CLASS_HANDLE impGetStringClass (); + CORINFO_CLASS_HANDLE impGetObjectClass (); + + //========================================================================= + // PROTECTED + //========================================================================= + +protected : + + //-------------------- Stack manipulation --------------------------------- + + unsigned impStkSize; // Size of the full stack + StackEntry impSmallStack[16]; // Use this array is possible + + + struct SavedStack // used to save/restore stack contents. + { + unsigned ssDepth; // number of values on stack + StackEntry * ssTrees; // saved tree values + }; + + bool impIsPrimitive(CorInfoType type); + bool impILConsumesAddr(const BYTE* codeAddr, CORINFO_METHOD_HANDLE fncHandle, CORINFO_MODULE_HANDLE scpHandle); + + void impResolveToken(const BYTE* addr, CORINFO_RESOLVED_TOKEN * pResolvedToken, CorInfoTokenKind kind); + void impPushOnStackNoType(GenTreePtr tree); + + void impPushOnStack (GenTreePtr tree, + typeInfo ti); + void impPushNullObjRefOnStack(); + StackEntry impPopStack (); + StackEntry impPopStack (CORINFO_CLASS_HANDLE& structTypeRet); + GenTreePtr impPopStack (typeInfo& ti); + StackEntry& impStackTop (unsigned n = 0); + + void impSaveStackState (SavedStack * savePtr, + bool copy); + void impRestoreStackState(SavedStack * savePtr); + + GenTreePtr impImportLdvirtftn (GenTreePtr thisPtr, + CORINFO_RESOLVED_TOKEN * pResolvedToken, + CORINFO_CALL_INFO* pCallInfo); + + void impImportAndPushBox (CORINFO_RESOLVED_TOKEN * pResolvedToken); + + + bool impCanPInvokeInline(var_types callRetTyp); + bool impCanPInvokeInlineCallSite(var_types callRetTyp); + void impCheckForPInvokeCall( GenTreePtr call, + CORINFO_METHOD_HANDLE methHnd, + CORINFO_SIG_INFO * sig, + unsigned mflags); + GenTreePtr impImportIndirectCall(CORINFO_SIG_INFO * sig, + IL_OFFSETX ilOffset = BAD_IL_OFFSET); +#ifdef INLINE_NDIRECT + void impPopArgsForUnmanagedCall(GenTreePtr call, + CORINFO_SIG_INFO * sig); +#endif // INLINE_NDIRECT + + void impInsertHelperCall(CORINFO_HELPER_DESC * helperCall); + void impHandleAccessAllowed(CorInfoIsAccessAllowedResult result, + CORINFO_HELPER_DESC * helperCall); + void impHandleAccessAllowedInternal(CorInfoIsAccessAllowedResult result, + CORINFO_HELPER_DESC * helperCall); + + void impInsertCalloutForDelegate(CORINFO_METHOD_HANDLE callerMethodHnd, + CORINFO_METHOD_HANDLE calleeMethodHnd, + CORINFO_CLASS_HANDLE delegateTypeHnd); + + var_types impImportCall (OPCODE opcode, + CORINFO_RESOLVED_TOKEN * pResolvedToken, + CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken, // Is this a "constrained." call on a type parameter? + GenTreePtr newobjThis, + int prefixFlags, + CORINFO_CALL_INFO* callInfo, + IL_OFFSETX ilOffset = BAD_IL_OFFSET); + + bool impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO * methInfo); + + GenTreePtr impFixupStructReturn(GenTreePtr call, + CORINFO_CLASS_HANDLE retClsHnd); + GenTreePtr impFixupStructReturnType(GenTreePtr op, + CORINFO_CLASS_HANDLE retClsHnd); + +#ifdef DEBUG + var_types impImportJitTestLabelMark(int numArgs); +#endif // DEBUG + + GenTreePtr impInitClass(CORINFO_RESOLVED_TOKEN * pResolvedToken); + + GenTreePtr impImportStaticReadOnlyField(void * fldAddr, var_types lclTyp); + + GenTreePtr impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN * pResolvedToken, + CORINFO_ACCESS_FLAGS access, + CORINFO_FIELD_INFO * pFieldInfo, + var_types lclTyp); + + static void impBashVarAddrsToI (GenTreePtr tree1, + GenTreePtr tree2 = NULL); + + GenTreePtr impImplicitIorI4Cast(GenTreePtr tree, + var_types dstTyp); + + GenTreePtr impImplicitR4orR8Cast(GenTreePtr tree, + var_types dstTyp); + + void impImportLeave (BasicBlock * block); + void impResetLeaveBlock (BasicBlock * block, + unsigned jmpAddr); + BOOL impLocAllocOnStack (); + GenTreePtr impIntrinsic (CORINFO_CLASS_HANDLE clsHnd, + CORINFO_METHOD_HANDLE method, + CORINFO_SIG_INFO * sig, + int memberRef, + bool readonlyCall, + CorInfoIntrinsics * pIntrinsicID); + GenTreePtr impArrayAccessIntrinsic(CORINFO_CLASS_HANDLE clsHnd, + CORINFO_SIG_INFO * sig, + int memberRef, + bool readonlyCall, + CorInfoIntrinsics intrinsicID); + GenTreePtr impInitializeArrayIntrinsic(CORINFO_SIG_INFO * sig); + + GenTreePtr impMethodPointer(CORINFO_RESOLVED_TOKEN * pResolvedToken, + CORINFO_CALL_INFO * pCallInfo); + + GenTreePtr impTransformThis (GenTreePtr thisPtr, + CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken, + CORINFO_THIS_TRANSFORM transform); + + //----------------- Manipulating the trees and stmts ---------------------- + + GenTreePtr impTreeList; // Trees for the BB being imported + GenTreePtr impTreeLast; // The last tree for the current BB + + enum { CHECK_SPILL_ALL = -1, CHECK_SPILL_NONE = -2 }; + +public: + void impBeginTreeList (); + void impEndTreeList (BasicBlock * block, + GenTreePtr firstStmt, + GenTreePtr lastStmt); + void impEndTreeList (BasicBlock * block); + void impAppendStmtCheck (GenTreePtr stmt, + unsigned chkLevel); + void impAppendStmt (GenTreePtr stmt, + unsigned chkLevel); + void impInsertStmtBefore (GenTreePtr stmt, + GenTreePtr stmtBefore); + GenTreePtr impAppendTree (GenTreePtr tree, + unsigned chkLevel, + IL_OFFSETX offset); + void impInsertTreeBefore (GenTreePtr tree, + IL_OFFSETX offset, + GenTreePtr stmtBefore); + void impAssignTempGen (unsigned tmp, + GenTreePtr val, + unsigned curLevel, + GenTreePtr * pAfterStmt = NULL, + IL_OFFSETX ilOffset = BAD_IL_OFFSET, + BasicBlock * block = NULL); + void impAssignTempGen (unsigned tmpNum, + GenTreePtr val, + CORINFO_CLASS_HANDLE structHnd, + unsigned curLevel, + GenTreePtr * pAfterStmt = NULL, + IL_OFFSETX ilOffset = BAD_IL_OFFSET, + BasicBlock * block = NULL); + GenTreePtr impCloneExpr (GenTreePtr tree, + GenTreePtr * clone, + CORINFO_CLASS_HANDLE structHnd, + unsigned curLevel, + GenTreePtr * pAfterStmt + DEBUGARG(const char * reason) ); + GenTreePtr impAssignStruct (GenTreePtr dest, + GenTreePtr src, + CORINFO_CLASS_HANDLE structHnd, + unsigned curLevel, + GenTreePtr * pAfterStmt = NULL, + BasicBlock * block = NULL); + GenTreePtr impAssignStructPtr (GenTreePtr dest, + GenTreePtr src, + CORINFO_CLASS_HANDLE structHnd, + unsigned curLevel, + GenTreePtr * pAfterStmt = NULL, + BasicBlock * block = NULL); + + GenTreePtr impGetStructAddr (GenTreePtr structVal, + CORINFO_CLASS_HANDLE structHnd, + unsigned curLevel, + bool willDeref); + GenTreePtr impNormStructVal (GenTreePtr structVal, + CORINFO_CLASS_HANDLE structHnd, + unsigned curLevel, + bool forceNormalization = false); + + GenTreePtr impTokenToHandle (CORINFO_RESOLVED_TOKEN * pResolvedToken, + BOOL *pRuntimeLookup = NULL, + BOOL mustRestoreHandle = FALSE, + BOOL importParent = FALSE); + + GenTreePtr impParentClassTokenToHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, + BOOL *pRuntimeLookup = NULL, + BOOL mustRestoreHandle = FALSE) + { + return impTokenToHandle(pResolvedToken, pRuntimeLookup, mustRestoreHandle, TRUE); + } + + GenTreePtr impLookupToTree(CORINFO_LOOKUP *pLookup, + unsigned flags, + void *compileTimeHandle); + + GenTreePtr impRuntimeLookupToTree(CORINFO_RUNTIME_LOOKUP_KIND kind, + CORINFO_RUNTIME_LOOKUP *pLookup, + void * compileTimeHandle); + + GenTreePtr impReadyToRunLookupToTree(CORINFO_CONST_LOOKUP *pLookup, + unsigned flags, + void *compileTimeHandle); + + GenTreePtr impReadyToRunHelperToTree(CORINFO_RESOLVED_TOKEN * pResolvedToken, + CorInfoHelpFunc helper, + var_types type, + GenTreePtr arg = NULL); + + GenTreePtr impCastClassOrIsInstToTree(GenTreePtr op1, + GenTreePtr op2, + CORINFO_RESOLVED_TOKEN * pResolvedToken, + bool isCastClass ); + + bool VarTypeIsMultiByteAndCanEnreg(var_types type, + CORINFO_CLASS_HANDLE typeClass, + unsigned *typeSize); + +private: + + //----------------- Importing the method ---------------------------------- + + CORINFO_CONTEXT_HANDLE impTokenLookupContextHandle; // The context used for looking up tokens. + +#ifdef DEBUG + unsigned impCurOpcOffs; + const char * impCurOpcName; + bool impNestedStackSpill; + + + // For displaying instrs with generated native code (-n:B) + GenTreePtr impLastILoffsStmt; // oldest stmt added for which we did not gtStmtLastILoffs + void impNoteLastILoffs (); +#endif + + /* IL offset of the stmt currently being imported. It gets set to + BAD_IL_OFFSET after it has been set in the appended trees. Then it gets + updated at IL offsets for which we have to report mapping info. + It also includes flag bits, so use jitGetILoffs() + to get the actual IL offset value. + */ + + IL_OFFSETX impCurStmtOffs; + void impCurStmtOffsSet (IL_OFFSET offs); + + void impNoteBranchOffs (); + + unsigned impInitBlockLineInfo (); + + GenTreePtr impCheckForNullPointer (GenTreePtr obj); + bool impIsThis (GenTreePtr obj); + bool impIsLDFTN_TOKEN (const BYTE * delegateCreateStart, const BYTE * newobjCodeAddr); + bool impIsDUP_LDVIRTFTN_TOKEN(const BYTE * delegateCreateStart, const BYTE * newobjCodeAddr); + + GenTreeArgList* impPopList (unsigned count, + unsigned * flagsPtr, + CORINFO_SIG_INFO* sig, + GenTreeArgList* prefixTree = NULL); + + GenTreeArgList* impPopRevList (unsigned count, + unsigned * flagsPtr, + CORINFO_SIG_INFO* sig, + unsigned skipReverseCount = 0); + + /* + * Get current IL offset with stack-empty info incoporated + */ + IL_OFFSETX impCurILOffset (IL_OFFSET offs, bool callInstruction = false); + + //---------------- Spilling the importer stack ---------------------------- + + struct PendingDsc + { + PendingDsc * pdNext; + BasicBlock * pdBB; + SavedStack pdSavedStack; + ThisInitState pdThisPtrInit; + }; + + PendingDsc * impPendingList; // list of BBs currently waiting to be imported. + PendingDsc * impPendingFree; // Freed up dscs that can be reused + + // We keep a byte-per-block map (dynamically extended) in the top-level Compiler object of a compilation. + ExpandArray<BYTE> impPendingBlockMembers; + + // Return the byte for "b" (allocating/extending impPendingBlockMembers if necessary.) + // Operates on the map in the top-level ancestor. + BYTE impGetPendingBlockMember(BasicBlock* blk) + { + return impInlineRoot()->impPendingBlockMembers.Get(blk->bbInd()); + } + + // Set the byte for "b" to "val" (allocating/extending impPendingBlockMembers if necessary.) + // Operates on the map in the top-level ancestor. + void impSetPendingBlockMember(BasicBlock* blk, BYTE val) + { + impInlineRoot()->impPendingBlockMembers.Set(blk->bbInd(), val); + } + + bool impCanReimport; + + bool impSpillStackEntry (unsigned level, + unsigned varNum +#ifdef DEBUG + , bool bAssertOnRecursion + , const char * reason +#endif + ); + + + void impSpillStackEnsure (bool spillLeaves = false); + void impEvalSideEffects (); + void impSpillSpecialSideEff (); + void impSpillSideEffects (bool spillGlobEffects, + unsigned chkLevel + DEBUGARG(const char * reason) ); + void impSpillValueClasses (); + void impSpillEvalStack(); + static fgWalkPreFn impFindValueClasses; + void impSpillLclRefs (ssize_t lclNum); + + BasicBlock * impPushCatchArgOnStack (BasicBlock * hndBlk, + CORINFO_CLASS_HANDLE clsHnd); + + void impImportBlockCode (BasicBlock * block); + + void impReimportMarkBlock (BasicBlock * block); + void impReimportMarkSuccessors(BasicBlock * block); + + void impVerifyEHBlock (BasicBlock * block, + bool isTryStart); + + void impImportBlockPending (BasicBlock * block); + + // Similar to impImportBlockPending, but assumes that block has already been imported once and is being + // reimported for some reason. It specifically does *not* look at verCurrentState to set the EntryState + // for the block, but instead, just re-uses the block's existing EntryState. + void impReimportBlockPending (BasicBlock * block); + + var_types impGetByRefResultType (genTreeOps oper, + bool fUnsigned, + GenTreePtr * pOp1, + GenTreePtr * pOp2); + + void impImportBlock (BasicBlock * block); + + // Assumes that "block" is a basic block that completes with a non-empty stack. We will assign the values + // on the stack to local variables (the "spill temp" variables). The successor blocks will assume that + // its incoming stack contents are in those locals. This requires "block" and its successors to agree on + // the variables that will be used -- and for all the predecessors of those successors, and the + // successors of those predecessors, etc. Call such a set of blocks closed under alternating + // successor/predecessor edges a "spill clique." A block is a "predecessor" or "successor" member of the + // clique (or, conceivably, both). Each block has a specified sequence of incoming and outgoing spill + // temps. If "block" already has its outgoing spill temps assigned (they are always a contiguous series + // of local variable numbers, so we represent them with the base local variable number), returns that. + // Otherwise, picks a set of spill temps, and propagates this choice to all blocks in the spill clique of + // which "block" is a member (asserting, in debug mode, that no block in this clique had its spill temps + // chosen already. More precisely, that the incoming or outgoing spill temps are not chosen, depending + // on which kind of member of the clique the block is). + unsigned impGetSpillTmpBase (BasicBlock * block); + + // Assumes that "block" is a basic block that completes with a non-empty stack. We have previously + // assigned the values on the stack to local variables (the "spill temp" variables). The successor blocks + // will assume that its incoming stack contents are in those locals. This requires "block" and its + // successors to agree on the variables and their types that will be used. The CLI spec allows implicit + // conversions between 'int' and 'native int' or 'float' and 'double' stack types. So one predecessor can + // push an int and another can push a native int. For 64-bit we have chosen to implement this by typing + // the "spill temp" as native int, and then importing (or re-importing as needed) so that all the + // predecessors in the "spill clique" push a native int (sign-extending if needed), and all the + // successors receive a native int. Similarly float and double are unified to double. + // This routine is called after a type-mismatch is detected, and it will walk the spill clique to mark + // blocks for re-importation as appropriate (both successors, so they get the right incoming type, and + // predecessors, so they insert an upcast if needed). + void impReimportSpillClique (BasicBlock * block); + + // When we compute a "spill clique" (see above) these byte-maps are allocated to have a byte per basic + // block, and represent the predecessor and successor members of the clique currently being computed. + // *** Access to these will need to be locked in a parallel compiler. + ExpandArray<BYTE> impSpillCliquePredMembers; + ExpandArray<BYTE> impSpillCliqueSuccMembers; + + enum SpillCliqueDir + { + SpillCliquePred, SpillCliqueSucc + }; + + // Abstract class for receiving a callback while walking a spill clique + class SpillCliqueWalker { + public: + virtual void Visit(SpillCliqueDir predOrSucc, BasicBlock* blk) = 0; + }; + + // This class is used for setting the bbStkTempsIn and bbStkTempsOut on the blocks within a spill clique + class SetSpillTempsBase : public SpillCliqueWalker { + unsigned m_baseTmp; + public: + SetSpillTempsBase(unsigned baseTmp) : m_baseTmp(baseTmp) { } + virtual void Visit(SpillCliqueDir predOrSucc, BasicBlock* blk); + }; + + // This class is used for implementing impReimportSpillClique part on each block within the spill clique + class ReimportSpillClique : public SpillCliqueWalker { + Compiler * m_pComp; + public: + ReimportSpillClique(Compiler * pComp) : m_pComp(pComp) { } + virtual void Visit(SpillCliqueDir predOrSucc, BasicBlock* blk); + }; + + // This is the heart of the algorithm for walking spill cliques. It invokes callback->Visit for each + // predecessor or successor within the spill clique + void impWalkSpillCliqueFromPred(BasicBlock* pred, SpillCliqueWalker * callback); + + // For a BasicBlock that has already been imported, the EntryState has an array of GenTrees for the + // incoming locals. This walks that list an resets the types of the GenTrees to match the types of + // the VarDscs. They get out of sync when we have int/native int issues (see impReimportSpillClique). + void impRetypeEntryStateTemps(BasicBlock * blk); + + BYTE impSpillCliqueGetMember(SpillCliqueDir predOrSucc, BasicBlock* blk); + void impSpillCliqueSetMember(SpillCliqueDir predOrSucc, BasicBlock* blk, BYTE val); + + void impPushVar(GenTree* op, typeInfo tiRetVal); + void impLoadVar(unsigned lclNum, IL_OFFSET offset, typeInfo tiRetVal); + void impLoadVar(unsigned lclNum, IL_OFFSET offset) { impLoadVar(lclNum, offset, lvaTable[lclNum].lvVerTypeInfo); } + void impLoadArg(unsigned ilArgNum, IL_OFFSET offset); + void impLoadLoc(unsigned ilLclNum, IL_OFFSET offset); + bool impReturnInstruction(BasicBlock *block, int prefixFlags, OPCODE &opcode); + void impAbortInline(bool abortThisInlineOnly, bool contextDependent, const char *reason); + +#ifdef _TARGET_ARM_ + void impMarkLclDstNotPromotable(unsigned tmpNum, GenTreePtr op, CORINFO_CLASS_HANDLE hClass); + GenTreePtr impAssignHfaToVar(GenTreePtr op, CORINFO_CLASS_HANDLE hClass); +#endif + + // A free list of linked list nodes used to represent to-do stacks of basic blocks. + struct BlockListNode + { + BasicBlock* m_blk; + BlockListNode* m_next; + BlockListNode(BasicBlock* blk, BlockListNode* next = NULL) : m_blk(blk), m_next(next) {} + void* operator new (size_t sz, Compiler* comp); + }; + BlockListNode* impBlockListNodeFreeList; + + BlockListNode* AllocBlockListNode(); + void FreeBlockListNode(BlockListNode* node); + + bool impIsValueType (typeInfo* pTypeInfo); + var_types mangleVarArgsType (var_types type); + regNumber getCallArgIntRegister (regNumber floatReg); + regNumber getCallArgFloatRegister (regNumber intReg); + + //--------------------------- Inlining------------------------------------- + +#if defined(DEBUG) || MEASURE_INLINING + static unsigned jitTotalMethodCompiled; + static unsigned jitTotalMethodInlined; + static unsigned jitTotalInlineCandidates; + static unsigned jitTotalInlineCandidatesWithNonNullReturn; + static unsigned jitTotalNumLocals; + static unsigned jitTotalInlineReturnFromALocal; + static unsigned jitInlineInitVarsFailureCount; + static unsigned jitCheckCanInlineCallCount; + static unsigned jitCheckCanInlineFailureCount; + + static unsigned jitInlineGetMethodInfoCallCount; + static unsigned jitInlineInitClassCallCount; + static unsigned jitInlineCanInlineCallCount; + + static unsigned jitIciStmtIsTheLastInBB; + static unsigned jitInlineeContainsOnlyOneBB; + + #define INLINER_FAILED "\nINLINER FAILED: " + #define INLINER_WARNING "\nINLINER WARNING: " + #define INLINER_INFO "\nINLINER INFO: " + +#endif // defined(DEBUG) || MEASURE_INLINING + +#ifdef DEBUG + static LONG jitNestingLevel; +#endif // DEBUG + + bool seenConditionalJump; + + unsigned impInlineSize; // max IL size for inlining + + static BOOL impIsAddressInLocal(GenTreePtr tree, GenTreePtr * lclVarTreeOut); + + int impEstimateCallsiteNativeSize (CORINFO_METHOD_INFO * methInfo); + + JitInlineResult impCanInlineNative (int callsiteNativeEstimate, + int calleeNativeSizeEstimate, + InlInlineHints inlineHints, + InlineInfo * pInlineInfo); + + // STATIC inlining decision based on the IL code. + JitInlineResult impCanInlineIL (CORINFO_METHOD_HANDLE fncHandle, + CORINFO_METHOD_INFO * methInfo, + bool forceInline); + + JitInlineResult impCheckCanInline(GenTreePtr call, + CORINFO_METHOD_HANDLE fncHandle, + unsigned methAttr, + CORINFO_CONTEXT_HANDLE exactContextHnd, + InlineCandidateInfo ** ppInlineCandidateInfo); + + JitInlineResult impInlineRecordArgInfo(InlineInfo * pInlineInfo, + GenTreePtr curArgVal, + unsigned argNum); + + JitInlineResult impInlineInitVars (InlineInfo * pInlineInfo); + + unsigned impInlineFetchLocal(unsigned lclNum + DEBUGARG(const char * reason) ); + + GenTreePtr impInlineFetchArg(unsigned lclNum, + InlArgInfo * inlArgInfo, + InlLclVarInfo * lclTypeInfo); + + BOOL impInlineIsThis(GenTreePtr tree, InlArgInfo * inlArgInfo); + + BOOL impInlineIsGuaranteedThisDerefBeforeAnySideEffects( + GenTreePtr additionalTreesToBeEvaluatedBefore, + GenTreePtr variableBeingDereferenced, + InlArgInfo * inlArgInfo); + void impMarkInlineCandidate(GenTreePtr call, CORINFO_CONTEXT_HANDLE exactContextHnd); + + + bool impTailCallRetTypeCompatible(var_types callerRetType, + CORINFO_CLASS_HANDLE callerRetTypeClass, + var_types calleeRetType, + CORINFO_CLASS_HANDLE calleeRetTypeClass); + + bool impIsTailCallILPattern(bool tailPrefixed, + OPCODE curOpcode, + const BYTE *codeAddrOfNextOpcode, + const BYTE *codeEnd, + bool *IsCallPopRet = nullptr); + + bool impIsImplicitTailCallCandidate(OPCODE curOpcode, + const BYTE *codeAddrOfNextOpcode, + const BYTE *codeEnd, + int prefixFlags); + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX FlowGraph XX +XX XX +XX Info about the basic-blocks, their contents and the flow analysis XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + + +public : + + BasicBlock * fgFirstBB; // Beginning of the basic block list + BasicBlock * fgLastBB; // End of the basic block list + BasicBlock * fgFirstColdBlock; // First block to be placed in the cold section +#if FEATURE_EH_FUNCLETS + BasicBlock * fgFirstFuncletBB; // First block of outlined funclets (to allow block insertion before the funclets) +#endif + BasicBlock * fgFirstBBScratch; // Block inserted for initialization stuff. Is nullptr if no such block has been created. + BasicBlockList* fgReturnBlocks; // list of BBJ_RETURN blocks + unsigned fgEdgeCount; // # of control flow edges between the BBs + unsigned fgBBcount; // # of BBs in the method +#ifdef DEBUG + unsigned fgBBcountAtCodegen; // # of BBs in the method at the start of codegen +#endif + unsigned fgBBNumMax; // The max bbNum that has been assigned to basic blocks + unsigned fgDomBBcount; // # of BBs for which we have dominator and reachability information + BasicBlock** fgBBInvPostOrder; // The flow graph stored in an array sorted in topological order, needed to compute dominance. Indexed by block number. Size: fgBBNumMax + 1. + + // After the dominance tree is computed, we cache a DFS preorder number and DFS postorder number to compute dominance queries in O(1). + // fgDomTreePreOrder and fgDomTreePostOrder are arrays giving the block's preorder and postorder number, respectively. + // The arrays are indexed by basic block number. (Note that blocks are numbered starting from one. Thus, we always waste + // element zero. This makes debugging easier and makes the code less likely to suffer from bugs stemming from forgetting + // to add or subtract one from the block number to form an array index). The arrays are of size fgBBNumMax + 1. + unsigned * fgDomTreePreOrder; + unsigned * fgDomTreePostOrder; + + bool fgBBVarSetsInited; + + + // Allocate array like T* a = new T[fgBBNumMax + 1]; + // Using helper so we don't keep forgetting +1. + template <typename T> + T* fgAllocateTypeForEachBlk(CompMemKind cmk = CMK_Unknown) + { + return (T*) compGetMem((fgBBNumMax + 1) * sizeof(T), cmk); + } + + // BlockSets are relative to a specific set of BasicBlock numbers. If that changes + // (if the blocks are renumbered), this changes. BlockSets from different epochs + // cannot be meaningfully combined. Note that new blocks can be created with higher + // block numbers without changing the basic block epoch. These blocks *cannot* + // participate in a block set until the blocks are all renumbered, causing the epoch + // to change. This is useful if continuing to use previous block sets is valuable. + // If the epoch is zero, then it is uninitialized, and block sets can't be used. + unsigned fgCurBBEpoch; + + unsigned GetCurBasicBlockEpoch() + { + return fgCurBBEpoch; + } + + // The number of basic blocks in the current epoch. When the blocks are renumbered, + // this is fgBBcount. As blocks are added, fgBBcount increases, fgCurBBEpochSize remains + // the same, until a new BasicBlock epoch is created, such as when the blocks are all renumbered. + unsigned fgCurBBEpochSize; + + // The number of "size_t" elements required to hold a bitset large enough for fgCurBBEpochSize + // bits. This is precomputed to avoid doing math every time BasicBlockBitSetTraits::GetArrSize() is called. + unsigned fgBBSetCountInSizeTUnits; + + void NewBasicBlockEpoch() + { + INDEBUG(unsigned oldEpochArrSize = fgBBSetCountInSizeTUnits); + + // We have a new epoch. Compute and cache the size needed for new BlockSets. + fgCurBBEpoch++; + fgCurBBEpochSize = fgBBNumMax + 1; + fgBBSetCountInSizeTUnits = unsigned(roundUp(fgCurBBEpochSize, sizeof(size_t) * 8)) / unsigned(sizeof(size_t) * 8); + +#ifdef DEBUG + // All BlockSet objects are now invalid! + fgReachabilitySetsValid = false; // the bbReach sets are now invalid! + fgEnterBlksSetValid = false; // the fgEnterBlks set is now invalid! + + if (verbose) + { + unsigned epochArrSize = BasicBlockBitSetTraits::GetArrSize(this, sizeof(size_t)); + printf("\nNew BlockSet epoch %d, # of blocks (including unused BB00): %u, bitset array size: %u (%s)", + fgCurBBEpoch, + fgCurBBEpochSize, + epochArrSize, + (epochArrSize <= 1) ? "short" : "long"); + if ((fgCurBBEpoch != 1) && ((oldEpochArrSize <= 1) != (epochArrSize <= 1))) + { + // If we're not just establishing the first epoch, and the epoch array size has changed such that we're + // going to change our bitset representation from short (just a size_t bitset) to long (a pointer to an + // array of size_t bitsets), then print that out. + printf("; NOTE: BlockSet size was previously %s!", + (oldEpochArrSize <= 1) ? "short" : "long"); + } + printf("\n"); + } +#endif // DEBUG + } + + void EnsureBasicBlockEpoch() + { + if (fgCurBBEpochSize != fgBBNumMax + 1) + { + NewBasicBlockEpoch(); + } + } + + + BasicBlock * fgNewBasicBlock (BBjumpKinds jumpKind); + void fgEnsureFirstBBisScratch(); + bool fgFirstBBisScratch(); + bool fgBBisScratch(BasicBlock* block); + + void fgExtendEHRegionBefore (BasicBlock* block); + void fgExtendEHRegionAfter (BasicBlock* block); + + BasicBlock* fgNewBBbefore (BBjumpKinds jumpKind, + BasicBlock * block, + bool extendRegion); + + BasicBlock* fgNewBBafter (BBjumpKinds jumpKind, + BasicBlock * block, + bool extendRegion); + + BasicBlock * fgNewBBinRegion (BBjumpKinds jumpKind, + unsigned tryIndex, + unsigned hndIndex, + BasicBlock * nearBlk, + bool putInFilter = false, + bool runRarely = false, + bool insertAtEnd = false); + + BasicBlock * fgNewBBinRegion (BBjumpKinds jumpKind, + BasicBlock * srcBlk, + bool runRarely = false, + bool insertAtEnd = false); + + BasicBlock* fgNewBBinRegion (BBjumpKinds jumpKind); + + BasicBlock * fgNewBBinRegionWorker(BBjumpKinds jumpKind, + BasicBlock * afterBlk, + unsigned xcptnIndex, + bool putInTryRegion); + + void fgInsertBBbefore (BasicBlock * insertBeforeBlk, + BasicBlock * newBlk); + void fgInsertBBafter (BasicBlock * insertAfterBlk, + BasicBlock * newBlk); + void fgUnlinkBlock (BasicBlock * block); + +#if OPT_BOOL_OPS // Used to detect multiple logical "not" assignments. + bool fgMultipleNots; +#endif + + bool fgModified; // True if the flow graph has been modified recently + bool fgComputePredsDone; // Have we computed the bbPreds list + bool fgCheapPredsValid; // Is the bbCheapPreds list valid? + bool fgDomsComputed; // Have we computed the dominator sets? + + bool fgHasSwitch; // any BBJ_SWITCH jumps? + bool fgHasPostfix; // any postfix ++/-- found? + unsigned fgIncrCount; // number of increment nodes found + + BlockSet fgEnterBlks; // Set of blocks which have a special transfer of control; the "entry" blocks plus EH handler begin blocks. + +#ifdef DEBUG + bool fgReachabilitySetsValid; // Are the bbReach sets valid? + bool fgEnterBlksSetValid; // Is the fgEnterBlks set valid? +#endif // DEBUG + + bool fgRemoveRestOfBlock; // true if we know that we will throw + bool fgStmtRemoved; // true if we remove statements -> need new DFA + + // There are two modes for ordering of the trees. + // - In FGOrderTree, the dominant ordering is the tree order, and the nodes contained in + // each tree and sub-tree are contiguous, and can be traversed (in gtNext/gtPrev order) + // by traversing the tree according to the order of the operands. + // - In FGOrderLinear, the dominant ordering is the linear order. Each statement is either + // a top-level statement (GTF_STMT_TOP_LEVEL), or its nodes are ordered within the previous + // top-level statement. It is an invariant that such nodes are FULLY embedded in the top- + // level statement (i.e. both the first and last node, in execution order, for the top-level + // statement DO NOT belong to one of the embedded trees). It is possible that we will want + // to relax this requirement, but it makes it easier to validate the order. + + enum FlowGraphOrder { FGOrderTree, FGOrderLinear }; + FlowGraphOrder fgOrder; + + // The following are boolean flags that keep track of the state of internal data structures + + bool fgStmtListThreaded; + bool fgCanRelocateEHRegions; // true if we are allowed to relocate the EH regions + bool fgEdgeWeightsComputed; // true after we have called fgComputeEdgeWeights + bool fgHaveValidEdgeWeights; // true if we were successful in computing all of the edge weights + bool fgSlopUsedInEdgeWeights; // true if their was some slop used when computing the edge weights + bool fgRangeUsedInEdgeWeights; // true if some of the edgeWeight are expressed in Min..Max form + bool fgNeedsUpdateFlowGraph; // true if we need to run fgUpdateFlowGraph + BasicBlock::weight_t fgCalledWeight; // count of the number of times this method was called + // This is derived from the profile data + // or is BB_UNITY_WEIGHT when we don't have profile data + +#if FEATURE_EH_FUNCLETS + bool fgFuncletsCreated; // true if the funclet creation phase has been run +#endif // FEATURE_EH_FUNCLETS + + bool fgGlobalMorph; // indicates if we are during the global morphing phase + // since fgMorphTree can be called from several places + bool fgExpandInline; // indicates that we are creating tree for the inliner + + bool impBoxTempInUse; // the temp below is valid and available + unsigned impBoxTemp; // a temporary that is used for boxing + +#ifdef DEBUG + bool jitFallbackCompile; // Are we doing a fallback compile? That is, have we executed a NO_WAY assert, + // and we are trying to compile again in a "safer", minopts mode? +#endif + +#if defined(DEBUG) + unsigned impInlinedCodeSize; +#endif + + //------------------------------------------------------------------------- + + void fgInit (); + + void fgImport (); + + void fgInline (); + + GenTreePtr fgGetCritSectOfStaticMethod(); + +#if !defined(_TARGET_X86_) + + void fgAddSyncMethodEnterExit(); + + GenTree* fgCreateMonitorTree(unsigned lvaMonitorBool, unsigned lvaThisVar, BasicBlock* block, bool enter); + + void fgConvertSyncReturnToLeave(BasicBlock* block); + +#endif // !_TARGET_X86_ + + bool fgMoreThanOneReturnBlock(); + + // The number of separate return points in the method. + unsigned fgReturnCount; + + void fgAddInternal (); + + bool fgFoldConditional (BasicBlock * block); + + void fgMorphStmts (BasicBlock * block, + bool * mult, bool * lnot, bool * loadw); + void fgMorphBlocks (); + + bool fgMorphBlockStmt (BasicBlock * block, + GenTreePtr stmt + DEBUGARG(const char * msg) ); + + void fgSetOptions (); + +#ifdef DEBUG + static fgWalkPreFn fgAssertNoQmark; + void fgPreExpandQmarkChecks (GenTreePtr expr); + void fgPostExpandQmarkChecks (); + static void fgCheckQmarkAllowedForm (GenTreePtr tree); +#endif + + IL_OFFSET fgFindBlockILOffset (BasicBlock* block); + + BasicBlock* fgSplitBlockAtBeginning (BasicBlock* curr); + BasicBlock* fgSplitBlockAtEnd (BasicBlock* curr); + BasicBlock* fgSplitBlockAfterStatement (BasicBlock* curr, GenTree *stmt); + BasicBlock* fgSplitEdge (BasicBlock* curr, BasicBlock* succ); + + GenTreeStmt* fgNewStmtFromTree (GenTreePtr tree, BasicBlock* block, + IL_OFFSETX offs); + GenTreeStmt* fgNewStmtFromTree (GenTreePtr tree); + GenTreeStmt* fgNewStmtFromTree (GenTreePtr tree, BasicBlock* block); + GenTreeStmt* fgNewStmtFromTree (GenTreePtr tree, IL_OFFSETX offs); + + GenTreePtr fgGetTopLevelQmark (GenTreePtr expr, GenTreePtr* ppDst = NULL); + void fgExpandQmarkForCastInstOf(BasicBlock* block, GenTreePtr stmt); + void fgExpandQmarkStmt (BasicBlock* block, GenTreePtr expr); + void fgExpandQmarkNodes (); + + void fgMorph (); + + // Do "simple lowering." This functionality is (conceptually) part of "general" + // lowering that is distributed between fgMorph and the lowering phase of LSRA. + void fgSimpleLowering(); + + bool fgShouldCreateAssignOp(GenTreePtr tree, bool *bReverse); + + GenTreePtr fgInitThisClass (); + + GenTreePtr fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, + CorInfoHelpFunc helper); + + GenTreePtr fgGetSharedCCtor(CORINFO_CLASS_HANDLE cls); + + void fgLocalVarLiveness(); + + void fgLocalVarLivenessInit(); + GenTreePtr fgPerStatementLocalVarLiveness(GenTreePtr startNode, + GenTreePtr relopNode, + GenTreePtr lshNode); + void fgPerBlockLocalVarLiveness(); + + VARSET_VALRET_TP fgGetHandlerLiveVars(BasicBlock *block); + + void fgLiveVarAnalysis (bool updateInternalOnly = false); + + // This is used in the liveness computation, as a temporary. When we use the + // arbitrary-length VarSet representation, it is better not to allocate a new one + // at each call. + VARSET_TP fgMarkIntfUnionVS; + + bool fgMarkIntf (VARSET_VALARG_TP varSet); + + bool fgMarkIntf (VARSET_VALARG_TP varSet1, + VARSET_VALARG_TP varSet2); + + void fgUpdateRefCntForClone(BasicBlock* addedToBlock, + GenTreePtr clonedTree); + + void fgUpdateRefCntForExtract(GenTreePtr wholeTree, + GenTreePtr keptTree); + VARSET_VALRET_TP fgComputeLife (VARSET_VALARG_TP life, + GenTreePtr startNode, + GenTreePtr endNode, + VARSET_VALARG_TP volatileVars + DEBUGARG( bool * treeModf)); + + bool fgRemoveDeadStore(GenTree** pTree, LclVarDsc* varDsc, VARSET_TP life, bool* doAgain DEBUGARG(bool* treeModf)); + + // For updating liveset during traversal AFTER fgComputeLife has completed + VARSET_VALRET_TP fgGetVarBits (GenTreePtr tree); + VARSET_VALRET_TP fgUpdateLiveSet (VARSET_VALARG_TP liveSet, + GenTreePtr tree); + + // Returns the set of live variables after endTree, + // assuming that liveSet is the set of live variables BEFORE tree. + // Requires that fgComputeLife has completed, and that tree is in the same + // statement as endTree, and that it comes before endTree in execution order + + VARSET_VALRET_TP fgUpdateLiveSet(VARSET_VALARG_TP liveSet, + GenTreePtr tree, + GenTreePtr endTree) + { + VARSET_TP VARSET_INIT(this, newLiveSet, liveSet); + while (tree != NULL && tree != endTree->gtNext) + { + VarSetOps::AssignNoCopy(this, newLiveSet, fgUpdateLiveSet(newLiveSet, tree)); + tree = tree->gtNext; + } + assert (tree == endTree->gtNext); + return newLiveSet; + } + + void fgInterBlockLocalVarLiveness(); + + // The presence of "x op= y" operations presents some difficulties for SSA: this is both a use of some SSA name of + // "x", and a def of a new SSA name for "x". The tree only has one local variable for "x", so it has to choose whether + // to treat that as the use or def. It chooses the "use", and thus the old SSA name. This map allows us to record/recover + // the "def" SSA number, given the lcl var node for "x" in such a tree. + typedef SimplerHashTable<GenTreePtr, PtrKeyFuncs<GenTree>, unsigned, DefaultSimplerHashBehavior> NodeToUnsignedMap; + NodeToUnsignedMap* m_opAsgnVarDefSsaNums; + NodeToUnsignedMap* GetOpAsgnVarDefSsaNums() + { + if (m_opAsgnVarDefSsaNums == NULL) + { + m_opAsgnVarDefSsaNums = new (getAllocator()) NodeToUnsignedMap(getAllocator()); + } + return m_opAsgnVarDefSsaNums; + } + + // Requires value numbering phase to have completed. Returns the value number ("gtVN") of the + // "tree," EXCEPT in the case of GTF_VAR_USEASG, because the tree node's gtVN member is the + // "use" VN. Performs a lookup into the map of (use asg tree -> def VN.) to return the "def's" + // VN. + inline ValueNum GetUseAsgDefVNOrTreeVN(GenTreePtr tree); + + // Requires that "lcl" has the GTF_VAR_DEF flag set. Returns the SSA number of "lcl". + // Except: assumes that lcl is a def, and if it is + // a def appearing in "lcl op= rhs" (GTF_VAR_USEASG), looks up and returns the SSA number for the "def", + // rather than the "use" SSA number recorded in the tree "lcl". + inline unsigned GetSsaNumForLocalVarDef(GenTreePtr lcl); + + // Some assignments assign to a local "indirectly": they are part of a comma expression that takes the address + // of the local (or a field thereof), assigns this address to a temp, and uses an indirection of this temp as + // the LHS of the assignment. This actually arises in exactly one situation. At the source level we assign one + // struct local to another: "s1 = s2". This becomes a copyblk. If "s2" is promoted into field variables "s2f0", + // ..."s2fn", then the copyblk will morph to a comma expression that takes the address of "s1" and does field-wise + // assignments: + // (byref addrS1 = &s1, + // *(addrS1 * offsetof(f0)) = s2f0, + // ... + // *(addrS1 * offsetof(fn)) = s2fn) + // + // It would be a shame, given the simple form at the source level, to be unable to track the values in the + // fields of "s1" after this. But "s1" does not appear in the assignments that modify it. How, then, to + // give it SSA names and value numbers? + // + // The solution is to use the side table described below to annotate each of the field-wise assignments at the + // end with an instance of the structure below, whose fields are described in the declaration. + struct IndirectAssignmentAnnotation + { + unsigned m_lclNum; // The local num that is being indirectly assigned. + FieldSeqNode* m_fieldSeq; // If the LHS of the struct assignment is itself a struct field dereference, + // as in "s0.g = s2", then "m_lclNum" would be "s0", and "m_fieldSeq" would + // be the singleton field sequence "g". The individual assignments would + // further append the fields of "s.g" to that. + bool m_isEntire; // True iff this assignment writes all of m_lclNum. (This can occur if the + // structure has a single field). + unsigned m_defSsaNum; // The new SSA number of "m_lclNum" after the assignment. + unsigned m_useSsaNum; // Only valid if "m_isEntire" is false; if so, the SSA number of "m_lclNum" before the + // assignment. + + IndirectAssignmentAnnotation(unsigned lclNum, + FieldSeqNode* fldSeq, + bool isEntire, + unsigned defSsaNum = SsaConfig::RESERVED_SSA_NUM, + unsigned useSsaNum = SsaConfig::RESERVED_SSA_NUM) + : m_lclNum(lclNum), m_fieldSeq(fldSeq), m_isEntire(isEntire), m_defSsaNum(defSsaNum), m_useSsaNum(useSsaNum) + {} + + }; + typedef SimplerHashTable<GenTreePtr, PtrKeyFuncs<GenTree>, IndirectAssignmentAnnotation*, DefaultSimplerHashBehavior> NodeToIndirAssignMap; + NodeToIndirAssignMap* m_indirAssignMap; + NodeToIndirAssignMap* GetIndirAssignMap() + { + if (m_indirAssignMap == NULL) + { + // Create a CompAllocator that labels sub-structure with CMK_IndirAssignMap, and use that for allocation. + IAllocator* ialloc = new (this, CMK_IndirAssignMap) CompAllocator(this, CMK_IndirAssignMap); + m_indirAssignMap = new (ialloc) NodeToIndirAssignMap(ialloc); + } + return m_indirAssignMap; + } + + + // Performs SSA conversion. + void fgSsaBuild(); + + // Reset any data structures to the state expected by "fgSsaBuild", so it can be run again. + void fgResetForSsa(); + + unsigned fgSsaPassesCompleted; // Number of times fgSsaBuild has been run. + + // Returns "true" iff lcl "lclNum" should be excluded from SSA. + inline bool fgExcludeFromSsa(unsigned lclNum); + + // The value numbers for this compilation. + ValueNumStore* vnStore; + public: + + ValueNumStore* GetValueNumStore() { return vnStore; } + + // Do value numbering (assign a value number to each + // tree node). + void fgValueNumber(); + + // Updates "fgCurHeap" via the assignment H[elemTypeEq][arrVN][inx][fldSeq] = rhsVN. + // Assumes that "elemTypeEq" is the (equivalence class rep) of the array element type. + // The 'indType' is the indirection type of the lhs of the assignment and will typically + // match the element type of the array or fldSeq. When this type doesn't match + // or if the fldSeq is 'NotAField' we invalidate the array contents H[elemTypeEq][arrVN] + // + void fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq, + ValueNum arrVN, + ValueNum inxVN, + FieldSeqNode* fldSeq, + ValueNum rhsVN, + var_types indType); + + // Requires that "tree" is a GT_IND marked as an array index, and that its address argument + // has been parsed to yield the other input arguments. If evaluation of the address + // can raise exceptions, those should be captured in the exception set "excVN." + // Assumes that "elemTypeEq" is the (equivalence class rep) of the array element type. + // Marks "tree" with the VN for H[elemTypeEq][arrVN][inx][fldSeq] (for the liberal VN; a new unique + // VN for the conservative VN.) Also marks the tree's argument as the address of an array element. + // The type tree->TypeGet() will typically match the element type of the array or fldSeq. + // When this type doesn't match or if the fldSeq is 'NotAField' we return a new unique VN + // + ValueNum fgValueNumberArrIndexVal(GenTreePtr tree, + CORINFO_CLASS_HANDLE elemTypeEq, + ValueNum arrVN, + ValueNum inxVN, + ValueNum excVN, + FieldSeqNode* fldSeq); + + // Requires "funcApp" to be a VNF_PtrToArrElem, and "addrXvn" to represent the exception set thrown + // by evaluating the array index expression "tree". Returns the value number resulting from + // dereferencing the array in the current heap state. If "tree" is non-null, it must be the + // "GT_IND" that does the dereference, and it is given the returned value number. + ValueNum fgValueNumberArrIndexVal(GenTreePtr tree, + struct VNFuncApp* funcApp, + ValueNum addrXvn); + + unsigned fgVNPassesCompleted; // Number of times fgValueNumber has been run. + + // Utility functions for fgValueNumber. + + // Perform value-numbering for the trees in "blk". When giving VN's to the SSA + // names defined by phi definitions at the start of "blk", "newVNsForPhis" indicates + // that these should be given new VN's, irrespective of the values of the LHS. + // If "false", then we may assume that all inputs to phi RHS's of such definitions + // have already been assigned value numbers; if they are all assigned the *same* value + // number, then the LHS SSA name gets the same VN. + void fgValueNumberBlock(BasicBlock* blk, bool newVNsForPhis); + + // Requires that "entryBlock" is the entry block of loop "loopNum", and that "loopNum" is the + // innermost loop of which "entryBlock" is the entry. Returns the value number that should be + // assumed for the heap at the start "entryBlk". + ValueNum fgHeapVNForLoopSideEffects(BasicBlock* entryBlock, unsigned loopNum); + + // Called when an operation (performed by "tree", described by "msg") may cause the global Heap to be mutated. + void fgMutateHeap (GenTreePtr tree + DEBUGARG(const char * msg) ); + + // Tree caused an update in the current heap VN. If "tree" has an associated heap SSA #, record that + // value in that SSA #. + void fgValueNumberRecordHeapSsa(GenTreePtr tree); + + // The input 'tree' is a leaf node that is a constant + // Assign the proper value number to the tree + void fgValueNumberTreeConst(GenTreePtr tree); + + // Assumes that all inputs to "tree" have had value numbers assigned; assigns a VN to tree. + // (With some exceptions: the VN of the lhs of an assignment is assigned as part of the + // assignment.) + // If "evalAsgLhsInd" is true, evaluate a GT_IND node, even if it's labeled as the LHS of + // an assignment. + void fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd = false); + + // Does value-numbering for a cast tree. + void fgValueNumberCastTree(GenTreePtr tree); + + // Does value-numbering for a call. We interpret some helper calls. + void fgValueNumberCall(GenTreeCall* call); + + // The VN of some nodes in "args" may have changed -- reassign VNs to the arg list nodes. + void fgUpdateArgListVNs(GenTreeArgList* args); + + // Does value-numbering for a helper "call" that has a VN function symbol "vnf". + void fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueNumPair vnpExc); + + // Requires "helpCall" to be a helper call. Assigns it a value number; + // we understand the semantics of some of the calls. Returns "true" if + // the call may modify the heap (we assume arbitrary memory side effects if so). + bool fgValueNumberHelperCall(GenTreeCall* helpCall); + + + // Requires "helpFunc" to be pure. Returns the corresponding VNFunc. + VNFunc fgValueNumberHelperMethVNFunc(CorInfoHelpFunc helpFunc); + + // This is the current value number for the "Heap" implicit variable while + // doing value numbering. This is the value number under the "liberal" interpretation + // of heap values; the "conservative" interpretation needs no VN, since every access of + // the heap yields an unknown value. + ValueNum fgCurHeapVN; + + + // Return a "pseudo"-class handle for an array element type. If "elemType" is TYP_STRUCT, + // requires "elemStructType" to be non-null (and to have a low-order zero). Otherwise, low order bit + // is 1, and the rest is an encoding of "elemTyp". + static CORINFO_CLASS_HANDLE EncodeElemType(var_types elemTyp, CORINFO_CLASS_HANDLE elemStructType) + { + if (elemStructType != nullptr) + { + assert(elemTyp == TYP_STRUCT || elemTyp == TYP_REF || elemTyp == TYP_BYREF || varTypeIsIntegral(elemTyp)); + assert((size_t(elemStructType) & 0x1) == 0x0); // Make sure the encoding below is valid. + return elemStructType; + } + else + { + elemTyp = varTypeUnsignedToSigned(elemTyp); + return CORINFO_CLASS_HANDLE(size_t(elemTyp) << 1 | 0x1); + } + } + // If "clsHnd" is the result of an "EncodePrim" call, returns true and sets "*pPrimType" to the + // var_types it represents. Otherwise, returns TYP_STRUCT (on the assumption that "clsHnd" is + // the struct type of the element). + static var_types DecodeElemType(CORINFO_CLASS_HANDLE clsHnd) + { + size_t clsHndVal = size_t(clsHnd); + if (clsHndVal & 0x1) + { + return var_types(clsHndVal >> 1); + } + else + { + return TYP_STRUCT; + } + } + + +#ifdef DEBUG + // Print a representation of "vnp" or "vn" on standard output. + // If "level" is non-zero, we also print out a partial expansion of the value. + void vnpPrint(ValueNumPair vnp, unsigned level); + void vnPrint (ValueNum vn, unsigned level); +#endif + + // Dominator computation member functions + // Not exposed outside Compiler +protected: + + bool fgDominate (BasicBlock *b1, BasicBlock *b2); // Return true if b1 dominates b2 + + bool fgReachable (BasicBlock *b1, BasicBlock *b2); // Returns true if block b1 can reach block b2 + + void fgComputeDoms (); // Computes the immediate dominators for each basic block in the + // flow graph. We first assume the fields bbIDom on each + // basic block are invalid. This computation is needed later + // by fgBuildDomTree to build the dominance tree structure. + // Based on: A Simple, Fast Dominance Algorithm + // by Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy + + BlockSet_ValRet_T fgGetDominatorSet (BasicBlock* block); // Returns a set of blocks that dominate the given block. + // Note: this is relatively slow compared to calling fgDominate(), + // especially if dealing with a single block versus block check. + + void fgComputeReachabilitySets(); // Compute bbReach sets. (Also sets BBF_GC_SAFE_POINT flag on blocks.) + + void fgComputeEnterBlocksSet(); // Compute the set of entry blocks, 'fgEnterBlks'. + + bool fgRemoveUnreachableBlocks(); // Remove blocks determined to be unreachable by the bbReach sets. + + void fgComputeReachability (); // Perform flow graph node reachability analysis. + + BasicBlock * fgIntersectDom (BasicBlock *a, BasicBlock *b); // Intersect two immediate dominator sets. + + void fgDfsInvPostOrder (); // In order to compute dominance using fgIntersectDom, the flow graph nodes must be + // processed in topological sort, this function takes care of that. + + void fgDfsInvPostOrderHelper (BasicBlock* block, + BlockSet& visited, + unsigned* count); + + BlockSet_ValRet_T fgDomFindStartNodes (); // Computes which basic blocks don't have incoming edges in the flow graph. Returns this as a set. + + BlockSet_ValRet_T fgDomTreeEntryNodes (BasicBlockList** domTree); // Computes which nodes in the dominance forest are root nodes. Returns this as a set. + +#ifdef DEBUG + void fgDispDomTree (BasicBlockList** domTree); // Helper that prints out the Dominator Tree in debug builds. +#endif // DEBUG + + void fgBuildDomTree (); // Once we compute all the immediate dominator sets for each node in the flow graph + // (performed by fgComputeDoms), this procedure builds the dominance tree represented + // adjacency lists. + + // In order to speed up the queries of the form 'Does A dominates B', we can perform a DFS preorder and postorder traversal of the dominance tree and the + // dominance query will become A dominates B iif preOrder(A) <= preOrder(B) && postOrder(A) >= postOrder(B) making the computation O(1). + void fgTraverseDomTree (unsigned bbNum, + BasicBlockList** domTree, + unsigned* preNum, + unsigned* postNum); + + // When the flow graph changes, we need to update the block numbers, predecessor lists, reachability sets, and dominators. + void fgUpdateChangedFlowGraph(); + +public: + + // Compute the predecessors of the blocks in the control flow graph. + void fgComputePreds (); + + // Remove all predecessor information. + void fgRemovePreds (); + + // Compute the cheap flow graph predecessors lists. This is used in some early phases + // before the full predecessors lists are computed. + void fgComputeCheapPreds(); + +private: + + void fgAddCheapPred (BasicBlock* block, + BasicBlock* blockPred); + + void fgRemoveCheapPred (BasicBlock* block, + BasicBlock* blockPred); + +public: + + enum GCPollType + { + GCPOLL_NONE, + GCPOLL_CALL, + GCPOLL_INLINE + }; + + + // Initialize the per-block variable sets (used for liveness analysis). + void fgInitBlockVarSets(); + + //true if we've gone through and created GC Poll calls. + bool fgGCPollsCreated; + void fgMarkGCPollBlocks(); + void fgCreateGCPolls(); + bool fgCreateGCPoll(GCPollType pollType, BasicBlock * block); + + // Requires that "block" is a block that returns from + // a finally. Returns the number of successors (jump targets of + // of blocks in the covered "try" that did a "LEAVE".) + unsigned fgNSuccsOfFinallyRet(BasicBlock * block); + + // Requires that "block" is a block that returns (in the sense of BBJ_EHFINALLYRET) from + // a finally. Returns its "i"th successor (jump targets of + // of blocks in the covered "try" that did a "LEAVE".) + // Requires that "i" < fgNSuccsOfFinallyRet(block). + BasicBlock * fgSuccOfFinallyRet(BasicBlock * block, unsigned i); + + private: + // Factor out common portions of the impls of the methods above. + void fgSuccOfFinallyRetWork(BasicBlock * block, unsigned i, BasicBlock ** bres, unsigned * nres); + public: + + // For many purposes, it is desirable to be able to enumerate the *distinct* targets of a switch statement, + // skipping duplicate targets. (E.g., in flow analyses that are only interested in the set of possible targets.) + // SwitchUniqueSuccSet contains the non-duplicated switch targets. + // (Code that modifies the jump table of a switch has an obligation to call Compiler::UpdateSwitchTableTarget, + // which in turn will call the "UpdateTarget" method of this type if a SwitchUniqueSuccSet has already + // been computed for the switch block. If a switch block is deleted or is transformed into a non-switch, + // we leave the entry associated with the block, but it will no longer be accessed.) + struct SwitchUniqueSuccSet + { + unsigned numDistinctSuccs; // Number of distinct targets of the switch. + BasicBlock** nonDuplicates; // Array of "numDistinctSuccs", containing all the distinct switch target successors. + + // The switch block "switchBlk" just had an entry with value "from" modified to the value "to". + // Update "this" as necessary: if "from" is no longer an element of the jump table of "switchBlk", + // remove it from "this", and ensure that "to" is a member. Use "alloc" to do any required allocation. + void UpdateTarget(IAllocator* alloc, BasicBlock* switchBlk, BasicBlock* from, BasicBlock* to); + }; + + typedef SimplerHashTable<BasicBlock*, PtrKeyFuncs<BasicBlock>, SwitchUniqueSuccSet, DefaultSimplerHashBehavior> BlockToSwitchDescMap; + + private: + // Maps BasicBlock*'s that end in switch statements to SwitchUniqueSuccSets that allow + // iteration over only the distinct successors. + BlockToSwitchDescMap* m_switchDescMap; + public: + BlockToSwitchDescMap* GetSwitchDescMap() + { + if (m_switchDescMap == NULL) + { + m_switchDescMap = new (getAllocator()) BlockToSwitchDescMap(getAllocator()); + } + return m_switchDescMap; + } + + // Requires "switchBlock" to be a block that ends in a switch. Returns + // the corresponding SwitchUniqueSuccSet. + SwitchUniqueSuccSet GetDescriptorForSwitch(BasicBlock* switchBlk); + + // The switch block "switchBlk" just had an entry with value "from" modified to the value "to". + // Update "this" as necessary: if "from" is no longer an element of the jump table of "switchBlk", + // remove it from "this", and ensure that "to" is a member. + void UpdateSwitchTableTarget(BasicBlock* switchBlk, BasicBlock* from, BasicBlock* to); + + // Remove the "SwitchUniqueSuccSet" of "switchBlk" in the BlockToSwitchDescMap. + void fgInvalidateSwitchDescMapEntry(BasicBlock* switchBlk); + + BasicBlock * fgFirstBlockOfHandler(BasicBlock* block); + + flowList * fgGetPredForBlock (BasicBlock* block, + BasicBlock* blockPred); + + flowList* fgGetPredForBlock (BasicBlock* block, + BasicBlock* blockPred, + flowList*** ptrToPred); + + flowList * fgSpliceOutPred (BasicBlock* block, + BasicBlock* blockPred); + + flowList * fgRemoveRefPred (BasicBlock* block, + BasicBlock* blockPred); + + flowList* fgRemoveAllRefPreds(BasicBlock* block, + BasicBlock* blockPred); + + flowList* fgRemoveAllRefPreds(BasicBlock* block, + flowList** ptrToPred); + + void fgRemoveBlockAsPred(BasicBlock* block); + + void fgChangeSwitchBlock(BasicBlock* oldSwitchBlock, + BasicBlock* newSwitchBlock); + + void fgReplaceSwitchJumpTarget(BasicBlock* blockSwitch, + BasicBlock* newTarget, + BasicBlock* oldTarget); + + void fgReplaceJumpTarget(BasicBlock* block, + BasicBlock* newTarget, + BasicBlock* oldTarget); + + void fgReplacePred (BasicBlock* block, + BasicBlock* oldPred, + BasicBlock* newPred); + + flowList * fgAddRefPred (BasicBlock* block, + BasicBlock* blockPred, + flowList* oldEdge = NULL, + bool initializingPreds = false); // Only set to 'true' when we are computing preds in fgComputePreds() + + void fgFindBasicBlocks (); + + bool fgIsBetterFallThrough(BasicBlock * bCur, BasicBlock * bAlt); + + bool fgCheckEHCanInsertAfterBlock(BasicBlock* blk, unsigned regionIndex, bool putInTryRegion); + + BasicBlock * fgFindInsertPoint (unsigned regionIndex, + bool putInTryRegion, + BasicBlock * startBlk, + BasicBlock * endBlk, + BasicBlock * nearBlk, + BasicBlock * jumpBlk, + bool runRarely); + + unsigned fgGetNestingLevel (BasicBlock * block, + unsigned * pFinallyNesting = NULL); + + void fgRemoveEmptyBlocks(); + + void fgRemoveLinearOrderDependencies(GenTreePtr stmt); + + void fgRemoveStmt (BasicBlock * block, + GenTreePtr stmt, + bool updateRefCnt = true); + + bool fgCheckRemoveStmt (BasicBlock * block, + GenTreePtr stmt); + + void fgCreateLoopPreHeader(unsigned lnum); + + void fgUnreachableBlock(BasicBlock * block); + + void fgRemoveJTrue (BasicBlock * block); + + BasicBlock * fgLastBBInMainFunction(); + + BasicBlock * fgEndBBAfterMainFunction(); + + void fgUnlinkRange (BasicBlock * bBeg, + BasicBlock * bEnd); + + void fgRemoveBlock (BasicBlock * block, + bool unreachable); + + bool fgCanCompactBlocks(BasicBlock * block, + BasicBlock * bNext); + + void fgCompactBlocks (BasicBlock * block, + BasicBlock * bNext); + + void fgUpdateLoopsAfterCompacting(BasicBlock * block, BasicBlock* bNext); + + BasicBlock * fgConnectFallThrough(BasicBlock * bSrc, + BasicBlock * bDst); + + bool fgRenumberBlocks(); + + bool fgExpandRarelyRunBlocks(); + + bool fgEhAllowsMoveBlock(BasicBlock * bBefore, BasicBlock * bAfter); + + void fgMoveBlocksAfter(BasicBlock * bStart, BasicBlock * bEnd, BasicBlock * insertAfterBlk); + + enum FG_RELOCATE_TYPE + { + FG_RELOCATE_TRY, // relocate the 'try' region + FG_RELOCATE_HANDLER // relocate the handler region (including the filter if necessary) + }; + BasicBlock * fgRelocateEHRange(unsigned regionIndex, FG_RELOCATE_TYPE relocateType); + +#if FEATURE_EH_FUNCLETS +#if defined(_TARGET_ARM_) + void fgClearFinallyTargetBit(BasicBlock * block); +#endif // defined(_TARGET_ARM_) + bool fgIsIntraHandlerPred(BasicBlock* predBlock, BasicBlock* block); + bool fgAnyIntraHandlerPreds(BasicBlock* block); + void fgInsertFuncletPrologBlock(BasicBlock* block); + void fgCreateFuncletPrologBlocks(); + void fgCreateFunclets(); +#else // !FEATURE_EH_FUNCLETS + bool fgRelocateEHRegions(); +#endif // !FEATURE_EH_FUNCLETS + + bool fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock* target); + + bool fgBlockEndFavorsTailDuplication(BasicBlock *block); + + bool fgBlockIsGoodTailDuplicationCandidate(BasicBlock *block); + + bool fgOptimizeFallthroughTailDup(BasicBlock* block, BasicBlock* target); + + bool fgOptimizeEmptyBlock(BasicBlock* block); + + bool fgOptimizeBranchToEmptyUnconditional(BasicBlock* block, BasicBlock* bDest); + + bool fgOptimizeBranch(BasicBlock * bJump); + + bool fgOptimizeSwitchBranches(BasicBlock * block); + + bool fgOptimizeBranchToNext(BasicBlock* block, BasicBlock* bNext, BasicBlock* bPrev); + + bool fgOptimizeSwitchJumps(); +#ifdef DEBUG + void fgPrintEdgeWeights(); +#endif + void fgComputeEdgeWeights(); + + void fgReorderBlocks (); + + void fgDetermineFirstColdBlock(); + + bool fgIsForwardBranch (BasicBlock * bJump, BasicBlock * bSrc = NULL); + + bool fgUpdateFlowGraph (bool doTailDup = false); + + void fgFindOperOrder (); + + void fgSplitMethodTrees(); + + // method that returns if you should split here + typedef bool (fgSplitPredicate)(GenTree * tree, GenTree *parent, fgWalkData *data); + + void fgSplitProcessOneTree(GenTree *tree, fgSplitPredicate pred); + + static fgWalkPreFn fgSplitHelper; + + void fgSetBlockOrder (); + + void fgRemoveReturnBlock(BasicBlock * block); + + /* Helper code that has been factored out */ + inline void fgConvertBBToThrowBB(BasicBlock * block); + + bool fgCastNeeded(GenTreePtr tree, var_types toType); + GenTreePtr fgDoNormalizeOnStore(GenTreePtr tree); + GenTreePtr fgMakeTmpArgNode(unsigned tmpVarNum); + + /* The following check for loops that don't execute calls */ + + bool fgLoopCallMarked; + + void fgLoopCallTest (BasicBlock *srcBB, + BasicBlock *dstBB); + void fgLoopCallMark (); + + void fgMarkLoopHead (BasicBlock * block); + + unsigned fgGetCodeEstimate(BasicBlock * block); + +#if XML_FLOWGRAPHS + const char * fgProcessEscapes(const char * nameIn, escapeMapping_t *map); + FILE * fgOpenXmlFlowGraphFile(bool * wbDontClose); + bool fgDumpXmlFlowGraph(); + +#endif // XML_FLOWGRAPHS + +#ifdef DEBUG + void fgDispDoms (); + void fgDispReach (); + void fgDispBBLiveness (BasicBlock * block); + void fgDispBBLiveness (); + void fgTableDispBasicBlock (BasicBlock * block, + int ibcColWidth = 0); + void fgDispBasicBlocks (BasicBlock * firstBlock, + BasicBlock * lastBlock, + bool dumpTrees); + void fgDispBasicBlocks (bool dumpTrees = false); + void fgDumpStmtTree (GenTreePtr stmt, unsigned blkNum); + void fgDumpTrees (BasicBlock* firstBlock, + BasicBlock* lastBlock); + + static fgWalkPreFn fgStress64RsltMulCB; + void fgStress64RsltMul (); + void fgDebugCheckUpdate (); + void fgDebugCheckBBlist (bool checkBBNum = false, bool checkBBRefs = true); + void fgDebugCheckBlockLinks (); + void fgDebugCheckLinks (bool morphTrees = false); + void fgDebugCheckNodeLinks (BasicBlock* block, GenTreePtr stmt); + unsigned fgDebugCheckLinearTree (BasicBlock* block, + GenTreePtr stmt, + GenTreePtr tree, + bool printNodes = false); + void fgDebugCheckLinearNodeLinks (BasicBlock* block, GenTreePtr topLevelStmt, bool printNodes = false); + void fgDebugCheckFlags (GenTreePtr tree); +#endif + + static void fgOrderBlockOps (GenTreePtr tree, + regMaskTP reg0, + regMaskTP reg1, + regMaskTP reg2, + GenTreePtr * opsPtr, // OUT + regMaskTP * regsPtr); // OUT + + static GenTreeStmt* fgFindTopLevelStmtBackwards(GenTreeStmt* stmt); + static GenTreePtr fgGetFirstNode (GenTreePtr tree); + static void fgSnipNode (GenTreeStmt* stmt, GenTreePtr node); + static void fgSnipInnerNode (GenTreePtr node); + static void fgDeleteTreeFromList(GenTreeStmt* stmt, GenTreePtr tree); + static bool fgTreeIsInStmt(GenTree* tree, GenTreeStmt* stmt); + static void fgInsertTreeInListBefore(GenTree* tree, GenTree* insertionPoint, GenTreeStmt* stmt); + static void fgInsertTreeInListAfter(GenTree* tree, GenTree* insertionPoint, GenTreeStmt* stmt); + GenTreeStmt* fgInsertTreeBeforeAsEmbedded(GenTree* tree, GenTree* before, GenTreeStmt* stmt, BasicBlock* block); + GenTreeStmt* fgInsertTreeAfterAsEmbedded(GenTree* tree, GenTree* before, GenTreeStmt* stmt, BasicBlock* block); + bool fgNodeContainsEmbeddedStatement(GenTree* tree, GenTreeStmt* topLevel); + void fgRemoveContainedEmbeddedStatements(GenTreePtr tree, GenTreeStmt* topLevel, BasicBlock* block); + bool fgStmtContainsNode(GenTreeStmt* stmt, GenTree* tree); + + inline bool fgIsInlining() { return fgExpandInline; } + + void fgTraverseRPO(); + + //--------------------- Walking the trees in the IR ----------------------- + + struct fgWalkData + { + Compiler * compiler; + fgWalkPreFn * wtprVisitorFn; + fgWalkPostFn * wtpoVisitorFn; + void * pCallbackData; // user-provided data + bool wtprLclsOnly; // whether to only visit lclvar nodes + GenTreePtr parent; // parent of current node, provided to callback + GenTreeStack *parentStack; // stack of parent nodes, if asked for +#ifdef DEBUG + bool printModified; // callback can use this +#endif + }; + + template<bool computeStack> + fgWalkResult fgWalkTreePreRec (GenTreePtr *pTree, fgWalkData *fgWalkPre); + + // general purpose tree-walker that is capable of doing pre- and post- order + // callbacks at the same time + template<bool doPreOrder, bool doPostOrder> + fgWalkResult fgWalkTreeRec (GenTreePtr *pTree, fgWalkData *fgWalkPre); + + fgWalkResult fgWalkTreePre (GenTreePtr *pTree, + fgWalkPreFn *visitor, + void *pCallBackData = NULL, + bool lclVarsOnly = false, + bool computeStack = false); + + fgWalkResult fgWalkTree (GenTreePtr *pTree, + fgWalkPreFn *preVisitor, + fgWalkPostFn *postVisitor, + void *pCallBackData = NULL); + + void fgWalkAllTreesPre (fgWalkPreFn *visitor, + void *pCallBackData); + + //----- Postorder + + template<bool computeStack> + fgWalkResult fgWalkTreePostRec (GenTreePtr *pTree, fgWalkData *fgWalkPre); + + fgWalkResult fgWalkTreePost (GenTreePtr *pTree, + fgWalkPostFn *visitor, + void *pCallBackData = NULL, + bool computeStack = false); + + // An fgWalkPreFn that looks for expressions that have inline throws in + // minopts mode. Basically it looks for tress with gtOverflowEx() or + // GTF_IND_RNGCHK. It returns WALK_ABORT if one is found. It + // returns WALK_SKIP_SUBTREES if GTF_EXCEPT is not set (assumes flags + // properly propagated to parent trees). It returns WALK_CONTINUE + // otherwise. + static fgWalkResult fgChkThrowCB (GenTreePtr * pTree, + Compiler::fgWalkData * data); + static fgWalkResult fgChkLocAllocCB(GenTreePtr * pTree, + Compiler::fgWalkData * data); + static fgWalkResult fgChkQmarkCB(GenTreePtr * pTree, + Compiler::fgWalkData * data); + + /************************************************************************** + * PROTECTED + *************************************************************************/ + +protected : + + friend class SsaBuilder; + friend struct ValueNumberState; + + //--------------------- Detect the basic blocks --------------------------- + + BasicBlock * * fgBBs; // Table of pointers to the BBs + + void fgInitBBLookup (); + BasicBlock * fgLookupBB (unsigned addr); + + void fgMarkJumpTarget (BYTE * jumpTarget, + IL_OFFSET offs); + + void fgFindJumpTargets (const BYTE * codeAddr, + IL_OFFSET codeSize, + BYTE * jumpTarget); + + void fgMarkBackwardJump (BasicBlock * startBlock, + BasicBlock * endBlock); + + void fgLinkBasicBlocks(); + + void fgMakeBasicBlocks (const BYTE * codeAddr, + IL_OFFSET codeSize, + BYTE * jumpTarget); + + void fgCheckBasicBlockControlFlow(); + + void fgControlFlowPermitted(BasicBlock* blkSrc, + BasicBlock* blkDest, + BOOL IsLeave = false /* is the src a leave block */); + + bool fgFlowToFirstBlockOfInnerTry(BasicBlock* blkSrc, + BasicBlock* blkDest, + bool sibling); + + bool fgProfileData_ILSizeMismatch; + ICorJitInfo::ProfileBuffer *fgProfileBuffer; + ULONG fgProfileBufferCount; + ULONG fgNumProfileRuns; + + unsigned fgStressBBProf() + { +#ifdef DEBUG + static ConfigDWORD fJitStressBBProf; + unsigned result = fJitStressBBProf.val(CLRConfig::INTERNAL_JitStressBBProf); + if (result == 0) + { + if (compStressCompile(STRESS_BB_PROFILE, 15)) + { + result = 1; + } + } + return result; +#else + return 0; +#endif + } + + bool fgHaveProfileData(); + bool fgGetProfileWeightForBasicBlock(IL_OFFSET offset, unsigned *weight); + + bool fgIsUsingProfileWeights() { return (fgHaveProfileData() || fgStressBBProf()); } + void fgInstrumentMethod(); + + //-------- Insert a statement at the start or end of a basic block -------- + +#ifdef DEBUG +public: + static bool fgBlockContainsStatementBounded(BasicBlock *block, GenTree* stmt, bool answerOnBoundExceeded = true); +#endif + +public: + GenTreeStmt* fgInsertStmtAtEnd (BasicBlock * block, + GenTreePtr node); +public: // Used by linear scan register allocation + GenTreeStmt* fgInsertStmtNearEnd(BasicBlock * block, + GenTreePtr node); +private: + GenTreePtr fgInsertStmtAtBeg (BasicBlock * block, + GenTreePtr stmt); + GenTreePtr fgInsertStmtAfter (BasicBlock * block, + GenTreePtr insertionPoint, + GenTreePtr stmt + ); +public: // Used by linear scan register allocation + GenTreePtr fgInsertStmtBefore(BasicBlock * block, + GenTreePtr insertionPoint, + GenTreePtr stmt + ); + void fgReplaceStmt (BasicBlock * block, + GenTreeStmt* stmt, + GenTreePtr newTree); + +private: + GenTreePtr fgInsertStmtListAfter(BasicBlock * block, + GenTreePtr stmtAfter, + GenTreePtr stmtList + ); + + GenTreePtr fgMorphSplitTree(GenTree **splitPoint, + GenTree *stmt, + BasicBlock *blk); + + // insert the given subtree 'tree' as a top level statement before 'insertionPoint'. Give it the specified source code IL offset. + GenTreeStmt* fgSpliceTreeBefore(BasicBlock* block, GenTreeStmt* insertionPoint, GenTree* tree, IL_OFFSETX ilOffset); + + // insert the given subtree as an embedded statement of parentStmt + GenTreeStmt* fgMakeEmbeddedStmt(BasicBlock *block, GenTreePtr tree, GenTreePtr parentStmt); + + // Insert the given single node before 'before'. + // Either the callee must ensure that 'before' is part of compCurStmt, + // or before->gtPrev must be non-null + void fgInsertLinearNodeBefore(GenTreePtr newNode, GenTreePtr before); + + // Create a new temporary variable to hold the result of *ppTree, + // and transform the graph accordingly. + GenTreeStmt* fgInsertEmbeddedFormTemp(GenTree** ppTree, unsigned lvaNum=BAD_VAR_NUM); + GenTree* fgInsertCommaFormTemp (GenTree** ppTree, CORINFO_CLASS_HANDLE structType = nullptr); + GenTree* fgMakeMultiUse (GenTree** ppTree); + + // After replacing oldChild with newChild, fixup the fgArgTabEntryPtr + // if it happens to be an argument to a call. + void fgFixupIfCallArg(ArrayStack<GenTree *> *parentStack, + GenTree *oldChild, + GenTree *newChild); + + //-------- Determine the order in which the trees will be evaluated ------- + + unsigned fgTreeSeqNum; + GenTree * fgTreeSeqLst; + GenTree * fgTreeSeqBeg; + + GenTree* fgSetTreeSeq (GenTree* tree, GenTree* prev = nullptr); + void fgSetTreeSeqHelper(GenTree * tree); + void fgSetTreeSeqFinish(GenTreePtr tree); + void fgSetStmtSeq (GenTree * tree); + void fgSetBlockOrder (BasicBlock * block); + + +#if FEATURE_STACK_FP_X87 + bool fgFPstLvlRedo; + void fgComputeFPlvls (GenTreePtr tree); +#endif // FEATURE_STACK_FP_X87 + + //------------------------- Morphing -------------------------------------- + + unsigned fgPtrArgCntCur; + unsigned fgPtrArgCntMax; + hashBv* fgOutgoingArgTemps; + hashBv* fgCurrentlyInUseArgTemps; + + bool compCanEncodePtrArgCntMax(); + + BasicBlock * fgRngChkTarget (BasicBlock * block, + unsigned stkDepth); + void fgSetRngChkTarget (GenTreePtr tree, + bool delay = true); + +#if REARRANGE_ADDS + void fgMoveOpsLeft (GenTreePtr tree); +#endif + + bool fgIsCommaThrow (GenTreePtr tree, + bool forFolding = false); + + bool fgIsThrow (GenTreePtr tree); + + bool fgInDifferentRegions(BasicBlock * blk1, BasicBlock * blk2); + bool fgIsBlockCold (BasicBlock * block); + + GenTreePtr fgMorphCastIntoHelper(GenTreePtr tree, + int helper, + GenTreePtr oper); + + GenTreePtr fgMorphIntoHelperCall(GenTreePtr tree, + int helper, + GenTreeArgList* args); + + GenTreePtr fgMorphStackArgForVarArgs(unsigned lclNum, var_types varType, unsigned lclOffs); + + bool fgMorphRelopToQmark (GenTreePtr tree); + + // A "MorphAddrContext" carries information from the surrounding context. If we are evaluating a byref address, + // it is useful to know whether the address will be immediately dereferenced, or whether the address value will + // be used, perhaps by passing it as an argument to a called method. This affects how null checking is done: + // for sufficiently small offsets, we can rely on OS page protection to implicitly null-check addresses that we + // know will be dereferenced. To know that reliance on implicit null checking is sound, we must further know that + // all offsets between the top-level indirection and the bottom are constant, and that their sum is sufficiently + // small; hence the other fields of MorphAddrContext. Finally, the odd structure of GT_COPYBLK, in which the second + // argument is a GT_LIST, requires us to "tell" that List node that its parent is a GT_COPYBLK, so it "knows" that + // each of its arguments should be evaluated in MACK_Ind contexts. (This would not be true for GT_LIST nodes representing + // method call argument lists.) + enum MorphAddrContextKind { + MACK_Ind, + MACK_Addr, + MACK_CopyBlock, // This is necessary so we know we have to start a new "Ind" context for each of the + // addresses in the arg list. + }; + struct MorphAddrContext { + MorphAddrContextKind m_kind; + bool m_allConstantOffsets; // Valid only for "m_kind == MACK_Ind". True iff all offsets between + // top-level indirection and here have been constants. + size_t m_totalOffset; // Valid only for "m_kind == MACK_Ind", and if "m_allConstantOffsets" is true. + // In that case, is the sum of those constant offsets. + + MorphAddrContext(MorphAddrContextKind kind) : m_kind(kind), m_allConstantOffsets(true), m_totalOffset(0) {} + }; + + // A MACK_CopyBlock context is immutable, so we can just make one of these and share it. + static MorphAddrContext s_CopyBlockMAC; + +#ifdef FEATURE_SIMD + GenTreePtr fgCopySIMDNode(GenTreeSIMD* simdNode); + GenTreePtr getSIMDStructFromField(GenTreePtr tree, var_types* baseTypeOut, unsigned* indexOut, unsigned* simdSizeOut, bool ignoreUsedInSIMDIntrinsic=false); + GenTreePtr fgMorphFieldAssignToSIMDIntrinsicSet(GenTreePtr tree); + GenTreePtr fgMorphFieldToSIMDIntrinsicGet(GenTreePtr tree); + bool fgMorphCombineSIMDFieldAssignments(BasicBlock* block, GenTreePtr stmt); + void impMarkContiguousSIMDFieldAssignments(GenTreePtr stmt); + + // fgPreviousCandidateSIMDFieldAsgStmt is only used for tracking previous simd field assignment + // in function: Complier::impMarkContiguousSIMDFieldAssignments. + GenTreePtr fgPreviousCandidateSIMDFieldAsgStmt; + +#endif // FEATURE_SIMD + GenTreePtr fgMorphArrayIndex (GenTreePtr tree); + GenTreePtr fgMorphCast (GenTreePtr tree); + GenTreePtr fgUnwrapProxy (GenTreePtr objRef); + GenTreeCall* fgMorphArgs (GenTreeCall* call); + void fgMakeOutgoingStructArgCopy(GenTreeCall* call, GenTree* args, unsigned argIndex, CORINFO_CLASS_HANDLE copyBlkClass); + void fgFixupStructReturn (GenTreePtr call); + GenTreePtr fgMorphLocalVar (GenTreePtr tree); + bool fgAddrCouldBeNull (GenTreePtr addr); + GenTreePtr fgMorphField (GenTreePtr tree, MorphAddrContext* mac); + bool fgCanFastTailCall (GenTreeCall* call); + void fgMorphTailCall (GenTreeCall* call); + static int fgEstimateCallStackSize(GenTreeCall* call); + GenTreePtr fgMorphCall (GenTreeCall* call); + GenTreePtr fgMorphCallInline (GenTreePtr call); + GenTreePtr fgOptimizeDelegateConstructor(GenTreePtr call, CORINFO_CONTEXT_HANDLE * ExactContextHnd); + GenTreePtr fgMorphLeaf (GenTreePtr tree); + void fgAssignSetVarDef (GenTreePtr tree); + GenTreePtr fgMorphOneAsgBlockOp(GenTreePtr tree); + GenTreePtr fgMorphInitBlock (GenTreePtr tree); + GenTreePtr fgMorphCopyBlock (GenTreePtr tree); + GenTreePtr fgMorphForRegisterFP(GenTreePtr tree); + GenTreePtr fgMorphSmpOp (GenTreePtr tree, MorphAddrContext* mac = NULL); + GenTreePtr fgMorphSmpOpPre (GenTreePtr tree); + GenTreePtr fgMorphDivByConst (GenTreeOp* tree); + GenTreePtr fgMorphModByConst (GenTreeOp* tree); + GenTreePtr fgMorphModToSubMulDiv(GenTreeOp* tree); + GenTreePtr fgMorphSmpOpOptional(GenTreeOp* tree); + GenTreePtr fgMorphRecognizeBoxNullable(GenTree* compare); + bool fgShouldUseMagicNumberDivide(GenTreeOp* tree); + + GenTreePtr fgMorphToEmulatedFP (GenTreePtr tree); + GenTreePtr fgMorphConst (GenTreePtr tree); +public: + GenTreePtr fgMorphTree (GenTreePtr tree, MorphAddrContext* mac = NULL); +private: +#if LOCAL_ASSERTION_PROP + void fgKillDependentAssertions (unsigned lclNum DEBUGARG(GenTreePtr tree)); +#endif + void fgMorphTreeDone (GenTreePtr tree, + GenTreePtr oldTree = NULL + DEBUG_ARG(int morphNum = 0)); + + GenTreePtr fgMorphStmt; + + unsigned fgGetBigOffsetMorphingTemp (var_types type); // We cache one temp per type to be + // used when morphing big offset. + + //----------------------- Liveness analysis ------------------------------- + + VARSET_TP fgCurUseSet; // vars used by block (before an assignment) + VARSET_TP fgCurDefSet; // vars assigned by block (before a use) + + bool fgCurHeapUse; // True iff the current basic block uses the heap before defining it. + bool fgCurHeapDef; // True iff the current basic block defines the heap. + bool fgCurHeapHavoc; // True if the current basic block is known to set the heap to a "havoc" value. + + void fgMarkUseDef(GenTreeLclVarCommon *tree, GenTree *asgdLclVar = NULL); + +#ifdef DEBUGGING_SUPPORT + void fgBeginScopeLife(VARSET_TP* inScope, VarScopeDsc* var); + void fgEndScopeLife (VARSET_TP* inScope, VarScopeDsc* var); + + void fgMarkInScope(BasicBlock * block, VARSET_VALARG_TP inScope); + void fgUnmarkInScope(BasicBlock * block, VARSET_VALARG_TP unmarkScope); + + void fgExtendDbgScopes(); + void fgExtendDbgLifetimes(); + +#ifdef DEBUG + void fgDispDebugScopes(); +#endif // DEBUG + +#endif // DEBUGGING_SUPPORT + + //------------------------------------------------------------------------- + // + // The following keeps track of any code we've added for things like array + // range checking or explicit calls to enable GC, and so on. + // +public: + enum addCodeKind + { + ACK_NONE, + ACK_RNGCHK_FAIL, // target when range check fails + ACK_PAUSE_EXEC, // target to stop (e.g. to allow GC) + ACK_DIV_BY_ZERO, // target for divide by zero (Not used on X86/X64) + ACK_ARITH_EXCPN, // target on arithmetic exception + ACK_OVERFLOW = ACK_ARITH_EXCPN, // target on overflow + ACK_COUNT + }; + + struct AddCodeDsc + { + AddCodeDsc * acdNext; + BasicBlock * acdDstBlk; // block to which we jump + unsigned acdData; + addCodeKind acdKind; // what kind of a label is this? + unsigned short acdStkLvl; + }; +private: + static unsigned acdHelper (addCodeKind codeKind); + + AddCodeDsc * fgAddCodeList; + bool fgAddCodeModf; + bool fgRngChkThrowAdded; + AddCodeDsc * fgExcptnTargetCache[ACK_COUNT]; + + BasicBlock * fgAddCodeRef (BasicBlock * srcBlk, + unsigned refData, + addCodeKind kind, + unsigned stkDepth = 0); +public: + AddCodeDsc * fgFindExcptnTarget(addCodeKind kind, + unsigned refData); +private: + bool fgIsCodeAdded (); + + bool fgIsThrowHlpBlk (BasicBlock * block); + unsigned fgThrowHlpBlkStkLevel(BasicBlock *block); + + unsigned fgBigOffsetMorphingTemps[TYP_COUNT]; + + static bool fgIsUnboundedInlineRecursion(inlExpPtr expLst, + BYTE * ilCode); + + JitInlineResult fgInvokeInlineeCompiler(GenTreeCall* call); + void fgInsertInlineeBlocks (InlineInfo * pInlineInfo); + GenTreePtr fgInlinePrependStatements(InlineInfo * inlineInfo); + +#ifdef _TARGET_ARM_ + GenTreePtr fgGetStructAsStructPtr(GenTreePtr tree); + GenTreePtr fgAssignHfaInlineeToVar(GenTreePtr child, CORINFO_CLASS_HANDLE retClsHnd); + void fgAttachHfaInlineeToAsg(GenTreePtr tree, GenTreePtr child, CORINFO_CLASS_HANDLE retClsHnd); +#endif + static fgWalkPreFn fgUpdateInlineReturnExpressionPlaceHolder; + +#ifdef DEBUG + static fgWalkPreFn fgDebugCheckInlineCandidates; +#endif + + void fgPromoteStructs(); + fgWalkResult fgMorphStructField(GenTreePtr tree, fgWalkData *fgWalkPre); + fgWalkResult fgMorphLocalField(GenTreePtr tree, fgWalkData *fgWalkPre); + void fgMarkImplicitByRefArgs(); + bool fgMorphImplicitByRefArgs(GenTreePtr tree, fgWalkData *fgWalkPre); + static fgWalkPreFn fgMarkAddrTakenLocalsPreCB; + static fgWalkPostFn fgMarkAddrTakenLocalsPostCB; + void fgMarkAddressExposedLocals(); + bool fgNodesMayInterfere(GenTree* store, GenTree* load); + + // Returns true if the type of tree is of size at least "width", or if "tree" is not a + // local variable. + bool fgFitsInOrNotLoc(GenTreePtr tree, unsigned width); + + // The given local variable, required to be a struct variable, is being assigned via + // a "lclField", to make it masquerade as an integral type in the ABI. Make sure that + // the variable is not enregistered, and is therefore not promoted independently. + void fgLclFldAssign(unsigned lclNum); + + static fgWalkPreFn gtHasLocalsWithAddrOpCB; + bool gtCanOptimizeTypeEquality(GenTreePtr tree); + bool gtIsTypeHandleToRuntimeTypeHelper(GenTreePtr tree); + bool gtIsActiveCSE_Candidate(GenTreePtr tree); + + //--------------- The following are used when copying trees --------------- + + inlExpPtr fgInlineExpList; + + int fgInlCount; + int fgInlQMarkCount; + +#ifdef DEBUG + unsigned fgInlinedCount; // Number of successful inline expansion of this method. +#endif + + bool fgIsBigOffset(size_t offset); + + // The following are used when morphing special cases of integer div/mod operations and also by codegen + bool fgIsSignedDivOptimizable(GenTreePtr divisor); + bool fgIsUnsignedDivOptimizable(GenTreePtr divisor); + bool fgIsSignedModOptimizable(GenTreePtr divisor); + bool fgIsUnsignedModOptimizable(GenTreePtr divisor); + + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Optimizer XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + + +public : + + void optInit (); + +protected : + + LclVarDsc * optIsTrackedLocal (GenTreePtr tree); + +public: + void optRemoveRangeCheck(GenTreePtr tree, GenTreePtr stmt, bool updateCSEcounts, unsigned sideEffFlags = 0, bool forceRemove = false); + bool optIsRangeCheckRemovable(GenTreePtr tree); +protected: + static fgWalkPreFn optValidRangeCheckIndex; + static fgWalkPreFn optRemoveTreeVisitor; // Helper passed to Compiler::fgWalkAllTreesPre() to decrement the LclVar usage counts + + void optRemoveTree(GenTreePtr deadTree, GenTreePtr keepList); + + /************************************************************************** + * + *************************************************************************/ + +protected: + + // Do hoisting for all loops. + void optHoistLoopCode(); + + // To represent sets of VN's that have already been hoisted in outer loops. + typedef SimplerHashTable<ValueNum, SmallPrimitiveKeyFuncs<ValueNum>, bool, DefaultSimplerHashBehavior> VNToBoolMap; + typedef VNToBoolMap VNSet; + + struct LoopHoistContext + { + private: + // The set of variables hoisted in the current loop (or nullptr if there are none). + VNSet* m_pHoistedInCurLoop; + + public: + // Value numbers of expressions that have been hoisted in parent loops in the loop nest. + VNSet m_hoistedInParentLoops; + // Value numbers of expressions that have been hoisted in the current (or most recent) loop in the nest. + // Previous decisions on loop-invariance of value numbers in the current loop. + VNToBoolMap m_curLoopVnInvariantCache; + + VNSet* GetHoistedInCurLoop(Compiler* comp) + { + if (m_pHoistedInCurLoop == nullptr) + { + m_pHoistedInCurLoop = new (comp->getAllocatorLoopHoist()) VNSet(comp->getAllocatorLoopHoist()); + } + return m_pHoistedInCurLoop; + } + + VNSet* ExtractHoistedInCurLoop() + { + VNSet* res = m_pHoistedInCurLoop; + m_pHoistedInCurLoop = nullptr; + return res; + } + + LoopHoistContext(Compiler* comp) : + m_hoistedInParentLoops(comp->getAllocatorLoopHoist()), + m_pHoistedInCurLoop(nullptr), + m_curLoopVnInvariantCache(comp->getAllocatorLoopHoist()) + {} + }; + + // Do hoisting for loop "lnum" (an index into the optLoopTable), and all loops nested within it. + // Tracks the expressions that have been hoisted by containing loops by temporary recording their + // value numbers in "m_hoistedInParentLoops". This set is not modified by the call. + void optHoistLoopNest(unsigned lnum, LoopHoistContext* hoistCtxt); + + // Do hoisting for a particular loop ("lnum" is an index into the optLoopTable.) + // Assumes that expressions have been hoisted in containing loops if their value numbers are in "m_hoistedInParentLoops". + // + void optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt); + + // Hoist all expressions in "blk" that are invariant in loop "lnum" (an index into the optLoopTable) + // outside of that loop. Exempt expressions whose value number is in "m_hoistedInParentLoops"; add VN's of hoisted + // expressions to "hoistInLoop". + void optHoistLoopExprsForBlock(BasicBlock* blk, unsigned lnum, LoopHoistContext* hoistCtxt); + + // Return true if the tree looks profitable to hoist out of loop 'lnum'. + bool optIsProfitableToHoistableTree(GenTreePtr tree,unsigned lnum); + + // Hoist all proper sub-expressions of "tree" (which occurs in "stmt", which occurs in "blk") + // that are invariant in loop "lnum" (an index into the optLoopTable) + // outside of that loop. Exempt expressions whose value number is in "hoistedInParents"; add VN's of hoisted + // expressions to "hoistInLoop". + // Returns "true" iff "tree" is loop-invariant (wrt "lnum"). + // Assumes that the value of "*firstBlockAndBeforeSideEffect" indicates that we're in the first block, and before any + // possible globally visible side effects. Assume is called in evaluation order, and updates this. + bool optHoistLoopExprsForTree(GenTreePtr tree, + unsigned lnum, + LoopHoistContext* hoistCtxt, + bool* firstBlockAndBeforeSideEffect, + bool* pHoistable); + + // Performs the hoisting 'tree' into the PreHeader for loop 'lnum' + void optHoistCandidate(GenTreePtr tree, unsigned lnum, LoopHoistContext* hoistCtxt); + + // Returns true iff the ValueNum "vn" represents a value that is loop-invariant in "lnum". + // Constants and init values are always loop invariant. + // VNPhi's connect VN's to the SSA definition, so we can know if the SSA def occurs in the loop. + bool optVNIsLoopInvariant(ValueNum vn, unsigned lnum, VNToBoolMap* recordedVNs); + + // Returns "true" iff "tree" is valid at the head of loop "lnum", in the context of the hoist substitution + // "subst". If "tree" is a local SSA var, it is valid if its SSA definition occurs outside of the loop, or + // if it is in the domain of "subst" (meaning that it's definition has been previously hoisted, with a "standin" + // local.) If tree is a constant, it is valid. Otherwise, if it is an operator, it is valid iff its children are. + bool optTreeIsValidAtLoopHead(GenTreePtr tree, unsigned lnum); + + // If "blk" is the entry block of a natural loop, returns true and sets "*pLnum" to the index of the loop + // in the loop table. + bool optBlockIsLoopEntry(BasicBlock* blk, unsigned* pLnum); + + // Records the set of "side effects" of all loops: fields (object instance and static) + // written to, and SZ-array element type equivalence classes updated. + void optComputeLoopSideEffects(); + +private: + // Requires "lnum" to be the index of an outermost loop in the loop table. Traverses the body of that loop, + // including all nested loops, and records the set of "side effects" of the loop: fields (object instance and static) + // written to, and SZ-array element type equivalence classes updated. + void optComputeLoopNestSideEffects(unsigned lnum); + + // Add the side effects of "blk" (which is required to be within a loop) to all loops of which it is a part. + void optComputeLoopSideEffectsOfBlock(BasicBlock* blk); + + // Hoist the expression "expr" out of loop "lnum". + void optPerformHoistExpr(GenTreePtr expr, + unsigned lnum); +public: + void optOptimizeBools(); +private: + GenTree * optIsBoolCond (GenTree * condBranch, + GenTree * * compPtr, + bool * boolPtr); +#ifdef DEBUG + void optOptimizeBoolsGcStress(BasicBlock * condBlock); +#endif +public : + + void optOptimizeLayout(); // Optimize the BasicBlock layout of the method + + void optOptimizeLoops(); // for "while-do" loops duplicates simple loop conditions and transforms + // the loop into a "do-while" loop + // Also finds all natural loops and records them in the loop table + + // Optionally clone loops in the loop table. + void optCloneLoops(); + + // Clone loop "loopInd" in the loop table. + void optCloneLoop(unsigned loopInd, LoopCloneContext* context); + + // Ensure that loop "loopInd" has a unique head block. (If the existing entry has + // non-loop predecessors other than the head entry, create a new, empty block that goes (only) to the entry, + // and redirects the preds of the entry to this new block.) Sets the weight of the newly created block to "ambientWeight". + void optEnsureUniqueHead(unsigned loopInd, unsigned ambientWeight); + + void optUnrollLoops (); // Unrolls loops (needs to have cost info) + +protected : + + // This enumeration describes what is killed by a call. + + enum callInterf + { + CALLINT_NONE, // no interference (most helpers) + CALLINT_REF_INDIRS, // kills GC ref indirections (SETFIELD OBJ) + CALLINT_SCL_INDIRS, // kills non GC ref indirections (SETFIELD non-OBJ) + CALLINT_ALL_INDIRS, // kills both GC ref and non GC ref indirections (SETFIELD STRUCT) + CALLINT_ALL, // kills everything (normal method call) + }; + + // A "LoopDsc" describes a ("natural") loop. We (currently) require the body of a loop to be a contiguous (in bbNext order) + // sequence of basic blocks. (At times, we may require the blocks in a loop to be "properly numbered" in bbNext order; + // we use comparisons on the bbNum to decide order.) + // The blocks that define the body are + // first <= top <= entry <= bottom . + // The "head" of the loop is a block outside the loop that has "entry" as a successor. We only support loops with a single 'head' block. + // The meanings of these blocks are given in the definitions below. Also see the picture at Compiler::optFindNaturalLoops(). + struct LoopDsc + { + BasicBlock * lpHead; // HEAD of the loop (not part of the looping of the loop) -- has ENTRY as a successor. + BasicBlock * lpFirst; // FIRST block (in bbNext order) reachable within this loop. (May be part of a nested loop, but not the outer loop.) + BasicBlock * lpTop; // loop TOP (the back edge from lpBottom reaches here) (in most cases FIRST and TOP are the same) + BasicBlock * lpEntry; // the ENTRY in the loop (in most cases TOP or BOTTOM) + BasicBlock * lpBottom; // loop BOTTOM (from here we have a back edge to the TOP) + BasicBlock * lpExit; // if a single exit loop this is the EXIT (in most cases BOTTOM) + + callInterf lpAsgCall; // "callInterf" for calls in the loop + ALLVARSET_TP lpAsgVars; // set of vars assigned within the loop (all vars, not just tracked) + varRefKinds lpAsgInds:8;// set of inds modified within the loop + + unsigned short lpFlags; // Mask of the LPFLG_* constants + + unsigned char lpExitCnt; // number of exits from the loop + + unsigned char lpParent; // The index of the most-nested loop that completely contains this one, + // or else BasicBlock::NOT_IN_LOOP if no such loop exists. + unsigned char lpChild; // The index of a nested loop, or else BasicBlock::NOT_IN_LOOP if no child exists. + // (Actually, an "immediately" nested loop -- + // no other child of this loop is a parent of lpChild.) + unsigned char lpSibling; // The index of another loop that is an immediate child of lpParent, + // or else BasicBlock::NOT_IN_LOOP. One can enumerate all the children of a loop + // by following "lpChild" then "lpSibling" links. + +#define LPFLG_DO_WHILE 0x0001 // it's a do-while loop (i.e ENTRY is at the TOP) +#define LPFLG_ONE_EXIT 0x0002 // the loop has only one exit + +#define LPFLG_ITER 0x0004 // for (i = icon or lclVar; test_condition(); i++) +#define LPFLG_HOISTABLE 0x0008 // the loop is in a form that is suitable for hoisting expressions +#define LPFLG_CONST 0x0010 // for (i=icon;i<icon;i++){ ... } - constant loop + +#define LPFLG_VAR_INIT 0x0020 // iterator is initialized with a local var (var # found in lpVarInit) +#define LPFLG_CONST_INIT 0x0040 // iterator is initialized with a constant (found in lpConstInit) + +#define LPFLG_VAR_LIMIT 0x0100 // iterator is compared with a local var (var # found in lpVarLimit) +#define LPFLG_CONST_LIMIT 0x0200 // iterator is compared with a constant (found in lpConstLimit) +#define LPFLG_ARRLEN_LIMIT 0x0400 // iterator is compared with a.len or a[i].len (found in lpArrLenLimit) + +#define LPFLG_HAS_PREHEAD 0x0800 // lpHead is known to be a preHead for this loop +#define LPFLG_REMOVED 0x1000 // has been removed from the loop table (unrolled or optimized away) +#define LPFLG_DONT_UNROLL 0x2000 // do not unroll this loop + +#define LPFLG_ASGVARS_YES 0x4000 // "lpAsgVars" has been computed +#define LPFLG_ASGVARS_INC 0x8000 // "lpAsgVars" is incomplete -- vars beyond those representable in an AllVarSet tyep are assigned to. + + + bool lpLoopHasHeapHavoc; // The loop contains an operation that we assume has arbitrary heap side effects. + // If this is set, the fields below may not be accurate (since they become irrelevant.) + bool lpContainsCall; // True if executing the loop body *may* execute a call + + VARSET_TP lpVarInOut; // The set of variables that are IN or OUT during the execution of this loop + VARSET_TP lpVarUseDef; // The set of variables that are USE or DEF during the execution of this loop + + int lpHoistedExprCount; // The register count for the non-FP expressions from inside this loop that have been hoisted + int lpLoopVarCount; // The register count for the non-FP LclVars that are read/written inside this loop + int lpVarInOutCount; // The register count for the non-FP LclVars that are alive inside or accross this loop + + int lpHoistedFPExprCount; // The register count for the FP expressions from inside this loop that have been hoisted + int lpLoopVarFPCount; // The register count for the FP LclVars that are read/written inside this loop + int lpVarInOutFPCount; // The register count for the FP LclVars that are alive inside or accross this loop + + typedef SimplerHashTable<CORINFO_FIELD_HANDLE, PtrKeyFuncs<struct CORINFO_FIELD_STRUCT_>, bool, DefaultSimplerHashBehavior> FieldHandleSet; + FieldHandleSet* lpFieldsModified; // This has entries (mappings to "true") for all static field and object instance fields modified + // in the loop. + + typedef SimplerHashTable<CORINFO_CLASS_HANDLE, PtrKeyFuncs<struct CORINFO_CLASS_STRUCT_>, bool, DefaultSimplerHashBehavior> ClassHandleSet; + ClassHandleSet* lpArrayElemTypesModified; // Bits set indicate the set of sz array element types such that arrays of that type are modified + // in the loop. + + // Adds the variable liveness information for 'blk' to 'this' LoopDsc + void AddVariableLiveness(Compiler* comp, BasicBlock* blk); + + inline void AddModifiedField(Compiler* comp, CORINFO_FIELD_HANDLE fldHnd); + // This doesn't *always* take a class handle -- it can also take primitive types, encoded as class handles (shifted left, with a low-order bit set to distinguish.) + // Use the {Encode/Decode}ElemType methods to construct/destruct these. + inline void AddModifiedElemType(Compiler* comp, CORINFO_CLASS_HANDLE structHnd); + + + /* The following values are set only for iterator loops, i.e. has the flag LPFLG_ITER set */ + + GenTreePtr lpIterTree; // The "i <op>= const" tree + unsigned lpIterVar (); // iterator variable # + int lpIterConst(); // the constant with which the iterator is incremented + genTreeOps lpIterOper (); // the type of the operation on the iterator (ASG_ADD, ASG_SUB, etc.) + void VERIFY_lpIterTree(); + + var_types lpIterOperType();// For overflow instructions + + union + { + int lpConstInit; // initial constant value of iterator : Valid if LPFLG_CONST_INIT + unsigned lpVarInit; // initial local var number to which we initialize the iterator : Valid if LPFLG_VAR_INIT + }; + + /* The following is for LPFLG_ITER loops only (i.e. the loop condition is "i RELOP const or var" */ + + GenTreePtr lpTestTree; // pointer to the node containing the loop test + genTreeOps lpTestOper(); // the type of the comparison between the iterator and the limit (GT_LE, GT_GE, etc.) + void VERIFY_lpTestTree(); + + bool lpIsReversed(); // true if the iterator node is the second operand in the loop condition + GenTreePtr lpIterator(); // the iterator node in the loop test + GenTreePtr lpLimit(); // the limit node in the loop test + + int lpConstLimit(); // limit constant value of iterator - loop condition is "i RELOP const" : Valid if LPFLG_CONST_LIMIT + unsigned lpVarLimit(); // the lclVar # in the loop condition ( "i RELOP lclVar" ) : Valid if LPFLG_VAR_LIMIT + bool lpArrLenLimit(Compiler* comp, ArrIndex* index); // The array length in the loop condition ( "i RELOP arr.len" or "i RELOP arr[i][j].len" ) : Valid if LPFLG_ARRLEN_LIMIT + + // Returns "true" iff "*this" contains the blk. + bool lpContains(BasicBlock* blk) + { + return lpFirst->bbNum <= blk->bbNum && blk->bbNum <= lpBottom->bbNum; + } + // Returns "true" iff "*this" (properly) contains the range [first, bottom] (allowing firsts + // to be equal, but requiring bottoms to be different.) + bool lpContains(BasicBlock* first, BasicBlock* bottom) + { + return lpFirst->bbNum <= first->bbNum && bottom->bbNum < lpBottom->bbNum; + } + + // Returns "true" iff "*this" (properly) contains "lp2" (allowing firsts to be equal, but requiring + // bottoms to be different.) + bool lpContains(const LoopDsc& lp2) + { + return lpContains(lp2.lpFirst, lp2.lpBottom); + } + + // Returns "true" iff "*this" is (properly) contained by the range [first, bottom] + // (allowing firsts to be equal, but requiring bottoms to be different.) + bool lpContainedBy(BasicBlock* first, BasicBlock* bottom) + { + return first->bbNum <= lpFirst->bbNum && lpBottom->bbNum < bottom->bbNum; + } + + // Returns "true" iff "*this" is (properly) contained by "lp2" + // (allowing firsts to be equal, but requiring bottoms to be different.) + bool lpContainedBy(const LoopDsc& lp2) + { + return lpContains(lp2.lpFirst, lp2.lpBottom); + } + + // Returns "true" iff "*this" is disjoint from the range [top, bottom]. + bool lpDisjoint(BasicBlock* first, BasicBlock* bottom) + { + return bottom->bbNum < lpFirst->bbNum || lpBottom->bbNum < first->bbNum; + } + // Returns "true" iff "*this" is disjoint from "lp2". + bool lpDisjoint(const LoopDsc& lp2) + { + return lpDisjoint(lp2.lpFirst, lp2.lpBottom); + } + // Returns "true" iff the loop is well-formed (see code for defn). + bool lpWellFormed() + { + return lpFirst->bbNum <= lpTop->bbNum + && lpTop->bbNum <= lpEntry->bbNum + && lpEntry->bbNum <= lpBottom->bbNum + && (lpHead->bbNum < lpTop->bbNum || lpHead->bbNum > lpBottom->bbNum); + } + + }; + + bool fgMightHaveLoop(); // returns true if there are any backedges + bool fgHasLoops; // True if this method has any loops, set in fgComputeReachability + LoopDsc optLoopTable[MAX_LOOP_NUM]; // loop descriptor table + unsigned char optLoopCount; // number of tracked loops + unsigned optCallCount; // number of calls made in the method + unsigned optIndirectCallCount; // number of virtual, interface and indirect calls made in the method + unsigned optNativeCallCount; // number of Pinvoke/Native calls made in the method + unsigned optLoopsCloned; // number of loops cloned in the current method. + +#ifdef DEBUG + unsigned optFindLoopNumberFromBeginBlock(BasicBlock *begBlk); + void optPrintLoopInfo(unsigned loopNum, + BasicBlock * lpHead, + BasicBlock * lpFirst, + BasicBlock * lpTop, + BasicBlock * lpEntry, + BasicBlock * lpBottom, + unsigned char lpExitCnt, + BasicBlock * lpExit, + unsigned parentLoop = BasicBlock::NOT_IN_LOOP + ); + void optPrintLoopInfo(unsigned lnum); + void optPrintLoopRecording(unsigned lnum); + + void optCheckPreds (); +#endif + + void optSetBlockWeights (); + + void optMarkLoopBlocks (BasicBlock *begBlk, + BasicBlock *endBlk, + bool excludeEndBlk); + + void optUnmarkLoopBlocks(BasicBlock *begBlk, + BasicBlock *endBlk); + + void optUpdateLoopsBeforeRemoveBlock(BasicBlock * block, + bool skipUnmarkLoop = false); + + bool optIsLoopTestEvalIntoTemp(GenTreePtr test, GenTreePtr* newTest); + unsigned optIsLoopIncrTree(GenTreePtr incr); + bool optCheckIterInLoopTest(unsigned loopInd, GenTreePtr test, BasicBlock* from, BasicBlock* to, unsigned iterVar); + bool optComputeIterInfo(GenTreePtr incr, BasicBlock* from, BasicBlock* to, unsigned* pIterVar); + bool optPopulateInitInfo(unsigned loopInd, GenTreePtr init, unsigned iterVar); + bool optExtractInitTestIncr(BasicBlock* head, BasicBlock* bottom, BasicBlock* exit, GenTreePtr* ppInit, GenTreePtr* ppTest, GenTreePtr* ppIncr); + + + void optRecordLoop (BasicBlock * head, + BasicBlock * first, + BasicBlock * top, + BasicBlock * entry, + BasicBlock * bottom, + BasicBlock * exit, + unsigned char exitCnt); + + void optFindNaturalLoops(); + + // Ensures that all the loops in the loop nest rooted at "loopInd" (an index into the loop table) are 'canonical' -- + // each loop has a unique "top." Returns "true" iff the flowgraph has been modified. + bool optCanonicalizeLoopNest(unsigned char loopInd); + + // Ensures that the loop "loopInd" (an index into the loop table) is 'canonical' -- it has a unique "top," + // unshared with any other loop. Returns "true" iff the flowgraph has been modified + bool optCanonicalizeLoop(unsigned char loopInd); + + // Requires "l1" to be a valid loop table index, and not "BasicBlock::NOT_IN_LOOP". Requires "l2" to be + // a valid loop table index, or else "BasicBlock::NOT_IN_LOOP". Returns true + // iff "l2" is not NOT_IN_LOOP, and "l1" contains "l2". + bool optLoopContains(unsigned l1, unsigned l2); + + // Requires "loopInd" to be a valid index into the loop table. + // Updates the loop table by changing loop "loopInd", whose head is required + // to be "from", to be "to". Also performs this transformation for any + // loop nested in "loopInd" that shares the same head as "loopInd". + void optUpdateLoopHead(unsigned loopInd, BasicBlock* from, BasicBlock* to); + + // Updates the successors of "blk": if "blk2" is a successor of "blk", and there is a mapping for "blk2->blk3" in "redirectMap", + // change "blk" so that "blk3" is this successor. + void optRedirectBlock(BasicBlock* blk, BlockToBlockMap* redirectMap); + + // Marks the containsCall information to "lnum" and any parent loops. + void AddContainsCallAllContainingLoops(unsigned lnum); + // Adds the variable liveness information from 'blk' to "lnum" and any parent loops. + void AddVariableLivenessAllContainingLoops(unsigned lnum, BasicBlock * blk); + // Adds "fldHnd" to the set of modified fields of "lnum" and any parent loops. + void AddModifiedFieldAllContainingLoops(unsigned lnum, CORINFO_FIELD_HANDLE fldHnd); + // Adds "elemType" to the set of modified array element types of "lnum" and any parent loops. + void AddModifiedElemTypeAllContainingLoops(unsigned lnum, CORINFO_CLASS_HANDLE elemType); + + // Requires that "from" and "to" have the same "bbJumpKind" (perhaps because "to" is a clone + // of "from".) Copies the jump destination from "from" to "to". + void optCopyBlkDest(BasicBlock* from, BasicBlock* to); + + + // The depth of the loop described by "lnum" (an index into the loop table.) (0 == top level) + unsigned optLoopDepth(unsigned lnum) + { + unsigned par = optLoopTable[lnum].lpParent; + if (par == BasicBlock::NOT_IN_LOOP) return 0; + else return 1 + optLoopDepth(par); + } + + void fgOptWhileLoop (BasicBlock * block); + + bool optComputeLoopRep (int constInit, + int constLimit, + int iterInc, + genTreeOps iterOper, + var_types iterType, + genTreeOps testOper, + bool unsignedTest, + bool dupCond, + unsigned * iterCount); +#if FEATURE_STACK_FP_X87 + +public: + VARSET_TP optAllFloatVars; // mask of all tracked FP variables + VARSET_TP optAllFPregVars; // mask of all enregistered FP variables + VARSET_TP optAllNonFPvars; // mask of all tracked non-FP variables +#endif // FEATURE_STACK_FP_X87 + +private: + static fgWalkPreFn optIsVarAssgCB; +protected: + + bool optIsVarAssigned(BasicBlock * beg, + BasicBlock * end, + GenTreePtr skip, + unsigned var); + + bool optIsVarAssgLoop(unsigned lnum, + unsigned var); + + int optIsSetAssgLoop(unsigned lnum, + ALLVARSET_VALARG_TP vars, + varRefKinds inds = VR_NONE); + + bool optNarrowTree (GenTreePtr tree, + var_types srct, + var_types dstt, + bool doit); + + /************************************************************************** + * Optimization conditions + *************************************************************************/ + + bool optFastCodeOrBlendedLoop(BasicBlock::weight_t bbWeight); + bool optPentium4(void); + bool optAvoidIncDec(BasicBlock::weight_t bbWeight); + bool optAvoidIntMult(void); + +#if FEATURE_ANYCSE + +protected : + + // The following is the upper limit on how many expressions we'll keep track + // of for the CSE analysis. + // + static const unsigned MAX_CSE_CNT = EXPSET_SZ; + + static const int MIN_CSE_COST = IND_COST_EX; + + + /* Generic list of nodes - used by the CSE logic */ + + struct treeLst + { + treeLst * tlNext; + GenTreePtr tlTree; + }; + + typedef struct treeLst * treeLstPtr; + + struct treeStmtLst + { + treeStmtLst * tslNext; + GenTreePtr tslTree; // tree node + GenTreePtr tslStmt; // statement containing the tree + BasicBlock * tslBlock; // block containing the statement + }; + + typedef struct treeStmtLst * treeStmtLstPtr; + + + // The following logic keeps track of expressions via a simple hash table. + + struct CSEdsc + { + CSEdsc * csdNextInBucket; // used by the hash table + + unsigned csdHashValue; // the orginal hashkey + + unsigned csdIndex; // 1..optCSECandidateCount + char csdLiveAcrossCall; // 0 or 1 + + unsigned short csdDefCount; // definition count + unsigned short csdUseCount; // use count (excluding the implicit uses at defs) + + unsigned csdDefWtCnt; // weighted def count + unsigned csdUseWtCnt; // weighted use count (excluding the implicit uses at defs) + + GenTreePtr csdTree; // treenode containing the 1st occurance + GenTreePtr csdStmt; // stmt containing the 1st occurance + BasicBlock * csdBlock; // block containing the 1st occurance + + treeStmtLstPtr csdTreeList; // list of matching tree nodes: head + treeStmtLstPtr csdTreeLast; // list of matching tree nodes: tail + }; + + static const size_t s_optCSEhashSize; + CSEdsc * * optCSEhash; + CSEdsc * * optCSEtab; + + void optCSEstop (); + + CSEdsc * optCSEfindDsc (unsigned index); + void optUnmarkCSE (GenTreePtr tree); + + // user defined callback data for the tree walk function optCSE_MaskHelper() + struct optCSE_MaskData + { + EXPSET_TP CSE_defMask; + EXPSET_TP CSE_useMask; + }; + + // Treewalk helper for optCSE_DefMask and optCSE_UseMask + static fgWalkPreFn optCSE_MaskHelper; + + // This function walks all the node for an given tree + // and return the mask of CSE definitions and uses for the tree + // + void optCSE_GetMaskData (GenTreePtr tree, optCSE_MaskData* pMaskData); + + // Given a binary tree node return true if it is safe to swap the order of evaluation for op1 and op2 + bool optCSE_canSwap (GenTreePtr tree); + + static fgWalkPostFn optPropagateNonCSE; + static fgWalkPreFn optHasNonCSEChild; + + static fgWalkPreFn optUnmarkCSEs; + + static int __cdecl optCSEcostCmpEx(const void *op1, const void *op2); + static int __cdecl optCSEcostCmpSz(const void *op1, const void *op2); + + void optCleanupCSEs(); + + +#ifdef DEBUG + void optEnsureClearCSEInfo(); +#endif // DEBUG + +#endif // FEATURE_ANYCSE + +#if FEATURE_VALNUM_CSE + /************************************************************************** + * Value Number based CSEs + *************************************************************************/ + +public : + + void optOptimizeValnumCSEs(); + +protected : + + void optValnumCSE_Init (); + unsigned optValnumCSE_Index(GenTreePtr tree, GenTreePtr stmt); + unsigned optValnumCSE_Locate(); + void optValnumCSE_InitDataFlow(); + void optValnumCSE_DataFlow(); + void optValnumCSE_Availablity(); + void optValnumCSE_Heuristic(); + void optValnumCSE_UnmarkCSEs(GenTreePtr deadTree, GenTreePtr keepList); + + +#endif // FEATURE_VALNUM_CSE + +#if FEATURE_ANYCSE + bool optDoCSE; // True when we have found a duplicate CSE tree + bool optValnumCSE_phase; // True when we are executing the optValnumCSE_phase + unsigned optCSECandidateTotal; // Grand total of CSE candidates for both Lexical and ValNum + unsigned optCSECandidateCount; // Count of CSE's candidates, reset for Lexical and ValNum CSE's + unsigned optCSEstart; // The first local variable number that is a CSE + unsigned optCSEcount; // The total count of CSE's introduced. + unsigned optCSEweight; // The weight of the current block when we are + // scanning for CSE expressions + + bool optIsCSEcandidate (GenTreePtr tree); + + // lclNumIsTrueCSE returns true if the LclVar was introduced by the CSE phase of the compiler + // + bool lclNumIsTrueCSE(unsigned lclNum) const + { + return ((optCSEcount > 0) && (lclNum >= optCSEstart) && (lclNum < optCSEstart+optCSEcount)); + } + + // lclNumIsCSE returns true if the LclVar should be treated like a CSE with regards to constant prop. + // + bool lclNumIsCSE(unsigned lclNum) const + { + return lvaTable[lclNum].lvIsCSE; + } + +#ifdef DEBUG + bool optConfigDisableCSE(bool lexicalCSE); + bool optConfigDisableCSE2(); +#endif + void optOptimizeCSEs(); + + +#endif // FEATURE_ANYCSE + + struct isVarAssgDsc + { + GenTreePtr ivaSkip; +#ifdef DEBUG + void * ivaSelf; +#endif + unsigned ivaVar; // Variable we are interested in, or -1 + ALLVARSET_TP ivaMaskVal; // Set of variables assigned to. This is a set of all vars, not tracked vars. + bool ivaMaskIncomplete; // Variables not representable in ivaMaskVal were assigned to. + varRefKinds ivaMaskInd; // What kind of indirect assignments are there? + callInterf ivaMaskCall; // What kind of calls are there? + }; + + static callInterf optCallInterf (GenTreePtr call); + +public: + // VN based copy propagation. + typedef ArrayStack<GenTreePtr> GenTreePtrStack; + typedef SimplerHashTable<unsigned, SmallPrimitiveKeyFuncs<unsigned>, GenTreePtrStack*, DefaultSimplerHashBehavior> LclNumToGenTreePtrStack; + + // Copy propagation functions. + void optCopyProp(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree, LclNumToGenTreePtrStack* curSsaName); + void optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName); + void optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName); + int optCopyProp_LclVarScore(LclVarDsc* lclVarDsc, LclVarDsc* copyVarDsc, bool preferOp2); + void optVnCopyProp(); + +#if ASSERTION_PROP + /************************************************************************** + * Value/Assertion propagation + *************************************************************************/ +public: + // The following is the upper limit on how many assertions we'll keep track + // of during global assertion propagation. + // + static const unsigned MAX_ASSERTION_CNT = EXPSET_SZ; + + // Data structures for assertion prop + enum optAssertionKind { OAK_INVALID, + OAK_EQUAL, + OAK_NOT_EQUAL, + OAK_SUBRANGE, + OAK_NO_THROW }; + + enum optOp1Kind { O1K_INVALID, + O1K_LCLVAR, + O1K_ARR_BND, + O1K_ARRLEN_OPER_BND, + O1K_ARRLEN_LOOP_BND, + O1K_EXACT_TYPE, + O1K_SUBTYPE }; + + enum optOp2Kind { O2K_INVALID, + O2K_LCLVAR_COPY, + O2K_IND_CNS_INT, + O2K_CONST_INT, + O2K_CONST_LONG, + O2K_CONST_DOUBLE, + O2K_ARR_LEN, + O2K_SUBRANGE }; + struct AssertionDsc + { + optAssertionKind assertionKind; + struct SsaVar + { + unsigned lclNum; // assigned to or property of this local var number + unsigned ssaNum; + }; + struct ArrBnd + { + ValueNum vnIdx; + ValueNum vnLen; + }; + struct AssertionDscOp1 + { + optOp1Kind kind; // a normal LclVar, or Exact-type or Subtype + ValueNum vn; + union + { + SsaVar lcl; + ArrBnd bnd; + }; + } op1; + struct AssertionDscOp2 + { + optOp2Kind kind; // a const or copy assignment + ValueNum vn; + struct IntVal + { + ssize_t iconVal; // integer + unsigned iconFlags; // gtFlags + }; + struct Range // integer subrange + { + ssize_t loBound; + ssize_t hiBound; + }; + union + { + SsaVar lcl; + IntVal u1; + __int64 lconVal; + double dconVal; + Range u2; + }; + } op2; + + bool IsArrLenArithBound() + { + return ((assertionKind == OAK_EQUAL || assertionKind == OAK_NOT_EQUAL) && op1.kind == O1K_ARRLEN_OPER_BND); + } + bool IsArrLenBound() + { + return ((assertionKind == OAK_EQUAL || assertionKind == OAK_NOT_EQUAL) && op1.kind == O1K_ARRLEN_LOOP_BND); + } + bool IsBoundsCheckNoThrow() + { + return ((assertionKind == OAK_NO_THROW) && + (op1.kind == O1K_ARR_BND)); + } + + bool IsCopyAssertion() + { + return ((assertionKind == OAK_EQUAL) && + (op1.kind == O1K_LCLVAR) && + (op2.kind == O2K_LCLVAR_COPY)); + } + + static bool SameKind(AssertionDsc* a1, AssertionDsc* a2) + { + return a1->assertionKind == a2->assertionKind && + a1->op1.kind == a2->op1.kind && + a1->op2.kind == a2->op2.kind; + } + + static bool ComplementaryKind(optAssertionKind kind, optAssertionKind kind2) + { + if (kind == OAK_EQUAL) + { + return kind2 == OAK_NOT_EQUAL; + } + else if (kind == OAK_NOT_EQUAL) + { + return kind2 == OAK_EQUAL; + } + return false; + } + + static ssize_t GetLowerBoundForIntegralType(var_types type) + { + switch (type) + { + case TYP_BYTE: + return SCHAR_MIN; + case TYP_SHORT: + return SHRT_MIN; + case TYP_CHAR: + return SCHAR_MIN; + case TYP_INT: + return INT_MIN; + case TYP_BOOL: + case TYP_UBYTE: + case TYP_USHORT: + case TYP_UINT: + return 0; + default: + unreached(); + } + } + static ssize_t GetUpperBoundForIntegralType(var_types type) + { + switch (type) + { + case TYP_BOOL: + return 1; + case TYP_BYTE: + return SCHAR_MAX; + case TYP_SHORT: + return SHRT_MAX; + case TYP_CHAR: + return SCHAR_MAX; + case TYP_INT: + return INT_MAX; + case TYP_UBYTE: + return UCHAR_MAX; + case TYP_USHORT: + return USHRT_MAX; + case TYP_UINT: + return UINT_MAX; + default: + unreached(); + } + } + + bool HasSameOp1(AssertionDsc* that, bool vnBased) + { + return (op1.kind == that->op1.kind) && + ((vnBased && (op1.vn == that->op1.vn)) || + (!vnBased && (op1.lcl.lclNum == that->op1.lcl.lclNum))); + } + + bool HasSameOp2(AssertionDsc* that, bool vnBased) + { + if (op2.kind != that->op2.kind) + { + return false; + } + switch (op2.kind) + { + case O2K_IND_CNS_INT: + case O2K_CONST_INT: + return ((op2.u1.iconVal == that->op2.u1.iconVal) && + (op2.u1.iconFlags == that->op2.u1.iconFlags)); + + case O2K_CONST_LONG: + return (op2.lconVal == that->op2.lconVal); + + case O2K_CONST_DOUBLE: + // exact match because of positive and negative zero. + return (memcmp(&op2.dconVal, &that->op2.dconVal, sizeof(double)) == 0); + + case O2K_LCLVAR_COPY: + case O2K_ARR_LEN: + return (op2.lcl.lclNum == that->op2.lcl.lclNum) && + (!vnBased || op2.lcl.ssaNum == that->op2.lcl.ssaNum); + + case O2K_SUBRANGE: + return ((op2.u2.loBound == that->op2.u2.loBound) && + (op2.u2.hiBound == that->op2.u2.hiBound)); + } + return false; + } + + + bool Complementary(AssertionDsc* that, bool vnBased) + { + return ComplementaryKind(assertionKind, that->assertionKind) && + HasSameOp1(that, vnBased) && HasSameOp2(that, vnBased); + } + + bool Equals(AssertionDsc* that, bool vnBased) + { + return (assertionKind == that->assertionKind) && + HasSameOp1(that, vnBased) && HasSameOp2(that, vnBased); + } + }; + +protected: + static fgWalkPreFn optAddCopiesCallback; + unsigned optAddCopyLclNum; + GenTreePtr optAddCopyAsgnNode; + + bool optLocalAssertionProp; // indicates that we are performing local assertion prop + bool optAssertionPropagated; // set to true if we modified the trees + bool optAssertionPropagatedCurrentStmt; +#ifdef DEBUG + GenTreePtr optAssertionPropCurrentTree; +#endif + AssertionDsc optAssertionTabPrivate[MAX_ASSERTION_CNT]; // table that holds info about value assignments + unsigned optAssertionCount; // total number of assertions in the assertion table + +public : + + unsigned GetAssertionCount() + { + return optAssertionCount; + } + EXPSET_TP* bbJtrueAssertionOut; + typedef SimplerHashTable<ValueNum, SmallPrimitiveKeyFuncs<ValueNum>, EXPSET_TP, DefaultSimplerHashBehavior> ValueNumToAssertsMap; + ValueNumToAssertsMap* optValueNumToAsserts; + + static const int NO_ASSERTION_INDEX = 0; + + // Assertion prop helpers. + AssertionDsc* optGetAssertion(unsigned assertIndex); + inline EXPSET_TP optGetAssertionBit(unsigned assertIndex) + { + assert((assertIndex > 0) && (assertIndex <= MAX_ASSERTION_CNT)); + return ((EXPSET_TP) 1 << (assertIndex - 1)); + }; + void optAssertionInit(bool isLocalProp); +#if LOCAL_ASSERTION_PROP + void optAssertionReset(unsigned limit); + void optAssertionRemove(unsigned index); +#endif + + // Assertion prop data flow functions. + void optAssertionPropMain(); + void optInitAssertionDataflowFlags(EXPSET_TP* jumpDestOut, EXPSET_TP* jumpDestGen); + GenTreePtr optVNAssertionPropCurStmt(BasicBlock* block, GenTreePtr stmt); + void optComputeAssertionGen(EXPSET_TP* jumpDestGen); + bool optIsTreeKnownIntValue(bool vnBased, GenTreePtr tree, ssize_t* pConstant, unsigned* pIconFlags); + + // Assertion Gen functions. + void optAssertionGen (GenTreePtr tree); + unsigned optAssertionGenPhiDefn(GenTreePtr tree); + unsigned optCreateJTrueArrayAssertion(GenTreePtr tree); + unsigned optAssertionGenJtrue (GenTreePtr tree); + unsigned optCreateJtrueAssertions(GenTreePtr op1, GenTreePtr op2, Compiler::optAssertionKind assertionKind); + unsigned optFindComplementary (unsigned assertionIndex); + + // Assertion creation functions. + unsigned optCreateAssertion(GenTreePtr op1, GenTreePtr op2, optAssertionKind assertionKind); + unsigned optCreateAssertion(GenTreePtr op1, GenTreePtr op2, optAssertionKind assertionKind, AssertionDsc* assertion); + void optCreateComplementaryAssertion(const AssertionDsc& candidateAssertion, GenTreePtr op1, GenTreePtr op2); + + bool optAssertionVnInvolvesNan(AssertionDsc* assertion); + unsigned optAddAssertion (AssertionDsc* assertion); + void optAddVnAssertionMapping(ValueNum vn, const EXPSET_TP& mask); + EXPSET_TP optGetVnMappedAssertions(ValueNum vn); + + // Used for respective assertion propagations. + unsigned optAssertionIsSubrange(GenTreePtr tree, var_types toType, EXPSET_TP assertions); + unsigned optAssertionIsSubtype(GenTreePtr tree, GenTreePtr methodTableArg, EXPSET_TP assertions); + unsigned optAssertionIsNonNullInternal(GenTreePtr op, EXPSET_TP assertions); + bool optAssertionIsNonNull(GenTreePtr op, EXPSET_TP assertions DEBUGARG(bool* pVnBased) DEBUGARG(unsigned* pIndex)); + + // Used for Relop propagation. + unsigned optGlobalAssertionIsEqualOrNotEqual(EXPSET_TP assertions, GenTreePtr op1, GenTreePtr op2); + unsigned optLocalAssertionIsEqualOrNotEqual(optOp1Kind op1Kind, unsigned lclNum, optOp2Kind op2Kind, ssize_t cnsVal, EXPSET_TP assertions); + + // Assertion prop for lcl var functions. + bool optAssertionProp_LclVarTypeCheck(GenTreePtr tree, LclVarDsc* lclVarDsc, LclVarDsc* copyVarDsc); + GenTreePtr optCopyAssertionProp(AssertionDsc* curAssertion, GenTreePtr tree, GenTreePtr stmt DEBUGARG(unsigned index)); + GenTreePtr optConstantAssertionProp(AssertionDsc* curAssertion, const GenTreePtr tree, const GenTreePtr stmt DEBUGARG(unsigned index)); + GenTreePtr optVnConstantAssertionProp(const GenTreePtr tree, const GenTreePtr stmt); + + // Assertion propagation functions. + GenTreePtr optAssertionProp(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionProp_LclVar(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionProp_Ind(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionProp_Cast(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionProp_Call(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionProp_RelOp(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionProp_Comma(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionProp_BndsChk(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionPropGlobal_RelOp(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionPropLocal_RelOp(EXPSET_TP assertions, const GenTreePtr tree, const GenTreePtr stmt); + GenTreePtr optAssertionProp_Update(const GenTreePtr newTree, const GenTreePtr tree, const GenTreePtr stmt); + + // Implied assertion functions. + EXPSET_TP optImpliedAssertions(unsigned assertionIndex, EXPSET_TP activeAssertions); + EXPSET_TP optImpliedByTypeOfAssertions(EXPSET_TP activeAssertions); + EXPSET_TP optImpliedByCopyAssertion(AssertionDsc* copyAssertion, AssertionDsc* depAssertion); + EXPSET_TP optImpliedByConstAssertion(AssertionDsc* curAssertion); + +#ifdef DEBUG + void optPrintAssertion (AssertionDsc* newAssertion, unsigned assertionIndex=0); + void optDebugCheckAssertions(unsigned index); +#endif + void optAddCopies(); +#endif // ASSERTION_PROP + + /************************************************************************** + * Range checks + *************************************************************************/ + +public : + struct LoopCloneVisitorInfo + { + LoopCloneContext* context; + unsigned loopNum; + GenTreePtr stmt; + LoopCloneVisitorInfo(LoopCloneContext* context, unsigned loopNum, GenTreePtr stmt) + : context(context) + , loopNum(loopNum) + , stmt(nullptr) {} + }; + + bool optIsStackLocalInvariant(unsigned loopNum, unsigned lclNum); + bool optExtractArrIndex(GenTreePtr tree, ArrIndex* result, unsigned lhsNum); + bool optReconstructArrIndex(GenTreePtr tree, ArrIndex* result, unsigned lhsNum); + bool optIdentifyLoopOptInfo(unsigned loopNum, LoopCloneContext* context); + static fgWalkPreFn optCanOptimizeByLoopCloningVisitor; + fgWalkResult optCanOptimizeByLoopCloning(GenTreePtr tree, LoopCloneVisitorInfo* info); + void optOptimizeIndexChecks(); + void optObtainLoopCloningOpts(LoopCloneContext* context); + bool optIsLoopClonable(unsigned loopInd); + + bool optCanCloneLoops(); + +#ifdef DEBUG + void optDebugLogLoopCloning(BasicBlock* block, GenTreePtr insertBefore); +#endif + void optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext* context DEBUGARG(bool fastPath)); + bool optComputeDerefConditions(unsigned loopNum, LoopCloneContext* context); + bool optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext* context); + BasicBlock* optInsertLoopChoiceConditions(LoopCloneContext* context, unsigned loopNum, BasicBlock* head, BasicBlock* slow); + void optInsertLoopCloningStress(BasicBlock* head); + +#if COUNT_RANGECHECKS + static unsigned optRangeChkRmv; + static unsigned optRangeChkAll; +#endif + +protected : + struct arraySizes + { + unsigned arrayVar; + int arrayDim; + + #define MAX_ARRAYS 4 // a magic max number of arrays tracked for bounds check elimination + }; + + struct RngChkDsc + { + RngChkDsc * rcdNextInBucket; // used by the hash table + + unsigned short rcdHashValue; // to make matching faster + unsigned short rcdIndex; // 0..optRngChkCount-1 + + GenTreePtr rcdTree; // the array index tree + }; + + unsigned optRngChkCount; + static const size_t optRngChkHashSize; + + ssize_t optGetArrayRefScaleAndIndex(GenTreePtr mul, + GenTreePtr *pIndex + DEBUGARG(bool bRngChk)); + GenTreePtr optFindLocalInit (BasicBlock *block, + GenTreePtr local, + VARSET_TP* pKilledInOut, + bool* isKilledAfterInit); + +#if FANCY_ARRAY_OPT + bool optIsNoMore (GenTreePtr op1, GenTreePtr op2, + int add1 = 0, int add2 = 0); +#endif + void optOptimizeInducIndexChecks(unsigned loopNum, arraySizes arrayDesc[]); + + bool optReachWithoutCall(BasicBlock * srcBB, + BasicBlock * dstBB); + +protected : + + bool optLoopsMarked; + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX RegAlloc XX +XX XX +XX Does the register allocation and puts the remaining lclVars on the stack XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + + +public : + +#ifndef LEGACY_BACKEND + bool doLSRA() const { return true; } +#else // LEGACY_BACKEND + bool doLSRA() const { return false; } +#endif // LEGACY_BACKEND + +#ifdef LEGACY_BACKEND + void raInit (); + void raAssignVars(); // register allocation +#endif // LEGACY_BACKEND + + VARSET_TP raRegVarsMask; // Set of all enregistered variables (not including FEATURE_STACK_FP_X87 enregistered variables) + regNumber raUpdateRegStateForArg(RegState *regState, LclVarDsc *argDsc); + + void raMarkStkVars (); + +protected: + + // Some things are used by both LSRA and regpredict allocators. + + FrameType rpFrameType; + bool rpMustCreateEBPCalled; // Set to true after we have called rpMustCreateEBPFrame once + +#ifdef LEGACY_BACKEND + regMaskTP rpMaskPInvokeEpilogIntf; // pinvoke epilog trashes esi/edi holding stack args needed to setup tail call's args +#endif // LEGACY_BACKEND + + bool rpMustCreateEBPFrame(INDEBUG(const char ** wbReason)); + +#if FEATURE_FP_REGALLOC + enum enumConfigRegisterFP + { + CONFIG_REGISTER_FP_NONE = 0x0, + CONFIG_REGISTER_FP_CALLEE_TRASH = 0x1, + CONFIG_REGISTER_FP_CALLEE_SAVED = 0x2, + CONFIG_REGISTER_FP_FULL = 0x3, + }; + enumConfigRegisterFP raConfigRegisterFP(); +#endif // FEATURE_FP_REGALLOC + +public: + regMaskTP raConfigRestrictMaskFP(); + +private: +#ifndef LEGACY_BACKEND + LinearScanInterface* m_pLinearScan; // Linear Scan allocator +#else // LEGACY_BACKEND + unsigned raAvoidArgRegMask; // Mask of incoming argument registers that we may need to avoid + VARSET_TP raLclRegIntf[REG_COUNT]; // variable to register interference graph + bool raNewBlocks; // True is we added killing blocks for FPU registers + unsigned rpPasses; // Number of passes made by the register predicter + unsigned rpPassesMax; // Maximum number of passes made by the register predicter + unsigned rpPassesPessimize; // Number of passes non-pessimizing made by the register predicter + unsigned rpStkPredict; // Weighted count of variables were predicted STK (lower means register allocation is better) + unsigned rpPredictSpillCnt; // Predicted number of integer spill tmps for the current tree + regMaskTP rpPredictAssignMask; // Mask of registers to consider in rpPredictAssignRegVars() + VARSET_TP rpLastUseVars; // Set of last use variables in rpPredictTreeRegUse + VARSET_TP rpUseInPlace; // Set of variables that we used in place + int rpAsgVarNum; // VarNum for the target of GT_ASG node + bool rpPredictAssignAgain; // Must rerun the rpPredictAssignRegVars() + bool rpAddedVarIntf; // Set to true if we need to add a new var intf + bool rpLostEnreg; // Set to true if we lost an enregister var that had lvDependReg set + bool rpReverseEBPenreg; // Decided to reverse the enregistration of EBP +public: + bool rpRegAllocDone; // Set to true after we have completed register allocation +private: + regMaskTP rpPredictMap[PREDICT_COUNT]; // Holds the regMaskTP for each of the enum values + + void raSetupArgMasks(RegState *r); + + const regNumber* raGetRegVarOrder (var_types regType, + unsigned * wbVarOrderSize); +#ifdef DEBUG + void raDumpVarIntf (); // Dump the variable to variable interference graph + void raDumpRegIntf (); // Dump the variable to register interference graph +#endif + void raAdjustVarIntf (); + + regMaskTP rpPredictRegMask (rpPredictReg predictReg, + var_types type); + + bool rpRecordRegIntf (regMaskTP regMask, + VARSET_VALARG_TP life + DEBUGARG( const char * msg)); + + bool rpRecordVarIntf (unsigned varNum, + VARSET_VALARG_TP intfVar + DEBUGARG( const char * msg)); + regMaskTP rpPredictRegPick (var_types type, + rpPredictReg predictReg, + regMaskTP lockedRegs); + + regMaskTP rpPredictGrabReg (var_types type, + rpPredictReg predictReg, + regMaskTP lockedRegs); + + static fgWalkPreFn rpMarkRegIntf; + + regMaskTP rpPredictAddressMode(GenTreePtr tree, + var_types type, + regMaskTP lockedRegs, + regMaskTP rsvdRegs, + GenTreePtr lenCSE); + + void rpPredictRefAssign (unsigned lclNum); + + regMaskTP rpPredictTreeRegUse (GenTreePtr tree, + rpPredictReg predictReg, + regMaskTP lockedRegs, + regMaskTP rsvdRegs); + + regMaskTP rpPredictAssignRegVars(regMaskTP regAvail); + + void rpPredictRegUse (); // Entry point + + unsigned raPredictTreeRegUse (GenTreePtr tree); + unsigned raPredictListRegUse (GenTreePtr list); + + void raSetRegVarOrder (var_types regType, + regNumber * customVarOrder, + unsigned * customVarOrderSize, + regMaskTP prefReg, + regMaskTP avoidReg); + + // We use (unsigned)-1 as an uninitialized sentinel for rpStkPredict and + // also as the maximum value of lvRefCntWtd. Don't allow overflow, and + // saturate at UINT_MAX - 1, to avoid using the sentinel. + void raAddToStkPredict(unsigned val) + { + unsigned newStkPredict = rpStkPredict + val; + if ((newStkPredict < rpStkPredict) || (newStkPredict == UINT_MAX)) + rpStkPredict = UINT_MAX - 1; + else + rpStkPredict = newStkPredict; + } + +#ifdef DEBUG +#if !FEATURE_FP_REGALLOC + void raDispFPlifeInfo (); +#endif +#endif + + regMaskTP genReturnRegForTree (GenTreePtr tree); +#endif // LEGACY_BACKEND + + /* raIsVarargsStackArg is called by raMaskStkVars and by + lvaSortByRefCount. It identifies the special case + where a varargs function has a parameter passed on the + stack, other than the special varargs handle. Such parameters + require special treatment, because they cannot be tracked + by the GC (their offsets in the stack are not known + at compile time). + */ + + bool raIsVarargsStackArg(unsigned lclNum) + { +#ifdef _TARGET_X86_ + + LclVarDsc *varDsc = &lvaTable[lclNum]; + + assert(varDsc->lvIsParam); + + return (info.compIsVarArgs && + !varDsc->lvIsRegArg && + (lclNum != lvaVarargsHandleArg)); + +#else // _TARGET_X86_ + + return false; + +#endif // _TARGET_X86_ + } + +#ifdef LEGACY_BACKEND + // Records the current prediction, if it's better than any previous recorded prediction. + void rpRecordPrediction(); + // Applies the best recorded prediction, if one exists and is better than the current prediction. + void rpUseRecordedPredictionIfBetter(); + + // Data members used in the methods above. + unsigned rpBestRecordedStkPredict; + struct VarRegPrediction + { + bool m_isEnregistered; + regNumberSmall m_regNum; + regNumberSmall m_otherReg; + }; + VarRegPrediction* rpBestRecordedPrediction; +#endif // LEGACY_BACKEND + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX EEInterface XX +XX XX +XX Get to the class and method info from the Execution Engine given XX +XX tokens for the class and method XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +public : + + /* These are the different addressing modes used to access a local var. + * The JIT has to report the location of the locals back to the EE + * for debugging purposes. + */ + + enum siVarLocType + { + VLT_REG, + VLT_REG_BYREF, // this type is currently only used for value types on X64 + VLT_REG_FP, + VLT_STK, + VLT_STK_BYREF, // this type is currently only used for value types on X64 + VLT_REG_REG, + VLT_REG_STK, + VLT_STK_REG, + VLT_STK2, + VLT_FPSTK, + VLT_FIXED_VA, + + VLT_COUNT, + VLT_INVALID + }; + + struct siVarLoc + { + siVarLocType vlType; + + union + { + // VLT_REG/VLT_REG_FP -- Any pointer-sized enregistered value (TYP_INT, TYP_REF, etc) + // eg. EAX + // VLT_REG_BYREF -- the specified register contains the address of the variable + // eg. [EAX] + + struct + { + regNumber vlrReg; + } + vlReg; + + // VLT_STK -- Any 32 bit value which is on the stack + // eg. [ESP+0x20], or [EBP-0x28] + // VLT_STK_BYREF -- the specified stack location contains the address of the variable + // eg. mov EAX, [ESP+0x20]; [EAX] + + struct + { + regNumber vlsBaseReg; + NATIVE_OFFSET vlsOffset; + } + vlStk; + + // VLT_REG_REG -- TYP_LONG/TYP_DOUBLE with both DWords enregistered + // eg. RBM_EAXEDX + + struct + { + regNumber vlrrReg1; + regNumber vlrrReg2; + } + vlRegReg; + + // VLT_REG_STK -- Partly enregistered TYP_LONG/TYP_DOUBLE + // eg { LowerDWord=EAX UpperDWord=[ESP+0x8] } + + struct + { + regNumber vlrsReg; + + struct + { + regNumber vlrssBaseReg; + NATIVE_OFFSET vlrssOffset; + } + vlrsStk; + } + vlRegStk; + + // VLT_STK_REG -- Partly enregistered TYP_LONG/TYP_DOUBLE + // eg { LowerDWord=[ESP+0x8] UpperDWord=EAX } + + struct + { + struct + { + regNumber vlsrsBaseReg; + NATIVE_OFFSET vlsrsOffset; + } + vlsrStk; + + regNumber vlsrReg; + } + vlStkReg; + + // VLT_STK2 -- Any 64 bit value which is on the stack, in 2 successsive DWords + // eg 2 DWords at [ESP+0x10] + + struct + { + regNumber vls2BaseReg; + NATIVE_OFFSET vls2Offset; + } + vlStk2; + + // VLT_FPSTK -- enregisterd TYP_DOUBLE (on the FP stack) + // eg. ST(3). Actually it is ST("FPstkHeight - vpFpStk") + + struct + { + unsigned vlfReg; + } + vlFPstk; + + // VLT_FIXED_VA -- fixed argument of a varargs function. + // The argument location depends on the size of the variable + // arguments (...). Inspecting the VARARGS_HANDLE indicates the + // location of the first arg. This argument can then be accessed + // relative to the position of the first arg + + struct + { + unsigned vlfvOffset; + } + vlFixedVarArg; + + // VLT_MEMORY + + struct + { + void *rpValue; // pointer to the in-process + // location of the value. + } vlMemory; + + }; + + // Helper functions + + bool vlIsInReg(regNumber reg); + bool vlIsOnStk(regNumber reg, signed offset); + }; + + /*************************************************************************/ + +public : + + // Get handles + + void eeGetCallInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, + CORINFO_RESOLVED_TOKEN * pConstrainedToken, + CORINFO_CALLINFO_FLAGS flags, + CORINFO_CALL_INFO* pResult); + inline CORINFO_CALLINFO_FLAGS addVerifyFlag(CORINFO_CALLINFO_FLAGS flags); + + void eeGetFieldInfo ( CORINFO_RESOLVED_TOKEN * pResolvedToken, + CORINFO_ACCESS_FLAGS flags, + CORINFO_FIELD_INFO *pResult); + + // Get the flags + + BOOL eeIsValueClass (CORINFO_CLASS_HANDLE clsHnd); + +#if defined(DEBUG) || defined(FEATURE_JIT_METHOD_PERF) || defined(FEATURE_SIMD) + + bool IsSuperPMIException(unsigned code) + { + // Copied from NDP\clr\src\ToolBox\SuperPMI\SuperPMI-Shared\ErrorHandling.h + + const unsigned EXCEPTIONCODE_DebugBreakorAV = 0xe0421000; + const unsigned EXCEPTIONCODE_MC = 0xe0422000; + const unsigned EXCEPTIONCODE_LWM = 0xe0423000; + const unsigned EXCEPTIONCODE_SASM = 0xe0424000; + const unsigned EXCEPTIONCODE_SSYM = 0xe0425000; + const unsigned EXCEPTIONCODE_CALLUTILS = 0xe0426000; + const unsigned EXCEPTIONCODE_TYPEUTILS = 0xe0427000; + const unsigned EXCEPTIONCODE_ASSERT = 0xe0440000; + + switch (code) + { + case EXCEPTIONCODE_DebugBreakorAV: + case EXCEPTIONCODE_MC: + case EXCEPTIONCODE_LWM: + case EXCEPTIONCODE_SASM: + case EXCEPTIONCODE_SSYM: + case EXCEPTIONCODE_CALLUTILS: + case EXCEPTIONCODE_TYPEUTILS: + case EXCEPTIONCODE_ASSERT: + return true; + default: + return false; + } + } + + const char* eeGetMethodName (CORINFO_METHOD_HANDLE hnd, const char** className); + const char* eeGetMethodFullName (CORINFO_METHOD_HANDLE hnd); + + bool eeIsNativeMethod(CORINFO_METHOD_HANDLE method); + CORINFO_METHOD_HANDLE eeGetMethodHandleForNative(CORINFO_METHOD_HANDLE method); +#endif + + var_types eeGetArgType (CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig); + var_types eeGetArgType (CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig, bool* isPinned); + unsigned eeGetArgSize (CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig); + + // VOM info, method sigs + + void eeGetSig (unsigned sigTok, + CORINFO_MODULE_HANDLE scope, + CORINFO_CONTEXT_HANDLE context, + CORINFO_SIG_INFO* retSig); + + void eeGetCallSiteSig (unsigned sigTok, + CORINFO_MODULE_HANDLE scope, + CORINFO_CONTEXT_HANDLE context, + CORINFO_SIG_INFO* retSig); + + void eeGetMethodSig (CORINFO_METHOD_HANDLE methHnd, + CORINFO_SIG_INFO* retSig, + CORINFO_CLASS_HANDLE owner = NULL); + + // Method entry-points, instrs + + void * eeGetFieldAddress (CORINFO_FIELD_HANDLE handle, + void ** *ppIndir); + + CORINFO_METHOD_HANDLE eeMarkNativeTarget(CORINFO_METHOD_HANDLE method); + + CORINFO_EE_INFO eeInfo; + bool eeInfoInitialized; + + CORINFO_EE_INFO * eeGetEEInfo(); + + unsigned eeGetArrayDataOffset(var_types type); + + GenTreePtr eeGetPInvokeCookie(CORINFO_SIG_INFO *szMetaSig); + + // Exceptions + + unsigned eeGetEHcount (CORINFO_METHOD_HANDLE handle); + + // Debugging support - Line number info + + void eeGetStmtOffsets(); + + unsigned eeBoundariesCount; + + struct boundariesDsc + { + UNATIVE_OFFSET nativeIP; + IL_OFFSET ilOffset; + unsigned sourceReason; + } + * eeBoundaries; // Boundaries to report to EE + void eeSetLIcount (unsigned count); + void eeSetLIinfo (unsigned which, + UNATIVE_OFFSET offs, + unsigned srcIP, + bool stkEmpty, + bool callInstruction); + void eeSetLIdone (); + +#ifdef DEBUG + static void eeDispILOffs(IL_OFFSET offs); + static void eeDispLineInfo(const boundariesDsc* line); + void eeDispLineInfos(); +#endif // DEBUG + + // Debugging support - Local var info + + void eeGetVars (); + + unsigned eeVarsCount; + + struct VarResultInfo + { + UNATIVE_OFFSET startOffset; + UNATIVE_OFFSET endOffset; + DWORD varNumber; + siVarLoc loc; + } + * eeVars; + void eeSetLVcount (unsigned count); + void eeSetLVinfo (unsigned which, + UNATIVE_OFFSET startOffs, + UNATIVE_OFFSET length, + unsigned varNum, + unsigned LVnum, + VarName namex, + bool avail, + const siVarLoc &loc); + void eeSetLVdone (); + +#ifdef DEBUG + void eeDispVar (ICorDebugInfo::NativeVarInfo* var); + void eeDispVars (CORINFO_METHOD_HANDLE ftn, + ULONG32 cVars, + ICorDebugInfo::NativeVarInfo* vars); +#endif // DEBUG + + // ICorJitInfo wrappers + + void eeReserveUnwindInfo(BOOL isFunclet, + BOOL isColdCode, + ULONG unwindSize); + + void eeAllocUnwindInfo(BYTE* pHotCode, + BYTE* pColdCode, + ULONG startOffset, + ULONG endOffset, + ULONG unwindSize, + BYTE* pUnwindBlock, + CorJitFuncKind funcKind); + + void eeSetEHcount(unsigned cEH); + + void eeSetEHinfo(unsigned EHnumber, + const CORINFO_EH_CLAUSE* clause); + + // Utility functions + +#if defined(DEBUG) + const wchar_t * eeGetCPString (size_t stringHandle); +#endif + +#if defined(DEBUG) || INLINE_MATH + const char * eeGetFieldName (CORINFO_FIELD_HANDLE fieldHnd, + const char ** classNamePtr = NULL); + const char* eeGetClassName (CORINFO_CLASS_HANDLE clsHnd); +#endif + + static CORINFO_METHOD_HANDLE eeFindHelper (unsigned helper); + static CorInfoHelpFunc eeGetHelperNum (CORINFO_METHOD_HANDLE method); + + static fgWalkPreFn CountSharedStaticHelper; + static bool IsSharedStaticHelper (GenTreePtr tree); + static bool IsTreeAlwaysHoistable (GenTreePtr tree); + + static CORINFO_FIELD_HANDLE eeFindJitDataOffs (unsigned jitDataOffs); + // returns true/false if 'field' is a Jit Data offset + static bool eeIsJitDataOffs (CORINFO_FIELD_HANDLE field); + // returns a number < 0 if 'field' is not a Jit Data offset, otherwise the data offset (limited to 2GB) + static int eeGetJitDataOffs (CORINFO_FIELD_HANDLE field); + +/*****************************************************************************/ + +public : + + void tmpInit (); + + enum TEMP_USAGE_TYPE { TEMP_USAGE_FREE, TEMP_USAGE_USED }; + + static var_types tmpNormalizeType (var_types type); + TempDsc* tmpGetTemp (var_types type); // get temp for the given type + void tmpRlsTemp (TempDsc* temp); + TempDsc* tmpFindNum (int temp, TEMP_USAGE_TYPE usageType = TEMP_USAGE_FREE) const; + + void tmpEnd (); + TempDsc* tmpListBeg (TEMP_USAGE_TYPE usageType = TEMP_USAGE_FREE) const; + TempDsc* tmpListNxt (TempDsc* curTemp, TEMP_USAGE_TYPE usageType = TEMP_USAGE_FREE) const; + void tmpDone (); + +#ifdef DEBUG + bool tmpAllFree () const; +#endif // DEBUG + +#ifndef LEGACY_BACKEND + void tmpPreAllocateTemps(var_types type, unsigned count); +#endif // !LEGACY_BACKEND + +protected : + +#ifdef LEGACY_BACKEND + unsigned tmpIntSpillMax; // number of int-sized spill temps + unsigned tmpDoubleSpillMax; // number of double-sized spill temps +#endif // LEGACY_BACKEND + + unsigned tmpCount; // Number of temps + unsigned tmpSize; // Size of all the temps +#ifdef DEBUG +public: + // Used by RegSet::rsSpillChk() + unsigned tmpGetCount; // Temps which haven't been released yet +#endif +private: + + static unsigned tmpSlot (unsigned size); // which slot in tmpFree[] or tmpUsed[] to use + + TempDsc* tmpFree[TEMP_MAX_SIZE / sizeof(int)]; + TempDsc* tmpUsed[TEMP_MAX_SIZE / sizeof(int)]; + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX CodeGenerator XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +public : + + CodeGenInterface* codeGen; + +#ifdef DEBUGGING_SUPPORT + + // The following holds information about instr offsets in terms of generated code. + + struct IPmappingDsc + { + IPmappingDsc * ipmdNext; // next line# record + IL_OFFSETX ipmdILoffsx; // the instr offset + emitLocation ipmdNativeLoc; // the emitter location of the native code corresponding to the IL offset + bool ipmdIsLabel; // Can this code be a branch label? + }; + + // Record the instr offset mapping to the generated code + + IPmappingDsc * genIPmappingList; + IPmappingDsc * genIPmappingLast; + + // Managed RetVal - A side hash table meant to record the mapping from a + // GT_CALL node to its IL offset. This info is used to emit sequence points + // that can be used by debugger to determine the native offset at which the + // managed RetVal will be available. + // + // In fact we can store IL offset in a GT_CALL node. This was ruled out in + // favor of a side table for two reasons: 1) We need IL offset for only those + // GT_CALL nodes (created during importation) that correspond to an IL call and + // whose return type is other than TYP_VOID. 2) GT_CALL node is a frequently used + // structure and IL offset is needed only when generating debuggable code. Therefore + // it is desirable to avoid memory size penalty in retail scenarios. + typedef SimplerHashTable<GenTreePtr, PtrKeyFuncs<GenTree>, IL_OFFSETX, DefaultSimplerHashBehavior> CallSiteILOffsetTable; + CallSiteILOffsetTable * genCallSite2ILOffsetMap; +#endif // DEBUGGING_SUPPORT + + unsigned genReturnLocal; // Local number for the return value when applicable. + BasicBlock * genReturnBB; // jumped to when not optimizing for speed. + + // The following properties are part of CodeGenContext. Getters are provided here for + // convenience and backward compatibility, but the properties can only be set by invoking + // the setter on CodeGenContext directly. + + __declspec(property(get = getEmitter)) emitter* genEmitter; + emitter* getEmitter() { return codeGen->getEmitter(); } + + const bool isFramePointerUsed() { return codeGen->isFramePointerUsed(); } + + __declspec(property(get = getInterruptible, put=setInterruptible)) bool genInterruptible; + bool getInterruptible() { return codeGen->genInterruptible; } + void setInterruptible(bool value) { codeGen->setInterruptible(value); } + +#if DOUBLE_ALIGN + const bool genDoubleAlign() { return codeGen->doDoubleAlign(); } + DWORD getCanDoubleAlign(); // Defined & used only by RegAlloc +#endif // DOUBLE_ALIGN + __declspec(property(get = getFullPtrRegMap, put=setFullPtrRegMap)) bool genFullPtrRegMap; + bool getFullPtrRegMap() { return codeGen->genFullPtrRegMap; } + void setFullPtrRegMap(bool value) { codeGen->setFullPtrRegMap(value); } + + + // Things that MAY belong either in CodeGen or CodeGenContext + +#if FEATURE_EH_FUNCLETS + FuncInfoDsc * compFuncInfos; + unsigned short compCurrFuncIdx; + unsigned short compFuncInfoCount; + + unsigned short compFuncCount() { assert(fgFuncletsCreated); return compFuncInfoCount; } + +#else // !FEATURE_EH_FUNCLETS + + // This is a no-op when there are no funclets! + void genUpdateCurrentFunclet(BasicBlock * block) { return; } + + FuncInfoDsc compFuncInfoRoot; + + static + const unsigned compCurrFuncIdx = 0; + + unsigned short compFuncCount() { return 1; } + +#endif // !FEATURE_EH_FUNCLETS + + FuncInfoDsc * funCurrentFunc (); + void funSetCurrentFunc (unsigned funcIdx); + FuncInfoDsc * funGetFunc (unsigned funcIdx); + unsigned int funGetFuncIdx (BasicBlock *block); + + + // LIVENESS + + + VARSET_TP compCurLife; // current live variables + GenTreePtr compCurLifeTree; // node after which compCurLife has been computed + + template<bool ForCodeGen> + void compChangeLife (VARSET_VALARG_TP newLife + DEBUGARG( GenTreePtr tree)); + + void genChangeLife (VARSET_VALARG_TP newLife + DEBUGARG( GenTreePtr tree)) + { + compChangeLife</*ForCodeGen*/true>(newLife DEBUGARG(tree)); + } + + template<bool ForCodeGen> + void compUpdateLife (GenTreePtr tree); + + // Updates "compCurLife" to its state after evaluate of "true". If "pLastUseVars" is + // non-null, sets "*pLastUseVars" to the set of tracked variables for which "tree" was a last + // use. (Can be more than one var in the case of dependently promoted struct vars.) + template<bool ForCodeGen> + void compUpdateLifeVar (GenTreePtr tree, VARSET_TP* pLastUseVars = NULL); + + template<bool ForCodeGen> + inline + void compUpdateLife (VARSET_VALARG_TP newLife); + + // Gets a register mask that represent the kill set for a helper call since + // not all JIT Helper calls follow the standard ABI on the target architecture. + regMaskTP compHelperCallKillSet (CorInfoHelpFunc helper); + + // Gets a register mask that represent the kill set for a NoGC helper call. + regMaskTP compNoGCHelperCallKillSet (CorInfoHelpFunc helper); + +#ifdef _TARGET_ARM_ + // Requires that "varDsc" be a promoted struct local variable being passed as an argument, beginning at "firstArgRegNum", + // which is assumed to have already been aligned to the register alignment restriction of the struct type. + // Adds bits to "*pArgSkippedRegMask" for any argument registers *not* used in passing "varDsc" -- i.e., internal + // "holes" caused by internal alignment constraints. For example, if the struct contained an int and a double, and we + // at R0 (on ARM), then R1 would be skipped, and the bit for R1 would be added to the mask. + void fgAddSkippedRegsInPromotedStructArg(LclVarDsc* varDsc, + unsigned firstArgRegNum, + regMaskTP* pArgSkippedRegMask); +#endif // _TARGET_ARM_ + + // If "tree" is a indirection (GT_IND, or GT_LDOBJ) whose arg is an ADDR, whose arg is a LCL_VAR, return that LCL_VAR node, else NULL. + GenTreePtr fgIsIndirOfAddrOfLocal(GenTreePtr tree); + + // This is indexed by GT_LDOBJ nodes that are address of promoted struct variables, which + // have been annotated with the GTF_VAR_DEATH flag. If such a node is *not* mapped in this + // table, one may assume that all the (tracked) field vars die at this point. Otherwise, + // the node maps to a pointer to a VARSET_TP, containing set bits for each of the tracked field + // vars of the promoted struct local that go dead at the given node (the set bits are the bits + // for the tracked var indices of the field vars, as in a live var set). + NodeToVarsetPtrMap* m_promotedStructDeathVars; + + NodeToVarsetPtrMap* GetPromotedStructDeathVars() + { + if (m_promotedStructDeathVars == NULL) + { + m_promotedStructDeathVars = new (getAllocator()) NodeToVarsetPtrMap(getAllocator()); + } + return m_promotedStructDeathVars; + } + + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX UnwindInfo XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +#if !defined(__GNUC__) +#pragma region Unwind information +#endif + +public: + + // + // Infrastructure functions: start/stop/reserve/emit. + // + + void unwindBegProlog(); + void unwindEndProlog(); + void unwindBegEpilog(); + void unwindEndEpilog(); + void unwindReserve(); + void unwindEmit(void* pHotCode, void* pColdCode); + + // + // Specific unwind information functions: called by code generation to indicate a particular + // prolog or epilog unwindable instruction has been generated. + // + + void unwindPush(regNumber reg); + void unwindAllocStack(unsigned size); + void unwindSetFrameReg(regNumber reg, unsigned offset); + void unwindSaveReg(regNumber reg, unsigned offset); + +#if defined(_TARGET_ARM_) + void unwindPushMaskInt(regMaskTP mask); + void unwindPushMaskFloat(regMaskTP mask); + void unwindPopMaskInt(regMaskTP mask); + void unwindPopMaskFloat(regMaskTP mask); + void unwindBranch16(); // The epilog terminates with a 16-bit branch (e.g., "bx lr") + void unwindNop(unsigned codeSizeInBytes); // Generate unwind NOP code. 'codeSizeInBytes' is 2 or 4 bytes. Only called via unwindPadding(). + void unwindPadding(); // Generate a sequence of unwind NOP codes representing instructions between the last instruction and the current location. +#endif // _TARGET_ARM_ + +#if defined(_TARGET_ARM64_) + void unwindNop(); + void unwindPadding(); // Generate a sequence of unwind NOP codes representing instructions between the last instruction and the current location. + void unwindSaveReg(regNumber reg, int offset); // str reg, [sp, #offset] + void unwindSaveRegPreindexed(regNumber reg, int offset); // str reg, [sp, #offset]! + void unwindSaveRegPair(regNumber reg1, regNumber reg2, int offset); // stp reg1, reg2, [sp, #offset] + void unwindSaveRegPairPreindexed(regNumber reg1, regNumber reg2, int offset); // stp reg1, reg2, [sp, #offset]! + void unwindSaveNext(); // unwind code: save_next + void unwindReturn(regNumber reg); // ret lr +#endif // defined(_TARGET_ARM64_) + + // + // Private "helper" functions for the unwind implementation. + // + +private: + +#if FEATURE_EH_FUNCLETS + void unwindGetFuncLocations(FuncInfoDsc* func, bool getHotSectionData, /* OUT */ emitLocation** ppStartLoc, /* OUT */ emitLocation** ppEndLoc); +#endif // FEATURE_EH_FUNCLETS + + void unwindReserveFunc(FuncInfoDsc* func); + void unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode); + +#if defined(_TARGET_AMD64_) + + void unwindReserveFuncHelper(FuncInfoDsc* func, bool isHotCode); + void unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pColdCode, bool isHotCode); + UNATIVE_OFFSET unwindGetCurrentOffset(FuncInfoDsc* func); + +#elif defined(_TARGET_ARM_) + + void unwindPushPopMaskInt(regMaskTP mask, bool useOpsize16); + void unwindPushPopMaskFloat(regMaskTP mask); + void unwindSplit(FuncInfoDsc* func); + +#endif // _TARGET_ARM_ + +#if !defined(__GNUC__) +#pragma endregion // Note: region is NOT under !defined(__GNUC__) +#endif + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX SIMD XX +XX XX +XX Info about SIMD types, methods and the SIMD assembly (i.e. the assembly XX +XX that contains the distinguished, well-known SIMD type definitions). XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + + // Get highest available instruction set for floating point codegen + InstructionSet getFloatingPointInstructionSet() + { +#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) + if (canUseAVX()) + { + return InstructionSet_AVX; + } + + // min bar is SSE2 + assert(canUseSSE2()); + return InstructionSet_SSE2; +#else + assert(!"getFPInstructionSet() is not implemented for target arch"); + unreached(); + InstructionSet_NONE; +#endif + } + + // Get highest available instruction set for SIMD codegen + InstructionSet getSIMDInstructionSet() + { +#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) + return getFloatingPointInstructionSet(); +#else + assert(!"Available instruction set(s) for SIMD codegen is not defined for target arch"); + unreached(); + return InstructionSet_NONE; +#endif + } + +#ifdef FEATURE_SIMD + + // Should we support SIMD intrinsics? + bool featureSIMD; + + // This is a temp lclVar allocated on the stack as TYP_SIMD. It is used to implement intrinsics + // that require indexed access to the individual fields of the vector, which is not well supported + // by the hardware. It is allocated when/if such situations are encountered during Lowering. + unsigned lvaSIMDInitTempVarNum; + + // SIMD Types + CORINFO_CLASS_HANDLE SIMDFloatHandle; + CORINFO_CLASS_HANDLE SIMDDoubleHandle; + CORINFO_CLASS_HANDLE SIMDIntHandle; + CORINFO_CLASS_HANDLE SIMDUShortHandle; + CORINFO_CLASS_HANDLE SIMDUByteHandle; + CORINFO_CLASS_HANDLE SIMDShortHandle; + CORINFO_CLASS_HANDLE SIMDByteHandle; + CORINFO_CLASS_HANDLE SIMDLongHandle; + CORINFO_CLASS_HANDLE SIMDUIntHandle; + CORINFO_CLASS_HANDLE SIMDULongHandle; + CORINFO_CLASS_HANDLE SIMDVector2Handle; + CORINFO_CLASS_HANDLE SIMDVector3Handle; + CORINFO_CLASS_HANDLE SIMDVector4Handle; + CORINFO_CLASS_HANDLE SIMDVectorHandle; + + // SIMD Methods + CORINFO_METHOD_HANDLE SIMDVectorFloat_set_Item; + CORINFO_METHOD_HANDLE SIMDVectorFloat_get_Length; + CORINFO_METHOD_HANDLE SIMDVectorFloat_op_Addition; + + // Check typeHnd to see if it is a SIMD type, and if so modify the type on the varDsc accordingly. + void checkForSIMDType(LclVarDsc* varDsc, CORINFO_CLASS_HANDLE typeHnd); + + // Returns true if the tree corresponds to a TYP_SIMD lcl var. + // Note that both SIMD vector args and locals are mared as lvSIMDType = true, but + // type of an arg node is TYP_BYREF and a local node is TYP_SIMD or TYP_STRUCT. + bool isSIMDTypeLocal(GenTree* tree) + { + return tree->OperIsLocal() && lvaTable[tree->AsLclVarCommon()->gtLclNum].lvSIMDType; + } + + // Returns true if the type of the tree can be inferred as TYP_SIMD + bool isSIMDType(GenTree* tree) + { + return varTypeIsSIMD(tree) || + (tree->OperGet() == GT_SIMD && tree->TypeGet() == TYP_STRUCT) || + isSIMDTypeLocal(tree); + } + + // Returns true if the type of the tree is a byref of TYP_SIMD + bool isAddrOfSIMDType(GenTree* tree) + { + if (tree->TypeGet() == TYP_BYREF || tree->TypeGet() == TYP_I_IMPL) + { + switch(tree->OperGet()) + { + case GT_ADDR: + return isSIMDType(tree->gtGetOp1()); + + case GT_LCL_VAR_ADDR: + return lvaTable[tree->AsLclVarCommon()->gtLclNum].lvSIMDType; + + default: + return isSIMDTypeLocal(tree); + } + } + + return false; + } + + static bool isRelOpSIMDIntrinsic(SIMDIntrinsicID intrinsicId) + { + return (intrinsicId == SIMDIntrinsicEqual || + intrinsicId == SIMDIntrinsicLessThan || + intrinsicId == SIMDIntrinsicLessThanOrEqual || + intrinsicId == SIMDIntrinsicGreaterThan || + intrinsicId == SIMDIntrinsicGreaterThanOrEqual + ); + } + + // Returns base type of a TYP_SIMD local. + // Returns TYP_UNKNOWN if the local is not TYP_SIMD. + var_types getBaseTypeOfSIMDLocal(GenTree* tree) + { + if (isSIMDTypeLocal(tree)) + { + return lvaTable[tree->AsLclVarCommon()->gtLclNum].lvBaseType; + } + + return TYP_UNKNOWN; + } + +#ifdef RYUJIT_CTPBUILD + // Note that, although the type handles are instance members of Compiler, the + // assembly handle is a static. This is so that we can avoid checking the + // assembly name at every call. + static volatile CORINFO_ASSEMBLY_HANDLE SIMDAssemblyHandle; + bool isSIMDModule(CORINFO_MODULE_HANDLE moduleHnd); +#endif // RYUJIT_CTPBUILD + + bool isSIMDClass(CORINFO_CLASS_HANDLE clsHnd) + { +#ifdef RYUJIT_CTPBUILD + return isSIMDModule(info.compCompHnd->getClassModule(clsHnd)); +#else // !RYUJIT_CTPBUILD + return info.compCompHnd->isInSIMDModule(clsHnd); +#endif // !RYUJIT_CTPBUILD + } + + bool isSIMDClass(typeInfo* pTypeInfo) + { + return pTypeInfo->IsStruct() && isSIMDClass(pTypeInfo->GetClassHandleForValueClass()); + } + + // Get the base (element) type and size in bytes for a SIMD type. Returns TYP_UNKNOWN + // if it is not a SIMD type or is an unsupported base type. + var_types getBaseTypeAndSizeOfSIMDType(CORINFO_CLASS_HANDLE typeHnd, + unsigned *sizeBytes = nullptr); + + var_types getBaseTypeOfSIMDType(CORINFO_CLASS_HANDLE typeHnd) + { + return getBaseTypeAndSizeOfSIMDType(typeHnd, nullptr); + } + + // Get SIMD Intrinsic info given the method handle. + // Also sets typeHnd, argCount, baseType and sizeBytes out params. + const SIMDIntrinsicInfo* getSIMDIntrinsicInfo(CORINFO_CLASS_HANDLE* typeHnd, + CORINFO_METHOD_HANDLE methodHnd, + CORINFO_SIG_INFO * sig, + bool isNewObj, + unsigned* argCount, + var_types* baseType, + unsigned* sizeBytes); + + // Pops and returns GenTree node from importers type stack. + // Normalizes TYP_STRUCT value in case of GT_CALL, GT_RET_EXPR and arg nodes. + GenTreePtr impSIMDPopStack(bool expectAddr = false); + + // Create a GT_SIMD tree for a Get property of SIMD vector with a fixed index. + GenTreeSIMD* impSIMDGetFixed(var_types baseType, + unsigned simdSize, + int index); + + // Creates a GT_SIMD tree for Select operation + GenTreePtr impSIMDSelect(CORINFO_CLASS_HANDLE typeHnd, + var_types baseType, + unsigned simdVectorSize, + GenTree* op1, + GenTree* op2, + GenTree* op3); + + // Creates a GT_SIMD tree for Min/Max operation + GenTreePtr impSIMDMinMax(SIMDIntrinsicID intrinsicId, + CORINFO_CLASS_HANDLE typeHnd, + var_types baseType, + unsigned simdVectorSize, + GenTree* op1, + GenTree* op2); + + // Transforms operands and returns the SIMD intrinsic to be applied on + // transformed operands to obtain given relop result. + SIMDIntrinsicID impSIMDRelOp(SIMDIntrinsicID relOpIntrinsicId, + CORINFO_CLASS_HANDLE typeHnd, + unsigned simdVectorSize, + var_types* baseType, + GenTree** op1, + GenTree** op2); + +#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) + // Transforms operands and returns the SIMD intrinsic to be applied on + // transformed operands to obtain == comparison result. + SIMDIntrinsicID impSIMDLongRelOpEqual(CORINFO_CLASS_HANDLE typeHnd, + unsigned simdVectorSize, + GenTree** op1, + GenTree** op2); + + // Transforms operands and returns the SIMD intrinsic to be applied on + // transformed operands to obtain > comparison result. + SIMDIntrinsicID impSIMDLongRelOpGreaterThan(CORINFO_CLASS_HANDLE typeHnd, + unsigned simdVectorSize, + GenTree** op1, + GenTree** op2); + + // Transforms operands and returns the SIMD intrinsic to be applied on + // transformed operands to obtain >= comparison result. + SIMDIntrinsicID impSIMDLongRelOpGreaterThanOrEqual(CORINFO_CLASS_HANDLE typeHnd, + unsigned simdVectorSize, + GenTree** op1, + GenTree** op2); + + // Transforms operands and returns the SIMD intrinsic to be applied on + // transformed operands to obtain >= comparison result in case of int32 + // and small int base type vectors. + SIMDIntrinsicID impSIMDIntegralRelOpGreaterThanOrEqual(CORINFO_CLASS_HANDLE typeHnd, + unsigned simdVectorSize, + var_types baseType, + GenTree** op1, + GenTree** op2); +#endif //defined(_TARGET_AMD64_) && !defined(LEGACY_BACKEND) + + void setLclRelatedToSIMDIntrinsic(GenTreePtr tree); + bool areFieldsContiguous(GenTreePtr op1, GenTreePtr op2); + bool areArrayElementsLocatedContiguously(GenTreePtr op1, GenTreePtr op2); + bool areArgumentsLocatedContiguously(GenTreePtr op1, GenTreePtr op2); + GenTreePtr createAddressNodeForSIMDInit(GenTreePtr tree, unsigned simdSize); + + // check methodHnd to see if it is a SIMD method that is expanded as an intrinsic in the JIT. + GenTreePtr impSIMDIntrinsic(OPCODE opcode, + GenTreePtr newobjThis, + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_METHOD_HANDLE method, + CORINFO_SIG_INFO * sig, + int memberRef); + + GenTreePtr getOp1ForConstructor(OPCODE opcode, + GenTreePtr newobjThis, + CORINFO_CLASS_HANDLE clsHnd); + + // Whether SIMD vector occupies part of SIMD register. + // SSE2: vector2f/3f are considered sub register SIMD types. + // AVX: vector2f, 3f and 4f are all considered sub register SIMD types. + bool isSubRegisterSIMDType(CORINFO_CLASS_HANDLE typeHnd) + { + unsigned sizeBytes = 0; + var_types baseType = getBaseTypeAndSizeOfSIMDType(typeHnd, &sizeBytes); + return (baseType == TYP_FLOAT) && (sizeBytes < getSIMDVectorRegisterByteLength()); + } + + bool isSubRegisterSIMDType(GenTreeSIMD* simdNode) + { + return (simdNode->gtSIMDSize < getSIMDVectorRegisterByteLength()); + } + + // Get the type for the hardware SIMD vector + var_types getSIMDVectorType() + { +#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) + if (canUseAVX()) + { + return TYP_SIMD32; + } + else + { + assert(canUseSSE2()); + return TYP_SIMD16; + } +#else + assert(!"getSIMDVectorType() unimplemented on target arch"); + unreached(); +#endif + } + + // Get the size of the SIMD type in bytes + int getSIMDTypeSizeInBytes(CORINFO_CLASS_HANDLE typeHnd) + { + unsigned sizeBytes = 0; + (void)getBaseTypeAndSizeOfSIMDType(typeHnd, &sizeBytes); + return sizeBytes; + } + + // Get the the number of elements of basetype of SIMD vector given by its size and baseType + static int getSIMDVectorLength(unsigned simdSize, var_types baseType); + + // Get the the number of elements of basetype of SIMD vector given by its type handle + int getSIMDVectorLength(CORINFO_CLASS_HANDLE typeHnd); + + // Get preferred alignment of SIMD type given by its type handle + int getSIMDTypeAlignment(CORINFO_CLASS_HANDLE typeHnd); + + // Get the number of bytes in a SIMD Vector. + unsigned getSIMDVectorRegisterByteLength() + { +#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) + if (canUseAVX()) + { + return YMM_REGSIZE_BYTES; + } + else + { + assert(canUseSSE2()); + return XMM_REGSIZE_BYTES; + } +#else + assert(!"getSIMDVectorRegisterByteLength() unimplemented on target arch"); + unreached(); +#endif + } + + // Returns the codegen type for a given SIMD size. + // TODO-Cleanup: Either eliminate this, once we have "plumbed" the SIMD types all the way + // through the JIT, or consider having different TYP_XX for the various sizes. + var_types getSIMDTypeForSize(unsigned size) + { + var_types simdType = TYP_UNDEF; + if (size == 8) + { + simdType = TYP_DOUBLE; + } + else if (size == 12) + { + simdType = TYP_SIMD12; + } + else if (size == 16) + { + simdType = TYP_SIMD16; + } +#ifdef FEATURE_AVX_SUPPORT + else if (size == 32) + { + simdType = TYP_SIMD32; + } +#endif // FEATURE_AVX_SUPPORT + else + { + noway_assert(!"Unexpected size for SIMD type"); + } + return simdType; + } + + unsigned getSIMDInitTempVarNum() + { + if (lvaSIMDInitTempVarNum == BAD_VAR_NUM) + { + lvaSIMDInitTempVarNum = lvaGrabTempWithImplicitUse(false DEBUGARG("SIMDInitTempVar")); + lvaTable[lvaSIMDInitTempVarNum].lvType = getSIMDVectorType(); + } + return lvaSIMDInitTempVarNum; + } + +#endif // FEATURE_SIMD + + // These routines need not be enclosed under FEATURE_SIMD since lvIsSIMDType() + // is defined for both FEATURE_SIMD and !FEATURE_SIMD apropriately. The use + // of this routines also avoids the need of #ifdef FEATURE_SIMD specific code. + + // Is this var is of type simd struct? + bool lclVarIsSIMDType(unsigned varNum) + { + LclVarDsc* varDsc = lvaTable + varNum; + return varDsc->lvIsSIMDType(); + } + + // Is this Local node a SIMD local? + bool lclVarIsSIMDType(GenTreeLclVarCommon* lclVarTree) + { + return lclVarIsSIMDType(lclVarTree->gtLclNum); + } + + // Returns true if the TYP_SIMD locals on stack are aligned at their + // preferred byte boundary specified by getSIMDTypeAlignment(). + bool isSIMDTypeLocalAligned(unsigned varNum) + { +#if defined(FEATURE_SIMD) && ALIGN_SIMD_TYPES + if (lclVarIsSIMDType(varNum) && lvaTable[varNum].lvType != TYP_BYREF) + { + bool ebpBased; + int off = lvaFrameAddress(varNum, &ebpBased); + // TODO-Cleanup: Can't this use the lvExactSize on the varDsc? + int alignment = getSIMDTypeAlignment(lvaTable[varNum].lvVerTypeInfo.GetClassHandle()); + bool isAligned = ((off % alignment) == 0); + noway_assert (isAligned || lvaTable[varNum].lvIsParam); + return isAligned; + } +#endif // FEATURE_SIMD + + return false; + } + + // Whether SSE2 is available + bool canUseSSE2() const + { +#ifdef _TARGET_XARCH_ + return opts.compCanUseSSE2; +#else + return false; +#endif + } + + bool canUseAVX() const + { +#ifdef FEATURE_AVX_SUPPORT + return opts.compCanUseAVX; +#else + return false; +#endif + } + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Compiler XX +XX XX +XX Generic info about the compilation and the method being compiled. XX +XX It is responsible for driving the other phases. XX +XX It is also responsible for all the memory management. XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +public : + + Compiler * InlineeCompiler; // The Compiler instance for the inlinee + + JitInlineResult compInlineResult; // The result of importing the inlinee method. + + bool compJmpOpUsed; // Does the method do a JMP + bool compLongUsed; // Does the method use TYP_LONG + bool compFloatingPointUsed; // Does the method use TYP_FLOAT or TYP_DOUBLE + bool compTailCallUsed; // Does the method do a tailcall + bool compLocallocUsed; // Does the method use localloc. + bool compQmarkUsed; // Does the method use GT_QMARK/GT_COLON + bool compQmarkRationalized; // Is it allowed to use a GT_QMARK/GT_COLON node. + bool compUnsafeCastUsed; // Does the method use LDIND/STIND to cast between scalar/refernce types + // NOTE: These values are only reliable after + // the importing is completely finished. + + ExpandArrayStack<GenTreePtr>* compQMarks; // The set of QMark nodes created in the current compilation, so + // we can iterate over these efficiently. + +#if CPU_USES_BLOCK_MOVE + bool compBlkOpUsed; // Does the method do a COPYBLK or INITBLK +#endif + + // State information - which phases have completed? + // These are kept together for easy discoverability + +#ifdef DEBUG + bool bRangeAllowStress; + bool compCodeGenDone; + int64_t compNumStatementLinksTraversed; // # of links traversed while doing debug checks + bool fgNormalizeEHDone; // Has the flowgraph EH normalization phase been done? +#endif // DEBUG + + bool fgLocalVarLivenessDone; // Note that this one is used outside of debug. + bool fgLocalVarLivenessChanged; +#if STACK_PROBES + bool compStackProbePrologDone; +#endif +#ifndef LEGACY_BACKEND + bool compLSRADone; +#endif // !LEGACY_BACKEND + bool compRationalIRForm; + + bool compGeneratingProlog; + bool compGeneratingEpilog; + bool compNeedsGSSecurityCookie; // There is an unsafe buffer (or localloc) on the stack. + // Insert cookie on frame and code to check the cookie, like VC++ -GS. + bool compGSReorderStackLayout; // There is an unsafe buffer on the stack, reorder locals and make local + // copies of susceptible parameters to avoid buffer overrun attacks through locals/params + bool getNeedsGSSecurityCookie() const { return compNeedsGSSecurityCookie; } + void setNeedsGSSecurityCookie() { compNeedsGSSecurityCookie = true; } + + FrameLayoutState lvaDoneFrameLayout; // The highest frame layout state that we've completed. During frame layout calculations, + // this is the level we are currently computing. + + //---------------------------- JITing options ----------------------------- + + enum codeOptimize + { + BLENDED_CODE, + SMALL_CODE, + FAST_CODE, + + COUNT_OPT_CODE + }; + + struct Options + { + unsigned eeFlags; // flags passed from the EE + unsigned compFlags; // method attributes + + codeOptimize compCodeOpt; // what type of code optimizations + + bool compUseFCOMI; + bool compUseCMOV; +#ifdef _TARGET_XARCH_ + bool compCanUseSSE2; // Allow CodeGen to use "movq XMM" instructions + +#ifdef FEATURE_AVX_SUPPORT + bool compCanUseAVX; // Allow CodeGen to use AVX 256-bit vectors for SIMD operations +#endif +#endif + + // optimize maximally and/or favor speed over size? + +#define DEFAULT_MIN_OPTS_CODE_SIZE 60000 +#define DEFAULT_MIN_OPTS_INSTR_COUNT 20000 +#define DEFAULT_MIN_OPTS_BB_COUNT 2000 +#define DEFAULT_MIN_OPTS_LV_NUM_COUNT 2000 +#define DEFAULT_MIN_OPTS_LV_REF_COUNT 8000 + +// Maximun number of locals before turning off the inlining +#define MAX_LV_NUM_COUNT_FOR_INLINING 512 + + bool compMinOpts; + unsigned instrCount; + unsigned lvRefCount; +# ifdef DEBUG + bool compMinOptsIsSet; + bool compMinOptsIsUsed; + + inline void SetMinOpts(bool val) + { assert(!compMinOptsIsUsed); + assert(!compMinOptsIsSet || (compMinOpts == val)); + compMinOpts = val; + compMinOptsIsSet = true; + } + inline bool MinOpts() + { assert(compMinOptsIsSet); + compMinOptsIsUsed = true; + return compMinOpts; + } +# else // !DEBUG + inline bool MinOpts() { return compMinOpts; } + inline void SetMinOpts(bool val) { compMinOpts = val; } +# endif // !DEBUG + + //true if the CLFLG_* for an optimization is set. + inline bool OptEnabled(unsigned optFlag) { return !!(compFlags & optFlag); } + +#ifdef FEATURE_READYTORUN_COMPILER + inline bool IsReadyToRun() { return (eeFlags & CORJIT_FLG_READYTORUN) != 0; } +#else + inline bool IsReadyToRun() { return false; } +#endif + +#ifdef DEBUGGING_SUPPORT + bool compScopeInfo; // Generate the LocalVar info ? + bool compDbgCode; // Generate debugger-friendly code? + bool compDbgInfo; // Gather debugging info? + bool compDbgEnC; +#else + static const bool compDbgCode; +#endif + +#ifdef PROFILING_SUPPORTED + bool compNoPInvokeInlineCB; +#else + static const bool compNoPInvokeInlineCB; +#endif + + bool compMustInlinePInvokeCalli; // Unmanaged CALLI in IL stubs must be inlined + +#ifdef DEBUG + bool compGcChecks; // Check arguments and return values to ensure they are sane + bool compStackCheckOnRet; // Check ESP on return to ensure it is correct + bool compStackCheckOnCall; // Check ESP after every call to ensure it is correct + +#endif + + bool compNeedSecurityCheck; // This flag really means where or not a security object needs + // to be allocated on the stack. + + // It will be set to true in the following cases: + // 1. When the method being compiled has a declarative security + // (i.e. when CORINFO_FLG_NOSECURITYWRAP is reset for the current method). + // This is also the case when we inject a prolog and epilog in the method. + // (or) + // 2. When the method being compiled has imperative security (i.e. the method + // calls into another method that has CORINFO_FLG_SECURITYCHECK flag set). + // (or) + // 3. When opts.compDbgEnC is true. (See also Compiler::compCompile). + // + // When this flag is set, jit will allocate a gc-reference local variable (lvaSecurityObject), + // which gets reported as a GC root to stackwalker. + // (See also ICodeManager::GetAddrOfSecurityObject.) + + +#if RELOC_SUPPORT + bool compReloc; +#endif +#ifdef UNIX_AMD64_ABI + // This flag is indicating if there is a need to align the frame. + // On AMD64-Windows, if there are calls, 4 slots for the outgoing ars are allocated, except for + // FastTailCall. This slots makes the frame size non-zero, so alignment logic will be called. + // On AMD64-Unix, there are no such slots. There is a possibility to have calls in the method with frame size of 0. + // The frame alignment logic won't kick in. This flags takes care of the AMD64-Unix case by remembering that there + // are calls and making sure the frame alignment logic is executed. + bool compNeedToAlignFrame; +#endif // UNIX_AMD64_ABI + bool compProcedureSplitting; // Separate cold code from hot code + + bool genFPorder; // Preserve FP order (operations are non-commutative) + bool genFPopt; // Can we do frame-pointer-omission optimization? + bool altJit; // True if we are an altjit and are compiling this method + +#ifdef DEBUG + bool compProcedureSplittingEH; // Separate cold code from hot code for functions with EH + bool dspCode; // Display native code generated + bool dspEHTable; // Display the EH table reported to the VM + bool dspInstrs; // Display the IL instructions intermixed with the native code output + bool dspEmit; // Display emitter output + bool dspLines; // Display source-code lines intermixed with native code output + bool dmpHex; // Display raw bytes in hex of native code output + bool varNames; // Display variables names in native code output + bool disAsm; // Display native code as it is generated + bool disAsmSpilled; // Display native code when any register spilling occurs + bool disDiffable; // Makes the Disassembly code 'diff-able' + bool disAsm2; // Display native code after it is generated using external disassembler + bool dspOrder; // Display names of each of the methods that we ngen/jit + bool dspUnwind; // Display the unwind info output + bool dspDiffable; // Makes the Jit Dump 'diff-able' (currently uses same COMPLUS_* flag as disDiffable) + bool compLargeBranches; // Force using large conditional branches + bool dspGCtbls; // Display the GC tables +#endif + +#ifdef LATE_DISASM + bool doLateDisasm; // Run the late disassembler +#endif // LATE_DISASM + +#if DUMP_GC_TABLES && !defined(DEBUG) && defined(JIT32_GCENCODER) + // Only the JIT32_GCENCODER implements GC dumping in non-DEBUG code. + #pragma message("NOTE: this non-debug build has GC ptr table dumping always enabled!") + static const bool dspGCtbls = true; +#endif + + // We need stack probes to guarantee that we won't trigger a stack overflow + // when calling unmanaged code until they get a chance to set up a frame, because + // the EE will have no idea where it is. + // + // We will only be doing this currently for hosted environments. Unfortunately + // we need to take care of stubs, so potentially, we will have to do the probes + // for any call. We have a plan for not needing for stubs though + bool compNeedStackProbes; + + // Whether to emit Enter/Leave/TailCall hooks using a dummy stub (DummyProfilerELTStub()) + // This options helps one to make JIT behave as if it is under profiler. + bool compJitELTHookEnabled; + +#if FEATURE_TAILCALL_OPT + // Whether opportunistic or implicit tail call optimization is enabled. + bool compTailCallOpt; +#endif + + GCPollType compGCPollType; + } + opts; + +#ifdef ALT_JIT + static AssemblyNamesList2* s_pAltJitExcludeAssembliesList; +#endif // ALT_JIT + +#ifdef DEBUG + + static bool s_dspMemStats; // Display per-phase memory statistics for every function + + template<typename T> + T dspPtr(T p) + { + return (p == 0) ? 0 : (opts.dspDiffable ? T(0xD1FFAB1E) : p); + } + + template<typename T> + T dspOffset(T o) + { + return (o == 0) ? 0 : (opts.dspDiffable ? T(0xD1FFAB1E) : o); + } + + static int dspTreeID(GenTree* tree) + { + return tree->gtTreeID; + } + static void printTreeID(GenTree* tree) + { + if (tree == nullptr) + { + printf("[------]"); + } + else + { + printf("[%06d]", dspTreeID(tree)); + } + } + +#endif // DEBUG + + + +#define STRESS_MODES \ + \ + STRESS_MODE(NONE) \ + \ + /* "Variations" stress areas which we try to mix up with each other. */ \ + /* These should not be exhaustively used as they might */ \ + /* hide/trivialize other areas */ \ + \ + STRESS_MODE(REGS) STRESS_MODE(DBL_ALN) STRESS_MODE(LCL_FLDS) STRESS_MODE(UNROLL_LOOPS) \ + STRESS_MODE(MAKE_CSE) STRESS_MODE(ENREG_FP) STRESS_MODE(INLINE) STRESS_MODE(CLONE_EXPR) \ + STRESS_MODE(USE_FCOMI) STRESS_MODE(USE_CMOV) STRESS_MODE(FOLD) \ + STRESS_MODE(BB_PROFILE) STRESS_MODE(OPT_BOOLS_GC) STRESS_MODE(REMORPH_TREES) \ + STRESS_MODE(64RSLT_MUL) STRESS_MODE(DO_WHILE_LOOPS) STRESS_MODE(MIN_OPTS) \ + STRESS_MODE(REVERSE_FLAG) /* Will set GTF_REVERSE_OPS whenever we can */ \ + STRESS_MODE(REVERSE_COMMA) /* Will reverse commas created with gtNewCommaNode */ \ + STRESS_MODE(TAILCALL) /* Will make the call as a tailcall whenever legal */ \ + STRESS_MODE(CATCH_ARG) /* Will spill catch arg */ \ + STRESS_MODE(UNSAFE_BUFFER_CHECKS) \ + STRESS_MODE(NULL_OBJECT_CHECK) \ + STRESS_MODE(PINVOKE_RESTORE_ESP) \ + \ + STRESS_MODE(GENERIC_VARN) STRESS_MODE(COUNT_VARN) \ + \ + /* "Check" stress areas that can be exhaustively used if we */ \ + /* dont care about performance at all */ \ + \ + STRESS_MODE(FORCE_INLINE) /* Treat every method as AggressiveInlining */ \ + STRESS_MODE(CHK_FLOW_UPDATE) \ + STRESS_MODE(EMITTER) STRESS_MODE(CHK_REIMPORT) STRESS_MODE(FLATFP) \ + \ + STRESS_MODE(GENERIC_CHECK) STRESS_MODE(COUNT) \ + + enum compStressArea + { +#define STRESS_MODE(mode) STRESS_##mode, + STRESS_MODES +#undef STRESS_MODE + }; + +#ifdef DEBUG + static + const LPCWSTR s_compStressModeNames[STRESS_COUNT + 1]; + BYTE compActiveStressModes[STRESS_COUNT]; +#endif // DEBUG + + #define MAX_STRESS_WEIGHT 100 + + bool compStressCompile(compStressArea stressArea, + unsigned weightPercentage); + + bool compTailCallStress() + { +#ifdef DEBUG + static ConfigDWORD fTailcallStress; + return (fTailcallStress.val(CLRConfig::INTERNAL_TailcallStress) !=0 + || compStressCompile(STRESS_TAILCALL, 5) + ); +#else + return false; +#endif + } + + codeOptimize compCodeOpt() + { +#if 0 + // Switching between size & speed has measurable throughput impact + // (3.5% on NGen mscorlib when measured). It used to be enabled for + // DEBUG, but should generate identical code between CHK & RET builds, + // so that's not acceptable. + // TODO-Throughput: Figure out what to do about size vs. speed & throughput. + // Investigate the cause of the throughput regression. + + return opts.compCodeOpt; +#else + return BLENDED_CODE; +#endif + } + + //--------------------- Info about the procedure -------------------------- + + struct Info + { + COMP_HANDLE compCompHnd; + CORINFO_MODULE_HANDLE compScopeHnd; + CORINFO_CLASS_HANDLE compClassHnd; + CORINFO_METHOD_HANDLE compMethodHnd; + CORINFO_METHOD_INFO* compMethodInfo; + + BOOL hasCircularClassConstraints; + BOOL hasCircularMethodConstraints; + +#if defined(DEBUG) || defined(LATE_DISASM) + const char* compMethodName; + const char* compClassName; + const char* compFullName; +#endif // defined(DEBUG) || defined(LATE_DISASM) + +#ifdef DEBUG + unsigned compMethodHashPrivate; + unsigned compMethodHash(); +#endif + +#ifdef PSEUDORANDOM_NOP_INSERTION + // things for pseudorandom nop insertion + unsigned compChecksum; + CLRRandom compRNG; +#endif + + // The following holds the FLG_xxxx flags for the method we're compiling. + unsigned compFlags; + + // The following holds the class attributes for the method we're compiling. + unsigned compClassAttr; + + const BYTE * compCode; + IL_OFFSET compILCodeSize; // The IL code size + UNATIVE_OFFSET compNativeCodeSize; // The native code size, after instructions are issued. This + // is less than (compTotalHotCodeSize + compTotalColdCodeSize) only if: + // (1) the code is not hot/cold split, and we issued less code than we expected, or + // (2) the code is hot/cold split, and we issued less code than we expected + // in the cold section (the hot section will always be padded out to compTotalHotCodeSize). + + bool compIsStatic : 1; // Is the method static (no 'this' pointer)? + bool compIsVarArgs : 1; // Does the method have varargs parameters? + bool compIsContextful : 1; // contextful method + bool compInitMem : 1; // Is the CORINFO_OPT_INIT_LOCALS bit set in the method info options? + bool compUnwrapContextful: 1; // JIT should unwrap proxies when possible + bool compProfilerCallback: 1; // JIT inserted a profiler Enter callback + bool compPublishStubParam: 1; // EAX captured in prolog will be available through an instrinsic + bool compRetBuffDefStack: 1; // The ret buff argument definitely points into the stack. + + var_types compRetType; + var_types compRetNativeType; + unsigned compILargsCount; // Number of arguments (incl. implicit but not hidden) + unsigned compArgsCount; // Number of arguments (incl. implicit and hidden) + unsigned compRetBuffArg; // position of hidden return param var (0, 1) (BAD_VAR_NUM means not present); + int compTypeCtxtArg; // position of hidden param for type context for generic code (CORINFO_CALLCONV_PARAMTYPE) + unsigned compThisArg; // position of implicit this pointer param (not to be confused with lvaArg0Var) + unsigned compILlocalsCount; // Number of vars : args + locals (incl. implicit but not hidden) + unsigned compLocalsCount; // Number of vars : args + locals (incl. implicit and hidden) + unsigned compMaxStack; + UNATIVE_OFFSET compTotalHotCodeSize; // Total number of bytes of Hot Code in the method + UNATIVE_OFFSET compTotalColdCodeSize; // Total number of bytes of Cold Code in the method + +#if INLINE_NDIRECT + unsigned compCallUnmanaged; // count of unmanaged calls + unsigned compLvFrameListRoot; // lclNum for the Frame root +#endif + unsigned compXcptnsCount; // Number of exception-handling clauses read in the method's IL. + // You should generally use compHndBBtabCount instead: it is the + // current number of EH clauses (after additions like synchronized + // methods and funclets, and removals like unreachable code deletion). + + bool compMatchedVM; // true if the VM is "matched": either the JIT is a cross-compiler and the VM expects that, + // or the JIT is a "self-host" compiler (e.g., x86 hosted targeting x86) and the VM expects that. + +#if defined(DEBUGGING_SUPPORT) || defined(DEBUG) + + /* The following holds IL scope information about local variables. + */ + + unsigned compVarScopesCount; + VarScopeDsc* compVarScopes; + + /* The following holds information about instr offsets for + * which we need to report IP-mappings + */ + + IL_OFFSET * compStmtOffsets; // sorted + unsigned compStmtOffsetsCount; + ICorDebugInfo::BoundaryTypes compStmtOffsetsImplicit; + +#endif // DEBUGGING_SUPPORT || DEBUG + + #define CPU_X86 0x0100 // The generic X86 CPU + #define CPU_X86_PENTIUM_4 0x0110 + + #define CPU_X64 0x0200 // The generic x64 CPU + #define CPU_AMD_X64 0x0210 // AMD x64 CPU + #define CPU_INTEL_X64 0x0240 // Intel x64 CPU + + #define CPU_ARM 0x0300 // The generic ARM CPU + + unsigned genCPU; // What CPU are we running on + } + info; + +#if defined(DEBUG) + + void compDispLocalVars(); + +#endif // DEBUGGING_SUPPORT || DEBUG + + //-------------------------- Global Compiler Data ------------------------------------ + +#ifdef DEBUG + static unsigned s_compMethodsCount; // to produce unique label names + unsigned compGenTreeID; +#endif + + BasicBlock * compCurBB; // the current basic block in process + GenTreePtr compCurStmt; // the current statement in process +#ifdef DEBUG + unsigned compCurStmtNum; // to give all statements an increasing StmtNum when printing dumps +#endif + + // The following is used to create the 'method JIT info' block. + size_t compInfoBlkSize; + BYTE * compInfoBlkAddr; + + EHblkDsc * compHndBBtab; // array of EH data + unsigned compHndBBtabCount; // element count of used elements in EH data array + unsigned compHndBBtabAllocCount; // element count of allocated elements in EH data array + +#if defined(_TARGET_X86_) + + //------------------------------------------------------------------------- + // Tracking of region covered by the monitor in synchronized methods + void* syncStartEmitCookie; // the emitter cookie for first instruction after the call to MON_ENTER + void* syncEndEmitCookie; // the emitter cookie for first instruction after the call to MON_EXIT + +#endif // !_TARGET_X86_ + + Phases previousCompletedPhase; // the most recently completed phase + + //------------------------------------------------------------------------- + // The following keeps track of how many bytes of local frame space we've + // grabbed so far in the current function, and how many argument bytes we + // need to pop when we return. + // + + unsigned compLclFrameSize; // secObject+lclBlk+locals+temps + + // Count of callee-saved regs we pushed in the prolog. + // Does not include EBP for isFramePointerUsed() and double-aligned frames. + // In case of Amd64 this doesn't include float regs saved on stack. + unsigned compCalleeRegsPushed; + +#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) + // Mask of callee saved float regs on stack. + regMaskTP compCalleeFPRegsSavedMask; + + // Quirk for VS debug-launch scenario to work: + // Bytes of padding between save-reg area and locals. + #define VSQUIRK_STACK_PAD (2*REGSIZE_BYTES) + unsigned compVSQuirkStackPaddingNeeded; +#endif + + unsigned compArgSize; // total size of arguments in bytes (including register args (lvIsRegArg)) + + unsigned compMapILargNum (unsigned ILargNum); // map accounting for hidden args + unsigned compMapILvarNum (unsigned ILvarNum); // map accounting for hidden args + unsigned compMap2ILvarNum(unsigned varNum); // map accounting for hidden args + + //------------------------------------------------------------------------- + + static void compStartup (); // One-time initialization + static void compShutdown (); // One-time finalization + + void compInit (norls_allocator * pAlloc, InlineInfo * inlineInfo); + void compDone (); + + static void compDisplayStaticSizes(FILE* fout); + + //------------ Some utility functions -------------- + + void* compGetHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ + void ** ppIndirection); /* OUT */ + + // Several JIT/EE interface functions return a CorInfoType, and also return a + // class handle as an out parameter if the type is a value class. Returns the + // size of the type these describe. + unsigned compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd); + +#ifdef DEBUG + // Components used by the compiler may write unit test suites, and + // have them run within this method. They will be run only once per process, and only + // in debug. (Perhaps should be under the control of a COMPLUS flag.) + // These should fail by asserting. + void compDoComponentUnitTestsOnce(); +#endif // DEBUG + + int compCompile (CORINFO_METHOD_HANDLE methodHnd, + CORINFO_MODULE_HANDLE classPtr, + COMP_HANDLE compHnd, + CORINFO_METHOD_INFO * methodInfo, + void * * methodCodePtr, + ULONG * methodCodeSize, + unsigned compileFlags); + void compCompileFinish(); + int compCompileHelper (CORINFO_MODULE_HANDLE classPtr, + COMP_HANDLE compHnd, + CORINFO_METHOD_INFO * methodInfo, + void * * methodCodePtr, + ULONG * methodCodeSize, + unsigned compileFlags, + CorInfoInstantiationVerification instVerInfo); + + norls_allocator * compGetAllocator(); + +#if MEASURE_MEM_ALLOC + struct MemStats + { + unsigned allocCnt; // # of allocs + UINT64 allocSz; // total size of those alloc. + UINT64 allocSzMax; // Maximum single allocation. + UINT64 allocSzByKind[CMK_Count]; // Classified by "kind". + UINT64 nraTotalSizeAlloc; + UINT64 nraTotalSizeUsed; + + static const char* s_CompMemKindNames[]; // Names of the kinds. + + MemStats() + : allocCnt(0), allocSz(0), allocSzMax(0), nraTotalSizeAlloc(0), nraTotalSizeUsed(0) + { + for (int i = 0; i < CMK_Count; i++) allocSzByKind[i] = 0; + } + MemStats(const MemStats& ms) + : allocCnt(ms.allocCnt), allocSz(ms.allocSz), allocSzMax(ms.allocSzMax), nraTotalSizeAlloc(ms.nraTotalSizeAlloc), nraTotalSizeUsed(ms.nraTotalSizeUsed) + { + for (int i = 0; i < CMK_Count; i++) allocSzByKind[i] = ms.allocSzByKind[i]; + } + + // Until we have ubiquitous constructors. + void Init() + { + this->MemStats::MemStats(); + } + + void AddAlloc(size_t sz, CompMemKind cmk) + { + allocCnt += 1; + allocSz += sz; + if (sz > allocSzMax) + { + allocSzMax = sz; + } + allocSzByKind[cmk] += sz; + } + + void Print(FILE* f); // Print these stats to f. + void PrintByKind(FILE* f); // Do just the by-kind histogram part. + }; + MemStats genMemStats; + + struct AggregateMemStats: public MemStats + { + unsigned nMethods; + + AggregateMemStats(): MemStats(), nMethods(0) {} + + void Add(const MemStats& ms) + { + nMethods++; + allocCnt += ms.allocCnt; + allocSz += ms.allocSz; + allocSzMax = max(allocSzMax, ms.allocSzMax); + for (int i = 0; i < CMK_Count; i++) allocSzByKind[i] += ms.allocSzByKind[i]; + nraTotalSizeAlloc += ms.nraTotalSizeAlloc; + nraTotalSizeUsed += ms.nraTotalSizeUsed; + } + + void Print(FILE* f); // Print these stats to stdout. + }; + + static CritSecObject s_memStatsLock; // This lock protects the data structures below. + static MemStats s_maxCompMemStats; // Stats for the compilation with the largest amount allocated. + static AggregateMemStats s_aggMemStats; // Aggregates statistics for all compilations. + +#endif // MEASURE_MEM_ALLOC + +#if LOOP_HOIST_STATS + unsigned m_loopsConsidered; + bool m_curLoopHasHoistedExpression; + unsigned m_loopsWithHoistedExpressions; + unsigned m_totalHoistedExpressions; + + void AddLoopHoistStats(); + void PrintPerMethodLoopHoistStats(); + + static CritSecObject s_loopHoistStatsLock; // This lock protects the data structures below. + static unsigned s_loopsConsidered; + static unsigned s_loopsWithHoistedExpressions; + static unsigned s_totalHoistedExpressions; + + static void PrintAggregateLoopHoistStats(FILE* f); +#endif // LOOP_HOIST_STATS + + void * compGetMemArray (size_t numElem, size_t elemSize, CompMemKind cmk = CMK_Unknown); + void * compGetMemArrayA (size_t numElem, size_t elemSize, CompMemKind cmk = CMK_Unknown); + void * compGetMem (size_t sz, CompMemKind cmk = CMK_Unknown); + void * compGetMemA (size_t sz, CompMemKind cmk = CMK_Unknown); + static + void * compGetMemCallback (void *, size_t, CompMemKind cmk = CMK_Unknown); + void compFreeMem (void *); + + bool compIsForImportOnly(); + bool compIsForInlining(); + void compSetInlineResult(const JitInlineResult& result); + bool compDonotInline(); + +#ifdef DEBUG + const char * compLocalVarName (unsigned varNum, unsigned offs); + VarName compVarName (regNumber reg, + bool isFloatReg = false); + const char * compRegVarName (regNumber reg, + bool displayVar = false, + bool isFloatReg = false); + const char * compRegPairName (regPairNo regPair); + const char * compRegNameForSize (regNumber reg, + size_t size); + const char * compFPregVarName (unsigned fpReg, + bool displayVar = false); + void compDspSrcLinesByNativeIP (UNATIVE_OFFSET curIP); + void compDspSrcLinesByLineNum (unsigned line, + bool seek = false); +#endif // DEBUG + + //------------------------------------------------------------------------- + +#ifdef DEBUGGING_SUPPORT + typedef ListNode<VarScopeDsc*> VarScopeListNode; + + struct VarScopeMapInfo + { + VarScopeListNode* head; + VarScopeListNode* tail; + static VarScopeMapInfo* Create(VarScopeListNode* node, IAllocator* alloc) + { + VarScopeMapInfo* info = new (alloc) VarScopeMapInfo; + info->head = node; + info->tail = node; + return info; + } + }; + + // Max value of scope count for which we would use linear search; for larger values we would use hashtable lookup. + static const unsigned MAX_LINEAR_FIND_LCL_SCOPELIST = 32; + + typedef SimplerHashTable<unsigned, SmallPrimitiveKeyFuncs<unsigned>, VarScopeMapInfo*, DefaultSimplerHashBehavior> VarNumToScopeDscMap; + + // Map to keep variables' scope indexed by varNum containing it's scope dscs at the index. + VarNumToScopeDscMap* compVarScopeMap; + + VarScopeDsc* compFindLocalVar(unsigned varNum, unsigned lifeBeg, unsigned lifeEnd); + + VarScopeDsc* compFindLocalVar(unsigned varNum, unsigned offs); + + VarScopeDsc* compFindLocalVarLinear(unsigned varNum, unsigned offs); + + void compInitVarScopeMap(); + + VarScopeDsc** compEnterScopeList; // List has the offsets where variables + // enter scope, sorted by instr offset + unsigned compNextEnterScope; + + VarScopeDsc** compExitScopeList; // List has the offsets where variables + // go out of scope, sorted by instr offset + unsigned compNextExitScope; + + + void compInitScopeLists (); + + void compResetScopeLists (); + + VarScopeDsc* compGetNextEnterScope (unsigned offs, bool scan=false); + + VarScopeDsc* compGetNextExitScope (unsigned offs, bool scan=false); + + void compProcessScopesUntil (unsigned offset, + VARSET_TP* inScope, + void (Compiler::*enterScopeFn)(VARSET_TP* inScope, VarScopeDsc*), + void (Compiler::*exitScopeFn) (VARSET_TP* inScope, VarScopeDsc*)); + +#ifdef DEBUG + void compDispScopeLists (); +#endif // DEBUG + +#endif // DEBUGGING_SUPPORT + + + bool compIsProfilerHookNeeded(); + + //------------------------------------------------------------------------- + /* Statistical Data Gathering */ + + void compJitStats(); // call this function and enable + // various ifdef's below for statistical data + +#if CALL_ARG_STATS + void compCallArgStats(); + static void compDispCallArgStats(FILE* fout); +#endif + + + //------------------------------------------------------------------------- + +protected : + +#ifdef DEBUG + bool skipMethod(); +#endif + + norls_allocator * compAllocator; + +public: + // This one presents an implementation of the "IAllocator" abstract class that uses "compAllocator", + // suitable for use by utilcode collection types. + IAllocator* compAsIAllocator; + +#if MEASURE_MEM_ALLOC + IAllocator* compAsIAllocatorBitset; // An allocator that uses the CMK_bitset tracker. + IAllocator* compAsIAllocatorGC; // An allocator that uses the CMK_GC tracker. + IAllocator* compAsIAllocatorLoopHoist; // An allocator that uses the CMK_LoopHoist tracker. +#ifdef DEBUG + IAllocator* compAsIAllocatorDebugOnly; // An allocator that uses the CMK_DebugOnly tracker. +#endif // DEBUG +#endif // MEASURE_MEM_ALLOC + +protected: + + unsigned compMaxUncheckedOffsetForNullObject; + + void compInitOptions (unsigned compileFlags); + + void compSetProcessor(); + void compInitDebuggingInfo(); + void compSetOptimizationLevel(); +#ifdef _TARGET_ARMARCH_ + bool compRsvdRegCheck(FrameLayoutState curState); +#endif + void compCompile (void * * methodCodePtr, + ULONG * methodCodeSize, + unsigned compileFlags); + + // Data required for generating profiler Enter/Leave/TailCall hooks +#ifdef PROFILING_SUPPORTED + bool compProfilerHookNeeded; // Whether profiler Enter/Leave/TailCall hook needs to be generated for the method + void *compProfilerMethHnd; // Profiler handle of the method being compiled. Passed as param to ELT callbacks + bool compProfilerMethHndIndirected; // Whether compProfilerHandle is pointer to the handle or is an actual handle +#endif + +public: + // Assumes called as part of process shutdown; does any compiler-specific work associated with that. + static void ProcessShutdownWork(ICorStaticInfo* statInfo); + + IAllocator* getAllocator() { return compAsIAllocator; } + +#if MEASURE_MEM_ALLOC + IAllocator* getAllocatorBitset() { return compAsIAllocatorBitset; } + IAllocator* getAllocatorGC() { return compAsIAllocatorGC; } + IAllocator* getAllocatorLoopHoist() { return compAsIAllocatorLoopHoist; } +#else // !MEASURE_MEM_ALLOC + IAllocator* getAllocatorBitset() { return compAsIAllocator; } + IAllocator* getAllocatorGC() { return compAsIAllocator; } + IAllocator* getAllocatorLoopHoist() { return compAsIAllocator; } +#endif // !MEASURE_MEM_ALLOC + +#ifdef DEBUG + IAllocator* getAllocatorDebugOnly() + { +#if MEASURE_MEM_ALLOC + return compAsIAllocatorDebugOnly; +#else // !MEASURE_MEM_ALLOC + return compAsIAllocator; +#endif // !MEASURE_MEM_ALLOC + } +#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 : + + // Set to TRUE if verification cannot be skipped for this method + // If we detect unverifiable code, we will lazily check + // canSkipMethodVerification() to see if verification is REALLY needed. + BOOL tiVerificationNeeded; + + // It it initially TRUE, and it gets set to FALSE if we run into unverifiable code + // Note that this is valid only if tiVerificationNeeded was ever TRUE. + BOOL tiIsVerifiableCode; + + // Set to TRUE if runtime callout is needed for this method + BOOL tiRuntimeCalloutNeeded; + + // Set to TRUE if security prolog/epilog callout is needed for this method + // Note: This flag is different than compNeedSecurityCheck. + // compNeedSecurityCheck means whether or not a security object needs + // to be allocated on the stack, which is currently true for EnC as well. + // tiSecurityCalloutNeeded means whether or not security callouts need + // to be inserted in the jitted code. + BOOL tiSecurityCalloutNeeded; + + // Returns TRUE if child is equal to or a subtype of parent for merge purposes + // This support is necessary to suport 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; + + // Set pDest from the primitive value type. + // Eg. System.Int32 -> ELEMENT_TYPE_I4 + + BOOL tiFromPrimitiveValueClass (typeInfo *pDest, + const typeInfo *pVC) const; + +#ifdef DEBUG + // <BUGNUM> VSW 471305 + // IJW allows assigning REF to BYREF. The following allows us to temporarily + // bypass the assert check in gcMarkRegSetGCref and gcMarkRegSetByref + // We use a "short" as we need to push/pop this scope. + // </BUGNUM> + short compRegSetCheckLevel; +#endif + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX IL verification stuff XX +XX XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +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; + + // Argument of ICorJitInfo::resolveToken. It is used to determine the handling + // of ICorJitInfo::resolveToken failure during verification. + CORINFO_RESOLVED_TOKEN * verResolveTokenInProgress; + + 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 verMakeTypeInfo(CORINFO_CLASS_HANDLE clsHnd, bool bashStructToRef = false); // converts from jit type representation to typeInfo + typeInfo verMakeTypeInfo(CorInfoType ciType, CORINFO_CLASS_HANDLE clsHnd); // converts from jit type representation to typeInfo + BOOL verIsSDArray(typeInfo ti); + typeInfo verGetArrayElemType(typeInfo ti); + + typeInfo verParseArgSigToTypeInfo(CORINFO_SIG_INFO* sig, + CORINFO_ARG_LIST_HANDLE args); + BOOL verNeedsVerification(); + 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)); + void verRaiseVerifyExceptionIfNeeded(INDEBUG(const char* reason) DEBUGARG(const char* file) 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 (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); + + + + // Register allocator + void raInitStackFP (); + void raEnregisterVarsPrePassStackFP (); + void raSetRegLclBirthDeath (GenTreePtr tree, VARSET_VALARG_TP lastlife, bool fromLDOBJ); + void raEnregisterVarsPostPassStackFP (); + void raGenerateFPRefCounts (); + void raEnregisterVarsStackFP (); + void raUpdateHeightsForVarsStackFP (VARSET_VALARG_TP mask); + + regNumber raRegForVarStackFP (unsigned varTrackedIndex); + void raAddPayloadStackFP (VARSET_VALARG_TP mask, unsigned weight); + + // returns true if enregistering v1 would save more mem accesses than v2 + bool raVarIsGreaterValueStackFP (LclVarDsc *lv1, LclVarDsc *lv2); + + + +#ifdef DEBUG + void raDumpHeightsStackFP(); + void raDumpVariableRegIntfFloat(); +#endif + +#if FEATURE_STACK_FP_X87 + + // Currently, we use FP transition blocks in only 2 situations: + // + // -conditional jump on longs where FP stack differs with target: it's not strictly + // necessary, but its low frequency and the code would get complicated if we try to + // inline the FP stack adjustment, as we have a lot of special casing going on to try + // minimize the way we generate the jump code. + // -case statements of switch where the FP stack differs with the one of evaluating the switch () statement + // We do this as we want to codegen switch as a jumptable. Again, this is low frequency. + // + // However, transition blocks have 2 problems + // + // - Procedure splitting: current implementation of procedure splitting requires all basic blocks to + // be known at codegen time, as it generates all hot blocks first and cold blocks later. This ties + // us up in codegen and is a solvable problem (we could make procedure splitting generate blocks + // in the right place without preordering them), this causes us to have to generate the transition + // blocks in the cold area if we want procedure splitting. + // + // + // - Thread abort exceptions and transition blocks. Transition blocks were designed under the assumption + // that no exceptions can happen inside them. Unfortunately Thread.Abort can happen in any instruction, + // and if we have handlers we will have to try to call them. Fixing this the right way would imply + // having multiple try native code regions for a single try il region. This is doable and shouldnt be + // a big change in the exception. + // + // Given the low frequency of the cases where we have transition blocks, I've decided to dumb down optimizations + // For these 2 cases: + // + // - When there is a chance that we will have FP transition blocks, we won't do procedure splitting. + // - When a method has a handler, it won't enregister any FP variables that go thru a conditional long or + // a switch statement. + // + // If at any point we find we need to optimize this, we should throw work at unblocking the restrictions our + // current procedure splitting and exception code have. + bool compMayHaveTransitionBlocks; + + VARSET_TP raMaskDontEnregFloat; // mask for additional restrictions + + VARSET_TP raLclRegIntfFloat[REG_FPCOUNT]; + + unsigned raCntStkStackFP; + unsigned raCntWtdStkDblStackFP; + unsigned raCntStkParamDblStackFP; + + // Payload in mem accesses for enregistering a variable (we dont want to mix with refcounts) + // TODO: Do we want to put this in LclVarDsc? + unsigned raPayloadStackFP[lclMAX_TRACKED]; + unsigned raHeightsStackFP[lclMAX_TRACKED][FP_VIRTUALREGISTERS+1]; +#ifdef DEBUG + // Useful for debugging + unsigned raHeightsNonWeightedStackFP[lclMAX_TRACKED][FP_VIRTUALREGISTERS+1]; +#endif +#endif //FEATURE_STACK_FP_X87 + +#ifdef DEBUG + // One line log function. Default level is 0. Increasing it gives you + // more log information + + // levels are currently unused: #define JITDUMP(level,...) (); + void JitLogEE (unsigned level, const char* fmt, ...); + + bool compDebugBreak; + + bool compJitHaltMethod(); + +#endif + + /* + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XX XX + XX GS Security checks for unsafe buffers XX + XX XX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + */ +public: + + struct ShadowParamVarInfo + { + FixedBitVect *assignGroup; // the closure set of variables whose values depend on each other + unsigned shadowCopy; // valid only if mayNeedShadowCopy()==true + + static bool mayNeedShadowCopy(LclVarDsc* varDsc) + { +#if defined(_TARGET_AMD64_) && !defined(LEGACY_BACKEND) + // GS cookie logic to create shadow slots, create trees to copy reg args to shadow + // slots and update all trees to refer to shadow slots is done immediately after + // fgMorph(). Lsra could potentially mark a param as DoNotEnregister after JIT determines + // not to shadow a parameter. Also, LSRA could potentially spill a param which is passed + // in register. Therefore, conservatively all params may need a shadow copy. Note that + // GS cookie logic further checks whether the param is a ptr or an unsafe buffer before + // creating a shadow slot even though this routine returns true. + // + // TODO-AMD64-CQ: Revisit this conservative approach as it could create more shadow slots than + // required. There are two cases under which a reg arg could potentially be used from its + // home location: + // a) LSRA marks it as DoNotEnregister (see LinearScan::identifyCandidates()) + // b) LSRA spills it + // + // Possible solution to address case (a) + // - The conditions under which LSRA marks a varDsc as DoNotEnregister could be checked + // in this routine. Note that live out of exception handler is something we may not be + // able to do it here since GS cookie logic is invoked ahead of liveness computation. + // Therefore, for methods with exception handling and need GS cookie check we might have + // to take conservative approach. + // + // Possible solution to address case (b) + // - Whenver a parameter passed in an argument register needs to be spilled by LSRA, we + // create a new spill temp if the method needs GS cookie check. + return varDsc->lvIsParam; +#else // !(defined(_TARGET_AMD64_) && defined(LEGACY_BACKEND)) + return varDsc->lvIsParam && !varDsc->lvIsRegArg; +#endif + } + +#ifdef DEBUG + void Print() + { + printf("assignGroup [%p]; shadowCopy: [%d];\n", assignGroup, shadowCopy); + } +#endif + }; + + GSCookie *gsGlobalSecurityCookieAddr; // Address of global cookie for unsafe buffer checks + GSCookie gsGlobalSecurityCookieVal; // Value of global cookie if addr is NULL + ShadowParamVarInfo *gsShadowVarInfo; // Table used by shadow param analysis code + + void gsGSChecksInitCookie(); // Grabs cookie variable + void gsCopyShadowParams(); // Identify vulnerable params and create dhadow copies + bool gsFindVulnerableParams (); // Shadow param analysis code + void gsParamsToShadows(); // Insert copy code and replave param uses by shadow + + static fgWalkPreFn gsMarkPtrsAndAssignGroups; // Shadow param analysis tree-walk + static fgWalkPreFn gsReplaceShadowParams; // Shadow param replacement tree-walk + +#define ALWAYS_INLINE_SIZE 16 // Method with <= ALWAYS_INLINE_SIZE IL bytes will always be inlined. +#define DEFAULT_MAX_INLINE_SIZE 100 // Method with > DEFAULT_MAX_INLINE_SIZE IL bytes will never be inlined. + // This can be overwritten by setting complus_JITInlineSize env variable. +#define IMPLEMENTATION_MAX_INLINE_SIZE _UI16_MAX // Maximum method size supported by this implementation + +#define NATIVE_SIZE_INVALID (-10000) + + static bool s_compInSamplingMode; + bool compIsMethodForLRSampling; // Is this the method suitable as a sample for the linear regression? + int compNativeSizeEstimate; // The estimated native size of this method. + InlInlineHints compInlineeHints; // Inlining hints from the inline candidate. + +#ifdef DEBUG + CodeSeqSM fgCodeSeqSm; // The code sequence state machine used in the inliner. +#endif +private: +#ifdef FEATURE_JIT_METHOD_PERF + JitTimer* pCompJitTimer; // Timer data structure (by phases) for current compilation. + static CompTimeSummaryInfo s_compJitTimerSummary; // Summary of the Timer information for the whole run. + + static LPCWSTR JitTimeLogCsv(); // Retrieve the file name for CSV from ConfigDWORD. + static LPCWSTR compJitTimeLogFilename; // If a log file for JIT time is desired, filename to write it to. +#endif + inline void EndPhase(Phases phase); // Indicate the end of the given phase. + +#ifdef FEATURE_CLRSQM + // These variables are associated with maintaining SQM data about compile time. + unsigned __int64 m_compCyclesAtEndOfInlining; // The thread-virtualized cycle count at the end of the inlining phase in the current compilation. + DWORD m_compTickCountAtEndOfInlining; // The result of GetTickCount() (# ms since some epoch marker) at the end of the inlining phase in the current compilation. + + // Records the SQM-relevant (cycles and tick count). Should be called after inlining is complete. + // (We do this after inlining because this marks the last point at which the JIT is likely to cause + // type-loading and class initialization). + void RecordSqmStateAtEndOfInlining(); + // Assumes being called at the end of compilation. Update the SQM state. + void RecordSqmStateAtEndOfCompilation(); + + // Does anything SQM related necessary at process shutdown time. + static void ProcessShutdownSQMWork(ICorStaticInfo* statInfo); +#endif // FEATURE_CLRSQM + +public: +#if FUNC_INFO_LOGGING + static LPCWSTR compJitFuncInfoFilename; // If a log file for per-function information is required, this is the filename to write it to. + static FILE* compJitFuncInfoFile; // And this is the actual FILE* to write to. +#endif // FUNC_INFO_LOGGING + + Compiler* prevCompiler; // Previous compiler on stack for TLS Compiler* linked list for reentrant compilers. + + // Is the compilation in a full trust context? + bool compIsFullTrust(); + + // Should we actually fire the noway assert body and the exception handler? + bool compShouldThrowOnNoway(); + +#ifdef DEBUG + private: + NodeToTestDataMap* m_nodeTestData; + + static const unsigned FIRST_LOOP_HOIST_CSE_CLASS = 1000; + unsigned m_loopHoistCSEClass; // LoopHoist test annotations turn into CSE requirements; we + // label them with CSE Class #'s starting at FIRST_LOOP_HOIST_CSE_CLASS. + // Current kept in this. + public: + NodeToTestDataMap* GetNodeTestData() + { + Compiler* compRoot = impInlineRoot(); + if (compRoot->m_nodeTestData == NULL) + { + compRoot->m_nodeTestData = new (getAllocatorDebugOnly()) NodeToTestDataMap(getAllocatorDebugOnly()); + } + return compRoot->m_nodeTestData; + } + + typedef SimplerHashTable<GenTreePtr, PtrKeyFuncs<GenTree>, int, DefaultSimplerHashBehavior> NodeToIntMap; + + // Returns the set (i.e., the domain of the result map) of nodes that are keys in m_nodeTestData, and + // currently occur in the AST graph. + NodeToIntMap* FindReachableNodesInNodeTestData(); + + // Node "from" is being eliminated, and being replaced by node "to". If "from" had any associated + // test data, associate that data with "to". + void TransferTestDataToNode(GenTreePtr from, GenTreePtr to); + + // Requires that "to" is a clone of "from". If any nodes in the "from" tree + // have annotations, attach similar annotations to the corresponding nodes in "to". + void CopyTestDataToCloneTree(GenTreePtr from, GenTreePtr to); + + // These are the methods that test that the various conditions implied by the + // test attributes are satisfied. + void JitTestCheckSSA(); // SSA builder tests. + void JitTestCheckVN(); // Value numbering tests. +#endif // DEBUG + + // The "FieldSeqStore", for canonicalizing field sequences. See the definition of FieldSeqStore for + // operations. + FieldSeqStore* m_fieldSeqStore; + + FieldSeqStore* GetFieldSeqStore() + { + Compiler* compRoot = impInlineRoot(); + if (compRoot->m_fieldSeqStore == NULL) + { + // Create a CompAllocator that labels sub-structure with CMK_FieldSeqStore, and use that for allocation. + IAllocator* ialloc = new (this, CMK_FieldSeqStore) CompAllocator(this, CMK_FieldSeqStore); + compRoot->m_fieldSeqStore = new (ialloc) FieldSeqStore(ialloc); + } + return compRoot->m_fieldSeqStore; + } + + typedef SimplerHashTable<GenTreePtr, PtrKeyFuncs<GenTree>, FieldSeqNode*, DefaultSimplerHashBehavior> NodeToFieldSeqMap; + + // Some nodes of "TYP_BYREF" or "TYP_I_IMPL" actually represent the address of a field within a struct, but since the offset of + // the field is zero, there's no "GT_ADD" node. We normally attach a field sequence to the constant that is + // added, but what do we do when that constant is zero, and is thus not present? We use this mechanism to + // attach the field sequence directly to the address node. + NodeToFieldSeqMap* m_zeroOffsetFieldMap; + + NodeToFieldSeqMap* GetZeroOffsetFieldMap() + { + // Don't need to worry about inlining here + if (m_zeroOffsetFieldMap == NULL) + { + // Create a CompAllocator that labels sub-structure with CMK_ZeroOffsetFieldMap, and use that for allocation. + IAllocator* ialloc = new (this, CMK_ZeroOffsetFieldMap) CompAllocator(this, CMK_ZeroOffsetFieldMap); + m_zeroOffsetFieldMap = new (ialloc) NodeToFieldSeqMap(ialloc); + } + return m_zeroOffsetFieldMap; + } + + // Requires that "op1" is a node of type "TYP_BYREF" or "TYP_I_IMPL". We are dereferencing this with the fields in + // "fieldSeq", whose offsets are required all to be zero. Ensures that any field sequence annotation currently on + // "op1" or its components is augmented by appending "fieldSeq". In practice, if "op1" is a GT_LCL_FLD, it has + // a field sequence as a member; otherwise, it may be the addition of an a byref and a constant, where the const + // has a field sequence -- in this case "fieldSeq" is appended to that of the constant; otherwise, we + // record the the field sequence using the ZeroOffsetFieldMap described above. + void fgAddFieldSeqForZeroOffset(GenTreePtr op1, FieldSeqNode* fieldSeq); + + + typedef SimplerHashTable<const GenTree*, PtrKeyFuncs<GenTree>, ArrayInfo, DefaultSimplerHashBehavior> NodeToArrayInfoMap; + NodeToArrayInfoMap* m_arrayInfoMap; + + NodeToArrayInfoMap* GetArrayInfoMap() + { + Compiler* compRoot = impInlineRoot(); + if (compRoot->m_arrayInfoMap == NULL) + { + // Create a CompAllocator that labels sub-structure with CMK_ArrayInfoMap, and use that for allocation. + IAllocator* ialloc = new (this, CMK_ArrayInfoMap) CompAllocator(this, CMK_ArrayInfoMap); + compRoot->m_arrayInfoMap = new (ialloc) NodeToArrayInfoMap(ialloc); + } + return compRoot->m_arrayInfoMap; + } + + NodeToUnsignedMap* m_heapSsaMap; + + // In some cases, we want to assign intermediate SSA #'s to heap states, and know what nodes create those heap states. + // (We do this for try blocks, where, if the try block doesn't do a call that loses track of the heap state, all the possible + // heap states are possible initial states of the corresponding catch block(s).) + NodeToUnsignedMap* GetHeapSsaMap() + { + Compiler* compRoot = impInlineRoot(); + if (compRoot->m_heapSsaMap == nullptr) + { + // Create a CompAllocator that labels sub-structure with CMK_ArrayInfoMap, and use that for allocation. + IAllocator* ialloc = new (this, CMK_ArrayInfoMap) CompAllocator(this, CMK_ArrayInfoMap); + compRoot->m_heapSsaMap = new (ialloc) NodeToUnsignedMap(ialloc); + } + return compRoot->m_heapSsaMap; + } + + // The Refany type is the only struct type whose structure is implicitly assumed by IL. We need its fields. + CORINFO_CLASS_HANDLE m_refAnyClass; + CORINFO_FIELD_HANDLE GetRefanyDataField() + { + if (m_refAnyClass == NULL) + { + m_refAnyClass = info.compCompHnd->getBuiltinClass(CLASSID_TYPED_BYREF); + } + return info.compCompHnd->getFieldInClass(m_refAnyClass, 0); + } + CORINFO_FIELD_HANDLE GetRefanyTypeField() + { + if (m_refAnyClass == NULL) + { + m_refAnyClass = info.compCompHnd->getBuiltinClass(CLASSID_TYPED_BYREF); + } + return info.compCompHnd->getFieldInClass(m_refAnyClass, 1); + } + +#if VARSET_COUNTOPS + static BitSetSupport::BitSetOpCounter m_varsetOpCounter; +#endif +#if ALLVARSET_COUNTOPS + static BitSetSupport::BitSetOpCounter m_allvarsetOpCounter; +#endif + + static HelperCallProperties s_helperCallProperties; + +}; // end of class Compiler + +// Inline methods of CompAllocator. +void * CompAllocator::Alloc(size_t sz) +{ +#if MEASURE_MEM_ALLOC + return m_comp->compGetMem(sz, m_cmk); +#else + return m_comp->compGetMem(sz); +#endif +} + +void * CompAllocator::ArrayAlloc(size_t elems, size_t elemSize) +{ +#if MEASURE_MEM_ALLOC + return m_comp->compGetMemArray(elems, elemSize, m_cmk); +#else + return m_comp->compGetMemArray(elems, elemSize); +#endif +} + + +// LclVarDsc constructor. Uses Compiler, so must come after Compiler definition. +inline +LclVarDsc::LclVarDsc(Compiler* comp) + : +#if ASSERTION_PROP + lvRefBlks(BlockSetOps::UninitVal()), +#endif // ASSERTION_PROP + lvPerSsaData(comp->getAllocator()) +{ +} + + +/* +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Miscellaneous Compiler stuff XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +// Values used to mark the types a stack slot is used for + +const unsigned TYPE_REF_INT = 0x01; // slot used as a 32-bit int +const unsigned TYPE_REF_LNG = 0x02; // slot used as a 64-bit long +const unsigned TYPE_REF_FLT = 0x04; // slot used as a 32-bit float +const unsigned TYPE_REF_DBL = 0x08; // slot used as a 64-bit float +const unsigned TYPE_REF_PTR = 0x10; // slot used as a 32-bit pointer +const unsigned TYPE_REF_BYR = 0x20; // slot used as a byref pointer +const unsigned TYPE_REF_STC = 0x40; // slot used as a struct +const unsigned TYPE_REF_TYPEMASK = 0x7F; // bits that represent the type + +//const unsigned TYPE_REF_ADDR_TAKEN = 0x80; // slots address was taken + +/***************************************************************************** + * + * Variables to keep track of total code amounts. + */ + +#if DISPLAY_SIZES + +extern size_t grossVMsize; +extern size_t grossNCsize; +extern size_t totalNCsize; + +extern unsigned genMethodICnt; +extern unsigned genMethodNCnt; +extern size_t gcHeaderISize; +extern size_t gcPtrMapISize; +extern size_t gcHeaderNSize; +extern size_t gcPtrMapNSize; + +#endif // DISPLAY_SIZES + +/***************************************************************************** + * + * Variables to keep track of basic block counts (more data on 1 BB methods) + */ + +#if COUNT_BASIC_BLOCKS +extern histo bbCntTable; +extern histo bbOneBBSizeTable; +#endif + + +/***************************************************************************** + * + * Used by optFindNaturalLoops to gather statistical information such as + * - total number of natural loops + * - number of loops with 1, 2, ... exit conditions + * - number of loops that have an iterator (for like) + * - number of loops that have a constant iterator + */ + +#if COUNT_LOOPS + +extern unsigned totalLoopMethods; // counts the total number of methods that have natural loops +extern unsigned maxLoopsPerMethod; // counts the maximum number of loops a method has +extern unsigned totalLoopOverflows; // # of methods that identified more loops than we can represent +extern unsigned totalLoopCount; // counts the total number of natural loops +extern unsigned totalUnnatLoopCount; // counts the total number of (not-necessarily natural) loops +extern unsigned totalUnnatLoopOverflows; // # of methods that identified more unnatural loops than we can represent +extern unsigned iterLoopCount; // counts the # of loops with an iterator (for like) +extern unsigned simpleTestLoopCount; // counts the # of loops with an iterator and a simple loop condition (iter < const) +extern unsigned constIterLoopCount; // counts the # of loops with a constant iterator (for like) +extern bool hasMethodLoops; // flag to keep track if we already counted a method as having loops +extern unsigned loopsThisMethod; // counts the number of loops in the current method +extern bool loopOverflowThisMethod; // True if we exceeded the max # of loops in the method. +extern histo loopCountTable; // Histogram of loop counts +extern histo loopExitCountTable; // Histogram of loop exit counts + +#endif // COUNT_LOOPS + +/***************************************************************************** + * variables to keep track of how many iterations we go in a dataflow pass + */ + +#if DATAFLOW_ITER + +extern unsigned CSEiterCount; // counts the # of iteration for the CSE dataflow +extern unsigned CFiterCount; // counts the # of iteration for the Const Folding dataflow + +#endif // DATAFLOW_ITER + +#if MEASURE_BLOCK_SIZE +extern size_t genFlowNodeSize; +extern size_t genFlowNodeCnt; +#endif // MEASURE_BLOCK_SIZE + +#if MEASURE_NODE_SIZE +struct NodeSizeStats +{ + void Init() + { + genTreeNodeCnt = 0; + genTreeNodeSize = 0; + genTreeNodeActualSize = 0; + } + + size_t genTreeNodeCnt; + size_t genTreeNodeSize; // The size we allocate + size_t genTreeNodeActualSize; // The actual size of the node. Note that the actual size will likely be smaller than the + // allocated size, but we sometimes use SetOper()/ChangeOper() to change a smaller node + // to a larger one. TODO-Cleanup: add stats on SetOper()/ChangeOper() usage to quanitfy this. +}; +extern NodeSizeStats genNodeSizeStats; // Total node size stats +extern NodeSizeStats genNodeSizeStatsPerFunc; // Per-function node size stats +extern histo genTreeNcntHist; +extern histo genTreeNsizHist; +#endif // MEASURE_NODE_SIZE + +/***************************************************************************** + * Count fatal errors (including noway_asserts). + */ + +#if MEASURE_FATAL +extern unsigned fatal_badCode; +extern unsigned fatal_noWay; +extern unsigned fatal_NOMEM; +extern unsigned fatal_noWayAssertBody; +#ifdef DEBUG +extern unsigned fatal_noWayAssertBodyArgs; +#endif // DEBUG +extern unsigned fatal_NYI; +#endif // MEASURE_FATAL + +/***************************************************************************** + * Codegen + */ + +#ifdef _TARGET_XARCH_ + +const instruction INS_SHIFT_LEFT_LOGICAL = INS_shl; +const instruction INS_SHIFT_RIGHT_LOGICAL = INS_shr; +const instruction INS_SHIFT_RIGHT_ARITHM = INS_sar; + +const instruction INS_AND = INS_and; +const instruction INS_OR = INS_or; +const instruction INS_XOR = INS_xor; +const instruction INS_NEG = INS_neg; +const instruction INS_TEST = INS_test; +const instruction INS_MUL = INS_imul; +const instruction INS_SIGNED_DIVIDE = INS_idiv; +const instruction INS_UNSIGNED_DIVIDE = INS_div; +const instruction INS_BREAKPOINT = INS_int3; +const instruction INS_ADDC = INS_adc; +const instruction INS_SUBC = INS_sbb; +const instruction INS_NOT = INS_not; + +#endif + +#ifdef _TARGET_ARM_ + +const instruction INS_SHIFT_LEFT_LOGICAL = INS_lsl; +const instruction INS_SHIFT_RIGHT_LOGICAL = INS_lsr; +const instruction INS_SHIFT_RIGHT_ARITHM = INS_asr; + +const instruction INS_AND = INS_and; +const instruction INS_OR = INS_orr; +const instruction INS_XOR = INS_eor; +const instruction INS_NEG = INS_rsb; +const instruction INS_TEST = INS_tst; +const instruction INS_MUL = INS_mul; +const instruction INS_SIGNED_DIVIDE = INS_sdiv; +const instruction INS_UNSIGNED_DIVIDE = INS_udiv; +const instruction INS_BREAKPOINT = INS_bkpt; +const instruction INS_ADDC = INS_adc; +const instruction INS_SUBC = INS_sbc; +const instruction INS_NOT = INS_mvn; + +#endif + +#ifdef _TARGET_ARM64_ + +const instruction INS_SHIFT_LEFT_LOGICAL = INS_lsl; +const instruction INS_SHIFT_RIGHT_LOGICAL = INS_lsr; +const instruction INS_SHIFT_RIGHT_ARITHM = INS_asr; + +const instruction INS_AND = INS_and; +const instruction INS_OR = INS_orr; +const instruction INS_XOR = INS_eor; +const instruction INS_NEG = INS_neg; +const instruction INS_TEST = INS_tst; +const instruction INS_MUL = INS_mul; +const instruction INS_SIGNED_DIVIDE = INS_sdiv; +const instruction INS_UNSIGNED_DIVIDE = INS_udiv; +const instruction INS_BREAKPOINT = INS_bkpt; +const instruction INS_ADDC = INS_adc; +const instruction INS_SUBC = INS_sbc; +const instruction INS_NOT = INS_mvn; + +#endif + +/*****************************************************************************/ + +extern const BYTE genTypeSizes[]; +extern const BYTE genTypeAlignments[]; +extern const BYTE genTypeStSzs[]; +extern const BYTE genActualTypes[]; + +/*****************************************************************************/ + +// Define the frame size at which we will generate a loop to probe the stack. +// VERY_LARGE_FRAME_SIZE_REG_MASK is the set of registers we need to use for the probing loop. + +#ifdef _TARGET_ARM_ +// The looping probe code is 40 bytes, whereas the straight-line probing for +// the (0x2000..0x3000) case is 44, so use looping for anything 0x2000 bytes +// or greater, to generate smaller code. + +#define VERY_LARGE_FRAME_SIZE (2 * CORINFO_PAGE_SIZE) +#else +#define VERY_LARGE_FRAME_SIZE (3 * CORINFO_PAGE_SIZE) +#endif + +#ifdef _TARGET_ARM_ +#define VERY_LARGE_FRAME_SIZE_REG_MASK (RBM_R4 | RBM_R5 | RBM_R6) +#elif defined(_TARGET_ARM64_) +#define VERY_LARGE_FRAME_SIZE_REG_MASK (RBM_R9 | RBM_R10 | RBM_R11) +#endif + +/*****************************************************************************/ + +#define REG_CORRUPT regNumber(REG_NA+1) +#define RBM_CORRUPT (RBM_ILLEGAL|regMaskTP(1)) +#define REG_PAIR_CORRUPT regPairNo(REG_PAIR_NONE+1) + +/*****************************************************************************/ + +extern BasicBlock dummyBB; + +/*****************************************************************************/ +/*****************************************************************************/ + +// foreach_treenode_execution_order: An iterator that iterates through all the tree +// nodes of a statement in execution order. +// __stmt: a GT_STMT type GenTree* +// __node: a GenTree*, already declared, that gets updated with each node in the statement, in execution order + +#define foreach_treenode_execution_order(__node, __stmt) \ + for ((__node) = (__stmt)->gtStmt.gtStmtList; (__node); (__node) = (__node)->gtNext) + +// foreach_block: An iterator over all blocks in the function. +// __compiler: the Compiler* object +// __block : a BasicBlock*, already declared, that gets updated each iteration. + +#define foreach_block(__compiler, __block) \ + for ((__block) = (__compiler)->fgFirstBB; (__block); (__block) = (__block)->bbNext) + +/*****************************************************************************/ +/*****************************************************************************/ + +#ifdef DEBUG + +void dumpConvertedVarSet(Compiler* comp, VARSET_VALARG_TP vars); + +/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Debugging helpers XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +/*****************************************************************************/ +/* The following functions are intended to be called from the debugger, to dump + * various data structures. The can be used in the debugger Watch or Quick Watch + * windows. They are designed to be short to type and take as few arguments as + * possible. The 'c' versions take a Compiler*, whereas the 'd' versions use the TlsCompiler. + * See the function definition comment for more details. + */ + +void cBlock(Compiler* comp, BasicBlock* block); +void cBlocks(Compiler* comp); +void cBlocksV(Compiler* comp); +void cTree(Compiler* comp, GenTree* tree); +void cTrees(Compiler* comp); +void cEH(Compiler* comp); +void cVar(Compiler* comp, unsigned lclNum); +void cVarDsc(Compiler* comp, LclVarDsc* varDsc); +void cVars(Compiler* comp); +void cBlockPreds(Compiler* comp, BasicBlock* block); +void cReach(Compiler* comp); +void cDoms(Compiler* comp); +void cLiveness(Compiler* comp); +void cCVarSet(Compiler* comp, VARSET_VALARG_TP vars); + +void dBlock(BasicBlock* block); +void dBlocks(); +void dBlocksV(); +void dTree(GenTree* tree); +void dTrees(); +void dEH(); +void dVar(unsigned lclNum); +void dVarDsc(LclVarDsc* varDsc); +void dVars(); +void dBlockPreds(BasicBlock* block); +void dReach(); +void dDoms(); +void dLiveness(); +void dCVarSet(VARSET_VALARG_TP vars); + +void dVarSet(VARSET_VALARG_TP vars); +void dRegMask(regMaskTP mask); + +#endif // DEBUG + +#include "compiler.hpp" // All the shared inline functions + +/*****************************************************************************/ +#endif //_COMPILER_H_ +/*****************************************************************************/ |