summaryrefslogtreecommitdiff
path: root/src/jit/compiler.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/compiler.h')
-rw-r--r--src/jit/compiler.h8757
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_
+/*****************************************************************************/