summaryrefslogtreecommitdiff
path: root/src/jit/compiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/compiler.cpp')
-rw-r--r--src/jit/compiler.cpp10380
1 files changed, 10380 insertions, 0 deletions
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
new file mode 100644
index 0000000000..afbecdfc60
--- /dev/null
+++ b/src/jit/compiler.cpp
@@ -0,0 +1,10380 @@
+// 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
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
+#include "jitpch.h"
+#ifdef _MSC_VER
+#pragma hdrstop
+#endif // _MSC_VER
+#include "hostallocator.h"
+#include "emit.h"
+#include "ssabuilder.h"
+#include "valuenum.h"
+#include "rangecheck.h"
+
+#ifndef LEGACY_BACKEND
+#include "lower.h"
+#endif // !LEGACY_BACKEND
+
+#include "jittelemetry.h"
+
+#if defined(DEBUG)
+// Column settings for COMPlus_JitDumpIR. We could(should) make these programmable.
+#define COLUMN_OPCODE 30
+#define COLUMN_OPERANDS (COLUMN_OPCODE + 25)
+#define COLUMN_KINDS 110
+#define COLUMN_FLAGS (COLUMN_KINDS + 32)
+#endif
+
+#if defined(DEBUG)
+unsigned Compiler::jitTotalMethodCompiled = 0;
+#endif // defined(DEBUG)
+
+#if defined(DEBUG)
+LONG Compiler::jitNestingLevel = 0;
+#endif // defined(DEBUG)
+
+#ifdef ALT_JIT
+// static
+bool Compiler::s_pAltJitExcludeAssembliesListInitialized = false;
+AssemblyNamesList2* Compiler::s_pAltJitExcludeAssembliesList = nullptr;
+#endif // ALT_JIT
+
+/*****************************************************************************/
+inline unsigned getCurTime()
+{
+ SYSTEMTIME tim;
+
+ GetSystemTime(&tim);
+
+ return (((tim.wHour * 60) + tim.wMinute) * 60 + tim.wSecond) * 1000 + tim.wMilliseconds;
+}
+
+/*****************************************************************************/
+#ifdef DEBUG
+/*****************************************************************************/
+
+static FILE* jitSrcFilePtr;
+
+static unsigned jitCurSrcLine;
+
+void Compiler::JitLogEE(unsigned level, const char* fmt, ...)
+{
+ va_list args;
+
+ if (verbose)
+ {
+ va_start(args, fmt);
+ vflogf(jitstdout, fmt, args);
+ va_end(args);
+ }
+
+ va_start(args, fmt);
+ vlogf(level, fmt, args);
+ va_end(args);
+}
+
+void Compiler::compDspSrcLinesByLineNum(unsigned line, bool seek)
+{
+ if (!jitSrcFilePtr)
+ {
+ return;
+ }
+
+ if (jitCurSrcLine == line)
+ {
+ return;
+ }
+
+ if (jitCurSrcLine > line)
+ {
+ if (!seek)
+ {
+ return;
+ }
+
+ if (fseek(jitSrcFilePtr, 0, SEEK_SET) != 0)
+ {
+ printf("Compiler::compDspSrcLinesByLineNum: fseek returned an error.\n");
+ }
+ jitCurSrcLine = 0;
+ }
+
+ if (!seek)
+ {
+ printf(";\n");
+ }
+
+ do
+ {
+ char temp[128];
+ size_t llen;
+
+ if (!fgets(temp, sizeof(temp), jitSrcFilePtr))
+ {
+ return;
+ }
+
+ if (seek)
+ {
+ continue;
+ }
+
+ llen = strlen(temp);
+ if (llen && temp[llen - 1] == '\n')
+ {
+ temp[llen - 1] = 0;
+ }
+
+ printf("; %s\n", temp);
+ } while (++jitCurSrcLine < line);
+
+ if (!seek)
+ {
+ printf(";\n");
+ }
+}
+
+/*****************************************************************************/
+
+void Compiler::compDspSrcLinesByNativeIP(UNATIVE_OFFSET curIP)
+{
+#ifdef DEBUGGING_SUPPORT
+
+ static IPmappingDsc* nextMappingDsc;
+ static unsigned lastLine;
+
+ if (!opts.dspLines)
+ {
+ return;
+ }
+
+ if (curIP == 0)
+ {
+ if (genIPmappingList)
+ {
+ nextMappingDsc = genIPmappingList;
+ lastLine = jitGetILoffs(nextMappingDsc->ipmdILoffsx);
+
+ unsigned firstLine = jitGetILoffs(nextMappingDsc->ipmdILoffsx);
+
+ unsigned earlierLine = (firstLine < 5) ? 0 : firstLine - 5;
+
+ compDspSrcLinesByLineNum(earlierLine, true); // display previous 5 lines
+ compDspSrcLinesByLineNum(firstLine, false);
+ }
+ else
+ {
+ nextMappingDsc = nullptr;
+ }
+
+ return;
+ }
+
+ if (nextMappingDsc)
+ {
+ UNATIVE_OFFSET offset = nextMappingDsc->ipmdNativeLoc.CodeOffset(genEmitter);
+
+ if (offset <= curIP)
+ {
+ IL_OFFSET nextOffs = jitGetILoffs(nextMappingDsc->ipmdILoffsx);
+
+ if (lastLine < nextOffs)
+ {
+ compDspSrcLinesByLineNum(nextOffs);
+ }
+ else
+ {
+ // This offset corresponds to a previous line. Rewind to that line
+
+ compDspSrcLinesByLineNum(nextOffs - 2, true);
+ compDspSrcLinesByLineNum(nextOffs);
+ }
+
+ lastLine = nextOffs;
+ nextMappingDsc = nextMappingDsc->ipmdNext;
+ }
+ }
+
+#endif
+}
+
+/*****************************************************************************/
+#endif // DEBUG
+
+/*****************************************************************************/
+#if defined(DEBUG) || MEASURE_NODE_SIZE || MEASURE_BLOCK_SIZE || DISPLAY_SIZES || CALL_ARG_STATS
+
+static unsigned genMethodCnt; // total number of methods JIT'ted
+unsigned genMethodICnt; // number of interruptible methods
+unsigned genMethodNCnt; // number of non-interruptible methods
+static unsigned genSmallMethodsNeedingExtraMemoryCnt = 0;
+
+#endif
+
+/*****************************************************************************/
+#if MEASURE_NODE_SIZE
+NodeSizeStats genNodeSizeStats;
+NodeSizeStats genNodeSizeStatsPerFunc;
+
+unsigned genTreeNcntHistBuckets[] = {10, 20, 30, 40, 50, 100, 200, 300, 400, 500, 1000, 5000, 10000, 0};
+Histogram genTreeNcntHist(HostAllocator::getHostAllocator(), genTreeNcntHistBuckets);
+
+unsigned genTreeNsizHistBuckets[] = {1000, 5000, 10000, 50000, 100000, 500000, 1000000, 0};
+Histogram genTreeNsizHist(HostAllocator::getHostAllocator(), genTreeNsizHistBuckets);
+#endif // MEASURE_NODE_SIZE
+
+/*****************************************************************************
+ *
+ * Variables to keep track of total code amounts.
+ */
+
+#if DISPLAY_SIZES
+
+size_t grossVMsize; // Total IL code size
+size_t grossNCsize; // Native code + data size
+size_t totalNCsize; // Native code + data + GC info size (TODO-Cleanup: GC info size only accurate for JIT32_GCENCODER)
+size_t gcHeaderISize; // GC header size: interruptible methods
+size_t gcPtrMapISize; // GC pointer map size: interruptible methods
+size_t gcHeaderNSize; // GC header size: non-interruptible methods
+size_t gcPtrMapNSize; // GC pointer map size: non-interruptible methods
+
+#endif // DISPLAY_SIZES
+
+/*****************************************************************************
+ *
+ * Variables to keep track of argument counts.
+ */
+
+#if CALL_ARG_STATS
+
+unsigned argTotalCalls;
+unsigned argHelperCalls;
+unsigned argStaticCalls;
+unsigned argNonVirtualCalls;
+unsigned argVirtualCalls;
+
+unsigned argTotalArgs; // total number of args for all calls (including objectPtr)
+unsigned argTotalDWordArgs;
+unsigned argTotalLongArgs;
+unsigned argTotalFloatArgs;
+unsigned argTotalDoubleArgs;
+
+unsigned argTotalRegArgs;
+unsigned argTotalTemps;
+unsigned argTotalLclVar;
+unsigned argTotalDeferred;
+unsigned argTotalConst;
+
+unsigned argTotalObjPtr;
+unsigned argTotalGTF_ASGinArgs;
+
+unsigned argMaxTempsPerMethod;
+
+unsigned argCntBuckets[] = {0, 1, 2, 3, 4, 5, 6, 10, 0};
+Histogram argCntTable(HostAllocator::getHostAllocator(), argCntBuckets);
+
+unsigned argDWordCntBuckets[] = {0, 1, 2, 3, 4, 5, 6, 10, 0};
+Histogram argDWordCntTable(HostAllocator::getHostAllocator(), argDWordCntBuckets);
+
+unsigned argDWordLngCntBuckets[] = {0, 1, 2, 3, 4, 5, 6, 10, 0};
+Histogram argDWordLngCntTable(HostAllocator::getHostAllocator(), argDWordLngCntBuckets);
+
+unsigned argTempsCntBuckets[] = {0, 1, 2, 3, 4, 5, 6, 10, 0};
+Histogram argTempsCntTable(HostAllocator::getHostAllocator(), argTempsCntBuckets);
+
+#endif // CALL_ARG_STATS
+
+/*****************************************************************************
+ *
+ * Variables to keep track of basic block counts.
+ */
+
+#if COUNT_BASIC_BLOCKS
+
+// --------------------------------------------------
+// Basic block count frequency table:
+// --------------------------------------------------
+// <= 1 ===> 26872 count ( 56% of total)
+// 2 .. 2 ===> 669 count ( 58% of total)
+// 3 .. 3 ===> 4687 count ( 68% of total)
+// 4 .. 5 ===> 5101 count ( 78% of total)
+// 6 .. 10 ===> 5575 count ( 90% of total)
+// 11 .. 20 ===> 3028 count ( 97% of total)
+// 21 .. 50 ===> 1108 count ( 99% of total)
+// 51 .. 100 ===> 182 count ( 99% of total)
+// 101 .. 1000 ===> 34 count (100% of total)
+// 1001 .. 10000 ===> 0 count (100% of total)
+// --------------------------------------------------
+
+unsigned bbCntBuckets[] = {1, 2, 3, 5, 10, 20, 50, 100, 1000, 10000, 0};
+Histogram bbCntTable(HostAllocator::getHostAllocator(), bbCntBuckets);
+
+/* Histogram for the IL opcode size of methods with a single basic block */
+
+unsigned bbSizeBuckets[] = {1, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 0};
+Histogram bbOneBBSizeTable(HostAllocator::getHostAllocator(), bbSizeBuckets);
+
+#endif // COUNT_BASIC_BLOCKS
+
+/*****************************************************************************
+ *
+ * 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
+
+unsigned totalLoopMethods; // counts the total number of methods that have natural loops
+unsigned maxLoopsPerMethod; // counts the maximum number of loops a method has
+unsigned totalLoopOverflows; // # of methods that identified more loops than we can represent
+unsigned totalLoopCount; // counts the total number of natural loops
+unsigned totalUnnatLoopCount; // counts the total number of (not-necessarily natural) loops
+unsigned totalUnnatLoopOverflows; // # of methods that identified more unnatural loops than we can represent
+unsigned iterLoopCount; // counts the # of loops with an iterator (for like)
+unsigned simpleTestLoopCount; // counts the # of loops with an iterator and a simple loop condition (iter < const)
+unsigned constIterLoopCount; // counts the # of loops with a constant iterator (for like)
+bool hasMethodLoops; // flag to keep track if we already counted a method as having loops
+unsigned loopsThisMethod; // counts the number of loops in the current method
+bool loopOverflowThisMethod; // True if we exceeded the max # of loops in the method.
+
+/* Histogram for number of loops in a method */
+
+unsigned loopCountBuckets[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0};
+Histogram loopCountTable(HostAllocator::getHostAllocator(), loopCountBuckets);
+
+/* Histogram for number of loop exits */
+
+unsigned loopExitCountBuckets[] = {0, 1, 2, 3, 4, 5, 6, 0};
+Histogram loopExitCountTable(HostAllocator::getHostAllocator(), loopExitCountBuckets);
+
+#endif // COUNT_LOOPS
+
+//------------------------------------------------------------------------
+// getJitGCType: Given the VM's CorInfoGCType convert it to the JIT's var_types
+//
+// Arguments:
+// gcType - an enum value that originally came from an element
+// of the BYTE[] returned from getClassGClayout()
+//
+// Return Value:
+// The corresponsing enum value from the JIT's var_types
+//
+// Notes:
+// The gcLayout of each field of a struct is returned from getClassGClayout()
+// as a BYTE[] but each BYTE element is actually a CorInfoGCType value
+// Note when we 'know' that there is only one element in theis array
+// the JIT will often pass the address of a single BYTE, instead of a BYTE[]
+//
+
+var_types Compiler::getJitGCType(BYTE gcType)
+{
+ var_types result = TYP_UNKNOWN;
+ CorInfoGCType corInfoType = (CorInfoGCType)gcType;
+
+ if (corInfoType == TYPE_GC_NONE)
+ {
+ result = TYP_I_IMPL;
+ }
+ else if (corInfoType == TYPE_GC_REF)
+ {
+ result = TYP_REF;
+ }
+ else if (corInfoType == TYPE_GC_BYREF)
+ {
+ result = TYP_BYREF;
+ }
+ else
+ {
+ noway_assert(!"Bad value of 'gcType'");
+ }
+ return result;
+}
+
+#if FEATURE_MULTIREG_ARGS
+//---------------------------------------------------------------------------
+// getStructGcPtrsFromOp: Given a GenTree node of TYP_STRUCT that represents
+// a pass by value argument, return the gcPtr layout
+// for the pointers sized fields
+// Arguments:
+// op - the operand of TYP_STRUCT that is passed by value
+// gcPtrsOut - an array of BYTES that are written by this method
+// they will contain the VM's CorInfoGCType values
+// for each pointer sized field
+// Return Value:
+// Two [or more] values are written into the gcPtrs array
+//
+// Note that for ARM64 there will alwys be exactly two pointer sized fields
+
+void Compiler::getStructGcPtrsFromOp(GenTreePtr op, BYTE* gcPtrsOut)
+{
+ assert(op->TypeGet() == TYP_STRUCT);
+
+#ifdef _TARGET_ARM64_
+ if (op->OperGet() == GT_OBJ)
+ {
+ CORINFO_CLASS_HANDLE objClass = op->gtObj.gtClass;
+
+ int structSize = info.compCompHnd->getClassSize(objClass);
+ assert(structSize <= 2 * TARGET_POINTER_SIZE);
+
+ BYTE gcPtrsTmp[2] = {TYPE_GC_NONE, TYPE_GC_NONE};
+
+ info.compCompHnd->getClassGClayout(objClass, &gcPtrsTmp[0]);
+
+ gcPtrsOut[0] = gcPtrsTmp[0];
+ gcPtrsOut[1] = gcPtrsTmp[1];
+ }
+ else if (op->OperGet() == GT_LCL_VAR)
+ {
+ GenTreeLclVarCommon* varNode = op->AsLclVarCommon();
+ unsigned varNum = varNode->gtLclNum;
+ assert(varNum < lvaCount);
+ LclVarDsc* varDsc = &lvaTable[varNum];
+
+ // At this point any TYP_STRUCT LclVar must be a 16-byte pass by value argument
+ assert(varDsc->lvSize() == 2 * TARGET_POINTER_SIZE);
+
+ gcPtrsOut[0] = varDsc->lvGcLayout[0];
+ gcPtrsOut[1] = varDsc->lvGcLayout[1];
+ }
+ else
+#endif
+ {
+ noway_assert(!"Unsupported Oper for getStructGcPtrsFromOp");
+ }
+}
+#endif // FEATURE_MULTIREG_ARGS
+
+#ifdef ARM_SOFTFP
+//---------------------------------------------------------------------------
+// IsSingleFloat32Struct:
+// Check if the given struct type contains only one float32 value type
+//
+// Arguments:
+// clsHnd - the handle for the struct type
+//
+// Return Value:
+// true if the given struct type contains only one float32 value type,
+// false otherwise.
+//
+
+bool Compiler::isSingleFloat32Struct(CORINFO_CLASS_HANDLE clsHnd)
+{
+ for (;;)
+ {
+ // all of class chain must be of value type and must have only one field
+ if (!info.compCompHnd->isValueClass(clsHnd) && info.compCompHnd->getClassNumInstanceFields(clsHnd) != 1)
+ {
+ return false;
+ }
+
+ CORINFO_CLASS_HANDLE* pClsHnd = &clsHnd;
+ CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(clsHnd, 0);
+ CorInfoType fieldType = info.compCompHnd->getFieldType(fldHnd, pClsHnd);
+
+ switch (fieldType)
+ {
+ case CORINFO_TYPE_VALUECLASS:
+ clsHnd = *pClsHnd;
+ break;
+
+ case CORINFO_TYPE_FLOAT:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+}
+#endif // ARM_SOFTFP
+
+//-----------------------------------------------------------------------------
+// getPrimitiveTypeForStruct:
+// Get the "primitive" type that is is used for a struct
+// of size 'structSize'.
+// We examine 'clsHnd' to check the GC layout of the struct and
+// return TYP_REF for structs that simply wrap an object.
+// If the struct is a one element HFA, we will return the
+// proper floating point type.
+//
+// Arguments:
+// structSize - the size of the struct type, cannot be zero
+// clsHnd - the handle for the struct type, used when may have
+// an HFA or if we need the GC layout for an object ref.
+//
+// Return Value:
+// The primitive type (i.e. byte, short, int, long, ref, float, double)
+// used to pass or return structs of this size.
+// If we shouldn't use a "primitive" type then TYP_UNKNOWN is returned.
+// Notes:
+// For 32-bit targets (X86/ARM32) the 64-bit TYP_LONG type is not
+// considered a primitive type by this method.
+// So a struct that wraps a 'long' is passed and returned in the
+// same way as any other 8-byte struct
+// For ARM32 if we have an HFA struct that wraps a 64-bit double
+// we will return TYP_DOUBLE.
+//
+var_types Compiler::getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS_HANDLE clsHnd)
+{
+ assert(structSize != 0);
+
+ var_types useType;
+
+ switch (structSize)
+ {
+ case 1:
+ useType = TYP_BYTE;
+ break;
+
+ case 2:
+ useType = TYP_SHORT;
+ break;
+
+#ifndef _TARGET_XARCH_
+ case 3:
+ useType = TYP_INT;
+ break;
+
+#endif // _TARGET_XARCH_
+
+#ifdef _TARGET_64BIT_
+ case 4:
+ if (IsHfa(clsHnd))
+ {
+ // A structSize of 4 with IsHfa, it must be an HFA of one float
+ useType = TYP_FLOAT;
+ }
+ else
+ {
+ useType = TYP_INT;
+ }
+ break;
+
+#ifndef _TARGET_XARCH_
+ case 5:
+ case 6:
+ case 7:
+ useType = TYP_I_IMPL;
+ break;
+
+#endif // _TARGET_XARCH_
+#endif // _TARGET_64BIT_
+
+ case TARGET_POINTER_SIZE:
+#ifdef ARM_SOFTFP
+ // For ARM_SOFTFP, HFA is unsupported so we need to check in another way
+ // This matters only for size-4 struct cause bigger structs would be processed with RetBuf
+ if (isSingleFloat32Struct(clsHnd))
+#else // !ARM_SOFTFP
+ if (IsHfa(clsHnd))
+#endif // ARM_SOFTFP
+ {
+#ifdef _TARGET_64BIT_
+ var_types hfaType = GetHfaType(clsHnd);
+
+ // A structSize of 8 with IsHfa, we have two possiblities:
+ // An HFA of one double or an HFA of two floats
+ //
+ // Check and exclude the case of an HFA of two floats
+ if (hfaType == TYP_DOUBLE)
+ {
+ // We have an HFA of one double
+ useType = TYP_DOUBLE;
+ }
+ else
+ {
+ assert(hfaType == TYP_FLOAT);
+
+ // We have an HFA of two floats
+ // This should be passed or returned in two FP registers
+ useType = TYP_UNKNOWN;
+ }
+#else // a 32BIT target
+ // A structSize of 4 with IsHfa, it must be an HFA of one float
+ useType = TYP_FLOAT;
+#endif // _TARGET_64BIT_
+ }
+ else
+ {
+ BYTE gcPtr = 0;
+ // Check if this pointer-sized struct is wrapping a GC object
+ info.compCompHnd->getClassGClayout(clsHnd, &gcPtr);
+ useType = getJitGCType(gcPtr);
+ }
+ break;
+
+#ifdef _TARGET_ARM_
+ case 8:
+ if (IsHfa(clsHnd))
+ {
+ var_types hfaType = GetHfaType(clsHnd);
+
+ // A structSize of 8 with IsHfa, we have two possiblities:
+ // An HFA of one double or an HFA of two floats
+ //
+ // Check and exclude the case of an HFA of two floats
+ if (hfaType == TYP_DOUBLE)
+ {
+ // We have an HFA of one double
+ useType = TYP_DOUBLE;
+ }
+ else
+ {
+ assert(hfaType == TYP_FLOAT);
+
+ // We have an HFA of two floats
+ // This should be passed or returned in two FP registers
+ useType = TYP_UNKNOWN;
+ }
+ }
+ else
+ {
+ // We don't have an HFA
+ useType = TYP_UNKNOWN;
+ }
+ break;
+#endif // _TARGET_ARM_
+
+ default:
+ useType = TYP_UNKNOWN;
+ break;
+ }
+
+ return useType;
+}
+
+//-----------------------------------------------------------------------------
+// getArgTypeForStruct:
+// Get the type that is used to pass values of the given struct type.
+// If you have already retrieved the struct size then it should be
+// passed as the optional third argument, as this allows us to avoid
+// an extra call to getClassSize(clsHnd)
+//
+// Arguments:
+// clsHnd - the handle for the struct type
+// wbPassStruct - An "out" argument with information about how
+// the struct is to be passed
+// structSize - the size of the struct type,
+// or zero if we should call getClassSize(clsHnd)
+//
+// Return Value:
+// For wbPassStruct you can pass a 'nullptr' and nothing will be written
+// or returned for that out parameter.
+// When *wbPassStruct is SPK_PrimitiveType this method's return value
+// is the primitive type used to pass the struct.
+// When *wbPassStruct is SPK_ByReference this method's return value
+// is always TYP_UNKNOWN and the struct type is passed by reference to a copy
+// When *wbPassStruct is SPK_ByValue or SPK_ByValueAsHfa this method's return value
+// is always TYP_STRUCT and the struct type is passed by value either
+// using multiple registers or on the stack.
+//
+// Assumptions:
+// The size must be the size of the given type.
+// The given class handle must be for a value type (struct).
+//
+// Notes:
+// About HFA types:
+// When the clsHnd is a one element HFA type we return the appropriate
+// floating point primitive type and *wbPassStruct is SPK_PrimitiveType
+// If there are two or more elements in the HFA type then the this method's
+// return value is TYP_STRUCT and *wbPassStruct is SPK_ByValueAsHfa
+//
+var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
+ structPassingKind* wbPassStruct,
+ unsigned structSize /* = 0 */)
+{
+ var_types useType = TYP_UNKNOWN;
+ structPassingKind howToPassStruct = SPK_Unknown; // We must change this before we return
+
+ if (structSize == 0)
+ {
+ structSize = info.compCompHnd->getClassSize(clsHnd);
+ }
+ assert(structSize > 0);
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ // An 8-byte struct may need to be passed in a floating point register
+ // So we always consult the struct "Classifier" routine
+ //
+ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
+ eeGetSystemVAmd64PassStructInRegisterDescriptor(clsHnd, &structDesc);
+
+ // If we have one eightByteCount then we can set 'useType' based on that
+ if (structDesc.eightByteCount == 1)
+ {
+ // Set 'useType' to the type of the first eightbyte item
+ useType = GetEightByteType(structDesc, 0);
+ }
+
+#elif defined(_TARGET_X86_)
+
+ // On x86 we never pass structs as primitive types (unless the VM unwraps them for us)
+ useType = TYP_UNKNOWN;
+
+#else // all other targets
+
+ // The largest primitive type is 8 bytes (TYP_DOUBLE)
+ // so we can skip calling getPrimitiveTypeForStruct when we
+ // have a struct that is larger than that.
+ //
+ if (structSize <= sizeof(double))
+ {
+ // We set the "primitive" useType based upon the structSize
+ // and also examine the clsHnd to see if it is an HFA of count one
+ useType = getPrimitiveTypeForStruct(structSize, clsHnd);
+ }
+
+#endif // all other targets
+
+ // Did we change this struct type into a simple "primitive" type?
+ //
+ if (useType != TYP_UNKNOWN)
+ {
+ // Yes, we should use the "primitive" type in 'useType'
+ howToPassStruct = SPK_PrimitiveType;
+ }
+ else // We can't replace the struct with a "primitive" type
+ {
+ // See if we can pass this struct by value, possibly in multiple registers
+ // or if we should pass it by reference to a copy
+ //
+ if (structSize <= MAX_PASS_MULTIREG_BYTES)
+ {
+ // Structs that are HFA's are passed by value in multiple registers
+ if (IsHfa(clsHnd))
+ {
+ // HFA's of count one should have been handled by getPrimitiveTypeForStruct
+ assert(GetHfaCount(clsHnd) >= 2);
+
+ // setup wbPassType and useType indicate that this is passed by value as an HFA
+ // using multiple registers
+ // (when all of the parameters registers are used, then the stack will be used)
+ howToPassStruct = SPK_ByValueAsHfa;
+ useType = TYP_STRUCT;
+ }
+ else // Not an HFA struct type
+ {
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ // The case of (structDesc.eightByteCount == 1) should have already been handled
+ if (structDesc.eightByteCount > 1)
+ {
+ // setup wbPassType and useType indicate that this is passed by value in multiple registers
+ // (when all of the parameters registers are used, then the stack will be used)
+ howToPassStruct = SPK_ByValue;
+ useType = TYP_STRUCT;
+ }
+ else
+ {
+ assert(structDesc.eightByteCount == 0);
+ // Otherwise we pass this struct by reference to a copy
+ // setup wbPassType and useType indicate that this is passed using one register
+ // (by reference to a copy)
+ howToPassStruct = SPK_ByReference;
+ useType = TYP_UNKNOWN;
+ }
+
+#elif defined(_TARGET_ARM64_)
+
+ // Structs that are pointer sized or smaller should have been handled by getPrimitiveTypeForStruct
+ assert(structSize > TARGET_POINTER_SIZE);
+
+ // On ARM64 structs that are 9-16 bytes are passed by value in multiple registers
+ //
+ if (structSize <= (TARGET_POINTER_SIZE * 2))
+ {
+ // setup wbPassType and useType indicate that this is passed by value in multiple registers
+ // (when all of the parameters registers are used, then the stack will be used)
+ howToPassStruct = SPK_ByValue;
+ useType = TYP_STRUCT;
+ }
+ else // a structSize that is 17-32 bytes in size
+ {
+ // Otherwise we pass this struct by reference to a copy
+ // setup wbPassType and useType indicate that this is passed using one register
+ // (by reference to a copy)
+ howToPassStruct = SPK_ByReference;
+ useType = TYP_UNKNOWN;
+ }
+
+#elif defined(_TARGET_X86_) || defined(_TARGET_ARM_)
+
+ // Otherwise we pass this struct by value on the stack
+ // setup wbPassType and useType indicate that this is passed by value according to the X86/ARM32 ABI
+ howToPassStruct = SPK_ByValue;
+ useType = TYP_STRUCT;
+
+#else // _TARGET_XXX_
+
+ noway_assert(!"Unhandled TARGET in getArgTypeForStruct (with FEATURE_MULTIREG_ARGS=1)");
+
+#endif // _TARGET_XXX_
+ }
+ }
+ else // (structSize > MAX_PASS_MULTIREG_BYTES)
+ {
+ // We have a (large) struct that can't be replaced with a "primitive" type
+ // and can't be passed in multiple registers
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
+
+ // Otherwise we pass this struct by value on the stack
+ // setup wbPassType and useType indicate that this is passed by value according to the X86/ARM32 ABI
+ howToPassStruct = SPK_ByValue;
+ useType = TYP_STRUCT;
+
+#elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+
+ // Otherwise we pass this struct by reference to a copy
+ // setup wbPassType and useType indicate that this is passed using one register (by reference to a copy)
+ howToPassStruct = SPK_ByReference;
+ useType = TYP_UNKNOWN;
+
+#else // _TARGET_XXX_
+
+ noway_assert(!"Unhandled TARGET in getArgTypeForStruct");
+
+#endif // _TARGET_XXX_
+ }
+ }
+
+ // 'howToPassStruct' must be set to one of the valid values before we return
+ assert(howToPassStruct != SPK_Unknown);
+ if (wbPassStruct != nullptr)
+ {
+ *wbPassStruct = howToPassStruct;
+ }
+ return useType;
+}
+
+//-----------------------------------------------------------------------------
+// getReturnTypeForStruct:
+// Get the type that is used to return values of the given struct type.
+// If you have already retrieved the struct size then it should be
+// passed as the optional third argument, as this allows us to avoid
+// an extra call to getClassSize(clsHnd)
+//
+// Arguments:
+// clsHnd - the handle for the struct type
+// wbReturnStruct - An "out" argument with information about how
+// the struct is to be returned
+// structSize - the size of the struct type,
+// or zero if we should call getClassSize(clsHnd)
+//
+// Return Value:
+// For wbReturnStruct you can pass a 'nullptr' and nothing will be written
+// or returned for that out parameter.
+// When *wbReturnStruct is SPK_PrimitiveType this method's return value
+// is the primitive type used to return the struct.
+// When *wbReturnStruct is SPK_ByReference this method's return value
+// is always TYP_UNKNOWN and the struct type is returned using a return buffer
+// When *wbReturnStruct is SPK_ByValue or SPK_ByValueAsHfa this method's return value
+// is always TYP_STRUCT and the struct type is returned using multiple registers.
+//
+// Assumptions:
+// The size must be the size of the given type.
+// The given class handle must be for a value type (struct).
+//
+// Notes:
+// About HFA types:
+// When the clsHnd is a one element HFA type then this method's return
+// value is the appropriate floating point primitive type and
+// *wbReturnStruct is SPK_PrimitiveType.
+// If there are two or more elements in the HFA type and the target supports
+// multireg return types then the return value is TYP_STRUCT and
+// *wbReturnStruct is SPK_ByValueAsHfa.
+// Additionally if there are two or more elements in the HFA type and
+// the target doesn't support multreg return types then it is treated
+// as if it wasn't an HFA type.
+// About returning TYP_STRUCT:
+// Whenever this method's return value is TYP_STRUCT it always means
+// that multiple registers are used to return this struct.
+//
+var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
+ structPassingKind* wbReturnStruct /* = nullptr */,
+ unsigned structSize /* = 0 */)
+{
+ var_types useType = TYP_UNKNOWN;
+ structPassingKind howToReturnStruct = SPK_Unknown; // We must change this before we return
+
+ assert(clsHnd != NO_CLASS_HANDLE);
+
+ if (structSize == 0)
+ {
+ structSize = info.compCompHnd->getClassSize(clsHnd);
+ }
+ assert(structSize > 0);
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ // An 8-byte struct may need to be returned in a floating point register
+ // So we always consult the struct "Classifier" routine
+ //
+ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
+ eeGetSystemVAmd64PassStructInRegisterDescriptor(clsHnd, &structDesc);
+
+ // If we have one eightByteCount then we can set 'useType' based on that
+ if (structDesc.eightByteCount == 1)
+ {
+ // Set 'useType' to the type of the first eightbyte item
+ useType = GetEightByteType(structDesc, 0);
+ assert(structDesc.passedInRegisters == true);
+ }
+
+#else // not UNIX_AMD64
+
+ // The largest primitive type is 8 bytes (TYP_DOUBLE)
+ // so we can skip calling getPrimitiveTypeForStruct when we
+ // have a struct that is larger than that.
+ //
+ if (structSize <= sizeof(double))
+ {
+ // We set the "primitive" useType based upon the structSize
+ // and also examine the clsHnd to see if it is an HFA of count one
+ useType = getPrimitiveTypeForStruct(structSize, clsHnd);
+ }
+
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+#ifdef _TARGET_64BIT_
+ // Note this handles an odd case when FEATURE_MULTIREG_RET is disabled and HFAs are enabled
+ //
+ // getPrimitiveTypeForStruct will return TYP_UNKNOWN for a struct that is an HFA of two floats
+ // because when HFA are enabled, normally we would use two FP registers to pass or return it
+ //
+ // But if we don't have support for multiple register return types, we have to change this.
+ // Since we what we have an 8-byte struct (float + float) we change useType to TYP_I_IMPL
+ // so that the struct is returned instead using an 8-byte integer register.
+ //
+ if ((FEATURE_MULTIREG_RET == 0) && (useType == TYP_UNKNOWN) && (structSize == (2 * sizeof(float))) && IsHfa(clsHnd))
+ {
+ useType = TYP_I_IMPL;
+ }
+#endif
+
+ // Did we change this struct type into a simple "primitive" type?
+ //
+ if (useType != TYP_UNKNOWN)
+ {
+ // Yes, we should use the "primitive" type in 'useType'
+ howToReturnStruct = SPK_PrimitiveType;
+ }
+ else // We can't replace the struct with a "primitive" type
+ {
+ // See if we can return this struct by value, possibly in multiple registers
+ // or if we should return it using a return buffer register
+ //
+ if ((FEATURE_MULTIREG_RET == 1) && (structSize <= MAX_RET_MULTIREG_BYTES))
+ {
+ // Structs that are HFA's are returned in multiple registers
+ if (IsHfa(clsHnd))
+ {
+ // HFA's of count one should have been handled by getPrimitiveTypeForStruct
+ assert(GetHfaCount(clsHnd) >= 2);
+
+ // setup wbPassType and useType indicate that this is returned by value as an HFA
+ // using multiple registers
+ howToReturnStruct = SPK_ByValueAsHfa;
+ useType = TYP_STRUCT;
+ }
+ else // Not an HFA struct type
+ {
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ // The case of (structDesc.eightByteCount == 1) should have already been handled
+ if (structDesc.eightByteCount > 1)
+ {
+ // setup wbPassType and useType indicate that this is returned by value in multiple registers
+ howToReturnStruct = SPK_ByValue;
+ useType = TYP_STRUCT;
+ assert(structDesc.passedInRegisters == true);
+ }
+ else
+ {
+ assert(structDesc.eightByteCount == 0);
+ // Otherwise we return this struct using a return buffer
+ // setup wbPassType and useType indicate that this is return using a return buffer register
+ // (reference to a return buffer)
+ howToReturnStruct = SPK_ByReference;
+ useType = TYP_UNKNOWN;
+ assert(structDesc.passedInRegisters == false);
+ }
+
+#elif defined(_TARGET_ARM64_)
+
+ // Structs that are pointer sized or smaller should have been handled by getPrimitiveTypeForStruct
+ assert(structSize > TARGET_POINTER_SIZE);
+
+ // On ARM64 structs that are 9-16 bytes are returned by value in multiple registers
+ //
+ if (structSize <= (TARGET_POINTER_SIZE * 2))
+ {
+ // setup wbPassType and useType indicate that this is return by value in multiple registers
+ howToReturnStruct = SPK_ByValue;
+ useType = TYP_STRUCT;
+ }
+ else // a structSize that is 17-32 bytes in size
+ {
+ // Otherwise we return this struct using a return buffer
+ // setup wbPassType and useType indicate that this is returned using a return buffer register
+ // (reference to a return buffer)
+ howToReturnStruct = SPK_ByReference;
+ useType = TYP_UNKNOWN;
+ }
+
+#elif defined(_TARGET_ARM_) || defined(_TARGET_X86_)
+
+ // Otherwise we return this struct using a return buffer
+ // setup wbPassType and useType indicate that this is returned using a return buffer register
+ // (reference to a return buffer)
+ howToReturnStruct = SPK_ByReference;
+ useType = TYP_UNKNOWN;
+
+#else // _TARGET_XXX_
+
+ noway_assert(!"Unhandled TARGET in getReturnTypeForStruct (with FEATURE_MULTIREG_ARGS=1)");
+
+#endif // _TARGET_XXX_
+ }
+ }
+ else // (structSize > MAX_RET_MULTIREG_BYTES) || (FEATURE_MULTIREG_RET == 0)
+ {
+ // We have a (large) struct that can't be replaced with a "primitive" type
+ // and can't be returned in multiple registers
+
+ // We return this struct using a return buffer register
+ // setup wbPassType and useType indicate that this is returned using a return buffer register
+ // (reference to a return buffer)
+ howToReturnStruct = SPK_ByReference;
+ useType = TYP_UNKNOWN;
+ }
+ }
+
+ // 'howToReturnStruct' must be set to one of the valid values before we return
+ assert(howToReturnStruct != SPK_Unknown);
+ if (wbReturnStruct != nullptr)
+ {
+ *wbReturnStruct = howToReturnStruct;
+ }
+ return useType;
+}
+
+/*****************************************************************************
+ * variables to keep track of how many iterations we go in a dataflow pass
+ */
+
+#if DATAFLOW_ITER
+
+unsigned CSEiterCount; // counts the # of iteration for the CSE dataflow
+unsigned CFiterCount; // counts the # of iteration for the Const Folding dataflow
+
+#endif // DATAFLOW_ITER
+
+#if MEASURE_BLOCK_SIZE
+size_t genFlowNodeSize;
+size_t genFlowNodeCnt;
+#endif // MEASURE_BLOCK_SIZE
+
+/*****************************************************************************/
+// We keep track of methods we've already compiled.
+
+/*****************************************************************************
+ * Declare the statics
+ */
+
+#ifdef DEBUG
+/* static */
+unsigned Compiler::s_compMethodsCount = 0; // to produce unique label names
+
+/* static */
+bool Compiler::s_dspMemStats = false;
+#endif
+
+#ifndef DEBUGGING_SUPPORT
+/* static */
+const bool Compiler::Options::compDbgCode = false;
+#endif
+
+#ifndef PROFILING_SUPPORTED
+const bool Compiler::Options::compNoPInvokeInlineCB = false;
+#endif
+
+/*****************************************************************************
+ *
+ * One time initialization code
+ */
+
+/* static */
+void Compiler::compStartup()
+{
+#if DISPLAY_SIZES
+ grossVMsize = grossNCsize = totalNCsize = 0;
+#endif // DISPLAY_SIZES
+
+ // Initialize the JIT's allocator.
+ ArenaAllocator::startup();
+
+ /* Initialize the table of tree node sizes */
+
+ GenTree::InitNodeSize();
+
+#ifdef JIT32_GCENCODER
+ // Initialize the GC encoder lookup table
+
+ GCInfo::gcInitEncoderLookupTable();
+#endif
+
+ /* Initialize the emitter */
+
+ emitter::emitInit();
+
+ // Static vars of ValueNumStore
+ ValueNumStore::InitValueNumStoreStatics();
+
+ compDisplayStaticSizes(jitstdout);
+}
+
+/*****************************************************************************
+ *
+ * One time finalization code
+ */
+
+/* static */
+void Compiler::compShutdown()
+{
+#ifdef ALT_JIT
+ if (s_pAltJitExcludeAssembliesList != nullptr)
+ {
+ s_pAltJitExcludeAssembliesList->~AssemblyNamesList2(); // call the destructor
+ s_pAltJitExcludeAssembliesList = nullptr;
+ }
+#endif // ALT_JIT
+
+ ArenaAllocator::shutdown();
+
+ /* Shut down the emitter */
+
+ emitter::emitDone();
+
+#if defined(DEBUG) || defined(INLINE_DATA)
+ // Finish reading and/or writing inline xml
+ InlineStrategy::FinalizeXml();
+#endif // defined(DEBUG) || defined(INLINE_DATA)
+
+#if defined(DEBUG) || MEASURE_NODE_SIZE || MEASURE_BLOCK_SIZE || DISPLAY_SIZES || CALL_ARG_STATS
+ if (genMethodCnt == 0)
+ {
+ return;
+ }
+#endif
+
+ // Where should we write our statistics output?
+ FILE* fout = jitstdout;
+
+#ifdef FEATURE_JIT_METHOD_PERF
+ if (compJitTimeLogFilename != NULL)
+ {
+ // I assume that this will return NULL if it fails for some reason, and
+ // that...
+ FILE* jitTimeLogFile = _wfopen(compJitTimeLogFilename, W("a"));
+ // ...Print will return silently with a NULL argument.
+ CompTimeSummaryInfo::s_compTimeSummary.Print(jitTimeLogFile);
+ fclose(jitTimeLogFile);
+ }
+#endif // FEATURE_JIT_METHOD_PERF
+
+#if FUNC_INFO_LOGGING
+ if (compJitFuncInfoFile != nullptr)
+ {
+ fclose(compJitFuncInfoFile);
+ compJitFuncInfoFile = nullptr;
+ }
+#endif // FUNC_INFO_LOGGING
+
+#if COUNT_RANGECHECKS
+ if (optRangeChkAll > 0)
+ {
+ fprintf(fout, "Removed %u of %u range checks\n", optRangeChkRmv, optRangeChkAll);
+ }
+#endif // COUNT_RANGECHECKS
+
+#if DISPLAY_SIZES
+
+ if (grossVMsize && grossNCsize)
+ {
+ fprintf(fout, "\n");
+ fprintf(fout, "--------------------------------------\n");
+ fprintf(fout, "Function and GC info size stats\n");
+ fprintf(fout, "--------------------------------------\n");
+
+ fprintf(fout, "[%7u VM, %8u %6s %4u%%] %s\n", grossVMsize, grossNCsize, Target::g_tgtCPUName,
+ 100 * grossNCsize / grossVMsize, "Total (excluding GC info)");
+
+ fprintf(fout, "[%7u VM, %8u %6s %4u%%] %s\n", grossVMsize, totalNCsize, Target::g_tgtCPUName,
+ 100 * totalNCsize / grossVMsize, "Total (including GC info)");
+
+ if (gcHeaderISize || gcHeaderNSize)
+ {
+ fprintf(fout, "\n");
+
+ fprintf(fout, "GC tables : [%7uI,%7uN] %7u byt (%u%% of IL, %u%% of %s).\n",
+ gcHeaderISize + gcPtrMapISize, gcHeaderNSize + gcPtrMapNSize, totalNCsize - grossNCsize,
+ 100 * (totalNCsize - grossNCsize) / grossVMsize, 100 * (totalNCsize - grossNCsize) / grossNCsize,
+ Target::g_tgtCPUName);
+
+ fprintf(fout, "GC headers : [%7uI,%7uN] %7u byt, [%4.1fI,%4.1fN] %4.1f byt/meth\n", gcHeaderISize,
+ gcHeaderNSize, gcHeaderISize + gcHeaderNSize, (float)gcHeaderISize / (genMethodICnt + 0.001),
+ (float)gcHeaderNSize / (genMethodNCnt + 0.001),
+ (float)(gcHeaderISize + gcHeaderNSize) / genMethodCnt);
+
+ fprintf(fout, "GC ptr maps : [%7uI,%7uN] %7u byt, [%4.1fI,%4.1fN] %4.1f byt/meth\n", gcPtrMapISize,
+ gcPtrMapNSize, gcPtrMapISize + gcPtrMapNSize, (float)gcPtrMapISize / (genMethodICnt + 0.001),
+ (float)gcPtrMapNSize / (genMethodNCnt + 0.001),
+ (float)(gcPtrMapISize + gcPtrMapNSize) / genMethodCnt);
+ }
+ else
+ {
+ fprintf(fout, "\n");
+
+ fprintf(fout, "GC tables take up %u bytes (%u%% of instr, %u%% of %6s code).\n",
+ totalNCsize - grossNCsize, 100 * (totalNCsize - grossNCsize) / grossVMsize,
+ 100 * (totalNCsize - grossNCsize) / grossNCsize, Target::g_tgtCPUName);
+ }
+
+#ifdef DEBUG
+#if DOUBLE_ALIGN
+ fprintf(fout, "%u out of %u methods generated with double-aligned stack\n",
+ Compiler::s_lvaDoubleAlignedProcsCount, genMethodCnt);
+#endif
+#endif
+ }
+
+#endif // DISPLAY_SIZES
+
+#if CALL_ARG_STATS
+ compDispCallArgStats(fout);
+#endif
+
+#if COUNT_BASIC_BLOCKS
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "Basic block count frequency table:\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ bbCntTable.dump(fout);
+ fprintf(fout, "--------------------------------------------------\n");
+
+ fprintf(fout, "\n");
+
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "IL method size frequency table for methods with a single basic block:\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ bbOneBBSizeTable.dump(fout);
+ fprintf(fout, "--------------------------------------------------\n");
+#endif // COUNT_BASIC_BLOCKS
+
+#if COUNT_LOOPS
+
+ fprintf(fout, "\n");
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, "Loop stats\n");
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, "Total number of methods with loops is %5u\n", totalLoopMethods);
+ fprintf(fout, "Total number of loops is %5u\n", totalLoopCount);
+ fprintf(fout, "Maximum number of loops per method is %5u\n", maxLoopsPerMethod);
+ fprintf(fout, "# of methods overflowing nat loop table is %5u\n", totalLoopOverflows);
+ fprintf(fout, "Total number of 'unnatural' loops is %5u\n", totalUnnatLoopCount);
+ fprintf(fout, "# of methods overflowing unnat loop limit is %5u\n", totalUnnatLoopOverflows);
+ fprintf(fout, "Total number of loops with an iterator is %5u\n", iterLoopCount);
+ fprintf(fout, "Total number of loops with a simple iterator is %5u\n", simpleTestLoopCount);
+ fprintf(fout, "Total number of loops with a constant iterator is %5u\n", constIterLoopCount);
+
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "Loop count frequency table:\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ loopCountTable.dump(fout);
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "Loop exit count frequency table:\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ loopExitCountTable.dump(fout);
+ fprintf(fout, "--------------------------------------------------\n");
+
+#endif // COUNT_LOOPS
+
+#if DATAFLOW_ITER
+
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, "Total number of iterations in the CSE dataflow loop is %5u\n", CSEiterCount);
+ fprintf(fout, "Total number of iterations in the CF dataflow loop is %5u\n", CFiterCount);
+
+#endif // DATAFLOW_ITER
+
+#if MEASURE_NODE_SIZE
+
+ fprintf(fout, "\n");
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, "GenTree node allocation stats\n");
+ fprintf(fout, "---------------------------------------------------\n");
+
+ fprintf(fout, "Allocated %6u tree nodes (%7u bytes total, avg %4u bytes per method)\n",
+ genNodeSizeStats.genTreeNodeCnt, genNodeSizeStats.genTreeNodeSize,
+ genNodeSizeStats.genTreeNodeSize / genMethodCnt);
+
+ fprintf(fout, "Allocated %7u bytes of unused tree node space (%3.2f%%)\n",
+ genNodeSizeStats.genTreeNodeSize - genNodeSizeStats.genTreeNodeActualSize,
+ (float)(100 * (genNodeSizeStats.genTreeNodeSize - genNodeSizeStats.genTreeNodeActualSize)) /
+ genNodeSizeStats.genTreeNodeSize);
+
+ fprintf(fout, "\n");
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, "Distribution of per-method GenTree node counts:\n");
+ genTreeNcntHist.dump(fout);
+
+ fprintf(fout, "\n");
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, "Distribution of per-method GenTree node allocations (in bytes):\n");
+ genTreeNsizHist.dump(fout);
+
+#endif // MEASURE_NODE_SIZE
+
+#if MEASURE_BLOCK_SIZE
+
+ fprintf(fout, "\n");
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, "BasicBlock and flowList/BasicBlockList allocation stats\n");
+ fprintf(fout, "---------------------------------------------------\n");
+
+ fprintf(fout, "Allocated %6u basic blocks (%7u bytes total, avg %4u bytes per method)\n", BasicBlock::s_Count,
+ BasicBlock::s_Size, BasicBlock::s_Size / genMethodCnt);
+ fprintf(fout, "Allocated %6u flow nodes (%7u bytes total, avg %4u bytes per method)\n", genFlowNodeCnt,
+ genFlowNodeSize, genFlowNodeSize / genMethodCnt);
+
+#endif // MEASURE_BLOCK_SIZE
+
+#if MEASURE_MEM_ALLOC
+
+#ifdef DEBUG
+ // Under debug, we only dump memory stats when the COMPlus_* variable is defined.
+ // Under non-debug, we don't have the COMPlus_* variable, and we always dump it.
+ if (s_dspMemStats)
+#endif
+ {
+ fprintf(fout, "\nAll allocations:\n");
+ s_aggMemStats.Print(jitstdout);
+
+ fprintf(fout, "\nLargest method:\n");
+ s_maxCompMemStats.Print(jitstdout);
+ }
+
+#endif // MEASURE_MEM_ALLOC
+
+#if LOOP_HOIST_STATS
+#ifdef DEBUG // Always display loop stats in retail
+ if (JitConfig.DisplayLoopHoistStats() != 0)
+#endif // DEBUG
+ {
+ PrintAggregateLoopHoistStats(jitstdout);
+ }
+#endif // LOOP_HOIST_STATS
+
+#if MEASURE_PTRTAB_SIZE
+
+ fprintf(fout, "\n");
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, "GC pointer table stats\n");
+ fprintf(fout, "---------------------------------------------------\n");
+
+ fprintf(fout, "Reg pointer descriptor size (internal): %8u (avg %4u per method)\n", GCInfo::s_gcRegPtrDscSize,
+ GCInfo::s_gcRegPtrDscSize / genMethodCnt);
+
+ fprintf(fout, "Total pointer table size: %8u (avg %4u per method)\n", GCInfo::s_gcTotalPtrTabSize,
+ GCInfo::s_gcTotalPtrTabSize / genMethodCnt);
+
+#endif // MEASURE_PTRTAB_SIZE
+
+#if MEASURE_NODE_SIZE || MEASURE_BLOCK_SIZE || MEASURE_PTRTAB_SIZE || DISPLAY_SIZES
+
+ if (genMethodCnt != 0)
+ {
+ fprintf(fout, "\n");
+ fprintf(fout, "A total of %6u methods compiled", genMethodCnt);
+#if DISPLAY_SIZES
+ if (genMethodICnt || genMethodNCnt)
+ {
+ fprintf(fout, " (%u interruptible, %u non-interruptible)", genMethodICnt, genMethodNCnt);
+ }
+#endif // DISPLAY_SIZES
+ fprintf(fout, ".\n");
+ }
+
+#endif // MEASURE_NODE_SIZE || MEASURE_BLOCK_SIZE || MEASURE_PTRTAB_SIZE || DISPLAY_SIZES
+
+#if EMITTER_STATS
+ emitterStats(fout);
+#endif
+
+#if MEASURE_FATAL
+ fprintf(fout, "\n");
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, "Fatal errors stats\n");
+ fprintf(fout, "---------------------------------------------------\n");
+ fprintf(fout, " badCode: %u\n", fatal_badCode);
+ fprintf(fout, " noWay: %u\n", fatal_noWay);
+ fprintf(fout, " NOMEM: %u\n", fatal_NOMEM);
+ fprintf(fout, " noWayAssertBody: %u\n", fatal_noWayAssertBody);
+#ifdef DEBUG
+ fprintf(fout, " noWayAssertBodyArgs: %u\n", fatal_noWayAssertBodyArgs);
+#endif // DEBUG
+ fprintf(fout, " NYI: %u\n", fatal_NYI);
+#endif // MEASURE_FATAL
+}
+
+/*****************************************************************************
+ * Display static data structure sizes.
+ */
+
+/* static */
+void Compiler::compDisplayStaticSizes(FILE* fout)
+{
+
+#if MEASURE_NODE_SIZE
+ /*
+ IMPORTANT: Use the following code to check the alignment of
+ GenTree members (in a retail build, of course).
+ */
+
+ GenTree* gtDummy = nullptr;
+
+ fprintf(fout, "\n");
+ fprintf(fout, "Offset / size of gtOper = %2u / %2u\n", offsetof(GenTree, gtOper), sizeof(gtDummy->gtOper));
+ fprintf(fout, "Offset / size of gtType = %2u / %2u\n", offsetof(GenTree, gtType), sizeof(gtDummy->gtType));
+#if FEATURE_ANYCSE
+ fprintf(fout, "Offset / size of gtCSEnum = %2u / %2u\n", offsetof(GenTree, gtCSEnum),
+ sizeof(gtDummy->gtCSEnum));
+#endif // FEATURE_ANYCSE
+#if ASSERTION_PROP
+ fprintf(fout, "Offset / size of gtAssertionNum = %2u / %2u\n", offsetof(GenTree, gtAssertionNum),
+ sizeof(gtDummy->gtAssertionNum));
+#endif // ASSERTION_PROP
+#if FEATURE_STACK_FP_X87
+ fprintf(fout, "Offset / size of gtFPlvl = %2u / %2u\n", offsetof(GenTree, gtFPlvl),
+ sizeof(gtDummy->gtFPlvl));
+#endif // FEATURE_STACK_FP_X87
+ // TODO: The section that report GenTree sizes should be made into a public static member function of the GenTree
+ // class (see https://github.com/dotnet/coreclr/pull/493)
+ // fprintf(fout, "Offset / size of gtCostEx = %2u / %2u\n", offsetof(GenTree, _gtCostEx ),
+ // sizeof(gtDummy->_gtCostEx ));
+ // fprintf(fout, "Offset / size of gtCostSz = %2u / %2u\n", offsetof(GenTree, _gtCostSz ),
+ // sizeof(gtDummy->_gtCostSz ));
+ fprintf(fout, "Offset / size of gtFlags = %2u / %2u\n", offsetof(GenTree, gtFlags),
+ sizeof(gtDummy->gtFlags));
+ fprintf(fout, "Offset / size of gtVNPair = %2u / %2u\n", offsetof(GenTree, gtVNPair),
+ sizeof(gtDummy->gtVNPair));
+ fprintf(fout, "Offset / size of gtRsvdRegs = %2u / %2u\n", offsetof(GenTree, gtRsvdRegs),
+ sizeof(gtDummy->gtRsvdRegs));
+#ifdef LEGACY_BACKEND
+ fprintf(fout, "Offset / size of gtUsedRegs = %2u / %2u\n", offsetof(GenTree, gtUsedRegs),
+ sizeof(gtDummy->gtUsedRegs));
+#endif // LEGACY_BACKEND
+#ifndef LEGACY_BACKEND
+ fprintf(fout, "Offset / size of gtLsraInfo = %2u / %2u\n", offsetof(GenTree, gtLsraInfo),
+ sizeof(gtDummy->gtLsraInfo));
+#endif // !LEGACY_BACKEND
+ fprintf(fout, "Offset / size of gtNext = %2u / %2u\n", offsetof(GenTree, gtNext), sizeof(gtDummy->gtNext));
+ fprintf(fout, "Offset / size of gtPrev = %2u / %2u\n", offsetof(GenTree, gtPrev), sizeof(gtDummy->gtPrev));
+ fprintf(fout, "\n");
+
+#if SMALL_TREE_NODES
+ fprintf(fout, "Small tree node size = %3u\n", TREE_NODE_SZ_SMALL);
+#endif // SMALL_TREE_NODES
+ fprintf(fout, "Large tree node size = %3u\n", TREE_NODE_SZ_LARGE);
+ fprintf(fout, "Size of GenTree = %3u\n", sizeof(GenTree));
+ fprintf(fout, "Size of GenTreeUnOp = %3u\n", sizeof(GenTreeUnOp));
+ fprintf(fout, "Size of GenTreeOp = %3u\n", sizeof(GenTreeOp));
+ fprintf(fout, "Size of GenTreeVal = %3u\n", sizeof(GenTreeVal));
+ fprintf(fout, "Size of GenTreeIntConCommon = %3u\n", sizeof(GenTreeIntConCommon));
+ fprintf(fout, "Size of GenTreePhysReg = %3u\n", sizeof(GenTreePhysReg));
+#ifndef LEGACY_BACKEND
+ fprintf(fout, "Size of GenTreeJumpTable = %3u\n", sizeof(GenTreeJumpTable));
+#endif // !LEGACY_BACKEND
+ fprintf(fout, "Size of GenTreeIntCon = %3u\n", sizeof(GenTreeIntCon));
+ fprintf(fout, "Size of GenTreeLngCon = %3u\n", sizeof(GenTreeLngCon));
+ fprintf(fout, "Size of GenTreeDblCon = %3u\n", sizeof(GenTreeDblCon));
+ fprintf(fout, "Size of GenTreeStrCon = %3u\n", sizeof(GenTreeStrCon));
+ fprintf(fout, "Size of GenTreeLclVarCommon = %3u\n", sizeof(GenTreeLclVarCommon));
+ fprintf(fout, "Size of GenTreeLclVar = %3u\n", sizeof(GenTreeLclVar));
+ fprintf(fout, "Size of GenTreeLclFld = %3u\n", sizeof(GenTreeLclFld));
+ fprintf(fout, "Size of GenTreeRegVar = %3u\n", sizeof(GenTreeRegVar));
+ fprintf(fout, "Size of GenTreeCast = %3u\n", sizeof(GenTreeCast));
+ fprintf(fout, "Size of GenTreeBox = %3u\n", sizeof(GenTreeBox));
+ fprintf(fout, "Size of GenTreeField = %3u\n", sizeof(GenTreeField));
+ fprintf(fout, "Size of GenTreeArgList = %3u\n", sizeof(GenTreeArgList));
+ fprintf(fout, "Size of GenTreeColon = %3u\n", sizeof(GenTreeColon));
+ fprintf(fout, "Size of GenTreeCall = %3u\n", sizeof(GenTreeCall));
+ fprintf(fout, "Size of GenTreeCmpXchg = %3u\n", sizeof(GenTreeCmpXchg));
+ fprintf(fout, "Size of GenTreeFptrVal = %3u\n", sizeof(GenTreeFptrVal));
+ fprintf(fout, "Size of GenTreeQmark = %3u\n", sizeof(GenTreeQmark));
+ fprintf(fout, "Size of GenTreeIntrinsic = %3u\n", sizeof(GenTreeIntrinsic));
+ fprintf(fout, "Size of GenTreeIndex = %3u\n", sizeof(GenTreeIndex));
+ fprintf(fout, "Size of GenTreeArrLen = %3u\n", sizeof(GenTreeArrLen));
+ fprintf(fout, "Size of GenTreeBoundsChk = %3u\n", sizeof(GenTreeBoundsChk));
+ fprintf(fout, "Size of GenTreeArrElem = %3u\n", sizeof(GenTreeArrElem));
+ fprintf(fout, "Size of GenTreeAddrMode = %3u\n", sizeof(GenTreeAddrMode));
+ fprintf(fout, "Size of GenTreeIndir = %3u\n", sizeof(GenTreeIndir));
+ fprintf(fout, "Size of GenTreeStoreInd = %3u\n", sizeof(GenTreeStoreInd));
+ fprintf(fout, "Size of GenTreeRetExpr = %3u\n", sizeof(GenTreeRetExpr));
+ fprintf(fout, "Size of GenTreeStmt = %3u\n", sizeof(GenTreeStmt));
+ fprintf(fout, "Size of GenTreeObj = %3u\n", sizeof(GenTreeObj));
+ fprintf(fout, "Size of GenTreeClsVar = %3u\n", sizeof(GenTreeClsVar));
+ fprintf(fout, "Size of GenTreeArgPlace = %3u\n", sizeof(GenTreeArgPlace));
+ fprintf(fout, "Size of GenTreeLabel = %3u\n", sizeof(GenTreeLabel));
+ fprintf(fout, "Size of GenTreePhiArg = %3u\n", sizeof(GenTreePhiArg));
+ fprintf(fout, "Size of GenTreePutArgStk = %3u\n", sizeof(GenTreePutArgStk));
+ fprintf(fout, "\n");
+#endif // MEASURE_NODE_SIZE
+
+#if MEASURE_BLOCK_SIZE
+
+ BasicBlock* bbDummy = nullptr;
+
+ fprintf(fout, "\n");
+ fprintf(fout, "Offset / size of bbNext = %3u / %3u\n", offsetof(BasicBlock, bbNext),
+ sizeof(bbDummy->bbNext));
+ fprintf(fout, "Offset / size of bbNum = %3u / %3u\n", offsetof(BasicBlock, bbNum),
+ sizeof(bbDummy->bbNum));
+ fprintf(fout, "Offset / size of bbPostOrderNum = %3u / %3u\n", offsetof(BasicBlock, bbPostOrderNum),
+ sizeof(bbDummy->bbPostOrderNum));
+ fprintf(fout, "Offset / size of bbRefs = %3u / %3u\n", offsetof(BasicBlock, bbRefs),
+ sizeof(bbDummy->bbRefs));
+ fprintf(fout, "Offset / size of bbFlags = %3u / %3u\n", offsetof(BasicBlock, bbFlags),
+ sizeof(bbDummy->bbFlags));
+ fprintf(fout, "Offset / size of bbWeight = %3u / %3u\n", offsetof(BasicBlock, bbWeight),
+ sizeof(bbDummy->bbWeight));
+ fprintf(fout, "Offset / size of bbJumpKind = %3u / %3u\n", offsetof(BasicBlock, bbJumpKind),
+ sizeof(bbDummy->bbJumpKind));
+ fprintf(fout, "Offset / size of bbJumpOffs = %3u / %3u\n", offsetof(BasicBlock, bbJumpOffs),
+ sizeof(bbDummy->bbJumpOffs));
+ fprintf(fout, "Offset / size of bbJumpDest = %3u / %3u\n", offsetof(BasicBlock, bbJumpDest),
+ sizeof(bbDummy->bbJumpDest));
+ fprintf(fout, "Offset / size of bbJumpSwt = %3u / %3u\n", offsetof(BasicBlock, bbJumpSwt),
+ sizeof(bbDummy->bbJumpSwt));
+ fprintf(fout, "Offset / size of bbTreeList = %3u / %3u\n", offsetof(BasicBlock, bbTreeList),
+ sizeof(bbDummy->bbTreeList));
+ fprintf(fout, "Offset / size of bbEntryState = %3u / %3u\n", offsetof(BasicBlock, bbEntryState),
+ sizeof(bbDummy->bbEntryState));
+ fprintf(fout, "Offset / size of bbStkTempsIn = %3u / %3u\n", offsetof(BasicBlock, bbStkTempsIn),
+ sizeof(bbDummy->bbStkTempsIn));
+ fprintf(fout, "Offset / size of bbStkTempsOut = %3u / %3u\n", offsetof(BasicBlock, bbStkTempsOut),
+ sizeof(bbDummy->bbStkTempsOut));
+ fprintf(fout, "Offset / size of bbTryIndex = %3u / %3u\n", offsetof(BasicBlock, bbTryIndex),
+ sizeof(bbDummy->bbTryIndex));
+ fprintf(fout, "Offset / size of bbHndIndex = %3u / %3u\n", offsetof(BasicBlock, bbHndIndex),
+ sizeof(bbDummy->bbHndIndex));
+ fprintf(fout, "Offset / size of bbCatchTyp = %3u / %3u\n", offsetof(BasicBlock, bbCatchTyp),
+ sizeof(bbDummy->bbCatchTyp));
+ fprintf(fout, "Offset / size of bbStkDepth = %3u / %3u\n", offsetof(BasicBlock, bbStkDepth),
+ sizeof(bbDummy->bbStkDepth));
+ fprintf(fout, "Offset / size of bbFPinVars = %3u / %3u\n", offsetof(BasicBlock, bbFPinVars),
+ sizeof(bbDummy->bbFPinVars));
+ fprintf(fout, "Offset / size of bbPreds = %3u / %3u\n", offsetof(BasicBlock, bbPreds),
+ sizeof(bbDummy->bbPreds));
+ fprintf(fout, "Offset / size of bbReach = %3u / %3u\n", offsetof(BasicBlock, bbReach),
+ sizeof(bbDummy->bbReach));
+ fprintf(fout, "Offset / size of bbIDom = %3u / %3u\n", offsetof(BasicBlock, bbIDom),
+ sizeof(bbDummy->bbIDom));
+ fprintf(fout, "Offset / size of bbDfsNum = %3u / %3u\n", offsetof(BasicBlock, bbDfsNum),
+ sizeof(bbDummy->bbDfsNum));
+ fprintf(fout, "Offset / size of bbCodeOffs = %3u / %3u\n", offsetof(BasicBlock, bbCodeOffs),
+ sizeof(bbDummy->bbCodeOffs));
+ fprintf(fout, "Offset / size of bbCodeOffsEnd = %3u / %3u\n", offsetof(BasicBlock, bbCodeOffsEnd),
+ sizeof(bbDummy->bbCodeOffsEnd));
+ fprintf(fout, "Offset / size of bbVarUse = %3u / %3u\n", offsetof(BasicBlock, bbVarUse),
+ sizeof(bbDummy->bbVarUse));
+ fprintf(fout, "Offset / size of bbVarDef = %3u / %3u\n", offsetof(BasicBlock, bbVarDef),
+ sizeof(bbDummy->bbVarDef));
+ fprintf(fout, "Offset / size of bbVarTmp = %3u / %3u\n", offsetof(BasicBlock, bbVarTmp),
+ sizeof(bbDummy->bbVarTmp));
+ fprintf(fout, "Offset / size of bbLiveIn = %3u / %3u\n", offsetof(BasicBlock, bbLiveIn),
+ sizeof(bbDummy->bbLiveIn));
+ fprintf(fout, "Offset / size of bbLiveOut = %3u / %3u\n", offsetof(BasicBlock, bbLiveOut),
+ sizeof(bbDummy->bbLiveOut));
+ fprintf(fout, "Offset / size of bbHeapSsaPhiFunc = %3u / %3u\n", offsetof(BasicBlock, bbHeapSsaPhiFunc),
+ sizeof(bbDummy->bbHeapSsaPhiFunc));
+ fprintf(fout, "Offset / size of bbHeapSsaNumIn = %3u / %3u\n", offsetof(BasicBlock, bbHeapSsaNumIn),
+ sizeof(bbDummy->bbHeapSsaNumIn));
+ fprintf(fout, "Offset / size of bbHeapSsaNumOut = %3u / %3u\n", offsetof(BasicBlock, bbHeapSsaNumOut),
+ sizeof(bbDummy->bbHeapSsaNumOut));
+
+#ifdef DEBUGGING_SUPPORT
+ fprintf(fout, "Offset / size of bbScope = %3u / %3u\n", offsetof(BasicBlock, bbScope),
+ sizeof(bbDummy->bbScope));
+#endif // DEBUGGING_SUPPORT
+
+ fprintf(fout, "Offset / size of bbCseGen = %3u / %3u\n", offsetof(BasicBlock, bbCseGen),
+ sizeof(bbDummy->bbCseGen));
+ fprintf(fout, "Offset / size of bbCseIn = %3u / %3u\n", offsetof(BasicBlock, bbCseIn),
+ sizeof(bbDummy->bbCseIn));
+ fprintf(fout, "Offset / size of bbCseOut = %3u / %3u\n", offsetof(BasicBlock, bbCseOut),
+ sizeof(bbDummy->bbCseOut));
+
+ fprintf(fout, "Offset / size of bbEmitCookie = %3u / %3u\n", offsetof(BasicBlock, bbEmitCookie),
+ sizeof(bbDummy->bbEmitCookie));
+
+#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+ fprintf(fout, "Offset / size of bbUnwindNopEmitCookie = %3u / %3u\n", offsetof(BasicBlock, bbUnwindNopEmitCookie),
+ sizeof(bbDummy->bbUnwindNopEmitCookie));
+#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+
+#ifdef VERIFIER
+ fprintf(fout, "Offset / size of bbStackIn = %3u / %3u\n", offsetof(BasicBlock, bbStackIn),
+ sizeof(bbDummy->bbStackIn));
+ fprintf(fout, "Offset / size of bbStackOut = %3u / %3u\n", offsetof(BasicBlock, bbStackOut),
+ sizeof(bbDummy->bbStackOut));
+ fprintf(fout, "Offset / size of bbTypesIn = %3u / %3u\n", offsetof(BasicBlock, bbTypesIn),
+ sizeof(bbDummy->bbTypesIn));
+ fprintf(fout, "Offset / size of bbTypesOut = %3u / %3u\n", offsetof(BasicBlock, bbTypesOut),
+ sizeof(bbDummy->bbTypesOut));
+#endif // VERIFIER
+
+#if FEATURE_STACK_FP_X87
+ fprintf(fout, "Offset / size of bbFPStateX87 = %3u / %3u\n", offsetof(BasicBlock, bbFPStateX87),
+ sizeof(bbDummy->bbFPStateX87));
+#endif // FEATURE_STACK_FP_X87
+
+#ifdef DEBUG
+ fprintf(fout, "Offset / size of bbLoopNum = %3u / %3u\n", offsetof(BasicBlock, bbLoopNum),
+ sizeof(bbDummy->bbLoopNum));
+#endif // DEBUG
+
+ fprintf(fout, "\n");
+ fprintf(fout, "Size of BasicBlock = %3u\n", sizeof(BasicBlock));
+
+#endif // MEASURE_BLOCK_SIZE
+
+#if EMITTER_STATS
+ emitterStaticStats(fout);
+#endif
+}
+
+/*****************************************************************************
+ *
+ * Constructor
+ */
+
+void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo)
+{
+ assert(pAlloc);
+ compAllocator = pAlloc;
+
+ // Inlinee Compile object will only be allocated when needed for the 1st time.
+ InlineeCompiler = nullptr;
+
+ // Set the inline info.
+ impInlineInfo = inlineInfo;
+
+ eeInfoInitialized = false;
+
+ compDoAggressiveInlining = false;
+
+ if (compIsForInlining())
+ {
+ m_inlineStrategy = nullptr;
+ compInlineResult = inlineInfo->inlineResult;
+ compAsIAllocator = nullptr; // We shouldn't be using the compAsIAllocator for other than the root compiler.
+#if MEASURE_MEM_ALLOC
+ compAsIAllocatorBitset = nullptr;
+ compAsIAllocatorGC = nullptr;
+ compAsIAllocatorLoopHoist = nullptr;
+#ifdef DEBUG
+ compAsIAllocatorDebugOnly = nullptr;
+#endif // DEBUG
+#endif // MEASURE_MEM_ALLOC
+
+ compQMarks = nullptr;
+ }
+ else
+ {
+ m_inlineStrategy = new (this, CMK_Inlining) InlineStrategy(this);
+ compInlineResult = nullptr;
+ compAsIAllocator = new (this, CMK_Unknown) CompAllocator(this, CMK_AsIAllocator);
+#if MEASURE_MEM_ALLOC
+ compAsIAllocatorBitset = new (this, CMK_Unknown) CompAllocator(this, CMK_bitset);
+ compAsIAllocatorGC = new (this, CMK_Unknown) CompAllocator(this, CMK_GC);
+ compAsIAllocatorLoopHoist = new (this, CMK_Unknown) CompAllocator(this, CMK_LoopHoist);
+#ifdef DEBUG
+ compAsIAllocatorDebugOnly = new (this, CMK_Unknown) CompAllocator(this, CMK_DebugOnly);
+#endif // DEBUG
+#endif // MEASURE_MEM_ALLOC
+
+ compQMarks = new (this, CMK_Unknown) ExpandArrayStack<GenTreePtr>(getAllocator());
+ }
+
+#ifdef FEATURE_TRACELOGGING
+ // Make sure JIT telemetry is initialized as soon as allocations can be made
+ // but no later than a point where noway_asserts can be thrown.
+ // 1. JIT telemetry could allocate some objects internally.
+ // 2. NowayAsserts are tracked through telemetry.
+ // Note: JIT telemetry could gather data when compiler is not fully initialized.
+ // So you have to initialize the compiler variables you use for telemetry.
+ assert((unsigned)PHASE_PRE_IMPORT == 0);
+ previousCompletedPhase = PHASE_PRE_IMPORT;
+ info.compILCodeSize = 0;
+ info.compMethodHnd = nullptr;
+ compJitTelemetry.Initialize(this);
+#endif
+
+#ifdef DEBUG
+ bRangeAllowStress = false;
+#endif
+
+ fgInit();
+ lvaInit();
+
+ if (!compIsForInlining())
+ {
+ codeGen = getCodeGenerator(this);
+#ifdef LEGACY_BACKEND
+ raInit();
+#endif // LEGACY_BACKEND
+ optInit();
+#ifndef LEGACY_BACKEND
+ hashBv::Init(this);
+#endif // !LEGACY_BACKEND
+
+ compVarScopeMap = nullptr;
+
+ // If this method were a real constructor for Compiler, these would
+ // become method initializations.
+ impPendingBlockMembers = ExpandArray<BYTE>(getAllocator());
+ impSpillCliquePredMembers = ExpandArray<BYTE>(getAllocator());
+ impSpillCliqueSuccMembers = ExpandArray<BYTE>(getAllocator());
+
+ memset(&lvHeapPerSsaData, 0, sizeof(PerSsaArray));
+ lvHeapPerSsaData.Init(getAllocator());
+ lvHeapNumSsaNames = 0;
+
+ //
+ // Initialize all the per-method statistics gathering data structures.
+ //
+
+ optLoopsCloned = 0;
+
+#if MEASURE_MEM_ALLOC
+ genMemStats.Init();
+#endif // MEASURE_MEM_ALLOC
+#if LOOP_HOIST_STATS
+ m_loopsConsidered = 0;
+ m_curLoopHasHoistedExpression = false;
+ m_loopsWithHoistedExpressions = 0;
+ m_totalHoistedExpressions = 0;
+#endif // LOOP_HOIST_STATS
+#if MEASURE_NODE_SIZE
+ genNodeSizeStatsPerFunc.Init();
+#endif // MEASURE_NODE_SIZE
+ }
+ else
+ {
+ codeGen = nullptr;
+ }
+
+ compJmpOpUsed = false;
+ compLongUsed = false;
+ compTailCallUsed = false;
+ compLocallocUsed = false;
+ compQmarkRationalized = false;
+ compQmarkUsed = false;
+ compFloatingPointUsed = false;
+ compUnsafeCastUsed = false;
+#if CPU_USES_BLOCK_MOVE
+ compBlkOpUsed = false;
+#endif
+#if FEATURE_STACK_FP_X87
+ compMayHaveTransitionBlocks = false;
+#endif
+ compNeedsGSSecurityCookie = false;
+ compGSReorderStackLayout = false;
+#if STACK_PROBES
+ compStackProbePrologDone = false;
+#endif
+
+ compGeneratingProlog = false;
+ compGeneratingEpilog = false;
+
+#ifndef LEGACY_BACKEND
+ compLSRADone = false;
+#endif // !LEGACY_BACKEND
+ compRationalIRForm = false;
+
+#ifdef DEBUG
+ compCodeGenDone = false;
+ compRegSetCheckLevel = 0;
+ opts.compMinOptsIsUsed = false;
+#endif
+ opts.compMinOptsIsSet = false;
+
+ // Used by fgFindJumpTargets for inlining heuristics.
+ opts.instrCount = 0;
+
+ // Used to track when we should consider running EarlyProp
+ optMethodFlags = 0;
+
+ for (unsigned i = 0; i < MAX_LOOP_NUM; i++)
+ {
+ AllVarSetOps::AssignNoCopy(this, optLoopTable[i].lpAsgVars, AllVarSetOps::UninitVal());
+ }
+
+#ifdef DEBUG
+ m_nodeTestData = nullptr;
+ m_loopHoistCSEClass = FIRST_LOOP_HOIST_CSE_CLASS;
+#endif
+ m_switchDescMap = nullptr;
+ m_blockToEHPreds = nullptr;
+ m_fieldSeqStore = nullptr;
+ m_zeroOffsetFieldMap = nullptr;
+ m_arrayInfoMap = nullptr;
+ m_heapSsaMap = nullptr;
+ m_refAnyClass = nullptr;
+
+#ifdef DEBUG
+ if (!compIsForInlining())
+ {
+ compDoComponentUnitTestsOnce();
+ }
+#endif // DEBUG
+
+ vnStore = nullptr;
+ m_opAsgnVarDefSsaNums = nullptr;
+ m_indirAssignMap = nullptr;
+ fgSsaPassesCompleted = 0;
+ fgVNPassesCompleted = 0;
+
+ // check that HelperCallProperties are initialized
+
+ assert(s_helperCallProperties.IsPure(CORINFO_HELP_GETSHARED_GCSTATIC_BASE));
+ assert(!s_helperCallProperties.IsPure(CORINFO_HELP_GETFIELDOBJ)); // quick sanity check
+
+ // We start with the flow graph in tree-order
+ fgOrder = FGOrderTree;
+
+#ifdef FEATURE_SIMD
+ // SIMD Types
+ SIMDFloatHandle = nullptr;
+ SIMDDoubleHandle = nullptr;
+ SIMDIntHandle = nullptr;
+ SIMDUShortHandle = nullptr;
+ SIMDUByteHandle = nullptr;
+ SIMDShortHandle = nullptr;
+ SIMDByteHandle = nullptr;
+ SIMDLongHandle = nullptr;
+ SIMDUIntHandle = nullptr;
+ SIMDULongHandle = nullptr;
+ SIMDVector2Handle = nullptr;
+ SIMDVector3Handle = nullptr;
+ SIMDVector4Handle = nullptr;
+ SIMDVectorHandle = nullptr;
+#endif
+
+#ifdef DEBUG
+ inlRNG = nullptr;
+#endif
+
+ compUsesThrowHelper = false;
+}
+
+/*****************************************************************************
+ *
+ * Destructor
+ */
+
+void Compiler::compDone()
+{
+}
+
+void* Compiler::compGetHelperFtn(CorInfoHelpFunc ftnNum, /* IN */
+ void** ppIndirection) /* OUT */
+{
+ void* addr;
+
+ if (info.compMatchedVM)
+ {
+ addr = info.compCompHnd->getHelperFtn(ftnNum, ppIndirection);
+ }
+ else
+ {
+ // If we don't have a matched VM, we won't get valid results when asking for a helper function.
+ addr = (void*)0xCA11CA11; // "callcall"
+ }
+
+ return addr;
+}
+
+unsigned Compiler::compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd)
+{
+ var_types sigType = genActualType(JITtype2varType(cit));
+ unsigned sigSize;
+ sigSize = genTypeSize(sigType);
+ if (cit == CORINFO_TYPE_VALUECLASS)
+ {
+ sigSize = info.compCompHnd->getClassSize(clsHnd);
+ }
+ else if (cit == CORINFO_TYPE_REFANY)
+ {
+ sigSize = 2 * sizeof(void*);
+ }
+ return sigSize;
+}
+
+#ifdef DEBUG
+static bool DidComponentUnitTests = false;
+
+void Compiler::compDoComponentUnitTestsOnce()
+{
+ if (!JitConfig.RunComponentUnitTests())
+ {
+ return;
+ }
+
+ if (!DidComponentUnitTests)
+ {
+ DidComponentUnitTests = true;
+ ValueNumStore::RunTests(this);
+ BitSetSupport::TestSuite(getAllocatorDebugOnly());
+ }
+}
+#endif // DEBUG
+
+/******************************************************************************
+ *
+ * The Emitter uses this callback function to allocate its memory
+ */
+
+/* static */
+void* Compiler::compGetMemCallback(void* p, size_t size, CompMemKind cmk)
+{
+ assert(p);
+
+ return ((Compiler*)p)->compGetMem(size, cmk);
+}
+
+/*****************************************************************************
+ *
+ * The central memory allocation routine used by the compiler. Normally this
+ * is a simple inline method defined in compiler.hpp, but for debugging it's
+ * often convenient to keep it non-inline.
+ */
+
+#ifdef DEBUG
+
+void* Compiler::compGetMem(size_t sz, CompMemKind cmk)
+{
+#if 0
+#if SMALL_TREE_NODES
+ if (sz != TREE_NODE_SZ_SMALL &&
+ sz != TREE_NODE_SZ_LARGE && sz > 32)
+ {
+ printf("Alloc %3u bytes\n", sz);
+ }
+#else
+ if (sz != sizeof(GenTree) && sz > 32)
+ {
+ printf("Alloc %3u bytes\n", sz);
+ }
+#endif
+#endif // 0
+
+#if MEASURE_MEM_ALLOC
+ genMemStats.AddAlloc(sz, cmk);
+#endif
+
+ void* ptr = compAllocator->allocateMemory(sz);
+
+ // Verify that the current block is aligned. Only then will the next
+ // block allocated be on an aligned boundary.
+ assert((size_t(ptr) & (sizeof(size_t) - 1)) == 0);
+
+ return ptr;
+}
+
+#endif
+
+/*****************************************************************************/
+#ifdef DEBUG
+/*****************************************************************************/
+
+VarName Compiler::compVarName(regNumber reg, bool isFloatReg)
+{
+ if (isFloatReg)
+ {
+#if FEATURE_STACK_FP_X87
+ assert(reg < FP_STK_SIZE); // would like to have same assert as below but sometimes you get -1?
+#else
+ assert(genIsValidFloatReg(reg));
+#endif
+ }
+ else
+ {
+ assert(genIsValidReg(reg));
+ }
+
+ if ((info.compVarScopesCount > 0) && compCurBB && opts.varNames)
+ {
+ unsigned lclNum;
+ LclVarDsc* varDsc;
+
+ /* Look for the matching register */
+ for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
+ {
+ /* If the variable is not in a register, or not in the register we're looking for, quit. */
+ /* Also, if it is a compiler generated variable (i.e. slot# > info.compVarScopesCount), don't bother. */
+ if ((varDsc->lvRegister != 0) && (varDsc->lvRegNum == reg) && (varDsc->IsFloatRegType() || !isFloatReg) &&
+ (varDsc->lvSlotNum < info.compVarScopesCount))
+ {
+ /* check if variable in that register is live */
+ if (VarSetOps::IsMember(this, compCurLife, varDsc->lvVarIndex))
+ {
+ /* variable is live - find the corresponding slot */
+ VarScopeDsc* varScope =
+ compFindLocalVar(varDsc->lvSlotNum, compCurBB->bbCodeOffs, compCurBB->bbCodeOffsEnd);
+ if (varScope)
+ {
+ return varScope->vsdName;
+ }
+ }
+ }
+ }
+
+#ifdef LEGACY_BACKEND
+ // maybe var is marked dead, but still used (last use)
+ if (!isFloatReg && codeGen->regSet.rsUsedTree[reg] != NULL)
+ {
+ GenTreePtr nodePtr;
+
+ if (GenTree::OperIsUnary(codeGen->regSet.rsUsedTree[reg]->OperGet()))
+ {
+ assert(codeGen->regSet.rsUsedTree[reg]->gtOp.gtOp1 != NULL);
+ nodePtr = codeGen->regSet.rsUsedTree[reg]->gtOp.gtOp1;
+ }
+ else
+ {
+ nodePtr = codeGen->regSet.rsUsedTree[reg];
+ }
+
+ if ((nodePtr->gtOper == GT_REG_VAR) && (nodePtr->gtRegVar.gtRegNum == reg) &&
+ (nodePtr->gtRegVar.gtLclNum < info.compVarScopesCount))
+ {
+ VarScopeDsc* varScope =
+ compFindLocalVar(nodePtr->gtRegVar.gtLclNum, compCurBB->bbCodeOffs, compCurBB->bbCodeOffsEnd);
+ if (varScope)
+ return varScope->vsdName;
+ }
+ }
+#endif // LEGACY_BACKEND
+ }
+ return nullptr;
+}
+
+const char* Compiler::compRegVarName(regNumber reg, bool displayVar, bool isFloatReg)
+{
+
+#ifdef _TARGET_ARM_
+ isFloatReg = genIsValidFloatReg(reg);
+#endif
+
+ if (displayVar && (reg != REG_NA))
+ {
+ VarName varName = compVarName(reg, isFloatReg);
+
+ if (varName)
+ {
+ const int NAME_VAR_REG_BUFFER_LEN = 4 + 256 + 1;
+ static char nameVarReg[2][NAME_VAR_REG_BUFFER_LEN]; // to avoid overwriting the buffer when have 2
+ // consecutive calls before printing
+ static int index = 0; // for circular index into the name array
+
+ index = (index + 1) % 2; // circular reuse of index
+ sprintf_s(nameVarReg[index], NAME_VAR_REG_BUFFER_LEN, "%s'%s'", getRegName(reg, isFloatReg),
+ VarNameToStr(varName));
+
+ return nameVarReg[index];
+ }
+ }
+
+ /* no debug info required or no variable in that register
+ -> return standard name */
+
+ return getRegName(reg, isFloatReg);
+}
+
+#define MAX_REG_PAIR_NAME_LENGTH 10
+
+const char* Compiler::compRegPairName(regPairNo regPair)
+{
+ static char regNameLong[MAX_REG_PAIR_NAME_LENGTH];
+
+ if (regPair == REG_PAIR_NONE)
+ {
+ return "NA|NA";
+ }
+
+ assert(regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST);
+
+ strcpy_s(regNameLong, sizeof(regNameLong), compRegVarName(genRegPairLo(regPair)));
+ strcat_s(regNameLong, sizeof(regNameLong), "|");
+ strcat_s(regNameLong, sizeof(regNameLong), compRegVarName(genRegPairHi(regPair)));
+ return regNameLong;
+}
+
+const char* Compiler::compRegNameForSize(regNumber reg, size_t size)
+{
+ if (size == 0 || size >= 4)
+ {
+ return compRegVarName(reg, true);
+ }
+
+ // clang-format off
+ static
+ const char * sizeNames[][2] =
+ {
+ { "al", "ax" },
+ { "cl", "cx" },
+ { "dl", "dx" },
+ { "bl", "bx" },
+#ifdef _TARGET_AMD64_
+ { "spl", "sp" }, // ESP
+ { "bpl", "bp" }, // EBP
+ { "sil", "si" }, // ESI
+ { "dil", "di" }, // EDI
+ { "r8b", "r8w" },
+ { "r9b", "r9w" },
+ { "r10b", "r10w" },
+ { "r11b", "r11w" },
+ { "r12b", "r12w" },
+ { "r13b", "r13w" },
+ { "r14b", "r14w" },
+ { "r15b", "r15w" },
+#endif // _TARGET_AMD64_
+ };
+ // clang-format on
+
+ assert(isByteReg(reg));
+ assert(genRegMask(reg) & RBM_BYTE_REGS);
+ assert(size == 1 || size == 2);
+
+ return sizeNames[reg][size - 1];
+}
+
+const char* Compiler::compFPregVarName(unsigned fpReg, bool displayVar)
+{
+ const int NAME_VAR_REG_BUFFER_LEN = 4 + 256 + 1;
+ static char nameVarReg[2][NAME_VAR_REG_BUFFER_LEN]; // to avoid overwriting the buffer when have 2 consecutive calls
+ // before printing
+ static int index = 0; // for circular index into the name array
+
+ index = (index + 1) % 2; // circular reuse of index
+
+#if FEATURE_STACK_FP_X87
+ /* 'fpReg' is the distance from the bottom of the stack, ie.
+ * it is independant of the current FP stack level
+ */
+
+ if (displayVar && codeGen->genFPregCnt)
+ {
+ assert(fpReg < FP_STK_SIZE);
+ assert(compCodeGenDone || (fpReg <= codeGen->compCurFPState.m_uStackSize));
+
+ int pos = codeGen->genFPregCnt - (fpReg + 1 - codeGen->genGetFPstkLevel());
+ if (pos >= 0)
+ {
+ VarName varName = compVarName((regNumber)pos, true);
+
+ if (varName)
+ {
+ sprintf_s(nameVarReg[index], NAME_VAR_REG_BUFFER_LEN, "ST(%d)'%s'", fpReg, VarNameToStr(varName));
+ return nameVarReg[index];
+ }
+ }
+ }
+#endif // FEATURE_STACK_FP_X87
+
+ /* no debug info required or no variable in that register
+ -> return standard name */
+
+ sprintf_s(nameVarReg[index], NAME_VAR_REG_BUFFER_LEN, "ST(%d)", fpReg);
+ return nameVarReg[index];
+}
+
+const char* Compiler::compLocalVarName(unsigned varNum, unsigned offs)
+{
+ unsigned i;
+ VarScopeDsc* t;
+
+ for (i = 0, t = info.compVarScopes; i < info.compVarScopesCount; i++, t++)
+ {
+ if (t->vsdVarNum != varNum)
+ {
+ continue;
+ }
+
+ if (offs >= t->vsdLifeBeg && offs < t->vsdLifeEnd)
+ {
+ return VarNameToStr(t->vsdName);
+ }
+ }
+
+ return nullptr;
+}
+
+/*****************************************************************************/
+#endif // DEBUG
+/*****************************************************************************/
+
+void Compiler::compSetProcessor()
+{
+ unsigned compileFlags = opts.eeFlags;
+
+#if defined(_TARGET_ARM_)
+ info.genCPU = CPU_ARM;
+#elif defined(_TARGET_AMD64_)
+ info.genCPU = CPU_X64;
+#elif defined(_TARGET_X86_)
+ if (compileFlags & CORJIT_FLG_TARGET_P4)
+ info.genCPU = CPU_X86_PENTIUM_4;
+ else
+ info.genCPU = CPU_X86;
+#endif
+
+ //
+ // Processor specific optimizations
+ //
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef _TARGET_AMD64_
+ opts.compUseFCOMI = false;
+ opts.compUseCMOV = true;
+ opts.compCanUseSSE2 = true;
+
+#ifdef FEATURE_AVX_SUPPORT
+ // COMPlus_EnableAVX can be used to disable using AVX if available on a target machine.
+ // Note that FEATURE_AVX_SUPPORT is not enabled for ctpjit
+ opts.compCanUseAVX = false;
+ if (((compileFlags & CORJIT_FLG_PREJIT) == 0) && ((compileFlags & CORJIT_FLG_USE_AVX2) != 0))
+ {
+ if (JitConfig.EnableAVX() != 0)
+ {
+ opts.compCanUseAVX = true;
+ if (!compIsForInlining())
+ {
+ codeGen->getEmitter()->SetUseAVX(true);
+ }
+ }
+ }
+#endif
+#endif //_TARGET_AMD64_
+
+#ifdef _TARGET_X86_
+ opts.compUseFCOMI = ((opts.eeFlags & CORJIT_FLG_USE_FCOMI) != 0);
+ opts.compUseCMOV = ((opts.eeFlags & CORJIT_FLG_USE_CMOV) != 0);
+ opts.compCanUseSSE2 = ((opts.eeFlags & CORJIT_FLG_USE_SSE2) != 0);
+
+#ifdef DEBUG
+ if (opts.compUseFCOMI)
+ opts.compUseFCOMI = !compStressCompile(STRESS_USE_FCOMI, 50);
+ if (opts.compUseCMOV)
+ opts.compUseCMOV = !compStressCompile(STRESS_USE_CMOV, 50);
+
+ // Should we override the SSE2 setting
+ enum
+ {
+ SSE2_FORCE_DISABLE = 0,
+ SSE2_FORCE_USE = 1,
+ SSE2_FORCE_INVALID = -1
+ };
+
+ if (JitConfig.JitCanUseSSE2() == SSE2_FORCE_DISABLE)
+ opts.compCanUseSSE2 = false;
+ else if (JitConfig.JitCanUseSSE2() == SSE2_FORCE_USE)
+ opts.compCanUseSSE2 = true;
+ else if (opts.compCanUseSSE2)
+ opts.compCanUseSSE2 = !compStressCompile(STRESS_GENERIC_VARN, 50);
+#endif // DEBUG
+#endif // _TARGET_X86_
+}
+
+#ifdef PROFILING_SUPPORTED
+// A Dummy routine to receive Enter/Leave/Tailcall profiler callbacks.
+// These are used when complus_JitEltHookEnabled=1
+#ifdef _TARGET_AMD64_
+void DummyProfilerELTStub(UINT_PTR ProfilerHandle, UINT_PTR callerSP)
+{
+ return;
+}
+#else //! _TARGET_AMD64_
+void DummyProfilerELTStub(UINT_PTR ProfilerHandle)
+{
+ return;
+}
+#endif //!_TARGET_AMD64_
+
+#endif // PROFILING_SUPPORTED
+
+bool Compiler::compIsFullTrust()
+{
+ return (info.compCompHnd->canSkipMethodVerification(info.compMethodHnd) == CORINFO_VERIFICATION_CAN_SKIP);
+}
+
+bool Compiler::compShouldThrowOnNoway(
+#ifdef FEATURE_TRACELOGGING
+ const char* filename, unsigned line
+#endif
+ )
+{
+#ifdef FEATURE_TRACELOGGING
+ compJitTelemetry.NotifyNowayAssert(filename, line);
+#endif
+ // In min opts, we don't want the noway assert to go through the exception
+ // path. Instead we want it to just silently go through codegen for
+ // compat reasons.
+ // If we are not in full trust, we should always fire for security.
+ return !opts.MinOpts() || !compIsFullTrust();
+}
+
+// ConfigInteger does not offer an option for decimal flags. Any numbers are interpreted as hex.
+// I could add the decimal option to ConfigInteger or I could write a function to reinterpret this
+// value as the user intended.
+unsigned ReinterpretHexAsDecimal(unsigned in)
+{
+ // ex: in: 0x100 returns: 100
+ unsigned result = 0;
+ unsigned index = 1;
+
+ // default value
+ if (in == INT_MAX)
+ {
+ return in;
+ }
+
+ while (in)
+ {
+ unsigned digit = in % 16;
+ in >>= 4;
+ assert(digit < 10);
+ result += digit * index;
+ index *= 10;
+ }
+ return result;
+}
+
+void Compiler::compInitOptions(CORJIT_FLAGS* jitFlags)
+{
+#ifdef UNIX_AMD64_ABI
+ opts.compNeedToAlignFrame = false;
+#endif // UNIX_AMD64_ABI
+ memset(&opts, 0, sizeof(opts));
+
+ unsigned compileFlags = jitFlags->corJitFlags;
+
+ if (compIsForInlining())
+ {
+ assert((compileFlags & CORJIT_FLG_LOST_WHEN_INLINING) == 0);
+ assert(compileFlags & CORJIT_FLG_SKIP_VERIFICATION);
+ }
+
+ opts.jitFlags = jitFlags;
+ opts.eeFlags = compileFlags;
+ opts.compFlags = CLFLG_MAXOPT; // Default value is for full optimization
+
+ if (opts.eeFlags & (CORJIT_FLG_DEBUG_CODE | CORJIT_FLG_MIN_OPT))
+ {
+ opts.compFlags = CLFLG_MINOPT;
+ }
+ // Don't optimize .cctors (except prejit) or if we're an inlinee
+ else if (!(opts.eeFlags & CORJIT_FLG_PREJIT) && ((info.compFlags & FLG_CCTOR) == FLG_CCTOR) && !compIsForInlining())
+ {
+ opts.compFlags = CLFLG_MINOPT;
+ }
+
+ // Default value is to generate a blend of size and speed optimizations
+ //
+ opts.compCodeOpt = BLENDED_CODE;
+
+ // If the EE sets SIZE_OPT or if we are compiling a Class constructor
+ // we will optimize for code size at the expense of speed
+ //
+ if ((opts.eeFlags & CORJIT_FLG_SIZE_OPT) || ((info.compFlags & FLG_CCTOR) == FLG_CCTOR))
+ {
+ opts.compCodeOpt = SMALL_CODE;
+ }
+ //
+ // If the EE sets SPEED_OPT we will optimize for speed at the expense of code size
+ //
+ else if (opts.eeFlags & CORJIT_FLG_SPEED_OPT)
+ {
+ opts.compCodeOpt = FAST_CODE;
+ assert((opts.eeFlags & CORJIT_FLG_SIZE_OPT) == 0);
+ }
+
+//-------------------------------------------------------------------------
+
+#ifdef DEBUGGING_SUPPORT
+ opts.compDbgCode = (opts.eeFlags & CORJIT_FLG_DEBUG_CODE) != 0;
+ opts.compDbgInfo = (opts.eeFlags & CORJIT_FLG_DEBUG_INFO) != 0;
+ opts.compDbgEnC = (opts.eeFlags & CORJIT_FLG_DEBUG_EnC) != 0;
+#if REGEN_SHORTCUTS || REGEN_CALLPAT
+ // We never want to have debugging enabled when regenerating GC encoding patterns
+ opts.compDbgCode = false;
+ opts.compDbgInfo = false;
+ opts.compDbgEnC = false;
+#endif
+#endif
+
+ compSetProcessor();
+
+#ifdef DEBUG
+ opts.dspOrder = false;
+ if (compIsForInlining())
+ {
+ verbose = impInlineInfo->InlinerCompiler->verbose;
+ }
+ else
+ {
+ verbose = false;
+ codeGen->setVerbose(false);
+ }
+ verboseTrees = verbose && shouldUseVerboseTrees();
+ verboseSsa = verbose && shouldUseVerboseSsa();
+ asciiTrees = shouldDumpASCIITrees();
+ opts.dspDiffable = compIsForInlining() ? impInlineInfo->InlinerCompiler->opts.dspDiffable : false;
+#endif
+
+ opts.compNeedSecurityCheck = false;
+ opts.altJit = false;
+
+#if defined(LATE_DISASM) && !defined(DEBUG)
+ // For non-debug builds with the late disassembler built in, we currently always do late disassembly
+ // (we have no way to determine when not to, since we don't have class/method names).
+ // In the DEBUG case, this is initialized to false, below.
+ opts.doLateDisasm = true;
+#endif
+
+#ifdef DEBUG
+
+ const JitConfigValues::MethodSet* pfAltJit;
+ if (opts.eeFlags & CORJIT_FLG_PREJIT)
+ {
+ pfAltJit = &JitConfig.AltJitNgen();
+ }
+ else
+ {
+ pfAltJit = &JitConfig.AltJit();
+ }
+
+#ifdef ALT_JIT
+ if (pfAltJit->contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ opts.altJit = true;
+ }
+
+ unsigned altJitLimit = ReinterpretHexAsDecimal(JitConfig.AltJitLimit());
+ if (altJitLimit > 0 && Compiler::jitTotalMethodCompiled >= altJitLimit)
+ {
+ opts.altJit = false;
+ }
+#endif // ALT_JIT
+
+#else // !DEBUG
+
+ const char* altJitVal;
+ if (opts.eeFlags & CORJIT_FLG_PREJIT)
+ {
+ altJitVal = JitConfig.AltJitNgen().list();
+ }
+ else
+ {
+ altJitVal = JitConfig.AltJit().list();
+ }
+
+#ifdef ALT_JIT
+ // In release mode, you either get all methods or no methods. You must use "*" as the parameter, or we ignore it.
+ // You don't get to give a regular expression of methods to match.
+ // (Partially, this is because we haven't computed and stored the method and class name except in debug, and it
+ // might be expensive to do so.)
+ if ((altJitVal != nullptr) && (strcmp(altJitVal, "*") == 0))
+ {
+ opts.altJit = true;
+ }
+#endif // ALT_JIT
+
+#endif // !DEBUG
+
+#ifdef ALT_JIT
+ // Take care of COMPlus_AltJitExcludeAssemblies.
+ if (opts.altJit)
+ {
+ // First, initialize the AltJitExcludeAssemblies list, but only do it once.
+ if (!s_pAltJitExcludeAssembliesListInitialized)
+ {
+ const wchar_t* wszAltJitExcludeAssemblyList = JitConfig.AltJitExcludeAssemblies();
+ if (wszAltJitExcludeAssemblyList != nullptr)
+ {
+ // NOTE: The Assembly name list is allocated in the process heap, not in the no-release heap, which is
+ // reclaimed
+ // for every compilation. This is ok because we only allocate once, due to the static.
+ s_pAltJitExcludeAssembliesList = new (HostAllocator::getHostAllocator())
+ AssemblyNamesList2(wszAltJitExcludeAssemblyList, HostAllocator::getHostAllocator());
+ }
+ s_pAltJitExcludeAssembliesListInitialized = true;
+ }
+
+ if (s_pAltJitExcludeAssembliesList != nullptr)
+ {
+ // We have an exclusion list. See if this method is in an assembly that is on the list.
+ // Note that we check this for every method, since we might inline across modules, and
+ // if the inlinee module is on the list, we don't want to use the altjit for it.
+ const char* methodAssemblyName = info.compCompHnd->getAssemblyName(
+ info.compCompHnd->getModuleAssembly(info.compCompHnd->getClassModule(info.compClassHnd)));
+ if (s_pAltJitExcludeAssembliesList->IsInList(methodAssemblyName))
+ {
+ opts.altJit = false;
+ }
+ }
+ }
+#endif // ALT_JIT
+
+#ifdef DEBUG
+
+ bool altJitConfig = !pfAltJit->isEmpty();
+
+ // If we have a non-empty AltJit config then we change all of these other
+ // config values to refer only to the AltJit. Otherwise, a lot of COMPlus_* variables
+ // would apply to both the altjit and the normal JIT, but we only care about
+ // debugging the altjit if the COMPlus_AltJit configuration is set.
+ //
+ if (compIsForImportOnly() && (!altJitConfig || opts.altJit))
+ {
+ if (JitConfig.JitImportBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ assert(!"JitImportBreak reached");
+ }
+ }
+
+ bool verboseDump = false;
+ bool dumpIR = false;
+ bool dumpIRTypes = false;
+ bool dumpIRLocals = false;
+ bool dumpIRRegs = false;
+ bool dumpIRSsa = false;
+ bool dumpIRValnums = false;
+ bool dumpIRCosts = false;
+ bool dumpIRFlags = false;
+ bool dumpIRKinds = false;
+ bool dumpIRNodes = false;
+ bool dumpIRNoLists = false;
+ bool dumpIRNoLeafs = false;
+ bool dumpIRNoStmts = false;
+ bool dumpIRTrees = false;
+ bool dumpIRLinear = false;
+ bool dumpIRDataflow = false;
+ bool dumpIRBlockHeaders = false;
+ bool dumpIRExit = false;
+ LPCWSTR dumpIRPhase = nullptr;
+ LPCWSTR dumpIRFormat = nullptr;
+
+ if (!altJitConfig || opts.altJit)
+ {
+ LPCWSTR dumpIRFormat = nullptr;
+
+ // We should only enable 'verboseDump' when we are actually compiling a matching method
+ // and not enable it when we are just considering inlining a matching method.
+ //
+ if (!compIsForInlining())
+ {
+ if (opts.eeFlags & CORJIT_FLG_PREJIT)
+ {
+ if (JitConfig.NgenDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ verboseDump = true;
+ }
+ unsigned ngenHashDumpVal = (unsigned)JitConfig.NgenHashDump();
+ if ((ngenHashDumpVal != (DWORD)-1) && (ngenHashDumpVal == info.compMethodHash()))
+ {
+ verboseDump = true;
+ }
+ if (JitConfig.NgenDumpIR().contains(info.compMethodName, info.compClassName,
+ &info.compMethodInfo->args))
+ {
+ dumpIR = true;
+ }
+ unsigned ngenHashDumpIRVal = (unsigned)JitConfig.NgenHashDumpIR();
+ if ((ngenHashDumpIRVal != (DWORD)-1) && (ngenHashDumpIRVal == info.compMethodHash()))
+ {
+ dumpIR = true;
+ }
+ dumpIRFormat = JitConfig.NgenDumpIRFormat();
+ dumpIRPhase = JitConfig.NgenDumpIRPhase();
+ }
+ else
+ {
+ if (JitConfig.JitDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ verboseDump = true;
+ }
+ unsigned jitHashDumpVal = (unsigned)JitConfig.JitHashDump();
+ if ((jitHashDumpVal != (DWORD)-1) && (jitHashDumpVal == info.compMethodHash()))
+ {
+ verboseDump = true;
+ }
+ if (JitConfig.JitDumpIR().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ dumpIR = true;
+ }
+ unsigned jitHashDumpIRVal = (unsigned)JitConfig.JitHashDumpIR();
+ if ((jitHashDumpIRVal != (DWORD)-1) && (jitHashDumpIRVal == info.compMethodHash()))
+ {
+ dumpIR = true;
+ }
+ dumpIRFormat = JitConfig.JitDumpIRFormat();
+ dumpIRPhase = JitConfig.JitDumpIRPhase();
+ }
+ }
+
+ if (dumpIRPhase == nullptr)
+ {
+ dumpIRPhase = W("*");
+ }
+
+ this->dumpIRPhase = dumpIRPhase;
+
+ if (dumpIRFormat != nullptr)
+ {
+ this->dumpIRFormat = dumpIRFormat;
+ }
+
+ dumpIRTrees = false;
+ dumpIRLinear = true;
+ if (dumpIRFormat != nullptr)
+ {
+ for (LPCWSTR p = dumpIRFormat; (*p != 0);)
+ {
+ for (; (*p != 0); p++)
+ {
+ if (*p != L' ')
+ {
+ break;
+ }
+ }
+
+ if (*p == 0)
+ {
+ break;
+ }
+
+ static bool dumpedHelp = false;
+
+ if ((*p == L'?') && (!dumpedHelp))
+ {
+ printf("*******************************************************************************\n");
+ printf("\n");
+ dFormatIR();
+ printf("\n");
+ printf("\n");
+ printf("Available specifiers (comma separated):\n");
+ printf("\n");
+ printf("? dump out value of COMPlus_JitDumpIRFormat and this list of values\n");
+ printf("\n");
+ printf("linear linear IR dump (default)\n");
+ printf("tree tree IR dump (traditional)\n");
+ printf("mixed intermingle tree dump with linear IR dump\n");
+ printf("\n");
+ printf("dataflow use data flow form of linear IR dump\n");
+ printf("structural use structural form of linear IR dump\n");
+ printf("all implies structural, include everything\n");
+ printf("\n");
+ printf("kinds include tree node kinds in dump, example: \"kinds=[LEAF][LOCAL]\"\n");
+ printf("flags include tree node flags in dump, example: \"flags=[CALL][GLOB_REF]\" \n");
+ printf("types includes tree node types in dump, example: \".int\"\n");
+ printf("locals include local numbers and tracking numbers in dump, example: \"(V3,T1)\"\n");
+ printf("regs include register assignments in dump, example: \"(rdx)\"\n");
+ printf("ssa include SSA numbers in dump, example: \"<d:3>\" or \"<u:3>\"\n");
+ printf("valnums include Value numbers in dump, example: \"<v:$c4>\" or \"<v:$c4,$c5>\"\n");
+ printf("\n");
+ printf("nolist exclude GT_LIST nodes from dump\n");
+ printf("noleafs exclude LEAF nodes from dump (fold into operations)\n");
+ printf("nostmts exclude GT_STMTS from dump (unless required by dependencies)\n");
+ printf("\n");
+ printf("blkhdrs include block headers\n");
+ printf("exit exit program after last phase dump (used with single method)\n");
+ printf("\n");
+ printf("*******************************************************************************\n");
+ dumpedHelp = true;
+ }
+
+ if (wcsncmp(p, W("types"), 5) == 0)
+ {
+ dumpIRTypes = true;
+ }
+
+ if (wcsncmp(p, W("locals"), 6) == 0)
+ {
+ dumpIRLocals = true;
+ }
+
+ if (wcsncmp(p, W("regs"), 4) == 0)
+ {
+ dumpIRRegs = true;
+ }
+
+ if (wcsncmp(p, W("ssa"), 3) == 0)
+ {
+ dumpIRSsa = true;
+ }
+
+ if (wcsncmp(p, W("valnums"), 7) == 0)
+ {
+ dumpIRValnums = true;
+ }
+
+ if (wcsncmp(p, W("costs"), 5) == 0)
+ {
+ dumpIRCosts = true;
+ }
+
+ if (wcsncmp(p, W("flags"), 5) == 0)
+ {
+ dumpIRFlags = true;
+ }
+
+ if (wcsncmp(p, W("kinds"), 5) == 0)
+ {
+ dumpIRKinds = true;
+ }
+
+ if (wcsncmp(p, W("nodes"), 5) == 0)
+ {
+ dumpIRNodes = true;
+ }
+
+ if (wcsncmp(p, W("exit"), 4) == 0)
+ {
+ dumpIRExit = true;
+ }
+
+ if (wcsncmp(p, W("nolists"), 7) == 0)
+ {
+ dumpIRNoLists = true;
+ }
+
+ if (wcsncmp(p, W("noleafs"), 7) == 0)
+ {
+ dumpIRNoLeafs = true;
+ }
+
+ if (wcsncmp(p, W("nostmts"), 7) == 0)
+ {
+ dumpIRNoStmts = true;
+ }
+
+ if (wcsncmp(p, W("trees"), 5) == 0)
+ {
+ dumpIRTrees = true;
+ dumpIRLinear = false;
+ }
+
+ if (wcsncmp(p, W("structural"), 10) == 0)
+ {
+ dumpIRLinear = true;
+ dumpIRNoStmts = false;
+ dumpIRNoLeafs = false;
+ dumpIRNoLists = false;
+ }
+
+ if (wcsncmp(p, W("all"), 3) == 0)
+ {
+ dumpIRLinear = true;
+ dumpIRKinds = true;
+ dumpIRFlags = true;
+ dumpIRTypes = true;
+ dumpIRLocals = true;
+ dumpIRRegs = true;
+ dumpIRSsa = true;
+ dumpIRValnums = true;
+ dumpIRCosts = true;
+ dumpIRNoStmts = false;
+ dumpIRNoLeafs = false;
+ dumpIRNoLists = false;
+ }
+
+ if (wcsncmp(p, W("linear"), 6) == 0)
+ {
+ dumpIRTrees = false;
+ dumpIRLinear = true;
+ }
+
+ if (wcsncmp(p, W("mixed"), 5) == 0)
+ {
+ dumpIRTrees = true;
+ dumpIRLinear = true;
+ }
+
+ if (wcsncmp(p, W("dataflow"), 8) == 0)
+ {
+ dumpIRDataflow = true;
+ dumpIRNoLeafs = true;
+ dumpIRNoLists = true;
+ dumpIRNoStmts = true;
+ }
+
+ if (wcsncmp(p, W("blkhdrs"), 7) == 0)
+ {
+ dumpIRBlockHeaders = true;
+ }
+
+ for (; (*p != 0); p++)
+ {
+ if (*p == L',')
+ {
+ p++;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (verboseDump)
+ {
+ verbose = true;
+ }
+
+ if (dumpIR)
+ {
+ this->dumpIR = true;
+ }
+
+ if (dumpIRTypes)
+ {
+ this->dumpIRTypes = true;
+ }
+
+ if (dumpIRLocals)
+ {
+ this->dumpIRLocals = true;
+ }
+
+ if (dumpIRRegs)
+ {
+ this->dumpIRRegs = true;
+ }
+
+ if (dumpIRSsa)
+ {
+ this->dumpIRSsa = true;
+ }
+
+ if (dumpIRValnums)
+ {
+ this->dumpIRValnums = true;
+ }
+
+ if (dumpIRCosts)
+ {
+ this->dumpIRCosts = true;
+ }
+
+ if (dumpIRFlags)
+ {
+ this->dumpIRFlags = true;
+ }
+
+ if (dumpIRKinds)
+ {
+ this->dumpIRKinds = true;
+ }
+
+ if (dumpIRNodes)
+ {
+ this->dumpIRNodes = true;
+ }
+
+ if (dumpIRNoLists)
+ {
+ this->dumpIRNoLists = true;
+ }
+
+ if (dumpIRNoLeafs)
+ {
+ this->dumpIRNoLeafs = true;
+ }
+
+ if (dumpIRNoLeafs && dumpIRDataflow)
+ {
+ this->dumpIRDataflow = true;
+ }
+
+ if (dumpIRNoStmts)
+ {
+ this->dumpIRNoStmts = true;
+ }
+
+ if (dumpIRTrees)
+ {
+ this->dumpIRTrees = true;
+ }
+
+ if (dumpIRLinear)
+ {
+ this->dumpIRLinear = true;
+ }
+
+ if (dumpIRBlockHeaders)
+ {
+ this->dumpIRBlockHeaders = true;
+ }
+
+ if (dumpIRExit)
+ {
+ this->dumpIRExit = true;
+ }
+
+#endif // DEBUG
+
+#ifdef FEATURE_SIMD
+#ifdef _TARGET_AMD64_
+ // Minimum bar for availing SIMD benefits is SSE2 on AMD64.
+ featureSIMD = ((opts.eeFlags & CORJIT_FLG_FEATURE_SIMD) != 0);
+#endif // _TARGET_AMD64_
+#endif // FEATURE_SIMD
+
+ if (compIsForInlining() || compIsForImportOnly())
+ {
+ return;
+ }
+ // The rest of the opts fields that we initialize here
+ // should only be used when we generate code for the method
+ // They should not be used when importing or inlining
+
+ opts.genFPorder = true;
+ opts.genFPopt = true;
+
+ opts.instrCount = 0;
+ opts.lvRefCount = 0;
+
+#if FEATURE_TAILCALL_OPT
+ // By default opportunistic tail call optimization is enabled
+ opts.compTailCallOpt = true;
+ opts.compTailCallLoopOpt = true;
+#endif
+
+#ifdef DEBUG
+ opts.dspInstrs = false;
+ opts.dspEmit = false;
+ opts.dspLines = false;
+ opts.varNames = false;
+ opts.dmpHex = false;
+ opts.disAsm = false;
+ opts.disAsmSpilled = false;
+ opts.disDiffable = false;
+ opts.dspCode = false;
+ opts.dspEHTable = false;
+ opts.dspGCtbls = false;
+ opts.disAsm2 = false;
+ opts.dspUnwind = false;
+ s_dspMemStats = false;
+ opts.compLongAddress = false;
+ opts.compJitELTHookEnabled = false;
+
+#ifdef LATE_DISASM
+ opts.doLateDisasm = false;
+#endif // LATE_DISASM
+
+ compDebugBreak = false;
+
+ // If we have a non-empty AltJit config then we change all of these other
+ // config values to refer only to the AltJit.
+ //
+ if (!altJitConfig || opts.altJit)
+ {
+ if (opts.eeFlags & CORJIT_FLG_PREJIT)
+ {
+ if ((JitConfig.NgenOrder() & 1) == 1)
+ {
+ opts.dspOrder = true;
+ }
+
+ if (JitConfig.NgenGCDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ opts.dspGCtbls = true;
+ }
+
+ if (JitConfig.NgenDisasm().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ opts.disAsm = true;
+ }
+ if (JitConfig.NgenDisasm().contains("SPILLED", nullptr, nullptr))
+ {
+ opts.disAsmSpilled = true;
+ }
+
+ if (JitConfig.NgenUnwindDump().contains(info.compMethodName, info.compClassName,
+ &info.compMethodInfo->args))
+ {
+ opts.dspUnwind = true;
+ }
+
+ if (JitConfig.NgenEHDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ opts.dspEHTable = true;
+ }
+ }
+ else
+ {
+ if ((JitConfig.JitOrder() & 1) == 1)
+ {
+ opts.dspOrder = true;
+ }
+
+ if (JitConfig.JitGCDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ opts.dspGCtbls = true;
+ }
+
+ if (JitConfig.JitDisasm().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ opts.disAsm = true;
+ }
+
+ if (JitConfig.JitDisasm().contains("SPILLED", nullptr, nullptr))
+ {
+ opts.disAsmSpilled = true;
+ }
+
+ if (JitConfig.JitUnwindDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ opts.dspUnwind = true;
+ }
+
+ if (JitConfig.JitEHDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ opts.dspEHTable = true;
+ }
+ }
+
+#ifdef LATE_DISASM
+ if (JitConfig.JitLateDisasm().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ opts.doLateDisasm = true;
+#endif // LATE_DISASM
+
+ // This one applies to both Ngen/Jit Disasm output: COMPlus_JitDiffableDasm=1
+ if (JitConfig.DiffableDasm() != 0)
+ {
+ opts.disDiffable = true;
+ opts.dspDiffable = true;
+ }
+
+ if (JitConfig.DisplayMemStats() != 0)
+ {
+ s_dspMemStats = true;
+ }
+
+ if (JitConfig.JitLongAddress() != 0)
+ {
+ opts.compLongAddress = true;
+ }
+ }
+
+ if (verboseDump)
+ {
+ opts.dspCode = true;
+ opts.dspEHTable = true;
+ opts.dspGCtbls = true;
+ opts.disAsm2 = true;
+ opts.dspUnwind = true;
+ verbose = true;
+ verboseTrees = shouldUseVerboseTrees();
+ verboseSsa = shouldUseVerboseSsa();
+ codeGen->setVerbose(true);
+ }
+
+ treesBeforeAfterMorph = (JitConfig.TreesBeforeAfterMorph() == 1);
+ morphNum = 0; // Initialize the morphed-trees counting.
+
+ expensiveDebugCheckLevel = JitConfig.JitExpensiveDebugCheckLevel();
+ if (expensiveDebugCheckLevel == 0)
+ {
+ // If we're in a stress mode that modifies the flowgraph, make 1 the default.
+ if (fgStressBBProf() || compStressCompile(STRESS_DO_WHILE_LOOPS, 30))
+ {
+ expensiveDebugCheckLevel = 1;
+ }
+ }
+
+ if (verbose)
+ {
+ printf("****** START compiling %s (MethodHash=%08x)\n", info.compFullName, info.compMethodHash());
+ printf("Generating code for %s %s\n", Target::g_tgtPlatformName, Target::g_tgtCPUName);
+ printf(""); // in our logic this causes a flush
+ }
+
+ if (JitConfig.JitBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ assert(!"JitBreak reached");
+ }
+
+ unsigned jitHashBreakVal = (unsigned)JitConfig.JitHashBreak();
+ if ((jitHashBreakVal != (DWORD)-1) && (jitHashBreakVal == info.compMethodHash()))
+ {
+ assert(!"JitHashBreak reached");
+ }
+
+ if (verbose ||
+ JitConfig.JitDebugBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args) ||
+ JitConfig.JitBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ compDebugBreak = true;
+ }
+
+ memset(compActiveStressModes, 0, sizeof(compActiveStressModes));
+
+#endif // DEBUG
+
+//-------------------------------------------------------------------------
+
+#ifdef DEBUGGING_SUPPORT
+#ifdef DEBUG
+ assert(!codeGen->isGCTypeFixed());
+ opts.compGcChecks = (JitConfig.JitGCChecks() != 0) || compStressCompile(STRESS_GENERIC_VARN, 5);
+
+ enum
+ {
+ STACK_CHECK_ON_RETURN = 0x1,
+ STACK_CHECK_ON_CALL = 0x2,
+ STACK_CHECK_ALL = 0x3,
+ };
+
+ DWORD dwJitStackChecks = JitConfig.JitStackChecks();
+ if (compStressCompile(STRESS_GENERIC_VARN, 5))
+ {
+ dwJitStackChecks = STACK_CHECK_ALL;
+ }
+ opts.compStackCheckOnRet = (dwJitStackChecks & DWORD(STACK_CHECK_ON_RETURN)) != 0;
+ opts.compStackCheckOnCall = (dwJitStackChecks & DWORD(STACK_CHECK_ON_CALL)) != 0;
+#endif
+
+#ifdef PROFILING_SUPPORTED
+ opts.compNoPInvokeInlineCB = (opts.eeFlags & CORJIT_FLG_PROF_NO_PINVOKE_INLINE) ? true : false;
+
+ // Cache the profiler handle
+ if (opts.eeFlags & CORJIT_FLG_PROF_ENTERLEAVE)
+ {
+ BOOL hookNeeded;
+ BOOL indirected;
+ info.compCompHnd->GetProfilingHandle(&hookNeeded, &compProfilerMethHnd, &indirected);
+ compProfilerHookNeeded = !!hookNeeded;
+ compProfilerMethHndIndirected = !!indirected;
+ }
+ else
+ {
+ compProfilerHookNeeded = false;
+ compProfilerMethHnd = nullptr;
+ compProfilerMethHndIndirected = false;
+ }
+
+#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_)
+ // Right now this ELT hook option is enabled only for arm and amd64
+
+ // Honour complus_JitELTHookEnabled only if VM has not asked us to generate profiler
+ // hooks in the first place. That is, Override VM only if it hasn't asked for a
+ // profiler callback for this method.
+ if (!compProfilerHookNeeded && (JitConfig.JitELTHookEnabled() != 0))
+ {
+ opts.compJitELTHookEnabled = true;
+ }
+
+ // TBD: Exclude PInvoke stubs
+ if (opts.compJitELTHookEnabled)
+ {
+ compProfilerMethHnd = (void*)DummyProfilerELTStub;
+ compProfilerMethHndIndirected = false;
+ }
+#endif // _TARGET_ARM_ || _TARGET_AMD64_
+
+#endif // PROFILING_SUPPORTED
+
+#if FEATURE_TAILCALL_OPT
+ const wchar_t* strTailCallOpt = JitConfig.TailCallOpt();
+ if (strTailCallOpt != nullptr)
+ {
+ opts.compTailCallOpt = (UINT)_wtoi(strTailCallOpt) != 0;
+ }
+
+ if (JitConfig.TailCallLoopOpt() == 0)
+ {
+ opts.compTailCallLoopOpt = false;
+ }
+#endif
+
+ opts.compMustInlinePInvokeCalli = (opts.eeFlags & CORJIT_FLG_IL_STUB) ? true : false;
+
+ opts.compScopeInfo = opts.compDbgInfo;
+#endif // DEBUGGING_SUPPORT
+
+#ifdef LATE_DISASM
+ codeGen->getDisAssembler().disOpenForLateDisAsm(info.compMethodName, info.compClassName,
+ info.compMethodInfo->args.pSig);
+#endif
+
+//-------------------------------------------------------------------------
+
+#if RELOC_SUPPORT
+ opts.compReloc = (opts.eeFlags & CORJIT_FLG_RELOC) ? true : false;
+#endif
+
+#ifdef DEBUG
+#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
+ // Whether encoding of absolute addr as PC-rel offset is enabled in RyuJIT
+ opts.compEnablePCRelAddr = (JitConfig.EnablePCRelAddr() != 0);
+#endif
+#endif // DEBUG
+
+ opts.compProcedureSplitting = (opts.eeFlags & CORJIT_FLG_PROCSPLIT) ? true : false;
+
+#ifdef _TARGET_ARM64_
+ // TODO-ARM64-NYI: enable hot/cold splitting
+ opts.compProcedureSplitting = false;
+#endif // _TARGET_ARM64_
+
+#ifdef DEBUG
+ opts.compProcedureSplittingEH = opts.compProcedureSplitting;
+#endif // DEBUG
+
+ if (opts.compProcedureSplitting)
+ {
+ // Note that opts.compdbgCode is true under ngen for checked assemblies!
+ opts.compProcedureSplitting = !opts.compDbgCode;
+
+#ifdef DEBUG
+ // JitForceProcedureSplitting is used to force procedure splitting on checked assemblies.
+ // This is useful for debugging on a checked build. Note that we still only do procedure
+ // splitting in the zapper.
+ if (JitConfig.JitForceProcedureSplitting().contains(info.compMethodName, info.compClassName,
+ &info.compMethodInfo->args))
+ {
+ opts.compProcedureSplitting = true;
+ }
+
+ // JitNoProcedureSplitting will always disable procedure splitting.
+ if (JitConfig.JitNoProcedureSplitting().contains(info.compMethodName, info.compClassName,
+ &info.compMethodInfo->args))
+ {
+ opts.compProcedureSplitting = false;
+ }
+ //
+ // JitNoProcedureSplittingEH will disable procedure splitting in functions with EH.
+ if (JitConfig.JitNoProcedureSplittingEH().contains(info.compMethodName, info.compClassName,
+ &info.compMethodInfo->args))
+ {
+ opts.compProcedureSplittingEH = false;
+ }
+#endif
+ }
+
+ fgProfileBuffer = nullptr;
+ fgProfileData_ILSizeMismatch = false;
+ fgNumProfileRuns = 0;
+ if (opts.eeFlags & CORJIT_FLG_BBOPT)
+ {
+ assert(!compIsForInlining());
+ HRESULT hr;
+ hr = info.compCompHnd->getBBProfileData(info.compMethodHnd, &fgProfileBufferCount, &fgProfileBuffer,
+ &fgNumProfileRuns);
+
+ // a failed result that also has a non-NULL fgProfileBuffer
+ // indicates that the ILSize for the method no longer matches
+ // the ILSize for the method when profile data was collected.
+ //
+ // We will discard the IBC data in this case
+ //
+ if (FAILED(hr) && (fgProfileBuffer != nullptr))
+ {
+ fgProfileData_ILSizeMismatch = true;
+ fgProfileBuffer = nullptr;
+ }
+#ifdef DEBUG
+ // A successful result implies a non-NULL fgProfileBuffer
+ //
+ if (SUCCEEDED(hr))
+ {
+ assert(fgProfileBuffer != nullptr);
+ }
+
+ // A failed result implies a NULL fgProfileBuffer
+ // see implementation of Compiler::fgHaveProfileData()
+ //
+ if (FAILED(hr))
+ {
+ assert(fgProfileBuffer == nullptr);
+ }
+#endif
+ }
+
+ opts.compNeedStackProbes = false;
+
+#ifdef DEBUG
+ if (JitConfig.StackProbesOverride() != 0 || compStressCompile(STRESS_GENERIC_VARN, 5))
+ {
+ opts.compNeedStackProbes = true;
+ }
+#endif
+
+#ifdef DEBUG
+ // Now, set compMaxUncheckedOffsetForNullObject for STRESS_NULL_OBJECT_CHECK
+ if (compStressCompile(STRESS_NULL_OBJECT_CHECK, 30))
+ {
+ compMaxUncheckedOffsetForNullObject = (size_t)JitConfig.JitMaxUncheckedOffset();
+ if (verbose)
+ {
+ printf("STRESS_NULL_OBJECT_CHECK: compMaxUncheckedOffsetForNullObject=0x%X\n",
+ compMaxUncheckedOffsetForNullObject);
+ }
+ }
+
+ if (verbose)
+ {
+ printf("OPTIONS: compCodeOpt = %s\n",
+ (opts.compCodeOpt == BLENDED_CODE)
+ ? "BLENDED_CODE"
+ : (opts.compCodeOpt == SMALL_CODE) ? "SMALL_CODE"
+ : (opts.compCodeOpt == FAST_CODE) ? "FAST_CODE" : "UNKNOWN_CODE");
+
+ printf("OPTIONS: compDbgCode = %s\n", dspBool(opts.compDbgCode));
+ printf("OPTIONS: compDbgInfo = %s\n", dspBool(opts.compDbgInfo));
+ printf("OPTIONS: compDbgEnC = %s\n", dspBool(opts.compDbgEnC));
+ printf("OPTIONS: compProcedureSplitting = %s\n", dspBool(opts.compProcedureSplitting));
+ printf("OPTIONS: compProcedureSplittingEH = %s\n", dspBool(opts.compProcedureSplittingEH));
+
+ if ((opts.eeFlags & CORJIT_FLG_BBOPT) && fgHaveProfileData())
+ {
+ printf("OPTIONS: using real profile data\n");
+ }
+
+ if (fgProfileData_ILSizeMismatch)
+ {
+ printf("OPTIONS: discarded IBC profile data due to mismatch in ILSize\n");
+ }
+
+ if (opts.eeFlags & CORJIT_FLG_PREJIT)
+ {
+ printf("OPTIONS: Jit invoked for ngen\n");
+ }
+ printf("OPTIONS: Stack probing is %s\n", opts.compNeedStackProbes ? "ENABLED" : "DISABLED");
+ }
+#endif
+
+ opts.compGCPollType = GCPOLL_NONE;
+ if (opts.eeFlags & CORJIT_FLG_GCPOLL_CALLS)
+ {
+ opts.compGCPollType = GCPOLL_CALL;
+ }
+ else if (opts.eeFlags & CORJIT_FLG_GCPOLL_INLINE)
+ {
+ // make sure that the EE didn't set both flags.
+ assert(opts.compGCPollType == GCPOLL_NONE);
+ opts.compGCPollType = GCPOLL_INLINE;
+ }
+}
+
+#ifdef DEBUG
+
+void JitDump(const char* pcFormat, ...)
+{
+ va_list lst;
+ va_start(lst, pcFormat);
+ vflogf(jitstdout, pcFormat, lst);
+ va_end(lst);
+}
+
+bool Compiler::compJitHaltMethod()
+{
+ /* This method returns true when we use an INS_BREAKPOINT to allow us to step into the generated native code */
+ /* Note that this these two "Jit" environment variables also work for ngen images */
+
+ if (JitConfig.JitHalt().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ return true;
+ }
+
+ /* Use this Hash variant when there are a lot of method with the same name and different signatures */
+
+ unsigned fJitHashHaltVal = (unsigned)JitConfig.JitHashHalt();
+ if ((fJitHashHaltVal != (unsigned)-1) && (fJitHashHaltVal == info.compMethodHash()))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/*****************************************************************************
+ * Should we use a "stress-mode" for the given stressArea. We have different
+ * areas to allow the areas to be mixed in different combinations in
+ * different methods.
+ * 'weight' indicates how often (as a percentage) the area should be stressed.
+ * It should reflect the usefulness:overhead ratio.
+ */
+
+const LPCWSTR Compiler::s_compStressModeNames[STRESS_COUNT + 1] = {
+#define STRESS_MODE(mode) W("STRESS_") W(#mode),
+
+ STRESS_MODES
+#undef STRESS_MODE
+};
+
+bool Compiler::compStressCompile(compStressArea stressArea, unsigned weight)
+{
+ unsigned hash;
+ DWORD stressLevel;
+
+ if (!bRangeAllowStress)
+ {
+ return false;
+ }
+
+ if (!JitConfig.JitStressOnly().isEmpty() &&
+ !JitConfig.JitStressOnly().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ return false;
+ }
+
+ bool doStress = false;
+ const wchar_t* strStressModeNames;
+
+ // Does user explicitly prevent using this STRESS_MODE through the command line?
+ const wchar_t* strStressModeNamesNot = JitConfig.JitStressModeNamesNot();
+ if ((strStressModeNamesNot != nullptr) &&
+ (wcsstr(strStressModeNamesNot, s_compStressModeNames[stressArea]) != nullptr))
+ {
+ if (verbose)
+ {
+ printf("JitStressModeNamesNot contains %ws\n", s_compStressModeNames[stressArea]);
+ }
+ doStress = false;
+ goto _done;
+ }
+
+ // Does user explicitly set this STRESS_MODE through the command line?
+ strStressModeNames = JitConfig.JitStressModeNames();
+ if (strStressModeNames != nullptr)
+ {
+ if (wcsstr(strStressModeNames, s_compStressModeNames[stressArea]) != nullptr)
+ {
+ if (verbose)
+ {
+ printf("JitStressModeNames contains %ws\n", s_compStressModeNames[stressArea]);
+ }
+ doStress = true;
+ goto _done;
+ }
+
+ // This stress mode name did not match anything in the stress
+ // mode whitelist. If user has requested only enable mode,
+ // don't allow this stress mode to turn on.
+ const bool onlyEnableMode = JitConfig.JitStressModeNamesOnly() != 0;
+
+ if (onlyEnableMode)
+ {
+ doStress = false;
+ goto _done;
+ }
+ }
+
+ // 0: No stress (Except when explicitly set in complus_JitStressModeNames)
+ // !=2: Vary stress. Performance will be slightly/moderately degraded
+ // 2: Check-all stress. Performance will be REALLY horrible
+ stressLevel = getJitStressLevel();
+
+ assert(weight <= MAX_STRESS_WEIGHT);
+
+ /* Check for boundary conditions */
+
+ if (stressLevel == 0 || weight == 0)
+ {
+ return false;
+ }
+
+ // Should we allow unlimited stress ?
+ if (stressArea > STRESS_COUNT_VARN && stressLevel == 2)
+ {
+ return true;
+ }
+
+ if (weight == MAX_STRESS_WEIGHT)
+ {
+ doStress = true;
+ goto _done;
+ }
+
+ // Get a hash which can be compared with 'weight'
+
+ assert(stressArea != 0);
+ hash = (info.compMethodHash() ^ stressArea ^ stressLevel) % MAX_STRESS_WEIGHT;
+
+ assert(hash < MAX_STRESS_WEIGHT && weight <= MAX_STRESS_WEIGHT);
+ doStress = (hash < weight);
+
+_done:
+
+ if (doStress && !compActiveStressModes[stressArea])
+ {
+ if (verbose)
+ {
+ printf("\n\n*** JitStress: %ws ***\n\n", s_compStressModeNames[stressArea]);
+ }
+ compActiveStressModes[stressArea] = 1;
+ }
+
+ return doStress;
+}
+
+#endif // DEBUG
+
+void Compiler::compInitDebuggingInfo()
+{
+ assert(!compIsForInlining());
+
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("*************** In compInitDebuggingInfo() for %s\n", info.compFullName);
+ }
+#endif
+
+ /*-------------------------------------------------------------------------
+ *
+ * Get hold of the local variable records, if there are any
+ */
+
+ info.compVarScopesCount = 0;
+
+#ifdef DEBUGGING_SUPPORT
+ if (opts.compScopeInfo)
+#endif
+ {
+ eeGetVars();
+ }
+
+#ifdef DEBUGGING_SUPPORT
+ compInitVarScopeMap();
+
+ if (opts.compScopeInfo || opts.compDbgCode)
+ {
+ compInitScopeLists();
+ }
+
+ if (opts.compDbgCode && (info.compVarScopesCount > 0))
+ {
+ /* Create a new empty basic block. fgExtendDbgLifetimes() may add
+ initialization of variables which are in scope right from the
+ start of the (real) first BB (and therefore artificially marked
+ as alive) into this block.
+ */
+
+ fgEnsureFirstBBisScratch();
+
+ fgInsertStmtAtEnd(fgFirstBB, gtNewNothingNode());
+
+ JITDUMP("Debuggable code - Add new BB%02u to perform initialization of variables [%08X]\n", fgFirstBB->bbNum,
+ dspPtr(fgFirstBB));
+ }
+#endif // DEBUGGING_SUPPORT
+
+ /*-------------------------------------------------------------------------
+ *
+ * Read the stmt-offsets table and the line-number table
+ */
+
+ info.compStmtOffsetsImplicit = ICorDebugInfo::NO_BOUNDARIES;
+
+ // We can only report debug info for EnC at places where the stack is empty.
+ // Actually, at places where there are not live temps. Else, we won't be able
+ // to map between the old and the new versions correctly as we won't have
+ // any info for the live temps.
+
+ assert(!opts.compDbgEnC || !opts.compDbgInfo ||
+ 0 == (info.compStmtOffsetsImplicit & ~ICorDebugInfo::STACK_EMPTY_BOUNDARIES));
+
+ info.compStmtOffsetsCount = 0;
+
+#ifdef DEBUGGING_SUPPORT
+ if (opts.compDbgInfo)
+#endif
+ {
+ /* Get hold of the line# records, if there are any */
+
+ eeGetStmtOffsets();
+
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("info.compStmtOffsetsCount = %d\n", info.compStmtOffsetsCount);
+ printf("info.compStmtOffsetsImplicit = %04Xh", info.compStmtOffsetsImplicit);
+
+ if (info.compStmtOffsetsImplicit)
+ {
+ printf(" ( ");
+ if (info.compStmtOffsetsImplicit & ICorDebugInfo::STACK_EMPTY_BOUNDARIES)
+ {
+ printf("STACK_EMPTY ");
+ }
+ if (info.compStmtOffsetsImplicit & ICorDebugInfo::NOP_BOUNDARIES)
+ {
+ printf("NOP ");
+ }
+ if (info.compStmtOffsetsImplicit & ICorDebugInfo::CALL_SITE_BOUNDARIES)
+ {
+ printf("CALL_SITE ");
+ }
+ printf(")");
+ }
+ printf("\n");
+ IL_OFFSET* pOffs = info.compStmtOffsets;
+ for (unsigned i = 0; i < info.compStmtOffsetsCount; i++, pOffs++)
+ {
+ printf("%02d) IL_%04Xh\n", i, *pOffs);
+ }
+ }
+#endif
+ }
+}
+
+void Compiler::compSetOptimizationLevel()
+{
+ unsigned compileFlags;
+ bool theMinOptsValue;
+ unsigned jitMinOpts;
+
+ compileFlags = opts.eeFlags;
+
+ if (compIsForInlining())
+ {
+ theMinOptsValue = impInlineInfo->InlinerCompiler->opts.MinOpts();
+ goto _SetMinOpts;
+ }
+
+ theMinOptsValue = false;
+
+ if (opts.compFlags == CLFLG_MINOPT)
+ {
+ JITLOG((LL_INFO100, "CLFLG_MINOPT set for method %s\n", info.compFullName));
+ theMinOptsValue = true;
+ }
+
+#ifdef DEBUG
+ jitMinOpts = JitConfig.JitMinOpts();
+
+ if (!theMinOptsValue && (jitMinOpts > 0))
+ {
+ unsigned methodCount = Compiler::jitTotalMethodCompiled;
+ unsigned methodCountMask = methodCount & 0xFFF;
+ unsigned kind = (jitMinOpts & 0xF000000) >> 24;
+ switch (kind)
+ {
+ default:
+ if (jitMinOpts <= methodCount)
+ {
+ if (verbose)
+ {
+ printf(" Optimizations disabled by JitMinOpts and methodCount\n");
+ }
+ theMinOptsValue = true;
+ }
+ break;
+ case 0xD:
+ {
+ unsigned firstMinopts = (jitMinOpts >> 12) & 0xFFF;
+ unsigned secondMinopts = (jitMinOpts >> 0) & 0xFFF;
+
+ if ((firstMinopts == methodCountMask) || (secondMinopts == methodCountMask))
+ {
+ if (verbose)
+ {
+ printf("0xD: Optimizations disabled by JitMinOpts and methodCountMask\n");
+ }
+ theMinOptsValue = true;
+ }
+ }
+ break;
+ case 0xE:
+ {
+ unsigned startMinopts = (jitMinOpts >> 12) & 0xFFF;
+ unsigned endMinopts = (jitMinOpts >> 0) & 0xFFF;
+
+ if ((startMinopts <= methodCountMask) && (endMinopts >= methodCountMask))
+ {
+ if (verbose)
+ {
+ printf("0xE: Optimizations disabled by JitMinOpts and methodCountMask\n");
+ }
+ theMinOptsValue = true;
+ }
+ }
+ break;
+ case 0xF:
+ {
+ unsigned bitsZero = (jitMinOpts >> 12) & 0xFFF;
+ unsigned bitsOne = (jitMinOpts >> 0) & 0xFFF;
+
+ if (((methodCountMask & bitsOne) == bitsOne) && ((~methodCountMask & bitsZero) == bitsZero))
+ {
+ if (verbose)
+ {
+ printf("0xF: Optimizations disabled by JitMinOpts and methodCountMask\n");
+ }
+ theMinOptsValue = true;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!theMinOptsValue)
+ {
+ if (JitConfig.JitMinOptsName().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ theMinOptsValue = true;
+ }
+ }
+
+ if (compStressCompile(STRESS_MIN_OPTS, 5))
+ {
+ theMinOptsValue = true;
+ }
+ // For PREJIT we never drop down to MinOpts
+ // unless unless CLFLG_MINOPT is set
+ else if (!(compileFlags & CORJIT_FLG_PREJIT))
+ {
+ if ((unsigned)JitConfig.JitMinOptsCodeSize() < info.compILCodeSize)
+ {
+ JITLOG((LL_INFO10, "IL Code Size exceeded, using MinOpts for method %s\n", info.compFullName));
+ theMinOptsValue = true;
+ }
+ else if ((unsigned)JitConfig.JitMinOptsInstrCount() < opts.instrCount)
+ {
+ JITLOG((LL_INFO10, "IL instruction count exceeded, using MinOpts for method %s\n", info.compFullName));
+ theMinOptsValue = true;
+ }
+ else if ((unsigned)JitConfig.JitMinOptsBbCount() < fgBBcount)
+ {
+ JITLOG((LL_INFO10, "Basic Block count exceeded, using MinOpts for method %s\n", info.compFullName));
+ theMinOptsValue = true;
+ }
+ else if ((unsigned)JitConfig.JitMinOptsLvNumCount() < lvaCount)
+ {
+ JITLOG((LL_INFO10, "Local Variable Num count exceeded, using MinOpts for method %s\n", info.compFullName));
+ theMinOptsValue = true;
+ }
+ else if ((unsigned)JitConfig.JitMinOptsLvRefCount() < opts.lvRefCount)
+ {
+ JITLOG((LL_INFO10, "Local Variable Ref count exceeded, using MinOpts for method %s\n", info.compFullName));
+ theMinOptsValue = true;
+ }
+ if (theMinOptsValue == true)
+ {
+ JITLOG((LL_INFO10000, "IL Code Size,Instr %4d,%4d, Basic Block count %3d, Local Variable Num,Ref count "
+ "%3d,%3d for method %s\n",
+ info.compILCodeSize, opts.instrCount, fgBBcount, lvaCount, opts.lvRefCount, info.compFullName));
+ if (JitConfig.JitBreakOnMinOpts() != 0)
+ {
+ assert(!"MinOpts enabled");
+ }
+ }
+ }
+#else // !DEBUG
+ // Retail check if we should force Minopts due to the complexity of the method
+ // For PREJIT we never drop down to MinOpts
+ // unless unless CLFLG_MINOPT is set
+ if (!theMinOptsValue && !(compileFlags & CORJIT_FLG_PREJIT) &&
+ ((DEFAULT_MIN_OPTS_CODE_SIZE < info.compILCodeSize) || (DEFAULT_MIN_OPTS_INSTR_COUNT < opts.instrCount) ||
+ (DEFAULT_MIN_OPTS_BB_COUNT < fgBBcount) || (DEFAULT_MIN_OPTS_LV_NUM_COUNT < lvaCount) ||
+ (DEFAULT_MIN_OPTS_LV_REF_COUNT < opts.lvRefCount)))
+ {
+ theMinOptsValue = true;
+ }
+#endif // DEBUG
+
+ JITLOG((LL_INFO10000,
+ "IL Code Size,Instr %4d,%4d, Basic Block count %3d, Local Variable Num,Ref count %3d,%3d for method %s\n",
+ info.compILCodeSize, opts.instrCount, fgBBcount, lvaCount, opts.lvRefCount, info.compFullName));
+
+#if 0
+ // The code in this #if has been useful in debugging loop cloning issues, by
+ // enabling selective enablement of the loop cloning optimization according to
+ // method hash.
+#ifdef DEBUG
+ if (!theMinOptsValue)
+ {
+ unsigned methHash = info.compMethodHash();
+ char* lostr = getenv("opthashlo");
+ unsigned methHashLo = 0;
+ if (lostr != NULL)
+ {
+ sscanf_s(lostr, "%x", &methHashLo);
+ // methHashLo = (unsigned(atoi(lostr)) << 2); // So we don't have to use negative numbers.
+ }
+ char* histr = getenv("opthashhi");
+ unsigned methHashHi = UINT32_MAX;
+ if (histr != NULL)
+ {
+ sscanf_s(histr, "%x", &methHashHi);
+ // methHashHi = (unsigned(atoi(histr)) << 2); // So we don't have to use negative numbers.
+ }
+ if (methHash < methHashLo || methHash > methHashHi)
+ {
+ theMinOptsValue = true;
+ }
+ else
+ {
+ printf("Doing optimization in in %s (0x%x).\n", info.compFullName, methHash);
+ }
+ }
+#endif
+#endif
+
+_SetMinOpts:
+
+ // Set the MinOpts value
+ opts.SetMinOpts(theMinOptsValue);
+
+#ifdef DEBUG
+ if (verbose && !compIsForInlining())
+ {
+ printf("OPTIONS: opts.MinOpts() == %s\n", opts.MinOpts() ? "true" : "false");
+ }
+#endif
+
+ /* Control the optimizations */
+
+ if (opts.MinOpts() || opts.compDbgCode)
+ {
+ opts.compFlags &= ~CLFLG_MAXOPT;
+ opts.compFlags |= CLFLG_MINOPT;
+ }
+
+ if (!compIsForInlining())
+ {
+ codeGen->setFramePointerRequired(false);
+ codeGen->setFrameRequired(false);
+
+ if (opts.MinOpts() || opts.compDbgCode)
+ {
+ codeGen->setFrameRequired(true);
+ }
+
+#if !defined(_TARGET_AMD64_)
+ // The VM sets CORJIT_FLG_FRAMED for two reasons: (1) the COMPlus_JitFramed variable is set, or
+ // (2) the function is marked "noinline". The reason for #2 is that people mark functions
+ // noinline to ensure the show up on in a stack walk. But for AMD64, we don't need a frame
+ // pointer for the frame to show up in stack walk.
+ if (compileFlags & CORJIT_FLG_FRAMED)
+ codeGen->setFrameRequired(true);
+#endif
+
+ if (compileFlags & CORJIT_FLG_RELOC)
+ {
+ codeGen->genAlignLoops = false; // loop alignment not supported for prejitted code
+
+ // The zapper doesn't set CORJIT_FLG_ALIGN_LOOPS, and there is
+ // no reason for it to set it as the JIT doesn't currently support loop alignment
+ // for prejitted images. (The JIT doesn't know the final address of the code, hence
+ // it can't align code based on unknown addresses.)
+ assert((compileFlags & CORJIT_FLG_ALIGN_LOOPS) == 0);
+ }
+ else
+ {
+ codeGen->genAlignLoops = (compileFlags & CORJIT_FLG_ALIGN_LOOPS) != 0;
+ }
+ }
+
+ info.compUnwrapContextful = !opts.MinOpts() && !opts.compDbgCode;
+
+ fgCanRelocateEHRegions = true;
+}
+
+#ifdef _TARGET_ARMARCH_
+// Function compRsvdRegCheck:
+// given a curState to use for calculating the total frame size
+// it will return true if the REG_OPT_RSVD should be reserved so
+// that it can be use to form large offsets when accessing stack
+// based LclVar including both incoming and out going argument areas.
+//
+// The method advances the frame layout state to curState by calling
+// lvaFrameSize(curState).
+//
+bool Compiler::compRsvdRegCheck(FrameLayoutState curState)
+{
+ // Always do the layout even if returning early. Callers might
+ // depend on us to do the layout.
+ unsigned frameSize = lvaFrameSize(curState);
+
+ if (opts.MinOpts())
+ {
+ // Have a recovery path in case we fail to reserve REG_OPT_RSVD and go
+ // over the limit of SP and FP offset ranges due to large
+ // temps.
+ return true;
+ }
+
+ unsigned calleeSavedRegMaxSz = CALLEE_SAVED_REG_MAXSZ;
+ if (compFloatingPointUsed)
+ {
+ calleeSavedRegMaxSz += CALLEE_SAVED_FLOAT_MAXSZ;
+ }
+
+ noway_assert(frameSize > calleeSavedRegMaxSz);
+
+#if defined(_TARGET_ARM64_)
+
+ // TODO-ARM64-CQ: update this!
+ return true; // just always assume we'll need it, for now
+
+#else // _TARGET_ARM_
+
+ // frame layout:
+ //
+ // low addresses
+ // inArgs compArgSize
+ // origSP --->
+ // LR --->
+ // R11 --->
+ // + callee saved regs CALLEE_SAVED_REG_MAXSZ (32 bytes)
+ // optional saved fp regs 16 * sizeof(float) (64 bytes)
+ // - lclSize
+ // incl. TEMPS MAX_SPILL_TEMP_SIZE
+ // + incl. outArgs
+ // SP --->
+ // -
+ // high addresses
+
+ // With codeGen->isFramePointerRequired we use R11 to access incoming args with positive offsets
+ // and use R11 to access LclVars with negative offsets in the non funclet or
+ // main region we use SP with positive offsets. The limiting factor in the
+ // codeGen->isFramePointerRequired case is that we need the offset to be less than or equal to 0x7C
+ // for negative offsets, but positive offsets can be imm12 limited by vldr/vstr
+ // using +/-imm8.
+ //
+ // Subtract 4 bytes for alignment of a local var because number of temps could
+ // trigger a misaligned double or long.
+ //
+ unsigned maxR11ArgLimit = (compFloatingPointUsed ? 0x03FC : 0x0FFC);
+ unsigned maxR11LclLimit = 0x0078;
+
+ if (codeGen->isFramePointerRequired())
+ {
+ unsigned maxR11LclOffs = frameSize;
+ unsigned maxR11ArgOffs = compArgSize + (2 * REGSIZE_BYTES);
+ if (maxR11LclOffs > maxR11LclLimit || maxR11ArgOffs > maxR11ArgLimit)
+ {
+ return true;
+ }
+ }
+
+ // So this case is the SP based frame case, but note that we also will use SP based
+ // offsets for R11 based frames in the non-funclet main code area. However if we have
+ // passed the above max_R11_offset check these SP checks won't fire.
+
+ // Check local coverage first. If vldr/vstr will be used the limit can be +/-imm8.
+ unsigned maxSPLclLimit = (compFloatingPointUsed ? 0x03F8 : 0x0FF8);
+ if (frameSize > (codeGen->isFramePointerUsed() ? (maxR11LclLimit + maxSPLclLimit) : maxSPLclLimit))
+ {
+ return true;
+ }
+
+ // Check arguments coverage.
+ if ((!codeGen->isFramePointerUsed() || (compArgSize > maxR11ArgLimit)) && (compArgSize + frameSize) > maxSPLclLimit)
+ {
+ return true;
+ }
+
+ // We won't need to reserve REG_OPT_RSVD.
+ //
+ return false;
+#endif // _TARGET_ARM_
+}
+#endif // _TARGET_ARMARCH_
+
+void Compiler::compFunctionTraceStart()
+{
+#ifdef DEBUG
+ if (compIsForInlining())
+ {
+ return;
+ }
+
+ if ((JitConfig.JitFunctionTrace() != 0) && !opts.disDiffable)
+ {
+ LONG newJitNestingLevel = InterlockedIncrement(&Compiler::jitNestingLevel);
+ if (newJitNestingLevel <= 0)
+ {
+ printf("{ Illegal nesting level %d }\n", newJitNestingLevel);
+ }
+
+ for (LONG i = 0; i < newJitNestingLevel - 1; i++)
+ {
+ printf(" ");
+ }
+ printf("{ Start Jitting %s (MethodHash=%08x)\n", info.compFullName,
+ info.compMethodHash()); /* } editor brace matching workaround for this printf */
+ }
+#endif // DEBUG
+}
+
+void Compiler::compFunctionTraceEnd(void* methodCodePtr, ULONG methodCodeSize, bool isNYI)
+{
+#ifdef DEBUG
+ assert(!compIsForInlining());
+
+ if ((JitConfig.JitFunctionTrace() != 0) && !opts.disDiffable)
+ {
+ LONG newJitNestingLevel = InterlockedDecrement(&Compiler::jitNestingLevel);
+ if (newJitNestingLevel < 0)
+ {
+ printf("{ Illegal nesting level %d }\n", newJitNestingLevel);
+ }
+
+ for (LONG i = 0; i < newJitNestingLevel; i++)
+ {
+ printf(" ");
+ }
+ /* { editor brace-matching workaround for following printf */
+ printf("} Jitted Entry %03x at" FMT_ADDR "method %s size %08x%s\n", Compiler::jitTotalMethodCompiled,
+ DBG_ADDR(methodCodePtr), info.compFullName, methodCodeSize,
+ isNYI ? " NYI" : (compIsForImportOnly() ? " import only" : ""));
+ }
+#endif // DEBUG
+}
+
+//*********************************************************************************************
+// #Phases
+//
+// This is the most interesting 'toplevel' function in the JIT. It goes through the operations of
+// importing, morphing, optimizations and code generation. This is called from the EE through the
+// code:CILJit::compileMethod function.
+//
+// For an overview of the structure of the JIT, see:
+// https://github.com/dotnet/coreclr/blob/master/Documentation/botr/ryujit-overview.md
+//
+void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, CORJIT_FLAGS* compileFlags)
+{
+ if (compIsForInlining())
+ {
+ // Notify root instance that an inline attempt is about to import IL
+ impInlineRoot()->m_inlineStrategy->NoteImport();
+ }
+
+ hashBv::Init(this);
+
+ VarSetOps::AssignAllowUninitRhs(this, compCurLife, VarSetOps::UninitVal());
+
+ /* The temp holding the secret stub argument is used by fgImport() when importing the intrinsic. */
+
+ if (info.compPublishStubParam)
+ {
+ assert(lvaStubArgumentVar == BAD_VAR_NUM);
+ lvaStubArgumentVar = lvaGrabTempWithImplicitUse(false DEBUGARG("stub argument"));
+ lvaTable[lvaStubArgumentVar].lvType = TYP_I_IMPL;
+ }
+
+ EndPhase(PHASE_PRE_IMPORT);
+
+ compFunctionTraceStart();
+
+ /* Convert the instrs in each basic block to a tree based intermediate representation */
+
+ fgImport();
+
+ assert(!fgComputePredsDone);
+ if (fgCheapPredsValid)
+ {
+ // Remove cheap predecessors before inlining; allowing the cheap predecessor lists to be inserted
+ // with inlined blocks causes problems.
+ fgRemovePreds();
+ }
+
+ if (compIsForInlining())
+ {
+ /* Quit inlining if fgImport() failed for any reason. */
+
+ if (compDonotInline())
+ {
+ return;
+ }
+
+ /* Filter out unimported BBs */
+
+ fgRemoveEmptyBlocks();
+
+ return;
+ }
+
+ assert(!compDonotInline());
+
+ EndPhase(PHASE_IMPORTATION);
+
+ // Maybe the caller was not interested in generating code
+ if (compIsForImportOnly())
+ {
+ compFunctionTraceEnd(nullptr, 0, false);
+ return;
+ }
+
+#if !FEATURE_EH
+ // If we aren't yet supporting EH in a compiler bring-up, remove as many EH handlers as possible, so
+ // we can pass tests that contain try/catch EH, but don't actually throw any exceptions.
+ fgRemoveEH();
+#endif // !FEATURE_EH
+
+ if (compileFlags->corJitFlags & CORJIT_FLG_BBINSTR)
+ {
+ fgInstrumentMethod();
+ }
+
+ // We could allow ESP frames. Just need to reserve space for
+ // pushing EBP if the method becomes an EBP-frame after an edit.
+ // Note that requiring a EBP Frame disallows double alignment. Thus if we change this
+ // we either have to disallow double alignment for E&C some other way or handle it in EETwain.
+
+ if (opts.compDbgEnC)
+ {
+ codeGen->setFramePointerRequired(true);
+
+ // Since we need a slots for security near ebp, its not possible
+ // to do this after an Edit without shifting all the locals.
+ // So we just always reserve space for these slots in case an Edit adds them
+ opts.compNeedSecurityCheck = true;
+
+ // We don't care about localloc right now. If we do support it,
+ // EECodeManager::FixContextForEnC() needs to handle it smartly
+ // in case the localloc was actually executed.
+ //
+ // compLocallocUsed = true;
+ }
+
+ EndPhase(PHASE_POST_IMPORT);
+
+ /* Initialize the BlockSet epoch */
+
+ NewBasicBlockEpoch();
+
+ /* Massage the trees so that we can generate code out of them */
+
+ fgMorph();
+ EndPhase(PHASE_MORPH);
+
+ /* GS security checks for unsafe buffers */
+ if (getNeedsGSSecurityCookie())
+ {
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("\n*************** -GS checks for unsafe buffers \n");
+ }
+#endif
+
+ gsGSChecksInitCookie();
+
+ if (compGSReorderStackLayout)
+ {
+ gsCopyShadowParams();
+ }
+
+#ifdef DEBUG
+ if (verbose)
+ {
+ fgDispBasicBlocks(true);
+ printf("\n");
+ }
+#endif
+ }
+ EndPhase(PHASE_GS_COOKIE);
+
+ /* Compute bbNum, bbRefs and bbPreds */
+
+ JITDUMP("\nRenumbering the basic blocks for fgComputePred\n");
+ fgRenumberBlocks();
+
+ noway_assert(!fgComputePredsDone); // This is the first time full (not cheap) preds will be computed.
+ fgComputePreds();
+ EndPhase(PHASE_COMPUTE_PREDS);
+
+ /* If we need to emit GC Poll calls, mark the blocks that need them now. This is conservative and can
+ * be optimized later. */
+ fgMarkGCPollBlocks();
+ EndPhase(PHASE_MARK_GC_POLL_BLOCKS);
+
+ /* From this point on the flowgraph information such as bbNum,
+ * bbRefs or bbPreds has to be kept updated */
+
+ // Compute the edge weights (if we have profile data)
+ fgComputeEdgeWeights();
+ EndPhase(PHASE_COMPUTE_EDGE_WEIGHTS);
+
+#if FEATURE_EH_FUNCLETS
+
+ /* Create funclets from the EH handlers. */
+
+ fgCreateFunclets();
+ EndPhase(PHASE_CREATE_FUNCLETS);
+
+#endif // FEATURE_EH_FUNCLETS
+
+ if (!opts.MinOpts() && !opts.compDbgCode)
+ {
+ optOptimizeLayout();
+ EndPhase(PHASE_OPTIMIZE_LAYOUT);
+
+ // Compute reachability sets and dominators.
+ fgComputeReachability();
+ }
+
+ // Transform each GT_ALLOCOBJ node into either an allocation helper call or
+ // local variable allocation on the stack.
+ ObjectAllocator objectAllocator(this);
+ objectAllocator.Run();
+
+ if (!opts.MinOpts() && !opts.compDbgCode)
+ {
+ /* Perform loop inversion (i.e. transform "while" loops into
+ "repeat" loops) and discover and classify natural loops
+ (e.g. mark iterative loops as such). Also marks loop blocks
+ and sets bbWeight to the loop nesting levels
+ */
+
+ optOptimizeLoops();
+ EndPhase(PHASE_OPTIMIZE_LOOPS);
+
+ // Clone loops with optimization opportunities, and
+ // choose the one based on dynamic condition evaluation.
+ optCloneLoops();
+ EndPhase(PHASE_CLONE_LOOPS);
+
+ /* Unroll loops */
+ optUnrollLoops();
+ EndPhase(PHASE_UNROLL_LOOPS);
+ }
+
+#ifdef DEBUG
+ fgDebugCheckLinks();
+#endif
+
+ /* Create the variable table (and compute variable ref counts) */
+
+ lvaMarkLocalVars();
+ EndPhase(PHASE_MARK_LOCAL_VARS);
+
+ // IMPORTANT, after this point, every place where trees are modified or cloned
+ // the local variable reference counts must be updated
+ // You can test the value of the following variable to see if
+ // the local variable ref counts must be updated
+ //
+ assert(lvaLocalVarRefCounted == true);
+
+ if (!opts.MinOpts() && !opts.compDbgCode)
+ {
+ /* Optimize boolean conditions */
+
+ optOptimizeBools();
+ EndPhase(PHASE_OPTIMIZE_BOOLS);
+
+ // optOptimizeBools() might have changed the number of blocks; the dominators/reachability might be bad.
+ }
+
+ /* Figure out the order in which operators are to be evaluated */
+ fgFindOperOrder();
+ EndPhase(PHASE_FIND_OPER_ORDER);
+
+ // Weave the tree lists. Anyone who modifies the tree shapes after
+ // this point is responsible for calling fgSetStmtSeq() to keep the
+ // nodes properly linked.
+ // This can create GC poll calls, and create new BasicBlocks (without updating dominators/reachability).
+ fgSetBlockOrder();
+ EndPhase(PHASE_SET_BLOCK_ORDER);
+
+ // IMPORTANT, after this point, every place where tree topology changes must redo evaluation
+ // order (gtSetStmtInfo) and relink nodes (fgSetStmtSeq) if required.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef DEBUG
+ // Now we have determined the order of evaluation and the gtCosts for every node.
+ // If verbose, dump the full set of trees here before the optimization phases mutate them
+ //
+ if (verbose)
+ {
+ fgDispBasicBlocks(true); // 'true' will call fgDumpTrees() after dumping the BasicBlocks
+ printf("\n");
+ }
+#endif
+
+ // At this point we know if we are fully interruptible or not
+ if (!opts.MinOpts() && !opts.compDbgCode)
+ {
+ bool doSsa = true;
+ bool doEarlyProp = true;
+ bool doValueNum = true;
+ bool doLoopHoisting = true;
+ bool doCopyProp = true;
+ bool doAssertionProp = true;
+ bool doRangeAnalysis = true;
+
+#ifdef DEBUG
+ doSsa = (JitConfig.JitDoSsa() != 0);
+ doEarlyProp = doSsa && (JitConfig.JitDoEarlyProp() != 0);
+ doValueNum = doSsa && (JitConfig.JitDoValueNumber() != 0);
+ doLoopHoisting = doValueNum && (JitConfig.JitDoLoopHoisting() != 0);
+ doCopyProp = doValueNum && (JitConfig.JitDoCopyProp() != 0);
+ doAssertionProp = doValueNum && (JitConfig.JitDoAssertionProp() != 0);
+ doRangeAnalysis = doAssertionProp && (JitConfig.JitDoRangeAnalysis() != 0);
+#endif
+
+ if (doSsa)
+ {
+ fgSsaBuild();
+ EndPhase(PHASE_BUILD_SSA);
+ }
+
+ if (doEarlyProp)
+ {
+ /* Propagate array length and rewrite getType() method call */
+ optEarlyProp();
+ EndPhase(PHASE_EARLY_PROP);
+ }
+
+ if (doValueNum)
+ {
+ fgValueNumber();
+ EndPhase(PHASE_VALUE_NUMBER);
+ }
+
+ if (doLoopHoisting)
+ {
+ /* Hoist invariant code out of loops */
+ optHoistLoopCode();
+ EndPhase(PHASE_HOIST_LOOP_CODE);
+ }
+
+ if (doCopyProp)
+ {
+ /* Perform VN based copy propagation */
+ optVnCopyProp();
+ EndPhase(PHASE_VN_COPY_PROP);
+ }
+
+#if FEATURE_ANYCSE
+ /* Remove common sub-expressions */
+ optOptimizeCSEs();
+#endif // FEATURE_ANYCSE
+
+#if ASSERTION_PROP
+ if (doAssertionProp)
+ {
+ /* Assertion propagation */
+ optAssertionPropMain();
+ EndPhase(PHASE_ASSERTION_PROP_MAIN);
+ }
+
+ if (doRangeAnalysis)
+ {
+ /* Optimize array index range checks */
+ RangeCheck rc(this);
+ rc.OptimizeRangeChecks();
+ EndPhase(PHASE_OPTIMIZE_INDEX_CHECKS);
+ }
+#endif // ASSERTION_PROP
+
+ /* update the flowgraph if we modified it during the optimization phase*/
+ if (fgModified)
+ {
+ fgUpdateFlowGraph();
+ EndPhase(PHASE_UPDATE_FLOW_GRAPH);
+
+ // Recompute the edge weight if we have modified the flow graph
+ fgComputeEdgeWeights();
+ EndPhase(PHASE_COMPUTE_EDGE_WEIGHTS2);
+ }
+ }
+
+#ifdef _TARGET_AMD64_
+ // Check if we need to add the Quirk for the PPP backward compat issue
+ compQuirkForPPPflag = compQuirkForPPP();
+#endif
+
+ fgDetermineFirstColdBlock();
+ EndPhase(PHASE_DETERMINE_FIRST_COLD_BLOCK);
+
+#ifdef DEBUG
+ fgDebugCheckLinks(compStressCompile(STRESS_REMORPH_TREES, 50));
+
+ // Stash the current estimate of the function's size if necessary.
+ if (verbose)
+ {
+ compSizeEstimate = 0;
+ compCycleEstimate = 0;
+ for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->bbNext)
+ {
+ for (GenTreeStmt* stmt = block->firstStmt(); stmt != nullptr; stmt = stmt->getNextStmt())
+ {
+ compSizeEstimate += stmt->GetCostSz();
+ compCycleEstimate += stmt->GetCostEx();
+ }
+ }
+ }
+#endif
+
+#ifndef LEGACY_BACKEND
+ // rationalize trees
+ Rationalizer rat(this); // PHASE_RATIONALIZE
+ rat.Run();
+#endif // !LEGACY_BACKEND
+
+ // Here we do "simple lowering". When the RyuJIT backend works for all
+ // platforms, this will be part of the more general lowering phase. For now, though, we do a separate
+ // pass of "final lowering." We must do this before (final) liveness analysis, because this creates
+ // range check throw blocks, in which the liveness must be correct.
+ fgSimpleLowering();
+ EndPhase(PHASE_SIMPLE_LOWERING);
+
+#ifdef LEGACY_BACKEND
+ /* Local variable liveness */
+ fgLocalVarLiveness();
+ EndPhase(PHASE_LCLVARLIVENESS);
+#endif // !LEGACY_BACKEND
+
+#ifdef DEBUG
+ fgDebugCheckBBlist();
+ fgDebugCheckLinks();
+#endif
+
+ /* Enable this to gather statistical data such as
+ * call and register argument info, flowgraph and loop info, etc. */
+
+ compJitStats();
+
+#ifdef _TARGET_ARM_
+ if (compLocallocUsed)
+ {
+ // We reserve REG_SAVED_LOCALLOC_SP to store SP on entry for stack unwinding
+ codeGen->regSet.rsMaskResvd |= RBM_SAVED_LOCALLOC_SP;
+ }
+#endif // _TARGET_ARM_
+#ifdef _TARGET_ARMARCH_
+ if (compRsvdRegCheck(PRE_REGALLOC_FRAME_LAYOUT))
+ {
+ // We reserve R10/IP1 in this case to hold the offsets in load/store instructions
+ codeGen->regSet.rsMaskResvd |= RBM_OPT_RSVD;
+ assert(REG_OPT_RSVD != REG_FP);
+ }
+
+#ifdef DEBUG
+ //
+ // Display the pre-regalloc frame offsets that we have tentatively decided upon
+ //
+ if (verbose)
+ lvaTableDump();
+#endif
+#endif // _TARGET_ARMARCH_
+
+ /* Assign registers to variables, etc. */
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifndef LEGACY_BACKEND
+ ///////////////////////////////////////////////////////////////////////////////
+ // Dominator and reachability sets are no longer valid. They haven't been
+ // maintained up to here, and shouldn't be used (unless recomputed).
+ ///////////////////////////////////////////////////////////////////////////////
+ fgDomsComputed = false;
+
+ /* Create LSRA before Lowering, this way Lowering can initialize the TreeNode Map */
+ m_pLinearScan = getLinearScanAllocator(this);
+
+ /* Lower */
+ Lowering lower(this, m_pLinearScan); // PHASE_LOWERING
+ lower.Run();
+
+ assert(lvaSortAgain == false); // We should have re-run fgLocalVarLiveness() in lower.Run()
+ lvaTrackedFixed = true; // We can not add any new tracked variables after this point.
+
+ /* Now that lowering is completed we can proceed to perform register allocation */
+ m_pLinearScan->doLinearScan();
+ EndPhase(PHASE_LINEAR_SCAN);
+
+ // Copied from rpPredictRegUse()
+ genFullPtrRegMap = (codeGen->genInterruptible || !codeGen->isFramePointerUsed());
+#else // LEGACY_BACKEND
+
+ lvaTrackedFixed = true; // We cannot add any new tracked variables after this point.
+ // For the classic JIT32 at this point lvaSortAgain can be set and raAssignVars() will call lvaSortOnly()
+
+ // Now do "classic" register allocation.
+ raAssignVars();
+ EndPhase(PHASE_RA_ASSIGN_VARS);
+#endif // LEGACY_BACKEND
+
+#ifdef DEBUG
+ fgDebugCheckLinks();
+#endif
+
+ /* Generate code */
+
+ codeGen->genGenerateCode(methodCodePtr, methodCodeSize);
+
+#ifdef FEATURE_JIT_METHOD_PERF
+ if (pCompJitTimer)
+ pCompJitTimer->Terminate(this, CompTimeSummaryInfo::s_compTimeSummary);
+#endif
+
+ RecordStateAtEndOfCompilation();
+
+#ifdef FEATURE_TRACELOGGING
+ compJitTelemetry.NotifyEndOfCompilation();
+#endif
+
+#if defined(DEBUG)
+ ++Compiler::jitTotalMethodCompiled;
+#endif // defined(DEBUG)
+
+ compFunctionTraceEnd(*methodCodePtr, *methodCodeSize, false);
+
+#if FUNC_INFO_LOGGING
+ if (compJitFuncInfoFile != nullptr)
+ {
+ assert(!compIsForInlining());
+#ifdef DEBUG // We only have access to info.compFullName in DEBUG builds.
+ fprintf(compJitFuncInfoFile, "%s\n", info.compFullName);
+#elif FEATURE_SIMD
+ fprintf(compJitFuncInfoFile, " %s\n", eeGetMethodFullName(info.compMethodHnd));
+#endif
+ fprintf(compJitFuncInfoFile, ""); // in our logic this causes a flush
+ }
+#endif // FUNC_INFO_LOGGING
+}
+
+/*****************************************************************************/
+void Compiler::ProcessShutdownWork(ICorStaticInfo* statInfo)
+{
+}
+
+#ifdef _TARGET_AMD64_
+// Check if we need to add the Quirk for the PPP backward compat issue.
+// This Quirk addresses a compatibility issue between the new RyuJit and the previous JIT64.
+// A backward compatibity issue called 'PPP' exists where a PInvoke call passes a 32-byte struct
+// into a native API which basically writes 48 bytes of data into the struct.
+// With the stack frame layout used by the RyuJIT the extra 16 bytes written corrupts a
+// caller saved register and this leads to an A/V in the calling method.
+// The older JIT64 jit compiler just happened to have a different stack layout and/or
+// caller saved register set so that it didn't hit the A/V in the caller.
+// By increasing the amount of stack allocted for the struct by 32 bytes we can fix this.
+//
+// Return true if we actually perform the Quirk, otherwise return false
+//
+bool Compiler::compQuirkForPPP()
+{
+ if (lvaCount != 2)
+ { // We require that there are exactly two locals
+ return false;
+ }
+
+ if (compTailCallUsed)
+ { // Don't try this quirk if a tail call was used
+ return false;
+ }
+
+ bool hasOutArgs = false;
+ LclVarDsc* varDscExposedStruct = nullptr;
+
+ unsigned lclNum;
+ LclVarDsc* varDsc;
+
+ /* Look for struct locals that are address taken */
+ for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
+ {
+ if (varDsc->lvIsParam) // It can't be a parameter
+ {
+ continue;
+ }
+
+ // We require that the OutgoingArg space lclVar exists
+ if (lclNum == lvaOutgoingArgSpaceVar)
+ {
+ hasOutArgs = true; // Record that we saw it
+ continue;
+ }
+
+ // Look for a 32-byte address exposed Struct and record its varDsc
+ if ((varDsc->TypeGet() == TYP_STRUCT) && varDsc->lvAddrExposed && (varDsc->lvExactSize == 32))
+ {
+ varDscExposedStruct = varDsc;
+ }
+ }
+
+ // We only perform the Quirk when there are two locals
+ // one of them is a address exposed struct of size 32
+ // and the other is the outgoing arg space local
+ //
+ if (hasOutArgs && (varDscExposedStruct != nullptr))
+ {
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("\nAdding a backwards compatibility quirk for the 'PPP' issue\n");
+ }
+#endif // DEBUG
+
+ // Increase the exact size of this struct by 32 bytes
+ // This fixes the PPP backward compat issue
+ varDscExposedStruct->lvExactSize += 32;
+
+ return true;
+ }
+ return false;
+}
+#endif // _TARGET_AMD64_
+
+/*****************************************************************************/
+
+#ifdef DEBUG
+void* forceFrameJIT; // used to force to frame &useful for fastchecked debugging
+
+bool Compiler::skipMethod()
+{
+ static ConfigMethodRange fJitRange;
+ fJitRange.EnsureInit(JitConfig.JitRange());
+ assert(!fJitRange.Error());
+
+ // Normally JitConfig.JitRange() is null, we don't want to skip
+ // jitting any methods.
+ //
+ // So, the logic below relies on the fact that a null range string
+ // passed to ConfigMethodRange represents the set of all methods.
+
+ if (!fJitRange.Contains(info.compCompHnd, info.compMethodHnd))
+ {
+ return true;
+ }
+
+ if (JitConfig.JitExclude().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ return true;
+ }
+
+ if (!JitConfig.JitInclude().isEmpty() &&
+ !JitConfig.JitInclude().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+#endif
+
+/*****************************************************************************/
+
+int Compiler::compCompile(CORINFO_METHOD_HANDLE methodHnd,
+ CORINFO_MODULE_HANDLE classPtr,
+ COMP_HANDLE compHnd,
+ CORINFO_METHOD_INFO* methodInfo,
+ void** methodCodePtr,
+ ULONG* methodCodeSize,
+ CORJIT_FLAGS* compileFlags)
+{
+#ifdef FEATURE_JIT_METHOD_PERF
+ static bool checkedForJitTimeLog = false;
+
+ if (!checkedForJitTimeLog)
+ {
+ // Call into VM to get the config strings. FEATURE_JIT_METHOD_PERF is enabled for
+ // retail builds. Do not call the regular Config helper here as it would pull
+ // in a copy of the config parser into the clrjit.dll.
+ InterlockedCompareExchangeT(&Compiler::compJitTimeLogFilename, compHnd->getJitTimeLogFilename(), NULL);
+
+ // At a process or module boundary clear the file and start afresh.
+ JitTimer::PrintCsvHeader();
+
+ checkedForJitTimeLog = true;
+ }
+ if ((Compiler::compJitTimeLogFilename != NULL) || (JitTimeLogCsv() != NULL))
+ {
+ pCompJitTimer = JitTimer::Create(this, methodInfo->ILCodeSize);
+ }
+ else
+ {
+ pCompJitTimer = NULL;
+ }
+#endif // FEATURE_JIT_METHOD_PERF
+
+#ifdef DEBUG
+ Compiler* me = this;
+ forceFrameJIT = (void*)&me; // let us see the this pointer in fastchecked build
+ // set this early so we can use it without relying on random memory values
+ verbose = compIsForInlining() ? impInlineInfo->InlinerCompiler->verbose : false;
+
+ this->dumpIR = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIR : false;
+ this->dumpIRPhase = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRPhase : nullptr;
+ this->dumpIRFormat = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRFormat : nullptr;
+ this->dumpIRTypes = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRTypes : false;
+ this->dumpIRLocals = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRLocals : false;
+ this->dumpIRRegs = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRRegs : false;
+ this->dumpIRSsa = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRSsa : false;
+ this->dumpIRValnums = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRValnums : false;
+ this->dumpIRCosts = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRCosts : false;
+ this->dumpIRFlags = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRFlags : false;
+ this->dumpIRKinds = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRKinds : false;
+ this->dumpIRNodes = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRNodes : false;
+ this->dumpIRNoLists = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRNoLists : false;
+ this->dumpIRNoLeafs = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRNoLeafs : false;
+ this->dumpIRNoStmts = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRNoStmts : false;
+ this->dumpIRTrees = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRTrees : false;
+ this->dumpIRLinear = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRLinear : false;
+ this->dumpIRDataflow = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRDataflow : false;
+ this->dumpIRBlockHeaders = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRBlockHeaders : NULL;
+ this->dumpIRExit = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRExit : NULL;
+
+#endif
+
+#if defined(DEBUG) || defined(INLINE_DATA)
+ info.compMethodHashPrivate = 0;
+#endif // defined(DEBUG) || defined(INLINE_DATA)
+
+#if FUNC_INFO_LOGGING
+ LPCWSTR tmpJitFuncInfoFilename = JitConfig.JitFuncInfoFile();
+
+ if (tmpJitFuncInfoFilename != nullptr)
+ {
+ LPCWSTR oldFuncInfoFileName =
+ InterlockedCompareExchangeT(&compJitFuncInfoFilename, tmpJitFuncInfoFilename, NULL);
+ if (oldFuncInfoFileName == nullptr)
+ {
+ assert(compJitFuncInfoFile == nullptr);
+ compJitFuncInfoFile = _wfopen(compJitFuncInfoFilename, W("a"));
+ if (compJitFuncInfoFile == nullptr)
+ {
+#if defined(DEBUG) && !defined(FEATURE_PAL) // no 'perror' in the PAL
+ perror("Failed to open JitFuncInfoLogFile");
+#endif // defined(DEBUG) && !defined(FEATURE_PAL)
+ }
+ }
+ }
+#endif // FUNC_INFO_LOGGING
+
+ // if (s_compMethodsCount==0) setvbuf(jitstdout, NULL, _IONBF, 0);
+
+ info.compCompHnd = compHnd;
+ info.compMethodHnd = methodHnd;
+ info.compMethodInfo = methodInfo;
+
+ // Do we have a matched VM? Or are we "abusing" the VM to help us do JIT work (such as using an x86 native VM
+ // with an ARM-targeting "altjit").
+ info.compMatchedVM = IMAGE_FILE_MACHINE_TARGET == info.compCompHnd->getExpectedTargetArchitecture();
+
+#if defined(ALT_JIT) && defined(UNIX_AMD64_ABI)
+ // ToDo: This code is to allow us to run UNIX codegen on Windows for now. Remove when appropriate.
+ // Make sure that the generated UNIX altjit code is skipped on Windows. The static jit codegen is used to run.
+ info.compMatchedVM = false;
+#endif // UNIX_AMD64_ABI
+
+#if COR_JIT_EE_VERSION > 460
+ compMaxUncheckedOffsetForNullObject = eeGetEEInfo()->maxUncheckedOffsetForNullObject;
+#else // COR_JIT_EE_VERSION <= 460
+ compMaxUncheckedOffsetForNullObject = MAX_UNCHECKED_OFFSET_FOR_NULL_OBJECT;
+#endif // COR_JIT_EE_VERSION > 460
+
+ // Set the context for token lookup.
+ if (compIsForInlining())
+ {
+ impTokenLookupContextHandle = impInlineInfo->tokenLookupContextHandle;
+
+ assert(impInlineInfo->inlineCandidateInfo->clsHandle == compHnd->getMethodClass(methodHnd));
+ info.compClassHnd = impInlineInfo->inlineCandidateInfo->clsHandle;
+
+ assert(impInlineInfo->inlineCandidateInfo->clsAttr == info.compCompHnd->getClassAttribs(info.compClassHnd));
+ // printf("%x != %x\n", impInlineInfo->inlineCandidateInfo->clsAttr,
+ // info.compCompHnd->getClassAttribs(info.compClassHnd));
+ info.compClassAttr = impInlineInfo->inlineCandidateInfo->clsAttr;
+ }
+ else
+ {
+ impTokenLookupContextHandle = MAKE_METHODCONTEXT(info.compMethodHnd);
+
+ info.compClassHnd = compHnd->getMethodClass(methodHnd);
+ info.compClassAttr = info.compCompHnd->getClassAttribs(info.compClassHnd);
+ }
+
+ info.compProfilerCallback = false; // Assume false until we are told to hook this method.
+
+#if defined(DEBUG) || defined(LATE_DISASM)
+ const char* classNamePtr;
+
+ info.compMethodName = eeGetMethodName(methodHnd, &classNamePtr);
+ unsigned len = (unsigned)roundUp(strlen(classNamePtr) + 1);
+ info.compClassName = (char*)compGetMem(len, CMK_DebugOnly);
+ strcpy_s((char*)info.compClassName, len, classNamePtr);
+
+ info.compFullName = eeGetMethodFullName(methodHnd);
+#endif // defined(DEBUG) || defined(LATE_DISASM)
+
+#ifdef DEBUG
+ if (!compIsForInlining())
+ {
+ JitTls::GetLogEnv()->setCompiler(this);
+ }
+
+ // Have we been told to be more selective in our Jitting?
+ if (skipMethod())
+ {
+ if (compIsForInlining())
+ {
+ compInlineResult->NoteFatal(InlineObservation::CALLEE_MARKED_AS_SKIPPED);
+ }
+ return CORJIT_SKIPPED;
+ }
+
+ // Opt-in to jit stress based on method hash ranges.
+ //
+ // Note the default (with JitStressRange not set) is that all
+ // methods will be subject to stress.
+ static ConfigMethodRange fJitStressRange;
+ fJitStressRange.EnsureInit(JitConfig.JitStressRange());
+ assert(!fJitStressRange.Error());
+ bRangeAllowStress = fJitStressRange.Contains(info.compCompHnd, info.compMethodHnd);
+
+#endif // DEBUG
+
+ // Set this before the first 'BADCODE'
+ // Skip verification where possible
+ tiVerificationNeeded = (compileFlags->corJitFlags & CORJIT_FLG_SKIP_VERIFICATION) == 0;
+
+ assert(!compIsForInlining() || !tiVerificationNeeded); // Inlinees must have been verified.
+
+ // assume the code is verifiable unless proven otherwise
+ tiIsVerifiableCode = TRUE;
+
+ tiRuntimeCalloutNeeded = false;
+
+ CorInfoInstantiationVerification instVerInfo = INSTVER_GENERIC_PASSED_VERIFICATION;
+
+ if (!compIsForInlining() && tiVerificationNeeded)
+ {
+ instVerInfo = compHnd->isInstantiationOfVerifiedGeneric(methodHnd);
+
+ if (tiVerificationNeeded && (instVerInfo == INSTVER_GENERIC_FAILED_VERIFICATION))
+ {
+ CorInfoCanSkipVerificationResult canSkipVerificationResult =
+ info.compCompHnd->canSkipMethodVerification(info.compMethodHnd);
+
+ switch (canSkipVerificationResult)
+ {
+ case CORINFO_VERIFICATION_CANNOT_SKIP:
+ // We cannot verify concrete instantiation.
+ // We can only verify the typical/open instantiation
+ // The VM should throw a VerificationException instead of allowing this.
+ NO_WAY("Verification of closed instantiations is not supported");
+ break;
+
+ case CORINFO_VERIFICATION_CAN_SKIP:
+ // The VM should first verify the open instantiation. If unverifiable code
+ // is detected, it should pass in CORJIT_FLG_SKIP_VERIFICATION.
+ assert(!"The VM should have used CORJIT_FLG_SKIP_VERIFICATION");
+ tiVerificationNeeded = false;
+ break;
+
+ case CORINFO_VERIFICATION_RUNTIME_CHECK:
+ // This is a concrete generic instantiation with unverifiable code, that also
+ // needs a runtime callout.
+ tiVerificationNeeded = false;
+ tiRuntimeCalloutNeeded = true;
+ break;
+
+ case CORINFO_VERIFICATION_DONT_JIT:
+ // We cannot verify concrete instantiation.
+ // We can only verify the typical/open instantiation
+ // The VM should throw a VerificationException instead of allowing this.
+ BADCODE("NGEN of unverifiable transparent code is not supported");
+ break;
+ }
+ }
+
+ // load any constraints for verification, noting any cycles to be rejected by the verifying importer
+ if (tiVerificationNeeded)
+ {
+ compHnd->initConstraintsForVerification(methodHnd, &info.hasCircularClassConstraints,
+ &info.hasCircularMethodConstraints);
+ }
+ }
+
+ /* Setup an error trap */
+
+ struct Param
+ {
+ Compiler* pThis;
+
+ CORINFO_MODULE_HANDLE classPtr;
+ COMP_HANDLE compHnd;
+ CORINFO_METHOD_INFO* methodInfo;
+ void** methodCodePtr;
+ ULONG* methodCodeSize;
+ CORJIT_FLAGS* compileFlags;
+
+ CorInfoInstantiationVerification instVerInfo;
+ int result;
+ } param;
+ param.pThis = this;
+ param.classPtr = classPtr;
+ param.compHnd = compHnd;
+ param.methodInfo = methodInfo;
+ param.methodCodePtr = methodCodePtr;
+ param.methodCodeSize = methodCodeSize;
+ param.compileFlags = compileFlags;
+ param.instVerInfo = instVerInfo;
+ param.result = CORJIT_INTERNALERROR;
+
+ setErrorTrap(compHnd, Param*, pParam, &param) // ERROR TRAP: Start normal block
+ {
+ pParam->result = pParam->pThis->compCompileHelper(pParam->classPtr, pParam->compHnd, pParam->methodInfo,
+ pParam->methodCodePtr, pParam->methodCodeSize,
+ pParam->compileFlags, pParam->instVerInfo);
+ }
+ finallyErrorTrap() // ERROR TRAP: The following block handles errors
+ {
+ /* Cleanup */
+
+ if (compIsForInlining())
+ {
+ goto DoneCleanUp;
+ }
+
+ /* Tell the emitter that we're done with this function */
+
+ genEmitter->emitEndCG();
+
+ DoneCleanUp:
+ compDone();
+ }
+ endErrorTrap() // ERROR TRAP: End
+
+ return param.result;
+}
+
+#if defined(DEBUG) || defined(INLINE_DATA)
+unsigned Compiler::Info::compMethodHash() const
+{
+ if (compMethodHashPrivate == 0)
+ {
+ compMethodHashPrivate = compCompHnd->getMethodHash(compMethodHnd);
+ }
+ return compMethodHashPrivate;
+}
+#endif // defined(DEBUG) || defined(INLINE_DATA)
+
+void Compiler::compCompileFinish()
+{
+#if defined(DEBUG) || MEASURE_NODE_SIZE || MEASURE_BLOCK_SIZE || DISPLAY_SIZES || CALL_ARG_STATS
+ genMethodCnt++;
+#endif
+
+#if MEASURE_MEM_ALLOC
+ {
+ // Grab the relevant lock.
+ CritSecHolder statsLock(s_memStatsLock);
+
+ // Make the updates.
+ genMemStats.nraTotalSizeAlloc = compGetAllocator()->getTotalBytesAllocated();
+ genMemStats.nraTotalSizeUsed = compGetAllocator()->getTotalBytesUsed();
+ s_aggMemStats.Add(genMemStats);
+ if (genMemStats.allocSz > s_maxCompMemStats.allocSz)
+ {
+ s_maxCompMemStats = genMemStats;
+ }
+ }
+
+#ifdef DEBUG
+ if (s_dspMemStats || verbose)
+ {
+ printf("\nAllocations for %s (MethodHash=%08x)\n", info.compFullName, info.compMethodHash());
+ genMemStats.Print(jitstdout);
+ }
+#endif // DEBUG
+#endif // MEASURE_MEM_ALLOC
+
+#if LOOP_HOIST_STATS
+ AddLoopHoistStats();
+#endif // LOOP_HOIST_STATS
+
+#if MEASURE_NODE_SIZE
+ genTreeNcntHist.record(static_cast<unsigned>(genNodeSizeStatsPerFunc.genTreeNodeCnt));
+ genTreeNsizHist.record(static_cast<unsigned>(genNodeSizeStatsPerFunc.genTreeNodeSize));
+#endif
+
+#if defined(DEBUG)
+ // Small methods should fit in ArenaAllocator::getDefaultPageSize(), or else
+ // we should bump up ArenaAllocator::getDefaultPageSize()
+
+ if ((info.compILCodeSize <= 32) && // Is it a reasonably small method?
+ (info.compNativeCodeSize < 512) && // Some trivial methods generate huge native code. eg. pushing a single huge
+ // struct
+ (impInlinedCodeSize <= 128) && // Is the the inlining reasonably bounded?
+ // Small methods cannot meaningfully have a big number of locals
+ // or arguments. We always track arguments at the start of
+ // the prolog which requires memory
+ (info.compLocalsCount <= 32) && (!opts.MinOpts()) && // We may have too many local variables, etc
+ (getJitStressLevel() == 0) && // We need extra memory for stress
+ !compAllocator->bypassHostAllocator() && // ArenaAllocator::getDefaultPageSize() is artificially low for
+ // DirectAlloc
+ (compAllocator->getTotalBytesAllocated() > (2 * ArenaAllocator::getDefaultPageSize())) &&
+// Factor of 2x is because data-structures are bigger under DEBUG
+#ifndef LEGACY_BACKEND
+ // RyuJIT backend needs memory tuning! TODO-Cleanup: remove this case when memory tuning is complete.
+ (compAllocator->getTotalBytesAllocated() > (10 * ArenaAllocator::getDefaultPageSize())) &&
+#endif
+ !verbose) // We allocate lots of memory to convert sets to strings for JitDump
+ {
+ genSmallMethodsNeedingExtraMemoryCnt++;
+
+ // Less than 1% of all methods should run into this.
+ // We cannot be more strict as there are always degenerate cases where we
+ // would need extra memory (like huge structs as locals - see lvaSetStruct()).
+ assert((genMethodCnt < 500) || (genSmallMethodsNeedingExtraMemoryCnt < (genMethodCnt / 100)));
+ }
+#endif // DEBUG
+
+#if defined(DEBUG) || defined(INLINE_DATA)
+
+ m_inlineStrategy->DumpData();
+ m_inlineStrategy->DumpXml();
+
+#endif
+
+#ifdef DEBUG
+ if (opts.dspOrder)
+ {
+ // mdMethodDef __stdcall CEEInfo::getMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod)
+ mdMethodDef currentMethodToken = info.compCompHnd->getMethodDefFromMethod(info.compMethodHnd);
+
+ unsigned profCallCount = 0;
+ if (((opts.eeFlags & CORJIT_FLG_BBOPT) != 0) && fgHaveProfileData())
+ {
+ assert(fgProfileBuffer[0].ILOffset == 0);
+ profCallCount = fgProfileBuffer[0].ExecutionCount;
+ }
+
+ static bool headerPrinted = false;
+ if (!headerPrinted)
+ {
+ // clang-format off
+ headerPrinted = true;
+ printf(" | Profiled | Exec- | Method has | calls | Num |LclV |AProp| CSE | Reg |bytes | %3s code size | \n", Target::g_tgtCPUName);
+ printf(" mdToken | | RGN | Count | EH | FRM | LOOP | NRM | IND | BBs | Cnt | Cnt | Cnt | Alloc | IL | HOT | COLD | method name \n");
+ printf("---------+-----+------+----------+----+-----+------+-----+-----+-----+-----+-----+-----+---------+------+-------+-------+-----------\n");
+ // 06001234 | PRF | HOT | 219 | EH | ebp | LOOP | 15 | 6 | 12 | 17 | 12 | 8 | 28 p2 | 145 | 211 | 123 | System.Example(int)
+ // clang-format on
+ }
+
+ printf("%08X | ", currentMethodToken);
+
+ CorInfoRegionKind regionKind = info.compMethodInfo->regionKind;
+
+ if (opts.altJit)
+ {
+ printf("ALT | ");
+ }
+ else if (fgHaveProfileData())
+ {
+ printf("PRF | ");
+ }
+ else
+ {
+ printf(" | ");
+ }
+
+ if (regionKind == CORINFO_REGION_NONE)
+ {
+ printf(" | ");
+ }
+ else if (regionKind == CORINFO_REGION_HOT)
+ {
+ printf(" HOT | ");
+ }
+ else if (regionKind == CORINFO_REGION_COLD)
+ {
+ printf("COLD | ");
+ }
+ else if (regionKind == CORINFO_REGION_JIT)
+ {
+ printf(" JIT | ");
+ }
+ else
+ {
+ printf("UNKN | ");
+ }
+
+ printf("%8d | ", profCallCount);
+
+ if (compHndBBtabCount > 0)
+ {
+ printf("EH | ");
+ }
+ else
+ {
+ printf(" | ");
+ }
+
+ if (rpFrameType == FT_EBP_FRAME)
+ {
+ printf("%3s | ", STR_FPBASE);
+ }
+ else if (rpFrameType == FT_ESP_FRAME)
+ {
+ printf("%3s | ", STR_SPBASE);
+ }
+#if DOUBLE_ALIGN
+ else if (rpFrameType == FT_DOUBLE_ALIGN_FRAME)
+ {
+ printf("dbl | ");
+ }
+#endif
+ else // (rpFrameType == FT_NOT_SET)
+ {
+ printf("??? | ");
+ }
+
+ if (fgHasLoops)
+ {
+ printf("LOOP |");
+ }
+ else
+ {
+ printf(" |");
+ }
+
+ printf(" %3d |", optCallCount);
+ printf(" %3d |", optIndirectCallCount);
+ printf(" %3d |", fgBBcountAtCodegen);
+ printf(" %3d |", lvaCount);
+
+ if (opts.MinOpts())
+ {
+ printf(" MinOpts |");
+ }
+ else
+ {
+ printf(" %3d |", optAssertionCount);
+#if FEATURE_ANYCSE
+ printf(" %3d |", optCSEcount);
+#else
+ printf(" %3d |", 0);
+#endif // FEATURE_ANYCSE
+ }
+
+#ifndef LEGACY_BACKEND
+ printf(" LSRA |"); // TODO-Cleanup: dump some interesting LSRA stat into the order file?
+#else // LEGACY_BACKEND
+ printf("%s%4d p%1d |", (tmpCount > 0) ? "T" : " ", rpStkPredict / BB_UNITY_WEIGHT, rpPasses);
+#endif // LEGACY_BACKEND
+ printf(" %4d |", info.compMethodInfo->ILCodeSize);
+ printf(" %5d |", info.compTotalHotCodeSize);
+ printf(" %5d |", info.compTotalColdCodeSize);
+
+ printf(" %s\n", eeGetMethodFullName(info.compMethodHnd));
+ printf(""); // in our logic this causes a flush
+ }
+
+ if (verbose)
+ {
+ printf("****** DONE compiling %s\n", info.compFullName);
+ printf(""); // in our logic this causes a flush
+ }
+
+ // Only call _DbgBreakCheck when we are jitting, not when we are ngen-ing
+ // For ngen the int3 or breakpoint instruction will be right at the
+ // start of the ngen method and we will stop when we execute it.
+ //
+ if ((opts.eeFlags & CORJIT_FLG_PREJIT) == 0)
+ {
+ if (compJitHaltMethod())
+ {
+#if !defined(_TARGET_ARM64_) && !defined(PLATFORM_UNIX)
+ // TODO-ARM64-NYI: re-enable this when we have an OS that supports a pop-up dialog
+
+ // Don't do an assert, but just put up the dialog box so we get just-in-time debugger
+ // launching. When you hit 'retry' it will continue and naturally stop at the INT 3
+ // that the JIT put in the code
+ _DbgBreakCheck(__FILE__, __LINE__, "JitHalt");
+#endif
+ }
+ }
+#endif // DEBUG
+}
+
+#ifdef PSEUDORANDOM_NOP_INSERTION
+// this is zlib adler32 checksum. source came from windows base
+
+#define BASE 65521L // largest prime smaller than 65536
+#define NMAX 5552
+// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
+
+#define DO1(buf, i) \
+ { \
+ s1 += buf[i]; \
+ s2 += s1; \
+ }
+#define DO2(buf, i) \
+ DO1(buf, i); \
+ DO1(buf, i + 1);
+#define DO4(buf, i) \
+ DO2(buf, i); \
+ DO2(buf, i + 2);
+#define DO8(buf, i) \
+ DO4(buf, i); \
+ DO4(buf, i + 4);
+#define DO16(buf) \
+ DO8(buf, 0); \
+ DO8(buf, 8);
+
+unsigned adler32(unsigned adler, char* buf, unsigned int len)
+{
+ unsigned int s1 = adler & 0xffff;
+ unsigned int s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == NULL)
+ return 1L;
+
+ while (len > 0)
+ {
+ k = len < NMAX ? len : NMAX;
+ len -= k;
+ while (k >= 16)
+ {
+ DO16(buf);
+ buf += 16;
+ k -= 16;
+ }
+ if (k != 0)
+ do
+ {
+ s1 += *buf++;
+ s2 += s1;
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ return (s2 << 16) | s1;
+}
+#endif
+
+unsigned getMethodBodyChecksum(__in_z char* code, int size)
+{
+#ifdef PSEUDORANDOM_NOP_INSERTION
+ return adler32(0, code, size);
+#else
+ return 0;
+#endif
+}
+
+int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
+ COMP_HANDLE compHnd,
+ CORINFO_METHOD_INFO* methodInfo,
+ void** methodCodePtr,
+ ULONG* methodCodeSize,
+ CORJIT_FLAGS* compileFlags,
+ CorInfoInstantiationVerification instVerInfo)
+{
+ CORINFO_METHOD_HANDLE methodHnd = info.compMethodHnd;
+
+ info.compCode = methodInfo->ILCode;
+ info.compILCodeSize = methodInfo->ILCodeSize;
+
+ if (info.compILCodeSize == 0)
+ {
+ BADCODE("code size is zero");
+ }
+
+ if (compIsForInlining())
+ {
+#ifdef DEBUG
+ unsigned methAttr_Old = impInlineInfo->inlineCandidateInfo->methAttr;
+ unsigned methAttr_New = info.compCompHnd->getMethodAttribs(info.compMethodHnd);
+ unsigned flagsToIgnore = CORINFO_FLG_DONT_INLINE | CORINFO_FLG_FORCEINLINE;
+ assert((methAttr_Old & (~flagsToIgnore)) == (methAttr_New & (~flagsToIgnore)));
+#endif
+
+ info.compFlags = impInlineInfo->inlineCandidateInfo->methAttr;
+ }
+ else
+ {
+ info.compFlags = info.compCompHnd->getMethodAttribs(info.compMethodHnd);
+#ifdef PSEUDORANDOM_NOP_INSERTION
+ info.compChecksum = getMethodBodyChecksum((char*)methodInfo->ILCode, methodInfo->ILCodeSize);
+#endif
+ }
+
+ // compInitOptions will set the correct verbose flag.
+
+ compInitOptions(compileFlags);
+
+#ifdef ALT_JIT
+ if (!compIsForInlining() && !opts.altJit)
+ {
+ // We're an altjit, but the COMPlus_AltJit configuration did not say to compile this method,
+ // so skip it.
+ return CORJIT_SKIPPED;
+ }
+#endif // ALT_JIT
+
+#ifdef DEBUG
+
+ if (verbose)
+ {
+ printf("IL to import:\n");
+ dumpILRange(info.compCode, info.compILCodeSize);
+ }
+
+#endif
+
+ // Check for COMPlus_AgressiveInlining
+ if (JitConfig.JitAggressiveInlining())
+ {
+ compDoAggressiveInlining = true;
+ }
+
+ if (compDoAggressiveInlining)
+ {
+ info.compFlags |= CORINFO_FLG_FORCEINLINE;
+ }
+
+#ifdef DEBUG
+
+ // Check for ForceInline stress.
+ if (compStressCompile(STRESS_FORCE_INLINE, 0))
+ {
+ info.compFlags |= CORINFO_FLG_FORCEINLINE;
+ }
+
+ if (compIsForInlining())
+ {
+ JITLOG((LL_INFO100000, "\nINLINER impTokenLookupContextHandle for %s is 0x%p.\n",
+ eeGetMethodFullName(info.compMethodHnd), dspPtr(impTokenLookupContextHandle)));
+ }
+
+ // Force verification if asked to do so
+ if (JitConfig.JitForceVer())
+ {
+ tiVerificationNeeded = (instVerInfo == INSTVER_NOT_INSTANTIATION);
+ }
+
+ if (tiVerificationNeeded)
+ {
+ JITLOG((LL_INFO10000, "tiVerificationNeeded initially set to true for %s\n", info.compFullName));
+ }
+#endif // DEBUG
+
+ /* Since tiVerificationNeeded can be turned off in the middle of
+ compiling a method, and it might have caused blocks to be queued up
+ for reimporting, impCanReimport can be used to check for reimporting. */
+
+ impCanReimport = (tiVerificationNeeded || compStressCompile(STRESS_CHK_REIMPORT, 15));
+
+ // Need security prolog/epilog callouts when there is a declarative security in the method.
+ tiSecurityCalloutNeeded = ((info.compFlags & CORINFO_FLG_NOSECURITYWRAP) == 0);
+
+ if (tiSecurityCalloutNeeded || (info.compFlags & CORINFO_FLG_SECURITYCHECK))
+ {
+ // We need to allocate the security object on the stack
+ // 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.
+ opts.compNeedSecurityCheck = true;
+ }
+
+ /* Initialize set a bunch of global values */
+
+ info.compScopeHnd = classPtr;
+ info.compXcptnsCount = methodInfo->EHcount;
+ info.compMaxStack = methodInfo->maxStack;
+ compHndBBtab = nullptr;
+ compHndBBtabCount = 0;
+ compHndBBtabAllocCount = 0;
+
+ info.compNativeCodeSize = 0;
+ info.compTotalHotCodeSize = 0;
+ info.compTotalColdCodeSize = 0;
+
+#ifdef DEBUG
+ compCurBB = nullptr;
+ lvaTable = nullptr;
+
+ // Reset node ID counter
+ compGenTreeID = 0;
+#endif
+
+ /* Initialize emitter */
+
+ if (!compIsForInlining())
+ {
+ codeGen->getEmitter()->emitBegCG(this, compHnd);
+ }
+
+ info.compIsStatic = (info.compFlags & CORINFO_FLG_STATIC) != 0;
+
+ info.compIsContextful = (info.compClassAttr & CORINFO_FLG_CONTEXTFUL) != 0;
+
+ info.compPublishStubParam = (opts.eeFlags & CORJIT_FLG_PUBLISH_SECRET_PARAM) != 0;
+
+ switch (methodInfo->args.getCallConv())
+ {
+ case CORINFO_CALLCONV_VARARG:
+ case CORINFO_CALLCONV_NATIVEVARARG:
+ info.compIsVarArgs = true;
+ break;
+ case CORINFO_CALLCONV_DEFAULT:
+ info.compIsVarArgs = false;
+ break;
+ default:
+ BADCODE("bad calling convention");
+ }
+ info.compRetNativeType = info.compRetType = JITtype2varType(methodInfo->args.retType);
+
+ info.compCallUnmanaged = 0;
+ info.compLvFrameListRoot = BAD_VAR_NUM;
+
+#if FEATURE_FIXED_OUT_ARGS
+ lvaOutgoingArgSpaceSize = 0;
+#endif
+
+ lvaGenericsContextUsed = false;
+
+ info.compInitMem = ((methodInfo->options & CORINFO_OPT_INIT_LOCALS) != 0);
+
+ /* Allocate the local variable table */
+
+ lvaInitTypeRef();
+
+ if (!compIsForInlining())
+ {
+ compInitDebuggingInfo();
+ }
+
+ const bool forceInline = !!(info.compFlags & CORINFO_FLG_FORCEINLINE);
+
+ if (!compIsForInlining() && (opts.eeFlags & CORJIT_FLG_PREJIT))
+ {
+ // We're prejitting the root method. We also will analyze it as
+ // a potential inline candidate.
+ InlineResult prejitResult(this, methodHnd, "prejit");
+
+ // Do the initial inline screen.
+ impCanInlineIL(methodHnd, methodInfo, forceInline, &prejitResult);
+
+ // Temporarily install the prejitResult as the
+ // compInlineResult so it's available to fgFindJumpTargets
+ // and can accumulate more observations as the IL is
+ // scanned.
+ //
+ // We don't pass prejitResult in as a parameter to avoid
+ // potential aliasing confusion -- the other call to
+ // fgFindBasicBlocks may have set up compInlineResult and
+ // the code in fgFindJumpTargets references that data
+ // member extensively.
+ assert(compInlineResult == nullptr);
+ assert(impInlineInfo == nullptr);
+ compInlineResult = &prejitResult;
+
+ // Find the basic blocks. We must do this regardless of
+ // inlineability, since we are prejitting this method.
+ //
+ // This will also update the status of this method as
+ // an inline candidate.
+ fgFindBasicBlocks();
+
+ // Undo the temporary setup.
+ assert(compInlineResult == &prejitResult);
+ compInlineResult = nullptr;
+
+ // If still a viable, discretionary inline, assess
+ // profitability.
+ if (prejitResult.IsDiscretionaryCandidate())
+ {
+ prejitResult.DetermineProfitability(methodInfo);
+ }
+
+ // Handle the results of the inline analysis.
+ if (prejitResult.IsFailure())
+ {
+ // This method is a bad inlinee according to our
+ // analysis. We will let the InlineResult destructor
+ // mark it as noinline in the prejit image to save the
+ // jit some work.
+ //
+ // This decision better not be context-dependent.
+ assert(prejitResult.IsNever());
+ }
+ else
+ {
+ // This looks like a viable inline candidate. Since
+ // we're not actually inlining, don't report anything.
+ prejitResult.SetReported();
+ }
+ }
+ else
+ {
+ // We are jitting the root method, or inlining.
+ fgFindBasicBlocks();
+ }
+
+ // If we're inlining and the candidate is bad, bail out.
+ if (compDonotInline())
+ {
+ goto _Next;
+ }
+
+ compSetOptimizationLevel();
+
+#if COUNT_BASIC_BLOCKS
+ bbCntTable.record(fgBBcount);
+
+ if (fgBBcount == 1)
+ {
+ bbOneBBSizeTable.record(methodInfo->ILCodeSize);
+ }
+#endif // COUNT_BASIC_BLOCKS
+
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("Basic block list for '%s'\n", info.compFullName);
+ fgDispBasicBlocks();
+ }
+#endif
+
+#ifdef DEBUG
+ /* Give the function a unique number */
+
+ if (opts.disAsm || opts.dspEmit || verbose)
+ {
+ s_compMethodsCount = ~info.compMethodHash() & 0xffff;
+ }
+ else
+ {
+ s_compMethodsCount++;
+ }
+#endif
+
+ if (compIsForInlining())
+ {
+ compInlineResult->NoteInt(InlineObservation::CALLEE_NUMBER_OF_BASIC_BLOCKS, fgBBcount);
+
+ if (compInlineResult->IsFailure())
+ {
+ goto _Next;
+ }
+ }
+
+#ifdef DEBUG
+ if (JitConfig.DumpJittedMethods() == 1 && !compIsForInlining())
+ {
+ printf("Compiling %4d %s::%s, IL size = %u, hsh=0x%x\n", Compiler::jitTotalMethodCompiled, info.compClassName,
+ info.compMethodName, info.compILCodeSize, info.compMethodHash());
+ }
+ if (compIsForInlining())
+ {
+ compGenTreeID = impInlineInfo->InlinerCompiler->compGenTreeID;
+ }
+#endif
+
+ compCompile(methodCodePtr, methodCodeSize, compileFlags);
+
+#ifdef DEBUG
+ if (compIsForInlining())
+ {
+ impInlineInfo->InlinerCompiler->compGenTreeID = compGenTreeID;
+ }
+#endif
+
+_Next:
+
+ if (compDonotInline())
+ {
+ // Verify we have only one inline result in play.
+ assert(impInlineInfo->inlineResult == compInlineResult);
+ }
+
+ if (!compIsForInlining())
+ {
+ compCompileFinish();
+
+ // Did we just compile for a target architecture that the VM isn't expecting? If so, the VM
+ // can't used the generated code (and we better be an AltJit!).
+
+ if (!info.compMatchedVM)
+ {
+ return CORJIT_SKIPPED;
+ }
+
+#ifdef ALT_JIT
+#ifdef DEBUG
+ if (JitConfig.RunAltJitCode() == 0)
+ {
+ return CORJIT_SKIPPED;
+ }
+#endif // DEBUG
+#endif // ALT_JIT
+ }
+
+ /* Success! */
+ return CORJIT_OK;
+}
+
+/*****************************************************************************/
+#ifdef DEBUGGING_SUPPORT
+/*****************************************************************************/
+
+//------------------------------------------------------------------------
+// compFindLocalVarLinear: Linear search for variable's scope containing offset.
+//
+// Arguments:
+// varNum The variable number to search for in the array of scopes.
+// offs The offset value which should occur within the life of the variable.
+//
+// Return Value:
+// VarScopeDsc* of a matching variable that contains the offset within its life
+// begin and life end or nullptr when there is no match found.
+//
+// Description:
+// Linear search for matching variables with their life begin and end containing
+// the offset.
+// or NULL if one couldn't be found.
+//
+// Note:
+// Usually called for scope count = 4. Could be called for values upto 8.
+//
+VarScopeDsc* Compiler::compFindLocalVarLinear(unsigned varNum, unsigned offs)
+{
+ for (unsigned i = 0; i < info.compVarScopesCount; i++)
+ {
+ VarScopeDsc* dsc = &info.compVarScopes[i];
+ if ((dsc->vsdVarNum == varNum) && (dsc->vsdLifeBeg <= offs) && (dsc->vsdLifeEnd > offs))
+ {
+ return dsc;
+ }
+ }
+ return nullptr;
+}
+
+//------------------------------------------------------------------------
+// compFindLocalVar: Search for variable's scope containing offset.
+//
+// Arguments:
+// varNum The variable number to search for in the array of scopes.
+// offs The offset value which should occur within the life of the variable.
+//
+// Return Value:
+// VarScopeDsc* of a matching variable that contains the offset within its life
+// begin and life end.
+// or NULL if one couldn't be found.
+//
+// Description:
+// Linear search for matching variables with their life begin and end containing
+// the offset only when the scope count is < MAX_LINEAR_FIND_LCL_SCOPELIST,
+// else use the hashtable lookup.
+//
+VarScopeDsc* Compiler::compFindLocalVar(unsigned varNum, unsigned offs)
+{
+ if (info.compVarScopesCount < MAX_LINEAR_FIND_LCL_SCOPELIST)
+ {
+ return compFindLocalVarLinear(varNum, offs);
+ }
+ else
+ {
+ VarScopeDsc* ret = compFindLocalVar(varNum, offs, offs);
+ assert(ret == compFindLocalVarLinear(varNum, offs));
+ return ret;
+ }
+}
+
+//------------------------------------------------------------------------
+// compFindLocalVar: Search for variable's scope containing offset.
+//
+// Arguments:
+// varNum The variable number to search for in the array of scopes.
+// lifeBeg The life begin of the variable's scope
+// lifeEnd The life end of the variable's scope
+//
+// Return Value:
+// VarScopeDsc* of a matching variable that contains the offset within its life
+// begin and life end, or NULL if one couldn't be found.
+//
+// Description:
+// Following are the steps used:
+// 1. Index into the hashtable using varNum.
+// 2. Iterate through the linked list at index varNum to find a matching
+// var scope.
+//
+VarScopeDsc* Compiler::compFindLocalVar(unsigned varNum, unsigned lifeBeg, unsigned lifeEnd)
+{
+ assert(compVarScopeMap != nullptr);
+
+ VarScopeMapInfo* info;
+ if (compVarScopeMap->Lookup(varNum, &info))
+ {
+ VarScopeListNode* list = info->head;
+ while (list != nullptr)
+ {
+ if ((list->data->vsdLifeBeg <= lifeBeg) && (list->data->vsdLifeEnd > lifeEnd))
+ {
+ return list->data;
+ }
+ list = list->next;
+ }
+ }
+ return nullptr;
+}
+
+//-------------------------------------------------------------------------
+// compInitVarScopeMap: Create a scope map so it can be looked up by varNum
+//
+// Description:
+// Map.K => Map.V :: varNum => List(ScopeDsc)
+//
+// Create a scope map that can be indexed by varNum and can be iterated
+// on it's values to look for matching scope when given an offs or
+// lifeBeg and lifeEnd.
+//
+// Notes:
+// 1. Build the map only when we think linear search is slow, i.e.,
+// MAX_LINEAR_FIND_LCL_SCOPELIST is large.
+// 2. Linked list preserves original array order.
+//
+void Compiler::compInitVarScopeMap()
+{
+ if (info.compVarScopesCount < MAX_LINEAR_FIND_LCL_SCOPELIST)
+ {
+ return;
+ }
+
+ assert(compVarScopeMap == nullptr);
+
+ compVarScopeMap = new (getAllocator()) VarNumToScopeDscMap(getAllocator());
+
+ // 599 prime to limit huge allocations; for ex: duplicated scopes on single var.
+ compVarScopeMap->Reallocate(min(info.compVarScopesCount, 599));
+
+ for (unsigned i = 0; i < info.compVarScopesCount; ++i)
+ {
+ unsigned varNum = info.compVarScopes[i].vsdVarNum;
+
+ VarScopeListNode* node = VarScopeListNode::Create(&info.compVarScopes[i], getAllocator());
+
+ // Index by varNum and if the list exists append "node" to the "list".
+ VarScopeMapInfo* info;
+ if (compVarScopeMap->Lookup(varNum, &info))
+ {
+ info->tail->next = node;
+ info->tail = node;
+ }
+ // Create a new list.
+ else
+ {
+ info = VarScopeMapInfo::Create(node, getAllocator());
+ compVarScopeMap->Set(varNum, info);
+ }
+ }
+}
+
+static int __cdecl genCmpLocalVarLifeBeg(const void* elem1, const void* elem2)
+{
+ return (*((VarScopeDsc**)elem1))->vsdLifeBeg - (*((VarScopeDsc**)elem2))->vsdLifeBeg;
+}
+
+static int __cdecl genCmpLocalVarLifeEnd(const void* elem1, const void* elem2)
+{
+ return (*((VarScopeDsc**)elem1))->vsdLifeEnd - (*((VarScopeDsc**)elem2))->vsdLifeEnd;
+}
+
+inline void Compiler::compInitScopeLists()
+{
+ if (info.compVarScopesCount == 0)
+ {
+ compEnterScopeList = compExitScopeList = nullptr;
+ return;
+ }
+
+ // Populate the 'compEnterScopeList' and 'compExitScopeList' lists
+
+ compEnterScopeList = new (this, CMK_DebugInfo) VarScopeDsc*[info.compVarScopesCount];
+ compExitScopeList = new (this, CMK_DebugInfo) VarScopeDsc*[info.compVarScopesCount];
+
+ for (unsigned i = 0; i < info.compVarScopesCount; i++)
+ {
+ compEnterScopeList[i] = compExitScopeList[i] = &info.compVarScopes[i];
+ }
+
+ qsort(compEnterScopeList, info.compVarScopesCount, sizeof(*compEnterScopeList), genCmpLocalVarLifeBeg);
+ qsort(compExitScopeList, info.compVarScopesCount, sizeof(*compExitScopeList), genCmpLocalVarLifeEnd);
+}
+
+void Compiler::compResetScopeLists()
+{
+ if (info.compVarScopesCount == 0)
+ {
+ return;
+ }
+
+ assert(compEnterScopeList && compExitScopeList);
+
+ compNextEnterScope = compNextExitScope = 0;
+}
+
+VarScopeDsc* Compiler::compGetNextEnterScope(unsigned offs, bool scan)
+{
+ assert(info.compVarScopesCount);
+ assert(compEnterScopeList && compExitScopeList);
+
+ if (compNextEnterScope < info.compVarScopesCount)
+ {
+ assert(compEnterScopeList[compNextEnterScope]);
+ unsigned nextEnterOff = compEnterScopeList[compNextEnterScope]->vsdLifeBeg;
+ assert(scan || (offs <= nextEnterOff));
+
+ if (!scan)
+ {
+ if (offs == nextEnterOff)
+ {
+ return compEnterScopeList[compNextEnterScope++];
+ }
+ }
+ else
+ {
+ if (nextEnterOff <= offs)
+ {
+ return compEnterScopeList[compNextEnterScope++];
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+VarScopeDsc* Compiler::compGetNextExitScope(unsigned offs, bool scan)
+{
+ assert(info.compVarScopesCount);
+ assert(compEnterScopeList && compExitScopeList);
+
+ if (compNextExitScope < info.compVarScopesCount)
+ {
+ assert(compExitScopeList[compNextExitScope]);
+ unsigned nextExitOffs = compExitScopeList[compNextExitScope]->vsdLifeEnd;
+ assert(scan || (offs <= nextExitOffs));
+
+ if (!scan)
+ {
+ if (offs == nextExitOffs)
+ {
+ return compExitScopeList[compNextExitScope++];
+ }
+ }
+ else
+ {
+ if (nextExitOffs <= offs)
+ {
+ return compExitScopeList[compNextExitScope++];
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+// The function will call the callback functions for scopes with boundaries
+// at instrs from the current status of the scope lists to 'offset',
+// ordered by instrs.
+
+void Compiler::compProcessScopesUntil(unsigned offset,
+ VARSET_TP* inScope,
+ void (Compiler::*enterScopeFn)(VARSET_TP* inScope, VarScopeDsc*),
+ void (Compiler::*exitScopeFn)(VARSET_TP* inScope, VarScopeDsc*))
+{
+ assert(offset != BAD_IL_OFFSET);
+ assert(inScope != nullptr);
+
+ bool foundExit = false, foundEnter = true;
+ VarScopeDsc* scope;
+ VarScopeDsc* nextExitScope = nullptr;
+ VarScopeDsc* nextEnterScope = nullptr;
+ unsigned offs = offset, curEnterOffs = 0;
+
+ goto START_FINDING_SCOPES;
+
+ // We need to determine the scopes which are open for the current block.
+ // This loop walks over the missing blocks between the current and the
+ // previous block, keeping the enter and exit offsets in lockstep.
+
+ do
+ {
+ foundExit = foundEnter = false;
+
+ if (nextExitScope)
+ {
+ (this->*exitScopeFn)(inScope, nextExitScope);
+ nextExitScope = nullptr;
+ foundExit = true;
+ }
+
+ offs = nextEnterScope ? nextEnterScope->vsdLifeBeg : offset;
+
+ while ((scope = compGetNextExitScope(offs, true)) != nullptr)
+ {
+ foundExit = true;
+
+ if (!nextEnterScope || scope->vsdLifeEnd > nextEnterScope->vsdLifeBeg)
+ {
+ // We overshot the last found Enter scope. Save the scope for later
+ // and find an entering scope
+
+ nextExitScope = scope;
+ break;
+ }
+
+ (this->*exitScopeFn)(inScope, scope);
+ }
+
+ if (nextEnterScope)
+ {
+ (this->*enterScopeFn)(inScope, nextEnterScope);
+ curEnterOffs = nextEnterScope->vsdLifeBeg;
+ nextEnterScope = nullptr;
+ foundEnter = true;
+ }
+
+ offs = nextExitScope ? nextExitScope->vsdLifeEnd : offset;
+
+ START_FINDING_SCOPES:
+
+ while ((scope = compGetNextEnterScope(offs, true)) != nullptr)
+ {
+ foundEnter = true;
+
+ if ((nextExitScope && scope->vsdLifeBeg >= nextExitScope->vsdLifeEnd) || (scope->vsdLifeBeg > curEnterOffs))
+ {
+ // We overshot the last found exit scope. Save the scope for later
+ // and find an exiting scope
+
+ nextEnterScope = scope;
+ break;
+ }
+
+ (this->*enterScopeFn)(inScope, scope);
+
+ if (!nextExitScope)
+ {
+ curEnterOffs = scope->vsdLifeBeg;
+ }
+ }
+ } while (foundExit || foundEnter);
+}
+
+/*****************************************************************************/
+#endif // DEBUGGING_SUPPORT
+/*****************************************************************************/
+
+#if defined(DEBUGGING_SUPPORT) && defined(DEBUG)
+
+void Compiler::compDispScopeLists()
+{
+ unsigned i;
+
+ printf("Local variable scopes = %d\n", info.compVarScopesCount);
+
+ if (info.compVarScopesCount)
+ {
+ printf(" \tVarNum \tLVNum \t Name \tBeg \tEnd\n");
+ }
+
+ printf("Sorted by enter scope:\n");
+ for (i = 0; i < info.compVarScopesCount; i++)
+ {
+ VarScopeDsc* varScope = compEnterScopeList[i];
+ assert(varScope);
+ printf("%2d: \t%02Xh \t%02Xh \t%10s \t%03Xh \t%03Xh", i, varScope->vsdVarNum, varScope->vsdLVnum,
+ VarNameToStr(varScope->vsdName) == nullptr ? "UNKNOWN" : VarNameToStr(varScope->vsdName),
+ varScope->vsdLifeBeg, varScope->vsdLifeEnd);
+
+ if (compNextEnterScope == i)
+ {
+ printf(" <-- next enter scope");
+ }
+
+ printf("\n");
+ }
+
+ printf("Sorted by exit scope:\n");
+ for (i = 0; i < info.compVarScopesCount; i++)
+ {
+ VarScopeDsc* varScope = compExitScopeList[i];
+ assert(varScope);
+ printf("%2d: \t%02Xh \t%02Xh \t%10s \t%03Xh \t%03Xh", i, varScope->vsdVarNum, varScope->vsdLVnum,
+ VarNameToStr(varScope->vsdName) == nullptr ? "UNKNOWN" : VarNameToStr(varScope->vsdName),
+ varScope->vsdLifeBeg, varScope->vsdLifeEnd);
+
+ if (compNextExitScope == i)
+ {
+ printf(" <-- next exit scope");
+ }
+
+ printf("\n");
+ }
+}
+
+#endif
+
+#if defined(DEBUG)
+
+void Compiler::compDispLocalVars()
+{
+ printf("info.compVarScopesCount = %d\n", info.compVarScopesCount);
+
+ if (info.compVarScopesCount > 0)
+ {
+ printf(" \tVarNum \tLVNum \t Name \tBeg \tEnd\n");
+ }
+
+ for (unsigned i = 0; i < info.compVarScopesCount; i++)
+ {
+ VarScopeDsc* varScope = &info.compVarScopes[i];
+ printf("%2d: \t%02Xh \t%02Xh \t%10s \t%03Xh \t%03Xh\n", i, varScope->vsdVarNum, varScope->vsdLVnum,
+ VarNameToStr(varScope->vsdName) == nullptr ? "UNKNOWN" : VarNameToStr(varScope->vsdName),
+ varScope->vsdLifeBeg, varScope->vsdLifeEnd);
+ }
+}
+
+#endif
+
+/*****************************************************************************/
+
+// Compile a single method
+
+int jitNativeCode(CORINFO_METHOD_HANDLE methodHnd,
+ CORINFO_MODULE_HANDLE classPtr,
+ COMP_HANDLE compHnd,
+ CORINFO_METHOD_INFO* methodInfo,
+ void** methodCodePtr,
+ ULONG* methodCodeSize,
+ CORJIT_FLAGS* compileFlags,
+ void* inlineInfoPtr)
+{
+ //
+ // A non-NULL inlineInfo means we are compiling the inlinee method.
+ //
+ InlineInfo* inlineInfo = (InlineInfo*)inlineInfoPtr;
+
+ bool jitFallbackCompile = false;
+START:
+ int result = CORJIT_INTERNALERROR;
+
+ ArenaAllocator* pAlloc = nullptr;
+ ArenaAllocator alloc;
+
+ if (inlineInfo)
+ {
+ // Use inliner's memory allocator when compiling the inlinee.
+ pAlloc = inlineInfo->InlinerCompiler->compGetAllocator();
+ }
+ else
+ {
+ IEEMemoryManager* pMemoryManager = compHnd->getMemoryManager();
+
+ // Try to reuse the pre-inited allocator
+ pAlloc = ArenaAllocator::getPooledAllocator(pMemoryManager);
+
+ if (pAlloc == nullptr)
+ {
+ alloc = ArenaAllocator(pMemoryManager);
+ pAlloc = &alloc;
+ }
+ }
+
+ Compiler* pComp;
+ pComp = nullptr;
+
+ struct Param
+ {
+ Compiler* pComp;
+ ArenaAllocator* pAlloc;
+ ArenaAllocator* alloc;
+ bool jitFallbackCompile;
+
+ CORINFO_METHOD_HANDLE methodHnd;
+ CORINFO_MODULE_HANDLE classPtr;
+ COMP_HANDLE compHnd;
+ CORINFO_METHOD_INFO* methodInfo;
+ void** methodCodePtr;
+ ULONG* methodCodeSize;
+ CORJIT_FLAGS* compileFlags;
+ InlineInfo* inlineInfo;
+
+ int result;
+ } param;
+ param.pComp = nullptr;
+ param.pAlloc = pAlloc;
+ param.alloc = &alloc;
+ param.jitFallbackCompile = jitFallbackCompile;
+ param.methodHnd = methodHnd;
+ param.classPtr = classPtr;
+ param.compHnd = compHnd;
+ param.methodInfo = methodInfo;
+ param.methodCodePtr = methodCodePtr;
+ param.methodCodeSize = methodCodeSize;
+ param.compileFlags = compileFlags;
+ param.inlineInfo = inlineInfo;
+ param.result = result;
+
+ setErrorTrap(compHnd, Param*, pParamOuter, &param)
+ {
+ setErrorTrap(nullptr, Param*, pParam, pParamOuter)
+ {
+ if (pParam->inlineInfo)
+ {
+ // Lazily create the inlinee compiler object
+ if (pParam->inlineInfo->InlinerCompiler->InlineeCompiler == nullptr)
+ {
+ pParam->inlineInfo->InlinerCompiler->InlineeCompiler =
+ (Compiler*)pParam->pAlloc->allocateMemory(roundUp(sizeof(*pParam->pComp)));
+ }
+
+ // Use the inlinee compiler object
+ pParam->pComp = pParam->inlineInfo->InlinerCompiler->InlineeCompiler;
+#ifdef DEBUG
+// memset(pParam->pComp, 0xEE, sizeof(Compiler));
+#endif
+ }
+ else
+ {
+ // Allocate create the inliner compiler object
+ pParam->pComp = (Compiler*)pParam->pAlloc->allocateMemory(roundUp(sizeof(*pParam->pComp)));
+ }
+
+ // push this compiler on the stack (TLS)
+ pParam->pComp->prevCompiler = JitTls::GetCompiler();
+ JitTls::SetCompiler(pParam->pComp);
+
+// PREFIX_ASSUME gets turned into ASSERT_CHECK and we cannot have it here
+#if defined(_PREFAST_) || defined(_PREFIX_)
+ PREFIX_ASSUME(pParam->pComp != NULL);
+#else
+ assert(pParam->pComp != nullptr);
+#endif
+
+ pParam->pComp->compInit(pParam->pAlloc, pParam->inlineInfo);
+
+#ifdef DEBUG
+ pParam->pComp->jitFallbackCompile = pParam->jitFallbackCompile;
+#endif
+
+ // Now generate the code
+ pParam->result =
+ pParam->pComp->compCompile(pParam->methodHnd, pParam->classPtr, pParam->compHnd, pParam->methodInfo,
+ pParam->methodCodePtr, pParam->methodCodeSize, pParam->compileFlags);
+ }
+ finallyErrorTrap()
+ {
+ // Add a dummy touch to pComp so that it is kept alive, and is easy to get to
+ // during debugging since all other data can be obtained through it.
+ //
+ if (pParamOuter->pComp) // If OOM is thrown when allocating memory for pComp, we will end up here.
+ // In that case, pComp is still NULL.
+ {
+ pParamOuter->pComp->info.compCode = nullptr;
+
+ // pop the compiler off the TLS stack only if it was linked above
+ assert(JitTls::GetCompiler() == pParamOuter->pComp);
+ JitTls::SetCompiler(JitTls::GetCompiler()->prevCompiler);
+ }
+
+ if (pParamOuter->inlineInfo == nullptr)
+ {
+ // Free up the allocator we were using
+ pParamOuter->pAlloc->destroy();
+ }
+ }
+ endErrorTrap()
+ }
+ impJitErrorTrap()
+ {
+ // If we were looking at an inlinee....
+ if (inlineInfo != nullptr)
+ {
+ // Note that we failed to compile the inlinee, and that
+ // there's no point trying to inline it again anywhere else.
+ inlineInfo->inlineResult->NoteFatal(InlineObservation::CALLEE_COMPILATION_ERROR);
+ }
+ param.result = __errc;
+ }
+ endErrorTrap()
+
+ result = param.result;
+
+ if (!inlineInfo && (result == CORJIT_INTERNALERROR || result == CORJIT_RECOVERABLEERROR) && !jitFallbackCompile)
+ {
+ // If we failed the JIT, reattempt with debuggable code.
+ jitFallbackCompile = true;
+
+ // Update the flags for 'safer' code generation.
+ compileFlags->corJitFlags |= CORJIT_FLG_MIN_OPT;
+ compileFlags->corJitFlags &= ~(CORJIT_FLG_SIZE_OPT | CORJIT_FLG_SPEED_OPT);
+
+ goto START;
+ }
+
+ return result;
+}
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+
+// GetTypeFromClassificationAndSizes:
+// Returns the type of the eightbyte accounting for the classification and size of the eightbyte.
+//
+// args:
+// classType: classification type
+// size: size of the eightbyte.
+//
+// static
+var_types Compiler::GetTypeFromClassificationAndSizes(SystemVClassificationType classType, int size)
+{
+ var_types type = TYP_UNKNOWN;
+ switch (classType)
+ {
+ case SystemVClassificationTypeInteger:
+ if (size == 1)
+ {
+ type = TYP_BYTE;
+ }
+ else if (size <= 2)
+ {
+ type = TYP_SHORT;
+ }
+ else if (size <= 4)
+ {
+ type = TYP_INT;
+ }
+ else if (size <= 8)
+ {
+ type = TYP_LONG;
+ }
+ else
+ {
+ assert(false && "GetTypeFromClassificationAndSizes Invalid Integer classification type.");
+ }
+ break;
+ case SystemVClassificationTypeIntegerReference:
+ type = TYP_REF;
+ break;
+ case SystemVClassificationTypeIntegerByRef:
+ type = TYP_BYREF;
+ break;
+ case SystemVClassificationTypeSSE:
+ if (size <= 4)
+ {
+ type = TYP_FLOAT;
+ }
+ else if (size <= 8)
+ {
+ type = TYP_DOUBLE;
+ }
+ else
+ {
+ assert(false && "GetTypeFromClassificationAndSizes Invalid SSE classification type.");
+ }
+ break;
+
+ default:
+ assert(false && "GetTypeFromClassificationAndSizes Invalid classification type.");
+ break;
+ }
+
+ return type;
+}
+
+//-------------------------------------------------------------------
+// GetEightByteType: Returns the type of eightbyte slot of a struct
+//
+// Arguments:
+// structDesc - struct classification description.
+// slotNum - eightbyte slot number for the struct.
+//
+// Return Value:
+// type of the eightbyte slot of the struct
+//
+// static
+var_types Compiler::GetEightByteType(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc,
+ unsigned slotNum)
+{
+ var_types eightByteType = TYP_UNDEF;
+ unsigned len = structDesc.eightByteSizes[slotNum];
+
+ switch (structDesc.eightByteClassifications[slotNum])
+ {
+ case SystemVClassificationTypeInteger:
+ // See typelist.h for jit type definition.
+ // All the types of size < 4 bytes are of jit type TYP_INT.
+ if (structDesc.eightByteSizes[slotNum] <= 4)
+ {
+ eightByteType = TYP_INT;
+ }
+ else if (structDesc.eightByteSizes[slotNum] <= 8)
+ {
+ eightByteType = TYP_LONG;
+ }
+ else
+ {
+ assert(false && "GetEightByteType Invalid Integer classification type.");
+ }
+ break;
+ case SystemVClassificationTypeIntegerReference:
+ assert(len == REGSIZE_BYTES);
+ eightByteType = TYP_REF;
+ break;
+ case SystemVClassificationTypeIntegerByRef:
+ assert(len == REGSIZE_BYTES);
+ eightByteType = TYP_BYREF;
+ break;
+ case SystemVClassificationTypeSSE:
+ if (structDesc.eightByteSizes[slotNum] <= 4)
+ {
+ eightByteType = TYP_FLOAT;
+ }
+ else if (structDesc.eightByteSizes[slotNum] <= 8)
+ {
+ eightByteType = TYP_DOUBLE;
+ }
+ else
+ {
+ assert(false && "GetEightByteType Invalid SSE classification type.");
+ }
+ break;
+ default:
+ assert(false && "GetEightByteType Invalid classification type.");
+ break;
+ }
+
+ return eightByteType;
+}
+
+//------------------------------------------------------------------------------------------------------
+// GetStructTypeOffset: Gets the type, size and offset of the eightbytes of a struct for System V systems.
+//
+// Arguments:
+// 'structDesc' - struct description
+// 'type0' - out param; returns the type of the first eightbyte.
+// 'type1' - out param; returns the type of the second eightbyte.
+// 'offset0' - out param; returns the offset of the first eightbyte.
+// 'offset1' - out param; returns the offset of the second eightbyte.
+//
+// static
+void Compiler::GetStructTypeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc,
+ var_types* type0,
+ var_types* type1,
+ unsigned __int8* offset0,
+ unsigned __int8* offset1)
+{
+ *offset0 = structDesc.eightByteOffsets[0];
+ *offset1 = structDesc.eightByteOffsets[1];
+
+ *type0 = TYP_UNKNOWN;
+ *type1 = TYP_UNKNOWN;
+
+ // Set the first eightbyte data
+ if (structDesc.eightByteCount >= 1)
+ {
+ *type0 = GetEightByteType(structDesc, 0);
+ }
+
+ // Set the second eight byte data
+ if (structDesc.eightByteCount == 2)
+ {
+ *type1 = GetEightByteType(structDesc, 1);
+ }
+}
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+
+/*****************************************************************************/
+/*****************************************************************************/
+
+#ifdef DEBUG
+Compiler::NodeToIntMap* Compiler::FindReachableNodesInNodeTestData()
+{
+ NodeToIntMap* reachable = new (getAllocatorDebugOnly()) NodeToIntMap(getAllocatorDebugOnly());
+
+ if (m_nodeTestData == nullptr)
+ {
+ return reachable;
+ }
+
+ // Otherwise, iterate.
+
+ for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->bbNext)
+ {
+ for (GenTreePtr stmt = block->FirstNonPhiDef(); stmt != nullptr; stmt = stmt->gtNext)
+ {
+ for (GenTreePtr tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext)
+ {
+ TestLabelAndNum tlAndN;
+
+ // For call nodes, translate late args to what they stand for.
+ if (tree->OperGet() == GT_CALL)
+ {
+ GenTreeCall* call = tree->AsCall();
+ GenTreeArgList* args = call->gtCallArgs;
+ unsigned i = 0;
+ while (args != nullptr)
+ {
+ GenTreePtr arg = args->Current();
+ if (arg->gtFlags & GTF_LATE_ARG)
+ {
+ // Find the corresponding late arg.
+ GenTreePtr lateArg = nullptr;
+ for (unsigned j = 0; j < call->fgArgInfo->ArgCount(); j++)
+ {
+ if (call->fgArgInfo->ArgTable()[j]->argNum == i)
+ {
+ lateArg = call->fgArgInfo->ArgTable()[j]->node;
+ break;
+ }
+ }
+ assert(lateArg != nullptr);
+ if (GetNodeTestData()->Lookup(lateArg, &tlAndN))
+ {
+ reachable->Set(lateArg, 0);
+ }
+ }
+ i++;
+ args = args->Rest();
+ }
+ }
+
+ if (GetNodeTestData()->Lookup(tree, &tlAndN))
+ {
+ reachable->Set(tree, 0);
+ }
+ }
+ }
+ }
+ return reachable;
+}
+
+void Compiler::TransferTestDataToNode(GenTreePtr from, GenTreePtr to)
+{
+ TestLabelAndNum tlAndN;
+ // We can't currently associate multiple annotations with a single node.
+ // If we need to, we can fix this...
+
+ // If the table is null, don't create it just to do the lookup, which would fail...
+ if (m_nodeTestData != nullptr && GetNodeTestData()->Lookup(from, &tlAndN))
+ {
+ assert(!GetNodeTestData()->Lookup(to, &tlAndN));
+ // We can't currently associate multiple annotations with a single node.
+ // If we need to, we can fix this...
+ TestLabelAndNum tlAndNTo;
+ assert(!GetNodeTestData()->Lookup(to, &tlAndNTo));
+
+ GetNodeTestData()->Remove(from);
+ GetNodeTestData()->Set(to, tlAndN);
+ }
+}
+
+void Compiler::CopyTestDataToCloneTree(GenTreePtr from, GenTreePtr to)
+{
+ if (m_nodeTestData == nullptr)
+ {
+ return;
+ }
+ if (from == nullptr)
+ {
+ assert(to == nullptr);
+ return;
+ }
+ // Otherwise...
+ TestLabelAndNum tlAndN;
+ if (GetNodeTestData()->Lookup(from, &tlAndN))
+ {
+ // We can't currently associate multiple annotations with a single node.
+ // If we need to, we can fix this...
+ TestLabelAndNum tlAndNTo;
+ assert(!GetNodeTestData()->Lookup(to, &tlAndNTo));
+ GetNodeTestData()->Set(to, tlAndN);
+ }
+ // Now recurse, in parallel on both trees.
+
+ genTreeOps oper = from->OperGet();
+ unsigned kind = from->OperKind();
+ assert(oper == to->OperGet());
+
+ // Cconstant or leaf nodes have no children.
+ if (kind & (GTK_CONST | GTK_LEAF))
+ {
+ return;
+ }
+
+ // Otherwise, is it a 'simple' unary/binary operator?
+
+ if (kind & GTK_SMPOP)
+ {
+ if (from->gtOp.gtOp1 != nullptr)
+ {
+ assert(to->gtOp.gtOp1 != nullptr);
+ CopyTestDataToCloneTree(from->gtOp.gtOp1, to->gtOp.gtOp1);
+ }
+ else
+ {
+ assert(to->gtOp.gtOp1 == nullptr);
+ }
+
+ if (from->gtGetOp2() != nullptr)
+ {
+ assert(to->gtGetOp2() != nullptr);
+ CopyTestDataToCloneTree(from->gtGetOp2(), to->gtGetOp2());
+ }
+ else
+ {
+ assert(to->gtGetOp2() == nullptr);
+ }
+
+ return;
+ }
+
+ // Otherwise, see what kind of a special operator we have here.
+
+ switch (oper)
+ {
+ case GT_STMT:
+ CopyTestDataToCloneTree(from->gtStmt.gtStmtExpr, to->gtStmt.gtStmtExpr);
+ return;
+
+ case GT_CALL:
+ CopyTestDataToCloneTree(from->gtCall.gtCallObjp, to->gtCall.gtCallObjp);
+ CopyTestDataToCloneTree(from->gtCall.gtCallArgs, to->gtCall.gtCallArgs);
+ CopyTestDataToCloneTree(from->gtCall.gtCallLateArgs, to->gtCall.gtCallLateArgs);
+
+ if (from->gtCall.gtCallType == CT_INDIRECT)
+ {
+ CopyTestDataToCloneTree(from->gtCall.gtCallCookie, to->gtCall.gtCallCookie);
+ CopyTestDataToCloneTree(from->gtCall.gtCallAddr, to->gtCall.gtCallAddr);
+ }
+ // The other call types do not have additional GenTree arguments.
+
+ return;
+
+ case GT_FIELD:
+ CopyTestDataToCloneTree(from->gtField.gtFldObj, to->gtField.gtFldObj);
+ return;
+
+ case GT_ARR_ELEM:
+ assert(from->gtArrElem.gtArrRank == to->gtArrElem.gtArrRank);
+ for (unsigned dim = 0; dim < from->gtArrElem.gtArrRank; dim++)
+ {
+ CopyTestDataToCloneTree(from->gtArrElem.gtArrInds[dim], to->gtArrElem.gtArrInds[dim]);
+ }
+ CopyTestDataToCloneTree(from->gtArrElem.gtArrObj, to->gtArrElem.gtArrObj);
+ return;
+
+ case GT_CMPXCHG:
+ CopyTestDataToCloneTree(from->gtCmpXchg.gtOpLocation, to->gtCmpXchg.gtOpLocation);
+ CopyTestDataToCloneTree(from->gtCmpXchg.gtOpValue, to->gtCmpXchg.gtOpValue);
+ CopyTestDataToCloneTree(from->gtCmpXchg.gtOpComparand, to->gtCmpXchg.gtOpComparand);
+ return;
+
+ case GT_ARR_BOUNDS_CHECK:
+#ifdef FEATURE_SIMD
+ case GT_SIMD_CHK:
+#endif // FEATURE_SIMD
+ CopyTestDataToCloneTree(from->gtBoundsChk.gtArrLen, to->gtBoundsChk.gtArrLen);
+ CopyTestDataToCloneTree(from->gtBoundsChk.gtIndex, to->gtBoundsChk.gtIndex);
+ return;
+
+ default:
+ unreached();
+ }
+}
+
+#endif // DEBUG
+
+/*
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XX XX
+XX jvc XX
+XX XX
+XX Functions for the stand-alone version of the JIT . XX
+XX XX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
+
+/*****************************************************************************/
+void codeGeneratorCodeSizeBeg()
+{
+}
+/*****************************************************************************/
+
+/*****************************************************************************
+ *
+ * If any temporary tables are smaller than 'genMinSize2free' we won't bother
+ * freeing them.
+ */
+
+const size_t genMinSize2free = 64;
+
+/*****************************************************************************/
+
+/*****************************************************************************
+ *
+ * Used for counting pointer assignments.
+ */
+
+/*****************************************************************************/
+void codeGeneratorCodeSizeEnd()
+{
+}
+/*****************************************************************************
+ *
+ * Gather statistics - mainly used for the standalone
+ * Enable various #ifdef's to get the information you need
+ */
+
+void Compiler::compJitStats()
+{
+#if CALL_ARG_STATS
+
+ /* Method types and argument statistics */
+ compCallArgStats();
+#endif // CALL_ARG_STATS
+}
+
+#if CALL_ARG_STATS
+
+/*****************************************************************************
+ *
+ * Gather statistics about method calls and arguments
+ */
+
+void Compiler::compCallArgStats()
+{
+ GenTreePtr args;
+ GenTreePtr argx;
+
+ BasicBlock* block;
+ GenTreePtr stmt;
+ GenTreePtr call;
+
+ unsigned argNum;
+
+ unsigned argDWordNum;
+ unsigned argLngNum;
+ unsigned argFltNum;
+ unsigned argDblNum;
+
+ unsigned regArgNum;
+ unsigned regArgDeferred;
+ unsigned regArgTemp;
+
+ unsigned regArgLclVar;
+ unsigned regArgConst;
+
+ unsigned argTempsThisMethod = 0;
+
+ assert(fgStmtListThreaded);
+
+ for (block = fgFirstBB; block; block = block->bbNext)
+ {
+ for (stmt = block->bbTreeList; stmt; stmt = stmt->gtNext)
+ {
+ assert(stmt->gtOper == GT_STMT);
+
+ for (call = stmt->gtStmt.gtStmtList; call; call = call->gtNext)
+ {
+ if (call->gtOper != GT_CALL)
+ continue;
+
+ argNum =
+
+ regArgNum = regArgDeferred = regArgTemp =
+
+ regArgConst = regArgLclVar =
+
+ argDWordNum = argLngNum = argFltNum = argDblNum = 0;
+
+ argTotalCalls++;
+
+ if (!call->gtCall.gtCallObjp)
+ {
+ if (call->gtCall.gtCallType == CT_HELPER)
+ {
+ argHelperCalls++;
+ }
+ else
+ {
+ argStaticCalls++;
+ }
+ }
+ else
+ {
+ /* We have a 'this' pointer */
+
+ argDWordNum++;
+ argNum++;
+ regArgNum++;
+ regArgDeferred++;
+ argTotalObjPtr++;
+
+ if (call->gtFlags & (GTF_CALL_VIRT_VTABLE | GTF_CALL_VIRT_STUB))
+ {
+ /* virtual function */
+ argVirtualCalls++;
+ }
+ else
+ {
+ argNonVirtualCalls++;
+ }
+ }
+
+#ifdef LEGACY_BACKEND
+ // TODO-Cleaenup: We need to add support below for additional node types that RyuJIT backend has in the
+ // IR.
+ // Gather arguments information.
+
+ for (args = call->gtCall.gtCallArgs; args; args = args->gtOp.gtOp2)
+ {
+ argx = args->gtOp.gtOp1;
+
+ argNum++;
+
+ switch (genActualType(argx->TypeGet()))
+ {
+ case TYP_INT:
+ case TYP_REF:
+ case TYP_BYREF:
+ argDWordNum++;
+ break;
+
+ case TYP_LONG:
+ argLngNum++;
+ break;
+
+ case TYP_FLOAT:
+ argFltNum++;
+ break;
+
+ case TYP_DOUBLE:
+ argDblNum++;
+ break;
+
+ case TYP_VOID:
+ /* This is a deferred register argument */
+ assert(argx->gtOper == GT_NOP);
+ assert(argx->gtFlags & GTF_LATE_ARG);
+ argDWordNum++;
+ break;
+ }
+
+ /* Is this argument a register argument? */
+
+ if (argx->gtFlags & GTF_LATE_ARG)
+ {
+ regArgNum++;
+
+ /* We either have a deferred argument or a temp */
+
+ if (argx->gtOper == GT_NOP)
+ {
+ regArgDeferred++;
+ }
+ else
+ {
+ assert(argx->gtOper == GT_ASG);
+ regArgTemp++;
+ }
+ }
+ }
+
+ /* Look at the register arguments and count how many constants, local vars */
+
+ for (args = call->gtCall.gtCallLateArgs; args; args = args->gtOp.gtOp2)
+ {
+ argx = args->gtOp.gtOp1;
+
+ switch (argx->gtOper)
+ {
+ case GT_CNS_INT:
+ regArgConst++;
+ break;
+
+ case GT_LCL_VAR:
+ regArgLclVar++;
+ break;
+ }
+ }
+
+ assert(argNum == argDWordNum + argLngNum + argFltNum + argDblNum);
+ assert(regArgNum == regArgDeferred + regArgTemp);
+
+ argTotalArgs += argNum;
+ argTotalRegArgs += regArgNum;
+
+ argTotalDWordArgs += argDWordNum;
+ argTotalLongArgs += argLngNum;
+ argTotalFloatArgs += argFltNum;
+ argTotalDoubleArgs += argDblNum;
+
+ argTotalDeferred += regArgDeferred;
+ argTotalTemps += regArgTemp;
+ argTotalConst += regArgConst;
+ argTotalLclVar += regArgLclVar;
+
+ argTempsThisMethod += regArgTemp;
+
+ argCntTable.record(argNum);
+ argDWordCntTable.record(argDWordNum);
+ argDWordLngCntTable.record(argDWordNum + (2 * argLngNum));
+#endif // LEGACY_BACKEND
+ }
+ }
+ }
+
+ argTempsCntTable.record(argTempsThisMethod);
+
+ if (argMaxTempsPerMethod < argTempsThisMethod)
+ {
+ argMaxTempsPerMethod = argTempsThisMethod;
+ }
+}
+
+/* static */
+void Compiler::compDispCallArgStats(FILE* fout)
+{
+ if (argTotalCalls == 0)
+ return;
+
+ fprintf(fout, "\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "Call stats\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "Total # of calls = %d, calls / method = %.3f\n\n", argTotalCalls,
+ (float)argTotalCalls / genMethodCnt);
+
+ fprintf(fout, "Percentage of helper calls = %4.2f %%\n", (float)(100 * argHelperCalls) / argTotalCalls);
+ fprintf(fout, "Percentage of static calls = %4.2f %%\n", (float)(100 * argStaticCalls) / argTotalCalls);
+ fprintf(fout, "Percentage of virtual calls = %4.2f %%\n", (float)(100 * argVirtualCalls) / argTotalCalls);
+ fprintf(fout, "Percentage of non-virtual calls = %4.2f %%\n\n", (float)(100 * argNonVirtualCalls) / argTotalCalls);
+
+ fprintf(fout, "Average # of arguments per call = %.2f%%\n\n", (float)argTotalArgs / argTotalCalls);
+
+ fprintf(fout, "Percentage of DWORD arguments = %.2f %%\n", (float)(100 * argTotalDWordArgs) / argTotalArgs);
+ fprintf(fout, "Percentage of LONG arguments = %.2f %%\n", (float)(100 * argTotalLongArgs) / argTotalArgs);
+ fprintf(fout, "Percentage of FLOAT arguments = %.2f %%\n", (float)(100 * argTotalFloatArgs) / argTotalArgs);
+ fprintf(fout, "Percentage of DOUBLE arguments = %.2f %%\n\n", (float)(100 * argTotalDoubleArgs) / argTotalArgs);
+
+ if (argTotalRegArgs == 0)
+ return;
+
+ /*
+ fprintf(fout, "Total deferred arguments = %d \n", argTotalDeferred);
+
+ fprintf(fout, "Total temp arguments = %d \n\n", argTotalTemps);
+
+ fprintf(fout, "Total 'this' arguments = %d \n", argTotalObjPtr);
+ fprintf(fout, "Total local var arguments = %d \n", argTotalLclVar);
+ fprintf(fout, "Total constant arguments = %d \n\n", argTotalConst);
+ */
+
+ fprintf(fout, "\nRegister Arguments:\n\n");
+
+ fprintf(fout, "Percentage of deferred arguments = %.2f %%\n", (float)(100 * argTotalDeferred) / argTotalRegArgs);
+ fprintf(fout, "Percentage of temp arguments = %.2f %%\n\n", (float)(100 * argTotalTemps) / argTotalRegArgs);
+
+ fprintf(fout, "Maximum # of temps per method = %d\n\n", argMaxTempsPerMethod);
+
+ fprintf(fout, "Percentage of ObjPtr arguments = %.2f %%\n", (float)(100 * argTotalObjPtr) / argTotalRegArgs);
+ // fprintf(fout, "Percentage of global arguments = %.2f %%\n", (float)(100 * argTotalDWordGlobEf) /
+ // argTotalRegArgs);
+ fprintf(fout, "Percentage of constant arguments = %.2f %%\n", (float)(100 * argTotalConst) / argTotalRegArgs);
+ fprintf(fout, "Percentage of lcl var arguments = %.2f %%\n\n", (float)(100 * argTotalLclVar) / argTotalRegArgs);
+
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "Argument count frequency table (includes ObjPtr):\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ argCntTable.dump(fout);
+ fprintf(fout, "--------------------------------------------------\n");
+
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "DWORD argument count frequency table (w/o LONG):\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ argDWordCntTable.dump(fout);
+ fprintf(fout, "--------------------------------------------------\n");
+
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "Temps count frequency table (per method):\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ argTempsCntTable.dump(fout);
+ fprintf(fout, "--------------------------------------------------\n");
+
+ /*
+ fprintf(fout, "--------------------------------------------------\n");
+ fprintf(fout, "DWORD argument count frequency table (w/ LONG):\n");
+ fprintf(fout, "--------------------------------------------------\n");
+ argDWordLngCntTable.dump(fout);
+ fprintf(fout, "--------------------------------------------------\n");
+ */
+}
+
+#endif // CALL_ARG_STATS
+
+// JIT time end to end, and by phases.
+
+#ifdef FEATURE_JIT_METHOD_PERF
+// Static variables
+CritSecObject CompTimeSummaryInfo::s_compTimeSummaryLock;
+CompTimeSummaryInfo CompTimeSummaryInfo::s_compTimeSummary;
+#endif // FEATURE_JIT_METHOD_PERF
+
+#if defined(FEATURE_JIT_METHOD_PERF) || DUMP_FLOWGRAPHS
+const char* PhaseNames[] = {
+#define CompPhaseNameMacro(enum_nm, string_nm, short_nm, hasChildren, parent) string_nm,
+#include "compphases.h"
+};
+
+const char* PhaseEnums[] = {
+#define CompPhaseNameMacro(enum_nm, string_nm, short_nm, hasChildren, parent) #enum_nm,
+#include "compphases.h"
+};
+
+const LPCWSTR PhaseShortNames[] = {
+#define CompPhaseNameMacro(enum_nm, string_nm, short_nm, hasChildren, parent) W(short_nm),
+#include "compphases.h"
+};
+#endif // defined(FEATURE_JIT_METHOD_PERF) || DUMP_FLOWGRAPHS
+
+#ifdef FEATURE_JIT_METHOD_PERF
+bool PhaseHasChildren[] = {
+#define CompPhaseNameMacro(enum_nm, string_nm, short_nm, hasChildren, parent) hasChildren,
+#include "compphases.h"
+};
+
+int PhaseParent[] = {
+#define CompPhaseNameMacro(enum_nm, string_nm, short_nm, hasChildren, parent) parent,
+#include "compphases.h"
+};
+
+CompTimeInfo::CompTimeInfo(unsigned byteCodeBytes)
+ : m_byteCodeBytes(byteCodeBytes), m_totalCycles(0), m_parentPhaseEndSlop(0), m_timerFailure(false)
+{
+ for (int i = 0; i < PHASE_NUMBER_OF; i++)
+ {
+ m_invokesByPhase[i] = 0;
+ m_cyclesByPhase[i] = 0;
+ }
+}
+
+bool CompTimeSummaryInfo::IncludedInFilteredData(CompTimeInfo& info)
+{
+ return false; // info.m_byteCodeBytes < 10;
+}
+
+void CompTimeSummaryInfo::AddInfo(CompTimeInfo& info)
+{
+ if (info.m_timerFailure)
+ return; // Don't update if there was a failure.
+
+ CritSecHolder timeLock(s_compTimeSummaryLock);
+ m_numMethods++;
+
+ bool includeInFiltered = IncludedInFilteredData(info);
+
+ // Update the totals and maxima.
+ m_total.m_byteCodeBytes += info.m_byteCodeBytes;
+ m_maximum.m_byteCodeBytes = max(m_maximum.m_byteCodeBytes, info.m_byteCodeBytes);
+ m_total.m_totalCycles += info.m_totalCycles;
+ m_maximum.m_totalCycles = max(m_maximum.m_totalCycles, info.m_totalCycles);
+
+ if (includeInFiltered)
+ {
+ m_numFilteredMethods++;
+ m_filtered.m_byteCodeBytes += info.m_byteCodeBytes;
+ m_filtered.m_totalCycles += info.m_totalCycles;
+ m_filtered.m_parentPhaseEndSlop += info.m_parentPhaseEndSlop;
+ }
+
+ for (int i = 0; i < PHASE_NUMBER_OF; i++)
+ {
+ m_total.m_invokesByPhase[i] += info.m_invokesByPhase[i];
+ m_total.m_cyclesByPhase[i] += info.m_cyclesByPhase[i];
+ if (includeInFiltered)
+ {
+ m_filtered.m_invokesByPhase[i] += info.m_invokesByPhase[i];
+ m_filtered.m_cyclesByPhase[i] += info.m_cyclesByPhase[i];
+ }
+ m_maximum.m_cyclesByPhase[i] = max(m_maximum.m_cyclesByPhase[i], info.m_cyclesByPhase[i]);
+ }
+ m_total.m_parentPhaseEndSlop += info.m_parentPhaseEndSlop;
+ m_maximum.m_parentPhaseEndSlop = max(m_maximum.m_parentPhaseEndSlop, info.m_parentPhaseEndSlop);
+}
+
+// Static
+LPCWSTR Compiler::compJitTimeLogFilename = NULL;
+
+void CompTimeSummaryInfo::Print(FILE* f)
+{
+ if (f == NULL)
+ return;
+ // Otherwise...
+ double countsPerSec = CycleTimer::CyclesPerSecond();
+ if (countsPerSec == 0.0)
+ {
+ fprintf(f, "Processor does not have a high-frequency timer.\n");
+ return;
+ }
+
+ fprintf(f, "JIT Compilation time report:\n");
+ fprintf(f, " Compiled %d methods.\n", m_numMethods);
+ if (m_numMethods != 0)
+ {
+ fprintf(f, " Compiled %d bytecodes total (%d max, %8.2f avg).\n", m_total.m_byteCodeBytes,
+ m_maximum.m_byteCodeBytes, (double)m_total.m_byteCodeBytes / (double)m_numMethods);
+ double totTime_ms = ((double)m_total.m_totalCycles / countsPerSec) * 1000.0;
+ fprintf(f, " Time: total: %10.3f Mcycles/%10.3f ms\n", ((double)m_total.m_totalCycles / 1000000.0),
+ totTime_ms);
+ fprintf(f, " max: %10.3f Mcycles/%10.3f ms\n", ((double)m_maximum.m_totalCycles) / 1000000.0,
+ ((double)m_maximum.m_totalCycles / countsPerSec) * 1000.0);
+ fprintf(f, " avg: %10.3f Mcycles/%10.3f ms\n",
+ ((double)m_total.m_totalCycles) / 1000000.0 / (double)m_numMethods, totTime_ms / (double)m_numMethods);
+
+ fprintf(f, " Total time by phases:\n");
+ fprintf(f, " PHASE inv/meth Mcycles time (ms) %% of total max (ms)\n");
+ fprintf(f, " --------------------------------------------------------------------------------------\n");
+ // Ensure that at least the names array and the Phases enum have the same number of entries:
+ assert(sizeof(PhaseNames) / sizeof(const char*) == PHASE_NUMBER_OF);
+ for (int i = 0; i < PHASE_NUMBER_OF; i++)
+ {
+ double phase_tot_ms = (((double)m_total.m_cyclesByPhase[i]) / countsPerSec) * 1000.0;
+ double phase_max_ms = (((double)m_maximum.m_cyclesByPhase[i]) / countsPerSec) * 1000.0;
+ // Indent nested phases, according to depth.
+ int ancPhase = PhaseParent[i];
+ while (ancPhase != -1)
+ {
+ fprintf(f, " ");
+ ancPhase = PhaseParent[ancPhase];
+ }
+ fprintf(f, " %-30s %5.2f %10.2f %9.3f %8.2f%% %8.3f\n", PhaseNames[i],
+ ((double)m_total.m_invokesByPhase[i]) / ((double)m_numMethods),
+ ((double)m_total.m_cyclesByPhase[i]) / 1000000.0, phase_tot_ms, (phase_tot_ms * 100.0 / totTime_ms),
+ phase_max_ms);
+ }
+ fprintf(f, "\n 'End phase slop' should be very small (if not, there's unattributed time): %9.3f Mcycles.\n",
+ m_total.m_parentPhaseEndSlop);
+ }
+ if (m_numFilteredMethods > 0)
+ {
+ fprintf(f, " Compiled %d methods that meet the filter requirement.\n", m_numFilteredMethods);
+ fprintf(f, " Compiled %d bytecodes total (%8.2f avg).\n", m_filtered.m_byteCodeBytes,
+ (double)m_filtered.m_byteCodeBytes / (double)m_numFilteredMethods);
+ double totTime_ms = ((double)m_filtered.m_totalCycles / countsPerSec) * 1000.0;
+ fprintf(f, " Time: total: %10.3f Mcycles/%10.3f ms\n", ((double)m_filtered.m_totalCycles / 1000000.0),
+ totTime_ms);
+ fprintf(f, " avg: %10.3f Mcycles/%10.3f ms\n",
+ ((double)m_filtered.m_totalCycles) / 1000000.0 / (double)m_numFilteredMethods,
+ totTime_ms / (double)m_numFilteredMethods);
+
+ fprintf(f, " Total time by phases:\n");
+ fprintf(f, " PHASE inv/meth Mcycles time (ms) %% of total\n");
+ fprintf(f, " --------------------------------------------------------------------------------------\n");
+ // Ensure that at least the names array and the Phases enum have the same number of entries:
+ assert(sizeof(PhaseNames) / sizeof(const char*) == PHASE_NUMBER_OF);
+ for (int i = 0; i < PHASE_NUMBER_OF; i++)
+ {
+ double phase_tot_ms = (((double)m_filtered.m_cyclesByPhase[i]) / countsPerSec) * 1000.0;
+ // Indent nested phases, according to depth.
+ int ancPhase = PhaseParent[i];
+ while (ancPhase != -1)
+ {
+ fprintf(f, " ");
+ ancPhase = PhaseParent[ancPhase];
+ }
+ fprintf(f, " %-30s %5.2f %10.2f %9.3f %8.2f%%\n", PhaseNames[i],
+ ((double)m_filtered.m_invokesByPhase[i]) / ((double)m_numFilteredMethods),
+ ((double)m_filtered.m_cyclesByPhase[i]) / 1000000.0, phase_tot_ms,
+ (phase_tot_ms * 100.0 / totTime_ms));
+ }
+ fprintf(f, "\n 'End phase slop' should be very small (if not, there's unattributed time): %9.3f Mcycles.\n",
+ m_filtered.m_parentPhaseEndSlop);
+ }
+}
+
+JitTimer::JitTimer(unsigned byteCodeSize) : m_info(byteCodeSize)
+{
+#ifdef DEBUG
+ m_lastPhase = (Phases)-1;
+#endif
+
+ unsigned __int64 threadCurCycles;
+ if (GetThreadCycles(&threadCurCycles))
+ {
+ m_start = threadCurCycles;
+ m_curPhaseStart = threadCurCycles;
+ }
+}
+
+void JitTimer::EndPhase(Phases phase)
+{
+ // Otherwise...
+ // We re-run some phases currently, so this following assert doesn't work.
+ // assert((int)phase > (int)m_lastPhase); // We should end phases in increasing order.
+
+ unsigned __int64 threadCurCycles;
+ if (GetThreadCycles(&threadCurCycles))
+ {
+ unsigned __int64 phaseCycles = (threadCurCycles - m_curPhaseStart);
+ // If this is not a leaf phase, the assumption is that the last subphase must have just recently ended.
+ // Credit the duration to "slop", the total of which should be very small.
+ if (PhaseHasChildren[phase])
+ {
+ m_info.m_parentPhaseEndSlop += phaseCycles;
+ }
+ else
+ {
+ // It is a leaf phase. Credit duration to it.
+ m_info.m_invokesByPhase[phase]++;
+ m_info.m_cyclesByPhase[phase] += phaseCycles;
+ // Credit the phase's ancestors, if any.
+ int ancPhase = PhaseParent[phase];
+ while (ancPhase != -1)
+ {
+ m_info.m_cyclesByPhase[ancPhase] += phaseCycles;
+ ancPhase = PhaseParent[ancPhase];
+ }
+ // Did we just end the last phase?
+ if (phase + 1 == PHASE_NUMBER_OF)
+ {
+ m_info.m_totalCycles = (threadCurCycles - m_start);
+ }
+ else
+ {
+ m_curPhaseStart = threadCurCycles;
+ }
+ }
+ }
+#ifdef DEBUG
+ m_lastPhase = phase;
+#endif
+}
+
+CritSecObject JitTimer::s_csvLock;
+
+LPCWSTR Compiler::JitTimeLogCsv()
+{
+ LPCWSTR jitTimeLogCsv = JitConfig.JitTimeLogCsv();
+ return jitTimeLogCsv;
+}
+
+void JitTimer::PrintCsvHeader()
+{
+ LPCWSTR jitTimeLogCsv = Compiler::JitTimeLogCsv();
+ if (jitTimeLogCsv == NULL)
+ {
+ return;
+ }
+
+ CritSecHolder csvLock(s_csvLock);
+
+ FILE* fp = _wfopen(jitTimeLogCsv, W("r"));
+ if (fp == nullptr)
+ {
+ // File doesn't exist, so create it and write the header
+
+ // Use write mode, so we rewrite the file, and retain only the last compiled process/dll.
+ // Ex: ngen install mscorlib won't print stats for "ngen" but for "mscorsvw"
+ FILE* fp = _wfopen(jitTimeLogCsv, W("w"));
+ fprintf(fp, "\"Method Name\",");
+ fprintf(fp, "\"Method Index\",");
+ fprintf(fp, "\"IL Bytes\",");
+ fprintf(fp, "\"Basic Blocks\",");
+ fprintf(fp, "\"Opt Level\",");
+ fprintf(fp, "\"Loops Cloned\",");
+
+ for (int i = 0; i < PHASE_NUMBER_OF; i++)
+ {
+ fprintf(fp, "\"%s\",", PhaseNames[i]);
+ }
+
+ InlineStrategy::DumpCsvHeader(fp);
+
+ fprintf(fp, "\"Total Cycles\",");
+ fprintf(fp, "\"CPS\"\n");
+ }
+ fclose(fp);
+}
+
+extern ICorJitHost* g_jitHost;
+
+void JitTimer::PrintCsvMethodStats(Compiler* comp)
+{
+ LPCWSTR jitTimeLogCsv = Compiler::JitTimeLogCsv();
+ if (jitTimeLogCsv == NULL)
+ {
+ return;
+ }
+
+ // eeGetMethodFullName uses locks, so don't enter crit sec before this call.
+ const char* methName = comp->eeGetMethodFullName(comp->info.compMethodHnd);
+
+ // Try and access the SPMI index to report in the data set.
+ //
+ // If the jit is not hosted under SPMI this will return the
+ // default value of zero.
+ //
+ // Query the jit host directly here instead of going via the
+ // config cache, since value will change for each method.
+ int index = g_jitHost->getIntConfigValue(W("SuperPMIMethodContextNumber"), 0);
+
+ CritSecHolder csvLock(s_csvLock);
+
+ FILE* fp = _wfopen(jitTimeLogCsv, W("a"));
+ fprintf(fp, "\"%s\",", methName);
+ fprintf(fp, "%d,", index);
+ fprintf(fp, "%u,", comp->info.compILCodeSize);
+ fprintf(fp, "%u,", comp->fgBBcount);
+ fprintf(fp, "%u,", comp->opts.MinOpts());
+ fprintf(fp, "%u,", comp->optLoopsCloned);
+ unsigned __int64 totCycles = 0;
+ for (int i = 0; i < PHASE_NUMBER_OF; i++)
+ {
+ if (!PhaseHasChildren[i])
+ totCycles += m_info.m_cyclesByPhase[i];
+ fprintf(fp, "%I64u,", m_info.m_cyclesByPhase[i]);
+ }
+
+ comp->m_inlineStrategy->DumpCsvData(fp);
+
+ fprintf(fp, "%I64u,", m_info.m_totalCycles);
+ fprintf(fp, "%f\n", CycleTimer::CyclesPerSecond());
+ fclose(fp);
+}
+
+// Completes the timing of the current method, and adds it to "sum".
+void JitTimer::Terminate(Compiler* comp, CompTimeSummaryInfo& sum)
+{
+#ifdef DEBUG
+ unsigned __int64 totCycles2 = 0;
+ for (int i = 0; i < PHASE_NUMBER_OF; i++)
+ {
+ if (!PhaseHasChildren[i])
+ totCycles2 += m_info.m_cyclesByPhase[i];
+ }
+ // We include m_parentPhaseEndSlop in the next phase's time also (we probably shouldn't)
+ // totCycles2 += m_info.m_parentPhaseEndSlop;
+ assert(totCycles2 == m_info.m_totalCycles);
+#endif
+
+ PrintCsvMethodStats(comp);
+
+ sum.AddInfo(m_info);
+}
+#endif // FEATURE_JIT_METHOD_PERF
+
+#if MEASURE_MEM_ALLOC
+// static vars.
+CritSecObject Compiler::s_memStatsLock; // Default constructor.
+Compiler::AggregateMemStats Compiler::s_aggMemStats; // Default constructor.
+Compiler::MemStats Compiler::s_maxCompMemStats; // Default constructor.
+
+const char* Compiler::MemStats::s_CompMemKindNames[] = {
+#define CompMemKindMacro(kind) #kind,
+#include "compmemkind.h"
+};
+
+void Compiler::MemStats::Print(FILE* f)
+{
+ fprintf(f, "count: %10u, size: %10llu, max = %10llu\n", allocCnt, allocSz, allocSzMax);
+ fprintf(f, "allocateMemory: %10llu, nraUsed: %10llu\n", nraTotalSizeAlloc, nraTotalSizeUsed);
+ PrintByKind(f);
+}
+
+void Compiler::MemStats::PrintByKind(FILE* f)
+{
+ fprintf(f, "\nAlloc'd bytes by kind:\n %20s | %10s | %7s\n", "kind", "size", "pct");
+ fprintf(f, " %20s-+-%10s-+-%7s\n", "--------------------", "----------", "-------");
+ float allocSzF = static_cast<float>(allocSz);
+ for (int cmk = 0; cmk < CMK_Count; cmk++)
+ {
+ float pct = 100.0f * static_cast<float>(allocSzByKind[cmk]) / allocSzF;
+ fprintf(f, " %20s | %10llu | %6.2f%%\n", s_CompMemKindNames[cmk], allocSzByKind[cmk], pct);
+ }
+ fprintf(f, "\n");
+}
+
+void Compiler::AggregateMemStats::Print(FILE* f)
+{
+ fprintf(f, "For %9u methods:\n", nMethods);
+ fprintf(f, " count: %12u (avg %7u per method)\n", allocCnt, allocCnt / nMethods);
+ fprintf(f, " alloc size : %12llu (avg %7llu per method)\n", allocSz, allocSz / nMethods);
+ fprintf(f, " max alloc : %12llu\n", allocSzMax);
+ fprintf(f, "\n");
+ fprintf(f, " allocateMemory : %12llu (avg %7llu per method)\n", nraTotalSizeAlloc, nraTotalSizeAlloc / nMethods);
+ fprintf(f, " nraUsed : %12llu (avg %7llu per method)\n", nraTotalSizeUsed, nraTotalSizeUsed / nMethods);
+ PrintByKind(f);
+}
+#endif // MEASURE_MEM_ALLOC
+
+#if LOOP_HOIST_STATS
+// Static fields.
+CritSecObject Compiler::s_loopHoistStatsLock; // Default constructor.
+unsigned Compiler::s_loopsConsidered = 0;
+unsigned Compiler::s_loopsWithHoistedExpressions = 0;
+unsigned Compiler::s_totalHoistedExpressions = 0;
+
+// static
+void Compiler::PrintAggregateLoopHoistStats(FILE* f)
+{
+ fprintf(f, "\n");
+ fprintf(f, "---------------------------------------------------\n");
+ fprintf(f, "Loop hoisting stats\n");
+ fprintf(f, "---------------------------------------------------\n");
+
+ double pctWithHoisted = 0.0;
+ if (s_loopsConsidered > 0)
+ {
+ pctWithHoisted = 100.0 * (double(s_loopsWithHoistedExpressions) / double(s_loopsConsidered));
+ }
+ double exprsPerLoopWithExpr = 0.0;
+ if (s_loopsWithHoistedExpressions > 0)
+ {
+ exprsPerLoopWithExpr = double(s_totalHoistedExpressions) / double(s_loopsWithHoistedExpressions);
+ }
+ fprintf(f, "Considered %d loops. Of these, we hoisted expressions out of %d (%6.2f%%).\n", s_loopsConsidered,
+ s_loopsWithHoistedExpressions, pctWithHoisted);
+ fprintf(f, " A total of %d expressions were hoisted, an average of %5.2f per loop-with-hoisted-expr.\n",
+ s_totalHoistedExpressions, exprsPerLoopWithExpr);
+}
+
+void Compiler::AddLoopHoistStats()
+{
+ CritSecHolder statsLock(s_loopHoistStatsLock);
+
+ s_loopsConsidered += m_loopsConsidered;
+ s_loopsWithHoistedExpressions += m_loopsWithHoistedExpressions;
+ s_totalHoistedExpressions += m_totalHoistedExpressions;
+}
+
+void Compiler::PrintPerMethodLoopHoistStats()
+{
+ double pctWithHoisted = 0.0;
+ if (m_loopsConsidered > 0)
+ {
+ pctWithHoisted = 100.0 * (double(m_loopsWithHoistedExpressions) / double(m_loopsConsidered));
+ }
+ double exprsPerLoopWithExpr = 0.0;
+ if (m_loopsWithHoistedExpressions > 0)
+ {
+ exprsPerLoopWithExpr = double(m_totalHoistedExpressions) / double(m_loopsWithHoistedExpressions);
+ }
+ printf("Considered %d loops. Of these, we hoisted expressions out of %d (%5.2f%%).\n", m_loopsConsidered,
+ m_loopsWithHoistedExpressions, pctWithHoisted);
+ printf(" A total of %d expressions were hoisted, an average of %5.2f per loop-with-hoisted-expr.\n",
+ m_totalHoistedExpressions, exprsPerLoopWithExpr);
+}
+#endif // LOOP_HOIST_STATS
+
+//------------------------------------------------------------------------
+// RecordStateAtEndOfInlining: capture timing data (if enabled) after
+// inlining as completed.
+//
+// Note:
+// Records data needed for SQM and inlining data dumps. 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 Compiler::RecordStateAtEndOfInlining()
+{
+#if defined(DEBUG) || defined(INLINE_DATA) || defined(FEATURE_CLRSQM)
+
+ m_compCyclesAtEndOfInlining = 0;
+ m_compTickCountAtEndOfInlining = 0;
+ bool b = CycleTimer::GetThreadCyclesS(&m_compCyclesAtEndOfInlining);
+ if (!b)
+ {
+ return; // We don't have a thread cycle counter.
+ }
+ m_compTickCountAtEndOfInlining = GetTickCount();
+
+#endif // defined(DEBUG) || defined(INLINE_DATA) || defined(FEATURE_CLRSQM)
+}
+
+//------------------------------------------------------------------------
+// RecordStateAtEndOfCompilation: capture timing data (if enabled) after
+// compilation is completed.
+
+void Compiler::RecordStateAtEndOfCompilation()
+{
+#if defined(DEBUG) || defined(INLINE_DATA) || defined(FEATURE_CLRSQM)
+
+ // Common portion
+ m_compCycles = 0;
+ unsigned __int64 compCyclesAtEnd;
+ bool b = CycleTimer::GetThreadCyclesS(&compCyclesAtEnd);
+ if (!b)
+ {
+ return; // We don't have a thread cycle counter.
+ }
+ assert(compCyclesAtEnd >= m_compCyclesAtEndOfInlining);
+
+ m_compCycles = compCyclesAtEnd - m_compCyclesAtEndOfInlining;
+
+#endif // defined(DEBUG) || defined(INLINE_DATA) || defined(FEATURE_CLRSQM)
+
+#ifdef FEATURE_CLRSQM
+
+ // SQM only portion
+ unsigned __int64 mcycles64 = m_compCycles / ((unsigned __int64)1000000);
+ unsigned mcycles;
+ if (mcycles64 > UINT32_MAX)
+ {
+ mcycles = UINT32_MAX;
+ }
+ else
+ {
+ mcycles = (unsigned)mcycles64;
+ }
+
+ DWORD ticksAtEnd = GetTickCount();
+ assert(ticksAtEnd >= m_compTickCountAtEndOfInlining);
+ DWORD compTicks = ticksAtEnd - m_compTickCountAtEndOfInlining;
+
+ if (mcycles >= 1000)
+ {
+ info.compCompHnd->logSQMLongJitEvent(mcycles, compTicks, info.compILCodeSize, fgBBcount, opts.MinOpts(),
+ info.compMethodHnd);
+ }
+
+#endif // FEATURE_CLRSQM
+}
+
+#if FUNC_INFO_LOGGING
+// static
+LPCWSTR Compiler::compJitFuncInfoFilename = nullptr;
+
+// static
+FILE* Compiler::compJitFuncInfoFile = nullptr;
+#endif // FUNC_INFO_LOGGING
+
+#ifdef DEBUG
+
+// dumpConvertedVarSet() is just like dumpVarSet(), except we assume the varset bits are tracked
+// variable indices, and we convert them to variable numbers, sort the variable numbers, and
+// print them as variable numbers. To do this, we use a temporary set indexed by
+// variable number. We can't use the "all varset" type because it is still size-limited, and might
+// not be big enough to handle all possible variable numbers.
+void dumpConvertedVarSet(Compiler* comp, VARSET_VALARG_TP vars)
+{
+ BYTE* pVarNumSet; // trivial set: one byte per varNum, 0 means not in set, 1 means in set.
+
+ size_t varNumSetBytes = comp->lvaCount * sizeof(BYTE);
+ pVarNumSet = (BYTE*)_alloca(varNumSetBytes);
+ memset(pVarNumSet, 0, varNumSetBytes); // empty the set
+
+ VARSET_ITER_INIT(comp, iter, vars, varIndex);
+ while (iter.NextElem(comp, &varIndex))
+ {
+ unsigned varNum = comp->lvaTrackedToVarNum[varIndex];
+ assert(varNum < comp->lvaCount);
+ pVarNumSet[varNum] = 1; // This varNum is in the set
+ }
+
+ bool first = true;
+ printf("{");
+ for (size_t varNum = 0; varNum < comp->lvaCount; varNum++)
+ {
+ if (pVarNumSet[varNum] == 1)
+ {
+ if (!first)
+ {
+ printf(" ");
+ }
+ printf("V%02u", varNum);
+ first = false;
+ }
+ }
+ printf("}");
+}
+
+/*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 versions that start with 'c' take a Compiler* as the first argument.
+ * The versions that start with 'd' use the tlsCompiler, so don't require a Compiler*.
+ *
+ * Summary:
+ * cBlock, dBlock : Display a basic block (call fgDispBasicBlock()).
+ * cBlocks, dBlocks : Display all the basic blocks of a function (call fgDispBasicBlocks()).
+ * cBlocksV, dBlocksV : Display all the basic blocks of a function (call fgDispBasicBlocks(true)).
+ * "V" means "verbose", and will dump all the trees.
+ * cTree, dTree : Display a tree (call gtDispTree()).
+ * cTrees, dTrees : Display all the trees in a function (call fgDumpTrees()).
+ * cEH, dEH : Display the EH handler table (call fgDispHandlerTab()).
+ * cVar, dVar : Display a local variable given its number (call lvaDumpEntry()).
+ * cVarDsc, dVarDsc : Display a local variable given a LclVarDsc* (call lvaDumpEntry()).
+ * cVars, dVars : Display the local variable table (call lvaTableDump()).
+ * cVarsFinal, dVarsFinal : Display the local variable table (call lvaTableDump(FINAL_FRAME_LAYOUT)).
+ * cBlockCheapPreds, dBlockCheapPreds : Display a block's cheap predecessors (call block->dspCheapPreds()).
+ * cBlockPreds, dBlockPreds : Display a block's predecessors (call block->dspPreds()).
+ * cBlockSuccs, dBlockSuccs : Display a block's successors (call block->dspSuccs(compiler)).
+ * cReach, dReach : Display all block reachability (call fgDispReach()).
+ * cDoms, dDoms : Display all block dominators (call fgDispDoms()).
+ * cLiveness, dLiveness : Display per-block variable liveness (call fgDispBBLiveness()).
+ * cCVarSet, dCVarSet : Display a "converted" VARSET_TP: the varset is assumed to be tracked variable
+ * indices. These are converted to variable numbers and sorted. (Calls
+ * dumpConvertedVarSet()).
+ *
+ * cFuncIR, dFuncIR : Display all the basic blocks of a function in linear IR form.
+ * cLoopIR, dLoopIR : Display a loop in linear IR form.
+ * dLoopNumIR : Display a loop (given number) in linear IR form.
+ * cBlockIR, dBlockIR : Display a basic block in linear IR form.
+ * cTreeIR, dTreeIR : Display a tree in linear IR form.
+ * dTabStopIR : Display spaces to the next tab stop column
+ * cTreeTypeIR dTreeTypeIR : Display tree type
+ * cTreeKindsIR dTreeKindsIR : Display tree kinds
+ * cTreeFlagsIR dTreeFlagsIR : Display tree flags
+ * cOperandIR dOperandIR : Display tree operand
+ * cLeafIR dLeafIR : Display tree leaf
+ * cIndirIR dIndirIR : Display indir tree as [t#] or [leaf]
+ * cListIR dListIR : Display tree list
+ * cSsaNumIR dSsaNumIR : Display SSA number as <u|d:#>
+ * cValNumIR dValNumIR : Display Value number as <v{l|c}:#{,R}>
+ * cDependsIR : Display dependencies of a tree DEP(t# ...) node
+ * based on child comma tree nodes
+ * dFormatIR : Display dump format specified on command line
+ *
+ *
+ * The following don't require a Compiler* to work:
+ * dVarSet : Display a VARSET_TP (call dumpVarSet()).
+ * dRegMask : Display a regMaskTP (call dspRegMask(mask)).
+ */
+
+void cBlock(Compiler* comp, BasicBlock* block)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Block %u\n", sequenceNumber++);
+ comp->fgTableDispBasicBlock(block);
+}
+
+void cBlocks(Compiler* comp)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Blocks %u\n", sequenceNumber++);
+ comp->fgDispBasicBlocks();
+}
+
+void cBlocksV(Compiler* comp)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *BlocksV %u\n", sequenceNumber++);
+ comp->fgDispBasicBlocks(true);
+}
+
+void cTree(Compiler* comp, GenTree* tree)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Tree %u\n", sequenceNumber++);
+ comp->gtDispTree(tree, nullptr, ">>>");
+}
+
+void cTrees(Compiler* comp)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Trees %u\n", sequenceNumber++);
+ comp->fgDumpTrees(comp->fgFirstBB, nullptr);
+}
+
+void cEH(Compiler* comp)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *EH %u\n", sequenceNumber++);
+ comp->fgDispHandlerTab();
+}
+
+void cVar(Compiler* comp, unsigned lclNum)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Var %u\n", sequenceNumber++);
+ comp->lvaDumpEntry(lclNum, Compiler::FINAL_FRAME_LAYOUT);
+}
+
+void cVarDsc(Compiler* comp, LclVarDsc* varDsc)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *VarDsc %u\n", sequenceNumber++);
+ unsigned lclNum = (unsigned)(varDsc - comp->lvaTable);
+ comp->lvaDumpEntry(lclNum, Compiler::FINAL_FRAME_LAYOUT);
+}
+
+void cVars(Compiler* comp)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Vars %u\n", sequenceNumber++);
+ comp->lvaTableDump();
+}
+
+void cVarsFinal(Compiler* comp)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Vars %u\n", sequenceNumber++);
+ comp->lvaTableDump(Compiler::FINAL_FRAME_LAYOUT);
+}
+
+void cBlockCheapPreds(Compiler* comp, BasicBlock* block)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *BlockCheapPreds %u\n",
+ sequenceNumber++);
+ block->dspCheapPreds();
+}
+
+void cBlockPreds(Compiler* comp, BasicBlock* block)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *BlockPreds %u\n", sequenceNumber++);
+ block->dspPreds();
+}
+
+void cBlockSuccs(Compiler* comp, BasicBlock* block)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *BlockSuccs %u\n", sequenceNumber++);
+ block->dspSuccs(comp);
+}
+
+void cReach(Compiler* comp)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Reach %u\n", sequenceNumber++);
+ comp->fgDispReach();
+}
+
+void cDoms(Compiler* comp)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Doms %u\n", sequenceNumber++);
+ comp->fgDispDoms();
+}
+
+void cLiveness(Compiler* comp)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== *Liveness %u\n", sequenceNumber++);
+ comp->fgDispBBLiveness();
+}
+
+void cCVarSet(Compiler* comp, VARSET_VALARG_TP vars)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== dCVarSet %u\n", sequenceNumber++);
+ dumpConvertedVarSet(comp, vars);
+ printf("\n"); // dumpConvertedVarSet() doesn't emit a trailing newline
+}
+
+void dBlock(BasicBlock* block)
+{
+ cBlock(JitTls::GetCompiler(), block);
+}
+
+void dBlocks()
+{
+ cBlocks(JitTls::GetCompiler());
+}
+
+void dBlocksV()
+{
+ cBlocksV(JitTls::GetCompiler());
+}
+
+void dTree(GenTree* tree)
+{
+ cTree(JitTls::GetCompiler(), tree);
+}
+
+void dTrees()
+{
+ cTrees(JitTls::GetCompiler());
+}
+
+void dEH()
+{
+ cEH(JitTls::GetCompiler());
+}
+
+void dVar(unsigned lclNum)
+{
+ cVar(JitTls::GetCompiler(), lclNum);
+}
+
+void dVarDsc(LclVarDsc* varDsc)
+{
+ cVarDsc(JitTls::GetCompiler(), varDsc);
+}
+
+void dVars()
+{
+ cVars(JitTls::GetCompiler());
+}
+
+void dVarsFinal()
+{
+ cVarsFinal(JitTls::GetCompiler());
+}
+
+void dBlockPreds(BasicBlock* block)
+{
+ cBlockPreds(JitTls::GetCompiler(), block);
+}
+
+void dBlockCheapPreds(BasicBlock* block)
+{
+ cBlockCheapPreds(JitTls::GetCompiler(), block);
+}
+
+void dBlockSuccs(BasicBlock* block)
+{
+ cBlockSuccs(JitTls::GetCompiler(), block);
+}
+
+void dReach()
+{
+ cReach(JitTls::GetCompiler());
+}
+
+void dDoms()
+{
+ cDoms(JitTls::GetCompiler());
+}
+
+void dLiveness()
+{
+ cLiveness(JitTls::GetCompiler());
+}
+
+void dCVarSet(VARSET_VALARG_TP vars)
+{
+ cCVarSet(JitTls::GetCompiler(), vars);
+}
+
+void dRegMask(regMaskTP mask)
+{
+ static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
+ printf("===================================================================== dRegMask %u\n", sequenceNumber++);
+ dspRegMask(mask);
+ printf("\n"); // dspRegMask() doesn't emit a trailing newline
+}
+
+void dBlockList(BasicBlockList* list)
+{
+ printf("WorkList: ");
+ while (list != nullptr)
+ {
+ printf("BB%02u ", list->block->bbNum);
+ list = list->next;
+ }
+ printf("\n");
+}
+
+// Global variables available in debug mode. That are set by debug APIs for finding
+// Trees, Stmts, and/or Blocks using id or bbNum.
+// That can be used in watch window or as a way to get address of fields for data break points.
+
+GenTree* dbTree;
+GenTreeStmt* dbStmt;
+BasicBlock* dbTreeBlock;
+BasicBlock* dbBlock;
+
+// Debug APIs for finding Trees, Stmts, and/or Blocks.
+// As a side effect, they set the debug variables above.
+
+GenTree* dFindTree(GenTree* tree, unsigned id)
+{
+ GenTree* child;
+
+ if (tree == nullptr)
+ {
+ return nullptr;
+ }
+
+ if (tree->gtTreeID == id)
+ {
+ dbTree = tree;
+ return tree;
+ }
+
+ unsigned childCount = tree->NumChildren();
+ for (unsigned childIndex = 0; childIndex < childCount; childIndex++)
+ {
+ child = tree->GetChild(childIndex);
+ child = dFindTree(child, id);
+ if (child != nullptr)
+ {
+ return child;
+ }
+ }
+
+ return nullptr;
+}
+
+GenTree* dFindTree(unsigned id)
+{
+ Compiler* comp = JitTls::GetCompiler();
+ BasicBlock* block;
+ GenTree* tree;
+
+ dbTreeBlock = nullptr;
+ dbTree = nullptr;
+
+ for (block = comp->fgFirstBB; block != nullptr; block = block->bbNext)
+ {
+ for (GenTreeStmt* stmt = block->firstStmt(); stmt; stmt = stmt->gtNextStmt)
+ {
+ tree = dFindTree(stmt, id);
+ if (tree != nullptr)
+ {
+ dbTreeBlock = block;
+ return tree;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+GenTreeStmt* dFindStmt(unsigned id)
+{
+ Compiler* comp = JitTls::GetCompiler();
+ BasicBlock* block;
+
+ dbStmt = nullptr;
+
+ unsigned stmtId = 0;
+ for (block = comp->fgFirstBB; block != nullptr; block = block->bbNext)
+ {
+ for (GenTreeStmt* stmt = block->firstStmt(); stmt; stmt = stmt->gtNextStmt)
+ {
+ stmtId++;
+ if (stmtId == id)
+ {
+ dbStmt = stmt;
+ return stmt;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+BasicBlock* dFindBlock(unsigned bbNum)
+{
+ Compiler* comp = JitTls::GetCompiler();
+ BasicBlock* block = nullptr;
+
+ dbBlock = nullptr;
+ for (block = comp->fgFirstBB; block != nullptr; block = block->bbNext)
+ {
+ if (block->bbNum == bbNum)
+ {
+ dbBlock = block;
+ break;
+ }
+ }
+
+ return block;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out function in linear IR form
+ */
+
+void cFuncIR(Compiler* comp)
+{
+ BasicBlock* block;
+
+ printf("Method %s::%s, hsh=0x%x\n", comp->info.compClassName, comp->info.compMethodName,
+ comp->info.compMethodHash());
+
+ printf("\n");
+
+ for (block = comp->fgFirstBB; block != nullptr; block = block->bbNext)
+ {
+ cBlockIR(comp, block);
+ }
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out the format specifiers from COMPlus_JitDumpIRFormat
+ */
+
+void dFormatIR()
+{
+ Compiler* comp = JitTls::GetCompiler();
+
+ if (comp->dumpIRFormat != nullptr)
+ {
+ printf("COMPlus_JitDumpIRFormat=%ls", comp->dumpIRFormat);
+ }
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out function in linear IR form
+ */
+
+void dFuncIR()
+{
+ cFuncIR(JitTls::GetCompiler());
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out loop in linear IR form
+ */
+
+void cLoopIR(Compiler* comp, Compiler::LoopDsc* loop)
+{
+ BasicBlock* blockHead = loop->lpHead;
+ BasicBlock* blockFirst = loop->lpFirst;
+ BasicBlock* blockTop = loop->lpTop;
+ BasicBlock* blockEntry = loop->lpEntry;
+ BasicBlock* blockBottom = loop->lpBottom;
+ BasicBlock* blockExit = loop->lpExit;
+ BasicBlock* blockLast = blockBottom->bbNext;
+ BasicBlock* block;
+
+ printf("LOOP\n");
+ printf("\n");
+ printf("HEAD BB%02u\n", blockHead->bbNum);
+ printf("FIRST BB%02u\n", blockFirst->bbNum);
+ printf("TOP BB%02u\n", blockTop->bbNum);
+ printf("ENTRY BB%02u\n", blockEntry->bbNum);
+ if (loop->lpExitCnt == 1)
+ {
+ printf("EXIT BB%02u\n", blockExit->bbNum);
+ }
+ else
+ {
+ printf("EXITS %u", loop->lpExitCnt);
+ }
+ printf("BOTTOM BB%02u\n", blockBottom->bbNum);
+ printf("\n");
+
+ cBlockIR(comp, blockHead);
+ for (block = blockFirst; ((block != nullptr) && (block != blockLast)); block = block->bbNext)
+ {
+ cBlockIR(comp, block);
+ }
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out loop in linear IR form
+ */
+
+void dLoopIR(Compiler::LoopDsc* loop)
+{
+ cLoopIR(JitTls::GetCompiler(), loop);
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out loop (given loop number) in linear IR form
+ */
+
+void dLoopNumIR(unsigned loopNum)
+{
+ Compiler* comp = JitTls::GetCompiler();
+
+ if (loopNum >= comp->optLoopCount)
+ {
+ printf("loopNum %u out of range\n");
+ return;
+ }
+
+ Compiler::LoopDsc* loop = &comp->optLoopTable[loopNum];
+ cLoopIR(JitTls::GetCompiler(), loop);
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump spaces to specified tab stop
+ */
+
+int dTabStopIR(int curr, int tabstop)
+{
+ int chars = 0;
+
+ if (tabstop <= curr)
+ {
+ chars += printf(" ");
+ }
+
+ for (int i = curr; i < tabstop; i++)
+ {
+ chars += printf(" ");
+ }
+
+ return chars;
+}
+
+void cNodeIR(Compiler* comp, GenTree* tree);
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out block in linear IR form
+ */
+
+void cBlockIR(Compiler* comp, BasicBlock* block)
+{
+ bool noStmts = comp->dumpIRNoStmts;
+ bool trees = comp->dumpIRTrees;
+
+ if (comp->dumpIRBlockHeaders)
+ {
+ block->dspBlockHeader(comp);
+ }
+ else
+ {
+ printf("BB%02u:\n", block->bbNum);
+ }
+
+ printf("\n");
+
+ if (!block->IsLIR())
+ {
+ for (GenTreeStmt* stmt = block->firstStmt(); stmt; stmt = stmt->gtNextStmt)
+ {
+ // Print current stmt.
+
+ if (trees)
+ {
+ cTree(comp, stmt);
+ printf("\n");
+ printf("=====================================================================\n");
+ }
+
+ if (comp->compRationalIRForm)
+ {
+ GenTree* tree;
+
+ foreach_treenode_execution_order(tree, stmt)
+ {
+ cNodeIR(comp, tree);
+ }
+ }
+ else
+ {
+ cTreeIR(comp, stmt);
+ }
+
+ if (!noStmts && !trees)
+ {
+ printf("\n");
+ }
+ }
+ }
+ else
+ {
+ for (GenTree* node = block->bbTreeList; node != nullptr; node = node->gtNext)
+ {
+ cNodeIR(comp, node);
+ }
+ }
+
+ int chars = 0;
+
+ chars += dTabStopIR(chars, COLUMN_OPCODE);
+
+ chars += printf(" ");
+ switch (block->bbJumpKind)
+ {
+ case BBJ_EHFINALLYRET:
+ chars += printf("BRANCH(EHFINALLYRET)");
+ break;
+
+ case BBJ_EHFILTERRET:
+ chars += printf("BRANCH(EHFILTERRET)");
+ break;
+
+ case BBJ_EHCATCHRET:
+ chars += printf("BRANCH(EHCATCHRETURN)");
+ chars += dTabStopIR(chars, COLUMN_OPERANDS);
+ chars += printf(" BB%02u", block->bbJumpDest->bbNum);
+ break;
+
+ case BBJ_THROW:
+ chars += printf("BRANCH(THROW)");
+ break;
+
+ case BBJ_RETURN:
+ chars += printf("BRANCH(RETURN)");
+ break;
+
+ case BBJ_NONE:
+ // For fall-through blocks
+ chars += printf("BRANCH(NONE)");
+ break;
+
+ case BBJ_ALWAYS:
+ chars += printf("BRANCH(ALWAYS)");
+ chars += dTabStopIR(chars, COLUMN_OPERANDS);
+ chars += printf(" BB%02u", block->bbJumpDest->bbNum);
+ if (block->bbFlags & BBF_KEEP_BBJ_ALWAYS)
+ {
+ chars += dTabStopIR(chars, COLUMN_KINDS);
+ chars += printf("; [KEEP_BBJ_ALWAYS]");
+ }
+ break;
+
+ case BBJ_LEAVE:
+ chars += printf("BRANCH(LEAVE)");
+ chars += dTabStopIR(chars, COLUMN_OPERANDS);
+ chars += printf(" BB%02u", block->bbJumpDest->bbNum);
+ break;
+
+ case BBJ_CALLFINALLY:
+ chars += printf("BRANCH(CALLFINALLY)");
+ chars += dTabStopIR(chars, COLUMN_OPERANDS);
+ chars += printf(" BB%02u", block->bbJumpDest->bbNum);
+ break;
+
+ case BBJ_COND:
+ chars += printf("BRANCH(COND)");
+ chars += dTabStopIR(chars, COLUMN_OPERANDS);
+ chars += printf(" BB%02u", block->bbJumpDest->bbNum);
+ break;
+
+ case BBJ_SWITCH:
+ chars += printf("BRANCH(SWITCH)");
+ chars += dTabStopIR(chars, COLUMN_OPERANDS);
+
+ unsigned jumpCnt;
+ jumpCnt = block->bbJumpSwt->bbsCount;
+ BasicBlock** jumpTab;
+ jumpTab = block->bbJumpSwt->bbsDstTab;
+ do
+ {
+ chars += printf("%c BB%02u", (jumpTab == block->bbJumpSwt->bbsDstTab) ? ' ' : ',', (*jumpTab)->bbNum);
+ } while (++jumpTab, --jumpCnt);
+ break;
+
+ default:
+ unreached();
+ break;
+ }
+
+ printf("\n");
+ if (block->bbNext != nullptr)
+ {
+ printf("\n");
+ }
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out block in linear IR form
+ */
+
+void dBlockIR(BasicBlock* block)
+{
+ cBlockIR(JitTls::GetCompiler(), block);
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree node type for linear IR form
+ */
+
+int cTreeTypeIR(Compiler* comp, GenTree* tree)
+{
+ int chars = 0;
+
+ var_types type = tree->TypeGet();
+
+ const char* typeName = varTypeName(type);
+ chars += printf(".%s", typeName);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree node type for linear IR form
+ */
+
+int dTreeTypeIR(GenTree* tree)
+{
+ int chars = cTreeTypeIR(JitTls::GetCompiler(), tree);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree node kind for linear IR form
+ */
+
+int cTreeKindsIR(Compiler* comp, GenTree* tree)
+{
+ int chars = 0;
+
+ unsigned kind = tree->OperKind();
+
+ chars += printf("kinds=");
+ if (kind == GTK_SPECIAL)
+ {
+ chars += printf("[SPECIAL]");
+ }
+ if (kind & GTK_CONST)
+ {
+ chars += printf("[CONST]");
+ }
+ if (kind & GTK_LEAF)
+ {
+ chars += printf("[LEAF]");
+ }
+ if (kind & GTK_UNOP)
+ {
+ chars += printf("[UNOP]");
+ }
+ if (kind & GTK_BINOP)
+ {
+ chars += printf("[BINOP]");
+ }
+ if (kind & GTK_LOGOP)
+ {
+ chars += printf("[LOGOP]");
+ }
+ if (kind & GTK_ASGOP)
+ {
+ chars += printf("[ASGOP]");
+ }
+ if (kind & GTK_COMMUTE)
+ {
+ chars += printf("[COMMUTE]");
+ }
+ if (kind & GTK_EXOP)
+ {
+ chars += printf("[EXOP]");
+ }
+ if (kind & GTK_LOCAL)
+ {
+ chars += printf("[LOCAL]");
+ }
+ if (kind & GTK_SMPOP)
+ {
+ chars += printf("[SMPOP]");
+ }
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree node kind for linear IR form
+ */
+
+int dTreeKindsIR(GenTree* tree)
+{
+ int chars = cTreeKindsIR(JitTls::GetCompiler(), tree);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree node flags for linear IR form
+ */
+
+int cTreeFlagsIR(Compiler* comp, GenTree* tree)
+{
+ int chars = 0;
+
+ if (tree->gtFlags != 0)
+ {
+ chars += printf("flags=");
+
+ // Node flags
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#if defined(DEBUG)
+#if SMALL_TREE_NODES
+ if (comp->dumpIRNodes)
+ {
+ if (tree->gtDebugFlags & GTF_DEBUG_NODE_LARGE)
+ {
+ chars += printf("[NODE_LARGE]");
+ }
+ if (tree->gtDebugFlags & GTF_DEBUG_NODE_SMALL)
+ {
+ chars += printf("[NODE_SMALL]");
+ }
+ }
+#endif // SMALL_TREE_NODES
+ if (tree->gtDebugFlags & GTF_DEBUG_NODE_MORPHED)
+ {
+ chars += printf("[MORPHED]");
+ }
+#endif // defined(DEBUG)
+
+ if (tree->gtFlags & GTF_COLON_COND)
+ {
+ chars += printf("[COLON_COND]");
+ }
+
+ // Operator flags
+
+ genTreeOps op = tree->OperGet();
+ switch (op)
+ {
+
+ case GT_LCL_VAR:
+ case GT_LCL_VAR_ADDR:
+ case GT_LCL_FLD:
+ case GT_LCL_FLD_ADDR:
+ case GT_STORE_LCL_FLD:
+ case GT_STORE_LCL_VAR:
+ case GT_REG_VAR:
+
+ if (tree->gtFlags & GTF_VAR_DEF)
+ {
+ chars += printf("[VAR_DEF]");
+ }
+ if (tree->gtFlags & GTF_VAR_USEASG)
+ {
+ chars += printf("[VAR_USEASG]");
+ }
+ if (tree->gtFlags & GTF_VAR_USEDEF)
+ {
+ chars += printf("[VAR_USEDEF]");
+ }
+ if (tree->gtFlags & GTF_VAR_CAST)
+ {
+ chars += printf("[VAR_CAST]");
+ }
+ if (tree->gtFlags & GTF_VAR_ITERATOR)
+ {
+ chars += printf("[VAR_ITERATOR]");
+ }
+ if (tree->gtFlags & GTF_VAR_CLONED)
+ {
+ chars += printf("[VAR_CLONED]");
+ }
+ if (tree->gtFlags & GTF_VAR_DEATH)
+ {
+ chars += printf("[VAR_DEATH]");
+ }
+ if (tree->gtFlags & GTF_VAR_ARR_INDEX)
+ {
+ chars += printf("[VAR_ARR_INDEX]");
+ }
+#if defined(DEBUG)
+ if (tree->gtDebugFlags & GTF_DEBUG_VAR_CSE_REF)
+ {
+ chars += printf("[VAR_CSE_REF]");
+ }
+#endif
+ if (op == GT_REG_VAR)
+ {
+ if (tree->gtFlags & GTF_REG_BIRTH)
+ {
+ chars += printf("[REG_BIRTH]");
+ }
+ }
+ break;
+
+ case GT_NOP:
+
+ if (tree->gtFlags & GTF_NOP_DEATH)
+ {
+ chars += printf("[NOP_DEATH]");
+ }
+ break;
+
+ case GT_NO_OP:
+
+ if (tree->gtFlags & GTF_NO_OP_NO)
+ {
+ chars += printf("[NO_OP_NO]");
+ }
+ break;
+
+ case GT_FIELD:
+
+ if (tree->gtFlags & GTF_FLD_NULLCHECK)
+ {
+ chars += printf("[FLD_NULLCHECK]");
+ }
+ if (tree->gtFlags & GTF_FLD_VOLATILE)
+ {
+ chars += printf("[FLD_VOLATILE]");
+ }
+ break;
+
+ case GT_INDEX:
+
+ if (tree->gtFlags & GTF_INX_RNGCHK)
+ {
+ chars += printf("[INX_RNGCHK]");
+ }
+ if (tree->gtFlags & GTF_INX_REFARR_LAYOUT)
+ {
+ chars += printf("[INX_REFARR_LAYOUT]");
+ }
+ if (tree->gtFlags & GTF_INX_STRING_LAYOUT)
+ {
+ chars += printf("[INX_STRING_LAYOUT]");
+ }
+ break;
+
+ case GT_IND:
+ case GT_STOREIND:
+
+ if (tree->gtFlags & GTF_IND_VOLATILE)
+ {
+ chars += printf("[IND_VOLATILE]");
+ }
+ if (tree->gtFlags & GTF_IND_REFARR_LAYOUT)
+ {
+ chars += printf("[IND_REFARR_LAYOUT]");
+ }
+ if (tree->gtFlags & GTF_IND_TGTANYWHERE)
+ {
+ chars += printf("[IND_TGTANYWHERE]");
+ }
+ if (tree->gtFlags & GTF_IND_TLS_REF)
+ {
+ chars += printf("[IND_TLS_REF]");
+ }
+ if (tree->gtFlags & GTF_IND_ASG_LHS)
+ {
+ chars += printf("[IND_ASG_LHS]");
+ }
+ if (tree->gtFlags & GTF_IND_UNALIGNED)
+ {
+ chars += printf("[IND_UNALIGNED]");
+ }
+ if (tree->gtFlags & GTF_IND_INVARIANT)
+ {
+ chars += printf("[IND_INVARIANT]");
+ }
+ if (tree->gtFlags & GTF_IND_ARR_LEN)
+ {
+ chars += printf("[IND_ARR_INDEX]");
+ }
+ break;
+
+ case GT_CLS_VAR:
+
+ if (tree->gtFlags & GTF_CLS_VAR_ASG_LHS)
+ {
+ chars += printf("[CLS_VAR_ASG_LHS]");
+ }
+ break;
+
+ case GT_ADDR:
+
+ if (tree->gtFlags & GTF_ADDR_ONSTACK)
+ {
+ chars += printf("[ADDR_ONSTACK]");
+ }
+ break;
+
+ case GT_MUL:
+
+ if (tree->gtFlags & GTF_MUL_64RSLT)
+ {
+ chars += printf("[64RSLT]");
+ }
+ if (tree->gtFlags & GTF_ADDRMODE_NO_CSE)
+ {
+ chars += printf("[ADDRMODE_NO_CSE]");
+ }
+ break;
+
+ case GT_ADD:
+
+ if (tree->gtFlags & GTF_ADDRMODE_NO_CSE)
+ {
+ chars += printf("[ADDRMODE_NO_CSE]");
+ }
+ break;
+
+ case GT_LSH:
+
+ if (tree->gtFlags & GTF_ADDRMODE_NO_CSE)
+ {
+ chars += printf("[ADDRMODE_NO_CSE]");
+ }
+ break;
+
+ case GT_MOD:
+ case GT_UMOD:
+
+ if (tree->gtFlags & GTF_MOD_INT_RESULT)
+ {
+ chars += printf("[MOD_INT_RESULT]");
+ }
+ break;
+
+ case GT_EQ:
+ case GT_NE:
+ case GT_LT:
+ case GT_LE:
+ case GT_GT:
+ case GT_GE:
+
+ if (tree->gtFlags & GTF_RELOP_NAN_UN)
+ {
+ chars += printf("[RELOP_NAN_UN]");
+ }
+ if (tree->gtFlags & GTF_RELOP_JMP_USED)
+ {
+ chars += printf("[RELOP_JMP_USED]");
+ }
+ if (tree->gtFlags & GTF_RELOP_QMARK)
+ {
+ chars += printf("[RELOP_QMARK]");
+ }
+ if (tree->gtFlags & GTF_RELOP_SMALL)
+ {
+ chars += printf("[RELOP_SMALL]");
+ }
+ break;
+
+ case GT_QMARK:
+
+ if (tree->gtFlags & GTF_QMARK_CAST_INSTOF)
+ {
+ chars += printf("[QMARK_CAST_INSTOF]");
+ }
+ break;
+
+ case GT_BOX:
+
+ if (tree->gtFlags & GTF_BOX_VALUE)
+ {
+ chars += printf("[BOX_VALUE]");
+ }
+ break;
+
+ case GT_CNS_INT:
+
+ {
+ unsigned handleKind = (tree->gtFlags & GTF_ICON_HDL_MASK);
+
+ switch (handleKind)
+ {
+
+ case GTF_ICON_SCOPE_HDL:
+
+ chars += printf("[ICON_SCOPE_HDL]");
+ break;
+
+ case GTF_ICON_CLASS_HDL:
+
+ chars += printf("[ICON_CLASS_HDL]");
+ break;
+
+ case GTF_ICON_METHOD_HDL:
+
+ chars += printf("[ICON_METHOD_HDL]");
+ break;
+
+ case GTF_ICON_FIELD_HDL:
+
+ chars += printf("[ICON_FIELD_HDL]");
+ break;
+
+ case GTF_ICON_STATIC_HDL:
+
+ chars += printf("[ICON_STATIC_HDL]");
+ break;
+
+ case GTF_ICON_STR_HDL:
+
+ chars += printf("[ICON_STR_HDL]");
+ break;
+
+ case GTF_ICON_PSTR_HDL:
+
+ chars += printf("[ICON_PSTR_HDL]");
+ break;
+
+ case GTF_ICON_PTR_HDL:
+
+ chars += printf("[ICON_PTR_HDL]");
+ break;
+
+ case GTF_ICON_VARG_HDL:
+
+ chars += printf("[ICON_VARG_HDL]");
+ break;
+
+ case GTF_ICON_PINVKI_HDL:
+
+ chars += printf("[ICON_PINVKI_HDL]");
+ break;
+
+ case GTF_ICON_TOKEN_HDL:
+
+ chars += printf("[ICON_TOKEN_HDL]");
+ break;
+
+ case GTF_ICON_TLS_HDL:
+
+ chars += printf("[ICON_TLD_HDL]");
+ break;
+
+ case GTF_ICON_FTN_ADDR:
+
+ chars += printf("[ICON_FTN_ADDR]");
+ break;
+
+ case GTF_ICON_CIDMID_HDL:
+
+ chars += printf("[ICON_CIDMID_HDL]");
+ break;
+
+ case GTF_ICON_BBC_PTR:
+
+ chars += printf("[ICON_BBC_PTR]");
+ break;
+
+ case GTF_ICON_FIELD_OFF:
+
+ chars += printf("[ICON_FIELD_OFF]");
+ break;
+ }
+ }
+ break;
+
+ case GT_OBJ:
+ case GT_STORE_OBJ:
+ if (tree->AsObj()->HasGCPtr())
+ {
+ chars += printf("[BLK_HASGCPTR]");
+ }
+ __fallthrough;
+
+ case GT_BLK:
+ case GT_DYN_BLK:
+ case GT_STORE_BLK:
+ case GT_STORE_DYN_BLK:
+
+ if (tree->gtFlags & GTF_BLK_VOLATILE)
+ {
+ chars += printf("[BLK_VOLATILE]");
+ }
+ if (tree->AsBlk()->IsUnaligned())
+ {
+ chars += printf("[BLK_UNALIGNED]");
+ }
+ break;
+
+ case GT_CALL:
+
+ if (tree->gtFlags & GTF_CALL_UNMANAGED)
+ {
+ chars += printf("[CALL_UNMANAGED]");
+ }
+ if (tree->gtFlags & GTF_CALL_INLINE_CANDIDATE)
+ {
+ chars += printf("[CALL_INLINE_CANDIDATE]");
+ }
+ if (tree->gtFlags & GTF_CALL_NONVIRT)
+ {
+ chars += printf("[CALL_NONVIRT]");
+ }
+ if (tree->gtFlags & GTF_CALL_VIRT_VTABLE)
+ {
+ chars += printf("[CALL_VIRT_VTABLE]");
+ }
+ if (tree->gtFlags & GTF_CALL_VIRT_STUB)
+ {
+ chars += printf("[CALL_VIRT_STUB]");
+ }
+ if (tree->gtFlags & GTF_CALL_NULLCHECK)
+ {
+ chars += printf("[CALL_NULLCHECK]");
+ }
+ if (tree->gtFlags & GTF_CALL_POP_ARGS)
+ {
+ chars += printf("[CALL_POP_ARGS]");
+ }
+ if (tree->gtFlags & GTF_CALL_HOISTABLE)
+ {
+ chars += printf("[CALL_HOISTABLE]");
+ }
+ if (tree->gtFlags & GTF_CALL_REG_SAVE)
+ {
+ chars += printf("[CALL_REG_SAVE]");
+ }
+
+ // More flags associated with calls.
+
+ {
+ GenTreeCall* call = tree->AsCall();
+
+ if (call->gtCallMoreFlags & GTF_CALL_M_EXPLICIT_TAILCALL)
+ {
+ chars += printf("[CALL_M_EXPLICIT_TAILCALL]");
+ }
+ if (call->gtCallMoreFlags & GTF_CALL_M_TAILCALL)
+ {
+ chars += printf("[CALL_M_TAILCALL]");
+ }
+ if (call->gtCallMoreFlags & GTF_CALL_M_VARARGS)
+ {
+ chars += printf("[CALL_M_VARARGS]");
+ }
+ if (call->gtCallMoreFlags & GTF_CALL_M_RETBUFFARG)
+ {
+ chars += printf("[CALL_M_RETBUFFARG]");
+ }
+ if (call->gtCallMoreFlags & GTF_CALL_M_DELEGATE_INV)
+ {
+ chars += printf("[CALL_M_DELEGATE_INV]");
+ }
+ if (call->gtCallMoreFlags & GTF_CALL_M_NOGCCHECK)
+ {
+ chars += printf("[CALL_M_NOGCCHECK]");
+ }
+ if (call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC)
+ {
+ chars += printf("[CALL_M_SPECIAL_INTRINSIC]");
+ }
+
+ if (call->IsUnmanaged())
+ {
+ if (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_THISCALL)
+ {
+ chars += printf("[CALL_M_UNMGD_THISCALL]");
+ }
+ }
+ else if (call->IsVirtualStub())
+ {
+ if (call->gtCallMoreFlags & GTF_CALL_M_VIRTSTUB_REL_INDIRECT)
+ {
+ chars += printf("[CALL_M_VIRTSTUB_REL_INDIRECT]");
+ }
+ }
+ else if (!call->IsVirtual())
+ {
+ if (call->gtCallMoreFlags & GTF_CALL_M_NONVIRT_SAME_THIS)
+ {
+ chars += printf("[CALL_M_NONVIRT_SAME_THIS]");
+ }
+ }
+
+ if (call->gtCallMoreFlags & GTF_CALL_M_FRAME_VAR_DEATH)
+ {
+ chars += printf("[CALL_M_FRAME_VAR_DEATH]");
+ }
+#ifndef LEGACY_BACKEND
+ if (call->gtCallMoreFlags & GTF_CALL_M_TAILCALL_VIA_HELPER)
+ {
+ chars += printf("[CALL_M_TAILCALL_VIA_HELPER]");
+ }
+#endif
+#if FEATURE_TAILCALL_OPT
+ if (call->gtCallMoreFlags & GTF_CALL_M_IMPLICIT_TAILCALL)
+ {
+ chars += printf("[CALL_M_IMPLICIT_TAILCALL]");
+ }
+#endif
+ if (call->gtCallMoreFlags & GTF_CALL_M_PINVOKE)
+ {
+ chars += printf("[CALL_M_PINVOKE]");
+ }
+ }
+ break;
+
+ case GT_STMT:
+
+ if (tree->gtFlags & GTF_STMT_CMPADD)
+ {
+ chars += printf("[STMT_CMPADD]");
+ }
+ if (tree->gtFlags & GTF_STMT_HAS_CSE)
+ {
+ chars += printf("[STMT_HAS_CSE]");
+ }
+ break;
+
+ default:
+
+ {
+ unsigned flags = (tree->gtFlags & (~(unsigned)(GTF_COMMON_MASK | GTF_OVERFLOW)));
+ if (flags != 0)
+ {
+ chars += printf("[%08X]", flags);
+ }
+ }
+ break;
+ }
+
+ // Common flags.
+
+ if (tree->gtFlags & GTF_ASG)
+ {
+ chars += printf("[ASG]");
+ }
+ if (tree->gtFlags & GTF_CALL)
+ {
+ chars += printf("[CALL]");
+ }
+ switch (op)
+ {
+ case GT_MUL:
+ case GT_CAST:
+ case GT_ADD:
+ case GT_SUB:
+ case GT_ASG_ADD:
+ case GT_ASG_SUB:
+ if (tree->gtFlags & GTF_OVERFLOW)
+ {
+ chars += printf("[OVERFLOW]");
+ }
+ break;
+ default:
+ break;
+ }
+ if (tree->gtFlags & GTF_EXCEPT)
+ {
+ chars += printf("[EXCEPT]");
+ }
+ if (tree->gtFlags & GTF_GLOB_REF)
+ {
+ chars += printf("[GLOB_REF]");
+ }
+ if (tree->gtFlags & GTF_ORDER_SIDEEFF)
+ {
+ chars += printf("[ORDER_SIDEEFF]");
+ }
+ if (tree->gtFlags & GTF_REVERSE_OPS)
+ {
+ if (op != GT_LCL_VAR)
+ {
+ chars += printf("[REVERSE_OPS]");
+ }
+ }
+ if (tree->gtFlags & GTF_REG_VAL)
+ {
+ chars += printf("[REG_VAL]");
+ }
+ if (tree->gtFlags & GTF_SPILLED)
+ {
+ chars += printf("[SPILLED_OPER]");
+ }
+#if defined(LEGACY_BACKEND)
+ if (tree->gtFlags & GTF_SPILLED_OP2)
+ {
+ chars += printf("[SPILLED_OP2]");
+ }
+#endif
+ if (tree->gtFlags & GTF_ZSF_SET)
+ {
+ chars += printf("[ZSF_SET]");
+ }
+#if FEATURE_SET_FLAGS
+ if (tree->gtFlags & GTF_SET_FLAGS)
+ {
+ if ((op != GT_IND) && (op != GT_STOREIND))
+ {
+ chars += printf("[ZSF_SET_FLAGS]");
+ }
+ }
+#endif
+ if (tree->gtFlags & GTF_IND_NONFAULTING)
+ {
+ if ((op == GT_IND) || (op == GT_STOREIND))
+ {
+ chars += printf("[IND_NONFAULTING]");
+ }
+ }
+ if (tree->gtFlags & GTF_MAKE_CSE)
+ {
+ chars += printf("[MAKE_CSE]");
+ }
+ if (tree->gtFlags & GTF_DONT_CSE)
+ {
+ chars += printf("[DONT_CSE]");
+ }
+ if (tree->gtFlags & GTF_BOOLEAN)
+ {
+ chars += printf("[BOOLEAN]");
+ }
+ if (tree->gtFlags & GTF_SMALL_OK)
+ {
+ chars += printf("[SMALL_OK]");
+ }
+ if (tree->gtFlags & GTF_UNSIGNED)
+ {
+ chars += printf("[SMALL_UNSIGNED]");
+ }
+ if (tree->gtFlags & GTF_LATE_ARG)
+ {
+ chars += printf("[SMALL_LATE_ARG]");
+ }
+ if (tree->gtFlags & GTF_SPILL)
+ {
+ chars += printf("[SPILL]");
+ }
+ if (tree->gtFlags & GTF_SPILL_HIGH)
+ {
+ chars += printf("[SPILL_HIGH]");
+ }
+ if (tree->gtFlags & GTF_REUSE_REG_VAL)
+ {
+ if (op == GT_CNS_INT)
+ {
+ chars += printf("[REUSE_REG_VAL]");
+ }
+ }
+ }
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree node flags for linear IR form
+ */
+
+int dTreeFlagsIR(GenTree* tree)
+{
+ int chars = cTreeFlagsIR(JitTls::GetCompiler(), tree);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out SSA number on tree node for linear IR form
+ */
+
+int cSsaNumIR(Compiler* comp, GenTree* tree)
+{
+ int chars = 0;
+
+ if (tree->gtLclVarCommon.HasSsaName())
+ {
+ if (tree->gtFlags & GTF_VAR_USEASG)
+ {
+ assert(tree->gtFlags & GTF_VAR_DEF);
+ chars += printf("<u:%d><d:%d>", tree->gtLclVarCommon.gtSsaNum, comp->GetSsaNumForLocalVarDef(tree));
+ }
+ else
+ {
+ chars += printf("<%s:%d>", (tree->gtFlags & GTF_VAR_DEF) ? "d" : "u", tree->gtLclVarCommon.gtSsaNum);
+ }
+ }
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out SSA number on tree node for linear IR form
+ */
+
+int dSsaNumIR(GenTree* tree)
+{
+ int chars = cSsaNumIR(JitTls::GetCompiler(), tree);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out Value Number on tree node for linear IR form
+ */
+
+int cValNumIR(Compiler* comp, GenTree* tree)
+{
+ int chars = 0;
+
+ if (tree->gtVNPair.GetLiberal() != ValueNumStore::NoVN)
+ {
+ assert(tree->gtVNPair.GetConservative() != ValueNumStore::NoVN);
+ ValueNumPair vnp = tree->gtVNPair;
+ ValueNum vn;
+ if (vnp.BothEqual())
+ {
+ chars += printf("<v:");
+ vn = vnp.GetLiberal();
+ chars += printf(STR_VN "%x", vn);
+ if (ValueNumStore::isReservedVN(vn))
+ {
+ chars += printf("R");
+ }
+ chars += printf(">");
+ }
+ else
+ {
+ vn = vnp.GetLiberal();
+ chars += printf("<v:");
+ chars += printf(STR_VN "%x", vn);
+ if (ValueNumStore::isReservedVN(vn))
+ {
+ chars += printf("R");
+ }
+ chars += printf(",");
+ vn = vnp.GetConservative();
+ chars += printf(STR_VN "%x", vn);
+ if (ValueNumStore::isReservedVN(vn))
+ {
+ chars += printf("R");
+ }
+ chars += printf(">");
+ }
+ }
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out Value Number on tree node for linear IR form
+ */
+
+int dValNumIR(GenTree* tree)
+{
+ int chars = cValNumIR(JitTls::GetCompiler(), tree);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree leaf node for linear IR form
+ */
+
+int cLeafIR(Compiler* comp, GenTree* tree)
+{
+ int chars = 0;
+ genTreeOps op = tree->OperGet();
+ const char* ilKind = nullptr;
+ const char* ilName = nullptr;
+ unsigned ilNum = 0;
+ unsigned lclNum = 0;
+ bool hasSsa = false;
+
+ switch (op)
+ {
+
+ case GT_PHI_ARG:
+ case GT_LCL_VAR:
+ case GT_LCL_VAR_ADDR:
+ case GT_STORE_LCL_VAR:
+ case GT_REG_VAR:
+
+ lclNum = tree->gtLclVarCommon.gtLclNum;
+ comp->gtGetLclVarNameInfo(lclNum, &ilKind, &ilName, &ilNum);
+ if (ilName != nullptr)
+ {
+ chars += printf("%s", ilName);
+ }
+ else
+ {
+ LclVarDsc* varDsc = comp->lvaTable + lclNum;
+ chars += printf("%s%d", ilKind, ilNum);
+ if (comp->dumpIRLocals)
+ {
+ chars += printf("(V%02u", lclNum);
+ if (varDsc->lvTracked)
+ {
+ chars += printf(":T%02u", varDsc->lvVarIndex);
+ }
+ if (comp->dumpIRRegs)
+ {
+ if (varDsc->lvRegister)
+ {
+ if (isRegPairType(varDsc->TypeGet()))
+ {
+ chars += printf(":%s:%s",
+ getRegName(varDsc->lvOtherReg), // hi32
+ getRegName(varDsc->lvRegNum)); // lo32
+ }
+ else
+ {
+ chars += printf(":%s", getRegName(varDsc->lvRegNum));
+ }
+ }
+ else
+ {
+ switch (tree->GetRegTag())
+ {
+ case GenTree::GT_REGTAG_REG:
+ chars += printf(":%s", comp->compRegVarName(tree->gtRegNum));
+ break;
+#if CPU_LONG_USES_REGPAIR
+ case GenTree::GT_REGTAG_REGPAIR:
+ chars += printf(":%s", comp->compRegPairName(tree->gtRegPair));
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+ chars += printf(")");
+ }
+ else if (comp->dumpIRRegs)
+ {
+ if (varDsc->lvRegister)
+ {
+ chars += printf("(");
+ if (isRegPairType(varDsc->TypeGet()))
+ {
+ chars += printf("%s:%s",
+ getRegName(varDsc->lvOtherReg), // hi32
+ getRegName(varDsc->lvRegNum)); // lo32
+ }
+ else
+ {
+ chars += printf("%s", getRegName(varDsc->lvRegNum));
+ }
+ chars += printf(")");
+ }
+ else
+ {
+ switch (tree->GetRegTag())
+ {
+ case GenTree::GT_REGTAG_REG:
+ chars += printf("(%s)", comp->compRegVarName(tree->gtRegNum));
+ break;
+#if CPU_LONG_USES_REGPAIR
+ case GenTree::GT_REGTAG_REGPAIR:
+ chars += printf("(%s)", comp->compRegPairName(tree->gtRegPair));
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (op == GT_REG_VAR)
+ {
+ if (isFloatRegType(tree->gtType))
+ {
+ assert(tree->gtRegVar.gtRegNum == tree->gtRegNum);
+ chars += printf("(FPV%u)", tree->gtRegNum);
+ }
+ else
+ {
+ chars += printf("(%s)", comp->compRegVarName(tree->gtRegVar.gtRegNum));
+ }
+ }
+
+ hasSsa = true;
+ break;
+
+ case GT_LCL_FLD:
+ case GT_LCL_FLD_ADDR:
+ case GT_STORE_LCL_FLD:
+
+ lclNum = tree->gtLclVarCommon.gtLclNum;
+ comp->gtGetLclVarNameInfo(lclNum, &ilKind, &ilName, &ilNum);
+ if (ilName != nullptr)
+ {
+ chars += printf("%s+%u", ilName, tree->gtLclFld.gtLclOffs);
+ }
+ else
+ {
+ chars += printf("%s%d+%u", ilKind, ilNum, tree->gtLclFld.gtLclOffs);
+ LclVarDsc* varDsc = comp->lvaTable + lclNum;
+ if (comp->dumpIRLocals)
+ {
+ chars += printf("(V%02u", lclNum);
+ if (varDsc->lvTracked)
+ {
+ chars += printf(":T%02u", varDsc->lvVarIndex);
+ }
+ if (comp->dumpIRRegs)
+ {
+ if (varDsc->lvRegister)
+ {
+ if (isRegPairType(varDsc->TypeGet()))
+ {
+ chars += printf(":%s:%s",
+ getRegName(varDsc->lvOtherReg), // hi32
+ getRegName(varDsc->lvRegNum)); // lo32
+ }
+ else
+ {
+ chars += printf(":%s", getRegName(varDsc->lvRegNum));
+ }
+ }
+ else
+ {
+ switch (tree->GetRegTag())
+ {
+ case GenTree::GT_REGTAG_REG:
+ chars += printf(":%s", comp->compRegVarName(tree->gtRegNum));
+ break;
+#if CPU_LONG_USES_REGPAIR
+ case GenTree::GT_REGTAG_REGPAIR:
+ chars += printf(":%s", comp->compRegPairName(tree->gtRegPair));
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+ chars += printf(")");
+ }
+ else if (comp->dumpIRRegs)
+ {
+ if (varDsc->lvRegister)
+ {
+ chars += printf("(");
+ if (isRegPairType(varDsc->TypeGet()))
+ {
+ chars += printf("%s:%s",
+ getRegName(varDsc->lvOtherReg), // hi32
+ getRegName(varDsc->lvRegNum)); // lo32
+ }
+ else
+ {
+ chars += printf("%s", getRegName(varDsc->lvRegNum));
+ }
+ chars += printf(")");
+ }
+ else
+ {
+ switch (tree->GetRegTag())
+ {
+ case GenTree::GT_REGTAG_REG:
+ chars += printf("(%s)", comp->compRegVarName(tree->gtRegNum));
+ break;
+#if CPU_LONG_USES_REGPAIR
+ case GenTree::GT_REGTAG_REGPAIR:
+ chars += printf("(%s)", comp->compRegPairName(tree->gtRegPair));
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // TODO: We probably want to expand field sequence.
+ // gtDispFieldSeq(tree->gtLclFld.gtFieldSeq);
+
+ hasSsa = true;
+ break;
+
+ case GT_CNS_INT:
+
+ if (tree->IsIconHandle())
+ {
+#if 0
+ // TODO: Commented out because sometimes the CLR throws
+ // and exception when asking the names of some handles.
+ // Need to investigate.
+
+ const char* className;
+ const char* fieldName;
+ const char* methodName;
+ const wchar_t* str;
+
+ switch (tree->GetIconHandleFlag())
+ {
+
+ case GTF_ICON_SCOPE_HDL:
+
+ chars += printf("SCOPE(?)");
+ break;
+
+ case GTF_ICON_CLASS_HDL:
+
+ className = comp->eeGetClassName((CORINFO_CLASS_HANDLE)tree->gtIntCon.gtIconVal);
+ chars += printf("CLASS(%s)", className);
+ break;
+
+ case GTF_ICON_METHOD_HDL:
+
+ methodName = comp->eeGetMethodName((CORINFO_METHOD_HANDLE)tree->gtIntCon.gtIconVal,
+ &className);
+ chars += printf("METHOD(%s.%s)", className, methodName);
+ break;
+
+ case GTF_ICON_FIELD_HDL:
+
+ fieldName = comp->eeGetFieldName((CORINFO_FIELD_HANDLE)tree->gtIntCon.gtIconVal,
+ &className);
+ chars += printf("FIELD(%s.%s) ", className, fieldName);
+ break;
+
+ case GTF_ICON_STATIC_HDL:
+
+ fieldName = comp->eeGetFieldName((CORINFO_FIELD_HANDLE)tree->gtIntCon.gtIconVal,
+ &className);
+ chars += printf("STATIC_FIELD(%s.%s)", className, fieldName);
+ break;
+
+ case GTF_ICON_STR_HDL:
+
+ str = comp->eeGetCPString(tree->gtIntCon.gtIconVal);
+ chars += printf("\"%S\"", str);
+ break;
+
+ case GTF_ICON_PSTR_HDL:
+
+ chars += printf("PSTR(?)");
+ break;
+
+ case GTF_ICON_PTR_HDL:
+
+ chars += printf("PTR(?)");
+ break;
+
+ case GTF_ICON_VARG_HDL:
+
+ chars += printf("VARARG(?)");
+ break;
+
+ case GTF_ICON_PINVKI_HDL:
+
+ chars += printf("PINVOKE(?)");
+ break;
+
+ case GTF_ICON_TOKEN_HDL:
+
+ chars += printf("TOKEN(%08X)", tree->gtIntCon.gtIconVal);
+ break;
+
+ case GTF_ICON_TLS_HDL:
+
+ chars += printf("TLS(?)");
+ break;
+
+ case GTF_ICON_FTN_ADDR:
+
+ chars += printf("FTN(?)");
+ break;
+
+ case GTF_ICON_CIDMID_HDL:
+
+ chars += printf("CIDMID(?)");
+ break;
+
+ case GTF_ICON_BBC_PTR:
+
+ chars += printf("BBC(?)");
+ break;
+
+ default:
+
+ chars += printf("HANDLE(?)");
+ break;
+ }
+#else
+#ifdef _TARGET_64BIT_
+ if ((tree->gtIntCon.gtIconVal & 0xFFFFFFFF00000000LL) != 0)
+ {
+ chars += printf("HANDLE(0x%llx)", dspPtr(tree->gtIntCon.gtIconVal));
+ }
+ else
+#endif
+ {
+ chars += printf("HANDLE(0x%0x)", dspPtr(tree->gtIntCon.gtIconVal));
+ }
+#endif
+ }
+ else
+ {
+ if (tree->TypeGet() == TYP_REF)
+ {
+ assert(tree->gtIntCon.gtIconVal == 0);
+ chars += printf("null");
+ }
+#ifdef _TARGET_64BIT_
+ else if ((tree->gtIntCon.gtIconVal & 0xFFFFFFFF00000000LL) != 0)
+ {
+ chars += printf("0x%llx", tree->gtIntCon.gtIconVal);
+ }
+ else
+#endif
+ {
+ chars += printf("%ld(0x%x)", tree->gtIntCon.gtIconVal, tree->gtIntCon.gtIconVal);
+ }
+ }
+ break;
+
+ case GT_CNS_LNG:
+
+ chars += printf("CONST(LONG)");
+ break;
+
+ case GT_CNS_DBL:
+
+ chars += printf("CONST(DOUBLE)");
+ break;
+
+ case GT_CNS_STR:
+
+ chars += printf("CONST(STR)");
+ break;
+
+ case GT_JMP:
+
+ {
+ const char* methodName;
+ const char* className;
+
+ methodName = comp->eeGetMethodName((CORINFO_METHOD_HANDLE)tree->gtVal.gtVal1, &className);
+ chars += printf(" %s.%s", className, methodName);
+ }
+ break;
+
+ case GT_NO_OP:
+ case GT_START_NONGC:
+ case GT_PROF_HOOK:
+ case GT_CATCH_ARG:
+ case GT_MEMORYBARRIER:
+ case GT_ARGPLACE:
+ case GT_PINVOKE_PROLOG:
+#ifndef LEGACY_BACKEND
+ case GT_JMPTABLE:
+#endif
+ // Do nothing.
+ break;
+
+ case GT_RET_EXPR:
+
+ chars += printf("t%d", tree->gtRetExpr.gtInlineCandidate->gtTreeID);
+ break;
+
+ case GT_PHYSREG:
+
+ chars += printf("%s", getRegName(tree->gtPhysReg.gtSrcReg, varTypeIsFloating(tree)));
+ break;
+
+ case GT_LABEL:
+
+ if (tree->gtLabel.gtLabBB)
+ {
+ chars += printf("BB%02u", tree->gtLabel.gtLabBB->bbNum);
+ }
+ else
+ {
+ chars += printf("BB?");
+ }
+ break;
+
+ case GT_IL_OFFSET:
+
+ if (tree->gtStmt.gtStmtILoffsx == BAD_IL_OFFSET)
+ {
+ chars += printf("?");
+ }
+ else
+ {
+ chars += printf("0x%x", jitGetILoffs(tree->gtStmt.gtStmtILoffsx));
+ }
+ break;
+
+ case GT_CLS_VAR:
+ case GT_CLS_VAR_ADDR:
+ default:
+
+ if (tree->OperIsLeaf())
+ {
+ chars += printf("<leaf nyi: %s>", tree->OpName(tree->OperGet()));
+ }
+
+ chars += printf("t%d", tree->gtTreeID);
+ break;
+ }
+
+ if (comp->dumpIRTypes)
+ {
+ chars += cTreeTypeIR(comp, tree);
+ }
+ if (comp->dumpIRValnums)
+ {
+ chars += cValNumIR(comp, tree);
+ }
+ if (hasSsa && comp->dumpIRSsa)
+ {
+ chars += cSsaNumIR(comp, tree);
+ }
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree leaf node for linear IR form
+ */
+
+int dLeafIR(GenTree* tree)
+{
+ int chars = cLeafIR(JitTls::GetCompiler(), tree);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree indir node for linear IR form
+ */
+
+int cIndirIR(Compiler* comp, GenTree* tree)
+{
+ assert(tree->gtOper == GT_IND);
+
+ int chars = 0;
+ GenTree* child;
+
+ chars += printf("[");
+ child = tree->GetChild(0);
+ chars += cLeafIR(comp, child);
+ chars += printf("]");
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree indir node for linear IR form
+ */
+
+int dIndirIR(GenTree* tree)
+{
+ int chars = cIndirIR(JitTls::GetCompiler(), tree);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree operand node for linear IR form
+ */
+
+int cOperandIR(Compiler* comp, GenTree* operand)
+{
+ int chars = 0;
+
+ if (operand == nullptr)
+ {
+ chars += printf("t?");
+ return chars;
+ }
+
+ bool dumpTypes = comp->dumpIRTypes;
+ bool dumpValnums = comp->dumpIRValnums;
+ bool foldIndirs = comp->dumpIRDataflow;
+ bool foldLeafs = comp->dumpIRNoLeafs;
+ bool foldCommas = comp->dumpIRDataflow;
+ bool dumpDataflow = comp->dumpIRDataflow;
+ bool foldLists = comp->dumpIRNoLists;
+ bool dumpRegs = comp->dumpIRRegs;
+
+ genTreeOps op = operand->OperGet();
+
+ if (foldLeafs && operand->OperIsLeaf())
+ {
+ if ((op == GT_ARGPLACE) && foldLists)
+ {
+ return chars;
+ }
+ chars += cLeafIR(comp, operand);
+ }
+ else if (dumpDataflow && (operand->OperIsAssignment() || (op == GT_STORE_LCL_VAR) || (op == GT_STORE_LCL_FLD)))
+ {
+ operand = operand->GetChild(0);
+ chars += cOperandIR(comp, operand);
+ }
+ else if ((op == GT_INDEX) && foldIndirs)
+ {
+ chars += printf("[t%d]", operand->gtTreeID);
+ if (dumpTypes)
+ {
+ chars += cTreeTypeIR(comp, operand);
+ }
+ if (dumpValnums)
+ {
+ chars += cValNumIR(comp, operand);
+ }
+ }
+ else if ((op == GT_IND) && foldIndirs)
+ {
+ chars += cIndirIR(comp, operand);
+ if (dumpTypes)
+ {
+ chars += cTreeTypeIR(comp, operand);
+ }
+ if (dumpValnums)
+ {
+ chars += cValNumIR(comp, operand);
+ }
+ }
+ else if ((op == GT_COMMA) && foldCommas)
+ {
+ operand = operand->GetChild(1);
+ chars += cOperandIR(comp, operand);
+ }
+ else if ((op == GT_LIST) && foldLists)
+ {
+ GenTree* list = operand;
+ unsigned childCount = list->NumChildren();
+
+ operand = list->GetChild(0);
+ int operandChars = cOperandIR(comp, operand);
+ chars += operandChars;
+ if (childCount > 1)
+ {
+ if (operandChars > 0)
+ {
+ chars += printf(", ");
+ }
+ operand = list->GetChild(1);
+ if (operand->gtOper == GT_LIST)
+ {
+ chars += cListIR(comp, operand);
+ }
+ else
+ {
+ chars += cOperandIR(comp, operand);
+ }
+ }
+ }
+ else
+ {
+ chars += printf("t%d", operand->gtTreeID);
+ if (dumpRegs)
+ {
+ regNumber regNum = operand->GetReg();
+ if (regNum != REG_NA)
+ {
+ chars += printf("(%s)", getRegName(regNum));
+ }
+ }
+ if (dumpTypes)
+ {
+ chars += cTreeTypeIR(comp, operand);
+ }
+ if (dumpValnums)
+ {
+ chars += cValNumIR(comp, operand);
+ }
+ }
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree operand node for linear IR form
+ */
+
+int dOperandIR(GenTree* operand)
+{
+ int chars = cOperandIR(JitTls::GetCompiler(), operand);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree list of nodes for linear IR form
+ */
+
+int cListIR(Compiler* comp, GenTree* list)
+{
+ int chars = 0;
+ int operandChars;
+
+ assert(list->gtOper == GT_LIST);
+
+ GenTree* child;
+ unsigned childCount;
+
+ childCount = list->NumChildren();
+ assert(childCount == 1 || childCount == 2);
+
+ operandChars = 0;
+ for (unsigned childIndex = 0; childIndex < childCount; childIndex++)
+ {
+ if ((childIndex > 0) && (operandChars > 0))
+ {
+ chars += printf(", ");
+ }
+
+ child = list->GetChild(childIndex);
+ operandChars = cOperandIR(comp, child);
+ chars += operandChars;
+ }
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree list of nodes for linear IR form
+ */
+
+int dListIR(GenTree* list)
+{
+ int chars = cListIR(JitTls::GetCompiler(), list);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree dependencies based on comma nodes for linear IR form
+ */
+
+int cDependsIR(Compiler* comp, GenTree* comma, bool* first)
+{
+ int chars = 0;
+
+ assert(comma->gtOper == GT_COMMA);
+
+ GenTree* child;
+
+ child = comma->GetChild(0);
+ if (child->gtOper == GT_COMMA)
+ {
+ chars += cDependsIR(comp, child, first);
+ }
+ else
+ {
+ if (!(*first))
+ {
+ chars += printf(", ");
+ }
+ chars += printf("t%d", child->gtTreeID);
+ *first = false;
+ }
+
+ child = comma->GetChild(1);
+ if (child->gtOper == GT_COMMA)
+ {
+ chars += cDependsIR(comp, child, first);
+ }
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree dependencies based on comma nodes for linear IR form
+ */
+
+int dDependsIR(GenTree* comma)
+{
+ int chars = 0;
+ bool first = TRUE;
+
+ chars = cDependsIR(JitTls::GetCompiler(), comma, &first);
+
+ return chars;
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree node in linear IR form
+ */
+
+void cNodeIR(Compiler* comp, GenTree* tree)
+{
+ bool foldLeafs = comp->dumpIRNoLeafs;
+ bool foldIndirs = comp->dumpIRDataflow;
+ bool foldLists = comp->dumpIRNoLists;
+ bool dataflowView = comp->dumpIRDataflow;
+ bool dumpTypes = comp->dumpIRTypes;
+ bool dumpValnums = comp->dumpIRValnums;
+ bool noStmts = comp->dumpIRNoStmts;
+ genTreeOps op = tree->OperGet();
+ unsigned childCount = tree->NumChildren();
+ GenTree* child;
+
+ // What are we skipping?
+
+ if (tree->OperIsLeaf())
+ {
+ if (foldLeafs)
+ {
+ return;
+ }
+ }
+ else if (op == GT_IND)
+ {
+ if (foldIndirs)
+ {
+ return;
+ }
+ }
+ else if (op == GT_LIST)
+ {
+ if (foldLists)
+ {
+ return;
+ }
+ }
+ else if (op == GT_STMT)
+ {
+ if (noStmts)
+ {
+ if (dataflowView)
+ {
+ child = tree->GetChild(0);
+ if (child->gtOper != GT_COMMA)
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+ }
+ }
+ else if (op == GT_COMMA)
+ {
+ if (dataflowView)
+ {
+ return;
+ }
+ }
+
+ bool nodeIsValue = tree->IsValue();
+
+ // Dump tree id or dataflow destination.
+
+ int chars = 0;
+
+ // if (comp->compRationalIRForm)
+ // {
+ // chars += printf("R");
+ // }
+
+ chars += printf(" ");
+ if (dataflowView && tree->OperIsAssignment())
+ {
+ child = tree->GetChild(0);
+ chars += cOperandIR(comp, child);
+ }
+ else if (dataflowView && ((op == GT_STORE_LCL_VAR) || (op == GT_STORE_LCL_FLD)))
+ {
+ chars += cLeafIR(comp, tree);
+ }
+ else if (dataflowView && (op == GT_STOREIND))
+ {
+ child = tree->GetChild(0);
+ chars += printf("[");
+ chars += cOperandIR(comp, child);
+ chars += printf("]");
+ if (dumpTypes)
+ {
+ chars += cTreeTypeIR(comp, tree);
+ }
+ if (dumpValnums)
+ {
+ chars += cValNumIR(comp, tree);
+ }
+ }
+ else if (nodeIsValue)
+ {
+ chars += printf("t%d", tree->gtTreeID);
+ if (comp->dumpIRRegs)
+ {
+ regNumber regNum = tree->GetReg();
+ if (regNum != REG_NA)
+ {
+ chars += printf("(%s)", getRegName(regNum));
+ }
+ }
+ if (dumpTypes)
+ {
+ chars += cTreeTypeIR(comp, tree);
+ }
+ if (dumpValnums)
+ {
+ chars += cValNumIR(comp, tree);
+ }
+ }
+
+ // Dump opcode and tree ID if need in dataflow view.
+
+ chars += dTabStopIR(chars, COLUMN_OPCODE);
+ const char* opName = tree->OpName(op);
+ chars += printf(" %c %s", nodeIsValue ? '=' : ' ', opName);
+
+ if (dataflowView)
+ {
+ if (tree->OperIsAssignment() || (op == GT_STORE_LCL_VAR) || (op == GT_STORE_LCL_FLD) || (op == GT_STOREIND))
+ {
+ chars += printf("(t%d)", tree->gtTreeID);
+ }
+ }
+
+ // Dump modifiers for opcodes to help with readability
+
+ if (op == GT_CALL)
+ {
+ GenTreeCall* call = tree->AsCall();
+
+ if (call->gtCallType == CT_USER_FUNC)
+ {
+ if (call->IsVirtualStub())
+ {
+ chars += printf(":VS");
+ }
+ else if (call->IsVirtualVtable())
+ {
+ chars += printf(":VT");
+ }
+ else if (call->IsVirtual())
+ {
+ chars += printf(":V");
+ }
+ }
+ else if (call->gtCallType == CT_HELPER)
+ {
+ chars += printf(":H");
+ }
+ else if (call->gtCallType == CT_INDIRECT)
+ {
+ chars += printf(":I");
+ }
+ else if (call->IsUnmanaged())
+ {
+ chars += printf(":U");
+ }
+ else
+ {
+ if (call->IsVirtualStub())
+ {
+ chars += printf(":XVS");
+ }
+ else if (call->IsVirtualVtable())
+ {
+ chars += printf(":XVT");
+ }
+ else
+ {
+ chars += printf(":?");
+ }
+ }
+
+ if (call->IsUnmanaged())
+ {
+ if (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_THISCALL)
+ {
+ chars += printf(":T");
+ }
+ }
+
+ if (tree->gtFlags & GTF_CALL_NULLCHECK)
+ {
+ chars += printf(":N");
+ }
+ }
+ else if (op == GT_INTRINSIC)
+ {
+ CorInfoIntrinsics intrin = tree->gtIntrinsic.gtIntrinsicId;
+
+ chars += printf(":");
+ switch (intrin)
+ {
+ case CORINFO_INTRINSIC_Sin:
+ chars += printf("Sin");
+ break;
+ case CORINFO_INTRINSIC_Cos:
+ chars += printf("Cos");
+ break;
+ case CORINFO_INTRINSIC_Sqrt:
+ chars += printf("Sqrt");
+ break;
+ case CORINFO_INTRINSIC_Cosh:
+ chars += printf("Cosh");
+ break;
+ case CORINFO_INTRINSIC_Sinh:
+ chars += printf("Sinh");
+ break;
+ case CORINFO_INTRINSIC_Tan:
+ chars += printf("Tan");
+ break;
+ case CORINFO_INTRINSIC_Tanh:
+ chars += printf("Tanh");
+ break;
+ case CORINFO_INTRINSIC_Asin:
+ chars += printf("Asin");
+ break;
+ case CORINFO_INTRINSIC_Acos:
+ chars += printf("Acos");
+ break;
+ case CORINFO_INTRINSIC_Atan:
+ chars += printf("Atan");
+ break;
+ case CORINFO_INTRINSIC_Atan2:
+ chars += printf("Atan2");
+ break;
+ case CORINFO_INTRINSIC_Log10:
+ chars += printf("Log10");
+ break;
+ case CORINFO_INTRINSIC_Pow:
+ chars += printf("Pow");
+ break;
+ case CORINFO_INTRINSIC_Exp:
+ chars += printf("Exp");
+ break;
+ case CORINFO_INTRINSIC_Ceiling:
+ chars += printf("Ceiling");
+ break;
+ case CORINFO_INTRINSIC_Floor:
+ chars += printf("Floor");
+ break;
+ default:
+ chars += printf("unknown(%d)", intrin);
+ break;
+ }
+ }
+
+ // Dump operands.
+
+ chars += dTabStopIR(chars, COLUMN_OPERANDS);
+
+ // Dump operator specific fields as operands
+
+ switch (op)
+ {
+ default:
+ break;
+ case GT_FIELD:
+
+ {
+ const char* className = nullptr;
+ const char* fieldName = comp->eeGetFieldName(tree->gtField.gtFldHnd, &className);
+
+ chars += printf(" %s.%s", className, fieldName);
+ }
+ break;
+
+ case GT_CALL:
+
+ if (tree->gtCall.gtCallType != CT_INDIRECT)
+ {
+ const char* methodName;
+ const char* className;
+
+ methodName = comp->eeGetMethodName(tree->gtCall.gtCallMethHnd, &className);
+
+ chars += printf(" %s.%s", className, methodName);
+ }
+ break;
+
+ case GT_STORE_LCL_VAR:
+ case GT_STORE_LCL_FLD:
+
+ if (!dataflowView)
+ {
+ chars += printf(" ");
+ chars += cLeafIR(comp, tree);
+ }
+ break;
+
+ case GT_STORE_CLS_VAR:
+
+ chars += printf(" ???");
+ break;
+
+ case GT_LEA:
+
+ GenTreeAddrMode* lea = tree->AsAddrMode();
+ GenTree* base = lea->Base();
+ GenTree* index = lea->Index();
+ unsigned scale = lea->gtScale;
+ unsigned offset = lea->gtOffset;
+
+ chars += printf(" [");
+ if (base != nullptr)
+ {
+ chars += cOperandIR(comp, base);
+ }
+ if (index != nullptr)
+ {
+ if (base != nullptr)
+ {
+ chars += printf("+");
+ }
+ chars += cOperandIR(comp, index);
+ if (scale > 1)
+ {
+ chars += printf("*%u", scale);
+ }
+ }
+ if ((offset != 0) || ((base == nullptr) && (index == nullptr)))
+ {
+ if ((base != nullptr) || (index != nullptr))
+ {
+ chars += printf("+");
+ }
+ chars += printf("%u", offset);
+ }
+ chars += printf("]");
+ break;
+ }
+
+ // Dump operands.
+
+ if (tree->OperIsLeaf())
+ {
+ chars += printf(" ");
+ chars += cLeafIR(comp, tree);
+ }
+ else if (op == GT_LEA)
+ {
+ // Already dumped it above.
+ }
+ else if (op == GT_PHI)
+ {
+ if (tree->gtOp.gtOp1 != nullptr)
+ {
+ bool first = true;
+ for (GenTreeArgList* args = tree->gtOp.gtOp1->AsArgList(); args != nullptr; args = args->Rest())
+ {
+ child = args->Current();
+ if (!first)
+ {
+ chars += printf(",");
+ }
+ first = false;
+ chars += printf(" ");
+ chars += cOperandIR(comp, child);
+ }
+ }
+ }
+ else
+ {
+ bool hasComma = false;
+ bool first = true;
+ int operandChars = 0;
+ for (unsigned childIndex = 0; childIndex < childCount; childIndex++)
+ {
+ child = tree->GetChild(childIndex);
+ if (child == nullptr)
+ {
+ continue;
+ }
+
+ if (child->gtOper == GT_COMMA)
+ {
+ hasComma = true;
+ }
+
+ if (dataflowView && (childIndex == 0))
+ {
+ if ((op == GT_ASG) || (op == GT_STOREIND))
+ {
+ continue;
+ }
+ }
+
+ if (!first)
+ {
+ chars += printf(",");
+ }
+
+ bool isList = (child->gtOper == GT_LIST);
+ if (!isList || !foldLists)
+ {
+ if (foldLeafs && (child->gtOper == GT_ARGPLACE))
+ {
+ continue;
+ }
+ chars += printf(" ");
+ operandChars = cOperandIR(comp, child);
+ chars += operandChars;
+ if (operandChars > 0)
+ {
+ first = false;
+ }
+ }
+ else
+ {
+ assert(isList);
+ chars += printf(" ");
+ operandChars = cOperandIR(comp, child);
+ chars += operandChars;
+ if (operandChars > 0)
+ {
+ first = false;
+ }
+ }
+ }
+
+ if (dataflowView && hasComma)
+ {
+ chars += printf(", DEPS(");
+ first = true;
+ for (unsigned childIndex = 0; childIndex < childCount; childIndex++)
+ {
+ child = tree->GetChild(childIndex);
+ if (child->gtOper == GT_COMMA)
+ {
+ chars += cDependsIR(comp, child, &first);
+ }
+ }
+ chars += printf(")");
+ }
+ }
+
+ // Dump kinds, flags, costs
+
+ if (comp->dumpIRKinds || comp->dumpIRFlags || comp->dumpIRCosts)
+ {
+ chars += dTabStopIR(chars, COLUMN_KINDS);
+ chars += printf(";");
+ if (comp->dumpIRKinds)
+ {
+ chars += printf(" ");
+ chars += cTreeKindsIR(comp, tree);
+ }
+ if (comp->dumpIRFlags && (tree->gtFlags != 0))
+ {
+ if (comp->dumpIRKinds)
+ {
+ chars += dTabStopIR(chars, COLUMN_FLAGS);
+ }
+ else
+ {
+ chars += printf(" ");
+ }
+ chars += cTreeFlagsIR(comp, tree);
+ }
+ if (comp->dumpIRCosts && (tree->gtCostsInitialized))
+ {
+ chars += printf(" CostEx=%d, CostSz=%d", tree->GetCostEx(), tree->GetCostSz());
+ }
+ }
+
+ printf("\n");
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree in linear IR form
+ */
+
+void cTreeIR(Compiler* comp, GenTree* tree)
+{
+ bool foldLeafs = comp->dumpIRNoLeafs;
+ bool foldIndirs = comp->dumpIRDataflow;
+ bool foldLists = comp->dumpIRNoLists;
+ bool dataflowView = comp->dumpIRDataflow;
+ bool dumpTypes = comp->dumpIRTypes;
+ bool dumpValnums = comp->dumpIRValnums;
+ bool noStmts = comp->dumpIRNoStmts;
+ genTreeOps op = tree->OperGet();
+ unsigned childCount = tree->NumChildren();
+ GenTree* child;
+
+ // Recurse and dump trees that this node depends on.
+
+ if (tree->OperIsLeaf())
+ {
+ }
+ else if (tree->OperIsBinary() && tree->IsReverseOp())
+ {
+ child = tree->GetChild(1);
+ cTreeIR(comp, child);
+ child = tree->GetChild(0);
+ cTreeIR(comp, child);
+ }
+ else if (op == GT_PHI)
+ {
+ // Don't recurse.
+ }
+ else
+ {
+ assert(!tree->IsReverseOp());
+ for (unsigned childIndex = 0; childIndex < childCount; childIndex++)
+ {
+ child = tree->GetChild(childIndex);
+ if (child != nullptr)
+ {
+ cTreeIR(comp, child);
+ }
+ }
+ }
+
+ cNodeIR(comp, tree);
+}
+
+/*****************************************************************************
+ *
+ * COMPlus_JitDumpIR support - dump out tree in linear IR form
+ */
+
+void dTreeIR(GenTree* tree)
+{
+ cTreeIR(JitTls::GetCompiler(), tree);
+}
+
+#endif // DEBUG
+
+#if VARSET_COUNTOPS
+// static
+BitSetSupport::BitSetOpCounter Compiler::m_varsetOpCounter("VarSetOpCounts.log");
+#endif
+#if ALLVARSET_COUNTOPS
+// static
+BitSetSupport::BitSetOpCounter Compiler::m_allvarsetOpCounter("AllVarSetOpCounts.log");
+#endif
+
+// static
+HelperCallProperties Compiler::s_helperCallProperties;
+
+/*****************************************************************************/
+/*****************************************************************************/