summaryrefslogtreecommitdiff
path: root/src/ToolBox/superpmi/superpmi
diff options
context:
space:
mode:
Diffstat (limited to 'src/ToolBox/superpmi/superpmi')
-rw-r--r--src/ToolBox/superpmi/superpmi/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi/CMakeLists.txt76
-rw-r--r--src/ToolBox/superpmi/superpmi/commandline.cpp543
-rw-r--r--src/ToolBox/superpmi/superpmi/commandline.h74
-rw-r--r--src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp69
-rw-r--r--src/ToolBox/superpmi/superpmi/coreclrcallbacks.h18
-rw-r--r--src/ToolBox/superpmi/superpmi/cycletimer.cpp60
-rw-r--r--src/ToolBox/superpmi/superpmi/cycletimer.h28
-rw-r--r--src/ToolBox/superpmi/superpmi/filecache.cpp150
-rw-r--r--src/ToolBox/superpmi/superpmi/filecache.h78
-rw-r--r--src/ToolBox/superpmi/superpmi/icorjitinfo.cpp2044
-rw-r--r--src/ToolBox/superpmi/superpmi/icorjitinfo.h26
-rw-r--r--src/ToolBox/superpmi/superpmi/ieememorymanager.cpp110
-rw-r--r--src/ToolBox/superpmi/superpmi/ieememorymanager.h113
-rw-r--r--src/ToolBox/superpmi/superpmi/iexecutionengine.cpp213
-rw-r--r--src/ToolBox/superpmi/superpmi/iexecutionengine.h151
-rw-r--r--src/ToolBox/superpmi/superpmi/jitdebugger.cpp459
-rw-r--r--src/ToolBox/superpmi/superpmi/jitdebugger.h33
-rw-r--r--src/ToolBox/superpmi/superpmi/jithost.cpp120
-rw-r--r--src/ToolBox/superpmi/superpmi/jithost.h20
-rw-r--r--src/ToolBox/superpmi/superpmi/jitinstance.cpp436
-rw-r--r--src/ToolBox/superpmi/superpmi/jitinstance.h57
-rw-r--r--src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp126
-rw-r--r--src/ToolBox/superpmi/superpmi/methodstatsemitter.h29
-rw-r--r--src/ToolBox/superpmi/superpmi/neardiffer.cpp1031
-rw-r--r--src/ToolBox/superpmi/superpmi/neardiffer.h86
-rw-r--r--src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp587
-rw-r--r--src/ToolBox/superpmi/superpmi/superpmi.cpp564
-rw-r--r--src/ToolBox/superpmi/superpmi/superpmi.h27
29 files changed, 7329 insertions, 0 deletions
diff --git a/src/ToolBox/superpmi/superpmi/.gitmirror b/src/ToolBox/superpmi/superpmi/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/CMakeLists.txt b/src/ToolBox/superpmi/superpmi/CMakeLists.txt
new file mode 100644
index 0000000000..8756fa0e32
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/CMakeLists.txt
@@ -0,0 +1,76 @@
+project(superpmi)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+add_definitions(-DFEATURE_NO_HOST)
+add_definitions(-DSELF_NO_HOST)
+
+if(WIN32)
+ #use static crt
+ add_definitions(-MT)
+endif(WIN32)
+
+include_directories(.)
+include_directories(../superpmi-shared)
+
+# When it is ready (the build works on all platforms, referencing the coredistools
+# package), define this:
+# add_definitions(-DUSE_COREDISTOOLS)
+
+set(SUPERPMI_SOURCES
+ commandline.cpp
+ coreclrcallbacks.cpp
+ cycletimer.cpp
+ icorjitinfo.cpp
+ ieememorymanager.cpp
+ iexecutionengine.cpp
+ jitdebugger.cpp
+ jitinstance.cpp
+ methodstatsemitter.cpp
+ neardiffer.cpp
+ parallelsuperpmi.cpp
+ superpmi.cpp
+ jithost.cpp
+ ../superpmi-shared/callutils.cpp
+ ../superpmi-shared/compileresult.cpp
+ ../superpmi-shared/errorhandling.cpp
+ ../superpmi-shared/logging.cpp
+ ../superpmi-shared/mclist.cpp
+ ../superpmi-shared/methodcontext.cpp
+ ../superpmi-shared/methodcontextreader.cpp
+ ../superpmi-shared/simpletimer.cpp
+ ../superpmi-shared/spmiutil.cpp
+ ../superpmi-shared/tocfile.cpp
+ ../superpmi-shared/typeutils.cpp
+)
+
+add_precompiled_header(
+ standardpch.h
+ ../superpmi-shared/standardpch.cpp
+ SUPERPMI_SOURCES
+)
+
+add_executable(superpmi
+ ${SUPERPMI_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(superpmi
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(superpmi
+ version.lib
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/superpmi.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS superpmi DESTINATION .)
diff --git a/src/ToolBox/superpmi/superpmi/commandline.cpp b/src/ToolBox/superpmi/superpmi/commandline.cpp
new file mode 100644
index 0000000000..56a2f32d1d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/commandline.cpp
@@ -0,0 +1,543 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CommandLine.cpp - tiny very specific command line parser
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "commandline.h"
+#include "superpmi.h"
+#include "mclist.h"
+#include "methodcontext.h"
+#include "logging.h"
+
+// NOTE: this is parsed by parallelsuperpmi.cpp::ProcessChildStdOut() to determine if an incorrect
+// argument usage error has occurred.
+const char* const g_SuperPMIUsageFirstLine = "SuperPMI is a JIT compiler testing tool.";
+
+void CommandLine::DumpHelp(const char* program)
+{
+ printf("%s\n", g_SuperPMIUsageFirstLine);
+ printf("\n");
+ printf("Usage: %s [options] jitname [jitname2] filename.mc\n", program);
+ printf(" jitname" PLATFORM_SHARED_LIB_SUFFIX_A " - path of jit to be tested\n");
+ printf(" jitname2" PLATFORM_SHARED_LIB_SUFFIX_A " - optional path of second jit to be tested\n");
+ printf(" filename.mc - load method contexts from filename.mc\n");
+ printf(" -j[it] Name - optionally -jit can be used to specify jits\n");
+ printf(" -l[oad] filename - optionally -load can be used to specify method contexts\n");
+ printf("\n");
+ printf("Options:\n");
+ printf("\n");
+ printf(" -boe\n");
+ printf(" Break on error return from compileMethod\n");
+ printf("\n");
+ printf(" -boa\n");
+ printf(" Break on assert from the JIT\n");
+ printf("\n");
+ printf(" -v[erbosity] messagetypes\n");
+ printf(" Controls which types of messages SuperPMI logs. Specify a string of\n");
+ printf(" characters representing message categories to enable, where:\n");
+ printf(" e - errors (internal fatal errors that are non-recoverable)\n");
+ printf(" w - warnings (internal conditions that are unusual, but not serious)\n");
+ printf(" m - missing (failures due to missing JIT-EE interface details)\n");
+ printf(" i - issues (issues found with the JIT, e.g. asm diffs, asserts)\n");
+ printf(" n - information (notifications/summaries, e.g. 'Loaded 5 Jitted 4 FailedCompile 1')\n");
+ printf(" v - verbose (status messages, e.g. 'Jit startup took '151.12ms')\n");
+ printf(" d - debug (lots of detailed output)\n");
+ printf(" a - all (enable all message types; overrides other enable message types)\n");
+ printf(" q - quiet (disable all output; overrides all others)\n");
+ printf(" e.g. '-v ew' only writes error and warning messages to the console.\n");
+ printf(" 'q' takes precedence over any other message type specified.\n");
+ printf(" Default set of messages enabled is 'ewminv'.\n");
+ printf("\n");
+ printf(" -w[riteLogFile] logfile\n");
+ printf(" Write log messages to the specified file.\n");
+ printf("\n");
+ printf(" -c[ompile] <indices>\n");
+ printf(" Compile only those method contexts whose indices are specified.\n");
+ printf(" Indices can be either a single index, comma separated values,\n");
+ printf(" a range, or the name of a .MCL file with newline delimited indices.\n");
+ printf(" e.g. -compile 20\n");
+ printf(" e.g. -compile 20,25,30,32\n");
+ printf(" e.g. -compile 10-99\n");
+ printf(" e.g. -compile 5,10-99,101,201-300\n");
+ printf(" e.g. -compile failed.mcl\n");
+ printf("\n");
+ printf(" -m[atchHash] <MD5 Hash>\n");
+ printf(" Compile only method context with specific MD5 hash\n");
+ printf("\n");
+ printf(" -e[mitMethodStats] <stats-types>\n");
+ printf(" Emit method statistics in CSV format to filename.mc.stats.\n");
+ printf(" Specify a string of characters representing statistics to emit, where:\n");
+ printf(" i - method IL code size\n");
+ printf(" a - method compiled ASM code size\n");
+ printf(" h - method hash to uniquely identify a method across MCH files\n");
+ printf(" n - method number inside the source MCH\n");
+ printf(" t - method throughput time\n");
+ printf(" * - all available method stats\n");
+ printf("\n");
+ printf(" -a[pplyDiff]\n");
+ printf(" Compare the compile result generated from the provided JIT with the\n");
+ printf(" compile result stored with the MC. If two JITs are provided, this\n");
+ printf(" compares the compile results generated by the two JITs.\n");
+ printf("\n");
+ printf(" -r[eproName] prefix\n");
+ printf(" Write out failing methods to prefix-n.mc\n");
+ printf("\n");
+ printf(" -f[ailingMCList] mclfilename\n");
+ printf(" Write out failing methods to mclfilename.\n");
+ printf(" If using -applyDiff and no -diffMCList is specified,\n");
+ printf(" comparison failures also get written to mclfilename.\n");
+ printf("\n");
+ printf(" -diffMCList diffMCLfilename\n");
+ printf(" Write out methods that differ between compilations to diffMCLfilename.\n");
+ printf(" This only works with -applyDiff.\n");
+ printf("\n");
+ printf(" -p[arallel] [workerCount]\n");
+ printf(" Run in parallel mode by spawning 'workerCount' processes to do processing.\n");
+ printf(" If 'workerCount' is not specified, the number of workers used is\n");
+ printf(" the number of processors on the machine.\n");
+ printf("\n");
+ printf(" -skipCleanup\n");
+ printf(" Skip deletion of temporary files created by child SuperPMI processes with -parallel.\n");
+ printf("\n");
+ printf(" -target <target>\n");
+ printf(" Used by the assembly differences calculator. This specifies the target\n");
+ printf(" architecture for cross-compilation. Currently allowed <target> value: arm64\n");
+ printf("\n");
+#ifdef USE_COREDISTOOLS
+ printf(" -coredistools\n");
+ printf(" Use disassembly tools from the CoreDisTools library\n");
+ printf("\n");
+#endif // USE_COREDISTOOLS
+ printf("Inputs are case sensitive.\n");
+ printf("\n");
+ printf("SuperPMI method contexts are stored in files with extension .MC, implying\n");
+ printf("a single method context, or .MCH, implying a set of method contexts. Either\n");
+ printf("extension works equivalently.\n");
+ printf("\n");
+ printf("Exit codes:\n");
+ printf("0 : success\n");
+ printf("-1 : general fatal error (e.g., failed to initialize, failed to read files)\n");
+ printf("-2 : JIT failed to initialize\n");
+ printf("1 : there were compilation failures\n");
+ printf("2 : there were assembly diffs\n");
+ printf("\n");
+ printf("Examples:\n");
+ printf(" %s " MAKEDLLNAME_A("clrjit") " test.mch\n", program);
+ printf(" ; compile all functions in test.mch using " MAKEDLLNAME_A("clrjit") "\n");
+ printf(" %s -p " MAKEDLLNAME_A("clrjit") " test.mch\n", program);
+ printf(" ; same as above, but use all available processors to compile in parallel\n");
+ printf(" %s -f fail.mcl " MAKEDLLNAME_A("clrjit") " test.mch\n", program);
+ printf(" ; if there are any failures, record their MC numbers in the file fail.mcl\n");
+}
+
+//Assumption: All inputs are initialized to default or real value. we'll just set the stuff we see on the command line.
+//Assumption: Single byte names are passed in.. mb stuff doesnt cause an obvious problem... but it might have issues...
+//Assumption: Values larger than 2^31 aren't expressible from the commandline.... (atoi) Unless you pass in negatives.. :-|
+bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o)
+{
+ size_t argLen = 0;
+ size_t tempLen = 0;
+ bool foundJit = false;
+ bool foundFile = false;
+
+ if (argc == 1) //Print help when no args are passed
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ for (int i = 1; i<argc; i++)
+ {
+ bool isASwitch = (argv[i][0] == '-');
+#ifndef FEATURE_PAL
+ if (argv[i][0] == '/') // Also accept "/" on Windows
+ {
+ isASwitch = true;
+ }
+#endif // !FEATURE_PAL
+
+ //Process a switch
+ if (isASwitch)
+ {
+ argLen = strlen(argv[i]);
+
+ if (argLen >1)
+ argLen--; //adjust for leading switch
+ else
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if ((_strnicmp(&argv[i][1], "help", argLen) == 0) ||
+ (_strnicmp(&argv[i][1], "HELP", argLen) == 0) ||
+ (_strnicmp(&argv[i][1], "?", argLen) == 0))
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+ else if ((_strnicmp(&argv[i][1], "load", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processMethodContext:
+
+ tempLen = strlen(argv[i]);
+ if (tempLen == 0)
+ {
+ LogError("Arg '%s' is invalid, name of file missing.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ o->nameOfInputMethodContextFile = new char[tempLen + 1];
+ strcpy_s(o->nameOfInputMethodContextFile, tempLen + 1, argv[i]);
+ foundFile = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "jit", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processJit:
+
+ tempLen = strlen(argv[i]);
+ if (tempLen == 0)
+ {
+ LogError("Arg '%s' is invalid, name of jit missing.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ char *tempStr = new char[tempLen + 1];
+ strcpy_s(tempStr, tempLen + 1, argv[i]);
+ if (!foundJit)
+ {
+ o->nameOfJit = tempStr;
+ foundJit = true;
+ }
+ else
+ {
+ o->nameOfJit2 = tempStr;
+ }
+ }
+ else if ((_strnicmp(&argv[i][1], "reproName", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ tempLen = strlen(argv[i]);
+ if (tempLen == 0)
+ {
+ LogError("Arg '%s' is invalid, name of prefix missing.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ char *tempStr = new char[tempLen + 1];
+ strcpy_s(tempStr, tempLen + 1, argv[i]);
+ o->reproName = tempStr;
+ }
+ else if ((_strnicmp(&argv[i][1], "failingMCList", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->mclFilename = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "diffMCList", 10) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->diffMCLFilename = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "target", 6) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->targetArchitecture = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "boe", 3) == 0))
+ {
+ o->breakOnError = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "boa", 3) == 0))
+ {
+ o->breakOnAssert = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "verbosity", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ Logger::SetLogLevel(Logger::ParseLogLevelString(argv[i]));
+ }
+ else if ((_strnicmp(&argv[i][1], "writeLogFile", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->writeLogFile = argv[i];
+ Logger::OpenLogFile(argv[i]);
+ }
+ else if ((_strnicmp(&argv[i][1], "emitMethodStats", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->methodStatsTypes = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "applyDiff", argLen) == 0))
+ {
+ o->applyDiff = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "compile", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ bool isValidList = MCList::processArgAsMCL(argv[i], &o->indexCount, &o->indexes);
+ if (!isValidList)
+ {
+ LogError("Arg '%s' is invalid, needed method context list.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->hash != nullptr)
+ {
+ LogError("Cannot use both method context list and method context hash.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->offset > 0 && o->increment > 0)
+ {
+ LogError("Cannot use method context list in parallel mode.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->compileList = argv[i]; // Save this in case we need it for -parallel.
+ }
+#ifdef USE_COREDISTOOLS
+ else if ((_strnicmp(&argv[i][1], "coredistools", argLen) == 0)) {
+ o->useCoreDisTools = true;
+ }
+#endif // USE_COREDISTOOLS
+ else if ((_strnicmp(&argv[i][1], "matchHash", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if (strlen(argv[i]) != (MD5_HASH_BUFFER_SIZE - 1))
+ {
+ LogError("Arg '%s' is invalid, needed a valid method context hash.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount > 0)
+ {
+ LogError("Cannot use both method context list and method context hash.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->offset > 0 && o->increment > 0)
+ {
+ LogError("Cannot use method context hash in parallel mode.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ o->hash = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "parallel", argLen) == 0))
+ {
+ o->parallel = true;
+
+ // Is there another argument?
+ if (i + 1 < argc)
+ {
+ // If so, does it look like a worker count?
+ bool isWorkerCount = true;
+ size_t nextlen = strlen(argv[i + 1]);
+ for (size_t j = 0; j < nextlen; j++)
+ {
+ if (!isdigit(argv[i + 1][j]))
+ {
+ isWorkerCount = false; // Doesn't look like a worker count; bail out and let someone else handle it.
+ break;
+ }
+ }
+ if (isWorkerCount)
+ {
+ ++i;
+ o->workerCount = atoi(argv[i]);
+
+ if (o->workerCount < 1)
+ {
+ LogError("Invalid workers count specified, workers count must be at least 1.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->workerCount > MAXIMUM_WAIT_OBJECTS)
+ {
+ LogError("Invalid workers count specified, workers count cannot be more than %d.", MAXIMUM_WAIT_OBJECTS);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ }
+ }
+ else if ((_stricmp(&argv[i][1], "skipCleanup") == 0))
+ {
+ o->skipCleanup = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "stride", argLen) == 0))
+ {
+ // "-stride" is an internal switch used by -parallel. Usage is:
+ //
+ // -stride offset increment
+ //
+ // It compiles methods in this series until end-of-file:
+ // offset, offset+increment, offset+2*increment, offset+3*increment, ...
+
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->offset = atoi(argv[i]);
+
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->increment = atoi(argv[i]);
+
+ if (o->offset < 1 || o->increment < 1)
+ {
+ LogError("Incorrect offset/increment specified for -stride. Offset and increment both must be > 0.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount > 0)
+ {
+ LogError("Cannot use method context list in parallel mode.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->hash != nullptr)
+ {
+ LogError("Cannot use method context hash in parallel mode.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ else
+ {
+ LogError("Unknown switch '%s' passed as argument.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ //Process an input filename
+ //String comparisons on file extensions must be case-insensitive since we run on Windows
+ else
+ {
+ char *lastdot = strrchr(argv[i], '.');
+ if (lastdot == nullptr)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if (_stricmp(lastdot, PLATFORM_SHARED_LIB_SUFFIX_A) == 0)
+ goto processJit;
+ else if (_stricmp(lastdot, ".mc") == 0)
+ goto processMethodContext;
+ else if (_stricmp(lastdot, ".mch") == 0)
+ goto processMethodContext;
+ else if (_stricmp(lastdot, ".mct") == 0)
+ goto processMethodContext;
+ else
+ {
+ LogError("Unknown file type passed as argument, '%s'.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ }
+
+ // Do some argument validation.
+
+ if (o->nameOfJit == nullptr)
+ {
+ LogError("Missing name of a Jit.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->nameOfInputMethodContextFile == nullptr)
+ {
+ LogError("Missing name of an input file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->diffMCLFilename != nullptr && !o->applyDiff)
+ {
+ LogError("-diffMCList specified without -applyDiff.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->targetArchitecture != nullptr && (0 != _stricmp(o->targetArchitecture, "arm64")))
+ {
+ LogError("Illegal target architecture specified with -target (only arm64 is supported).");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->skipCleanup && !o->parallel)
+ {
+ LogError("-skipCleanup requires -parallel.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+}
diff --git a/src/ToolBox/superpmi/superpmi/commandline.h b/src/ToolBox/superpmi/superpmi/commandline.h
new file mode 100644
index 0000000000..051eb6def2
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/commandline.h
@@ -0,0 +1,74 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CommandLine.h - tiny very specific command line parser
+//----------------------------------------------------------
+#ifndef _CommandLine
+#define _CommandLine
+
+class CommandLine
+{
+public:
+
+ class Options
+ {
+ public:
+ Options() :
+ nameOfJit(nullptr),
+ nameOfJit2(nullptr),
+ nameOfInputMethodContextFile(nullptr),
+ writeLogFile(nullptr),
+ reproName(nullptr),
+ breakOnError(false),
+ breakOnAssert(false),
+ applyDiff(false),
+ parallel(false),
+ useCoreDisTools(false),
+ skipCleanup(false),
+ workerCount(-1),
+ indexCount(-1),
+ indexes(nullptr),
+ hash(nullptr),
+ methodStatsTypes(nullptr),
+ mclFilename(nullptr),
+ diffMCLFilename(nullptr),
+ targetArchitecture(nullptr),
+ compileList(nullptr),
+ offset(-1),
+ increment(-1)
+ {
+ }
+
+ char* nameOfJit;
+ char* nameOfJit2;
+ char* nameOfInputMethodContextFile;
+ char* writeLogFile;
+ char* reproName;
+ bool breakOnError;
+ bool breakOnAssert;
+ bool applyDiff;
+ bool parallel; // User specified to use /parallel mode.
+ bool useCoreDisTools; // Use CoreDisTools library instead of Msvcdis
+ bool skipCleanup; // In /parallel mode, do we skip cleanup of temporary files? Used for debugging /parallel.
+ int workerCount; // Number of workers to use for /parallel mode. -1 (or 1) means don't use parallel mode.
+ int indexCount; // If indexCount is -1 and hash points to nullptr it means compile all.
+ int* indexes;
+ char* hash;
+ char* methodStatsTypes;
+ char* mclFilename;
+ char* diffMCLFilename;
+ char* targetArchitecture;
+ char* compileList;
+ int offset;
+ int increment;
+ };
+
+ static bool Parse(int argc, char* argv[], /* OUT */ Options* o);
+
+private:
+ static void DumpHelp(const char* program);
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp
new file mode 100644
index 0000000000..7a66d20940
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp
@@ -0,0 +1,69 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "spmiutil.h"
+#include "coreclrcallbacks.h"
+#include "iexecutionengine.h"
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
+{
+ MyIEE *iee = InitIExecutionEngine();
+ return iee;
+}
+
+/*#pragma warning( suppress :4996 ) //deprecated
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength)
+{
+ DebugBreakorAV(131);
+ return 0;
+}
+*/
+
+HANDLE ourHeap = nullptr;
+
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes)
+{
+ if(ourHeap==nullptr)
+ ourHeap = HeapCreate(0, 4096 ,0);
+ if(ourHeap==nullptr)
+ {
+ LogError("HeapCreate Failed");
+ __debugbreak();
+ return nullptr;
+ }
+ LPVOID result = HeapAlloc(ourHeap, dwFlags, dwBytes);
+// LogDebug("EEHeapAllocInProcessHeap %p %u %u", result, dwFlags, dwBytes);
+ return result;
+}
+
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem)
+{
+// return true;
+ return HeapFree(ourHeap, dwFlags, lpMem);
+}
+
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
+{
+ if(strcmp(functionName, "EEHeapAllocInProcessHeap")==0)
+ return (void*)EEHeapAllocInProcessHeap;
+ if(strcmp(functionName, "EEHeapFreeInProcessHeap")==0)
+ return (void*)EEHeapFreeInProcessHeap;
+ DebugBreakorAV(132);
+ return nullptr;
+}
+
+CoreClrCallbacks *InitCoreClrCallbacks()
+{
+ CoreClrCallbacks *temp = new CoreClrCallbacks();
+ ::ZeroMemory(temp, sizeof(CoreClrCallbacks));
+
+ temp->m_hmodCoreCLR = (HINSTANCE)(size_t)0xbadbad01; // any non-null value seems okay...
+ temp->m_pfnIEE = IEE_t;
+ temp->m_pfnGetCORSystemDirectory = nullptr;//GetCORSystemDirectory;
+ temp->m_pfnGetCLRFunction = GetCLRFunction;
+
+ return temp;
+}
diff --git a/src/ToolBox/superpmi/superpmi/coreclrcallbacks.h b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.h
new file mode 100644
index 0000000000..cb3815525e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _CoreClrCallbacks
+#define _CoreClrCallbacks
+
+#include "runtimedetails.h"
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t();
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength);
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes);
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem);
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName);
+CoreClrCallbacks *InitCoreClrCallbacks();
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/cycletimer.cpp b/src/ToolBox/superpmi/superpmi/cycletimer.cpp
new file mode 100644
index 0000000000..de0e19b935
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/cycletimer.cpp
@@ -0,0 +1,60 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "cycletimer.h"
+
+CycleTimer::CycleTimer()
+{
+ start = 0;
+ stop = 0;
+ overhead = QueryOverhead();
+}
+
+CycleTimer::~CycleTimer()
+{
+}
+
+void CycleTimer::Start()
+{
+ BOOL retVal = QueryThreadCycleTime(GetCurrentThread(), &start);
+
+ if(retVal == FALSE)
+ {
+ LogError("CycleTimer::Start unable to QPC. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+void CycleTimer::Stop()
+{
+ BOOL retVal = QueryThreadCycleTime(GetCurrentThread(), &stop);
+
+ if(retVal == FALSE)
+ {
+ LogError("CycleTimer::Stop unable to QPC. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+unsigned __int64 CycleTimer::GetCycles()
+{
+ return stop - start - overhead;
+}
+
+unsigned __int64 CycleTimer::QueryOverhead()
+{
+ unsigned __int64 tot = 0;
+ unsigned __int64 startCycles;
+ unsigned __int64 endCycles;
+ const int N = 1000;
+ for (int i = 0; i < N; i++)
+ {
+ QueryThreadCycleTime(GetCurrentThread(), &startCycles);
+ QueryThreadCycleTime(GetCurrentThread(), &endCycles);
+ tot += (endCycles-startCycles);
+ }
+ return tot/N;
+} \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/cycletimer.h b/src/ToolBox/superpmi/superpmi/cycletimer.h
new file mode 100644
index 0000000000..9ad855501a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/cycletimer.h
@@ -0,0 +1,28 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _CycleTimer
+#define _CycleTimer
+
+#include "errorhandling.h"
+
+class CycleTimer
+{
+public:
+ CycleTimer();
+ ~CycleTimer();
+
+ void Start();
+ void Stop();
+ unsigned __int64 GetCycles();
+ unsigned __int64 QueryOverhead();
+
+private:
+ // Cycles
+ unsigned __int64 start;
+ unsigned __int64 stop;
+ unsigned __int64 overhead;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/filecache.cpp b/src/ToolBox/superpmi/superpmi/filecache.cpp
new file mode 100644
index 0000000000..dcd783a09b
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/filecache.cpp
@@ -0,0 +1,150 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// TODO-Cleanup: this class is unused
+
+#include "standardpch.h"
+#include "filecache.h"
+
+#define FileCacheSize 0xFFFFFF //needs to be bigger than the biggest read request.
+
+HANDLE FileCache::cachedHandle;
+bool FileCache::openAsCache;
+BYTE *FileCache::rawBuff;
+unsigned int FileCache::offset;
+unsigned int FileCache::length;
+__int64 FileCache::fileoffset;
+
+HANDLE
+FileCache::CreateFileA(
+ _In_ LPCSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ )
+{
+ openAsCache = false;
+ rawBuff = nullptr;
+ offset = 0;
+ length = 0;
+
+ if((dwShareMode&CACHE_THIS_FILE)==CACHE_THIS_FILE)
+ {
+ dwShareMode ^= CACHE_THIS_FILE;
+ openAsCache = true;
+ }
+ HANDLE temp = ::CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+ if(openAsCache)
+ cachedHandle = temp; //yes yes.. this is unsafe.. but one accessor now is okay. bswhack
+ return temp;
+}
+
+HANDLE
+FileCache::CreateFileW(
+ _In_ LPCWSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ )
+{
+ openAsCache = false;
+ rawBuff = nullptr;
+ offset = 0;
+ length = 0;
+ return ::CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+}
+
+
+//Somewhat sloppy quick copy... we don't treat lpNumberOfBytesRead etc correctly
+BOOL
+FileCache::ReadFile(
+ _In_ HANDLE hFile,
+ _Out_writes_bytes_to_opt_(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer,
+ _In_ DWORD nNumberOfBytesToRead,
+ _Out_opt_ LPDWORD lpNumberOfBytesRead,
+ _Inout_opt_ LPOVERLAPPED lpOverlapped
+ )
+{
+ if(!openAsCache)
+ return ::ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
+ else
+ {
+ if(rawBuff == nullptr)
+ {
+ rawBuff = new BYTE[FileCacheSize];
+ length = FileCacheSize;
+ offset = FileCacheSize;
+ }
+ if(nNumberOfBytesToRead > FileCacheSize)
+ {
+ printf("ERROR: nNumberOfBytesToRead exceeds FileCacheSize %u > %u\n", nNumberOfBytesToRead, FileCacheSize);
+ __debugbreak();
+ }
+ if((offset+nNumberOfBytesToRead) > length)
+ {
+ memmove(rawBuff, &rawBuff[offset], length-offset); //Use memmove since we have overlapping regions more than half the time
+ ::ReadFile(hFile, &rawBuff[length-offset], offset, lpNumberOfBytesRead, lpOverlapped);
+ if(*lpNumberOfBytesRead ==0)
+ __debugbreak();
+ length -= offset-(*lpNumberOfBytesRead);
+ LARGE_INTEGER DataTemp;
+ LARGE_INTEGER zero;
+ zero.QuadPart = 0;
+ ::SetFilePointerEx(hFile, zero, &DataTemp, FILE_CURRENT);
+ fileoffset = DataTemp.QuadPart;
+
+ offset = 0;
+ }
+ memcpy(lpBuffer, &rawBuff[offset], nNumberOfBytesToRead);
+ offset+=nNumberOfBytesToRead;
+ if(offset > FileCacheSize)
+ __debugbreak();
+ return true;
+ }
+}
+
+BOOL
+FileCache::WriteFile(
+ _In_ HANDLE hFile,
+ _In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer,
+ _In_ DWORD nNumberOfBytesToWrite,
+ _Out_opt_ LPDWORD lpNumberOfBytesWritten,
+ _Inout_opt_ LPOVERLAPPED lpOverlapped
+ )
+{
+ if(!openAsCache)
+ return ::WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
+ else
+ {
+ printf("ERROR: We only support one file open via the cache.\n");
+ __debugbreak();
+ return false;
+ }
+}
+
+BOOL
+FileCache::CloseHandle(
+ _In_ HANDLE hObject
+ )
+{
+ if(!openAsCache)
+ return ::CloseHandle(hObject);
+ else
+ {
+ if(rawBuff!=nullptr)
+ delete []rawBuff;
+ return ::CloseHandle(hObject);
+ }
+}
+__int64 FileCache::GetFilePos(HANDLE hFile)
+{
+ return (fileoffset - (__int64)length) + (__int64)offset;
+}
diff --git a/src/ToolBox/superpmi/superpmi/filecache.h b/src/ToolBox/superpmi/superpmi/filecache.h
new file mode 100644
index 0000000000..51df5edde9
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/filecache.h
@@ -0,0 +1,78 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// TODO-Cleanup: this class is unused
+
+//----------------------------------------------------------
+// FileCache.h - very simple read ahead/ReadFile abstraction
+//----------------------------------------------------------
+#ifndef _FileCache
+#define _FileCache
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define CACHE_THIS_FILE 0xFF
+
+class FileCache
+{
+public:
+
+static HANDLE
+CreateFileA(
+ _In_ LPCSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ );
+
+static HANDLE
+CreateFileW(
+ _In_ LPCWSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ );
+
+static BOOL
+ReadFile(
+ _In_ HANDLE hFile,
+ _Out_writes_bytes_to_opt_(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer,
+ _In_ DWORD nNumberOfBytesToRead,
+ _Out_opt_ LPDWORD lpNumberOfBytesRead,
+ _Inout_opt_ LPOVERLAPPED lpOverlapped
+ );
+
+static BOOL
+WriteFile(
+ _In_ HANDLE hFile,
+ _In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer,
+ _In_ DWORD nNumberOfBytesToWrite,
+ _Out_opt_ LPDWORD lpNumberOfBytesWritten,
+ _Inout_opt_ LPOVERLAPPED lpOverlapped
+ );
+
+static BOOL
+CloseHandle(
+ _In_ HANDLE hObject
+ );
+
+static __int64 GetFilePos(HANDLE hFile);
+
+private:
+ static HANDLE cachedHandle;
+ static bool openAsCache;
+ static BYTE *rawBuff;
+ static unsigned int offset;
+ static unsigned int length;
+ static __int64 fileoffset;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
new file mode 100644
index 0000000000..41b0195a6d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
@@ -0,0 +1,2044 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "icorjitinfo.h"
+#include "jitdebugger.h"
+#include "spmiutil.h"
+
+ICorJitInfo *pICJI = nullptr;
+
+ICorJitInfo* InitICorJitInfo(JitInstance *jitInstance)
+{
+ MyICJI *icji = new MyICJI();
+ icji->jitInstance = jitInstance;
+ pICJI = icji;
+ return icji;
+}
+
+//Stuff on ICorStaticInfo
+/**********************************************************************************/
+//
+// ICorMethodInfo
+//
+/**********************************************************************************/
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD MyICJI::getMethodAttribs (CORINFO_METHOD_HANDLE ftn /* IN */)
+{
+ jitInstance->mc->cr->AddCall("getMethodAttribs");
+ return jitInstance->mc->repGetMethodAttribs(ftn);
+}
+
+// sets private JIT flags, which can be, retrieved using getAttrib.
+void MyICJI::setMethodAttribs (CORINFO_METHOD_HANDLE ftn,/* IN */
+ CorInfoMethodRuntimeFlags attribs/* IN */)
+{
+ jitInstance->mc->cr->AddCall("setMethodAttribs");
+ jitInstance->mc->cr->recSetMethodAttribs(ftn, attribs);
+}
+
+
+// Given a method descriptor ftnHnd, extract signature information into sigInfo
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+void MyICJI::getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodSig");
+ jitInstance->mc->repGetMethodSig(ftn, sig, memberParent);
+}
+
+
+ /*********************************************************************
+ * Note the following methods can only be used on functions known
+ * to be IL. This includes the method being compiled and any method
+ * that 'getMethodInfo' returns true for
+ *********************************************************************/
+
+ // return information about a method private to the implementation
+ // returns false if method is not IL, or is otherwise unavailable.
+ // This method is used to fetch data needed to inline functions
+ bool MyICJI::getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodInfo");
+ DWORD exceptionCode = 0;
+ bool value = jitInstance->mc->repGetMethodInfo(ftn, info, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+ return value;
+}
+
+// Decides if you have any limitations for inlining. If everything's OK, it will return
+// INLINE_PASS and will fill out pRestrictions with a mask of restrictions the caller of this
+// function must respect. If caller passes pRestrictions = nullptr, if there are any restrictions
+// INLINE_FAIL will be returned
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+//
+// The inlined method need not be verified
+
+CorInfoInline MyICJI::canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("canInline");
+
+ DWORD exceptionCode = 0;
+ CorInfoInline result = jitInstance->mc->repCanInline(callerHnd, calleeHnd, pRestrictions, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+ return result;
+}
+
+// Reports whether or not a method can be inlined, and why. canInline is responsible for reporting all
+// inlining results when it returns INLINE_FAIL and INLINE_NEVER. All other results are reported by the
+// JIT.
+void MyICJI::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * reason)
+{
+ jitInstance->mc->cr->AddCall("reportInliningDecision");
+ jitInstance->mc->cr->recReportInliningDecision(inlinerHnd, inlineeHnd, inlineResult, reason);
+}
+
+
+// Returns false if the call is across security boundaries thus we cannot tailcall
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+bool MyICJI::canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("canTailCall");
+ return jitInstance->mc->repCanTailCall(callerHnd, declaredCalleeHnd, exactCalleeHnd, fIsTailPrefix);
+}
+
+// Reports whether or not a method can be tail called, and why.
+// canTailCall is responsible for reporting all results when it returns
+// false. All other results are reported by the JIT.
+void MyICJI::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason)
+{
+ jitInstance->mc->cr->AddCall("reportTailCallDecision");
+ jitInstance->mc->cr->recReportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+}
+
+// get individual exception handler
+void MyICJI::getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getEHinfo");
+ jitInstance->mc->repGetEHinfo(ftn, EHnumber, clause);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE MyICJI::getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodClass");
+ return jitInstance->mc->repGetMethodClass(method);
+}
+
+// return module it belongs to
+CORINFO_MODULE_HANDLE MyICJI::getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodModule");
+ LogError("Hit unimplemented getMethodModule");
+ DebugBreakorAV(7);
+ return 0;
+}
+
+// This function returns the offset of the specified method in the
+// vtable of it's owning class or interface.
+void MyICJI::getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodVTableOffset");
+ jitInstance->mc->repGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+}
+
+// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+// getIntrinsicID() returns the intrinsic ID.
+CorInfoIntrinsics MyICJI::getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getIntrinsicID");
+ return jitInstance->mc->repGetIntrinsicID(method, pMustExpand);
+}
+
+// Is the given module the System.Numerics.Vectors module?
+bool MyICJI::isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ )
+{
+ jitInstance->mc->cr->AddCall("isInSIMDModule");
+ return jitInstance->mc->repIsInSIMDModule(classHnd) ? true : false;
+}
+
+// return the unmanaged calling convention for a PInvoke
+CorInfoUnmanagedCallConv MyICJI::getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("getUnmanagedCallConv");
+ return jitInstance->mc->repGetUnmanagedCallConv(method);
+}
+
+// return if any marshaling is required for PInvoke methods. Note that
+// method == 0 => calli. The call site sig is only needed for the varargs or calli case
+BOOL MyICJI::pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ )
+{
+ jitInstance->mc->cr->AddCall("pInvokeMarshalingRequired");
+ return jitInstance->mc->repPInvokeMarshalingRequired(method, callSiteSig);
+}
+
+// Check constraints on method type arguments (only).
+// The parent class should be checked separately using satisfiesClassConstraints(parent).
+BOOL MyICJI::satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("satisfiesMethodConstraints");
+ return jitInstance->mc->repSatisfiesMethodConstraints(parent, method);
+}
+
+// Given a delegate target class, a target method parent class, a target method,
+// a delegate class, check if the method signature is compatible with the Invoke method of the delegate
+// (under the typical instantiation of any free type variables in the memberref signatures).
+BOOL MyICJI::isCompatibleDelegate(
+ CORINFO_CLASS_HANDLE objCls, /* type of the delegate target, if any */
+ CORINFO_CLASS_HANDLE methodParentCls, /* exact parent of the target method, if any */
+ CORINFO_METHOD_HANDLE method, /* (representative) target method, if any */
+ CORINFO_CLASS_HANDLE delegateCls, /* exact type of the delegate */
+ BOOL *pfIsOpenDelegate /* is the delegate open */
+ )
+{
+ jitInstance->mc->cr->AddCall("isCompatibleDelegate");
+ return jitInstance->mc->repIsCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate);
+}
+
+// Determines whether the delegate creation obeys security transparency rules
+BOOL MyICJI::isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ )
+{
+ jitInstance->mc->cr->AddCall("isDelegateCreationAllowed");
+ return jitInstance->mc->repIsDelegateCreationAllowed(delegateHnd, calleeHnd);
+}
+
+
+// Indicates if the method is an instance of the generic
+// method that passes (or has passed) verification
+CorInfoInstantiationVerification MyICJI::isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("isInstantiationOfVerifiedGeneric");
+ return jitInstance->mc->repIsInstantiationOfVerifiedGeneric(method);
+}
+
+// Loads the constraints on a typical method definition, detecting cycles;
+// for use in verification.
+void MyICJI::initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("initConstraintsForVerification");
+ jitInstance->mc->repInitConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+}
+
+// Returns enum whether the method does not require verification
+// Also see ICorModuleInfo::canSkipVerification
+CorInfoCanSkipVerificationResult MyICJI::canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ )
+{
+ jitInstance->mc->cr->AddCall("canSkipMethodVerification");
+ return jitInstance->mc->repCanSkipMethodVerification(ftnHandle, FALSE);
+}
+
+// load and restore the method
+void MyICJI::methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("methodMustBeLoadedBeforeCodeIsRun");
+ jitInstance->mc->cr->recMethodMustBeLoadedBeforeCodeIsRun(method);
+}
+
+CORINFO_METHOD_HANDLE MyICJI::mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("mapMethodDeclToMethodImpl");
+ LogError("Hit unimplemented mapMethodDeclToMethodImpl");
+ DebugBreakorAV(17);
+ return 0;
+}
+
+// Returns the global cookie for the /GS unsafe buffer checks
+// The cookie might be a constant value (JIT), or a handle to memory location (Ngen)
+void MyICJI::getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ )
+{
+ jitInstance->mc->cr->AddCall("getGSCookie");
+ jitInstance->mc->repGetGSCookie(pCookieVal, ppCookieVal);
+}
+
+/**********************************************************************************/
+//
+// ICorModuleInfo
+//
+/**********************************************************************************/
+
+// Resolve metadata token into runtime method handles.
+void MyICJI::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ DWORD exceptionCode = 0;
+ jitInstance->mc->cr->AddCall("resolveToken");
+ jitInstance->mc->repResolveToken(pResolvedToken, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+}
+
+// Resolve metadata token into runtime method handles.
+bool MyICJI::tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ jitInstance->mc->cr->AddCall("tryResolveToken");
+ return jitInstance->mc->repTryResolveToken(pResolvedToken);
+}
+
+// Signature information about the call sig
+void MyICJI::findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("findSig");
+ jitInstance->mc->repFindSig(module, sigTOK, context, sig);
+}
+
+// for Varargs, the signature at the call site may differ from
+// the signature at the definition. Thus we need a way of
+// fetching the call site information
+void MyICJI::findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("findCallSiteSig");
+ jitInstance->mc->repFindCallSiteSig(module, methTOK, context, sig);
+}
+
+CORINFO_CLASS_HANDLE MyICJI::getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */)
+{
+ jitInstance->mc->cr->AddCall("getTokenTypeAsHandle");
+ return jitInstance->mc->repGetTokenTypeAsHandle(pResolvedToken);
+}
+
+// Returns true if the module does not require verification
+//
+// If fQuickCheckOnlyWithoutCommit=TRUE, the function only checks that the
+// module does not currently require verification in the current AppDomain.
+// This decision could change in the future, and so should not be cached.
+// If it is cached, it should only be used as a hint.
+// This is only used by ngen for calculating certain hints.
+//
+
+// Returns enum whether the module does not require verification
+// Also see ICorMethodInfo::canSkipMethodVerification();
+CorInfoCanSkipVerificationResult MyICJI::canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("canSkipVerification");
+ LogError("Hit unimplemented canSkipVerification");
+ DebugBreakorAV(22);
+ return CORINFO_VERIFICATION_CANNOT_SKIP;
+}
+
+// Checks if the given metadata token is valid
+BOOL MyICJI::isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("isValidToken");
+ return jitInstance->mc->repIsValidToken(module, metaTOK);
+}
+
+// Checks if the given metadata token is valid StringRef
+BOOL MyICJI::isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("isValidStringRef");
+ return jitInstance->mc->repIsValidStringRef(module, metaTOK);
+}
+
+BOOL MyICJI::shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE scope
+ )
+{
+ jitInstance->mc->cr->AddCall("shouldEnforceCallvirtRestriction");
+ return jitInstance->mc->repShouldEnforceCallvirtRestriction(scope);
+}
+
+/**********************************************************************************/
+//
+// ICorClassInfo
+//
+/**********************************************************************************/
+
+// If the value class 'cls' is isomorphic to a primitive type it will
+// return that type, otherwise it will return CORINFO_TYPE_VALUECLASS
+CorInfoType MyICJI::asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("asCorInfoType");
+ return jitInstance->mc->repAsCorInfoType(cls);
+}
+
+// for completeness
+const char* MyICJI::getClassName (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassName");
+ const char* result = jitInstance->mc->repGetClassName(cls);
+ return result;
+}
+
+
+// Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
+// If fNamespace=TRUE, include the namespace/enclosing classes
+// If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
+// If fAssembly=TRUE, suffix with a comma and the full assembly qualification
+// return size of representation
+int MyICJI::appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ )
+{
+ jitInstance->mc->cr->AddCall("appendClassName");
+ const WCHAR* result = jitInstance->mc->repAppendClassName(cls, fNamespace, fFullInst, fAssembly);
+ int nLen = 0;
+ if (ppBuf != nullptr && result != nullptr)
+ {
+ nLen = (int)wcslen(result);
+ if (*pnBufLen > nLen)
+ {
+ wcscpy_s(*ppBuf, *pnBufLen, result );
+ (*ppBuf) += nLen;
+ (*pnBufLen) -= nLen;
+ }
+ }
+ return nLen;
+}
+
+// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+BOOL MyICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ jitInstance->mc->cr->AddCall("isValueClass");
+ return jitInstance->mc->repIsValueClass(cls);
+}
+
+// If this method returns true, JIT will do optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType()
+BOOL MyICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ jitInstance->mc->cr->AddCall("canInlineTypeCheckWithObjectVTable");
+ return jitInstance->mc->repCanInlineTypeCheckWithObjectVTable(cls);
+}
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD MyICJI::getClassAttribs (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassAttribs");
+ return jitInstance->mc->repGetClassAttribs(cls);
+}
+
+// Returns "TRUE" iff "cls" is a struct type such that return buffers used for returning a value
+// of this type must be stack-allocated. This will generally be true only if the struct
+// contains GC pointers, and does not exceed some size limit. Maintaining this as an invariant allows
+// an optimization: the JIT may assume that return buffer pointers for return types for which this predicate
+// returns TRUE are always stack allocated, and thus, that stores to the GC-pointer fields of such return
+// buffers do not require GC write barriers.
+BOOL MyICJI::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ jitInstance->mc->cr->AddCall("isStructRequiringStackAllocRetBuf");
+ return jitInstance->mc->repIsStructRequiringStackAllocRetBuf(cls);
+}
+
+CORINFO_MODULE_HANDLE MyICJI::getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassModule");
+ LogError("Hit unimplemented getClassModule");
+ DebugBreakorAV(28);
+ return (CORINFO_MODULE_HANDLE)0;
+ //jitInstance->mc->getClassModule(cls);
+}
+
+// Returns the assembly that contains the module "mod".
+CORINFO_ASSEMBLY_HANDLE MyICJI::getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ )
+{
+ jitInstance->mc->cr->AddCall("getModuleAssembly");
+ LogError("Hit unimplemented getModuleAssembly");
+ DebugBreakorAV(28);
+ return (CORINFO_ASSEMBLY_HANDLE)0;
+
+// return jitInstance->mc->getModuleAssembly(mod);
+}
+
+// Returns the name of the assembly "assem".
+const char* MyICJI::getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE assem
+ )
+{
+ jitInstance->mc->cr->AddCall("getAssemblyName");
+ LogError("Hit unimplemented getAssemblyName");
+ DebugBreakorAV(28);
+ return nullptr;
+// return jitInstance->mc->getAssemblyName(assem);
+}
+
+// Allocate and delete process-lifetime objects. Should only be
+// referred to from static fields, lest a leak occur.
+// Note that "LongLifetimeFree" does not execute destructors, if "obj"
+// is an array of a struct type with a destructor.
+void* MyICJI::LongLifetimeMalloc(size_t sz)
+{
+ jitInstance->mc->cr->AddCall("LongLifetimeMalloc");
+ LogError("Hit unimplemented LongLifetimeMalloc");
+ DebugBreakorAV(32);
+ return 0;
+}
+
+void MyICJI::LongLifetimeFree(void* obj)
+{
+ jitInstance->mc->cr->AddCall("LongLifetimeFree");
+ LogError("Hit unimplemented LongLifetimeFree");
+ DebugBreakorAV(33);
+}
+
+size_t MyICJI::getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassModuleIdForStatics");
+ return jitInstance->mc->repGetClassModuleIdForStatics(cls, pModule, ppIndirection);
+}
+
+// return the number of bytes needed by an instance of the class
+unsigned MyICJI::getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassSize");
+ return jitInstance->mc->repGetClassSize(cls);
+}
+
+unsigned MyICJI::getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassAlignmentRequirement");
+ return jitInstance->mc->repGetClassAlignmentRequirement(cls, fDoubleAlignHint);
+}
+
+// This is only called for Value classes. It returns a boolean array
+// in representing of 'cls' from a GC perspective. The class is
+// assumed to be an array of machine words
+// (of length // getClassSize(cls) / sizeof(void*)),
+// 'gcPtrs' is a poitner to an array of BYTEs of this length.
+// getClassGClayout fills in this array so that gcPtrs[i] is set
+// to one of the CorInfoGCType values which is the GC type of
+// the i-th machine word of an object of type 'cls'
+// returns the number of GC pointers in the array
+unsigned MyICJI::getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassGClayout");
+ return jitInstance->mc->repGetClassGClayout(cls, gcPtrs);
+}
+
+// returns the number of instance fields in a class
+unsigned MyICJI::getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassNumInstanceFields");
+ return jitInstance->mc->repGetClassNumInstanceFields(cls);
+}
+
+CORINFO_FIELD_HANDLE MyICJI::getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldInClass");
+ return jitInstance->mc->repGetFieldInClass(clsHnd, num);
+}
+
+BOOL MyICJI::checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ )
+{
+ jitInstance->mc->cr->AddCall("checkMethodModifier");
+ BOOL result = jitInstance->mc->repCheckMethodModifier(hMethod, modifier, fOptional);
+ return result;
+}
+
+// returns the "NEW" helper optimized for "newCls."
+CorInfoHelpFunc MyICJI::getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ )
+{
+ jitInstance->mc->cr->AddCall("getNewHelper");
+ return jitInstance->mc->repGetNewHelper(pResolvedToken, callerHandle);
+}
+
+// returns the newArr (1-Dim array) helper optimized for "arrayCls."
+CorInfoHelpFunc MyICJI::getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ )
+{
+ jitInstance->mc->cr->AddCall("getNewArrHelper");
+ return jitInstance->mc->repGetNewArrHelper(arrayCls);
+}
+
+// returns the optimized "IsInstanceOf" or "ChkCast" helper
+CorInfoHelpFunc MyICJI::getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ )
+{
+ jitInstance->mc->cr->AddCall("getCastingHelper");
+ return jitInstance->mc->repGetCastingHelper(pResolvedToken, fThrowing);
+}
+
+// returns helper to trigger static constructor
+CorInfoHelpFunc MyICJI::getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ )
+{
+ jitInstance->mc->cr->AddCall("getSharedCCtorHelper");
+ return jitInstance->mc->repGetSharedCCtorHelper(clsHnd);
+}
+
+CorInfoHelpFunc MyICJI::getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE ftn
+ )
+{
+ jitInstance->mc->cr->AddCall("getSecurityPrologHelper");
+ return jitInstance->mc->repGetSecurityPrologHelper(ftn);
+}
+
+// This is not pretty. Boxing nullable<T> actually returns
+// a boxed<T> not a boxed Nullable<T>. This call allows the verifier
+// to call back to the EE on the 'box' instruction and get the transformed
+// type to use for verification.
+CORINFO_CLASS_HANDLE MyICJI::getTypeForBox(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getTypeForBox");
+ return jitInstance->mc->repGetTypeForBox(cls);
+}
+
+// returns the correct box helper for a particular class. Note
+// that if this returns CORINFO_HELP_BOX, the JIT can assume
+// 'standard' boxing (allocate object and copy), and optimize
+CorInfoHelpFunc MyICJI::getBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getBoxHelper");
+ return jitInstance->mc->repGetBoxHelper(cls);
+}
+
+// returns the unbox helper. If 'helperCopies' points to a true
+// value it means the JIT is requesting a helper that unboxes the
+// value into a particular location and thus has the signature
+// void unboxHelper(void* dest, CORINFO_CLASS_HANDLE cls, Object* obj)
+// Otherwise (it is null or points at a FALSE value) it is requesting
+// a helper that returns a poitner to the unboxed data
+// void* unboxHelper(CORINFO_CLASS_HANDLE cls, Object* obj)
+// The EE has the option of NOT returning the copy style helper
+// (But must be able to always honor the non-copy style helper)
+// The EE set 'helperCopies' on return to indicate what kind of
+// helper has been created.
+
+CorInfoHelpFunc MyICJI::getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getUnBoxHelper");
+ CorInfoHelpFunc result = jitInstance->mc->repGetUnBoxHelper(cls);
+ return result;
+}
+
+bool MyICJI::getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ jitInstance->mc->cr->AddCall("getReadyToRunHelper");
+ return jitInstance->mc->repGetReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup);
+}
+
+void MyICJI::getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ jitInstance->mc->cr->AddCall("getReadyToRunDelegateCtorHelper");
+ jitInstance->mc->repGetReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+}
+
+const char* MyICJI::getHelperName(
+ CorInfoHelpFunc funcNum
+ )
+{
+ jitInstance->mc->cr->AddCall("getHelperName");
+ return jitInstance->mc->repGetHelperName(funcNum);
+}
+
+// This function tries to initialize the class (run the class constructor).
+// this function returns whether the JIT must insert helper calls before
+// accessing static field or method.
+//
+// See code:ICorClassInfo#ClassConstruction.
+CorInfoInitClassResult MyICJI::initClass(
+ CORINFO_FIELD_HANDLE field, // Non-nullptr - inquire about cctor trigger before static field access
+ // nullptr - inquire about cctor trigger in method prolog
+ CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
+ CORINFO_CONTEXT_HANDLE context, // Exact context of method
+ BOOL speculative // TRUE means don't actually run it
+ )
+{
+ jitInstance->mc->cr->AddCall("initClass");
+ return jitInstance->mc->repInitClass(field, method, context, speculative);
+}
+
+// This used to be called "loadClass". This records the fact
+// that the class must be loaded (including restored if necessary) before we execute the
+// code that we are currently generating. When jitting code
+// the function loads the class immediately. When zapping code
+// the zapper will if necessary use the call to record the fact that we have
+// to do a fixup/restore before running the method currently being generated.
+//
+// This is typically used to ensure value types are loaded before zapped
+// code that manipulates them is executed, so that the GC can access information
+// about those value types.
+void MyICJI::classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("classMustBeLoadedBeforeCodeIsRun");
+ jitInstance->mc->cr->recClassMustBeLoadedBeforeCodeIsRun(cls);
+}
+
+// returns the class handle for the special builtin classes
+CORINFO_CLASS_HANDLE MyICJI::getBuiltinClass (
+ CorInfoClassId classId
+ )
+{
+ jitInstance->mc->cr->AddCall("getBuiltinClass");
+ return jitInstance->mc->repGetBuiltinClass(classId);
+}
+
+// "System.Int32" ==> CORINFO_TYPE_INT..
+CorInfoType MyICJI::getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getTypeForPrimitiveValueClass");
+ return jitInstance->mc->repGetTypeForPrimitiveValueClass(cls);
+}
+
+// TRUE if child is a subtype of parent
+// if parent is an interface, then does child implement / extend parent
+BOOL MyICJI::canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ )
+{
+ jitInstance->mc->cr->AddCall("canCast");
+ return jitInstance->mc->repCanCast(child, parent);
+}
+
+// TRUE if cls1 and cls2 are considered equivalent types.
+BOOL MyICJI::areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ jitInstance->mc->cr->AddCall("areTypesEquivalent");
+ return jitInstance->mc->repAreTypesEquivalent(cls1, cls2);
+}
+
+// returns is the intersection of cls1 and cls2.
+CORINFO_CLASS_HANDLE MyICJI::mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ jitInstance->mc->cr->AddCall("mergeClasses");
+ return jitInstance->mc->repMergeClasses(cls1, cls2);
+}
+
+// Given a class handle, returns the Parent type.
+// For COMObjectType, it returns Class Handle of System.Object.
+// Returns 0 if System.Object is passed in.
+CORINFO_CLASS_HANDLE MyICJI::getParentType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getParentType");
+ return jitInstance->mc->repGetParentType(cls);
+}
+
+// Returns the CorInfoType of the "child type". If the child type is
+// not a primitive type, *clsRet will be set.
+// Given an Array of Type Foo, returns Foo.
+// Given BYREF Foo, returns Foo
+CorInfoType MyICJI::getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ )
+{
+ jitInstance->mc->cr->AddCall("getChildType");
+ return jitInstance->mc->repGetChildType(clsHnd, clsRet);
+}
+
+// Check constraints on type arguments of this class and parent classes
+BOOL MyICJI::satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("satisfiesClassConstraints");
+ return jitInstance->mc->repSatisfiesClassConstraints(cls);
+}
+
+// Check if this is a single dimensional array type
+BOOL MyICJI::isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("isSDArray");
+ return jitInstance->mc->repIsSDArray(cls);
+}
+
+// Get the numbmer of dimensions in an array
+unsigned MyICJI::getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getArrayRank");
+ return jitInstance->mc->repGetArrayRank(cls);
+}
+
+// Get static field data for an array
+void * MyICJI::getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ )
+{
+ jitInstance->mc->cr->AddCall("getArrayInitializationData");
+ return jitInstance->mc->repGetArrayInitializationData(field, size);
+}
+
+// Check Visibility rules.
+CorInfoIsAccessAllowedResult MyICJI::canAccessClass(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper /* If canAccessMethod returns something other
+ than ALLOWED, then this is filled in. */
+ )
+{
+ jitInstance->mc->cr->AddCall("canAccessClass");
+ return jitInstance->mc->repCanAccessClass(pResolvedToken, callerHandle, pAccessHelper);
+}
+
+/**********************************************************************************/
+//
+// ICorFieldInfo
+//
+/**********************************************************************************/
+
+// this function is for debugging only. It returns the field name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* MyICJI::getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldName");
+ return jitInstance->mc->repGetFieldName(ftn, moduleName);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE MyICJI::getFieldClass (
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldClass");
+ return jitInstance->mc->repGetFieldClass(field);
+}
+
+// Return the field's type, if it is CORINFO_TYPE_VALUECLASS 'structType' is set
+// the field's value class (if 'structType' == 0, then don't bother
+// the structure info).
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+CorInfoType MyICJI::getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldType");
+ return jitInstance->mc->repGetFieldType(field, structType, memberParent);
+}
+
+// return the data member's instance offset
+unsigned MyICJI::getFieldOffset(
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldOffset");
+ return jitInstance->mc->repGetFieldOffset(field);
+}
+
+// TODO: jit64 should be switched to the same plan as the i386 jits - use
+// getClassGClayout to figure out the need for writebarrier helper, and inline the copying.
+// The interpretted value class copy is slow. Once this happens, USE_WRITE_BARRIER_HELPERS
+bool MyICJI::isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field)
+{
+ jitInstance->mc->cr->AddCall("isWriteBarrierHelperRequired");
+ bool result = jitInstance->mc->repIsWriteBarrierHelperRequired(field);
+ return result;
+}
+
+void MyICJI::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldInfo");
+ jitInstance->mc->repGetFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+}
+
+// Returns true iff "fldHnd" represents a static field.
+bool MyICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
+{
+ jitInstance->mc->cr->AddCall("isFieldStatic");
+ return jitInstance->mc->repIsFieldStatic(fldHnd);
+}
+
+/*********************************************************************************/
+//
+// ICorDebugInfo
+//
+/*********************************************************************************/
+
+// Query the EE to find out where interesting break points
+// in the code are. The native compiler will ensure that these places
+// have a corresponding break point in native code.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void MyICJI::getBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ unsigned int *cILOffsets, // [OUT] size of pILOffsets
+ DWORD **pILOffsets, // [OUT] IL offsets of interest
+ // jit MUST free with freeArray!
+ ICorDebugInfo::BoundaryTypes *implictBoundaries // [OUT] tell jit, all boundries of this type
+ )
+{
+ jitInstance->mc->cr->AddCall("getBoundaries");
+ jitInstance->mc->repGetBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+
+ //The JIT will want to call freearray on the array we pass back, so move the data into a form that complies with this
+ if(*cILOffsets > 0)
+ {
+ DWORD *realOffsets = (DWORD*)allocateArray(*cILOffsets*sizeof(ICorDebugInfo::BoundaryTypes));
+ memcpy(realOffsets, *pILOffsets, *cILOffsets*sizeof(ICorDebugInfo::BoundaryTypes));
+ *pILOffsets = realOffsets;
+ }
+ else
+ *pILOffsets = 0;
+}
+
+// Report back the mapping from IL to native code,
+// this map should include all boundaries that 'getBoundaries'
+// reported as interesting to the debugger.
+
+// Note that debugger (and profiler) is assuming that all of the
+// offsets form a contiguous block of memory, and that the
+// OffsetMapping is sorted in order of increasing native offset.
+void MyICJI::setBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cMap, // [IN] size of pMap
+ ICorDebugInfo::OffsetMapping *pMap // [IN] map including all points of interest.
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ jitInstance->mc->cr->AddCall("setBoundaries");
+ jitInstance->mc->cr->recSetBoundaries(ftn, cMap, pMap);
+
+ freeArray(pMap);//see note in recSetBoundaries... we own this array and own destroying it.
+}
+
+// Query the EE to find out the scope of local varables.
+// normally the JIT would trash variables after last use, but
+// under debugging, the JIT needs to keep them live over their
+// entire scope so that they can be inspected.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void MyICJI::getVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 *cVars, // [OUT] size of 'vars'
+ ICorDebugInfo::ILVarInfo **vars, // [OUT] scopes of variables of interest
+ // jit MUST free with freeArray!
+ bool *extendOthers // [OUT] it TRUE, then assume the scope
+ // of unmentioned vars is entire method
+ )
+{
+ jitInstance->mc->cr->AddCall("getVars");
+ jitInstance->mc->repGetVars(ftn, cVars, vars, extendOthers);
+
+ //The JIT will want to call freearray on the array we pass back, so move the data into a form that complies with this
+ if(*cVars > 0)
+ {
+ ICorDebugInfo::ILVarInfo *realOffsets = (ICorDebugInfo::ILVarInfo*)allocateArray(*cVars*sizeof(ICorDebugInfo::ILVarInfo));
+ memcpy(realOffsets, *vars, *cVars*sizeof(ICorDebugInfo::ILVarInfo));
+ *vars = realOffsets;
+ }
+ else
+ *vars = nullptr;
+}
+
+// Report back to the EE the location of every variable.
+// note that the JIT might split lifetimes into different
+// locations etc.
+
+void MyICJI::setVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cVars, // [IN] size of 'vars'
+ ICorDebugInfo::NativeVarInfo *vars // [IN] map telling where local vars are stored at what points
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ jitInstance->mc->cr->AddCall("setVars");
+ jitInstance->mc->cr->recSetVars(ftn, cVars, vars);
+ freeArray(vars);//See note in recSetVars... we own destroying this array
+}
+
+/*-------------------------- Misc ---------------------------------------*/
+
+// Used to allocate memory that needs to handed to the EE.
+// For eg, use this to allocated memory for reporting debug info,
+// which will be handed to the EE by setVars() and setBoundaries()
+void * MyICJI::allocateArray(
+ ULONG cBytes
+ )
+{
+ return jitInstance->allocateArray(cBytes);
+}
+
+// JitCompiler will free arrays passed by the EE using this
+// For eg, The EE returns memory in getVars() and getBoundaries()
+// to the JitCompiler, which the JitCompiler should release using
+// freeArray()
+void MyICJI::freeArray(
+ void *array
+ )
+{
+ jitInstance->freeArray(array);
+}
+
+/*********************************************************************************/
+//
+// ICorArgInfo
+//
+/*********************************************************************************/
+
+// advance the pointer to the argument list.
+// a ptr of 0, is special and always means the first argument
+CORINFO_ARG_LIST_HANDLE MyICJI::getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getArgNext");
+ return jitInstance->mc->repGetArgNext(args);
+}
+
+// Get the type of a particular argument
+// CORINFO_TYPE_UNDEF is returned when there are no more arguments
+// If the type returned is a primitive type (or an enum) *vcTypeRet set to nullptr
+// otherwise it is set to the TypeHandle associted with the type
+// Enumerations will always look their underlying type (probably should fix this)
+// Otherwise vcTypeRet is the type as would be seen by the IL,
+// The return value is the type that is used for calling convention purposes
+// (Thus if the EE wants a value class to be passed like an int, then it will
+// return CORINFO_TYPE_INT
+CorInfoTypeWithMod MyICJI::getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ )
+{
+ DWORD exceptionCode = 0;
+ jitInstance->mc->cr->AddCall("getArgType");
+ CorInfoTypeWithMod value = jitInstance->mc->repGetArgType(sig, args, vcTypeRet, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+ return value;
+}
+
+// If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+CORINFO_CLASS_HANDLE MyICJI::getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ DWORD exceptionCode = 0;
+ jitInstance->mc->cr->AddCall("getArgClass");
+ CORINFO_CLASS_HANDLE value = jitInstance->mc->repGetArgClass(sig, args, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+ return value;
+}
+
+// Returns type of HFA for valuetype
+CorInfoType MyICJI::getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ )
+{
+ jitInstance->mc->cr->AddCall("getHFAType");
+ LogError("Hit unimplemented getHFAType");
+ DebugBreakorAV(75);
+ return (CorInfoType)0;
+}
+
+/*****************************************************************************
+* ICorErrorInfo contains methods to deal with SEH exceptions being thrown
+* from the corinfo interface. These methods may be called when an exception
+* with code EXCEPTION_COMPLUS is caught.
+*****************************************************************************/
+
+// Returns the HRESULT of the current exception
+HRESULT MyICJI::GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ jitInstance->mc->cr->AddCall("GetErrorHRESULT");
+ LogError("Hit unimplemented GetErrorHRESULT");
+ DebugBreakorAV(76);
+ return 0;
+}
+
+// Fetches the message of the current exception
+// Returns the size of the message (including terminating null). This can be
+// greater than bufferLength if the buffer is insufficient.
+ULONG MyICJI::GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ )
+{
+ jitInstance->mc->cr->AddCall("GetErrorMessage");
+ LogError("Hit unimplemented GetErrorMessage");
+ DebugBreakorAV(77);
+ return 0;
+}
+
+// returns EXCEPTION_EXECUTE_HANDLER if it is OK for the compile to handle the
+// exception, abort some work (like the inlining) and continue compilation
+// returns EXCEPTION_CONTINUE_SEARCH if exception must always be handled by the EE
+// things like ThreadStoppedException ...
+// returns EXCEPTION_CONTINUE_EXECUTION if exception is fixed up by the EE
+
+int MyICJI::FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ jitInstance->mc->cr->AddCall("FilterException");
+ int result = jitInstance->mc->repFilterException(pExceptionPointers);
+ return result;
+}
+
+// Cleans up internal EE tracking when an exception is caught.
+void MyICJI::HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ jitInstance->mc->cr->AddCall("HandleException");
+}
+
+void MyICJI::ThrowExceptionForJitResult(
+ HRESULT result)
+{
+ jitInstance->mc->cr->AddCall("ThrowExceptionForJitResult");
+ LogError("Hit unimplemented ThrowExceptionForJitResult");
+ DebugBreakorAV(80);
+}
+
+//Throws an exception defined by the given throw helper.
+void MyICJI::ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper)
+{
+ jitInstance->mc->cr->AddCall("ThrowExceptionForHelper");
+ LogError("Hit unimplemented ThrowExceptionForHelper");
+ DebugBreakorAV(81);
+}
+
+/*****************************************************************************
+ * ICorStaticInfo contains EE interface methods which return values that are
+ * constant from invocation to invocation. Thus they may be embedded in
+ * persisted information like statically generated code. (This is of course
+ * assuming that all code versions are identical each time.)
+ *****************************************************************************/
+
+// Return details about EE internal data structures
+void MyICJI::getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ )
+{
+ jitInstance->mc->cr->AddCall("getEEInfo");
+ jitInstance->mc->repGetEEInfo(pEEInfoOut);
+}
+
+// Returns name of the JIT timer log
+LPCWSTR MyICJI::getJitTimeLogFilename()
+{
+ jitInstance->mc->cr->AddCall("getJitTimeLogFilename");
+ //we have the ability to replay this, but we treat it in this case as EE context
+// return jitInstance->eec->jitTimeLogFilename;
+
+ // We want to be able to set COMPLUS_JitTimeLogFile when replaying, to collect JIT
+ // statistics. So, just do a getenv() call. This isn't quite as thorough as
+ // the normal CLR config value functions (which also check the registry), and we've
+ // also hard-coded the variable name here instead of using:
+ // CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitTimeLogFile);
+ // like in the VM, but it works for our purposes.
+ return GetEnvironmentVariableWithDefaultW(W("COMPlus_JitTimeLogFile"));
+}
+
+ /*********************************************************************************/
+ //
+ // Diagnostic methods
+ //
+ /*********************************************************************************/
+
+// this function is for debugging only. Returns method token.
+// Returns mdMethodDefNil for dynamic methods.
+mdMethodDef MyICJI::getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodDefFromMethod");
+ mdMethodDef result = jitInstance->mc->repGetMethodDefFromMethod(hMethod);
+ return result;
+}
+
+// this function is for debugging only. It returns the method name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* MyICJI::getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodName");
+ return jitInstance->mc->repGetMethodName(ftn, moduleName);
+}
+
+// this function is for debugging only. It returns a value that
+// is will always be the same for a given method. It is used
+// to implement the 'jitRange' functionality
+unsigned MyICJI::getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodHash");
+ return jitInstance->mc->repGetMethodHash(ftn);
+}
+
+// this function is for debugging only.
+size_t MyICJI::findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("findNameOfToken");
+ return jitInstance->mc->repFindNameOfToken(module, metaTOK, szFQName, FQNameCapacity);
+}
+
+bool MyICJI::getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ )
+{
+ jitInstance->mc->cr->AddCall("getSystemVAmd64PassStructInRegisterDescriptor");
+ return jitInstance->mc->repGetSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
+}
+
+//Stuff on ICorDynamicInfo
+DWORD MyICJI::getThreadTLSIndex(
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getThreadTLSIndex");
+ return jitInstance->mc->repGetThreadTLSIndex(ppIndirection);
+}
+
+const void * MyICJI::getInlinedCallFrameVptr(
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getInlinedCallFrameVptr");
+ return jitInstance->mc->repGetInlinedCallFrameVptr(ppIndirection);
+}
+
+LONG * MyICJI::getAddrOfCaptureThreadGlobal(
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getAddrOfCaptureThreadGlobal");
+ return jitInstance->mc->repGetAddrOfCaptureThreadGlobal(ppIndirection);
+}
+
+SIZE_T* MyICJI::getAddrModuleDomainID(CORINFO_MODULE_HANDLE module)
+{
+ jitInstance->mc->cr->AddCall("getAddrModuleDomainID");
+ LogError("Hit unimplemented getAddrModuleDomainID");
+ DebugBreakorAV(88);
+ return 0;
+}
+
+// return the native entry point to an EE helper (see CorInfoHelpFunc)
+void* MyICJI::getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getHelperFtn");
+ return jitInstance->mc->repGetHelperFtn(ftnNum, ppIndirection);
+}
+
+// return a callable address of the function (native code). This function
+// may return a different value (depending on whether the method has
+// been JITed or not.
+void MyICJI::getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags)
+{
+ jitInstance->mc->cr->AddCall("getFunctionEntryPoint");
+ jitInstance->mc->repGetFunctionEntryPoint(ftn, pResult, accessFlags);
+}
+
+// return a directly callable address. This can be used similarly to the
+// value returned by getFunctionEntryPoint() except that it is
+// guaranteed to be multi callable entrypoint.
+void MyICJI::getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult)
+{
+ jitInstance->mc->cr->AddCall("getFunctionFixedEntryPoint");
+ jitInstance->mc->repGetFunctionFixedEntryPoint(ftn, pResult);
+}
+
+// get the synchronization handle that is passed to monXstatic function
+void* MyICJI::getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodSync");
+ return jitInstance->mc->repGetMethodSync(ftn, ppIndirection);
+}
+
+// These entry points must be called if a handle is being embedded in
+// the code to be passed to a JIT helper function. (as opposed to just
+// being passed back into the ICorInfo interface.)
+
+// get slow lazy string literal helper to use (CORINFO_HELP_STRCNS*).
+// Returns CORINFO_HELP_UNDEF if lazy string literal helper cannot be used.
+CorInfoHelpFunc MyICJI::getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ )
+{
+ jitInstance->mc->cr->AddCall("getLazyStringLiteralHelper");
+ return jitInstance->mc->repGetLazyStringLiteralHelper(handle);
+}
+
+
+CORINFO_MODULE_HANDLE MyICJI::embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("embedModuleHandle");
+ return jitInstance->mc->repEmbedModuleHandle(handle, ppIndirection);
+}
+
+CORINFO_CLASS_HANDLE MyICJI::embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("embedClassHandle");
+ return jitInstance->mc->repEmbedClassHandle(handle, ppIndirection);
+}
+
+CORINFO_METHOD_HANDLE MyICJI::embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("embedMethodHandle");
+ return jitInstance->mc->repEmbedMethodHandle(handle, ppIndirection);
+}
+
+CORINFO_FIELD_HANDLE MyICJI::embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("embedFieldHandle");
+ return jitInstance->mc->repEmbedFieldHandle(handle, ppIndirection);
+}
+
+// Given a module scope (module), a method handle (context) and
+// a metadata token (metaTOK), fetch the handle
+// (type, field or method) associated with the token.
+// If this is not possible at compile-time (because the current method's
+// code is shared and the token contains generic parameters)
+// then indicate how the handle should be looked up at run-time.
+//
+void MyICJI::embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * pResult)
+{
+ jitInstance->mc->cr->AddCall("embedGenericHandle");
+ jitInstance->mc->repEmbedGenericHandle(pResolvedToken, fEmbedParent, pResult);
+}
+
+// Return information used to locate the exact enclosing type of the current method.
+// Used only to invoke .cctor method from code shared across generic instantiations
+// !needsRuntimeLookup statically known (enclosing type of method itself)
+// needsRuntimeLookup:
+// CORINFO_LOOKUP_THISOBJ use vtable pointer of 'this' param
+// CORINFO_LOOKUP_CLASSPARAM use vtable hidden param
+// CORINFO_LOOKUP_METHODPARAM use enclosing type of method-desc hidden param
+CORINFO_LOOKUP_KIND MyICJI::getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ )
+{
+ jitInstance->mc->cr->AddCall("getLocationOfThisType");
+ return jitInstance->mc->repGetLocationOfThisType(context);
+}
+
+// return the unmanaged target *if method has already been prelinked.*
+void* MyICJI::getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getPInvokeUnmanagedTarget");
+ void *result = jitInstance->mc->repGetPInvokeUnmanagedTarget(method, ppIndirection);
+ return result;
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void* MyICJI::getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getAddressOfPInvokeFixup");
+ return jitInstance->mc->repGetAddressOfPInvokeFixup(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void MyICJI::getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ )
+{
+ jitInstance->mc->cr->AddCall("getAddressOfPInvokeTarget");
+ jitInstance->mc->repGetAddressOfPInvokeTarget(method, pLookup);
+}
+
+// Generate a cookie based on the signature that would needs to be passed
+// to CORINFO_HELP_PINVOKE_CALLI
+LPVOID MyICJI::GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("GetCookieForPInvokeCalliSig");
+ return jitInstance->mc->repGetCookieForPInvokeCalliSig(szMetaSig, ppIndirection);
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool MyICJI::canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ )
+{
+ jitInstance->mc->cr->AddCall("canGetCookieForPInvokeCalliSig");
+ return jitInstance->mc->repCanGetCookieForPInvokeCalliSig(szMetaSig);
+}
+
+// Gets a handle that is checked to see if the current method is
+// included in "JustMyCode"
+CORINFO_JUST_MY_CODE_HANDLE MyICJI::getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getJustMyCodeHandle");
+ return jitInstance->mc->repGetJustMyCodeHandle(method, ppIndirection);
+}
+
+// Gets a method handle that can be used to correlate profiling data.
+// This is the IP of a native method, or the address of the descriptor struct
+// for IL. Always guaranteed to be unique per process, and not to move. */
+void MyICJI::GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ )
+{
+ jitInstance->mc->cr->AddCall("GetProfilingHandle");
+ jitInstance->mc->repGetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+}
+
+// Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+void MyICJI::getCallInfo(
+ // Token info
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+
+ //Generics info
+ CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken,
+
+ //Security info
+ CORINFO_METHOD_HANDLE callerHandle,
+
+ //Jit info
+ CORINFO_CALLINFO_FLAGS flags,
+
+ //out params
+ CORINFO_CALL_INFO *pResult
+ )
+{
+ jitInstance->mc->cr->AddCall("getCallInfo");
+ DWORD exceptionCode = 0;
+ jitInstance->mc->repGetCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+}
+
+BOOL MyICJI::canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE hInstanceType)
+
+{
+ jitInstance->mc->cr->AddCall("canAccessFamily");
+ return jitInstance->mc->repCanAccessFamily(hCaller, hInstanceType);
+}
+// Returns TRUE if the Class Domain ID is the RID of the class (currently true for every class
+// except reflection emitted classes and generics)
+BOOL MyICJI::isRIDClassDomainID(CORINFO_CLASS_HANDLE cls)
+{
+ jitInstance->mc->cr->AddCall("isRIDClassDomainID");
+ LogError("Hit unimplemented isRIDClassDomainID");
+ DebugBreakorAV(107);
+ return false;
+}
+
+// returns the class's domain ID for accessing shared statics
+unsigned MyICJI::getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassDomainID");
+ return jitInstance->mc->repGetClassDomainID(cls, ppIndirection);
+}
+
+
+// return the data's address (for static fields only)
+void* MyICJI::getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldAddress");
+ return jitInstance->mc->repGetFieldAddress(field, ppIndirection);
+}
+
+// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+CORINFO_VARARGS_HANDLE MyICJI::getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getVarArgsHandle");
+ return jitInstance->mc->repGetVarArgsHandle(pSig, ppIndirection);
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool MyICJI::canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ )
+{
+ jitInstance->mc->cr->AddCall("canGetVarArgsHandle");
+ return jitInstance->mc->repCanGetVarArgsHandle(pSig);
+}
+
+// Allocate a string literal on the heap and return a handle to it
+InfoAccessType MyICJI::constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ )
+{
+ jitInstance->mc->cr->AddCall("constructStringLiteral");
+ return jitInstance->mc->repConstructStringLiteral(module, metaTok, ppValue);
+}
+
+InfoAccessType MyICJI::emptyStringLiteral(
+ void **ppValue
+ )
+{
+ jitInstance->mc->cr->AddCall("emptyStringLiteral");
+ return jitInstance->mc->repEmptyStringLiteral(ppValue);
+}
+
+// (static fields only) given that 'field' refers to thread local store,
+// return the ID (TLS index), which is used to find the begining of the
+// TLS data area for the particular DLL 'field' is associated with.
+DWORD MyICJI::getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldThreadLocalStoreID");
+ return jitInstance->mc->repGetFieldThreadLocalStoreID(field, ppIndirection);
+}
+
+// Sets another object to intercept calls to "self" and current method being compiled
+void MyICJI::setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ )
+{
+ jitInstance->mc->cr->AddCall("setOverride");
+ LogError("Hit unimplemented setOverride");
+ DebugBreakorAV(115);
+}
+
+// Adds an active dependency from the context method's module to the given module
+// This is internal callback for the EE. JIT should not call it directly.
+void MyICJI::addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ )
+{
+ jitInstance->mc->cr->AddCall("addActiveDependency");
+ LogError("Hit unimplemented addActiveDependency");
+ DebugBreakorAV(116);
+}
+
+CORINFO_METHOD_HANDLE MyICJI::GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ )
+{
+ jitInstance->mc->cr->AddCall("GetDelegateCtor");
+ return jitInstance->mc->repGetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData);
+}
+
+void MyICJI::MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ )
+{
+ jitInstance->mc->cr->AddCall("MethodCompileComplete");
+ LogError("Hit unimplemented MethodCompileComplete");
+ DebugBreakorAV(118);
+}
+
+// return a thunk that will copy the arguments for the given signature.
+void* MyICJI::getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ )
+{
+ jitInstance->mc->cr->AddCall("getTailCallCopyArgsThunk");
+ return jitInstance->mc->repGetTailCallCopyArgsThunk(pSig, flags);
+}
+
+//Stuff directly on ICorJitInfo
+
+// Returns extended flags for a particular compilation instance.
+DWORD MyICJI::getJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes)
+{
+ jitInstance->mc->cr->AddCall("getJitFlags");
+ return jitInstance->mc->repGetJitFlags(jitFlags, sizeInBytes);
+}
+
+// Runs the given function with the given parameter under an error trap
+// and returns true if the function completes successfully. We fake this
+// up a bit for SuperPMI and simply catch all exceptions.
+bool MyICJI::runWithErrorTrap(void (*function)(void*), void *param)
+{
+ return RunWithErrorTrap(function, param);
+}
+
+// return memory manager that the JIT can use to allocate a regular memory
+IEEMemoryManager* MyICJI::getMemoryManager()
+{
+ return InitIEEMemoryManager(jitInstance);
+}
+
+// get a block of memory for the code, readonly data, and read-write data
+void MyICJI::allocMem (
+ ULONG hotCodeSize, /* IN */
+ ULONG coldCodeSize, /* IN */
+ ULONG roDataSize, /* IN */
+ ULONG xcptnsCount, /* IN */
+ CorJitAllocMemFlag flag, /* IN */
+ void ** hotCodeBlock, /* OUT */
+ void ** coldCodeBlock, /* OUT */
+ void ** roDataBlock /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("allocMem");
+ //TODO-Cleanup: investigate if we need to check roDataBlock as well. Could hot block size be ever 0?
+ *hotCodeBlock = HeapAlloc(jitInstance->mc->cr->getCodeHeap(),0,hotCodeSize);
+ if (coldCodeSize>0)
+ *coldCodeBlock = HeapAlloc(jitInstance->mc->cr->getCodeHeap(),0,coldCodeSize);
+ else
+ *coldCodeBlock = nullptr;
+ *roDataBlock = HeapAlloc(jitInstance->mc->cr->getCodeHeap(),0,roDataSize);
+ jitInstance->mc->cr->recAllocMem(hotCodeSize, coldCodeSize, roDataSize, xcptnsCount, flag, hotCodeBlock, coldCodeBlock, roDataBlock);
+}
+
+// Reserve memory for the method/funclet's unwind information.
+// Note that this must be called before allocMem. It should be
+// called once for the main method, once for every funclet, and
+// once for every block of cold code for which allocUnwindInfo
+// will be called.
+//
+// This is necessary because jitted code must allocate all the
+// memory needed for the unwindInfo at the allocMem call.
+// For prejitted code we split up the unwinding information into
+// separate sections .rdata and .pdata.
+//
+void MyICJI::reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("reserveUnwindInfo");
+ jitInstance->mc->cr->recReserveUnwindInfo(isFunclet, isColdCode, unwindSize);
+}
+
+// Allocate and initialize the .rdata and .pdata for this method or
+// funclet, and get the block of memory needed for the machine-specific
+// unwind information (the info for crawling the stack frame).
+// Note that allocMem must be called first.
+//
+// Parameters:
+//
+// pHotCode main method code buffer, always filled in
+// pColdCode cold code buffer, only filled in if this is cold code,
+// null otherwise
+// startOffset start of code block, relative to appropriate code buffer
+// (e.g. pColdCode if cold, pHotCode if hot).
+// endOffset end of code block, relative to appropriate code buffer
+// unwindSize size of unwind info pointed to by pUnwindBlock
+// pUnwindBlock pointer to unwind info
+// funcKind type of funclet (main method code, handler, filter)
+//
+void MyICJI::allocUnwindInfo (
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("allocUnwindInfo");
+ jitInstance->mc->cr->recAllocUnwindInfo(pHotCode, pColdCode, startOffset, endOffset, unwindSize, pUnwindBlock, funcKind);
+}
+
+// Get a block of memory needed for the code manager information,
+// (the info for enumerating the GC pointers while crawling the
+// stack frame).
+// Note that allocMem must be called first
+void * MyICJI::allocGCInfo (
+ size_t size /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("allocGCInfo");
+ void *temp = (unsigned char*)HeapAlloc(jitInstance->mc->cr->getCodeHeap(),0,size);
+ jitInstance->mc->cr->recAllocGCInfo(size, temp);
+
+ return temp;
+}
+
+// Only used on x64.
+void MyICJI::yieldExecution()
+{
+ jitInstance->mc->cr->AddCall("yieldExecution");
+}
+
+// Indicate how many exception handler blocks are to be returned.
+// This is guaranteed to be called before any 'setEHinfo' call.
+// Note that allocMem must be called before this method can be called.
+void MyICJI::setEHcount (
+ unsigned cEH /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("setEHcount");
+ jitInstance->mc->cr->recSetEHcount(cEH);
+}
+
+// Set the values for one particular exception handler block.
+//
+// Handler regions should be lexically contiguous.
+// This is because FinallyIsUnwinding() uses lexicality to
+// determine if a "finally" clause is executing.
+void MyICJI::setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("setEHinfo");
+ jitInstance->mc->cr->recSetEHinfo(EHnumber, clause);
+}
+
+// Level 1 -> fatalError, Level 2 -> Error, Level 3 -> Warning
+// Level 4 means happens 10 times in a run, level 5 means 100, level 6 means 1000 ...
+// returns non-zero if the logging succeeded
+BOOL MyICJI::logMsg(unsigned level, const char* fmt, va_list args)
+{
+ jitInstance->mc->cr->AddCall("logMsg");
+
+// if(level<=2)
+// {
+ //jitInstance->mc->cr->recMessageLog(fmt, args);
+ //DebugBreakorAV(0x99);
+ //}
+ jitInstance->mc->cr->recMessageLog(fmt, args);
+ return 0;
+}
+
+// do an assert. will return true if the code should retry (DebugBreak)
+// returns false, if the assert should be igored.
+int MyICJI::doAssert(const char* szFile, int iLine, const char* szExpr)
+{
+ jitInstance->mc->cr->AddCall("doAssert");
+ char buff[16 * 1024];
+ sprintf_s(buff, sizeof(buff), "%s (%d) - %s", szFile, iLine, szExpr);
+
+ LogIssue(ISSUE_ASSERT, "%s", buff);
+ jitInstance->mc->cr->recMessageLog(buff);
+
+ // Under "/boa", ask the user if they want to attach a debugger. If they do, the debugger will be attached,
+ // then we'll call DebugBreakorAV(), which will issue a __debugbreak() and actually cause
+ // us to stop in the debugger.
+ if (breakOnDebugBreakorAV)
+ {
+ DbgBreakCheck(szFile, iLine, szExpr);
+ }
+
+ DebugBreakorAV(0x7b);
+ return 0;
+}
+
+void MyICJI::reportFatalError(CorJitResult result)
+{
+ jitInstance->mc->cr->AddCall("reportFatalError");
+ jitInstance->mc->cr->recReportFatalError(result);
+}
+
+// allocate a basic block profile buffer where execution counts will be stored
+// for jitted basic blocks.
+HRESULT MyICJI::allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer
+ )
+{
+ jitInstance->mc->cr->AddCall("allocBBProfileBuffer");
+ return jitInstance->mc->cr->repAllocBBProfileBuffer(count, profileBuffer);
+}
+
+// get profile information to be used for optimizing the current method. The format
+// of the buffer is the same as the format the JIT passes to allocBBProfileBuffer.
+HRESULT MyICJI::getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG * count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer,
+ ULONG * numRuns
+ )
+{
+ jitInstance->mc->cr->AddCall("getBBProfileData");
+ return jitInstance->mc->repGetBBProfileData(ftnHnd, count, profileBuffer, numRuns);
+}
+
+// Associates a native call site, identified by its offset in the native code stream, with
+// the signature information and method handle the JIT used to lay out the call site. If
+// the call site has no signature information (e.g. a helper call) or has no method handle
+// (e.g. a CALLI P/Invoke), then null should be passed instead.
+void MyICJI::recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("recordCallSite");
+ jitInstance->mc->cr->repRecordCallSite(instrOffset, callSig, methodHandle);
+}
+
+// A relocation is recorded if we are pre-jitting.
+// A jump thunk may be inserted if we are jitting
+void MyICJI::recordRelocation(
+ void * location, /* IN */
+ void * target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("recordRelocation");
+ jitInstance->mc->cr->repRecordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+WORD MyICJI::getRelocTypeHint(void * target)
+{
+ jitInstance->mc->cr->AddCall("getRelocTypeHint");
+ WORD result = jitInstance->mc->repGetRelocTypeHint(target);
+ return result;
+}
+
+// A callback to identify the range of address known to point to
+// compiler-generated native entry points that call back into
+// MSIL.
+void MyICJI::getModuleNativeEntryPointRange(
+ void ** pStart, /* OUT */
+ void ** pEnd /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getModuleNativeEntryPointRange");
+ LogError("Hit unimplemented getModuleNativeEntryPointRange");
+ DebugBreakorAV(128);
+}
+
+// For what machine does the VM expect the JIT to generate code? The VM
+// returns one of the IMAGE_FILE_MACHINE_* values. Note that if the VM
+// is cross-compiling (such as the case for crossgen), it will return a
+// different value than if it was compiling for the host architecture.
+//
+DWORD MyICJI::getExpectedTargetArchitecture()
+{
+#if defined(_TARGET_X86_)
+ return IMAGE_FILE_MACHINE_I386;
+#elif defined(_TARGET_AMD64_)
+ return IMAGE_FILE_MACHINE_AMD64;
+#elif defined(_TARGET_ARM_)
+ return IMAGE_FILE_MACHINE_ARMNT;
+#elif defined(_TARGET_ARM64_)
+ return IMAGE_FILE_MACHINE_ARM64;
+#else
+ return IMAGE_FILE_MACHINE_UNKNOWN;
+#endif
+}
diff --git a/src/ToolBox/superpmi/superpmi/icorjitinfo.h b/src/ToolBox/superpmi/superpmi/icorjitinfo.h
new file mode 100644
index 0000000000..2182db3be6
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitInfo
+#define _ICorJitInfo
+
+#include "runtimedetails.h"
+#include "ieememorymanager.h"
+
+extern ICorJitInfo *pICJI;
+
+class MyICJI : public ICorJitInfo
+{
+
+#include "icorjitinfoimpl.h"
+
+public:
+
+ //Added extras... todo add padding to detect corruption?
+ JitInstance *jitInstance;
+};
+
+ICorJitInfo* InitICorJitInfo(JitInstance *jitInstance);
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/ieememorymanager.cpp b/src/ToolBox/superpmi/superpmi/ieememorymanager.cpp
new file mode 100644
index 0000000000..8bb95b1f4d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/ieememorymanager.cpp
@@ -0,0 +1,110 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "spmiutil.h"
+#include "ieememorymanager.h"
+
+IEEMemoryManager *pIEEMM = nullptr;
+HANDLE processHeap = INVALID_HANDLE_VALUE;
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+
+HRESULT STDMETHODCALLTYPE MyIEEMM::QueryInterface(REFIID id, void **pInterface)
+{
+ DebugBreakorAV(133);
+ return 0;
+}
+ULONG STDMETHODCALLTYPE MyIEEMM::AddRef()
+{
+ DebugBreakorAV(134);
+ return 0;
+}
+ULONG STDMETHODCALLTYPE MyIEEMM::Release()
+{
+ DebugBreakorAV(135);
+ return 0;
+}
+
+HANDLE virtHeap = INVALID_HANDLE_VALUE;
+
+//***************************************************************************
+// IEEMemoryManager methods for locking
+//***************************************************************************
+LPVOID STDMETHODCALLTYPE MyIEEMM::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+ if(virtHeap == INVALID_HANDLE_VALUE)
+ virtHeap = HeapCreate(0, 0xFFFF, 0);
+ if(virtHeap != INVALID_HANDLE_VALUE)
+ return HeapAlloc(virtHeap, HEAP_ZERO_MEMORY, dwSize);
+ return nullptr;
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return HeapFree(virtHeap, 0, lpAddress);
+}
+SIZE_T STDMETHODCALLTYPE MyIEEMM::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ DebugBreakorAV(136);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ DebugBreakorAV(137);
+ return 0;
+}
+HANDLE STDMETHODCALLTYPE MyIEEMM::ClrGetProcessHeap()
+{
+ DebugBreakorAV(138);
+ return 0;
+}
+HANDLE STDMETHODCALLTYPE MyIEEMM::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ DebugBreakorAV(139);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrHeapDestroy(HANDLE hHeap)
+{
+ DebugBreakorAV(140);
+ return 0;
+}
+LPVOID STDMETHODCALLTYPE MyIEEMM::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
+{
+ return HeapAlloc(hHeap,dwFlags,dwBytes);
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ return HeapFree(hHeap, dwFlags, lpMem);
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ DebugBreakorAV(141);
+ return 0;
+}
+HANDLE STDMETHODCALLTYPE MyIEEMM::ClrGetProcessExecutableHeap()
+{
+ if (processHeap == INVALID_HANDLE_VALUE)
+ {
+ DWORD flOptions = 0;
+#ifndef FEATURE_PAL // TODO-Review: PAL doesn't have HEAP_CREATE_ENABLE_EXECUTE. Is this ok?
+ flOptions = HEAP_CREATE_ENABLE_EXECUTE;
+#endif // !FEATURE_PAL
+ processHeap = HeapCreate(flOptions, 10000, 0);
+ }
+ return processHeap;
+}
+
+IEEMemoryManager *InitIEEMemoryManager(JitInstance *jitInstance)
+{
+ if(pIEEMM==nullptr)
+ {
+ MyIEEMM *ieemm = new MyIEEMM();
+ ieemm->jitInstance = jitInstance;
+ pIEEMM = ieemm;
+ }
+ return pIEEMM;
+}
diff --git a/src/ToolBox/superpmi/superpmi/ieememorymanager.h b/src/ToolBox/superpmi/superpmi/ieememorymanager.h
new file mode 100644
index 0000000000..90763bc233
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/ieememorymanager.h
@@ -0,0 +1,113 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _IEEMemoryManager
+#define _IEEMemoryManager
+
+#include "runtimedetails.h"
+#include "jitinstance.h"
+
+/*
+interface IEEMemoryManager : IUnknown
+{
+ LPVOID ClrVirtualAlloc(
+ [in] LPVOID lpAddress, // region to reserve or commit
+ [in] SIZE_T dwSize, // size of region
+ [in] DWORD flAllocationType, // type of allocation
+ [in] DWORD flProtect // type of access protection
+ )
+
+ BOOL ClrVirtualFree(
+ [in] LPVOID lpAddress, // address of region
+ [in] SIZE_T dwSize, // size of region
+ [in] DWORD dwFreeType // operation type
+ )
+
+ SIZE_T ClrVirtualQuery(
+ [in] const void* lpAddress, // address of region
+ [in] PMEMORY_BASIC_INFORMATION lpBuffer, // information buffer
+ [in] SIZE_T dwLength // size of buffer
+ )
+
+ BOOL ClrVirtualProtect(
+ [in] LPVOID lpAddress, // region of committed pages
+ [in] SIZE_T dwSize, // size of the region
+ [in] DWORD flNewProtect, // desired access protection
+ [in] DWORD* lpflOldProtect // old protection
+ )
+
+ HANDLE ClrGetProcessHeap()
+
+ HANDLE ClrHeapCreate(
+ [in] DWORD flOptions, // heap allocation attributes
+ [in] SIZE_T dwInitialSize, // initial heap size
+ [in] SIZE_T dwMaximumSize // maximum heap size
+ )
+
+ BOOL ClrHeapDestroy(
+ [in] HANDLE hHeap // handle to heap
+ )
+
+ LPVOID ClrHeapAlloc(
+ [in] HANDLE hHeap, // handle to private heap block
+ [in] DWORD dwFlags, // heap allocation control
+ [in] SIZE_T dwBytes // number of bytes to allocate
+ )
+
+ BOOL ClrHeapFree(
+ [in] HANDLE hHeap, // handle to heap
+ [in] DWORD dwFlags, // heap free options
+ [in] LPVOID lpMem // pointer to memory
+ )
+
+ BOOL ClrHeapValidate(
+ [in] HANDLE hHeap, // handle to heap
+ [in] DWORD dwFlags, // heap access options
+ [in] const void* lpMem // optional pointer to memory block
+ )
+
+ HANDLE ClrGetProcessExecutableHeap()
+
+}; // interface IEEMemoryManager
+
+*/
+extern HANDLE virtHeap;
+extern IEEMemoryManager *pIEEMM;
+extern HANDLE processHeap;
+class MyIEEMM : public IEEMemoryManager
+{
+private:
+
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IEEMemoryManager methods for locking
+ //***************************************************************************
+ LPVOID STDMETHODCALLTYPE ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
+ BOOL STDMETHODCALLTYPE ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
+ SIZE_T STDMETHODCALLTYPE ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
+ BOOL STDMETHODCALLTYPE ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessHeap();
+ HANDLE STDMETHODCALLTYPE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize);
+ BOOL STDMETHODCALLTYPE ClrHeapDestroy(HANDLE hHeap);
+ LPVOID STDMETHODCALLTYPE ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
+ BOOL STDMETHODCALLTYPE ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
+ BOOL STDMETHODCALLTYPE ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessExecutableHeap();
+
+public:
+ //Added extras... todo add padding to detect corruption?
+ JitInstance *jitInstance;
+};
+
+IEEMemoryManager *InitIEEMemoryManager(JitInstance *jitInstance);
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/iexecutionengine.cpp b/src/ToolBox/superpmi/superpmi/iexecutionengine.cpp
new file mode 100644
index 0000000000..db9e121d8e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/iexecutionengine.cpp
@@ -0,0 +1,213 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "spmiutil.h"
+#include "iexecutionengine.h"
+
+LPVOID TLS_Slots[MAX_PREDEFINED_TLS_SLOT];
+class MyIEE;
+IExecutionEngine *pIEE = nullptr;
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+
+HRESULT STDMETHODCALLTYPE MyIEE::QueryInterface(REFIID id, void **pInterface)
+{
+ //TODO-Cleanup: check the rid
+ *pInterface = InitIEEMemoryManager(nullptr);
+ return 0;
+}
+ULONG STDMETHODCALLTYPE MyIEE::AddRef()
+{
+ DebugBreakorAV(142);
+ return 0;
+}
+ULONG STDMETHODCALLTYPE MyIEE::Release()
+{
+ DebugBreakorAV(143);
+ return 0;
+}
+
+//***************************************************************************
+// IExecutionEngine methods for TLS
+//***************************************************************************
+
+DWORD TlsIndex = 42;
+
+// Associate a callback for cleanup with a TLS slot
+VOID STDMETHODCALLTYPE MyIEE::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ //TODO-Cleanup: figure an appropriate realish value for this
+}
+
+// Get the TLS block for fast Get/Set operations
+LPVOID* STDMETHODCALLTYPE MyIEE::TLS_GetDataBlock()
+{
+ //We were previously allocating a TlsIndex with
+ //the master slot index set to a nullptr
+ //so in the new version we just return nullptr
+ //and it seems to be working for now
+ return nullptr;
+}
+
+// Get the value at a slot
+LPVOID STDMETHODCALLTYPE MyIEE::TLS_GetValue(DWORD slot)
+{
+ /* if(slot>MAX_PREDEFINED_TLS_SLOT)
+ __debugbreak();
+ void *thing = TlsGetValue(TlsIndex);
+
+ // if(slot == 0x9)
+ //return 0; //trick out the contract system to be as off as possible.
+ //TODO-Cleanup: does anything beyond contracts care? This seems like a pretty thin mock.
+ */
+ return TLS_Slots[slot];
+}
+
+// Get the value at a slot, return FALSE if TLS info block doesn't exist
+BOOL STDMETHODCALLTYPE MyIEE::TLS_CheckValue(DWORD slot, LPVOID * pValue)
+{
+ DebugBreakorAV(144);
+ //TODO-Cleanup: does anything beyond contracts care? This seems like a pretty thin mock.
+ return true;
+}
+// Set the value at a slot
+VOID STDMETHODCALLTYPE MyIEE::TLS_SetValue(DWORD slot, LPVOID pData)
+{
+ if (slot > MAX_PREDEFINED_TLS_SLOT)
+ {
+ DebugBreakorAV(143);
+ return;
+ }
+ void *thing = TlsGetValue(TlsIndex);//TODO-Cleanup: this seems odd.. explain?
+
+ //TODO-Cleanup: does anything beyond contracts care? This seems like a pretty thin mock.
+ TLS_Slots[slot] = pData;
+}
+// Free TLS memory block and make callback
+VOID STDMETHODCALLTYPE MyIEE::TLS_ThreadDetaching()
+{
+ DebugBreakorAV(145);
+}
+
+//***************************************************************************
+// IExecutionEngine methods for locking
+//***************************************************************************
+
+CRITSEC_COOKIE STDMETHODCALLTYPE MyIEE::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
+{
+ return (CRITSEC_COOKIE)(size_t)0xbad01241;
+}
+void STDMETHODCALLTYPE MyIEE::DestroyLock(CRITSEC_COOKIE lock)
+{
+ DebugBreakorAV(146);
+}
+void STDMETHODCALLTYPE MyIEE::AcquireLock(CRITSEC_COOKIE lock)
+{
+}
+void STDMETHODCALLTYPE MyIEE::ReleaseLock(CRITSEC_COOKIE lock)
+{
+}
+
+EVENT_COOKIE STDMETHODCALLTYPE MyIEE::CreateAutoEvent(BOOL bInitialState)
+{
+ DebugBreakorAV(147);
+ return 0;
+}
+EVENT_COOKIE STDMETHODCALLTYPE MyIEE::CreateManualEvent(BOOL bInitialState)
+{
+ DebugBreakorAV(148);
+ return 0;
+}
+void STDMETHODCALLTYPE MyIEE::CloseEvent(EVENT_COOKIE event)
+{
+ DebugBreakorAV(149);
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrSetEvent(EVENT_COOKIE event)
+{
+ DebugBreakorAV(150);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrResetEvent(EVENT_COOKIE event)
+{
+ DebugBreakorAV(151);
+ return 0;
+}
+DWORD STDMETHODCALLTYPE MyIEE::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ DebugBreakorAV(152);
+ return 0;
+}
+DWORD STDMETHODCALLTYPE MyIEE::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds)
+{
+ DebugBreakorAV(153);
+ return 0;
+}
+SEMAPHORE_COOKIE STDMETHODCALLTYPE MyIEE::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ DebugBreakorAV(154);
+ return 0;
+}
+void STDMETHODCALLTYPE MyIEE::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ DebugBreakorAV(155);
+}
+DWORD STDMETHODCALLTYPE MyIEE::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ DebugBreakorAV(156);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ DebugBreakorAV(157);
+ return 0;
+}
+MUTEX_COOKIE STDMETHODCALLTYPE MyIEE::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName)
+{
+ DebugBreakorAV(158);
+ return 0;
+}
+void STDMETHODCALLTYPE MyIEE::ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ DebugBreakorAV(159);
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ DebugBreakorAV(160);
+ return 0;
+}
+DWORD STDMETHODCALLTYPE MyIEE::ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ DebugBreakorAV(161);
+ return 0;
+}
+
+DWORD STDMETHODCALLTYPE MyIEE::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ DebugBreakorAV(162);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrAllocationDisallowed()
+{
+ DebugBreakorAV(163);
+ return 0;
+}
+void STDMETHODCALLTYPE MyIEE::GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ DebugBreakorAV(164);
+}
+
+MyIEE *InitIExecutionEngine()
+{
+ MyIEE *iee = new MyIEE();
+ pIEE = iee;
+ return iee;
+}
diff --git a/src/ToolBox/superpmi/superpmi/iexecutionengine.h b/src/ToolBox/superpmi/superpmi/iexecutionengine.h
new file mode 100644
index 0000000000..f1a76b9a4f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/iexecutionengine.h
@@ -0,0 +1,151 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _IExecutionEngine
+#define _IExecutionEngine
+
+#include "ieememorymanager.h"
+
+/*
+interface IExecutionEngine : IUnknown
+{
+ // Thread Local Storage is based on logical threads. The underlying
+ // implementation could be threads, fibers, or something more exotic.
+ // Slot numbers are predefined. This is not a general extensibility
+ // mechanism.
+
+ // Associate a callback function for releasing TLS on thread/fiber death.
+ // This can be NULL.
+ void TLS_AssociateCallback([in] DWORD slot, [in] PTLS_CALLBACK_FUNCTION callback)
+
+ // May be called once to get the master TLS block slot for fast Get/Set operations
+ DWORD TLS_GetMasterSlotIndex()
+
+ // Get the value at a slot
+ PVOID TLS_GetValue([in] DWORD slot)
+
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL TLS_CheckValue([in] DWORD slot, [out] PVOID * pValue)
+
+ // Set the value at a slot
+ void TLS_SetValue([in] DWORD slot, [in] PVOID pData)
+
+ // Free TLS memory block and make callback
+ void TLS_ThreadDetaching()
+
+ // Critical Sections are sometimes exposed to the host and therefore need to be
+ // reflected from all CLR DLLs to the EE.
+ //
+ // In addition, we always monitor interactions between the lock & the GC, based
+ // on the GC mode in which the lock is acquired and we restrict what operations
+ // are permitted while holding the lock based on this.
+ //
+ // Finally, we we rank all our locks to prevent deadlock across all the DLLs of
+ // the CLR. This is the level argument to CreateLock.
+ //
+ // All usage of these locks must be exception-safe. To achieve this, we suggest
+ // using Holders (see holder.h & crst.h). In fact, within the EE code cannot
+ // hold locks except by using exception-safe holders.
+
+ CRITSEC_COOKIE CreateLock([in] LPCSTR szTag, [in] LPCSTR level, [in] CrstFlags flags)
+
+ void DestroyLock([in] CRITSEC_COOKIE lock)
+
+ void AcquireLock([in] CRITSEC_COOKIE lock)
+
+ void ReleaseLock([in] CRITSEC_COOKIE lock)
+
+ EVENT_COOKIE CreateAutoEvent([in] BOOL bInitialState)
+ EVENT_COOKIE CreateManualEvent([in] BOOL bInitialState)
+ void CloseEvent([in] EVENT_COOKIE event)
+ BOOL ClrSetEvent([in] EVENT_COOKIE event)
+ BOOL ClrResetEvent([in] EVENT_COOKIE event)
+ DWORD WaitForEvent([in] EVENT_COOKIE event, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ DWORD WaitForSingleObject([in] HANDLE handle, [in] DWORD dwMilliseconds)
+
+ // OS header file defines CreateSemaphore.
+ SEMAPHORE_COOKIE ClrCreateSemaphore([in] DWORD dwInitial, [in] DWORD dwMax)
+ void ClrCloseSemaphore([in] SEMAPHORE_COOKIE semaphore)
+ DWORD ClrWaitForSemaphore([in] SEMAPHORE_COOKIE semaphore, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ BOOL ClrReleaseSemaphore([in] SEMAPHORE_COOKIE semaphore, [in] LONG lReleaseCount, [in] LONG *lpPreviousCount)
+
+ MUTEX_COOKIE ClrCreateMutex([in]LPSECURITY_ATTRIBUTES lpMutexAttributes, [in]BOOL bInitialOwner, [in]LPCTSTR lpName)
+ DWORD ClrWaitForMutex([in] MUTEX_COOKIE mutex, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ BOOL ClrReleaseMutex([in] MUTEX_COOKIE mutex)
+ void ClrCloseMutex([in] MUTEX_COOKIE mutex)
+
+ DWORD ClrSleepEx([in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+
+ BOOL ClrAllocationDisallowed()
+
+ void GetLastThrownObjectExceptionFromThread([out] void **ppvException)
+
+}; // interface IExecutionEngine
+*/
+
+extern LPVOID TLS_Slots[MAX_PREDEFINED_TLS_SLOT];
+extern IExecutionEngine *pIEE;
+
+class MyIEE : public IExecutionEngine
+{
+private:
+
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IExecutionEngine methods for TLS
+ //***************************************************************************
+ // Associate a callback for cleanup with a TLS slot
+ VOID STDMETHODCALLTYPE TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback);
+
+ // Get the TLS block for fast Get/Set operations
+ LPVOID* STDMETHODCALLTYPE TLS_GetDataBlock();
+ // Get the value at a slot
+ LPVOID STDMETHODCALLTYPE TLS_GetValue(DWORD slot);
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL STDMETHODCALLTYPE TLS_CheckValue(DWORD slot, LPVOID * pValue);
+ // Set the value at a slot
+ VOID STDMETHODCALLTYPE TLS_SetValue(DWORD slot, LPVOID pData);
+ // Free TLS memory block and make callback
+ VOID STDMETHODCALLTYPE TLS_ThreadDetaching();
+
+ //***************************************************************************
+ // IExecutionEngine methods for locking
+ //***************************************************************************
+ CRITSEC_COOKIE STDMETHODCALLTYPE CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags);
+ void STDMETHODCALLTYPE DestroyLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE AcquireLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE ReleaseLock(CRITSEC_COOKIE lock);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateAutoEvent(BOOL bInitialState);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateManualEvent(BOOL bInitialState);
+ void STDMETHODCALLTYPE CloseEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrSetEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrResetEvent(EVENT_COOKIE event);
+ DWORD STDMETHODCALLTYPE WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds);
+ SEMAPHORE_COOKIE STDMETHODCALLTYPE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax);
+ void STDMETHODCALLTYPE ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore);
+ DWORD STDMETHODCALLTYPE ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount);
+ MUTEX_COOKIE STDMETHODCALLTYPE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName);
+ void STDMETHODCALLTYPE ClrCloseMutex(MUTEX_COOKIE mutex);
+ BOOL STDMETHODCALLTYPE ClrReleaseMutex(MUTEX_COOKIE mutex);
+ DWORD STDMETHODCALLTYPE ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrAllocationDisallowed();
+ void STDMETHODCALLTYPE GetLastThrownObjectExceptionFromThread(void **ppvException);
+};
+
+MyIEE *InitIExecutionEngine();
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/jitdebugger.cpp b/src/ToolBox/superpmi/superpmi/jitdebugger.cpp
new file mode 100644
index 0000000000..867664e82f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jitdebugger.cpp
@@ -0,0 +1,459 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//*****************************************************************************
+// JitDebugger.cpp
+//
+// Code to help with invoking the just-in-time debugger
+//
+//*****************************************************************************
+
+#include "standardpch.h"
+#include "runtimedetails.h"
+#include "logging.h"
+#include "jitdebugger.h"
+
+// JIT debugging is broken due to utilcode changes to support LongFile. We need to re-copy
+// or adjust the implementation of the below functions so they link properly.
+#if 0
+#ifndef FEATURE_PAL // No just-in-time debugger under PAL
+#define FEATURE_JIT_DEBUGGING
+#endif // !FEATURE_PAL
+#endif // 0
+
+#ifndef FEATURE_JIT_DEBUGGING
+
+int DbgBreakCheck(
+ const char* szFile,
+ int iLine,
+ const char* szExpr)
+{
+ LogError("SuperPMI: Assert Failure (PID %d, Thread %d/%x)\n"
+ "%s\n"
+ "\n"
+ "%s, Line: %d\n",
+ GetCurrentProcessId(), GetCurrentThreadId(), GetCurrentThreadId(),
+ szExpr, szFile, iLine);
+
+ return 1;
+}
+
+#else // FEATURE_JIT_DEBUGGING
+
+// Some definitions to make this code look more like the CLR utilcode versions it was stolen from.
+#define WszCreateEvent CreateEventW
+#define WszGetModuleFileName GetModuleFileNameW
+
+#ifdef WszRegOpenKeyEx
+#undef WszRegOpenKeyEx
+#define WszRegOpenKeyEx RegOpenKeyExW
+#endif
+
+#ifndef _WIN64
+//------------------------------------------------------------------------------
+// Returns TRUE if we are running on a 64-bit OS in WoW, FALSE otherwise.
+BOOL RunningInWow64()
+{
+ static int s_Wow64Process;
+
+ if (s_Wow64Process == 0)
+ {
+ BOOL fWow64Process = FALSE;
+
+ if (!IsWow64Process(GetCurrentProcess(), &fWow64Process))
+ fWow64Process = FALSE;
+
+ s_Wow64Process = fWow64Process ? 1 : -1;
+ }
+
+ return (s_Wow64Process == 1) ? TRUE : FALSE;
+}
+#endif
+
+//------------------------------------------------------------------------------
+//
+// GetRegistryLongValue - Reads a configuration LONG value from the registry.
+//
+// Parameters
+// hKeyParent -- Parent key
+// szKey -- key to open
+// szName -- name of the value
+// pValue -- put value here, if found
+// fReadNonVirtualizedKey -- whether to read 64-bit hive on WOW64
+//
+// Returns
+// TRUE -- If the value was found and read
+// FALSE -- The value was not found, could not be read, or was not DWORD
+//
+// Exceptions
+// None
+//------------------------------------------------------------------------------
+BOOL GetRegistryLongValue(HKEY hKeyParent,
+ LPCWSTR szKey,
+ LPCWSTR szName,
+ long *pValue,
+ BOOL fReadNonVirtualizedKey)
+{
+ DWORD ret; // Return value from registry operation.
+ HKEY hkey; // Registry key.
+ long iValue; // The value to read.
+ DWORD iType; // Type of value to get.
+ DWORD iSize; // Size of buffer.
+ REGSAM samDesired = KEY_READ; // Desired access rights to the key
+
+ if (fReadNonVirtualizedKey)
+ {
+ if (RunningInWow64())
+ {
+ samDesired |= KEY_WOW64_64KEY;
+ }
+ }
+
+ ret = WszRegOpenKeyEx(hKeyParent, szKey, 0, samDesired, &hkey);
+
+ // If we opened the key, see if there is a value.
+ if (ret == ERROR_SUCCESS)
+ {
+ iType = REG_DWORD;
+ iSize = sizeof(long);
+ ret = RegQueryValueExW(hkey, szName, NULL, &iType, reinterpret_cast<BYTE*>(&iValue), &iSize);
+
+ if (ret == ERROR_SUCCESS && iType == REG_DWORD && iSize == sizeof(long))
+ { // We successfully read a DWORD value.
+ *pValue = iValue;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+} // GetRegistryLongValue
+
+//----------------------------------------------------------------------------
+//
+// GetCurrentModuleFileName - Retrieve the current module's filename
+//
+// Arguments:
+// pBuffer - output string buffer
+// pcchBuffer - the number of characters of the string buffer
+//
+// Return Value:
+// S_OK on success, else detailed error code.
+//
+// Note:
+//
+//----------------------------------------------------------------------------
+HRESULT GetCurrentModuleFileName(__out_ecount(*pcchBuffer) LPWSTR pBuffer, __inout DWORD *pcchBuffer)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if ((pBuffer == NULL) || (pcchBuffer == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ // Get the appname to look up in the exclusion or inclusion list.
+ WCHAR appPath[MAX_PATH + 2];
+
+ DWORD ret = WszGetModuleFileName(NULL, appPath, NumItems(appPath));
+
+ if ((ret == NumItems(appPath)) || (ret == 0))
+ {
+ // The module file name exceeded maxpath, or GetModuleFileName failed.
+ return E_UNEXPECTED;
+ }
+
+ // Pick off the part after the path.
+ WCHAR* appName = wcsrchr(appPath, L'\\');
+
+ // If no backslash, use the whole name; if there is a backslash, skip it.
+ appName = appName ? appName+1 : appPath;
+
+ if (*pcchBuffer < wcslen(appName))
+ {
+ *pcchBuffer = static_cast<DWORD>(wcslen(appName)) + 1;
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+
+ wcscpy_s(pBuffer, *pcchBuffer, appName);
+ return S_OK;
+}
+
+
+//----------------------------------------------------------------------------
+//
+// IsCurrentModuleFileNameInAutoExclusionList - decide if the current module's filename
+// is in the AutoExclusionList list
+//
+// Arguments:
+// None
+//
+// Return Value:
+// TRUE or FALSE
+//
+// Note:
+// This function cannot be used in out of process scenarios like DAC because it
+// looks at current module's filename. In OOP we want to use target process's
+// module's filename.
+//
+//----------------------------------------------------------------------------
+BOOL IsCurrentModuleFileNameInAutoExclusionList()
+{
+ HKEY hKeyHolder;
+
+ // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\\AutoExclusionList"
+ DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerAutoExclusionListKey, 0, KEY_READ, &hKeyHolder);
+
+ if (ret != ERROR_SUCCESS)
+ {
+ // there's not even an AutoExclusionList hive
+ return FALSE;
+ }
+
+ WCHAR wszAppName[MAX_PATH];
+ DWORD cchAppName = NumItems(wszAppName);
+
+ // Get the appname to look up in the exclusion or inclusion list.
+ if (GetCurrentModuleFileName(wszAppName, &cchAppName) != S_OK)
+ {
+ // Assume it is not on the exclusion list if we cannot find the module's filename.
+ return FALSE;
+ }
+
+ // Look in AutoExclusionList key for appName get the size of any value stored there.
+ DWORD value, valueType, valueSize = sizeof(value);
+ ret = RegQueryValueExW(hKeyHolder, wszAppName, 0, &valueType, reinterpret_cast<BYTE*>(&value), &valueSize);
+ if ((ret == ERROR_SUCCESS) && (valueType == REG_DWORD) && (value == 1))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+} // IsCurrentModuleFileNameInAutoExclusionList
+
+
+
+//*****************************************************************************
+// Retrieve information regarding what registered default debugger
+//*****************************************************************************
+void GetDebuggerSettingInfo(LPWSTR wszDebuggerString, DWORD cchDebuggerString, BOOL *pfAuto)
+{
+ HRESULT hr = GetDebuggerSettingInfoWorker(wszDebuggerString, &cchDebuggerString, pfAuto);
+
+ if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ {
+ // error!
+ }
+} // GetDebuggerSettingInfo
+
+//---------------------------------------------------------------------------------------
+//
+// GetDebuggerSettingInfoWorker - retrieve information regarding what registered default debugger
+//
+// Arguments:
+// * wszDebuggerString - [out] the string buffer to store the registered debugger launch
+// string
+// * pcchDebuggerString - [in, out] the size of string buffer in characters
+// * pfAuto - [in] the flag to indicate whether the debugger neeeds to be launched
+// automatically
+//
+// Return Value:
+// HRESULT indicating success or failure.
+//
+// Notes:
+// * wszDebuggerString can be NULL. When wszDebuggerString is NULL, pcchDebuggerString should
+// * point to a DWORD of zero. pcchDebuggerString cannot be NULL, and the DWORD pointed by
+// * pcchDebuggerString will store the used or required string buffer size in characters.
+HRESULT GetDebuggerSettingInfoWorker(__out_ecount_part_opt(*pcchDebuggerString, *pcchDebuggerString) LPWSTR wszDebuggerString, DWORD * pcchDebuggerString, BOOL * pfAuto)
+{
+ if ((pcchDebuggerString == NULL) || ((wszDebuggerString == NULL) && (*pcchDebuggerString != 0)))
+ {
+ return E_INVALIDARG;
+ }
+
+ // Initialize the output values before we start.
+ if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0))
+ {
+ *wszDebuggerString = L'\0';
+ }
+
+ if (pfAuto != NULL)
+ {
+ *pfAuto = FALSE;
+ }
+
+ HKEY hKey;
+
+ // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"
+ DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerKey, 0, KEY_READ, &hKey);
+
+ if (ret != ERROR_SUCCESS)
+ { // Wow, there's not even an AeDebug hive, so no native debugger, no auto.
+ return S_OK;
+ }
+
+ // Look in AeDebug key for "Debugger"; get the size of any value stored there.
+ DWORD valueType, valueSize;
+ ret = RegQueryValueExW(hKey, kUnmanagedDebuggerValue, 0, &valueType, 0, &valueSize);
+
+ if ((wszDebuggerString == NULL) || (*pcchDebuggerString < valueSize / sizeof(WCHAR)))
+ {
+ *pcchDebuggerString = valueSize / sizeof(WCHAR) + 1;
+ RegCloseKey(hKey);
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+
+ *pcchDebuggerString = valueSize / sizeof(WCHAR);
+
+ // The size of an empty string with the null terminator is 2.
+ BOOL fIsDebuggerStringEmptry = valueSize <= 2 ? TRUE : FALSE;
+
+ if ((ret != ERROR_SUCCESS) || (valueType != REG_SZ) || fIsDebuggerStringEmptry)
+ {
+ RegCloseKey(hKey);
+ return S_OK;
+ }
+
+ ret = RegQueryValueExW(hKey, kUnmanagedDebuggerValue, NULL, NULL, reinterpret_cast< LPBYTE >(wszDebuggerString), &valueSize);
+ if (ret != ERROR_SUCCESS)
+ {
+ *wszDebuggerString = L'\0';
+ RegCloseKey(hKey);
+ return S_OK;
+ }
+
+ if (pfAuto != NULL)
+ {
+ BOOL fAuto = FALSE;
+
+ // Get the appname to look up in DebugApplications key.
+ WCHAR wzAppName[MAX_PATH];
+ DWORD cchAppName = NumItems(wzAppName);
+ long iValue;
+
+ // Check DebugApplications setting
+ if ((SUCCEEDED(GetCurrentModuleFileName(wzAppName, &cchAppName))) &&
+ (
+ GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsKey, wzAppName, &iValue, TRUE)
+ ) &&
+ (iValue == 1))
+ {
+ fAuto = TRUE;
+ }
+ else
+ {
+ // Look in AeDebug key for "Auto"; get the size of any value stored there.
+ ret = RegQueryValueExW(hKey, kUnmanagedDebuggerAutoValue, 0, &valueType, 0, &valueSize);
+ if ((ret == ERROR_SUCCESS) && (valueType == REG_SZ) && (valueSize / sizeof(WCHAR) < MAX_PATH))
+ {
+ WCHAR wzAutoKey[MAX_PATH];
+ valueSize = NumItems(wzAutoKey) * sizeof(WCHAR);
+ RegQueryValueExW(hKey, kUnmanagedDebuggerAutoValue, NULL, NULL, reinterpret_cast< LPBYTE >(wzAutoKey), &valueSize);
+
+ // The OS's behavior is to consider Auto to be FALSE unless the first character is set
+ // to 1. They don't take into consideration the following characters. Also if the value
+ // isn't present they assume an Auto value of FALSE.
+ if ((wzAutoKey[0] == L'1') && !IsCurrentModuleFileNameInAutoExclusionList())
+ {
+ fAuto = TRUE;
+ }
+ }
+ }
+
+ *pfAuto = fAuto;
+ }
+
+ RegCloseKey(hKey);
+ return S_OK;
+} // GetDebuggerSettingInfoWorker
+
+BOOL LaunchJITDebugger()
+{
+ BOOL fSuccess = FALSE;
+
+ WCHAR debugger[1000];
+ GetDebuggerSettingInfo(debugger, NumItems(debugger), NULL);
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ // We can leave this event as it is since it is inherited by a child process.
+ // We will block one scheduler, but the process is asking a user if they want to attach debugger.
+ HANDLE eventHandle = WszCreateEvent(&sa, TRUE, FALSE, NULL);
+ if (eventHandle == NULL) {
+ return FALSE;
+ }
+
+ WCHAR cmdLine[1000];
+ swprintf_s(cmdLine, debugger, GetCurrentProcessId(), eventHandle);
+
+ STARTUPINFOW StartupInfo;
+ memset(&StartupInfo, 0, sizeof(StartupInfo));
+ StartupInfo.cb = sizeof(StartupInfo);
+ StartupInfo.lpDesktop = L"Winsta0\\Default";
+
+ PROCESS_INFORMATION ProcessInformation;
+ if (CreateProcessW(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation))
+ {
+ WaitForSingleObject(eventHandle, INFINITE);
+ fSuccess = TRUE;
+ }
+
+ CloseHandle(eventHandle);
+
+ return fSuccess;
+}
+
+
+// See if we should invoke the just-in-time debugger on an assert.
+int DbgBreakCheck(
+ const char* szFile,
+ int iLine,
+ const char* szExpr)
+{
+ char dialogText[1000];
+ char dialogTitle[1000];
+
+ sprintf_s(dialogText, sizeof(dialogText), "%s\n\n%s, Line: %d\n\nAbort - Kill program\nRetry - Debug\nIgnore - Keep running\n",
+ szExpr, szFile, iLine);
+ sprintf_s(dialogTitle, sizeof(dialogTitle), "SuperPMI: Assert Failure (PID %d, Thread %d/%x) ",
+ GetCurrentProcessId(), GetCurrentThreadId(), GetCurrentThreadId());
+
+ // Tell user there was an error.
+ int ret = MessageBoxA(NULL, dialogText, dialogTitle, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION | MB_TOPMOST);
+
+ switch(ret)
+ {
+ case IDABORT:
+ TerminateProcess(GetCurrentProcess(), 1);
+ break;
+
+ // Tell caller to break at the correct loction.
+ case IDRETRY:
+
+ if (IsDebuggerPresent())
+ {
+ SetErrorMode(0);
+ }
+ else
+ {
+ LaunchJITDebugger();
+ }
+
+ return 1;
+
+ case IDIGNORE:
+ // nothing to do
+ break;
+ }
+
+ return 0;
+}
+
+#endif // FEATURE_JIT_DEBUGGING
diff --git a/src/ToolBox/superpmi/superpmi/jitdebugger.h b/src/ToolBox/superpmi/superpmi/jitdebugger.h
new file mode 100644
index 0000000000..eee2318f8f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jitdebugger.h
@@ -0,0 +1,33 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _JitDebugger
+#define _JitDebugger
+
+extern bool breakOnDebugBreakorAV; // It's kind of awful that I'm making this global, but it was kind of awful that it was file-global already.
+
+//
+// Functions to support just-in-time debugging.
+//
+
+BOOL GetRegistryLongValue(HKEY hKeyParent, // Parent key.
+ LPCWSTR szKey, // Key name to look at.
+ LPCWSTR szName, // Name of value to get.
+ long *pValue, // Put value here, if found.
+ BOOL fReadNonVirtualizedKey); // Whether to read 64-bit hive on WOW64
+
+HRESULT GetCurrentModuleFileName(__out_ecount(*pcchBuffer) LPWSTR pBuffer, __inout DWORD *pcchBuffer);
+
+#ifndef _WIN64
+BOOL RunningInWow64();
+#endif
+
+BOOL IsCurrentModuleFileNameInAutoExclusionList();
+HRESULT GetDebuggerSettingInfoWorker(__out_ecount_part_opt(*pcchDebuggerString, *pcchDebuggerString) LPWSTR wszDebuggerString, DWORD * pcchDebuggerString, BOOL * pfAuto);
+void GetDebuggerSettingInfo(LPWSTR wszDebuggerString, DWORD cchDebuggerString, BOOL *pfAuto);
+
+int DbgBreakCheck(const char* szFile, int iLine, const char* szExpr);
+
+#endif // !_JitDebugger
diff --git a/src/ToolBox/superpmi/superpmi/jithost.cpp b/src/ToolBox/superpmi/superpmi/jithost.cpp
new file mode 100644
index 0000000000..cb61e48de2
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jithost.cpp
@@ -0,0 +1,120 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "superpmi.h"
+#include "jitinstance.h"
+#include "icorjitinfo.h"
+#include "jithost.h"
+
+// Look for 'key' as an environment variable named COMPlus_<key>. The returned value
+// is nullptr if it is not found, or a string if found. If not nullptr, the returned
+// value must be freed with jitInstance.freeLongLivedArray(value).
+wchar_t* GetCOMPlusVariable(const wchar_t* key, JitInstance& jitInstance)
+{
+ static const wchar_t Prefix[] = W("COMPlus_");
+ static const size_t PrefixLen = (sizeof(Prefix) / sizeof(Prefix[0])) - 1;
+
+ // Prepend "COMPlus_" to the provided key
+ size_t keyLen = wcslen(key);
+ size_t keyBufferLen = keyLen + PrefixLen + 1;
+ wchar_t* keyBuffer = reinterpret_cast<wchar_t*>(jitInstance.allocateArray(static_cast<ULONG>(sizeof(wchar_t) * keyBufferLen)));
+ wcscpy_s(keyBuffer, keyBufferLen, Prefix);
+ wcscpy_s(&keyBuffer[PrefixLen], keyLen + 1, key);
+
+ // Look up the environment variable
+ DWORD valueLen = GetEnvironmentVariableW(keyBuffer, nullptr, 0);
+ if (valueLen == 0)
+ {
+ jitInstance.freeArray(keyBuffer);
+ return nullptr;
+ }
+
+ // Note this value must live as long as the jit instance does.
+ wchar_t* value = reinterpret_cast<wchar_t*>(jitInstance.allocateLongLivedArray(sizeof(wchar_t) * valueLen));
+ DWORD newValueLen = GetEnvironmentVariableW(keyBuffer, value, valueLen);
+
+ jitInstance.freeArray(keyBuffer);
+ if (valueLen < newValueLen)
+ {
+ jitInstance.freeLongLivedArray(value);
+ return nullptr;
+ }
+
+ return value;
+}
+
+JitHost::JitHost(JitInstance& jitInstance) : jitInstance(jitInstance)
+{
+}
+
+void* JitHost::allocateMemory(size_t size, bool usePageAllocator)
+{
+ return InitIEEMemoryManager(&jitInstance)->ClrVirtualAlloc(nullptr, size, 0, 0);
+}
+
+void JitHost::freeMemory(void* block, bool usePageAllocator)
+{
+ InitIEEMemoryManager(&jitInstance)->ClrVirtualFree(block, 0, 0);
+}
+
+int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
+{
+ jitInstance.mc->cr->AddCall("getIntConfigValue");
+ int result = jitInstance.mc->repGetIntConfigValue(key, defaultValue);
+
+ if (result != defaultValue)
+ {
+ return result;
+ }
+
+ // Look for special case keys.
+ if (wcscmp(key, W("SuperPMIMethodContextNumber")) == 0)
+ {
+ return jitInstance.mc->index;
+ }
+
+ // If the result is the default value, probe the environment for
+ // a COMPlus variable with the same name.
+ wchar_t* complus = GetCOMPlusVariable(key, jitInstance);
+ if (complus == nullptr)
+ {
+ return defaultValue;
+ }
+
+ // Parse the value as a hex integer.
+ wchar_t* endPtr;
+ result = static_cast<int>(wcstoul(complus, &endPtr, 16));
+ bool succeeded = (errno != ERANGE) && (endPtr != complus);
+ jitInstance.freeLongLivedArray(complus);
+
+ return succeeded ? result : defaultValue;
+}
+
+const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
+{
+ jitInstance.mc->cr->AddCall("getStringConfigValue");
+ const wchar_t *result = jitInstance.mc->repGetStringConfigValue(key);
+
+ if (result != nullptr)
+ {
+ // Now we need to dup it, so you can call freeStringConfigValue() on what we return.
+ size_t resultLenInChars = wcslen(result) + 1;
+ wchar_t *dupResult = (wchar_t*)jitInstance.allocateLongLivedArray((ULONG)(sizeof(wchar_t) * resultLenInChars));
+ wcscpy_s(dupResult, resultLenInChars, result);
+
+ return dupResult;
+ }
+
+ // If the result is the default value, probe the environment for
+ // a COMPlus variable with the same name.
+ return GetCOMPlusVariable(key, jitInstance);
+}
+
+void JitHost::freeStringConfigValue(const wchar_t* value)
+{
+ jitInstance.mc->cr->AddCall("freeStringConfigValue");
+ jitInstance.freeLongLivedArray((void*)value);
+}
diff --git a/src/ToolBox/superpmi/superpmi/jithost.h b/src/ToolBox/superpmi/superpmi/jithost.h
new file mode 100644
index 0000000000..60181cd907
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jithost.h
@@ -0,0 +1,20 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _JITHOST
+#define _JITHOST
+
+class JitHost : public ICorJitHost
+{
+public:
+ JitHost(JitInstance& jitInstance);
+
+#include "icorjithostimpl.h"
+
+private:
+ JitInstance& jitInstance;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/jitinstance.cpp b/src/ToolBox/superpmi/superpmi/jitinstance.cpp
new file mode 100644
index 0000000000..5003e91f96
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jitinstance.cpp
@@ -0,0 +1,436 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "superpmi.h"
+#include "jitinstance.h"
+#include "coreclrcallbacks.h"
+#include "icorjitinfo.h"
+#include "jithost.h"
+#include "errorhandling.h"
+#include "spmiutil.h"
+
+JitInstance *JitInstance::InitJit(char *nameOfJit, bool breakOnAssert, SimpleTimer *st1, MethodContext* firstContext)
+{
+ JitInstance *jit = new JitInstance();
+ if (jit == nullptr)
+ {
+ LogError("Failed to allocate a JitInstance");
+ return nullptr;
+ }
+
+ if (st1 != nullptr)
+ st1->Start();
+ HRESULT hr = jit->StartUp(nameOfJit, false, breakOnAssert, firstContext);
+ if (st1 != nullptr)
+ st1->Stop();
+ if (hr != S_OK)
+ {
+ LogError("Startup of JIT(%s) failed %d", nameOfJit, hr);
+ return nullptr;
+ }
+ if (st1 != nullptr)
+ LogVerbose("Jit startup took %fms", st1->GetMilliseconds());
+ return jit;
+}
+
+HRESULT JitInstance::StartUp(char * PathToJit, bool copyJit, bool parambreakOnDebugBreakorAV, MethodContext* firstContext)
+{
+ //startup jit
+ DWORD dwRetVal = 0;
+ UINT uRetVal = 0;
+ BOOL bRetVal = FALSE;
+
+ breakOnDebugBreakorAV = parambreakOnDebugBreakorAV;
+
+ char pFullPathName[MAX_PATH];
+ char lpTempPathBuffer[MAX_PATH];
+ char szTempFileName[MAX_PATH];
+
+//Get an allocator instance
+//Note: we do this to keep cleanup somewhat simple...
+ ourHeap = ::HeapCreate(0,0,0);
+ if(ourHeap == nullptr)
+ {
+ LogError("Failed to get a new heap (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+
+//find the full jit path
+ dwRetVal = ::GetFullPathNameA(PathToJit, MAX_PATH, pFullPathName, nullptr);
+ if (dwRetVal == 0)
+ {
+ LogError("GetFullPathName failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+
+//Store the full path to the jit
+ PathToOriginalJit = (char *)::HeapAlloc(ourHeap, 0, MAX_PATH);
+ if(PathToOriginalJit == nullptr)
+ {
+ LogError("1st HeapAlloc failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ ::strcpy_s(PathToOriginalJit, MAX_PATH, pFullPathName);
+
+ if(copyJit)
+ {
+ //Get a temp file location
+ dwRetVal = ::GetTempPathA(MAX_PATH, lpTempPathBuffer);
+ if (dwRetVal == 0)
+ {
+ LogError("GetTempPath failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ if (dwRetVal > MAX_PATH)
+ {
+ LogError("GetTempPath returned a path that was larger than MAX_PATH");
+ return E_FAIL;
+ }
+ //Get a temp filename
+ uRetVal = ::GetTempFileNameA(lpTempPathBuffer, "Jit", 0, szTempFileName);
+ if (uRetVal == 0)
+ {
+ LogError("GetTempFileName failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ dwRetVal = (DWORD)::strlen(szTempFileName);
+
+ //Store the full path to the temp jit
+ PathToTempJit = (char *)::HeapAlloc(ourHeap, 0, MAX_PATH);
+ if(PathToTempJit == nullptr)
+ {
+ LogError("2nd HeapAlloc failed 0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ ::strcpy_s(PathToTempJit, MAX_PATH, szTempFileName);
+
+ //Copy Temp File
+ bRetVal = ::CopyFileA(PathToOriginalJit, PathToTempJit, FALSE);
+ if (bRetVal == FALSE)
+ {
+ LogError("CopyFile failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ }
+ else
+ PathToTempJit = PathToOriginalJit;
+
+#ifndef FEATURE_PAL // No file version APIs in the PAL
+ //Do a quick version check
+ DWORD dwHandle = 0;
+ DWORD fviSize = GetFileVersionInfoSizeA(PathToTempJit, &dwHandle);
+
+ if ((fviSize != 0)&&(dwHandle==0))
+ {
+ unsigned char *fviData = new unsigned char[fviSize];
+ if (GetFileVersionInfoA(PathToTempJit, dwHandle, fviSize, fviData))
+ {
+ UINT size = 0;
+ VS_FIXEDFILEINFO *verInfo = nullptr;
+ if (VerQueryValueA(fviData, "\\", (LPVOID*)&verInfo, &size))
+ {
+ if (size)
+ {
+ if (verInfo->dwSignature == 0xfeef04bd)
+ LogDebug("'%s' is version %u.%u.%u.%u", PathToTempJit,
+ (verInfo->dwFileVersionMS)>>16, (verInfo->dwFileVersionMS)&0xFFFF,
+ (verInfo->dwFileVersionLS)>>16, (verInfo->dwFileVersionLS)&0xFFFF);
+ }
+ }
+ }
+ delete[] fviData;
+ }
+#endif // !FEATURE_PAL
+
+//Load Library
+ hLib = ::LoadLibraryA(PathToTempJit);
+ if(hLib == 0)
+ {
+ LogError("LoadLibrary failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+
+//get entry points
+ pngetJit = (PgetJit)::GetProcAddress(hLib, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return -1;
+ }
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(hLib, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return -1;
+ }
+ pnjitStartup = (PjitStartup)::GetProcAddress(hLib, "jitStartup");
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ CoreClrCallbacks *cccallbacks = InitCoreClrCallbacks();
+ pnsxsJitStartup(*cccallbacks);
+
+ // Setup ICorJitHost and call jitStartup if necessary
+ if (pnjitStartup != nullptr)
+ {
+ mc = firstContext;
+ jitHost = new JitHost(*this);
+ pnjitStartup(jitHost);
+ }
+
+ pJitInstance = pngetJit();
+ if(pJitInstance == nullptr)
+ {
+ LogError("pngetJit gave us null");
+ return -1;
+ }
+
+ // Check the JIT version identifier.
+
+ GUID versionId;
+ memset(&versionId, 0, sizeof(GUID));
+ pJitInstance->getVersionIdentifier(&versionId);
+
+ if (memcmp(&versionId, &JITEEVersionIdentifier, sizeof(GUID)) != 0)
+ {
+ // Mismatched version ID. Fail the load.
+ pJitInstance = NULL;
+
+ LogError("Jit Compiler has wrong version identifier");
+ return -1;
+ }
+
+
+ icji = InitICorJitInfo(this);
+
+ return S_OK;
+}
+
+bool JitInstance::reLoad(MethodContext* firstContext)
+{
+ FreeLibrary(hLib);
+
+//Load Library
+ hLib = ::LoadLibraryA(PathToTempJit);
+ if(hLib == 0)
+ {
+ LogError("LoadLibrary failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+
+//get entry points
+ pngetJit = (PgetJit)::GetProcAddress(hLib, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(hLib, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+ pnjitStartup = (PjitStartup)::GetProcAddress(hLib, "jitStartup");
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ CoreClrCallbacks *cccallbacks = InitCoreClrCallbacks();
+ pnsxsJitStartup(*cccallbacks);
+
+ // Setup ICorJitHost and call jitStartup if necessary
+ if (pnjitStartup != nullptr)
+ {
+ mc = firstContext;
+ jitHost = new JitHost(*this);
+ pnjitStartup(jitHost);
+ }
+
+ pJitInstance = pngetJit();
+ if(pJitInstance == nullptr)
+ {
+ LogError("pngetJit gave us null");
+ return false;
+ }
+
+ icji = InitICorJitInfo(this);
+
+ return true;
+}
+
+JitInstance::Result JitInstance::CompileMethod(MethodContext *MethodToCompile, int mcIndex, bool collectThroughput)
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException
+ {
+ JitInstance* pThis;
+ JitInstance::Result result;
+ CORINFO_METHOD_INFO info;
+ unsigned flags;
+ int mcIndex;
+ bool collectThroughput;
+ } param;
+ param.pThis = this;
+ param.result = RESULT_SUCCESS; // assume success
+ param.flags = 0;
+ param.mcIndex = mcIndex;
+ param.collectThroughput = collectThroughput;
+
+ //store to instance field our raw values, so we can figure things out a bit later...
+ mc = MethodToCompile;
+
+ times[0] = 0;
+ times[1] = 0;
+
+ mc->repEnvironmentSet(); //Sets envvars
+ stj.Start();
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ BYTE *NEntryBlock = nullptr;
+ ULONG NCodeSizeBlock = 0;
+
+ pParam->pThis->mc->repCompileMethod(&pParam->info, &pParam->flags);
+ if (pParam->collectThroughput)
+ {
+ pParam->pThis->lt.Start();
+ }
+ CorJitResult temp = pParam->pThis->pJitInstance->compileMethod(pParam->pThis->icji, &pParam->info, pParam->flags, &NEntryBlock, &NCodeSizeBlock);
+ if (pParam->collectThroughput)
+ {
+ pParam->pThis->lt.Stop();
+ pParam->pThis->times[0] = pParam->pThis->lt.GetCycles();
+ }
+ if ((SpmiTargetArchitecture == SPMI_TARGET_ARCHITECTURE_ARM64) && (temp == CORJIT_SKIPPED))
+ {
+ // For altjit, treat SKIPPED as OK
+ temp = CORJIT_OK;
+ }
+ if (temp == CORJIT_OK)
+ {
+ //capture the results of compilation
+ pParam->pThis->mc->cr->recCompileMethod(&NEntryBlock, &NCodeSizeBlock, temp);
+ pParam->pThis->mc->cr->recAllocMemCapture();
+ pParam->pThis->mc->cr->recAllocGCInfoCapture();
+
+ pParam->pThis->mc->cr->recMessageLog("Successful Compile");
+ }
+ else
+ {
+ LogDebug("compileMethod failed with result %d", temp);
+ pParam->result = RESULT_ERROR;
+ }
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndStop)
+ {
+ SpmiException e(&param.exceptionPointers);
+
+ if (e.GetCode() == EXCEPTIONCODE_MC)
+ {
+ char *message = e.GetExceptionMessage();
+ LogMissing("Method context %d failed to replay: %s", mcIndex, message);
+ e.DeleteMessage();
+ param.result = RESULT_MISSING;
+ }
+ else
+ {
+ e.ShowAndDeleteMessage();
+ param.result = RESULT_ERROR;
+ }
+ }
+ PAL_ENDTRY
+
+ stj.Stop();
+ if (collectThroughput)
+ {
+ // If we get here, we know it compiles
+ timeResult(param.info, param.flags);
+ }
+ mc->repEnvironmentUnset(); //Unsets envvars
+
+ mc->cr->secondsToCompile = stj.GetSeconds();
+
+ return param.result;
+}
+
+void JitInstance::timeResult(CORINFO_METHOD_INFO info, unsigned flags)
+{
+ BYTE *NEntryBlock = nullptr;
+ ULONG NCodeSizeBlock = 0;
+
+ int sampleSize = 10;
+ // Save 2 smallest times. To help reduce noise, we will look at the closest pair of these.
+ unsigned __int64 time;
+
+ for (int i = 0; i < sampleSize; i++)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ lt.Start();
+ pJitInstance->compileMethod(icji, &info, flags, &NEntryBlock, &NCodeSizeBlock);
+ lt.Stop();
+ time = lt.GetCycles();
+ if (times[1] == 0)
+ {
+ if (time < times[0])
+ {
+ times[1] = times[0];
+ times[0] = time;
+ }
+ else
+ times[1] = time;
+ }
+ else if (time < times[1])
+ {
+ if (time < times[0])
+ {
+ times[1] = times[0];
+ times[0] = time;
+ }
+ else
+ times[1] = time;
+ }
+ }
+}
+
+/*-------------------------- Misc ---------------------------------------*/
+
+// Used to allocate memory that needs to handed to the EE.
+// For eg, use this to allocated memory for reporting debug info,
+// which will be handed to the EE by setVars() and setBoundaries()
+void * JitInstance::allocateArray(
+ ULONG cBytes
+ )
+{
+ mc->cr->AddCall("allocateArray");
+ return HeapAlloc(mc->cr->getCodeHeap(),0,cBytes);
+}
+
+// Used to allocate memory that needs to live as long as the jit
+// instance does.
+void * JitInstance::allocateLongLivedArray(
+ ULONG cBytes
+ )
+{
+ return HeapAlloc(ourHeap,0,cBytes);
+}
+
+// JitCompiler will free arrays passed by the EE using this
+// For eg, The EE returns memory in getVars() and getBoundaries()
+// to the JitCompiler, which the JitCompiler should release using
+// freeArray()
+void JitInstance::freeArray(
+ void *array
+ )
+{
+ mc->cr->AddCall("freeArray");
+ HeapFree(mc->cr->getCodeHeap(),0,array);
+}
+
+// Used to free memory allocated by JitInstance::allocateLongLivedArray.
+void JitInstance::freeLongLivedArray(
+ void *array
+ )
+{
+ HeapFree(ourHeap,0,array);
+}
diff --git a/src/ToolBox/superpmi/superpmi/jitinstance.h b/src/ToolBox/superpmi/superpmi/jitinstance.h
new file mode 100644
index 0000000000..c85c2f5bee
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jitinstance.h
@@ -0,0 +1,57 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _JitInstance
+#define _JitInstance
+
+#include "superpmi.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "cycletimer.h"
+
+class JitInstance
+{
+private:
+ char *PathToOriginalJit;
+ char *PathToTempJit;
+ HANDLE ourHeap;
+ HMODULE hLib;
+ PgetJit pngetJit;
+ PjitStartup pnjitStartup;
+ PsxsJitStartup pnsxsJitStartup;
+ ICorJitHost *jitHost;
+ ICorJitInfo *icji;
+ SimpleTimer stj;
+
+ JitInstance() {};
+ void timeResult(CORINFO_METHOD_INFO info, unsigned flags);
+
+public:
+ enum Result
+ {
+ RESULT_ERROR,
+ RESULT_SUCCESS,
+ RESULT_MISSING
+ };
+ CycleTimer lt;
+ MethodContext *mc;
+ ULONGLONG times[2];
+ ICorJitCompiler *pJitInstance;
+
+ // Allocate and initialize the jit provided
+ static JitInstance *InitJit(char *nameOfJit, bool breakOnAssert, SimpleTimer *st1, MethodContext* firstContext);
+
+ HRESULT StartUp(char *PathToJit, bool copyJit, bool breakOnDebugBreakorAV, MethodContext* firstContext);
+ bool reLoad(MethodContext* firstContext);
+
+ Result CompileMethod(MethodContext *MethodToCompile, int mcIndex, bool collectThroughput);
+
+ void* allocateArray(ULONG size);
+ void* allocateLongLivedArray(ULONG size);
+ void freeArray(void* array);
+ void freeLongLivedArray(void* array);
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp b/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp
new file mode 100644
index 0000000000..0a43f02dd9
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp
@@ -0,0 +1,126 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//-----------------------------------------------------------------------------
+// MethodStatsEmitter.cpp - Emits useful method stats for compiled methods for analysis
+//-----------------------------------------------------------------------------
+
+#include "standardpch.h"
+#include "methodstatsemitter.h"
+#include "logging.h"
+
+MethodStatsEmitter::MethodStatsEmitter(char *nameOfInput)
+{
+ char filename[MAX_PATH + 1];
+ sprintf_s(filename, MAX_PATH + 1, "%s.stats", nameOfInput);
+
+ hStatsFile = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hStatsFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", filename, GetLastError());
+ }
+}
+
+MethodStatsEmitter::~MethodStatsEmitter()
+{
+ if (hStatsFile != INVALID_HANDLE_VALUE)
+ {
+ if (CloseHandle(hStatsFile) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ }
+ }
+}
+
+void MethodStatsEmitter::Emit(int methodNumber, MethodContext *mc, ULONGLONG firstTime, ULONGLONG secondTime)
+{
+ if (hStatsFile != INVALID_HANDLE_VALUE)
+ {
+ //Print the CSV header row
+ char rowData[2048];
+ DWORD charCount = 0;
+ DWORD bytesWritten = 0;
+
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'h') != NULL || strchr(statsTypes, 'H') != NULL)
+ {
+ //Obtain the method Hash
+ char md5Hash[MD5_HASH_BUFFER_SIZE];
+ if (mc->dumpMethodMD5HashToBuffer(md5Hash, MD5_HASH_BUFFER_SIZE) != MD5_HASH_BUFFER_SIZE)
+ md5Hash[0] = 0;
+
+ charCount += sprintf(rowData + charCount, "%s,", md5Hash);
+ }
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'n') != NULL || strchr(statsTypes, 'N') != NULL)
+ {
+ charCount += sprintf(rowData + charCount, "%d,", methodNumber);
+ }
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'i') != NULL || strchr(statsTypes, 'I') != NULL)
+ {
+ //Obtain the IL code size for this method
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+ mc->repCompileMethod(&info, &flags);
+
+ charCount += sprintf(rowData + charCount, "%d,", info.ILCodeSize);
+ }
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'a') != NULL || strchr(statsTypes, 'A') != NULL)
+ {
+ //Obtain the compiled method ASM size
+ BYTE *temp;
+ DWORD codeSize;
+ CorJitResult result;
+ if (mc->cr->CompileMethod != nullptr)
+ mc->cr->repCompileMethod(&temp, &codeSize, &result);
+ else
+ codeSize = 0;//this is likely a thin mc
+
+ charCount += sprintf(rowData + charCount, "%d,", codeSize);
+ }
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 't') != NULL || strchr(statsTypes, 'T') != NULL)
+ {
+ charCount += sprintf(rowData + charCount, "%llu,%llu,", firstTime, secondTime);
+ }
+
+ //get rid of the final ',' and replace it with a '\n'
+ rowData[charCount - 1] = '\n';
+
+ if (!WriteFile(hStatsFile, rowData, charCount, &bytesWritten, nullptr) || bytesWritten != charCount)
+ {
+ LogError("Failed to write row header '%s'. GetLastError()=%u", rowData, GetLastError());
+ }
+ }
+}
+
+void MethodStatsEmitter::SetStatsTypes(char *types)
+{
+ statsTypes = types;
+
+ if (hStatsFile != INVALID_HANDLE_VALUE)
+ {
+ //Print the CSV header row
+ char rowHeader[1024];
+ DWORD charCount = 0;
+ DWORD bytesWritten = 0;
+
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'h') != NULL || strchr(statsTypes, 'H') != NULL)
+ charCount += sprintf(rowHeader + charCount, "HASH,");
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'n') != NULL || strchr(statsTypes, 'N') != NULL)
+ charCount += sprintf(rowHeader + charCount, "METHOD_NUMBER,");
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'i') != NULL || strchr(statsTypes, 'I') != NULL)
+ charCount += sprintf(rowHeader + charCount, "IL_CODE_SIZE,");
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'a') != NULL || strchr(statsTypes, 'A') != NULL)
+ charCount += sprintf(rowHeader + charCount, "ASM_CODE_SIZE,");
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 't') != NULL || strchr(statsTypes, 'T') != NULL)
+ charCount += sprintf(rowHeader + charCount, "Time1,Time2,");
+
+ //get rid of the final ',' and replace it with a '\n'
+ rowHeader[charCount - 1] = '\n';
+
+ if (!WriteFile(hStatsFile, rowHeader, charCount, &bytesWritten, nullptr) || bytesWritten != charCount)
+ {
+ LogError("Failed to write row header '%s'. GetLastError()=%u", rowHeader, GetLastError());
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/methodstatsemitter.h b/src/ToolBox/superpmi/superpmi/methodstatsemitter.h
new file mode 100644
index 0000000000..fb651b04b2
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/methodstatsemitter.h
@@ -0,0 +1,29 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//-----------------------------------------------------------------------------
+// MethodStatsEmitter.h - Emits useful method stats for compiled methods for analysis
+//-----------------------------------------------------------------------------
+#ifndef _MethodStatsEmitter
+#define _MethodStatsEmitter
+
+#include "methodcontext.h"
+#include "jitinstance.h"
+
+class MethodStatsEmitter
+{
+
+private:
+ char *statsTypes;
+ HANDLE hStatsFile;
+
+public:
+ MethodStatsEmitter(char *nameOfInput);
+ ~MethodStatsEmitter();
+
+ void Emit(int methodNumber, MethodContext *mc, ULONGLONG firstTime, ULONGLONG secondTime);
+ void SetStatsTypes(char *types);
+};
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/neardiffer.cpp b/src/ToolBox/superpmi/superpmi/neardiffer.cpp
new file mode 100644
index 0000000000..5b2e3b1b57
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/neardiffer.cpp
@@ -0,0 +1,1031 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// nearDiffer.cpp - differ that handles code that is very similar
+//----------------------------------------------------------
+
+#include "standardpch.h"
+
+#ifdef USE_COREDISTOOLS
+#include "coredistools.h"
+#endif // USE_COREDISTOOLS
+
+#include "logging.h"
+#include "neardiffer.h"
+
+#ifdef USE_COREDISTOOLS
+
+//
+// Helper functions to print messages from CoreDisTools Library
+// The file/linenumber information is from this helper itself,
+// since we are only linking with the CoreDisTools library.
+//
+static void LogFromCoreDisToolsHelper(LogLevel level, const char *msg, va_list argList)
+{
+ Logger::LogVprintf(__func__, __FILE__, __LINE__, level, argList, msg);
+}
+
+#define LOGGER(L) \
+static void Log##L(const char *msg, ...) \
+{\
+ va_list argList; \
+ va_start(argList, msg); \
+ LogFromCoreDisToolsHelper (LOGLEVEL_##L, msg, argList); \
+ va_end(argList); \
+}
+
+LOGGER(VERBOSE)
+LOGGER(ERROR)
+LOGGER(WARNING)
+
+const PrintControl CorPrinter= { LogERROR, LogWARNING, LogVERBOSE, LogVERBOSE };
+
+#endif // USE_COREDISTOOLS
+
+//
+// The NearDiff Disassembler Initialization
+//
+void NearDiffer::InitAsmDiff()
+{
+#ifdef USE_COREDISTOOLS
+ if (UseCoreDisTools)
+ {
+ corAsmDiff = NewDiffer(Target_Host, &CorPrinter, NearDiffer::compareOffsets);
+ }
+#endif // USE_COREDISTOOLS
+}
+
+//
+// The NearDiff destructor
+//
+NearDiffer::~NearDiffer()
+{
+#ifdef USE_COREDISTOOLS
+ if (corAsmDiff != nullptr)
+ {
+ FinishDiff(corAsmDiff);
+ }
+#endif // USE_COREDISTOOLS
+}
+
+// At a high level, the near differ takes in a method context and two compile results, performs
+// some simple fixups, and then compares the main artifacts of the compile result (i.e. generated
+// code, GC info, EH info, debug info, etc.) for equality. In order to be fast, the fixups and
+// definitions of "equality" are minimal; for example, the GC info check just does a simple memcmp.
+//
+// The entrypoint into the near differ is nearDiffer::compare; its doc comments will have more
+// details on what it does. That function in turn fans out to various other components. For asm
+// diffing, the main function of interest will be nearDiffer::compareCodeSection.
+//
+// Most of the diffing logic is architecture-independent, with the following exceptions:
+//
+// - The MSDIS instance must be created with knowledge of the architecture it is working with.
+// - The heuristics to compare different literal operand values has some architecture-specific
+// assumptions.
+// - The code stream is fixed up using relocations recorded during compilation time. The logic
+// for applying these should, in theory, be architecture independent, but depending on how
+// the runtime implements this from platform to platform, there might be subtle differences here.
+//
+
+#ifdef USE_MSVCDIS
+
+DIS* NearDiffer::GetMsVcDis()
+{
+ DIS *disasm;
+
+#ifdef _TARGET_AMD64_
+ if ((TargetArchitecture != nullptr) && (0 == _stricmp(TargetArchitecture, "arm64")))
+ {
+ disasm = DIS::PdisNew(DIS::distArm64);
+ }
+ else
+ {
+ disasm = DIS::PdisNew(DIS::distX8664);
+ }
+#elif defined(_TARGET_X86_)
+ disasm = DIS::PdisNew(DIS::distX86);
+#endif
+
+ return disasm;
+}
+
+#endif // USE_MSVCDIS
+
+//
+// Simple, quick-and-dirty disassembler. If NearDiffer::compareCodeSection finds that two code
+// streams differ, it will call this to dump the two differing code blocks to the log. The dump
+// is logged under the verbose logging level.
+//
+// The output format is in MSDIS's disassembly format.
+//
+// Arguments:
+// block - A pointer to the code block to diassemble.
+// blocksize - The size of the code block to disassemble.
+// originalAddr - The original base address of the code block.
+//
+void NearDiffer::DumpCodeBlock(unsigned char *block, ULONG blocksize, void *originalAddr)
+{
+#ifdef USE_MSVCDIS
+ DIS *disasm = GetMsVcDis();
+ size_t offset = 0;
+ std::string codeBlock;
+
+ while (offset < blocksize)
+ {
+ DIS::INSTRUCTION instr;
+ DIS::OPERAND ops[3];
+
+ size_t instrSize = disasm->CbDisassemble((DIS::ADDR)originalAddr + offset, (void *)(block + offset), 15);
+ if(instrSize==0)
+ {
+ LogWarning("Zero sized instruction");
+ break;
+ }
+ disasm->FDecode(&instr, ops, 3);
+
+ wchar_t instrMnemonicWide[64]; // I never know how much to allocate...
+ disasm->CchFormatInstr(instrMnemonicWide, 64);
+ char instrMnemonic[128];
+ size_t count;
+ wcstombs_s(&count, instrMnemonic, 128, instrMnemonicWide, 64);
+
+ const size_t minInstrBytes = 7;
+ size_t instrBytes = max(instrSize, minInstrBytes);
+ size_t buffSize = _snprintf(nullptr, 0, "%p %s\n", (void*)((size_t)originalAddr+offset), instrMnemonic) + 3 * instrBytes + 1;
+ char *buff = new char[buffSize];
+ int written = 0;
+ written += sprintf_s(buff, buffSize, "%p ", (void*)((size_t)originalAddr+offset));
+ for (size_t i = 0; i < instrBytes; i++)
+ {
+ if (i < instrSize)
+ {
+ written += sprintf_s(buff + written, buffSize - written, "%02X ", *(const uint8_t*)(block + offset + i));
+ }
+ else
+ {
+ written += sprintf_s(buff + written, buffSize - written, " ");
+ }
+ }
+ written += sprintf_s(buff + written, buffSize - written, "%s\n", instrMnemonic);
+ codeBlock += buff;
+ delete[] buff;
+ offset += instrSize;
+ }
+ LogVerbose("Code dump:\n%s", codeBlock.c_str());
+ delete disasm;
+#else // !USE_MSVCDIS
+ LogVerbose("No disassembler");
+#endif // !USE_MSVCDIS
+}
+
+//
+// Struct to capture the information required by offset comparator.
+//
+struct DiffData
+{
+ // Common Data
+ CompileResult *cr;
+
+ // Details of the first block
+ size_t blocksize1;
+ size_t datablock1;
+ size_t datablockSize1;
+ size_t originalBlock1;
+ size_t originalDataBlock1;
+ size_t otherCodeBlock1;
+ size_t otherCodeBlockSize1;
+
+ // Details of the second block
+ size_t blocksize2;
+ size_t datablock2;
+ size_t datablockSize2;
+ size_t originalBlock2;
+ size_t originalDataBlock2;
+ size_t otherCodeBlock2;
+ size_t otherCodeBlockSize2;
+};
+
+//
+// NearDiff Offset Comparator.
+// Determine whether two syntactically different constants are
+// semantically equivalent, using certain heuristics.
+//
+bool NearDiffer::compareOffsets(const void *payload,
+ size_t blockOffset,
+ size_t instrLen,
+ uint64_t offset1,
+ uint64_t offset2)
+{
+ // The trivial case
+ if (offset1 == offset2)
+ {
+ return true;
+ }
+
+ const DiffData *data = (const DiffData *)payload;
+ size_t ip1 = data->originalBlock1 + blockOffset;
+ size_t ip2 = data->originalBlock2 + blockOffset;
+ size_t ipRelOffset1 = ip1 + instrLen + (size_t)offset1;
+ size_t ipRelOffset2 = ip2 + instrLen + (size_t)offset2;
+
+ // Case where we have a call into flat address -- the most common case.
+ size_t gOffset1 = ipRelOffset1;
+ size_t gOffset2 = ipRelOffset2;
+ if ((DWORD)gOffset1 == (DWORD)gOffset2) //make sure the lower 32bits match (best we can do in the current replay form)
+ return true;
+
+ //Case where we have an offset into the read only section (e.g. loading a float value)
+ size_t roOffset1a = (size_t)offset1 - data->originalDataBlock1;
+ size_t roOffset2a = (size_t)offset2 - data->originalDataBlock2;
+ if ((roOffset1a == roOffset2a) && (roOffset1a < data->datablockSize1)) //Confirm its an offset that fits inside our RoRegion
+ return true;
+
+ // This case is written to catch IP-relative offsets to the RO data-section
+ // For example:
+ //
+ size_t roOffset1b = ipRelOffset1 - data->originalDataBlock1;
+ size_t roOffset2b = ipRelOffset2 - data->originalDataBlock2;
+ if ((roOffset1b == roOffset2b) && (roOffset1b < data->datablockSize1)) //Confirm its an offset that fits inside our RoRegion
+ return true;
+
+ //Case where we push an address to our own code section.
+ size_t gOffset1a = (size_t)offset1 - data->originalBlock1;
+ size_t gOffset2a = (size_t)offset2 - data->originalBlock2;
+ if ((gOffset1a == gOffset2a) && (gOffset1a < data->blocksize1)) //Confirm its in our code region
+ return true;
+
+ //Case where we push an address in the other codeblock.
+ size_t gOffset1b = (size_t)offset1 - data->otherCodeBlock1;
+ size_t gOffset2b = (size_t)offset2 - data->otherCodeBlock2;
+ if ((gOffset1b == gOffset2b) && (gOffset1b < data->otherCodeBlockSize1)) //Confirm it's in the other code region
+ return true;
+
+ //Case where we have an offset into the hot codeblock from the cold code block (why?)
+ size_t ocOffset1 = ipRelOffset1 - data->otherCodeBlock1;
+ size_t ocOffset2 = ipRelOffset2 - data->otherCodeBlock2;
+ if (ocOffset1 == ocOffset2) //Would be nice to check to see if it fits in the other code block
+ return true;
+
+ //VSD calling case.
+ size_t Offset1 = (ipRelOffset1 - 8);
+ if (data->cr->CallTargetTypes->GetIndex((DWORDLONG)Offset1) != (DWORD)-1)
+ {
+ // This logging is too noisy, so disable it.
+ //LogVerbose("Found VSD callsite, did softer compare than ideal");
+ return true;
+ }
+
+ //x86 VSD calling cases.
+ size_t Offset1b = (size_t)offset1 - 4;
+ size_t Offset2b = (size_t)offset2;
+ if (data->cr->CallTargetTypes->GetIndex((DWORDLONG)Offset1b) != (DWORD)-1)
+ {
+ // This logging is too noisy, so disable it.
+ //LogVerbose("Found VSD callsite, did softer compare than ideal");
+ return true;
+ }
+ if (data->cr->CallTargetTypes->GetIndex((DWORDLONG)Offset2b) != (DWORD)-1)
+ {
+ // This logging is too noisy, so disable it.
+ //LogVerbose("Found VSD callsite, did softer compare than ideal");
+ return true;
+ }
+
+ //Case might be a field address that we handed out to handle inlined values being loaded into
+ //a register as an immediate value (and where the address is encoded as an indirect immediate load)
+ size_t realTargetAddr = (size_t)data->cr->searchAddressMap((void*)gOffset2);
+ if (realTargetAddr == gOffset1)
+ return true;
+
+ //Case might be a field address that we handed out to handle inlined values being loaded into
+ //a register as an immediate value (and where the address is encoded and loaded by immediate into a register)
+ realTargetAddr = (size_t)data->cr->searchAddressMap((void*)offset2);
+ if (realTargetAddr == offset1)
+ return true;
+ if (realTargetAddr == 0x424242)//this offset matches what we got back from a getTailCallCopyArgsThunk
+ return true;
+
+ realTargetAddr = (size_t)data->cr->searchAddressMap((void*)(gOffset2));
+ if (realTargetAddr != -1) //we know this was passed out as a bbloc
+ return true;
+
+ return false;
+}
+
+//
+// Compares two code sections for syntactic equality. This is the core of the asm diffing logic.
+//
+// This mostly relies on MSDIS's decoded representation of an instruction to compare for equality.
+// That is, using MSDIS's internal IR, this goes through the code stream and compares, instruction
+// by instruction, op code and operand values for equality.
+//
+// Obviously, just blindly comparing operand values will raise a lot of false alarms. In order to
+// compensate for phenomena like literal pointer addresses in the code stream changing, this applies
+// some heuristics on mismatching operand values to try to normalize them a little bit. Essentially,
+// if operand values don't match, they are re-interpreted as various relative deltas from known base
+// addresses. For example, a common case is a pointer into the read-only data section. One of the
+// heuristics subtracts both operand values from the base address of the read-only data section and
+// checks to see if they are the same distance away from their respective read-only base addresses.
+//
+// Notes:
+// - The core syntactic comparison is platform agnostic; we compare op codes and operand values
+// using MSDIS's architecture-independent IR (i.e. the data structures defined in msvcdis.h).
+// Only the disassembler instance itself is initialized differently based on the target arch-
+// itecture.
+// - That being said, the heuristics themselves are not guaranteed to be platform agnostic. For
+// instance, there is a case that applies only to x86 VSD calls. When porting the near differ
+// to new platforms, these special cases should be examined and ported with care.
+//
+// Arguments:
+// mc - The method context of the method to diff. Unused.
+// cr1 - The first compile result to compare. Unused.
+// cr2 - The second compile result to compare. Unused.
+// block1 - A pointer to the first code block to diassemble.
+// blocksize1 - The size of the first code block to compare.
+// datablock1 - A pointer to the first read-only data block to compare. Unused.
+// datablockSize1 - The size of the first read-only data block to compare.
+// originalBlock1 - The original base address of the first code block.
+// originalDataBlock1 - The original base address of the first read-only data block.
+// otherCodeBlock1 - The original base address of the first cold code block. Note that this is
+// just an address; we don't need the cold code buffer.
+// otherCodeBlockSize1- The size of the first cold code block.
+// block2 - A pointer to the second code block to diassemble.
+// blocksize2 - The size of the second code block to compare.
+// datablock2 - A pointer to the second read-only data block to compare.
+// datablockSize2 - The size of the second read-only data block to compare.
+// originalBlock2 - The original base address of the second code block.
+// originalDataBlock2 - The original base address of the second read-only data block.
+// otherCodeBlock2 - The original base address of the second cold code block. Note that this is
+// just an address; we don't need the cold code buffer.
+// otherCodeBlockSize2- The size of the second cold code block.
+//
+// Return Value:
+// True if the code sections are syntactically identical; false otherwise.
+//
+
+bool NearDiffer::compareCodeSection(
+ MethodContext *mc,
+ CompileResult *cr1,
+ CompileResult *cr2,
+ unsigned char *block1,
+ ULONG blocksize1,
+ unsigned char *datablock1,
+ ULONG datablockSize1,
+ void *originalBlock1,
+ void *originalDataBlock1,
+ void *otherCodeBlock1,
+ ULONG otherCodeBlockSize1,
+ unsigned char *block2,
+ ULONG blocksize2,
+ unsigned char *datablock2,
+ ULONG datablockSize2,
+ void *originalBlock2,
+ void *originalDataBlock2,
+ void *otherCodeBlock2,
+ ULONG otherCodeBlockSize2)
+{
+ DiffData data =
+ {
+ cr2,
+
+ // Details of the first block
+ (size_t)blocksize1,
+ (size_t)datablock1,
+ (size_t)datablockSize1,
+ (size_t)originalBlock1,
+ (size_t)originalDataBlock1,
+ (size_t)otherCodeBlock1,
+ (size_t)otherCodeBlockSize1,
+
+ // Details of the second block
+ (size_t)blocksize2,
+ (size_t)datablock2,
+ (size_t)datablockSize2,
+ (size_t)originalBlock2,
+ (size_t)originalDataBlock2,
+ (size_t)otherCodeBlock2,
+ (size_t)otherCodeBlockSize2
+ };
+
+#ifdef USE_COREDISTOOLS
+ if (UseCoreDisTools)
+ {
+ bool areSame = NearDiffCodeBlocks(corAsmDiff, &data,
+ (const uint8_t *)originalBlock1, block1, blocksize1,
+ (const uint8_t *)originalBlock2, block2, blocksize2);
+
+ if (!areSame)
+ {
+ DumpDiffBlocks(corAsmDiff, (const uint8_t *) originalBlock1,
+ block1, blocksize1, (const uint8_t *) originalBlock2,
+ block2, blocksize2);
+ }
+
+ return areSame;
+ }
+#endif // USE_COREDISTOOLS
+
+#ifdef USE_MSVCDIS
+ bool haveSeenRet = false;
+ DIS *disasm_1 = GetMsVcDis();
+ DIS *disasm_2 = GetMsVcDis();
+
+ size_t offset = 0;
+
+ if (blocksize1 != blocksize2)
+ {
+ LogVerbose("Code sizes don't match %u != %u", blocksize1, blocksize2);
+ goto DumpDetails;
+ }
+
+ while (offset < blocksize1)
+ {
+ DIS::INSTRUCTION instr_1;
+ DIS::INSTRUCTION instr_2;
+ const int MaxOperandCount = 5;
+ DIS::OPERAND ops_1[MaxOperandCount];
+ DIS::OPERAND ops_2[MaxOperandCount];
+
+ // Zero out the locals, just in case.
+ memset(&instr_1, 0, sizeof(instr_1));
+ memset(&instr_2, 0, sizeof(instr_2));
+ memset(&ops_1, 0, sizeof(ops_1));
+ memset(&ops_2, 0, sizeof(ops_2));
+
+ size_t instrSize_1 = disasm_1->CbDisassemble((DIS::ADDR)originalBlock1 + offset, (void *)(block1 + offset), 15);
+ size_t instrSize_2 = disasm_2->CbDisassemble((DIS::ADDR)originalBlock2 + offset, (void *)(block2 + offset), 15);
+
+ if (instrSize_1 != instrSize_2)
+ {
+ LogVerbose("Different instruction sizes %llu %llu", instrSize_1, instrSize_2);
+ goto DumpDetails;
+ }
+ if (instrSize_1 == 0)
+ {
+ if (haveSeenRet)
+ {
+ // This logging is pretty noisy, so disable it.
+ //LogVerbose("instruction size of zero after seeing a ret (soft issue?).");
+ break;
+ }
+ LogWarning("instruction size of zero.");
+ goto DumpDetails;
+ }
+
+ bool FDecodeError = false;
+ if (!disasm_1->FDecode(&instr_1, ops_1, MaxOperandCount))
+ {
+ LogWarning("FDecode of instr_1 returned false.");
+ FDecodeError = true;
+ }
+ if (!disasm_2->FDecode(&instr_2, ops_2, MaxOperandCount))
+ {
+ LogWarning("FDecode of instr_2 returned false.");
+ FDecodeError = true;
+ }
+
+ wchar_t instrMnemonic_1[64]; // I never know how much to allocate...
+ disasm_1->CchFormatInstr(instrMnemonic_1, 64);
+ wchar_t instrMnemonic_2[64]; // I never know how much to allocate...
+ disasm_2->CchFormatInstr(instrMnemonic_2, 64);
+ if (wcscmp(instrMnemonic_1, L"ret") == 0)
+ haveSeenRet = true;
+ if (wcscmp(instrMnemonic_1, L"rep ret") == 0)
+ haveSeenRet = true;
+
+ // First, check to see if these instructions are actually identical.
+ // This is done 1) to avoid the detailed comparison of the fields of instr_1
+ // and instr_2 if they are identical, and 2) because in the event that
+ // there are bugs or unimplemented instructions in FDecode, we don't want
+ // to count them as diffs if they are bitwise identical.
+
+ if (memcmp((block1 + offset), (block2 + offset), instrSize_1) != 0)
+ {
+ if (FDecodeError)
+ {
+ LogWarning("FDecode returned false.");
+ goto DumpDetails;
+ }
+
+ if (instr_1.opa != instr_2.opa)
+ {
+ LogVerbose("different opa %d %d", instr_1.opa, instr_2.opa);
+ goto DumpDetails;
+ }
+ if (instr_1.coperand != instr_2.coperand)
+ {
+ LogVerbose("different coperand %u %u", (unsigned int)instr_1.coperand, (unsigned int)instr_2.coperand);
+ goto DumpDetails;
+ }
+ if (instr_1.dwModifiers != instr_2.dwModifiers)
+ {
+ LogVerbose("different dwModifiers %u %u", instr_1.dwModifiers, instr_2.dwModifiers);
+ goto DumpDetails;
+ }
+
+ for (size_t i = 0; i < instr_1.coperand; i++)
+ {
+ if (ops_1[i].cb != ops_2[i].cb)
+ {
+ LogVerbose("different cb %llu %llu", ops_1[i].cb, ops_2[i].cb);
+ goto DumpDetails;
+ }
+ if (ops_1[i].imcls != ops_2[i].imcls)
+ {
+ LogVerbose("different imcls %d %d", ops_1[i].imcls, ops_2[i].imcls);
+ goto DumpDetails;
+ }
+ if (ops_1[i].opcls != ops_2[i].opcls)
+ {
+ LogVerbose("different opcls %d %d", ops_1[i].opcls, ops_2[i].opcls);
+ goto DumpDetails;
+ }
+ if (ops_1[i].rega1 != ops_2[i].rega1)
+ {
+ LogVerbose("different rega1 %d %d", ops_1[i].rega1, ops_2[i].rega1);
+ goto DumpDetails;
+ }
+ if (ops_1[i].rega2 != ops_2[i].rega2)
+ {
+ LogVerbose("different rega2 %d %d", ops_1[i].rega2, ops_2[i].rega2);
+ goto DumpDetails;
+ }
+ if (ops_1[i].rega3 != ops_2[i].rega3)
+ {
+ LogVerbose("different rega3 %d %d", ops_1[i].rega3, ops_2[i].rega3);
+ goto DumpDetails;
+ }
+ if (ops_1[i].wScale != ops_2[i].wScale)
+ {
+ LogVerbose("different wScale %u %u", ops_1[i].wScale, ops_2[i].wScale);
+ goto DumpDetails;
+ }
+
+ //
+ // These are special.. we can often reason out exactly why these values
+ // are different using heuristics.
+ //
+ // Why is Instruction size passed as zero?
+ // Ans: Because the implementation of areOffsetsEquivalent() uses
+ // the instruction size to compute absolute offsets in the case of
+ // PC-relative addressing, and MSVCDis already reports the
+ // absolute offsets! For example:
+ // 0F 2E 05 67 00 9A FD ucomiss xmm0, dword ptr[FFFFFFFFFD9A006Eh]
+ //
+
+ if (compareOffsets(&data, offset, 0, ops_1[i].dwl, ops_2[i].dwl))
+ {
+ continue;
+ }
+ else
+ {
+ size_t gOffset1 = (size_t)originalBlock1 + offset + (size_t)ops_1[i].dwl;
+ size_t gOffset2 = (size_t)originalBlock2 + offset + (size_t)ops_2[i].dwl;
+
+ LogVerbose("operand %d dwl is different", i);
+#ifdef _TARGET_AMD64_
+ LogVerbose("gOffset1 %016llX", gOffset1);
+ LogVerbose("gOffset2 %016llX", gOffset2);
+ LogVerbose("gOffset1 - gOffset2 %016llX", gOffset1 - gOffset2);
+#elif defined(_TARGET_X86_)
+ LogVerbose("gOffset1 %08X", gOffset1);
+ LogVerbose("gOffset2 %08X", gOffset2);
+ LogVerbose("gOffset1 - gOffset2 %08X", gOffset1 - gOffset2);
+#endif
+ LogVerbose("dwl1 %016llX", ops_1[i].dwl);
+ LogVerbose("dwl2 %016llX", ops_2[i].dwl);
+ goto DumpDetails;
+ }
+ }
+ }
+ offset += instrSize_1;
+ }
+ delete disasm_1;
+ delete disasm_2;
+ return true;
+
+DumpDetails:
+ LogVerbose("block1 %p", block1);
+ LogVerbose("block2 %p", block2);
+ LogVerbose("originalBlock1 [%p,%p)", originalBlock1, (const uint8_t *)originalBlock1 + blocksize1);
+ LogVerbose("originalBlock2 [%p,%p)", originalBlock2, (const uint8_t *)originalBlock2 + blocksize2);
+ LogVerbose("blocksize1 %08X", blocksize1);
+ LogVerbose("blocksize2 %08X", blocksize2);
+ LogVerbose("dataBlock1 [%p,%p)", originalDataBlock1, (const uint8_t *)originalDataBlock1 + datablockSize1);
+ LogVerbose("dataBlock2 [%p,%p)", originalDataBlock2, (const uint8_t *)originalDataBlock2 + datablockSize2);
+ LogVerbose("datablockSize1 %08X", datablockSize1);
+ LogVerbose("datablockSize2 %08X", datablockSize2);
+ LogVerbose("otherCodeBlock1 [%p,%p)", otherCodeBlock1, (const uint8_t *)otherCodeBlock1 + otherCodeBlockSize1);
+ LogVerbose("otherCodeBlock2 [%p,%p)", otherCodeBlock2, (const uint8_t *)otherCodeBlock2 + otherCodeBlockSize2);
+ LogVerbose("otherCodeBlockSize1 %08X", otherCodeBlockSize1);
+ LogVerbose("otherCodeBlockSize2 %08X", otherCodeBlockSize2);
+
+#ifdef _TARGET_AMD64_
+ LogVerbose("offset %016llX", offset);
+ LogVerbose("addr1 %016llX", (size_t)originalBlock1 + offset);
+ LogVerbose("addr2 %016llX", (size_t)originalBlock2 + offset);
+#elif defined(_TARGET_X86_)
+ LogVerbose("offset %08X", offset);
+ LogVerbose("addr1 %08X", (size_t)originalBlock1 + offset);
+ LogVerbose("addr2 %08X", (size_t)originalBlock2 + offset);
+#endif
+
+ LogVerbose("Block1:");
+ DumpCodeBlock(block1, blocksize1, originalBlock1);
+ LogVerbose("Block2:");
+ DumpCodeBlock(block2, blocksize2, originalBlock2);
+
+ if (disasm_1 != nullptr)
+ delete disasm_1;
+ if (disasm_2 != nullptr)
+ delete disasm_2;
+ return false;
+#else // !USE_MSVCDIS
+ return false; // No disassembler; assume there are differences
+#endif // !USE_MSVCDIS
+}
+
+//
+// Compares two read-only data sections for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+// block1 - A pointer to the first code block to diassemble.
+// blocksize1 - The size of the first code block to compare.
+// originalDataBlock1 - The original base address of the first read-only data block.
+// block2 - A pointer to the second code block to diassemble.
+// blocksize2 - The size of the second code block to compare.
+// originalDataBlock2 - The original base address of the second read-only data block.
+//
+// Return Value:
+// True if the read-only data sections are identical; false otherwise.
+//
+bool NearDiffer::compareReadOnlyDataBlock(MethodContext *mc, CompileResult *cr1, CompileResult *cr2,
+ unsigned char *block1, ULONG blocksize1, void *originalDataBlock1,
+ unsigned char *block2, ULONG blocksize2, void *originalDataBlock2)
+{
+ //no rodata
+ if(blocksize1==0 && blocksize2==0)
+ return true;
+
+ if(blocksize1!=blocksize2)
+ {
+ LogVerbose("compareReadOnlyDataBlock found non-matching sizes %u %u", blocksize1, blocksize2);
+ return false;
+ }
+
+ //TODO-Cleanup: The values on the datablock seem to wobble. Need further investigation to evaluate a good near comparison for these
+ return true;
+}
+
+//
+// Compares two EH info blocks for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the EH info blocks are identical; false otherwise.
+//
+bool NearDiffer::compareEHInfo(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ ULONG cEHSize_1;
+ ULONG ehFlags_1;
+ ULONG tryOffset_1;
+ ULONG tryLength_1;
+ ULONG handlerOffset_1;
+ ULONG handlerLength_1;
+ ULONG classToken_1;
+
+ ULONG cEHSize_2;
+ ULONG ehFlags_2;
+ ULONG tryOffset_2;
+ ULONG tryLength_2;
+ ULONG handlerOffset_2;
+ ULONG handlerLength_2;
+ ULONG classToken_2;
+
+
+ cEHSize_1 = cr1->repSetEHcount();
+ cEHSize_2 = cr2->repSetEHcount();
+
+ //no exception
+ if(cEHSize_1==0 && cEHSize_2==0)
+ return true;
+
+ if(cEHSize_1!=cEHSize_2)
+ {
+ LogVerbose("compareEHInfo found non-matching sizes %u %u", cEHSize_1, cEHSize_2);
+ return false;
+ }
+
+ for(unsigned int i=0;i<cEHSize_1;i++)
+ {
+ cr1->repSetEHinfo(i, &ehFlags_1, &tryOffset_1, &tryLength_1, &handlerOffset_1, &handlerLength_1, &classToken_1);
+ cr2->repSetEHinfo(i, &ehFlags_2, &tryOffset_2, &tryLength_2, &handlerOffset_2, &handlerLength_2, &classToken_2);
+ if(ehFlags_1!=ehFlags_2)
+ {
+ LogVerbose("EH flags don't match %u != %u", ehFlags_1, ehFlags_2);
+ return false;
+ }
+ if((tryOffset_1!=tryOffset_2) || (tryLength_1!=tryLength_2))
+ {
+ LogVerbose("EH try information don't match, offset: %u %u, length: %u %u", tryOffset_1, tryOffset_2, tryLength_1, tryLength_2);
+ return false;
+ }
+ if((handlerOffset_1!=handlerOffset_2) || (handlerLength_1!=handlerLength_2))
+ {
+ LogVerbose("EH handler information don't match, offset: %u %u, length: %u %u", handlerOffset_1, handlerOffset_2, handlerLength_1, handlerLength_2);
+ return false;
+ }
+ if(classToken_1!=classToken_2)
+ {
+ LogVerbose("EH class tokens don't match %u!=%u", classToken_1, classToken_2);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//
+// Compares two GC info blocks for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the GC info blocks are identical; false otherwise.
+//
+bool NearDiffer::compareGCInfo(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ void *gcInfo1;
+ size_t gcInfo1Size;
+ void *gcInfo2;
+ size_t gcInfo2Size;
+
+ cr1->repAllocGCInfo(&gcInfo1Size, &gcInfo1);
+ cr2->repAllocGCInfo(&gcInfo2Size, &gcInfo2);
+
+ if (gcInfo1Size != gcInfo2Size)
+ {
+ LogVerbose("Reported GCInfo sizes don't match: %u != %u", (unsigned int)gcInfo1Size, (unsigned int)gcInfo2Size);
+ return false;
+ }
+
+ if (memcmp(gcInfo1, gcInfo2, gcInfo1Size) != 0)
+ {
+ LogVerbose("GCInfo doesn't match.");
+ return false;
+ }
+
+ return true;
+}
+
+//
+// Compares two sets of native var info for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the native var info is identical; false otherwise.
+//
+bool NearDiffer::compareVars(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ CORINFO_METHOD_HANDLE ftn_1;
+ ULONG32 cVars_1;
+ ICorDebugInfo::NativeVarInfo *vars_1;
+
+ CORINFO_METHOD_HANDLE ftn_2;
+ ULONG32 cVars_2;
+ ICorDebugInfo::NativeVarInfo *vars_2;
+
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+ mc->repCompileMethod(&info, &flags);
+
+ bool set1 = cr1->repSetVars(&ftn_1, &cVars_1, &vars_1);
+ bool set2 = cr2->repSetVars(&ftn_2, &cVars_2, &vars_2);
+ if((set1==false)&&(set2==false))
+ return true; // we don't have boundaries for either of these.
+ if(((set1==true)&&(set2==false))||((set1==false)&&(set2==true)))
+ {
+ LogVerbose("missing matching vars sets");
+ return false;
+ }
+
+ //no vars
+ if(cVars_1==0 && cVars_2==0)
+ {
+ return true;
+ }
+
+ if(ftn_1!=ftn_2)
+ {
+ //We would like to find out this situation
+ __debugbreak();
+ LogVerbose("compareVars found non-matching CORINFO_METHOD_HANDLE %p %p", ftn_1, ftn_2);
+ return false;
+ }
+ if(ftn_1!=info.ftn)
+ {
+ LogVerbose("compareVars found issues with the CORINFO_METHOD_HANDLE %p %p", ftn_1, info.ftn);
+ return false;
+ }
+
+ if(cVars_1!=cVars_2)
+ {
+ LogVerbose("compareVars found non-matching var count %u %u", cVars_1, cVars_2);
+ return false;
+ }
+
+ //TODO-Cleanup: The values on the NativeVarInfo array seem to wobble. Need further investigation to evaluate a good near comparison for these
+ //for(unsigned int i=0;i<cVars_1;i++)
+ //{
+ // if(vars_1[i].startOffset!=vars_2[i].startOffset)
+ // {
+ // LogVerbose("compareVars found non-matching startOffsets %u %u for var: %u", vars_1[i].startOffset, vars_2[i].startOffset, i);
+ // return false;
+ // }
+ //}
+
+ return true;
+}
+
+//
+// Compares two sets of native offset mappings for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the native offset mappings are identical; false otherwise.
+//
+bool NearDiffer::compareBoundaries(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ CORINFO_METHOD_HANDLE ftn_1;
+ ULONG32 cMap_1;
+ ICorDebugInfo::OffsetMapping *map_1;
+
+ CORINFO_METHOD_HANDLE ftn_2;
+ ULONG32 cMap_2;
+ ICorDebugInfo::OffsetMapping *map_2;
+
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+ mc->repCompileMethod(&info, &flags);
+
+ bool set1 = cr1->repSetBoundaries(&ftn_1, &cMap_1, &map_1);
+ bool set2 = cr2->repSetBoundaries(&ftn_2, &cMap_2, &map_2);
+ if((set1==false)&&(set2==false))
+ return true; // we don't have boundaries for either of these.
+ if(((set1==true)&&(set2==false))||((set1==false)&&(set2==true)))
+ {
+ LogVerbose("missing matching boundary sets");
+ return false;
+ }
+
+ if(ftn_1!=ftn_2)
+ {
+ LogVerbose("compareBoundaries found non-matching CORINFO_METHOD_HANDLE %p %p", ftn_1, ftn_2);
+ return false;
+ }
+
+ //no maps
+ if(cMap_1==0 && cMap_2==0)
+ return true;
+
+ if(cMap_1!=cMap_2)
+ {
+ LogVerbose("compareBoundaries found non-matching var count %u %u", cMap_1, cMap_2);
+ return false;
+ }
+
+ for(unsigned int i=0;i<cMap_1;i++)
+ {
+ if(map_1[i].ilOffset!=map_2[i].ilOffset)
+ {
+ LogVerbose("compareBoundaries found non-matching ilOffset %u %u for map: %u", map_1[i].ilOffset, map_2[i].ilOffset, i);
+ return false;
+ }
+ if(map_1[i].nativeOffset!=map_2[i].nativeOffset)
+ {
+ LogVerbose("compareBoundaries found non-matching nativeOffset %u %u for map: %u", map_1[i].nativeOffset, map_2[i].nativeOffset, i);
+ return false;
+ }
+ if(map_1[i].source!=map_2[i].source)
+ {
+ LogVerbose("compareBoundaries found non-matching source %u %u for map: %u", (unsigned int)map_1[i].source, (unsigned int)map_2[i].source, i);
+ return false;
+ }
+ }
+
+
+ return true;
+}
+
+//
+// Compares two compiled versions of a method for equality. This is the main driver for the various
+// components of near diffing.
+//
+// Before starting the diffing process, this applies some fixups to the code stream based on relocations
+// recorded during compilation, using the original base address that was used when compiling the method.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the compile results are identical; false otherwise.
+//
+bool NearDiffer::compare(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ ULONG hotCodeSize_1;
+ ULONG coldCodeSize_1;
+ ULONG roDataSize_1;
+ ULONG xcptnsCount_1;
+ CorJitAllocMemFlag flag_1;
+ unsigned char *hotCodeBlock_1;
+ unsigned char *coldCodeBlock_1;
+ unsigned char *roDataBlock_1;
+ void *orig_hotCodeBlock_1;
+ void *orig_coldCodeBlock_1;
+ void *orig_roDataBlock_1;
+
+ ULONG hotCodeSize_2;
+ ULONG coldCodeSize_2;
+ ULONG roDataSize_2;
+ ULONG xcptnsCount_2;
+ CorJitAllocMemFlag flag_2;
+ unsigned char *hotCodeBlock_2;
+ unsigned char *coldCodeBlock_2;
+ unsigned char *roDataBlock_2;
+ void *orig_hotCodeBlock_2;
+ void *orig_coldCodeBlock_2;
+ void *orig_roDataBlock_2;
+
+ cr1->repAllocMem(&hotCodeSize_1, &coldCodeSize_1, &roDataSize_1, &xcptnsCount_1, &flag_1,
+ &hotCodeBlock_1, &coldCodeBlock_1, &roDataBlock_1, &orig_hotCodeBlock_1, &orig_coldCodeBlock_1, &orig_roDataBlock_1);
+ cr2->repAllocMem(&hotCodeSize_2, &coldCodeSize_2, &roDataSize_2, &xcptnsCount_2, &flag_2,
+ &hotCodeBlock_2, &coldCodeBlock_2, &roDataBlock_2, &orig_hotCodeBlock_2, &orig_coldCodeBlock_2, &orig_roDataBlock_2);
+
+ LogDebug("HCS1 %d CCS1 %d RDS1 %d xcpnt1 %d flag1 %08X, HCB %p CCB %p RDB %p ohcb %p occb %p odb %p",
+ hotCodeSize_1, coldCodeSize_1, roDataSize_1, xcptnsCount_1, flag_1,
+ hotCodeBlock_1, coldCodeBlock_1, roDataBlock_1,
+ orig_hotCodeBlock_1, orig_coldCodeBlock_1, orig_roDataBlock_1);
+ LogDebug("HCS2 %d CCS2 %d RDS2 %d xcpnt2 %d flag2 %08X, HCB %p CCB %p RDB %p ohcb %p occb %p odb %p",
+ hotCodeSize_2, coldCodeSize_2, roDataSize_2, xcptnsCount_2, flag_2,
+ hotCodeBlock_2, coldCodeBlock_2, roDataBlock_2,
+ orig_hotCodeBlock_2, orig_coldCodeBlock_2, orig_roDataBlock_2);
+
+ cr1->applyRelocs(hotCodeBlock_1, hotCodeSize_1, orig_hotCodeBlock_1);
+ cr2->applyRelocs(hotCodeBlock_2, hotCodeSize_2, orig_hotCodeBlock_2);
+ cr1->applyRelocs(coldCodeBlock_1, coldCodeSize_1, orig_coldCodeBlock_1);
+ cr2->applyRelocs(coldCodeBlock_2, coldCodeSize_2, orig_coldCodeBlock_2);
+ cr1->applyRelocs(roDataBlock_1, roDataSize_1, orig_roDataBlock_1);
+ cr2->applyRelocs(roDataBlock_2, roDataSize_2, orig_roDataBlock_2);
+
+ if(!compareCodeSection(mc, cr1, cr2,
+ hotCodeBlock_1, hotCodeSize_1, roDataBlock_1, roDataSize_1, orig_hotCodeBlock_1, orig_roDataBlock_1, orig_coldCodeBlock_1, coldCodeSize_1,
+ hotCodeBlock_2, hotCodeSize_2, roDataBlock_2, roDataSize_2, orig_hotCodeBlock_2, orig_roDataBlock_2, orig_coldCodeBlock_2, coldCodeSize_2))
+ return false;
+
+ if(!compareCodeSection(mc, cr1, cr2,
+ coldCodeBlock_1, coldCodeSize_1, roDataBlock_1, roDataSize_1, orig_coldCodeBlock_1, orig_roDataBlock_1, orig_hotCodeBlock_1, hotCodeSize_1,
+ coldCodeBlock_2, coldCodeSize_2, roDataBlock_2, roDataSize_2, orig_coldCodeBlock_2, orig_roDataBlock_2, orig_hotCodeBlock_2, hotCodeSize_2))
+ return false;
+
+ if(!compareReadOnlyDataBlock(mc, cr1, cr2,
+ roDataBlock_1, roDataSize_1, orig_roDataBlock_1,
+ roDataBlock_2, roDataSize_2, orig_roDataBlock_2))
+ return false;
+
+ if(!compareEHInfo(mc, cr1, cr2))
+ return false;
+
+ if (!compareGCInfo(mc, cr1, cr2))
+ return false;
+
+ if (!compareVars(mc, cr1, cr2))
+ return false;
+
+ if(!compareBoundaries(mc, cr1, cr2))
+ return false;
+
+ return true;
+}
diff --git a/src/ToolBox/superpmi/superpmi/neardiffer.h b/src/ToolBox/superpmi/superpmi/neardiffer.h
new file mode 100644
index 0000000000..e3ffe1c790
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/neardiffer.h
@@ -0,0 +1,86 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// nearDiffer.h - differ that handles code that is very similar
+//----------------------------------------------------------
+#ifndef _nearDiffer
+#define _nearDiffer
+
+#include "methodcontext.h"
+#include "compileresult.h"
+
+class NearDiffer
+{
+public:
+
+ NearDiffer(const char *targetArch, bool useCorDisTools)
+ : TargetArchitecture(targetArch)
+ , UseCoreDisTools(useCorDisTools)
+#ifdef USE_COREDISTOOLS
+ , corAsmDiff(nullptr)
+#endif // USE_COREDISTOOLS
+ {
+ }
+
+ ~NearDiffer();
+
+ void InitAsmDiff();
+
+ bool compare(MethodContext *mc, CompileResult *cr1,CompileResult *cr2);
+
+ const char* TargetArchitecture;
+ const bool UseCoreDisTools;
+
+private:
+
+ void DumpCodeBlock(unsigned char *block, ULONG blocksize, void *originalAddr);
+
+ bool compareCodeSection(
+ MethodContext *mc,
+ CompileResult *cr1,
+ CompileResult *cr2,
+ unsigned char *block1,
+ ULONG blocksize1,
+ unsigned char *datablock1,
+ ULONG datablockSize1,
+ void *originalBlock1,
+ void *originalDataBlock1,
+ void *otherCodeBlock1,
+ ULONG otherCodeBlockSize1,
+ unsigned char *block2,
+ ULONG blocksize2,
+ unsigned char *datablock2,
+ ULONG datablockSize2,
+ void *originalBlock2,
+ void *originalDataBlock2,
+ void *otherCodeBlock2,
+ ULONG otherCodeBlockSize2);
+
+ bool compareReadOnlyDataBlock(MethodContext *mc, CompileResult *cr1, CompileResult *cr2,
+ unsigned char *block1, ULONG blocksize1, void *originalDataBlock1,
+ unsigned char *block2, ULONG blocksize2, void *originalDataBlock2);
+ bool compareEHInfo(MethodContext *mc, CompileResult *cr1, CompileResult *cr2);
+ bool compareGCInfo(MethodContext *mc, CompileResult *cr1, CompileResult *cr2);
+ bool compareVars(MethodContext *mc, CompileResult *cr1, CompileResult *cr2);
+ bool compareBoundaries(MethodContext *mc, CompileResult *cr1, CompileResult *cr2);
+
+ static bool compareOffsets(const void *payload,
+ size_t blockOffset,
+ size_t instrLen,
+ uint64_t offset1,
+ uint64_t offset2);
+
+#ifdef USE_COREDISTOOLS
+ CorAsmDiff *corAsmDiff;
+#endif // USE_COREDISTOOLS
+
+#ifdef USE_MSVCDIS
+ DIS* GetMsVcDis();
+#endif // USE_MSVCDIS
+
+};
+
+#endif // _nearDiffer
diff --git a/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp b/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp
new file mode 100644
index 0000000000..8c5232315e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp
@@ -0,0 +1,587 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "superpmi.h"
+#include "simpletimer.h"
+#include "mclist.h"
+#include "commandline.h"
+#include "errorhandling.h"
+
+#define MAX_LOG_LINE_SIZE 0x1000 //4 KB
+
+bool closeRequested = false; // global variable to communicate CTRL+C between threads.
+
+bool StartProcess(char *commandLine, HANDLE hStdOutput, HANDLE hStdError, HANDLE *hProcess)
+{
+ LogDebug("StartProcess commandLine=%s", commandLine);
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ si.hStdOutput = hStdOutput;
+ si.hStdError = hStdError;
+
+ ZeroMemory(&pi, sizeof(pi));
+
+ // Start the child process.
+ if (!CreateProcess(NULL, // No module name (use command line)
+ commandLine, // Command line
+ NULL, // Process handle not inheritable
+ NULL, // Thread handle not inheritable
+ TRUE, // Set handle inheritance to TRUE (required to use STARTF_USESTDHANDLES)
+ 0, // No creation flags
+ NULL, // Use parent's environment block
+ NULL, // Use parent's starting directory
+ &si, // Pointer to STARTUPINFO structure
+ &pi)) // Pointer to PROCESS_INFORMATION structure
+ {
+ LogError("CreateProcess failed (%d). CommandLine: %s", GetLastError(), commandLine);
+ *hProcess = INVALID_HANDLE_VALUE;
+ return false;
+ }
+
+ *hProcess = pi.hProcess;
+ return true;
+}
+
+void ReadMCLToArray(char *mclFilename, int **arr, int *count)
+{
+ *count = 0;
+ *arr = nullptr;
+ char* buff = nullptr;
+
+ HANDLE hFile = CreateFileA(mclFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Unable to open '%s'. GetLastError()=%u", mclFilename, GetLastError());
+ goto Cleanup;
+ }
+
+ LARGE_INTEGER DataTemp;
+ if (GetFileSizeEx(hFile, &DataTemp) == 0)
+ {
+ LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ goto Cleanup;
+ }
+
+ if (DataTemp.QuadPart > MAXMCLFILESIZE)
+ {
+ LogError("Size %d exceeds max size of %d", DataTemp.QuadPart, MAXMCLFILESIZE);
+ goto Cleanup;
+ }
+
+ int sz;
+ sz = (int)(DataTemp.QuadPart);
+
+ buff = new char[sz];
+ DWORD bytesRead;
+ if (ReadFile(hFile, buff, sz, &bytesRead, nullptr) == 0)
+ {
+ LogError("ReadFile failed. GetLastError()=%u", GetLastError());
+ goto Cleanup;
+ }
+
+ for (int i = 0; i < sz; i++)
+ {
+ if (buff[i] == 0x0d)
+ (*count)++;
+ }
+
+ if (*count <= 0)
+ return;
+
+ *arr = new int[*count];
+ for (int j = 0, arrIndex = 0; j < sz; )
+ {
+ //seek the first number on the line
+ while (!isdigit((unsigned char)buff[j]))
+ j++;
+ //read in the number
+ (*arr)[arrIndex++] = atoi(&buff[j]);
+ //seek to the start of next line
+ while ((j < sz) && (buff[j] != 0x0a))
+ j++;
+ j++;
+ }
+
+Cleanup:
+ if (buff != nullptr)
+ delete[] buff;
+
+ if (hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+}
+
+bool WriteArrayToMCL(char *mclFilename, int *arr, int count)
+{
+ HANDLE hMCLFile = CreateFileA(mclFilename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ bool result = true;
+
+ if (hMCLFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", mclFilename, GetLastError());
+ result = false;
+ goto Cleanup;
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ char strMethodIndex[12];
+ DWORD charCount = 0;
+ DWORD bytesWritten = 0;
+
+ charCount = sprintf(strMethodIndex, "%d\r\n", arr[i]);
+
+ if (!WriteFile(hMCLFile, strMethodIndex, charCount, &bytesWritten, nullptr) || (bytesWritten != charCount))
+ {
+ LogError("Failed to write method index '%d'. GetLastError()=%u", strMethodIndex, GetLastError());
+ result = false;
+ goto Cleanup;
+ }
+ }
+
+Cleanup:
+ if (hMCLFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hMCLFile);
+
+ return result;
+}
+
+void ProcessChildStdErr(char *stderrFilename)
+{
+ char buff[MAX_LOG_LINE_SIZE];
+
+ FILE *fp = fopen(stderrFilename, "r");
+
+ if (fp == NULL)
+ {
+ LogError("Unable to open '%s'.", stderrFilename);
+ goto Cleanup;
+ }
+
+ while (fgets(buff, MAX_LOG_LINE_SIZE, fp) != NULL)
+ {
+ //get rid of the '\n' at the end of line
+ size_t buffLen = strlen(buff);
+ if (buff[buffLen - 1] == '\n')
+ buff[buffLen - 1] = 0;
+
+ if (strncmp(buff, "ERROR: ", 7) == 0)
+ LogError("%s", &buff[7]); //log as Error and remove the "ERROR: " in front
+ else if (strncmp(buff, "WARNING: ", 9) == 0)
+ LogWarning("%s", &buff[9]); //log as Warning and remove the "WARNING: " in front
+ else if (strlen(buff) > 0)
+ LogWarning("%s", buff); //unknown output, log it as a warning
+ }
+
+Cleanup:
+ if (fp != NULL)
+ fclose(fp);
+}
+
+void ProcessChildStdOut(const CommandLine::Options& o, char *stdoutFilename, int *loaded, int *jitted, int *failed, int *diffs, bool *usageError)
+{
+ char buff[MAX_LOG_LINE_SIZE];
+
+ FILE *fp = fopen(stdoutFilename, "r");
+
+ if (fp == NULL)
+ {
+ LogError("Unable to open '%s'.", stdoutFilename);
+ goto Cleanup;
+ }
+
+ while (fgets(buff, MAX_LOG_LINE_SIZE, fp) != NULL)
+ {
+ //get rid of the '\n' at the end of line
+ size_t buffLen = strlen(buff);
+ if (buff[buffLen - 1] == '\n')
+ buff[buffLen - 1] = 0;
+
+ if (strncmp(buff, "MISSING: ", 9) == 0)
+ LogMissing("%s", &buff[9]); //log as Missing and remove the "MISSING: " in front
+ else if (strncmp(buff, "ISSUE: ", 7) == 0)
+ {
+ if (strncmp(&buff[7], "<ASM_DIFF> ", 11) == 0)
+ LogIssue(ISSUE_ASM_DIFF, "%s", &buff[18]); //log as Issue and remove the "ISSUE: <ASM_DIFF>" in front
+ else if (strncmp(&buff[7], "<ASSERT> ", 9) == 0)
+ LogIssue(ISSUE_ASSERT, "%s", &buff[16]); //log as Issue and remove the "ISSUE: <ASSERT>" in front
+ }
+ else if (strncmp(buff, g_SuperPMIUsageFirstLine, strlen(g_SuperPMIUsageFirstLine)) == 0)
+ {
+ *usageError = true; //Signals that we had a SuperPMI command line usage error
+
+ //Read the entire stdout file and printf it
+ printf("%s", buff);
+ while (fgets(buff, MAX_LOG_LINE_SIZE, fp) != NULL)
+ {
+ printf("%s", buff);
+ }
+ break;
+ }
+ else if (strncmp(buff, g_AllFormatStringFixedPrefix, strlen(g_AllFormatStringFixedPrefix)) == 0)
+ {
+ if (o.applyDiff)
+ {
+ int temp1 = 0, temp2 = 0, temp3 = 0, temp4 = 0;
+ int converted = sscanf(buff, g_AsmDiffsSummaryFormatString, &temp1, &temp2, &temp3, &temp4);
+ if (converted != 4)
+ {
+ LogError("Couldn't parse status message: \"%s\"", buff);
+ }
+ else
+ {
+ *loaded += temp1;
+ *jitted += temp2;
+ *failed += temp3;
+ *diffs += temp4;
+ }
+ }
+ else
+ {
+ int temp1 = 0, temp2 = 0, temp3 = 0;
+ int converted = sscanf(buff, g_SummaryFormatString, &temp1, &temp2, &temp3);
+ if (converted != 3)
+ {
+ LogError("Couldn't parse status message: \"%s\"", buff);
+ }
+ else
+ {
+ *loaded += temp1;
+ *jitted += temp2;
+ *failed += temp3;
+ *diffs = -1;
+ }
+ }
+ }
+ }
+
+Cleanup:
+ if (fp != NULL)
+ fclose(fp);
+}
+
+#ifndef FEATURE_PAL // TODO-Porting: handle Ctrl-C signals gracefully on Unix
+BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
+{
+ //Since the child SuperPMI.exe processes share the same console
+ //We don't need to kill them individually as they also receive the Ctrl-C
+
+ closeRequested = true; //set a flag to indicate we need to quit
+ return TRUE;
+}
+#endif // !FEATURE_PAL
+
+int __cdecl compareInt(const void *arg1, const void *arg2)
+{
+ return (*(const int *)arg1) - (*(const int *)arg2);
+}
+
+// 'arrWorkerMCLPath' is an array of strings of size 'workerCount'.
+void MergeWorkerMCLs(char *mclFilename, char **arrWorkerMCLPath, int workerCount)
+{
+ int **MCL = new int*[workerCount], *MCLCount = new int[workerCount], totalCount = 0;
+
+ for (int i = 0; i < workerCount; i++)
+ {
+ //Read the next partial MCL file
+ ReadMCLToArray(arrWorkerMCLPath[i], &MCL[i], &MCLCount[i]);
+
+ totalCount += MCLCount[i];
+ }
+
+ int *mergedMCL = new int[totalCount];
+ int index = 0;
+
+ for (int i = 0; i < workerCount; i++)
+ {
+ for (int j = 0; j < MCLCount[i]; j++)
+ mergedMCL[index++] = MCL[i][j];
+ }
+
+ qsort(mergedMCL, totalCount, sizeof(int), compareInt);
+
+ //Write the merged MCL array back to disk
+ if (!WriteArrayToMCL(mclFilename, mergedMCL, totalCount))
+ LogError("Unable to write to MCL file %s.", mclFilename);
+}
+
+// From the arguments that we parsed, construct the arguments to pass to the child processes.
+#define MAX_CMDLINE_SIZE 0x1000 //4 KB
+char* ConstructChildProcessArgs(const CommandLine::Options& o)
+{
+ int bytesWritten = 0;
+ char* spmiArgs = new char[MAX_CMDLINE_SIZE];
+ *spmiArgs = '\0';
+
+ // We don't pass through /parallel, /skipCleanup, /verbosity, /failingMCList, or /diffMCList. Everything else we need to reconstruct and pass through.
+
+#define ADDSTRING(s) if (s != nullptr) { bytesWritten += sprintf_s(spmiArgs + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " %s", s); }
+#define ADDARG_BOOL(b,arg) if (b) { bytesWritten += sprintf_s(spmiArgs + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " %s", arg); }
+#define ADDARG_STRING(s,arg) if (s != nullptr) { bytesWritten += sprintf_s(spmiArgs + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " %s %s", arg, s); }
+
+ ADDARG_BOOL(o.breakOnError, "-boe");
+ ADDARG_BOOL(o.breakOnAssert, "-boa");
+ ADDARG_BOOL(o.applyDiff, "-applyDiff");
+ ADDARG_STRING(o.reproName, "-reproName");
+ ADDARG_STRING(o.writeLogFile, "-writeLogFile");
+ ADDARG_STRING(o.methodStatsTypes, "-emitMethodStats");
+ ADDARG_STRING(o.reproName, "-reproName");
+ ADDARG_STRING(o.hash, "-matchHash");
+ ADDARG_STRING(o.targetArchitecture, "-target");
+ ADDARG_STRING(o.compileList, "-compile");
+
+ ADDSTRING(o.nameOfJit);
+ ADDSTRING(o.nameOfJit2);
+ ADDSTRING(o.nameOfInputMethodContextFile);
+
+#undef ADDSTRING
+#undef ADDARG_BOOL
+#undef ADDARG_STRING
+
+ return spmiArgs;
+}
+
+int doParallelSuperPMI(CommandLine::Options& o)
+{
+ HRESULT hr = E_FAIL;
+ SimpleTimer st;
+ st.Start();
+
+#ifndef FEATURE_PAL // TODO-Porting: handle Ctrl-C signals gracefully on Unix
+ //Register a ConsoleCtrlHandler
+ if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
+ {
+ LogError("Failed to set control handler.");
+ return 1;
+ }
+#endif // !FEATURE_PAL
+
+ char tempPath[MAX_PATH];
+ if (!GetTempPath(MAX_PATH, tempPath))
+ {
+ LogError("Failed to get path to temp folder.");
+ return 1;
+ }
+
+ if (o.workerCount <= 0)
+ {
+ //Use the default value which is the number of processors on the machine.
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+
+ o.workerCount = sysinfo.dwNumberOfProcessors;
+
+ //If we ever execute on a machine which has more than MAXIMUM_WAIT_OBJECTS(64) CPU cores
+ //we still can't spawn more than the max supported by WaitForMultipleObjects()
+ if (o.workerCount > MAXIMUM_WAIT_OBJECTS)
+ o.workerCount = MAXIMUM_WAIT_OBJECTS;
+ }
+
+ // Obtain the folder path of the current executable, which we will use to spawn ourself.
+ char* spmiFilename = new char[MAX_PATH];
+ if (!GetModuleFileName(NULL, spmiFilename, MAX_PATH))
+ {
+ LogError("Failed to get current exe path.");
+ return 1;
+ }
+
+ char* spmiArgs = ConstructChildProcessArgs(o);
+
+ // TODO: merge all this output to a single call to LogVerbose to avoid all the newlines.
+ LogVerbose("Using child (%s) with args (%s)", spmiFilename, spmiArgs);
+ if (o.mclFilename != nullptr)
+ LogVerbose(" failingMCList=%s", o.mclFilename);
+ if (o.diffMCLFilename != nullptr)
+ LogVerbose(" diffMCLFilename=%s", o.diffMCLFilename);
+ LogVerbose(" workerCount=%d, skipCleanup=%d.", o.workerCount, o.skipCleanup);
+
+ HANDLE *hProcesses = new HANDLE[o.workerCount];
+ HANDLE *hStdOutput = new HANDLE[o.workerCount];
+ HANDLE *hStdError = new HANDLE[o.workerCount];
+
+ char** arrFailingMCListPath = new char*[o.workerCount];
+ char** arrDiffMCListPath = new char*[o.workerCount];
+ char** arrStdOutputPath = new char*[o.workerCount];
+ char** arrStdErrorPath = new char*[o.workerCount];
+
+ // Add a random number to the temporary file names to allow multiple parallel SuperPMI to happen at once.
+ unsigned int randNumber = 0;
+#ifdef FEATURE_PAL
+ PAL_Random(/* bStrong */ FALSE, &randNumber, sizeof(randNumber));
+#else // !FEATURE_PAL
+ rand_s(&randNumber);
+#endif // !FEATURE_PAL
+
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ if (o.mclFilename != nullptr)
+ {
+ arrFailingMCListPath[i] = new char[MAX_PATH];
+ sprintf_s(arrFailingMCListPath[i], MAX_PATH, "%sParallelSuperPMI-%u-%d.mcl", tempPath, randNumber, i);
+ }
+ else
+ {
+ arrFailingMCListPath[i] = nullptr;
+ }
+
+ if (o.diffMCLFilename != nullptr)
+ {
+ arrDiffMCListPath[i] = new char[MAX_PATH];
+ sprintf_s(arrDiffMCListPath[i], MAX_PATH, "%sParallelSuperPMI-Diff-%u-%d.mcl", tempPath, randNumber, i);
+ }
+ else
+ {
+ arrDiffMCListPath[i] = nullptr;
+ }
+
+ arrStdOutputPath[i] = new char[MAX_PATH];
+ arrStdErrorPath[i] = new char[MAX_PATH];
+
+ sprintf_s(arrStdOutputPath[i], MAX_PATH, "%sParallelSuperPMI-stdout-%u-%d.txt", tempPath, randNumber, i);
+ sprintf_s(arrStdErrorPath[i], MAX_PATH, "%sParallelSuperPMI-stderr-%u-%d.txt", tempPath, randNumber, i);
+ }
+
+ char cmdLine[MAX_CMDLINE_SIZE];
+ cmdLine[0] = '\0';
+ int bytesWritten;
+
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ bytesWritten = sprintf_s(cmdLine, MAX_CMDLINE_SIZE, "%s -stride %d %d", spmiFilename, i + 1, o.workerCount);
+
+ if (o.mclFilename != nullptr)
+ {
+ bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -failingMCList %s", arrFailingMCListPath[i]);
+ }
+
+ if (o.diffMCLFilename != nullptr)
+ {
+ bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -diffMCList %s", arrDiffMCListPath[i]);
+ }
+
+ bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -v ewmin %s", spmiArgs);
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE; // Let newly created stdout/stderr handles be inherited.
+
+ LogDebug("stdout %i=%s", i, arrStdOutputPath[i]);
+ hStdOutput[i] = CreateFileA(arrStdOutputPath[i], GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hStdOutput[i] == INVALID_HANDLE_VALUE)
+ {
+ LogError("Unable to open '%s'. GetLastError()=%u", arrStdOutputPath[i], GetLastError());
+ return -1;
+ }
+
+ LogDebug("stderr %i=%s", i, arrStdErrorPath[i]);
+ hStdError[i] = CreateFileA(arrStdErrorPath[i], GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hStdError[i] == INVALID_HANDLE_VALUE)
+ {
+ LogError("Unable to open '%s'. GetLastError()=%u", arrStdErrorPath[i], GetLastError());
+ return -1;
+ }
+
+ //Create a SuperPMI worker process and redirect its output to file
+ if (!StartProcess(cmdLine, hStdOutput[i], hStdError[i], &hProcesses[i]))
+ {
+ return -1;
+ }
+ }
+
+ WaitForMultipleObjects(o.workerCount, hProcesses, true, INFINITE);
+
+ // Close stdout/stderr
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ CloseHandle(hStdOutput[i]);
+ CloseHandle(hStdError[i]);
+ }
+
+ DWORD exitCode = 0; // 0 == assume success
+
+ if (!closeRequested)
+ {
+ // Figure out the error code to use. We use the largest magnitude error code of the children.
+ // Mainly, if any child returns non-zero, we want to return non-zero, to indicate failure.
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ DWORD exitCodeTmp;
+ BOOL ok = GetExitCodeProcess(hProcesses[i], &exitCodeTmp);
+ if (ok && (exitCodeTmp > exitCode))
+ {
+ exitCode = exitCodeTmp;
+ }
+ }
+
+ bool usageError = false; //variable to flag if we hit a usage error in SuperPMI
+
+ int loaded = 0, jitted = 0, failed = 0, diffs = 0;
+
+ //Read the stderr files and log them as errors
+ //Read the stdout files and parse them for counts and log any MISSING or ISSUE errors
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ ProcessChildStdErr(arrStdErrorPath[i]);
+ ProcessChildStdOut(o, arrStdOutputPath[i], &loaded, &jitted, &failed, &diffs, &usageError);
+ if (usageError)
+ break;
+ }
+
+ if (o.mclFilename != nullptr && !usageError)
+ {
+ //Concat the resulting .mcl files
+ MergeWorkerMCLs(o.mclFilename, arrFailingMCListPath, o.workerCount);
+ }
+
+ if (o.diffMCLFilename != nullptr && !usageError)
+ {
+ //Concat the resulting diff .mcl files
+ MergeWorkerMCLs(o.diffMCLFilename, arrDiffMCListPath, o.workerCount);
+ }
+
+ if (!usageError)
+ {
+ if (o.applyDiff)
+ {
+ LogInfo(g_AsmDiffsSummaryFormatString, loaded, jitted, failed, diffs);
+ }
+ else
+ {
+ LogInfo(g_SummaryFormatString, loaded, jitted, failed);
+ }
+ }
+
+ st.Stop();
+ LogVerbose("Total time: %fms", st.GetMilliseconds());
+ }
+
+ if (!o.skipCleanup)
+ {
+ // Delete all temporary files generated
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ if (arrFailingMCListPath[i] != nullptr)
+ {
+ DeleteFile(arrFailingMCListPath[i]);
+ }
+ if (arrDiffMCListPath[i] != nullptr)
+ {
+ DeleteFile(arrDiffMCListPath[i]);
+ }
+ DeleteFile(arrStdOutputPath[i]);
+ DeleteFile(arrStdErrorPath[i]);
+ }
+ }
+
+ return (int)exitCode;
+}
diff --git a/src/ToolBox/superpmi/superpmi/superpmi.cpp b/src/ToolBox/superpmi/superpmi/superpmi.cpp
new file mode 100644
index 0000000000..ce352070f8
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/superpmi.cpp
@@ -0,0 +1,564 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+
+#ifdef USE_COREDISTOOLS
+#include "coredistools.h"
+#endif // USE_COREDISTOOLS
+
+#include "commandline.h"
+#include "superpmi.h"
+#include "jitinstance.h"
+#include "neardiffer.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextreader.h"
+#include "mclist.h"
+#include "methodstatsemitter.h"
+
+extern int doParallelSuperPMI(CommandLine::Options& o);
+
+// NOTE: these output status strings are parsed by parallelsuperpmi.cpp::ProcessChildStdOut().
+// There must be a single, fixed prefix common to all strings, to ease the determination of when
+// to parse the string fully.
+const char* const g_AllFormatStringFixedPrefix = "Loaded ";
+const char* const g_SummaryFormatString = "Loaded %d Jitted %d FailedCompile %d";
+const char* const g_AsmDiffsSummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Diffs %d";
+
+//#define SuperPMI_ChewMemory 0x7FFFFFFF //Amount of address space to consume on startup
+
+SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture;
+
+void SetSuperPmiTargetArchitecture(const char* targetArchitecture)
+{
+#ifdef _TARGET_AMD64_
+ if ((targetArchitecture != nullptr) && (0 == _stricmp(targetArchitecture, "arm64")))
+ {
+ SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_ARM64;
+ }
+ else
+ {
+ SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_AMD64;
+ }
+#elif defined(_TARGET_X86_)
+ SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_X86;
+#endif
+}
+
+// This function uses PAL_TRY, so it can't be in the a function that requires object unwinding. Extracting it out here
+// avoids compiler error.
+//
+void InvokeNearDiffer(
+ NearDiffer* nearDiffer,
+ CommandLine::Options* o,
+ MethodContext** mc,
+ CompileResult** crl,
+ int* matchCount,
+ MethodContextReader** reader,
+ MCList* failingMCL,
+ MCList* diffMCL
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException
+ {
+ NearDiffer* nearDiffer;
+ CommandLine::Options* o;
+ MethodContext** mc;
+ CompileResult** crl;
+ int* matchCount;
+ MethodContextReader** reader;
+ MCList* failingMCL;
+ MCList* diffMCL;
+ } param;
+ param.nearDiffer = nearDiffer;
+ param.o = o;
+ param.mc = mc;
+ param.crl = crl;
+ param.matchCount = matchCount;
+ param.reader = reader;
+ param.failingMCL = failingMCL;
+ param.diffMCL = diffMCL;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ if (pParam->nearDiffer->compare(*pParam->mc, *pParam->crl, (*pParam->mc)->cr))
+ {
+ (*pParam->matchCount)++;
+ }
+ else
+ {
+ LogIssue(ISSUE_ASM_DIFF,
+ "main method %d of size %d differs", (*pParam->reader)->GetMethodContextIndex(), (*pParam->mc)->methodSize);
+
+ //This is a difference in ASM outputs from Jit1 & Jit2 and not a playback failure
+ //We will add this MC to the diffMCList if one is requested
+ //Otherwise this will end up in failingMCList
+ if ((*pParam->o).diffMCLFilename != nullptr)
+ (*pParam->diffMCL).AddMethodToMCL((*pParam->reader)->GetMethodContextIndex());
+ else if ((*pParam->o).mclFilename != nullptr)
+ (*pParam->failingMCL).AddMethodToMCL((*pParam->reader)->GetMethodContextIndex());
+ }
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndStop)
+ {
+ SpmiException e(&param.exceptionPointers);
+
+ LogError("main method %d of size %d failed to load and compile correctly. EnvCnt=%d",
+ (*reader)->GetMethodContextIndex(), (*mc)->methodSize, (*mc)->repEnvironmentGetCount());
+ e.ShowAndDeleteMessage();
+ if ((*o).mclFilename != nullptr)
+ (*failingMCL).AddMethodToMCL((*reader)->GetMethodContextIndex());
+ }
+ PAL_ENDTRY
+}
+
+// Run superpmi. The return value is as follows:
+// 0 : success
+// -1 : general fatal error (e.g., failed to initialize, failed to read files)
+// -2 : JIT failed to initialize
+// 1 : there were compilation failures
+// 2 : there were asm diffs
+int __cdecl main(int argc, char* argv[])
+{
+#ifdef FEATURE_PAL
+ if (0 != PAL_Initialize(argc, argv))
+ {
+ fprintf(stderr, "Error: Fail to PAL_Initialize\n");
+ return -1;
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+
+ SimpleTimer st1;
+ SimpleTimer st2;
+ SimpleTimer st3;
+ SimpleTimer st4;
+ st2.Start();
+ JitInstance::Result res, res2;
+ HRESULT hr = E_FAIL;
+ MethodContext *mc = nullptr;
+ JitInstance *jit = nullptr, *jit2 = nullptr;
+ MethodStatsEmitter *methodStatsEmitter = nullptr;
+
+#ifdef SuperPMI_ChewMemory
+ //Chew up the base 2gb of memory on x86... helpful in finding any places where classhandles etc are de-ref'd
+ SYSTEM_INFO sSysInfo;
+ GetSystemInfo(&sSysInfo);
+
+ LPVOID lpvAddr;
+#undef VirtualAlloc
+ do
+ {
+ lpvAddr = VirtualAlloc(NULL, sSysInfo.dwPageSize, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
+ } while ((size_t)lpvAddr < SuperPMI_ChewMemory);
+#endif
+
+ bool collectThroughput = false;
+ MCList failingMCL, diffMCL;
+
+ CommandLine::Options o;
+ if (!CommandLine::Parse(argc, argv, &o))
+ {
+ return -1;
+ }
+
+ if (o.parallel)
+ {
+ return doParallelSuperPMI(o);
+ }
+
+ SetSuperPmiTargetArchitecture(o.targetArchitecture);
+
+ if (o.methodStatsTypes != NULL && (strchr(o.methodStatsTypes, '*') != NULL || strchr(o.methodStatsTypes, 't') != NULL || strchr(o.methodStatsTypes, 'T') != NULL))
+ {
+ collectThroughput = true;
+ }
+
+ LogVerbose("Using jit(%s) with input (%s)", o.nameOfJit, o.nameOfInputMethodContextFile);
+ std::string indexesStr = " indexCount=";
+ indexesStr += std::to_string(o.indexCount);
+ indexesStr += " (";
+ for (int i = 0; i < o.indexCount; i++)
+ {
+ indexesStr += std::to_string(o.indexes[i]);
+ if (i < (o.indexCount - 1))
+ indexesStr += ",";
+ }
+ indexesStr += ")";
+ LogVerbose(indexesStr.c_str());
+
+ if (o.methodStatsTypes != nullptr)
+ LogVerbose(" EmitMethodStats-Types=%s", o.methodStatsTypes);
+
+ if (o.hash != nullptr)
+ LogVerbose(" MD5Hash=%s", o.hash);
+
+ if (o.mclFilename != nullptr)
+ LogVerbose(" failingMCList=%s", o.mclFilename);
+
+ if (o.offset > 0 && o.increment > 0)
+ LogVerbose(" offset=%d increment=%d", o.offset, o.increment);
+
+ if (o.methodStatsTypes != nullptr)
+ {
+ methodStatsEmitter = new MethodStatsEmitter(o.nameOfInputMethodContextFile);
+ methodStatsEmitter->SetStatsTypes(o.methodStatsTypes);
+ }
+
+ if (o.mclFilename != nullptr)
+ {
+ failingMCL.InitializeMCL(o.mclFilename);
+ }
+ if (o.diffMCLFilename != nullptr)
+ {
+ diffMCL.InitializeMCL(o.diffMCLFilename);
+ }
+
+ // The method context reader handles skipping any unrequested method contexts
+ // Used in conjunction with an MCI file, it does a lot less work...
+ MethodContextReader *reader = new MethodContextReader(o.nameOfInputMethodContextFile, o.indexes, o.indexCount, o.hash, o.offset, o.increment);
+ if (!reader->isValid())
+ {
+ return -1;
+ }
+
+ int loadedCount = 0;
+ int jittedCount = 0;
+ int matchCount = 0;
+ int failCount = 0;
+ int index = 0;
+
+ st1.Start();
+ NearDiffer nearDiffer(o.targetArchitecture, o.useCoreDisTools);
+
+ if (o.applyDiff)
+ {
+ nearDiffer.InitAsmDiff();
+ }
+
+ while (true)
+ {
+ MethodContextBuffer mcb = reader->GetNextMethodContext();
+ if (mcb.Error())
+ {
+ return -1;
+ }
+ else if (mcb.allDone())
+ {
+ LogDebug("Done processing method contexts");
+ break;
+ }
+ if ((loadedCount % 500 == 0) && (loadedCount > 0))
+ {
+ st1.Stop();
+ if (o.applyDiff)
+ {
+ LogVerbose(" %2.1f%% - Loaded %d Jitted %d Matching %d FailedCompile %d at %d per second",
+ reader->PercentComplete(),
+ loadedCount,
+ jittedCount,
+ matchCount,
+ failCount,
+ (int)((double)500 / st1.GetSeconds()));
+ }
+ else
+ {
+ LogVerbose(" %2.1f%% - Loaded %d Jitted %d FailedCompile %d at %d per second",
+ reader->PercentComplete(),
+ loadedCount,
+ jittedCount,
+ failCount,
+ (int)((double)500 / st1.GetSeconds()));
+ }
+ st1.Start();
+ }
+
+ // Now read the data into a MethodContext. This could throw if the method context data is corrupt.
+
+ loadedCount++;
+ if (!MethodContext::Initialize(loadedCount, mcb.buff, mcb.size, &mc))
+ return -1;
+
+ if (jit == nullptr)
+ {
+ SimpleTimer st4;
+
+ jit = JitInstance::InitJit(o.nameOfJit, o.breakOnAssert, &st4, mc);
+ if (jit == nullptr)
+ {
+ // InitJit already printed a failure message
+ return -2;
+ }
+
+ if (o.nameOfJit2 != nullptr)
+ {
+ jit2 = JitInstance::InitJit(o.nameOfJit2, o.breakOnAssert, &st4, mc);
+ if (jit2 == nullptr)
+ {
+ // InitJit already printed a failure message
+ return -2;
+ }
+ }
+ }
+
+ // I needed to reason about what crl contains at any point in time
+ // Here is my guess based on reading the code so far
+ // crl initially contains the CompileResult from the MCH file
+ // However if we have a second jit it has the CompileResult from Jit1
+ CompileResult *crl = mc->cr;
+
+ mc->cr = new CompileResult();
+ mc->originalCR = crl;
+
+ jittedCount++;
+ st3.Start();
+ res = jit->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput);
+ st3.Stop();
+ LogDebug("Method %d compiled in %fms, result %d", reader->GetMethodContextIndex(), st3.GetMilliseconds(), res);
+
+ if ((res == JitInstance::RESULT_SUCCESS) && Logger::IsLogLevelEnabled(LOGLEVEL_DEBUG))
+ {
+ mc->cr->dumpToConsole(); // Dump the compile results if doing debug logging
+ }
+
+ if (o.nameOfJit2 != nullptr)
+ {
+ // Lets get the results for the 2nd JIT
+ // We will save the first JIT's CR to save space for the 2nd JIT CR
+ // Note that the recorded CR is still stored in MC->originalCR
+ crl = mc->cr;
+ mc->cr = new CompileResult();
+
+ st4.Start();
+ res2 = jit2->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput);
+ st4.Stop();
+ LogDebug("Method %d compiled by JIT2 in %fms, result %d", reader->GetMethodContextIndex(), st4.GetMilliseconds(), res2);
+
+ if ((res2 == JitInstance::RESULT_SUCCESS) && Logger::IsLogLevelEnabled(LOGLEVEL_DEBUG))
+ {
+ mc->cr->dumpToConsole(); // Dump the compile results if doing debug logging
+ }
+
+ if (res2 == JitInstance::RESULT_ERROR)
+ {
+ LogError("JIT2 main method %d of size %d failed to load and compile correctly. EnvCnt=%d",
+ reader->GetMethodContextIndex(), mc->methodSize, mc->repEnvironmentGetCount());
+ }
+
+ // Methods that don't compile due to missing JIT-EE information
+ // should still be added to the failing MC list.
+ // However, we will not add this MC# if JIT1 also failed, Else there will be duplicate logging
+ if ((res == JitInstance::RESULT_SUCCESS) &&
+ (res2 != JitInstance::RESULT_SUCCESS) &&
+ (o.mclFilename != nullptr))
+ {
+ failingMCL.AddMethodToMCL(reader->GetMethodContextIndex());
+ }
+ }
+
+ if (res == JitInstance::RESULT_SUCCESS)
+ {
+ if (collectThroughput)
+ {
+ if (o.nameOfJit2 != nullptr && res2 == JitInstance::RESULT_SUCCESS)
+ {
+ //TODO-Bug?: bug in getting the lowest cycle time??
+ ULONGLONG dif1, dif2, dif3, dif4;
+ dif1 = (jit->times[0] - jit2->times[0]) * (jit->times[0] - jit2->times[0]);
+ dif2 = (jit->times[0] - jit2->times[1]) * (jit->times[0] - jit2->times[1]);
+ dif3 = (jit->times[1] - jit2->times[0]) * (jit->times[1] - jit2->times[0]);
+ dif4 = (jit->times[1] - jit2->times[1]) * (jit->times[1] - jit2->times[1]);
+
+ if (dif1 < dif2)
+ {
+ if (dif3 < dif4)
+ {
+ if (dif1 < dif3)
+ {
+ crl->clockCyclesToCompile = jit->times[0];
+ mc->cr->clockCyclesToCompile = jit2->times[0];
+ }
+ else
+ {
+ crl->clockCyclesToCompile = jit->times[1];
+ mc->cr->clockCyclesToCompile = jit2->times[0];
+ }
+ }
+ else
+ {
+ if (dif1 < dif4)
+ {
+ crl->clockCyclesToCompile = jit->times[0];
+ mc->cr->clockCyclesToCompile = jit2->times[0];
+ }
+ else
+ {
+ crl->clockCyclesToCompile = jit->times[1];
+ mc->cr->clockCyclesToCompile = jit2->times[1];
+ }
+ }
+ }
+ else
+ {
+ if (dif3 < dif4)
+ {
+ if (dif2 < dif3)
+ {
+ crl->clockCyclesToCompile = jit->times[0];
+ mc->cr->clockCyclesToCompile = jit2->times[1];
+ }
+ else
+ {
+ crl->clockCyclesToCompile = jit->times[1];
+ mc->cr->clockCyclesToCompile = jit2->times[0];
+ }
+ }
+ else
+ {
+ if (dif2 < dif4)
+ {
+ crl->clockCyclesToCompile = jit->times[0];
+ mc->cr->clockCyclesToCompile = jit2->times[1];
+ }
+ else
+ {
+ crl->clockCyclesToCompile = jit->times[1];
+ mc->cr->clockCyclesToCompile = jit2->times[1];
+ }
+ }
+ }
+
+ if (methodStatsEmitter != nullptr)
+ {
+ methodStatsEmitter->Emit(reader->GetMethodContextIndex(), mc, crl->clockCyclesToCompile, mc->cr->clockCyclesToCompile);
+ }
+ }
+ else
+ {
+ if (jit->times[0] > jit->times[1])
+ mc->cr->clockCyclesToCompile = jit->times[1];
+ else
+ mc->cr->clockCyclesToCompile = jit->times[0];
+ if (methodStatsEmitter != nullptr)
+ {
+ methodStatsEmitter->Emit(reader->GetMethodContextIndex(), mc, mc->cr->clockCyclesToCompile, 0);
+ }
+ }
+ }
+
+ if (!collectThroughput && methodStatsEmitter != nullptr)
+ {
+ //We have a separate call to Emit for collectThroughput
+ methodStatsEmitter->Emit(reader->GetMethodContextIndex(), mc, -1, -1);
+ }
+
+ if (o.applyDiff)
+ {
+ // We need at least two compile results to diff: they can either both come from JIT
+ // invocations, or one can be loaded from the method context file.
+
+ // We need to check both CompileResults to ensure we have a valid CR
+ if (crl->AllocMem == nullptr || mc->cr->AllocMem == nullptr)
+ {
+ LogError("method %d is missing a compileResult, cannot do diffing", reader->GetMethodContextIndex());
+
+ // If we are here this means that either we have 2 Jits and the second Jit failed to compile
+ // Or we have single Jit and the MethodContext doesn't have an originalCR
+ // In both cases we don't need to add this to the MCList again
+ }
+ else
+ {
+ InvokeNearDiffer(&nearDiffer, &o, &mc, &crl, &matchCount, &reader, &failingMCL, &diffMCL);
+ }
+ }
+ }
+ else
+ {
+ failCount++;
+
+ if (o.mclFilename != nullptr)
+ failingMCL.AddMethodToMCL(reader->GetMethodContextIndex());
+
+ // The following only apply specifically to failures caused by errors (as opposed
+ // to, for instance, failures caused by missing JIT-EE details).
+ if (res == JitInstance::RESULT_ERROR)
+ {
+ LogError("main method %d of size %d failed to load and compile correctly. EnvCnt=%d", reader->GetMethodContextIndex(), mc->methodSize, mc->repEnvironmentGetCount());
+ if ((o.reproName != nullptr) && (o.indexCount == -1))
+ {
+ char buff[500];
+ sprintf_s(buff, 500, "%s-%d.mc", o.reproName, reader->GetMethodContextIndex());
+ HANDLE hFileOut = CreateFileA(buff, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output '%s'. GetLastError()=%u", buff, GetLastError());
+ return -1;
+ }
+ mc->saveToFile(hFileOut);
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("CloseHandle for output file failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ LogInfo("Wrote out repro to '%s'", buff);
+ }
+ if (o.breakOnError)
+ {
+ if (o.indexCount == -1)
+ LogInfo("HINT: to repro add '/c %d' to cmdline", reader->GetMethodContextIndex());
+ __debugbreak();
+ }
+ }
+ }
+
+ delete crl;
+ delete mc;
+ }
+ delete reader;
+
+ int result = 0;
+
+ // NOTE: these output status strings are parsed by parallelsuperpmi.cpp::ProcessChildStdOut().
+ if (o.applyDiff)
+ {
+ LogInfo(g_AsmDiffsSummaryFormatString, loadedCount, jittedCount, failCount, jittedCount - failCount - matchCount);
+
+ if (matchCount != jittedCount)
+ {
+ result = 2;
+ }
+ }
+ else
+ {
+ LogInfo(g_SummaryFormatString, loadedCount, jittedCount, failCount);
+ }
+
+ // Failure to JIT overrides diffs for the error code.
+ if (failCount > 0)
+ {
+ result = 1;
+ }
+
+ st2.Stop();
+ LogVerbose("Total time: %fms", st2.GetMilliseconds());
+
+ if (methodStatsEmitter != nullptr)
+ {
+ delete methodStatsEmitter;
+ }
+
+ if (o.mclFilename != nullptr)
+ {
+ failingMCL.CloseMCL();
+ }
+ if (o.diffMCLFilename != nullptr)
+ {
+ diffMCL.CloseMCL();
+ }
+ Logger::Shutdown();
+ return result;
+}
diff --git a/src/ToolBox/superpmi/superpmi/superpmi.h b/src/ToolBox/superpmi/superpmi/superpmi.h
new file mode 100644
index 0000000000..d5b7bdaa2b
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/superpmi.h
@@ -0,0 +1,27 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _SuperPMI
+#define _SuperPMI
+
+#include "errorhandling.h"
+
+enum SPMI_TARGET_ARCHITECTURE
+{
+ SPMI_TARGET_ARCHITECTURE_X86,
+ SPMI_TARGET_ARCHITECTURE_AMD64,
+ SPMI_TARGET_ARCHITECTURE_ARM64
+};
+
+extern SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture;
+extern void SetSuperPmiTargetArchitecture(const char* targetArchitecture);
+
+extern const char* const g_SuperPMIUsageFirstLine;
+
+extern const char* const g_AllFormatStringFixedPrefix;
+extern const char* const g_SummaryFormatString;
+extern const char* const g_AsmDiffsSummaryFormatString;
+
+#endif