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.cpp3294
1 files changed, 3264 insertions, 30 deletions
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index b54657202a..d56a79a203 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -24,6 +24,16 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#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) || MEASURE_INLINING
unsigned Compiler::jitTotalMethodCompiled = 0;
unsigned Compiler::jitTotalMethodInlined = 0;
@@ -373,6 +383,151 @@ histo loopExitCountTable(DefaultAllocator::Singleton(), loopExitCountBucke
#endif // COUNT_LOOPS
+//------------------------------------------------------------------------
+// argOrReturnTypeForStruct: Get the "primitive" type, if any, that is used to pass or return
+// values of the given struct type.
+//
+// Arguments:
+// clsHnd - the handle for the struct type
+// forReturn - true if we asking for this in a GT_RETURN context
+// false if we are asking for this in a parameter passing context
+//
+// Return Value:
+// The primitive type used to pass or return the struct, if applicable, or
+// TYP_UNKNOWN otherwise.
+//
+// Assumptions:
+// The given class handle must be for a value type (struct).
+//
+// Notes:
+// Most of the work is done by the method of the same name that takes the
+// size of the struct.
+
+
+var_types Compiler::argOrReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, bool forReturn)
+{
+ unsigned size = info.compCompHnd->getClassSize(clsHnd);
+ return argOrReturnTypeForStruct(size, clsHnd, forReturn);
+}
+
+//------------------------------------------------------------------------
+// argOrReturnTypeForStruct: Get the "primitive" type, if any, that is used to pass or return
+// values of the given struct type.
+//
+// Arguments:
+// size - the size of the struct type
+// clsHnd - the handle for the struct type
+// forReturn - true if we asking for this in a GT_RETURN context
+// false if we are asking for this in a parameter passing context
+//
+// Return Value:
+// The primitive type used to pass or return the struct, if applicable, or
+// TYP_UNKNOWN otherwise.
+//
+// Assumptions:
+// The size must be the size of the given type.
+// The given class handle must be for a value type (struct).
+//
+// Notes:
+// Some callers call into this method directly, instead of the above method,
+// when they have already determined the size.
+// This is to avoid a redundant call across the JIT/EE interface.
+
+var_types Compiler::argOrReturnTypeForStruct(unsigned size, CORINFO_CLASS_HANDLE clsHnd, bool forReturn)
+{
+ BYTE gcPtr = 0;
+ var_types useType = TYP_UNKNOWN;
+
+ switch (size)
+ {
+ case 1:
+ useType = TYP_BYTE;
+ break;
+
+ case 2:
+ useType = TYP_SHORT;
+ break;
+
+#ifndef _TARGET_XARCH_
+ case 3:
+ useType = TYP_INT;
+ break;
+#endif // _TARGET_AMD64_
+
+#ifdef _TARGET_64BIT_
+ case 4:
+ useType = TYP_INT;
+ break;
+#endif // _TARGET_64BIT_
+
+ // Pointer size
+#ifdef _TARGET_64BIT_
+#ifndef _TARGET_AMD64_
+ case 5:
+ case 6:
+ case 7:
+#endif // _TARGET_AMD64_
+ case 8:
+#else // !_TARGET_64BIT_
+ case 4:
+#endif // !_TARGET_64BIT_
+ info.compCompHnd->getClassGClayout(clsHnd, &gcPtr);
+ if (gcPtr == TYPE_GC_NONE)
+ {
+ useType = TYP_I_IMPL;
+ }
+ else if (gcPtr == TYPE_GC_REF)
+ {
+ useType = TYP_REF;
+ }
+ else if (gcPtr == TYPE_GC_BYREF)
+ {
+ useType = TYP_BYREF;
+ }
+ else
+ {
+ assert(!"Bad value of CorInfoGCType");
+ }
+ break;
+
+ default:
+#if FEATURE_MULTIREG_STRUCT_RET
+ if (forReturn)
+ {
+ if (size <= MAX_RET_MULTIREG_BYTES)
+ {
+#ifdef _TARGET_ARM64_
+ assert(size > TARGET_POINTER_SIZE);
+
+ // For structs that are 9 to 16 bytes in size set useType to TYP_STRUCT,
+ // as this means a 9-16 byte struct value in two registers
+ //
+ useType = TYP_STRUCT;
+#endif // _TARGET_ARM64_
+ }
+ }
+#endif // FEATURE_MULTIREG_STRUCT_RET
+
+#if FEATURE_MULTIREG_STRUCT_ARGS
+ if (!forReturn)
+ {
+ if (size <= MAX_PASS_MULTIREG_BYTES)
+ {
+#ifdef _TARGET_ARM64_
+ assert(size > TARGET_POINTER_SIZE);
+
+ // For structs that are 9 to 16 bytes in size set useType to TYP_STRUCT,
+ // as this means a 9-16 byte struct value in two registers
+ //
+ useType = TYP_STRUCT;
+#endif // _TARGET_ARM64_
+ }
+ }
+#endif // FEATURE_MULTIREG_STRUCT_ARGS
+ break;
+ }
+ return useType;
+}
/*****************************************************************************
* variables to keep track of how many iterations we go in a dataflow pass
@@ -476,7 +631,7 @@ void Compiler::compStartup()
/* static */
void Compiler::compShutdown()
-{
+{
#ifdef ALT_JIT
if (s_pAltJitExcludeAssembliesList != nullptr)
{
@@ -931,9 +1086,7 @@ void Compiler::compDisplayStaticSizes(FILE* fout)
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));
-#if INLINE_MATH
- fprintf(fout, "Size of GenTreeMath = %3u\n", sizeof(GenTreeMath));
-#endif // INLINE_MATH
+ 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));
@@ -1049,6 +1202,7 @@ void Compiler::compInit(norls_allocator * pAlloc, InlineInfo * in
eeInfoInitialized = false;
+ compDoAggressiveInlining = false;
if (compIsForInlining())
{
@@ -1079,6 +1233,20 @@ void Compiler::compInit(norls_allocator * pAlloc, InlineInfo * in
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
@@ -1165,8 +1333,8 @@ void Compiler::compInit(norls_allocator * pAlloc, InlineInfo * in
compCodeGenDone = false;
compRegSetCheckLevel = 0;
opts.compMinOptsIsUsed = false;
- opts.compMinOptsIsSet = false;
#endif
+ opts.compMinOptsIsSet = false;
//Used by fgFindJumpTargets for inlining heuristics.
opts.instrCount = 0;
@@ -1602,9 +1770,6 @@ void Compiler::compSetProcessor()
#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
-#ifdef RYUJIT_CTPBUILD
-#error Cannot support AVX in a CTP JIT
-#endif // RYUJIT_CTPBUILD
opts.compCanUseAVX = false;
if (((compileFlags & CORJIT_FLG_PREJIT) == 0) &&
((compileFlags & CORJIT_FLG_USE_AVX2) != 0))
@@ -1678,8 +1843,15 @@ bool Compiler::compIsFullTrust()
}
-bool Compiler::compShouldThrowOnNoway()
+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.
@@ -1911,8 +2083,31 @@ void Compiler::compInitOptions(unsigned compileFlags)
}
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;
+
if (opts.eeFlags & CORJIT_FLG_PREJIT)
{
static ConfigMethodSet fNgenDump;
@@ -1925,6 +2120,23 @@ void Compiler::compInitOptions(unsigned compileFlags)
unsigned ngenHashDumpVal = (unsigned) fNgenHashDump.val(CLRConfig::INTERNAL_NgenHashDump);
if ((ngenHashDumpVal != (DWORD)-1) && (ngenHashDumpVal == info.compMethodHash()))
verboseDump = true;
+
+ static ConfigMethodSet fNgenDumpIR;
+ fNgenDumpIR.ensureInit(CLRConfig::INTERNAL_NgenDumpIR);
+
+ if (fNgenDumpIR.contains(info.compMethodName, info.compClassName, info.compMethodInfo->args.pSig))
+ dumpIR = true;
+
+ static ConfigDWORD fNgenHashDumpIR;
+ unsigned ngenHashDumpIRVal = (unsigned) fNgenHashDumpIR.val(CLRConfig::INTERNAL_NgenHashDumpIR);
+ if ((ngenHashDumpIRVal != (DWORD)-1) && (ngenHashDumpIRVal == info.compMethodHash()))
+ dumpIR = true;
+
+ static ConfigString ngenDumpIRFormat;
+ dumpIRFormat = ngenDumpIRFormat.val(CLRConfig::INTERNAL_NgenDumpIRFormat);
+
+ static ConfigString ngenDumpIRPhase;
+ dumpIRPhase = ngenDumpIRPhase.val(CLRConfig::INTERNAL_NgenDumpIRPhase);
}
else
{
@@ -1938,6 +2150,224 @@ void Compiler::compInitOptions(unsigned compileFlags)
unsigned jitHashDumpVal = (unsigned) fJitHashDump.val(CLRConfig::INTERNAL_JitHashDump);
if ((jitHashDumpVal != (DWORD)-1) && (jitHashDumpVal == info.compMethodHash()))
verboseDump = true;
+
+ static ConfigMethodSet fJitDumpIR;
+ fJitDumpIR.ensureInit(CLRConfig::INTERNAL_JitDumpIR);
+
+ if (fJitDumpIR.contains(info.compMethodName, info.compClassName, info.compMethodInfo->args.pSig))
+ dumpIR = true;
+
+ static ConfigDWORD fJitHashDumpIR;
+ unsigned jitHashDumpIRVal = (unsigned) fJitHashDumpIR.val(CLRConfig::INTERNAL_JitHashDumpIR);
+ if ((jitHashDumpIRVal != (DWORD)-1) && (jitHashDumpIRVal == info.compMethodHash()))
+ dumpIR = true;
+
+ static ConfigString jitDumpIRFormat;
+ dumpIRFormat = jitDumpIRFormat.val(CLRConfig::INTERNAL_JitDumpIRFormat);
+
+ static ConfigString jitDumpIRPhase;
+ dumpIRPhase = jitDumpIRPhase.val(CLRConfig::INTERNAL_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;
+ }
+ }
+ }
}
}
@@ -1946,17 +2376,102 @@ void Compiler::compInitOptions(unsigned compileFlags)
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.
-#ifdef RYUJIT_CTPBUILD
- static ConfigDWORD fFeatureSIMD;
- featureSIMD = (opts.compCanUseSSE2 && (fFeatureSIMD.val(CLRConfig::EXTERNAL_FeatureSIMD) != 0));
-#else // !RYUJIT_CTPBUILD
featureSIMD = ((opts.eeFlags & CORJIT_FLG_FEATURE_SIMD) != 0);
-#endif // !RYUJIT_CTPBUILD
#endif // _TARGET_AMD64_
#endif // FEATURE_SIMD
@@ -1983,6 +2498,7 @@ void Compiler::compInitOptions(unsigned compileFlags)
#if FEATURE_TAILCALL_OPT
// By default opportunistic tail call optimization is enabled
opts.compTailCallOpt = true;
+ opts.compTailCallLoopOpt = true;
#endif
#ifdef DEBUG
@@ -2225,6 +2741,11 @@ void Compiler::compInitOptions(unsigned compileFlags)
{
opts.compTailCallOpt = (UINT)_wtoi(strTailCallOpt) != 0;
}
+ static ConfigDWORD fTailCallLoopOpt;
+ if (fTailCallLoopOpt.val(CLRConfig::EXTERNAL_TailCallLoopOpt) == 0)
+ {
+ opts.compTailCallLoopOpt = false;
+ }
#endif
opts.compMustInlinePInvokeCalli = (opts.eeFlags & CORJIT_FLG_IL_STUB) ? true : false;
@@ -2242,6 +2763,14 @@ void Compiler::compInitOptions(unsigned compileFlags)
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
+ static ConfigDWORD fEnablePCRelAddr;
+ opts.compEnablePCRelAddr = (fEnablePCRelAddr.val(CLRConfig::INTERNAL_JitEnablePCRelAddr) != 0);
+#endif
+#endif //DEBUG
+
opts.compProcedureSplitting = (opts.eeFlags & CORJIT_FLG_PROCSPLIT) ? true : false;
#ifdef _TARGET_ARM64_
@@ -2389,13 +2918,10 @@ void Compiler::compInitOptions(unsigned compileFlags)
void JitDump(const char* pcFormat, ...)
{
- if (GetTlsCompiler()->verbose)
- {
- va_list lst;
- va_start(lst, pcFormat);
- logf_stdout(pcFormat, lst);
- va_end(lst);
- }
+ va_list lst;
+ va_start(lst, pcFormat);
+ logf_stdout(pcFormat, lst);
+ va_end(lst);
}
bool Compiler::compJitHaltMethod()
@@ -2992,6 +3518,7 @@ void Compiler::compCompile(void * * methodCodePtr,
unsigned compileFlags)
{
hashBv::Init(this);
+
VarSetOps::AssignAllowUninitRhs(this, compCurLife, VarSetOps::UninitVal());
/* The temp holding the secret stub argument is used by fgImport() when importing the intrinsic. */
@@ -3238,6 +3765,7 @@ void Compiler::compCompile(void * * methodCodePtr,
if (!opts.MinOpts() && !opts.compDbgCode)
{
bool doSsa = true;
+ bool doEarlyProp = true;
bool doValueNum = true;
bool doLoopHoisting = true;
bool doCopyProp = true;
@@ -3247,6 +3775,7 @@ void Compiler::compCompile(void * * methodCodePtr,
#ifdef DEBUG
static ConfigDWORD fJitDoOptConfig[6];
doSsa = (fJitDoOptConfig[0].val(CLRConfig::INTERNAL_JitDoSsa) != 0);
+ doEarlyProp = doSsa && (fJitDoOptConfig[1].val(CLRConfig::INTERNAL_JitDoEarlyProp) != 0);
doValueNum = doSsa && (fJitDoOptConfig[1].val(CLRConfig::INTERNAL_JitDoValueNumber) != 0);
doLoopHoisting = doValueNum && (fJitDoOptConfig[2].val(CLRConfig::INTERNAL_JitDoLoopHoisting) != 0);
doCopyProp = doValueNum && (fJitDoOptConfig[3].val(CLRConfig::INTERNAL_JitDoCopyProp) != 0);
@@ -3260,6 +3789,13 @@ void Compiler::compCompile(void * * methodCodePtr,
EndPhase(PHASE_BUILD_SSA);
}
+ if (doEarlyProp)
+ {
+ /* Propagate array length and rewrite getType() method call */
+ optEarlyProp();
+ EndPhase(PHASE_EARLY_PROP);
+ }
+
if (doValueNum)
{
fgValueNumber();
@@ -3296,7 +3832,6 @@ void Compiler::compCompile(void * * methodCodePtr,
if (doRangeAnalysis)
{
/* Optimize array index range checks */
- // optOptimizeIndexChecks();
RangeCheck rc(this);
rc.OptimizeRangeChecks();
EndPhase(PHASE_OPTIMIZE_INDEX_CHECKS);
@@ -3315,14 +3850,15 @@ void Compiler::compCompile(void * * methodCodePtr,
}
}
+#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
- fgDumpXmlFlowGraph();
-#endif
-
-#ifdef DEBUG
fgDebugCheckLinks(compStressCompile(STRESS_REMORPH_TREES, 50));
#endif
@@ -3450,6 +3986,10 @@ void Compiler::compCompile(void * * methodCodePtr,
RecordSqmStateAtEndOfCompilation();
#endif // FEATURE_CLRSQM
+#ifdef FEATURE_TRACELOGGING
+ compJitTelemetry.NotifyEndOfCompilation();
+#endif
+
#if defined(DEBUG) || MEASURE_INLINING
++Compiler::jitTotalMethodCompiled;
Compiler::jitTotalNumLocals += lvaCount;
@@ -3484,6 +4024,82 @@ 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
@@ -3552,6 +4168,28 @@ int Compiler::compCompile(CORINFO_METHOD_HANDLE methodHnd,
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 : NULL;
+ this->dumpIRFormat = compIsForInlining() ? impInlineInfo->InlinerCompiler->dumpIRFormat : NULL;
+ 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;
+
info.compMethodHashPrivate = 0;
#endif
@@ -4103,6 +4741,22 @@ int Compiler::compCompileHelper (CORINFO_MODULE_HANDLE clas
dumpILRange(info.compCode, info.compILCodeSize);
}
+#endif
+
+ // Check for COMPLUS_AgressiveInlining
+ static ConfigDWORD fJitAggressiveInlining;
+ if (fJitAggressiveInlining.val(CLRConfig::INTERNAL_JitAggressiveInlining))
+ {
+ compDoAggressiveInlining = true;
+ }
+
+ if (compDoAggressiveInlining)
+ {
+ info.compFlags |= CORINFO_FLG_FORCEINLINE;
+ }
+
+#ifdef DEBUG
+
// Check for ForceInline stress.
if (compStressCompile(STRESS_FORCE_INLINE, 0))
{
@@ -4381,6 +5035,16 @@ _Next:
{
return CORJIT_SKIPPED;
}
+
+#ifdef ALT_JIT
+#ifdef DEBUG
+ static ConfigDWORD fRunAltJitCode;
+ if (fRunAltJitCode.val(CLRConfig::INTERNAL_RunAltJitCode) == 0)
+ {
+ return CORJIT_SKIPPED;
+ }
+#endif // DEBUG
+#endif // ALT_JIT
}
/* Success! */
@@ -5688,22 +6352,38 @@ void Compiler::compDispCallArgStats(FILE* fout)
// 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 char* PhaseNames[] =
+const LPCWSTR PhaseShortNames[] =
{
-#define CompPhaseNameMacro(enum_nm, string_nm, hasChildren, parent) string_nm,
+#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, hasChildren, parent) hasChildren,
+#define CompPhaseNameMacro(enum_nm, string_nm, short_nm, hasChildren, parent) hasChildren,
#include "compphases.h"
};
int PhaseParent[] =
{
-#define CompPhaseNameMacro(enum_nm, string_nm, hasChildren, parent) parent,
+#define CompPhaseNameMacro(enum_nm, string_nm, short_nm, hasChildren, parent) parent,
#include "compphases.h"
};
@@ -6243,6 +6923,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
* 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)).
@@ -6252,6 +6933,26 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
* 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)).
@@ -6321,6 +7022,13 @@ void cVars(Compiler* comp)
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
@@ -6417,6 +7125,11 @@ void dVars()
cVars(GetTlsCompiler());
}
+void dVarsFinal()
+{
+ cVarsFinal(GetTlsCompiler());
+}
+
void dBlockPreds(BasicBlock* block)
{
cBlockPreds(GetTlsCompiler(), block);
@@ -6472,6 +7185,2527 @@ dBlockList(BasicBlockList* list)
}
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 = GetTlsCompiler();
+ 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 = GetTlsCompiler();
+ 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 = GetTlsCompiler();
+ 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 = GetTlsCompiler();
+
+ if (comp->dumpIRFormat != NULL)
+ {
+ printf("COMPLUS_JitDumpIRFormat=%ls", comp->dumpIRFormat);
+ }
+}
+
+/*****************************************************************************
+ *
+ * COMPLUS_JitDumpIR support - dump out function in linear IR form
+ */
+
+void dFuncIR()
+{
+ cFuncIR(GetTlsCompiler());
+}
+
+/*****************************************************************************
+ *
+ * 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(GetTlsCompiler(), loop);
+}
+
+/*****************************************************************************
+ *
+ * COMPLUS_JitDumpIR support - dump out loop (given loop number) in linear IR form
+ */
+
+void dLoopNumIR(unsigned loopNum)
+{
+ Compiler* comp = GetTlsCompiler();
+
+ if (loopNum >= comp->optLoopCount)
+ {
+ printf("loopNum %u out of range\n");
+ return;
+ }
+
+ Compiler::LoopDsc* loop = &comp->optLoopTable[loopNum];
+ cLoopIR(GetTlsCompiler(), 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");
+ for (GenTreeStmt* stmt = block->firstStmt(); stmt; stmt = stmt->gtNextStmt)
+ {
+ // Skip embedded stmts. They should have already been dumped prior to the stmt
+ // that they are embedded into. Even though they appear on the stmt list
+ // after the stmt they are embedded into. Don't understand the rationale for that
+ // but make the dataflow view look consistent.
+
+ if ((stmt->gtFlags & GTF_STMT_TOP_LEVEL) == 0)
+ {
+ continue;
+ }
+
+ // 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");
+ }
+ }
+
+ 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 != NULL)
+ {
+ printf("\n");
+ }
+}
+
+/*****************************************************************************
+ *
+ * COMPLUS_JitDumpIR support - dump out block in linear IR form
+ */
+
+void dBlockIR(BasicBlock* block)
+{
+ cBlockIR(GetTlsCompiler(), 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(GetTlsCompiler(), 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(GetTlsCompiler(), 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)
+ {
+ if (!comp->dumpIRNodes)
+ {
+ if ((tree->gtFlags & (~(GTF_NODE_LARGE|GTF_NODE_SMALL))) == 0)
+ {
+ return chars;
+ }
+ }
+
+ chars += printf("flags=");
+
+ // Node flags
+
+#if defined(DEBUG) && SMALL_TREE_NODES
+ if (comp->dumpIRNodes)
+ {
+ if (tree->gtFlags & GTF_NODE_LARGE)
+ {
+ chars += printf("[NODE_LARGE]");
+ }
+ if (tree->gtFlags & GTF_NODE_SMALL)
+ {
+ chars += printf("[NODE_SMALL]");
+ }
+ }
+#endif
+ if (tree->gtFlags & GTF_MORPHED)
+ {
+ chars += printf("[MORPHED]");
+ }
+ 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 (tree->gtFlags & GTFD_VAR_CSE_REF)
+ {
+ chars += printf("[VAR_CSE_REF]");
+ }
+ 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_COPYBLK:
+ case GT_INITBLK:
+ case GT_COPYOBJ:
+
+ if (tree->gtFlags & GTF_BLK_HASGCPTR)
+ {
+ chars += printf("[BLK_HASGCPTR]");
+ }
+ if (tree->gtFlags & GTF_BLK_VOLATILE)
+ {
+ chars += printf("[BLK_VOLATILE]");
+ }
+ if (tree->gtFlags & GTF_BLK_UNALIGNED)
+ {
+ 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]");
+ }
+ if (tree->gtFlags & GTF_STMT_TOP_LEVEL)
+ {
+ chars += printf("[STMT_TOP_LEVEL]");
+ }
+ if (tree->gtFlags & GTF_STMT_SKIP_LOWER)
+ {
+ chars += printf("[STMT_SKIP_LOWER]");
+ }
+ 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_REDINDEX_CHECK)
+ {
+ chars += printf("[REDINDEX_CHECK]");
+ }
+ if (tree->gtFlags & GTF_REDINDEX_CHECK)
+ {
+ chars += printf("[REDINDEX_CHECK]");
+ }
+ 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 FEATURE_ANYCSE
+ if (tree->gtFlags & GTF_DEAD)
+ {
+ chars += printf("[DEAD]");
+ }
+#endif
+ 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(GetTlsCompiler(), 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(GetTlsCompiler(), 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(GetTlsCompiler(), 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_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(GetTlsCompiler(), 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(GetTlsCompiler(), 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 == NULL)
+ {
+ 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(GetTlsCompiler(), 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(GetTlsCompiler(), 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(GetTlsCompiler(), 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;
+ }
+ }
+
+ // 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
+ {
+ 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(" = %s", 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 = NULL;
+ 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 != NULL)
+ {
+ chars += cOperandIR(comp, base);
+ }
+ if (index != NULL)
+ {
+ if (base != NULL)
+ {
+ chars += printf("+");
+ }
+ chars += cOperandIR(comp, index);
+ if (scale > 1)
+ {
+ chars += printf("*%u", scale);
+ }
+ }
+ if ((offset != 0) || ((base == NULL) && (index == NULL)))
+ {
+ if ((base != NULL) || (index != NULL))
+ {
+ 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 != NULL)
+ {
+ bool first = true;
+ for (GenTreeArgList* args = tree->gtOp.gtOp1->AsArgList(); args != NULL; 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 == NULL)
+ {
+ 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 != NULL)
+ {
+ cTreeIR(comp, child);
+ }
+ }
+ }
+
+ cNodeIR(comp, tree);
+}
+
+/*****************************************************************************
+ *
+ * COMPLUS_JitDumpIR support - dump out tree in linear IR form
+ */
+
+void dTreeIR(GenTree* tree)
+{
+ cTreeIR(GetTlsCompiler(), tree);
+}
+
#endif // DEBUG
#if VARSET_COUNTOPS