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.h9301
1 files changed, 9301 insertions, 0 deletions
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
new file mode 100644
index 0000000000..05047c5ecb
--- /dev/null
+++ b/src/jit/compiler.h
@@ -0,0 +1,9301 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more 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 "varset.h"
+#include "gentree.h"
+#include "lir.h"
+#include "block.h"
+#include "inline.h"
+#include "jiteh.h"
+#include "instr.h"
+#include "regalloc.h"
+#include "sm.h"
+#include "simplerhash.h"
+#include "cycletimer.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"
+#include "jittelemetry.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"
+
+// This is only used locally in the JIT to indicate that
+// a verification block should be inserted
+#define SEH_VERIFICATION_EXCEPTION 0xe0564552 // VER
+
+/*****************************************************************************
+ * 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
+class FgStack; // defined in flowgraph.cpp
+#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 */
+
+/*****************************************************************************/
+
+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);
+
+unsigned ReinterpretHexAsDecimal(unsigned);
+
+/*****************************************************************************/
+
+#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;
+ GenTreePtr m_tree;
+
+ DefLoc() : m_blk(nullptr), m_tree(nullptr)
+ {
+ }
+};
+
+// 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;
+
+ LclSsaVarDsc()
+ {
+ }
+};
+
+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.
+
+ 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 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"
+
+ unsigned char lvIsMultiRegArg : 1; // true if this is a multireg LclVar struct used in an argument context
+ unsigned char lvIsMultiRegRet : 1; // true if this is a multireg LclVar struct assigned from a multireg call
+
+#ifdef FEATURE_HFA
+ unsigned char _lvIsHfa : 1; // Is this a struct variable who's class handle is an HFA type
+ unsigned char _lvIsHfaRegArg : 1; // Is this a HFA argument variable? // TODO-CLEANUP: Remove this and replace
+ // with (lvIsRegArg && lvIsHfa())
+ unsigned char _lvHfaTypeIsFloat : 1; // Is the HFA type float or double?
+#endif // FEATURE_HFA
+
+#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
+ // Note that both SIMD vector args and locals are marked as lvSIMDType = true, but the
+ // type of an arg node is TYP_BYREF and a local node is TYP_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;
+
+#if FEATURE_MULTIREG_ARGS
+ regNumber lvRegNumForSlot(unsigned slotNum)
+ {
+ if (slotNum == 0)
+ {
+ return lvArgReg;
+ }
+ else if (slotNum == 1)
+ {
+ return lvOtherArgReg;
+ }
+ else
+ {
+ assert(false && "Invalid slotNum!");
+ }
+
+ unreached();
+ }
+#endif // FEATURE_MULTIREG_ARGS
+
+ bool lvIsHfa() const
+ {
+#ifdef FEATURE_HFA
+ return _lvIsHfa;
+#else
+ return false;
+#endif
+ }
+
+ void lvSetIsHfa()
+ {
+#ifdef FEATURE_HFA
+ _lvIsHfa = true;
+#endif
+ }
+
+ bool lvIsHfaRegArg() const
+ {
+#ifdef FEATURE_HFA
+ return _lvIsHfaRegArg;
+#else
+ return false;
+#endif
+ }
+
+ void lvSetIsHfaRegArg()
+ {
+#ifdef FEATURE_HFA
+ _lvIsHfaRegArg = true;
+#endif
+ }
+
+ bool lvHfaTypeIsFloat() const
+ {
+#ifdef FEATURE_HFA
+ return _lvHfaTypeIsFloat;
+#else
+ return false;
+#endif
+ }
+
+ void lvSetHfaTypeIsFloat(bool value)
+ {
+#ifdef FEATURE_HFA
+ _lvHfaTypeIsFloat = value;
+#endif
+ }
+
+ // on Arm64 - Returns 1-4 indicating the number of register slots used by the HFA
+ // on Arm32 - Returns the total number of single FP register slots used by the HFA, max is 8
+ //
+ unsigned lvHfaSlots() const
+ {
+ assert(lvIsHfa());
+ assert(lvType == TYP_STRUCT);
+#ifdef _TARGET_ARM_
+ return lvExactSize / sizeof(float);
+#else // _TARGET_ARM64_
+ if (lvHfaTypeIsFloat())
+ {
+ return lvExactSize / sizeof(float);
+ }
+ else
+ {
+ return lvExactSize / sizeof(double);
+ }
+#endif // _TARGET_ARM64_
+ }
+
+ // lvIsMultiRegArgOrRet()
+ // returns true if this is a multireg LclVar struct used in an argument context
+ // or if this is a multireg LclVar struct assigned from a multireg call
+ bool lvIsMultiRegArgOrRet()
+ {
+ return lvIsMultiRegArg || lvIsMultiRegRet;
+ }
+
+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.
+
+#if FEATURE_MULTIREG_ARGS
+ regNumberSmall _lvOtherArgReg; // Used for the second part of the struct passed in a register.
+ // Note this is defined but not used by ARM32
+#endif // FEATURE_MULTIREG_ARGS
+
+#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);
+ }
+
+#if FEATURE_MULTIREG_ARGS
+ __declspec(property(get = GetOtherArgReg, put = SetOtherArgReg)) regNumber lvOtherArgReg;
+
+ regNumber GetOtherArgReg() const
+ {
+ return (regNumber)_lvOtherArgReg;
+ }
+
+ void SetOtherArgReg(regNumber reg)
+ {
+ _lvOtherArgReg = (regNumberSmall)reg;
+ assert(_lvOtherArgReg == reg);
+ }
+#endif // FEATURE_MULTIREG_ARGS
+
+#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(varTypeIsStruct(lvType) || (lvType == TYP_BLK) || (lvPromoted && lvUnusedStruct));
+ return (unsigned)(roundUp(lvExactSize, TARGET_POINTER_SIZE));
+ }
+
+#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
+ 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 isFloatRegType(lvType) || lvIsHfaRegArg();
+ }
+ var_types GetHfaType() const
+ {
+ return lvIsHfa() ? (lvHfaTypeIsFloat() ? TYP_FLOAT : TYP_DOUBLE) : TYP_UNDEF;
+ }
+ void SetHfaType(var_types type)
+ {
+ assert(varTypeIsFloating(type));
+ lvSetHfaTypeIsFloat(type == TYP_FLOAT);
+ }
+
+#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);
+
+// 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, short_nm, hasChildren, parent) enum_nm,
+#include "compphases.h"
+ PHASE_NUMBER_OF
+};
+
+extern const char* PhaseNames[];
+extern const char* PhaseEnums[];
+extern const LPCWSTR PhaseShortNames[];
+
+//---------------------------------------------------------------
+// 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
+};
+
+#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_numMethods(0), m_total(0), m_maximum(0), m_numFilteredMethods(0), m_filtered(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;
+
+#ifdef UNIX_AMD64_ABI
+ jitstd::vector<CFI_CODE>* cfiCodes;
+#endif // UNIX_AMD64_ABI
+
+#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
+{
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ fgArgTabEntry()
+ {
+ otherRegNum = REG_NA;
+ isStruct = false; // is this a struct arg
+ }
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+
+ 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 argument's 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, or is forced
+ // to be on the stack despite its arg list position.
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ bool isStruct : 1; // True if this is a struct arg
+
+ regNumber otherRegNum; // The (second) register to use when passing this argument.
+
+ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+
+#ifdef _TARGET_ARM_
+ void SetIsHfaRegArg(bool hfaRegArg)
+ {
+ isHfaRegArg = hfaRegArg;
+ }
+
+ void SetIsBackFilled(bool backFilled)
+ {
+ isBackFilled = backFilled;
+ }
+
+ bool IsBackFilled() const
+ {
+ return isBackFilled;
+ }
+#else // !_TARGET_ARM_
+ // To make the callers easier, we allow these calls (and the isHfaRegArg and isBackFilled data members) for all
+ // platforms.
+ void SetIsHfaRegArg(bool hfaRegArg)
+ {
+ }
+
+ void SetIsBackFilled(bool backFilled)
+ {
+ }
+
+ bool IsBackFilled() const
+ {
+ return false;
+ }
+#endif // !_TARGET_ARM_
+
+#ifdef DEBUG
+ void Dump();
+#endif
+};
+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 hasRegArgs; // true if we have one or more register arguments
+ bool hasStackArgs; // true if we have one or more stack arguments
+ 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);
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ fgArgTabEntryPtr AddRegArg(
+ unsigned argNum,
+ GenTreePtr node,
+ GenTreePtr parent,
+ regNumber regNum,
+ unsigned numRegs,
+ unsigned alignment,
+ const bool isStruct,
+ const regNumber otherRegNum = REG_NA,
+ const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* const structDescPtr = nullptr);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ fgArgTabEntryPtr AddStkArg(unsigned argNum,
+ GenTreePtr node,
+ GenTreePtr parent,
+ unsigned numSlots,
+ unsigned alignment FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(const bool isStruct));
+
+ 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;
+ }
+ unsigned GetNextSlotNum()
+ {
+ return nextSlotNum;
+ }
+ bool HasRegArgs()
+ {
+ return hasRegArgs;
+ }
+ bool HasStackArgs()
+ {
+ return hasStackArgs;
+ }
+};
+
+#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, JitSimplerHashBehavior> 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;
+ friend class LIR;
+ friend class ObjectAllocator;
+
+#ifndef _TARGET_64BIT_
+ friend class DecomposeLongs;
+#endif // !_TARGET_64BIT_
+
+ /*
+ 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 dumpIR;
+ bool dumpIRNodes;
+ bool dumpIRTypes;
+ bool dumpIRKinds;
+ bool dumpIRLocals;
+ bool dumpIRRegs;
+ bool dumpIRSsa;
+ bool dumpIRValnums;
+ bool dumpIRCosts;
+ bool dumpIRFlags;
+ bool dumpIRNoLists;
+ bool dumpIRNoLeafs;
+ bool dumpIRNoStmts;
+ bool dumpIRTrees;
+ bool dumpIRLinear;
+ bool dumpIRDataflow;
+ bool dumpIRBlockHeaders;
+ bool dumpIRExit;
+ LPCWSTR dumpIRPhase;
+ LPCWSTR dumpIRFormat;
+ 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
+
+#if FEATURE_MULTIREG_RET
+ GenTreePtr impAssignMultiRegTypeToVar(GenTreePtr op, CORINFO_CLASS_HANDLE hClass);
+#endif // FEATURE_MULTIREG_RET
+
+#ifdef ARM_SOFTFP
+ bool isSingleFloat32Struct(CORINFO_CLASS_HANDLE hClass);
+#endif // ARM_SOFTFP
+
+ //-------------------------------------------------------------------------
+ // 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 instead of the general purpose registers.
+ //
+
+ bool IsHfa(CORINFO_CLASS_HANDLE hClass);
+ bool IsHfa(GenTreePtr tree);
+
+ var_types GetHfaType(GenTreePtr tree);
+ unsigned GetHfaCount(GenTreePtr tree);
+
+ var_types GetHfaType(CORINFO_CLASS_HANDLE hClass);
+ unsigned GetHfaCount(CORINFO_CLASS_HANDLE hClass);
+
+ bool IsMultiRegPassedType(CORINFO_CLASS_HANDLE hClass);
+ bool IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass);
+
+ //-------------------------------------------------------------------------
+ // 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 bbInExnFlowRegions(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);
+
+ // Return the EH descriptor for the most nested region that may handle exceptions raised in this BasicBlock (or
+ // nullptr if this block's exceptions propagate to caller).
+ EHblkDsc* ehGetBlockExnFlowDsc(BasicBlock* block);
+
+ EHblkDsc* ehIsBlockTryLast(BasicBlock* block);
+ EHblkDsc* ehIsBlockHndLast(BasicBlock* block);
+ bool ehIsBlockEHLast(BasicBlock* block);
+
+ bool ehBlockHasExnFlowDsc(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*, JitSimplerHashBehavior>
+ BlockToFlowListMap;
+ BlockToFlowListMap* m_blockToEHPreds;
+ BlockToFlowListMap* GetBlockToEHPreds()
+ {
+ if (m_blockToEHPreds == nullptr)
+ {
+ 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 = nullptr, 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 = nullptr,
+ GenTreePtr op2 = nullptr);
+
+ 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 = nullptr, unsigned handle1 = 0, void* handle2 = nullptr);
+
+ unsigned gtTokenToIconFlags(unsigned token);
+
+ GenTreePtr gtNewIconEmbHndNode(void* value,
+ void* pValue,
+ unsigned flags,
+ unsigned handle1 = 0,
+ void* handle2 = nullptr,
+ void* compileTimeHandle = nullptr);
+
+ GenTreePtr gtNewIconEmbScpHndNode(CORINFO_MODULE_HANDLE scpHnd, unsigned hnd1 = 0, void* hnd2 = nullptr);
+ GenTreePtr gtNewIconEmbClsHndNode(CORINFO_CLASS_HANDLE clsHnd, unsigned hnd1 = 0, void* hnd2 = nullptr);
+ GenTreePtr gtNewIconEmbMethHndNode(CORINFO_METHOD_HANDLE methHnd, unsigned hnd1 = 0, void* hnd2 = nullptr);
+ GenTreePtr gtNewIconEmbFldHndNode(CORINFO_FIELD_HANDLE fldHnd, unsigned hnd1 = 0, void* hnd2 = nullptr);
+
+ 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);
+
+ GenTreeBlk* gtNewBlkOpNode(
+ genTreeOps oper, GenTreePtr dst, GenTreePtr srcOrFillVal, GenTreePtr sizeOrClsTok, bool isVolatile);
+
+ GenTree* gtNewBlkOpNode(GenTreePtr dst, GenTreePtr srcOrFillVal, unsigned size, bool isVolatile, bool isCopyBlock);
+
+protected:
+ void gtBlockOpInit(GenTreePtr result, GenTreePtr dst, GenTreePtr srcOrFillVal, bool isVolatile);
+
+public:
+ GenTree* gtNewObjNode(CORINFO_CLASS_HANDLE structHnd, GenTreePtr addr);
+ void gtSetObjGcInfo(GenTreeObj* objNode);
+ GenTree* gtNewStructVal(CORINFO_CLASS_HANDLE structHnd, GenTreePtr addr);
+ GenTree* gtNewBlockVal(GenTreePtr addr, unsigned size);
+
+ GenTree* gtNewCpObjNode(GenTreePtr dst, GenTreePtr src, CORINFO_CLASS_HANDLE structHnd, bool isVolatile);
+
+ 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 = nullptr);
+
+ 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 = nullptr, 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);
+ GenTreeArgList* gtNewArgList(GenTreePtr op1, GenTreePtr op2, GenTreePtr op3);
+
+ GenTreeArgList* gtNewAggregate(GenTree* element);
+
+ 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);
+
+ 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);
+
+ GenTreePtr gtNewAllocObjNode(unsigned int helper, CORINFO_CLASS_HANDLE clsHnd, var_types type, GenTreePtr op1);
+
+ //------------------------------------------------------------------------
+ // Other GenTree functions
+
+ GenTreePtr gtClone(GenTree* tree, bool complexOK = false);
+
+ GenTreePtr gtCloneExpr(GenTree* tree, unsigned addFlags = 0, unsigned varNum = (unsigned)-1, int varVal = 0);
+
+ GenTreePtr gtReplaceTree(GenTreePtr stmt, GenTreePtr tree, GenTreePtr replacementTree);
+
+ void gtUpdateSideEffects(GenTreePtr tree, unsigned oldGtFlags, unsigned newGtFlags);
+
+ // 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);
+
+ // Returns true iff the secondNode can be swapped with firstNode.
+ bool gtCanSwapOrder(GenTree* firstNode, GenTree* secondNode);
+
+ unsigned gtSetEvalOrder(GenTree* tree);
+
+#if FEATURE_STACK_FP_X87
+ bool gtFPstLvlRedo;
+ void gtComputeFPlvls(GenTreePtr tree);
+#endif // FEATURE_STACK_FP_X87
+
+ 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);
+
+ // Return true if call is a recursive call; return false otherwise.
+ bool gtIsRecursiveCall(GenTreeCall* call)
+ {
+ return (call->gtCallMethHnd == info.compMethodHnd);
+ }
+
+ //-------------------------------------------------------------------------
+
+ GenTreePtr gtFoldExpr(GenTreePtr tree);
+ GenTreePtr
+#ifdef __clang__
+ // TODO-Amd64-Unix: Remove this when the clang optimizer is fixed and/or the method implementation is
+ // refactored in a simpler code. This is a workaround for a bug in the clang-3.5 optimizer. The issue is that in
+ // release build the optimizer is mistyping (or just wrongly decides to use 32 bit operation for a corner case
+ // of MIN_LONG) the args of the (ltemp / lval2) to int (it does a 32 bit div operation instead of 64 bit) - see
+ // the implementation of the method in gentree.cpp. For the case of lval1 and lval2 equal to MIN_LONG
+ // (0x8000000000000000) this results in raising a SIGFPE. The method implementation is rather complex. Disable
+ // optimizations for now.
+ __attribute__((optnone))
+#endif // __clang__
+ gtFoldExprConst(GenTreePtr tree);
+ GenTreePtr gtFoldExprSpecial(GenTreePtr tree);
+ GenTreePtr gtFoldExprCompare(GenTreePtr tree);
+
+ //-------------------------------------------------------------------------
+ // Get the handle, if any.
+ CORINFO_CLASS_HANDLE gtGetStructHandleIfPresent(GenTreePtr tree);
+ // Get the handle, and assert if not found.
+ CORINFO_CLASS_HANDLE gtGetStructHandle(GenTreePtr tree);
+
+//-------------------------------------------------------------------------
+// Functions to display the trees
+
+#ifdef DEBUG
+ void gtDispNode(GenTreePtr tree, IndentStack* indentStack, __in_z const char* msg, bool isLIR);
+
+ void gtDispVN(GenTreePtr tree);
+ void gtDispConst(GenTreePtr tree);
+ void gtDispLeaf(GenTreePtr tree, IndentStack* indentStack);
+ void gtDispNodeName(GenTreePtr tree);
+ void gtDispRegVal(GenTreePtr tree);
+
+ enum IndentInfo
+ {
+ IINone,
+ IIArc,
+ IIArcTop,
+ IIArcBottom,
+ IIEmbedded,
+ IIError,
+ IndentInfoCount
+ };
+ void gtDispChild(GenTreePtr child,
+ IndentStack* indentStack,
+ IndentInfo arcType,
+ __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,
+ bool isLIR = false);
+ void gtGetLclVarNameInfo(unsigned lclNum, const char** ilKindOut, const char** ilNameOut, unsigned* ilNumOut);
+ 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, int listCount, char* bufp, unsigned bufLength);
+ void gtGetLateArgMsg(GenTreePtr call, GenTreePtr arg, int argNum, int listCount, char* bufp, unsigned bufLength);
+ void gtDispArgList(GenTreePtr tree, IndentStack* indentStack);
+ void gtDispFieldSeq(FieldSeqNode* pfsn);
+
+ void gtDispRange(LIR::ReadOnlyRange const& range);
+
+ void gtDispTreeRange(LIR::Range& containingRange, GenTree* tree);
+
+ void gtDispLIRNode(GenTree* node);
+#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 FEATURE_UNIX_AMD64_STRUCT_PASSING
+ // Only for AMD64 System V cache the first caller stack homed argument.
+ unsigned lvaFirstStackIncomingArgNum; // First argument with stack slot in the caller.
+#endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+#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 DEBUGARG(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_
+
+ unsigned lvaInlinedPInvokeFrameVar; // variable representing the InlinedCallFrame
+ unsigned lvaReversePInvokeFrameVar; // variable representing the reverse PInvoke frame
+#if FEATURE_FIXED_OUT_ARGS
+ unsigned lvaPInvokeFrameRegSaveVar; // variable representing the RegSave for PInvoke inlining.
+#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
+
+ unsigned lvaNewObjArrayArgs; // variable with arguments for new MD array helper
+
+ // 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();
+#ifdef UNIX_AMD64_ABI
+ int lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs, int* callerArgOffset);
+#else // !UNIX_AMD64_ABI
+ int lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs);
+#endif // !UNIX_AMD64_ABI
+ 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 lvaDecRefCnts(BasicBlock* basicBlock, GenTreePtr tree);
+ void lvaRecursiveDecRefCounts(GenTreePtr tree);
+ void lvaRecursiveIncRefCounts(GenTreePtr tree);
+
+#ifdef DEBUG
+ 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
+
+ // Struct parameters that are passed by reference are marked as both lvIsParam and lvIsTemp
+ // (this is an overload of lvIsTemp because there are no temp parameters).
+ // For x64 this is 3, 5, 6, 7, >8 byte structs that are passed by reference.
+ // For ARM64, this is structs larger than 16 bytes that are passed by reference.
+ bool lvaIsImplicitByRefLocal(unsigned varNum)
+ {
+#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ LclVarDsc* varDsc = &(lvaTable[varNum]);
+ if (varDsc->lvIsParam && varDsc->lvIsTemp)
+ {
+ assert((varDsc->lvType == TYP_STRUCT) || (varDsc->lvType == TYP_BYREF));
+ return true;
+ }
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ return false;
+ }
+
+ // Returns true if this local var is a multireg struct
+ bool lvaIsMultiregStruct(LclVarDsc* varDsc);
+
+ // 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];
+
+ lvaStructPromotionInfo()
+ : typeHnd(nullptr), canPromote(false), requiresScratchVar(false), containsHoles(false), customLayout(false)
+ {
+ }
+ };
+
+ 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;
+ InlineStrategy* m_inlineStrategy;
+
+ // The Compiler* that is the root of the inlining tree of which "this" is a member.
+ Compiler* impInlineRoot();
+
+#if defined(DEBUG) || defined(INLINE_DATA)
+ unsigned __int64 getInlineCycleCount()
+ {
+ return m_compCycles;
+ }
+#endif // defined(DEBUG) || defined(INLINE_DATA)
+
+ 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
+
+#define SMALL_STACK_SIZE 16 // number of elements in impSmallStack
+
+ StackEntry impSmallStack[SMALL_STACK_SIZE]; // Use this array if 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);
+
+ void impImportNewObjArray(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo);
+
+ 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);
+ void impPopArgsForUnmanagedCall(GenTreePtr call, CORINFO_SIG_INFO* sig);
+
+ 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_OFFSET rawILOffset);
+
+ bool impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo);
+
+ GenTreePtr impFixupCallStructReturn(GenTreePtr call, CORINFO_CLASS_HANDLE retClsHnd);
+
+ GenTreePtr impInitCallLongReturn(GenTreePtr call);
+
+ 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 = nullptr);
+
+ 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,
+ bool tailCall,
+ 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 = nullptr,
+ IL_OFFSETX ilOffset = BAD_IL_OFFSET,
+ BasicBlock* block = nullptr);
+ void impAssignTempGen(unsigned tmpNum,
+ GenTreePtr val,
+ CORINFO_CLASS_HANDLE structHnd,
+ unsigned curLevel,
+ GenTreePtr* pAfterStmt = nullptr,
+ IL_OFFSETX ilOffset = BAD_IL_OFFSET,
+ BasicBlock* block = nullptr);
+ 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 = nullptr,
+ BasicBlock* block = nullptr);
+ GenTreePtr impAssignStructPtr(GenTreePtr dest,
+ GenTreePtr src,
+ CORINFO_CLASS_HANDLE structHnd,
+ unsigned curLevel,
+ GenTreePtr* pAfterStmt = nullptr,
+ BasicBlock* block = nullptr);
+
+ GenTreePtr impGetStructAddr(GenTreePtr structVal,
+ CORINFO_CLASS_HANDLE structHnd,
+ unsigned curLevel,
+ bool willDeref);
+
+ var_types impNormStructType(CORINFO_CLASS_HANDLE structHnd,
+ BYTE* gcLayout = nullptr,
+ unsigned* numGCVars = nullptr,
+ var_types* simdBaseType = nullptr);
+
+ GenTreePtr impNormStructVal(GenTreePtr structVal,
+ CORINFO_CLASS_HANDLE structHnd,
+ unsigned curLevel,
+ bool forceNormalization = false);
+
+ GenTreePtr impTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken,
+ BOOL* pRuntimeLookup = nullptr,
+ BOOL mustRestoreHandle = FALSE,
+ BOOL importParent = FALSE);
+
+ GenTreePtr impParentClassTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken,
+ BOOL* pRuntimeLookup = nullptr,
+ BOOL mustRestoreHandle = FALSE)
+ {
+ return impTokenToHandle(pResolvedToken, pRuntimeLookup, mustRestoreHandle, TRUE);
+ }
+
+ GenTreePtr impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken,
+ CORINFO_LOOKUP* pLookup,
+ unsigned flags,
+ void* compileTimeHandle);
+
+ GenTreePtr impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken,
+ CORINFO_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,
+ GenTreeArgList* arg = nullptr,
+ CORINFO_LOOKUP_KIND* pGenericLookupKind = nullptr);
+
+ GenTreePtr impCastClassOrIsInstToTree(GenTreePtr op1,
+ GenTreePtr op2,
+ CORINFO_RESOLVED_TOKEN* pResolvedToken,
+ bool isCastClass);
+
+ bool VarTypeIsMultiByteAndCanEnreg(var_types type,
+ CORINFO_CLASS_HANDLE typeClass,
+ unsigned* typeSize,
+ bool forReturn);
+
+ static bool IsIntrinsicImplementedByUserCall(CorInfoIntrinsics intrinsicId);
+ static bool IsTargetIntrinsic(CorInfoIntrinsics intrinsicId);
+ static bool IsMathIntrinsic(CorInfoIntrinsics intrinsicId);
+ static bool IsMathIntrinsic(GenTreePtr tree);
+
+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);
+ bool impIsAnySTLOC(OPCODE opcode)
+ {
+ return ((opcode == CEE_STLOC) || (opcode == CEE_STLOC_S) ||
+ ((opcode >= CEE_STLOC_0) && (opcode <= CEE_STLOC_3)));
+ }
+
+ GenTreeArgList* impPopList(unsigned count,
+ unsigned* flagsPtr,
+ CORINFO_SIG_INFO* sig,
+ GenTreeArgList* prefixTree = nullptr);
+
+ 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);
+
+#ifdef _TARGET_ARM_
+ void impMarkLclDstNotPromotable(unsigned tmpNum, 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 = nullptr) : 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);
+
+#if FEATURE_VARARG
+ regNumber getCallArgIntRegister(regNumber floatReg);
+ regNumber getCallArgFloatRegister(regNumber intReg);
+#endif // FEATURE_VARARG
+
+#if defined(DEBUG)
+ static unsigned jitTotalMethodCompiled;
+#endif
+
+#ifdef DEBUG
+ static LONG jitNestingLevel;
+#endif // DEBUG
+
+ bool seenConditionalJump;
+
+ static BOOL impIsAddressInLocal(GenTreePtr tree, GenTreePtr* lclVarTreeOut);
+
+ void impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, InlineResult* inlineResult);
+
+ // STATIC inlining decision based on the IL code.
+ void impCanInlineIL(CORINFO_METHOD_HANDLE fncHandle,
+ CORINFO_METHOD_INFO* methInfo,
+ bool forceInline,
+ InlineResult* inlineResult);
+
+ void impCheckCanInline(GenTreePtr call,
+ CORINFO_METHOD_HANDLE fncHandle,
+ unsigned methAttr,
+ CORINFO_CONTEXT_HANDLE exactContextHnd,
+ InlineCandidateInfo** ppInlineCandidateInfo,
+ InlineResult* inlineResult);
+
+ void impInlineRecordArgInfo(InlineInfo* pInlineInfo,
+ GenTreePtr curArgVal,
+ unsigned argNum,
+ InlineResult* inlineResult);
+
+ void 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, CORINFO_CALL_INFO* callInfo);
+
+ 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 isRecursive,
+ bool* IsCallPopRet = nullptr);
+
+ bool impIsImplicitTailCallCandidate(
+ OPCODE curOpcode, const BYTE* codeAddrOfNextOpcode, const BYTE* codeEnd, int prefixFlags, bool isRecursive);
+
+ /*
+ 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.
+
+ 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_
+
+ void fgAddReversePInvokeEnterExit();
+
+ 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* fgSplitBlockAfterNode(BasicBlock* curr, GenTree* node); // for LIR
+ 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 = nullptr);
+ 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();
+
+#ifdef LEGACY_BACKEND
+ GenTreePtr fgLegacyPerStatementLocalVarLiveness(GenTreePtr startNode, GenTreePtr relopNode, GenTreePtr asgdLclVar);
+#else
+ void fgPerNodeLocalVarLiveness(GenTree* node, GenTree* asgdLclVar);
+ void fgPerStatementLocalVarLiveness(GenTree* node, GenTree* asgdLclVar);
+#endif
+ 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);
+
+ void fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call);
+
+ bool fgComputeLifeLocal(VARSET_TP& life, VARSET_TP& keepAliveVars, GenTree* lclVarNode, GenTree* node);
+
+ VARSET_VALRET_TP fgComputeLife(VARSET_VALARG_TP life,
+ GenTreePtr startNode,
+ GenTreePtr endNode,
+ VARSET_VALARG_TP volatileVars,
+ bool* pStmtInfoDirty DEBUGARG(bool* treeModf));
+
+ VARSET_VALRET_TP fgComputeLifeLIR(VARSET_VALARG_TP life, BasicBlock* block, VARSET_VALARG_TP volatileVars);
+
+ bool fgRemoveDeadStore(GenTree** pTree,
+ LclVarDsc* varDsc,
+ VARSET_TP life,
+ bool* doAgain,
+ bool* pStmtInfoDirty DEBUGARG(bool* treeModf));
+
+ bool fgTryRemoveDeadLIRStore(LIR::Range& blockRange, GenTree* node, GenTree** next);
+
+ // 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 != nullptr && 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, JitSimplerHashBehavior> NodeToUnsignedMap;
+ NodeToUnsignedMap* m_opAsgnVarDefSsaNums;
+ NodeToUnsignedMap* GetOpAsgnVarDefSsaNums()
+ {
+ if (m_opAsgnVarDefSsaNums == nullptr)
+ {
+ 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*, JitSimplerHashBehavior>
+ NodeToIndirAssignMap;
+ NodeToIndirAssignMap* m_indirAssignMap;
+ NodeToIndirAssignMap* GetIndirAssignMap()
+ {
+ if (m_indirAssignMap == nullptr)
+ {
+ // 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 block assignment.
+ void fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd);
+
+ // Does value-numbering for a cast tree.
+ void fgValueNumberCastTree(GenTreePtr tree);
+
+ // Does value-numbering for an intrinsic tree.
+ void fgValueNumberIntrinsic(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(varTypeIsStruct(elemTyp) || 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;
+ }
+ }
+
+ // Convert a BYTE which represents the VM's CorInfoGCtype to the JIT's var_types
+ var_types getJitGCType(BYTE gcType);
+
+ enum structPassingKind
+ {
+ SPK_Unknown, // Invalid value, never returned
+ SPK_PrimitiveType, // The struct is passed/returned using a primitive type.
+ SPK_ByValue, // The struct is passed/returned by value (using the ABI rules)
+ // for ARM64 and UNIX_X64 in multiple registers. (when all of the
+ // parameters registers are used, then the stack will be used)
+ // for X86 passed on the stack, for ARM32 passed in registers
+ // or the stack or split between registers and the stack.
+ SPK_ByValueAsHfa, // The struct is passed/returned as an HFA in multiple registers.
+ SPK_ByReference
+ }; // The struct is passed/returned by reference to a copy/buffer.
+
+ // Get the "primitive" type that is is used when we are given a struct of size 'structSize'.
+ // For pointer sized structs the 'clsHnd' is used to determine if the struct contains GC ref.
+ // A "primitive" type is one of the scalar types: byte, short, int, long, ref, float, double
+ // If we can't or shouldn't use a "primitive" type then TYP_UNKNOWN is returned.
+ //
+ var_types getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS_HANDLE clsHnd);
+
+ // Get the type that is used to pass values of the given struct type.
+ // If you have already retrieved the struct size then pass it as the optional third argument
+ //
+ var_types getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
+ structPassingKind* wbPassStruct,
+ unsigned structSize = 0);
+
+ // Get the type that is used to return values of the given struct type.
+ // If you have already retrieved the struct size then pass it as the optional third argument
+ //
+ var_types getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
+ structPassingKind* wbPassStruct = nullptr,
+ unsigned structSize = 0);
+
+#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, JitSimplerHashBehavior>
+ 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 == nullptr)
+ {
+ m_switchDescMap = new (getAllocator()) BlockToSwitchDescMap(getAllocator());
+ }
+ return m_switchDescMap;
+ }
+
+ // Invalidate the map of unique switch block successors. For example, since the hash key of the map
+ // depends on block numbers, we must invalidate the map when the blocks are renumbered, to ensure that
+ // we don't accidentally look up and return the wrong switch data.
+ void InvalidateUniqueSwitchSuccMap()
+ {
+ m_switchDescMap = nullptr;
+ }
+
+ // 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 = nullptr,
+ 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 = nullptr);
+
+ void fgRemoveEmptyBlocks();
+
+ 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 = nullptr);
+
+ bool fgUpdateFlowGraph(bool doTailDup = false);
+
+ void fgFindOperOrder();
+
+ // method that returns if you should split here
+ typedef bool(fgSplitPredicate)(GenTree* tree, GenTree* parent, fgWalkData* data);
+
+ 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 FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(const bool passedInRegisters));
+
+ // 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 DUMP_FLOWGRAPHS
+ const char* fgProcessEscapes(const char* nameIn, escapeMapping_t* map);
+ FILE* fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, LPCWSTR type);
+ bool fgDumpFlowGraph(Phases phase);
+
+#endif // DUMP_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 fgDumpBlock(BasicBlock* block);
+ 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);
+ void fgDebugCheckFlags(GenTreePtr tree);
+#endif
+
+#ifdef LEGACY_BACKEND
+ static void fgOrderBlockOps(GenTreePtr tree,
+ regMaskTP reg0,
+ regMaskTP reg1,
+ regMaskTP reg2,
+ GenTreePtr* opsPtr, // OUT
+ regMaskTP* regsPtr); // OUT
+#endif // LEGACY_BACKEND
+
+ static GenTreePtr fgGetFirstNode(GenTreePtr tree);
+ static bool fgTreeIsInStmt(GenTree* tree, GenTreeStmt* stmt);
+
+ 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>
+ static 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>
+ static fgWalkResult fgWalkTreeRec(GenTreePtr* pTree, fgWalkData* fgWalkPre);
+
+ fgWalkResult fgWalkTreePre(GenTreePtr* pTree,
+ fgWalkPreFn* visitor,
+ void* pCallBackData = nullptr,
+ bool lclVarsOnly = false,
+ bool computeStack = false);
+
+ fgWalkResult fgWalkTree(GenTreePtr* pTree,
+ fgWalkPreFn* preVisitor,
+ fgWalkPostFn* postVisitor,
+ void* pCallBackData = nullptr);
+
+ void fgWalkAllTreesPre(fgWalkPreFn* visitor, void* pCallBackData);
+
+ //----- Postorder
+
+ template <bool computeStack>
+ static fgWalkResult fgWalkTreePostRec(GenTreePtr* pTree, fgWalkData* fgWalkPre);
+
+ fgWalkResult fgWalkTreePost(GenTreePtr* pTree,
+ fgWalkPostFn* visitor,
+ void* pCallBackData = nullptr,
+ 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);
+
+ void fgObserveInlineConstants(OPCODE opcode, const FgStack& stack, bool isInlining);
+
+ void fgAdjustForAddressExposedOrWrittenThis();
+
+ bool fgProfileData_ILSizeMismatch;
+ ICorJitInfo::ProfileBuffer* fgProfileBuffer;
+ ULONG fgProfileBufferCount;
+ ULONG fgNumProfileRuns;
+
+ unsigned fgStressBBProf()
+ {
+#ifdef DEBUG
+ unsigned result = JitConfig.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);
+
+private:
+ GenTreePtr fgInsertStmtListAfter(BasicBlock* block, GenTreePtr stmtAfter, GenTreePtr stmtList);
+
+ GenTreePtr fgMorphSplitTree(GenTree** splitPoint, GenTree* stmt, BasicBlock* blk);
+
+ // Create a new temporary variable to hold the result of *ppTree,
+ // and transform the graph accordingly.
+ 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);
+
+public:
+ void fgFixupArgTabEntryPtr(GenTreePtr parentCall, GenTreePtr oldArg, GenTreePtr newArg);
+
+private:
+ // Recognize a bitwise rotation pattern and convert into a GT_ROL or a GT_ROR node.
+ GenTreePtr fgRecognizeAndMorphBitwiseRotation(GenTreePtr tree);
+ bool fgOperIsBitwiseRotationRoot(genTreeOps oper);
+
+ //-------- Determine the order in which the trees will be evaluated -------
+
+ unsigned fgTreeSeqNum;
+ GenTree* fgTreeSeqLst;
+ GenTree* fgTreeSeqBeg;
+
+ GenTree* fgSetTreeSeq(GenTree* tree, GenTree* prev = nullptr, bool isLIR = false);
+ void fgSetTreeSeqHelper(GenTree* tree, bool isLIR);
+ void fgSetTreeSeqFinish(GenTreePtr tree, bool isLIR);
+ void fgSetStmtSeq(GenTree* tree);
+ void fgSetBlockOrder(BasicBlock* block);
+
+ //------------------------- Morphing --------------------------------------
+
+ unsigned fgPtrArgCntCur;
+ unsigned fgPtrArgCntMax;
+ hashBv* fgOutgoingArgTemps;
+ hashBv* fgCurrentlyInUseArgTemps;
+
+ bool compCanEncodePtrArgCntMax();
+
+ 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 FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(
+ const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structDescPtr));
+
+ 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);
+ void fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCall* recursiveTailCall);
+ GenTreePtr fgAssignRecursiveCallArgToCallerParam(GenTreePtr arg,
+ fgArgTabEntryPtr argTabEntry,
+ BasicBlock* block,
+ IL_OFFSETX callILOffset,
+ GenTreePtr tmpAssignmentInsertionPoint,
+ GenTreePtr paramAssignmentInsertionPoint);
+ static int fgEstimateCallStackSize(GenTreeCall* call);
+ GenTreePtr fgMorphCall(GenTreeCall* call);
+ void fgMorphCallInline(GenTreeCall* call, InlineResult* result);
+ void fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result);
+#if DEBUG
+ void fgNoteNonInlineCandidate(GenTreePtr tree, GenTreeCall* call);
+ static fgWalkPreFn fgFindNonInlineCandidate;
+#endif
+ 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 fgMorphBlkToInd(GenTreeBlk* tree, var_types type);
+ GenTreePtr fgMorphGetStructAddr(GenTreePtr* pTree, CORINFO_CLASS_HANDLE clsHnd, bool isRValue = false);
+ GenTreePtr fgMorphBlkNode(GenTreePtr tree, bool isDest);
+ GenTreePtr fgMorphBlockOperand(GenTreePtr tree, var_types asgType, unsigned blockWidth, bool isDest);
+ GenTreePtr fgMorphCopyBlock(GenTreePtr tree);
+ GenTreePtr fgMorphForRegisterFP(GenTreePtr tree);
+ GenTreePtr fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac = nullptr);
+ 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 = nullptr);
+
+private:
+#if LOCAL_ASSERTION_PROP
+ void fgKillDependentAssertions(unsigned lclNum DEBUGARG(GenTreePtr tree));
+#endif
+ void fgMorphTreeDone(GenTreePtr tree, GenTreePtr oldTree = nullptr DEBUGARG(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 = nullptr);
+
+#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:
+ struct AddCodeDsc
+ {
+ AddCodeDsc* acdNext;
+ BasicBlock* acdDstBlk; // block to which we jump
+ unsigned acdData;
+ SpecialCodeKind acdKind; // what kind of a special block is this?
+ unsigned short acdStkLvl;
+ };
+
+private:
+ static unsigned acdHelper(SpecialCodeKind codeKind);
+
+ AddCodeDsc* fgAddCodeList;
+ bool fgAddCodeModf;
+ bool fgRngChkThrowAdded;
+ AddCodeDsc* fgExcptnTargetCache[SCK_COUNT];
+
+ BasicBlock* fgRngChkTarget(BasicBlock* block, unsigned stkDepth, SpecialCodeKind kind);
+
+ BasicBlock* fgAddCodeRef(BasicBlock* srcBlk, unsigned refData, SpecialCodeKind kind, unsigned stkDepth = 0);
+
+public:
+ AddCodeDsc* fgFindExcptnTarget(SpecialCodeKind kind, unsigned refData);
+
+private:
+ bool fgIsCodeAdded();
+
+ bool fgIsThrowHlpBlk(BasicBlock* block);
+ unsigned fgThrowHlpBlkStkLevel(BasicBlock* block);
+
+ unsigned fgBigOffsetMorphingTemps[TYP_COUNT];
+
+ unsigned fgCheckInlineDepthAndRecursion(InlineInfo* inlineInfo);
+ void fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* result);
+ void fgInsertInlineeBlocks(InlineInfo* pInlineInfo);
+ GenTreePtr fgInlinePrependStatements(InlineInfo* inlineInfo);
+
+#if FEATURE_MULTIREG_RET
+ GenTreePtr fgGetStructAsStructPtr(GenTreePtr tree);
+ GenTreePtr fgAssignStructInlineeToVar(GenTreePtr child, CORINFO_CLASS_HANDLE retClsHnd);
+ void fgAttachStructInlineeToAsg(GenTreePtr tree, GenTreePtr child, CORINFO_CLASS_HANDLE retClsHnd);
+#endif // FEATURE_MULTIREG_RET
+
+ 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(GenTree** pTree, 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);
+
+#ifdef DEBUG
+ bool fgPrintInlinedMethods;
+#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, JitSimplerHashBehavior> 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_pHoistedInCurLoop(nullptr)
+ , m_hoistedInParentLoops(comp->getAllocatorLoopHoist())
+ , 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)
+ };
+
+public:
+ // 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
+ // type 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,
+ JitSimplerHashBehavior>
+ 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,
+ JitSimplerHashBehavior>
+ 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);
+ }
+ };
+
+protected:
+ bool fgMightHaveLoop(); // returns true if there are any backedges
+ bool fgHasLoops; // True if this method has any loops, set in fgComputeReachability
+
+public:
+ LoopDsc optLoopTable[MAX_LOOP_NUM]; // loop descriptor table
+ unsigned char optLoopCount; // number of tracked loops
+
+protected:
+ 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. Note that the predecessor lists are not updated.
+ 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, ValueNumPair vnpNarrow, 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 = 2;
+
+ /* 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(GenTree* firstNode, GenTree* secondNode);
+ bool optCSE_canSwap(GenTree* 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 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*, JitSimplerHashBehavior>
+ LclNumToGenTreePtrStack;
+
+ // Kill set to track variables with intervening definitions.
+ VARSET_TP optCopyPropKillSet;
+
+ // 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);
+ bool optIsSsaLocal(GenTreePtr tree);
+ int optCopyProp_LclVarScore(LclVarDsc* lclVarDsc, LclVarDsc* copyVarDsc, bool preferOp2);
+ void optVnCopyProp();
+
+ /**************************************************************************
+ * Early value propagation
+ *************************************************************************/
+ struct SSAName
+ {
+ unsigned m_lvNum;
+ unsigned m_ssaNum;
+
+ SSAName(unsigned lvNum, unsigned ssaNum) : m_lvNum(lvNum), m_ssaNum(ssaNum)
+ {
+ }
+
+ static unsigned GetHashCode(SSAName ssaNm)
+ {
+ return (ssaNm.m_lvNum << 16) | (ssaNm.m_ssaNum);
+ }
+
+ static bool Equals(SSAName ssaNm1, SSAName ssaNm2)
+ {
+ return (ssaNm1.m_lvNum == ssaNm2.m_lvNum) && (ssaNm1.m_ssaNum == ssaNm2.m_ssaNum);
+ }
+ };
+
+#define OMF_HAS_NEWARRAY 0x00000001 // Method contains 'new' of an array
+#define OMF_HAS_NEWOBJ 0x00000002 // Method contains 'new' of an object type.
+#define OMF_HAS_ARRAYREF 0x00000004 // Method contains array element loads or stores.
+#define OMF_HAS_VTABLEREF 0x00000008 // Method contains method table reference.
+#define OMF_HAS_NULLCHECK 0x00000010 // Method contains null check.
+
+ unsigned optMethodFlags;
+
+ // Recursion bound controls how far we can go backwards tracking for a SSA value.
+ // No throughput diff was found with backward walk bound between 3-8.
+ static const int optEarlyPropRecurBound = 5;
+
+ enum class optPropKind
+ {
+ OPK_INVALID,
+ OPK_ARRAYLEN,
+ OPK_OBJ_GETTYPE,
+ OPK_NULLCHECK
+ };
+
+ bool gtIsVtableRef(GenTreePtr tree);
+ GenTreePtr getArrayLengthFromAllocation(GenTreePtr tree);
+ GenTreePtr getObjectHandleNodeFromAllocation(GenTreePtr tree);
+ GenTreePtr optPropGetValueRec(unsigned lclNum, unsigned ssaNum, optPropKind valueKind, int walkDepth);
+ GenTreePtr optPropGetValue(unsigned lclNum, unsigned ssaNum, optPropKind valueKind);
+ bool optEarlyPropRewriteTree(GenTreePtr tree);
+ bool optDoEarlyPropForBlock(BasicBlock* block);
+ bool optDoEarlyPropForFunc();
+ void optEarlyProp();
+ void optFoldNullCheck(GenTreePtr tree);
+ bool optCanMoveNullCheckPastTree(GenTreePtr tree, bool isInsideTry);
+
+#if ASSERTION_PROP
+ /**************************************************************************
+ * Value/Assertion propagation
+ *************************************************************************/
+public:
+ // Data structures for assertion prop
+ BitVecTraits* apTraits;
+ ASSERT_TP apFull;
+ ASSERT_TP apEmpty;
+
+ enum optAssertionKind
+ {
+ OAK_INVALID,
+ OAK_EQUAL,
+ OAK_NOT_EQUAL,
+ OAK_SUBRANGE,
+ OAK_NO_THROW,
+ OAK_COUNT
+ };
+
+ enum optOp1Kind
+ {
+ O1K_INVALID,
+ O1K_LCLVAR,
+ O1K_ARR_BND,
+ O1K_ARRLEN_OPER_BND,
+ O1K_ARRLEN_LOOP_BND,
+ O1K_CONSTANT_LOOP_BND,
+ O1K_EXACT_TYPE,
+ O1K_SUBTYPE,
+ O1K_VALUE_NUMBER,
+ O1K_COUNT
+ };
+
+ 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,
+ O2K_COUNT
+ };
+ 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 IsConstantBound()
+ {
+ return ((assertionKind == OAK_EQUAL || assertionKind == OAK_NOT_EQUAL) &&
+ op1.kind == O1K_CONSTANT_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_INT:
+ return INT_MIN;
+ case TYP_BOOL:
+ case TYP_UBYTE:
+ case TYP_CHAR:
+ 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_INT:
+ return INT_MAX;
+ case TYP_UBYTE:
+ return UCHAR_MAX;
+ case TYP_CHAR:
+ 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));
+
+ case O2K_INVALID:
+ // we will return false
+ break;
+
+ default:
+ assert(!"Unexpected value for op2.kind in AssertionDsc.");
+ break;
+ }
+ 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);
+ }
+ };
+
+ typedef unsigned short AssertionIndex;
+
+protected:
+ static fgWalkPreFn optAddCopiesCallback;
+ static fgWalkPreFn optVNAssertionPropCurStmtVisitor;
+ 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
+ AssertionIndex* optComplementaryAssertionMap;
+ ExpandArray<ASSERT_TP>* optAssertionDep; // table that holds dependent assertions (assertions
+ // using the value of a local var) for each local var
+ AssertionDsc* optAssertionTabPrivate; // table that holds info about value assignments
+ AssertionIndex optAssertionCount; // total number of assertions in the assertion table
+ AssertionIndex optMaxAssertionCount;
+
+public:
+ void optVnNonNullPropCurStmt(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree);
+ fgWalkResult optVNConstantPropCurStmt(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree);
+ GenTreePtr optVNConstantPropOnRelOp(GenTreePtr tree);
+ GenTreePtr optVNConstantPropOnJTrue(BasicBlock* block, GenTreePtr stmt, GenTreePtr test);
+ GenTreePtr optVNConstantPropOnTree(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree);
+ GenTreePtr optPrepareTreeForReplacement(GenTreePtr extractTree, GenTreePtr replaceTree);
+
+ AssertionIndex GetAssertionCount()
+ {
+ return optAssertionCount;
+ }
+ ASSERT_TP* bbJtrueAssertionOut;
+ typedef SimplerHashTable<ValueNum, SmallPrimitiveKeyFuncs<ValueNum>, ASSERT_TP, JitSimplerHashBehavior>
+ ValueNumToAssertsMap;
+ ValueNumToAssertsMap* optValueNumToAsserts;
+
+ static const AssertionIndex NO_ASSERTION_INDEX = 0;
+
+ // Assertion prop helpers.
+ ASSERT_TP& GetAssertionDep(unsigned lclNum);
+ AssertionDsc* optGetAssertion(AssertionIndex assertIndex);
+ void optAssertionInit(bool isLocalProp);
+ void optAssertionTraitsInit(AssertionIndex assertionCount);
+#if LOCAL_ASSERTION_PROP
+ void optAssertionReset(AssertionIndex limit);
+ void optAssertionRemove(AssertionIndex index);
+#endif
+
+ // Assertion prop data flow functions.
+ void optAssertionPropMain();
+ GenTreePtr optVNAssertionPropCurStmt(BasicBlock* block, GenTreePtr stmt);
+ bool optIsTreeKnownIntValue(bool vnBased, GenTreePtr tree, ssize_t* pConstant, unsigned* pIconFlags);
+ ASSERT_TP* optInitAssertionDataflowFlags();
+ ASSERT_TP* optComputeAssertionGen();
+
+ // Assertion Gen functions.
+ void optAssertionGen(GenTreePtr tree);
+ AssertionIndex optAssertionGenPhiDefn(GenTreePtr tree);
+ AssertionIndex optCreateJTrueBoundsAssertion(GenTreePtr tree);
+ AssertionIndex optAssertionGenJtrue(GenTreePtr tree);
+ AssertionIndex optCreateJtrueAssertions(GenTreePtr op1, GenTreePtr op2, Compiler::optAssertionKind assertionKind);
+ AssertionIndex optFindComplementary(AssertionIndex assertionIndex);
+ void optMapComplementary(AssertionIndex assertionIndex, AssertionIndex index);
+
+ // Assertion creation functions.
+ AssertionIndex optCreateAssertion(GenTreePtr op1, GenTreePtr op2, optAssertionKind assertionKind);
+ AssertionIndex optCreateAssertion(GenTreePtr op1,
+ GenTreePtr op2,
+ optAssertionKind assertionKind,
+ AssertionDsc* assertion);
+ void optCreateComplementaryAssertion(AssertionIndex assertionIndex, GenTreePtr op1, GenTreePtr op2);
+
+ bool optAssertionVnInvolvesNan(AssertionDsc* assertion);
+ AssertionIndex optAddAssertion(AssertionDsc* assertion);
+ void optAddVnAssertionMapping(ValueNum vn, AssertionIndex index);
+#ifdef DEBUG
+ void optPrintVnAssertionMapping();
+#endif
+ ASSERT_TP optGetVnMappedAssertions(ValueNum vn);
+
+ // Used for respective assertion propagations.
+ AssertionIndex optAssertionIsSubrange(GenTreePtr tree, var_types toType, ASSERT_VALARG_TP assertions);
+ AssertionIndex optAssertionIsSubtype(GenTreePtr tree, GenTreePtr methodTableArg, ASSERT_VALARG_TP assertions);
+ AssertionIndex optAssertionIsNonNullInternal(GenTreePtr op, ASSERT_VALARG_TP assertions);
+ bool optAssertionIsNonNull(GenTreePtr op,
+ ASSERT_VALARG_TP assertions DEBUGARG(bool* pVnBased) DEBUGARG(AssertionIndex* pIndex));
+
+ // Used for Relop propagation.
+ AssertionIndex optGlobalAssertionIsEqualOrNotEqual(ASSERT_VALARG_TP assertions, GenTreePtr op1, GenTreePtr op2);
+ AssertionIndex optLocalAssertionIsEqualOrNotEqual(
+ optOp1Kind op1Kind, unsigned lclNum, optOp2Kind op2Kind, ssize_t cnsVal, ASSERT_VALARG_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(AssertionIndex index));
+ GenTreePtr optConstantAssertionProp(AssertionDsc* curAssertion,
+ const GenTreePtr tree,
+ const GenTreePtr stmt DEBUGARG(AssertionIndex index));
+ GenTreePtr optVnConstantAssertionProp(const GenTreePtr tree, const GenTreePtr stmt);
+
+ // Assertion propagation functions.
+ GenTreePtr optAssertionProp(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionProp_LclVar(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionProp_Ind(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionProp_Cast(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionProp_Call(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionProp_RelOp(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionProp_Comma(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionPropLocal_RelOp(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optAssertionProp_Update(const GenTreePtr newTree, const GenTreePtr tree, const GenTreePtr stmt);
+ GenTreePtr optNonNullAssertionProp_Call(ASSERT_VALARG_TP assertions, const GenTreePtr tree, const GenTreePtr stmt);
+
+ // Implied assertion functions.
+ void optImpliedAssertions(AssertionIndex assertionIndex, ASSERT_TP& activeAssertions);
+ void optImpliedByTypeOfAssertions(ASSERT_TP& activeAssertions);
+ void optImpliedByCopyAssertion(AssertionDsc* copyAssertion, AssertionDsc* depAssertion, ASSERT_TP& result);
+ void optImpliedByConstAssertion(AssertionDsc* curAssertion, ASSERT_TP& result);
+
+ ASSERT_VALRET_TP optNewFullAssertSet();
+ ASSERT_VALRET_TP optNewEmptyAssertSet();
+
+#ifdef DEBUG
+ void optPrintAssertion(AssertionDsc* newAssertion, AssertionIndex assertionIndex = 0);
+ void optDebugCheckAssertion(AssertionDsc* assertion);
+ void optDebugCheckAssertions(AssertionIndex AssertionIndex);
+#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 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
+
+ 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 rpPredictBlkAsgRegUse(GenTreePtr tree, rpPredictReg predictReg, regMaskTP lockedRegs, regMaskTP rsvdRegs);
+
+ 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 = nullptr);
+
+ // 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();
+
+ // Gets the offset of a SDArray's first element
+ unsigned eeGetArrayDataOffset(var_types type);
+ // Gets the offset of a MDArray's first element
+ unsigned eeGetMDArrayDataOffset(var_types type, unsigned rank);
+
+ GenTreePtr eeGetPInvokeCookie(CORINFO_SIG_INFO* szMetaSig);
+
+ // Returns the page size for the target machine as reported by the EE.
+ inline size_t eeGetPageSize()
+ {
+#if COR_JIT_EE_VERSION > 460
+ return eeGetEEInfo()->osPageSize;
+#else // COR_JIT_EE_VERSION <= 460
+ return CORINFO_PAGE_SIZE;
+#endif // COR_JIT_EE_VERSION > 460
+ }
+
+ // Returns the frame size at which we will generate a loop to probe the stack.
+ inline size_t getVeryLargeFrameSize()
+ {
+#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.
+ return 2 * eeGetPageSize();
+#else
+ return 3 * eeGetPageSize();
+#endif
+ }
+
+ inline bool generateCFIUnwindCodes()
+ {
+#if COR_JIT_EE_VERSION > 460 && defined(UNIX_AMD64_ABI)
+ return eeGetEEInfo()->targetAbi == CORINFO_CORERT_ABI;
+#else
+ return false;
+#endif
+ }
+
+ // 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);
+
+ WORD eeGetRelocTypeHint(void* target);
+
+ // ICorStaticInfo wrapper functions
+
+ bool eeTryResolveToken(CORINFO_RESOLVED_TOKEN* resolvedToken);
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+#ifdef DEBUG
+ static void dumpSystemVClassificationType(SystemVClassificationType ct);
+#endif // DEBUG
+
+ void eeGetSystemVAmd64PassStructInRegisterDescriptor(
+ /*IN*/ CORINFO_CLASS_HANDLE structHnd,
+ /*OUT*/ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ template <typename ParamType>
+ bool eeRunWithErrorTrap(void (*function)(ParamType*), ParamType* param)
+ {
+ return eeRunWithErrorTrapImp(reinterpret_cast<void (*)(void*)>(function), reinterpret_cast<void*>(param));
+ }
+
+ bool eeRunWithErrorTrapImp(void (*function)(void*), void* param);
+
+ // Utility functions
+
+ const char* eeGetFieldName(CORINFO_FIELD_HANDLE fieldHnd, const char** classNamePtr = nullptr);
+
+#if defined(DEBUG)
+ const wchar_t* eeGetCPString(size_t stringHandle);
+#endif
+
+ const char* eeGetClassName(CORINFO_CLASS_HANDLE clsHnd);
+
+ 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, JitSimplerHashBehavior>
+ 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 = nullptr);
+
+ 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_OBJ) whose arg is an ADDR, whose arg is a LCL_VAR, return that LCL_VAR
+ // node, else NULL.
+ static GenTreePtr fgIsIndirOfAddrOfLocal(GenTreePtr tree);
+
+ // This is indexed by GT_OBJ 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 == nullptr)
+ {
+ 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);
+
+ void unwindBegPrologWindows();
+ void unwindPushWindows(regNumber reg);
+ void unwindAllocStackWindows(unsigned size);
+ void unwindSetFrameRegWindows(regNumber reg, unsigned offset);
+ void unwindSaveRegWindows(regNumber reg, unsigned offset);
+
+#ifdef UNIX_AMD64_ABI
+ void unwindBegPrologCFI();
+ void unwindPushCFI(regNumber reg);
+ void unwindAllocStackCFI(unsigned size);
+ void unwindSetFrameRegCFI(regNumber reg, unsigned offset);
+ void unwindSaveRegCFI(regNumber reg, unsigned offset);
+ int mapRegNumToDwarfReg(regNumber reg);
+ void createCfiCode(FuncInfoDsc* func, UCHAR codeOffset, UCHAR opcode, USHORT dwarfReg, INT offset = 0);
+#endif // UNIX_AMD64_ABI
+#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();
+ return 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;
+
+ // Get the handle for a SIMD type.
+ CORINFO_CLASS_HANDLE gtGetStructHandleForSIMD(var_types simdType, var_types simdBaseType)
+ {
+ if (simdBaseType == TYP_FLOAT)
+ {
+ switch (simdType)
+ {
+ case TYP_SIMD8:
+ return SIMDVector2Handle;
+ case TYP_SIMD12:
+ return SIMDVector3Handle;
+ case TYP_SIMD16:
+ if ((getSIMDVectorType() == TYP_SIMD32) || (SIMDVector4Handle != NO_CLASS_HANDLE))
+ {
+ return SIMDVector4Handle;
+ }
+ break;
+ case TYP_SIMD32:
+ break;
+ default:
+ unreached();
+ }
+ }
+ assert(simdType == getSIMDVectorType());
+ switch (simdBaseType)
+ {
+ case TYP_FLOAT:
+ return SIMDFloatHandle;
+ case TYP_DOUBLE:
+ return SIMDDoubleHandle;
+ case TYP_INT:
+ return SIMDIntHandle;
+ case TYP_CHAR:
+ return SIMDUShortHandle;
+ case TYP_USHORT:
+ return SIMDUShortHandle;
+ case TYP_UBYTE:
+ return SIMDUByteHandle;
+ case TYP_SHORT:
+ return SIMDShortHandle;
+ case TYP_BYTE:
+ return SIMDByteHandle;
+ case TYP_LONG:
+ return SIMDLongHandle;
+ case TYP_UINT:
+ return SIMDUIntHandle;
+ case TYP_ULONG:
+ return SIMDULongHandle;
+ default:
+ assert(!"Didn't find a class handle for simdType");
+ }
+ return NO_CLASS_HANDLE;
+ }
+
+ // SIMD Methods
+ CORINFO_METHOD_HANDLE SIMDVectorFloat_set_Item;
+ CORINFO_METHOD_HANDLE SIMDVectorFloat_get_Length;
+ CORINFO_METHOD_HANDLE SIMDVectorFloat_op_Addition;
+
+ // 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 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 varTypeIsSIMD(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;
+ }
+
+ bool isSIMDClass(CORINFO_CLASS_HANDLE clsHnd)
+ {
+ return info.compCompHnd->isInSIMDModule(clsHnd);
+ }
+
+ 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(var_types type, bool expectAddr = false);
+
+ // Create a GT_SIMD tree for a Get property of SIMD vector with a fixed index.
+ GenTreeSIMD* impSIMDGetFixed(var_types simdType, 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 areArrayElementsContiguous(GenTreePtr op1, GenTreePtr op2);
+ bool areArgumentsContiguous(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.
+ // This is the maximum SIMD type supported for this target.
+ 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.
+ int getSIMDTypeAlignment(var_types simdType);
+
+ // Get the number of bytes in a SIMD Vector for the current compilation.
+ 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
+ }
+
+ // The minimum and maximum possible number of bytes in a SIMD vector.
+ unsigned int maxSIMDStructBytes()
+ {
+ return getSIMDVectorRegisterByteLength();
+ }
+ unsigned int minSIMDStructBytes()
+ {
+ return emitTypeSize(TYP_SIMD8);
+ }
+
+#ifdef FEATURE_AVX_SUPPORT
+ // (maxPossibleSIMDStructBytes is for use in a context that requires a compile-time constant.)
+ static const unsigned maxPossibleSIMDStructBytes = 32;
+#else // !FEATURE_AVX_SUPPORT
+ static const unsigned maxPossibleSIMDStructBytes = 16;
+#endif // !FEATURE_AVX_SUPPORT
+
+ // Returns the codegen type for a given SIMD size.
+ var_types getSIMDTypeForSize(unsigned size)
+ {
+ var_types simdType = TYP_UNDEF;
+ if (size == 8)
+ {
+ simdType = TYP_SIMD8;
+ }
+ 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
+
+public:
+ //------------------------------------------------------------------------
+ // largestEnregisterableStruct: The size in bytes of the largest struct that can be enregistered.
+ //
+ // Notes: It is not guaranteed that the struct of this size or smaller WILL be a
+ // candidate for enregistration.
+
+ unsigned largestEnregisterableStructSize()
+ {
+#ifdef FEATURE_SIMD
+ unsigned vectorRegSize = getSIMDVectorRegisterByteLength();
+ if (vectorRegSize > TARGET_POINTER_SIZE)
+ {
+ return vectorRegSize;
+ }
+ else
+#endif // FEATURE_SIMD
+ {
+ return TARGET_POINTER_SIZE;
+ }
+ }
+
+private:
+ // 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].lvType);
+ 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
+
+ InlineResult* compInlineResult; // The result of importing the inlinee method.
+
+ bool compDoAggressiveInlining; // If true, mark every method as CORINFO_FLG_FORCEINLINE
+ 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
+
+#ifdef DEBUG
+ // State information - which phases have completed?
+ // These are kept together for easy discoverability
+
+ bool bRangeAllowStress;
+ bool compCodeGenDone;
+ int64_t compNumStatementLinksTraversed; // # of links traversed while doing debug checks
+ bool fgNormalizeEHDone; // Has the flowgraph EH normalization phase been done?
+ size_t compSizeEstimate; // The estimated size of the method as per `gtSetEvalOrder`.
+ size_t compCycleEstimate; // The estimated cycle count of the method as per `gtSetEvalOrder`
+#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 compUsesThrowHelper; // There is a call to a THOROW_HELPER for the compiled method.
+
+ 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
+ {
+ CORJIT_FLAGS* jitFlags; // all flags passed from the EE
+ unsigned eeFlags; // CorJitFlag 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;
+ bool compMinOptsIsSet;
+#ifdef DEBUG
+ bool compMinOptsIsUsed;
+
+ inline bool MinOpts()
+ {
+ assert(compMinOptsIsSet);
+ compMinOptsIsUsed = true;
+ return compMinOpts;
+ }
+ inline bool IsMinOptsSet()
+ {
+ return compMinOptsIsSet;
+ }
+#else // !DEBUG
+ inline bool MinOpts()
+ {
+ return compMinOpts;
+ }
+ inline bool IsMinOptsSet()
+ {
+ return compMinOptsIsSet;
+ }
+#endif // !DEBUG
+ inline void SetMinOpts(bool val)
+ {
+ assert(!compMinOptsIsUsed);
+ assert(!compMinOptsIsSet || (compMinOpts == val));
+ compMinOpts = val;
+ compMinOptsIsSet = true;
+ }
+
+ // 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
+
+ // true if we should use the PINVOKE_{BEGIN,END} helpers instead of generating
+ // PInvoke transitions inline (e.g. when targeting CoreRT).
+ inline bool ShouldUsePInvokeHelpers()
+ {
+#if COR_JIT_EE_VERSION > 460
+ return (jitFlags->corJitFlags2 & CORJIT_FLG2_USE_PINVOKE_HELPERS) != 0;
+#else
+ return false;
+#endif
+ }
+
+ // true if we should use insert the REVERSE_PINVOKE_{ENTER,EXIT} helpers in the method
+ // prolog/epilog
+ inline bool IsReversePInvoke()
+ {
+#if COR_JIT_EE_VERSION > 460
+ return (jitFlags->corJitFlags2 & CORJIT_FLG2_REVERSE_PINVOKE) != 0;
+#else
+ return false;
+#endif
+ }
+
+ // true if we must generate code compatible with JIT32 quirks
+ inline bool IsJit32Compat()
+ {
+#if defined(_TARGET_X86_) && COR_JIT_EE_VERSION > 460
+ return (jitFlags->corJitFlags2 & CORJIT_FLG2_DESKTOP_QUIRKS) != 0;
+#else
+ return false;
+#endif
+ }
+
+ // true if we must generate code compatible with Jit64 quirks
+ inline bool IsJit64Compat()
+ {
+#if defined(_TARGET_AMD64_) && COR_JIT_EE_VERSION > 460
+ return (jitFlags->corJitFlags2 & CORJIT_FLG2_DESKTOP_QUIRKS) != 0;
+#elif defined(_TARGET_AMD64_) && !defined(FEATURE_CORECLR)
+ return true;
+#else
+ 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 DEBUG
+#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
+ bool compEnablePCRelAddr; // Whether absolute addr be encoded as PC-rel offset by RyuJIT where possible
+#endif
+#endif // DEBUG
+
+#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 compLongAddress; // Force using large pseudo instructions for long address
+ // (IF_LARGEJMP/IF_LARGEADR/IF_LARGLDC)
+ 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;
+ // Whether optimization of transforming a recursive tail call into a loop is enabled.
+ bool compTailCallLoopOpt;
+#endif
+
+#ifdef ARM_SOFTFP
+ static const bool compUseSoftFP = true;
+#else // !ARM_SOFTFP
+ static const bool compUseSoftFP = false;
+#endif
+
+ GCPollType compGCPollType;
+ } opts;
+
+#ifdef ALT_JIT
+ static bool s_pAltJitExcludeAssembliesListInitialized;
+ 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 == ZERO) ? ZERO : (opts.dspDiffable ? T(0xD1FFAB1E) : p);
+ }
+
+ template <typename T>
+ T dspOffset(T o)
+ {
+ return (o == ZERO) ? ZERO : (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
+
+// clang-format off
+#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(LEGACY_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(RANDOM_INLINE) \
+ \
+ 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
+ };
+// clang-format on
+
+#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);
+
+#ifdef DEBUG
+
+ bool compInlineStress()
+ {
+ return compStressCompile(STRESS_LEGACY_INLINE, 50);
+ }
+
+ bool compRandomInlineStress()
+ {
+ return compStressCompile(STRESS_RANDOM_INLINE, 50);
+ }
+
+#endif // DEBUG
+
+ bool compTailCallStress()
+ {
+#ifdef DEBUG
+ return (JitConfig.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
+ }
+
+#ifdef DEBUG
+ CLRRandom* inlRNG;
+#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)
+
+#if defined(DEBUG) || defined(INLINE_DATA)
+ // Method hash is logcally const, but computed
+ // on first demand.
+ mutable unsigned compMethodHashPrivate;
+ unsigned compMethodHash() const;
+#endif // defined(DEBUG) || defined(INLINE_DATA)
+
+#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; // Return type of the method as declared in IL
+ var_types compRetNativeType; // Normalized return type as per target arch ABI
+ 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
+
+ unsigned compCallUnmanaged; // count of unmanaged calls
+ unsigned compLvFrameListRoot; // lclNum for the Frame root
+ 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;
+
+ // Returns true if the method being compiled returns a non-void and non-struct value.
+ // Note that lvaInitTypeRef() normalizes compRetNativeType for struct returns in a
+ // single register as per target arch ABI (e.g on Amd64 Windows structs of size 1, 2,
+ // 4 or 8 gets normalized to TYP_BYTE/TYP_SHORT/TYP_INT/TYP_LONG; On Arm HFA structs).
+ // Methods returning such structs are considered to return non-struct return value and
+ // this method returns true in that case.
+ bool compMethodReturnsNativeScalarType()
+ {
+ return (info.compRetType != TYP_VOID) && !varTypeIsStruct(info.compRetNativeType);
+ }
+
+ // Returns true if the method being compiled returns RetBuf addr as its return value
+ bool compMethodReturnsRetBufAddr()
+ {
+ // There are cases where implicit RetBuf argument should be explicitly returned in a register.
+ // In such cases the return type is changed to TYP_BYREF and appropriate IR is generated.
+ // These cases are:
+ // 1. Profiler Leave calllback expects the address of retbuf as return value for
+ // methods with hidden RetBuf argument. impReturnInstruction() when profiler
+ // callbacks are needed creates GT_RETURN(TYP_BYREF, op1 = Addr of RetBuf) for
+ // methods with hidden RetBufArg.
+ //
+ // 2. As per the System V ABI, the address of RetBuf needs to be returned by
+ // methods with hidden RetBufArg in RAX. In such case GT_RETURN is of TYP_BYREF,
+ // returning the address of RetBuf.
+ //
+ // 3. Windows 64-bit native calling convention also requires the address of RetBuff
+ // to be returned in RAX.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef _TARGET_AMD64_
+ return (info.compRetBuffArg != BAD_VAR_NUM);
+#else // !_TARGET_AMD64_
+ return (compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM);
+#endif // !_TARGET_AMD64_
+ }
+
+ // Returns true if the method returns a value in more than one return register
+ // TODO-ARM-Bug: Deal with multi-register genReturnLocaled structs?
+ // TODO-ARM64: Does this apply for ARM64 too?
+ bool compMethodReturnsMultiRegRetType()
+ {
+#if FEATURE_MULTIREG_RET
+#if defined(_TARGET_X86_)
+ // On x86 only 64-bit longs are returned in multiple registers
+ return varTypeIsLong(info.compRetNativeType);
+#else // targets: X64-UNIX, ARM64 or ARM32
+ // On all other targets that support multireg return values:
+ // Methods returning a struct in multiple registers have a return value of TYP_STRUCT.
+ // Such method's compRetNativeType is TYP_STRUCT without a hidden RetBufArg
+ return varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM);
+#endif // TARGET_XXX
+#else // not FEATURE_MULTIREG_RET
+ // For this architecture there are no multireg returns
+ return false;
+#endif // FEATURE_MULTIREG_RET
+ }
+
+#if FEATURE_MULTIREG_ARGS
+ // Given a GenTree node of TYP_STRUCT that represents a pass by value argument
+ // return the gcPtr layout for the pointers sized fields
+ void getStructGcPtrsFromOp(GenTreePtr op, BYTE* gcPtrsOut);
+#endif // FEATURE_MULTIREG_ARGS
+
+ // Returns true if the method being compiled returns a value
+ bool compMethodHasRetVal()
+ {
+ return compMethodReturnsNativeScalarType() || compMethodReturnsRetBufAddr() ||
+ compMethodReturnsMultiRegRetType();
+ }
+
+#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_) && !FEATURE_STACK_FP_X87
+ // Mask of callee saved float regs on stack.
+ regMaskTP compCalleeFPRegsSavedMask;
+#endif
+#ifdef _TARGET_AMD64_
+// 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;
+ bool compQuirkForPPPflag;
+#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(ArenaAllocator* 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,
+ CORJIT_FLAGS* compileFlags);
+ void compCompileFinish();
+ int compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
+ COMP_HANDLE compHnd,
+ CORINFO_METHOD_INFO* methodInfo,
+ void** methodCodePtr,
+ ULONG* methodCodeSize,
+ CORJIT_FLAGS* compileFlags,
+ CorInfoInstantiationVerification instVerInfo);
+
+ ArenaAllocator* 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 jitstdout.
+ };
+
+ 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();
+ 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*, JitSimplerHashBehavior>
+ 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
+
+ ArenaAllocator* 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
+
+ void compFunctionTraceStart();
+ void compFunctionTraceEnd(void* methodCodePtr, ULONG methodCodeSize, bool isNYI);
+
+protected:
+ size_t compMaxUncheckedOffsetForNullObject;
+
+ void compInitOptions(CORJIT_FLAGS* compileFlags);
+
+ void compSetProcessor();
+ void compInitDebuggingInfo();
+ void compSetOptimizationLevel();
+#ifdef _TARGET_ARMARCH_
+ bool compRsvdRegCheck(FrameLayoutState curState);
+#endif
+ void compCompile(void** methodCodePtr, ULONG* methodCodeSize, CORJIT_FLAGS* compileFlags);
+
+ // Data required for generating profiler Enter/Leave/TailCall hooks
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#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
+#ifdef _TARGET_AMD64_
+ bool compQuirkForPPP(); // Check if this method should be Quirked for the PPP issue
+#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;
+
+ 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; // Lcl var num, valid only if not set to NO_SHADOW_COPY
+
+ 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 DEFAULT_MAX_INLINE_SIZE 100 // Methods with > DEFAULT_MAX_INLINE_SIZE IL bytes will never be inlined.
+ // This can be overwritten by setting complus_JITInlineSize env variable.
+
+#define DEFAULT_MAX_INLINE_DEPTH 20 // Methods at more than this level deep will not be inlined
+
+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.
+
+#if defined(DEBUG) || defined(INLINE_DATA) || defined(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.
+ unsigned __int64 m_compCycles; // Net cycle count for 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.
+#endif // defined(DEBUG) || defined(INLINE_DATA) || defined(FEATURE_CLRSQM)
+
+ // 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 RecordStateAtEndOfInlining();
+ // Assumes being called at the end of compilation. Update the SQM state.
+ void RecordStateAtEndOfCompilation();
+
+#ifdef FEATURE_CLRSQM
+ // 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();
+
+#ifndef FEATURE_TRACELOGGING
+ // Should we actually fire the noway assert body and the exception handler?
+ bool compShouldThrowOnNoway();
+#else // FEATURE_TRACELOGGING
+ // Should we actually fire the noway assert body and the exception handler?
+ bool compShouldThrowOnNoway(const char* filename, unsigned line);
+
+ // Telemetry instance to use per method compilation.
+ JitTelemetry compJitTelemetry;
+
+ // Get common parameters that have to be logged with most telemetry data.
+ void compGetTelemetryDefaults(const char** assemblyName,
+ const char** scopeName,
+ const char** methodName,
+ unsigned* methodHash);
+#endif // !FEATURE_TRACELOGGING
+
+#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 == nullptr)
+ {
+ compRoot->m_nodeTestData = new (getAllocatorDebugOnly()) NodeToTestDataMap(getAllocatorDebugOnly());
+ }
+ return compRoot->m_nodeTestData;
+ }
+
+ typedef SimplerHashTable<GenTreePtr, PtrKeyFuncs<GenTree>, int, JitSimplerHashBehavior> 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 == nullptr)
+ {
+ // 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*, JitSimplerHashBehavior> 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 == nullptr)
+ {
+ // 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.
+ //
+ // One exception above is that "op1" is a node of type "TYP_REF" where "op1" is a GT_LCL_VAR.
+ // This happens when System.Object vtable pointer is a regular field at offset 0 in System.Private.CoreLib in
+ // CoreRT. Such case is handled same as the default case.
+ void fgAddFieldSeqForZeroOffset(GenTreePtr op1, FieldSeqNode* fieldSeq);
+
+ typedef SimplerHashTable<const GenTree*, PtrKeyFuncs<GenTree>, ArrayInfo, JitSimplerHashBehavior>
+ NodeToArrayInfoMap;
+ NodeToArrayInfoMap* m_arrayInfoMap;
+
+ NodeToArrayInfoMap* GetArrayInfoMap()
+ {
+ Compiler* compRoot = impInlineRoot();
+ if (compRoot->m_arrayInfoMap == 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_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 == nullptr)
+ {
+ m_refAnyClass = info.compCompHnd->getBuiltinClass(CLASSID_TYPED_BYREF);
+ }
+ return info.compCompHnd->getFieldInClass(m_refAnyClass, 0);
+ }
+ CORINFO_FIELD_HANDLE GetRefanyTypeField()
+ {
+ if (m_refAnyClass == nullptr)
+ {
+ 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;
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ static var_types GetTypeFromClassificationAndSizes(SystemVClassificationType classType, int size);
+ static var_types GetEightByteType(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc,
+ unsigned slotNum);
+ static void GetStructTypeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc,
+ var_types* type0,
+ var_types* type1,
+ unsigned __int8* offset0,
+ unsigned __int8* offset1);
+ void fgMorphSystemVStructArgs(GenTreeCall* call, bool hasStructArgument);
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+
+ void fgMorphMultiregStructArgs(GenTreeCall* call);
+ GenTreePtr fgMorphMultiregStructArg(GenTreePtr arg, fgArgTabEntryPtr fgEntryPtr);
+
+}; // 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)
+ : // Initialize the ArgRegs to REG_STK.
+ // The morph will do the right thing to change
+ // to the right register if passed in register.
+ _lvArgReg(REG_STK)
+ ,
+#if FEATURE_MULTIREG_ARGS
+ _lvOtherArgReg(REG_STK)
+ ,
+#endif // FEATURE_MULTIREG_ARGS
+#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 Histogram bbCntTable;
+extern Histogram 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 Histogram loopCountTable; // Histogram of loop counts
+extern Histogram 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 Histogram genTreeNcntHist;
+extern Histogram 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[];
+
+/*****************************************************************************/
+
+// VERY_LARGE_FRAME_SIZE_REG_MASK is the set of registers we need to use for
+// the probing loop generated for very large stack frames (see `getVeryLargeFrameSize`).
+
+#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 cVarsFinal(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 cFuncIR(Compiler* comp);
+void cBlockIR(Compiler* comp, BasicBlock* block);
+void cLoopIR(Compiler* comp, Compiler::LoopDsc* loop);
+void cTreeIR(Compiler* comp, GenTree* tree);
+int cTreeTypeIR(Compiler* comp, GenTree* tree);
+int cTreeKindsIR(Compiler* comp, GenTree* tree);
+int cTreeFlagsIR(Compiler* comp, GenTree* tree);
+int cOperandIR(Compiler* comp, GenTree* operand);
+int cLeafIR(Compiler* comp, GenTree* tree);
+int cIndirIR(Compiler* comp, GenTree* tree);
+int cListIR(Compiler* comp, GenTree* list);
+int cSsaNumIR(Compiler* comp, GenTree* tree);
+int cValNumIR(Compiler* comp, GenTree* tree);
+int cDependsIR(Compiler* comp, GenTree* comma, bool* first);
+
+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 dVarsFinal();
+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);
+
+void dFuncIR();
+void dBlockIR(BasicBlock* block);
+void dTreeIR(GenTree* tree);
+void dLoopIR(Compiler::LoopDsc* loop);
+void dLoopNumIR(unsigned loopNum);
+int dTabStopIR(int curr, int tabstop);
+int dTreeTypeIR(GenTree* tree);
+int dTreeKindsIR(GenTree* tree);
+int dTreeFlagsIR(GenTree* tree);
+int dOperandIR(GenTree* operand);
+int dLeafIR(GenTree* tree);
+int dIndirIR(GenTree* tree);
+int dListIR(GenTree* list);
+int dSsaNumIR(GenTree* tree);
+int dValNumIR(GenTree* tree);
+int dDependsIR(GenTree* comma);
+void dFormatIR();
+
+GenTree* dFindTree(GenTree* tree, unsigned id);
+GenTree* dFindTree(unsigned id);
+GenTreeStmt* dFindStmt(unsigned id);
+BasicBlock* dFindBlock(unsigned bbNum);
+
+#endif // DEBUG
+
+#include "compiler.hpp" // All the shared inline functions
+
+/*****************************************************************************/
+#endif //_COMPILER_H_
+/*****************************************************************************/