summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ToolBox/CMakeLists.txt3
-rw-r--r--src/ToolBox/superpmi/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/CMakeLists.txt5
-rw-r--r--src/ToolBox/superpmi/mcs/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/mcs/CMakeLists.txt76
-rw-r--r--src/ToolBox/superpmi/mcs/commandline.cpp581
-rw-r--r--src/ToolBox/superpmi/mcs/commandline.h76
-rw-r--r--src/ToolBox/superpmi/mcs/mcs.cpp108
-rw-r--r--src/ToolBox/superpmi/mcs/mcs.h9
-rw-r--r--src/ToolBox/superpmi/mcs/verbasmdump.cpp68
-rw-r--r--src/ToolBox/superpmi/mcs/verbasmdump.h19
-rw-r--r--src/ToolBox/superpmi/mcs/verbconcat.cpp99
-rw-r--r--src/ToolBox/superpmi/mcs/verbconcat.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbdump.cpp36
-rw-r--r--src/ToolBox/superpmi/mcs/verbdump.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumpmap.cpp64
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumpmap.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumptoc.cpp32
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumptoc.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbfracture.cpp74
-rw-r--r--src/ToolBox/superpmi/mcs/verbfracture.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbildump.cpp625
-rw-r--r--src/ToolBox/superpmi/mcs/verbildump.h24
-rw-r--r--src/ToolBox/superpmi/mcs/verbinteg.cpp37
-rw-r--r--src/ToolBox/superpmi/mcs/verbinteg.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbmerge.cpp470
-rw-r--r--src/ToolBox/superpmi/mcs/verbmerge.h29
-rw-r--r--src/ToolBox/superpmi/mcs/verbremovedup.cpp145
-rw-r--r--src/ToolBox/superpmi/mcs/verbremovedup.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbsmarty.cpp96
-rw-r--r--src/ToolBox/superpmi/mcs/verbsmarty.h22
-rw-r--r--src/ToolBox/superpmi/mcs/verbstat.cpp74
-rw-r--r--src/ToolBox/superpmi/mcs/verbstat.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbstrip.cpp150
-rw-r--r--src/ToolBox/superpmi/mcs/verbstrip.h18
-rw-r--r--src/ToolBox/superpmi/mcs/verbtoc.cpp108
-rw-r--r--src/ToolBox/superpmi/mcs/verbtoc.h17
-rw-r--r--src/ToolBox/superpmi/readme.txt86
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/asmdumper.cpp91
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/asmdumper.h18
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/callutils.cpp416
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/callutils.h44
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/compileresult.cpp1072
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/compileresult.h276
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/crlwmlist.h46
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/errorhandling.cpp151
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/errorhandling.h88
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h70
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/icorjithostimpl.h55
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h1316
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/lightweightmap.h726
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/logging.cpp342
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/logging.h108
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/lwmlist.h149
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/mclist.cpp258
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/mclist.h35
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp6509
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontext.h1179
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.cpp127
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.h104
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp469
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontextreader.h123
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/registertablexarch.h124
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/runtimedetails.h39
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/simpletimer.cpp56
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/simpletimer.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/spmiutil.cpp109
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/spmiutil.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/standardpch.cpp7
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/standardpch.h106
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/tocfile.cpp79
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/tocfile.h78
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/typeutils.cpp169
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/typeutils.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp62
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.h19
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp120
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp2284
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.h30
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.cpp72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.h43
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.cpp154
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.h70
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/jithost.cpp64
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/jithost.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp309
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.def5
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.h17
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt73
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp62
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.h19
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp68
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.h26
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp1901
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.h26
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.cpp72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.h107
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.cpp157
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.h149
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/jithost.cpp49
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/jithost.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.cpp140
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.h23
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp231
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.def5
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.h13
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp58
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.h20
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp59
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.h24
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp1726
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.h24
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.cpp72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.h108
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.cpp158
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.h150
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/jithost.cpp40
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/jithost.h22
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp217
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.def5
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.h13
-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.cpp2039
-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
-rw-r--r--tests/src/JIT/superpmi/app.config27
-rw-r--r--tests/src/JIT/superpmi/collect_alltests.cmd36
-rwxr-xr-xtests/src/JIT/superpmi/collect_alltests.sh10
-rw-r--r--tests/src/JIT/superpmi/collect_runtest.cmd41
-rwxr-xr-xtests/src/JIT/superpmi/runtests.sh41
-rw-r--r--tests/src/JIT/superpmi/superpmicollect.cs773
-rw-r--r--tests/src/JIT/superpmi/superpmicollect.csproj56
164 files changed, 35499 insertions, 1 deletions
diff --git a/src/ToolBox/CMakeLists.txt b/src/ToolBox/CMakeLists.txt
index 36b147324c..4e7f436868 100644
--- a/src/ToolBox/CMakeLists.txt
+++ b/src/ToolBox/CMakeLists.txt
@@ -1 +1,2 @@
-add_subdirectory(SOS) \ No newline at end of file
+add_subdirectory(SOS)
+add_subdirectory(superpmi)
diff --git a/src/ToolBox/superpmi/.gitmirror b/src/ToolBox/superpmi/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/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/CMakeLists.txt b/src/ToolBox/superpmi/CMakeLists.txt
new file mode 100644
index 0000000000..d4308d2e03
--- /dev/null
+++ b/src/ToolBox/superpmi/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_subdirectory(superpmi)
+add_subdirectory(mcs)
+add_subdirectory(superpmi-shim-collector)
+add_subdirectory(superpmi-shim-counter)
+add_subdirectory(superpmi-shim-simple)
diff --git a/src/ToolBox/superpmi/mcs/.gitmirror b/src/ToolBox/superpmi/mcs/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/.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/mcs/CMakeLists.txt b/src/ToolBox/superpmi/mcs/CMakeLists.txt
new file mode 100644
index 0000000000..7e6ff70471
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/CMakeLists.txt
@@ -0,0 +1,76 @@
+project(mcs)
+
+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)
+
+set(MCS_SOURCES
+ commandline.cpp
+ mcs.cpp
+ verbasmdump.cpp
+ verbconcat.cpp
+ verbdump.cpp
+ verbdumpmap.cpp
+ verbdumptoc.cpp
+ verbfracture.cpp
+ verbildump.cpp
+ verbinteg.cpp
+ verbmerge.cpp
+ verbremovedup.cpp
+ verbsmarty.cpp
+ verbstat.cpp
+ verbstrip.cpp
+ verbtoc.cpp
+ ../superpmi-shared/asmdumper.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/methodcontextiterator.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
+ MCS_SOURCES
+)
+
+add_executable(mcs
+ ${MCS_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(mcs
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(mcs
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/mcs.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS mcs DESTINATION .)
diff --git a/src/ToolBox/superpmi/mcs/commandline.cpp b/src/ToolBox/superpmi/mcs/commandline.cpp
new file mode 100644
index 0000000000..ea6a4a3e72
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/commandline.cpp
@@ -0,0 +1,581 @@
+//
+// 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 "logging.h"
+#include "mclist.h"
+
+void CommandLine::DumpHelp(const char* program)
+{
+ printf("MCS is a utility for examining and manipulating SuperPMI MC files.\n");
+ printf("\n");
+ printf("Usage: %s [options] {verb} {verb options}", program);
+ printf("\n");
+ printf("Options:\n");
+ printf("\n");
+ printf(" -v[erbosity] messagetypes\n");
+ printf(" Controls which types of messages MCS 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(" n - information (notifications/summaries, e.g. 'Loaded 42, Saved 23')\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 'ewmnv'.\n");
+ printf("\n");
+ printf(" -writeLogFile logfile\n");
+ printf(" Write log messages to the specified file.\n");
+ printf("\n");
+ printf("Verbs:\n");
+ printf("\n");
+ printf(" -ASMDump {optional range} inputfile outputfile\n");
+ printf(" Dump out the asm file for each input methodContext.\n");
+ printf(" inputfile is read and output is written to outputfile.\n");
+ printf(" e.g. -ASMDump a.mc a.asm\n");
+ printf("\n");
+ printf(" -concat file1 file2\n");
+ printf(" Concatenate two files without regard to internal formatting.\n");
+ printf(" file2 is appended to file1.\n");
+ printf(" e.g. -concat a.mch b.mch\n");
+ printf("\n");
+ printf(" -copy range file1 file2\n");
+ printf(" Copy methodContext numbers in range from file1 to file2.\n");
+ printf(" file1 is read and file2 is written\n");
+ printf(" e.g. -copy a.mch b.mch\n");
+ printf("\n");
+ printf(" -dump {optional range} inputfile\n");
+ printf(" Dump details for each methodContext\n");
+ printf(" e.g. -dump a.mc\n");
+ printf("\n");
+ printf(" -dumpMap inputfile\n");
+ printf(" Dump a map from MC index to function name to the console, in CSV format\n");
+ printf(" e.g. -dumpMap a.mc\n");
+ printf("\n");
+ printf(" -dumpToc inputfile\n");
+ printf(" Dump a TOC file\n");
+ printf(" e.g. -dumpToc a.mct\n");
+ printf("\n");
+ printf(" -fracture range inputfile outputfile\n");
+ printf(" Break the input file into chunks sized by range.\n");
+ printf(" If '-thin' is also passed, CompileResults are stripped from the input file when written.\n");
+ printf(" e.g. '-fracture 3 a.mch b-' leads to b-0.mch, b-1.mch, etc., with 3 mc's in each file.\n");
+ printf("\n");
+ printf(" -ildump {optional range} inputfile\n");
+ printf(" Dump raw IL for each methodContext\n");
+ printf(" e.g. -ildump a.mc\n");
+ printf("\n");
+ printf(" -integ inputfile\n");
+ printf(" Check the integrity of each methodContext\n");
+ printf(" e.g. -integ a.mc\n");
+ printf("\n");
+ printf(" -merge outputfile pattern\n");
+ printf(" Merge all the input files matching the pattern.\n");
+ printf(" e.g. -merge a.mch *.mc\n");
+ printf(" e.g. -merge a.mch c:\\foo\\bar\\*.mc\n");
+ printf(" e.g. -merge a.mch relpath\\*.mc\n");
+ printf(" e.g. -merge a.mch .\n");
+ printf(" e.g. -merge a.mch onedir\n");
+ printf("\n");
+ printf(" -merge outputfile pattern -recursive\n");
+ printf(" Merge all the input files matching the pattern, in the specified and all child directories.\n");
+ printf(" e.g. -merge a.mch *.mc -recursive\n");
+ printf("\n");
+ printf(" -removeDup inputfile outputfile\n");
+ printf(" Copy methodContexts from inputfile to outputfile, skipping duplicates.\n");
+ printf(" e.g. -removeDup a.mc b.mc\n");
+ printf("\n");
+ printf(" -removeDup -legacy inputfile outputfile\n");
+ printf(" Copy methodContexts from inputfile to outputfile, skipping duplicates.\n");
+ printf(" Comparisons are performed using the legacy method and may take much longer\n");
+ printf(" e.g. -removeDup -legacy a.mc b.mc\n");
+ printf("\n");
+ printf(" -removeDup -thin inputfile outputfile\n");
+ printf(" Copy methodContexts from inputfile to outputfile, skipping duplicates.\n");
+ printf(" CompileResults are stripped from the input file when written.\n");
+ printf(" e.g. -removeDup -thin a.mc b.mc\n");
+ printf(" e.g. -removeDup -legacy -thin a.mc b.mc\n");
+ printf("\n");
+ printf(" -smarty range inputfile outputfile\n");
+ printf(" Write smarty Test IDs of the range from inputfile to outputfile.\n");
+ printf(" e.g. -smarty 2 a.mc b.mc\n");
+ printf("\n");
+ printf(" -stat {optional range} inputfile outputfile\n");
+ printf(" Report various statistics per method context.\n");
+ printf(" inputfile is read and statistics are written into outputfile\n");
+ printf(" e.g. -stat a.mc a.csv\n");
+ printf("\n");
+ printf(" -strip range inputfile outputfile\n");
+ printf(" Copy method contexts from one file to another, skipping ranged items.\n");
+ printf(" inputfile is read and records not in range are written to outputfile.\n");
+ printf(" e.g. -strip 2 a.mc b.mc\n");
+ printf("\n");
+ printf(" -toc inputfile\n");
+ printf(" Create a Table of Contents file for inputfile to allow better random access\n");
+ printf(" to the mch file.\n");
+ printf(" e.g. '-toc a.mch' creates a.mch.mct\n");
+ printf("\n");
+ printf("Range descriptions are either a single number, or a text file with .mcl extension\n");
+ printf("containing a sorted list of line delimited numbers.\n");
+ printf(" e.g. -strip 2 a.mc b.mc\n");
+ printf(" e.g. -strip list.mcl a.mc b.mc\n");
+ printf("\n");
+ printf("Note: Inputs are case insensitive.\n");
+}
+
+//Assumption: All inputs are initialized to default or real value. we'll just set the stuff in what 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 foundVerb = false;
+ bool foundFile1 = false;
+ bool foundFile2 = 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], "?", argLen) == 0))
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+ else if ((_strnicmp(&argv[i][1], "ASMDump", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionASMDump = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "concat", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionConcat = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "copy", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionCopy = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "dump", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionDump = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "fracture", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionFracture = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "dumpmap", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionDumpMap = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "dumptoc", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionDumpToc = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "ildump", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionILDump = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "merge", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionMerge = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "recursive", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ o->recursive = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "toc", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionTOC = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "input", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processInput:
+
+ tempLen = strlen(argv[i]);
+ if (tempLen == 0)
+ {
+ printf("ERROR: CommandLine::Parse() Arg '%s' is invalid, name of file missing.\n", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (foundFile1 == false)
+ {
+ o->nameOfFile1 = new char[tempLen + 1];
+ strcpy_s(o->nameOfFile1, tempLen + 1, argv[i]);
+ foundFile1 = true;
+ }
+ else if (foundFile2 == false)
+ {
+ o->nameOfFile2 = new char[tempLen + 1];
+ strcpy_s(o->nameOfFile2, tempLen + 1, argv[i]);
+ foundFile2 = true;
+ }
+ else
+ {
+ printf("ERROR: CommandLine::Parse() Arg '%s' is invalid, too many files given.\n", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ else if ((_strnicmp(&argv[i][1], "integ", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionInteg = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "mcl", argLen) == 0))
+ {
+ if (i + 1 >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processMCL:
+ i++;
+ processMCL2:
+
+ bool isValidList = MCList::processArgAsMCL(argv[i], &o->indexCount, &o->indexes);
+ if (!isValidList)
+ i--;
+ }
+ else if ((_strnicmp(&argv[i][1], "removeDup", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionRemoveDup = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "stat", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionStat = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "strip", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionStrip = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "thin", argLen) == 0))
+ {
+ o->stripCR = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "legacy", argLen) == 0))
+ {
+ o->legacyCompare = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "smarty", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ o->actionSmarty = true;
+ foundVerb = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ 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;
+ }
+
+ Logger::OpenLogFile(argv[i]);
+ }
+ else
+ {
+ LogError("CommandLine::Parse() - Unknown verb '%s'", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ }
+ //Process an input filename
+ else
+ {
+ char *lastdot = strrchr(argv[i], '.');
+ if (lastdot != nullptr)
+ {
+ if (_stricmp(lastdot, ".mcl") == 0)
+ goto processMCL2;
+ }
+ goto processInput;
+ }
+ }
+
+ if (o->recursive)
+ {
+ if (!o->actionMerge)
+ {
+ LogError("CommandLine::Parse() '-recursive' requires -merge.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+
+ if (o->actionASMDump)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() -ASMDump needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionConcat)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-concat' needs two input files (second will be used as output).");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionMerge)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-merge' needs an output file (the first) and a file pattern (the second).");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionCopy)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-copy' needs one input and one output.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount == 0)
+ {
+ LogError("CommandLine::Parse() -copy requires a range.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionDump)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-dump' needs one input file, but didn't see one.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionFracture)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-fracture' needs one input and one output.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount == 0)
+ {
+ LogError("CommandLine::Parse() -fracture requires a range.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount > 1)
+ {
+ LogWarning("CommandLine::Parse() -fracture found multiple ranges, we'll use the first one.");
+ }
+ return true;
+ }
+ if (o->actionDumpMap)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-dumpMap' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionDumpToc)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-dumpToc' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionILDump)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-ildump' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionTOC)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-toc' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionInteg)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-integ' needs one input file, but didn't see one.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionRemoveDup)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() -removeDup needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionStat)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-stat' needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionStrip)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() -strip needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount == 0)
+ {
+ LogError("CommandLine::Parse() -strip requires a range.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionSmarty)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-smarty' needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+
+ DumpHelp(argv[0]);
+ return false;
+}
diff --git a/src/ToolBox/superpmi/mcs/commandline.h b/src/ToolBox/superpmi/mcs/commandline.h
new file mode 100644
index 0000000000..a04969696b
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/commandline.h
@@ -0,0 +1,76 @@
+//
+// 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() :
+ actionASMDump(false),
+ actionConcat(false),
+ actionCopy(false),
+ actionDump(false),
+ actionDumpMap(false),
+ actionDumpToc(false),
+ actionFracture(false),
+ actionILDump(false),
+ actionInteg(false),
+ actionMerge(false),
+ actionRemoveDup(false),
+ actionSmarty(false),
+ actionStat(false),
+ actionStrip(false),
+ actionTOC(false),
+ legacyCompare(false),
+ recursive(false),
+ stripCR(false),
+ nameOfFile1(nullptr),
+ nameOfFile2(nullptr),
+ nameOfFile3(nullptr),
+ indexCount(-1),
+ indexes(nullptr)
+ {
+ }
+
+ bool actionASMDump;
+ bool actionConcat;
+ bool actionCopy;
+ bool actionDump;
+ bool actionDumpMap;
+ bool actionDumpToc;
+ bool actionFracture;
+ bool actionILDump;
+ bool actionInteg;
+ bool actionMerge;
+ bool actionRemoveDup;
+ bool actionSmarty;
+ bool actionStat;
+ bool actionStrip;
+ bool actionTOC;
+ bool legacyCompare;
+ bool recursive;
+ bool stripCR;
+ char* nameOfFile1;
+ char* nameOfFile2;
+ char* nameOfFile3;
+ int indexCount;
+ int* indexes;
+ };
+
+ static bool Parse(int argc, char* argv[], /* OUT */ Options* o);
+
+private:
+ static void DumpHelp(const char* program);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/mcs.cpp b/src/ToolBox/superpmi/mcs/mcs.cpp
new file mode 100644
index 0000000000..d2debdd90f
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/mcs.cpp
@@ -0,0 +1,108 @@
+//
+// 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 "mcs.h"
+#include "commandline.h"
+#include "verbasmdump.h"
+#include "verbinteg.h"
+#include "verbdump.h"
+#include "verbfracture.h"
+#include "verbdumpmap.h"
+#include "verbdumptoc.h"
+#include "verbildump.h"
+#include "verbtoc.h"
+#include "verbremovedup.h"
+#include "verbstat.h"
+#include "verbconcat.h"
+#include "verbmerge.h"
+#include "verbstrip.h"
+#include "verbsmarty.h"
+#include "logging.h"
+
+int __cdecl main(int argc, char* argv[])
+{
+#ifdef FEATURE_PAL
+ if (0 != PAL_Initialize(argc, argv))
+ {
+ fprintf(stderr, "Error: Fail to PAL_Initialize\n");
+ exit(1);
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+
+ CommandLine::Options o;
+ if(!CommandLine::Parse(argc, argv, &o))
+ {
+ return -1;
+ }
+
+ //execute the chosen command.
+ int exitCode = 0;
+ if (o.actionASMDump)
+ {
+ exitCode = verbASMDump::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
+ }
+ if (o.actionConcat)
+ {
+ exitCode = verbConcat::DoWork(o.nameOfFile1, o.nameOfFile2);
+ }
+ if (o.actionMerge)
+ {
+ exitCode = verbMerge::DoWork(o.nameOfFile1, o.nameOfFile2, o.recursive);
+ }
+ if (o.actionCopy)
+ {
+ exitCode = verbStrip::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes, false, o.stripCR);
+ }
+ if (o.actionDump)
+ {
+ exitCode = verbDump::DoWork(o.nameOfFile1, o.indexCount, o.indexes);
+ }
+ if (o.actionFracture)
+ {
+ exitCode = verbFracture::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes, o.stripCR);
+ }
+ if (o.actionDumpMap)
+ {
+ exitCode = verbDumpMap::DoWork(o.nameOfFile1);
+ }
+ if (o.actionDumpToc)
+ {
+ exitCode = verbDumpToc::DoWork(o.nameOfFile1);
+ }
+ if (o.actionILDump)
+ {
+ exitCode = verbILDump::DoWork(o.nameOfFile1, o.indexCount, o.indexes);
+ }
+ if (o.actionInteg)
+ {
+ exitCode = verbInteg::DoWork(o.nameOfFile1);
+ }
+ if (o.actionRemoveDup)
+ {
+ exitCode = verbRemoveDup::DoWork(o.nameOfFile1, o.nameOfFile2, o.stripCR, o.legacyCompare);
+ }
+ if (o.actionStat)
+ {
+ exitCode = verbStat::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
+ }
+ if (o.actionStrip)
+ {
+ exitCode = verbStrip::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes, true, o.stripCR);
+ }
+ if (o.actionTOC)
+ {
+ exitCode = verbTOC::DoWork(o.nameOfFile1);
+ }
+ if (o.actionSmarty)
+ {
+ exitCode = verbSmarty::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
+ }
+
+ Logger::Shutdown();
+ return exitCode;
+}
diff --git a/src/ToolBox/superpmi/mcs/mcs.h b/src/ToolBox/superpmi/mcs/mcs.h
new file mode 100644
index 0000000000..3eb19502db
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/mcs.h
@@ -0,0 +1,9 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _MCS
+#define _MCS
+
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbasmdump.cpp b/src/ToolBox/superpmi/mcs/verbasmdump.cpp
new file mode 100644
index 0000000000..3f018b3a45
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbasmdump.cpp
@@ -0,0 +1,68 @@
+//
+// 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 "verbasmdump.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "asmdumper.h"
+#include "errorhandling.h"
+
+#define BUFFER_SIZE 0xFFFFFF
+
+int verbASMDump::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes)
+{
+ LogVerbose("Loading from '%s' and writing ASM output into '%s-MC#.asm'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(indexCount, indexes, true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ char buff[500];
+ sprintf_s(buff, 500, "%s-%d.asm", nameOfOutput, mci.MethodContextNumber());
+
+ 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;
+ }
+
+ if (mc->cr->IsEmpty())
+ {
+ const size_t bufflen = 4096;
+ DWORD bytesWritten;
+ char buff[bufflen];
+ ZeroMemory(buff, bufflen * sizeof(char));
+ int buff_offset = sprintf_s(buff, bufflen, ";;Method context has no compile result");
+ WriteFile(hFileOut, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+ }
+ else
+ {
+ ASMDumper::DumpToFile(hFileOut, mc, mc->cr);
+ }
+
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ savedCount++;
+ }
+
+ LogInfo("Asm'd %d", savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbasmdump.h b/src/ToolBox/superpmi/mcs/verbasmdump.h
new file mode 100644
index 0000000000..693366ef91
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbasmdump.h
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbASMDump.h - verb that
+//----------------------------------------------------------
+#ifndef _verbASMDump
+#define _verbASMDump
+
+class verbASMDump
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes);
+};
+#endif
+
+
diff --git a/src/ToolBox/superpmi/mcs/verbconcat.cpp b/src/ToolBox/superpmi/mcs/verbconcat.cpp
new file mode 100644
index 0000000000..36620f11b2
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbconcat.cpp
@@ -0,0 +1,99 @@
+//
+// 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 "verbconcat.h"
+#include "simpletimer.h"
+#include "logging.h"
+
+#define BUFFER_SIZE 0xFFFFFF
+
+int verbConcat::DoWork(const char *nameOfFile1, const char *nameOfFile2)
+{
+ SimpleTimer st1;
+
+ LogVerbose("Concatenating '%s'+'%s' into %s", nameOfFile1, nameOfFile2, nameOfFile1);
+
+ LARGE_INTEGER DataTemp1;
+ LARGE_INTEGER DataTemp2;
+
+ HANDLE hFileIn1 = CreateFileA(nameOfFile1, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileIn1 == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfFile1, GetLastError());
+ return -1;
+ }
+ if(GetFileSizeEx(hFileIn1, &DataTemp1)==0)
+ {
+ LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LONG highDWORD = 0;
+ DWORD dwPtr = SetFilePointer(hFileIn1, 0, &highDWORD, FILE_END);
+ if (dwPtr == INVALID_SET_FILE_POINTER)
+ {
+ LogError("Failed to SetFilePointer on input 1 '%s'. GetLastError()=%u", nameOfFile1, GetLastError());
+ return -1;
+ }
+
+ HANDLE hFileIn2 = CreateFileA(nameOfFile2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileIn2 == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 2 '%s'. GetLastError()=%u", nameOfFile2, GetLastError());
+ return -1;
+ }
+ if(GetFileSizeEx(hFileIn2, &DataTemp2)==0)
+ {
+ LogError("2nd GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ unsigned char* buffer = new unsigned char[BUFFER_SIZE];
+
+ st1.Start();
+ for(LONGLONG offset = 0; offset < DataTemp2.QuadPart; offset += BUFFER_SIZE)
+ {
+ DWORD bytesRead = -1;
+ BOOL res = ReadFile(hFileIn2, buffer, BUFFER_SIZE, &bytesRead, nullptr);
+ if(res == 0)
+ {
+ LogError("Failed to read '%s' from offset %lld. GetLastError()=%u", nameOfFile2, offset, GetLastError());
+ return -1;
+ }
+ DWORD bytesWritten = -1;
+ BOOL res2 = WriteFile(hFileIn1, buffer, bytesRead, &bytesWritten, nullptr);
+ if(res2 == 0)
+ {
+ LogError("Failed to write '%s' at offset %lld. GetLastError()=%u", nameOfFile1, offset, GetLastError());
+ return -1;
+ }
+ if(bytesRead!=bytesWritten)
+ {
+ LogError("Failed to read/write matching bytes %u!=%u", bytesRead, bytesWritten);
+ return -1;
+ }
+ }
+ st1.Stop();
+
+ delete[] buffer;
+
+ if(CloseHandle(hFileIn1)==0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ if(CloseHandle(hFileIn2)==0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Read/Wrote %lld MB @ %4.2f MB/s.\n",
+ DataTemp2.QuadPart/(1000*1000),
+ (((double)DataTemp2.QuadPart)/(1000*1000))/st1.GetSeconds()); //yes yes.. http://en.wikipedia.org/wiki/Megabyte_per_second#Megabyte_per_second
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbconcat.h b/src/ToolBox/superpmi/mcs/verbconcat.h
new file mode 100644
index 0000000000..26e55857bd
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbconcat.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbConcat.h - verb that concatenates two files
+//----------------------------------------------------------
+#ifndef _verbConcat
+#define _verbConcat
+
+class verbConcat
+{
+public:
+ static int DoWork(const char *nameOfFile1, const char *nameOfFile2);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbdump.cpp b/src/ToolBox/superpmi/mcs/verbdump.cpp
new file mode 100644
index 0000000000..290cbdbac5
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdump.cpp
@@ -0,0 +1,36 @@
+//
+// 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 "verbdump.h"
+#include "logging.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "errorhandling.h"
+
+int verbDump::DoWork(const char *nameOfInput, int indexCount, const int *indexes)
+{
+ LogVerbose("Dumping '%s' to console", nameOfInput);
+
+ MethodContextIterator mci(indexCount, indexes);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int dumpedCount = 0;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ mc->dumpToConsole(mci.MethodContextNumber());
+ dumpedCount++;
+ }
+
+ LogVerbose("Dumped %d methodContexts", dumpedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbdump.h b/src/ToolBox/superpmi/mcs/verbdump.h
new file mode 100644
index 0000000000..32f4b0d661
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdump.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbDump.h - verb that Dumps a MC file
+//----------------------------------------------------------
+#ifndef _verbDump
+#define _verbDump
+
+class verbDump
+{
+public:
+ static int DoWork(const char *nameofInput, int indexCount, const int *indexes);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbdumpmap.cpp b/src/ToolBox/superpmi/mcs/verbdumpmap.cpp
new file mode 100644
index 0000000000..3fc8268f5f
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumpmap.cpp
@@ -0,0 +1,64 @@
+//
+// 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 "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "verbdumpmap.h"
+#include "verbildump.h"
+
+// Dump the CSV format header for all the columns we're going to dump.
+void DumpMapHeader()
+{
+ printf("index,");
+ // printf("process name,");
+ printf("method name,");
+ printf("full signature\n");
+}
+
+void DumpMap(int index, MethodContext *mc)
+{
+ CORINFO_METHOD_INFO cmi;
+ unsigned int flags = 0;
+
+ mc->repCompileMethod(&cmi, &flags);
+
+ const char *moduleName = nullptr;
+ const char *methodName = mc->repGetMethodName(cmi.ftn, &moduleName);
+ const char *className = mc->repGetClassName(mc->repGetMethodClass(cmi.ftn));
+
+ printf("%d,", index);
+ // printf("\"%s\",", mc->cr->repProcessName());
+ printf("%s:%s,", className, methodName);
+
+ // Also, dump the full method signature
+ printf("\"");
+ DumpAttributeToConsoleBare(mc->repGetMethodAttribs(cmi.ftn));
+ DumpPrimToConsoleBare(mc, cmi.args.retType, (DWORDLONG)cmi.args.retTypeClass);
+ printf(" %s(", methodName);
+ DumpSigToConsoleBare(mc, &cmi.args);
+ printf(")\"\n");
+}
+
+int verbDumpMap::DoWork(const char *nameOfInput)
+{
+ MethodContextIterator mci;
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ DumpMapHeader();
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ DumpMap(mci.MethodContextNumber(), mc);
+ }
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbdumpmap.h b/src/ToolBox/superpmi/mcs/verbdumpmap.h
new file mode 100644
index 0000000000..129b8d8440
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumpmap.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbDumpMap.h - verb that dumps a map of index to function name for an MC file
+//----------------------------------------------------------
+#ifndef _verbDumpMap
+#define _verbDumpMap
+
+class verbDumpMap
+{
+public:
+ static int DoWork(const char *nameofInput);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbdumptoc.cpp b/src/ToolBox/superpmi/mcs/verbdumptoc.cpp
new file mode 100644
index 0000000000..2837f0bf3e
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumptoc.cpp
@@ -0,0 +1,32 @@
+//
+// 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 "verbdumptoc.h"
+#include "methodcontext.h"
+#include "tocfile.h"
+#include "runtimedetails.h"
+
+int verbDumpToc::DoWork(const char *nameOfInput)
+{
+ TOCFile tf;
+
+ tf.LoadToc(nameOfInput, false);
+
+ for (size_t i = 0; i < tf.GetTocCount(); i++)
+ {
+ const TOCElement* te = tf.GetElementPtr(i);
+ printf("%4u: %016llX ", te->Number, te->Offset);
+
+ for (int j = 0; j < sizeof(te->Hash); j++)
+ {
+ printf("%02x ", te->Hash[j]);
+ }
+
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbdumptoc.h b/src/ToolBox/superpmi/mcs/verbdumptoc.h
new file mode 100644
index 0000000000..c2ea880b52
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumptoc.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbDumpToc.h - verb that dumps a MCH Table of Context file
+//----------------------------------------------------------
+#ifndef _verbDumpToc
+#define _verbDumpToc
+
+class verbDumpToc
+{
+public:
+ static int DoWork(const char *nameOfInput1);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbfracture.cpp b/src/ToolBox/superpmi/mcs/verbfracture.cpp
new file mode 100644
index 0000000000..bcc6d04e1d
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbfracture.cpp
@@ -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.
+//
+
+#include "standardpch.h"
+#include "verbfracture.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "errorhandling.h"
+#include "logging.h"
+
+int verbFracture::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR)
+{
+ int rangeSize = indexes[0];
+
+ LogVerbose("Reading from '%s' copying %d MethodContexts files into each output file of '%s'", nameOfInput, rangeSize, nameOfOutput);
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int fileCount = 0;
+ char fileName[512];
+
+ HANDLE hFileOut = INVALID_HANDLE_VALUE;
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ if ((hFileOut == INVALID_HANDLE_VALUE) || (((mci.MethodContextNumber() - 1) % rangeSize) == 0))
+ {
+ if (hFileOut != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("1st CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ hFileOut = INVALID_HANDLE_VALUE;
+ }
+ sprintf_s(fileName, 512, "%s-%0*d.mch", nameOfOutput, 5, fileCount++);
+ hFileOut = CreateFileA(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", fileName, GetLastError());
+ return -1;
+ }
+ }
+ if (stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ mc->saveToFile(hFileOut);
+ }
+
+ if (hFileOut != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ }
+
+ LogInfo("Output fileCount %d", fileCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbfracture.h b/src/ToolBox/superpmi/mcs/verbfracture.h
new file mode 100644
index 0000000000..feddbe1908
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbfracture.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbFracture.h - verb that copies N items into each child file.
+//----------------------------------------------------------
+#ifndef _verbFracture
+#define _verbFracture
+
+class verbFracture
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbildump.cpp b/src/ToolBox/superpmi/mcs/verbildump.cpp
new file mode 100644
index 0000000000..23b68da003
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbildump.cpp
@@ -0,0 +1,625 @@
+//
+// 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 "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "verbildump.h"
+
+void DumpPrimToConsoleBare(MethodContext *mc, CorInfoType prim, DWORDLONG classHandle)
+{
+ switch(prim)
+ {
+ case CORINFO_TYPE_VOID: printf("void"); return;
+ case CORINFO_TYPE_BOOL: printf("bool"); return;
+ case CORINFO_TYPE_CHAR: printf("char"); return;
+ case CORINFO_TYPE_BYTE: printf("int8"); return;
+ case CORINFO_TYPE_UBYTE: printf("unsigned int8"); return;
+ case CORINFO_TYPE_SHORT: printf("int16"); return;
+ case CORINFO_TYPE_USHORT: printf("unsigned int16"); return;
+ case CORINFO_TYPE_INT: printf("int32"); return;
+ case CORINFO_TYPE_UINT: printf("unsigned int32"); return;
+ case CORINFO_TYPE_LONG: printf("int64"); return;
+ case CORINFO_TYPE_ULONG: printf("unsigned int64"); return;
+ case CORINFO_TYPE_NATIVEINT: printf("native int"); return;
+ case CORINFO_TYPE_NATIVEUINT: printf("native unsigned int"); return;
+ case CORINFO_TYPE_FLOAT: printf("float32"); return;
+ case CORINFO_TYPE_DOUBLE: printf("float64"); return;
+// case CORINFO_TYPE_STRING: printf("string"); return;
+ case CORINFO_TYPE_PTR: printf("ptr"); return;
+ case CORINFO_TYPE_BYREF: printf("byref"); return;
+ case CORINFO_TYPE_VALUECLASS: printf("valueclass %s", mc->repGetClassName((CORINFO_CLASS_HANDLE)classHandle)); return;
+ case CORINFO_TYPE_CLASS: printf("class %s", mc->repGetClassName((CORINFO_CLASS_HANDLE)classHandle)); return;
+ case CORINFO_TYPE_REFANY: printf("refany"); return;
+ case CORINFO_TYPE_VAR: printf("var"); return;
+ default:
+ LogWarning("unknown type in PrimToString(0x%x)",prim);
+ __debugbreak();
+ return;
+ }
+}
+void DumpSigToConsoleBare(MethodContext *mc, CORINFO_SIG_INFO *pSig)
+{
+ CORINFO_ARG_LIST_HANDLE currentItem = pSig->args;
+ DWORD exceptionCode;
+
+ for(int i=0; i < (int)pSig->numArgs; i++)
+ {
+ DWORDLONG dl;
+ CorInfoTypeWithMod type = mc->repGetArgType(pSig, currentItem, (CORINFO_CLASS_HANDLE *)&dl, &exceptionCode);
+ CorInfoType cit = strip(type);
+ if (cit == CORINFO_TYPE_CLASS)
+ dl = (DWORDLONG)mc->repGetArgClass(pSig, currentItem, &exceptionCode);
+ if ((type & CORINFO_TYPE_MOD_PINNED) == CORINFO_TYPE_MOD_PINNED)
+ printf("pinned ");
+ DumpPrimToConsoleBare(mc, cit, dl);
+ currentItem = mc->repGetArgNext(currentItem);
+ if (i + 1 < (int)pSig->numArgs)
+ printf(", ");
+ }
+}
+
+
+void DumpILToConsoleBare(unsigned char *ilCode, int len)
+{
+ int i,j,k;
+ for(i=0;i<len;i++)
+ {
+ printf("IL_%04x: ",i);
+ switch(ilCode[i])
+ {
+ case 0x00:printf("nop");continue;
+ case 0x01:printf("break");continue;
+ case 0x02:printf("ldarg.0");continue;
+ case 0x03:printf("ldarg.1");continue;
+ case 0x04:printf("ldarg.2");continue;
+ case 0x05:printf("ldarg.3");continue;
+ case 0x06:printf("ldloc.0");continue;
+ case 0x07:printf("ldloc.1");continue;
+ case 0x08:printf("ldloc.2");continue;
+ case 0x09:printf("ldloc.3");continue;
+ case 0x0a:printf("stloc.0");continue;
+ case 0x0b:printf("stloc.1");continue;
+ case 0x0c:printf("stloc.2");continue;
+ case 0x0d:printf("stloc.3");continue;
+ case 0x0e: //ldarg.s X
+ printf("ldarg.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x0f: //ldarga.s X
+ printf("ldarga.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x10: //starg.s X
+ printf("starg.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x11: //ldloc.s X
+ printf("ldloc.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x12: //ldloca.s X
+ printf("ldloca.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x13: //stloc.s X
+ printf("stloc.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x14:printf("ldnull");continue;
+ case 0x15:printf("ldc.i4.m1");continue;
+ case 0x16:printf("ldc.i4.0");continue;
+ case 0x17:printf("ldc.i4.1");continue;
+ case 0x18:printf("ldc.i4.2");continue;
+ case 0x19:printf("ldc.i4.3");continue;
+ case 0x1a:printf("ldc.i4.4");continue;
+ case 0x1b:printf("ldc.i4.5");continue;
+ case 0x1c:printf("ldc.i4.6");continue;
+ case 0x1d:printf("ldc.i4.7");continue;
+ case 0x1e:printf("ldc.i4.8");continue;
+ case 0x1f: //ldc.i4.s X
+ printf("ldc.i4.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x20: //ldc.i4 XXXX
+ printf("ldc.i4 0x%02x%02x%02x%02x", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x21: //ldc.i8 XXXXXXXX
+ printf("ldc.i8 0x%02x%02x%02x%02x%02x%02x%02x%02x", ilCode[i+8], ilCode[i+7], ilCode[i+6], ilCode[i+5], ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=8;
+ continue;
+ case 0x22: //ldc.r4 XXXX
+ printf("ldc.r4 float32(0x%02x%02x%02x%02x)", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x23: //ldc.r8 XXXXXXXX
+ printf("ldc.r8 float64(0x%02x%02x%02x%02x%02x%02x%02x%02x)", ilCode[i+8], ilCode[i+7], ilCode[i+6], ilCode[i+5], ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=8;
+ continue;
+ case 0x25:printf("dup");continue;
+ case 0x26:printf("pop");continue;
+ case 0x27: //JMP <T>
+ printf("jmp <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x28: //call <T>
+ printf("call <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x29: //calli <T>
+ printf("calli <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x2a:printf("ret");continue;
+ case 0x2b: //br.s X
+ printf("br.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2c: //brfalse.s X
+ printf("brfalse.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2d: //brtrue.s X
+ printf("brtrue.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2e: //beq.s X
+ printf("beq.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2f: //bgt.s X
+ printf("bgt.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x30: //bgt.s X
+ printf("bgt.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x31: //ble.s X
+ printf("ble.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x32: //blt.s X
+ printf("blt.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x33: //bne.un.s X
+ printf("bne.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x34: //bge.un.s X
+ printf("bge.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x35: //bgt.un.s X
+ printf("bgt.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x36: //ble.un.s X
+ printf("ble.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x37: //blt.un.s X
+ printf("blt.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x38: //br XXXX
+ printf("br IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x39: //brfalse XXXX
+ printf("brfalse IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3a: //brtrue XXXX
+ printf("brtrue IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3b: //beq XXXX
+ printf("beq IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3c: //bgt XXXX
+ printf("bgt IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3d: //bgt XXXX
+ printf("bgt IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3e: //ble XXXX
+ printf("ble IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3f: //blt XXXX
+ printf("blt IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x40: //bne.un XXXX
+ printf("bne.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x41: //bge.un XXXX
+ printf("bge.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x42: //bgt.un XXXX
+ printf("bgt.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x43: //ble.un XXXX
+ printf("ble.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x44: //blt.un XXXX
+ printf("blt.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x45: //switch NNNN NNNN*XXXX
+ printf("switch (0x%02x%02x%02x%02x)", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ k = (ilCode[i+4]<<24)|(ilCode[i+3]<<16)|(ilCode[i+2]<<8)|(ilCode[i+1]<<0);
+ i+=4;
+ for(j=0;j<k;j++)
+ {
+ printf(" <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ }
+ continue;
+ case 0x46:printf("ldind.i1");continue;
+ case 0x47:printf("ldind.u1");continue;
+ case 0x48:printf("ldind.i2");continue;
+ case 0x49:printf("ldind.u2");continue;
+ case 0x4a:printf("ldind.i4");continue;
+ case 0x4b:printf("ldind.u4");continue;
+ case 0x4c:printf("ldind.i8");continue;
+ case 0x4d:printf("ldind.u8");continue;
+ case 0x4e:printf("ldind.r4");continue;
+ case 0x4f:printf("ldind.r8");continue;
+ case 0x50:printf("ldind.ref");continue;
+ case 0x51:printf("stind.ref");continue;
+ case 0x52:printf("stind.i1");continue;
+ case 0x53:printf("stind.i2");continue;
+ case 0x54:printf("stind.i4");continue;
+ case 0x55:printf("stind.i8");continue;
+ case 0x56:printf("stind.r4");continue;
+ case 0x57:printf("stind.r8");continue;
+ case 0x58:printf("add");continue;
+ case 0x59:printf("sub");continue;
+ case 0x5a:printf("mul");continue;
+ case 0x5b:printf("div");continue;
+ case 0x5c:printf("div.un");continue;
+ case 0x5d:printf("rem");continue;
+ case 0x5e:printf("rem.un");continue;
+ case 0x5f:printf("and");continue;
+ case 0x60:printf("or");continue;
+ case 0x61:printf("xor");continue;
+ case 0x62:printf("shl");continue;
+ case 0x63:printf("shr");continue;
+ case 0x64:printf("shr.un");continue;
+ case 0x65:printf("neg");continue;
+ case 0x66:printf("not");continue;
+ case 0x67:printf("conv.i1");continue;
+ case 0x68:printf("conv.i2");continue;
+ case 0x69:printf("conv.i4");continue;
+ case 0x6a:printf("conv.i8");continue;
+ case 0x6b:printf("conv.r4");continue;
+ case 0x6c:printf("conv.r8");continue;
+ case 0x6d:printf("conv.u4");continue;
+ case 0x6e:printf("conv.u8");continue;
+ case 0x6f: //callvirt <T>
+ printf("callvirt <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x70: //cpobj <T>
+ printf("cpobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x71: //ldobj <T>
+ printf("ldobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x72: //ldstr <T>
+ printf("ldstr <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x73: //newobj <T>
+ printf("newobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x74: //castclass <T>
+ printf("castclass <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x75: //isinst <T>
+ printf("isinst <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x76:printf("conv.r.un");continue;
+ case 0x79: //unbox <T>
+ printf("unbox <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7a:printf("throw");continue;
+ case 0x7b: //ldfld <T>
+ printf("ldfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7c: //ldflda <T>
+ printf("ldflda <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7d: //stfld <T>
+ printf("stfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7e: //ldsfld <T>
+ printf("ldsfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7f: //ldsflda <T>
+ printf("ldsflda <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x80: //stsfld <T>
+ printf("stsfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x81: //stobj <T>
+ printf("stobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x82:printf("conv.ovf.i1.un");continue;
+ case 0x83:printf("conv.ovf.i2.un");continue;
+ case 0x84:printf("conv.ovf.i4.un");continue;
+ case 0x85:printf("conv.ovf.i8.un");continue;
+ case 0x86:printf("conv.ovf.u1.un");continue;
+ case 0x87:printf("conv.ovf.u2.un");continue;
+ case 0x88:printf("conv.ovf.u4.un");continue;
+ case 0x89:printf("conv.ovf.u8.un");continue;
+ case 0x8a:printf("conv.ovf.i.un");continue;
+ case 0x8b:printf("conv.ovf.u.un");continue;
+ case 0x8c: //box <T>
+ printf("box <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x8d: //newarr <T>
+ printf("newarr <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x8e:printf("ldlen");continue;
+ case 0x8f:printf("ldelema <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x90:printf("ldelem.i1");continue;
+ case 0x91:printf("ldelem.u1");continue;
+ case 0x92:printf("ldelem.i2");continue;
+ case 0x93:printf("ldelem.u2");continue;
+ case 0x94:printf("ldelem.i4");continue;
+ case 0x95:printf("ldelem.u4");continue;
+ case 0x96:printf("ldelem.i8");continue;
+ case 0x97:printf("ldelem.i");continue;
+ case 0x98:printf("ldelem.r4");continue;
+ case 0x99:printf("ldelem.r8");continue;
+ case 0x9a:printf("ldelem.ref");continue;
+ case 0x9b:printf("stelem.i");continue;
+ case 0x9c:printf("stelem.i1");continue;
+ case 0x9d:printf("stelem.i2");continue;
+ case 0x9e:printf("stelem.i4");continue;
+ case 0x9f:printf("stelem.i8");continue;
+ case 0xa0:printf("stelem.r4");continue;
+ case 0xa1:printf("stelem.r8");continue;
+ case 0xa2:printf("stelem.ref");continue;
+ case 0xa3:printf("stelem <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xa4:printf("stelem <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xa5:printf("unbox.any <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xb3:printf("conv.ovf.i1");continue;
+ case 0xb4:printf("conv.ovf.u1");continue;
+ case 0xb5:printf("conv.ovf.i2");continue;
+ case 0xb6:printf("conv.ovf.u2");continue;
+ case 0xb7:printf("conv.ovf.i4");continue;
+ case 0xb8:printf("conv.ovf.u4");continue;
+ case 0xb9:printf("conv.ovf.i8");continue;
+ case 0xba:printf("conv.ovf.u8");continue;
+ case 0xc2: //refanyval <T>
+ printf("refanyval <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xc3:printf("ckfinite");continue;
+ case 0xc6: //mkrefany <T>
+ printf("mkrefany <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xd0: //ldtoken <T>
+ printf("ldtoken <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xd1:printf("conv.u2");continue;
+ case 0xd2:printf("conv.u1");continue;
+ case 0xd3:printf("conv.i");continue;
+ case 0xd4:printf("conv.ovf.i");continue;
+ case 0xd5:printf("conv.ovf.u");continue;
+ case 0xd6:printf("add.ovf");continue;
+ case 0xd7:printf("add.ovf.un");continue;
+ case 0xd8:printf("mul.ovf");continue;
+ case 0xd9:printf("mul.ovf.un");continue;
+ case 0xda:printf("sub.ovf");continue;
+ case 0xdb:printf("sub.ovf.un");continue;
+ case 0xdc:printf("endfinally");continue;
+ case 0xdd: //leave XXXX
+ printf("leave 0x%02x%02x%02x%02x", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xde: //leave.s X
+ printf("leave 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0xdf:printf("stind.i");continue;
+ case 0xe0:printf("conv.u");continue;
+ case 0xfe:
+ i++;
+ switch(ilCode[i])
+ {
+ case 0x00:printf("arglist");continue;
+ case 0x01:printf("ceq");continue;
+ case 0x02:printf("cgt");continue;
+ case 0x03:printf("cgt.un");continue;
+ case 0x04:printf("clt");continue;
+ case 0x05:printf("clt.un");continue;
+ case 0x06: //ldftn <T>
+ printf("ldftn <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x07: //ldvirtftn <T>
+ printf("ldvirtftn <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x09: //ldarg XX
+ printf("ldarg 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0a: //ldarga XX
+ printf("ldarga 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0b: //starg XX
+ printf("starg 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0c: //ldloc XX
+ printf("ldloc 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0d: //ldloca XX
+ printf("ldloca 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0e: //stloc XX
+ printf("stloc 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0f:printf("localloc");continue;
+ case 0x11:printf("endfilter");continue;
+ case 0x12: //unaligned X
+ printf("unaligned. 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x13:printf("volatile.");continue;
+ case 0x14:printf("tail.");continue;
+ case 0x15: //initobj <T>
+ printf("initobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x16://incomplete?
+ printf("constrained. <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x17:printf("cpblk");continue;
+ case 0x18:printf("initblk");continue;
+ case 0x19:printf("no.");continue; //incomplete?
+ case 0x1a:printf("rethrow");continue;
+ case 0x1c: //sizeof <T>
+ printf("sizeof <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x1d:printf("refanytype");continue;
+ default:
+ LogError("unknown ilCode 0xfe%2x at offset %d in MethodGen::PrettyPrint", ilCode[i], i);
+ break;
+ }
+ default:
+ LogError("unknown ilCode 0x%02x at offset %d in MethodGen::PrettyPrint", ilCode[i], i);
+ break;
+ }
+ printf("\n");
+ }
+}
+char * DumpAttributeToConsoleBare(DWORD attribute)
+{
+ const char *s_static = "static";
+ const char *s_dontInline = "$dontInline ";
+ const char *s_constructor = "$constructor";
+ const char *s_cfnw = "$noSecurityWrap";
+
+#define ifPrint(s,t) else if((s&attribute)==s) {printf(t); printf(" ");}
+
+ if(0);
+ ifPrint(CORINFO_FLG_STATIC, s_static)
+ ifPrint(CORINFO_FLG_DONT_INLINE, s_dontInline)
+ ifPrint(CORINFO_FLG_CONSTRUCTOR, s_constructor)
+ ifPrint(CORINFO_FLG_NOSECURITYWRAP, s_cfnw)
+ else
+ {
+ LogError("unknown attribute %x", attribute);
+ __debugbreak();
+ }
+ return nullptr;
+
+#undef ifPrint
+}
+
+void DumpIL(MethodContext *mc)
+{
+ CORINFO_METHOD_INFO cmi;
+ unsigned int flags = 0;
+
+ mc->repCompileMethod(&cmi, &flags);
+
+ const char *moduleName = nullptr;
+ const char *methodName = mc->repGetMethodName(cmi.ftn, &moduleName);
+ const char *className = mc->repGetClassName(mc->repGetMethodClass(cmi.ftn));
+
+ printf("// ProcessName - '%s'\n", mc->cr->repProcessName());
+ printf(".assembly extern mscorlib{}\n");
+ printf(".assembly %s{}\n", moduleName);
+ printf(".class %s\n", className);
+ printf("{\n");
+ printf(" .method ");
+ DumpAttributeToConsoleBare(mc->repGetMethodAttribs(cmi.ftn));
+ DumpPrimToConsoleBare(mc, cmi.args.retType, (DWORDLONG)cmi.args.retTypeClass);
+ printf(" %s(", methodName);
+ DumpSigToConsoleBare(mc, &cmi.args);
+ printf(")\n");
+ printf(" {\n");
+ printf(" .maxstack %u\n", cmi.maxStack);
+ printf(" .locals%s(", (((cmi.options&CORINFO_OPT_INIT_LOCALS)==CORINFO_OPT_INIT_LOCALS)?" init ":" "));
+ DumpSigToConsoleBare(mc, &cmi.locals);
+ printf(")\n");
+ DumpILToConsoleBare(cmi.ILCode, cmi.ILCodeSize);
+ printf(" }\n");
+ printf("}\n");
+}
+
+int verbILDump::DoWork(const char *nameOfInput, int indexCount, const int *indexes)
+{
+ LogVerbose("// Reading from '%s' dumping raw IL for MC Indexes to console", nameOfInput);
+
+ MethodContextIterator mci(indexCount, indexes);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int dumpedCount = 0;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ DumpIL(mc);
+ dumpedCount++;
+ }
+
+ LogInfo("// Dumped %d", dumpedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbildump.h b/src/ToolBox/superpmi/mcs/verbildump.h
new file mode 100644
index 0000000000..e947fcf40c
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbildump.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbILDump.h - verb that attempts to dump the raw IL for a MC
+//----------------------------------------------------------
+#ifndef _verbILDump
+#define _verbILDump
+
+#include "methodcontext.h"
+
+class verbILDump
+{
+public:
+ static int DoWork(const char *nameOfInput1, int indexCount, const int *indexes);
+};
+
+void DumpPrimToConsoleBare(MethodContext *mc, CorInfoType prim, DWORDLONG classHandle);
+void DumpSigToConsoleBare(MethodContext *mc, CORINFO_SIG_INFO *pSig);
+char * DumpAttributeToConsoleBare(DWORD attribute);
+
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbinteg.cpp b/src/ToolBox/superpmi/mcs/verbinteg.cpp
new file mode 100644
index 0000000000..9b1057769a
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbinteg.cpp
@@ -0,0 +1,37 @@
+//
+// 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 "verbinteg.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+int verbInteg::DoWork(const char *nameOfInput)
+{
+ LogVerbose("Checking the integrity of '%s'", nameOfInput);
+
+ SimpleTimer st2;
+ st2.Start();
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ // Nothing to do except load the current one.
+ }
+
+ st2.Stop();
+ LogInfo("Checked the integrity of %d methodContexts at %d per second",
+ mci.MethodContextNumber(), (int)((double)mci.MethodContextNumber() / st2.GetSeconds()));
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbinteg.h b/src/ToolBox/superpmi/mcs/verbinteg.h
new file mode 100644
index 0000000000..d45ea15307
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbinteg.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbInteg.h - verb that checks the integrity of a MC file
+//----------------------------------------------------------
+#ifndef _verbInteg
+#define _verbInteg
+
+class verbInteg
+{
+public:
+ static int DoWork(const char *nameofInput);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbmerge.cpp b/src/ToolBox/superpmi/mcs/verbmerge.cpp
new file mode 100644
index 0000000000..c4acfd8769
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbmerge.cpp
@@ -0,0 +1,470 @@
+//
+// 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 "verbmerge.h"
+#include "simpletimer.h"
+#include "logging.h"
+
+// Do reads/writes in large 256MB chunks.
+#define BUFFER_SIZE 0x10000000
+
+// MergePathStrings: take two file system path components, compose them together, and return the merged pathname string.
+// The caller must delete the returned string with delete[].
+//
+// static
+char* verbMerge::MergePathStrings(const char* dir, const char* file)
+{
+ size_t dirlen = strlen(dir);
+ size_t filelen = strlen(file);
+ size_t newlen = dirlen + 1 /* slash */ + filelen + 1 /* null */;
+ char* newpath = new char[newlen];
+ strcpy(newpath, dir);
+ strcat(newpath, DIRECTORY_SEPARATOR_STR_A);
+ strcat(newpath, file);
+ return newpath;
+}
+
+// AppendFile: append the file named by 'fileName' to the output file referred to by 'hFileOut'. The 'hFileOut'
+// handle is assumed to be open, and the file position is assumed to be at the correct spot for writing, to append.
+//
+// 'buffer' is memory that can be used to do reading/buffering.
+//
+// static
+int verbMerge::AppendFile(HANDLE hFileOut, const char* fileName, unsigned char* buffer, size_t bufferSize)
+{
+ int result = 0; // default to zero == success
+
+ LogInfo("Appending file '%s'", fileName);
+
+ HANDLE hFileIn = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileIn == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input file '%s'. GetLastError()=%u", fileName, GetLastError());
+ return -1;
+ }
+
+ LARGE_INTEGER fileSize;
+ if (GetFileSizeEx(hFileIn, &fileSize) == 0)
+ {
+ LogError("GetFileSizeEx on '%s' failed. GetLastError()=%u", fileName, GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+
+ for (LONGLONG offset = 0; offset < fileSize.QuadPart; offset += bufferSize)
+ {
+ DWORD bytesRead = -1;
+ BOOL res = ReadFile(hFileIn, buffer, (DWORD)bufferSize, &bytesRead, nullptr);
+ if (!res)
+ {
+ LogError("Failed to read '%s' from offset %lld. GetLastError()=%u", fileName, offset, GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+ DWORD bytesWritten = -1;
+ BOOL res2 = WriteFile(hFileOut, buffer, bytesRead, &bytesWritten, nullptr);
+ if (!res2)
+ {
+ LogError("Failed to write output file at offset %lld. GetLastError()=%u", offset, GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+ if (bytesRead != bytesWritten)
+ {
+ LogError("Failed to read/write matching bytes %u!=%u", bytesRead, bytesWritten);
+ result = -1;
+ goto CLEAN_UP;
+ }
+ }
+
+CLEAN_UP:
+
+ if (CloseHandle(hFileIn) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ result = -1;
+ }
+
+ return result;
+}
+
+// Return true if this is a directory
+//
+// static
+bool verbMerge::DirectoryFilterDirectories(WIN32_FIND_DATAA* findData)
+{
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ {
+ // It's a directory. See if we want to exclude it because of other reasons, such as:
+ // 1. reparse points: avoid the possibility of loops
+ // 2. system directories
+ // 3. hidden directories
+ // 4. "." or ".."
+
+#ifndef FEATURE_PAL // FILE_ATTRIBUTE_REPARSE_POINT is not defined in the PAL
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
+ return false;
+#endif // !FEATURE_PAL
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0)
+ return false;
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)
+ return false;
+
+ if (strcmp(findData->cFileName, ".") == 0)
+ return false;
+ if (strcmp(findData->cFileName, "..") == 0)
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+// Return true if this is a file.
+//
+// static
+bool verbMerge::DirectoryFilterFile(WIN32_FIND_DATAA* findData)
+{
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ // This is not a directory, so it must be a file.
+ return true;
+ }
+
+ return false;
+}
+
+// static
+int __cdecl verbMerge::WIN32_FIND_DATAA_qsort_helper(const void* p1, const void* p2)
+{
+ const WIN32_FIND_DATAA* file1 = (WIN32_FIND_DATAA*)p1;
+ const WIN32_FIND_DATAA* file2 = (WIN32_FIND_DATAA*)p2;
+ return strcmp(file1->cFileName, file2->cFileName);
+}
+
+// Enumerate a directory for the files specified by "searchPattern". For each element in the directory,
+// pass it to the filter function. If the filter returns true, we keep it, otherwise we ignore it. Return
+// an array of information for the files that we kept, sorted by filename.
+//
+// Returns 0 on success, non-zero on failure.
+// If success, fileArray and elemCount are set.
+//
+// static
+int verbMerge::FilterDirectory(const char* searchPattern, DirectoryFilterFunction_t filter, /* out */ WIN32_FIND_DATAA** ppFileArray, int* pElemCount)
+{
+ // First, build up a list, then create an array and sort it after we know how many elements there are.
+ struct findDataList
+ {
+ findDataList(WIN32_FIND_DATAA* newFindData, findDataList* newNext)
+ : findData(*newFindData)
+ , next(newNext)
+ {
+ }
+
+ static void DeleteList(findDataList* root)
+ {
+ for (findDataList* loop = root; loop != nullptr; )
+ {
+ findDataList* tmp = loop;
+ loop = loop->next;
+ delete tmp;
+ }
+ }
+
+ WIN32_FIND_DATAA findData;
+ findDataList* next;
+ };
+
+ WIN32_FIND_DATAA* retArray = nullptr;
+ findDataList* first = nullptr;
+
+ int result = 0; // default to zero == success
+ int elemCount = 0;
+
+ // NOTE: this function only works on Windows 7 and later.
+ WIN32_FIND_DATAA findData;
+ HANDLE hSearch;
+#ifdef FEATURE_PAL
+ // PAL doesn't have FindFirstFileEx(). So just use FindFirstFile(). The only reason we use
+ // the Ex version is potentially better performance (don't populate short name; use large fetch),
+ // not functionality.
+ hSearch = FindFirstFileA(searchPattern, &findData);
+#else // !FEATURE_PAL
+ hSearch = FindFirstFileExA(searchPattern,
+ FindExInfoBasic, // We don't care about the short names
+ &findData,
+ FindExSearchNameMatch, // standard name matching
+ NULL,
+ FIND_FIRST_EX_LARGE_FETCH);
+#endif // !FEATURE_PAL
+
+ if (hSearch == INVALID_HANDLE_VALUE)
+ {
+ DWORD lastErr = GetLastError();
+ if (lastErr == ERROR_FILE_NOT_FOUND)
+ {
+ // This is ok; there was just nothing matching the pattern.
+ }
+ else
+ {
+ LogError("Failed to find pattern '%s'. GetLastError()=%u", searchPattern, GetLastError());
+ }
+ goto CLEAN_UP;
+ }
+
+ while (true)
+ {
+ // Do something with findData...
+
+ if (filter(&findData))
+ {
+ // Prepend it to the list.
+ first = new findDataList(&findData, first);
+ ++elemCount;
+ }
+
+ BOOL ok = FindNextFileA(hSearch, &findData);
+ if (!ok)
+ {
+ DWORD err = GetLastError();
+ if (err != ERROR_NO_MORE_FILES)
+ {
+ LogError("Failed to find next file. GetLastError()=%u", GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+ break;
+ }
+ }
+
+ // Now sort the list. Create an array to put everything in.
+
+ int i;
+
+ retArray = new WIN32_FIND_DATAA[elemCount];
+ i = 0;
+ for (findDataList* tmp = first; tmp != nullptr; tmp = tmp->next)
+ {
+ retArray[i++] = tmp->findData;
+ }
+
+ qsort(retArray, elemCount, sizeof(retArray[0]), WIN32_FIND_DATAA_qsort_helper);
+
+CLEAN_UP:
+
+ findDataList::DeleteList(first);
+
+ if ((hSearch != INVALID_HANDLE_VALUE) && !FindClose(hSearch))
+ {
+ LogError("Failed to close search handle. GetLastError()=%u", GetLastError());
+ delete[] retArray;
+ return -1;
+ }
+
+ *ppFileArray = retArray;
+ *pElemCount = elemCount;
+ return result;
+}
+
+// Append all files in the given directory matching the file pattern.
+//
+// static
+int verbMerge::AppendAllInDir(HANDLE hFileOut, const char* dir, const char* file, unsigned char* buffer, size_t bufferSize, bool recursive, /* out */ LONGLONG* size)
+{
+ int result = 0; // default to zero == success
+ LONGLONG totalSize = 0;
+
+ char* searchPattern = MergePathStrings(dir, file);
+
+ WIN32_FIND_DATAA* fileArray = nullptr;
+ int elemCount = 0;
+ result = FilterDirectory(searchPattern, DirectoryFilterFile, &fileArray, &elemCount);
+ if (result != 0)
+ {
+ goto CLEAN_UP;
+ }
+
+ for (int i = 0; i < elemCount; i++)
+ {
+ const WIN32_FIND_DATAA& findData = fileArray[i];
+ char* fileFullPath = MergePathStrings(dir, findData.cFileName);
+
+ // Is it zero length? If so, skip it.
+ if ((findData.nFileSizeLow == 0) && (findData.nFileSizeHigh == 0))
+ {
+ LogInfo("Skipping zero-length file '%s'", fileFullPath);
+ }
+ else
+ {
+ result = AppendFile(hFileOut, fileFullPath, buffer, bufferSize);
+ if (result != 0)
+ {
+ // Error was already logged.
+ delete[] fileFullPath;
+ goto CLEAN_UP;
+ }
+ }
+
+ delete[] fileFullPath;
+ totalSize += ((LONGLONG)findData.nFileSizeHigh << 32) + (LONGLONG)findData.nFileSizeLow;
+ }
+
+ // If we need to recurse, then search the directory again for directories, and recursively search each one.
+ if (recursive)
+ {
+ delete[] searchPattern;
+ delete[] fileArray;
+
+ searchPattern = MergePathStrings(dir, "*");
+ fileArray = nullptr;
+ elemCount = 0;
+ result = FilterDirectory(searchPattern, DirectoryFilterDirectories, &fileArray, &elemCount);
+ if (result != 0)
+ {
+ goto CLEAN_UP;
+ }
+
+ LONGLONG dirSize = 0;
+ for (int i = 0; i < elemCount; i++)
+ {
+ const WIN32_FIND_DATAA& findData = fileArray[i];
+
+ char* fileFullPath = MergePathStrings(dir, findData.cFileName);
+ result = AppendAllInDir(hFileOut, fileFullPath, file, buffer, bufferSize, recursive, &dirSize);
+ delete[] fileFullPath;
+ if (result != 0)
+ {
+ // Error was already logged.
+ goto CLEAN_UP;
+ }
+
+ totalSize += dirSize;
+ }
+ }
+
+CLEAN_UP:
+
+ delete[] searchPattern;
+ delete[] fileArray;
+
+ if (result == 0)
+ {
+ *size = totalSize;
+ }
+
+ return result;
+}
+
+// Merge a set of .MC files into an output .MCH file. The .MC files to merge are given as a pattern, one of:
+// 1. *.mc -- simple pattern. Assumes current directory.
+// 2. foo\bar\*.mc -- simple pattern with relative directory.
+// 3. c:\foo\bar\baz\*.mc -- simple pattern with full path.
+// If no pattern is given, then the last component of the path is expected to be a directory name, and the pattern is assumed to be "*" (that is, all files).
+//
+// If "recursive" is true, then the pattern is searched for in the specified directory (or implicit current directory) and
+// all sub-directories, recursively.
+//
+// static
+int verbMerge::DoWork(const char* nameOfOutputFile, const char* pattern, bool recursive)
+{
+ int result = 0; // default to zero == success
+ SimpleTimer st1;
+
+ LogInfo("Merging files matching '%s' into '%s'", pattern, nameOfOutputFile);
+
+ HANDLE hFileOut = CreateFileA(nameOfOutputFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", nameOfOutputFile, GetLastError());
+ return -1;
+ }
+
+ // Create a buffer we can use for all the copies.
+ unsigned char* buffer = new unsigned char[BUFFER_SIZE];
+ char* dir = nullptr;
+ const char* file = nullptr;
+
+ dir = _strdup(pattern);
+ char* lastSlash = strrchr(dir, DIRECTORY_SEPARATOR_CHAR_A);
+ if (lastSlash == NULL)
+ {
+ // The user may have passed a relative path without a slash, or the current directory.
+ // If there is a wildcard, we use it as the file pattern. If there isn't, we assume it's a relative directory name
+ // and use it as a directory, with "*" as the file pattern.
+ const char* wildcard = strchr(dir, '*');
+ if (wildcard == NULL)
+ {
+ file = "*";
+ }
+ else
+ {
+ file = dir;
+ dir = _strdup(".");
+ }
+ }
+ else
+ {
+ const char* wildcard = strchr(lastSlash, '*');
+ if (wildcard == NULL)
+ {
+ file = "*";
+
+ // Minor canonicalization: if there is a trailing last slash, strip it (probably should do this in a loop...)
+ if (*(lastSlash + 1) == '\0')
+ {
+ *lastSlash = '\0';
+ }
+ }
+ else
+ {
+ // ok, we found a wildcard after the last slash, so assume there is a pattern. Strip it at the last slash.
+ *lastSlash = '\0';
+ file = lastSlash + 1;
+ }
+ }
+
+ LONGLONG totalSize = 0;
+ LONGLONG dirSize = 0;
+
+ st1.Start();
+
+ result = AppendAllInDir(hFileOut, dir, file, buffer, BUFFER_SIZE, recursive, &dirSize);
+ if (result != 0)
+ {
+ goto CLEAN_UP;
+ }
+ totalSize += dirSize;
+
+ st1.Stop();
+
+ LogInfo("Read/Wrote %lld MB @ %4.2f MB/s.",
+ totalSize/(1000*1000),
+ (((double)totalSize)/(1000*1000))/st1.GetSeconds()); //yes yes.. http://en.wikipedia.org/wiki/Megabyte_per_second#Megabyte_per_second
+
+CLEAN_UP:
+
+ free((void*)dir);
+ delete[] buffer;
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ result = -1;
+ }
+
+ if (result != 0)
+ {
+ // There was a failure. Delete the output file, to avoid leaving some half-created file.
+ BOOL ok = DeleteFileA(nameOfOutputFile);
+ if (!ok)
+ {
+ LogError("Failed to delete file after MCS /merge failed. GetLastError()=%u", GetLastError());
+ }
+ }
+
+ return result;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbmerge.h b/src/ToolBox/superpmi/mcs/verbmerge.h
new file mode 100644
index 0000000000..1d612426f3
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbmerge.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.
+//
+
+//----------------------------------------------------------
+// verbMerge.h - verb that merges multiple .MC into one .MCH file
+//----------------------------------------------------------
+#ifndef _verbMerge
+#define _verbMerge
+
+class verbMerge
+{
+public:
+ static int DoWork(const char* nameOfOutputFile, const char* pattern, bool recursive);
+
+private:
+ typedef bool (*DirectoryFilterFunction_t)(WIN32_FIND_DATAA*);
+ static bool DirectoryFilterDirectories(WIN32_FIND_DATAA* findData);
+ static bool DirectoryFilterFile(WIN32_FIND_DATAA* findData);
+ static int __cdecl WIN32_FIND_DATAA_qsort_helper(const void* p1, const void* p2);
+ static int FilterDirectory(const char* searchPattern, DirectoryFilterFunction_t filter, /* out */ WIN32_FIND_DATAA** ppFileArray, int* pElemCount);
+
+ static char* MergePathStrings(const char* dir, const char* file);
+
+ static int AppendFile(HANDLE hFileOut, const char* fileName, unsigned char* buffer, size_t bufferSize);
+ static int AppendAllInDir(HANDLE hFileOut, const char* dir, const char* file, unsigned char* buffer, size_t bufferSize, bool recursive, /* out */ LONGLONG* size);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbremovedup.cpp b/src/ToolBox/superpmi/mcs/verbremovedup.cpp
new file mode 100644
index 0000000000..eca5dc1b09
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbremovedup.cpp
@@ -0,0 +1,145 @@
+//
+// 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 "verbremovedup.h"
+#include "simpletimer.h"
+#include "lightweightmap.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+//We use a hash to limit the number of comparisons we need to do.
+//The first level key to our hash map is ILCodeSize and the second
+//level map key is just an index and the value is an existing MC Hash.
+
+LightWeightMap<int, DenseLightWeightMap<char *> *> *inFile = nullptr;
+
+bool unique(MethodContext *mc)
+{
+ if (inFile == nullptr)
+ inFile = new LightWeightMap<int, DenseLightWeightMap<char *> *>();
+
+ CORINFO_METHOD_INFO newInfo;
+ unsigned newFlags = 0;
+ mc->repCompileMethod(&newInfo, &newFlags);
+
+ char *md5Buff = new char[MD5_HASH_BUFFER_SIZE];
+ mc->dumpMethodMD5HashToBuffer(md5Buff, MD5_HASH_BUFFER_SIZE);
+
+ if (inFile->GetIndex(newInfo.ILCodeSize) == -1)
+ inFile->Add(newInfo.ILCodeSize, new DenseLightWeightMap<char *>());
+
+ DenseLightWeightMap<char *> *ourRank = inFile->Get(newInfo.ILCodeSize);
+
+ for (int i = 0; i < (int)ourRank->GetCount(); i++)
+ {
+ char *md5Buff2 = ourRank->Get(i);
+
+ if (strncmp(md5Buff, md5Buff2, MD5_HASH_BUFFER_SIZE) == 0)
+ {
+ delete[] md5Buff;
+ return false;
+ }
+ }
+
+ ourRank->Append(md5Buff);
+ return true;
+}
+
+LightWeightMap<int, DenseLightWeightMap<MethodContext *> *> *inFileLegacy = nullptr;
+
+bool uniqueLegacy(MethodContext *mc)
+{
+ if (inFileLegacy == nullptr)
+ inFileLegacy = new LightWeightMap<int, DenseLightWeightMap<MethodContext *> *>();
+
+ CORINFO_METHOD_INFO newInfo;
+ unsigned newFlags = 0;
+ mc->repCompileMethod(&newInfo, &newFlags);
+
+ if (inFileLegacy->GetIndex(newInfo.ILCodeSize) == -1)
+ inFileLegacy->Add(newInfo.ILCodeSize, new DenseLightWeightMap<MethodContext *>());
+
+ DenseLightWeightMap<MethodContext *> *ourRank = inFileLegacy->Get(newInfo.ILCodeSize);
+
+ for (int i = 0; i < (int)ourRank->GetCount(); i++)
+ {
+ MethodContext *scratch = ourRank->Get(i);
+ if (mc->Equal(scratch))
+ {
+ return false;
+ }
+ }
+
+ // We store the MethodContext in our map.
+ ourRank->Append(mc);
+ return true;
+}
+
+int verbRemoveDup::DoWork(const char *nameOfInput, const char *nameOfOutput, bool stripCR, bool legacyCompare)
+{
+ LogVerbose("Removing duplicates from '%s', writing to '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, 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", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.CurrentTakeOwnership();
+ if (stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ if (legacyCompare)
+ {
+ if (uniqueLegacy(mc))
+ {
+ mc->saveToFile(hFileOut);
+ savedCount++;
+
+ // In this case, for the legacy comparer, it has placed the 'mc' in the 'inFileLegacy' table, so we can't delete it.
+ }
+ else
+ {
+ delete mc; // we no longer need this
+ }
+ }
+ else
+ {
+ if (unique(mc))
+ {
+ mc->saveToFile(hFileOut);
+ savedCount++;
+ }
+ delete mc; // we no longer need this
+ }
+ }
+
+ // We're leaking 'inFile' or 'inFileLegacy', but the process is going away, so it shouldn't matter.
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbremovedup.h b/src/ToolBox/superpmi/mcs/verbremovedup.h
new file mode 100644
index 0000000000..ab0cb98692
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbremovedup.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbRemoveDup.h - verb that attempts to remove dups
+//----------------------------------------------------------
+#ifndef _verbRemoveDup
+#define _verbRemoveDup
+
+class verbRemoveDup
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, bool stripCR, bool legacyCompare);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbsmarty.cpp b/src/ToolBox/superpmi/mcs/verbsmarty.cpp
new file mode 100644
index 0000000000..9b881cdcbf
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbsmarty.cpp
@@ -0,0 +1,96 @@
+//
+// 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 "verbsmarty.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+//
+// Constructs a new verbSmarty.
+//
+// Arguments:
+// hFile - A handle to the output file that we are writing the Smarty Test IDs
+//
+// Assumptions:
+// hFile refers to an open and writeable file handle.
+//
+verbSmarty::verbSmarty(HANDLE hFile)
+{
+ m_hFile=hFile;
+}
+
+//
+// Dumps the Smarty TestID to file
+//
+// Arguments:
+// testID - Smarty Test ID
+//
+void verbSmarty::DumpTestInfo(int testID)
+{
+ #define bufflen 4096
+ DWORD bytesWritten;
+
+ char buff[bufflen];
+ int buff_offset = 0;
+ ZeroMemory(buff, bufflen * sizeof(char));
+
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, "%i\r\n", testID);
+ WriteFile(m_hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+}
+
+
+int verbSmarty::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes)
+{
+ LogVerbose("Reading from '%s' reading Smarty ID for the Mc Indexes and writing into '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(indexCount, indexes);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ verbSmarty *verbList = new verbSmarty(hFileOut);
+
+ //TODO-Cleanup: look to use toc for this
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ int testID = mc->repGetTestID();
+ if (testID != -1)
+ {
+ //write to the file
+ verbList->DumpTestInfo(testID);
+ }
+ else
+ {
+ LogError("Smarty ID not found for '%s'", mc->cr->repProcessName());
+ }
+ }
+
+ delete verbList;
+
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbsmarty.h b/src/ToolBox/superpmi/mcs/verbsmarty.h
new file mode 100644
index 0000000000..994695da79
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbsmarty.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbSmarty.h - verb that outputs Smarty test ID for mc
+//----------------------------------------------------------
+#ifndef _verbSmarty
+#define _verbSmarty
+
+class verbSmarty
+{
+public:
+ verbSmarty(HANDLE hFile);
+ void DumpTestInfo(int testID);
+ static int DoWork(const char *nameOfInput, const char *nameofOutput, int indexCount, const int *indexes);
+
+private:
+ HANDLE m_hFile;
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbstat.cpp b/src/ToolBox/superpmi/mcs/verbstat.cpp
new file mode 100644
index 0000000000..473f452f96
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstat.cpp
@@ -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.
+//
+
+#include "standardpch.h"
+#include "verbstat.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "errorhandling.h"
+
+int verbStat::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes)
+{
+ LogVerbose("Stat'ing from '%s' and writing output into '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(indexCount, indexes, true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ #define bufflen 50000
+ DWORD bytesWritten;
+ char buff[bufflen];
+ int offset = 0;
+ ZeroMemory(&buff[0], bufflen);
+ offset += sprintf_s(buff, bufflen, "Title,MC#,");
+ offset += MethodContext::dumpStatTitleToBuffer(&buff[offset], bufflen - offset);
+ buff[offset++] = 0x0d;
+ buff[offset++] = 0x0a;
+ WriteFile(hFileOut, &buff[0], offset, &bytesWritten, nullptr);
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ offset = 0;
+ ZeroMemory(&buff[0], bufflen);
+ if ((mc->cr->ProcessName != nullptr) && (mc->cr->ProcessName->GetCount() > 0))
+ {
+ const char *procname = mc->cr->repProcessName();
+ strcpy_s(&buff[offset], bufflen, procname);
+ offset += (int)strlen(procname);
+ }
+ buff[offset++] = ',';
+ offset += sprintf_s(&buff[offset], bufflen - offset, "%d,", mci.MethodContextNumber());
+ offset += mc->dumpStatToBuffer(&buff[offset], bufflen - offset);
+ buff[offset++] = 0x0d;
+ buff[offset++] = 0x0a;
+ WriteFile(hFileOut, &buff[0], offset, &bytesWritten, nullptr);
+ savedCount++;
+ }
+
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Loaded %d, Stat'd %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbstat.h b/src/ToolBox/superpmi/mcs/verbstat.h
new file mode 100644
index 0000000000..53a3a78d6f
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstat.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbStat.h - verb that
+//----------------------------------------------------------
+#ifndef _verbStat
+#define _verbStat
+
+class verbStat
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbstrip.cpp b/src/ToolBox/superpmi/mcs/verbstrip.cpp
new file mode 100644
index 0000000000..8783b1a767
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstrip.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.
+//
+
+#include "standardpch.h"
+#include "verbstrip.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "errorhandling.h"
+#include "methodcontextreader.h"
+#include "methodcontextiterator.h"
+
+// verbStrip::DoWork handles both "-copy" and "-strip". These both copy from input file to output file,
+// but treat the passed-in indexes in opposite ways.
+int verbStrip::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool strip, bool stripCR)
+{
+ if (strip)
+ return DoWorkTheOldWay(nameOfInput, nameOfOutput, indexCount, indexes, stripCR);
+ SimpleTimer *st1 = new SimpleTimer();
+
+ LogVerbose("Reading from '%s' removing Mc Indexes and writing into '%s'", nameOfInput, nameOfOutput);
+
+ int loadedCount = 0;
+ MethodContext *mc = nullptr;
+ int savedCount = 0;
+ int index = 0;
+
+ // 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(nameOfInput, indexes, indexCount);
+ if (!reader->isValid())
+ {
+ return -1;
+ }
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ if(indexCount == -1)
+ strip = true; //Copy command with no indexes listed should copy all the inputs...
+ while(true)
+ {
+ MethodContextBuffer mcb = reader->GetNextMethodContext();
+ if (mcb.Error())
+ {
+ return -1;
+ }
+ else if (mcb.allDone())
+ {
+ break;
+ }
+
+ loadedCount++;
+ if((loadedCount%500==0)&&(loadedCount>0))
+ {
+ st1->Stop();
+ LogVerbose("%2.1f%% - Loaded %d at %d per second", reader->PercentComplete(), loadedCount, (int)((double)500 / st1->GetSeconds()));
+ st1->Start();
+ }
+
+ if (!MethodContext::Initialize(loadedCount, mcb.buff, mcb.size, &mc))
+ return -1;
+
+ if(stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ mc->saveToFile(hFileOut);
+ savedCount++;
+ delete mc;
+ }
+ if(CloseHandle(hFileOut)==0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ LogInfo("Loaded %d, Saved %d", loadedCount, savedCount);
+
+ return 0;
+}
+
+
+// This is only used for "-strip".
+int verbStrip::DoWorkTheOldWay(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR)
+{
+ LogVerbose("Reading from '%s' removing MC Indexes and writing into '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+ bool write;
+ int index = 0; // Can't use MethodContextIterator indexing, since we want the opposite of that.
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ write = true; // assume we'll write it
+ if (index < indexCount)
+ {
+ if (indexes[index] == mci.MethodContextNumber())
+ {
+ index++;
+ write = false;
+ }
+ }
+
+ if (write)
+ {
+ if (stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ mc->saveToFile(hFileOut);
+ savedCount++;
+ }
+ }
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ if (index < indexCount)
+ LogWarning("Didn't use all of index count input %d < %d (i.e. didn't see MC #%d)", index, indexCount, indexes[index]);
+
+ LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbstrip.h b/src/ToolBox/superpmi/mcs/verbstrip.h
new file mode 100644
index 0000000000..9db77736a8
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstrip.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.
+//
+
+//----------------------------------------------------------
+// verbStrip.h - verb that removes a list of mc's from an MCH file
+//----------------------------------------------------------
+#ifndef _verbStrip
+#define _verbStrip
+
+class verbStrip
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes, bool strip, bool stripCR);
+ static int DoWorkTheOldWay(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbtoc.cpp b/src/ToolBox/superpmi/mcs/verbtoc.cpp
new file mode 100644
index 0000000000..a99fbf0183
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbtoc.cpp
@@ -0,0 +1,108 @@
+//
+// 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 "verbtoc.h"
+#include "methodcontext.h"
+#include "methodcontextreader.h"
+#include "methodcontextiterator.h"
+#include "simpletimer.h"
+
+class TOCElementNode
+{
+public:
+ TOCElementNode *Next;
+ TOCElement tocElement;
+
+ TOCElementNode(int number, __int64 offset)
+ : Next(nullptr)
+ , tocElement(number, offset)
+ {
+ }
+};
+
+int verbTOC::DoWork(const char *nameOfInput)
+{
+ LogVerbose("Indexing from '%s' into '%s.mct'", nameOfInput, nameOfInput);
+
+ MethodContextIterator mci;
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ TOCElementNode *head = nullptr;
+ TOCElementNode *curElem = nullptr;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ TOCElementNode *nxt = new TOCElementNode(mci.MethodContextNumber(), mci.CurrentPos());
+ mc->dumpMethodMD5HashToBuffer(nxt->tocElement.Hash, MD5_HASH_BUFFER_SIZE);
+
+ if (curElem != nullptr)
+ {
+ curElem->Next = nxt;
+ }
+ else
+ {
+ head = nxt;
+ }
+ curElem = nxt;
+ savedCount++;
+ }
+
+ size_t maxLen = strlen(nameOfInput) + 5;
+ char *nameOfOutput = (char*)_alloca(maxLen);
+ strcpy_s(nameOfOutput, maxLen, nameOfInput);
+ strcat_s(nameOfOutput, maxLen, ".mct");
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ DWORD written;
+ // Write out the signature "INDX" and then the element count
+ LARGE_INTEGER token;
+ token.u.LowPart = *(const int*)"INDX"; // cuz Type Safety is for languages that have good IO facilities
+ token.u.HighPart = savedCount;
+ if (!WriteFile(hFileOut, &token, sizeof(token), &written, nullptr) || written != sizeof(token))
+ {
+ LogError("Failed to write index header. GetLastError()=%u", GetLastError());
+ }
+
+ // Now just dump sizeof(TOCElement) byte chunks into the file.
+ // I could probably do this more efficiently, but I don't think it matters
+ DWORD chunkSize = sizeof(TOCElement);
+ for (curElem = head; curElem != nullptr; curElem = curElem->Next)
+ {
+ if (!WriteFile(hFileOut, &curElem->tocElement, chunkSize, &written, nullptr) || written != chunkSize)
+ {
+ LogError("Failed to write index element '%d'. GetLastError()=%u", curElem->tocElement.Number, GetLastError());
+ return -1;
+ }
+ }
+ // Now write out a final "INDX" to flag the end of the file...
+ if (!WriteFile(hFileOut, &token.u.LowPart, sizeof(token.u.LowPart), &written, nullptr) || (written != sizeof(token.u.LowPart)))
+ {
+ LogError("Failed to write index terminal. GetLastError()=%u", GetLastError());
+ }
+
+ LogInfo("Loaded %d, added %d to Table of Contents", mci.MethodContextNumber(), savedCount);
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbtoc.h b/src/ToolBox/superpmi/mcs/verbtoc.h
new file mode 100644
index 0000000000..7eea371191
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbtoc.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbTOC.h - verb that creates a Table of Context for a MCH file
+//----------------------------------------------------------
+#ifndef _verbTOC
+#define _verbTOC
+
+class verbTOC
+{
+public:
+ static int DoWork(const char *nameOfInput1);
+};
+#endif
diff --git a/src/ToolBox/superpmi/readme.txt b/src/ToolBox/superpmi/readme.txt
new file mode 100644
index 0000000000..8429e4f38e
--- /dev/null
+++ b/src/ToolBox/superpmi/readme.txt
@@ -0,0 +1,86 @@
+OVERVIEW
+========
+
+This directory contains the SuperPMI tool used for testing the .NET
+just-in-time (JIT) compiler.
+
+SuperPMI has two uses:
+1. Verification that a JIT code change doesn't cause any asserts.
+2. Finding test code where two JIT compilers generate different code, or
+verifying that the two compilers generate the same code.
+
+Case #1 is useful for doing quick regression checking when making a source
+code change to the JIT compiler. The process is: (a) make a JIT source code
+change, (b) run that newly built JIT through a SuperPMI run to verify no
+asserts have been introduced.
+
+Case #2 is useful for generating assembly language diffs, to help analyze the
+impact of a JIT code change.
+
+SuperPMI works in two phases: collection and playback. In the collection
+phase, the system is configured to collect SuperPMI data. Then, run any
+set of .NET managed programs. When these managed programs invoke the JIT
+compiler, SuperPMI gathers and captures all information passed between the
+JIT and its .NET host. In the playback phase, SuperPMI loads the JIT directly,
+and causes it to compile all the functions that it previously compiled,
+but using the collected data to provide answers to various questions that
+the JIT needs to ask. The .NET execution engine (EE) is not invoked at all.
+
+
+TOOLS
+==========
+
+There are two native executable tools: superpmi and mcs. There is a .NET Core
+C# program that is built as part of the coreclr repo tests build called
+superpmicollect.exe.
+
+All will show a help screen if passed -?.
+
+
+COLLECTION
+==========
+
+Set the following environment variables:
+
+ SuperPMIShimLogPath=<full path to an empty temporary directory>
+ SuperPMIShimPath=<full path to clrjit.dll, the "standalone" JIT>
+ COMPlus_AltJit=*
+ COMPlus_AltJitName=superpmi-shim-collector.dll
+
+(On Linux, use libclrjit.so and libsuperpmi-shim-collector.so. On Mac,
+use libclrjit.dylib and libsuperpmi-shim-collector.dylib.)
+
+Then, run some managed programs. When done running programs, un-set these
+variables.
+
+Now, you will have a large number of .mc files. Merge these using the mcs
+tool:
+
+ mcs -merge base.mch *.mc
+
+One benefit of SuperPMI is the ability to remove duplicated compilations, so
+on replay only unique functions are compiled. Use the following to create a
+"unique" set of functions:
+
+ mcs -removeDup -thin base.mch unique.mch
+
+Note that -thin is not required. However, it will delete all the compilation
+result collected during the collection phase, which makes the resulting MCH
+file smaller. Those compilation results are not required for playback.
+
+Use the superpmicollect.exe tool to automate and simplify this process.
+
+
+PLAYBACK
+========
+
+Once you have a merged, de-duplicated MCH collection, you can play it back
+using:
+
+ superpmi unique.mch clrjit.dll
+
+You can do this much faster by utilizing all the processors on your machine,
+and replaying in parallel, using:
+
+ superpmi -p unique.mch clrjit.dll
+
diff --git a/src/ToolBox/superpmi/superpmi-shared/.gitmirror b/src/ToolBox/superpmi/superpmi-shared/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/.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-shared/asmdumper.cpp b/src/ToolBox/superpmi/superpmi-shared/asmdumper.cpp
new file mode 100644
index 0000000000..240e228c1e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/asmdumper.cpp
@@ -0,0 +1,91 @@
+//
+// 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 "asmdumper.h"
+
+void ASMDumper::DumpToFile(HANDLE hFile, MethodContext *mc, CompileResult *cr)
+{
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+ mc->repCompileMethod(&info, &flags);
+
+ #define bufflen 4096
+ DWORD bytesWritten;
+ char buff[bufflen];
+
+ int buff_offset = 0;
+ ZeroMemory(buff, bufflen * sizeof(char));
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, ";;Generated from SuperPMI on original input '%s'",
+ cr->repProcessName());
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, "\r\n Method Name \"%s\"",
+ mc->repGetMethodName(info.ftn,nullptr));
+ WriteFile(hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+
+ ULONG hotCodeSize;
+ ULONG coldCodeSize;
+ ULONG roDataSize;
+ ULONG xcptnsCount;
+ CorJitAllocMemFlag flag;
+ unsigned char *hotCodeBlock;
+ unsigned char *coldCodeBlock;
+ unsigned char *roDataBlock;
+ void *orig_hotCodeBlock;
+ void *orig_coldCodeBlock;
+ void *orig_roDataBlock;
+
+ cr->repAllocMem(&hotCodeSize, &coldCodeSize, &roDataSize, &xcptnsCount, &flag,
+ &hotCodeBlock, &coldCodeBlock, &roDataBlock, &orig_hotCodeBlock, &orig_coldCodeBlock, &orig_roDataBlock);
+ cr->applyRelocs(hotCodeBlock, hotCodeSize, orig_hotCodeBlock);
+ cr->applyRelocs(coldCodeBlock, coldCodeSize, orig_coldCodeBlock);
+ cr->applyRelocs(roDataBlock, roDataSize, orig_roDataBlock);
+
+#ifdef USE_MSVCDIS
+
+#ifdef _TARGET_AMD64_
+ DIS *disasm = DIS::PdisNew(DIS::distX8664);
+#elif _TARGET_X86_
+ DIS *disasm = DIS::PdisNew(DIS::distX86);
+#endif
+ size_t offset = 0;
+ while (offset < hotCodeSize)
+ {
+ buff_offset = 0;
+ ZeroMemory(buff, bufflen * sizeof(char));
+
+ DIS::INSTRUCTION instr;
+ DIS::OPERAND ops[3];
+
+ size_t instrSize = disasm->CbDisassemble(0, (void *)(hotCodeBlock + offset), 15);
+ if(instrSize==0)
+ {
+ LogWarning("Zero sized instruction");
+ break;
+ }
+ disasm->FDecode(&instr, ops, 3);
+
+ wchar_t instrMnemonic[64]; // I never know how much to allocate...
+ disasm->CchFormatInstr(instrMnemonic, 64);
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, "\r\n%p %S", (void*)((size_t)orig_hotCodeBlock+offset), instrMnemonic);
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, " ; ");
+ for(unsigned int i=0;i<instrSize;i++)
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, "%02x ", *((BYTE*)(hotCodeBlock + offset + i) ));
+ WriteFile(hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+ offset += instrSize;
+ }
+
+ delete disasm;
+
+#else // !USE_MSVCDIS
+
+ buff_offset = 0;
+ ZeroMemory(buff, bufflen * sizeof(char));
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, ";; No disassembler available");
+ WriteFile(hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+
+#endif // !USE_MSVCDIS
+
+ FlushFileBuffers(hFile);
+} \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shared/asmdumper.h b/src/ToolBox/superpmi/superpmi-shared/asmdumper.h
new file mode 100644
index 0000000000..d848d60e20
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/asmdumper.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 _ASMDumper
+#define _ASMDumper
+
+#include "methodcontext.h"
+#include "compileresult.h"
+
+class ASMDumper
+{
+public:
+ static void DumpToFile(HANDLE hFile, MethodContext *mc, CompileResult *cr);
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shared/callutils.cpp b/src/ToolBox/superpmi/superpmi-shared/callutils.cpp
new file mode 100644
index 0000000000..027929bec0
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/callutils.cpp
@@ -0,0 +1,416 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CallUtils.cpp - Utility code for analyzing and working with managed calls
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "callutils.h"
+#include "typeutils.h"
+#include "errorhandling.h"
+#include "logging.h"
+
+// String representations of the JIT helper functions
+const char *kHelperName[CORINFO_HELP_COUNT] =
+{
+#define JITHELPER(code, pfnHelper, sig) #code,
+#define DYNAMICJITHELPER(code, pfnHelper,sig) #code,
+#include "jithelpers.h"
+};
+
+//-------------------------------------------------------------------------------------------------
+
+//
+// Provides information about the target of an outgoing call, based on where it was emitted in the
+// generated code stream.
+//
+// Primarily, this returns what the destination of the call is (e.g. a method, a helper function), but this
+// can also provide:
+//
+// - A symbolic name for the call target (i.e. the function name).
+// - For non-helper methods, the method signature of the call target.
+//
+// Arguments:
+// mc - The method context of the method containing this call site.
+// cr - The compile result for the method containing this call site.
+// callInstrOffset - The native offset of the call site in the generated code stream.
+// outSigInfo - [out] The signature of the outgoing call. Optional (pass nullptr if unwanted).
+// outCallTargetSymbol - [out] A string representation of the outgoing call. Optional (pass nullptr if
+// unwanted).
+//
+// Return Value:
+// What type of call the outgoing call is.
+//
+// Notes:
+// - This depends on the JIT having registered the call site with the EE through recordCallSite. If the
+// JIT didn't do this, GetDirectCallSite can obtain most of the same information for direct calls.
+// - If the call site is for a helper method, then outSigInfo will not be changed, since helper calls
+// have no signature information.
+// - If you pass in a valid pointer for outCallTargetSymbol, this function will allocate memory for it
+// if it is able to understand that call (i.e. if it does not return CallType_Unknown). You, the caller,
+// are responsible for freeing the memory (with delete[]).
+//
+CallType CallUtils::GetRecordedCallSiteInfo(MethodContext *mc,
+ CompileResult *cr,
+ unsigned int callInstrOffset,
+ /*out*/ CORINFO_SIG_INFO *outSigInfo,
+ /*out*/ char **outCallTargetSymbol)
+{
+ AssertCodeMsg(mc != nullptr, EXCEPTIONCODE_CALLUTILS,
+ "Null method context passed into GetCallTargetInfo for call at offset %x.",
+ callInstrOffset);
+ AssertCodeMsg(cr != nullptr, EXCEPTIONCODE_CALLUTILS,
+ "Null compile result passed into GetCallTargetInfo for call at offset %x.",
+ callInstrOffset);
+
+ CallType targetType = CallType_Unknown;
+
+ CORINFO_SIG_INFO callSig;
+ bool recordedCallSig = cr->fndRecordCallSiteSigInfo(callInstrOffset, &callSig);
+
+ CORINFO_METHOD_HANDLE methodHandle = nullptr;
+ bool recordedMethodHandle = cr->fndRecordCallSiteMethodHandle(callInstrOffset, &methodHandle);
+
+ if (recordedCallSig)
+ {
+ if (outSigInfo != nullptr)
+ *outSigInfo = callSig;
+
+ if (outCallTargetSymbol != nullptr)
+ *outCallTargetSymbol = (char *)GetMethodFullName(mc, methodHandle, callSig);
+
+ targetType = CallType_UserFunction;
+ }
+ else if (recordedMethodHandle)
+ {
+ CorInfoHelpFunc helperNum = CallUtils::GetHelperNum(methodHandle);
+ AssertCodeMsg(helperNum != CORINFO_HELP_UNDEF, EXCEPTIONCODE_CALLUTILS,
+ "Unknown call at offset %x with method handle %016llX.",
+ callInstrOffset, methodHandle);
+
+ size_t length = strlen(kHelperName[helperNum]) + 1;
+ *outCallTargetSymbol = new char[length];
+ strcpy_s(*outCallTargetSymbol, length, kHelperName[helperNum]);
+
+ targetType = CallType_Helper;
+ }
+ else
+ {
+ LogWarning("Call site at offset %x was not recorded via recordCallSite.", callInstrOffset);
+ }
+
+ return targetType;
+}
+
+//
+// Provides information about the target of an outgoing call, based on the outgoing call's target address.
+//
+// Primarily, this returns what the destination of the call is (e.g. a method, a helper function), but this
+// can also provide:
+//
+// - A symbolic name for the call target (i.e. the function name).
+// - For certain types of managed methods, the method signature of the call target.
+//
+// Arguments:
+// mc - The method context of the method containing this outgoing call.
+// callTarget - The target address of the outgoing call.
+// outSigInfo - [out] The signature of the outgoing call. Optional (pass nullptr if unwanted).
+// outCallTargetSymbol - [out] A string representation of the outgoing call. Optional (pass nullptr if
+// unwanted).
+//
+// Return Value:
+// What type of call the outgoing call is.
+//
+// Assumptions:
+// The given method address does not point to a jump stub.
+//
+// Notes:
+// - This only works for direct calls that have a static target address.
+// - If you pass in a valid pointer for outCallTargetSymbol, this function will allocate memory for it
+// if it is able to understand that call (i.e. if it does not return CallType_Unknown). You, the caller,
+// are responsible for freeing the memory (with delete[]).
+//
+
+CallType CallUtils::GetDirectCallSiteInfo(MethodContext *mc,
+ void *callTarget,
+ /*out*/ CORINFO_SIG_INFO *outSigInfo,
+ /*out*/ char **outCallTargetSymbol)
+{
+ AssertCodeMsg(mc != nullptr, EXCEPTIONCODE_CALLUTILS,
+ "Null method context passed into GetCallTargetInfo for call to target %016llX.", callTarget);
+
+ CallType targetType = CallType_Unknown;
+ MethodContext::DLD functionEntryPoint;
+ CORINFO_METHOD_HANDLE methodHandle;
+
+ // Try to first obtain a method handle associated with this call target
+ functionEntryPoint.A = (DWORDLONG)callTarget;
+ functionEntryPoint.B = 0; // TODO-Cleanup: we should be more conscious of this...
+
+ if (mc->fndGetFunctionEntryPoint(functionEntryPoint, &methodHandle))
+ {
+ // Now try to obtain the call info associated with this method handle
+
+ struct Param {
+ MethodContext* mc;
+ CORINFO_SIG_INFO* outSigInfo;
+ char** outCallTargetSymbol;
+ CallType* pTargetType;
+ CORINFO_METHOD_HANDLE* pMethodHandle;
+ } param;
+ param.mc = mc;
+ param.outSigInfo = outSigInfo;
+ param.outCallTargetSymbol = outCallTargetSymbol;
+ param.pTargetType = &targetType;
+ param.pMethodHandle = &methodHandle;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ CORINFO_CALL_INFO callInfo;
+
+ pParam->mc->repGetCallInfoFromMethodHandle(*pParam->pMethodHandle, &callInfo);
+
+ if (pParam->outSigInfo != nullptr)
+ *pParam->outSigInfo = callInfo.sig;
+
+ if (pParam->outCallTargetSymbol != nullptr)
+ *pParam->outCallTargetSymbol = (char *)GetMethodFullName(pParam->mc, *pParam->pMethodHandle, callInfo.sig);
+
+ *pParam->pTargetType = CallType_UserFunction;
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchMC)
+ {
+ LogWarning("Didn't find call info for method handle %016llX (call target: %016llX)",
+ methodHandle, callTarget);
+ }
+ PAL_ENDTRY
+ }
+ else
+ {
+ // No method handle associated with this target, so check if it's a helper
+ CorInfoHelpFunc helperNum;
+
+ if (mc->fndGetHelperFtn(callTarget, &helperNum))
+ {
+ if (outCallTargetSymbol != nullptr)
+ {
+ size_t length = strlen(kHelperName[helperNum]) + 1;
+ *outCallTargetSymbol = new char[length];
+ strcpy_s(*outCallTargetSymbol, length, kHelperName[helperNum]);
+ }
+
+ targetType = CallType_Helper;
+ }
+ else
+ {
+ LogWarning("Call to target %016llX has no method handle and is not a helper call.",
+ callTarget);
+ }
+ }
+
+ return targetType;
+}
+
+//-------------------------------------------------------------------------------------------------
+// Utilty code that was stolen from various sections of the JIT codebase and tweaked to go through
+// SuperPMI's method context replaying instead of directly making calls into the JIT/EE interface.
+//-------------------------------------------------------------------------------------------------
+
+// Stolen from Compiler::impMethodInfo_hasRetBuffArg (in the importer)
+bool CallUtils::HasRetBuffArg(MethodContext *mc, CORINFO_SIG_INFO args)
+{
+ if (args.retType != CORINFO_TYPE_VALUECLASS &&
+ args.retType != CORINFO_TYPE_REFANY)
+ {
+ return false;
+ }
+
+#if defined(_TARGET_AMD64_)
+ // We don't need a return buffer if:
+ // i) TYP_STRUCT argument that can fit into a single register and
+ // ii) Power of two sized TYP_STRUCT on AMD64.
+ unsigned size = mc->repGetClassSize(args.retTypeClass);
+ return (size > sizeof(void*)) || ((size & (size-1)) != 0);
+#else
+ return true;
+#endif
+}
+
+// Originally from src/jit/ee_il_dll.cpp
+const char *CallUtils::GetMethodName(MethodContext *mc,
+ CORINFO_METHOD_HANDLE method,
+ const char **classNamePtr)
+{
+ if (GetHelperNum(method))
+ {
+ if (classNamePtr != nullptr)
+ *classNamePtr = "HELPER";
+
+ // The JIT version uses the getHelperName JIT/EE interface call, but this is easier for us
+ return kHelperName[GetHelperNum(method)];
+ }
+
+ if (IsNativeMethod(method))
+ {
+ if (classNamePtr != nullptr)
+ *classNamePtr = "NATIVE";
+ method = GetMethodHandleForNative(method);
+ }
+
+ return(mc->repGetMethodName(method, classNamePtr));
+}
+
+// Originally from src/jit/eeinterface.cpp
+const char *CallUtils::GetMethodFullName(MethodContext *mc,
+ CORINFO_METHOD_HANDLE hnd,
+ CORINFO_SIG_INFO sig)
+{
+ const char* returnType = NULL;
+
+ const char* className;
+ const char* methodName = GetMethodName(mc, hnd, &className);
+ if ((GetHelperNum(hnd) != CORINFO_HELP_UNDEF) || IsNativeMethod(hnd))
+ {
+ return methodName;
+ }
+
+ size_t length = 0;
+ unsigned i;
+
+ /* Generating the full signature is a two-pass process. First we have to walk
+ the components in order to assess the total size, then we allocate the buffer
+ and copy the elements into it.
+ */
+
+ /* Right now there is a race-condition in the EE, className can be NULL */
+
+ /* initialize length with length of className and '.' */
+
+ if (className != nullptr)
+ length = strlen(className)+1;
+ else
+ {
+ // Tweaked to avoid using CRT assertions
+ Assert(strlen("<NULL>.") == 7);
+ length = 7;
+ }
+
+ /* add length of methodName and opening bracket */
+ length += strlen(methodName) + 1;
+
+ CORINFO_ARG_LIST_HANDLE argList = sig.args;
+
+ for (i = 0; i < sig.numArgs; i++)
+ {
+ // Tweaked to use EE types instead of JIT-specific types
+ CORINFO_CLASS_HANDLE typeHandle;
+ DWORD exception;
+ CorInfoType type = strip(mc->repGetArgType(&sig, argList, &typeHandle, &exception));
+
+ length += strlen(TypeUtils::GetCorInfoTypeName(type));
+ argList = mc->repGetArgNext(argList);
+ }
+
+ /* add ',' if there is more than one argument */
+
+ if (sig.numArgs > 1)
+ length += (sig.numArgs - 1);
+
+ // Tweaked to use EE types instead of JIT-specific types
+ if (sig.retType != CORINFO_TYPE_VOID)
+ {
+ returnType = TypeUtils::GetCorInfoTypeName(sig.retType);
+ length += strlen(returnType) + 1; // don't forget the delimiter ':'
+ }
+
+ // Does it have a 'this' pointer? Don't count explicit this, which has the this pointer type as the first element of the arg type list
+ if (sig.hasThis() && !sig.hasExplicitThis())
+ {
+ // Tweaked to avoid using CRT assertions
+ Assert(strlen(":this") == 5);
+ length += 5;
+ }
+
+ /* add closing bracket and null terminator */
+
+ length += 2;
+
+ char *retName = new char[length]; // Tweaked to use "new" instead of compGetMem
+
+ /* Now generate the full signature string in the allocated buffer */
+
+ if (className)
+ {
+ strcpy_s(retName, length, className);
+ strcat_s(retName, length, ":");
+ }
+ else
+ {
+ strcpy_s(retName, length, "<NULL>.");
+ }
+
+ strcat_s(retName, length, methodName);
+
+ // append the signature
+ strcat_s(retName, length, "(");
+
+ argList = sig.args;
+
+ for (i = 0; i < sig.numArgs; i++)
+ {
+ // Tweaked to use EE types instead of JIT-specific types
+ CORINFO_CLASS_HANDLE typeHandle;
+ DWORD exception;
+ CorInfoType type = strip(mc->repGetArgType(&sig, argList, &typeHandle, &exception));
+ strcat_s(retName, length, TypeUtils::GetCorInfoTypeName(type));
+
+ argList = mc->repGetArgNext(argList);
+ if (i + 1 < sig.numArgs)
+ strcat_s(retName, length, ",");
+ }
+
+ strcat_s(retName, length, ")");
+
+ if (returnType)
+ {
+ strcat_s(retName, length, ":");
+ strcat_s(retName, length, returnType);
+ }
+
+ // Does it have a 'this' pointer? Don't count explicit this, which has the this pointer type as the first element of the arg type list
+ if (sig.hasThis() && !sig.hasExplicitThis())
+ {
+ strcat_s(retName, length, ":this");
+ }
+
+ // Tweaked to avoid using CRT assertions
+ Assert(strlen(retName) == (length-1));
+
+ return(retName);
+}
+
+// Originally from jit/compiler.hpp
+inline CorInfoHelpFunc CallUtils::GetHelperNum(CORINFO_METHOD_HANDLE method)
+{
+ // Helpers are marked by the fact that they are odd numbers
+ if (!(((size_t) method) & 1))
+ return(CORINFO_HELP_UNDEF);
+ return((CorInfoHelpFunc) (((size_t) method) >> 2));
+}
+
+// Originally from jit/compiler.hpp
+inline bool CallUtils::IsNativeMethod(CORINFO_METHOD_HANDLE method)
+{
+ return ((((size_t)method) & 0x2) == 0x2);
+}
+
+// Originally from jit/compiler.hpp
+inline CORINFO_METHOD_HANDLE CallUtils::GetMethodHandleForNative(CORINFO_METHOD_HANDLE method)
+{
+ // Tweaked to avoid using CRT assertions
+ Assert((((size_t) method) & 0x3) == 0x2);
+ return (CORINFO_METHOD_HANDLE)(((size_t)method)& ~0x3);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/callutils.h b/src/ToolBox/superpmi/superpmi-shared/callutils.h
new file mode 100644
index 0000000000..f428d77795
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/callutils.h
@@ -0,0 +1,44 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//----------------------------------------------------------
+// CallUtils.h - Utility code for analyzing and working with managed calls
+//----------------------------------------------------------
+#ifndef _CallUtils
+#define _CallUtils
+
+#include "methodcontext.h"
+
+enum CallType
+{
+ CallType_UserFunction = 0,
+ CallType_Helper,
+ CallType_Unknown = -1
+};
+
+class CallUtils
+{
+public:
+ static CallType GetRecordedCallSiteInfo(MethodContext *mc,
+ CompileResult *cr,
+ unsigned int callInstrOffset,
+ /*out*/ CORINFO_SIG_INFO *outSigInfo,
+ /*out*/ char **outCallTargetSymbol);
+ static CallType GetDirectCallSiteInfo(MethodContext *mc,
+ void *callTarget,
+ /*out*/ CORINFO_SIG_INFO *outSigInfo,
+ /*out*/ char **outCallTargetSymbol);
+ static bool HasRetBuffArg(MethodContext *mc, CORINFO_SIG_INFO args);
+ static CorInfoHelpFunc GetHelperNum(CORINFO_METHOD_HANDLE method);
+ static bool IsNativeMethod(CORINFO_METHOD_HANDLE method);
+ static CORINFO_METHOD_HANDLE GetMethodHandleForNative(CORINFO_METHOD_HANDLE method);
+ static const char *GetMethodName(MethodContext *mc,
+ CORINFO_METHOD_HANDLE method,
+ const char **classNamePtr);
+ static const char *GetMethodFullName(MethodContext *mc,
+ CORINFO_METHOD_HANDLE hnd,
+ CORINFO_SIG_INFO sig);
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp b/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp
new file mode 100644
index 0000000000..dfb7ecd7de
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp
@@ -0,0 +1,1072 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CompileResult.cpp - CompileResult contains the stuff generated by a compilation
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "compileresult.h"
+#include "methodcontext.h"
+
+CompileResult::CompileResult()
+{
+ #define LWM(map,key,value) map = nullptr;
+ #include "crlwmlist.h"
+
+ //Not persisted to disk. though should it be?
+ CallTargetTypes = new LightWeightMap<DWORDLONG, DWORD>();
+
+ allocMemDets.hotCodeSize = 0;
+ allocMemDets.coldCodeSize = 0;
+ allocMemDets.roDataSize = 0;
+ allocMemDets.xcptnsCount = 0;
+ allocMemDets.flag = (CorJitAllocMemFlag)0;
+ allocMemDets.hotCodeBlock = 0;
+ allocMemDets.coldCodeBlock = 0;
+ allocMemDets.roDataBlock = 0;
+
+ allocGCInfoDets.retval = 0;
+ allocGCInfoDets.size = 0;
+
+ codeHeap = nullptr;
+}
+
+CompileResult::~CompileResult()
+{
+ #define LWM(map,key,value) if (map != nullptr) delete map;
+ #include "crlwmlist.h"
+
+ if (CallTargetTypes != nullptr) delete CallTargetTypes;
+
+#ifndef FEATURE_PAL // PAL doesn't have HeapDestroy()
+ if(codeHeap != nullptr)
+ ::HeapDestroy(codeHeap);
+#endif // !FEATURE_PAL
+}
+
+// Is the CompileResult empty? Define this as whether all the maps that store information given by the JIT are empty.
+// This is useful when determining if a function won't apply after a "mcs -removeDump -thin" operation has been run.
+bool CompileResult::IsEmpty()
+{
+ bool isEmpty = true;
+
+ #define LWM(map,key,value) if (map != nullptr) isEmpty = false;
+ #include "crlwmlist.h"
+
+ return isEmpty;
+}
+
+HANDLE CompileResult::getCodeHeap()
+{
+ if(codeHeap == nullptr)
+ codeHeap = ::HeapCreate(0,0,0);
+ if(codeHeap == nullptr)
+ {
+ LogError("CompileResult::codeHeap() failed to acquire a heap.");
+ __debugbreak();
+ }
+ return codeHeap;
+}
+
+void CompileResult::recAssert(const char *assertText)
+{
+ if(AssertLog == nullptr)
+ AssertLog = new DenseLightWeightMap<DWORD>();
+
+ AssertLog->Append(AssertLog->AddBuffer((const unsigned char*)assertText, (DWORD)strlen(assertText)+1));
+}
+void CompileResult::dmpAssertLog(DWORD key, DWORD value)
+{
+ const char *assert = (const char *)AssertLog->GetBuffer(value);
+ printf("AssertLog key %u, value '%s'", key, assert);
+ AssertLog->Unlock();
+}
+const char *CompileResult::repAssert()
+{
+ if((AssertLog==nullptr)||(AssertLog->GetCount()==0))
+ return nullptr;
+ return (const char *)AssertLog->GetBuffer(AssertLog->Get((DWORD)0));
+}
+
+void CompileResult::AddCall(const char *name)
+{
+ if(CallLog == nullptr)
+ CallLog = new DenseLightWeightMap<DWORD>();
+ //if(name[0] != '+')
+ //CallLog->Append(CallLog->AddBuffer((const unsigned char *)name, (DWORD)strlen(name)+1));
+}
+unsigned int CompileResult::CallLog_GetCount()
+{
+ return CallLog->GetCount();
+}
+
+bool CompileResult::CallLog_Contains(const char *str)
+{
+ return (CallLog->Contains((unsigned char *)str, (unsigned int)strlen(str))>0);
+}
+void CompileResult::dmpCallLog(DWORD key, DWORD value)
+{
+ const char* temp = (const char*)CallLog->GetBuffer(value);
+ printf("CallLog %u '%s'", key, temp);
+ CallLog->Unlock();
+}
+
+void CompileResult::dumpToConsole()
+{
+ printf("***************************************** CompileResult\n");
+
+ #define LWM(map,key,value) dumpLWM(this,map)
+ #define DENSELWM(map,value) dumpLWMDense(this,map)
+ #include "crlwmlist.h"
+
+ printf("-----------------------------------------\n");
+}
+
+//Note - EE allocates these blocks (and the exception blocks) in a single linear region.
+//Note - EE assures that RoBlock is 8 byte aligned
+void CompileResult::recAllocMem(ULONG hotCodeSize, ULONG coldCodeSize, ULONG roDataSize, ULONG xcptnsCount, CorJitAllocMemFlag flag,
+ void **hotCodeBlock, void **coldCodeBlock, void **roDataBlock)
+{
+ //Grab the values, so we can scrape the real answers in the capture method
+ allocMemDets.hotCodeSize = hotCodeSize;
+ allocMemDets.coldCodeSize = coldCodeSize;
+ allocMemDets.roDataSize = roDataSize;
+ allocMemDets.xcptnsCount = xcptnsCount;
+ allocMemDets.flag = flag;
+ allocMemDets.hotCodeBlock = *hotCodeBlock;
+ allocMemDets.coldCodeBlock = *coldCodeBlock;
+ allocMemDets.roDataBlock = *roDataBlock;
+}
+void CompileResult::recAllocMemCapture()
+{
+ if(AllocMem == nullptr)
+ AllocMem = new LightWeightMap<DWORD, Agnostic_AllocMemDetails>();
+
+ Agnostic_AllocMemDetails value;
+
+ value.hotCodeSize = (DWORD)allocMemDets.hotCodeSize;
+ value.coldCodeSize = (DWORD)allocMemDets.coldCodeSize;
+ value.roDataSize = (DWORD)allocMemDets.roDataSize;
+ value.xcptnsCount = (DWORD)allocMemDets.xcptnsCount;
+ value.flag = (DWORD)allocMemDets.flag;
+ value.hotCodeBlock_offset = (DWORD)AllocMem->AddBuffer((const unsigned char*)allocMemDets.hotCodeBlock, allocMemDets.hotCodeSize);
+ value.coldCodeBlock_offset = (DWORD)AllocMem->AddBuffer((const unsigned char*)allocMemDets.coldCodeBlock, allocMemDets.coldCodeSize);
+ value.roDataBlock_offset = (DWORD)AllocMem->AddBuffer((const unsigned char*)allocMemDets.roDataBlock, allocMemDets.roDataSize);
+ value.hotCodeBlock = (DWORDLONG)allocMemDets.hotCodeBlock;
+ value.coldCodeBlock = (DWORDLONG)allocMemDets.coldCodeBlock;
+ value.roDataBlock = (DWORDLONG)allocMemDets.roDataBlock;
+
+ AllocMem->Add(0, value);
+}
+void CompileResult::dmpAllocMem(DWORD key, const Agnostic_AllocMemDetails& value)
+{
+ printf("AllocMem key 0, value hotCodeSize-%u coldCodeSize-%u roDataSize-%u xcptnsCount-%u flag-%08X hotCodeBlock_offset-%u coldCodeBlock_offset-%u roDataBlock_offset-%u hotCodeBlock-%016llX coldCodeBlock-%016llX roDataBlock-%016llX",
+ value.hotCodeSize,
+ value.coldCodeSize,
+ value.roDataSize,
+ value.xcptnsCount,
+ value.flag,
+ value.hotCodeBlock_offset,
+ value.coldCodeBlock_offset,
+ value.roDataBlock_offset,
+ value.hotCodeBlock,
+ value.coldCodeBlock,
+ value.roDataBlock);
+}
+
+// We can't allocate memory in the same place is was during recording, so we pass back code/data block pointers
+// that point into the AllocMem LightWeightMap, but also return what the original addresses were during recording.
+void CompileResult::repAllocMem(
+ ULONG *hotCodeSize,
+ ULONG *coldCodeSize,
+ ULONG *roDataSize,
+ ULONG *xcptnsCount,
+ CorJitAllocMemFlag *flag,
+ unsigned char **hotCodeBlock,
+ unsigned char **coldCodeBlock,
+ unsigned char **roDataBlock,
+ void **orig_hotCodeBlock,
+ void **orig_coldCodeBlock,
+ void **orig_roDataBlock)
+{
+ Agnostic_AllocMemDetails value;
+
+ value = AllocMem->Get(0);
+
+ *hotCodeSize = (ULONG)value.hotCodeSize;
+ *coldCodeSize = (ULONG)value.coldCodeSize;
+ *roDataSize = (ULONG)value.roDataSize;
+ *xcptnsCount = (ULONG)value.xcptnsCount;
+ *flag = (CorJitAllocMemFlag)value.flag;
+
+ if(*hotCodeSize>0)
+ *hotCodeBlock = AllocMem->GetBuffer(value.hotCodeBlock_offset);
+ else
+ *hotCodeBlock = nullptr;
+
+ if(*coldCodeSize>0)
+ *coldCodeBlock = AllocMem->GetBuffer(value.coldCodeBlock_offset);
+ else
+ *coldCodeBlock = nullptr;
+
+ if(*roDataSize>0)
+ *roDataBlock = AllocMem->GetBuffer(value.roDataBlock_offset);
+ else
+ *roDataBlock = nullptr;
+
+ *orig_hotCodeBlock = (void *)value.hotCodeBlock;
+ *orig_coldCodeBlock = (void *)value.coldCodeBlock;
+ *orig_roDataBlock = (void *)value.roDataBlock;
+}
+
+//Note - Ownership of pMap is transfered with this call. In replay icorjitinfo we should free it.
+void CompileResult::recSetBoundaries(CORINFO_METHOD_HANDLE ftn, ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap)
+{
+ if(SetBoundaries == nullptr)
+ SetBoundaries = new LightWeightMap<DWORD, Agnostic_SetBoundaries>();
+
+ Agnostic_SetBoundaries value;
+
+ value.ftn = (DWORDLONG)ftn;
+ value.cMap = (DWORD)cMap;
+ value.pMap_offset = (DWORD)SetBoundaries->AddBuffer((const unsigned char*)pMap, sizeof(ICorDebugInfo::OffsetMapping)*cMap);
+
+ SetBoundaries->Add(0, value);
+}
+void CompileResult::dmpSetBoundaries(DWORD key, const Agnostic_SetBoundaries& value)
+{
+ ICorDebugInfo::OffsetMapping *om = (ICorDebugInfo::OffsetMapping *)SetBoundaries->GetBuffer(value.pMap_offset);
+ printf("SetBoundaries key 0, value ftn-%016llX cMap-%u %u{",
+ value.ftn,
+ value.cMap,
+ value.pMap_offset);
+ for(unsigned int i=0;i<value.cMap;i++)
+ {
+ if (i != 0)
+ printf(", ");
+ printf("%u %u %u", om[i].ilOffset, om[i].nativeOffset, om[i].source);
+ }
+ printf("}");
+ SetBoundaries->Unlock();
+}
+bool CompileResult::repSetBoundaries(CORINFO_METHOD_HANDLE *ftn, ULONG32 *cMap, ICorDebugInfo::OffsetMapping **pMap)
+{
+ if((SetBoundaries == nullptr)||(SetBoundaries->GetCount()==0))
+ {
+ *ftn = (CORINFO_METHOD_HANDLE)-1;
+ *cMap = -1;
+ *pMap = nullptr;
+ return false;
+ }
+ Agnostic_SetBoundaries value;
+
+ value = SetBoundaries->Get(0);
+
+ *ftn = (CORINFO_METHOD_HANDLE)value.ftn;
+ *cMap = (ULONG32)value.cMap;
+ *pMap = (ICorDebugInfo::OffsetMapping *)SetBoundaries->GetBuffer(value.pMap_offset);
+ return true;
+}
+
+//Note - Ownership of vars is transfered with this call. In replay icorjitinfo we should free it.
+void CompileResult::recSetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars)
+{
+ if(SetVars == nullptr)
+ SetVars = new LightWeightMap<DWORD, Agnostic_SetVars>();
+
+ Agnostic_SetVars value;
+
+ value.ftn = (DWORDLONG)ftn;
+ value.cVars = (DWORD)cVars;
+ value.vars_offset = (DWORD)SetVars->AddBuffer((const unsigned char*)vars, sizeof(ICorDebugInfo::NativeVarInfo)*cVars); //not deep enough.. vlt memory is pointer sized.
+
+ SetVars->Add(0, value);
+}
+void CompileResult::dmpSetVars(DWORD key, const Agnostic_SetVars& value)
+{
+ ICorDebugInfo::NativeVarInfo *om = (ICorDebugInfo::NativeVarInfo *)SetVars->GetBuffer(value.vars_offset);
+ printf("SetVars key %u, value ftn-%016llX cVars-%u %u{",
+ key,
+ value.ftn,
+ value.cVars,
+ value.vars_offset);
+ for (unsigned int i = 0; i < value.cVars; i++)
+ {
+ if (i != 0)
+ printf(", ");
+ printf("so-%u eo-%u var-%u", om[i].startOffset, om[i].endOffset, om[i].varNumber);
+ }
+ printf("}");
+ SetVars->Unlock();
+}
+bool CompileResult::repSetVars(CORINFO_METHOD_HANDLE *ftn, ULONG32 *cVars, ICorDebugInfo::NativeVarInfo **vars)
+{
+ if((SetVars == nullptr)||(SetVars->GetCount()==0))
+ {
+ *ftn = (CORINFO_METHOD_HANDLE)-1;
+ *cVars = -1;
+ *vars = nullptr;
+ return false;
+ }
+
+ Agnostic_SetVars value;
+
+ value = SetVars->Get(0);
+
+ *ftn = (CORINFO_METHOD_HANDLE)value.ftn;
+ *cVars = (ULONG32)value.cVars;
+ *vars = (ICorDebugInfo::NativeVarInfo*)SetVars->GetBuffer(value.vars_offset);
+
+ return true;
+}
+
+void CompileResult::recAllocGCInfo(size_t size, void* retval)
+{
+ allocGCInfoDets.size = size;
+ allocGCInfoDets.retval = retval;
+}
+void CompileResult::recAllocGCInfoCapture()
+{
+ if(AllocGCInfo == nullptr)
+ AllocGCInfo = new LightWeightMap<DWORD, Agnostic_AllocGCInfo>();
+
+ Agnostic_AllocGCInfo value;
+
+ value.size = allocGCInfoDets.size;
+ value.retval_offset = (DWORD)AllocGCInfo->AddBuffer((const unsigned char *)allocGCInfoDets.retval, (DWORD)allocGCInfoDets.size);
+
+ AllocGCInfo->Add(0, value);
+}
+void CompileResult::dmpAllocGCInfo(DWORD key, const Agnostic_AllocGCInfo& value)
+{
+ const unsigned char *buff = AllocGCInfo->GetBuffer(value.retval_offset);
+ printf("AllocGCInfo key 0, ");
+ printf("sz-%llu %p{ ", value.size, buff);
+ for(unsigned int i=0; i<value.size; i++)
+ printf("%02X ", *(buff+i));
+ printf("}");
+ AllocGCInfo->Unlock();
+}
+void CompileResult::repAllocGCInfo(size_t *size, void **retval)
+{
+ Agnostic_AllocGCInfo value;
+
+ value = AllocGCInfo->Get(0);
+
+ *size = (size_t)value.size;
+ if(*size>0)
+ *retval = (void *)AllocGCInfo->GetBuffer(value.retval_offset);
+}
+
+void CompileResult::recCompileMethod(BYTE **nativeEntry, ULONG *nativeSizeOfCode, CorJitResult result)
+{
+ if(CompileMethod == nullptr)
+ CompileMethod = new LightWeightMap<DWORD, Agnostic_CompileMethodResults>();
+
+ Agnostic_CompileMethodResults value;
+ value.nativeEntry = (DWORDLONG)*nativeEntry;
+ value.nativeSizeOfCode = (DWORD)*nativeSizeOfCode;
+ value.CorJitResult = (DWORD)result;
+
+ CompileMethod->Add(0, value);
+}
+void CompileResult::dmpCompileMethod(DWORD key, const Agnostic_CompileMethodResults& value)
+{
+ printf("CompileMethod key %u, value nativeEntry-%016llX nativeSizeOfCode-%u CorJitResult-%u",
+ key, value.nativeEntry, value.nativeSizeOfCode, value.CorJitResult);
+}
+void CompileResult::repCompileMethod(BYTE **nativeEntry, ULONG *nativeSizeOfCode, CorJitResult *result)
+{
+ Agnostic_CompileMethodResults value;
+ value = CompileMethod->Get(0);
+ *nativeEntry = (BYTE *)value.nativeEntry;
+ *nativeSizeOfCode = (ULONG)value.nativeSizeOfCode;
+ *result = (CorJitResult)value.CorJitResult;
+}
+
+void CompileResult::recMessageLog(const char* fmt, ...)
+{
+ // TODO-Cleanup: ???
+ return;
+ if(MessageLog == nullptr)
+ MessageLog = new DenseLightWeightMap<DWORD>();
+
+ va_list args;
+
+ // retrieve the variable arguments
+ va_start( args, fmt );
+
+ size_t len = _vscprintf(fmt, args) + 1; //space for the terminator
+
+ unsigned char *messageLogBuffer = new unsigned char[len];
+ vsprintf_s((char*)messageLogBuffer, len, fmt, args);
+ messageLogBuffer[len-1] = 0;
+ MessageLog->Append(MessageLog->AddBuffer(messageLogBuffer, (DWORD)len));
+ delete []messageLogBuffer;
+}
+void CompileResult::dmpMessageLog(DWORD key, DWORD value)
+{
+ printf("MessageLog NYI");
+}
+
+void CompileResult::recClassMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE cls)
+{
+ if(ClassMustBeLoadedBeforeCodeIsRun == nullptr)
+ ClassMustBeLoadedBeforeCodeIsRun = new DenseLightWeightMap<DWORDLONG>();
+
+ ClassMustBeLoadedBeforeCodeIsRun->Append((DWORDLONG)cls);
+}
+void CompileResult::dmpClassMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value)
+{
+ printf("ClassMustBeLoadedBeforeCodeIsRun key %u, value cls-%016llX",
+ key, value);
+}
+
+void CompileResult::recReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult, const char * reason)
+{
+ if(ReportInliningDecision == nullptr)
+ ReportInliningDecision = new DenseLightWeightMap<Agnostic_ReportInliningDecision>();
+
+ Agnostic_ReportInliningDecision value;
+
+ value.inlinerHnd = (DWORDLONG)inlinerHnd;
+ value.inlineeHnd = (DWORDLONG)inlineeHnd;
+ value.inlineResult = (DWORD)inlineResult;
+ if(reason!=nullptr)
+ value.reason_offset = (DWORD)ReportInliningDecision->AddBuffer((unsigned char*)reason, (DWORD)strlen(reason)+1);
+ else
+ value.reason_offset = -1;
+
+ ReportInliningDecision->Append(value);
+}
+void CompileResult::dmpReportInliningDecision(DWORD key, const Agnostic_ReportInliningDecision& value)
+{
+ const char *reason = (const char*)ReportInliningDecision->GetBuffer(value.reason_offset);
+ printf("ReportInliningDecision key %u, value inliner-%016llX inlinee-%016llX res-%u reason-'%s'",
+ key,
+ value.inlinerHnd,
+ value.inlineeHnd,
+ value.inlineResult,
+ reason);
+ ReportInliningDecision->Unlock();
+}
+CorInfoInline CompileResult::repReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd)
+{
+ CorInfoInline result = INLINE_FAIL;
+ if(ReportInliningDecision!=nullptr)
+ {
+ Agnostic_ReportInliningDecision *items = ReportInliningDecision->GetRawItems();
+ unsigned int cnt = ReportInliningDecision->GetCount();
+ for(unsigned int i=0;i<cnt;i++)
+ {
+ if((items[i].inlinerHnd == (DWORDLONG)inlinerHnd)&&(items[i].inlineeHnd == (DWORDLONG)inlineeHnd)&&
+ (items[i].inlineResult == INLINE_PASS))
+ result = INLINE_PASS;
+ }
+ }
+ return result;
+}
+
+void CompileResult::recSetEHcount(unsigned cEH)
+{
+ if(SetEHcount == nullptr)
+ SetEHcount = new LightWeightMap<DWORD, DWORD>();
+
+ SetEHcount->Add((DWORD)0, (DWORD)cEH);
+}
+void CompileResult::dmpSetEHcount(DWORD key, DWORD value)
+{
+ printf("SetEHcount key %u, value %u", key, value);
+}
+ULONG CompileResult::repSetEHcount()
+{
+ if(SetEHcount==nullptr)
+ SetEHcount = new LightWeightMap<DWORD, DWORD>();
+
+ ULONG ehCount;
+ int index = SetEHcount->GetIndex(0);
+ if(index < 0)
+ ehCount = 0;
+ else
+ ehCount = (ULONG)SetEHcount->Get(index);
+ return ehCount;
+}
+
+void CompileResult::recSetEHinfo(unsigned EHnumber, const CORINFO_EH_CLAUSE *clause)
+{
+ if(SetEHinfo == nullptr)
+ SetEHinfo = new LightWeightMap<DWORD, Agnostic_CORINFO_EH_CLAUSE2>();
+
+ Agnostic_CORINFO_EH_CLAUSE2 value;
+ value.Flags = (DWORD)clause->Flags;
+ value.TryOffset = (DWORD)clause->TryOffset;
+ value.TryLength = (DWORD)clause->TryLength;
+ value.HandlerOffset = (DWORD)clause->HandlerOffset;
+ value.HandlerLength = (DWORD)clause->HandlerLength;
+ value.ClassToken = (DWORD)clause->ClassToken;
+
+ SetEHinfo->Add((DWORD)EHnumber, value);
+}
+void CompileResult::dmpSetEHinfo(DWORD key, const Agnostic_CORINFO_EH_CLAUSE2& value)
+{
+ printf("SetEHinfo key %u, value flg-%u to-%u tl-%u ho-%u hl-%u",
+ key,
+ value.Flags,
+ value.TryOffset,
+ value.TryLength,
+ value.HandlerOffset,
+ value.HandlerLength);
+ if ((CORINFO_EH_CLAUSE_FLAGS)value.Flags == CORINFO_EH_CLAUSE_FILTER)
+ {
+ printf(" fo-%u", value.ClassToken); // FilterOffset
+ }
+ else if ((CORINFO_EH_CLAUSE_FLAGS)value.Flags == CORINFO_EH_CLAUSE_NONE)
+ {
+ printf(" cls-%08X", value.ClassToken);
+ }
+ // else, no need to print for finally/fault handlers
+}
+void CompileResult::repSetEHinfo(unsigned EHnumber, ULONG *flags, ULONG *tryOffset, ULONG *tryLength, ULONG *handlerOffset, ULONG *handlerLength, ULONG *classToken)
+{
+ Agnostic_CORINFO_EH_CLAUSE2 value;
+ value = SetEHinfo->Get(EHnumber);
+
+ *flags=(ULONG)value.Flags;
+ *tryOffset=(ULONG)value.TryOffset;
+ *tryLength=(ULONG)value.TryLength;
+ *handlerOffset=(ULONG)value.HandlerOffset;
+ *handlerLength=(ULONG)value.HandlerLength;
+ *classToken=(ULONG)value.ClassToken;
+}
+
+void CompileResult::recSetMethodAttribs(CORINFO_METHOD_HANDLE ftn, CorInfoMethodRuntimeFlags attribs)
+{
+ if(SetMethodAttribs == nullptr)
+ SetMethodAttribs = new LightWeightMap<DWORDLONG, DWORD>();
+
+ SetMethodAttribs->Add((DWORDLONG)ftn, (DWORD)attribs);
+}
+void CompileResult::dmpSetMethodAttribs(DWORDLONG key, DWORD value)
+{
+ printf("SetMethodAttribs key ftn-%016llX, value attr-%08X", key, value);
+}
+CorInfoMethodRuntimeFlags CompileResult::repSetMethodAttribs (CORINFO_METHOD_HANDLE ftn)
+{
+ if((SetMethodAttribs==nullptr)||(SetMethodAttribs->GetIndex((DWORDLONG)ftn)==-1))
+ return (CorInfoMethodRuntimeFlags)0;
+ CorInfoMethodRuntimeFlags result = (CorInfoMethodRuntimeFlags)SetMethodAttribs->Get((DWORDLONG)ftn);
+ return result;
+}
+
+void CompileResult::recMethodMustBeLoadedBeforeCodeIsRun(CORINFO_METHOD_HANDLE method)
+{
+ if(MethodMustBeLoadedBeforeCodeIsRun == nullptr)
+ MethodMustBeLoadedBeforeCodeIsRun = new DenseLightWeightMap<DWORDLONG>();
+
+ MethodMustBeLoadedBeforeCodeIsRun->Append((DWORDLONG)method);
+}
+void CompileResult::dmpMethodMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value)
+{
+ printf("MethodMustBeLoadedBeforeCodeIsRun key %u, value ftn-%016llX", key, value);
+}
+
+void CompileResult::recReportTailCallDecision(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult, const char * reason)
+{
+ if(ReportTailCallDecision == nullptr)
+ ReportTailCallDecision = new DenseLightWeightMap<Agnostic_ReportTailCallDecision>();
+
+ Agnostic_ReportTailCallDecision value;
+
+ value.callerHnd = (DWORDLONG)callerHnd;
+ value.calleeHnd = (DWORDLONG)calleeHnd;
+ value.fIsTailPrefix = (DWORD)fIsTailPrefix;
+ value.tailCallResult = (DWORD)tailCallResult;
+ if(reason!=nullptr) //protect strlen
+ value.reason_index = (DWORD)ReportTailCallDecision->AddBuffer((unsigned char*)reason, (DWORD)strlen(reason)+1);
+ else
+ value.reason_index = (DWORD)-1;
+
+ ReportTailCallDecision->Append(value);
+}
+void CompileResult::dmpReportTailCallDecision(DWORD key, const Agnostic_ReportTailCallDecision& value)
+{
+ const char *reason = (const char*)ReportTailCallDecision->GetBuffer(value.reason_index);
+ printf("ReportTailCallDecision key-%u, value cr-%016llX ce-%016llX tail-%u call-%u -%s",
+ key, value.callerHnd, value.calleeHnd, value.tailCallResult, value.tailCallResult, reason);
+ ReportTailCallDecision->Unlock();
+}
+
+void CompileResult::recReportFatalError(CorJitResult result)
+{
+ if(ReportFatalError == nullptr)
+ ReportFatalError = new DenseLightWeightMap<DWORD>();
+
+ ReportFatalError->Append((DWORD)result);
+}
+void CompileResult::dmpReportFatalError(DWORD key, DWORD value)
+{
+ printf("ReportFatalError key Count-%u, value result-%08X", key, value);
+}
+
+void CompileResult::recRecordRelocation(void *location, void *target, WORD fRelocType, WORD slotNum, INT32 addlDelta)
+{
+ repRecordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+const char* relocationTypeToString(WORD fRelocType)
+{
+ switch (fRelocType)
+ {
+ // From winnt.h
+ case IMAGE_REL_BASED_ABSOLUTE : return "absolute";
+ case IMAGE_REL_BASED_HIGH : return "high";
+ case IMAGE_REL_BASED_LOW : return "low";
+ case IMAGE_REL_BASED_HIGHLOW : return "highlow";
+ case IMAGE_REL_BASED_HIGHADJ : return "highadj";
+ case IMAGE_REL_BASED_DIR64 : return "dir64";
+
+ // From corinfo.h
+ case IMAGE_REL_BASED_REL32 : return "rel32";
+ case IMAGE_REL_BASED_THUMB_BRANCH24 : return "thumb_branch24";
+ default : return "UNKNOWN";
+ }
+}
+void CompileResult::dmpRecordRelocation(DWORD key, const Agnostic_RecordRelocation& value)
+{
+ printf("RecordRelocation key %u, value loc-%016llX tgt-%016llX fRelocType-%u(%s) slotNum-%u addlDelta-%d",
+ key,
+ value.location,
+ value.target,
+ value.fRelocType,
+ relocationTypeToString((WORD)value.fRelocType),
+ value.slotNum,
+ (INT32)value.addlDelta);
+}
+void CompileResult::repRecordRelocation(void *location, void *target, WORD fRelocType, WORD slotNum, INT32 addlDelta)
+{
+ if(RecordRelocation == nullptr)
+ RecordRelocation = new DenseLightWeightMap<Agnostic_RecordRelocation>();
+
+ Agnostic_RecordRelocation value;
+
+ value.location = (DWORDLONG)location;
+ value.target = (DWORDLONG)target;
+ value.fRelocType = (DWORD)fRelocType;
+ value.slotNum = (DWORD)slotNum;
+ value.addlDelta = (DWORD)addlDelta;
+
+ RecordRelocation->Append(value);
+}
+
+// When we do a replay, we replace the CompileResult that was originally recorded. In most cases,
+// though, as part of our collection process we do a "thinning" operation, using "mcs -removeDup -thin",
+// which removes the CompileResult from the stored file. Because of this, we lose access to the original
+// addresses.
+//
+// Unfortunately, we may have generated code based on the original relative addresses. For example,
+// we might have generated a relocation based on the results of a call to GetFunctionEntryPoint, and
+// the delta from the generated code to that address fit in a rel32 reloc. Or, it didn't fit, but the
+// VM constructed a jump stub in the JIT code section that would fit, and returned that.
+//
+// Ideally, we would just keep all the original addresses and relocations, even if we delete the CompileResult.
+// If a new relocation has the same target as an original relocation, then simply substitute the original
+// delta and use that instead of the newly computed delta.
+//
+// For now, for rel32 relocations, if it doesn't fit, then simply pick an address immediately after the
+// current section (using originalAddr), assuming we needed a jump stub. We'll let multiple calls to potentially
+// different functions use the same address because even if they used different ones, and diffs were generated,
+// no textual diffs would appear because most of the textual call names are "hackishMethodName".
+void CompileResult::applyRelocs(unsigned char *block1, ULONG blocksize1, void *originalAddr)
+{
+ if(RecordRelocation == nullptr)
+ return;
+ if(blocksize1 == 0)
+ return;
+
+ size_t section_begin = (size_t)block1;
+ size_t section_end = (size_t)block1 + (size_t)blocksize1; // address is exclusive
+
+ LogDebug("applyRelocs block [%p,%p) block size %u, orig addr %p",
+ block1,
+ block1 + blocksize1,
+ blocksize1,
+ originalAddr);
+
+ for(unsigned int i=0;i<RecordRelocation->GetCount();i++)
+ {
+ Agnostic_RecordRelocation tmp = RecordRelocation->GetRawItems()[i];
+
+ if (Logger::IsLogLevelEnabled(LOGLEVEL_DEBUG))
+ {
+ printf(" ");
+ dmpRecordRelocation(i, tmp);
+ printf("\n");
+ }
+
+ switch (tmp.fRelocType)
+ {
+ #if defined(_TARGET_X86_)
+ case IMAGE_REL_BASED_HIGHLOW:
+ {
+ DWORDLONG fixupLocation = tmp.location;
+
+ size_t address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
+ if ( (section_begin <= address) && (address < section_end) ) //A reloc for our section?
+ {
+ LogDebug(" fixupLoc-%016llX (@%p) : %08X => %08X", fixupLocation, address, *(DWORD*)address, (DWORD)tmp.target);
+ *(DWORD*)address = (DWORD)tmp.target;
+ }
+ if (tmp.addlDelta != 0)
+ __debugbreak();
+ if (tmp.slotNum != 0)
+ __debugbreak();
+ }
+ break;
+ #endif // _TARGET_X86_
+
+ #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+ case IMAGE_REL_BASED_REL32:
+ {
+ DWORDLONG target = tmp.target + tmp.addlDelta;
+ DWORDLONG fixupLocation = tmp.location + tmp.slotNum;
+ DWORDLONG baseAddr = fixupLocation + sizeof(INT32);
+ INT64 delta = (INT64)((BYTE *)target - baseAddr);
+
+ #if defined(_TARGET_AMD64_)
+ if (delta != (INT64)(int)delta)
+ {
+ // This isn't going to fit in a signed 32-bit address. Use something that will fit,
+ // since we assume that original compilation fit fine. This is only an issue for
+ // 32-bit offsets on 64-bit targets.
+ target = (DWORDLONG)originalAddr + (DWORDLONG)blocksize1;
+ INT64 newdelta = (INT64)((BYTE *)target - baseAddr);
+
+ LogDebug(" REL32 overflow. Mapping target to %016llX. Mapping delta: %016llX => %016llX", target, delta, newdelta);
+
+ delta = newdelta;
+ }
+ #endif // defined(_TARGET_AMD64_)
+
+ if (delta != (INT64)(int)delta)
+ {
+ #if defined(_TARGET_AMD64_)
+ LogError("REL32 relocation overflows field! delta=0x%016llX", delta);
+ #else
+ LogError("REL32 relocation overflows field! delta=0x%08X", delta);
+ #endif
+ }
+
+ // Write 32-bits into location
+ size_t address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
+ if ( (section_begin <= address) && (address < section_end) ) //A reloc for our section?
+ {
+ LogDebug(" fixupLoc-%016llX (@%p) : %08X => %08X", fixupLocation, address, *(DWORD*)address, delta);
+ *(DWORD*)address = (DWORD)delta;
+ }
+ }
+ break;
+ #endif // defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+
+ #if defined(_TARGET_AMD64_)
+ case IMAGE_REL_BASED_DIR64:
+ {
+ DWORDLONG fixupLocation = tmp.location + tmp.slotNum;
+
+ // Write 64-bits into location
+ size_t address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
+ if ( (section_begin <= address) && (address < section_end) ) //A reloc for our section?
+ {
+ LogDebug(" fixupLoc-%016llX (@%p) %016llX => %016llX", fixupLocation, address, *(DWORDLONG*)address, tmp.target);
+ *(DWORDLONG*)address = tmp.target;
+ }
+ }
+ break;
+ #endif // defined(_TARGET_AMD64_)
+
+ default:
+ LogError("Unknown reloc type %u", tmp.fRelocType);
+ break;
+ }
+ }
+}
+
+void CompileResult::recProcessName(const char* name)
+{
+ if(ProcessName == nullptr)
+ ProcessName = new DenseLightWeightMap<DWORD>();
+
+ DWORD index = (DWORD)-1;
+ if(name != nullptr)
+ index = (DWORD)ProcessName->AddBuffer((unsigned char*)name, (DWORD)strlen(name)+1);
+
+ ProcessName->Append(index);
+}
+void CompileResult::dmpProcessName(DWORD key, DWORD value)
+{
+ const char *procName = (const char *)ProcessName->GetBuffer(value);
+ printf("ProcessName key %u, value '%s'", key, procName);
+ ProcessName->Unlock();
+}
+const char *CompileResult::repProcessName()
+{
+ if(ProcessName == nullptr)
+ return "hackishProcessName";
+
+ if(ProcessName->GetCount()>0)
+ {
+ return (const char*)ProcessName->GetBuffer(ProcessName->Get((DWORD)0));
+ }
+ return nullptr;
+}
+
+void CompileResult::recAddressMap(void *originalAddress, void *replayAddress, unsigned int size)
+{
+ if(AddressMap == nullptr)
+ AddressMap = new LightWeightMap<DWORDLONG, Agnostic_AddressMap>();
+
+ Agnostic_AddressMap value;
+
+ value.Address = (DWORDLONG)originalAddress;
+ value.size = (DWORD)size;
+
+ AddressMap->Add((DWORDLONG)replayAddress, value);
+}
+void CompileResult::dmpAddressMap(DWORDLONG key, const Agnostic_AddressMap& value)
+{
+ printf("AddressMap key %016llX, value addr-%016llX, size-%u",
+ key,
+ value.Address,
+ value.size);
+}
+void* CompileResult::repAddressMap(void *replayAddress)
+{
+ if (AddressMap == nullptr)
+ return nullptr;
+ Agnostic_AddressMap value;
+ value = AddressMap->Get((DWORDLONG)replayAddress);
+ return (void*)value.Address;
+}
+void *CompileResult::searchAddressMap(void *newAddress)
+{
+ if(AddressMap==nullptr)
+ return (void*)-1;
+ for(unsigned int i=0;i<AddressMap->GetCount();i++)
+ {
+ DWORDLONG replayAddress = AddressMap->GetRawKeys()[i];
+ Agnostic_AddressMap value = AddressMap->Get(replayAddress);
+ if((replayAddress<=(DWORDLONG)newAddress)&&((DWORDLONG)newAddress<(replayAddress+value.size)))
+ return (void *)(value.Address+((DWORDLONG)newAddress-replayAddress));
+ }
+ return (void*)-1;
+}
+
+void CompileResult::recReserveUnwindInfo(BOOL isFunclet, BOOL isColdCode, ULONG unwindSize)
+{
+ if(ReserveUnwindInfo == nullptr)
+ ReserveUnwindInfo = new DenseLightWeightMap<Agnostic_ReserveUnwindInfo>();
+
+ Agnostic_ReserveUnwindInfo value;
+
+ value.isFunclet = (DWORD)isFunclet;
+ value.isColdCode = (DWORD)isColdCode;
+ value.unwindSize = (DWORD)unwindSize;
+
+ ReserveUnwindInfo->Append(value);
+}
+void CompileResult::dmpReserveUnwindInfo(DWORD key, const Agnostic_ReserveUnwindInfo& value)
+{
+ printf("ReserveUnwindInfo key %u, value isFun-%u isCold-%u usz-%u",
+ key, value.isFunclet, value.isColdCode, value.unwindSize);
+}
+
+void CompileResult::recAllocUnwindInfo(BYTE *pHotCode, BYTE *pColdCode, ULONG startOffset, ULONG endOffset, ULONG unwindSize, BYTE *pUnwindBlock,
+ CorJitFuncKind funcKind)
+{
+ if(AllocUnwindInfo == nullptr)
+ AllocUnwindInfo = new DenseLightWeightMap<Agnostic_AllocUnwindInfo>();
+
+ Agnostic_AllocUnwindInfo value;
+ value.pHotCode = (DWORDLONG)pHotCode;
+ value.pColdCode = (DWORDLONG)pColdCode;
+ value.startOffset = (DWORD)startOffset;
+ value.endOffset = (DWORD)endOffset;
+ value.unwindSize = (DWORD)unwindSize;
+ value.pUnwindBlock_index = AllocUnwindInfo->AddBuffer((unsigned char*)pUnwindBlock, unwindSize);
+ value.funcKind = funcKind;
+
+ AllocUnwindInfo->Append(value);
+}
+void CompileResult::dmpAllocUnwindInfo(DWORD key, const Agnostic_AllocUnwindInfo& value)
+{
+ printf("AllocUnwindInfo key %u, value pHot-%016llX pCold-%016llX startOff-%u endOff-%u unwindSz-%u blki-%u funcKind-%u",
+ key,
+ value.pHotCode,
+ value.pColdCode,
+ value.startOffset,
+ value.endOffset,
+ value.unwindSize,
+ value.pUnwindBlock_index,
+ value.funcKind);
+}
+
+void CompileResult::recAllocBBProfileBuffer(ULONG count, ICorJitInfo::ProfileBuffer **profileBuffer, HRESULT result)
+{
+ if(AllocBBProfileBuffer == nullptr)
+ AllocBBProfileBuffer=new LightWeightMap<DWORD, Agnostic_AllocBBProfileBuffer>();
+
+ Agnostic_AllocBBProfileBuffer value;
+
+ value.count = (DWORD)count;
+ value.result = (DWORD)result;
+ value.profileBuffer_index = AllocBBProfileBuffer->AddBuffer((unsigned char*)*profileBuffer, count * sizeof(ICorJitInfo::ProfileBuffer));
+
+ AllocBBProfileBuffer->Add((DWORD)0, value);
+}
+void CompileResult::dmpAllocBBProfileBuffer(DWORD key, const Agnostic_AllocBBProfileBuffer& value)
+{
+ printf("AllocBBProfileBuffer key %u, value cnt-%u ind-%u res-%08X",
+ key,
+ value.count,
+ value.profileBuffer_index,
+ value.result);
+}
+HRESULT CompileResult::repAllocBBProfileBuffer(ULONG count, ICorJitInfo::ProfileBuffer **profileBuffer)
+{
+ Agnostic_AllocBBProfileBuffer value;
+ value = AllocBBProfileBuffer->Get((DWORD)0);
+
+ if(count != value.count)
+ __debugbreak();
+
+ HRESULT result = (HRESULT)value.result;
+ *profileBuffer = (ICorJitInfo::ProfileBuffer *)AllocBBProfileBuffer->GetBuffer(value.profileBuffer_index);
+ recAddressMap((void*)0x4242, (void*)*profileBuffer, count *(sizeof(ICorJitInfo::ProfileBuffer)));
+ return result;
+}
+
+void CompileResult::recRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO *callSig, CORINFO_METHOD_HANDLE methodHandle)
+{
+ repRecordCallSite(instrOffset, callSig, methodHandle);
+}
+
+void CompileResult::dmpRecordCallSite(DWORD key, const Agnostic_RecordCallSite& value)
+{
+ printf("RecordCallSite key %u, callSig{cc-%u rtc-%016llX rts-%016llX rt-%u flg-%u na-%u cc-%u ci-%u mc-%u mi-%u sig-%u pSig-%u scp-%016llX tok-%08X} ftn-%016llX",
+ key,
+ value.callSig.callConv,
+ value.callSig.retTypeClass,
+ value.callSig.retTypeSigClass,
+ value.callSig.retType,
+ value.callSig.flags,
+ value.callSig.numArgs,
+ value.callSig.sigInst_classInstCount,
+ value.callSig.sigInst_classInst_Index,
+ value.callSig.sigInst_methInstCount,
+ value.callSig.sigInst_methInst_Index,
+ value.callSig.cbSig,
+ value.callSig.pSig,
+ value.callSig.scope,
+ value.callSig.token,
+ value.methodHandle);
+}
+
+void CompileResult::repRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO *callSig, CORINFO_METHOD_HANDLE methodHandle)
+{
+ if (RecordCallSite == nullptr)
+ RecordCallSite = new LightWeightMap<DWORD, Agnostic_RecordCallSite>();
+
+ Agnostic_RecordCallSite value;
+ ZeroMemory(&value, sizeof(Agnostic_RecordCallSite));
+
+ if (callSig != nullptr)
+ {
+ value.callSig.callConv = (DWORD)callSig->callConv;
+ value.callSig.retTypeClass = (DWORDLONG)callSig->retTypeClass;
+ value.callSig.retTypeSigClass = (DWORDLONG)callSig->retTypeSigClass;
+ value.callSig.retType = (DWORD)callSig->retType;
+ value.callSig.flags = (DWORD)callSig->flags;
+ value.callSig.numArgs = (DWORD)callSig->numArgs;
+ value.callSig.sigInst_classInstCount = (DWORD)callSig->sigInst.classInstCount;
+ value.callSig.sigInst_classInst_Index = RecordCallSite->AddBuffer((unsigned char*)callSig->sigInst.classInst, callSig->sigInst.classInstCount*8); // porting issue
+ value.callSig.sigInst_methInstCount = (DWORD)callSig->sigInst.methInstCount;
+ value.callSig.sigInst_methInst_Index = RecordCallSite->AddBuffer((unsigned char*)callSig->sigInst.methInst, callSig->sigInst.methInstCount*8); // porting issue
+ value.callSig.args = (DWORDLONG)callSig->args;
+ value.callSig.cbSig = (DWORD)callSig->cbSig;
+ value.callSig.pSig = (DWORD)RecordCallSite->AddBuffer((unsigned char *)callSig->pSig, callSig->cbSig);
+ value.callSig.scope = (DWORDLONG)callSig->scope;
+ value.callSig.token = (DWORD)callSig->token;
+ }
+ else
+ {
+ value.callSig.callConv = (DWORD)-1;
+ value.callSig.retTypeClass = (DWORDLONG)-1;
+ value.callSig.retTypeSigClass = (DWORDLONG)-1;
+ value.callSig.retType = (DWORD)-1;
+ value.callSig.flags = (DWORD)-1;
+ value.callSig.numArgs = (DWORD)-1;
+ value.callSig.sigInst_classInstCount = (DWORD)-1;
+ value.callSig.sigInst_classInst_Index = (DWORD)-1;
+ value.callSig.sigInst_methInstCount = (DWORD)-1;
+ value.callSig.sigInst_methInst_Index = (DWORD)-1;
+ value.callSig.args = (DWORDLONG)-1;
+ value.callSig.cbSig = (DWORD)-1;
+ value.callSig.pSig = (DWORD)-1;
+ value.callSig.scope = (DWORDLONG)-1;
+ value.callSig.token = (DWORD)-1;
+ }
+
+ value.methodHandle = (DWORDLONG)methodHandle;
+
+ RecordCallSite->Add(instrOffset, value);
+}
+
+bool CompileResult::fndRecordCallSiteSigInfo(ULONG instrOffset, CORINFO_SIG_INFO *pCallSig)
+{
+ if (RecordCallSite == nullptr)
+ return false;
+
+ if (RecordCallSite->GetIndex(instrOffset) == -1)
+ return false;
+
+ Agnostic_RecordCallSite value = RecordCallSite->Get(instrOffset);
+
+ if (value.callSig.callConv == -1)
+ return false;
+
+ pCallSig->callConv = (CorInfoCallConv)value.callSig.callConv;
+ pCallSig->retTypeClass = (CORINFO_CLASS_HANDLE)value.callSig.retTypeClass;
+ pCallSig->retTypeSigClass = (CORINFO_CLASS_HANDLE)value.callSig.retTypeSigClass;
+ pCallSig->retType = (CorInfoType)value.callSig.retType;
+ pCallSig->flags = (unsigned)value.callSig.flags;
+ pCallSig->numArgs = (unsigned)value.callSig.numArgs;
+ pCallSig->sigInst.classInstCount = (unsigned)value.callSig.sigInst_classInstCount;
+ pCallSig->sigInst.classInst = (CORINFO_CLASS_HANDLE*)RecordCallSite->GetBuffer(value.callSig.sigInst_classInst_Index);
+ pCallSig->sigInst.methInstCount = (unsigned)value.callSig.sigInst_methInstCount;
+ pCallSig->sigInst.methInst = (CORINFO_CLASS_HANDLE*)RecordCallSite->GetBuffer(value.callSig.sigInst_methInst_Index);
+ pCallSig->args = (CORINFO_ARG_LIST_HANDLE)value.callSig.args;
+ pCallSig->cbSig = (unsigned int)value.callSig.cbSig;
+ pCallSig->pSig = (PCCOR_SIGNATURE)RecordCallSite->GetBuffer(value.callSig.pSig);
+ pCallSig->scope = (CORINFO_MODULE_HANDLE)value.callSig.scope;
+ pCallSig->token = (mdToken)value.callSig.token;
+
+ return true;
+}
+
+bool CompileResult::fndRecordCallSiteMethodHandle(ULONG instrOffset, CORINFO_METHOD_HANDLE *pMethodHandle)
+{
+ if (RecordCallSite == nullptr)
+ return false;
+
+ if (RecordCallSite->GetIndex(instrOffset) == -1)
+ return false;
+
+ Agnostic_RecordCallSite value = RecordCallSite->Get(instrOffset);
+ *pMethodHandle = (CORINFO_METHOD_HANDLE)value.methodHandle;
+
+ return true;
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/compileresult.h b/src/ToolBox/superpmi/superpmi-shared/compileresult.h
new file mode 100644
index 0000000000..8fc3f7a352
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/compileresult.h
@@ -0,0 +1,276 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//----------------------------------------------------------
+// CompileResult.h - CompileResult contains the stuff generated by a compilation
+//----------------------------------------------------------
+#ifndef _CompileResult
+#define _CompileResult
+
+#include "runtimedetails.h"
+#include "lightweightmap.h"
+
+class CompileResult
+{
+public:
+#pragma pack(push, 1)
+ struct Agnostic_RecordRelocation
+ {
+ DWORDLONG location;
+ DWORDLONG target;
+ DWORD fRelocType;
+ DWORD slotNum;
+ DWORD addlDelta;
+ };
+ struct Capture_AllocMemDetails
+ {
+ ULONG hotCodeSize;
+ ULONG coldCodeSize;
+ ULONG roDataSize;
+ ULONG xcptnsCount;
+ CorJitAllocMemFlag flag;
+ void *hotCodeBlock;
+ void *coldCodeBlock;
+ void *roDataBlock;
+ };
+ struct allocGCInfoDetails
+ {
+ size_t size;
+ void* retval;
+ };
+ struct Agnostic_AddressMap
+ {
+ DWORDLONG Address;
+ DWORD size;
+ };
+ struct Agnostic_AllocGCInfo
+ {
+ DWORDLONG size;
+ DWORD retval_offset;
+ };
+ struct Agnostic_AllocMemDetails
+ {
+ DWORD hotCodeSize;
+ DWORD coldCodeSize;
+ DWORD roDataSize;
+ DWORD xcptnsCount;
+ DWORD flag;
+ DWORD hotCodeBlock_offset;
+ DWORD coldCodeBlock_offset;
+ DWORD roDataBlock_offset;
+ DWORDLONG hotCodeBlock;
+ DWORDLONG coldCodeBlock;
+ DWORDLONG roDataBlock;
+ };
+ struct Agnostic_AllocUnwindInfo
+ {
+ DWORDLONG pHotCode;
+ DWORDLONG pColdCode;
+ DWORD startOffset;
+ DWORD endOffset;
+ DWORD unwindSize;
+ DWORD pUnwindBlock_index;
+ DWORD funcKind;
+ };
+ struct Agnostic_CompileMethodResults
+ {
+ DWORDLONG nativeEntry;
+ DWORD nativeSizeOfCode;
+ DWORD CorJitResult;
+ };
+ struct Agnostic_ReportInliningDecision
+ {
+ DWORDLONG inlinerHnd;
+ DWORDLONG inlineeHnd;
+ DWORD inlineResult;
+ DWORD reason_offset;
+ };
+ struct Agnostic_ReportTailCallDecision
+ {
+ DWORDLONG callerHnd;
+ DWORDLONG calleeHnd;
+ DWORD fIsTailPrefix;
+ DWORD tailCallResult;
+ DWORD reason_index;
+ };
+ struct Agnostic_ReserveUnwindInfo
+ {
+ DWORD isFunclet;
+ DWORD isColdCode;
+ DWORD unwindSize;
+ };
+ struct Agnostic_SetBoundaries
+ {
+ DWORDLONG ftn;
+ DWORD cMap;
+ DWORD pMap_offset;
+ };
+ struct Agnostic_SetVars
+ {
+ DWORDLONG ftn;
+ DWORD cVars;
+ DWORD vars_offset;
+ };
+ struct Agnostic_CORINFO_EH_CLAUSE2
+ {
+ DWORD Flags;
+ DWORD TryOffset;
+ DWORD TryLength;
+ DWORD HandlerOffset;
+ DWORD HandlerLength;
+ DWORD ClassToken; //one view of symetric union
+ };
+ struct Agnostic_AllocBBProfileBuffer
+ {
+ DWORD count;
+ DWORD profileBuffer_index;
+ DWORD result;
+ };
+ struct Agnostic_CORINFO_SIG_INFO2
+ {
+ DWORD callConv;
+ DWORDLONG retTypeClass;
+ DWORDLONG retTypeSigClass;
+ DWORD retType;
+ DWORD flags;
+ DWORD numArgs;
+ DWORD sigInst_classInstCount;
+ DWORD sigInst_classInst_Index;
+ DWORD sigInst_methInstCount;
+ DWORD sigInst_methInst_Index;
+ DWORDLONG args;
+ DWORD pSig;
+ DWORD cbSig;
+ DWORDLONG scope;
+ DWORD token;
+ };
+ struct Agnostic_RecordCallSite
+ {
+ Agnostic_CORINFO_SIG_INFO2 callSig;
+ DWORDLONG methodHandle;
+ };
+#pragma pack(pop)
+
+ CompileResult();
+ ~CompileResult();
+
+ bool IsEmpty();
+
+ void AddCall(const char *name);
+ unsigned int CallLog_GetCount();
+ bool CallLog_Contains(const char *str);
+ void dmpCallLog(DWORD key, DWORD value);
+
+ void dumpToConsole();
+
+ HANDLE getCodeHeap();
+
+ void recAssert(const char *buff);
+ void dmpAssertLog(DWORD key, DWORD value);
+ const char *repAssert();
+
+ void recAllocMem(ULONG hotCodeSize, ULONG coldCodeSize, ULONG roDataSize, ULONG xcptnsCount, CorJitAllocMemFlag flag,
+ void **hotCodeBlock, void **coldCodeBlock, void **roDataBlock);
+ void recAllocMemCapture();
+ void dmpAllocMem(DWORD key, const Agnostic_AllocMemDetails& value);
+ void repAllocMem(ULONG *hotCodeSize, ULONG *coldCodeSize, ULONG *roDataSize, ULONG *xcptnsCount, CorJitAllocMemFlag *flag,
+ unsigned char **hotCodeBlock, unsigned char **coldCodeBlock, unsigned char **roDataBlock,
+ void **orig_hotCodeBlock, void **orig_coldCodeBlock, void **orig_roDataBlock);
+
+ void recSetBoundaries(CORINFO_METHOD_HANDLE ftn, ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap);
+ void dmpSetBoundaries(DWORD key, const Agnostic_SetBoundaries& value);
+ bool repSetBoundaries(CORINFO_METHOD_HANDLE *ftn, ULONG32 *cMap, ICorDebugInfo::OffsetMapping **pMap);
+
+ void recSetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars);
+ void dmpSetVars(DWORD key, const Agnostic_SetVars& value);
+ bool repSetVars(CORINFO_METHOD_HANDLE *ftn, ULONG32 *cVars, ICorDebugInfo::NativeVarInfo **vars);
+
+ void recAllocGCInfo(size_t size, void *retval);
+ void recAllocGCInfoCapture();
+ void dmpAllocGCInfo(DWORD key, const Agnostic_AllocGCInfo& value);
+ void repAllocGCInfo(size_t *size, void **retval);
+
+ void recCompileMethod(BYTE **nativeEntry, ULONG *nativeSizeOfCode, CorJitResult result);
+ void dmpCompileMethod(DWORD key, const Agnostic_CompileMethodResults& value);
+ void repCompileMethod(BYTE **nativeEntry, ULONG *nativeSizeOfCode, CorJitResult *result);
+
+ void recMessageLog(const char* fmt, ...);
+ void dmpMessageLog(DWORD key, DWORD value);
+
+ void recClassMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE cls);
+ void dmpClassMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value);
+
+ void recReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd, CorInfoInline inlineResult, const char * reason);
+ void dmpReportInliningDecision(DWORD key, const Agnostic_ReportInliningDecision& value);
+ CorInfoInline CompileResult::repReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd);
+
+ void recSetEHcount(unsigned cEH);
+ void dmpSetEHcount(DWORD key, DWORD value);
+ ULONG repSetEHcount();
+
+ void recSetEHinfo(unsigned EHnumber, const CORINFO_EH_CLAUSE *clause);
+ void dmpSetEHinfo(DWORD key, const Agnostic_CORINFO_EH_CLAUSE2& value);
+ void repSetEHinfo(unsigned EHnumber, ULONG *flags, ULONG *tryOffset, ULONG *tryLength, ULONG *handlerOffset, ULONG *handlerLength, ULONG *classToken);
+
+ void recSetMethodAttribs (CORINFO_METHOD_HANDLE ftn, CorInfoMethodRuntimeFlags attribs);
+ void dmpSetMethodAttribs(DWORDLONG key, DWORD value);
+ CorInfoMethodRuntimeFlags repSetMethodAttribs (CORINFO_METHOD_HANDLE ftn);
+
+ void recMethodMustBeLoadedBeforeCodeIsRun(CORINFO_METHOD_HANDLE method);
+ void dmpMethodMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value);
+
+ void recReportTailCallDecision(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult, const char * reason);
+ void dmpReportTailCallDecision(DWORD key, const Agnostic_ReportTailCallDecision& value);
+
+ void recReportFatalError(CorJitResult result);
+ void dmpReportFatalError(DWORD key, DWORD value);
+
+ void recRecordRelocation(void *location, void *target, WORD fRelocType, WORD slotNum, INT32 addlDelta);
+ void dmpRecordRelocation(DWORD key, const Agnostic_RecordRelocation& value);
+ void repRecordRelocation(void *location, void *target, WORD fRelocType, WORD slotNum, INT32 addlDelta);
+ void applyRelocs(unsigned char *block1, ULONG blocksize1, void *originalAddr);
+
+ void recProcessName(const char* name);
+ void dmpProcessName(DWORD key, DWORD value);
+ const char *repProcessName();
+
+ void recAddressMap(void *original_address, void *replay_address, unsigned int size);
+ void dmpAddressMap(DWORDLONG key, const Agnostic_AddressMap& value);
+ void* repAddressMap(void *replay_address);
+ void *searchAddressMap(void *replay_address);
+
+ void recReserveUnwindInfo(BOOL isFunclet, BOOL isColdCode, ULONG unwindSize);
+ void dmpReserveUnwindInfo(DWORD key, const Agnostic_ReserveUnwindInfo& value);
+
+ void recAllocUnwindInfo(BYTE *pHotCode, BYTE *pColdCode, ULONG startOffset, ULONG endOffset, ULONG unwindSize, BYTE *pUnwindBlock,
+ CorJitFuncKind funcKind);
+ void dmpAllocUnwindInfo(DWORD key, const Agnostic_AllocUnwindInfo& value);
+
+ void recAllocBBProfileBuffer(ULONG count, ICorJitInfo::ProfileBuffer **profileBuffer, HRESULT result);
+ void dmpAllocBBProfileBuffer(DWORD key, const Agnostic_AllocBBProfileBuffer& value);
+ HRESULT repAllocBBProfileBuffer(ULONG count, ICorJitInfo::ProfileBuffer **profileBuffer);
+
+ void recRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO *callSig, CORINFO_METHOD_HANDLE methodHandle);
+ void dmpRecordCallSite(DWORD key, const Agnostic_RecordCallSite& value);
+ void repRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO *callSig, CORINFO_METHOD_HANDLE methodHandle);
+ bool fndRecordCallSiteSigInfo(ULONG instrOffset, CORINFO_SIG_INFO *pCallSig);
+ bool fndRecordCallSiteMethodHandle(ULONG instrOffset, CORINFO_METHOD_HANDLE *pMethodHandle);
+
+ DOUBLE secondsToCompile;
+ ULONGLONG clockCyclesToCompile;
+
+ #define LWM(map,key,value) LightWeightMap<key, value>* map;
+ #define DENSELWM(map,value) DenseLightWeightMap<value>* map;
+ #include "crlwmlist.h"
+
+//not persisted to disk.
+public:
+ LightWeightMap<DWORDLONG, DWORD> *CallTargetTypes;
+private:
+ HANDLE codeHeap;
+ Capture_AllocMemDetails allocMemDets;
+ allocGCInfoDetails allocGCInfoDets;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h b/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h
new file mode 100644
index 0000000000..84bf13e553
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h
@@ -0,0 +1,46 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// crlwmlist.h - List of all LightWeightMap in CompileResult.
+// To use, #define LWM(map, key, value) to something.
+// If you need to distinguish DenseLightWeightMap, #define DENSELWM(map, value) as well.
+//----------------------------------------------------------
+
+#ifndef LWM
+#error Define LWM before including this file.
+#endif
+
+// If the key is needed, then DENSELWM must be defined.
+#ifndef DENSELWM
+#define DENSELWM(map, value) LWM(map, this_is_an_error, value)
+#endif
+
+LWM(AddressMap, DWORDLONG, CompileResult::Agnostic_AddressMap)
+LWM(AllocBBProfileBuffer, DWORD, CompileResult::Agnostic_AllocBBProfileBuffer)
+LWM(AllocGCInfo, DWORD, CompileResult::Agnostic_AllocGCInfo)
+LWM(AllocMem, DWORD, CompileResult::Agnostic_AllocMemDetails)
+DENSELWM(AllocUnwindInfo, CompileResult::Agnostic_AllocUnwindInfo)
+DENSELWM(AssertLog, DWORD)
+DENSELWM(CallLog, DWORD)
+DENSELWM(ClassMustBeLoadedBeforeCodeIsRun, DWORDLONG)
+LWM(CompileMethod, DWORD, CompileResult::Agnostic_CompileMethodResults)
+DENSELWM(MessageLog, DWORD)
+DENSELWM(MethodMustBeLoadedBeforeCodeIsRun, DWORDLONG)
+DENSELWM(ProcessName, DWORD)
+LWM(RecordCallSite, DWORD, CompileResult::Agnostic_RecordCallSite)
+DENSELWM(RecordRelocation, CompileResult::Agnostic_RecordRelocation)
+DENSELWM(ReportFatalError, DWORD)
+DENSELWM(ReportInliningDecision, CompileResult::Agnostic_ReportInliningDecision)
+DENSELWM(ReportTailCallDecision, CompileResult::Agnostic_ReportTailCallDecision)
+DENSELWM(ReserveUnwindInfo, CompileResult::Agnostic_ReserveUnwindInfo)
+LWM(SetBoundaries, DWORD, CompileResult::Agnostic_SetBoundaries)
+LWM(SetEHcount, DWORD, DWORD)
+LWM(SetEHinfo, DWORD, CompileResult::Agnostic_CORINFO_EH_CLAUSE2)
+LWM(SetMethodAttribs, DWORDLONG, DWORD)
+LWM(SetVars, DWORD, CompileResult::Agnostic_SetVars)
+
+#undef LWM
+#undef DENSELWM
diff --git a/src/ToolBox/superpmi/superpmi-shared/errorhandling.cpp b/src/ToolBox/superpmi/superpmi-shared/errorhandling.cpp
new file mode 100644
index 0000000000..ad871db4ce
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/errorhandling.cpp
@@ -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.
+//
+
+#include "standardpch.h"
+#include "errorhandling.h"
+#include "logging.h"
+#include "runtimedetails.h"
+
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode)
+{
+ RaiseException(exceptionCode, 0, 0, nullptr);
+}
+
+// Allocating memory here seems moderately dangerous: we'll probably leak like a sieve...
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode, va_list args, const char *message)
+{
+ char *buffer = new char[8192];
+ ULONG_PTR *ptr = new ULONG_PTR();
+ *ptr = (ULONG_PTR)buffer;
+ _vsnprintf_s(buffer, 8192, 8191, message, args);
+
+ RaiseException(exceptionCode, 0, 1, ptr);
+}
+
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode, const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ ThrowException(exceptionCode, ap, msg);
+}
+
+SpmiException::SpmiException(PEXCEPTION_POINTERS exp)
+ : exCode(exp->ExceptionRecord->ExceptionCode)
+{
+ exMessage = (exp->ExceptionRecord->NumberParameters != 1) ? nullptr : (char *) exp->ExceptionRecord->ExceptionInformation[0];
+}
+
+SpmiException::SpmiException(DWORD exceptionCode, char* exceptionMessage)
+ : exCode(exceptionCode)
+ , exMessage(exceptionMessage)
+{
+}
+
+#if 0
+SpmiException::~SpmiException()
+{
+ delete[] exMessage;
+ exMessage = nullptr;
+}
+#endif
+
+char *SpmiException::GetExceptionMessage()
+{
+ return exMessage;
+}
+
+void SpmiException::ShowAndDeleteMessage()
+{
+ if (exMessage != nullptr)
+ {
+ LogError("Exception thrown: %s", exMessage);
+ delete[] exMessage;
+ exMessage = nullptr;
+ }
+}
+
+void SpmiException::DeleteMessage()
+{
+ delete[] exMessage;
+ exMessage = nullptr;
+}
+
+DWORD SpmiException::GetCode()
+{
+ return exCode;
+}
+
+// This filter function executes the handler only for EXCEPTIONCODE_MC, otherwise it continues the handler search.
+LONG FilterSuperPMIExceptions_CatchMC(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ return (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTIONCODE_MC) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
+}
+
+// This filter function captures the exception pointers and continues searching.
+LONG FilterSuperPMIExceptions_CaptureExceptionAndContinue(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ FilterSuperPMIExceptionsParam_CaptureException* pSPMIEParam = (FilterSuperPMIExceptionsParam_CaptureException *)lpvParam;
+ pSPMIEParam->exceptionPointers = *pExceptionPointers; // Capture the exception pointers for use later
+ pSPMIEParam->exceptionCode = pSPMIEParam->exceptionPointers.ExceptionRecord->ExceptionCode;
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+LONG FilterSuperPMIExceptions_CaptureExceptionAndStop(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ FilterSuperPMIExceptionsParam_CaptureException* pSPMIEParam = (FilterSuperPMIExceptionsParam_CaptureException *)lpvParam;
+ pSPMIEParam->exceptionPointers = *pExceptionPointers; // Capture the exception pointers for use later
+ pSPMIEParam->exceptionCode = pSPMIEParam->exceptionPointers.ExceptionRecord->ExceptionCode;
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+bool IsSuperPMIException(unsigned code)
+{
+ switch (code)
+ {
+ case EXCEPTIONCODE_MC:
+ case EXCEPTIONCODE_LWM:
+ case EXCEPTIONCODE_CALLUTILS:
+ case EXCEPTIONCODE_TYPEUTILS:
+ case EXCEPTIONCODE_ASSERT:
+ return true;
+ default:
+ if ((EXCEPTIONCODE_DebugBreakorAV <= code) && (code < EXCEPTIONCODE_DebugBreakorAV_MAX))
+ {
+ return true;
+ }
+ return false;
+ }
+}
+
+// This filter function executes the handler only for non-SuperPMI generated exceptions, otherwise it continues the handler search.
+// This allows for SuperPMI-thrown exceptions to pass through the JIT and be caught by the outer SuperPMI handler.
+LONG FilterSuperPMIExceptions_CatchNonSuperPMIException(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ return !IsSuperPMIException(pExceptionPointers->ExceptionRecord->ExceptionCode);
+}
+
+bool RunWithErrorTrap(void (*function)(void*), void* param)
+{
+ bool success = true;
+
+ struct TrapParam {
+ void (*function)(void*);
+ void *param;
+ } trapParam;
+ trapParam.function = function;
+ trapParam.param = param;
+
+ PAL_TRY(TrapParam*, pTrapParam, &trapParam)
+ {
+ pTrapParam->function(pTrapParam->param);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchNonSuperPMIException)
+ {
+ success = false;
+ }
+ PAL_ENDTRY
+
+ return success;
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/errorhandling.h b/src/ToolBox/superpmi/superpmi-shared/errorhandling.h
new file mode 100644
index 0000000000..412bf97ba6
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/errorhandling.h
@@ -0,0 +1,88 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// ErrorHandling.h - Helpers & whatnot for using SEH for errors
+//----------------------------------------------------------
+#ifndef _ErrorHandling
+#define _ErrorHandling
+
+#include "logging.h"
+
+// EXCEPTIONCODE_DebugBreakorAV is just the base exception number; calls to DebugBreakorAV()
+// pass a unique number to add to this. EXCEPTIONCODE_DebugBreakorAV_MAX is the maximum number
+// of this exception range.
+#define EXCEPTIONCODE_DebugBreakorAV 0xe0421000
+#define EXCEPTIONCODE_DebugBreakorAV_MAX 0xe0422000
+
+#define EXCEPTIONCODE_MC 0xe0422000
+#define EXCEPTIONCODE_LWM 0xe0423000
+#define EXCEPTIONCODE_CALLUTILS 0xe0426000
+#define EXCEPTIONCODE_TYPEUTILS 0xe0427000
+#define EXCEPTIONCODE_ASSERT 0xe0440000
+
+// RaiseException wrappers
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode);
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode, const char *message, ...);
+
+// Assert stuff
+#define AssertCodeMsg(expr, exCode, msg, ...) \
+ do { \
+ if (!(expr)) LogException(exCode, "SuperPMI assertion '%s' failed (" #msg ")", #expr, ##__VA_ARGS__); \
+ } while (0)
+
+#define AssertCode(expr, exCode) \
+ do { \
+ if (!(expr)) LogException(exCode, "SuperPMI assertion '%s' failed", #expr); \
+ } while (0)
+
+#define AssertMsg(expr, msg, ...) AssertCodeMsg(expr, EXCEPTIONCODE_ASSERT, msg, ##__VA_ARGS__)
+#define Assert(expr) AssertCode(expr, EXCEPTIONCODE_ASSERT)
+
+class SpmiException
+{
+private:
+ DWORD exCode;
+ char* exMessage;
+
+public:
+ SpmiException(PEXCEPTION_POINTERS exp);
+ SpmiException(DWORD exceptionCode, char* exceptionMessage);
+#if 0
+ ~SpmiException();
+#endif
+
+ char *GetExceptionMessage();
+ DWORD GetCode();
+
+ void ShowAndDeleteMessage();
+ void DeleteMessage();
+};
+
+//
+// Functions and types used by PAL_TRY-related macros.
+//
+
+extern LONG FilterSuperPMIExceptions_CatchMC(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam);
+
+struct FilterSuperPMIExceptionsParam_CaptureException
+{
+ EXCEPTION_POINTERS exceptionPointers;
+ DWORD exceptionCode;
+
+ FilterSuperPMIExceptionsParam_CaptureException()
+ : exceptionCode(0)
+ {
+ exceptionPointers.ExceptionRecord = nullptr;
+ exceptionPointers.ContextRecord = nullptr;
+ }
+};
+
+extern LONG FilterSuperPMIExceptions_CaptureExceptionAndContinue(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam);
+extern LONG FilterSuperPMIExceptions_CaptureExceptionAndStop(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam);
+
+extern bool RunWithErrorTrap(void (*function)(void*), void* param);
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h
new file mode 100644
index 0000000000..671b45b392
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h
@@ -0,0 +1,70 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitCompilerImpl
+#define _ICorJitCompilerImpl
+
+// ICorJitCompilerImpl: declare for implementation all the members of the ICorJitCompiler interface (which are
+// specified as pure virtual methods). This is done once, here, and all implementations share it,
+// to avoid duplicated declarations. This file is #include'd within all the ICorJitCompiler implementation
+// classes.
+//
+// NOTE: this file is in exactly the same order, with exactly the same whitespace, as the ICorJitCompiler
+// interface declaration (with the "virtual" and "= 0" syntax removed). This is to make it easy to compare
+// against the interface declaration.
+
+public:
+ // compileMethod is the main routine to ask the JIT Compiler to create native code for a method. The
+ // method to be compiled is passed in the 'info' parameter, and the code:ICorJitInfo is used to allow the
+ // JIT to resolve tokens, and make any other callbacks needed to create the code. nativeEntry, and
+ // nativeSizeOfCode are just for convenience because the JIT asks the EE for the memory to emit code into
+ // (see code:ICorJitInfo.allocMem), so really the EE already knows where the method starts and how big
+ // it is (in fact, it could be in more than one chunk).
+ //
+ // * In the 32 bit jit this is implemented by code:CILJit.compileMethod
+ // * For the 64 bit jit this is implemented by code:PreJit.compileMethod
+ //
+ // Note: Obfuscators that are hacking the JIT depend on this method having __stdcall calling convention
+ CorJitResult __stdcall compileMethod (
+ ICorJitInfo *comp, /* IN */
+ struct CORINFO_METHOD_INFO *info, /* IN */
+ unsigned /* code:CorJitFlag */ flags, /* IN */
+ BYTE **nativeEntry, /* OUT */
+ ULONG *nativeSizeOfCode /* OUT */
+ );
+
+ // Some JIT compilers (most notably Phoenix), cache information about EE structures from one invocation
+ // of the compiler to the next. This can be a problem when appdomains are unloaded, as some of this
+ // cached information becomes stale. The code:ICorJitCompiler.isCacheCleanupRequired is called by the EE
+ // early first to see if jit needs these notifications, and if so, the EE will call ClearCache is called
+ // whenever the compiler should abandon its cache (eg on appdomain unload)
+ void clearCache();
+ BOOL isCacheCleanupRequired();
+
+ // Do any appropriate work at process shutdown. Default impl is to do nothing.
+ void ProcessShutdownWork(ICorStaticInfo* info); /* {}; */
+
+ // The EE asks the JIT for a "version identifier". This represents the version of the JIT/EE interface.
+ // If the JIT doesn't implement the same JIT/EE interface expected by the EE (because the JIT doesn't
+ // return the version identifier that the EE expects), then the EE fails to load the JIT.
+ //
+ void getVersionIdentifier(
+ GUID* versionIdentifier /* OUT */
+ );
+
+ // When the EE loads the System.Numerics.Vectors assembly, it asks the JIT what length (in bytes) of
+ // SIMD vector it supports as an intrinsic type. Zero means that the JIT does not support SIMD
+ // intrinsics, so the EE should use the default size (i.e. the size of the IL implementation).
+ unsigned getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags); /* { return 0; } */
+
+ // IL obfuscators sometimes interpose on the EE-JIT interface. This function allows the VM to
+ // tell the JIT to use a particular ICorJitCompiler to implement the methods of this interface,
+ // and not to implement those methods itself. The JIT must not return this method when getJit()
+ // is called. Instead, it must pass along all calls to this interface from within its own
+ // ICorJitCompiler implementation. If 'realJitCompiler' is nullptr, then the JIT should resume
+ // executing all the functions itself.
+ void setRealJit(ICorJitCompiler* realJitCompiler); /* { } */
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjithostimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjithostimpl.h
new file mode 100644
index 0000000000..8bd09e5331
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/icorjithostimpl.h
@@ -0,0 +1,55 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef _ICorJitHostImpl
+#define _ICorJitHostImpl
+
+// ICorJitHost
+//
+// ICorJitHost provides the interface that the JIT uses to access some functionality that
+// would normally be provided by the operating system. This is intended to allow for
+// host-specific policies re: memory allocation, configuration value access, etc. It is
+// expected that the `ICorJitHost` value provided to `jitStartup` lives at least as
+// long as the JIT itself.
+
+// ICorJitHostImpl: declare for implementation all the members of the ICorJitHost interface (which are
+// specified as pure virtual methods). This is done once, here, and all implementations share it,
+// to avoid duplicated declarations. This file is #include'd within all the ICorJitHost implementation
+// classes.
+//
+// NOTE: this file is in exactly the same order, with exactly the same whitespace, as the ICorJitHost
+// interface declaration (with the "virtual" and "= 0" syntax removed). This is to make it easy to compare
+// against the interface declaration.
+
+public:
+ // Allocate memory of the given size in bytes. All bytes of the returned block
+ // must be initialized to zero. If `usePageAllocator` is true, the implementation
+ // should use an allocator that deals in OS pages if one exists.
+ void* allocateMemory(size_t size, bool usePageAllocator = false);
+
+ // Frees memory previous obtained by a call to `ICorJitHost::allocateMemory`. The
+ // value of the `usePageAllocator` parameter must match the value that was
+ // provided to the call to used to allocate the memory.
+ void freeMemory(void* block, bool usePageAllocator = false);
+
+ // Return an integer config value for the given key, if any exists.
+ int getIntConfigValue(
+ const wchar_t* name,
+ int defaultValue
+ );
+
+ // Return a string config value for the given key, if any exists.
+ const wchar_t* getStringConfigValue(
+ const wchar_t* name
+ );
+
+ // Free a string ConfigValue returned by the runtime.
+ // JITs using the getStringConfigValue query are required
+ // to return the string values to the runtime for deletion.
+ // This avoids leaking the memory in the JIT.
+ void freeStringConfigValue(
+ const wchar_t* value
+ );
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
new file mode 100644
index 0000000000..6eb862c8b8
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
@@ -0,0 +1,1316 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitInfoImpl
+#define _ICorJitInfoImpl
+
+// ICorJitInfoImpl: declare for implementation all the members of the ICorJitInfo interface (which are
+// specified as pure virtual methods). This is done once, here, and all implementations share it,
+// to avoid duplicated declarations. This file is #include'd within all the ICorJitInfo implementation
+// classes.
+//
+// NOTE: this file is in exactly the same order, with exactly the same whitespace, as the ICorJitInfo
+// interface declaration (with the "virtual" and "= 0" syntax removed). This is to make it easy to compare
+// against the interface declaration.
+
+public:
+ /**********************************************************************************/
+ //
+ // ICorMethodInfo
+ //
+ /**********************************************************************************/
+
+ // return flags (defined above, CORINFO_FLG_PUBLIC ...)
+ DWORD getMethodAttribs (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ );
+
+ // sets private JIT flags, which can be, retrieved using getAttrib.
+ void setMethodAttribs (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CorInfoMethodRuntimeFlags attribs /* IN */
+ );
+
+ // 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 getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent = NULL /* IN */
+ );
+
+ /*********************************************************************
+ * 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 getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ );
+
+ // 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 = NULL, 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 canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ );
+
+ // 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 reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * 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 canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ );
+
+ // 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 reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason);
+
+ // get individual exception handler
+ void getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ );
+
+ // return class it belongs to
+ CORINFO_CLASS_HANDLE getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ );
+
+ // return module it belongs to
+ CORINFO_MODULE_HANDLE getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ );
+
+ // This function returns the offset of the specified method in the
+ // vtable of it's owning class or interface.
+ void getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ );
+
+ // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+ // getIntrinsicID() returns the intrinsic ID.
+ // *pMustExpand tells whether or not JIT must expand the intrinsic.
+#if COR_JIT_EE_VERSION > 460
+ CorInfoIntrinsics getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand = NULL /* OUT */
+ );
+#else
+ CorInfoIntrinsics getIntrinsicID(
+ CORINFO_METHOD_HANDLE method
+ );
+#endif
+
+ // Is the given module the System.Numerics.Vectors module?
+ // This defaults to false.
+ bool isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ ); /* { return false; } */
+
+ // return the unmanaged calling convention for a PInvoke
+ CorInfoUnmanagedCallConv getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE 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 pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ );
+
+ // Check constraints on method type arguments (only).
+ // The parent class should be checked separately using satisfiesClassConstraints(parent).
+ BOOL satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE 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 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 */
+ );
+
+ // Determines whether the delegate creation obeys security transparency rules
+ BOOL isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ );
+
+
+ // Indicates if the method is an instance of the generic
+ // method that passes (or has passed) verification
+ CorInfoInstantiationVerification isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ );
+
+ // Loads the constraints on a typical method definition, detecting cycles;
+ // for use in verification.
+ void initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ );
+
+ // Returns enum whether the method does not require verification
+ // Also see ICorModuleInfo::canSkipVerification
+ CorInfoCanSkipVerificationResult canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ );
+
+ // load and restore the method
+ void methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ );
+
+ CORINFO_METHOD_HANDLE mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ );
+
+ // 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 getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ );
+
+ /**********************************************************************************/
+ //
+ // ICorModuleInfo
+ //
+ /**********************************************************************************/
+
+ // Resolve metadata token into runtime method handles. This function may not
+ // return normally (e.g. it may throw) if it encounters invalid metadata or other
+ // failures during token resolution.
+ void resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken);
+
+#if COR_JIT_EE_VERSION > 460
+ // Attempt to resolve a metadata token into a runtime method handle. Returns true
+ // if resolution succeeded and false otherwise (e.g. if it encounters invalid metadata
+ // during token reoslution). This method should be used instead of `resolveToken` in
+ // situations that need to be resilient to invalid metadata.
+ bool tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken);
+#endif
+
+ // Signature information about the call sig
+ void findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ );
+
+ // 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 findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ );
+
+ CORINFO_CLASS_HANDLE getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */);
+
+ // 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 canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ );
+
+ // Checks if the given metadata token is valid
+ BOOL isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ );
+
+ // Checks if the given metadata token is valid StringRef
+ BOOL isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ );
+
+ BOOL shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE 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 asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // for completeness
+ const char* getClassName (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+
+ // 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 appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ );
+
+ // Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+ BOOL isValueClass(CORINFO_CLASS_HANDLE cls);
+
+ // If this method returns true, JIT will do optimization to inline the check for
+ // GetTypeFromHandle(handle) == obj.GetType()
+ BOOL canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls);
+
+ // return flags (defined above, CORINFO_FLG_PUBLIC ...)
+ DWORD getClassAttribs (
+ CORINFO_CLASS_HANDLE 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 isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls);
+
+ CORINFO_MODULE_HANDLE getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Returns the assembly that contains the module "mod".
+ CORINFO_ASSEMBLY_HANDLE getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ );
+
+ // Returns the name of the assembly "assem".
+ const char* getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE 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* LongLifetimeMalloc(size_t sz);
+ void LongLifetimeFree(void* obj);
+
+ size_t getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ );
+
+ // return the number of bytes needed by an instance of the class
+ unsigned getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ unsigned getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint = FALSE
+ );
+
+ // 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 getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ );
+
+ // returns the number of instance fields in a class
+ unsigned getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ );
+
+ CORINFO_FIELD_HANDLE getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ );
+
+ BOOL checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ );
+
+ // returns the "NEW" helper optimized for "newCls."
+ CorInfoHelpFunc getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ );
+
+ // returns the newArr (1-Dim array) helper optimized for "arrayCls."
+ CorInfoHelpFunc getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ );
+
+ // returns the optimized "IsInstanceOf" or "ChkCast" helper
+ CorInfoHelpFunc getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ );
+
+ // returns helper to trigger static constructor
+ CorInfoHelpFunc getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ );
+
+ CorInfoHelpFunc getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE 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 getTypeForBox(
+ CORINFO_CLASS_HANDLE 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 getBoxHelper(
+ CORINFO_CLASS_HANDLE 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 getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+#if COR_JIT_EE_VERSION > 460
+ bool getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ );
+
+ void getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ );
+#else
+ void getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ );
+#endif
+
+
+ const char* getHelperName(
+ CorInfoHelpFunc
+ );
+
+ // 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 initClass(
+ CORINFO_FIELD_HANDLE field, // Non-NULL - inquire about cctor trigger before static field access
+ // NULL - 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 = FALSE // TRUE means don't actually run it
+ );
+
+ // 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 classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // returns the class handle for the special builtin classes
+ CORINFO_CLASS_HANDLE getBuiltinClass (
+ CorInfoClassId classId
+ );
+
+ // "System.Int32" ==> CORINFO_TYPE_INT..
+ CorInfoType getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // TRUE if child is a subtype of parent
+ // if parent is an interface, then does child implement / extend parent
+ BOOL canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ );
+
+ // TRUE if cls1 and cls2 are considered equivalent types.
+ BOOL areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ );
+
+ // returns is the intersection of cls1 and cls2.
+ CORINFO_CLASS_HANDLE mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE 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 getParentType (
+ CORINFO_CLASS_HANDLE 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 getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ );
+
+ // Check constraints on type arguments of this class and parent classes
+ BOOL satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Check if this is a single dimensional array type
+ BOOL isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Get the numbmer of dimensions in an array
+ unsigned getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Get static field data for an array
+ void * getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ );
+
+ // Check Visibility rules.
+ CorInfoIsAccessAllowedResult 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. */
+ );
+
+ /**********************************************************************************/
+ //
+ // 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* getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ );
+
+ // return class it belongs to
+ CORINFO_CLASS_HANDLE getFieldClass (
+ CORINFO_FIELD_HANDLE 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 getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent = NULL /* IN */
+ );
+
+ // return the data member's instance offset
+ unsigned getFieldOffset(
+ CORINFO_FIELD_HANDLE 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 isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field);
+
+ void getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ );
+
+ // Returns true iff "fldHnd" represents a static field.
+ bool isFieldStatic(CORINFO_FIELD_HANDLE 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 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
+ );
+
+ // 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 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
+ );
+
+ // 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 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
+ );
+
+ // Report back to the EE the location of every variable.
+ // note that the JIT might split lifetimes into different
+ // locations etc.
+
+ void 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
+ );
+
+ /*-------------------------- 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 * allocateArray(
+ ULONG 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 freeArray(
+ void *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 getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ );
+
+ // 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 NULL
+ // 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 getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ );
+
+ // If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+ CORINFO_CLASS_HANDLE getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ );
+
+ // Returns type of HFA for valuetype
+ CorInfoType getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ );
+
+ /*****************************************************************************
+ * 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 GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ );
+
+ // 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 GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ );
+
+ // 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 FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ );
+
+ // Cleans up internal EE tracking when an exception is caught.
+ void HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ );
+
+ void ThrowExceptionForJitResult(
+ HRESULT result);
+
+ //Throws an exception defined by the given throw helper.
+ void ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper);
+
+#if COR_JIT_EE_VERSION > 460
+ // Runs the given function under an error trap. This allows the JIT to make calls
+ // to interface functions that may throw exceptions without needing to be aware of
+ // the EH ABI, exception types, etc. Returns true if the given function completed
+ // successfully and false otherwise.
+ bool runWithErrorTrap(
+ void (*function)(void*), // The function to run
+ void* parameter // The context parameter that will be passed to the function and the handler
+ );
+#endif
+
+/*****************************************************************************
+ * 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 getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ );
+
+ // Returns name of the JIT timer log
+ LPCWSTR getJitTimeLogFilename();
+
+ /*********************************************************************************/
+ //
+ // Diagnostic methods
+ //
+ /*********************************************************************************/
+
+ // this function is for debugging only. Returns method token.
+ // Returns mdMethodDefNil for dynamic methods.
+ mdMethodDef getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ );
+
+ // 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* getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ );
+
+ // 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 getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ );
+
+ // this function is for debugging only.
+ size_t findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ );
+
+#if COR_JIT_EE_VERSION > 460
+
+ // returns whether the struct is enregisterable. Only valid on a System V VM. Returns true on success, false on failure.
+ bool getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ );
+
+#endif // COR_JIT_EE_VERSION
+
+/*****************************************************************************
+ * ICorDynamicInfo contains EE interface methods which return values that may
+ * change from invocation to invocation. They cannot be embedded in persisted
+ * data; they must be requeried each time the EE is run.
+ *****************************************************************************/
+
+ //
+ // These methods return values to the JIT which are not constant
+ // from session to session.
+ //
+ // These methods take an extra parameter : void **ppIndirection.
+ // If a JIT supports generation of prejit code (install-o-jit), it
+ // must pass a non-null value for this parameter, and check the
+ // resulting value. If *ppIndirection is NULL, code should be
+ // generated normally. If non-null, then the value of
+ // *ppIndirection is an address in the cookie table, and the code
+ // generator needs to generate an indirection through the table to
+ // get the resulting value. In this case, the return result of the
+ // function must NOT be directly embedded in the generated code.
+ //
+ // Note that if a JIT does not support prejit code generation, it
+ // may ignore the extra parameter & pass the default of NULL - the
+ // prejit ICorDynamicInfo implementation will see this & generate
+ // an error if the jitter is used in a prejit scenario.
+ //
+
+ // Return details about EE internal data structures
+
+ DWORD getThreadTLSIndex(
+ void **ppIndirection = NULL
+ );
+
+ const void * getInlinedCallFrameVptr(
+ void **ppIndirection = NULL
+ );
+
+ LONG * getAddrOfCaptureThreadGlobal(
+ void **ppIndirection = NULL
+ );
+
+ SIZE_T* getAddrModuleDomainID(CORINFO_MODULE_HANDLE module);
+
+ // return the native entry point to an EE helper (see CorInfoHelpFunc)
+ void* getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection = NULL
+ );
+
+ // 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 getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags = CORINFO_ACCESS_ANY);
+
+ // 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 getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult);
+
+ // get the synchronization handle that is passed to monXstatic function
+ void* getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection = NULL
+ );
+
+ // get slow lazy string literal helper to use (CORINFO_HELP_STRCNS*).
+ // Returns CORINFO_HELP_UNDEF if lazy string literal helper cannot be used.
+ CorInfoHelpFunc getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ );
+
+ CORINFO_MODULE_HANDLE embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection = NULL
+ );
+
+ CORINFO_CLASS_HANDLE embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection = NULL
+ );
+
+ CORINFO_METHOD_HANDLE embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection = NULL
+ );
+
+ CORINFO_FIELD_HANDLE embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection = NULL
+ );
+
+ // 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 embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * 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 getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ );
+
+ // NOTE: the two methods below--getPInvokeUnmanagedTarget and getAddressOfPInvokeFixup--are
+ // deprecated. New code (i.e. anything that can depend on COR_JIT_EE_VERSION being
+ // greater than 460) should instead use getAddressOfPInvokeTarget, which subsumes the
+ // functionality of these methods.
+
+ // return the unmanaged target *if method has already been prelinked.*
+ void* getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection = NULL
+ );
+
+ // return address of fixup area for late-bound PInvoke calls.
+ void* getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection = NULL
+ );
+
+#if COR_JIT_EE_VERSION > 460
+ // return the address of the PInvoke target. May be a fixup area in the
+ // case of late-bound PInvoke calls.
+ void getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ );
+#endif
+
+ // Generate a cookie based on the signature that would needs to be passed
+ // to CORINFO_HELP_PINVOKE_CALLI
+ LPVOID GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection = NULL
+ );
+
+ // 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 canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ );
+
+ // Gets a handle that is checked to see if the current method is
+ // included in "JustMyCode"
+ CORINFO_JUST_MY_CODE_HANDLE getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection = NULL
+ );
+
+ // 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 GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ );
+
+ // Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+ void 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
+ );
+
+ BOOL canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE 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 isRIDClassDomainID(CORINFO_CLASS_HANDLE cls);
+
+ // returns the class's domain ID for accessing shared statics
+ unsigned getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection = NULL
+ );
+
+
+ // return the data's address (for static fields only)
+ void* getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection = NULL
+ );
+
+ // registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+ CORINFO_VARARGS_HANDLE getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection = NULL
+ );
+
+ // 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 canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ );
+
+ // Allocate a string literal on the heap and return a handle to it
+ InfoAccessType constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ );
+
+ InfoAccessType emptyStringLiteral(
+ void **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 getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection = NULL
+ );
+
+ // Sets another object to intercept calls to "self" and current method being compiled
+ void setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ );
+
+ // 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 addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ );
+
+ CORINFO_METHOD_HANDLE GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ );
+
+ void MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ );
+
+ // return a thunk that will copy the arguments for the given signature.
+ void* getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ );
+
+ // return memory manager that the JIT can use to allocate a regular memory
+ IEEMemoryManager* getMemoryManager();
+
+ // get a block of memory for the code, readonly data, and read-write data
+ void 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 */
+ );
+
+ // 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 reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ );
+
+ // 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 allocUnwindInfo (
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ );
+
+ // 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 * allocGCInfo (
+ size_t size /* IN */
+ );
+
+ void 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 setEHcount (
+ unsigned cEH /* IN */
+ );
+
+ // 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 setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ );
+
+ // Level -> 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 logMsg(unsigned level, const char* fmt, va_list args);
+
+ // do an assert. will return true if the code should retry (DebugBreak)
+ // returns false, if the assert should be igored.
+ int doAssert(const char* szFile, int iLine, const char* szExpr);
+
+ void reportFatalError(CorJitResult result);
+
+ /*
+ struct ProfileBuffer // Also defined here: code:CORBBTPROF_BLOCK_DATA
+ {
+ ULONG ILOffset;
+ ULONG ExecutionCount;
+ };
+ */
+
+ // allocate a basic block profile buffer where execution counts will be stored
+ // for jitted basic blocks.
+ HRESULT allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer ** 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 getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG * count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer,
+ ULONG * 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 recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ );
+
+ // A relocation is recorded if we are pre-jitting.
+ // A jump thunk may be inserted if we are jitting
+ void recordRelocation(
+ void * location, /* IN */
+ void * target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ );
+
+ WORD getRelocTypeHint(void * target);
+
+ // A callback to identify the range of address known to point to
+ // compiler-generated native entry points that call back into
+ // MSIL.
+ void getModuleNativeEntryPointRange(
+ void ** pStart, /* OUT */
+ void ** pEnd /* OUT */
+ );
+
+ // 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 getExpectedTargetArchitecture();
+
+#if COR_JIT_EE_VERSION > 460
+ // Fetches extended flags for a particular compilation instance. Returns
+ // the number of bytes written to the provided buffer.
+ DWORD getJitFlags(
+ CORJIT_FLAGS* flags, /* IN: Points to a buffer that will hold the extended flags. */
+ DWORD sizeInBytes /* IN: The size of the buffer. Note that this is effectively a
+ version number for the CORJIT_FLAGS value. */
+ );
+#endif
+
+#endif // _ICorJitInfoImpl
diff --git a/src/ToolBox/superpmi/superpmi-shared/lightweightmap.h b/src/ToolBox/superpmi/superpmi-shared/lightweightmap.h
new file mode 100644
index 0000000000..860f545fac
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/lightweightmap.h
@@ -0,0 +1,726 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// LightWeightMap.h -
+// Notes:
+// improvements:
+// 1. we could pack the size down a bit with various encoding tricks (don't use 4 bytes for the numItems etc)
+// 2. Add() could find the right place to insert via binary search... though the list is normally very small
+// 3. Buffer encoding could easily be more compact
+//----------------------------------------------------------
+#ifndef _LightWeightMap
+#define _LightWeightMap
+
+#include "errorhandling.h"
+
+//#define DEBUG_LWM
+
+// Common base class that implements the raw buffer functionality.
+class LightWeightMapBuffer
+{
+public:
+ LightWeightMapBuffer()
+ {
+ InitialClear();
+ }
+
+ LightWeightMapBuffer(const LightWeightMapBuffer &lwm)
+ {
+ InitialClear();
+ bufferLength = lwm.bufferLength;
+
+ if((lwm.buffer!=nullptr)&&(lwm.bufferLength>0))
+ {
+ buffer = new unsigned char[lwm.bufferLength];
+ memcpy(buffer, lwm.buffer, lwm.bufferLength);
+ }
+ }
+
+ ~LightWeightMapBuffer()
+ {
+ delete []buffer;
+ }
+
+ unsigned int AddBuffer(const unsigned char *buff, unsigned int len)
+ {
+ return AddBuffer(buff, len, false);
+ }
+
+ unsigned int AddBuffer(const unsigned char *buff, unsigned int len, bool forceUnique)
+ {
+ if(len == 0)
+ return -1;
+ if(buff == nullptr)
+ return -1;
+ int index = Contains(buff, len); //See if there is already a copy of this data in our buffer
+ if((index != -1)&&(!forceUnique))
+ return index;
+ if(locked)
+ {
+ LogError("Added item that extended the buffer after it was locked by a call to GetBuffer()");
+ __debugbreak();
+ }
+
+ unsigned int newbuffsize = bufferLength + sizeof(unsigned int) + len;
+ unsigned char *newbuffer = new unsigned char[newbuffsize];
+ unsigned int newOffset = bufferLength;
+ if(bufferLength>0)
+ memcpy(newbuffer, buffer, bufferLength);
+ memcpy(newbuffer + bufferLength + sizeof(unsigned int), buff, len);
+ *((unsigned int *)(newbuffer + bufferLength)) = len;
+ bufferLength += sizeof(unsigned int) + len;
+ if(buffer!=nullptr)
+ delete []buffer;
+ buffer = newbuffer;
+ return newOffset + sizeof(unsigned int);
+ }
+
+ unsigned char *GetBuffer(unsigned int offset)
+ {
+ if(offset == (unsigned int)-1)
+ return nullptr;
+ AssertCodeMsg(offset < bufferLength, EXCEPTIONCODE_LWM,
+ "Hit offset bigger than bufferLength %u >= %u", offset, bufferLength);
+ locked = true;
+ // LogDebug("Address given %p", &buffer[offset]);
+ return &buffer[offset];
+ }
+
+ int Contains(const unsigned char *buff, unsigned int len)
+ {
+#ifdef DEBUG_LWM
+ LogDebug("New call to Contains %d {", len);
+ for(int i=0;i<len;i++)
+ LogDebug("0x%02x ", buff[len]);
+ LogDebug("}");
+#endif
+ if(len == 0)
+ return -1;
+ if(bufferLength == 0)
+ return -1;
+ unsigned int offset = 0;
+ while((offset+sizeof(unsigned int)+len) <= bufferLength)
+ {
+ unsigned int buffChunkLen = *(unsigned int*)(&buffer[offset]);
+#ifdef DEBUG_LWM
+ LogDebug("Investigating len %d @ %d", buffChunkLen, offset);
+#endif
+ if(buffChunkLen == len)
+ {
+#ifdef DEBUG_LWM
+ LogDebug("peering into {");
+ for(int i=0;i<len;i++)
+ LogDebug("0x%02x ", buff[len]);
+ LogDebug("}");
+#endif
+ if(memcmp(&buffer[offset+sizeof(unsigned int)], buff, len)==0)
+ {
+#ifdef DEBUG_LWM
+ LogDebug("Found!");
+#endif
+ return offset+sizeof(unsigned int);
+ }
+ }
+ offset += sizeof(unsigned int) + buffChunkLen;
+ }
+#ifdef DEBUG_LWM
+ LogDebug("NOT Found!");
+#endif
+ return -1;
+ }
+
+ void Unlock() //did you really mean to use this?
+ {
+ locked = false;
+ }
+
+protected:
+
+ void InitialClear()
+ {
+ buffer = nullptr;
+ bufferLength = 0;
+ locked = false;
+ }
+
+ unsigned char* buffer; // TODO-Cleanup: this should really be a linked list; we reallocate it with every call to AddBuffer().
+ unsigned int bufferLength;
+ bool locked;
+};
+
+template<typename _Key, typename _Item>
+class LightWeightMap : public LightWeightMapBuffer
+{
+public:
+ LightWeightMap()
+ {
+ InitialClear();
+ }
+
+ LightWeightMap(const LightWeightMap &lwm)
+ {
+ InitialClear();
+ numItems = lwm.numItems;
+ strideSize = lwm.strideSize;
+ bufferLength = lwm.bufferLength;
+ locked = false;
+
+ pKeys = nullptr;
+ pItems = nullptr;
+
+ if(lwm.pKeys!=nullptr)
+ {
+ pKeys = new _Key[numItems];
+ memcpy(pKeys, lwm.pKeys, numItems * sizeof(_Key));
+ }
+ if(lwm.pItems!=nullptr)
+ {
+ pItems = new _Item[numItems];
+ memcpy(pItems, lwm.pItems, numItems * sizeof(_Item));
+ }
+ if((lwm.buffer!=nullptr)&&(lwm.bufferLength>0))
+ {
+ buffer = new unsigned char[lwm.bufferLength];
+ memcpy(buffer, lwm.buffer, lwm.bufferLength);
+ }
+ }
+
+ ~LightWeightMap()
+ {
+ if(pKeys!=nullptr)
+ delete []pKeys;
+ if(pItems!=nullptr)
+ delete []pItems;
+ }
+
+ void ReadFromArray(const unsigned char *rawData, unsigned int size)
+ {
+ unsigned int sizeOfKey = sizeof(_Key);
+ unsigned int sizeOfItem = sizeof(_Item);
+ const unsigned char *ptr = rawData;
+
+ // The tag is optional, to roll forward previous formats which don't have
+ // the tag, but which also have the same format.
+ if (0 == memcmp(ptr, "LWM1", 4))
+ {
+ ptr += 4;
+ }
+
+ memcpy(&numItems, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+ strideSize = numItems;
+
+ if(numItems > 0)
+ {
+ //Read the buffersize
+ memcpy(&bufferLength, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ AssertCodeMsg(pKeys == nullptr, EXCEPTIONCODE_LWM, "Found existing pKeys");
+ pKeys = new _Key[numItems];
+ //Set the Keys
+ memcpy(pKeys, ptr, sizeOfKey * numItems);
+ ptr += sizeOfKey * numItems;
+
+ AssertCodeMsg(pItems == nullptr, EXCEPTIONCODE_LWM, "Found existing pItems");
+ pItems = new _Item[numItems];
+ //Set the Items
+ memcpy(pItems, ptr, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ AssertCodeMsg(buffer == nullptr, EXCEPTIONCODE_LWM, "Found existing buffer");
+ buffer = new unsigned char[bufferLength];
+ //Read the buffer
+ memcpy(buffer, ptr, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+ }
+
+ // If we have RTTI, we can make this assert report the correct type. No RTTI, though, when
+ // built with .NET Core, especially when built against the PAL.
+ AssertCodeMsg((ptr - rawData) == size, EXCEPTIONCODE_LWM,
+ "%s - Ended with unexpected sizes %Ix != %x",
+ "Unknown type" /*typeid(_Item).name()*/, ptr - rawData, size);
+ }
+
+ unsigned int CalculateArraySize()
+ {
+ int size = 4 /* tag */ + sizeof(unsigned int) /* numItems */;
+ if(numItems >0)
+ {
+ size += sizeof(unsigned int); //size of bufferLength
+ size += sizeof(_Key) * numItems; //size of keyset
+ size += sizeof(_Item) * numItems; //size of itemset
+ size += sizeof(unsigned char) * bufferLength; //bulk size of raw buffer
+ }
+ return size;
+ }
+
+ unsigned int DumpToArray(unsigned char *bytes)
+ {
+ unsigned char *ptr = bytes;
+ unsigned int size = CalculateArraySize();
+
+ //Write the tag
+ memcpy(ptr, "LWM1", 4);
+ ptr += 4;
+
+ //Write the header
+ memcpy(ptr, &numItems, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ if(numItems > 0)
+ {
+ unsigned int sizeOfKey = sizeof(_Key);
+ unsigned int sizeOfItem = sizeof(_Item);
+
+ //Write the buffersize
+ memcpy(ptr, &bufferLength, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ //Write the Keys
+ memcpy(ptr, pKeys, sizeOfKey * numItems);
+ ptr += sizeOfKey * numItems;
+
+ //Write the Items
+ memcpy(ptr, pItems, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ //Write the buffer
+ memcpy(ptr, buffer, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+ }
+
+ // If we have RTTI, we can make this assert report the correct type. No RTTI, though, when
+ // built with .NET Core, especially when built against the PAL.
+ AssertCodeMsg((ptr - bytes) == size, EXCEPTIONCODE_LWM,
+ "%s - Ended with unexpected sizes %p != %x",
+ "Unknown type" /*typeid(_Item).name()*/, (void*)(ptr - bytes), size);
+ return size;
+ }
+
+ //its worth noting that the acutal order of insert here doesnt meet what you migth expect. Its using memcmp, so
+ // since we are on a little endian machine we'd use the lowest 8 bits as the first part of the key. This is
+ // a side effect of using the same code for large structs and DWORDS etc...
+ bool Add(_Key key, _Item item)
+ {
+ //Make sure we have space left, expand if needed
+ if(numItems == strideSize)
+ {
+ _Key *tKeys = pKeys;
+ _Item *tItems = pItems;
+ pKeys = new _Key[(strideSize * 2) + 4];
+ memcpy(pKeys, tKeys, strideSize * sizeof(_Key));
+ pItems = new _Item[(strideSize * 2) + 4];
+ memcpy(pItems, tItems, strideSize * sizeof(_Item));
+ strideSize = (strideSize * 2) + 4;
+ delete []tKeys;
+ delete []tItems;
+ }
+ unsigned int insert = 0;
+ //Find the right place to insert O(n) version
+/* for(;insert < numItems; insert++)
+ {
+ int res = memcmp(&pKeys[insert], &key, sizeof(_Key));
+ if(res == 0)
+ return false;
+ if(res>0)
+ break;
+ }
+*/
+ //O(log n) version
+ int first = 0;
+ int mid = 0;
+ int last = numItems-1;
+ while (first <= last)
+ {
+ mid = (first + last) / 2; // compute mid point.
+ int res = memcmp(&pKeys[mid], &key, sizeof(_Key));
+
+ if (res < 0)
+ first = mid + 1; // repeat search in top half.
+ else if (res > 0)
+ last = mid - 1; // repeat search in bottom half.
+ else
+ return false; // found it. return position /////
+ }
+ insert = first;
+ if(insert!=first)
+ {
+ LogDebug("index = %u f %u mid = %u l %u***************************", insert, first, mid, last);
+ __debugbreak();
+ }
+
+ if(numItems>0)
+ {
+ for(unsigned int i=numItems; i>insert; i--)
+ {
+ pKeys[i] = pKeys[i-1];
+ pItems[i] = pItems[i-1];
+ }
+ }
+
+ pKeys[insert] = key;
+ pItems[insert] = item;
+ numItems++;
+ return true;
+ }
+
+ int GetIndex(_Key key)
+ {
+ if(numItems == 0)
+ return -1;
+
+ //O(log n) version
+ int first = 0;
+ int mid = 0;
+ int last = numItems;
+ while (first <= last)
+ {
+ mid = (first + last) / 2; // compute mid point.
+ int res = memcmp(&pKeys[mid], &key, sizeof(_Key));
+
+ if (res < 0)
+ first = mid + 1; // repeat search in top half.
+ else if (res > 0)
+ last = mid - 1; // repeat search in bottom half.
+ else
+ return mid; // found it. return position /////
+ }
+ return -1; // Didn't find key
+ }
+
+ _Item GetItem(int index)
+ {
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_LWM, "Didn't find Key");
+ return pItems[index]; // found it. return position /////
+ }
+
+ _Key GetKey(int index)
+ {
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_LWM, "Didn't find Key (in GetKey)");
+ return pKeys[index];
+ }
+
+ _Item Get(_Key key)
+ {
+ int index = GetIndex(key);
+ return GetItem(index);
+ }
+
+ _Item *GetRawItems()
+ {
+ return pItems;
+ }
+
+ _Key *GetRawKeys()
+ {
+ return pKeys;
+ }
+
+ unsigned int GetCount()
+ {
+ return numItems;
+ }
+
+private:
+
+ void InitialClear()
+ {
+ numItems = 0;
+ strideSize = 0;
+ pKeys = nullptr;
+ pItems = nullptr;
+ }
+
+ unsigned int numItems; // Number of active items in the pKeys and pItems arrays.
+ unsigned int strideSize; // Allocated count of items in the pKeys and pItems arrays.
+ _Key *pKeys;
+ _Item *pItems;
+};
+
+
+// Second implementation of LightWeightMap where the Key type is an unsigned int in the range [0 .. numItems - 1] (where
+// numItems is the number of items stored in the map). Keys are not stored, since the index into the pItems array is
+// the key. Appending to the end of the map is O(1), since we don't have to search for it, and we don't have to move anything down.
+
+template<typename _Item>
+class DenseLightWeightMap : public LightWeightMapBuffer
+{
+public:
+ DenseLightWeightMap()
+ {
+ InitialClear();
+ }
+
+ DenseLightWeightMap(const DenseLightWeightMap &lwm)
+ {
+ InitialClear();
+ numItems = lwm.numItems;
+ strideSize = lwm.strideSize;
+ bufferLength = lwm.bufferLength;
+
+ if(lwm.pItems!=nullptr)
+ {
+ pItems = new _Item[numItems];
+ memcpy(pItems, lwm.pItems, numItems * sizeof(_Item));
+ }
+ if((lwm.buffer!=nullptr)&&(lwm.bufferLength>0))
+ {
+ buffer = new unsigned char[lwm.bufferLength];
+ memcpy(buffer, lwm.buffer, lwm.bufferLength);
+ }
+ }
+
+ ~DenseLightWeightMap()
+ {
+ if(pItems!=nullptr)
+ delete []pItems;
+ }
+
+ void ReadFromArray(const unsigned char *rawData, unsigned int size)
+ {
+ unsigned int sizeOfItem = sizeof(_Item);
+ const unsigned char *ptr = rawData;
+
+ // Check tag; if this is a v1 LWM, convert it to a DenseLightWeightMap in memory
+ if (0 != memcmp(ptr, "DWM1", 4))
+ {
+ ReadFromArrayAndConvertLWM1(rawData, size);
+ return;
+ }
+ ptr += 4;
+
+ memcpy(&numItems, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+ strideSize = numItems;
+
+ if(numItems > 0)
+ {
+ //Read the buffersize
+ memcpy(&bufferLength, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ AssertCodeMsg(pItems == nullptr, EXCEPTIONCODE_LWM, "Found existing pItems");
+ pItems = new _Item[numItems];
+ //Set the Items
+ memcpy(pItems, ptr, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ AssertCodeMsg(buffer == nullptr, EXCEPTIONCODE_LWM, "Found existing buffer");
+ buffer = new unsigned char[bufferLength];
+ //Read the buffer
+ memcpy(buffer, ptr, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+ }
+
+ AssertCodeMsg((ptr - rawData) == size, EXCEPTIONCODE_LWM, "Ended with unexpected sizes %Ix != %x", ptr - rawData, size);
+ }
+
+private:
+
+ void ReadFromArrayAndConvertLWM1(const unsigned char *rawData, unsigned int size)
+ {
+ unsigned int sizeOfKey = sizeof(DWORD);
+ unsigned int sizeOfItem = sizeof(_Item);
+ const unsigned char *ptr = rawData;
+
+ memcpy(&numItems, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+ strideSize = numItems;
+
+ if(numItems > 0)
+ {
+ //Read the buffersize
+ memcpy(&bufferLength, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ DWORD* tKeys = new DWORD[numItems];
+ //Set the Keys
+ memcpy(tKeys, ptr, sizeOfKey * numItems);
+ ptr += sizeOfKey * numItems;
+
+ _Item* tItems = new _Item[numItems];
+ //Set the Items
+ memcpy(tItems, ptr, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ AssertCodeMsg(buffer == nullptr, EXCEPTIONCODE_LWM, "Found existing buffer");
+ buffer = new unsigned char[bufferLength];
+ //Read the buffer
+ memcpy(buffer, ptr, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+
+ // Convert to new format
+ AssertCodeMsg(pItems == nullptr, EXCEPTIONCODE_LWM, "Found existing pItems");
+ bool* tKeySeen = new bool[numItems]; // Used for assert, below: keys must be unique.
+ for (unsigned int i = 0; i < numItems; i++)
+ {
+ tKeySeen[i] = false;
+ }
+ pItems = new _Item[numItems];
+ for (unsigned int index = 0; index < numItems; index++)
+ {
+ unsigned int key = tKeys[index];
+ AssertCodeMsg(key < numItems, EXCEPTIONCODE_LWM, "Illegal key %d, numItems == %d", key, numItems);
+ AssertCodeMsg(!tKeySeen[key], EXCEPTIONCODE_LWM, "Duplicate key %d", key);
+ tKeySeen[key] = true;
+ pItems[key] = tItems[index];
+ }
+
+ // Note that if we get here, we've seen every key [0 .. numItems - 1].
+ delete[] tKeySeen;
+ delete[] tKeys;
+ delete[] tItems;
+ }
+
+ AssertCodeMsg((ptr - rawData) == size, EXCEPTIONCODE_LWM, "Ended with unexpected sizes %Ix != %x", ptr - rawData, size);
+ }
+
+public:
+
+ unsigned int CalculateArraySize()
+ {
+ int size = 4 /* tag */ + sizeof(unsigned int) /* numItems */;
+ if(numItems >0)
+ {
+ size += sizeof(unsigned int); //size of bufferLength
+ size += sizeof(_Item) * numItems; //size of itemset
+ size += sizeof(unsigned char) * bufferLength; //bulk size of raw buffer
+ }
+ return size;
+ }
+
+ unsigned int DumpToArray(unsigned char *bytes)
+ {
+ unsigned char *ptr = bytes;
+ unsigned int size = CalculateArraySize();
+
+ //Write the tag
+ memcpy(ptr, "DWM1", 4);
+ ptr += 4;
+
+ //Write the header
+ memcpy(ptr, &numItems, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ if(numItems > 0)
+ {
+ unsigned int sizeOfItem = sizeof(_Item);
+
+ //Write the buffersize
+ memcpy(ptr, &bufferLength, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ //Write the Items
+ memcpy(ptr, pItems, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ //Write the buffer
+ memcpy(ptr, buffer, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+ }
+
+ AssertCodeMsg((ptr - bytes) == size, EXCEPTIONCODE_LWM, "Ended with unexpected sizes %Ix != %x", ptr - bytes, size);
+ return size;
+ }
+
+ bool Append(_Item item)
+ {
+ //Make sure we have space left, expand if needed
+ if (numItems == strideSize)
+ {
+ // NOTE: if this is the first allocation, we'll just allocate 4 items. ok?
+ _Item *tItems = pItems;
+ pItems = new _Item[(strideSize * 2) + 4];
+ memcpy(pItems, tItems, strideSize * sizeof(_Item));
+ strideSize = (strideSize * 2) + 4;
+ delete []tItems;
+ }
+
+ pItems[numItems] = item;
+ numItems++;
+ return true;
+ }
+
+ int GetIndex(unsigned int key)
+ {
+ if (key >= numItems)
+ return -1;
+
+ return (int)key;
+ }
+
+ _Item GetItem(int index)
+ {
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_LWM, "Didn't find Key");
+ return pItems[index]; // found it. return position /////
+ }
+
+ _Item Get(unsigned int key)
+ {
+ int index = GetIndex(key);
+ return GetItem(index);
+ }
+
+ _Item *GetRawItems()
+ {
+ return pItems;
+ }
+
+ unsigned int GetCount()
+ {
+ return numItems;
+ }
+
+private:
+
+ void InitialClear()
+ {
+ numItems = 0;
+ strideSize = 0;
+ pItems = nullptr;
+ }
+
+ static int CompareKeys(unsigned int key1, unsigned int key2)
+ {
+ if (key1 < key2)
+ return -1;
+ else if (key1 > key2)
+ return 1;
+ else
+ return 0; // equal
+ }
+
+ unsigned int numItems; // Number of active items in the pKeys and pItems arrays.
+ unsigned int strideSize; // Allocated count of items in the pKeys and pItems arrays.
+ _Item *pItems;
+};
+
+#define dumpLWM(ptr,mapName) \
+ if (ptr->mapName != nullptr) \
+ { \
+ printf("%s - %u\n", #mapName, ptr->mapName->GetCount()); \
+ for (unsigned int i = 0; i < ptr->mapName->GetCount(); i++) \
+ { \
+ printf("%u-", i); \
+ ptr->dmp##mapName(ptr->mapName->GetRawKeys()[i], ptr->mapName->GetRawItems()[i]); \
+ printf("\n"); \
+ } \
+ }
+
+#define dumpLWMDense(ptr,mapName) \
+ if (ptr->mapName != nullptr) \
+ { \
+ printf("%s - %u\n", #mapName, ptr->mapName->GetCount()); \
+ for (unsigned int i = 0; i < ptr->mapName->GetCount(); i++) \
+ { \
+ printf("%u-", i); \
+ ptr->dmp##mapName(i, ptr->mapName->GetRawItems()[i]); \
+ printf("\n"); \
+ } \
+ }
+
+#endif // _LightWeightMap
diff --git a/src/ToolBox/superpmi/superpmi-shared/logging.cpp b/src/ToolBox/superpmi/superpmi-shared/logging.cpp
new file mode 100644
index 0000000000..5f7aa48a4f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/logging.cpp
@@ -0,0 +1,342 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// Logging.cpp - Common logging and console output infrastructure
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "logging.h"
+#include "errorhandling.h"
+#include <time.h>
+
+//
+// NOTE: Since the logging system is at the core of the error handling infrastructure, any errors
+// that occur while logging will print a message to the console. Fatal errors trigger a debugbreak.
+//
+
+bool Logger::s_initialized = false;
+UINT32 Logger::s_logLevel = LOGMASK_DEFAULT;
+HANDLE Logger::s_logFile = INVALID_HANDLE_VALUE;
+char *Logger::s_logFilePath = nullptr;
+CRITICAL_SECTION Logger::s_critSec;
+
+//
+// Initializes the logging subsystem. This must be called before invoking any of the logging functionality.
+//
+/* static */
+void Logger::Initialize()
+{
+ if (!s_initialized)
+ {
+ InitializeCriticalSection(&s_critSec);
+ s_initialized = true;
+ }
+}
+
+//
+// Shuts down the logging subsystem, freeing resources, closing handles, and such.
+//
+/* static */
+void Logger::Shutdown()
+{
+ if (s_initialized)
+ {
+ DeleteCriticalSection(&s_critSec);
+ CloseLogFile();
+ s_initialized = false;
+ }
+}
+
+//
+// Opens a log file at the given path and enables file-based logging, if the given path is valid.
+//
+/* static */
+void Logger::OpenLogFile(char *logFilePath)
+{
+ if (s_logFile == INVALID_HANDLE_VALUE && logFilePath != nullptr)
+ {
+ s_logFile = CreateFileA(logFilePath,
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_DELETE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL);
+
+ if (s_logFile != INVALID_HANDLE_VALUE)
+ {
+ // We may need the file path later in order to delete the log file
+ s_logFilePath = _strdup(logFilePath);
+ }
+ else
+ {
+ fprintf(stderr, "WARNING: [Logger::OpenLogFile] Failed to open log file '%s'. GetLastError()=%u\n", logFilePath, GetLastError());
+ }
+ }
+}
+
+//
+// Closes the currently open log file, if one is open.
+//
+/* static */
+void Logger::CloseLogFile()
+{
+ if (s_logFile != INVALID_HANDLE_VALUE)
+ {
+ // Avoid polluting the file system with empty log files
+ if (GetFileSize(s_logFile, nullptr) == 0 && s_logFilePath != nullptr)
+ {
+ // We can call this before closing the handle because DeleteFile just marks the file
+ // for deletion, i.e. it does not actually get deleted until its last handle is closed.
+ if (!DeleteFileA(s_logFilePath))
+ fprintf(stderr, "WARNING: [Logger::CloseLogFile] DeleteFile failed. GetLastError()=%u\n", GetLastError());
+ }
+
+ if (!CloseHandle(s_logFile))
+ fprintf(stderr, "WARNING: [Logger::CloseLogFile] CloseHandle failed. GetLastError()=%u\n", GetLastError());
+
+ s_logFile = INVALID_HANDLE_VALUE;
+
+ free(s_logFilePath);
+ s_logFilePath = nullptr;
+ }
+}
+
+//
+// Returns a bitmask representing the logging levels that are specified by the given string. The string
+// format is described explicitly in the command-line usage documentation for SuperPMI and MCS.
+//
+// In essence, each log level has a corresponding character representing it, and the presence of that
+// character in the specifier string indicates that the log mask should include that log level. The
+// "quiet" level will override any other level specified: that is, if a "q" is present in the string,
+// all other levels specified will be disregarded.
+//
+// If "q" is not specified, and "a" is specified, then all log levels are enabled. This is a shorthand
+// to avoid looking up all the log levels and enabling them all by specifying all the individual characters.
+//
+/* static */
+UINT32 Logger::ParseLogLevelString(const char *specifierStr)
+{
+ UINT32 logLevelMask = LOGMASK_NONE;
+
+ if (strchr(specifierStr, 'q') == nullptr) // "Quiet" overrides all other specifiers
+ {
+ if (strchr(specifierStr, 'a') != nullptr) // "All" overrides the other specifiers
+ {
+ logLevelMask |= LOGMASK_ALL;
+ }
+ else
+ {
+ if (strchr(specifierStr, 'e') != nullptr)
+ logLevelMask |= LOGLEVEL_ERROR;
+
+ if (strchr(specifierStr, 'w') != nullptr)
+ logLevelMask |= LOGLEVEL_WARNING;
+
+ if (strchr(specifierStr, 'm') != nullptr)
+ logLevelMask |= LOGLEVEL_MISSING;
+
+ if (strchr(specifierStr, 'i') != nullptr)
+ logLevelMask |= LOGLEVEL_ISSUE;
+
+ if (strchr(specifierStr, 'n') != nullptr)
+ logLevelMask |= LOGLEVEL_INFO;
+
+ if (strchr(specifierStr, 'v') != nullptr)
+ logLevelMask |= LOGLEVEL_VERBOSE;
+
+ if (strchr(specifierStr, 'd') != nullptr)
+ logLevelMask |= LOGLEVEL_DEBUG;
+ }
+ }
+
+ return logLevelMask;
+}
+
+/* static */
+void Logger::LogPrintf(const char *function, const char *file, int line,
+ LogLevel level, const char *msg, ...)
+{
+ va_list argList;
+ va_start(argList, msg);
+ LogVprintf(function, file, line, level, argList, msg);
+}
+
+//
+// Logs a message, if the given log level is enabled, to both the console and the log file. This is the
+// main logging function that all other logging functions eventually funnel into.
+//
+/* static */
+void Logger::LogVprintf(const char *function, const char *file, int line,
+ LogLevel level, va_list argList, const char *msg)
+{
+ if (!s_initialized)
+ {
+ fprintf(stderr, "ERROR: [Logger::LogVprintf] Invoked the logging system before initializing it.\n");
+ __debugbreak();
+ }
+
+ // Capture this first to make the timestamp more accurately reflect the actual time of logging
+ time_t timestamp = time(nullptr);
+
+ int fullMsgLen = _vscprintf(msg, argList) + 1; // This doesn't count the null terminator
+ char *fullMsg = new char[fullMsgLen];
+
+ _vsnprintf_s(fullMsg, fullMsgLen, fullMsgLen, msg, argList);
+ va_end(argList);
+
+ const char *logLevelStr = "INVALID_LOGLEVEL";
+ switch (level)
+ {
+ case LOGLEVEL_ERROR:
+ logLevelStr = "ERROR";
+ break;
+
+ case LOGLEVEL_WARNING:
+ logLevelStr = "WARNING";
+ break;
+
+ case LOGLEVEL_MISSING:
+ logLevelStr = "MISSING";
+ break;
+
+ case LOGLEVEL_ISSUE:
+ logLevelStr = "ISSUE";
+ break;
+
+ case LOGLEVEL_INFO:
+ logLevelStr = "INFO";
+ break;
+
+ case LOGLEVEL_VERBOSE:
+ logLevelStr = "VERBOSE";
+ break;
+
+ case LOGLEVEL_DEBUG:
+ logLevelStr = "DEBUG";
+ break;
+ }
+
+ // NOTE: This implementation doesn't guarantee that log messages will be written in chronological
+ // order, since Windows doesn't guarantee FIFO behavior when a thread relinquishes a lock. If
+ // maintaining chronological order is crucial, then we can implement a priority queueing system
+ // for log messages.
+
+ EnterCriticalSection(&s_critSec);
+
+ if (level & GetLogLevel())
+ {
+ // Sends error messages to stderr instead out stdout
+ FILE *dest = (level <= LOGLEVEL_WARNING) ? stderr : stdout;
+
+ if (level < LOGLEVEL_INFO)
+ fprintf(dest, "%s: ", logLevelStr);
+
+ fprintf(dest, "%s\n", fullMsg);
+
+ if (s_logFile != INVALID_HANDLE_VALUE)
+ {
+#ifndef FEATURE_PAL // TODO: no localtime_s() or strftime() in PAL
+ tm timeInfo;
+ errno_t err = localtime_s(&timeInfo, &timestamp);
+ if (err != 0)
+ {
+ fprintf(stderr, "WARNING: [Logger::LogVprintf] localtime failed with error %d.\n", err);
+ goto CleanUp;
+ }
+
+ size_t timeStrBuffSize = 20 * sizeof(char);
+ char *timeStr = (char *)malloc(timeStrBuffSize); // Use malloc so we can realloc if necessary
+
+ // This particular format string should always generate strings of the same size, but
+ // for the sake of robustness, we shouldn't rely on that assumption.
+ while (strftime(timeStr, timeStrBuffSize, "%Y-%m-%d %H:%M:%S", &timeInfo) == 0)
+ {
+ timeStrBuffSize *= 2;
+ timeStr = (char *)realloc(timeStr, timeStrBuffSize);
+ }
+#else // FEATURE_PAL
+ const char *timeStr = "";
+#endif // FEATURE_PAL
+
+ const char *logEntryFmtStr = "%s - %s [%s:%d] - %s - %s\r\n";
+ size_t logEntryBuffSize = _snprintf(nullptr, 0, logEntryFmtStr,
+ timeStr, function, file, line, logLevelStr, fullMsg) + 1;
+
+ char *logEntry = new char[logEntryBuffSize];
+ sprintf_s(logEntry, logEntryBuffSize, logEntryFmtStr,
+ timeStr, function, file, line, logLevelStr, fullMsg);
+
+ DWORD bytesWritten;
+
+ if (!WriteFile(s_logFile, logEntry, (DWORD)logEntryBuffSize - 1, &bytesWritten, nullptr))
+ fprintf(stderr, "WARNING: [Logger::LogVprintf] Failed to write to log file. GetLastError()=%u\n", GetLastError());
+
+ if (!FlushFileBuffers(s_logFile))
+ fprintf(stderr, "WARNING: [Logger::LogVprintf] Failed to flush log file. GetLastError()=%u\n", GetLastError());
+
+ delete[] logEntry;
+
+#ifndef FEATURE_PAL
+ free((void*)timeStr);
+#endif // !FEATURE_PAL
+ }
+ }
+
+#ifndef FEATURE_PAL
+CleanUp:
+#endif // !FEATURE_PAL
+
+ LeaveCriticalSection(&s_critSec);
+ delete[] fullMsg;
+}
+
+//
+// Special helper for logging exceptions. This logs the exception message given as a debug message.
+//
+/* static */
+void Logger::LogExceptionMessage(const char *function, const char *file, int line,
+ DWORD exceptionCode, const char *msg, ...)
+{
+ std::string fullMsg = "Exception thrown: ";
+ fullMsg += msg;
+
+ va_list argList;
+ va_start(argList, msg);
+ LogVprintf(function, file, line, LOGLEVEL_DEBUG, argList, fullMsg.c_str());
+}
+
+//
+// Logger for JIT issues. Identifies the issue type and logs the given message normally.
+//
+/* static */
+void IssueLogger::LogIssueHelper(const char *function, const char *file, int line,
+ IssueType issue, const char *msg, ...)
+{
+ std::string fullMsg;
+
+ switch (issue)
+ {
+ case ISSUE_ASSERT:
+ fullMsg += "<ASSERT>";
+ break;
+
+ case ISSUE_ASM_DIFF:
+ fullMsg += "<ASM_DIFF>";
+ break;
+
+ default:
+ fullMsg += "<UNKNOWN_ISSUE_TYPE>";
+ break;
+ }
+
+ fullMsg += " ";
+ fullMsg += msg;
+
+ va_list argList;
+ va_start(argList, msg);
+ Logger::LogVprintf(function, file, line, LOGLEVEL_ISSUE, argList, fullMsg.c_str());
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/logging.h b/src/ToolBox/superpmi/superpmi-shared/logging.h
new file mode 100644
index 0000000000..a2c388ee3e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/logging.h
@@ -0,0 +1,108 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// Logging.h - Common logging and console output infrastructure
+//----------------------------------------------------------
+#ifndef _Logging
+#define _Logging
+
+//
+// General purpose logging macros
+//
+
+#define LogMessage(level, ...) \
+ Logger::LogPrintf(__func__, __FILE__, __LINE__, level, __VA_ARGS__)
+
+#define LogError(...) LogMessage(LOGLEVEL_ERROR, __VA_ARGS__)
+#define LogWarning(...) LogMessage(LOGLEVEL_WARNING, __VA_ARGS__)
+#define LogMissing(...) LogMessage(LOGLEVEL_MISSING, __VA_ARGS__)
+#define LogInfo(...) LogMessage(LOGLEVEL_INFO, __VA_ARGS__)
+#define LogVerbose(...) LogMessage(LOGLEVEL_VERBOSE, __VA_ARGS__)
+#define LogDebug(...) LogMessage(LOGLEVEL_DEBUG, __VA_ARGS__)
+
+#define LogIssue(issue, msg, ...) \
+ IssueLogger::LogIssueHelper(__FUNCTION__, __FILE__, __LINE__, issue, msg, __VA_ARGS__)
+
+// Captures the exception message before throwing so we can log it at the point of occurrence
+#define LogException(exCode, msg, ...) \
+ do { \
+ Logger::LogExceptionMessage(__FUNCTION__, __FILE__, __LINE__, exCode, msg, __VA_ARGS__); \
+ ThrowException(exCode, msg, __VA_ARGS__); \
+ } while (0)
+
+// These are specified as flags so subsets of the logging functionality can be enabled/disabled at once
+enum LogLevel : UINT32
+{
+ LOGLEVEL_ERROR = 0x00000001, // Internal fatal errors that are non-recoverable
+ LOGLEVEL_WARNING = 0x00000002, // Internal conditions that are unusual, but not serious
+ LOGLEVEL_MISSING = 0x00000004, // Failures to due to missing JIT-EE details
+ LOGLEVEL_ISSUE = 0x00000008, // Issues found with the JIT, e.g. asm diffs, asserts
+ LOGLEVEL_INFO = 0x00000010, // Notifications/summaries, e.g. 'Loaded 5 Jitted 4 FailedCompile 1'
+ LOGLEVEL_VERBOSE = 0x00000020, // Status messages, e.g. 'Jit startup took 151.12ms'
+ LOGLEVEL_DEBUG = 0x00000040 // Detailed output that's only useful for SuperPMI debugging
+};
+
+// Preset log level combinations
+enum LogLevelMask : UINT32
+{
+ LOGMASK_NONE = 0x00000000,
+ LOGMASK_DEFAULT = (LOGLEVEL_DEBUG - 1), // Default is essentially "enable everything except debug"
+ LOGMASK_ALL = 0xffffffff
+};
+
+//
+// Manages the SuperPMI logging subsystem, including both file-based logging and logging to the console.
+//
+class Logger
+{
+private:
+ static bool s_initialized;
+ static UINT32 s_logLevel;
+ static HANDLE s_logFile;
+ static char *s_logFilePath;
+ static CRITICAL_SECTION s_critSec;
+
+public:
+ static void Initialize();
+ static void Shutdown();
+
+ static void OpenLogFile(char *logFilePath);
+ static void CloseLogFile();
+
+ static UINT32 ParseLogLevelString(const char *specifierStr);
+ static void SetLogLevel(UINT32 logLevelMask) { s_logLevel = logLevelMask; }
+ static UINT32 GetLogLevel() { return s_logLevel; }
+
+ // Return true if all specified log levels are enabled.
+ static bool IsLogLevelEnabled(UINT32 logLevelMask) { return (logLevelMask & GetLogLevel()) == logLevelMask; }
+
+ static void LogPrintf(const char *function, const char *file, int line,
+ LogLevel level, const char *msg, ...);
+ static void LogVprintf(const char *function, const char *file, int line,
+ LogLevel level, va_list argList, const char *msg);
+ static void LogExceptionMessage(const char *function, const char *file, int line,
+ DWORD exceptionCode, const char *msg, ...);
+};
+
+enum IssueType
+{
+ ISSUE_ASSERT,
+ ISSUE_ASM_DIFF
+};
+
+//
+// JIT issues have more granularity than other types of log messages. The logging of issues is abstracted
+// from the normal logger to reflect this. It also will enable us to track things specific to JIT issues,
+// like statistics on issues that were found during a run.
+//
+class IssueLogger
+{
+public:
+ static void LogIssueHelper(const char *function, const char *file, int line,
+ IssueType issue, const char *msg, ...);
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
new file mode 100644
index 0000000000..774e732620
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
@@ -0,0 +1,149 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// lwmlist.h - List of all LightWeightMap in MethodContext.
+// To use, #define LWM(map, key, value) to something.
+// If you need to distinguish DenseLightWeightMap, #define DENSELWM(map, value) as well.
+//----------------------------------------------------------
+
+#ifndef LWM
+#error Define LWM before including this file.
+#endif
+
+// If the key is needed, then DENSELWM must be defined.
+#ifndef DENSELWM
+#define DENSELWM(map, value) LWM(map, this_is_an_error, value)
+#endif
+
+LWM(AppendClassName, Agnostic_AppendClassName, DWORD)
+LWM(AreTypesEquivalent, DLDL, DWORD)
+LWM(AsCorInfoType, DWORDLONG, DWORD)
+LWM(CanAccessClass, Agnostic_CanAccessClassIn, Agnostic_CanAccessClassOut)
+LWM(CanAccessFamily, DLDL, DWORD)
+LWM(CanCast, DLDL, DWORD)
+LWM(CanGetCookieForPInvokeCalliSig, Agnostic_CORINFO_SIG_INFO, DWORD)
+LWM(CanGetVarArgsHandle, Agnostic_CORINFO_SIG_INFO, DWORD)
+LWM(CanInline, DLDL, Agnostic_CanInline)
+LWM(CanInlineTypeCheckWithObjectVTable, DWORDLONG, DWORD)
+LWM(CanSkipMethodVerification, DLD, DWORD)
+LWM(CanTailCall, Agnostic_CanTailCall, DWORD)
+LWM(CheckMethodModifier, Agnostic_CheckMethodModifier, DWORD)
+LWM(CompileMethod, DWORD, Agnostic_CompileMethod)
+LWM(ConstructStringLiteral, DLD, DLD)
+LWM(EmbedClassHandle, DWORDLONG, DLDL)
+LWM(EmbedFieldHandle, DWORDLONG, DLDL)
+LWM(EmbedGenericHandle, Agnostic_EmbedGenericHandle, Agnostic_CORINFO_GENERICHANDLE_RESULT)
+LWM(EmbedMethodHandle, DWORDLONG, DLDL)
+LWM(EmbedModuleHandle, DWORDLONG, DLDL)
+DENSELWM(EmptyStringLiteral, DLD)
+DENSELWM(Environment, Agnostic_Environment)
+DENSELWM(ErrorList, DWORD)
+LWM(FilterException, DWORD, DWORD)
+LWM(FindCallSiteSig, Agnostic_FindCallSiteSig, Agnostic_CORINFO_SIG_INFO)
+LWM(FindNameOfToken, DLD, DLD)
+LWM(FindSig, Agnostic_FindSig, Agnostic_CORINFO_SIG_INFO)
+LWM(GetAddressOfPInvokeFixup, DWORDLONG, DLDL)
+LWM(GetAddressOfPInvokeTarget, DWORDLONG, DLD)
+LWM(GetAddrOfCaptureThreadGlobal, DWORD, DLDL)
+LWM(GetArgClass, Agnostic_GetArgClass, Agnostic_GetArgClass_Value)
+LWM(GetArgNext, DWORDLONG, DWORDLONG)
+LWM(GetArgType, Agnostic_GetArgType, Agnostic_GetArgType_Value)
+LWM(GetArrayInitializationData, DLD, DWORDLONG)
+LWM(GetArrayRank, DWORDLONG, DWORD)
+LWM(GetBBProfileData, DWORDLONG, Agnostic_GetBBProfileData)
+LWM(GetBoundaries, DWORDLONG, Agnostic_GetBoundaries)
+LWM(GetBoxHelper, DWORDLONG, DWORD)
+LWM(GetBuiltinClass, DWORD, DWORDLONG)
+LWM(GetCallInfo, Agnostic_GetCallInfo, Agnostic_CORINFO_CALL_INFO)
+LWM(GetCastingHelper, Agnostic_GetCastingHelper, DWORD)
+LWM(GetChildType, DWORDLONG, DLD)
+LWM(GetClassAlignmentRequirement, DLD, DWORD)
+LWM(GetClassAttribs, DWORDLONG, DWORD)
+LWM(GetClassDomainID, DWORDLONG, DLD)
+LWM(GetClassGClayout, DWORDLONG, Agnostic_GetClassGClayout)
+LWM(GetClassModuleIdForStatics, DWORDLONG, Agnostic_GetClassModuleIdForStatics)
+LWM(GetClassName, DWORDLONG, DWORD)
+LWM(GetClassNumInstanceFields, DWORDLONG, DWORD)
+LWM(GetClassSize, DWORDLONG, DWORD)
+LWM(GetCookieForPInvokeCalliSig, Agnostic_CORINFO_SIG_INFO, DLDL)
+LWM(GetDelegateCtor, Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut)
+LWM(GetEEInfo, DWORD, Agnostic_CORINFO_EE_INFO)
+LWM(GetEHinfo, DLD, Agnostic_CORINFO_EH_CLAUSE)
+LWM(GetFieldAddress, DWORDLONG, Agnostic_GetFieldAddress)
+LWM(GetFieldClass, DWORDLONG, DWORDLONG)
+LWM(GetFieldInClass, DLD, DWORDLONG)
+LWM(GetFieldInfo, Agnostic_GetFieldInfo, Agnostic_CORINFO_FIELD_INFO)
+LWM(GetFieldName, DWORDLONG, DD)
+LWM(GetFieldOffset, DWORDLONG, DWORD)
+LWM(GetFieldThreadLocalStoreID, DWORDLONG, DLD)
+LWM(GetFieldType, DLDL, DLD)
+LWM(GetFunctionEntryPoint, DLD, DLD)
+LWM(GetFunctionFixedEntryPoint, DWORDLONG, Agnostic_CORINFO_CONST_LOOKUP)
+LWM(GetGSCookie, DWORD, DLDL)
+LWM(GetHelperFtn, DWORD, DLDL)
+LWM(GetHelperName, DWORD, DWORD)
+LWM(GetInlinedCallFrameVptr, DWORD, DLDL)
+LWM(GetIntConfigValue, Agnostic_ConfigIntInfo, DWORD)
+LWM(GetIntrinsicID, DWORDLONG, DD)
+LWM(GetJitFlags, DWORD, DD)
+LWM(GetJitTimeLogFilename, DWORD, DWORD)
+LWM(GetJustMyCodeHandle, DWORDLONG, DLDL)
+LWM(GetLazyStringLiteralHelper, DWORDLONG, DWORD)
+LWM(GetLocationOfThisType, DWORDLONG, Agnostic_CORINFO_LOOKUP_KIND)
+LWM(GetMethodAttribs, DWORDLONG, DWORD)
+LWM(GetMethodClass, DWORDLONG, DWORDLONG)
+LWM(GetMethodDefFromMethod, DWORDLONG, DWORD)
+LWM(GetMethodHash, DWORDLONG, DWORD)
+LWM(GetMethodInfo, DWORDLONG, Agnostic_GetMethodInfo)
+LWM(GetMethodName, DLD, DD)
+LWM(GetMethodSig, DLDL, Agnostic_CORINFO_SIG_INFO)
+LWM(GetMethodSync, DWORDLONG, DLDL)
+LWM(GetMethodVTableOffset, DWORDLONG, DD)
+LWM(GetNewArrHelper, DWORDLONG, DWORD)
+LWM(GetNewHelper, Agnostic_GetNewHelper, DWORD)
+LWM(GetParentType, DWORDLONG, DWORDLONG)
+LWM(GetPInvokeUnmanagedTarget, DWORDLONG, DLDL)
+LWM(GetProfilingHandle, DWORD, Agnostic_GetProfilingHandle)
+LWM(GetReadyToRunHelper, DWORDLONG, DWORD)
+LWM(GetReadyToRunDelegateCtorHelper, DWORDLONG, DWORD)
+LWM(GetRelocTypeHint, DWORDLONG, DWORD)
+LWM(GetSecurityPrologHelper, DWORDLONG, DWORD)
+LWM(GetSharedCCtorHelper, DWORDLONG, DWORD)
+LWM(GetStringConfigValue, DWORD, DWORD)
+LWM(GetSystemVAmd64PassStructInRegisterDescriptor, DWORDLONG, Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor)
+LWM(GetTailCallCopyArgsThunk, Agnostic_GetTailCallCopyArgsThunk, DWORDLONG)
+LWM(GetThreadTLSIndex, DWORD, DLD)
+LWM(GetTokenTypeAsHandle, Agnostic_CORINFO_RESOLVED_TOKEN, DWORDLONG)
+LWM(GetTypeForBox, DWORDLONG, DWORDLONG)
+LWM(GetTypeForPrimitiveValueClass, DWORDLONG, DWORD)
+LWM(GetUnBoxHelper, DWORDLONG, DWORD)
+LWM(GetUnmanagedCallConv, DWORDLONG, DWORD)
+LWM(GetVarArgsHandle, Agnostic_CORINFO_SIG_INFO, DLDL)
+LWM(GetVars, DWORDLONG, Agnostic_GetVars)
+DENSELWM(HandleException, DWORD)
+LWM(InitClass, Agnostic_InitClass, DWORD)
+LWM(InitConstraintsForVerification, DWORDLONG, DD)
+LWM(IsCompatibleDelegate, Agnostic_IsCompatibleDelegate, DD)
+LWM(IsDelegateCreationAllowed, DLDL, DWORD)
+LWM(IsFieldStatic, DWORDLONG, DWORD)
+LWM(IsInSIMDModule, DWORDLONG, DWORD)
+LWM(IsInstantiationOfVerifiedGeneric, DWORDLONG, DWORD)
+LWM(IsSDArray, DWORDLONG, DWORD)
+LWM(IsStructRequiringStackAllocRetBuf, DWORDLONG, DWORD)
+LWM(IsValidStringRef, DLD, DWORD)
+LWM(IsValidToken, DLD, DWORD)
+LWM(IsValueClass, DWORDLONG, DWORD)
+LWM(IsWriteBarrierHelperRequired, DWORDLONG, DWORD)
+LWM(MergeClasses, DLDL, DWORDLONG)
+LWM(PInvokeMarshalingRequired, Agnostic_PInvokeMarshalingRequired, DWORD)
+LWM(ResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout)
+LWM(TryResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout)
+LWM(SatisfiesClassConstraints, DWORDLONG, DWORD)
+LWM(SatisfiesMethodConstraints, DLDL, DWORD)
+LWM(ShouldEnforceCallvirtRestriction, DWORDLONG, DWORD)
+
+#undef LWM
+#undef DENSELWM
diff --git a/src/ToolBox/superpmi/superpmi-shared/mclist.cpp b/src/ToolBox/superpmi/superpmi-shared/mclist.cpp
new file mode 100644
index 0000000000..6a6f8701bf
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/mclist.cpp
@@ -0,0 +1,258 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MCList.h - MethodContext List utility class
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "mclist.h"
+#include "logging.h"
+
+bool MCList::processArgAsMCL(char *input, int *count, int **list)
+{
+ // If it contains only '0-9', '-', ',' try to see it as a range list, else try to load as a file
+ bool isRangeList = true;
+
+ size_t len = strlen(input);
+
+ for (unsigned int i=0; (i < len) && isRangeList; i++)
+ {
+ if ((input[i] != '-') && (input[i] != ',') && (!isdigit((unsigned char)input[i])))
+ isRangeList = false;
+ }
+
+ if (isRangeList)
+ {
+ //Count items
+ *count = 0;
+ unsigned rangeStart = 0;
+ bool inRange = false;
+ unsigned scratch = 0;
+ bool foundDigit = false;
+
+ char *tail = input+len;
+
+ for(char* head = input; head <= tail; head++)
+ {
+ scratch = 0;
+ foundDigit = false;
+ while((head<=tail)&&(isdigit((unsigned char)*head)))
+ {
+ scratch = (scratch*10)+((*head)-'0');
+ foundDigit = true;
+ head++;
+ }
+ if(foundDigit)
+ {
+ if(inRange)
+ {
+ inRange = false;
+ if(rangeStart>=scratch)
+ {
+ LogError("Invalid range in '%s'", input);
+ return false;
+ }
+ (*count) += scratch-rangeStart;
+ }
+ else
+ {
+ rangeStart = scratch;
+ (*count)++;
+ }
+ }
+ if(*head == '-')
+ inRange = true;
+ }
+
+ if (*count == 0)
+ {
+ LogError("Didn't find a list!");
+ return false;
+ }
+
+ inRange = false;
+ rangeStart = 0;
+
+ int *ll = new int[*count];
+ *list = ll;
+ int index = 0;
+ ll[index] = 0;
+
+ for(char* head = input; head <= tail; head++)
+ {
+ scratch = 0;
+ foundDigit = false;
+ while((head<=tail)&&(isdigit((unsigned char)*head)))
+ {
+ scratch = (scratch*10)+((*head)-'0');
+ foundDigit = true;
+ head++;
+ }
+ if(foundDigit)
+ {
+ if(inRange)
+ {
+ inRange = false;
+ for(unsigned int i=rangeStart+1;i<=scratch;i++)
+ ll[index++] = i;
+ }
+ else
+ {
+ rangeStart = scratch;
+ ll[index++] = scratch;
+ }
+ }
+ if(*head == '-')
+ inRange = true;
+ }
+ if(inRange)
+ {
+ LogError("Found invalid external range in '%s'", input);
+ return false;
+ }
+ goto checkMCL;
+ }
+ else
+ {
+ char *lastdot = strrchr(input,'.');
+ if(lastdot != nullptr && _stricmp(lastdot, ".mcl")==0)
+ {
+ //Read MCLFile
+ if (!getLineData(input, count, list))
+ return false;
+ if (*count >= 0)
+ goto checkMCL;
+ }
+ return false;
+ }
+
+checkMCL: //check that mcl list is increasing only
+ int *ll = (*list);
+ if (ll[0] == 0)
+ {
+ LogError("MCL list needs to start from 1!");
+ return false;
+ }
+ for (int i = 1; i < *count; i++)
+ {
+ if (ll[i-1] >= ll[i])
+ {
+ LogError("MCL list must be increasing.. found %d -> %d", ll[i - 1], ll[i]);
+ return false;
+ }
+ }
+ return true;
+}
+
+// Returns true on success, false on failure.
+// On success, sets *pIndexCount to the number of indices read, and *pIndexes to a new array with all the indices read. The caller must
+// free the memory with delete[].
+/* static */
+bool MCList::getLineData(const char *nameOfInput, /* OUT */ int *pIndexCount, /* OUT */ int **pIndexes)
+{
+ HANDLE hFile = CreateFileA(nameOfInput, 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", nameOfInput, GetLastError());
+ return false;
+ }
+ LARGE_INTEGER DataTemp;
+ if (!GetFileSizeEx(hFile, &DataTemp))
+ {
+ LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ return false;
+ }
+
+ if (DataTemp.QuadPart > MAXMCLFILESIZE)
+ {
+ LogError("Size %d exceeds max size of %d", DataTemp.QuadPart, MAXMCLFILESIZE);
+ return false;
+ }
+
+ int sz = DataTemp.u.LowPart;
+ char* buff = new char[sz];
+ DWORD bytesRead;
+ if (ReadFile(hFile, buff, sz, &bytesRead, nullptr) == 0)
+ {
+ LogError("ReadFile failed. GetLastError()=%u", GetLastError());
+ delete[] buff;
+ return false;
+ }
+ if (!CloseHandle(hFile))
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ delete[] buff;
+ return false;
+ }
+
+ // Count the lines. Note that the last line better be terminated by a newline.
+ int lineCount = 0;
+ for (int i = 0; i < sz; i++)
+ {
+ if (buff[i] == '\n')
+ {
+ lineCount++;
+ }
+ }
+
+ int* indexes = new int[lineCount];
+ int indexCount = 0;
+ int i = 0;
+ while (i < sz)
+ {
+ //seek the first number on the line. This will skip empty lines and lines with no digits.
+ while (!isdigit((unsigned char)buff[i]))
+ i++;
+ //read in the number
+ indexes[indexCount++] = atoi(&buff[i]);
+ //seek to the start of next line
+ while ((i < sz) && (buff[i] != '\n'))
+ i++;
+ i++;
+ }
+ delete[] buff;
+
+ *pIndexCount = indexCount;
+ *pIndexes = indexes;
+ return true;
+}
+
+void MCList::InitializeMCL(char *filename)
+{
+ hMCLFile = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hMCLFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", filename, GetLastError());
+ }
+}
+
+void MCList::AddMethodToMCL(int methodIndex)
+{
+ if (hMCLFile != INVALID_HANDLE_VALUE)
+ {
+ char strMethodIndex[12];
+ DWORD charCount = 0;
+ DWORD bytesWritten = 0;
+
+ charCount = sprintf(strMethodIndex, "%d\r\n", methodIndex);
+
+ if (!WriteFile(hMCLFile, strMethodIndex, charCount, &bytesWritten, nullptr) || bytesWritten != charCount)
+ {
+ LogError("Failed to write method index '%d'. GetLastError()=%u", strMethodIndex, GetLastError());
+ }
+ }
+}
+
+void MCList::CloseMCL()
+{
+ if (hMCLFile != INVALID_HANDLE_VALUE)
+ {
+ if (CloseHandle(hMCLFile) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ }
+ }
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/mclist.h b/src/ToolBox/superpmi/superpmi-shared/mclist.h
new file mode 100644
index 0000000000..93b2879569
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/mclist.h
@@ -0,0 +1,35 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MCList.h - MethodContext List utility class
+//----------------------------------------------------------
+#ifndef _MCList
+#define _MCList
+#define MAXMCLFILESIZE 0xFFFFFF
+
+class MCList
+{
+public:
+ static bool processArgAsMCL(char *input, int *count, int **list);
+
+ MCList()
+ {
+ //Initialize the static file handle
+ hMCLFile = INVALID_HANDLE_VALUE;
+ }
+
+ //Methods to create an MCL file
+ void InitializeMCL(char *filename);
+ void AddMethodToMCL(int methodIndex);
+ void CloseMCL();
+
+private:
+ static bool getLineData(const char *nameOfInput, /* OUT */ int *pIndexCount, /* OUT */ int **pIndexes);
+
+ //File handle for MCL file
+ HANDLE hMCLFile;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
new file mode 100644
index 0000000000..2c46065b48
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
@@ -0,0 +1,6509 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MethodContext.cpp - Primary structure to store all the EE-JIT details required to replay creation of a method
+// CompileResult contains the stuff generated by a compilation
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "md5.h"
+#include "methodcontext.h"
+#include "compileresult.h"
+#include "lightweightmap.h"
+#include "callutils.h"
+
+struct { int packetID; const char *message; } retiredPackets[] =
+{
+ { 6, "CanEmbedModuleHandleForHelper id 6 superseded by GetLazyStringLiteralHelper id 147 on 12/20/2013" },
+ { 13, "CheckMethodModifier id 13 superseded by id 142 on 2013/07/04. Re-record input with newer shim." },
+ { 14, "CompileMethod id 14 superseded by id 141 on 2013/07/03. Re-record input with newer shim." },
+ { 24, "FindNameOfToken id 24 superseded by id 145 on 2013/07/19. Re-record input with newer shim. Adjusted members to be proper." },
+ { 28, "GetArgClass id 28 superseded by id 139 on 2013/07/03. Re-record input with newer shim." },
+ { 30, "GetArgType id 30 superseded by id 140 on 2013/07/03. Re-record input with newer shim." },
+ { 93, "GetUnBoxHelper2 id 93 unused. 2016/02/19. Re-record input with newer shim." },
+ { 104, "IsValidToken id 104 superseded by id 144 on 2013/07/19. Re-record input with newer shim. Adjusted members to be proper." },
+ { 141, "CompileMethod id 141 superseded by id 142 on 2013/07/09. Re-record input with newer shim. We basically reset lots of other stuff too. :-)" },
+};
+int retiredPacketCount = 7;
+
+#define sparseMC //Support filling in details where guesses are okay and will still generate good code. (i.e. helper function addresses)
+
+#if 0
+// Enable these to get verbose logging during record or playback.
+#define DEBUG_REC(x) \
+ printf("rec"); \
+ x; \
+ printf("\n");
+
+#define DEBUG_REP(x) \
+ printf("rep"); \
+ x; \
+ printf("\n");
+#else
+#define DEBUG_REC(x)
+#define DEBUG_REP(x)
+#endif
+
+MethodContext::MethodContext()
+{
+ methodSize = 0;
+
+ #define LWM(map,key,value) map = nullptr;
+ #include "lwmlist.h"
+
+ cr = new CompileResult();
+ index = -1;
+}
+
+MethodContext::~MethodContext()
+{
+ Destroy();
+}
+
+void MethodContext::Destroy()
+{
+ #define LWM(map,key,value) if (map != nullptr) delete map;
+ #include "lwmlist.h"
+
+ delete cr;
+}
+
+#define sparseAddLen(target) \
+ if (target != nullptr) \
+ { \
+ if (target->GetCount() != 0) \
+ totalLen += target->CalculateArraySize() + 6; /* packet canary from lightweightmap + packet marker */ \
+ }
+
+#define sparseWriteFile(target) \
+ if (target != nullptr) \
+ { \
+ if (target->GetCount() != 0) \
+ { \
+ buff2[buffIndex++] = (unsigned char) Packet_##target; \
+ unsigned int loc = target->DumpToArray(&buff2[buffIndex + 4]); \
+ memcpy(&buff2[buffIndex], &loc, sizeof(unsigned int)); \
+ buffIndex += 4 + loc; \
+ buff2[buffIndex++] = 0x42; \
+ } \
+ }
+
+#define sparseWriteFileCR(target) \
+ if (cr != nullptr) \
+ { \
+ if (cr->target != nullptr) \
+ { \
+ if (cr->target->GetCount() != 0) \
+ { \
+ buff2[buffIndex++] = (unsigned char) PacketCR_##target; \
+ unsigned int loc = cr->target->DumpToArray(&buff2[buffIndex + 4]); \
+ memcpy(&buff2[buffIndex], &loc, sizeof(unsigned int)); \
+ buffIndex += 4 + loc; \
+ buff2[buffIndex++] = 0x42; \
+ } \
+ } \
+ }
+
+#define sparseReadFile(target, key, value) \
+ case Packet_##target: \
+ { \
+ target = new LightWeightMap<key, value>(); \
+ target->ReadFromArray(&buff2[buffIndex], localsize); \
+ break; \
+ }
+
+#define sparseReadFileCR(target, key, value) \
+ case PacketCR_##target: \
+ { \
+ cr->target = new LightWeightMap<key, value>(); \
+ cr->target->ReadFromArray(&buff2[buffIndex], localsize); \
+ break; \
+ }
+
+#define sparseReadFileDense(target, value) \
+ case Packet_##target: \
+ { \
+ target = new DenseLightWeightMap<value>(); \
+ target->ReadFromArray(&buff2[buffIndex], localsize); \
+ break; \
+ }
+
+#define sparseReadFileCRDense(target, value) \
+ case PacketCR_##target: \
+ { \
+ cr->target = new DenseLightWeightMap<value>(); \
+ cr->target->ReadFromArray(&buff2[buffIndex], localsize); \
+ break; \
+ }
+
+unsigned int MethodContext::calculateFileSize()
+{
+ //Calculate file size
+ unsigned int totalLen = 0;
+
+ #define LWM(map,key,value) sparseAddLen(map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ if (cr != nullptr)
+ {
+ #define LWM(map,key,value) sparseAddLen(cr->map);
+ #include "crlwmlist.h"
+ }
+
+ return totalLen;
+}
+
+unsigned int MethodContext::calculateRawFileSize()
+{
+ return 2 /* leading magic cookie 'm', 'c' */ + 4 /* 4-byte data length */ + calculateFileSize() + 2 /* end canary '4', '2' */;
+}
+
+unsigned int MethodContext::saveToFile(HANDLE hFile)
+{
+ unsigned int totalLen = calculateFileSize();
+ unsigned int totalFileSize = 2 /* leading magic cookie 'm', 'c' */ + 4 /* 4-byte data length */ + totalLen + 2 /* end canary '4', '2' */;
+
+ DWORD bytesWritten = 0;
+ unsigned int buffIndex = 0;
+ unsigned char *buff2 = new unsigned char[totalFileSize];
+ buff2[buffIndex++] = 'm';
+ buff2[buffIndex++] = 'c';
+ memcpy(&buff2[buffIndex], &totalLen, sizeof(unsigned int));
+ buffIndex += 4;
+
+ #define LWM(map,key,value) sparseWriteFile(map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ #define LWM(map,key,value) sparseWriteFileCR(map);
+ #include "crlwmlist.h"
+
+ // Write the end canary
+ buff2[buffIndex++] = '4';
+ buff2[buffIndex++] = '2';
+
+ Assert(buffIndex == totalFileSize);
+
+ WriteFile(hFile, buff2, totalFileSize, &bytesWritten, NULL);
+ delete[]buff2;
+ return bytesWritten;
+}
+
+// This code can't exist in a function with C++ objects needing destruction. Returns true on success
+// (and sets *ppmc with new MethodContext), false on failure.
+//
+// static
+bool MethodContext::Initialize(int loadedCount, unsigned char* buff, DWORD size, /* OUT */ MethodContext** ppmc)
+{
+ MethodContext* mc = new MethodContext();
+ mc->index = loadedCount;
+ *ppmc = mc;
+ return mc->Initialize(loadedCount, buff, size);
+}
+
+// static
+bool MethodContext::Initialize(int loadedCount, HANDLE hFile, /* OUT */ MethodContext** ppmc)
+{
+ MethodContext* mc = new MethodContext();
+ mc->index = loadedCount;
+ *ppmc = mc;
+ return mc->Initialize(loadedCount, hFile);
+}
+
+bool MethodContext::Initialize(int loadedCount, unsigned char* buff, DWORD size)
+{
+ bool result = true;
+
+ struct Param
+ {
+ unsigned char* buff;
+ DWORD size;
+ MethodContext* pThis;
+ } param;
+ param.buff = buff;
+ param.size = size;
+ param.pThis = this;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ pParam->pThis->MethodInitHelper(pParam->buff, pParam->size);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchMC)
+ {
+ LogError("Method %d is of low integrity.", loadedCount);
+ result = false;
+ }
+ PAL_ENDTRY
+
+ return result;
+}
+
+bool MethodContext::Initialize(int loadedCount, HANDLE hFile)
+{
+ bool result = true;
+
+ struct Param
+ {
+ HANDLE hFile;
+ MethodContext* pThis;
+ } param;
+ param.hFile = hFile;
+ param.pThis = this;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ pParam->pThis->MethodInitHelperFile(pParam->hFile);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchMC)
+ {
+ LogError("Method %d is of low integrity.", loadedCount);
+ result = false;
+ }
+ PAL_ENDTRY
+
+ return result;
+}
+
+void MethodContext::MethodInitHelperFile(HANDLE hFile)
+{
+ DWORD bytesRead;
+ char buff[512];
+ unsigned int totalLen = 0;
+
+ AssertCode(ReadFile(hFile, buff, 2 + sizeof(unsigned int), &bytesRead, NULL) == TRUE, EXCEPTIONCODE_MC); //Read Magic number and totalLen
+ AssertCodeMsg((buff[0] == 'm') && (buff[1] == 'c'), EXCEPTIONCODE_MC, "Didn't find magic number");
+ memcpy(&totalLen, &buff[2], sizeof(unsigned int));
+ unsigned char *buff2 = new unsigned char[totalLen + 2]; //total + End Canary
+ AssertCode(ReadFile(hFile, buff2, totalLen + 2, &bytesRead, NULL) == TRUE, EXCEPTIONCODE_MC);
+ AssertCodeMsg((buff2[totalLen] == '4') && (buff2[totalLen + 1] == '2'), EXCEPTIONCODE_MC, "Didn't find end canary");
+ MethodInitHelper(buff2, totalLen);
+}
+
+void MethodContext::MethodInitHelper(unsigned char *buff2, unsigned int totalLen)
+{
+ MethodContext::MethodContext();
+
+ unsigned int buffIndex = 0;
+ unsigned int localsize = 0;
+ unsigned char canary = 0xff;
+ unsigned char *buff3 = nullptr;
+
+ while (buffIndex < totalLen)
+ {
+ unsigned char packetType = buff2[buffIndex++];
+ memcpy(&localsize, &buff2[buffIndex], sizeof(unsigned int));
+ buffIndex += 4;
+
+ switch (packetType)
+ {
+ #define LWM(map,key,value) sparseReadFile(map,key,value)
+ #define DENSELWM(map,value) sparseReadFileDense(map,value)
+ #include "lwmlist.h"
+
+ #define LWM(map,key,value) sparseReadFileCR(map,key,value)
+ #define DENSELWM(map,value) sparseReadFileCRDense(map,value)
+ #include "crlwmlist.h"
+
+ default:
+ for (int i = 0; i < retiredPacketCount; i++)
+ {
+ AssertCodeMsg(retiredPackets[i].packetID != packetType, EXCEPTIONCODE_MC, "Ran into retired packet %u '%s'", packetType, retiredPackets[i].message);
+ }
+ LogException(EXCEPTIONCODE_MC, "Read ran into unknown packet type %u. Are you using a newer recorder?", packetType);
+ // break;
+ }
+ buffIndex += localsize;
+ canary = buff2[buffIndex++];
+ AssertCodeMsg(canary == 0x42, EXCEPTIONCODE_MC, "Didn't find trailing canary for map");
+ }
+ AssertCodeMsg((buff2[buffIndex++] == '4') && (buff2[buffIndex++] == '2'), EXCEPTIONCODE_MC, "Didn't find trailing canary for map");
+ delete[]buff2;
+}
+
+#define dumpStat(target) \
+ if (target != nullptr) \
+ { \
+ if (target->GetCount() > 0) \
+ { \
+ int t = sprintf_s(buff, len, "%u", target->GetCount()); \
+ buff += t; \
+ len -= t; \
+ } \
+ } \
+ { \
+ *buff++ = ','; \
+ len--; \
+ } \
+ if (target != nullptr) \
+ { \
+ int t = sprintf_s(buff, len, "%u", target->CalculateArraySize()); \
+ buff += t; \
+ len -= t; \
+ } \
+ { \
+ *buff++ = ','; \
+ len--; \
+ }
+
+// Dump statistics about each LightWeightMap to the buffer: count of elements, and total size in bytes of map.
+int MethodContext::dumpStatToBuffer(char *buff, int len)
+{
+ char *obuff = buff;
+ //assumption of enough buffer.. :-|
+
+ #define LWM(map,key,value) dumpStat(map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ #define LWM(map,key,value) dumpStat(cr->map);
+ #include "crlwmlist.h"
+
+ return (int)(buff - obuff);
+}
+int MethodContext::dumpStatTitleToBuffer(char *buff, int len)
+{
+ const char *title =
+
+ #define LWM(map,key,value) #map "," #map " SZ,"
+ #include "lwmlist.h"
+
+ #define LWM(map,key,value) "CR_" #map ",CR_" #map " SZ,"
+ #include "crlwmlist.h"
+
+ ;
+
+ int titleLen = (int)strlen(title);
+ if ((titleLen + 1) > len)
+ {
+ LogError("titleLen is larger than given len");
+ return 0;
+ }
+ strcpy_s(buff, len, title);
+ return titleLen;
+}
+
+#define softMapEqual(a) \
+ if (a != nullptr) \
+ { \
+ if (other->a == nullptr) return false; \
+ if (a->GetCount() != other->a->GetCount()) return false; \
+ } \
+ else if (other->a != nullptr) return false;
+
+
+bool MethodContext::Equal(MethodContext *other)
+{
+ //returns true if equal. Note this is permissive, that is to say that we may reason that too many things are equal. Adding more detailed checks would cause us to reason more things as unique;
+
+ //Compare MethodInfo's first.
+ CORINFO_METHOD_INFO otherInfo;
+ unsigned otherFlags = 0;
+ other->repCompileMethod(&otherInfo, &otherFlags);
+
+ CORINFO_METHOD_INFO ourInfo;
+ unsigned ourFlags = 0;
+ repCompileMethod(&ourInfo, &ourFlags);
+
+ if (otherInfo.ILCodeSize != ourInfo.ILCodeSize)
+ return false;
+ if (otherInfo.args.numArgs != ourInfo.args.numArgs)
+ return false;
+ if (otherInfo.args.retType != ourInfo.args.retType)
+ return false;
+ if (otherInfo.locals.numArgs != ourInfo.locals.numArgs)
+ return false;
+ if (otherInfo.EHcount != ourInfo.EHcount)
+ return false;
+ if (otherInfo.options != ourInfo.options)
+ return false;
+ for (unsigned int j = 0; j < otherInfo.ILCodeSize; j++)
+ if (otherInfo.ILCode[j] != ourInfo.ILCode[j])
+ return false;
+ if (otherInfo.maxStack != ourInfo.maxStack)
+ return false;
+ if (otherInfo.regionKind != ourInfo.regionKind)
+ return false;
+ if (otherInfo.args.callConv != ourInfo.args.callConv)
+ return false;
+ if (otherInfo.args.cbSig != ourInfo.args.cbSig)
+ return false;
+ if (otherInfo.args.flags != ourInfo.args.flags)
+ return false;
+ if (otherInfo.locals.cbSig != ourInfo.locals.cbSig)
+ return false;
+ if (otherFlags != ourFlags)
+ return false;
+
+ //Now compare the other maps to "estimate" equality.
+
+ #define LWM(map,key,value) softMapEqual(map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ #define LWM(map,key,value) softMapEqual(cr->map)
+ #include "crlwmlist.h"
+
+ //Base case is we "match"
+ return true;
+}
+
+//------------------------------------------------------------------------------
+// MethodContext::recGlobalContext
+// This method copies any relevant global (i.e. per-JIT-instance) data from
+// the given method context. Currently this is limited to configuiration
+// values, but may grow to encompass other information in the future (e.g.
+// any information that is exposed by the ICorJitHost interface and is
+// therefore accessible outside the context of a call to
+// `ICJI::compileMethod`).
+//
+// This method is intended to be called as part of initializing a method
+// during collection, similar to `methodContext::recEnvironment`.
+void MethodContext::recGlobalContext(const MethodContext& other)
+{
+ Assert(GetIntConfigValue == nullptr);
+ Assert(GetStringConfigValue == nullptr);
+
+ if (other.GetIntConfigValue != nullptr)
+ {
+ GetIntConfigValue = new LightWeightMap<Agnostic_ConfigIntInfo, DWORD>(*other.GetIntConfigValue);
+ }
+
+ if (other.GetStringConfigValue != nullptr)
+ {
+ GetStringConfigValue = new LightWeightMap<DWORD, DWORD>(*other.GetStringConfigValue);
+ }
+}
+
+void MethodContext::recEnvironment()
+{
+ if (Environment == nullptr)
+ Environment = new DenseLightWeightMap<Agnostic_Environment>();
+
+ char *l_EnvStr;
+ char *l_val;
+
+#ifdef FEATURE_PAL
+ l_EnvStr = GetEnvironmentStringsA();
+#else // !FEATURE_PAL
+ l_EnvStr = GetEnvironmentStrings();
+#endif // !FEATURE_PAL
+
+ l_val = l_EnvStr;
+
+ char* l_str = l_EnvStr;
+
+ int count = 0;
+ while (true)
+ {
+ if (*l_str == 0) break;
+ while (*l_str != 0) l_str++;
+ l_str++;
+ count++;
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ if ((_strnicmp(l_EnvStr, "complus_", 8) == 0) || (_strnicmp(l_EnvStr, "dbflag", 6) == 0) || (_strnicmp(l_EnvStr, "BVT_TEST_ID", 11) == 0))
+ {
+ char *val = l_EnvStr;
+ while (*val != '=')
+ val++;
+ *val++ = 0;
+ int nameind = Environment->AddBuffer((unsigned char*)l_EnvStr, (int)strlen(l_EnvStr) + 1);
+ int valind = Environment->AddBuffer((unsigned char*)val, (int)strlen(val) + 1);
+ Agnostic_Environment value;
+ value.name_index = nameind;
+ value.val_index = valind;
+ DWORD key = (DWORD)Environment->GetCount();
+ Environment->Append(value);
+ DEBUG_REC(dmpEnvironment(key, value));
+ l_EnvStr = val;
+ }
+ while (*l_EnvStr != '\0')
+ l_EnvStr++;
+ l_EnvStr++;
+ }
+ FreeEnvironmentStringsA(l_val);
+}
+void MethodContext::dmpEnvironment(DWORD key, const Agnostic_Environment& value)
+{
+ printf("Environment key %u, value '%s' '%s'", key, (LPCSTR)Environment->GetBuffer(value.name_index),
+ (LPCSTR)Environment->GetBuffer(value.val_index));
+ Environment->Unlock();
+}
+void MethodContext::repEnvironmentSet()
+{
+ if (Environment == nullptr)
+ return;
+ Agnostic_Environment val;
+ for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ {
+ val = Environment->Get((DWORD)i);
+ DEBUG_REP(dmpEnvironment(i, val));
+
+ SetEnvironmentVariableA((LPCSTR)Environment->GetBuffer(val.name_index), (LPCSTR)Environment->GetBuffer(val.val_index));
+ }
+}
+
+int MethodContext::repGetTestID()
+{
+ //CLR Test asset only - we capture the testID via smarty-environnent (BVT_TEST_ID) during record time
+ //This procedure returns the test id if found and -1 otherwise
+
+ if (Environment == nullptr)
+ return -1;
+ Agnostic_Environment val;
+ LPCSTR key;
+ int value = -1;
+ for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ {
+ val = Environment->Get((DWORD)i);
+ key = (LPCSTR)Environment->GetBuffer(val.name_index);
+
+ if (_strnicmp(key, "BVT_TEST_ID", 11) == 0)
+ {
+ value = atoi((LPCSTR)Environment->GetBuffer(val.val_index));
+ break;
+ }
+ }
+
+ if (value == -1)
+ {
+ LogError("Couldn't find Smarty test ID");
+ }
+
+ return value;
+}
+
+void MethodContext::repEnvironmentUnset()
+{
+ if (Environment == nullptr)
+ return;
+ Agnostic_Environment val;
+ for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ {
+ val = Environment->Get((DWORD)i);
+ SetEnvironmentVariableA((LPCSTR)Environment->GetBuffer(val.name_index), nullptr);
+ }
+}
+int MethodContext::repEnvironmentGetCount()
+{
+ int result = 0;
+ if (Environment != nullptr)
+ result = Environment->GetCount();
+ return result;
+}
+
+void MethodContext::dumpToConsole(int mcNumber)
+{
+ printf("*****************************************");
+ if (mcNumber != -1)
+ {
+ printf(" method context #%d", mcNumber);
+ }
+ printf("\n");
+
+ #define LWM(map,key,value) dumpLWM(this,map)
+ #define DENSELWM(map,value) dumpLWMDense(this,map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ #define LWM(map,key,value) dumpLWM(this->cr,map)
+ #define DENSELWM(map,value) dumpLWMDense(this->cr,map)
+ #include "crlwmlist.h"
+}
+
+const char* toString(CorInfoType cit)
+{
+ switch (cit)
+ {
+ case CORINFO_TYPE_UNDEF : return "undef";
+ case CORINFO_TYPE_VOID : return "void";
+ case CORINFO_TYPE_BOOL : return "bool";
+ case CORINFO_TYPE_CHAR : return "char";
+ case CORINFO_TYPE_BYTE : return "byte";
+ case CORINFO_TYPE_UBYTE : return "ubyte";
+ case CORINFO_TYPE_SHORT : return "short";
+ case CORINFO_TYPE_USHORT : return "ushort";
+ case CORINFO_TYPE_INT : return "int";
+ case CORINFO_TYPE_UINT : return "uint";
+ case CORINFO_TYPE_LONG : return "long";
+ case CORINFO_TYPE_ULONG : return "ulong";
+ case CORINFO_TYPE_NATIVEINT : return "nativeint";
+ case CORINFO_TYPE_NATIVEUINT : return "nativeuint";
+ case CORINFO_TYPE_FLOAT : return "float";
+ case CORINFO_TYPE_DOUBLE : return "double";
+ case CORINFO_TYPE_STRING : return "string";
+ case CORINFO_TYPE_PTR : return "ptr";
+ case CORINFO_TYPE_BYREF : return "byref";
+ case CORINFO_TYPE_VALUECLASS : return "valueclass";
+ case CORINFO_TYPE_CLASS : return "class";
+ case CORINFO_TYPE_REFANY : return "refany";
+ case CORINFO_TYPE_VAR : return "var";
+ default : return "UNKNOWN";
+ }
+}
+
+unsigned int toCorInfoSize(CorInfoType cit)
+{
+ switch (cit)
+ {
+ case CORINFO_TYPE_BOOL:
+ case CORINFO_TYPE_BYTE:
+ case CORINFO_TYPE_UBYTE:
+ return 1;
+
+ case CORINFO_TYPE_CHAR:
+ case CORINFO_TYPE_SHORT:
+ case CORINFO_TYPE_USHORT:
+ return 2;
+
+ case CORINFO_TYPE_FLOAT:
+ case CORINFO_TYPE_INT:
+ case CORINFO_TYPE_UINT:
+ return 4;
+
+ case CORINFO_TYPE_DOUBLE:
+ case CORINFO_TYPE_LONG:
+ case CORINFO_TYPE_ULONG:
+ return 8;
+
+ case CORINFO_TYPE_NATIVEINT:
+ case CORINFO_TYPE_NATIVEUINT:
+ case CORINFO_TYPE_PTR:
+ case CORINFO_TYPE_BYREF:
+ case CORINFO_TYPE_CLASS:
+ return sizeof(void *);
+
+ case CORINFO_TYPE_STRING:
+ case CORINFO_TYPE_VALUECLASS:
+ case CORINFO_TYPE_REFANY:
+ case CORINFO_TYPE_UNDEF:
+ case CORINFO_TYPE_VOID:
+ default:
+ __debugbreak();
+ return 0;
+ }
+ return -1;
+}
+
+void MethodContext::recCompileMethod(CORINFO_METHOD_INFO *info, unsigned flags)
+{
+ if (CompileMethod == nullptr)
+ CompileMethod = new LightWeightMap<DWORD, Agnostic_CompileMethod>();
+
+ Agnostic_CompileMethod value;
+
+ value.info.ftn = (DWORDLONG)info->ftn;
+ value.info.scope = (DWORDLONG)info->scope;
+ value.info.ILCode_offset = (DWORD)CompileMethod->AddBuffer(info->ILCode, info->ILCodeSize);
+ value.info.ILCodeSize = (DWORD)info->ILCodeSize;
+ value.info.maxStack = (DWORD)info->maxStack;
+ value.info.EHcount = (DWORD)info->EHcount;
+ value.info.options = (DWORD)info->options;
+ value.info.regionKind = (DWORD)info->regionKind;
+ value.info.args.callConv = (DWORD)info->args.callConv;
+ value.info.args.retTypeClass = (DWORDLONG)info->args.retTypeClass;
+ value.info.args.retTypeSigClass = (DWORDLONG)info->args.retTypeSigClass;
+ value.info.args.retType = (DWORD)info->args.retType;
+ value.info.args.flags = (DWORD)info->args.flags;
+ value.info.args.numArgs = (DWORD)info->args.numArgs;
+ value.info.args.sigInst_classInstCount = (DWORD)info->args.sigInst.classInstCount;
+ value.info.args.sigInst_classInst_Index = CompileMethod->AddBuffer((unsigned char*)info->args.sigInst.classInst, info->args.sigInst.classInstCount * 8); // porting issue
+ value.info.args.sigInst_methInstCount = (DWORD)info->args.sigInst.methInstCount;
+ value.info.args.sigInst_methInst_Index = CompileMethod->AddBuffer((unsigned char*)info->args.sigInst.methInst, info->args.sigInst.methInstCount * 8); // porting issue
+ value.info.args.args = (DWORDLONG)info->args.args;
+ value.info.args.cbSig = (DWORD)info->args.cbSig;
+ value.info.args.pSig = (DWORD)CompileMethod->AddBuffer((unsigned char *)info->args.pSig, info->args.cbSig);
+ value.info.args.scope = (DWORDLONG)info->args.scope;
+ value.info.args.token = (DWORD)info->args.token;
+ value.info.locals.callConv = (DWORD)info->locals.callConv;
+ value.info.locals.retTypeClass = (DWORDLONG)info->locals.retTypeClass;
+ value.info.locals.retTypeSigClass = (DWORDLONG)info->locals.retTypeSigClass;
+ value.info.locals.retType = (DWORD)info->locals.retType;
+ value.info.locals.flags = (DWORD)info->locals.flags;
+ value.info.locals.numArgs = (DWORD)info->locals.numArgs;
+ value.info.locals.sigInst_classInstCount = (DWORD)info->locals.sigInst.classInstCount;
+ value.info.locals.sigInst_classInst_Index = CompileMethod->AddBuffer((unsigned char*)info->locals.sigInst.classInst, info->locals.sigInst.classInstCount * 8); // porting issue
+ value.info.locals.sigInst_methInstCount = (DWORD)info->locals.sigInst.methInstCount;
+ value.info.locals.sigInst_methInst_Index = CompileMethod->AddBuffer((unsigned char*)info->locals.sigInst.methInst, info->locals.sigInst.methInstCount * 8); // porting issue
+ value.info.locals.args = (DWORDLONG)info->locals.args;
+ value.info.locals.cbSig = (DWORD)info->locals.cbSig;
+ value.info.locals.pSig = (DWORD)CompileMethod->AddBuffer((unsigned char *)info->locals.pSig, info->locals.cbSig);
+ value.info.locals.scope = (DWORDLONG)info->locals.scope;
+ value.info.locals.token = (DWORD)info->locals.token;
+ value.flags = (DWORD)flags;
+
+ CompileMethod->Add(0, value);
+ DEBUG_REC(dmpCompileMethod(0, value));
+}
+void MethodContext::dmpCompileMethod(DWORD key, const Agnostic_CompileMethod& value)
+{
+ printf("CompiledMethod key %u, value ftn-%016llX scp-%016llX ilo-%u ils-%u ms-%u ehc-%u opt-%u rk-%u "
+ "args{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} "
+ "locals{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} "
+ "flg-%08X",
+ key,
+ value.info.ftn,
+ value.info.scope,
+ value.info.ILCode_offset,
+ value.info.ILCodeSize,
+ value.info.maxStack,
+ value.info.EHcount,
+ value.info.options,
+ value.info.regionKind,
+ value.info.args.callConv,
+ value.info.args.retTypeClass,
+ value.info.args.retTypeSigClass,
+ value.info.args.retType,
+ toString((CorInfoType)value.info.args.retType),
+ value.info.args.flags,
+ value.info.args.numArgs,
+ value.info.args.sigInst_classInstCount,
+ value.info.args.sigInst_classInst_Index,
+ value.info.args.sigInst_methInstCount,
+ value.info.args.sigInst_methInst_Index,
+ value.info.args.args,
+ value.info.args.cbSig,
+ value.info.args.pSig,
+ value.info.args.scope,
+ value.info.args.token,
+ value.info.locals.callConv,
+ value.info.locals.retTypeClass,
+ value.info.locals.retTypeSigClass,
+ value.info.locals.retType,
+ toString((CorInfoType)value.info.locals.retType),
+ value.info.locals.flags,
+ value.info.locals.numArgs,
+ value.info.locals.sigInst_classInstCount,
+ value.info.locals.sigInst_classInst_Index,
+ value.info.locals.sigInst_methInstCount,
+ value.info.locals.sigInst_methInst_Index,
+ value.info.locals.args,
+ value.info.locals.cbSig,
+ value.info.locals.pSig,
+ value.info.locals.scope,
+ value.info.locals.token,
+ value.flags);
+}
+void MethodContext::repCompileMethod(CORINFO_METHOD_INFO *info, unsigned *flags)
+{
+ Agnostic_CompileMethod value;
+
+ value = CompileMethod->Get((DWORD)0); //The only item in this set is a single group of inputs to CompileMethod
+
+ info->ftn = (CORINFO_METHOD_HANDLE)value.info.ftn;
+ info->scope = (CORINFO_MODULE_HANDLE)value.info.scope;
+ info->ILCode = CompileMethod->GetBuffer(value.info.ILCode_offset);
+ info->ILCodeSize = (unsigned)value.info.ILCodeSize;
+ methodSize = info->ILCodeSize;
+ info->maxStack = (unsigned)value.info.maxStack;
+ info->EHcount = (unsigned)value.info.EHcount;
+ info->options = (CorInfoOptions)value.info.options;
+ info->regionKind = (CorInfoRegionKind)value.info.regionKind;
+ info->args.callConv = (CorInfoCallConv)value.info.args.callConv;
+ info->args.retTypeClass = (CORINFO_CLASS_HANDLE)value.info.args.retTypeClass;
+ info->args.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.info.args.retTypeSigClass;
+ info->args.retType = (CorInfoType)value.info.args.retType;
+ info->args.flags = (unsigned)value.info.args.flags;
+ info->args.numArgs = (unsigned)value.info.args.numArgs;
+ info->args.sigInst.classInstCount = (unsigned)value.info.args.sigInst_classInstCount;
+ info->args.sigInst.classInst = (CORINFO_CLASS_HANDLE*)CompileMethod->GetBuffer(value.info.args.sigInst_classInst_Index);
+ info->args.sigInst.methInstCount = (unsigned)value.info.args.sigInst_methInstCount;
+ info->args.sigInst.methInst = (CORINFO_CLASS_HANDLE*)CompileMethod->GetBuffer(value.info.args.sigInst_methInst_Index);
+ info->args.args = (CORINFO_ARG_LIST_HANDLE)value.info.args.args;
+ info->args.cbSig = (unsigned int)value.info.args.cbSig;
+ info->args.pSig = (PCCOR_SIGNATURE)CompileMethod->GetBuffer(value.info.args.pSig);
+ info->args.scope = (CORINFO_MODULE_HANDLE)value.info.args.scope;
+ info->args.token = (mdToken)value.info.args.token;
+ info->locals.callConv = (CorInfoCallConv)value.info.locals.callConv;
+ info->locals.retTypeClass = (CORINFO_CLASS_HANDLE)value.info.locals.retTypeClass;
+ info->locals.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.info.locals.retTypeSigClass;
+ info->locals.retType = (CorInfoType)value.info.locals.retType;
+ info->locals.flags = (unsigned)value.info.locals.flags;
+ info->locals.numArgs = (unsigned)value.info.locals.numArgs;
+ info->locals.sigInst.classInstCount = (unsigned)value.info.locals.sigInst_classInstCount;
+ info->locals.sigInst.classInst = (CORINFO_CLASS_HANDLE*)CompileMethod->GetBuffer(value.info.locals.sigInst_classInst_Index);
+ info->locals.sigInst.methInstCount = (unsigned)value.info.locals.sigInst_methInstCount;
+ info->locals.sigInst.methInst = (CORINFO_CLASS_HANDLE*)CompileMethod->GetBuffer(value.info.locals.sigInst_methInst_Index);
+ info->locals.args = (CORINFO_ARG_LIST_HANDLE)value.info.locals.args;
+ info->locals.cbSig = (unsigned int)value.info.locals.cbSig;
+ info->locals.pSig = (PCCOR_SIGNATURE)CompileMethod->GetBuffer(value.info.locals.pSig);
+ info->locals.scope = (CORINFO_MODULE_HANDLE)value.info.locals.scope;
+ info->locals.token = (mdToken)value.info.locals.token;
+ *flags = (unsigned)value.flags;
+ DEBUG_REP(dmpCompileMethod(0, value));
+}
+
+void MethodContext::recGetMethodClass(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CLASS_HANDLE classHandle)
+{
+ if (GetMethodClass == nullptr)
+ GetMethodClass = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetMethodClass->Add((DWORDLONG)methodHandle, (DWORDLONG)classHandle);
+ DEBUG_REC(dmpGetMethodClass((DWORDLONG)methodHandle, (DWORDLONG)classHandle));
+}
+void MethodContext::dmpGetMethodClass(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetMethodClass key %016llX, value %016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetMethodClass(CORINFO_METHOD_HANDLE methodHandle)
+{
+ AssertCodeMsg(GetMethodClass != nullptr, EXCEPTIONCODE_MC, "Found a null GetMethodClass. Probably missing a fatTrigger for %016llX.", (DWORDLONG)methodHandle);
+ int index = GetMethodClass->GetIndex((DWORDLONG)methodHandle);
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_MC, "Didn't find %016llX. Probably missing a fatTrigger", (DWORDLONG)methodHandle);
+ CORINFO_CLASS_HANDLE value = (CORINFO_CLASS_HANDLE)GetMethodClass->Get((DWORDLONG)methodHandle);
+ DEBUG_REP(dmpGetMethodClass((DWORDLONG)methodHandle, (DWORDLONG)value));
+ return value;
+}
+
+void MethodContext::recGetClassAttribs(CORINFO_CLASS_HANDLE classHandle, DWORD attribs)
+{
+ if (GetClassAttribs == nullptr)
+ GetClassAttribs = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetClassAttribs->Add((DWORDLONG)classHandle, (DWORD)attribs);
+ DEBUG_REC(dmpGetClassAttribs((DWORDLONG)classHandle, attribs));
+}
+void MethodContext::dmpGetClassAttribs(DWORDLONG key, DWORD value)
+{
+ printf("GetClassAttribs key %016llX, value %u", key, value);
+}
+DWORD MethodContext::repGetClassAttribs(CORINFO_CLASS_HANDLE classHandle)
+{
+ AssertCodeMsg(GetClassAttribs != nullptr, EXCEPTIONCODE_MC, "Found a null GetMethodClass. Probably missing a fatTrigger for %016llX.", (DWORDLONG)classHandle);
+ int index = GetClassAttribs->GetIndex((DWORDLONG)classHandle);
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_MC, "Didn't find %016llX. Probably missing a fatTrigger", (DWORDLONG)classHandle);
+ DWORD value = (DWORD)GetClassAttribs->Get((DWORDLONG)classHandle);
+ DEBUG_REP(dmpGetClassAttribs((DWORDLONG)classHandle, value));
+ return value;
+}
+
+void MethodContext::recGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle, DWORD attribs)
+{
+ if (GetMethodAttribs == nullptr)
+ GetMethodAttribs = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetMethodAttribs->Add((DWORDLONG)methodHandle, attribs);
+ DEBUG_REC(dmpGetMethodAttribs((DWORDLONG)methodHandle, attribs));
+}
+void MethodContext::dmpGetMethodAttribs(DWORDLONG key, DWORD value)
+{
+ printf("GetMethodAttribs key %016llX, value %u", key, value);
+}
+DWORD MethodContext::repGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle)
+{
+ AssertCodeMsg(GetMethodAttribs != nullptr, EXCEPTIONCODE_MC, "Found a null GetMethodAttribs. Probably missing a fatTrigger for %016llX.", (DWORDLONG)methodHandle);
+ int index = GetMethodAttribs->GetIndex((DWORDLONG)methodHandle);
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_MC, "Didn't find %016llX. Probably missing a fatTrigger", (DWORDLONG)methodHandle);
+ DWORD value = (DWORD)GetMethodAttribs->Get((DWORDLONG)methodHandle);
+ DEBUG_REP(dmpGetMethodAttribs((DWORDLONG)methodHandle, value));
+ if (cr->repSetMethodAttribs(methodHandle) == CORINFO_FLG_BAD_INLINEE)
+ value ^= CORINFO_FLG_DONT_INLINE;
+ return value;
+}
+
+//Note - the jit will call freearray on the array we give back....
+void MethodContext::recGetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars_in, bool *extendOthers)
+{
+ if (GetVars == nullptr)
+ GetVars = new LightWeightMap<DWORDLONG, Agnostic_GetVars>();
+
+ Agnostic_GetVars value;
+
+ value.cVars = (DWORD)*cVars;
+ value.vars_offset = (DWORD)GetVars->AddBuffer((unsigned char*)*vars_in, sizeof(ICorDebugInfo::ILVarInfo) * (*cVars));
+
+ value.extendOthers = (DWORD)*extendOthers;
+ GetVars->Add((DWORDLONG)ftn, value);
+ DEBUG_REC(dmpGetVars((DWORDLONG)ftn, value));
+}
+void MethodContext::dmpGetVars(DWORDLONG key, const Agnostic_GetVars& value)
+{
+ ICorDebugInfo::ILVarInfo *vars = (ICorDebugInfo::ILVarInfo *)GetVars->GetBuffer(value.vars_offset);
+ printf("GetVars key ftn-%016llX, value cVars-%u extendOthers-%u (", key, value.cVars, value.extendOthers);
+ for (unsigned int i = 0; i < value.cVars; i++)
+ printf("(%u %u %u %u)", i, vars[i].startOffset, vars[i].endOffset, vars[i].varNumber);
+ printf(")");
+ GetVars->Unlock();
+}
+void MethodContext::repGetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars_in, bool *extendOthers)
+{
+ Agnostic_GetVars value;
+ if (GetVars == nullptr)
+ {
+ *cVars = 0;
+ return;
+ }
+ value = GetVars->Get((DWORDLONG)ftn);
+ *cVars = (ULONG32)value.cVars;
+ if (*cVars > 0)
+ *vars_in = (ICorDebugInfo::ILVarInfo *)GetVars->GetBuffer(value.vars_offset);
+ *extendOthers = value.extendOthers != 0;
+ DEBUG_REP(dmpGetVars((DWORDLONG)ftn, value));
+}
+
+//Note - the jit will call freearray on the array we give back....
+void MethodContext::recGetBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int *cILOffsets, DWORD **pILOffsets, ICorDebugInfo::BoundaryTypes *implictBoundaries)
+{
+ if (GetBoundaries == nullptr)
+ GetBoundaries = new LightWeightMap<DWORDLONG, Agnostic_GetBoundaries>();
+
+ Agnostic_GetBoundaries value;
+
+ value.cILOffsets = (DWORD)*cILOffsets;
+ value.pILOffset_offset = (DWORD)GetBoundaries->AddBuffer((unsigned char*)*pILOffsets, sizeof(DWORD)*(*cILOffsets));
+ value.implicitBoundaries = *implictBoundaries;
+
+ GetBoundaries->Add((DWORDLONG)ftn, value);
+ DEBUG_REC(dmpGetBoundaries((DWORDLONG)ftn, value));
+}
+void MethodContext::dmpGetBoundaries(DWORDLONG key, const Agnostic_GetBoundaries& value)
+{
+ printf("GetBoundaries key ftn-%016llX, value cnt-%u imp-%u{", key, value.cILOffsets, value.implicitBoundaries);
+ DWORD *bnd = (DWORD *)GetBoundaries->GetBuffer(value.pILOffset_offset);
+ for (unsigned int i = 0; i < value.cILOffsets; i++)
+ {
+ printf("%u", bnd[i]);
+ if (i < (value.cILOffsets + 1))
+ printf(",");
+ }
+ GetBoundaries->Unlock();
+ printf("}");
+}
+void MethodContext::repGetBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int *cILOffsets, DWORD **pILOffsets, ICorDebugInfo::BoundaryTypes *implictBoundaries)
+{
+ Agnostic_GetBoundaries value;
+
+ value = GetBoundaries->Get((DWORDLONG)ftn);
+
+ *cILOffsets = (unsigned int)value.cILOffsets;
+ if (*cILOffsets > 0)
+ *pILOffsets = (DWORD *)GetBoundaries->GetBuffer(value.pILOffset_offset);
+ *implictBoundaries = (ICorDebugInfo::BoundaryTypes)value.implicitBoundaries;
+
+ DEBUG_REP(dmpGetBoundaries((DWORDLONG)ftn, value));
+}
+
+void MethodContext::recInitClass(CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context, BOOL speculative, CorInfoInitClassResult result)
+{
+ if (InitClass == nullptr)
+ InitClass = new LightWeightMap<Agnostic_InitClass, DWORD>();
+
+ Agnostic_InitClass key;
+ ZeroMemory(&key, sizeof(Agnostic_InitClass)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.field = (DWORDLONG)field;
+ key.method = (DWORDLONG)method;
+ key.context = (DWORDLONG)context;
+ key.speculative = (DWORD)speculative;
+
+ InitClass->Add(key, (DWORD)result);
+ DEBUG_REC(dmpInitClass(key, (DWORD)result));
+}
+void MethodContext::dmpInitClass(const Agnostic_InitClass& key, DWORD value)
+{
+ printf("InitClass key fld-%016llX meth-%016llX con-%016llX spec-%u, value res-%u", key.field, key.method, key.context, key.speculative, value);
+}
+CorInfoInitClassResult MethodContext::repInitClass(CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context, BOOL speculative)
+{
+ Agnostic_InitClass key;
+ ZeroMemory(&key, sizeof(Agnostic_InitClass)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.field = (DWORDLONG)field;
+ key.method = (DWORDLONG)method;
+ key.context = (DWORDLONG)context;
+ key.speculative = (DWORD)speculative;
+
+ AssertCodeMsg(InitClass != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.method);
+ AssertCodeMsg(InitClass->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.method);
+ CorInfoInitClassResult result = (CorInfoInitClassResult)InitClass->Get(key);
+
+ DEBUG_REP(dmpInitClass(key, result));
+ return result;
+}
+
+void MethodContext::recGetMethodName(CORINFO_METHOD_HANDLE ftn, char *methodname, const char **moduleName)
+{
+ if (GetMethodName == nullptr)
+ GetMethodName = new LightWeightMap<DLD, DD>();
+ DD value;
+ DLD key;
+ key.A = (DWORDLONG)ftn;
+ key.B = (moduleName != nullptr);
+
+ if (methodname != nullptr)
+ value.A = GetMethodName->AddBuffer((unsigned char *)methodname, (DWORD)strlen(methodname) + 1);
+ else
+ value.A = (DWORD)-1;
+
+ if (moduleName != nullptr)
+ value.B = GetMethodName->AddBuffer((unsigned char *)*moduleName, (DWORD)strlen(*moduleName) + 1);
+ else
+ value.B = (DWORD)-1;
+
+ GetMethodName->Add(key, value);
+ DEBUG_REC(dmpGetMethodName(key, value));
+}
+void MethodContext::dmpGetMethodName(DLD key, DD value)
+{
+ unsigned char *methodName = (unsigned char *)GetMethodName->GetBuffer(value.A);
+ unsigned char *moduleName = (unsigned char *)GetMethodName->GetBuffer(value.B);
+ printf("GetMethodName key - ftn-%016llX modNonNull-%u, value meth-'%s', mod-'%s'", key.A, key.B, methodName, moduleName);
+ GetMethodName->Unlock();
+}
+const char *MethodContext::repGetMethodName(CORINFO_METHOD_HANDLE ftn, const char **moduleName)
+{
+ const char* result = "hackishMethodName";
+ DD value;
+ DLD key;
+ key.A = (DWORDLONG)ftn;
+ key.B = (moduleName != nullptr);
+
+ int itemIndex = -1;
+ if (GetMethodName != nullptr)
+ itemIndex = GetMethodName->GetIndex(key);
+ if (itemIndex < 0)
+ {
+ if (moduleName != nullptr)
+ *moduleName = "hackishModuleName";
+ }
+ else
+ {
+ value = GetMethodName->Get(key);
+ if (moduleName != nullptr)
+ *moduleName = (const char *)GetMethodName->GetBuffer(value.B);
+ result = (const char *)GetMethodName->GetBuffer(value.A);
+ }
+ DEBUG_REP(dmpGetMethodName(key, value));
+ return result;
+}
+
+void MethodContext::recGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes, DWORD result)
+{
+ if (GetJitFlags == nullptr)
+ GetJitFlags = new LightWeightMap<DWORD, DD>();
+
+ DD value;
+ value.A = (DWORD)GetJitFlags->AddBuffer((unsigned char *)jitFlags, sizeInBytes);
+ value.B = result;
+
+ // NOTE: getJitFlags() is expected to be idempotent per method, so the mapping key is always
+ // zero.
+ GetJitFlags->Add((DWORD)0, value);
+ DEBUG_REC(dmpGetJitFlags((DWORD)0, value));
+}
+void MethodContext::dmpGetJitFlags(DWORD key, DD value)
+{
+ CORJIT_FLAGS *flags = (CORJIT_FLAGS*)GetJitFlags->GetBuffer(value.A);
+ printf("GetJitFlags key %u sizeInBytes-%u corJitFlags-%08X corJitFlags2-%08X", key, value.B, flags->corJitFlags, flags->corJitFlags2);
+ GetJitFlags->Unlock();
+}
+DWORD MethodContext::repGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes)
+{
+ DD value = GetJitFlags->Get((DWORD)0);
+ CORJIT_FLAGS *resultFlags = (CORJIT_FLAGS*)GetJitFlags->GetBuffer(value.A);
+ memcpy(jitFlags, resultFlags, value.B);
+ DEBUG_REP(dmpGetJitFlags((DWORD)0, value));
+ return value.B;
+}
+
+void MethodContext::recGetJitTimeLogFilename(LPCWSTR tempFileName)
+{
+ if (GetJitTimeLogFilename == nullptr)
+ GetJitTimeLogFilename = new LightWeightMap<DWORD, DWORD>();
+
+ DWORD name_index = -1;
+ if (tempFileName != nullptr)
+ {
+ name_index = (DWORD)GetJitTimeLogFilename->AddBuffer((unsigned char *)tempFileName, (DWORD)wcslen(tempFileName) + 2);
+ }
+ GetJitTimeLogFilename->Add((DWORD)0, name_index);
+ DEBUG_REC(dmpGetJitTimeLogFilename((DWORD)0, name_index));
+}
+void MethodContext::dmpGetJitTimeLogFilename(DWORD key, DWORD value)
+{
+ unsigned char *fileName = nullptr;
+ if (value != 0)
+ fileName = (unsigned char *)GetJitTimeLogFilename->GetBuffer(value);
+ printf("GetJitTimeLogFilename key %u, value '%s'", key, fileName);
+ GetJitTimeLogFilename->Unlock();
+}
+LPCWSTR MethodContext::repGetJitTimeLogFilename()
+{
+ DWORD offset = GetJitTimeLogFilename->Get((DWORD)0);
+ LPCWSTR value = nullptr;
+ if (offset != 0)
+ value = (LPCWSTR)GetJitTimeLogFilename->GetBuffer(offset);
+ DEBUG_REP(dmpGetJitTimeLogFilename((DWORD)0, offset));
+ return value;
+}
+
+void MethodContext::recCanInline(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, DWORD *pRestrictions, CorInfoInline response, DWORD exceptionCode)
+{
+ if (CanInline == nullptr)
+ CanInline = new LightWeightMap<DLDL, Agnostic_CanInline>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CanInline value;
+
+ key.A = (DWORDLONG)callerHnd;
+ key.B = (DWORDLONG)calleeHnd;
+
+ if (pRestrictions != nullptr)
+ value.Restrictions = (DWORD)*pRestrictions;
+ else
+ value.Restrictions = (DWORD)0;
+ value.result = (DWORD)response;
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ CanInline->Add(key, value);
+ DEBUG_REC(dmpCanInline(key, value));
+}
+void MethodContext::dmpCanInline(DLDL key, const Agnostic_CanInline& value)
+{
+ printf("CanInline key - callerHnd-%016llX calleeHnd-%016llX, value pRestrictions-%u result-%u exceptionCode-%08X", key.A, key.B, value.Restrictions, value.result, value.exceptionCode);
+}
+CorInfoInline MethodContext::repCanInline(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, DWORD* pRestrictions, DWORD *exceptionCode)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CanInline value;
+
+ key.A = (DWORDLONG)callerHnd;
+ key.B = (DWORDLONG)calleeHnd;
+
+ if ((CanInline == nullptr) || (CanInline->GetIndex(key) == -1))
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repCanInline saying INLINE_FAIL");
+ return INLINE_FAIL; //if we have no info, its pretty safe to say we can't inline it.
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in canInline", key.A, key.B);
+#endif
+ }
+
+ value = CanInline->Get(key);
+
+ *exceptionCode = value.exceptionCode;
+
+ if (pRestrictions != nullptr)
+ *pRestrictions = (DWORD)value.Restrictions;
+ CorInfoInline response = (CorInfoInline)value.result;
+ DEBUG_REP(dmpCanInline(key, value));
+ return response;
+}
+
+void MethodContext::recResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, DWORD exceptionCode)
+{
+ if (ResolveToken == nullptr)
+ ResolveToken = new LightWeightMap<Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout>();
+
+ Agnostic_CORINFO_RESOLVED_TOKENin key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKENin)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_RESOLVED_TOKENout value;
+
+ key.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.token = (DWORD)pResolvedToken->token;
+ key.tokenType = (DWORD)pResolvedToken->tokenType;
+
+ value.hClass = (DWORDLONG)pResolvedToken->hClass;
+ value.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ value.hField = (DWORDLONG)pResolvedToken->hField;
+ value.pTypeSpec_Index = (DWORD)ResolveToken->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ value.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ value.pMethodSpec_Index = (DWORD)ResolveToken->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ value.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ ResolveToken->Add(key, value);
+ DEBUG_REC(dmpResolveToken(key, value));
+}
+void MethodContext::dmpResolveToken(const Agnostic_CORINFO_RESOLVED_TOKENin& key, const Agnostic_CORINFO_RESOLVED_TOKENout& value)
+{
+ printf("ResolveToken key tc-%016llX ts-%016llX tok-%08X tt-%u",
+ key.tokenContext,
+ key.tokenScope,
+ key.token,
+ key.tokenType);
+ printf(", value cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u excp-%08X",
+ value.hClass,
+ value.hMethod,
+ value.hField,
+ value.pTypeSpec_Index,
+ value.cbTypeSpec,
+ value.pMethodSpec_Index,
+ value.cbMethodSpec,
+ value.exceptionCode);
+}
+void MethodContext::repResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, DWORD *exceptionCode)
+{
+ Agnostic_CORINFO_RESOLVED_TOKENin key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKENin)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_RESOLVED_TOKENout value;
+
+ key.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.token = (DWORD)pResolvedToken->token;
+ key.tokenType = (DWORD)pResolvedToken->tokenType;
+
+ AssertCodeMsg(ResolveToken->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+
+ value = ResolveToken->Get(key);
+
+ pResolvedToken->hClass = (CORINFO_CLASS_HANDLE)value.hClass;
+ pResolvedToken->hMethod = (CORINFO_METHOD_HANDLE)value.hMethod;
+ pResolvedToken->hField = (CORINFO_FIELD_HANDLE)value.hField;
+ pResolvedToken->pTypeSpec = (PCCOR_SIGNATURE)ResolveToken->GetBuffer(value.pTypeSpec_Index);
+ pResolvedToken->cbTypeSpec = (ULONG)value.cbTypeSpec;
+ pResolvedToken->pMethodSpec = (PCCOR_SIGNATURE)ResolveToken->GetBuffer(value.pMethodSpec_Index);
+ pResolvedToken->cbMethodSpec = (ULONG)value.cbMethodSpec;
+ *exceptionCode = (DWORD)value.exceptionCode;
+
+ DEBUG_REP(dmpResolveToken(key, value));
+}
+
+void MethodContext::recTryResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool success)
+{
+ if (TryResolveToken == nullptr)
+ TryResolveToken = new LightWeightMap<Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout>();
+
+ Agnostic_CORINFO_RESOLVED_TOKENin key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKENin)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_RESOLVED_TOKENout value;
+
+ key.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.token = (DWORD)pResolvedToken->token;
+ key.tokenType = (DWORD)pResolvedToken->tokenType;
+
+ value.hClass = (DWORDLONG)pResolvedToken->hClass;
+ value.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ value.hField = (DWORDLONG)pResolvedToken->hField;
+ value.pTypeSpec_Index = (DWORD)ResolveToken->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ value.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ value.pMethodSpec_Index = (DWORD)ResolveToken->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ value.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ value.exceptionCode = success ? 0 : 1;
+
+ TryResolveToken->Add(key, value);
+ DEBUG_REC(dmpTryResolveToken(key, value));
+}
+void MethodContext::dmpTryResolveToken(const Agnostic_CORINFO_RESOLVED_TOKENin& key, const Agnostic_CORINFO_RESOLVED_TOKENout& value)
+{
+ printf("TryResolveToken key tc-%016llX ts-%016llX tok-%08X tt-%u",
+ key.tokenContext,
+ key.tokenScope,
+ key.token,
+ key.tokenType);
+ printf(", value cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u failed-%u",
+ value.hClass,
+ value.hMethod,
+ value.hField,
+ value.pTypeSpec_Index,
+ value.cbTypeSpec,
+ value.pMethodSpec_Index,
+ value.cbMethodSpec,
+ value.exceptionCode);
+}
+bool MethodContext::repTryResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ Agnostic_CORINFO_RESOLVED_TOKENin key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKENin)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_RESOLVED_TOKENout value;
+
+ key.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.token = (DWORD)pResolvedToken->token;
+ key.tokenType = (DWORD)pResolvedToken->tokenType;
+
+ // Best-effort: if the `tryResolveToken` map is missing or the key is not found therein,
+ // fall back to the `resolveToken` map.
+ if (TryResolveToken != nullptr && TryResolveToken->GetIndex(key) != -1)
+ {
+ value = TryResolveToken->Get(key);
+ }
+ else
+ {
+ AssertCodeMsg(ResolveToken->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+ value = ResolveToken->Get(key);
+ }
+
+ pResolvedToken->hClass = (CORINFO_CLASS_HANDLE)value.hClass;
+ pResolvedToken->hMethod = (CORINFO_METHOD_HANDLE)value.hMethod;
+ pResolvedToken->hField = (CORINFO_FIELD_HANDLE)value.hField;
+ pResolvedToken->pTypeSpec = (PCCOR_SIGNATURE)ResolveToken->GetBuffer(value.pTypeSpec_Index);
+ pResolvedToken->cbTypeSpec = (ULONG)value.cbTypeSpec;
+ pResolvedToken->pMethodSpec = (PCCOR_SIGNATURE)ResolveToken->GetBuffer(value.pMethodSpec_Index);
+ pResolvedToken->cbMethodSpec = (ULONG)value.cbMethodSpec;
+
+ DEBUG_REP(dmpTryResolveToken(key, value));
+ return (DWORD)value.exceptionCode == 0;
+}
+
+void MethodContext::recGetCallInfo(
+ CORINFO_RESOLVED_TOKEN *pResolvedToken,
+ CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_CALLINFO_FLAGS flags,
+ CORINFO_CALL_INFO *pResult,
+ DWORD exceptionCode)
+{
+ if (GetCallInfo == nullptr)
+ GetCallInfo = new LightWeightMap<Agnostic_GetCallInfo, Agnostic_CORINFO_CALL_INFO>();
+
+ Agnostic_GetCallInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_GetCallInfo)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_CALL_INFO value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)GetCallInfo->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)GetCallInfo->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ if (pConstrainedResolvedToken != nullptr)
+ {
+ key.ConstrainedResolvedToken.tokenContext = (DWORDLONG)pConstrainedResolvedToken->tokenContext;
+ key.ConstrainedResolvedToken.tokenScope = (DWORDLONG)pConstrainedResolvedToken->tokenScope;
+ key.ConstrainedResolvedToken.token = (DWORD)pConstrainedResolvedToken->token;
+ key.ConstrainedResolvedToken.tokenType = (DWORD)pConstrainedResolvedToken->tokenType;
+ key.ConstrainedResolvedToken.hClass = (DWORDLONG)pConstrainedResolvedToken->hClass;
+ key.ConstrainedResolvedToken.hMethod = (DWORDLONG)pConstrainedResolvedToken->hMethod;
+ key.ConstrainedResolvedToken.hField = (DWORDLONG)pConstrainedResolvedToken->hField;
+ key.ConstrainedResolvedToken.typeSpec_Index = (DWORD)GetCallInfo->AddBuffer((unsigned char*)pConstrainedResolvedToken->pTypeSpec, pConstrainedResolvedToken->cbTypeSpec);
+ key.ConstrainedResolvedToken.cbTypeSpec = (DWORD)pConstrainedResolvedToken->cbTypeSpec;
+ key.ConstrainedResolvedToken.methodSpec_Index = (DWORD)GetCallInfo->AddBuffer((unsigned char*)pConstrainedResolvedToken->pMethodSpec, pConstrainedResolvedToken->cbMethodSpec);
+ key.ConstrainedResolvedToken.cbMethodSpec = (DWORD)pConstrainedResolvedToken->cbMethodSpec;
+ }
+ else
+ {
+ key.ConstrainedResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.token = (DWORD)0;
+ key.ConstrainedResolvedToken.tokenType = (DWORD)0;
+ key.ConstrainedResolvedToken.hClass = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.hMethod = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.hField = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ConstrainedResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ConstrainedResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ConstrainedResolvedToken.cbMethodSpec = (DWORD)0;
+ }
+
+ key.callerHandle = (DWORDLONG)callerHandle;
+ key.flags = (DWORD)flags;
+
+ if (exceptionCode == 0)
+ {
+ value.hMethod = (DWORDLONG)pResult->hMethod;
+ value.methodFlags = (DWORD)pResult->methodFlags;
+ value.classFlags = (DWORD)pResult->classFlags;
+ value.sig.callConv = (DWORD)pResult->sig.callConv;
+ value.sig.retTypeClass = (DWORDLONG)pResult->sig.retTypeClass;
+ value.sig.retTypeSigClass = (DWORDLONG)pResult->sig.retTypeSigClass;
+ value.sig.retType = (DWORD)pResult->sig.retType;
+ value.sig.flags = (DWORD)pResult->sig.flags;
+ value.sig.numArgs = (DWORD)pResult->sig.numArgs;
+ value.sig.sigInst_classInstCount = (DWORD)pResult->sig.sigInst.classInstCount;
+ value.sig.sigInst_classInst_Index = GetCallInfo->AddBuffer((unsigned char*)pResult->sig.sigInst.classInst, pResult->sig.sigInst.classInstCount * 8); //porting issue
+ value.sig.sigInst_methInstCount = (DWORD)pResult->sig.sigInst.methInstCount;
+ value.sig.sigInst_methInst_Index = GetCallInfo->AddBuffer((unsigned char*)pResult->sig.sigInst.methInst, pResult->sig.sigInst.methInstCount * 8); //porting issue
+ value.sig.args = (DWORDLONG)pResult->sig.args;
+ value.sig.cbSig = (DWORD)pResult->sig.cbSig;
+ value.sig.pSig = (DWORD)GetCallInfo->AddBuffer((unsigned char *)pResult->sig.pSig, pResult->sig.cbSig);
+ value.sig.scope = (DWORDLONG)pResult->sig.scope;
+ value.sig.token = (DWORD)pResult->sig.token;
+ if (flags & CORINFO_CALLINFO_VERIFICATION)
+ {
+ value.verMethodFlags = (DWORD)pResult->verMethodFlags;
+ value.verSig.callConv = (DWORD)pResult->verSig.callConv;
+ value.verSig.retTypeClass = (DWORDLONG)pResult->verSig.retTypeClass;
+ value.verSig.retTypeSigClass = (DWORDLONG)pResult->verSig.retTypeSigClass;
+ value.verSig.retType = (DWORD)pResult->verSig.retType;
+ value.verSig.flags = (DWORD)pResult->verSig.flags;
+ value.verSig.numArgs = (DWORD)pResult->verSig.numArgs;
+ value.verSig.sigInst_classInstCount = (DWORD)pResult->verSig.sigInst.classInstCount;
+ value.verSig.sigInst_classInst_Index = GetCallInfo->AddBuffer((unsigned char*)pResult->verSig.sigInst.classInst, pResult->verSig.sigInst.classInstCount * 8); //porting issue
+ value.verSig.sigInst_methInstCount = (DWORD)pResult->verSig.sigInst.methInstCount;
+ value.verSig.sigInst_methInst_Index = GetCallInfo->AddBuffer((unsigned char*)pResult->verSig.sigInst.methInst, pResult->verSig.sigInst.methInstCount * 8); //porting issue
+ value.verSig.args = (DWORDLONG)pResult->verSig.args;
+ value.verSig.cbSig = (DWORD)pResult->verSig.cbSig;
+ value.verSig.pSig = (DWORD)GetCallInfo->AddBuffer((unsigned char *)pResult->verSig.pSig, pResult->verSig.cbSig);
+ value.verSig.scope = (DWORDLONG)pResult->verSig.scope;
+ value.verSig.token = (DWORD)pResult->verSig.token;
+ }
+ else
+ {
+ value.verMethodFlags = (DWORD)0;
+ value.verSig.callConv = (DWORD)0;
+ value.verSig.retTypeClass = (DWORDLONG)0;
+ value.verSig.retTypeSigClass = (DWORDLONG)0;
+ value.verSig.retType = (DWORD)0;
+ value.verSig.flags = (DWORD)0;
+ value.verSig.numArgs = (DWORD)0;
+ value.verSig.sigInst_classInstCount = (DWORD)0;
+ value.verSig.sigInst_classInst_Index = (DWORD)0;
+ value.verSig.sigInst_methInstCount = (DWORD)0;
+ value.verSig.sigInst_methInst_Index = (DWORD)0;
+ value.verSig.args = (DWORDLONG)0;
+ value.verSig.cbSig = (DWORD)0;
+ value.verSig.pSig = (DWORD)0;
+ value.verSig.scope = (DWORDLONG)0;
+ value.verSig.token = (DWORD)0;
+ }
+
+ value.accessAllowed = (DWORD)pResult->accessAllowed;
+ value.callsiteCalloutHelper.helperNum = (DWORD)pResult->callsiteCalloutHelper.helperNum;
+ value.callsiteCalloutHelper.numArgs = (DWORD)pResult->callsiteCalloutHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ value.callsiteCalloutHelper.args[i].constant = (DWORDLONG)pResult->callsiteCalloutHelper.args[i].constant;
+ value.callsiteCalloutHelper.args[i].argType = (DWORD)pResult->callsiteCalloutHelper.args[i].argType;
+ }
+ value.thisTransform = (DWORD)pResult->thisTransform;
+
+ value.kind = (DWORD)pResult->kind;
+ value.nullInstanceCheck = (DWORD)pResult->nullInstanceCheck;
+ value.contextHandle = (DWORDLONG)pResult->contextHandle;
+ value.exactContextNeedsRuntimeLookup = (DWORD)pResult->exactContextNeedsRuntimeLookup;
+ value.stubLookup.lookupKind.needsRuntimeLookup = (DWORD)pResult->stubLookup.lookupKind.needsRuntimeLookup;
+ value.stubLookup.lookupKind.runtimeLookupKind = (DWORD)pResult->stubLookup.lookupKind.runtimeLookupKind;
+ if (pResult->stubLookup.lookupKind.needsRuntimeLookup)
+ {
+ value.stubLookup.constLookup.accessType = (DWORD)0;
+ value.stubLookup.constLookup.handle = (DWORDLONG)0;
+
+ value.stubLookup.runtimeLookup.signature = (DWORDLONG)pResult->stubLookup.runtimeLookup.signature; // needs to be a more flexible copy based on value
+ value.stubLookup.runtimeLookup.helper = (DWORD)pResult->stubLookup.runtimeLookup.helper;
+ value.stubLookup.runtimeLookup.indirections = (DWORD)pResult->stubLookup.runtimeLookup.indirections;
+ value.stubLookup.runtimeLookup.testForNull = (DWORD)pResult->stubLookup.runtimeLookup.testForNull;
+ value.stubLookup.runtimeLookup.testForFixup = (DWORD)pResult->stubLookup.runtimeLookup.testForFixup;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.stubLookup.runtimeLookup.offsets[i] = (DWORDLONG)pResult->stubLookup.runtimeLookup.offsets[i];
+ }
+ else
+ {
+ value.stubLookup.runtimeLookup.signature = (DWORDLONG)0;
+ value.stubLookup.runtimeLookup.helper = (DWORD)0;
+ value.stubLookup.runtimeLookup.indirections = (DWORD)0;
+ value.stubLookup.runtimeLookup.testForNull = (DWORD)0;
+ value.stubLookup.runtimeLookup.testForFixup = (DWORD)0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.stubLookup.runtimeLookup.offsets[i] = (DWORDLONG)0;
+
+ value.stubLookup.constLookup.accessType = (DWORD)pResult->stubLookup.constLookup.accessType;
+ value.stubLookup.constLookup.handle = (DWORDLONG)pResult->stubLookup.constLookup.handle;
+ }
+
+ value.instParamLookup.accessType = (DWORD)pResult->instParamLookup.accessType;
+ value.instParamLookup.handle = (DWORDLONG)pResult->instParamLookup.handle;
+ value.secureDelegateInvoke = (DWORD)pResult->secureDelegateInvoke;
+ }
+ else
+ ZeroMemory(&value, sizeof(Agnostic_CORINFO_CALL_INFO));
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ GetCallInfo->Add(key, value);
+ DEBUG_REC(dmpGetCallInfo(key, value));
+}
+void MethodContext::dmpGetCallInfo(const Agnostic_GetCallInfo& key, const Agnostic_CORINFO_CALL_INFO& value)
+{
+ printf("GetCallInfo key"
+ " rt{tc-%016llX ts-%016llX tok-%08X tt-%u cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u}"
+ " crt{tc-%016llX ts-%016llX tok-%08X tt-%u cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u}"
+ " ch-%016llX flg-%08X",
+ key.ResolvedToken.tokenContext,
+ key.ResolvedToken.tokenScope,
+ key.ResolvedToken.token,
+ key.ResolvedToken.tokenType,
+ key.ResolvedToken.hClass,
+ key.ResolvedToken.hMethod,
+ key.ResolvedToken.hField,
+ key.ResolvedToken.typeSpec_Index,
+ key.ResolvedToken.cbTypeSpec,
+ key.ResolvedToken.methodSpec_Index,
+ key.ResolvedToken.cbMethodSpec,
+ key.ConstrainedResolvedToken.tokenContext,
+ key.ConstrainedResolvedToken.tokenScope,
+ key.ConstrainedResolvedToken.token,
+ key.ConstrainedResolvedToken.tokenType,
+ key.ConstrainedResolvedToken.hClass,
+ key.ConstrainedResolvedToken.hMethod,
+ key.ConstrainedResolvedToken.hField,
+ key.ConstrainedResolvedToken.typeSpec_Index,
+ key.ConstrainedResolvedToken.cbTypeSpec,
+ key.ConstrainedResolvedToken.methodSpec_Index,
+ key.ConstrainedResolvedToken.cbMethodSpec,
+ key.callerHandle,
+ key.flags);
+ printf(", value mth-%016llX, mf-%08X cf-%08X"
+ " sig{flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u args-%016llX scp-%016llX tok-%08X}"
+ " vsig{flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u args-%016llX scp-%016llX tok-%08X}"
+ " ipl{at-%08X hnd-%016llX}"
+ " sdi-%08X"
+ " excp-%08X",
+ value.hMethod,
+ value.methodFlags,
+ value.classFlags,
+ value.sig.flags,
+ value.sig.numArgs,
+ value.sig.sigInst_classInstCount,
+ value.sig.sigInst_classInst_Index,
+ value.sig.sigInst_methInstCount,
+ value.sig.sigInst_methInst_Index,
+ value.sig.args,
+ value.sig.scope,
+ value.sig.token,
+ value.verSig.flags,
+ value.verSig.numArgs,
+ value.verSig.sigInst_classInstCount,
+ value.verSig.sigInst_classInst_Index,
+ value.verSig.sigInst_methInstCount,
+ value.verSig.sigInst_methInst_Index,
+ value.verSig.args,
+ value.verSig.scope,
+ value.verSig.token,
+ value.instParamLookup.accessType,
+ value.instParamLookup.handle,
+ value.secureDelegateInvoke,
+ value.exceptionCode);
+}
+void MethodContext::repGetCallInfo(
+ CORINFO_RESOLVED_TOKEN *pResolvedToken,
+ CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_CALLINFO_FLAGS flags,
+ CORINFO_CALL_INFO *pResult,
+ DWORD *exceptionCode)
+{
+ Agnostic_GetCallInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_GetCallInfo)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_CALL_INFO value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)GetCallInfo->Contains((unsigned char *)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)GetCallInfo->Contains((unsigned char *)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ if (pConstrainedResolvedToken != nullptr)
+ {
+ key.ConstrainedResolvedToken.tokenContext = (DWORDLONG)pConstrainedResolvedToken->tokenContext;
+ key.ConstrainedResolvedToken.tokenScope = (DWORDLONG)pConstrainedResolvedToken->tokenScope;
+ key.ConstrainedResolvedToken.token = (DWORD)pConstrainedResolvedToken->token;
+ key.ConstrainedResolvedToken.tokenType = (DWORD)pConstrainedResolvedToken->tokenType;
+ key.ConstrainedResolvedToken.hClass = (DWORDLONG)pConstrainedResolvedToken->hClass;
+ key.ConstrainedResolvedToken.hMethod = (DWORDLONG)pConstrainedResolvedToken->hMethod;
+ key.ConstrainedResolvedToken.hField = (DWORDLONG)pConstrainedResolvedToken->hField;
+ key.ConstrainedResolvedToken.typeSpec_Index = (DWORD)GetCallInfo->Contains((unsigned char *)pConstrainedResolvedToken->pTypeSpec, pConstrainedResolvedToken->cbTypeSpec);
+ key.ConstrainedResolvedToken.cbTypeSpec = (DWORD)pConstrainedResolvedToken->cbTypeSpec;
+ key.ConstrainedResolvedToken.methodSpec_Index = (DWORD)GetCallInfo->Contains((unsigned char *)pConstrainedResolvedToken->pMethodSpec, pConstrainedResolvedToken->cbMethodSpec);
+ key.ConstrainedResolvedToken.cbMethodSpec = (DWORD)pConstrainedResolvedToken->cbMethodSpec;
+ }
+ else
+ {
+ key.ConstrainedResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.token = (DWORD)0;
+ key.ConstrainedResolvedToken.tokenType = (DWORD)0;
+ key.ConstrainedResolvedToken.hClass = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.hMethod = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.hField = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ConstrainedResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ConstrainedResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ConstrainedResolvedToken.cbMethodSpec = (DWORD)0;
+ }
+ key.callerHandle = (DWORDLONG)callerHandle;
+ key.flags = (DWORD)flags;
+
+ AssertCodeMsg(GetCallInfo->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %08x, %016llx. Probably a missing exception in GetCallInfo", key.ResolvedToken.token, key.ResolvedToken.hClass);
+ value = GetCallInfo->Get(key);
+
+ pResult->hMethod = (CORINFO_METHOD_HANDLE)value.hMethod;
+ pResult->methodFlags = (unsigned)value.methodFlags;
+ pResult->classFlags = (unsigned)value.classFlags;
+ pResult->sig.callConv = (CorInfoCallConv)value.sig.callConv;
+ pResult->sig.retTypeClass = (CORINFO_CLASS_HANDLE)value.sig.retTypeClass;
+ pResult->sig.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.sig.retTypeSigClass;
+ pResult->sig.retType = (CorInfoType)value.sig.retType;
+ pResult->sig.flags = (unsigned)value.sig.flags;
+ pResult->sig.numArgs = (unsigned)value.sig.numArgs;
+ pResult->sig.sigInst.classInstCount = (unsigned)value.sig.sigInst_classInstCount;
+ pResult->sig.sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetCallInfo->GetBuffer(value.sig.sigInst_classInst_Index);
+ pResult->sig.sigInst.methInstCount = (unsigned)value.sig.sigInst_methInstCount;
+ pResult->sig.sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetCallInfo->GetBuffer(value.sig.sigInst_methInst_Index);
+ pResult->sig.args = (CORINFO_ARG_LIST_HANDLE)value.sig.args;
+ pResult->sig.cbSig = (unsigned int)value.sig.cbSig;
+ pResult->sig.pSig = (PCCOR_SIGNATURE)GetCallInfo->GetBuffer(value.sig.pSig);
+ pResult->sig.scope = (CORINFO_MODULE_HANDLE)value.sig.scope;
+ pResult->sig.token = (mdToken)value.sig.token;
+ if (flags & CORINFO_CALLINFO_VERIFICATION)
+ {
+ pResult->verMethodFlags = (unsigned)value.verMethodFlags;
+ pResult->verSig.callConv = (CorInfoCallConv)value.verSig.callConv;
+ pResult->verSig.retTypeClass = (CORINFO_CLASS_HANDLE)value.verSig.retTypeClass;
+ pResult->verSig.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.verSig.retTypeSigClass;
+ pResult->verSig.retType = (CorInfoType)value.verSig.retType;
+ pResult->verSig.flags = (unsigned)value.verSig.flags;
+ pResult->verSig.numArgs = (unsigned)value.verSig.numArgs;
+ pResult->verSig.sigInst.classInstCount = (unsigned)value.verSig.sigInst_classInstCount;
+ pResult->verSig.sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetCallInfo->GetBuffer(value.verSig.sigInst_classInst_Index);
+ pResult->verSig.sigInst.methInstCount = (unsigned)value.verSig.sigInst_methInstCount;
+ pResult->verSig.sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetCallInfo->GetBuffer(value.verSig.sigInst_methInst_Index);
+ pResult->verSig.args = (CORINFO_ARG_LIST_HANDLE)value.verSig.args;
+ pResult->verSig.cbSig = (unsigned int)value.verSig.cbSig;
+ pResult->verSig.pSig = (PCCOR_SIGNATURE)GetCallInfo->GetBuffer(value.verSig.pSig);
+ pResult->verSig.scope = (CORINFO_MODULE_HANDLE)value.verSig.scope;
+ pResult->verSig.token = (mdToken)value.verSig.token;
+ }
+ pResult->accessAllowed = (CorInfoIsAccessAllowedResult)value.accessAllowed;
+ pResult->callsiteCalloutHelper.helperNum = (CorInfoHelpFunc)value.callsiteCalloutHelper.helperNum;
+ pResult->callsiteCalloutHelper.numArgs = (unsigned)value.callsiteCalloutHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ pResult->callsiteCalloutHelper.args[i].constant = (size_t)value.callsiteCalloutHelper.args[i].constant;
+ pResult->callsiteCalloutHelper.args[i].argType = (CorInfoAccessAllowedHelperArgType)value.callsiteCalloutHelper.args[i].argType;
+ }
+ pResult->thisTransform = (CORINFO_THIS_TRANSFORM)value.thisTransform;
+ pResult->kind = (CORINFO_CALL_KIND)value.kind;
+ pResult->nullInstanceCheck = (BOOL)value.nullInstanceCheck;
+ pResult->contextHandle = (CORINFO_CONTEXT_HANDLE)value.contextHandle;
+ pResult->exactContextNeedsRuntimeLookup = (BOOL)value.exactContextNeedsRuntimeLookup;
+ pResult->stubLookup.lookupKind.needsRuntimeLookup = value.stubLookup.lookupKind.needsRuntimeLookup != 0;
+ pResult->stubLookup.lookupKind.runtimeLookupKind = (CORINFO_RUNTIME_LOOKUP_KIND)value.stubLookup.lookupKind.runtimeLookupKind;
+ if (pResult->stubLookup.lookupKind.needsRuntimeLookup)
+ {
+ pResult->stubLookup.runtimeLookup.signature = (LPVOID)value.stubLookup.runtimeLookup.signature; // needs to be a more flexible copy based on valuevalue.stubLookup.runtimeLookup.signature;
+ pResult->stubLookup.runtimeLookup.helper = (CorInfoHelpFunc)value.stubLookup.runtimeLookup.helper;
+ pResult->stubLookup.runtimeLookup.indirections = (WORD)value.stubLookup.runtimeLookup.indirections;
+ pResult->stubLookup.runtimeLookup.testForNull = value.stubLookup.runtimeLookup.testForNull != 0;
+ pResult->stubLookup.runtimeLookup.testForFixup = value.stubLookup.runtimeLookup.testForFixup != 0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ pResult->stubLookup.runtimeLookup.offsets[i] = (SIZE_T)value.stubLookup.runtimeLookup.offsets[i];
+ }
+ else
+ {
+ pResult->stubLookup.constLookup.accessType = (InfoAccessType)value.stubLookup.constLookup.accessType;
+ pResult->stubLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE)value.stubLookup.constLookup.handle;
+ }
+ if (pResult->kind == CORINFO_VIRTUALCALL_STUB)
+ {
+ cr->CallTargetTypes->Add((DWORDLONG)pResult->codePointerLookup.constLookup.addr, (DWORD)CORINFO_VIRTUALCALL_STUB);
+ }
+ pResult->instParamLookup.accessType = (InfoAccessType)value.instParamLookup.accessType;
+ pResult->instParamLookup.handle = (CORINFO_GENERIC_HANDLE)value.instParamLookup.handle;
+ pResult->secureDelegateInvoke = (BOOL)value.secureDelegateInvoke;
+ *exceptionCode = (DWORD)value.exceptionCode;
+
+ DEBUG_REP(dmpGetCallInfo(key, value));
+}
+
+//
+// Variant of repGetCallInfo that only requires a method handle, i.e. it performs a reverse lookup to find the
+// resolved token info that, along with the given method handle, was passed into getCallInfo.
+//
+// Arguments:
+// methodHandle - The method handle to find call info for.
+// pResult - [out] The call info for the given method.
+//
+// Notes:
+// If this fails to find a recorded call to getCallInfo with the given method handle, this will throw an
+// exception.
+//
+void MethodContext::repGetCallInfoFromMethodHandle(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CALL_INFO *pResult)
+{
+ if (GetCallInfo != nullptr)
+ {
+ for (unsigned int i = 0; i < GetCallInfo->GetCount(); i++)
+ {
+ Agnostic_GetCallInfo key = GetCallInfo->GetKey(i);
+ Agnostic_CORINFO_CALL_INFO val = GetCallInfo->GetItem(i);
+
+ if ((CORINFO_METHOD_HANDLE)val.hMethod == methodHandle)
+ {
+ CORINFO_RESOLVED_TOKEN resolvedToken;
+ DWORD exceptionCode;
+
+ resolvedToken.tokenContext = (CORINFO_CONTEXT_HANDLE)key.ResolvedToken.tokenContext;
+ resolvedToken.tokenScope = (CORINFO_MODULE_HANDLE)key.ResolvedToken.tokenScope;
+ resolvedToken.token = (mdToken)key.ResolvedToken.token;
+ resolvedToken.tokenType = (CorInfoTokenKind)key.ResolvedToken.tokenType;
+
+ repResolveToken(&resolvedToken, &exceptionCode);
+
+ // If the original call to getCallInfo passed in a null constrainedResolvedToken pointer,
+ // then we won't be able to replay it. In that case, we'll need to pass a null pointer into
+ // repGetCallInfo for constrainedResolvedToken, instead of just passing the address of our
+ // local (but meaningless) constrainedResolvedToken struct.
+ CORINFO_RESOLVED_TOKEN constrainedResolvedToken;
+ CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken = nullptr;
+
+ if (key.ConstrainedResolvedToken.tokenContext != 0 &&
+ key.ConstrainedResolvedToken.tokenScope != 0)
+ {
+ constrainedResolvedToken.tokenContext = (CORINFO_CONTEXT_HANDLE)key.ConstrainedResolvedToken.tokenContext;
+ constrainedResolvedToken.tokenScope = (CORINFO_MODULE_HANDLE)key.ConstrainedResolvedToken.tokenScope;
+ constrainedResolvedToken.token = (mdToken)key.ConstrainedResolvedToken.token;
+ constrainedResolvedToken.tokenType = (CorInfoTokenKind)key.ConstrainedResolvedToken.tokenType;
+ pConstrainedResolvedToken = &constrainedResolvedToken;
+
+ repResolveToken(pConstrainedResolvedToken, &exceptionCode);
+ }
+
+ repGetCallInfo(&resolvedToken,
+ pConstrainedResolvedToken,
+ (CORINFO_METHOD_HANDLE)key.callerHandle,
+ (CORINFO_CALLINFO_FLAGS)key.flags,
+ pResult,
+ &exceptionCode);
+ return;
+ }
+ }
+ }
+
+ // If we reached here, we didn't find a key associated with the given method handle
+ LogException(EXCEPTIONCODE_MC, "Didn't find key %016llX.", methodHandle);
+}
+
+
+void MethodContext::recGetIntrinsicID(CORINFO_METHOD_HANDLE method, bool* pMustExpand, CorInfoIntrinsics result)
+{
+ if (GetIntrinsicID == nullptr)
+ GetIntrinsicID = new LightWeightMap<DWORDLONG, DD>();
+
+ DD value;
+ value.A = (pMustExpand != nullptr) ? (DWORD) (*pMustExpand ? 1 : 0) : (DWORD)0;
+ value.B = (DWORD)result;
+
+ GetIntrinsicID->Add((DWORDLONG)method, value);
+ DEBUG_REC(dmpGetIntrinsicID((DWORDLONG)method, value));
+}
+void MethodContext::dmpGetIntrinsicID(DWORDLONG key, DD value)
+{
+ printf("GetIntrinsicID key mth-%016llX, mustExpand-%u, value intr-%u", key, value.A, value.B);
+}
+CorInfoIntrinsics MethodContext::repGetIntrinsicID(CORINFO_METHOD_HANDLE method, bool* pMustExpand)
+{
+ AssertCodeMsg(GetIntrinsicID != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)method);
+ AssertCodeMsg(GetIntrinsicID->GetIndex((DWORDLONG)method) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)method);
+
+ DD value;
+ value = GetIntrinsicID->Get((DWORDLONG)method);
+ if (pMustExpand != nullptr)
+ {
+ *pMustExpand = (value.A == 0) ? false : true;
+ }
+ CorInfoIntrinsics result = (CorInfoIntrinsics)value.B;
+
+ DEBUG_REP(dmpGetIntrinsicID((DWORDLONG)method, value));
+ return result;
+}
+
+void MethodContext::recIsInSIMDModule(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (IsInSIMDModule == nullptr)
+ IsInSIMDModule = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsInSIMDModule->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpIsInSIMDModule((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpIsInSIMDModule(DWORDLONG key, DWORD value)
+{
+ printf("IsInSIMDModule key mth-%016llX, value intr-%u", key, value);
+}
+BOOL MethodContext::repIsInSIMDModule(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(IsInSIMDModule != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(IsInSIMDModule->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ BOOL result = (BOOL)IsInSIMDModule->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpIsInSIMDModule((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CorInfoUnmanagedCallConv result)
+{
+ if (GetUnmanagedCallConv == nullptr)
+ GetUnmanagedCallConv = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetUnmanagedCallConv->Add((DWORDLONG)method, result);
+ DEBUG_REC(dmpGetUnmanagedCallConv((DWORDLONG)method, (DWORD)result));
+}
+void MethodContext::dmpGetUnmanagedCallConv(DWORDLONG key, DWORD result)
+{
+ printf("GetUnmanagedCallConv key ftn-%016llX, value res-%u", key, result);
+}
+CorInfoUnmanagedCallConv MethodContext::repGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method)
+{
+ if ((GetUnmanagedCallConv == nullptr) || (GetUnmanagedCallConv->GetIndex((DWORDLONG)method) == -1))
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetUnmanagedCallConv returning CORINFO_UNMANAGED_CALLCONV_STDCALL");
+ return CORINFO_UNMANAGED_CALLCONV_STDCALL;
+#else
+ LogException(EXCEPTIONCODE_MC, "Found a null GetUnmanagedCallConv. Probably missing a fatTrigger for %016llX.", (DWORDLONG)method);
+#endif
+ }
+ CorInfoUnmanagedCallConv result = (CorInfoUnmanagedCallConv)GetUnmanagedCallConv->Get((DWORDLONG)method);
+ DEBUG_REP(dmpGetUnmanagedCallConv((DWORDLONG)method, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recIsInstantiationOfVerifiedGeneric(CORINFO_METHOD_HANDLE method, CorInfoInstantiationVerification result)
+{
+ if (IsInstantiationOfVerifiedGeneric == nullptr)
+ IsInstantiationOfVerifiedGeneric = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsInstantiationOfVerifiedGeneric->Add((DWORDLONG)method, result);
+ DEBUG_REC(dmpIsInstantiationOfVerifiedGeneric((DWORDLONG)method, (DWORD)result));
+}
+void MethodContext::dmpIsInstantiationOfVerifiedGeneric(DWORDLONG key, DWORD value)
+{
+ printf("IsInstantiationOfVerifiedGeneric key ftn-%016llX, value res-%u", key, value);
+}
+CorInfoInstantiationVerification MethodContext::repIsInstantiationOfVerifiedGeneric(CORINFO_METHOD_HANDLE method)
+{
+ CorInfoInstantiationVerification result = (CorInfoInstantiationVerification)IsInstantiationOfVerifiedGeneric->Get((DWORDLONG)method);
+ DEBUG_REP(dmpIsInstantiationOfVerifiedGeneric((DWORDLONG)method, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recAsCorInfoType(CORINFO_CLASS_HANDLE cls, CorInfoType result)
+{
+ if (AsCorInfoType == nullptr)
+ AsCorInfoType = new LightWeightMap<DWORDLONG, DWORD>();
+
+ AsCorInfoType->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpAsCorInfoType((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpAsCorInfoType(DWORDLONG key, DWORD value)
+{
+ printf("AsCorInfoType key cls-%016llX, value cit-%u(%s)", key, value, toString((CorInfoType)value));
+}
+CorInfoType MethodContext::repAsCorInfoType(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg((AsCorInfoType != nullptr) && (AsCorInfoType->GetIndex((DWORDLONG)cls) != -1),
+ EXCEPTIONCODE_MC, "Didn't find %016llX. Probable cached value in JIT issue", (DWORDLONG)cls);
+ CorInfoType result = (CorInfoType)AsCorInfoType->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpAsCorInfoType((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recIsValueClass(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (IsValueClass == nullptr)
+ IsValueClass = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsValueClass->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpIsValueClass((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpIsValueClass(DWORDLONG key, DWORD value)
+{
+ printf("IsValueClass key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repIsValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg((IsValueClass != nullptr) && (IsValueClass->GetIndex((DWORDLONG)cls) != -1),
+ EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+
+ BOOL result = (BOOL)IsValueClass->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpIsValueClass((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recIsStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (IsStructRequiringStackAllocRetBuf == nullptr)
+ IsStructRequiringStackAllocRetBuf = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsStructRequiringStackAllocRetBuf->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpIsStructRequiringStackAllocRetBuf((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpIsStructRequiringStackAllocRetBuf(DWORDLONG key, DWORD value)
+{
+ printf("IsStructRequiringStackAllocRetBuf key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repIsStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(IsStructRequiringStackAllocRetBuf != nullptr, EXCEPTIONCODE_MC, "Found a null IsStructRequiringStackAllocRetBuf. Probably missing a fatTrigger for %016llX.", (DWORDLONG)cls);
+ AssertCodeMsg(IsStructRequiringStackAllocRetBuf->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ BOOL result = (BOOL)IsStructRequiringStackAllocRetBuf->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpIsStructRequiringStackAllocRetBuf((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetClassSize(CORINFO_CLASS_HANDLE cls, unsigned result)
+{
+ if (GetClassSize == nullptr)
+ GetClassSize = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetClassSize->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpGetClassSize((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpGetClassSize(DWORDLONG key, DWORD val)
+{
+ printf("GetClassSize key %016llX, value %u", key, val);
+}
+unsigned MethodContext::repGetClassSize(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(GetClassSize != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetClassSize->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ unsigned result = (unsigned)GetClassSize->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetClassSize((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls, unsigned result)
+{
+ if (GetClassNumInstanceFields == nullptr)
+ GetClassNumInstanceFields = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetClassNumInstanceFields->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpGetClassNumInstanceFields((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpGetClassNumInstanceFields(DWORDLONG key, DWORD value)
+{
+ printf("GetClassNumInstanceFields key cls-%016llX, value res-%u", key, value);
+}
+unsigned MethodContext::repGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(GetClassNumInstanceFields != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetClassNumInstanceFields->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+
+ unsigned result = (unsigned)GetClassNumInstanceFields->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetClassNumInstanceFields((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls, CorInfoHelpFunc result)
+{
+ if (GetNewArrHelper == nullptr)
+ GetNewArrHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetNewArrHelper->Add((DWORDLONG)arrayCls, result);
+ DEBUG_REC(dmpGetNewArrHelper((DWORDLONG)arrayCls, (DWORD)result));
+}
+void MethodContext::dmpGetNewArrHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetNewArrHelper key cls-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls)
+{
+ CorInfoHelpFunc result = (CorInfoHelpFunc)GetNewArrHelper->Get((DWORDLONG)arrayCls);
+ DEBUG_REP(dmpGetNewArrHelper((DWORDLONG)arrayCls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd, CorInfoHelpFunc result)
+{
+ if (GetSharedCCtorHelper == nullptr)
+ GetSharedCCtorHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetSharedCCtorHelper->Add((DWORDLONG)clsHnd, result);
+ DEBUG_REC(dmpGetSharedCCtorHelper((DWORDLONG)clsHnd, (DWORD)result));
+}
+void MethodContext::dmpGetSharedCCtorHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetSharedCCtorHelper key cls-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd)
+{
+ CorInfoHelpFunc result = (CorInfoHelpFunc)GetSharedCCtorHelper->Get((DWORDLONG)clsHnd);
+ DEBUG_REP(dmpGetSharedCCtorHelper((DWORDLONG)clsHnd, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetSecurityPrologHelper(CORINFO_METHOD_HANDLE ftn, CorInfoHelpFunc result)
+{
+ if (GetSecurityPrologHelper == nullptr)
+ GetSecurityPrologHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetSecurityPrologHelper->Add((DWORDLONG)ftn, result);
+ DEBUG_REC(dmpGetSecurityPrologHelper((DWORDLONG)ftn, (DWORD)result));
+}
+void MethodContext::dmpGetSecurityPrologHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetSecurityPrologHelper key ftn-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetSecurityPrologHelper(CORINFO_METHOD_HANDLE ftn)
+{
+ CorInfoHelpFunc result = (CorInfoHelpFunc)GetSecurityPrologHelper->Get((DWORDLONG)ftn);
+ DEBUG_REP(dmpGetSecurityPrologHelper((DWORDLONG)ftn, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetTypeForBox(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
+{
+ if (GetTypeForBox == nullptr)
+ GetTypeForBox = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetTypeForBox->Add((DWORDLONG)cls, (DWORDLONG)result);
+ DEBUG_REC(dmpGetTypeForBox((DWORDLONG)cls, (DWORDLONG)result));
+}
+void MethodContext::dmpGetTypeForBox(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetTypeForBox key cls-%016llX, value res-%016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetTypeForBox(CORINFO_CLASS_HANDLE cls)
+{
+ CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)GetTypeForBox->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetTypeForBox((DWORDLONG)cls, (DWORDLONG)result));
+ return result;
+}
+
+void MethodContext::recGetBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result)
+{
+ if (GetBoxHelper == nullptr)
+ GetBoxHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetBoxHelper->Add((DWORDLONG)cls, result);
+ DEBUG_REC(dmpGetBoxHelper((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpGetBoxHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetBoxHelper key cls-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetBoxHelper(CORINFO_CLASS_HANDLE cls)
+{
+ CorInfoHelpFunc result = (CorInfoHelpFunc)GetBoxHelper->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetBoxHelper((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetBuiltinClass(CorInfoClassId classId, CORINFO_CLASS_HANDLE result)
+{
+ if (GetBuiltinClass == nullptr)
+ GetBuiltinClass = new LightWeightMap<DWORD, DWORDLONG>();
+
+ GetBuiltinClass->Add((DWORD)classId, (DWORDLONG)result);
+ DEBUG_REC(dmpGetBuiltinClass((DWORDLONG)classId, (DWORDLONG)result));
+}
+void MethodContext::dmpGetBuiltinClass(DWORD key, DWORDLONG value)
+{
+ printf("GetBuiltinClass key cls-%08X, value cls-%016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetBuiltinClass(CorInfoClassId classId)
+{
+ CORINFO_CLASS_HANDLE value = (CORINFO_CLASS_HANDLE)GetBuiltinClass->Get((DWORD)classId);
+ DEBUG_REP(dmpGetBuiltinClass((DWORDLONG)classId, (DWORDLONG)value));
+ return value;
+}
+
+void MethodContext::recGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls, CorInfoType result)
+{
+ if (GetTypeForPrimitiveValueClass == nullptr)
+ GetTypeForPrimitiveValueClass = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetTypeForPrimitiveValueClass->Add((DWORDLONG)cls, result);
+ DEBUG_REC(dmpGetTypeForPrimitiveValueClass((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpGetTypeForPrimitiveValueClass(DWORDLONG key, DWORD value)
+{
+ printf("GetTypeForPrimitiveValueClass key cls-%016llX, value cit-%u(%s)", key, value, toString((CorInfoType)value));
+}
+CorInfoType MethodContext::repGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(GetTypeForPrimitiveValueClass != nullptr, EXCEPTIONCODE_MC, "Encountered an empty LWM while looking for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetTypeForPrimitiveValueClass->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ CorInfoType result = (CorInfoType)GetTypeForPrimitiveValueClass->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetTypeForPrimitiveValueClass((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetParentType(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
+{
+ if (GetParentType == nullptr)
+ GetParentType = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetParentType->Add((DWORDLONG)cls, (DWORDLONG)result);
+
+}
+void MethodContext::dmpGetParentType(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetParentType key cls-%016llX, value cls-%016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetParentType(CORINFO_CLASS_HANDLE cls)
+{
+ CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)GetParentType->Get((DWORDLONG)cls);
+ return result;
+}
+
+void MethodContext::recIsSDArray(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (IsSDArray == nullptr)
+ IsSDArray = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsSDArray->Add((DWORDLONG)cls, result);
+ DEBUG_REC(dmpIsSDArray((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpIsSDArray(DWORDLONG key, DWORD value)
+{
+ printf("IsSDArray key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repIsSDArray(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(IsSDArray != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(IsSDArray->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ BOOL temp = (BOOL)IsSDArray->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpIsSDArray((DWORDLONG)cls, (DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recGetFieldClass(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE result)
+{
+ if (GetFieldClass == nullptr)
+ GetFieldClass = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetFieldClass->Add((DWORDLONG)field, (DWORDLONG)result);
+ DEBUG_REC(dmpGetFieldClass((DWORDLONG)field, (DWORDLONG)result));
+}
+void MethodContext::dmpGetFieldClass(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetFieldClass key %016llX, value %016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetFieldClass(CORINFO_FIELD_HANDLE field)
+{
+ AssertCodeMsg(GetFieldClass != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)field);
+ AssertCodeMsg(GetFieldClass->GetIndex((DWORDLONG)field) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)field);
+ CORINFO_CLASS_HANDLE temp = (CORINFO_CLASS_HANDLE)GetFieldClass->Get((DWORDLONG)field);
+ DEBUG_REP(dmpGetFieldClass((DWORDLONG)field, (DWORDLONG)temp));
+ return temp;
+}
+
+void MethodContext::recGetFieldOffset(CORINFO_FIELD_HANDLE field, unsigned result)
+{
+ if (GetFieldOffset == nullptr)
+ GetFieldOffset = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetFieldOffset->Add((DWORDLONG)field, result);
+ DEBUG_REC(dmpGetFieldOffset((DWORDLONG)field, (DWORD)result));
+}
+void MethodContext::dmpGetFieldOffset(DWORDLONG key, DWORD value)
+{
+ printf("GetFieldOffset key FLD-%016llX, value %08X", key, value);
+}
+unsigned MethodContext::repGetFieldOffset(CORINFO_FIELD_HANDLE field)
+{
+ AssertCodeMsg((GetFieldOffset != nullptr) && (GetFieldOffset->GetIndex((DWORDLONG)field) != -1),
+ EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)field);
+
+ unsigned temp = (unsigned)GetFieldOffset->Get((DWORDLONG)field);
+ DEBUG_REP(dmpGetFieldOffset((DWORDLONG)field, (DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle, CorInfoHelpFunc result)
+{
+ if (GetLazyStringLiteralHelper == nullptr)
+ GetLazyStringLiteralHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetLazyStringLiteralHelper->Add((DWORDLONG)handle, result);
+ DEBUG_REC(dmpGetLazyStringLiteralHelper((DWORDLONG)handle, result));
+}
+
+void MethodContext::dmpGetLazyStringLiteralHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetLazyStringLiteralHelper key mod-%016llX, value res-%u", key, value);
+}
+
+CorInfoHelpFunc MethodContext::repGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle)
+{
+ AssertCodeMsg(GetLazyStringLiteralHelper != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)handle);
+ AssertCodeMsg(GetLazyStringLiteralHelper->GetIndex((DWORDLONG)handle) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)handle);
+ CorInfoHelpFunc temp = (CorInfoHelpFunc)GetLazyStringLiteralHelper->Get((DWORDLONG)handle);
+ DEBUG_REP(dmpGetLazyStringLiteralHelper((DWORDLONG)handle, temp));
+ return temp;
+}
+
+void MethodContext::recGetUnBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result)
+{
+ if (GetUnBoxHelper == nullptr)
+ GetUnBoxHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetUnBoxHelper->Add((DWORDLONG)cls, result);
+}
+void MethodContext::dmpGetUnBoxHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetUnBoxHelper key cls-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetUnBoxHelper(CORINFO_CLASS_HANDLE cls)
+{
+ CorInfoHelpFunc temp = (CorInfoHelpFunc)GetUnBoxHelper->Get((DWORDLONG)cls);
+ return temp;
+}
+
+void MethodContext::recGetReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup,
+ bool result
+ )
+{
+ LogError("getReadyToRunHelper NYI");
+
+ // TODO: we need a more sophisticated mapping
+
+ if (GetReadyToRunHelper == nullptr)
+ GetReadyToRunHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ //GetReadyToRunHelper->Add((DWORDLONG)cls, result);
+}
+void MethodContext::dmpGetReadyToRunHelper(DWORDLONG key, DWORD value)
+{
+ LogError("getReadyToRunHelper NYI");
+}
+bool MethodContext::repGetReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ LogError("getReadyToRunHelper NYI");
+ return false;
+}
+
+void MethodContext::recGetReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ LogError("GetReadyToRunDelegateCtorHelper NYI");
+
+ // TODO: we need a more sophisticated mapping
+
+ if (GetReadyToRunDelegateCtorHelper == nullptr)
+ GetReadyToRunDelegateCtorHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ //GetReadyToRunDelegateCtorHelper->Add((DWORDLONG)delegateType, result);
+}
+void MethodContext::dmpGetReadyToRunDelegateCtorHelper(DWORDLONG key, DWORD value)
+{
+ LogError("getReadyToRunDelegateCtorHelper NYI");
+}
+void MethodContext::repGetReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ LogError("getReadyToRunDelegateCtorHelper NYI");
+}
+
+void MethodContext::recGetHelperFtn(CorInfoHelpFunc ftnNum, void **ppIndirection, void *result)
+{
+ if (GetHelperFtn == nullptr)
+ GetHelperFtn = new LightWeightMap<DWORD, DLDL>();
+
+ DLDL value;
+ value.A = (DWORDLONG)*ppIndirection;
+ value.B = (DWORDLONG)result;
+
+ GetHelperFtn->Add((DWORD)ftnNum, value);
+ DEBUG_REC(dmpGetHelperFtn((DWORD)ftnNum, value));
+}
+void MethodContext::dmpGetHelperFtn(DWORD key, DLDL value)
+{
+ printf("GetHelperFtn key ftn-%u, value ppi-%016llX res-%016llX", key, value.A, value.B);
+}
+void* MethodContext::repGetHelperFtn(CorInfoHelpFunc ftnNum, void **ppIndirection)
+{
+ if ((GetHelperFtn == nullptr) || (GetHelperFtn->GetIndex((DWORD)ftnNum) == -1))
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetHelperFtn returning 0xCAFE0002 and 0XCAFE0003");
+ *ppIndirection = (void*)(size_t)0xCAFE0002;
+ return (void*)(size_t)0xCAFE0003;
+#else
+ LogException(EXCEPTIONCODE_MC, "Encountered an empty LWM while looking for %08X", (DWORD)ftnNum);
+#endif
+ }
+
+ DLDL value = (DLDL)GetHelperFtn->Get((DWORD)ftnNum);
+ *ppIndirection = (void *)value.A;
+ DEBUG_REP(dmpGetHelperFtn((DWORD)ftnNum, value));
+ return (void *)value.B;
+}
+
+//
+// Finds the identifier (i.e. the CorInfoHelpFunc enum) of a helper function, based on the address where it
+// is located in memory.
+//
+// Arguments:
+// functionAddress - The starting address of the helper function in memory.
+// pResult - [out] Pointer to write out the identifier of the helper function located at the given
+// address.
+//
+// Return Value:
+// True if there is a helper function associated with the given target address; false otherwise.
+//
+// Assumptions:
+// Only the lower 32 bits of the method address are necessary to identify the method.
+//
+// Notes:
+// - See notes for fndGetFunctionEntryPoint for a more in-depth discussion of why we only match on the
+// lower 32 bits of the target address.
+// - This might not work correctly with method contexts recorded via NGen compilation.
+//
+bool MethodContext::fndGetHelperFtn(void *functionAddress, CorInfoHelpFunc *pResult)
+{
+ if (GetHelperFtn != nullptr)
+ {
+ for (unsigned int i = 0; i < GetHelperFtn->GetCount(); i++)
+ {
+ DWORD key = GetHelperFtn->GetKey(i);
+ DLDL val = GetHelperFtn->GetItem(i);
+
+ // TODO-Cleanup: this only compares the function addresses, and doesn't account for
+ // ppIndirection, which will break if the helper is a dynamic helper function.
+ if (val.B == (DWORDLONG)functionAddress)
+ {
+ *pResult = (CorInfoHelpFunc)key;
+ return true;
+ }
+ }
+ }
+
+ LogDebug("fndGetHelperFtn - didn't find value %p", functionAddress);
+ return false;
+}
+
+void MethodContext::recGetJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE **ppIndirection, CORINFO_JUST_MY_CODE_HANDLE result)
+{
+ if (GetJustMyCodeHandle == nullptr)
+ GetJustMyCodeHandle = new LightWeightMap<DWORDLONG, DLDL>();
+ DLDL temp;
+ temp.A = (DWORDLONG)*ppIndirection;
+ temp.B = (DWORDLONG)result;
+ GetJustMyCodeHandle->Add((DWORDLONG)method, temp);
+ DEBUG_REC(dmpGetJustMyCodeHandle((DWORDLONG)method, temp));
+}
+void MethodContext::dmpGetJustMyCodeHandle(DWORDLONG key, DLDL value)
+{
+ printf("GetJustMyCodeHandle key ftn-%016llX, value pp-%016llX, res-%016llX", key, value.A, value.B);
+}
+CORINFO_JUST_MY_CODE_HANDLE MethodContext::repGetJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE **ppIndirection)
+{
+ DLDL temp = (DLDL)GetJustMyCodeHandle->Get((DWORDLONG)method);
+ *ppIndirection = (CORINFO_JUST_MY_CODE_HANDLE *)temp.A;
+ CORINFO_JUST_MY_CODE_HANDLE result = (CORINFO_JUST_MY_CODE_HANDLE)temp.B;
+ DEBUG_REP(dmpGetJustMyCodeHandle((DWORDLONG)method, temp));
+ return result;
+}
+
+void MethodContext::recGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult, CORINFO_ACCESS_FLAGS accessFlags)
+{
+ if (GetFunctionEntryPoint == nullptr)
+ GetFunctionEntryPoint = new LightWeightMap<DLD, DLD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORD)accessFlags;
+ value.A = (DWORDLONG)pResult->addr; //First union member
+ value.B = (DWORD)pResult->accessType;
+ GetFunctionEntryPoint->Add(key, value);
+ DEBUG_REC(dmpGetFunctionEntryPoint(key, value));
+}
+void MethodContext::dmpGetFunctionEntryPoint(DLD key, DLD value)
+{
+ printf("GetFunctionEntryPoint key ftn-%016llX af-%08X, value add-%016llX at-%u", key.A, key.B, value.A, value.B);
+}
+void MethodContext::repGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult, CORINFO_ACCESS_FLAGS accessFlags)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORD)accessFlags;
+
+ if (GetFunctionEntryPoint == nullptr)
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetFunctionEntryPoint fabricated result for request.");
+ pResult->accessType = (InfoAccessType)IAT_PVALUE;
+ pResult->addr = (void *)((DWORDLONG)ftn + 0x1c);
+ return;
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llX, %8x", (DWORDLONG)ftn, accessFlags);
+#endif
+ }
+ if (GetFunctionEntryPoint->GetIndex(key) == -1)
+ {
+#ifdef sparseMC
+ key.B ^= (DWORD)CORINFO_ACCESS_NONNULL;
+ if (GetFunctionEntryPoint->GetIndex(key) != -1)
+ {
+ LogDebug("Sparse - repGetFunctionEntryPoint found result with inverted CORINFO_ACCESS_NONNULL");
+ }
+ else
+ {
+ LogDebug("Sparse - repGetFunctionEntryPoint fabricated result for request.");
+ pResult->accessType = (InfoAccessType)IAT_PVALUE;
+ pResult->addr = (void *)((DWORDLONG)ftn + 0x1c);
+ return;
+ }
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llX, %8x", (DWORDLONG)ftn, accessFlags);
+#endif
+ }
+ value = GetFunctionEntryPoint->Get(key);
+
+ pResult->accessType = (InfoAccessType)value.B;
+ pResult->addr = (void *)value.A;
+ DEBUG_REP(dmpGetFunctionEntryPoint(key, value));
+}
+
+//
+// Finds the method handle associated with a method, based on the address where its generated code is located
+// in memory.
+//
+// Arguments:
+// methodAddress - The starting address of the generated code for the method.
+// pResult - [out] Pointer to a method handle to write into. If this successfully finds a method
+// handle associated with the given target address, it will be written to here.
+//
+// Return Value:
+// True if there is a helper function associated with the given target address; false otherwise.
+//
+// Assumptions:
+// - The given method address does not point to a jump stub.
+// - The given method is not a generic method.
+// - Only the lower 32 bits of the method address are necessary to identify the method.
+//
+// Notes:
+// On 64-bit platforms, this only checks if the lower 32 bits of the method address match a recorded
+// function entry point because, on AMD64, this only supports reverse lookups for near calls, which
+// encode their target address as a 32-bit PC-relative displacement.
+//
+// Practically speaking, there are two concrete reasons why we ignore the upper 64 bits. First, on
+// AMD64, when the JIT emits a near call, it records the displacement it encodes into the call with
+// the EE as a 32-bit relative "relocation". Consequently, this leads to the second reason why we
+// ignore the upper 64 bits: when SuperPMI is replaying method compilation, it patches up addresses
+// based on the "relocations" the JIT had previously recorded with the EE. Since these relocations
+// are only 32-bit deltas, what you'll usually end up seeing is that, after SuperPMI has applied
+// these fixups, the lower 32 bits of method addresses will match, but the upper 32 bits will differ.
+//
+bool MethodContext::fndGetFunctionEntryPoint(DLD value, CORINFO_METHOD_HANDLE *pResult)
+{
+ if (GetFunctionEntryPoint != nullptr)
+ {
+ for (unsigned int i = 0; i < GetFunctionEntryPoint->GetCount(); i++)
+ {
+ DLD key = GetFunctionEntryPoint->GetKey(i);
+ DLD val = GetFunctionEntryPoint->GetItem(i);
+
+ // TODO-Cleanup: we should be more conscious of the rest of the information in CORINFO_CONST_LOOKUP
+ if ((DWORD)val.A == (DWORD)value.A)
+ {
+ *pResult = (CORINFO_METHOD_HANDLE)key.A;
+ return true;
+ }
+ }
+ }
+
+ LogDebug("fndGetFunctionEntryPoint - didn't find value %016llX", value.A);
+ return false;
+}
+
+void MethodContext::recConstructStringLiteral(CORINFO_MODULE_HANDLE module, mdToken metaTok, void *pValue, InfoAccessType result)
+{
+ if (ConstructStringLiteral == nullptr)
+ ConstructStringLiteral = new LightWeightMap<DLD, DLD>();
+ DLD temp;
+ ZeroMemory(&temp, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD temp2;
+ temp.A = (DWORDLONG)module;
+ temp.B = (DWORD)metaTok;
+ temp2.A = (DWORDLONG)pValue;
+ temp2.B = (DWORD)result;
+
+ ConstructStringLiteral->Add(temp, temp2);
+ DEBUG_REC(dmpConstructStringLiteral(temp, temp2));
+}
+void MethodContext::dmpConstructStringLiteral(DLD key, DLD value)
+{
+ printf("ConstructStringLiteral key mod-%016llX tok-%08X, value pp-%016llX iat-%u", key.A, key.B, value.A, value.B);
+}
+InfoAccessType MethodContext::repConstructStringLiteral(CORINFO_MODULE_HANDLE module, mdToken metaTok, void **ppValue)
+{
+ DLD temp;
+ ZeroMemory(&temp, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD temp2;
+ temp.A = (DWORDLONG)module;
+ temp.B = (DWORD)metaTok;
+ AssertCodeMsg(ConstructStringLiteral != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)module);
+ AssertCodeMsg(ConstructStringLiteral->GetIndex(temp) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)module);
+ temp2 = ConstructStringLiteral->Get(temp);
+ *ppValue = (void *)temp2.A;
+ DEBUG_REP(dmpConstructStringLiteral(temp, temp2));
+ return (InfoAccessType)temp2.B;
+}
+
+void MethodContext::recEmptyStringLiteral(void **pValue, InfoAccessType result)
+{
+ if (EmptyStringLiteral == nullptr)
+ EmptyStringLiteral = new DenseLightWeightMap<DLD>();
+ DLD temp2;
+ temp2.A = (DWORDLONG)*pValue;
+ temp2.B = (DWORD)result;
+
+ EmptyStringLiteral->Append(temp2);
+}
+void MethodContext::dmpEmptyStringLiteral(DWORD key, DLD value)
+{
+ printf("EmptyStringLiteral key %u, value pVal-%016llX res-%u", key, value.A, value.B);
+}
+InfoAccessType MethodContext::repEmptyStringLiteral(void **ppValue)
+{
+ // TODO-Cleanup: sketchy if someone calls this twice
+ DLD temp2;
+ temp2 = EmptyStringLiteral->Get((DWORD)0);
+ *ppValue = (void *)temp2.A;
+ return (InfoAccessType)temp2.B;
+}
+
+void MethodContext::recGetArgType(CORINFO_SIG_INFO *sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE *vcTypeRet, CorInfoTypeWithMod result, DWORD exceptionCode)
+{
+ if (GetArgType == nullptr)
+ GetArgType = new LightWeightMap<Agnostic_GetArgType, Agnostic_GetArgType_Value>();
+
+ Agnostic_GetArgType key;
+ ZeroMemory(&key, sizeof(Agnostic_GetArgType)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetArgType_Value value;
+
+ //Only setting values for things the EE seems to pay attention to... this is necessary since some of the values
+ //are unset and fail our precise comparisions...
+ key.sig.callConv = (DWORD)0;
+ key.sig.retTypeClass = (DWORDLONG)0;
+ key.sig.retTypeSigClass = (DWORDLONG)0;
+ key.sig.retType = (DWORD)0;
+ key.sig.flags = (DWORD)sig->flags;
+ key.sig.numArgs = (DWORD)sig->numArgs;
+ key.sig.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ key.sig.sigInst_classInst_Index = (DWORD)GetArgType->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8);
+ key.sig.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ key.sig.sigInst_methInst_Index = (DWORD)GetArgType->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8);
+ key.sig.args = (DWORDLONG)0;
+ key.sig.cbSig = (DWORD)0;
+ key.sig.pSig = -1;
+ key.sig.scope = (DWORDLONG)sig->scope;
+ key.sig.token = (DWORD)0;
+ key.args = (DWORDLONG)args;
+
+ value.vcTypeRet = (DWORDLONG)*vcTypeRet;
+ value.result = (DWORD)result;
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ GetArgType->Add(key, value);
+ DEBUG_REC(dmpGetArgType(key, value));
+}
+void MethodContext::dmpGetArgType(const Agnostic_GetArgType& key, const Agnostic_GetArgType_Value& value)
+{
+ printf("GetArgType key flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u scp-%016llX arg-%016llX",
+ key.sig.flags,
+ key.sig.numArgs,
+ key.sig.sigInst_classInstCount,
+ key.sig.sigInst_classInst_Index,
+ key.sig.sigInst_methInstCount,
+ key.sig.sigInst_methInst_Index,
+ key.sig.scope,
+ key.args);
+ printf(", value rt-%016llX ci-%u excp-%08X", value.vcTypeRet, value.result, value.exceptionCode);
+}
+CorInfoTypeWithMod MethodContext::repGetArgType(CORINFO_SIG_INFO *sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE *vcTypeRet, DWORD *exceptionCode)
+{
+ Agnostic_GetArgType key;
+ ZeroMemory(&key, sizeof(Agnostic_GetArgType)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetArgType_Value value;
+
+ AssertCodeMsg(GetArgType != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in getArgType", key.sig.scope, key.args);
+ key.sig.callConv = (DWORD)0;
+ key.sig.retTypeClass = (DWORDLONG)0;
+ key.sig.retTypeSigClass = (DWORDLONG)0;
+ key.sig.retType = (DWORD)0;
+ key.sig.flags = (DWORD)sig->flags;
+ key.sig.numArgs = (DWORD)sig->numArgs;
+ key.sig.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ key.sig.sigInst_classInst_Index = (DWORD)GetArgType->Contains((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8);
+ key.sig.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ key.sig.sigInst_methInst_Index = (DWORD)GetArgType->Contains((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8);
+ key.sig.args = (DWORDLONG)0;
+ key.sig.cbSig = (DWORD)0;
+ key.sig.pSig = -1;
+ key.sig.scope = (DWORDLONG)sig->scope;
+ key.sig.token = (DWORD)0;
+ key.args = (DWORDLONG)args;
+
+ AssertCodeMsg(GetArgType->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in getArgType", key.sig.scope, key.args);
+
+ value = GetArgType->Get(key);
+ *vcTypeRet = (CORINFO_CLASS_HANDLE)value.vcTypeRet;
+ CorInfoTypeWithMod temp = (CorInfoTypeWithMod)value.result;
+ *exceptionCode = (DWORD)value.exceptionCode;
+
+ DEBUG_REP(dmpGetArgType(key, value));
+ return temp;
+}
+
+void MethodContext::recGetArgNext(CORINFO_ARG_LIST_HANDLE args, CORINFO_ARG_LIST_HANDLE result)
+{
+ if (GetArgNext == nullptr)
+ GetArgNext = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetArgNext->Add((DWORDLONG)args, (DWORDLONG)result);
+ DEBUG_REC(dmpGetArgNext((DWORDLONG)args, (DWORDLONG)result));
+}
+void MethodContext::dmpGetArgNext(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetArgNext key %016llX, value %016llX", key, value);
+}
+CORINFO_ARG_LIST_HANDLE MethodContext::repGetArgNext(CORINFO_ARG_LIST_HANDLE args)
+{
+ CORINFO_ARG_LIST_HANDLE temp = (CORINFO_ARG_LIST_HANDLE)GetArgNext->Get((DWORDLONG)args);
+ DEBUG_REP(dmpGetArgNext((DWORDLONG)args, (DWORDLONG)temp));
+ return temp;
+}
+void MethodContext::recGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO *sig, CORINFO_CLASS_HANDLE memberParent)
+{
+ if (GetMethodSig == nullptr)
+ GetMethodSig = new LightWeightMap<DLDL, Agnostic_CORINFO_SIG_INFO>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORDLONG)memberParent;
+
+ value.callConv = (DWORD)sig->callConv;
+ value.retTypeClass = (DWORDLONG)sig->retTypeClass;
+ value.retTypeSigClass = (DWORDLONG)sig->retTypeSigClass;
+ value.retType = (DWORD)sig->retType;
+ value.flags = (DWORD)sig->flags;
+ value.numArgs = (DWORD)sig->numArgs;
+ value.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ value.sigInst_classInst_Index = GetMethodSig->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8); //porting issue
+ value.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ value.sigInst_methInst_Index = GetMethodSig->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8); //porting issue
+ value.args = (DWORDLONG)sig->args;
+ value.cbSig = (DWORD)sig->cbSig;
+ value.pSig = (DWORD)GetMethodSig->AddBuffer((unsigned char *)sig->pSig, sig->cbSig);
+ value.scope = (DWORDLONG)sig->scope;
+ value.token = (DWORD)sig->token;
+
+ GetMethodSig->Add(key, value);
+ DEBUG_REC(dmpGetMethodSig(key, value));
+}
+void MethodContext::dmpGetMethodSig(DLDL key, const Agnostic_CORINFO_SIG_INFO& value)
+{
+ printf("GetMethodSig key ftn-%016llX prt-%016llX, value cc-%u rtc-%016llX rts-%016llX rt-%u(%s) flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u args-%016llX sig-%u pSig-%u scp-%016llX tok-%08X",
+ key.A, key.B,
+ value.callConv,
+ value.retTypeClass,
+ value.retTypeSigClass,
+ value.retType,
+ toString((CorInfoType)value.retType),
+ value.flags,
+ value.numArgs,
+ value.sigInst_classInstCount,
+ value.sigInst_classInst_Index,
+ value.sigInst_methInstCount,
+ value.sigInst_methInst_Index,
+ value.args,
+ value.cbSig,
+ value.pSig,
+ value.scope,
+ value.token);
+}
+void MethodContext::repGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO *sig, CORINFO_CLASS_HANDLE memberParent)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORDLONG)memberParent;
+
+ value = GetMethodSig->Get(key);
+
+ sig->callConv = (CorInfoCallConv)value.callConv;
+ sig->retTypeClass = (CORINFO_CLASS_HANDLE)value.retTypeClass;
+ sig->retTypeSigClass = (CORINFO_CLASS_HANDLE)value.retTypeSigClass;
+ sig->retType = (CorInfoType)value.retType;
+ sig->flags = (unsigned)value.flags;
+ sig->numArgs = (unsigned)value.numArgs;
+ sig->sigInst.classInstCount = (unsigned)value.sigInst_classInstCount;
+ sig->sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetMethodSig->GetBuffer(value.sigInst_classInst_Index);
+ sig->sigInst.methInstCount = (unsigned)value.sigInst_methInstCount;
+ sig->sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetMethodSig->GetBuffer(value.sigInst_methInst_Index);
+ sig->args = (CORINFO_ARG_LIST_HANDLE)value.args;
+ sig->cbSig = (unsigned int)value.cbSig;
+ sig->pSig = (PCCOR_SIGNATURE)GetMethodSig->GetBuffer(value.pSig);
+ sig->scope = (CORINFO_MODULE_HANDLE)value.scope;
+ sig->token = (mdToken)value.token;
+
+ DEBUG_REP(dmpGetMethodSig(key, value));
+}
+
+void MethodContext::recGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE result, DWORD exceptionCode)
+{
+ if (GetArgClass == nullptr)
+ GetArgClass = new LightWeightMap<Agnostic_GetArgClass, Agnostic_GetArgClass_Value>();
+
+ Agnostic_GetArgClass key;
+ ZeroMemory(&key, sizeof(Agnostic_GetArgClass)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetArgClass_Value value;
+
+ //Only setting values for things the EE seems to pay attention to... this is necessary since some of the values
+ //are unset and fail our precise comparisions...
+ key.sig.callConv = (DWORD)0;
+ key.sig.retTypeClass = (DWORDLONG)0;
+ key.sig.retTypeSigClass = (DWORDLONG)0;
+ key.sig.retType = (DWORD)0;
+ key.sig.flags = (DWORD)0;
+ key.sig.numArgs = (DWORD)0;
+ key.sig.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ key.sig.sigInst_classInst_Index = (DWORD)GetArgClass->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8);
+ key.sig.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ key.sig.sigInst_methInst_Index = (DWORD)GetArgClass->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8);
+ key.sig.args = (DWORDLONG)0;
+ key.sig.cbSig = (DWORD)0;
+ key.sig.pSig = -1;
+ key.sig.scope = (DWORDLONG)sig->scope;
+ key.sig.token = (DWORD)0;
+ key.args = (DWORDLONG)args;
+
+ value.result = (DWORDLONG)result;
+ value.exceptionCode = exceptionCode;
+
+ GetArgClass->Add(key, value);
+ DEBUG_REC(dmpGetArgClass(key, value));
+}
+void MethodContext::dmpGetArgClass(const Agnostic_GetArgClass& key, const Agnostic_GetArgClass_Value& value)
+{
+ printf("GetArgClass key cc-%u ci-%u mc-%u mi-%u scp-%016llX args-%016llX",
+ key.sig.sigInst_classInstCount,
+ key.sig.sigInst_classInst_Index,
+ key.sig.sigInst_methInstCount,
+ key.sig.sigInst_methInst_Index,
+ key.sig.scope,
+ key.args);
+ printf(", value %016llX excp-%08X", value.result, value.exceptionCode);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, DWORD *exceptionCode)
+{
+ Agnostic_GetArgClass key;
+ ZeroMemory(&key, sizeof(Agnostic_GetArgClass)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ AssertCodeMsg(GetArgClass != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in getArgClass", key.sig.scope, key.args);
+ key.sig.callConv = (DWORD)0;
+ key.sig.retTypeClass = (DWORDLONG)0;
+ key.sig.retTypeSigClass = (DWORDLONG)0;
+ key.sig.retType = (DWORD)0;
+ key.sig.flags = (DWORD)0;
+ key.sig.numArgs = (DWORD)0;
+ key.sig.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ key.sig.sigInst_classInst_Index = (DWORD)GetArgClass->Contains((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8);
+ key.sig.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ key.sig.sigInst_methInst_Index = (DWORD)GetArgClass->Contains((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8);
+ key.sig.args = (DWORDLONG)0;
+ key.sig.cbSig = (DWORD)0;
+ key.sig.pSig = -1;
+ key.sig.scope = (DWORDLONG)sig->scope;
+ key.sig.token = (DWORD)0;
+ key.args = (DWORDLONG)args;
+
+ AssertCodeMsg(GetArgClass->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in getArgClass", key.sig.scope, key.args);
+
+ Agnostic_GetArgClass_Value value = GetArgClass->Get(key);
+ *exceptionCode = value.exceptionCode;
+ DEBUG_REP(dmpGetArgClass(key, value));
+
+ return (CORINFO_CLASS_HANDLE)value.result;
+}
+
+void MethodContext::recGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, bool result, DWORD exceptionCode)
+{
+ if (GetMethodInfo == nullptr)
+ GetMethodInfo = new LightWeightMap<DWORDLONG, Agnostic_GetMethodInfo>();
+
+ Agnostic_GetMethodInfo value;
+ ZeroMemory(&value, sizeof(Agnostic_GetMethodInfo));
+
+ if (result)
+ {
+ value.info.ftn = (DWORDLONG)info->ftn;
+ value.info.scope = (DWORDLONG)info->scope;
+ value.info.ILCode_offset = (DWORD)GetMethodInfo->AddBuffer(info->ILCode, info->ILCodeSize);
+ value.info.ILCodeSize = (DWORD)info->ILCodeSize;
+ value.info.maxStack = (DWORD)info->maxStack;
+ value.info.EHcount = (DWORD)info->EHcount;
+ value.info.options = (DWORD)info->options;
+ value.info.regionKind = (DWORD)info->regionKind;
+ value.info.args.callConv = (DWORD)info->args.callConv;
+ value.info.args.retTypeClass = (DWORDLONG)info->args.retTypeClass;
+ value.info.args.retTypeSigClass = (DWORDLONG)info->args.retTypeSigClass;
+ value.info.args.retType = (DWORD)info->args.retType;
+ value.info.args.flags = (DWORD)info->args.flags;
+ value.info.args.numArgs = (DWORD)info->args.numArgs;
+ value.info.args.sigInst_classInstCount = (DWORD)info->args.sigInst.classInstCount;
+ value.info.args.sigInst_classInst_Index = (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->args.sigInst.classInst, info->args.sigInst.classInstCount * 8); //porting issue
+ value.info.args.sigInst_methInstCount = (DWORD)info->args.sigInst.methInstCount;
+ value.info.args.sigInst_methInst_Index = (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->args.sigInst.methInst, info->args.sigInst.methInstCount * 8); //porting issue
+ value.info.args.args = (DWORDLONG)info->args.args;
+ value.info.args.cbSig = (DWORD)info->args.cbSig;
+ value.info.args.pSig = (DWORD)GetMethodInfo->AddBuffer((unsigned char *)info->args.pSig, info->args.cbSig);
+ value.info.args.scope = (DWORDLONG)info->args.scope;
+ value.info.args.token = (DWORD)info->args.token;
+ value.info.locals.callConv = (DWORD)info->locals.callConv;
+ value.info.locals.retTypeClass = (DWORDLONG)info->locals.retTypeClass;
+ value.info.locals.retTypeSigClass = (DWORDLONG)info->locals.retTypeSigClass;
+ value.info.locals.retType = (DWORD)info->locals.retType;
+ value.info.locals.flags = (DWORD)info->locals.flags;
+ value.info.locals.numArgs = (DWORD)info->locals.numArgs;
+ value.info.locals.sigInst_classInstCount = (DWORD)info->locals.sigInst.classInstCount;
+ value.info.locals.sigInst_classInst_Index = (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->locals.sigInst.classInst, info->locals.sigInst.classInstCount * 8); //porting issue
+ value.info.locals.sigInst_methInstCount = (DWORD)info->locals.sigInst.methInstCount;
+ value.info.locals.sigInst_methInst_Index = (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->locals.sigInst.methInst, info->locals.sigInst.methInstCount * 8); //porting issue
+ value.info.locals.args = (DWORDLONG)info->locals.args;
+ value.info.locals.cbSig = (DWORD)info->locals.cbSig;
+ value.info.locals.pSig = (DWORD)GetMethodInfo->AddBuffer((unsigned char *)info->locals.pSig, info->locals.cbSig);
+ value.info.locals.scope = (DWORDLONG)info->locals.scope;
+ value.info.locals.token = (DWORD)info->locals.token;
+ }
+ value.result = result;
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ GetMethodInfo->Add((DWORDLONG)ftn, value);
+ DEBUG_REC(dmpGetMethodInfo((DWORDLONG)ftn, value));
+}
+void MethodContext::dmpGetMethodInfo(DWORDLONG key, const Agnostic_GetMethodInfo& value)
+{
+ printf("GetMethodInfo key ftn-%016llX", key);
+ printf(", value res-%u ftn-%016llX scp-%016llX ilo-%u ils-%u ms-%u ehc-%u opt-%08X rk-%u "
+ "args{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} "
+ "locals{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} "
+ "excp-%08X",
+ value.result,
+ value.info.ftn,
+ value.info.scope,
+ value.info.ILCode_offset,
+ value.info.ILCodeSize,
+ value.info.maxStack,
+ value.info.EHcount,
+ value.info.options,
+ value.info.regionKind,
+ value.info.args.callConv,
+ value.info.args.retTypeClass,
+ value.info.args.retTypeSigClass,
+ value.info.args.retType,
+ toString((CorInfoType)value.info.args.retType),
+ value.info.args.flags,
+ value.info.args.numArgs,
+ value.info.args.sigInst_classInstCount,
+ value.info.args.sigInst_classInst_Index,
+ value.info.args.sigInst_methInstCount,
+ value.info.args.sigInst_methInst_Index,
+ value.info.args.args,
+ value.info.args.cbSig,
+ value.info.args.pSig,
+ value.info.args.scope,
+ value.info.args.token,
+ value.info.locals.callConv,
+ value.info.locals.retTypeClass,
+ value.info.locals.retTypeSigClass,
+ value.info.locals.retType,
+ toString((CorInfoType)value.info.locals.retType),
+ value.info.locals.flags,
+ value.info.locals.numArgs,
+ value.info.locals.sigInst_classInstCount,
+ value.info.locals.sigInst_classInst_Index,
+ value.info.locals.sigInst_methInstCount,
+ value.info.locals.sigInst_methInst_Index,
+ value.info.locals.args,
+ value.info.locals.cbSig,
+ value.info.locals.pSig,
+ value.info.locals.scope,
+ value.info.locals.token,
+ value.exceptionCode);
+}
+bool MethodContext::repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, DWORD *exceptionCode)
+{
+ Agnostic_GetMethodInfo value;
+ AssertCodeMsg(GetMethodInfo != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llx. probably a missing exception in getMethodInfo", (DWORDLONG)ftn);
+ AssertCodeMsg(GetMethodInfo->GetIndex((DWORDLONG)ftn) != -1, EXCEPTIONCODE_MC, "Didn't find %016llx. probably a missing exception in getMethodInfo", (DWORDLONG)ftn);
+
+ value = GetMethodInfo->Get((DWORDLONG)ftn);
+ if (value.result)
+ {
+ info->ftn = (CORINFO_METHOD_HANDLE)value.info.ftn;
+ info->scope = (CORINFO_MODULE_HANDLE)value.info.scope;
+ info->ILCode = GetMethodInfo->GetBuffer(value.info.ILCode_offset);
+ info->ILCodeSize = (unsigned)value.info.ILCodeSize;
+ info->maxStack = (unsigned)value.info.maxStack;
+ info->EHcount = (unsigned)value.info.EHcount;
+ info->options = (CorInfoOptions)value.info.options;
+ info->regionKind = (CorInfoRegionKind)value.info.regionKind;
+ info->args.callConv = (CorInfoCallConv)value.info.args.callConv;
+ info->args.retTypeClass = (CORINFO_CLASS_HANDLE)value.info.args.retTypeClass;
+ info->args.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.info.args.retTypeSigClass;
+ info->args.retType = (CorInfoType)value.info.args.retType;
+ info->args.flags = (unsigned)value.info.args.flags;
+ info->args.numArgs = (unsigned)value.info.args.numArgs;
+ info->args.sigInst.classInstCount = (unsigned)value.info.args.sigInst_classInstCount;
+ info->args.sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetMethodInfo->GetBuffer(value.info.args.sigInst_classInst_Index);
+ info->args.sigInst.methInstCount = (unsigned)value.info.args.sigInst_methInstCount;
+ info->args.sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetMethodInfo->GetBuffer(value.info.args.sigInst_methInst_Index);
+ info->args.args = (CORINFO_ARG_LIST_HANDLE)value.info.args.args;
+ info->args.cbSig = (unsigned int)value.info.args.cbSig;
+ info->args.pSig = (PCCOR_SIGNATURE)GetMethodInfo->GetBuffer(value.info.args.pSig);
+ info->args.scope = (CORINFO_MODULE_HANDLE)value.info.args.scope;
+ info->args.token = (mdToken)value.info.args.token;
+ info->locals.callConv = (CorInfoCallConv)value.info.locals.callConv;
+ info->locals.retTypeClass = (CORINFO_CLASS_HANDLE)value.info.locals.retTypeClass;
+ info->locals.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.info.locals.retTypeSigClass;
+ info->locals.retType = (CorInfoType)value.info.locals.retType;
+ info->locals.flags = (unsigned)value.info.locals.flags;
+ info->locals.numArgs = (unsigned)value.info.locals.numArgs;
+ info->locals.sigInst.classInstCount = (unsigned)value.info.locals.sigInst_classInstCount;
+ info->locals.sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetMethodInfo->GetBuffer(value.info.locals.sigInst_classInst_Index);
+ info->locals.sigInst.methInstCount = (unsigned)value.info.locals.sigInst_methInstCount;
+ info->locals.sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetMethodInfo->GetBuffer(value.info.locals.sigInst_methInst_Index);
+ info->locals.args = (CORINFO_ARG_LIST_HANDLE)value.info.locals.args;
+ info->locals.cbSig = (unsigned int)value.info.locals.cbSig;
+ info->locals.pSig = (PCCOR_SIGNATURE)GetMethodInfo->GetBuffer(value.info.locals.pSig);
+ info->locals.scope = (CORINFO_MODULE_HANDLE)value.info.locals.scope;
+ info->locals.token = (mdToken)value.info.locals.token;
+ }
+ bool result = (bool)value.result;
+ *exceptionCode = (DWORD)value.exceptionCode;
+ DEBUG_REP(dmpGetMethodInfo((DWORDLONG)ftn, value));
+ return result;
+}
+
+void MethodContext::recGetNewHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CorInfoHelpFunc result)
+{
+ if (GetNewHelper == nullptr)
+ GetNewHelper = new LightWeightMap<Agnostic_GetNewHelper, DWORD>();
+
+ Agnostic_GetNewHelper key;
+ ZeroMemory(&key, sizeof(Agnostic_GetNewHelper)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ResolvedToken.token = (DWORD)0;
+ key.ResolvedToken.tokenType = (DWORD)0;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)0;
+ key.ResolvedToken.hField = (DWORDLONG)0;
+ key.ResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbMethodSpec = (DWORD)0;
+ key.callerHandle = (DWORDLONG)callerHandle;
+
+ GetNewHelper->Add(key, (DWORD)result);
+ DEBUG_REC(dmpGetNewHelper(key, (DWORD)result));
+}
+void MethodContext::dmpGetNewHelper(const Agnostic_GetNewHelper& key, DWORD value)
+{
+ printf("GetNewHelper key cls-%016llX chan-%016llX, value res-%u", key.ResolvedToken.hClass, key.callerHandle, value);
+}
+CorInfoHelpFunc MethodContext::repGetNewHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle)
+{
+ Agnostic_GetNewHelper key;
+ ZeroMemory(&key, sizeof(Agnostic_GetNewHelper)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ResolvedToken.token = (DWORD)0;
+ key.ResolvedToken.tokenType = (DWORD)0;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)0;
+ key.ResolvedToken.hField = (DWORDLONG)0;
+ key.ResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbMethodSpec = (DWORD)0;
+ key.callerHandle = (DWORDLONG)callerHandle;
+
+ AssertCodeMsg(GetNewHelper != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.ResolvedToken.hClass);
+ AssertCodeMsg(GetNewHelper->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.ResolvedToken.hClass);
+ CorInfoHelpFunc value = (CorInfoHelpFunc)GetNewHelper->Get(key);
+ DEBUG_REP(dmpGetNewHelper(key, value));
+ return value;
+}
+
+void MethodContext::recEmbedGenericHandle(CORINFO_RESOLVED_TOKEN *pResolvedToken, BOOL fEmbedParent, CORINFO_GENERICHANDLE_RESULT *pResult)
+{
+ if (EmbedGenericHandle == nullptr)
+ EmbedGenericHandle = new LightWeightMap<Agnostic_EmbedGenericHandle, Agnostic_CORINFO_GENERICHANDLE_RESULT>();
+
+ Agnostic_EmbedGenericHandle key;
+ ZeroMemory(&key, sizeof(Agnostic_EmbedGenericHandle)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_GENERICHANDLE_RESULT value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)EmbedGenericHandle->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)EmbedGenericHandle->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.fEmbedParent = (DWORD)fEmbedParent;
+
+ value.lookup.lookupKind.needsRuntimeLookup = (DWORD)pResult->lookup.lookupKind.needsRuntimeLookup;
+ value.lookup.lookupKind.runtimeLookupKind = (DWORD)pResult->lookup.lookupKind.runtimeLookupKind;
+ if (pResult->lookup.lookupKind.needsRuntimeLookup)
+ {
+ value.lookup.constLookup.accessType = (DWORD)0;
+ value.lookup.constLookup.handle = (DWORDLONG)0;
+ //copy the runtimeLookup view of the union
+ value.lookup.runtimeLookup.signature = (DWORDLONG)pResult->lookup.runtimeLookup.signature;
+ value.lookup.runtimeLookup.helper = (DWORD)pResult->lookup.runtimeLookup.helper;
+ value.lookup.runtimeLookup.indirections = (DWORD)pResult->lookup.runtimeLookup.indirections;
+ value.lookup.runtimeLookup.testForNull = (DWORD)pResult->lookup.runtimeLookup.testForNull;
+ value.lookup.runtimeLookup.testForFixup = (DWORD)pResult->lookup.runtimeLookup.testForFixup;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.lookup.runtimeLookup.offsets[i] = (DWORDLONG)pResult->lookup.runtimeLookup.offsets[i];
+ }
+ else
+ {
+ value.lookup.runtimeLookup.signature = (DWORDLONG)0;
+ value.lookup.runtimeLookup.helper = (DWORD)0;
+ value.lookup.runtimeLookup.indirections = (DWORD)0;
+ value.lookup.runtimeLookup.testForNull = (DWORD)0;
+ value.lookup.runtimeLookup.testForFixup = (DWORD)0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.lookup.runtimeLookup.offsets[i] = (DWORDLONG)0;
+ //copy the constLookup view of the union
+ value.lookup.constLookup.accessType = (DWORD)pResult->lookup.constLookup.accessType;
+ value.lookup.constLookup.handle = (DWORDLONG)pResult->lookup.constLookup.handle;
+ }
+ value.compileTimeHandle = (DWORDLONG)pResult->compileTimeHandle;
+ value.handleType = (DWORD)pResult->handleType;
+
+ EmbedGenericHandle->Add(key, value);
+ DEBUG_REC(dmpEmbedGenericHandle(key, value));
+}
+void MethodContext::dmpEmbedGenericHandle(const Agnostic_EmbedGenericHandle& key, const Agnostic_CORINFO_GENERICHANDLE_RESULT& value)
+{
+ printf("EmbedGenericHandle key rt{tokCon-%016llX tokScp-%016llX tok-%08X tokTyp-%08X cls-%016llX ftn-%016llX fld-%016llX tsi-%u cbts-%u msi-%u cbms-%u} emb-%u",
+ key.ResolvedToken.tokenContext,
+ key.ResolvedToken.tokenScope,
+ key.ResolvedToken.token,
+ key.ResolvedToken.tokenType,
+ key.ResolvedToken.hClass,
+ key.ResolvedToken.hMethod,
+ key.ResolvedToken.hField,
+ key.ResolvedToken.typeSpec_Index,
+ key.ResolvedToken.cbTypeSpec,
+ key.ResolvedToken.methodSpec_Index,
+ key.ResolvedToken.cbMethodSpec,
+ key.fEmbedParent);
+ printf(", value nrl-%u rlk-%u", value.lookup.lookupKind.needsRuntimeLookup, value.lookup.lookupKind.runtimeLookupKind);
+ if (value.lookup.lookupKind.needsRuntimeLookup)
+ {
+ printf(" sig-%016llX hlp-%u ind-%u tfn-%u tff-%u { ",
+ value.lookup.runtimeLookup.signature,
+ value.lookup.runtimeLookup.helper,
+ value.lookup.runtimeLookup.indirections,
+ value.lookup.runtimeLookup.testForNull,
+ value.lookup.runtimeLookup.testForFixup);
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ printf("%016llX ", value.lookup.runtimeLookup.offsets[i]);
+ printf("}");
+ }
+ else
+ {
+ printf(" at-%u han-%016llX",
+ value.lookup.constLookup.accessType,
+ value.lookup.constLookup.handle);
+ }
+ printf(" cth-%016llX ht-%u", value.compileTimeHandle, value.handleType);
+}
+void MethodContext::repEmbedGenericHandle(CORINFO_RESOLVED_TOKEN *pResolvedToken, BOOL fEmbedParent, CORINFO_GENERICHANDLE_RESULT *pResult)
+{
+ Agnostic_EmbedGenericHandle key;
+ ZeroMemory(&key, sizeof(Agnostic_EmbedGenericHandle)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_GENERICHANDLE_RESULT value;
+
+ AssertCodeMsg(EmbedGenericHandle != nullptr, EXCEPTIONCODE_MC, "Encountered an empty LWM while looking for ...");
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)EmbedGenericHandle->Contains((unsigned char *)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)EmbedGenericHandle->Contains((unsigned char *)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.fEmbedParent = (DWORD)fEmbedParent;
+
+ AssertCodeMsg(EmbedGenericHandle->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find ...");
+ value = EmbedGenericHandle->Get(key);
+
+ pResult->lookup.lookupKind.needsRuntimeLookup = value.lookup.lookupKind.needsRuntimeLookup != 0;
+ pResult->lookup.lookupKind.runtimeLookupKind = (CORINFO_RUNTIME_LOOKUP_KIND)value.lookup.lookupKind.runtimeLookupKind;
+ if (pResult->lookup.lookupKind.needsRuntimeLookup)
+ {
+ //copy the runtimeLookup view of the union
+ pResult->lookup.runtimeLookup.signature = (LPVOID)value.lookup.runtimeLookup.signature;
+ pResult->lookup.runtimeLookup.helper = (CorInfoHelpFunc)value.lookup.runtimeLookup.helper;
+ pResult->lookup.runtimeLookup.indirections = (WORD)value.lookup.runtimeLookup.indirections;
+ pResult->lookup.runtimeLookup.testForNull = value.lookup.runtimeLookup.testForNull != 0;
+ pResult->lookup.runtimeLookup.testForFixup = value.lookup.runtimeLookup.testForFixup != 0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ pResult->lookup.runtimeLookup.offsets[i] = (size_t)value.lookup.runtimeLookup.offsets[i];
+ }
+ else
+ {
+ pResult->lookup.constLookup.accessType = (InfoAccessType)value.lookup.constLookup.accessType;
+ pResult->lookup.constLookup.handle = (CORINFO_GENERIC_HANDLE)value.lookup.constLookup.handle;
+ }
+ pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)value.compileTimeHandle;
+ pResult->handleType = (CorInfoGenericHandleType)value.handleType;
+
+ DEBUG_REP(dmpEmbedGenericHandle(key, value));
+}
+
+void MethodContext::recGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE *clause)
+{
+ if (GetEHinfo == nullptr)
+ GetEHinfo = new LightWeightMap<DLD, Agnostic_CORINFO_EH_CLAUSE>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_EH_CLAUSE value;
+
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORD)EHnumber;
+
+ value.Flags = (DWORD)clause->Flags;
+ value.TryOffset = (DWORD)clause->TryOffset;
+ value.TryLength = (DWORD)clause->TryLength;
+ value.HandlerOffset = (DWORD)clause->HandlerOffset;
+ value.HandlerLength = (DWORD)clause->HandlerLength;
+ value.ClassToken = (DWORD)clause->ClassToken;
+
+ GetEHinfo->Add(key, value);
+ DEBUG_REC(dmpGetEHinfo(key, value));
+}
+void MethodContext::dmpGetEHinfo(DLD key, const Agnostic_CORINFO_EH_CLAUSE& value)
+{
+ printf("GetEHinfo key ftn-%016llX ehn-%u, value flg-%u to-%u tl-%u ho-%u hl-%u ct-%u",
+ key.A, key.B, value.Flags, value.TryOffset, value.TryLength, value.HandlerOffset, value.HandlerLength, value.ClassToken);
+}
+void MethodContext::repGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE *clause)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_EH_CLAUSE value;
+
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORD)EHnumber;
+
+ value = GetEHinfo->Get(key);
+
+ clause->Flags = (CORINFO_EH_CLAUSE_FLAGS)value.Flags;
+ clause->TryOffset = (DWORD)value.TryOffset;
+ clause->TryLength = (DWORD)value.TryLength;
+ clause->HandlerOffset = (DWORD)value.HandlerOffset;
+ clause->HandlerLength = (DWORD)value.HandlerLength;
+ clause->ClassToken = (DWORD)value.ClassToken;
+ DEBUG_REP(dmpGetEHinfo(key, value));
+}
+
+void MethodContext::recGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection)
+{
+ if (GetMethodVTableOffset == nullptr)
+ GetMethodVTableOffset = new LightWeightMap<DWORDLONG, DD>();
+
+ DD value;
+ value.A = (DWORD)*offsetOfIndirection;
+ value.B = (DWORD)*offsetAfterIndirection;
+ GetMethodVTableOffset->Add((DWORDLONG)method, value);
+ DEBUG_REC(dmpGetMethodVTableOffset((DWORDLONG)method, value));
+}
+void MethodContext::dmpGetMethodVTableOffset(DWORDLONG key, DD value)
+{
+ printf("GetMethodVTableOffset key ftn-%016llX, value offi-%u, offa-%u", key, value.A, value.B);
+}
+void MethodContext::repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection)
+{
+ DD value;
+
+ AssertCodeMsg(GetMethodVTableOffset != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)method);
+ AssertCodeMsg(GetMethodVTableOffset->GetIndex((DWORDLONG)method) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)method);
+ value = GetMethodVTableOffset->Get((DWORDLONG)method);
+
+ *offsetOfIndirection = (unsigned)value.A;
+ *offsetAfterIndirection = (unsigned)value.B;
+ DEBUG_REP(dmpGetMethodVTableOffset((DWORDLONG)method, value));
+}
+
+void MethodContext::recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_CLASS_HANDLE result)
+{
+ if (GetTokenTypeAsHandle == nullptr)
+ GetTokenTypeAsHandle = new LightWeightMap<Agnostic_CORINFO_RESOLVED_TOKEN, DWORDLONG>();
+
+ Agnostic_CORINFO_RESOLVED_TOKEN key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKEN)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.tokenContext = (DWORDLONG)0;
+ key.tokenScope = (DWORDLONG)0;
+ key.token = (DWORD)0;
+ key.tokenType = (DWORD)0;
+ key.hClass = (DWORDLONG)0;
+ key.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.hField = (DWORDLONG)pResolvedToken->hField;
+ key.typeSpec_Index = (DWORD)0;
+ key.cbTypeSpec = (DWORD)0;
+ key.methodSpec_Index = (DWORD)0;
+ key.cbMethodSpec = (DWORD)0;
+
+ GetTokenTypeAsHandle->Add(key, (DWORDLONG)result);
+}
+void MethodContext::dmpGetTokenTypeAsHandle(const Agnostic_CORINFO_RESOLVED_TOKEN& key, DWORDLONG value)
+{
+ printf("GetTokenTypeAsHandle key ftn-%016llX fld-%016llX, value cls-%016llX", key.hMethod, key.hField, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ Agnostic_CORINFO_RESOLVED_TOKEN key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKEN)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.tokenContext = (DWORDLONG)0;
+ key.tokenScope = (DWORDLONG)0;
+ key.token = (DWORD)0;
+ key.tokenType = (DWORD)0;
+ key.hClass = (DWORDLONG)0;
+ key.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.hField = (DWORDLONG)pResolvedToken->hField;
+ key.typeSpec_Index = (DWORD)0;
+ key.cbTypeSpec = (DWORD)0;
+ key.methodSpec_Index = (DWORD)0;
+ key.cbMethodSpec = (DWORD)0;
+
+ CORINFO_CLASS_HANDLE value = (CORINFO_CLASS_HANDLE)GetTokenTypeAsHandle->Get(key);
+ return value;
+}
+
+void MethodContext::recGetFieldInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult)
+{
+ if (GetFieldInfo == nullptr)
+ GetFieldInfo = new LightWeightMap<Agnostic_GetFieldInfo, Agnostic_CORINFO_FIELD_INFO>();
+ Agnostic_GetFieldInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_GetFieldInfo)); //Since dd has nested structs, and we use memcmp to compare, we need to zero out the padding bytes too
+ Agnostic_CORINFO_FIELD_INFO value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)GetFieldInfo->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)GetFieldInfo->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.callerHandle = (DWORDLONG)callerHandle;
+ key.flags = (DWORD)flags;
+
+ value.fieldAccessor = (DWORD)pResult->fieldAccessor;
+ value.fieldFlags = (DWORD)pResult->fieldFlags;
+ value.helper = (DWORD)pResult->helper;
+ value.offset = (DWORD)pResult->offset;
+ value.fieldType = (DWORD)pResult->fieldType;
+ value.structType = (DWORDLONG)pResult->structType;
+ value.accessAllowed = (DWORD)pResult->accessAllowed;
+ value.accessCalloutHelper.helperNum = (DWORD)pResult->accessCalloutHelper.helperNum;
+ value.accessCalloutHelper.numArgs = (DWORD)pResult->accessCalloutHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ value.accessCalloutHelper.args[i].constant = (DWORDLONG)pResult->accessCalloutHelper.args[i].constant;
+ value.accessCalloutHelper.args[i].argType = (DWORD)pResult->accessCalloutHelper.args[i].argType;
+ }
+ GetFieldInfo->Add(key, value);
+ DEBUG_REC(dmpGetFieldInfo(key, value));
+}
+void MethodContext::dmpGetFieldInfo(const Agnostic_GetFieldInfo& key, const Agnostic_CORINFO_FIELD_INFO& value)
+{
+ printf("GetFieldInfo key ch-%016llX flg-%08X rt{tc-%016llX ts-%016llX tok-%08X tt-%u cls-%016llX meth-%016llX fld-%016llX tsi-%u cbts-%u msi-%u cbms-%u}",
+ key.callerHandle,
+ key.flags,
+ key.ResolvedToken.tokenContext,
+ key.ResolvedToken.tokenScope,
+ key.ResolvedToken.token,
+ key.ResolvedToken.tokenType,
+ key.ResolvedToken.hClass,
+ key.ResolvedToken.hMethod,
+ key.ResolvedToken.hField,
+ key.ResolvedToken.typeSpec_Index,
+ key.ResolvedToken.cbTypeSpec,
+ key.ResolvedToken.methodSpec_Index,
+ key.ResolvedToken.cbMethodSpec);
+
+ printf(", value fa-%u fflg-%08X hlp-%u off-%u fT-%u(%s) sT-%016llX aa-%u hnum-%u na-%u {",
+ value.fieldAccessor,
+ value.fieldFlags,
+ value.helper,
+ value.offset,
+ value.fieldType,
+ toString((CorInfoType)value.fieldType),
+ value.structType,
+ value.accessAllowed,
+ value.accessCalloutHelper.helperNum,
+ value.accessCalloutHelper.numArgs);
+
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ switch ((CorInfoAccessAllowedHelperArgType)value.accessCalloutHelper.args[i].argType)
+ {
+ default:
+ printf("{%u: illegal}", i);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Field:
+ printf("{%u: fld-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Method:
+ printf("{%u: mth-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Class:
+ printf("{%u: cls-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Module:
+ printf("{%u: mod-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Const:
+ printf("{%u: const-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ }
+ }
+ printf("}");
+}
+void MethodContext::repGetFieldInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult)
+{
+ Agnostic_GetFieldInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_GetFieldInfo)); //Since dd has nested structs, and we use memcmp to compare, we need to zero out the padding bytes too
+ Agnostic_CORINFO_FIELD_INFO value;
+
+ AssertCodeMsg(GetFieldInfo != nullptr, EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)GetFieldInfo->Contains((unsigned char *)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)GetFieldInfo->Contains((unsigned char *)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.callerHandle = (DWORDLONG)callerHandle;
+ key.flags = (DWORD)flags;
+
+ DWORD origFlag = key.flags;
+
+ if (GetFieldInfo->GetIndex(key) == -1)
+ {
+#ifdef sparseMC
+ key.flags = origFlag ^ (DWORD)CORINFO_ACCESS_UNWRAP;
+ if (GetFieldInfo->GetIndex(key) != -1)
+ {
+ LogDebug("Sparse - repGetFieldInfo found value with inverted CORINFO_ACCESS_UNWRAP");
+ }
+ else
+ {
+ key.flags = origFlag ^ ((DWORD)CORINFO_ACCESS_THIS | (DWORD)CORINFO_ACCESS_UNWRAP);
+ if (GetFieldInfo->GetIndex(key) != -1)
+ {
+ LogDebug("Sparse - repGetFieldInfo found value with inverted CORINFO_ACCESS_UNWRAP|CORINFO_ACCESS_THIS");
+ }
+ else
+ {
+ key.flags = origFlag ^ (DWORD)CORINFO_ACCESS_INLINECHECK;
+ if (GetFieldInfo->GetIndex(key) != -1)
+ {
+ LogDebug("Sparse - repGetFieldInfo found value with inverted CORINFO_ACCESS_INLINECHECK");
+ }
+ else
+ {
+ LogException(EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+ }
+ }
+ }
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+#endif
+ }
+
+ value = GetFieldInfo->Get(key);
+
+ pResult->fieldAccessor = (CORINFO_FIELD_ACCESSOR)value.fieldAccessor;
+ pResult->fieldFlags = (unsigned)value.fieldFlags;
+ pResult->helper = (CorInfoHelpFunc)value.helper;
+ pResult->offset = (DWORD)value.offset;
+ pResult->fieldType = (CorInfoType)value.fieldType;
+ pResult->structType = (CORINFO_CLASS_HANDLE)value.structType;
+ pResult->accessAllowed = (CorInfoIsAccessAllowedResult)value.accessAllowed;
+ pResult->accessCalloutHelper.helperNum = (CorInfoHelpFunc)value.accessCalloutHelper.helperNum;
+ pResult->accessCalloutHelper.numArgs = (unsigned)value.accessCalloutHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ pResult->accessCalloutHelper.args[i].constant = (size_t)value.accessCalloutHelper.args[i].constant;
+ pResult->accessCalloutHelper.args[i].argType = (CorInfoAccessAllowedHelperArgType)value.accessCalloutHelper.args[i].argType;
+ }
+ DEBUG_REP(dmpGetFieldInfo(key, value));
+}
+
+void MethodContext::recEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void **ppIndirection, CORINFO_METHOD_HANDLE result)
+{
+ if (EmbedMethodHandle == nullptr)
+ EmbedMethodHandle = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+ if (ppIndirection == nullptr)
+ value.A = (DWORDLONG)0;
+ else
+ value.A = (DWORDLONG)*ppIndirection;
+ value.B = (DWORDLONG)result;
+
+ EmbedMethodHandle->Add((DWORDLONG)handle, value);
+ DEBUG_REC(dmpEmbedMethodHandle((DWORDLONG)handle, value));
+}
+void MethodContext::dmpEmbedMethodHandle(DWORDLONG key, DLDL value)
+{
+ printf("EmbedMethodHandle key ftn-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+CORINFO_METHOD_HANDLE MethodContext::repEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void **ppIndirection)
+{
+ DLDL value;
+
+ AssertCodeMsg(EmbedMethodHandle != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)handle);
+ AssertCodeMsg(EmbedMethodHandle->GetIndex((DWORDLONG)handle) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)handle);
+ value = EmbedMethodHandle->Get((DWORDLONG)handle);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+ DEBUG_REP(dmpEmbedMethodHandle((DWORDLONG)handle, value));
+ return (CORINFO_METHOD_HANDLE)value.B;
+}
+
+void MethodContext::recGetFieldAddress(CORINFO_FIELD_HANDLE field, void **ppIndirection, void *result, CorInfoType cit)
+{
+ if (GetFieldAddress == nullptr)
+ GetFieldAddress = new LightWeightMap<DWORDLONG, Agnostic_GetFieldAddress>();
+
+ Agnostic_GetFieldAddress value;
+ if (ppIndirection == nullptr)
+ value.ppIndirection = (DWORDLONG)0;
+ else
+ value.ppIndirection = (DWORDLONG)*ppIndirection;
+ value.fieldAddress = (DWORDLONG)result;
+
+ value.fieldValue = (DWORD)-1;
+
+ // Make an attempt at stashing a copy of the value
+ if (result > (void*)0xffff) //TODO-Cleanup: sometimes there is a field offset?
+ {
+ DWORDLONG scratch = 0x4242424242424242;
+ switch (cit)
+ {
+ case CORINFO_TYPE_BOOL:
+ case CORINFO_TYPE_BYTE:
+ case CORINFO_TYPE_UBYTE:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(BYTE), true);//important to not merge two fields into one address
+ break;
+ case CORINFO_TYPE_CHAR:
+ case CORINFO_TYPE_SHORT:
+ case CORINFO_TYPE_USHORT:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(WORD), true);//important to not merge two fields into one address
+ break;
+ case CORINFO_TYPE_INT:
+ case CORINFO_TYPE_UINT:
+ case CORINFO_TYPE_FLOAT:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(DWORD), true);//important to not merge two fields into one address
+ break;
+ case CORINFO_TYPE_LONG:
+ case CORINFO_TYPE_ULONG:
+ case CORINFO_TYPE_DOUBLE:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(DWORDLONG), true);//important to not merge two fields into one address
+ break;
+ case CORINFO_TYPE_NATIVEINT:
+ case CORINFO_TYPE_NATIVEUINT:
+ case CORINFO_TYPE_PTR:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(size_t), true);//important to not merge two fields into one address
+ GetFieldAddress->AddBuffer((unsigned char*)&scratch, sizeof(DWORD)); //Padding out the data so we can read it back "safetly" on x64
+ break;
+ default:
+ break;
+ }
+ }
+ GetFieldAddress->Add((DWORDLONG)field, value);
+ DEBUG_REC(dmpGetFieldAddress((DWORDLONG)field, value));
+}
+void MethodContext::dmpGetFieldAddress(DWORDLONG key, const Agnostic_GetFieldAddress& value)
+{
+ printf("GetFieldAddress key fld-%016llX, value ppi-%016llX addr-%016llX val-%u", key, value.ppIndirection, value.fieldAddress, value.fieldValue);
+}
+void* MethodContext::repGetFieldAddress(CORINFO_FIELD_HANDLE field, void **ppIndirection)
+{
+ Agnostic_GetFieldAddress value;
+
+ value = GetFieldAddress->Get((DWORDLONG)field);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.ppIndirection;
+ void *temp;
+
+ if (value.fieldValue != (DWORD)-1)
+ {
+ temp = (void*)GetFieldAddress->GetBuffer(value.fieldValue);
+ cr->recAddressMap((void*)value.fieldAddress, temp, toCorInfoSize(repGetFieldType(field, nullptr, nullptr)));
+ }
+ else
+ temp = (void*)value.fieldAddress;
+
+ DEBUG_REP(dmpGetFieldAddress((DWORDLONG)field, value));
+ return temp;
+}
+
+void MethodContext::recGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE *gcPtrs, unsigned len, unsigned result)
+{
+ if (GetClassGClayout == nullptr)
+ GetClassGClayout = new LightWeightMap<DWORDLONG, Agnostic_GetClassGClayout>();
+
+ Agnostic_GetClassGClayout value;
+
+ value.gcPtrs_Index = (DWORD)GetClassGClayout->AddBuffer((unsigned char*)gcPtrs, len*sizeof(BYTE));
+ value.len = (DWORD)len;
+ value.valCount = (DWORD)result;
+
+ GetClassGClayout->Add((DWORDLONG)cls, value);
+ DEBUG_REC(dmpGetClassGClayout((DWORDLONG)cls, value));
+}
+void MethodContext::dmpGetClassGClayout(DWORDLONG key, const Agnostic_GetClassGClayout& value)
+{
+ printf("GetClassGCLayout key %016llX, value len %u cnt %u {", key, value.len, value.valCount);
+ if (value.gcPtrs_Index != -1)
+ {
+ BYTE *ptr = (BYTE *)GetClassGClayout->GetBuffer(value.gcPtrs_Index);
+ for (unsigned int i = 0; i < value.len; i++)
+ {
+ printf("0x%02x", ptr[i]);
+ if (i + 1 < value.len)
+ printf(",");
+ }
+ GetClassGClayout->Unlock();
+ }
+ printf("}");
+}
+unsigned MethodContext::repGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE *gcPtrs)
+{
+ Agnostic_GetClassGClayout value;
+
+ AssertCodeMsg(GetClassGClayout != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetClassGClayout->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ value = GetClassGClayout->Get((DWORDLONG)cls);
+
+ unsigned int len = (unsigned int)value.len;
+ unsigned int index = (unsigned int)value.gcPtrs_Index;
+
+ if (index != -1)
+ {
+ BYTE *ptr = (BYTE *)GetClassGClayout->GetBuffer(index);
+ for (unsigned int i = 0; i < len; i++)
+ gcPtrs[i] = ptr[i];
+ }
+ DEBUG_REP(dmpGetClassGClayout((DWORDLONG)cls, value));
+ return (unsigned)value.valCount;
+}
+
+void MethodContext::recGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, BOOL fDoubleAlignHint, unsigned result)
+{
+ if (GetClassAlignmentRequirement == nullptr)
+ GetClassAlignmentRequirement = new LightWeightMap<DLD, DWORD>();
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)cls;
+ key.B = (DWORD)fDoubleAlignHint;
+
+ GetClassAlignmentRequirement->Add(key, (DWORD)result);
+ DEBUG_REC(dmpGetClassAlignmentRequirement(key, result));
+}
+void MethodContext::dmpGetClassAlignmentRequirement(DLD key, DWORD value)
+{
+ printf("GetClassAlignmentRequirement key %016llX %u, value %u", key.A, key.B, value);
+}
+unsigned MethodContext::repGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, BOOL fDoubleAlignHint)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.A = (DWORDLONG)cls;
+ key.B = (DWORD)fDoubleAlignHint;
+
+ unsigned result = (unsigned)GetClassAlignmentRequirement->Get(key);
+ DEBUG_REP(dmpGetClassAlignmentRequirement(key, result));
+ return result;
+}
+
+void MethodContext::recCanAccessClass(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper, CorInfoIsAccessAllowedResult result)
+{
+ if (CanAccessClass == nullptr)
+ CanAccessClass = new LightWeightMap<Agnostic_CanAccessClassIn, Agnostic_CanAccessClassOut>();
+
+ Agnostic_CanAccessClassIn key;
+ ZeroMemory(&key, sizeof(Agnostic_CanAccessClassIn)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ Agnostic_CanAccessClassOut value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)CanAccessClass->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)CanAccessClass->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.callerHandle = (DWORDLONG)callerHandle;
+
+ value.AccessHelper.helperNum = (DWORD)pAccessHelper->helperNum;
+ value.AccessHelper.numArgs = (DWORD)pAccessHelper->numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ value.AccessHelper.args[i].constant = (DWORDLONG)pAccessHelper->args[i].constant;
+ value.AccessHelper.args[i].argType = (DWORD)pAccessHelper->args[i].argType;
+ }
+ value.result = (DWORD)result;
+
+ CanAccessClass->Add(key, value);
+ DEBUG_REC(dmpCanAccessClass(key, value));
+}
+void MethodContext::dmpCanAccessClass(const Agnostic_CanAccessClassIn& key, const Agnostic_CanAccessClassOut& value)
+{
+ printf("CanAccessClass key tc-%016llX ts-%016llX tok-%08X tt-%u cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u",
+ key.ResolvedToken.tokenContext,
+ key.ResolvedToken.tokenScope,
+ key.ResolvedToken.token,
+ key.ResolvedToken.tokenType,
+ key.ResolvedToken.hClass,
+ key.ResolvedToken.hMethod,
+ key.ResolvedToken.hField,
+ key.ResolvedToken.typeSpec_Index,
+ key.ResolvedToken.cbTypeSpec,
+ key.ResolvedToken.methodSpec_Index,
+ key.ResolvedToken.cbMethodSpec);
+ printf(", value hnum-%u na-%u {",
+ value.AccessHelper.helperNum,
+ value.AccessHelper.numArgs);
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ printf("{%016llX %u}",
+ value.AccessHelper.args[i].constant,
+ value.AccessHelper.args[i].argType);
+ }
+ printf("} res-%u", value.result);
+}
+CorInfoIsAccessAllowedResult MethodContext::repCanAccessClass(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper)
+{
+ Agnostic_CanAccessClassIn key;
+ ZeroMemory(&key, sizeof(Agnostic_CanAccessClassIn)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CanAccessClassOut value;
+
+ AssertCodeMsg(CanAccessClass != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)pResolvedToken->hClass);
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)CanAccessClass->Contains((unsigned char *)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)CanAccessClass->Contains((unsigned char *)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.callerHandle = (DWORDLONG)callerHandle;
+
+ AssertCodeMsg(CanAccessClass->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)pResolvedToken->hClass);
+ value = CanAccessClass->Get(key);
+
+ pAccessHelper->helperNum = (CorInfoHelpFunc)value.AccessHelper.helperNum;
+ pAccessHelper->numArgs = (unsigned)value.AccessHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ pAccessHelper->args[i].constant = (size_t)value.AccessHelper.args[i].constant;
+ pAccessHelper->args[i].argType = (CorInfoAccessAllowedHelperArgType)value.AccessHelper.args[i].argType;
+ }
+ CorInfoIsAccessAllowedResult temp = (CorInfoIsAccessAllowedResult)value.result;
+ DEBUG_REP(dmpCanAccessClass(key, value));
+ return temp;
+}
+
+void MethodContext::recGetCastingHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, bool fThrowing, CorInfoHelpFunc result)
+{
+ if (GetCastingHelper == nullptr)
+ GetCastingHelper = new LightWeightMap<Agnostic_GetCastingHelper, DWORD>();
+
+ Agnostic_GetCastingHelper key;
+ ZeroMemory(&key, sizeof(Agnostic_GetCastingHelper)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ResolvedToken.token = (DWORD)0;
+ key.ResolvedToken.tokenType = (DWORD)0;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)0;
+ key.ResolvedToken.hField = (DWORDLONG)0;
+ key.ResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbMethodSpec = (DWORD)0;
+ key.fThrowing = (DWORD)fThrowing;
+
+ GetCastingHelper->Add(key, (DWORD)result);
+}
+void MethodContext::dmpGetCastingHelper(const Agnostic_GetCastingHelper& key, DWORD value)
+{
+ printf("GetCastingHelper key cls-%016llX, thw-%u, value res-%u", key.ResolvedToken.hClass, key.fThrowing, value);
+}
+CorInfoHelpFunc MethodContext::repGetCastingHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, bool fThrowing)
+{
+ Agnostic_GetCastingHelper key;
+ ZeroMemory(&key, sizeof(Agnostic_GetCastingHelper)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ResolvedToken.token = (DWORD)0;
+ key.ResolvedToken.tokenType = (DWORD)0;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)0;
+ key.ResolvedToken.hField = (DWORDLONG)0;
+ key.ResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbMethodSpec = (DWORD)0;
+ key.fThrowing = (DWORD)fThrowing;
+
+ CorInfoHelpFunc value = (CorInfoHelpFunc)GetCastingHelper->Get(key);
+ return value;
+}
+
+void MethodContext::recEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void **ppIndirection, CORINFO_MODULE_HANDLE result)
+{
+ if (EmbedModuleHandle == nullptr)
+ EmbedModuleHandle = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ EmbedModuleHandle->Add((DWORDLONG)handle, value);
+}
+void MethodContext::dmpEmbedModuleHandle(DWORDLONG key, DLDL value)
+{
+ printf("EmbedModuleHandle key mod-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+CORINFO_MODULE_HANDLE MethodContext::repEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void **ppIndirection)
+{
+ DLDL value;
+
+ value = EmbedModuleHandle->Get((DWORDLONG)handle);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ return (CORINFO_MODULE_HANDLE)value.B;
+
+}
+
+void MethodContext::recEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void **ppIndirection, CORINFO_CLASS_HANDLE result)
+{
+ if (EmbedClassHandle == nullptr)
+ EmbedClassHandle = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ EmbedClassHandle->Add((DWORDLONG)handle, value);
+ DEBUG_REC(dmpEmbedClassHandle((DWORDLONG)handle, value));
+}
+void MethodContext::dmpEmbedClassHandle(DWORDLONG key, DLDL value)
+{
+ printf("EmbedClassHandle key cls-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+CORINFO_CLASS_HANDLE MethodContext::repEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void **ppIndirection)
+{
+ DLDL value;
+
+ AssertCodeMsg(EmbedClassHandle != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)handle);
+ AssertCodeMsg(EmbedClassHandle->GetIndex((DWORDLONG)handle) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)handle);
+ value = EmbedClassHandle->Get((DWORDLONG)handle);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ DEBUG_REP(dmpEmbedClassHandle((DWORDLONG)handle, value));
+ return (CORINFO_CLASS_HANDLE)value.B;
+}
+
+void MethodContext::recPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, BOOL result)
+{
+ if (PInvokeMarshalingRequired == nullptr)
+ PInvokeMarshalingRequired = new LightWeightMap<Agnostic_PInvokeMarshalingRequired, DWORD>();
+
+ Agnostic_PInvokeMarshalingRequired key;
+ ZeroMemory(&key, sizeof(Agnostic_PInvokeMarshalingRequired)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.method = (DWORDLONG)method;
+ key.callSiteSig.callConv = (DWORD)0;
+ key.callSiteSig.retTypeClass = (DWORDLONG)0;
+ key.callSiteSig.retTypeSigClass = (DWORDLONG)0;
+ key.callSiteSig.retType = (DWORD)0;
+ key.callSiteSig.flags = (DWORD)0;
+ key.callSiteSig.numArgs = (DWORD)0;
+ key.callSiteSig.sigInst_classInstCount = (DWORD)0;
+ key.callSiteSig.sigInst_classInst_Index = (DWORD)0;
+ key.callSiteSig.sigInst_methInstCount = (DWORD)0;
+ key.callSiteSig.sigInst_methInst_Index = (DWORD)0;
+ key.callSiteSig.args = (DWORDLONG)0;
+ key.callSiteSig.pSig = (DWORD)PInvokeMarshalingRequired->AddBuffer((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig);
+ key.callSiteSig.cbSig = (DWORD)callSiteSig->cbSig;
+ key.callSiteSig.scope = (DWORDLONG)callSiteSig->scope;
+ key.callSiteSig.token = (DWORD)0;
+
+ PInvokeMarshalingRequired->Add(key, (DWORD)result);
+ DEBUG_REC(dmpPInvokeMarshalingRequired(key, (DWORD)result));
+}
+void MethodContext::dmpPInvokeMarshalingRequired(const Agnostic_PInvokeMarshalingRequired& key, DWORD value)
+{
+ printf("PInvokeMarshalingRequired key mth-%016llX scp-%016llX sig-%u, value res-%u",
+ key.method,
+ key.callSiteSig.scope,
+ key.callSiteSig.pSig,
+ value);
+}
+//Note the jit interface implementation seems to only care about scope and pSig from callSiteSig
+BOOL MethodContext::repPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig)
+{
+ if (PInvokeMarshalingRequired == nullptr)//so when we replay checked on free, we throw from lwm
+ return TRUE; // TODO-Cleanup: hackish...
+
+ Agnostic_PInvokeMarshalingRequired key;
+ ZeroMemory(&key, sizeof(Agnostic_PInvokeMarshalingRequired)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.method = (DWORDLONG)method;
+ key.callSiteSig.callConv = (DWORD)0;
+ key.callSiteSig.retTypeClass = (DWORDLONG)0;
+ key.callSiteSig.retTypeSigClass = (DWORDLONG)0;
+ key.callSiteSig.retType = (DWORD)0;
+ key.callSiteSig.flags = (DWORD)0;
+ key.callSiteSig.numArgs = (DWORD)0;
+ key.callSiteSig.sigInst_classInstCount = (DWORD)0;
+ key.callSiteSig.sigInst_classInst_Index = (DWORD)0;
+ key.callSiteSig.sigInst_methInstCount = (DWORD)0;
+ key.callSiteSig.sigInst_methInst_Index = (DWORD)0;
+ key.callSiteSig.args = (DWORDLONG)0;
+ key.callSiteSig.pSig = (DWORD)PInvokeMarshalingRequired->Contains((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig);
+ key.callSiteSig.cbSig = (DWORD)callSiteSig->cbSig;
+ key.callSiteSig.scope = (DWORDLONG)callSiteSig->scope;
+ key.callSiteSig.token = (DWORD)0;
+
+ DWORD value = PInvokeMarshalingRequired->Get(key);
+ DEBUG_REP(dmpPInvokeMarshalingRequired(key, value));
+ return value;
+}
+
+void MethodContext::recFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig)
+{
+ if (FindSig == nullptr)
+ FindSig = new LightWeightMap<Agnostic_FindSig, Agnostic_CORINFO_SIG_INFO>();
+
+ Agnostic_FindSig key;
+ ZeroMemory(&key, sizeof(Agnostic_FindSig)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.module = (DWORDLONG)module;
+ key.sigTOK = (DWORD)sigTOK;
+ key.context = (DWORDLONG)context;
+
+ value.callConv = (DWORD)sig->callConv;
+ value.retTypeClass = (DWORDLONG)sig->retTypeClass;
+ value.retTypeSigClass = (DWORDLONG)sig->retTypeSigClass;
+ value.retType = (DWORD)sig->retType;
+ value.flags = (DWORD)sig->flags;
+ value.numArgs = (DWORD)sig->numArgs;
+ value.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ value.sigInst_classInst_Index = FindSig->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8); //porting issue
+ value.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ value.sigInst_methInst_Index = FindSig->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8); //porting issue
+ value.args = (DWORDLONG)sig->args;
+ value.cbSig = (DWORD)sig->cbSig;
+ value.pSig = (DWORD)FindSig->AddBuffer((unsigned char *)sig->pSig, sig->cbSig);
+ value.scope = (DWORDLONG)sig->scope;
+ value.token = (DWORD)sig->token;
+
+ FindSig->Add(key, value);
+ DEBUG_REC(dmpFindSig(key, value));
+}
+void MethodContext::dmpFindSig(const Agnostic_FindSig& key, const Agnostic_CORINFO_SIG_INFO& value)
+{
+ printf("FindSig key module-%016llX sigTOK-%08X context-%016llX", key.module, key.sigTOK, key.context);
+ printf(", value callConv-%08X retTypeClass-%016llX retTypeSigClass-%016llX retType-%u(%s) flags-%08X numArgs-%08X classInstCount-%08X classInd-%08X "
+ "methInstCount-%08X methInd-%08X args-%016llX cbSig-%08X pSig-%08X scope-%016llX token-%08X",
+ value.callConv,
+ value.retTypeClass,
+ value.retTypeSigClass,
+ value.retType,
+ toString((CorInfoType)value.retType),
+ value.flags,
+ value.numArgs,
+ value.sigInst_classInstCount,
+ value.sigInst_classInst_Index,
+ value.sigInst_methInstCount,
+ value.sigInst_methInst_Index,
+ value.args,
+ value.cbSig,
+ value.pSig,
+ value.scope,
+ value.token);
+}
+void MethodContext::repFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig)
+{
+ Agnostic_FindSig key;
+ ZeroMemory(&key, sizeof(Agnostic_FindSig)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.module = (DWORDLONG)module;
+ key.sigTOK = (DWORD)sigTOK;
+ key.context = (DWORDLONG)context;
+
+ value = FindSig->Get(key);
+
+ sig->callConv = (CorInfoCallConv)value.callConv;
+ sig->retTypeClass = (CORINFO_CLASS_HANDLE)value.retTypeClass;
+ sig->retTypeSigClass = (CORINFO_CLASS_HANDLE)value.retTypeSigClass;
+ sig->retType = (CorInfoType)value.retType;
+ sig->flags = (unsigned)value.flags;
+ sig->numArgs = (unsigned)value.numArgs;
+ sig->sigInst.classInstCount = (unsigned)value.sigInst_classInstCount;
+ sig->sigInst.classInst = (CORINFO_CLASS_HANDLE*)FindSig->GetBuffer(value.sigInst_classInst_Index);
+ sig->sigInst.methInstCount = (unsigned)value.sigInst_methInstCount;
+ sig->sigInst.methInst = (CORINFO_CLASS_HANDLE*)FindSig->GetBuffer(value.sigInst_methInst_Index);
+ sig->args = (CORINFO_ARG_LIST_HANDLE)value.args;
+ sig->cbSig = (unsigned int)value.cbSig;
+ sig->pSig = (PCCOR_SIGNATURE)FindSig->GetBuffer(value.pSig);
+ sig->scope = (CORINFO_MODULE_HANDLE)value.scope;
+ sig->token = (mdToken)value.token;
+ DEBUG_REP(dmpFindSig(key, value));
+}
+
+void MethodContext::recGetEEInfo(CORINFO_EE_INFO *pEEInfoOut)
+{
+ if (GetEEInfo == nullptr)
+ GetEEInfo = new LightWeightMap<DWORD, Agnostic_CORINFO_EE_INFO>();
+
+ Agnostic_CORINFO_EE_INFO value;
+
+ value.inlinedCallFrameInfo.size = (DWORD)pEEInfoOut->inlinedCallFrameInfo.size;
+ value.inlinedCallFrameInfo.offsetOfGSCookie = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfGSCookie;
+ value.inlinedCallFrameInfo.offsetOfFrameVptr = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameVptr;
+ value.inlinedCallFrameInfo.offsetOfFrameLink = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameLink;
+ value.inlinedCallFrameInfo.offsetOfCallSiteSP = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfCallSiteSP;
+ value.inlinedCallFrameInfo.offsetOfCalleeSavedFP = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfCalleeSavedFP;
+ value.inlinedCallFrameInfo.offsetOfCallTarget = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfCallTarget;
+ value.inlinedCallFrameInfo.offsetOfReturnAddress = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfReturnAddress;
+ value.offsetOfThreadFrame = (DWORD)pEEInfoOut->offsetOfThreadFrame;
+ value.offsetOfGCState = (DWORD)pEEInfoOut->offsetOfGCState;
+ value.offsetOfDelegateInstance = (DWORD)pEEInfoOut->offsetOfDelegateInstance;
+ value.offsetOfDelegateFirstTarget = (DWORD)pEEInfoOut->offsetOfDelegateFirstTarget;
+ value.offsetOfSecureDelegateIndirectCell = (DWORD)pEEInfoOut->offsetOfSecureDelegateIndirectCell;
+ value.offsetOfTransparentProxyRP = (DWORD)pEEInfoOut->offsetOfTransparentProxyRP;
+ value.offsetOfRealProxyServer = (DWORD)pEEInfoOut->offsetOfRealProxyServer;
+ value.offsetOfObjArrayData = (DWORD)pEEInfoOut->offsetOfObjArrayData;
+ value.sizeOfReversePInvokeFrame = (DWORD)pEEInfoOut->sizeOfReversePInvokeFrame;
+ value.osPageSize = (DWORD)pEEInfoOut->osPageSize;
+ value.maxUncheckedOffsetForNullObject = (DWORD)pEEInfoOut->maxUncheckedOffsetForNullObject;
+ value.targetAbi = (DWORD)pEEInfoOut->targetAbi;
+ value.osType = (DWORD)pEEInfoOut->osType;
+ value.osMajor = (DWORD)pEEInfoOut->osMajor;
+ value.osMinor = (DWORD)pEEInfoOut->osMinor;
+ value.osBuild = (DWORD)pEEInfoOut->osBuild;
+
+ GetEEInfo->Add((DWORD)0, value);
+ DEBUG_REC(dmpGetEEInfo((DWORD)0, value));
+}
+void MethodContext::dmpGetEEInfo(DWORD key, const Agnostic_CORINFO_EE_INFO& value)
+{
+ printf("GetEEInfo key %u, value icfi{sz-%u ogs-%u ofv-%u ofl-%u ocsp-%u ocsfp-%u oct-%u ora-%u} "
+ "otf-%u ogcs-%u odi-%u odft-%u osdic-%u otrp-%u orps-%u ooad-%u srpf-%u osps-%u muono-%u tabi-%u osType-%u osMajor-%u osMinor-%u osBuild-%u",
+ key,
+ value.inlinedCallFrameInfo.size,
+ value.inlinedCallFrameInfo.offsetOfGSCookie,
+ value.inlinedCallFrameInfo.offsetOfFrameVptr,
+ value.inlinedCallFrameInfo.offsetOfFrameLink,
+ value.inlinedCallFrameInfo.offsetOfCallSiteSP,
+ value.inlinedCallFrameInfo.offsetOfCalleeSavedFP,
+ value.inlinedCallFrameInfo.offsetOfCallTarget,
+ value.inlinedCallFrameInfo.offsetOfReturnAddress,
+ value.offsetOfThreadFrame,
+ value.offsetOfGCState,
+ value.offsetOfDelegateInstance,
+ value.offsetOfDelegateFirstTarget,
+ value.offsetOfSecureDelegateIndirectCell,
+ value.offsetOfTransparentProxyRP,
+ value.offsetOfRealProxyServer,
+ value.offsetOfObjArrayData,
+ value.sizeOfReversePInvokeFrame,
+ value.osPageSize,
+ value.maxUncheckedOffsetForNullObject,
+ value.targetAbi,
+ value.osType,
+ value.osMajor,
+ value.osMinor,
+ value.osBuild);
+}
+void MethodContext::repGetEEInfo(CORINFO_EE_INFO *pEEInfoOut)
+{
+ Agnostic_CORINFO_EE_INFO value;
+
+ int index = -1;
+ if (GetEEInfo != nullptr)
+ index = GetEEInfo->GetIndex((DWORD)0);
+ if (index >= 0)
+ {
+ value = GetEEInfo->Get((DWORD)0);
+ pEEInfoOut->inlinedCallFrameInfo.size = (unsigned)value.inlinedCallFrameInfo.size;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfGSCookie = (unsigned)value.inlinedCallFrameInfo.offsetOfGSCookie;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameVptr = (unsigned)value.inlinedCallFrameInfo.offsetOfFrameVptr;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameLink = (unsigned)value.inlinedCallFrameInfo.offsetOfFrameLink;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCallSiteSP = (unsigned)value.inlinedCallFrameInfo.offsetOfCallSiteSP;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCalleeSavedFP = (unsigned)value.inlinedCallFrameInfo.offsetOfCalleeSavedFP;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCallTarget = (unsigned)value.inlinedCallFrameInfo.offsetOfCallTarget;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfReturnAddress = (unsigned)value.inlinedCallFrameInfo.offsetOfReturnAddress;
+ pEEInfoOut->offsetOfThreadFrame = (unsigned)value.offsetOfThreadFrame;
+ pEEInfoOut->offsetOfGCState = (unsigned)value.offsetOfGCState;
+ pEEInfoOut->offsetOfDelegateInstance = (unsigned)value.offsetOfDelegateInstance;
+ pEEInfoOut->offsetOfDelegateFirstTarget = (unsigned)value.offsetOfDelegateFirstTarget;
+ pEEInfoOut->offsetOfSecureDelegateIndirectCell = (unsigned)value.offsetOfSecureDelegateIndirectCell;
+ pEEInfoOut->offsetOfTransparentProxyRP = (unsigned)value.offsetOfTransparentProxyRP;
+ pEEInfoOut->offsetOfRealProxyServer = (unsigned)value.offsetOfRealProxyServer;
+ pEEInfoOut->offsetOfObjArrayData = (unsigned)value.offsetOfObjArrayData;
+ pEEInfoOut->sizeOfReversePInvokeFrame = (unsigned)value.sizeOfReversePInvokeFrame;
+ pEEInfoOut->osPageSize = (size_t)value.osPageSize;
+ pEEInfoOut->maxUncheckedOffsetForNullObject = (size_t)value.maxUncheckedOffsetForNullObject;
+ pEEInfoOut->targetAbi = (CORINFO_RUNTIME_ABI)value.targetAbi;
+ pEEInfoOut->osType = (CORINFO_OS)value.osType;
+ pEEInfoOut->osMajor = (unsigned)value.osMajor;
+ pEEInfoOut->osMinor = (unsigned)value.osMinor;
+ pEEInfoOut->osBuild = (unsigned)value.osBuild;
+ DEBUG_REP(dmpGetEEInfo((DWORD)0, value));
+ }
+ else
+ {
+ pEEInfoOut->inlinedCallFrameInfo.size = (unsigned)0x40;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfGSCookie = (unsigned)0;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameVptr = (unsigned)0x8;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameLink = (unsigned)0x10;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCallSiteSP = (unsigned)0x28;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCalleeSavedFP = (unsigned)0x38;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCallTarget = (unsigned)0x18;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfReturnAddress = (unsigned)0x30;
+ pEEInfoOut->offsetOfThreadFrame = (unsigned)0x10;
+ pEEInfoOut->offsetOfGCState = (unsigned)0xc;
+ pEEInfoOut->offsetOfDelegateInstance = (unsigned)0x8;
+ pEEInfoOut->offsetOfDelegateFirstTarget = (unsigned)0x18;
+ pEEInfoOut->offsetOfSecureDelegateIndirectCell = (unsigned)0x40;
+ pEEInfoOut->offsetOfTransparentProxyRP = (unsigned)0x8;
+ pEEInfoOut->offsetOfRealProxyServer = (unsigned)0x18;
+ pEEInfoOut->offsetOfObjArrayData = (unsigned)0x18;
+ pEEInfoOut->sizeOfReversePInvokeFrame = (unsigned)0x8;
+ pEEInfoOut->osPageSize = (size_t)0x1000;
+ pEEInfoOut->maxUncheckedOffsetForNullObject = (size_t)((32*1024)-1);
+ pEEInfoOut->targetAbi = CORINFO_DESKTOP_ABI;
+ pEEInfoOut->osType = (CORINFO_OS)0;
+ pEEInfoOut->osMajor = (unsigned)0;
+ pEEInfoOut->osMinor = (unsigned)0;
+ pEEInfoOut->osBuild = (unsigned)0;
+#ifdef DEBUG_REP
+ printf("repGetEEInfo - fell to default params\n");
+#endif
+ }
+}
+
+void MethodContext::recGetGSCookie(GSCookie *pCookieVal, GSCookie **ppCookieVal)
+{
+ if (GetGSCookie == nullptr)
+ GetGSCookie = new LightWeightMap<DWORD, DLDL>();
+
+ DLDL value;
+
+ if (pCookieVal != nullptr)
+ value.A = (DWORDLONG)*pCookieVal;
+ else
+ value.A = (DWORDLONG)0;
+
+ if (ppCookieVal != nullptr)
+ value.B = (DWORDLONG)*ppCookieVal;
+ else
+ value.B = (DWORDLONG)0;
+ GetGSCookie->Add((DWORD)0, value);
+}
+void MethodContext::dmpGetGSCookie(DWORD key, DLDL value)
+{
+ printf("GetGSCookie key 0, value pCookieVal-%016llX ppCookieVal-%016llX", value.A, value.B);
+}
+void MethodContext::repGetGSCookie(GSCookie *pCookieVal, GSCookie **ppCookieVal)
+{
+ DLDL value;
+
+ value = GetGSCookie->Get((DWORD)0);
+
+ if (pCookieVal != nullptr)
+ *pCookieVal = (GSCookie)value.A;
+ if (ppCookieVal != nullptr)
+ *ppCookieVal = (GSCookie*)value.B;
+}
+
+void MethodContext::recGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE *pModule, void **ppIndirection, size_t result)
+{
+ if (GetClassModuleIdForStatics == nullptr)
+ GetClassModuleIdForStatics = new LightWeightMap<DWORDLONG, Agnostic_GetClassModuleIdForStatics>();
+
+ Agnostic_GetClassModuleIdForStatics value;
+
+ if (pModule != nullptr)
+ value.Module = (DWORDLONG)*pModule;
+ else
+ value.Module = (DWORDLONG)0;
+ if (ppIndirection != nullptr)
+ value.pIndirection = (DWORDLONG)*ppIndirection;
+ else
+ value.pIndirection = (DWORDLONG)0;
+ value.result = (DWORDLONG)result;
+ GetClassModuleIdForStatics->Add((DWORDLONG)cls, value);
+}
+void MethodContext::dmpGetClassModuleIdForStatics(DWORDLONG key, const Agnostic_GetClassModuleIdForStatics& value)
+{
+ printf("GetClassModuleIdForStatics key cls-%016llX, value mod-%016llX pp-%016llX res-%016llX", key, value.Module, value.pIndirection, value.result);
+}
+size_t MethodContext::repGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE *pModule, void **ppIndirection)
+{
+ Agnostic_GetClassModuleIdForStatics value;
+
+ value = GetClassModuleIdForStatics->Get((DWORDLONG)cls);
+
+ if (pModule != nullptr)
+ *pModule = (CORINFO_MODULE_HANDLE)value.Module;
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.pIndirection;
+
+ return (size_t)value.result;
+}
+
+void MethodContext::recGetThreadTLSIndex(void **ppIndirection, DWORD result)
+{
+ if (GetThreadTLSIndex == nullptr)
+ GetThreadTLSIndex = new LightWeightMap<DWORD, DLD>();
+
+ DLD value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORD)result;
+
+ GetThreadTLSIndex->Add((DWORD)0, value);
+}
+void MethodContext::dmpGetThreadTLSIndex(DWORD key, DLD value)
+{
+ printf("GetThreadTLSIndex key 0, value ppIndirection-%016llX result-%08X", value.A, value.B);
+}
+DWORD MethodContext::repGetThreadTLSIndex(void **ppIndirection)
+{
+ DLD value;
+
+ value = GetThreadTLSIndex->Get((DWORD)0);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ return (DWORD)value.B;
+}
+
+void MethodContext::recGetInlinedCallFrameVptr(void **ppIndirection, const void * result)
+{
+ if (GetInlinedCallFrameVptr == nullptr)
+ GetInlinedCallFrameVptr = new LightWeightMap<DWORD, DLDL>();
+
+ DLDL value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetInlinedCallFrameVptr->Add((DWORD)0, value);
+}
+void MethodContext::dmpGetInlinedCallFrameVptr(DWORD key, DLDL value)
+{
+ printf("GetInlinedCallFrameVptr key 0, value ppIndirection-%016llX result-%016llX\n", value.A, value.B);
+}
+const void * MethodContext::repGetInlinedCallFrameVptr(void **ppIndirection)
+{
+ DLDL value;
+
+ value = GetInlinedCallFrameVptr->Get((DWORD)0);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ return (const void *)value.B;
+}
+
+void MethodContext::recGetAddrOfCaptureThreadGlobal(void **ppIndirection, LONG * result)
+{
+ if (GetAddrOfCaptureThreadGlobal == nullptr)
+ GetAddrOfCaptureThreadGlobal = new LightWeightMap<DWORD, DLDL>();
+
+ DLDL value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetAddrOfCaptureThreadGlobal->Add((DWORD)0, value);
+ DEBUG_REC(dmpGetAddrOfCaptureThreadGlobal((DWORD)0, value));
+}
+void MethodContext::dmpGetAddrOfCaptureThreadGlobal(DWORD key, DLDL value)
+{
+ printf("GetAddrOfCaptureThreadGlobal key %u, value ppi-%016llX res-%016llX", key, value.A, value.B);
+}
+LONG * MethodContext::repGetAddrOfCaptureThreadGlobal(void **ppIndirection)
+{
+ DLDL value;
+
+ if ((GetAddrOfCaptureThreadGlobal == nullptr) || (GetAddrOfCaptureThreadGlobal->GetIndex((DWORD)0) == -1))
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetAddrOfCaptureThreadGlobal returning 0xCAFE0001");
+ return (LONG*)(size_t)0xCAFE0001;
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find anything for GetAddrOfCaptureThreadGlobal", "");
+#endif
+ }
+ value = GetAddrOfCaptureThreadGlobal->Get((DWORD)0);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ DEBUG_REP(dmpGetAddrOfCaptureThreadGlobal((DWORD)0, value));
+ return (LONG *)value.B;
+}
+
+void MethodContext::recGetClassDomainID(CORINFO_CLASS_HANDLE cls, void **ppIndirection, unsigned result)
+{
+ if (GetClassDomainID == nullptr)
+ GetClassDomainID = new LightWeightMap<DWORDLONG, DLD>();
+
+ DLD value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORD)result;
+
+ GetClassDomainID->Add((DWORDLONG)cls, value);
+ DEBUG_REC(dmpGetClassDomainID((DWORDLONG)cls, value));
+}
+void MethodContext::dmpGetClassDomainID(DWORDLONG key, DLD value)
+{
+ printf("GetClassDomainID key cls-%016llX, value pp-%016llX res-%u", key, value.A, value.B);
+}
+unsigned MethodContext::repGetClassDomainID(CORINFO_CLASS_HANDLE cls, void **ppIndirection)
+{
+ DLD value;
+
+ AssertCodeMsg(GetClassDomainID != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetClassDomainID->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ value = GetClassDomainID->Get((DWORDLONG)cls);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ DEBUG_REP(dmpGetClassDomainID((DWORDLONG)cls, value));
+ return (unsigned)value.B;
+}
+
+void MethodContext::recGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND *result)
+{
+ if (GetLocationOfThisType == nullptr)
+ GetLocationOfThisType = new LightWeightMap<DWORDLONG, Agnostic_CORINFO_LOOKUP_KIND>();
+
+ Agnostic_CORINFO_LOOKUP_KIND value;
+
+ value.needsRuntimeLookup = (DWORD)result->needsRuntimeLookup;
+ value.runtimeLookupKind = (DWORD)result->runtimeLookupKind;
+ value.runtimeLookupFlags = (WORD)result->runtimeLookupFlags;
+ // We don't store result->runtimeLookupArgs, which is opaque data. Ok?
+
+ GetLocationOfThisType->Add((DWORDLONG)context, value);
+}
+void MethodContext::dmpGetLocationOfThisType(DWORDLONG key, const Agnostic_CORINFO_LOOKUP_KIND& value)
+{
+ printf("GetLocationOfThisType key ftn-%016llX, value nrl-%u rlk-%u", key, value.needsRuntimeLookup, value.runtimeLookupKind);
+}
+CORINFO_LOOKUP_KIND MethodContext::repGetLocationOfThisType(CORINFO_METHOD_HANDLE context)
+{
+ Agnostic_CORINFO_LOOKUP_KIND value1;
+ CORINFO_LOOKUP_KIND value2;
+
+ value1 = GetLocationOfThisType->Get((DWORDLONG)context);
+
+ value2.needsRuntimeLookup = value1.needsRuntimeLookup != 0;
+ value2.runtimeLookupKind = (CORINFO_RUNTIME_LOOKUP_KIND)value1.runtimeLookupKind;
+ value2.runtimeLookupFlags = (WORD)value1.runtimeLookupFlags;
+ value2.runtimeLookupArgs = nullptr; // We don't store this opaque data. Ok?
+
+ return value2;
+}
+
+void MethodContext::recGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs *pCtorData, CORINFO_METHOD_HANDLE result)
+{
+ if (GetDelegateCtor == nullptr)
+ GetDelegateCtor = new LightWeightMap<Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut>();
+
+ Agnostic_GetDelegateCtorIn key;
+ ZeroMemory(&key, sizeof(Agnostic_GetDelegateCtorIn)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetDelegateCtorOut value;
+
+ key.methHnd = (DWORDLONG)methHnd;
+ key.clsHnd = (DWORDLONG)clsHnd;
+ key.targetMethodHnd = (DWORDLONG)targetMethodHnd;
+
+ value.CtorData.pMethod = (DWORDLONG)pCtorData->pMethod;
+ value.CtorData.pArg3 = (DWORDLONG)pCtorData->pArg3;
+ value.CtorData.pArg4 = (DWORDLONG)pCtorData->pArg4;
+ value.CtorData.pArg5 = (DWORDLONG)pCtorData->pArg5;
+ value.result = (DWORDLONG)result;
+
+ GetDelegateCtor->Add(key, value);
+ DEBUG_REC(dmpGetDelegateCtor(key, value));
+}
+void MethodContext::dmpGetDelegateCtor(const Agnostic_GetDelegateCtorIn& key, const Agnostic_GetDelegateCtorOut& value)
+{
+ printf("GetDelegateCtor key ftn-%016llX cls-%016llX tftn-%016llX, value pm-%016llX a3-%016llX a4-%016llX a5-%016llX res-%016llX",
+ key.methHnd,
+ key.clsHnd,
+ key.targetMethodHnd,
+ value.CtorData.pMethod,
+ value.CtorData.pArg3,
+ value.CtorData.pArg4,
+ value.CtorData.pArg5,
+ value.result);
+}
+CORINFO_METHOD_HANDLE MethodContext::repGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs *pCtorData)
+{
+ Agnostic_GetDelegateCtorIn key;
+ ZeroMemory(&key, sizeof(Agnostic_GetDelegateCtorIn)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetDelegateCtorOut value;
+
+ key.methHnd = (DWORDLONG)methHnd;
+ key.clsHnd = (DWORDLONG)clsHnd;
+ key.targetMethodHnd = (DWORDLONG)targetMethodHnd;
+
+ AssertCodeMsg(GetDelegateCtor != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.methHnd);
+ AssertCodeMsg(GetDelegateCtor->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.methHnd);
+ value = GetDelegateCtor->Get(key);
+
+ pCtorData->pMethod = (void*)value.CtorData.pMethod;
+ pCtorData->pArg3 = (void*)value.CtorData.pArg3;
+ pCtorData->pArg4 = (void*)value.CtorData.pArg4;
+ pCtorData->pArg5 = (void*)value.CtorData.pArg5;
+ DEBUG_REP(dmpGetDelegateCtor(key, value));
+ return (CORINFO_METHOD_HANDLE)value.result;
+}
+
+void MethodContext::recGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult)
+{
+ if (GetFunctionFixedEntryPoint == nullptr)
+ GetFunctionFixedEntryPoint = new LightWeightMap<DWORDLONG, Agnostic_CORINFO_CONST_LOOKUP>();
+
+ Agnostic_CORINFO_CONST_LOOKUP value;
+
+ value.accessType = (DWORD)pResult->accessType;
+ value.handle = (DWORDLONG)pResult->handle;
+
+ GetFunctionFixedEntryPoint->Add((DWORDLONG)ftn, value);
+}
+void MethodContext::dmpGetFunctionFixedEntryPoint(DWORDLONG key, const Agnostic_CORINFO_CONST_LOOKUP& value)
+{
+ printf("GetFunctionFixedEntryPoint key ftn-%016llX, value at-%u han-%016llX", key, value.accessType, value.handle);
+}
+void MethodContext::repGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult)
+{
+ Agnostic_CORINFO_CONST_LOOKUP value;
+
+ value = GetFunctionFixedEntryPoint->Get((DWORDLONG)ftn);
+
+ pResult->accessType = (InfoAccessType)value.accessType;
+ pResult->handle = (CORINFO_GENERIC_HANDLE)value.handle;
+}
+
+void MethodContext::recGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num, CORINFO_FIELD_HANDLE result)
+{
+ if (GetFieldInClass == nullptr)
+ GetFieldInClass = new LightWeightMap<DLD, DWORDLONG>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)clsHnd;
+ key.B = (DWORD)num;
+
+ GetFieldInClass->Add(key, (DWORDLONG)result);
+ DEBUG_REC(dmpGetFieldInClass(key, (DWORDLONG)result));
+}
+void MethodContext::dmpGetFieldInClass(DLD key, DWORDLONG value)
+{
+ printf("GetFieldInClass key cls-%016llX ind-%u, value %016llX", key.A, key.B, value);
+}
+CORINFO_FIELD_HANDLE MethodContext::repGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)clsHnd;
+ key.B = (DWORD)num;
+
+ AssertCodeMsg((GetFieldInClass != nullptr) && (GetFieldInClass->GetIndex(key) != -1),
+ EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.A);
+ CORINFO_FIELD_HANDLE temp = (CORINFO_FIELD_HANDLE)GetFieldInClass->Get(key);
+
+ DEBUG_REP(dmpGetFieldInClass(key, (DWORDLONG)temp));
+ return temp;
+}
+
+void MethodContext::recGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE *structType, CORINFO_CLASS_HANDLE memberParent, CorInfoType result)
+{
+ if (GetFieldType == nullptr)
+ GetFieldType = new LightWeightMap<DLDL, DLD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+
+ key.A = (DWORDLONG)field;
+ key.B = (DWORDLONG)memberParent;
+
+ value.A = (DWORDLONG)*structType;
+ value.B = (DWORD)result;
+
+ GetFieldType->Add(key, value);
+ DEBUG_REC(dmpGetFieldType(key, value));
+}
+void MethodContext::dmpGetFieldType(DLDL key, DLD value)
+{
+ printf("GetFieldType key fld-%016llX cls-%016llX, value ch-%016llX cit-%u(%s)", key.A, key.B, value.A, value.B, toString((CorInfoType)value.B));
+}
+CorInfoType MethodContext::repGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE *structType, CORINFO_CLASS_HANDLE memberParent)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+
+ key.A = (DWORDLONG)field;
+ key.B = (DWORDLONG)memberParent;
+
+ AssertCodeMsg(GetFieldType != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.A);
+ AssertCodeMsg(GetFieldType->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.A);
+ value = GetFieldType->Get(key);
+
+
+ if (structType != nullptr)
+ *structType = (CORINFO_CLASS_HANDLE)value.A;
+
+ DEBUG_REP(dmpGetFieldType(key, value));
+ return (CorInfoType)value.B;
+}
+
+void MethodContext::recGetFieldName(CORINFO_FIELD_HANDLE ftn, const char **moduleName, const char* result)
+{
+ if (GetFieldName == nullptr)
+ GetFieldName = new LightWeightMap<DWORDLONG, DD>();
+
+ DD value;
+
+ if (result != nullptr)
+ value.A = GetFieldName->AddBuffer((unsigned char *)result, (DWORD)strlen(result) + 1);
+ else
+ value.A = (DWORD)-1;
+
+ if (moduleName != nullptr) //protect strlen
+ value.B = (DWORD)GetFieldName->AddBuffer((unsigned char *)*moduleName, (DWORD)strlen(*moduleName) + 1);
+ else
+ value.B = (DWORD)-1;
+
+ GetFieldName->Add((DWORDLONG)ftn, value);
+}
+void MethodContext::dmpGetFieldName(DWORDLONG key, DD value)
+{
+ unsigned char *fieldName = (unsigned char *)GetFieldName->GetBuffer(value.A);
+ unsigned char *moduleName = (unsigned char *)GetFieldName->GetBuffer(value.B);
+ printf("GetFieldName key - ftn-%016llX, value fld-'%s', mod-'%s'", key, fieldName, moduleName);
+ GetFieldName->Unlock();
+}
+const char* MethodContext::repGetFieldName(CORINFO_FIELD_HANDLE ftn, const char **moduleName)
+{
+ DD value;
+ if (GetFieldName == nullptr)
+ {
+ if (moduleName != nullptr)
+ *moduleName = "hackishModuleName";
+ return "hackishFieldName";
+ }
+ value = GetFieldName->Get((DWORDLONG)ftn);
+ if (moduleName != nullptr)
+ *moduleName = (const char *)GetFieldName->GetBuffer(value.B);
+ return (const char *)GetFieldName->GetBuffer(value.A);
+}
+
+void MethodContext::recCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (CanInlineTypeCheckWithObjectVTable == nullptr)
+ CanInlineTypeCheckWithObjectVTable = new LightWeightMap<DWORDLONG, DWORD>();
+
+ CanInlineTypeCheckWithObjectVTable->Add((DWORDLONG)cls, (DWORD)result);
+}
+void MethodContext::dmpCanInlineTypeCheckWithObjectVTable(DWORDLONG key, DWORD value)
+{
+ printf("CanInlineTypeCheckWithObjectVTable key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ return (BOOL)CanInlineTypeCheckWithObjectVTable->Get((DWORDLONG)cls);
+}
+
+void MethodContext::recSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method, BOOL result)
+{
+ if (SatisfiesMethodConstraints == nullptr)
+ SatisfiesMethodConstraints = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)parent;
+ key.B = (DWORDLONG)method;
+
+ SatisfiesMethodConstraints->Add(key, (DWORD)result);
+}
+void MethodContext::dmpSatisfiesMethodConstraints(DLDL key, DWORD value)
+{
+ printf("SatisfiesMethodConstraints key cls-%016llX ftn-%016llX, value res-%u", key.A, key.B, value);
+}
+BOOL MethodContext::repSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)parent;
+ key.B = (DWORDLONG)method;
+
+ BOOL value = (BOOL)SatisfiesMethodConstraints->Get(key);
+ return value;
+}
+
+void MethodContext::recInitConstraintsForVerification(CORINFO_METHOD_HANDLE method, BOOL *pfHasCircularClassConstraints,
+ BOOL *pfHasCircularMethodConstraint)
+{
+ if (InitConstraintsForVerification == nullptr)
+ InitConstraintsForVerification = new LightWeightMap<DWORDLONG, DD>();
+
+ DD value;
+
+ value.A = (DWORD)*pfHasCircularClassConstraints;
+ value.B = (DWORD)*pfHasCircularMethodConstraint;
+
+ InitConstraintsForVerification->Add((DWORDLONG)method, value);
+}
+void MethodContext::dmpInitConstraintsForVerification(DWORDLONG key, DD value)
+{
+ printf("InitConstraintsForVerification key ftn-%016llX, value circ-%u cirm-%u", key, value.A, value.B);
+}
+void MethodContext::repInitConstraintsForVerification(CORINFO_METHOD_HANDLE method, BOOL *pfHasCircularClassConstraints,
+ BOOL *pfHasCircularMethodConstraint)
+{
+ DD value;
+
+ value = InitConstraintsForVerification->Get((DWORDLONG)method);
+
+ *pfHasCircularClassConstraints = (BOOL)value.A;
+ *pfHasCircularMethodConstraint = (BOOL)value.B;
+}
+
+void MethodContext::recIsValidStringRef(CORINFO_MODULE_HANDLE module, unsigned metaTOK, BOOL result)
+{
+ if (IsValidStringRef == nullptr)
+ IsValidStringRef = new LightWeightMap<DLD, DWORD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+
+ IsValidStringRef->Add(key, (DWORD)result);
+}
+void MethodContext::dmpIsValidStringRef(DLD key, DWORD value)
+{
+ printf("IsValidStringRef key mod-%016llX tok-%08X, value res-%u", key.A, key.B, value);
+}
+BOOL MethodContext::repIsValidStringRef(CORINFO_MODULE_HANDLE module, unsigned metaTOK)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+
+ BOOL value = (BOOL)IsValidStringRef->Get(key);
+ return value;
+}
+
+void MethodContext::recGetHelperName(CorInfoHelpFunc funcNum, const char* result)
+{
+ if (GetHelperName == nullptr)
+ GetHelperName = new LightWeightMap<DWORD, DWORD>();
+
+ DWORD value = (DWORD)-1;
+ if (result != nullptr)
+ value = (DWORD)GetHelperName->AddBuffer((unsigned char*)result, (DWORD)strlen(result) + 1);
+
+ GetHelperName->Add((DWORD)funcNum, value);
+ DEBUG_REC(dmpGetHelperName((DWORD)funcNum, value));
+}
+void MethodContext::dmpGetHelperName(DWORD key, DWORD value)
+{
+ printf("GetHelperName key ftn-%u, value '%s'", key, (const char*)GetHelperName->GetBuffer(value));
+ GetHelperName->Unlock();
+}
+const char* MethodContext::repGetHelperName(CorInfoHelpFunc funcNum)
+{
+ if (GetHelperName == nullptr) return "Yickish helper name";
+
+ int itemIndex = GetHelperName->GetIndex((DWORD)funcNum);
+ if (itemIndex < 0)
+ {
+ return "hackishHelperName";
+ }
+ else
+ {
+ unsigned int buffIndex = GetHelperName->Get((DWORD)funcNum);
+ DEBUG_REP(dmpGetHelperName((DWORD)funcNum, buffIndex));
+ return (const char*)GetHelperName->GetBuffer(buffIndex);
+ }
+}
+
+
+void MethodContext::recCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent, BOOL result)
+{
+ if (CanCast == nullptr)
+ CanCast = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)child;
+ key.B = (DWORDLONG)parent;
+
+ CanCast->Add(key, (DWORD)result);
+ DEBUG_REC(dmpCanCast(key, (DWORD)result));
+}
+void MethodContext::dmpCanCast(DLDL key, DWORD value)
+{
+ printf("CanCast key chd-%016llX par-%016llX, value res-%u", key.A, key.B, value);
+}
+BOOL MethodContext::repCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)child;
+ key.B = (DWORDLONG)parent;
+
+ AssertCodeMsg(CanCast != nullptr, EXCEPTIONCODE_MC, "Didn't find anything %016llX, %016llX in map", (DWORDLONG)child, (DWORDLONG)parent);
+ AssertCodeMsg(CanCast->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX, %016llX %u in map", (DWORDLONG)child, (DWORDLONG)parent, CanCast->GetCount());
+ BOOL value = (BOOL)CanCast->Get(key);
+ DEBUG_REP(dmpCanCast(key, (DWORD)value));
+ return value;
+}
+
+void MethodContext::recGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE *clsRet, CorInfoType result)
+{
+ if (GetChildType == nullptr)
+ GetChildType = new LightWeightMap<DWORDLONG, DLD>();
+
+ DLD value;
+
+ value.A = (DWORDLONG)*clsRet;
+ value.B = (DWORD)result;
+
+ GetChildType->Add((DWORDLONG)clsHnd, value);
+ DEBUG_REC(dmpGetChildType((DWORDLONG)clsHnd, value));
+}
+void MethodContext::dmpGetChildType(DWORDLONG key, DLD value)
+{
+ printf("GetChildType key cls-%016llX, value clsr-%016llX cit-%u(%s)", key, value.A, value.B, toString((CorInfoType)value.B));
+}
+CorInfoType MethodContext::repGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE *clsRet)
+{
+ DLD value;
+
+ AssertCodeMsg(GetChildType != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)clsHnd);
+ AssertCodeMsg(GetChildType->GetIndex((DWORDLONG)clsHnd) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)clsHnd);
+ value = GetChildType->Get((DWORDLONG)clsHnd);
+
+ *clsRet = (CORINFO_CLASS_HANDLE)value.A;
+ DEBUG_REP(dmpGetChildType((DWORDLONG)clsHnd, value));
+ return (CorInfoType)value.B;
+}
+
+
+void MethodContext::recGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size, void *result)
+{
+ if (GetArrayInitializationData == nullptr)
+ GetArrayInitializationData = new LightWeightMap<DLD, DWORDLONG>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)field;
+ key.B = (DWORD)size;
+
+ GetArrayInitializationData->Add(key, (DWORDLONG)result);
+}
+void MethodContext::dmpGetArrayInitializationData(DLD key, DWORDLONG value)
+{
+ printf("GetArrayInitializationData key field-%016llX size-%08X, value result-%016llX", key.A, key.B, value);
+}
+void *MethodContext::repGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)field;
+ key.B = (DWORD)size;
+
+ void *value = (void*)GetArrayInitializationData->Get(key);
+ return value;
+}
+
+void MethodContext::recFilterException(struct _EXCEPTION_POINTERS *pExceptionPointers, int result)
+{
+ if (FilterException == nullptr)
+ FilterException = new LightWeightMap<DWORD, DWORD>();
+
+ FilterException->Add((DWORD)pExceptionPointers->ExceptionRecord->ExceptionCode, (DWORD)result);
+}
+void MethodContext::dmpFilterException(DWORD key, DWORD value)
+{
+ printf("FilterException key %u, value %u", key, value);
+}
+int MethodContext::repFilterException(struct _EXCEPTION_POINTERS *pExceptionPointers)
+{
+ if (FilterException == nullptr)
+ return EXCEPTION_CONTINUE_SEARCH;
+ if (FilterException->GetIndex((DWORD)pExceptionPointers->ExceptionRecord->ExceptionCode) < 0)
+ return EXCEPTION_CONTINUE_SEARCH;
+ else
+ {
+ int result = FilterException->Get((DWORD)pExceptionPointers->ExceptionRecord->ExceptionCode);
+ return result;
+ }
+
+}
+
+void MethodContext::recHandleException(struct _EXCEPTION_POINTERS *pExceptionPointers)
+{
+ if (HandleException == nullptr)
+ HandleException = new DenseLightWeightMap<DWORD>();
+
+ HandleException->Append(pExceptionPointers->ExceptionRecord->ExceptionCode);
+}
+void MethodContext::dmpHandleException(DWORD key, DWORD value)
+{
+ printf("HandleException key %u, value %u", key, value);
+}
+
+void MethodContext::recGetAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method, void **ppIndirection, void* result)
+{
+ if (GetAddressOfPInvokeFixup == nullptr)
+ GetAddressOfPInvokeFixup = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetAddressOfPInvokeFixup->Add((DWORDLONG)method, value);
+}
+void MethodContext::dmpGetAddressOfPInvokeFixup(DWORDLONG key, DLDL value)
+{
+ printf("GetAddressOfPInvokeFixup key ftn-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+void* MethodContext::repGetAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method, void **ppIndirection)
+{
+ DLDL value;
+
+ value = GetAddressOfPInvokeFixup->Get((DWORDLONG)method);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+ return (void*)value.B;
+}
+void MethodContext::recGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP *pLookup)
+{
+ if (GetAddressOfPInvokeTarget == nullptr)
+ GetAddressOfPInvokeTarget = new LightWeightMap<DWORDLONG, DLD>();
+
+ DLD value;
+
+ value.A = (DWORDLONG)pLookup->addr;
+ value.B = (DWORD)pLookup->accessType;
+
+ GetAddressOfPInvokeTarget->Add((DWORDLONG)method, value);
+}
+void MethodContext::dmpGetAddressOfPInvokeTarget(DWORDLONG key, DLD value)
+{
+ printf("GetAddressOfPInvokeTarget key ftn-%016llX, value addr-%016llX at-%u", key, value.A, value.B);
+}
+void MethodContext::repGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP *pLookup)
+{
+ DLD value = GetAddressOfPInvokeTarget->Get((DWORDLONG)method);
+
+ pLookup->addr = (void *)value.A;
+ pLookup->accessType = (InfoAccessType)value.B;
+}
+
+
+void MethodContext::recSatisfiesClassConstraints(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (SatisfiesClassConstraints == nullptr)
+ SatisfiesClassConstraints = new LightWeightMap<DWORDLONG, DWORD>();
+
+ SatisfiesClassConstraints->Add((DWORDLONG)cls, (DWORD)result);
+}
+void MethodContext::dmpSatisfiesClassConstraints(DWORDLONG key, DWORD value)
+{
+ printf("SatisfiesClassConstraints key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repSatisfiesClassConstraints(CORINFO_CLASS_HANDLE cls)
+{
+ return (BOOL)SatisfiesClassConstraints->Get((DWORDLONG)cls);
+}
+
+void MethodContext::recGetMethodHash(CORINFO_METHOD_HANDLE ftn, unsigned result)
+{
+ if (GetMethodHash == nullptr)
+ GetMethodHash = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetMethodHash->Add((DWORDLONG)ftn, (DWORD)result);
+ DEBUG_REC(dmpGetMethodHash((DWORDLONG)ftn, (DWORD) result));
+}
+void MethodContext::dmpGetMethodHash(DWORDLONG key, DWORD value)
+{
+ printf("GetMethodHash key %016llX, value %u", key, value);
+}
+unsigned MethodContext::repGetMethodHash(CORINFO_METHOD_HANDLE ftn)
+{
+ unsigned result = 0x43;
+ if (GetMethodHash != nullptr)
+ if (GetMethodHash->GetIndex((DWORDLONG)ftn) >= 0)
+ result = GetMethodHash->Get((DWORDLONG)ftn);
+ DEBUG_REP(dmpGetMethodHash((DWORDLONG)ftn, (DWORD) result));
+ return result;
+}
+
+void MethodContext::recCanTailCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE declaredCalleeHnd,
+ CORINFO_METHOD_HANDLE exactCalleeHnd, bool fIsTailPrefix, bool result)
+{
+ if (CanTailCall == nullptr)
+ CanTailCall = new LightWeightMap<Agnostic_CanTailCall, DWORD>();
+
+ Agnostic_CanTailCall key;
+ ZeroMemory(&key, sizeof(Agnostic_CanTailCall)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callerHnd = (DWORDLONG)callerHnd;
+ key.declaredCalleeHnd = (DWORDLONG)declaredCalleeHnd;
+ key.exactCalleeHnd = (DWORDLONG)exactCalleeHnd;
+ key.fIsTailPrefix = (DWORD)fIsTailPrefix;
+
+ CanTailCall->Add(key, (DWORD)result);
+ DEBUG_REC(dmpCanTailCall(key,(DWORD)result));
+}
+void MethodContext::dmpCanTailCall(const Agnostic_CanTailCall& key, DWORD value)
+{
+ printf("CanTailCall key clr-%016llX dcle-%016llX ecle-%016llX pfx-%u, value res-%u",
+ key.callerHnd,
+ key.declaredCalleeHnd,
+ key.exactCalleeHnd,
+ key.fIsTailPrefix,
+ value);
+}
+bool MethodContext::repCanTailCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE declaredCalleeHnd, CORINFO_METHOD_HANDLE exactCalleeHnd,
+ bool fIsTailPrefix)
+{
+ Agnostic_CanTailCall key;
+ ZeroMemory(&key, sizeof(Agnostic_CanTailCall)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callerHnd = (DWORDLONG)callerHnd;
+ key.declaredCalleeHnd = (DWORDLONG)declaredCalleeHnd;
+ key.exactCalleeHnd = (DWORDLONG)exactCalleeHnd;
+ key.fIsTailPrefix = (DWORD)fIsTailPrefix;
+
+ AssertCodeMsg(CanTailCall != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.callerHnd);
+ AssertCodeMsg(CanTailCall->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.callerHnd);
+ bool temp = CanTailCall->Get(key) != 0;
+ DEBUG_REP(dmpCanTailCall(key,(DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recIsCompatibleDelegate(CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls,
+ CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, BOOL *pfIsOpenDelegate, BOOL result)
+{
+ if (IsCompatibleDelegate == nullptr)
+ IsCompatibleDelegate = new LightWeightMap<Agnostic_IsCompatibleDelegate, DD>();
+ Agnostic_IsCompatibleDelegate key;
+ ZeroMemory(&key, sizeof(Agnostic_IsCompatibleDelegate)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DD value;
+
+ key.objCls = (DWORDLONG)objCls;
+ key.methodParentCls = (DWORDLONG)methodParentCls;
+ key.method = (DWORDLONG)method;
+ key.delegateCls = (DWORDLONG)delegateCls;
+
+ value.A = (DWORD)*pfIsOpenDelegate;
+ value.B = (DWORD)result;
+
+ IsCompatibleDelegate->Add(key, value);
+}
+void MethodContext::dmpIsCompatibleDelegate(const Agnostic_IsCompatibleDelegate& key, DD value)
+{
+ printf("IsCompatibleDelegate key objCls-%016llX methodParentCls-%016llX method-%016llX delegateCls-%016llX, value pfIsOpenDelegate-%08X result-%08X",
+ key.objCls, key.methodParentCls, key.method, key.delegateCls, value.A, value.B);
+}
+BOOL MethodContext::repIsCompatibleDelegate(CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls,
+ CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, BOOL *pfIsOpenDelegate)
+{
+ Agnostic_IsCompatibleDelegate key;
+ ZeroMemory(&key, sizeof(Agnostic_IsCompatibleDelegate)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DD value;
+
+ key.objCls = (DWORDLONG)objCls;
+ key.methodParentCls = (DWORDLONG)methodParentCls;
+ key.method = (DWORDLONG)method;
+ key.delegateCls = (DWORDLONG)delegateCls;
+
+ value = IsCompatibleDelegate->Get(key);
+
+ *pfIsOpenDelegate = (BOOL)value.A;
+ return (BOOL)value.B;
+}
+
+void MethodContext::recIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd, BOOL result)
+{
+ if (IsDelegateCreationAllowed == nullptr)
+ IsDelegateCreationAllowed = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(key));
+ DWORD value;
+
+ key.A = (DWORDLONG)delegateHnd;
+ key.B = (DWORDLONG)calleeHnd;
+
+ value = (DWORD)result;
+
+ IsDelegateCreationAllowed->Add(key, value);
+}
+void MethodContext::dmpIsDelegateCreationAllowed(DLDL key, DWORD value)
+{
+ printf("IsDelegateCreationAllowed key delegateHnd-%016llX calleeHnd-%016llX result-%08X",
+ key.A, key.B, value);
+}
+BOOL MethodContext::repIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(key));
+ DWORD value;
+
+ key.A = (DWORDLONG)delegateHnd;
+ key.B = (DWORDLONG)calleeHnd;
+
+ value = IsDelegateCreationAllowed->Get(key);
+
+ return (BOOL)value;
+}
+
+void MethodContext::recCanSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHandle, BOOL skip, CorInfoCanSkipVerificationResult result)
+{
+ if (CanSkipMethodVerification == nullptr)
+ CanSkipMethodVerification = new LightWeightMap<DLD, DWORD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)ftnHandle;
+ key.B = (DWORD)skip;
+
+ CanSkipMethodVerification->Add(key, (DWORD)result);
+ DEBUG_REC(dmpCanSkipMethodVerification(key,(DWORD)result));
+}
+void MethodContext::dmpCanSkipMethodVerification(DLD key, DWORD value)
+{
+ printf("CanSkipMethodVerification key ftn-%016llX skp-%u, value res-%u", key.A, key.B, value);
+}
+CorInfoCanSkipVerificationResult MethodContext::repCanSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHandle, BOOL skip)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)ftnHandle;
+ key.B = (DWORD)skip;
+
+ AssertCodeMsg(CanSkipMethodVerification != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)ftnHandle);
+ AssertCodeMsg(CanSkipMethodVerification->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)ftnHandle);
+
+ CorInfoCanSkipVerificationResult temp = (CorInfoCanSkipVerificationResult)CanSkipMethodVerification->Get(key);
+ DEBUG_REP(dmpCanSkipMethodVerification(key,(DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recFindCallSiteSig(CORINFO_MODULE_HANDLE module, unsigned methTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig)
+{
+ if (FindCallSiteSig == nullptr)
+ FindCallSiteSig = new LightWeightMap<Agnostic_FindCallSiteSig, Agnostic_CORINFO_SIG_INFO>();
+
+ Agnostic_FindCallSiteSig key;
+ ZeroMemory(&key, sizeof(Agnostic_FindCallSiteSig)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.module = (DWORDLONG)module;
+ key.methTok = (DWORD)methTOK;
+ key.context = (DWORDLONG)context;
+
+ value.callConv = (DWORD)sig->callConv;
+ value.retTypeClass = (DWORDLONG)sig->retTypeClass;
+ value.retTypeSigClass = (DWORDLONG)sig->retTypeSigClass;
+ value.retType = (DWORD)sig->retType;
+ value.flags = (DWORD)sig->flags;
+ value.numArgs = (DWORD)sig->numArgs;
+ value.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ value.sigInst_classInst_Index = FindCallSiteSig->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8); //porting issue
+ value.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ value.sigInst_methInst_Index = FindCallSiteSig->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8); //porting issue
+ value.args = (DWORDLONG)sig->args;
+ value.cbSig = (DWORD)sig->cbSig;
+ value.pSig = (DWORD)FindCallSiteSig->AddBuffer((unsigned char *)sig->pSig, sig->cbSig);
+ value.scope = (DWORDLONG)sig->scope;
+ value.token = (DWORD)sig->token;
+
+ FindCallSiteSig->Add(key, value);
+ DEBUG_REC(dmpFindCallSiteSig(key,value));
+}
+void MethodContext::dmpFindCallSiteSig(const Agnostic_FindCallSiteSig& key, const Agnostic_CORINFO_SIG_INFO& value)
+{
+ printf("dmpFindCallSiteSig key module-%016llX methTok-%08X context-%016llX", key.module, key.methTok, key.context);
+ printf(", value callConv-%08X retTypeClass-%016llX retTypeSigClass-%016llX retType-%u(%s) flags-%08X numArgs-%08X classInstCount-%08X classInd-%08X "
+ "methInstCount-%08X methInd-%08X args-%016llX cbSig-%08X pSig-%08X scope-%016llX token-%08X",
+ value.callConv,
+ value.retTypeClass,
+ value.retTypeSigClass,
+ value.retType,
+ toString((CorInfoType)value.retType),
+ value.flags,
+ value.numArgs,
+ value.sigInst_classInstCount,
+ value.sigInst_classInst_Index,
+ value.sigInst_methInstCount,
+ value.sigInst_methInst_Index,
+ value.args,
+ value.cbSig,
+ value.pSig,
+ value.scope,
+ value.token);
+}
+void MethodContext::repFindCallSiteSig(CORINFO_MODULE_HANDLE module, unsigned methTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig)
+{
+ Agnostic_FindCallSiteSig key;
+ ZeroMemory(&key, sizeof(Agnostic_FindCallSiteSig)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.module = (DWORDLONG)module;
+ key.methTok = (DWORD)methTOK;
+ key.context = (DWORDLONG)context;
+
+ AssertCodeMsg(FindCallSiteSig != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %08X", (DWORD)key.methTok);
+ AssertCodeMsg(FindCallSiteSig->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %08X", (DWORD)key.methTok);
+ value = FindCallSiteSig->Get(key);
+
+ sig->callConv = (CorInfoCallConv)value.callConv;
+ sig->retTypeClass = (CORINFO_CLASS_HANDLE)value.retTypeClass;
+ sig->retTypeSigClass = (CORINFO_CLASS_HANDLE)value.retTypeSigClass;
+ sig->retType = (CorInfoType)value.retType;
+ sig->flags = (unsigned)value.flags;
+ sig->numArgs = (unsigned)value.numArgs;
+ sig->sigInst.classInstCount = (unsigned)value.sigInst_classInstCount;
+ sig->sigInst.classInst = (CORINFO_CLASS_HANDLE*)FindCallSiteSig->GetBuffer(value.sigInst_classInst_Index);
+ sig->sigInst.methInstCount = (unsigned)value.sigInst_methInstCount;
+ sig->sigInst.methInst = (CORINFO_CLASS_HANDLE*)FindCallSiteSig->GetBuffer(value.sigInst_methInst_Index);
+ sig->args = (CORINFO_ARG_LIST_HANDLE)value.args;
+ sig->cbSig = (unsigned int)value.cbSig;
+ sig->pSig = (PCCOR_SIGNATURE)FindCallSiteSig->GetBuffer(value.pSig);
+ sig->scope = (CORINFO_MODULE_HANDLE)value.scope;
+ sig->token = (mdToken)value.token;
+ DEBUG_REP(dmpFindCallSiteSig(key,value));
+}
+
+void MethodContext::recShouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope, BOOL result)
+{
+ if (ShouldEnforceCallvirtRestriction == nullptr)
+ ShouldEnforceCallvirtRestriction = new LightWeightMap<DWORDLONG, DWORD>();
+ ShouldEnforceCallvirtRestriction->Add((DWORDLONG)scope, (DWORD)result);
+ DEBUG_REC(dmpShouldEnforceCallvirtRestriction((DWORDLONG)scope, (DWORD)result));
+}
+void MethodContext::dmpShouldEnforceCallvirtRestriction(DWORDLONG key, DWORD value)
+{
+ printf("ShouldEnforceCallvirtRestriction key %016llX, value %u", key, value);
+}
+BOOL MethodContext::repShouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope)
+{
+ AssertCodeMsg(ShouldEnforceCallvirtRestriction != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)scope);
+ AssertCodeMsg(ShouldEnforceCallvirtRestriction->GetIndex((DWORDLONG)scope) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)scope);
+ BOOL temp = (BOOL)ShouldEnforceCallvirtRestriction->Get((DWORDLONG)scope);
+ DEBUG_REC(dmpShouldEnforceCallvirtRestriction((DWORDLONG)scope, (DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recGetMethodSync(CORINFO_METHOD_HANDLE ftn, void **ppIndirection, void* result)
+{
+ if (GetMethodSync == nullptr)
+ GetMethodSync = new LightWeightMap<DWORDLONG, DLDL>();
+ DLDL value;
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetMethodSync->Add((DWORDLONG)ftn, value);
+}
+void MethodContext::dmpGetMethodSync(DWORDLONG key, DLDL value)
+{
+ printf("GetMethodSync key %016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+void* MethodContext::repGetMethodSync(CORINFO_METHOD_HANDLE ftn, void **ppIndirection)
+{
+ DLDL value;
+
+ value = (DLDL)GetMethodSync->Get((DWORDLONG)ftn);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+
+ return (void*)value.B;
+}
+
+void MethodContext::recGetVarArgsHandle(CORINFO_SIG_INFO *pSig, void **ppIndirection, CORINFO_VARARGS_HANDLE result)
+{
+ if (GetVarArgsHandle == nullptr)
+ GetVarArgsHandle = new LightWeightMap<Agnostic_CORINFO_SIG_INFO, DLDL>();
+
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLDL value;
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)pSig->cbSig;
+ key.pSig = (DWORD)GetVarArgsHandle->AddBuffer((unsigned char *)pSig->pSig, pSig->cbSig);
+ key.scope = (DWORDLONG)pSig->scope;
+ key.token = (DWORD)pSig->token;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetVarArgsHandle->Add(key, value);
+}
+void MethodContext::dmpGetVarArgsHandle(const Agnostic_CORINFO_SIG_INFO& key, DLDL value)
+{
+ printf("GetVarArgsHandle key cbSig-%08X pSig-%08X scope-%016llX token-%08X",
+ key.cbSig,
+ key.pSig,
+ key.scope,
+ key.token);
+ printf(", value ppIndirection-%016llX result-%016llX", value.A, value.B);
+}
+CORINFO_VARARGS_HANDLE MethodContext::repGetVarArgsHandle(CORINFO_SIG_INFO *pSig, void **ppIndirection)
+{
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLDL value;
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)pSig->cbSig;
+ key.pSig = (DWORD)GetVarArgsHandle->Contains((unsigned char *)pSig->pSig, pSig->cbSig);
+ key.scope = (DWORDLONG)pSig->scope;
+ key.token = (DWORD)pSig->token;
+
+ value = (DLDL)GetVarArgsHandle->Get(key);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+
+ return (CORINFO_VARARGS_HANDLE)value.B;
+}
+
+void MethodContext::recCanGetVarArgsHandle(CORINFO_SIG_INFO *pSig, bool result)
+{
+ if (CanGetVarArgsHandle == nullptr)
+ CanGetVarArgsHandle = new LightWeightMap<Agnostic_CORINFO_SIG_INFO, DWORD>();
+
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)0;
+ key.pSig = (DWORD)0;
+ key.scope = (DWORDLONG)pSig->scope;
+ key.token = (DWORD)pSig->token;
+
+ CanGetVarArgsHandle->Add(key, (DWORD)result);
+ DEBUG_REC(dmpCanGetVarArgsHandle(key, (DWORD)result));
+}
+void MethodContext::dmpCanGetVarArgsHandle(const Agnostic_CORINFO_SIG_INFO& key, DWORD value)
+{
+ printf("CanGetVarArgsHandle key scope-%016llX token-%08X, value result-%08X", key.scope, key.token, value);
+}
+bool MethodContext::repCanGetVarArgsHandle(CORINFO_SIG_INFO *pSig)
+{
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)0;
+ key.pSig = (DWORD)0;
+ key.scope = (DWORDLONG)pSig->scope;
+ key.token = (DWORD)pSig->token;
+
+ AssertCodeMsg(CanGetVarArgsHandle != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.token);
+ AssertCodeMsg(CanGetVarArgsHandle->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.token);
+ bool value = CanGetVarArgsHandle->Get(key) != 0;
+ DEBUG_REP(dmpCanGetVarArgsHandle(key, (DWORD)value));
+ return value;
+}
+
+void MethodContext::recGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void **ppIndirection, DWORD result)
+{
+ if (GetFieldThreadLocalStoreID == nullptr)
+ GetFieldThreadLocalStoreID = new LightWeightMap<DWORDLONG, DLD>();;
+
+ DLD value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORD)result;
+
+ GetFieldThreadLocalStoreID->Add((DWORDLONG)field, value);
+}
+void MethodContext::dmpGetFieldThreadLocalStoreID(DWORDLONG key, DLD value)
+{
+ printf("GetFieldThreadLocalStoreID key field-%016llX, value ppIndirection-%016llX result-%08X", key, value.A, value.B);
+}
+DWORD MethodContext::repGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void **ppIndirection)
+{
+ DLD value;
+ value = (DLD)GetFieldThreadLocalStoreID->Get((DWORDLONG)field);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+ return (DWORD)value.B;
+}
+
+void MethodContext::recGetBBProfileData(CORINFO_METHOD_HANDLE ftnHnd, ULONG *count, ICorJitInfo::ProfileBuffer **profileBuffer,
+ ULONG *numRuns, HRESULT result)
+{
+ if (GetBBProfileData == nullptr)
+ GetBBProfileData = new LightWeightMap<DWORDLONG, Agnostic_GetBBProfileData>();
+
+ Agnostic_GetBBProfileData value;
+
+ value.count = (DWORD)*count;
+ value.profileBuffer_index = GetBBProfileData->AddBuffer((unsigned char*)*profileBuffer, sizeof(ICorJitInfo::ProfileBuffer)*(*count));
+ value.numRuns = (DWORD)*numRuns;
+ value.result = (DWORD)result;
+
+ GetBBProfileData->Add((DWORDLONG)ftnHnd, value);
+}
+void MethodContext::dmpGetBBProfileData(DWORDLONG key, const Agnostic_GetBBProfileData& value)
+{
+ printf("GetBBProfileData key ftn-%016llX, value cnt-%u profileBuf-", key, value.count);
+ ICorJitInfo::ProfileBuffer* pBuf = (ICorJitInfo::ProfileBuffer *)GetBBProfileData->GetBuffer(value.profileBuffer_index);
+ for (DWORD i = 0; i < value.count; i++, pBuf++)
+ {
+ printf("{il-%u,cnt-%u}", pBuf->ILOffset, pBuf->ExecutionCount);
+ }
+ GetBBProfileData->Unlock();
+ printf(" numRuns-%u result-%u", value.numRuns, value.result);
+}
+HRESULT MethodContext::repGetBBProfileData(CORINFO_METHOD_HANDLE ftnHnd, ULONG *count, ICorJitInfo::ProfileBuffer **profileBuffer,
+ ULONG *numRuns)
+{
+ Agnostic_GetBBProfileData tempValue;
+
+ tempValue = GetBBProfileData->Get((DWORDLONG)ftnHnd);
+
+ *count = (ULONG)tempValue.count;
+ *profileBuffer = (ICorJitInfo::ProfileBuffer *)GetBBProfileData->GetBuffer(tempValue.profileBuffer_index);
+ *numRuns = (ULONG)tempValue.numRuns;
+ HRESULT result = (HRESULT)tempValue.result;
+ return result;
+}
+
+void MethodContext::recMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, CORINFO_CLASS_HANDLE result)
+{
+ if (MergeClasses == nullptr)
+ MergeClasses = new LightWeightMap<DLDL, DWORDLONG>();
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)cls1;
+ key.B = (DWORDLONG)cls2;
+
+ MergeClasses->Add(key, (DWORDLONG)result);
+}
+void MethodContext::dmpMergeClasses(DLDL key, DWORDLONG value)
+{
+ printf("MergeClasses NYI");
+}
+CORINFO_CLASS_HANDLE MethodContext::repMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DWORDLONG value;
+
+ key.A = (DWORDLONG)cls1;
+ key.B = (DWORDLONG)cls2;
+
+ AssertCodeMsg(MergeClasses->GetIndex(key) != -1, EXCEPTIONCODE_MC,
+ "Didn't find %016llX %016llX", (DWORDLONG)cls1, (DWORDLONG)cls2);
+ value = MergeClasses->Get(key);
+
+ return (CORINFO_CLASS_HANDLE)value;
+}
+
+void MethodContext::recGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection, LPVOID result)
+{
+ if (GetCookieForPInvokeCalliSig == nullptr)
+ GetCookieForPInvokeCalliSig = new LightWeightMap<Agnostic_CORINFO_SIG_INFO, DLDL>();
+
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLDL value;
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)szMetaSig->cbSig;
+ key.pSig = (DWORD)GetCookieForPInvokeCalliSig->AddBuffer((unsigned char *)szMetaSig->pSig, szMetaSig->cbSig);
+ key.scope = (DWORDLONG)szMetaSig->scope;
+ key.token = (DWORD)szMetaSig->token;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetCookieForPInvokeCalliSig->Add(key, value);
+}
+void MethodContext::dmpGetCookieForPInvokeCalliSig(const Agnostic_CORINFO_SIG_INFO& key, DLDL value)
+{
+ printf("GetCookieForPInvokeCalliSig NYI");
+}
+LPVOID MethodContext::repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection)
+{
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLDL value;
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)szMetaSig->cbSig;
+ key.pSig = (DWORD)GetCookieForPInvokeCalliSig->Contains((unsigned char *)szMetaSig->pSig, szMetaSig->cbSig);
+ key.scope = (DWORDLONG)szMetaSig->scope;
+ key.token = (DWORD)szMetaSig->token;
+
+ value = (DLDL)GetCookieForPInvokeCalliSig->Get(key);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+
+ return (CORINFO_VARARGS_HANDLE)value.B;
+}
+
+void MethodContext::recCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, bool result)
+{
+ if (CanGetCookieForPInvokeCalliSig == nullptr)
+ CanGetCookieForPInvokeCalliSig = new LightWeightMap<Agnostic_CORINFO_SIG_INFO, DWORD>();
+
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)0;
+ key.pSig = (DWORD)0;
+ key.scope = (DWORDLONG)szMetaSig->scope;
+ key.token = (DWORD)szMetaSig->token;
+
+ CanGetCookieForPInvokeCalliSig->Add(key, (DWORD)result);
+}
+void MethodContext::dmpCanGetCookieForPInvokeCalliSig(const Agnostic_CORINFO_SIG_INFO& key, DWORD value)
+{
+ printf("CanGetCookieForPInvokeCalliSig key scope-%016llX token-%08X, value result-%08X", key.scope, key.token, value);
+}
+bool MethodContext::repCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig)
+{
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)0;
+ key.pSig = (DWORD)0;
+ key.scope = (DWORDLONG)szMetaSig->scope;
+ key.token = (DWORD)szMetaSig->token;
+
+ DWORD temp = CanGetCookieForPInvokeCalliSig->Get(key);
+ return temp != 0;
+}
+
+void MethodContext::recCanAccessFamily(CORINFO_METHOD_HANDLE hCaller, CORINFO_CLASS_HANDLE hInstanceType, BOOL result)
+{
+ if (CanAccessFamily == nullptr)
+ CanAccessFamily = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)hCaller;
+ key.B = (DWORDLONG)hInstanceType;
+
+ CanAccessFamily->Add(key, (DWORD)result);
+}
+void MethodContext::dmpCanAccessFamily(DLDL key, DWORD value)
+{
+ printf("CanAccessFamily key cal-%016llX inst-%016llX, value %u", key.A, key.B, value);
+}
+BOOL MethodContext::repCanAccessFamily(CORINFO_METHOD_HANDLE hCaller, CORINFO_CLASS_HANDLE hInstanceType)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)hCaller;
+ key.B = (DWORDLONG)hInstanceType;
+
+ DWORD temp = CanAccessFamily->Get(key);
+ return (BOOL)temp;
+}
+
+void MethodContext::recErrorList(const char *error)
+{
+ if (ErrorList == nullptr)
+ ErrorList = new DenseLightWeightMap<DWORD>();
+
+ DWORD temp = (DWORD)-1;
+
+ if (error != nullptr)
+ temp = (DWORD)ErrorList->AddBuffer((unsigned char *)error, (DWORD)strlen(error) + 1);
+
+ ErrorList->Append(temp);
+}
+void MethodContext::dmpErrorList(DWORD key, DWORD value)
+{
+ printf("ErrorList NYI");
+}
+
+void MethodContext::recGetProfilingHandle(BOOL *pbHookFunction, void **pProfilerHandle, BOOL *pbIndirectedHandles)
+{
+ if (GetProfilingHandle == nullptr)
+ GetProfilingHandle = new LightWeightMap<DWORD, Agnostic_GetProfilingHandle>();
+
+ Agnostic_GetProfilingHandle value;
+ ZeroMemory(&value, sizeof(Agnostic_GetProfilingHandle)); //We use the input structs as a value and use memcmp to compare.. so we need to zero out padding too
+
+ value.bHookFunction = (DWORD)*pbHookFunction;
+ value.ProfilerHandle = (DWORDLONG)*pProfilerHandle;
+ value.bIndirectedHandles = (DWORD)*pbIndirectedHandles;
+ GetProfilingHandle->Add((DWORD)0, value);
+ DEBUG_REC(dmpGetProfilingHandle(0, value));
+}
+void MethodContext::dmpGetProfilingHandle(DWORD key, const Agnostic_GetProfilingHandle& value)
+{
+ printf("GetProfilingHandle key %u, value bHookFtn-%u profHnd-%016llX bIndHnd-%u",
+ key, value.bHookFunction, value.ProfilerHandle, value.bIndirectedHandles);
+}
+void MethodContext::repGetProfilingHandle(BOOL *pbHookFunction, void **pProfilerHandle, BOOL *pbIndirectedHandles)
+{
+ Agnostic_GetProfilingHandle value;
+
+ value = GetProfilingHandle->Get((DWORD)0);
+
+ *pbHookFunction = (BOOL)value.bHookFunction;
+ *pProfilerHandle = (void*)value.ProfilerHandle;
+ *pbIndirectedHandles = (BOOL)value.bIndirectedHandles;
+ DEBUG_REP(dmpGetProfilingHandle(0, value));
+}
+
+void MethodContext::recEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void **ppIndirection, CORINFO_FIELD_HANDLE result)
+{
+ if (EmbedFieldHandle == nullptr)
+ EmbedFieldHandle = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ EmbedFieldHandle->Add((DWORDLONG)handle, value);
+}
+void MethodContext::dmpEmbedFieldHandle(DWORDLONG key, DLDL value)
+{
+ printf("EmbedFieldHandle NYI");
+}
+CORINFO_FIELD_HANDLE MethodContext::repEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void **ppIndirection)
+{
+ DLDL value;
+
+ value = EmbedFieldHandle->Get((DWORDLONG)handle);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ return (CORINFO_FIELD_HANDLE)value.B;
+}
+
+void MethodContext::recAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, BOOL result)
+{
+ if (AreTypesEquivalent == nullptr)
+ AreTypesEquivalent = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)cls1;
+ key.B = (DWORDLONG)cls2;
+
+ AreTypesEquivalent->Add(key, (DWORD)result);
+}
+void MethodContext::dmpAreTypesEquivalent(DLDL key, DWORD value)
+{
+ printf("AreTypesEquivalent NYI");
+}
+BOOL MethodContext::repAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)cls1;
+ key.B = (DWORDLONG)cls2;
+
+ AssertCodeMsg(AreTypesEquivalent->GetIndex(key) != -1, EXCEPTIONCODE_MC,
+ "Didn't find %016llX %016llX", (DWORDLONG)cls1, (DWORDLONG)cls2);
+ BOOL value = (BOOL)AreTypesEquivalent->Get(key);
+ return value;
+}
+
+void MethodContext::recFindNameOfToken(CORINFO_MODULE_HANDLE module, mdToken metaTOK, char * szFQName, size_t FQNameCapacity, size_t result)
+{
+ if (FindNameOfToken == nullptr)
+ FindNameOfToken = new LightWeightMap<DLD, DLD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+
+ value.A = result;
+ value.B = FindNameOfToken->AddBuffer((unsigned char*)szFQName, (unsigned int)result);
+
+ FindNameOfToken->Add(key, value);
+ DEBUG_REC(dmpFindNameOfToken(key, value));
+}
+void MethodContext::dmpFindNameOfToken(DLD key, DLD value)
+{
+ //practically the name of a token wont be bigger than 4gb...
+ unsigned char *buff = new unsigned char[(unsigned int)value.A + 1];
+ ZeroMemory(buff, (unsigned int)value.A + 1);
+ memcpy(buff, FindNameOfToken->GetBuffer(value.B), (unsigned int)value.A);
+ FindNameOfToken->Unlock();
+ printf("FindNameOfToken key mod-%016llX tok-%08X, value '%s'", key.A, key.B, buff);
+ delete[]buff;
+}
+size_t MethodContext::repFindNameOfToken(CORINFO_MODULE_HANDLE module, mdToken metaTOK, char * szFQName, size_t FQNameCapacity)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+
+ value = FindNameOfToken->Get(key);
+
+ unsigned char* temp = nullptr;
+ if (value.B != (DWORD)-1)
+ {
+ temp = FindNameOfToken->GetBuffer(value.B);
+ memcpy(szFQName, temp, (size_t)value.A);
+ }
+
+ DEBUG_REP(dmpFindNameOfToken(key, value));
+ return (size_t)value.A;
+}
+
+void MethodContext::recGetSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr, bool result)
+{
+ if (GetSystemVAmd64PassStructInRegisterDescriptor == nullptr)
+ GetSystemVAmd64PassStructInRegisterDescriptor = new LightWeightMap<DWORDLONG, Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor>();
+
+ DWORDLONG key;
+ Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor value;
+
+ key = (DWORDLONG)structHnd;
+
+ value.passedInRegisters = (DWORD)structPassInRegDescPtr->passedInRegisters;
+ value.eightByteCount = (DWORD)structPassInRegDescPtr->eightByteCount;
+ for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
+ {
+ value.eightByteClassifications[i] = (DWORD)structPassInRegDescPtr->eightByteClassifications[i];
+ value.eightByteSizes[i] = (DWORD)structPassInRegDescPtr->eightByteSizes[i];
+ value.eightByteOffsets[i] = (DWORD)structPassInRegDescPtr->eightByteOffsets[i];
+ }
+ value.result = result ? 1 : 0;
+
+ GetSystemVAmd64PassStructInRegisterDescriptor->Add(key, value);
+ DEBUG_REC(dmpGetSystemVAmd64PassStructInRegisterDescriptor(key, value));
+}
+void MethodContext::dmpGetSystemVAmd64PassStructInRegisterDescriptor(DWORDLONG key, const Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor& value)
+{
+ printf("GetSystemVAmd64PassStructInRegisterDescriptor key structHnd-%016llX, value passInReg-%u 8bCount-%u", key, value.passedInRegisters, value.eightByteCount);
+ for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
+ {
+ printf(" 8bClass[%u]-%u 8bSz[%u]-%u 8bOff[%u]-%u",
+ i, value.eightByteClassifications[i],
+ i, value.eightByteSizes[i],
+ i, value.eightByteOffsets[i]);
+ }
+ printf(" result %u", value.result);
+}
+bool MethodContext::repGetSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
+{
+ DWORDLONG key;
+ Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor value;
+
+ key = (DWORDLONG)structHnd;
+
+ value = GetSystemVAmd64PassStructInRegisterDescriptor->Get(key);
+
+ structPassInRegDescPtr->passedInRegisters = value.passedInRegisters ? true : false;
+ structPassInRegDescPtr->eightByteCount = (unsigned __int8)value.eightByteCount;
+ for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
+ {
+ structPassInRegDescPtr->eightByteClassifications[i] = (SystemVClassificationType)value.eightByteClassifications[i];
+ structPassInRegDescPtr->eightByteSizes[i] = (unsigned __int8)value.eightByteSizes[i];
+ structPassInRegDescPtr->eightByteOffsets[i] = (unsigned __int8)value.eightByteOffsets[i];
+ }
+
+ DEBUG_REP(dmpGetSystemVAmd64PassStructInRegisterDescriptor(key, value));
+ return value.result ? true : false;
+}
+
+void MethodContext::recGetRelocTypeHint(void * target, WORD result)
+{
+ if (GetRelocTypeHint == nullptr)
+ GetRelocTypeHint = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetRelocTypeHint->Add((DWORDLONG)target, (DWORD)result);
+ DEBUG_REC(dmpGetRelocTypeHint((DWORDLONG)target, (DWORD)result));
+}
+void MethodContext::dmpGetRelocTypeHint(DWORDLONG key, DWORD value)
+{
+ printf("GetRelocTypeHint key tgt-%016llX, value hint-%u", key, value);
+}
+WORD MethodContext::repGetRelocTypeHint(void * target)
+{
+ if (GetRelocTypeHint == nullptr)
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetRelocTypeHint yielding fake answer...");
+ return 65535;
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)target);
+#endif
+ }
+ if (GetRelocTypeHint->GetIndex((DWORDLONG)target) == -1)
+ {
+ void *origAddr = cr->repAddressMap((void *)target);
+ if (origAddr != (void*)-1 && origAddr != nullptr)
+ {
+ if (GetRelocTypeHint->GetIndex((DWORDLONG)origAddr) == -1)
+ target = origAddr;
+ }
+ else
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetRelocTypeHint yielding fake answer...");
+ return 65535;
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)target);
+#endif
+ }
+ }
+
+ int index = GetRelocTypeHint->GetIndex((DWORDLONG)target);
+ WORD retVal = 0;
+ if (index == -1)
+ {
+ void *subtarget = cr->searchAddressMap(target);
+
+ int index2 = GetRelocTypeHint->GetIndex((DWORDLONG)subtarget);
+ if (index2 == -1)
+ {
+ // __debugbreak(); // seems like a source of pain
+ retVal = IMAGE_REL_BASED_REL32;
+ }
+ else
+ retVal = (WORD)GetRelocTypeHint->Get((DWORDLONG)subtarget);
+ }
+ else
+ retVal = (WORD)GetRelocTypeHint->Get((DWORDLONG)target);
+ DEBUG_REP(dmpGetRelocTypeHint((DWORDLONG)target, retVal));
+ return retVal;
+
+}
+
+void MethodContext::recIsWriteBarrierHelperRequired(CORINFO_FIELD_HANDLE field, bool result)
+{
+ if (IsWriteBarrierHelperRequired == nullptr)
+ IsWriteBarrierHelperRequired = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsWriteBarrierHelperRequired->Add((DWORDLONG)field, (DWORD)result);
+ DEBUG_REC(dmpIsWriteBarrierHelperRequired((DWORDLONG)field, (DWORD)result));
+}
+void MethodContext::dmpIsWriteBarrierHelperRequired(DWORDLONG key, DWORD value)
+{
+ printf("IsWriteBarrierHelperRequired key fld-%016llX, value res-%u", key, value);
+}
+bool MethodContext::repIsWriteBarrierHelperRequired(CORINFO_FIELD_HANDLE field)
+{
+ bool result = IsWriteBarrierHelperRequired->Get((DWORDLONG)field) != 0;
+ DEBUG_REP(dmpIsWriteBarrierHelperRequired((DWORDLONG)field, result));
+ return result;
+}
+
+void MethodContext::recIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK, BOOL result)
+{
+ if (IsValidToken == nullptr)
+ IsValidToken = new LightWeightMap<DLD, DWORD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+ IsValidToken->Add(key, (DWORD)result);
+}
+void MethodContext::dmpIsValidToken(DLD key, DWORD value)
+{
+ printf("IsValidToken key mod-%016llX tok-%08X, value res-%u", key.A, key.B, value);
+}
+BOOL MethodContext::repIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+ BOOL value = (BOOL)IsValidToken->Get(key);
+ return value;
+}
+
+void MethodContext::recGetClassName(CORINFO_CLASS_HANDLE cls, const char* result)
+{
+ if (GetClassName == nullptr)
+ GetClassName = new LightWeightMap<DWORDLONG, DWORD>();
+
+ DWORD temp = (DWORD)-1;
+ if (result != nullptr)
+ temp = (DWORD)GetClassName->AddBuffer((unsigned char*)result, (unsigned int)strlen(result) + 1);
+
+ GetClassName->Add((DWORDLONG)cls, (DWORD)temp);
+ DEBUG_REC(dmpGetClassName((DWORDLONG)cls, (DWORD)temp));
+}
+void MethodContext::dmpGetClassName(DWORDLONG key, DWORD value)
+{
+ printf("GetClassName key %016llX, value %s", key, GetClassName->GetBuffer(value));
+ GetClassName->Unlock();
+}
+const char* MethodContext::repGetClassName(CORINFO_CLASS_HANDLE cls)
+{
+ if (GetClassName == nullptr)
+ return "hackishClassName";
+ int index = GetClassName->GetIndex((DWORDLONG)cls);
+ if (index == -1)
+ return "hackishClassName";
+ int offset = GetClassName->Get((DWORDLONG)cls);
+ const char* name = (const char*)GetClassName->GetBuffer(offset);
+ DEBUG_REC(dmpGetClassName((DWORDLONG)cls, (DWORD)offset));
+ return name;
+}
+
+void MethodContext::recAppendClassName(CORINFO_CLASS_HANDLE cls, BOOL fNamespace, BOOL fFullInst, BOOL fAssembly, const WCHAR* result)
+{
+ if (AppendClassName == nullptr)
+ AppendClassName = new LightWeightMap<Agnostic_AppendClassName, DWORD>();
+
+ Agnostic_AppendClassName key;
+ ZeroMemory(&key, sizeof(Agnostic_AppendClassName)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.classHandle = (DWORDLONG) cls;
+ key.fNamespace = fNamespace;
+ key.fFullInst = fFullInst;
+ key.fAssembly = fAssembly;
+
+ DWORD temp = (DWORD)-1;
+ if (result != nullptr)
+ temp = (DWORD)AppendClassName->AddBuffer((unsigned char*)result, (unsigned int)((wcslen(result) * 2) + 2));
+
+ AppendClassName->Add(key, (DWORD)temp);
+ DEBUG_REC(dmpAppendClassName(key, (DWORD)temp));
+}
+
+void MethodContext::dmpAppendClassName(const Agnostic_AppendClassName& key, DWORD value)
+{
+ printf("AppendClassName key cls-%016llX ns-%u fi-%u as-%u, value %s",
+ key.classHandle, key.fNamespace, key.fFullInst, key.fAssembly, AppendClassName->GetBuffer(value));
+ AppendClassName->Unlock();
+}
+
+const WCHAR* MethodContext::repAppendClassName(CORINFO_CLASS_HANDLE cls, BOOL fNamespace, BOOL fFullInst, BOOL fAssembly)
+{
+ if (AppendClassName == nullptr)
+ return W("hackishClassName");
+
+ Agnostic_AppendClassName key;
+ ZeroMemory(&key, sizeof(Agnostic_AppendClassName)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.classHandle = (DWORDLONG) cls;
+ key.fNamespace = fNamespace;
+ key.fFullInst = fFullInst;
+ key.fAssembly = fAssembly;
+
+ int index = AppendClassName->GetIndex(key);
+ if (index == -1)
+ return W("hackishClassName");
+ int offset = AppendClassName->Get(key);
+ const WCHAR* name = (const WCHAR*)AppendClassName->GetBuffer(offset);
+ DEBUG_REC(dmpAppendClassName(key, (DWORD)offset));
+ return name;
+}
+
+void MethodContext::recGetTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, CorInfoHelperTailCallSpecialHandling flags, void* result)
+{
+ if (GetTailCallCopyArgsThunk == nullptr)
+ GetTailCallCopyArgsThunk = new LightWeightMap<Agnostic_GetTailCallCopyArgsThunk, DWORDLONG>();
+
+ Agnostic_GetTailCallCopyArgsThunk key;
+ ZeroMemory(&key, sizeof(Agnostic_GetTailCallCopyArgsThunk)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.Sig.callConv = (DWORD)pSig->callConv;
+ key.Sig.retTypeClass = (DWORDLONG)pSig->retTypeClass;
+ key.Sig.retTypeSigClass = (DWORDLONG)pSig->retTypeSigClass;
+ key.Sig.retType = (DWORD)pSig->retType;
+ key.Sig.flags = (DWORD)pSig->flags;
+ key.Sig.numArgs = (DWORD)pSig->numArgs;
+ key.Sig.sigInst_classInstCount = (DWORD)pSig->sigInst.classInstCount;
+ key.Sig.sigInst_classInst_Index = (DWORD)GetTailCallCopyArgsThunk->AddBuffer((unsigned char*)pSig->sigInst.classInst, pSig->sigInst.classInstCount * 8); //porting issue
+ key.Sig.sigInst_methInstCount = (DWORD)pSig->sigInst.methInstCount;
+ key.Sig.sigInst_methInst_Index = (DWORD)GetTailCallCopyArgsThunk->AddBuffer((unsigned char*)pSig->sigInst.methInst, pSig->sigInst.methInstCount * 8); //porting issue
+ key.Sig.args = (DWORDLONG)pSig->args;
+ key.Sig.cbSig = (DWORD)pSig->cbSig;
+ key.Sig.pSig = (DWORD)GetTailCallCopyArgsThunk->AddBuffer((unsigned char *)pSig->pSig, pSig->cbSig);
+ key.Sig.scope = (DWORDLONG)pSig->scope;
+ key.Sig.token = (DWORD)pSig->token;
+ key.flags = (DWORD)flags;
+
+ GetTailCallCopyArgsThunk->Add(key, (DWORDLONG)result);
+ DEBUG_REC(dmpGetTailCallCopyArgsThunk(key, (DWORDLONG)result));
+}
+void MethodContext::dmpGetTailCallCopyArgsThunk(const Agnostic_GetTailCallCopyArgsThunk& key, DWORDLONG value)
+{
+ printf("GetTailCallCopyArgsThunk key sig{cc-%u rtc-%016llX rts-%016llX rt-%u flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u sig-%u pSig-%u scp-%016llX tok-%08X} flg-%08X",
+ key.Sig.callConv,
+ key.Sig.retTypeClass,
+ key.Sig.retTypeSigClass,
+ key.Sig.retType,
+ key.Sig.flags,
+ key.Sig.numArgs,
+ key.Sig.sigInst_classInstCount,
+ key.Sig.sigInst_classInst_Index,
+ key.Sig.sigInst_methInstCount,
+ key.Sig.sigInst_methInst_Index,
+ key.Sig.cbSig,
+ key.Sig.pSig,
+ key.Sig.scope,
+ key.Sig.token,
+ key.flags);
+ printf(", value res-%016llX", value);
+}
+void* MethodContext::repGetTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, CorInfoHelperTailCallSpecialHandling flags)
+{
+ Agnostic_GetTailCallCopyArgsThunk key;
+ ZeroMemory(&key, sizeof(Agnostic_GetTailCallCopyArgsThunk)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ AssertCodeMsg(GetTailCallCopyArgsThunk != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for ...");
+ key.Sig.callConv = (DWORD)pSig->callConv;
+ key.Sig.retTypeClass = (DWORDLONG)pSig->retTypeClass;
+ key.Sig.retTypeSigClass = (DWORDLONG)pSig->retTypeSigClass;
+ key.Sig.retType = (DWORD)pSig->retType;
+ key.Sig.flags = (DWORD)pSig->flags;
+ key.Sig.numArgs = (DWORD)pSig->numArgs;
+ key.Sig.sigInst_classInstCount = (DWORD)pSig->sigInst.classInstCount;
+ key.Sig.sigInst_classInst_Index = (DWORD)GetTailCallCopyArgsThunk->Contains((unsigned char*)pSig->sigInst.classInst, pSig->sigInst.classInstCount * 8); //porting issue
+ key.Sig.sigInst_methInstCount = (DWORD)pSig->sigInst.methInstCount;
+ key.Sig.sigInst_methInst_Index = (DWORD)GetTailCallCopyArgsThunk->Contains((unsigned char*)pSig->sigInst.methInst, pSig->sigInst.methInstCount * 8); //porting issue
+ key.Sig.args = (DWORDLONG)pSig->args;
+ key.Sig.cbSig = (DWORD)pSig->cbSig;
+ key.Sig.pSig = (DWORD)GetTailCallCopyArgsThunk->Contains((unsigned char *)pSig->pSig, pSig->cbSig);
+ key.Sig.scope = (DWORDLONG)pSig->scope;
+ key.Sig.token = (DWORD)pSig->token;
+ key.flags = (DWORD)flags;
+
+ AssertCodeMsg(GetTailCallCopyArgsThunk->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.Sig.retTypeClass);
+ void *result = (void*)GetTailCallCopyArgsThunk->Get(key);
+ cr->recAddressMap((void*)0x424242, (void*)result, 1);
+ DEBUG_REP(dmpGetTailCallCopyArgsThunk(key, (DWORDLONG)result));
+ return result;
+}
+
+void MethodContext::recGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod, mdMethodDef result)
+{
+ if (GetMethodDefFromMethod == nullptr)
+ GetMethodDefFromMethod = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetMethodDefFromMethod->Add((DWORDLONG)hMethod, (DWORD)result);
+}
+void MethodContext::dmpGetMethodDefFromMethod(DWORDLONG key, DWORD value)
+{
+ printf("GetMethodDefFromMethod key ftn-%016llX, value res-%u", key, value);
+}
+mdMethodDef MethodContext::repGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod)
+{
+ // Since this is diagnostic, fake up a result if one wasn't recorded.
+ if (GetMethodDefFromMethod == nullptr)
+ return (mdMethodDef)0x06000000;
+
+ int index = GetMethodDefFromMethod->GetIndex((DWORDLONG)hMethod);
+ if (index < 0)
+ return (mdMethodDef)0x06000001;
+
+ return (mdMethodDef)GetMethodDefFromMethod->Get((DWORDLONG)hMethod);
+}
+
+void MethodContext::recCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, BOOL fOptional, BOOL result)
+{
+ if (CheckMethodModifier == nullptr)
+ CheckMethodModifier = new LightWeightMap<Agnostic_CheckMethodModifier, DWORD>();
+
+ Agnostic_CheckMethodModifier key;
+ ZeroMemory(&key, sizeof(Agnostic_CheckMethodModifier)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.hMethod = (DWORDLONG)hMethod;
+ //If the input matches something already in the buffer, just re-use that slot.. easier than searching for a soft key on rep.
+ if (modifier != nullptr)
+ key.modifier = (DWORD)CheckMethodModifier->AddBuffer((unsigned char*)modifier, (unsigned int)strlen(modifier) + 1);
+ else
+ key.modifier = (DWORD)-1;
+
+ key.fOptional = (DWORD)fOptional;
+
+ CheckMethodModifier->Add(key, (DWORD)result);
+}
+void MethodContext::dmpCheckMethodModifier(const Agnostic_CheckMethodModifier& key, DWORD value)
+{
+ printf("CheckMethodModifier key, ftn-%016llX mod-'%s' opt-%u, value res-%u", key.hMethod,
+ (unsigned char *)CheckMethodModifier->GetBuffer(key.modifier), key.fOptional, value);
+ CheckMethodModifier->Unlock();
+}
+BOOL MethodContext::repCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, BOOL fOptional)
+{
+ Agnostic_CheckMethodModifier key;
+ ZeroMemory(&key, sizeof(Agnostic_CheckMethodModifier)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.hMethod = (DWORDLONG)hMethod;
+ if (modifier != nullptr)
+ key.modifier = (DWORD)CheckMethodModifier->Contains((unsigned char *)modifier, (unsigned int)strlen(modifier) + 1);
+ else
+ key.modifier = (DWORD)-1;
+
+ key.fOptional = (DWORD)fOptional;
+
+ BOOL value = (BOOL)CheckMethodModifier->Get(key);
+ return value;
+}
+
+void MethodContext::recGetPInvokeUnmanagedTarget(CORINFO_METHOD_HANDLE method, void **ppIndirection, void* result)
+{
+ if (GetPInvokeUnmanagedTarget == nullptr)
+ GetPInvokeUnmanagedTarget = new LightWeightMap<DWORDLONG, DLDL>();
+ DLDL temp;
+ temp.A = (DWORDLONG)*ppIndirection;
+ temp.B = (DWORDLONG)result;
+
+ GetPInvokeUnmanagedTarget->Add((DWORDLONG)method, temp);
+ DEBUG_REC(dmpGetPInvokeUnmanagedTarget((DWORDLONG)method, temp));
+}
+void MethodContext::dmpGetPInvokeUnmanagedTarget(DWORDLONG key, DLDL value)
+{
+ printf("GetPInvokeUnmanagedTarget key ftn-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+void* MethodContext::repGetPInvokeUnmanagedTarget(CORINFO_METHOD_HANDLE method, void **ppIndirection)
+{
+ DLDL temp = (DLDL)GetPInvokeUnmanagedTarget->Get((DWORDLONG)method);
+ *ppIndirection = (void *)temp.A;
+ DEBUG_REP(dmpGetPInvokeUnmanagedTarget((DWORDLONG)method, temp));
+
+ return (void *)temp.B;
+}
+
+void MethodContext::recGetArrayRank(CORINFO_CLASS_HANDLE cls, unsigned result)
+{
+ if (GetArrayRank == nullptr)
+ GetArrayRank = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetArrayRank->Add((DWORDLONG)cls, (DWORD)result);
+}
+void MethodContext::dmpGetArrayRank(DWORDLONG key, DWORD value)
+{
+ printf("GetArrayRank key %016llX, value %u", key, value);
+}
+unsigned MethodContext::repGetArrayRank(CORINFO_CLASS_HANDLE cls)
+{
+ return (unsigned)GetArrayRank->Get((DWORDLONG)cls);
+}
+
+void MethodContext::recIsFieldStatic(CORINFO_FIELD_HANDLE fhld, bool result)
+{
+ if (IsFieldStatic == nullptr)
+ IsFieldStatic = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsFieldStatic->Add((DWORDLONG)fhld, (DWORD)result);
+ DEBUG_REC(dmpIsFieldStatic((DWORDLONG)fhld, (DWORD)result));
+}
+void MethodContext::dmpIsFieldStatic(DWORDLONG key, DWORD value)
+{
+ printf("IsFieldStatic key %016llX, value %u", key, value);
+}
+bool MethodContext::repIsFieldStatic(CORINFO_FIELD_HANDLE fhld)
+{
+ AssertCodeMsg(IsFieldStatic != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)fhld);
+ AssertCodeMsg(IsFieldStatic->GetIndex((DWORDLONG)fhld) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)fhld);
+ bool result = (bool)(IsFieldStatic->Get((DWORDLONG)fhld) != 0);
+ DEBUG_REP(dmpIsFieldStatic((DWORDLONG)fhld, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetIntConfigValue(const wchar_t *name, int defaultValue, int result)
+{
+ if (GetIntConfigValue == nullptr)
+ GetIntConfigValue = new LightWeightMap<Agnostic_ConfigIntInfo, DWORD>();
+
+ AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");
+
+ Agnostic_ConfigIntInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_ConfigIntInfo));
+
+ DWORD index = (DWORD)GetIntConfigValue->AddBuffer((unsigned char*)name, sizeof(wchar_t) * ((unsigned int)wcslen(name) + 1));
+
+ key.nameIndex = index;
+ key.defaultValue = defaultValue;
+
+ GetIntConfigValue->Add(key, result);
+ DEBUG_REC(dmpGetIntConfigValue(key, result));
+}
+
+void MethodContext::dmpGetIntConfigValue(const Agnostic_ConfigIntInfo& key, int value)
+{
+ const wchar_t *name = (const wchar_t*)GetIntConfigValue->GetBuffer(key.nameIndex);
+ printf("GetIntConfigValue name %S, default value %d, value %d",
+ name, key.defaultValue, value);
+ GetIntConfigValue->Unlock();
+}
+
+int MethodContext::repGetIntConfigValue(const wchar_t *name, int defaultValue)
+{
+ if (GetIntConfigValue == nullptr)
+ return defaultValue;
+
+ AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");
+
+ Agnostic_ConfigIntInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_ConfigIntInfo));
+
+ size_t nameLenInBytes = sizeof(wchar_t) * (wcslen(name) + 1);
+ int nameIndex = GetIntConfigValue->Contains((unsigned char *)name, (unsigned int)nameLenInBytes);
+ if (nameIndex == -1) // config name not in map
+ return defaultValue;
+
+ key.nameIndex = (DWORD)nameIndex;
+ key.defaultValue = defaultValue;
+
+ DWORD result = GetIntConfigValue->Get(key);
+ DEBUG_REP(dmpGetIntConfigValue(key, result));
+ return (int)result;
+}
+
+void MethodContext::recGetStringConfigValue(const wchar_t *name, const wchar_t *result)
+{
+ if (GetStringConfigValue == nullptr)
+ GetStringConfigValue = new LightWeightMap<DWORD, DWORD>();
+
+ AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");
+
+ DWORD nameIndex = (DWORD)GetStringConfigValue->AddBuffer((unsigned char*)name, sizeof(wchar_t) * ((unsigned int)wcslen(name) + 1));
+
+ DWORD resultIndex = (DWORD)-1;
+ if (result != nullptr)
+ resultIndex = (DWORD)GetStringConfigValue->AddBuffer((unsigned char*)result, sizeof(wchar_t) * ((unsigned int)wcslen(result) + 1));
+
+ GetStringConfigValue->Add(nameIndex, resultIndex);
+ DEBUG_REC(dmpGetStringConfigValue(nameIndex, resultIndex));
+}
+
+void MethodContext::dmpGetStringConfigValue(DWORD nameIndex, DWORD resultIndex)
+{
+ const wchar_t *name = (const wchar_t*)GetStringConfigValue->GetBuffer(nameIndex);
+ const wchar_t *result = (const wchar_t*)GetStringConfigValue->GetBuffer(resultIndex);
+ printf("GetStringConfigValue name %S, result %S", name, result);
+ GetStringConfigValue->Unlock();
+}
+
+const wchar_t *MethodContext::repGetStringConfigValue(const wchar_t *name)
+{
+ if (GetStringConfigValue == nullptr)
+ return nullptr;
+
+ AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");
+
+ size_t nameLenInBytes = sizeof(wchar_t) * (wcslen(name) + 1);
+ int nameIndex = GetStringConfigValue->Contains((unsigned char *)name, (unsigned int)nameLenInBytes);
+ if (nameIndex == -1) // config name not in map
+ return nullptr;
+
+ int resultIndex = GetStringConfigValue->Get(nameIndex);
+ const wchar_t *value = (const wchar_t *)GetStringConfigValue->GetBuffer(resultIndex);
+
+ DEBUG_REP(dmpGetStringConfigValue(nameIndex, resultIndex));
+
+ return value;
+}
+
+struct EnvironmentVariable
+{
+ char *name;
+ DWORD val_index;
+};
+int __cdecl compareEnvironmentVariable(const void *arg1, const void *arg2)
+{
+ return _stricmp(((EnvironmentVariable *)arg1)->name, ((EnvironmentVariable *)arg2)->name);
+}
+int MethodContext::dumpMethodIdentityInfoToBuffer(char *buff, int len)
+{
+ char *obuff = buff;
+
+ if (len < METHOD_IDENTITY_INFO_SIZE)
+ return -1;
+
+ //Obtain the Method Info structure for this method
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+
+ repCompileMethod(&info, &flags);
+
+ //Add the Method Signature
+ int t = sprintf_s(buff, len, "%s -- ", CallUtils::GetMethodFullName(this, info.ftn, info.args));
+ buff += t;
+ len -= t;
+
+ //Add Calling convention information, CorInfoOptions and CorInfoRegionKind
+ t = sprintf_s(buff, len, "CallingConvention: %d, CorInfoOptions: %d, CorInfoRegionKind: %d ",
+ info.args.callConv, info.options, info.regionKind);
+ buff += t;
+ len -= t;
+
+ //Add COMPLUS_* & dbflag environment variables to method Identity
+ //except complus_version and complus_defaultversion
+ //since they change the compilation behaviour of JIT
+ //we also need to sort them to ensure we don't produce a different
+ //hash based on the order of these variables
+ if (Environment != nullptr)
+ {
+ Agnostic_Environment val;
+ EnvironmentVariable *envValues = new EnvironmentVariable[Environment->GetCount()];
+ int envValCount = 0;
+
+ for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ {
+ val = Environment->Get((DWORD)i);
+ char *envVariable = (char *)Environment->GetBuffer(val.name_index);
+ if ((_strnicmp(envVariable, "complus_", 8) == 0 || _strnicmp(envVariable, "dbflag", 6) == 0) &&
+ (_stricmp(envVariable, "complus_version") != 0) &&
+ (_stricmp(envVariable, "complus_defaultversion") != 0))
+ {
+ envValues[envValCount].name = envVariable;
+ envValues[envValCount++].val_index = val.val_index;
+ }
+ }
+
+ //Do a quick sort on envValues if needed
+ if (envValCount > 1)
+ qsort(envValues, envValCount, sizeof(EnvironmentVariable), compareEnvironmentVariable);
+
+ //Append these values to the IdentityInfobuffer
+ for (int i = 0; i < envValCount; i++)
+ {
+ t = sprintf_s(buff, len, "%s=%s ", _strlwr(envValues[i].name),
+ _strlwr((char *)Environment->GetBuffer(envValues[i].val_index)));
+ buff += t;
+ len -= t;
+ }
+
+ delete[] envValues;
+ }
+
+ //Hash the IL Code for this method and append it to the ID info
+ char ilHash[MD5_HASH_BUFFER_SIZE];
+ dumpMD5HashToBuffer(info.ILCode, info.ILCodeSize, ilHash, MD5_HASH_BUFFER_SIZE);
+ t = sprintf_s(buff, len, "ILCode Hash: %s", ilHash);
+ buff += t;
+ len -= t;
+
+ return (int)(buff - obuff);
+}
+int MethodContext::dumpMethodMD5HashToBuffer(char *buff, int len)
+{
+ char bufferIdentityInfo[METHOD_IDENTITY_INFO_SIZE];
+
+ int cbLen = dumpMethodIdentityInfoToBuffer(bufferIdentityInfo, METHOD_IDENTITY_INFO_SIZE);
+
+ if (cbLen < 0)
+ return cbLen;
+
+ cbLen = dumpMD5HashToBuffer((BYTE *)bufferIdentityInfo, cbLen, buff, len);
+
+ return cbLen;
+}
+
+int MethodContext::dumpMD5HashToBuffer(BYTE *pBuffer, int bufLen, char *hash, int hashLen)
+{
+#ifdef FEATURE_PAL
+
+ MD5HASHDATA md5_hashdata;
+ MD5 md5_hasher;
+
+ if (hashLen < MD5_HASH_BUFFER_SIZE)
+ return -1;
+
+ md5_hasher.Hash(pBuffer, (ULONG)bufLen, &md5_hashdata);
+
+ DWORD md5_hashdata_size = sizeof(md5_hashdata.rgb) / sizeof(BYTE);
+ Assert(md5_hashdata_size == MD5_HASH_BYTE_SIZE);
+
+ for (DWORD i = 0; i < md5_hashdata_size; i++)
+ {
+ sprintf_s(hash + i * 2, hashLen - i * 2, "%02X", md5_hashdata.rgb[i]);
+ }
+
+ return MD5_HASH_BUFFER_SIZE; //if we had success we wrote MD5_HASH_BUFFER_SIZE bytes to the buffer
+
+#else // !FEATURE_PAL
+
+ HCRYPTPROV hProv = NULL; //CryptoProvider
+ HCRYPTHASH hHash = NULL;
+ BYTE bHash[MD5_HASH_BYTE_SIZE];
+ DWORD cbHash = MD5_HASH_BYTE_SIZE;
+
+ if (hashLen < MD5_HASH_BUFFER_SIZE)
+ return -1;
+
+ // Get handle to the crypto provider
+ if (!CryptAcquireContextA(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ goto OnError;
+
+ if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
+ goto OnError;
+
+ if (!CryptHashData(hHash, pBuffer, bufLen, 0))
+ goto OnError;
+
+ if (!CryptGetHashParam(hHash, HP_HASHVAL, bHash, &cbHash, 0))
+ goto OnError;
+
+ if (cbHash != MD5_HASH_BYTE_SIZE)
+ goto OnError;
+
+ for (DWORD i = 0; i < MD5_HASH_BYTE_SIZE; i++)
+ {
+ sprintf_s(hash + i * 2, hashLen - i * 2, "%02X", bHash[i]);
+ }
+
+ if (hHash != NULL)
+ CryptDestroyHash(hHash);
+ if (hProv != NULL)
+ CryptReleaseContext(hProv, 0);
+
+ return MD5_HASH_BUFFER_SIZE; //if we had success we wrote MD5_HASH_BUFFER_SIZE bytes to the buffer
+
+OnError:
+ AssertMsg(false, "Failed to create a hash using the Crypto API (Error %X)", GetLastError());
+
+ if (hHash != NULL)
+ CryptDestroyHash(hHash);
+ if (hProv != NULL)
+ CryptReleaseContext(hProv, 0);
+ return -1;
+
+#endif // !FEATURE_PAL
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
new file mode 100644
index 0000000000..5869c85b45
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
@@ -0,0 +1,1179 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MethodContext.h - Primary structure to store all the EE-JIT details required to replay creation of a method
+// CompileResult contains the stuff generated by a compilation
+//----------------------------------------------------------
+#ifndef _MethodContext
+#define _MethodContext
+
+#include "runtimedetails.h"
+#include "compileresult.h"
+#include "lightweightmap.h"
+#include "errorhandling.h"
+
+#define METHOD_IDENTITY_INFO_SIZE 0x10000 //We assume that the METHOD_IDENTITY_INFO_SIZE will not exceed 64KB
+
+#define MD5_HASH_BYTE_SIZE 16 //MD5 is 128-bit, so we need 16 bytes to store it
+#define MD5_HASH_BUFFER_SIZE 33 //MD5 is 128-bit, so we need 32 chars + 1 char to store null-terminator
+
+class MethodContext
+{
+public:
+#pragma pack(push, 1)
+ struct Agnostic_CORINFO_SIG_INFO
+ {
+ DWORD callConv;
+ DWORDLONG retTypeClass;
+ DWORDLONG retTypeSigClass;
+ DWORD retType;
+ DWORD flags;
+ DWORD numArgs;
+ DWORD sigInst_classInstCount;
+ DWORD sigInst_classInst_Index;
+ DWORD sigInst_methInstCount;
+ DWORD sigInst_methInst_Index;
+ DWORDLONG args;
+ DWORD pSig;
+ DWORD cbSig;
+ DWORDLONG scope;
+ DWORD token;
+ };
+ struct Agnostic_CORINFO_METHOD_INFO
+ {
+ DWORDLONG ftn;
+ DWORDLONG scope;
+ DWORD ILCode_offset;
+ DWORD ILCodeSize;
+ DWORD maxStack;
+ DWORD EHcount;
+ DWORD options;
+ DWORD regionKind;
+ Agnostic_CORINFO_SIG_INFO args;
+ Agnostic_CORINFO_SIG_INFO locals;
+ };
+ struct Agnostic_CompileMethod
+ {
+ Agnostic_CORINFO_METHOD_INFO info;
+ DWORD flags;
+ };
+ struct Agnostic_InitClass
+ {
+ DWORDLONG field;
+ DWORDLONG method;
+ DWORDLONG context;
+ DWORD speculative;
+ };
+ struct DLDL
+ {
+ DWORDLONG A;
+ DWORDLONG B;
+ };
+ struct Agnostic_CanInline
+ {
+ DWORD Restrictions;
+ DWORD result;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_GetClassGClayout
+ {
+ DWORD gcPtrs_Index;
+ DWORD len;
+ DWORD valCount;
+ };
+ struct DLD
+ {
+ DWORDLONG A;
+ DWORD B;
+ };
+ struct Agnostic_CORINFO_RESOLVED_TOKENin
+ {
+ DWORDLONG tokenContext;
+ DWORDLONG tokenScope;
+ DWORD token;
+ DWORD tokenType;
+ };
+ struct Agnostic_CORINFO_RESOLVED_TOKENout
+ {
+ DWORDLONG hClass;
+ DWORDLONG hMethod;
+ DWORDLONG hField;
+ DWORD pTypeSpec_Index;
+ DWORD cbTypeSpec;
+ DWORD pMethodSpec_Index;
+ DWORD cbMethodSpec;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_GetArgType
+ {
+ Agnostic_CORINFO_SIG_INFO sig;
+ DWORDLONG args;
+ };
+ struct Agnostic_GetArgClass
+ {
+ Agnostic_CORINFO_SIG_INFO sig;
+ DWORDLONG args;
+ };
+ struct Agnostic_GetBoundaries
+ {
+ DWORD cILOffsets;
+ DWORD pILOffset_offset;
+ DWORD implicitBoundaries;
+ };
+ struct Agnostic_CORINFO_EE_INFO
+ {
+ struct Agnostic_InlinedCallFrameInfo
+ {
+ DWORD size;
+ DWORD offsetOfGSCookie;
+ DWORD offsetOfFrameVptr;
+ DWORD offsetOfFrameLink;
+ DWORD offsetOfCallSiteSP;
+ DWORD offsetOfCalleeSavedFP;
+ DWORD offsetOfCallTarget;
+ DWORD offsetOfReturnAddress;
+ }
+ inlinedCallFrameInfo;
+ DWORD offsetOfThreadFrame;
+ DWORD offsetOfGCState;
+ DWORD offsetOfDelegateInstance;
+ DWORD offsetOfDelegateFirstTarget;
+ DWORD offsetOfSecureDelegateIndirectCell;
+ DWORD offsetOfTransparentProxyRP;
+ DWORD offsetOfRealProxyServer;
+ DWORD offsetOfObjArrayData;
+ DWORD sizeOfReversePInvokeFrame;
+ DWORD osPageSize;
+ DWORD maxUncheckedOffsetForNullObject;
+ DWORD targetAbi;
+ DWORD osType;
+ DWORD osMajor;
+ DWORD osMinor;
+ DWORD osBuild;
+ };
+ struct Agnostic_GetFieldAddress
+ {
+ DWORDLONG ppIndirection;
+ DWORDLONG fieldAddress;
+ DWORD fieldValue;
+ };
+ struct Agnostic_CORINFO_RESOLVED_TOKEN
+ {
+ DWORDLONG tokenContext;
+ DWORDLONG tokenScope;
+ DWORD token;
+ DWORD tokenType;
+ DWORDLONG hClass;
+ DWORDLONG hMethod;
+ DWORDLONG hField;
+ DWORD typeSpec_Index;
+ DWORD cbTypeSpec;
+ DWORD methodSpec_Index;
+ DWORD cbMethodSpec;
+ };
+ struct Agnostic_GetFieldInfo
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORDLONG callerHandle;
+ DWORD flags;
+ };
+ struct Agnostic_CORINFO_HELPER_ARG
+ {
+ DWORDLONG constant; //one view of a large union of ptr size
+ DWORD argType;
+ };
+ struct Agnostic_CORINFO_HELPER_DESC
+ {
+ DWORD helperNum;
+ DWORD numArgs;
+ Agnostic_CORINFO_HELPER_ARG args[CORINFO_ACCESS_ALLOWED_MAX_ARGS];
+ };
+ struct Agnostic_CORINFO_FIELD_INFO
+ {
+ DWORD fieldAccessor;
+ DWORD fieldFlags;
+ DWORD helper;
+ DWORD offset;
+ DWORD fieldType;
+ DWORDLONG structType;
+ DWORD accessAllowed;
+ Agnostic_CORINFO_HELPER_DESC accessCalloutHelper;
+ };
+ struct DD
+ {
+ DWORD A;
+ DWORD B;
+ };
+ struct Agnostic_CanTailCall
+ {
+ DWORDLONG callerHnd;
+ DWORDLONG declaredCalleeHnd;
+ DWORDLONG exactCalleeHnd;
+ WORD fIsTailPrefix;
+ };
+ struct Agnostic_Environment
+ {
+ DWORD name_index;;
+ DWORD val_index;
+ };
+ struct Agnostic_GetCallInfo
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ Agnostic_CORINFO_RESOLVED_TOKEN ConstrainedResolvedToken;
+ DWORDLONG callerHandle;
+ DWORD flags;
+ };
+ struct Agnostic_CORINFO_LOOKUP_KIND
+ {
+ DWORD needsRuntimeLookup;
+ DWORD runtimeLookupKind;
+ WORD runtimeLookupFlags;
+ };
+ struct Agnostic_CORINFO_RUNTIME_LOOKUP
+ {
+ DWORDLONG signature;
+ DWORD helper;
+ DWORD indirections;
+ DWORD testForNull;
+ DWORD testForFixup;
+ DWORDLONG offsets[CORINFO_MAXINDIRECTIONS];
+ };
+ struct Agnostic_CORINFO_CONST_LOOKUP
+ {
+ DWORD accessType;
+ DWORDLONG handle; // actually a union of two pointer sized things
+ };
+ struct Agnostic_CORINFO_LOOKUP
+ {
+ Agnostic_CORINFO_LOOKUP_KIND lookupKind;
+ Agnostic_CORINFO_RUNTIME_LOOKUP runtimeLookup; //This and constLookup actually a union, but with different layouts.. :-| copy the right one based on lookupKinds value
+ Agnostic_CORINFO_CONST_LOOKUP constLookup;
+ };
+ struct Agnostic_CORINFO_CALL_INFO
+ {
+ DWORDLONG hMethod;
+ DWORD methodFlags;
+ DWORD classFlags;
+ Agnostic_CORINFO_SIG_INFO sig;
+ DWORD verMethodFlags;
+ Agnostic_CORINFO_SIG_INFO verSig;
+ DWORD accessAllowed;
+ Agnostic_CORINFO_HELPER_DESC callsiteCalloutHelper;
+ DWORD thisTransform;
+ DWORD kind;
+ DWORD nullInstanceCheck;
+ DWORDLONG contextHandle;
+ DWORD exactContextNeedsRuntimeLookup;
+ Agnostic_CORINFO_LOOKUP stubLookup;//first view of union. others are matching or subordinate
+ Agnostic_CORINFO_CONST_LOOKUP instParamLookup;
+ DWORD secureDelegateInvoke;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_GetMethodInfo
+ {
+ Agnostic_CORINFO_METHOD_INFO info;
+ bool result;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_FindSig
+ {
+ DWORDLONG module;
+ DWORD sigTOK;
+ DWORDLONG context;
+ };
+ struct Agnostic_PInvokeMarshalingRequired
+ {
+ DWORDLONG method;
+ Agnostic_CORINFO_SIG_INFO callSiteSig;
+ };
+ struct Agnostic_CORINFO_EH_CLAUSE
+ {
+ DWORD Flags;
+ DWORD TryOffset;
+ DWORD TryLength;
+ DWORD HandlerOffset;
+ DWORD HandlerLength;
+ DWORD ClassToken;//first view of a two dword union
+ };
+ struct Agnostic_GetVars
+ {
+ DWORD cVars;
+ DWORD vars_offset;
+ DWORD extendOthers;
+ };
+ struct Agnostic_CanAccessClassIn
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORDLONG callerHandle;
+ };
+ struct Agnostic_CanAccessClassOut
+ {
+ Agnostic_CORINFO_HELPER_DESC AccessHelper;
+ DWORD result;
+ };
+ struct Agnostic_AppendClassName
+ {
+ DWORDLONG classHandle;
+ DWORD fNamespace;
+ DWORD fFullInst;
+ DWORD fAssembly;
+ };
+ struct Agnostic_CheckMethodModifier
+ {
+ DWORDLONG hMethod;
+ DWORD modifier;
+ DWORD fOptional;
+ };
+ struct Agnostic_EmbedGenericHandle
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORD fEmbedParent;
+ };
+ struct Agnostic_CORINFO_GENERICHANDLE_RESULT
+ {
+
+ Agnostic_CORINFO_LOOKUP lookup;
+ DWORDLONG compileTimeHandle;
+ DWORD handleType;
+ };
+ struct Agnostic_GetDelegateCtorIn
+ {
+ DWORDLONG methHnd;
+ DWORDLONG clsHnd;
+ DWORDLONG targetMethodHnd;
+ };
+ struct Agnostic_DelegateCtorArgs
+ {
+ DWORDLONG pMethod;
+ DWORDLONG pArg3;
+ DWORDLONG pArg4;
+ DWORDLONG pArg5;
+ };
+ struct Agnostic_GetDelegateCtorOut
+ {
+ Agnostic_DelegateCtorArgs CtorData;
+ DWORDLONG result;
+ };
+ struct Agnostic_FindCallSiteSig
+ {
+ DWORDLONG module;
+ DWORD methTok;
+ DWORDLONG context;
+ };
+ struct Agnostic_GetNewHelper
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORDLONG callerHandle;
+ };
+ struct Agnostic_GetCastingHelper
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORD fThrowing;
+ };
+ struct Agnostic_GetClassModuleIdForStatics
+ {
+ DWORDLONG Module;
+ DWORDLONG pIndirection;
+ DWORDLONG result;
+ };
+ struct Agnostic_IsCompatibleDelegate
+ {
+ DWORDLONG objCls;
+ DWORDLONG methodParentCls;
+ DWORDLONG method;
+ DWORDLONG delegateCls;
+ };
+ struct Agnostic_GetBBProfileData
+ {
+ DWORD count;
+ DWORD profileBuffer_index;
+ DWORD numRuns;
+ DWORD result;
+ };
+ struct Agnostic_GetProfilingHandle
+ {
+ DWORD bHookFunction;
+ DWORDLONG ProfilerHandle;
+ DWORD bIndirectedHandles;
+ };
+ struct Agnostic_GetTailCallCopyArgsThunk
+ {
+ Agnostic_CORINFO_SIG_INFO Sig;
+ DWORD flags;
+ };
+ struct Agnostic_GetArgClass_Value
+ {
+ DWORDLONG result;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_GetArgType_Value
+ {
+ DWORDLONG vcTypeRet;
+ DWORD result;
+ DWORD exceptionCode;
+ };
+
+ // Agnostic_ConfigIntInfo combines as a single key the name
+ // and defaultValue of a integer config query.
+ // Note: nameIndex is treated as a DWORD index to the name string.
+ struct Agnostic_ConfigIntInfo
+ {
+ DWORD nameIndex;
+ DWORD defaultValue;
+ };
+
+ // SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR
+ struct Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor
+ {
+ DWORD passedInRegisters; // Whether the struct is passable/passed (this includes struct returning) in registers.
+ DWORD eightByteCount; // Number of eightbytes for this struct.
+ DWORD eightByteClassifications[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; // The eightbytes type classification.
+ DWORD eightByteSizes[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; // The size of the eightbytes (an eightbyte could include padding. This represents the no padding size of the eightbyte).
+ DWORD eightByteOffsets[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; // The start offset of the eightbytes (in bytes).
+ DWORD result;
+ };
+
+#pragma pack(pop)
+
+ MethodContext();
+
+private:
+
+ MethodContext(HANDLE hFile);
+ MethodContext(unsigned char *buff, unsigned int totalLen);
+
+ void MethodInitHelper(unsigned char *buff, unsigned int totalLen);
+ void MethodInitHelperFile(HANDLE hFile);
+
+ bool Initialize(int loadedCount, unsigned char* buff, DWORD size);
+ bool Initialize(int loadedCount, HANDLE hFile);
+
+ int dumpMD5HashToBuffer(BYTE *pBuffer, int bufLen, char *buff, int len);
+
+public:
+
+ static bool Initialize(int loadedCount, unsigned char* buff, DWORD size, /* OUT */ MethodContext** ppmc);
+ static bool Initialize(int loadedCount, HANDLE hFile, /* OUT */ MethodContext** ppmc);
+ ~MethodContext();
+ void Destroy();
+
+ bool Equal(MethodContext *other);
+ unsigned int saveToFile(HANDLE hFile);
+ unsigned int calculateFileSize();
+ unsigned int calculateRawFileSize();
+ void dumpToConsole(int mcNumber = -1); // if mcNumber is not -1, display the method context number before the dumped info
+ int dumpStatToBuffer(char *buff, int len);
+ static int dumpStatTitleToBuffer(char *buff, int len);
+ int methodSize;
+
+ int dumpMethodIdentityInfoToBuffer(char *buff, int len);
+ int dumpMethodMD5HashToBuffer(char *buff, int len);
+
+ void recGlobalContext(const MethodContext& other);
+
+ void recEnvironment();
+ void dmpEnvironment(DWORD key, const Agnostic_Environment& value);
+ void repEnvironmentSet();
+ void repEnvironmentUnset();
+ int repEnvironmentGetCount();
+ int repGetTestID();
+
+ void recCompileMethod(CORINFO_METHOD_INFO *info, unsigned flags);
+ void dmpCompileMethod(DWORD key, const Agnostic_CompileMethod& value);
+ void repCompileMethod(CORINFO_METHOD_INFO *info, unsigned *flags);
+
+ void recGetMethodClass(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CLASS_HANDLE classHandle);
+ void dmpGetMethodClass(DWORDLONG key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetMethodClass(CORINFO_METHOD_HANDLE methodHandle);
+
+ void recGetClassAttribs(CORINFO_CLASS_HANDLE classHandle, DWORD attribs);
+ void dmpGetClassAttribs(DWORDLONG key, DWORD value);
+ DWORD repGetClassAttribs(CORINFO_CLASS_HANDLE classHandle);
+
+ void recGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle, DWORD attribs);
+ void dmpGetMethodAttribs(DWORDLONG key, DWORD value);
+ DWORD repGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle);
+
+ void recGetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars, bool *extendOthers);
+ void dmpGetVars(DWORDLONG key, const Agnostic_GetVars& value);
+ void repGetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars, bool *extendOthers);
+
+ void recGetBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int *cILOffsets, DWORD **pILOffsets, ICorDebugInfo::BoundaryTypes *implictBoundaries);
+ void dmpGetBoundaries(DWORDLONG key, const Agnostic_GetBoundaries& value);
+ void repGetBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int *cILOffsets, DWORD **pILOffsets, ICorDebugInfo::BoundaryTypes *implictBoundaries);
+
+ void recInitClass(CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context, BOOL speculative, CorInfoInitClassResult result);
+ void dmpInitClass(const Agnostic_InitClass& key, DWORD value);
+ CorInfoInitClassResult repInitClass(CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context, BOOL speculative);
+
+ void recGetMethodName(CORINFO_METHOD_HANDLE ftn, char *methodname, const char **moduleName);
+ void dmpGetMethodName(DLD key, DD value);
+ const char *repGetMethodName(CORINFO_METHOD_HANDLE ftn, const char **moduleName);
+
+ void MethodContext::recGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes, DWORD result);
+ void MethodContext::dmpGetJitFlags(DWORD key, DD value);
+ DWORD MethodContext::repGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes);
+
+ void recGetJitTimeLogFilename(LPCWSTR tempFileName);
+ void dmpGetJitTimeLogFilename(DWORD key, DWORD value);
+ LPCWSTR repGetJitTimeLogFilename();
+
+ void recCanInline(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, DWORD *pRestrictions, CorInfoInline response, DWORD exceptionCode);
+ void dmpCanInline(DLDL key, const Agnostic_CanInline& value);
+ CorInfoInline repCanInline(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, DWORD* pRestrictions, DWORD *exceptionCode);
+
+ void recResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, DWORD exceptionCode);
+ void dmpResolveToken(const Agnostic_CORINFO_RESOLVED_TOKENin& key, const Agnostic_CORINFO_RESOLVED_TOKENout& value);
+ void repResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, DWORD *exceptionCode);
+
+ void recTryResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool success);
+ void dmpTryResolveToken(const Agnostic_CORINFO_RESOLVED_TOKENin& key, const Agnostic_CORINFO_RESOLVED_TOKENout& value);
+ bool repTryResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken);
+
+ void recGetCallInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle, CORINFO_CALLINFO_FLAGS flags, CORINFO_CALL_INFO *pResult, DWORD exceptionCode);
+ void dmpGetCallInfo(const Agnostic_GetCallInfo& key, const Agnostic_CORINFO_CALL_INFO& value);
+ void repGetCallInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle, CORINFO_CALLINFO_FLAGS flags, CORINFO_CALL_INFO *pResult, DWORD *exceptionCode);
+ void repGetCallInfoFromMethodHandle(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CALL_INFO *pResult);
+
+ void recGetIntrinsicID(CORINFO_METHOD_HANDLE method, bool* pMustExpand, CorInfoIntrinsics result);
+ void dmpGetIntrinsicID(DWORDLONG key, DD value);
+ CorInfoIntrinsics repGetIntrinsicID(CORINFO_METHOD_HANDLE method, bool* pMustExpand);
+
+ void recGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CorInfoUnmanagedCallConv result);
+ void dmpGetUnmanagedCallConv(DWORDLONG key, DWORD result);
+ CorInfoUnmanagedCallConv repGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method);
+
+ void recIsInstantiationOfVerifiedGeneric(CORINFO_METHOD_HANDLE method, CorInfoInstantiationVerification result);
+ void dmpIsInstantiationOfVerifiedGeneric(DWORDLONG key, DWORD value);
+ CorInfoInstantiationVerification repIsInstantiationOfVerifiedGeneric(CORINFO_METHOD_HANDLE method);
+
+ void recAsCorInfoType(CORINFO_CLASS_HANDLE cls, CorInfoType result);
+ void dmpAsCorInfoType(DWORDLONG key, DWORD value);
+ CorInfoType repAsCorInfoType(CORINFO_CLASS_HANDLE cls);
+
+ void recIsValueClass(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpIsValueClass(DWORDLONG key, DWORD value);
+ BOOL repIsValueClass(CORINFO_CLASS_HANDLE cls);
+
+ void recIsStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpIsStructRequiringStackAllocRetBuf(DWORDLONG key, DWORD value);
+ BOOL repIsStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls);
+
+ void recGetClassSize(CORINFO_CLASS_HANDLE cls, unsigned result);
+ void dmpGetClassSize(DWORDLONG key, DWORD val);
+ unsigned repGetClassSize(CORINFO_CLASS_HANDLE cls);
+
+ void recGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls, unsigned result);
+ void dmpGetClassNumInstanceFields(DWORDLONG key, DWORD value);
+ unsigned repGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls);
+
+ void recGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls, CorInfoHelpFunc result);
+ void dmpGetNewArrHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls);
+
+ void recGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd, CorInfoHelpFunc result);
+ void dmpGetSharedCCtorHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd);
+
+ void recGetSecurityPrologHelper(CORINFO_METHOD_HANDLE ftn, CorInfoHelpFunc result);
+ void dmpGetSecurityPrologHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetSecurityPrologHelper(CORINFO_METHOD_HANDLE ftn);
+
+ void recGetTypeForBox(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result);
+ void dmpGetTypeForBox(DWORDLONG key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetTypeForBox(CORINFO_CLASS_HANDLE cls);
+
+ void recGetBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result);
+ void dmpGetBoxHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetBoxHelper(CORINFO_CLASS_HANDLE cls);
+
+ void recGetBuiltinClass(CorInfoClassId classId, CORINFO_CLASS_HANDLE result);
+ void dmpGetBuiltinClass(DWORD key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetBuiltinClass(CorInfoClassId classId);
+
+ void recGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls, CorInfoType result);
+ void dmpGetTypeForPrimitiveValueClass(DWORDLONG key, DWORD value);
+ CorInfoType repGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls);
+
+ void recGetParentType(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result);
+ void dmpGetParentType(DWORDLONG key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetParentType(CORINFO_CLASS_HANDLE cls);
+
+ void recIsSDArray(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpIsSDArray(DWORDLONG key, DWORD value);
+ BOOL repIsSDArray(CORINFO_CLASS_HANDLE cls);
+
+ void recIsInSIMDModule(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpIsInSIMDModule(DWORDLONG key, DWORD value);
+ BOOL repIsInSIMDModule(CORINFO_CLASS_HANDLE cls);
+
+ void recGetFieldClass(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE result);
+ void dmpGetFieldClass(DWORDLONG key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetFieldClass(CORINFO_FIELD_HANDLE field);
+
+ void recGetFieldOffset(CORINFO_FIELD_HANDLE field, unsigned result);
+ void dmpGetFieldOffset(DWORDLONG key, DWORD value);
+ unsigned repGetFieldOffset(CORINFO_FIELD_HANDLE field);
+
+ void recGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle, CorInfoHelpFunc result);
+ void dmpGetLazyStringLiteralHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle);
+
+ void recGetUnBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result);
+ void dmpGetUnBoxHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetUnBoxHelper(CORINFO_CLASS_HANDLE cls);
+
+ void recGetReadyToRunHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_LOOKUP_KIND* pGenericLookupKind, CorInfoHelpFunc id, CORINFO_CONST_LOOKUP* pLookup, bool result);
+ void dmpGetReadyToRunHelper(DWORDLONG key, DWORD value);
+ bool repGetReadyToRunHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_LOOKUP_KIND* pGenericLookupKind, CorInfoHelpFunc id, CORINFO_CONST_LOOKUP* pLookup);
+
+ void recGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod, CORINFO_CLASS_HANDLE delegateType, CORINFO_CONST_LOOKUP* pLookup);
+ void dmpGetReadyToRunDelegateCtorHelper(DWORDLONG key, DWORD value);
+ void repGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod, CORINFO_CLASS_HANDLE delegateType, CORINFO_CONST_LOOKUP* pLookup);
+
+ void recGetHelperFtn(CorInfoHelpFunc ftnNum, void **ppIndirection, void *result);
+ void dmpGetHelperFtn(DWORD key, DLDL value);
+ void* repGetHelperFtn(CorInfoHelpFunc ftnNum, void **ppIndirection);
+ bool fndGetHelperFtn(void *functionAddress, CorInfoHelpFunc *pResult);
+
+ void recGetJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE **ppIndirection, CORINFO_JUST_MY_CODE_HANDLE result);
+ void dmpGetJustMyCodeHandle(DWORDLONG key, DLDL value);
+ CORINFO_JUST_MY_CODE_HANDLE repGetJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE **ppIndirection);
+
+ void recGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult, CORINFO_ACCESS_FLAGS accessFlags);
+ void dmpGetFunctionEntryPoint(DLD key, DLD value);
+ void repGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult, CORINFO_ACCESS_FLAGS accessFlags);
+ bool fndGetFunctionEntryPoint(DLD value, CORINFO_METHOD_HANDLE *pResult);
+
+ void recConstructStringLiteral(CORINFO_MODULE_HANDLE module, mdToken metaTok, void *ppValue, InfoAccessType result);
+ void dmpConstructStringLiteral(DLD key, DLD value);
+ InfoAccessType repConstructStringLiteral(CORINFO_MODULE_HANDLE module, mdToken metaTok, void **ppValue);
+
+ void recEmptyStringLiteral(void **ppValue, InfoAccessType result);
+ void dmpEmptyStringLiteral(DWORD key, DLD value);
+ InfoAccessType repEmptyStringLiteral(void **ppValue);
+
+ void recGetArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE *vcTypeRet, CorInfoTypeWithMod result, DWORD exception);
+ void dmpGetArgType(const Agnostic_GetArgType& key, const Agnostic_GetArgType_Value& value);
+ CorInfoTypeWithMod repGetArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE *vcTypeRet, DWORD *exception);
+
+ void recGetArgNext(CORINFO_ARG_LIST_HANDLE args, CORINFO_ARG_LIST_HANDLE result);
+ void dmpGetArgNext(DWORDLONG key, DWORDLONG value);
+ CORINFO_ARG_LIST_HANDLE repGetArgNext(CORINFO_ARG_LIST_HANDLE args);
+
+ void recGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO *sig, CORINFO_CLASS_HANDLE memberParent);
+ void dmpGetMethodSig(DLDL key, const Agnostic_CORINFO_SIG_INFO& value);
+ void repGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO *sig, CORINFO_CLASS_HANDLE memberParent);
+
+ void recGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE result, DWORD exceptionCode);
+ void dmpGetArgClass(const Agnostic_GetArgClass& key, const Agnostic_GetArgClass_Value& value);
+ CORINFO_CLASS_HANDLE repGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, DWORD *exceptionCode);
+
+ void recGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, bool result, DWORD exceptionCode);
+ void dmpGetMethodInfo(DWORDLONG key, const Agnostic_GetMethodInfo& value);
+ bool repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, DWORD *exceptionCode);
+
+ void recGetNewHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CorInfoHelpFunc result);
+ void dmpGetNewHelper(const Agnostic_GetNewHelper& key, DWORD value);
+ CorInfoHelpFunc repGetNewHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle);
+
+ void recEmbedGenericHandle(CORINFO_RESOLVED_TOKEN *pResolvedToken, BOOL fEmbedParent, CORINFO_GENERICHANDLE_RESULT *pResult);
+ void dmpEmbedGenericHandle(const Agnostic_EmbedGenericHandle& key, const Agnostic_CORINFO_GENERICHANDLE_RESULT& value);
+ void repEmbedGenericHandle(CORINFO_RESOLVED_TOKEN *pResolvedToken, BOOL fEmbedParent, CORINFO_GENERICHANDLE_RESULT *pResult);
+
+ void recGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE *clause);
+ void dmpGetEHinfo(DLD key, const Agnostic_CORINFO_EH_CLAUSE& value);
+ void repGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE *clause);
+
+ void recGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection);
+ void dmpGetMethodVTableOffset(DWORDLONG key, DD value);
+ void repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection);
+
+ void recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_CLASS_HANDLE result);
+ void dmpGetTokenTypeAsHandle(const Agnostic_CORINFO_RESOLVED_TOKEN& key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken);
+
+ void recGetFieldInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult);
+ void dmpGetFieldInfo(const Agnostic_GetFieldInfo& key, const Agnostic_CORINFO_FIELD_INFO& value);
+ void repGetFieldInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult);
+
+ void recEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void **ppIndirection, CORINFO_METHOD_HANDLE result);
+ void dmpEmbedMethodHandle(DWORDLONG key, DLDL value);
+ CORINFO_METHOD_HANDLE repEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void **ppIndirection);
+
+ void recGetFieldAddress(CORINFO_FIELD_HANDLE field, void **ppIndirection, void *result, CorInfoType cit);
+ void dmpGetFieldAddress(DWORDLONG key, const Agnostic_GetFieldAddress& value);
+ void* repGetFieldAddress(CORINFO_FIELD_HANDLE field, void **ppIndirection);
+
+ void recGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE *gcPtrs, unsigned len, unsigned result);
+ void dmpGetClassGClayout(DWORDLONG key, const Agnostic_GetClassGClayout& value);
+ unsigned repGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE *gcPtrs);
+
+ void recGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, BOOL fDoubleAlignHint, unsigned result);
+ void dmpGetClassAlignmentRequirement(DLD key, DWORD value);
+ unsigned repGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, BOOL fDoubleAlignHint);
+
+ void recCanAccessClass(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper, CorInfoIsAccessAllowedResult result);
+ void dmpCanAccessClass(const Agnostic_CanAccessClassIn& key, const Agnostic_CanAccessClassOut& value);
+ CorInfoIsAccessAllowedResult repCanAccessClass(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper);
+
+ void recGetCastingHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, bool fThrowing, CorInfoHelpFunc result);
+ void dmpGetCastingHelper(const Agnostic_GetCastingHelper& key, DWORD value);
+ CorInfoHelpFunc repGetCastingHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, bool fThrowing);
+
+ void recEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void **ppIndirection, CORINFO_MODULE_HANDLE result);
+ void dmpEmbedModuleHandle(DWORDLONG key, DLDL value);
+ CORINFO_MODULE_HANDLE repEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void **ppIndirection);
+
+ void recEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void **ppIndirection, CORINFO_CLASS_HANDLE result);
+ void dmpEmbedClassHandle(DWORDLONG key, DLDL value);
+ CORINFO_CLASS_HANDLE repEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void **ppIndirection);
+
+ void recPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, BOOL result);
+ void dmpPInvokeMarshalingRequired(const Agnostic_PInvokeMarshalingRequired& key, DWORD value);
+ BOOL repPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig);
+
+ void recFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig);
+ void dmpFindSig(const Agnostic_FindSig& key, const Agnostic_CORINFO_SIG_INFO& value);
+ void repFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig);
+
+ void recGetEEInfo(CORINFO_EE_INFO *pEEInfoOut);
+ void dmpGetEEInfo(DWORD key, const Agnostic_CORINFO_EE_INFO& value);
+ void repGetEEInfo(CORINFO_EE_INFO *pEEInfoOut);
+
+ void recGetGSCookie(GSCookie *pCookieVal, GSCookie **ppCookieVal);
+ void dmpGetGSCookie(DWORD key, DLDL value);
+ void repGetGSCookie(GSCookie *pCookieVal, GSCookie **ppCookieVal);
+
+ void recGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE *pModule, void **ppIndirection, size_t result);
+ void dmpGetClassModuleIdForStatics(DWORDLONG key, const Agnostic_GetClassModuleIdForStatics& value);
+ size_t repGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE *pModule, void **ppIndirection);
+
+ void recGetThreadTLSIndex(void **ppIndirection, DWORD result);
+ void dmpGetThreadTLSIndex(DWORD key, DLD value);
+ DWORD repGetThreadTLSIndex(void **ppIndirection);
+
+ void recGetInlinedCallFrameVptr(void **ppIndirection, const void * result);
+ void dmpGetInlinedCallFrameVptr(DWORD key, DLDL value);
+ const void * repGetInlinedCallFrameVptr(void **ppIndirection);
+
+ void recGetAddrOfCaptureThreadGlobal(void **ppIndirection, LONG * result);
+ void dmpGetAddrOfCaptureThreadGlobal(DWORD key, DLDL value);
+ LONG * repGetAddrOfCaptureThreadGlobal(void **ppIndirection);
+
+ void recGetClassDomainID(CORINFO_CLASS_HANDLE cls, void **ppIndirection, unsigned result);
+ void dmpGetClassDomainID(DWORDLONG key, DLD value);
+ unsigned repGetClassDomainID(CORINFO_CLASS_HANDLE cls, void **ppIndirection);
+
+ void recGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND *result);
+ void dmpGetLocationOfThisType(DWORDLONG key, const Agnostic_CORINFO_LOOKUP_KIND& value);
+ CORINFO_LOOKUP_KIND repGetLocationOfThisType(CORINFO_METHOD_HANDLE context);
+
+ void recGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs *pCtorData, CORINFO_METHOD_HANDLE result);
+ void dmpGetDelegateCtor(const Agnostic_GetDelegateCtorIn& key, const Agnostic_GetDelegateCtorOut& value);
+ CORINFO_METHOD_HANDLE repGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs *pCtorData);
+
+ void recGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult);
+ void dmpGetFunctionFixedEntryPoint(DWORDLONG key, const Agnostic_CORINFO_CONST_LOOKUP& value);
+ void repGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult);
+
+ void recGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num, CORINFO_FIELD_HANDLE result);
+ void dmpGetFieldInClass(DLD key, DWORDLONG value);
+ CORINFO_FIELD_HANDLE repGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num);
+
+ void recGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE *structType, CORINFO_CLASS_HANDLE memberParent, CorInfoType result);
+ void dmpGetFieldType(DLDL key, DLD value);
+ CorInfoType repGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE *structType, CORINFO_CLASS_HANDLE memberParent);
+
+ void recGetFieldName(CORINFO_FIELD_HANDLE ftn, const char **moduleName, const char* result);
+ void dmpGetFieldName(DWORDLONG key, DD value);
+ const char* repGetFieldName(CORINFO_FIELD_HANDLE ftn, const char **moduleName);
+
+ void recCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpCanInlineTypeCheckWithObjectVTable(DWORDLONG key, DWORD value);
+ BOOL repCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls);
+
+ void recSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method, BOOL result);
+ void dmpSatisfiesMethodConstraints(DLDL key, DWORD value);
+ BOOL repSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method);
+
+ void recInitConstraintsForVerification(CORINFO_METHOD_HANDLE method, BOOL *pfHasCircularClassConstraints,
+ BOOL *pfHasCircularMethodConstraint);
+ void dmpInitConstraintsForVerification(DWORDLONG key, DD value);
+ void repInitConstraintsForVerification(CORINFO_METHOD_HANDLE method, BOOL *pfHasCircularClassConstraints,
+ BOOL *pfHasCircularMethodConstraint);
+
+ void recIsValidStringRef(CORINFO_MODULE_HANDLE module, unsigned metaTOK, BOOL result);
+ void dmpIsValidStringRef(DLD key, DWORD value);
+ BOOL repIsValidStringRef(CORINFO_MODULE_HANDLE module, unsigned metaTOK);
+
+ void recGetHelperName(CorInfoHelpFunc funcNum, const char* result);
+ void dmpGetHelperName(DWORD key, DWORD value);
+ const char* repGetHelperName(CorInfoHelpFunc funcNum);
+
+ void recCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent, BOOL result);
+ void dmpCanCast(DLDL key, DWORD value);
+ BOOL repCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent);
+
+ void recGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE *clsRet, CorInfoType result);
+ void dmpGetChildType(DWORDLONG key, DLD value);
+ CorInfoType repGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE *clsRet);
+
+ void recGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size, void *result);
+ void dmpGetArrayInitializationData(DLD key, DWORDLONG value);
+ void *repGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size);
+
+ void recFilterException(struct _EXCEPTION_POINTERS *pExceptionPointers, int result);
+ void dmpFilterException(DWORD key, DWORD value);
+ int repFilterException(struct _EXCEPTION_POINTERS *pExceptionPointers);
+
+ void recHandleException(struct _EXCEPTION_POINTERS *pExceptionPointers);
+ void dmpHandleException(DWORD key, DWORD value);
+
+ void recGetAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method, void **ppIndirection, void* result);
+ void dmpGetAddressOfPInvokeFixup(DWORDLONG key, DLDL value);
+ void* repGetAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method, void **ppIndirection);
+
+ void recGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP *pLookup);
+ void dmpGetAddressOfPInvokeTarget(DWORDLONG key, DLD value);
+ void repGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP *pLookup);
+
+ void recSatisfiesClassConstraints(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpSatisfiesClassConstraints(DWORDLONG key, DWORD value);
+ BOOL repSatisfiesClassConstraints(CORINFO_CLASS_HANDLE cls);
+
+ void recGetMethodHash(CORINFO_METHOD_HANDLE ftn, unsigned result);
+ void dmpGetMethodHash(DWORDLONG key, DWORD value);
+ unsigned repGetMethodHash(CORINFO_METHOD_HANDLE ftn);
+
+ void recCanTailCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE declaredCalleeHnd, CORINFO_METHOD_HANDLE exactCalleeHnd,
+ bool fIsTailPrefix, bool result);
+ void dmpCanTailCall(const Agnostic_CanTailCall& key, DWORD value);
+ bool repCanTailCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE declaredCalleeHnd, CORINFO_METHOD_HANDLE exactCalleeHnd,
+ bool fIsTailPrefix);
+
+ void recIsCompatibleDelegate(CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls,
+ CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, BOOL *pfIsOpenDelegate, BOOL result);
+ void dmpIsCompatibleDelegate(const Agnostic_IsCompatibleDelegate& key, DD value);
+ BOOL repIsCompatibleDelegate(CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls,
+ CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, BOOL *pfIsOpenDelegate);
+
+ void recIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd, BOOL result);
+ void dmpIsDelegateCreationAllowed(DLDL key, DWORD value);
+ BOOL repIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd);
+
+ void recCanSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHandle, BOOL skip, CorInfoCanSkipVerificationResult result);
+ void dmpCanSkipMethodVerification(DLD key, DWORD value);
+ CorInfoCanSkipVerificationResult repCanSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHandle, BOOL skip);
+
+ void recFindCallSiteSig(CORINFO_MODULE_HANDLE module, unsigned methTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig);
+ void dmpFindCallSiteSig(const Agnostic_FindCallSiteSig& key, const Agnostic_CORINFO_SIG_INFO& value);
+ void repFindCallSiteSig(CORINFO_MODULE_HANDLE module, unsigned methTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig);
+
+ void recShouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope, BOOL result);
+ void dmpShouldEnforceCallvirtRestriction(DWORDLONG key, DWORD value);
+ BOOL repShouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope);
+
+ void recGetMethodSync(CORINFO_METHOD_HANDLE ftn, void **ppIndirection, void* result);
+ void dmpGetMethodSync(DWORDLONG key, DLDL value);
+ void* repGetMethodSync(CORINFO_METHOD_HANDLE ftn, void **ppIndirection);
+
+ void recGetVarArgsHandle(CORINFO_SIG_INFO *pSig, void **ppIndirection, CORINFO_VARARGS_HANDLE result);
+ void dmpGetVarArgsHandle(const Agnostic_CORINFO_SIG_INFO& key, DLDL value);
+ CORINFO_VARARGS_HANDLE repGetVarArgsHandle(CORINFO_SIG_INFO *pSig, void **ppIndirection);
+
+ void recCanGetVarArgsHandle(CORINFO_SIG_INFO *pSig, bool result);
+ void dmpCanGetVarArgsHandle(const Agnostic_CORINFO_SIG_INFO& key, DWORD value);
+ bool repCanGetVarArgsHandle(CORINFO_SIG_INFO *pSig);
+
+ void recGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void **ppIndirection, DWORD result);
+ void dmpGetFieldThreadLocalStoreID(DWORDLONG key, DLD value);
+ DWORD repGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void **ppIndirection);
+
+ void recGetBBProfileData(CORINFO_METHOD_HANDLE ftnHnd, ULONG *count, ICorJitInfo::ProfileBuffer **profileBuffer,
+ ULONG *numRuns, HRESULT result);
+ void dmpGetBBProfileData(DWORDLONG key, const Agnostic_GetBBProfileData& value);
+ HRESULT repGetBBProfileData(CORINFO_METHOD_HANDLE ftnHnd, ULONG *count, ICorJitInfo::ProfileBuffer **profileBuffer,
+ ULONG *numRuns);
+
+ void recMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, CORINFO_CLASS_HANDLE result);
+ void dmpMergeClasses(DLDL key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2);
+
+ void recGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection, LPVOID result);
+ void dmpGetCookieForPInvokeCalliSig(const Agnostic_CORINFO_SIG_INFO& key, DLDL value);
+ LPVOID repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection);
+
+ void recCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, bool result);
+ void dmpCanGetCookieForPInvokeCalliSig(const Agnostic_CORINFO_SIG_INFO& key, DWORD value);
+ bool repCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig);
+
+ void recCanAccessFamily(CORINFO_METHOD_HANDLE hCaller, CORINFO_CLASS_HANDLE hInstanceType, BOOL result);
+ void dmpCanAccessFamily(DLDL key, DWORD value);
+ BOOL repCanAccessFamily(CORINFO_METHOD_HANDLE hCaller, CORINFO_CLASS_HANDLE hInstanceType);
+
+ void recErrorList(const char *error);
+ void dmpErrorList(DWORD key, DWORD value);
+
+ void recGetProfilingHandle(BOOL *pbHookFunction, void **pProfilerHandle, BOOL *pbIndirectedHandles);
+ void dmpGetProfilingHandle(DWORD key, const Agnostic_GetProfilingHandle& value);
+ void repGetProfilingHandle(BOOL *pbHookFunction, void **pProfilerHandle, BOOL *pbIndirectedHandles);
+
+ void recEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void **ppIndirection, CORINFO_FIELD_HANDLE result);
+ void dmpEmbedFieldHandle(DWORDLONG key, DLDL value);
+ CORINFO_FIELD_HANDLE repEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void **ppIndirection);
+
+ void recAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, BOOL result);
+ void dmpAreTypesEquivalent(DLDL key, DWORD value);
+ BOOL repAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2);
+
+ void recFindNameOfToken(CORINFO_MODULE_HANDLE module, mdToken metaTOK, char * szFQName, size_t FQNameCapacity, size_t result);
+ void dmpFindNameOfToken(DLD key, DLD value);
+ size_t repFindNameOfToken(CORINFO_MODULE_HANDLE module, mdToken metaTOK, char * szFQName, size_t FQNameCapacity);
+
+ void recGetSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr, bool result);
+ void dmpGetSystemVAmd64PassStructInRegisterDescriptor(DWORDLONG key, const Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor& value);
+ bool repGetSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr);
+
+ void recGetRelocTypeHint(void * target, WORD result);
+ void dmpGetRelocTypeHint(DWORDLONG key, DWORD value);
+ WORD repGetRelocTypeHint(void * target);
+
+ void recIsWriteBarrierHelperRequired(CORINFO_FIELD_HANDLE field, bool result);
+ void dmpIsWriteBarrierHelperRequired(DWORDLONG key, DWORD value);
+ bool repIsWriteBarrierHelperRequired(CORINFO_FIELD_HANDLE field);
+
+ void recIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK, BOOL result);
+ void dmpIsValidToken(DLD key, DWORD value);
+ BOOL repIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK);
+
+ void recGetClassName(CORINFO_CLASS_HANDLE cls, const char* result);
+ void dmpGetClassName(DWORDLONG key, DWORD value);
+ const char* repGetClassName(CORINFO_CLASS_HANDLE cls);
+
+ void recAppendClassName(CORINFO_CLASS_HANDLE cls, BOOL fNamespace, BOOL fFullInst, BOOL fAssembly, const WCHAR* result);
+ void dmpAppendClassName(const Agnostic_AppendClassName& key, DWORD value);
+ const WCHAR* repAppendClassName(CORINFO_CLASS_HANDLE cls, BOOL fNamespace, BOOL fFullInst, BOOL fAssembly);
+
+ void recGetTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, CorInfoHelperTailCallSpecialHandling flags, void* result);
+ void dmpGetTailCallCopyArgsThunk(const Agnostic_GetTailCallCopyArgsThunk& key, DWORDLONG value);
+ void* repGetTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, CorInfoHelperTailCallSpecialHandling flags);
+
+ void recGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod, mdMethodDef result);
+ void dmpGetMethodDefFromMethod(DWORDLONG key, DWORD value);
+ mdMethodDef repGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod);
+
+ void recCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, BOOL fOptional, BOOL result);
+ void dmpCheckMethodModifier(const Agnostic_CheckMethodModifier& key, DWORD value);
+ BOOL repCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, BOOL fOptional);
+
+ void recGetPInvokeUnmanagedTarget(CORINFO_METHOD_HANDLE method, void **ppIndirection, void* result);
+ void dmpGetPInvokeUnmanagedTarget(DWORDLONG key, DLDL value);
+ void* repGetPInvokeUnmanagedTarget(CORINFO_METHOD_HANDLE method, void **ppIndirection);
+
+ void recGetArrayRank(CORINFO_CLASS_HANDLE cls, unsigned result);
+ void dmpGetArrayRank(DWORDLONG key, DWORD value);
+ unsigned repGetArrayRank(CORINFO_CLASS_HANDLE cls);
+
+ void recIsFieldStatic(CORINFO_FIELD_HANDLE fhld, bool result);
+ void dmpIsFieldStatic(DWORDLONG key, DWORD value);
+ bool repIsFieldStatic(CORINFO_FIELD_HANDLE fhld);
+
+ void recGetIntConfigValue(const wchar_t *name, int defaultValue, int result);
+ void dmpGetIntConfigValue(const Agnostic_ConfigIntInfo& key, int value);
+ int repGetIntConfigValue(const wchar_t *name, int defaultValue);
+
+ void recGetStringConfigValue(const wchar_t *name, const wchar_t *result);
+ void dmpGetStringConfigValue(DWORD nameIndex, DWORD result);
+ const wchar_t *repGetStringConfigValue(const wchar_t *name);
+
+ CompileResult *cr;
+ CompileResult *originalCR;
+ int index;
+
+private:
+
+ #define LWM(map,key,value) LightWeightMap<key, value>* map;
+ #define DENSELWM(map,value) DenseLightWeightMap<value>* map;
+ #include "lwmlist.h"
+
+};
+
+
+// ********************* Please keep this up-to-date to ease adding more ***************
+// Highest packet number: 158
+// *************************************************************************************
+enum mcPackets
+{
+ Packet_AppendClassName = 149, // Added 8/6/2014 - needed for SIMD
+ Packet_AreTypesEquivalent = 1,
+ Packet_AsCorInfoType = 2,
+ Packet_CanAccessClass = 3,
+ Packet_CanAccessFamily = 4,
+ Packet_CanCast = 5,
+ Retired8 = 6,
+ Packet_GetLazyStringLiteralHelper = 147, //Added 12/20/2013 - as a replacement for CanEmbedModuleHandleForHelper
+ Packet_CanGetCookieForPInvokeCalliSig = 7,
+ Packet_CanGetVarArgsHandle = 8,
+ Packet_CanInline = 9,
+ Packet_CanInlineTypeCheckWithObjectVTable = 10,
+ Packet_CanSkipMethodVerification = 11,
+ Packet_CanTailCall = 12,
+ Retired4 = 13,
+ Packet_CheckMethodModifier = 142, //retired as 13 on 2013/07/04
+ Retired3 = 14,
+ Retired5 = 141, //retired as 14 on 2013/07/03
+ Packet_CompileMethod = 143, //retired as 141 on 2013/07/09
+ Packet_ConstructStringLiteral = 15,
+ Packet_EmbedClassHandle = 16,
+ Packet_EmbedFieldHandle = 17,
+ Packet_EmbedGenericHandle = 18,
+ Packet_EmbedMethodHandle = 19,
+ Packet_EmbedModuleHandle = 20,
+ Packet_EmptyStringLiteral = 21,
+ Packet_Environment = 136, //Added 4/3/2013
+ Packet_ErrorList = 22,
+ Packet_FilterException = 134,
+ Packet_FindCallSiteSig = 23,
+ Retired7 = 24,
+ Packet_FindNameOfToken = 145,//Added 7/19/2013 - adjusted members to proper types
+ Packet_GetSystemVAmd64PassStructInRegisterDescriptor = 156, //Added 2/17/2016
+ Packet_FindSig = 25,
+ Packet_GetAddressOfPInvokeFixup = 26,
+ Packet_GetAddressOfPInvokeTarget = 153, //Added 2/3/2016
+ Packet_GetAddrOfCaptureThreadGlobal = 27,
+ Retired1 = 28,
+ Packet_GetArgClass = 139, //retired as 28 on 2013/07/03
+ Packet_GetArgNext = 29,
+ Retired2 = 30,
+ Packet_GetArgType = 140, //retired as 30 on 2013/07/03
+ Packet_GetArrayInitializationData = 31,
+ Packet_GetArrayRank = 32,
+ Packet_GetBBProfileData = 33,
+ Packet_GetBoundaries = 34,
+ Packet_GetBoxHelper = 35,
+ Packet_GetBuiltinClass = 36,
+ Packet_GetCallInfo = 37,
+ Packet_GetCastingHelper = 38,
+ Packet_GetChildType = 39,
+ Packet_GetClassAlignmentRequirement = 40,
+ Packet_GetClassAttribs = 41,
+ Packet_GetClassDomainID = 42,
+ Packet_GetClassGClayout = 43,
+ Packet_GetClassModuleIdForStatics = 44,
+ Packet_GetClassName = 45,
+ Packet_GetClassNumInstanceFields = 46,
+ Packet_GetClassSize = 47,
+ Packet_GetIntConfigValue = 151, //Added 2/12/2015
+ Packet_GetStringConfigValue = 152, // Added 2/12/2015
+ Packet_GetCookieForPInvokeCalliSig = 48,
+ Packet_GetDelegateCtor = 49,
+ Packet_GetEEInfo = 50,
+ Packet_GetEHinfo = 51,
+ Packet_GetFieldAddress = 52,
+ Packet_GetFieldClass = 53,
+ Packet_GetFieldInClass = 54,
+ Packet_GetFieldInfo = 55,
+ Packet_GetFieldName = 56,
+ Packet_GetFieldOffset = 57,
+ Packet_GetFieldThreadLocalStoreID = 58,
+ Packet_GetFieldType = 59,
+ Packet_GetFunctionEntryPoint = 60,
+ Packet_GetFunctionFixedEntryPoint = 61,
+ Packet_GetGSCookie = 62,
+ Packet_GetHelperFtn = 63,
+ Packet_GetHelperName = 64,
+ Packet_GetInlinedCallFrameVptr = 65,
+ Packet_GetIntrinsicID = 66,
+ Packet_GetJitFlags = 154, //Added 2/3/2016
+ Packet_GetJitTimeLogFilename = 67,
+ Packet_GetJustMyCodeHandle = 68,
+ Packet_GetLocationOfThisType = 69,
+ Packet_GetMethodAttribs = 70,
+ Packet_GetMethodClass = 71,
+ Packet_GetMethodDefFromMethod = 72,
+ Packet_GetMethodHash = 73,
+ Packet_GetMethodInfo = 74,
+ Packet_GetMethodName = 75,
+ Packet_GetMethodSig = 76,
+ Packet_GetMethodSync = 77,
+ Packet_GetMethodVTableOffset = 78,
+ Packet_GetNewArrHelper = 79,
+ Packet_GetNewHelper = 80,
+ Packet_GetParentType = 81,
+ Packet_GetPInvokeUnmanagedTarget = 82,
+ Packet_GetProfilingHandle = 83,
+ Packet_GetRelocTypeHint = 84,
+ Packet_GetSecurityPrologHelper = 85,
+ Packet_GetSharedCCtorHelper = 86,
+ Packet_GetTailCallCopyArgsThunk = 87,
+ Packet_GetThreadTLSIndex = 88,
+ Packet_GetTokenTypeAsHandle = 89,
+ Packet_GetTypeForBox = 90,
+ Packet_GetTypeForPrimitiveValueClass = 91,
+ Packet_GetUnBoxHelper = 92,
+ Packet_GetReadyToRunHelper = 150, //Added 10/10/2014
+ Packet_GetReadyToRunDelegateCtorHelper = 157, //Added 3/30/2016
+ Packet_GetUnmanagedCallConv = 94,
+ Packet_GetVarArgsHandle = 95,
+ Packet_GetVars = 96,
+ Packet_HandleException = 135,
+ Packet_InitClass = 97,
+ Packet_InitConstraintsForVerification = 98,
+ Packet_IsCompatibleDelegate = 99,
+ Packet_IsDelegateCreationAllowed = 155,
+ Packet_IsFieldStatic = 137,//Added 4/9/2013 - needed for 4.5.1
+ Packet_IsInSIMDModule = 148,// Added 6/18/2014 - SIMD support
+ Packet_IsInstantiationOfVerifiedGeneric = 100,
+ Packet_IsSDArray = 101,
+ Packet_IsStructRequiringStackAllocRetBuf = 102,
+ Packet_IsValidStringRef = 103,
+ Retired6 = 104,
+ Packet_IsValidToken = 144,//Added 7/19/2013 - adjusted members to proper types
+ Packet_IsValueClass = 105,
+ Packet_IsWriteBarrierHelperRequired = 106,
+ Packet_MergeClasses = 107,
+ Packet_PInvokeMarshalingRequired = 108,
+ Packet_ResolveToken = 109,
+ Packet_TryResolveToken = 158, //Added 4/26/2016
+ Packet_SatisfiesClassConstraints = 110,
+ Packet_SatisfiesMethodConstraints = 111,
+ Packet_ShouldEnforceCallvirtRestriction = 112,
+
+ PacketCR_AddressMap = 113,
+ PacketCR_AllocBBProfileBuffer = 131,
+ PacketCR_AllocGCInfo = 114,
+ PacketCR_AllocMem = 115,
+ PacketCR_AllocUnwindInfo = 132,
+ PacketCR_AssertLog = 138, //Added 6/10/2013 - added to nicely support ilgen
+ PacketCR_CallLog = 116,
+ PacketCR_ClassMustBeLoadedBeforeCodeIsRun = 117,
+ PacketCR_CompileMethod = 118,
+ PacketCR_MessageLog = 119,
+ PacketCR_MethodMustBeLoadedBeforeCodeIsRun = 120,
+ PacketCR_ProcessName = 121,
+ PacketCR_RecordRelocation = 122,
+ PacketCR_ReportFatalError = 123,
+ PacketCR_ReportInliningDecision = 124,
+ PacketCR_ReportTailCallDecision = 125,
+ PacketCR_ReserveUnwindInfo = 133,
+ PacketCR_SetBoundaries = 126,
+ PacketCR_SetEHcount = 127,
+ PacketCR_SetEHinfo = 128,
+ PacketCR_SetMethodAttribs = 129,
+ PacketCR_SetVars = 130,
+ PacketCR_RecordCallSite = 146, //Added 10/28/2013 - to support indirect calls
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.cpp
new file mode 100644
index 0000000000..55f8045938
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.cpp
@@ -0,0 +1,127 @@
+//
+// 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 "methodcontext.h"
+#include "methodcontextiterator.h"
+
+
+bool MethodContextIterator::Initialize(const char* fileName)
+{
+ m_hFile = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (m_hFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open file '%s'. GetLastError()=%u", fileName, GetLastError());
+ return false;
+ }
+
+ LARGE_INTEGER DataTemp;
+ if (GetFileSizeEx(m_hFile, &DataTemp)==0)
+ {
+ LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ CloseHandle(m_hFile);
+ m_hFile = INVALID_HANDLE_VALUE;
+ return false;
+ }
+
+ m_fileSize = DataTemp.QuadPart;
+
+ if (m_progressReport)
+ {
+ m_timer->Start();
+ }
+
+ return true;
+}
+
+bool MethodContextIterator::Destroy()
+{
+ bool ret = true; // assume success
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle(m_hFile))
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ ret = false;
+ }
+ m_hFile = INVALID_HANDLE_VALUE;
+ }
+ delete m_mc;
+ m_mc = nullptr;
+
+ if (m_index < m_indexCount)
+ {
+ LogWarning("Didn't use all of index count input: %d < %d (i.e., didn't see MC #%d)",
+ m_index, m_indexCount, m_indexes[m_index]);
+ }
+
+ delete m_timer;
+ m_timer = nullptr;
+
+ return ret;
+}
+
+bool MethodContextIterator::MoveNext()
+{
+ if (m_mc != nullptr)
+ {
+ delete m_mc;
+ m_mc = nullptr;
+ }
+
+ while (true)
+ {
+ // Figure out where the pointer is currently.
+ LARGE_INTEGER pos;
+ pos.QuadPart = 0;
+ if (SetFilePointerEx(m_hFile, pos, &m_pos, FILE_CURRENT) == 0)
+ {
+ LogError("SetFilePointerEx failed. GetLastError()=%u", GetLastError());
+ return false; // any failure causes us to bail out.
+ }
+
+ if (m_pos.QuadPart >= m_fileSize)
+ {
+ return false;
+ }
+
+ // Load the current method context.
+ m_methodContextNumber++;
+
+ if (m_progressReport)
+ {
+ if (m_methodContextNumber % 500 == 0)
+ {
+ m_timer->Stop();
+ LogVerbose("Loaded %d at %d per second", m_methodContextNumber, (int)((double)500 / m_timer->GetSeconds()));
+ m_timer->Start();
+ }
+ }
+
+ if (!MethodContext::Initialize(m_methodContextNumber, m_hFile, &m_mc))
+ return false;
+
+ // If we have an array of indexes, skip the loaded indexes that have not been specified.
+
+ if (m_indexCount == -1)
+ {
+ break;
+ }
+ else if (m_index == m_indexCount)
+ {
+ return false; // we're beyond the array of indexes
+ }
+ else if (m_index < m_indexCount)
+ {
+ if (m_indexes[m_index] == m_methodContextNumber)
+ {
+ m_index++;
+ break;
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.h b/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.h
new file mode 100644
index 0000000000..d6002ba852
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.h
@@ -0,0 +1,104 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "simpletimer.h"
+
+// Class to implement method context hive reading and iterating.
+
+class MethodContextIterator
+{
+public:
+
+ MethodContextIterator(bool progressReport = false)
+ : m_hFile(INVALID_HANDLE_VALUE)
+ , m_fileSize(0)
+ , m_methodContextNumber(0)
+ , m_mc(nullptr)
+ , m_indexCount(-1)
+ , m_index(0)
+ , m_indexes(nullptr)
+ , m_progressReport(progressReport)
+ , m_timer(nullptr)
+ {
+ if (m_progressReport)
+ {
+ m_timer = new SimpleTimer();
+ }
+ }
+
+ MethodContextIterator(const int indexCount, const int* indexes, bool progressReport = false)
+ : m_hFile(INVALID_HANDLE_VALUE)
+ , m_fileSize(0)
+ , m_methodContextNumber(0)
+ , m_mc(nullptr)
+ , m_indexCount(indexCount)
+ , m_index(0)
+ , m_indexes(indexes)
+ , m_progressReport(progressReport)
+ , m_timer(nullptr)
+ {
+ if (m_progressReport)
+ {
+ m_timer = new SimpleTimer();
+ }
+ }
+
+ ~MethodContextIterator()
+ {
+ Destroy();
+ }
+
+ bool Initialize(const char* fileName);
+
+ bool Destroy();
+
+ bool MoveNext();
+
+ // The iterator class owns the memory returned by Current(); the caller should not delete it.
+ MethodContext* Current()
+ {
+ return m_mc;
+ }
+
+ // In this case, we are giving ownership of the MethodContext* to the caller. So, null out m_mc
+ // before we return, so we don't attempt to delete it in this class.
+ MethodContext* CurrentTakeOwnership()
+ {
+ MethodContext* ret = m_mc;
+ m_mc = nullptr;
+ return ret;
+ }
+
+ // Return the file position offset of the current method context.
+ __int64 CurrentPos()
+ {
+ return m_pos.QuadPart;
+ }
+
+ int MethodContextNumber()
+ {
+ return m_methodContextNumber;
+ }
+
+private:
+
+ HANDLE m_hFile;
+ int64_t m_fileSize;
+ int m_methodContextNumber;
+ MethodContext* m_mc;
+ LARGE_INTEGER m_pos;
+
+ // If m_indexCount==-1, use all method contexts. Otherwise, m_indexCount is the number of elements in the
+ // m_indexes array, which contains a sorted set of method context indexes to return. In this case, m_index
+ // is the index of the current element in m_indexes.
+ const int m_indexCount;
+ int m_index;
+ const int* m_indexes;
+
+ // Should we log a progress report as we are loading the method contexts?
+ // The timer is only used when m_progressReport==true.
+ bool m_progressReport;
+ SimpleTimer* m_timer;
+}; \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp
new file mode 100644
index 0000000000..1e375e0d6a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp
@@ -0,0 +1,469 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MethodContextReader.cpp - Abstraction for reading MethodContexts
+// Should eventually support multithreading
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "tocfile.h"
+#include "methodcontextreader.h"
+#include "methodcontext.h"
+#include "logging.h"
+#include "runtimedetails.h"
+
+// Just a helper...
+HANDLE MethodContextReader::OpenFile(const char *inputFile, DWORD flags)
+{
+ HANDLE fileHandle = CreateFileA(inputFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | flags, NULL);
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open file '%s'. GetLastError()=%u", inputFile, GetLastError());
+ }
+ return fileHandle;
+}
+
+static std::string to_lower(const std::string &input)
+{
+ std::string res = input;
+ std::transform(input.cbegin(), input.cend(), res.begin(), tolower);
+ return res;
+}
+
+// Looks for a file named foo.origSuffix.newSuffix or foo.newSuffix
+// but only if foo.origSuffix exists.
+//
+// Note: filename extensions must be lower-case, even on case-sensitive file systems!
+std::string MethodContextReader::CheckForPairedFile(const std::string &fileName, const std::string &origSuffix, const std::string &newSuffix)
+{
+ std::string tmp = to_lower(origSuffix);
+
+ // First, check to see if foo.origSuffix exists
+ size_t suffix_offset = fileName.find_last_of('.');
+ if ((SSIZE_T)suffix_offset <= 0
+ || (tmp != to_lower(fileName.substr(suffix_offset)))
+ || (GetFileAttributesA(fileName.c_str()) == INVALID_FILE_ATTRIBUTES))
+ return std::string();
+
+ // next, check foo.orig.new from foo.orig
+ tmp = fileName + newSuffix;
+ if (GetFileAttributesA(tmp.c_str()) != INVALID_FILE_ATTRIBUTES)
+ return tmp;
+
+ // Now let's check for foo.new from foo.new.orig
+ tmp = fileName.substr(0, suffix_offset);
+ if (GetFileAttributesA(tmp.c_str()) != INVALID_FILE_ATTRIBUTES)
+ return tmp;
+
+ // Finally, lets try foo.orig from foo.new
+ tmp += newSuffix;
+ if (GetFileAttributesA(tmp.c_str()) != INVALID_FILE_ATTRIBUTES)
+ return tmp;
+
+ return std::string();
+}
+
+
+MethodContextReader::MethodContextReader(const char *inputFileName, const int *indexes, int indexCount, char *hash, int offset, int increment)
+ : fileHandle(INVALID_HANDLE_VALUE)
+ , fileSize(0)
+ , curMCIndex(0)
+ , Indexes(indexes)
+ , IndexCount(indexCount)
+ , curIndexPos(0)
+ , Hash(hash)
+ , curTOCIndex(0)
+ , Offset(offset)
+ , Increment(increment)
+{
+ this->mutex = CreateMutexA(NULL, FALSE, nullptr);
+
+ std::string tocFileName, mchFile;
+
+ // First, check to see if they passed an MCH file (look for a paired MCT file)
+ tocFileName = MethodContextReader::CheckForPairedFile(inputFileName, ".mch", ".mct");
+ if (!tocFileName.empty())
+ {
+ mchFile = inputFileName;
+ }
+ else
+ {
+ // Okay, it wasn't an MCH file, let's check to see if it was an MCT file
+ // so check for a paired MCH file instead
+ mchFile = MethodContextReader::CheckForPairedFile(inputFileName, ".mct", ".mch");
+ if (!mchFile.empty())
+ {
+ tocFileName = inputFileName;
+ }
+ else
+ {
+ mchFile = inputFileName;
+ }
+ }
+
+ if (!tocFileName.empty())
+ this->tocFile.LoadToc(tocFileName.c_str());
+
+ // we'll get here even if we don't have a valid index file
+ this->fileHandle = OpenFile(mchFile.c_str(), (this->hasTOC() && this->hasIndex()) ? FILE_ATTRIBUTE_NORMAL : FILE_FLAG_SEQUENTIAL_SCAN);
+ if (this->fileHandle != INVALID_HANDLE_VALUE)
+ {
+ GetFileSizeEx(this->fileHandle, (PLARGE_INTEGER)&this->fileSize);
+ }
+}
+
+MethodContextReader::~MethodContextReader()
+{
+ if (fileHandle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(this->fileHandle);
+ }
+
+ CloseHandle(this->mutex);
+}
+
+bool MethodContextReader::AcquireLock()
+{
+ DWORD res = WaitForSingleObject(this->mutex, INFINITE);
+ return (res == WAIT_OBJECT_0);
+}
+
+void MethodContextReader::ReleaseLock()
+{
+ ReleaseMutex(this->mutex);
+}
+
+bool MethodContextReader::atEof()
+{
+ __int64 pos = 0;
+ SetFilePointerEx(this->fileHandle, *(PLARGE_INTEGER)&pos, (PLARGE_INTEGER)&pos, FILE_CURRENT); // LARGE_INTEGER is a crime against humanity
+ return pos == this->fileSize;
+}
+
+MethodContextBuffer MethodContextReader::ReadMethodContextNoLock(bool justSkip)
+{
+ DWORD bytesRead;
+ char buff[512];
+ unsigned int totalLen = 0;
+ if (atEof())
+ {
+ return MethodContextBuffer();
+ }
+ Assert(ReadFile(this->fileHandle, buff, 2 + sizeof(unsigned int), &bytesRead, NULL) == TRUE);
+ AssertMsg((buff[0] == 'm') && (buff[1] == 'c'), "Didn't find magic number");
+ memcpy(&totalLen, &buff[2], sizeof(unsigned int));
+ if (justSkip)
+ {
+ __int64 pos = totalLen + 2;
+ // Just move the file pointer ahead the correct number of bytes
+ AssertMsg(SetFilePointerEx(this->fileHandle, *(PLARGE_INTEGER)&pos, (PLARGE_INTEGER)&pos, FILE_CURRENT) == TRUE, "SetFilePointerEx failed (Error %X)", GetLastError());
+
+ //Increment curMCIndex as we advanced the file pointer by another MC
+ ++curMCIndex;
+
+ return MethodContextBuffer(0);
+ }
+ else
+ {
+ unsigned char *buff2 = new unsigned char[totalLen + 2]; //total + End Canary
+ Assert(ReadFile(this->fileHandle, buff2, totalLen + 2, &bytesRead, NULL) == TRUE);
+
+ //Increment curMCIndex as we read another MC
+ ++curMCIndex;
+
+ return MethodContextBuffer(buff2, totalLen);
+ }
+}
+
+MethodContextBuffer MethodContextReader::ReadMethodContext(bool acquireLock, bool justSkip)
+{
+ if (acquireLock && !this->AcquireLock())
+ {
+ LogError("Can't acquire the reader lock!");
+ return MethodContextBuffer(-1);
+ }
+
+ struct Param {
+ MethodContextReader* pThis;
+ MethodContextBuffer ret;
+ bool justSkip;
+ } param;
+ param.pThis = this;
+ param.ret = MethodContextBuffer(-2);
+ param.justSkip = justSkip;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ pParam->ret = pParam->pThis->ReadMethodContextNoLock(pParam->justSkip);
+ }
+ PAL_FINALLY
+ {
+ this->ReleaseLock();
+ }
+ PAL_ENDTRY
+
+ return param.ret;
+}
+
+// Read a method context buffer from the ContextCollection
+// (either a hive [single] or an index)
+MethodContextBuffer MethodContextReader::GetNextMethodContext()
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException
+ {
+ MethodContextReader* pThis;
+ MethodContextBuffer mcb;
+ } param;
+ param.pThis = this;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ pParam->mcb = pParam->pThis->GetNextMethodContextHelper();
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndStop)
+ {
+ LogError("Method %d is of low integrity.", GetMethodContextIndex());
+ param.mcb = MethodContextBuffer(-1);
+ }
+ PAL_ENDTRY
+
+ return param.mcb;
+}
+
+MethodContextBuffer MethodContextReader::GetNextMethodContextHelper()
+{
+ //If we have an offset/increment combo
+ if (this->Offset > 0 && this->Increment > 0)
+ return GetNextMethodContextFromOffsetIncrement();
+
+ //If we have an index
+ if (this->hasIndex())
+ {
+ if (this->curIndexPos < this->IndexCount)
+ {
+ //If we are not done with all of them
+ return GetNextMethodContextFromIndexes();
+ }
+ else //We are done with all of them, return
+ return MethodContextBuffer();
+ }
+
+ //If we have a hash
+ if (this->Hash != nullptr)
+ return GetNextMethodContextFromHash();
+
+ //If we don't have any of these options return all MCs one by one
+ return this->ReadMethodContext(true);
+}
+
+// Read a method context buffer from the ContextCollection using Indexes
+MethodContextBuffer MethodContextReader::GetNextMethodContextFromIndexes()
+{
+ //Assert if we don't have an Index or we are done with all the indexes
+ Assert(this->hasIndex() && this->curIndexPos < this->IndexCount);
+
+ if (this->hasTOC())
+ {
+ // If we have an index & we have a TOC, we can just jump to that method!
+ return this->GetSpecificMethodContext(this->Indexes[this->curIndexPos++]);
+ }
+ else
+ {
+ // Find the current method (either #0, or the previous index)
+ int curMethod = this->curIndexPos ? this->Indexes[this->curIndexPos - 1] : 0;
+ // Get the next method
+ int nextMethod = this->Indexes[this->curIndexPos++];
+ // Skip over methods until we get to the right now
+ while (++curMethod < nextMethod)
+ {
+ // Skip a method context
+ MethodContextBuffer mcb = this->ReadMethodContext(true, true);
+ if (mcb.allDone() || mcb.Error())
+ return mcb;
+ }
+ }
+ return this->ReadMethodContext(true);
+}
+
+// Read a method context buffer from the ContextCollection using Hash
+MethodContextBuffer MethodContextReader::GetNextMethodContextFromHash()
+{
+ //Assert if we don't have a valid hash
+ Assert(this->Hash != nullptr);
+
+ if (this->hasTOC())
+ {
+ //We have a TOC so lets go through the TOCElements
+ //one-by-one till we find a matching hash
+ for (; curTOCIndex < (int)this->tocFile.GetTocCount(); curTOCIndex++)
+ {
+ if (_strnicmp(this->Hash, this->tocFile.GetElementPtr(curTOCIndex)->Hash, MD5_HASH_BUFFER_SIZE) == 0)
+ {
+ //We found a match, return this specific method
+ return this->GetSpecificMethodContext(this->tocFile.GetElementPtr(curTOCIndex++)->Number);
+ }
+ }
+
+ //No more matches in the TOC for our hash value
+ return MethodContextBuffer();
+ }
+ else
+ {
+ // Keep reading all MCs until we hit a match
+ //or we reach the end or hit an error
+ while (true)
+ {
+ // Read a method context
+ // we can't skip because we need to calculate hashes
+ MethodContextBuffer mcb = this->ReadMethodContext(true, false);
+ if (mcb.allDone() || mcb.Error())
+ return mcb;
+
+ char mcHash[MD5_HASH_BUFFER_SIZE];
+
+ //Create a temporary copy of mcb.buff plus ending 2-byte canary
+ //this will get freed up by MethodContext constructor
+ unsigned char *buff = new unsigned char[mcb.size + 2];
+ memcpy(buff, mcb.buff, mcb.size + 2);
+
+ MethodContext *mc;
+
+ if (!MethodContext::Initialize(-1, buff, mcb.size, &mc))
+ return MethodContextBuffer(-1);
+
+ mc->dumpMethodMD5HashToBuffer(mcHash, MD5_HASH_BUFFER_SIZE);
+ delete mc;
+
+ if (_strnicmp(this->Hash, mcHash, MD5_HASH_BUFFER_SIZE) == 0)
+ {
+ //We found a match, return this specific method
+ return mcb;
+ }
+ }
+ }
+
+ //We should never get here under normal conditions
+ AssertMsg(true, "Unexpected condition hit while reading input file.");
+ return MethodContextBuffer(-1);
+}
+
+// Read a method context buffer from the ContextCollection using offset/increment
+MethodContextBuffer MethodContextReader::GetNextMethodContextFromOffsetIncrement()
+{
+ //Assert if we don't have a valid increment/offset combo
+ Assert(this->Offset > 0 && this->Increment > 0);
+
+ int methodNumber = this->curMCIndex > 0 ? this->curMCIndex + this->Increment : this->Offset;
+
+ if (this->hasTOC())
+ {
+ //Check if we are within the TOC
+ if ((int)this->tocFile.GetTocCount() >= methodNumber)
+ {
+ //We have a TOC so we can request a specific method context
+ return this->GetSpecificMethodContext(methodNumber);
+ }
+ else
+ return MethodContextBuffer();
+ }
+ else
+ {
+ // Keep skipping MCs until we get to the one we need to return
+ while (this->curMCIndex + 1 < methodNumber)
+ {
+ //skip over a method
+ MethodContextBuffer mcb = this->ReadMethodContext(true, true);
+ if (mcb.allDone() || mcb.Error())
+ return mcb;
+ }
+ }
+ return this->ReadMethodContext(true);
+}
+
+bool MethodContextReader::hasIndex()
+{
+ return this->IndexCount > 0;
+}
+
+bool MethodContextReader::hasTOC()
+{
+ return this->tocFile.GetTocCount() > 0;
+}
+
+bool MethodContextReader::isValid()
+{
+ return this->fileHandle != INVALID_HANDLE_VALUE && this->mutex != INVALID_HANDLE_VALUE;
+}
+
+double MethodContextReader::PercentComplete()
+{
+ if (this->hasIndex() && this->hasTOC())
+ {
+ // Best estimate I can come up with...
+ return 100.0 * (double)this->curIndexPos / (double)this->IndexCount;
+ }
+ this->AcquireLock();
+ __int64 pos = 0;
+ SetFilePointerEx(this->fileHandle, *(PLARGE_INTEGER)&pos, (PLARGE_INTEGER)&pos, FILE_CURRENT);
+ this->ReleaseLock();
+ return 100.0 * (double)pos / (double)this->fileSize;
+}
+
+// Binary search to get this method number from the index
+// Returns -1 for not found, or -2 for not indexed
+// Interview question alert: hurray for CLR headers incompatibility with STL :-(
+// Note that TOC is 0 based and MC# are 1 based!
+__int64 MethodContextReader::GetOffset(unsigned int methodNumber)
+{
+ if (!this->hasTOC())
+ return -2;
+ size_t high = this->tocFile.GetTocCount() - 1;
+ size_t low = 0;
+ while (low <= high)
+ {
+ size_t pos = (high + low) / 2;
+ unsigned int num = this->tocFile.GetElementPtr(pos)->Number;
+ if (num == methodNumber)
+ return this->tocFile.GetElementPtr(pos)->Offset;
+ if (num > methodNumber)
+ high = pos - 1;
+ else
+ low = pos + 1;
+ }
+ return -1;
+}
+
+MethodContextBuffer MethodContextReader::GetSpecificMethodContext(unsigned int methodNumber)
+{
+ __int64 pos = this->GetOffset(methodNumber);
+ if (pos < 0)
+ {
+ return MethodContextBuffer(-3);
+ }
+
+ // Take the IO lock before we set the file pointer, so we can do this on multiple threads
+ if (!this->AcquireLock())
+ {
+ return MethodContextBuffer(-2);
+ }
+ if (SetFilePointerEx(this->fileHandle, *(PLARGE_INTEGER)&pos, (PLARGE_INTEGER)&pos, FILE_BEGIN))
+ {
+ // ReadMethodContext will release the lock, but we already acquired it
+ MethodContextBuffer mcb = this->ReadMethodContext(false);
+
+ //The curMCIndex value updated by ReadMethodContext() is incorrect
+ //since we are repositioning the file pointer we need to update it
+ curMCIndex = methodNumber;
+
+ return mcb;
+ }
+ else
+ {
+ // Don't forget to release the lock!
+ this->ReleaseLock();
+ return MethodContextBuffer(-4);
+ }
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.h b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.h
new file mode 100644
index 0000000000..0068fa231f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.h
@@ -0,0 +1,123 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MethodContextReader.h - Abstraction for reading MethodContexts
+// Should eventually support multithreading
+//----------------------------------------------------------
+#ifndef _MethodContextReader
+#define _MethodContextReader
+
+#include "methodcontext.h"
+#include "tocfile.h"
+
+struct MethodContextBuffer
+{
+private:
+ static const int Completed = 0x1234abcd;
+public:
+ unsigned char *buff;
+ DWORD size;
+
+ MethodContextBuffer() : buff(nullptr), size(Completed) {}
+ MethodContextBuffer(DWORD error) : buff(nullptr), size(error) {}
+ MethodContextBuffer(unsigned char *b, DWORD e) : buff(b), size(e) {}
+
+ bool allDone() { return size == Completed && buff == nullptr; }
+ bool Error() { return size != 0 && size != Completed && buff == nullptr; }
+};
+
+// The pack(4) directive is so that each entry is 12 bytes, intead of 16
+#pragma pack(push)
+#pragma pack(4)
+class MethodContextReader
+{
+private:
+
+ // The MC/MCH file
+ HANDLE fileHandle;
+
+ // The size of the MC/MCH file
+ __int64 fileSize;
+
+ // Current MC index in the input MC/MCH file
+ int curMCIndex;
+
+ // The synchronization mutex
+ HANDLE mutex;
+ bool AcquireLock();
+ void ReleaseLock();
+
+ TOCFile tocFile;
+
+ // Method ranges to process
+ // If you have an index file, these things get processed
+ // much faster, now
+ const int *Indexes;
+ int IndexCount;
+ int curIndexPos;
+
+ // Method hash to process
+ // If you have an index file, these things get processed
+ // much faster, now
+ char *Hash;
+ int curTOCIndex;
+
+ // Offset/increment if running in parallel mode
+ // If you have an index file, these things get processed
+ // much faster, now
+ int Offset;
+ int Increment;
+
+ // Binary search to get this method number from the index
+ // Returns -1 for not found, or -2 for not indexed
+ __int64 GetOffset(unsigned int methodNumber);
+
+ // Just a helper...
+ static HANDLE OpenFile(const char *inputFile, DWORD flags = FILE_ATTRIBUTE_NORMAL);
+
+ MethodContextBuffer ReadMethodContextNoLock(bool justSkip = false);
+ MethodContextBuffer ReadMethodContext(bool acquireLock, bool justSkip = false);
+ MethodContextBuffer GetSpecificMethodContext(unsigned int methodNumber);
+
+ MethodContextBuffer GetNextMethodContextFromIndexes();
+ MethodContextBuffer GetNextMethodContextFromHash();
+ MethodContextBuffer GetNextMethodContextFromOffsetIncrement();
+ MethodContextBuffer GetNextMethodContextHelper();
+
+ // Looks for a file named foo.origSuffix.newSuffix or foo.newSuffix
+ // but only if foo.origSuffix exists
+ static std::string CheckForPairedFile(const std::string &fileName, const std::string &origSuffix, const std::string &newSuffix);
+
+ // are we're at the end of the file...
+ bool atEof();
+
+ // Do we have a valid TOC?
+ bool hasTOC();
+
+ // Do we have a valid index?
+ bool hasIndex();
+
+public:
+
+ MethodContextReader(const char *inputFileName, const int *indexes = nullptr, int indexCount = -1, char *hash = nullptr, int offset = -1, int increment = -1);
+ ~MethodContextReader();
+
+ // Read a method context buffer from the ContextCollection
+ // (either a hive [single] or an index)
+ MethodContextBuffer GetNextMethodContext();
+ // No C++ exceptions, so the constructor has to always succeed...
+ bool isValid();
+ double PercentComplete();
+
+ //Returns the index of the last MethodContext read by GetNextMethodContext
+ inline int GetMethodContextIndex()
+ {
+ return curMCIndex;
+ }
+};
+#pragma pack(pop)
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/registertablexarch.h b/src/ToolBox/superpmi/superpmi-shared/registertablexarch.h
new file mode 100644
index 0000000000..4704881082
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/registertablexarch.h
@@ -0,0 +1,124 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// RegisterTableXarch.h - X macro table for x86/AMD64 registers, for use with DISX86
+//----------------------------------------------------------
+
+#ifndef REGDEF
+#error Must define REGDEF macro before including this file
+#endif
+
+/*
+REGDEF(msdisID, name)
+*/
+
+// 32 bit general purpose registers
+REGDEF(DISX86::REGA::regaEax, "eax")
+REGDEF(DISX86::REGA::regaEcx, "ecx")
+REGDEF(DISX86::REGA::regaEdx, "edx")
+REGDEF(DISX86::REGA::regaEbx, "ebx")
+REGDEF(DISX86::REGA::regaEsp, "esp")
+REGDEF(DISX86::REGA::regaEbp, "ebp")
+REGDEF(DISX86::REGA::regaEsi, "esi")
+REGDEF(DISX86::REGA::regaEdi, "edi")
+REGDEF(DISX86::REGA::regaR8d, "r8d")
+REGDEF(DISX86::REGA::regaR9d, "r9d")
+REGDEF(DISX86::REGA::regaR10d, "r10d")
+REGDEF(DISX86::REGA::regaR11d, "r11d")
+REGDEF(DISX86::REGA::regaR12d, "r12d")
+REGDEF(DISX86::REGA::regaR13d, "r13d")
+REGDEF(DISX86::REGA::regaR14d, "r14d")
+REGDEF(DISX86::REGA::regaR15d, "r15d")
+
+// 64 bit general purpose registers
+REGDEF(DISX86::REGA::regaRax, "rax")
+REGDEF(DISX86::REGA::regaRcx, "rcx")
+REGDEF(DISX86::REGA::regaRdx, "rdx")
+REGDEF(DISX86::REGA::regaRbx, "rbx")
+REGDEF(DISX86::REGA::regaRsp, "rsp")
+REGDEF(DISX86::REGA::regaRbp, "rbp")
+REGDEF(DISX86::REGA::regaRsi, "rsi")
+REGDEF(DISX86::REGA::regaRdi, "rdi")
+REGDEF(DISX86::REGA::regaR8, "r8")
+REGDEF(DISX86::REGA::regaR9, "r9")
+REGDEF(DISX86::REGA::regaR10, "r10")
+REGDEF(DISX86::REGA::regaR11, "r11")
+REGDEF(DISX86::REGA::regaR12, "r12")
+REGDEF(DISX86::REGA::regaR13, "r13")
+REGDEF(DISX86::REGA::regaR14, "r14")
+REGDEF(DISX86::REGA::regaR15, "r15")
+
+// 16 bit general purpose registers
+REGDEF(DISX86::REGA::regaAx, "ax")
+REGDEF(DISX86::REGA::regaCx, "cx")
+REGDEF(DISX86::REGA::regaDx, "dx")
+REGDEF(DISX86::REGA::regaBx, "bx")
+REGDEF(DISX86::REGA::regaSp, "sp")
+REGDEF(DISX86::REGA::regaBp, "bp")
+REGDEF(DISX86::REGA::regaSi, "si")
+REGDEF(DISX86::REGA::regaDi, "di")
+REGDEF(DISX86::REGA::regaR8w, "r8w")
+REGDEF(DISX86::REGA::regaR9w, "r9w")
+REGDEF(DISX86::REGA::regaR10w, "r10w")
+REGDEF(DISX86::REGA::regaR11w, "r11w")
+REGDEF(DISX86::REGA::regaR12w, "r12w")
+REGDEF(DISX86::REGA::regaR13w, "r13w")
+REGDEF(DISX86::REGA::regaR14w, "r14w")
+REGDEF(DISX86::REGA::regaR15w, "r15w")
+
+// 8 bit general purpose registers
+REGDEF(DISX86::REGA::regaAl, "al")
+REGDEF(DISX86::REGA::regaCl, "cl")
+REGDEF(DISX86::REGA::regaDl, "dl")
+REGDEF(DISX86::REGA::regaBl, "bl")
+REGDEF(DISX86::REGA::regaSpl, "spl")
+REGDEF(DISX86::REGA::regaBpl, "bpl")
+REGDEF(DISX86::REGA::regaSil, "sil")
+REGDEF(DISX86::REGA::regaDil, "dil")
+REGDEF(DISX86::REGA::regaR8b, "r8b")
+REGDEF(DISX86::REGA::regaR9b, "r9b")
+REGDEF(DISX86::REGA::regaR10b, "r10b")
+REGDEF(DISX86::REGA::regaR11b, "r11b")
+REGDEF(DISX86::REGA::regaR12b, "r12b")
+REGDEF(DISX86::REGA::regaR13b, "r13b")
+REGDEF(DISX86::REGA::regaR14b, "r14b")
+REGDEF(DISX86::REGA::regaR15b, "r15b")
+
+// 8 bit general purpose registers
+REGDEF(DISX86::REGA::regaAh, "ah")
+REGDEF(DISX86::REGA::regaCh, "ch")
+REGDEF(DISX86::REGA::regaDh, "dh")
+REGDEF(DISX86::REGA::regaBh, "bh")
+
+// x87 floating point stack
+REGDEF(DISX86::REGA::regaSt0, "st0")
+REGDEF(DISX86::REGA::regaSt1, "st1")
+REGDEF(DISX86::REGA::regaSt2, "st2")
+REGDEF(DISX86::REGA::regaSt3, "st3")
+REGDEF(DISX86::REGA::regaSt4, "st4")
+REGDEF(DISX86::REGA::regaSt5, "st5")
+REGDEF(DISX86::REGA::regaSt6, "st6")
+REGDEF(DISX86::REGA::regaSt7, "st7")
+
+// XMM registers
+REGDEF(DISX86::REGA::regaXmm0, "xmm0")
+REGDEF(DISX86::REGA::regaXmm1, "xmm1")
+REGDEF(DISX86::REGA::regaXmm2, "xmm2")
+REGDEF(DISX86::REGA::regaXmm3, "xmm3")
+REGDEF(DISX86::REGA::regaXmm4, "xmm4")
+REGDEF(DISX86::REGA::regaXmm5, "xmm5")
+REGDEF(DISX86::REGA::regaXmm6, "xmm6")
+REGDEF(DISX86::REGA::regaXmm7, "xmm7")
+REGDEF(DISX86::REGA::regaXmm8, "xmm8")
+REGDEF(DISX86::REGA::regaXmm9, "xmm9")
+REGDEF(DISX86::REGA::regaXmm10, "xmm10")
+REGDEF(DISX86::REGA::regaXmm11, "xmm11")
+REGDEF(DISX86::REGA::regaXmm12, "xmm12")
+REGDEF(DISX86::REGA::regaXmm13, "xmm13")
+REGDEF(DISX86::REGA::regaXmm14, "xmm14")
+REGDEF(DISX86::REGA::regaXmm15, "xmm15")
+
+#undef REGDEF
diff --git a/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h b/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h
new file mode 100644
index 0000000000..77caafc181
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h
@@ -0,0 +1,39 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// RuntimeDetails.h - the collection of runtime includes that we need to access.
+//----------------------------------------------------------
+#ifndef _RuntimeDetails
+#define _RuntimeDetails
+
+//Our little collection of enough of the CLR data to get the JIT up and working...
+#define FEATURE_CLRSQM
+#ifdef _M_X64
+#define _TARGET_AMD64_ 1
+#endif
+#ifdef _M_IX86
+#define _TARGET_X86_ 1
+#endif
+#define __EXCEPTION_RECORD_CLR //trick out clrntexception.h to not include another exception record....
+#include <mscoree.h>
+#include <corjit.h>
+#include <utilcode.h>
+
+///Turn back on direct access to a few OS level things...
+#undef HeapCreate
+#undef HeapAlloc
+#undef HeapFree
+#undef HeapDestroy
+#undef TlsAlloc
+#undef TlsGetValue
+#undef TlsSetValue
+
+//Jit Exports
+typedef ICorJitCompiler* (__stdcall *PgetJit)();
+typedef void (__stdcall *PjitStartup)(ICorJitHost* host);
+typedef void (__stdcall *PsxsJitStartup)(CoreClrCallbacks const & cccallbacks);
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/simpletimer.cpp b/src/ToolBox/superpmi/superpmi-shared/simpletimer.cpp
new file mode 100644
index 0000000000..e9c76339bc
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/simpletimer.cpp
@@ -0,0 +1,56 @@
+//
+// 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 "logging.h"
+#include "simpletimer.h"
+
+SimpleTimer::SimpleTimer()
+{
+ start.QuadPart = 0;
+ stop.QuadPart = 0;
+
+ BOOL retVal = ::QueryPerformanceFrequency(&proc_freq);
+ if(retVal == FALSE)
+ {
+ LogDebug("SimpleTimer::SimpleTimer unable to QPF. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+SimpleTimer::~SimpleTimer()
+{
+}
+
+void SimpleTimer::Start()
+{
+ BOOL retVal = ::QueryPerformanceCounter(&start);
+ if(retVal == FALSE)
+ {
+ LogDebug("SimpleTimer::Start unable to QPC. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+void SimpleTimer::Stop()
+{
+ BOOL retVal = ::QueryPerformanceCounter(&stop);
+ if(retVal == FALSE)
+ {
+ LogDebug("SimpleTimer::Stop unable to QPC. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+double SimpleTimer::GetMilliseconds()
+{
+ return GetSeconds()*1000.0;
+}
+
+double SimpleTimer::GetSeconds()
+{
+ return ((stop.QuadPart - start.QuadPart) / (double)proc_freq.QuadPart);
+}
+
diff --git a/src/ToolBox/superpmi/superpmi-shared/simpletimer.h b/src/ToolBox/superpmi/superpmi-shared/simpletimer.h
new file mode 100644
index 0000000000..b5edd7c65e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/simpletimer.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _SimpleTimer
+#define _SimpleTimer
+
+class SimpleTimer
+{
+public:
+ SimpleTimer();
+ ~SimpleTimer();
+
+ void Start();
+ void Stop();
+ double GetMilliseconds();
+ double GetSeconds();
+
+private:
+ LARGE_INTEGER proc_freq;
+ LARGE_INTEGER start;
+ LARGE_INTEGER stop;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/spmiutil.cpp b/src/ToolBox/superpmi/superpmi-shared/spmiutil.cpp
new file mode 100644
index 0000000000..e99e6f4ae2
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/spmiutil.cpp
@@ -0,0 +1,109 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SPMIUtil.cpp - General utility functions
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "logging.h"
+#include "spmiutil.h"
+
+bool breakOnDebugBreakorAV = false;
+
+void DebugBreakorAV(int val)
+{
+ if (IsDebuggerPresent())
+ {
+ if (val == 0)
+ __debugbreak();
+ if (breakOnDebugBreakorAV)
+ __debugbreak();
+ }
+
+ int exception_code = EXCEPTIONCODE_DebugBreakorAV + val;
+ // assert((EXCEPTIONCODE_DebugBreakorAV <= exception_code) && (exception_code < EXCEPTIONCODE_DebugBreakorAV_MAX))
+ LogException(exception_code, "DebugBreak or AV Exception %d", val);
+}
+
+char* GetEnvironmentVariableWithDefaultA(const char* envVarName, const char* defaultValue)
+{
+ char* retString = nullptr;
+
+ // Figure out how much space we need to allocate
+ DWORD dwRetVal = ::GetEnvironmentVariableA(envVarName, nullptr, 0);
+ if (dwRetVal != 0)
+ {
+ retString = new char[dwRetVal];
+ dwRetVal = ::GetEnvironmentVariableA(envVarName, retString, dwRetVal);
+ }
+ else
+ {
+ if (defaultValue != nullptr)
+ {
+ dwRetVal = (DWORD)strlen(defaultValue) + 1; // add one for null terminator
+ retString = new char[dwRetVal];
+ memcpy_s(retString, dwRetVal, defaultValue, dwRetVal);
+ }
+ }
+
+ return retString;
+}
+
+WCHAR* GetEnvironmentVariableWithDefaultW(const WCHAR* envVarName, const WCHAR* defaultValue)
+{
+ WCHAR* retString = nullptr;
+
+ // Figure out how much space we need to allocate
+ DWORD dwRetVal = ::GetEnvironmentVariableW(envVarName, nullptr, 0);
+ if (dwRetVal != 0)
+ {
+ retString = new WCHAR[dwRetVal];
+ dwRetVal = ::GetEnvironmentVariableW(envVarName, retString, dwRetVal);
+ }
+ else
+ {
+ if (defaultValue != nullptr)
+ {
+ dwRetVal = (DWORD)wcslen(defaultValue) + 1; // add one for null terminator
+ retString = new WCHAR[dwRetVal];
+ memcpy_s(retString, dwRetVal * sizeof(WCHAR), defaultValue, dwRetVal * sizeof(WCHAR));
+ }
+ }
+
+ return retString;
+}
+
+#ifdef FEATURE_PAL
+// For some reason, the PAL doesn't have GetCommandLineA(). So write it.
+LPSTR GetCommandLineA()
+{
+ LPSTR pCmdLine = nullptr;
+ LPWSTR pwCmdLine = GetCommandLineW();
+
+ if (pwCmdLine != nullptr)
+ {
+ // Convert to ASCII
+
+ int n = WideCharToMultiByte(CP_ACP, 0, pwCmdLine, -1, nullptr, 0, nullptr, nullptr);
+ if (n == 0)
+ {
+ LogError("MultiByteToWideChar failed %d", GetLastError());
+ return nullptr;
+ }
+
+ pCmdLine = new char[n];
+
+ int n2 = WideCharToMultiByte(CP_ACP, 0, pwCmdLine, -1, pCmdLine, n, nullptr, nullptr);
+ if ((n2 == 0) || (n2 != n))
+ {
+ LogError("MultiByteToWideChar failed %d", GetLastError());
+ return nullptr;
+ }
+ }
+
+ return pCmdLine;
+}
+#endif // FEATURE_PAL
diff --git a/src/ToolBox/superpmi/superpmi-shared/spmiutil.h b/src/ToolBox/superpmi/superpmi-shared/spmiutil.h
new file mode 100644
index 0000000000..6c8db3948a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/spmiutil.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//----------------------------------------------------------
+// SPMIUtil.h - General utility functions
+//----------------------------------------------------------
+#ifndef _SPMIUtil
+#define _SPMIUtil
+
+#include "methodcontext.h"
+
+extern bool breakOnDebugBreakorAV;
+
+extern void DebugBreakorAV(int val); //Global(ish) error handler
+
+extern char* GetEnvironmentVariableWithDefaultA(const char* envVarName, const char* defaultValue = nullptr);
+
+extern WCHAR* GetEnvironmentVariableWithDefaultW(const WCHAR* envVarName, const WCHAR* defaultValue = nullptr);
+
+#ifdef FEATURE_PAL
+extern LPSTR GetCommandLineA();
+#endif // FEATURE_PAL
+
+#endif // !_SPMIUtil
diff --git a/src/ToolBox/superpmi/superpmi-shared/standardpch.cpp b/src/ToolBox/superpmi/superpmi-shared/standardpch.cpp
new file mode 100644
index 0000000000..b61444351e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/standardpch.cpp
@@ -0,0 +1,7 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// Don't touch this file: it's just here to create a precompiled header...
+#include "standardpch.h"
diff --git a/src/ToolBox/superpmi/superpmi-shared/standardpch.h b/src/ToolBox/superpmi/superpmi-shared/standardpch.h
new file mode 100644
index 0000000000..4164244085
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/standardpch.h
@@ -0,0 +1,106 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// The point of a PCH file is to never reparse files that never change.
+// Only include files here that will almost NEVER change. Headers for the project
+// itself are probably inappropriate, because if you change them, the entire
+// project will require a recompile. Generally just put SDK style stuff here...
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif // WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#ifdef INTERNAL_BUILD
+// There are a few features that reference Microsoft internal resources. We can't build these
+// in the open source version.
+#define USE_MSVCDIS
+
+// Disable CoreDisTools until coredistools.dll is statically-linked to the CRT, or until it is delayload linked.
+//#define USE_COREDISTOOLS
+#endif // INTERNAL_BUILD
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4996) // The compiler encountered a deprecated declaration.
+
+// On Windows, we build against PAL macros that convert to Windows SEH. But we don't want all the
+// Contract stuff that normally gets pulled it. Defining JIT_BUILD prevents this, just as it does
+// when building the JIT using parts of utilcode.
+#define JIT_BUILD
+
+// Defining this prevents:
+// error C2338 : / RTCc rejects conformant code, so it isn't supported by the C++ Standard Library.
+// Either remove this compiler option, or define _ALLOW_RTCc_IN_STL to acknowledge that you have received this warning.
+#ifndef _ALLOW_RTCc_IN_STL
+#define _ALLOW_RTCc_IN_STL
+#endif
+
+#define MSC_ONLY(x) x
+#else // !_MSC_VER
+#define MSC_ONLY(x)
+#endif // !_MSC_VER
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif // _CRT_SECURE_NO_WARNINGS
+
+#define _CRT_RAND_S
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <malloc.h>
+#include <assert.h>
+#include <wchar.h>
+#include <tchar.h>
+#include <specstrings.h>
+#include <math.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+// Getting STL to work with PAL is difficult, so reimplement STL functionality to not require it.
+#ifdef FEATURE_PAL
+#include "clr_std/string"
+#include "clr_std/algorithm"
+#else // !FEATURE_PAL
+#ifndef USE_STL
+#define USE_STL
+#endif // USE_STL
+#include <string>
+#include <algorithm>
+#endif // !FEATURE_PAL
+
+#ifdef USE_MSVCDIS
+#define DISLIB
+#include "..\external\msvcdis\inc\msvcdis.h"
+#include "..\external\msvcdis\inc\disx86.h"
+#include "..\external\msvcdis\inc\disarm64.h"
+#endif // USE_MSVCDIS
+
+#ifndef DIRECTORY_SEPARATOR_CHAR_A
+#define DIRECTORY_SEPARATOR_CHAR_A '\\'
+#endif
+#ifndef DIRECTORY_SEPARATOR_STR_A
+#define DIRECTORY_SEPARATOR_STR_A "\\"
+#endif
+
+#ifndef W
+#ifdef PLATFORM_UNIX
+#define W(str) u##str
+#else // PLATFORM_UNIX
+#define W(str) L##str
+#endif // PLATFORM_UNIX
+#endif // !W
+
+#ifdef FEATURE_PAL
+#define PLATFORM_SHARED_LIB_SUFFIX_A PAL_SHLIB_SUFFIX
+#else // !FEATURE_PAL
+#define PLATFORM_SHARED_LIB_SUFFIX_A ".dll"
+#endif // !FEATURE_PAL
+
+#define DEFAULT_REAL_JIT_NAME_A MAKEDLLNAME_A("clrjit2")
+#define DEFAULT_REAL_JIT_NAME_W MAKEDLLNAME_W("clrjit2")
diff --git a/src/ToolBox/superpmi/superpmi-shared/tocfile.cpp b/src/ToolBox/superpmi/superpmi-shared/tocfile.cpp
new file mode 100644
index 0000000000..c91ca89c93
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/tocfile.cpp
@@ -0,0 +1,79 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// TOCFile.cpp - Abstraction for reading a TOC file
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "tocfile.h"
+#include "logging.h"
+
+// Tries to load a Table of Contents
+void TOCFile::LoadToc(const char *inputFileName, bool validate)
+{
+ HANDLE hIndex = CreateFileA(inputFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hIndex == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open file '%s'. GetLastError()=%u", inputFileName, GetLastError());
+ return;
+ }
+
+ // Now read the index file
+ LARGE_INTEGER val; // I'm abusing LARGE_INTEGER here...
+ DWORD read;
+ if (!ReadFile(hIndex, &val, sizeof(val), &read, nullptr) ||
+ (val.u.LowPart != *(DWORD *)("INDX")))
+ {
+ CloseHandle(hIndex);
+ LogWarning("The index file %s is invalid: it seems to be missing the starting sentinel/length", inputFileName);
+ return;
+ }
+
+ this->m_tocCount = val.u.HighPart;
+ this->m_tocArray = new TOCElement[this->m_tocCount];
+
+ // Read the whole array
+ if (!ReadFile(hIndex, &this->m_tocArray[0], (DWORD)(this->m_tocCount * sizeof(TOCElement)), &read, nullptr) ||
+ (read != (DWORD)(this->m_tocCount * sizeof(TOCElement))))
+ {
+ CloseHandle(hIndex);
+ this->Clear();
+ LogWarning("The index file %s is invalid: it appears to be truncated.", inputFileName);
+ return;
+ }
+
+ // Get the last 4 byte token (more abuse of LARGE_INTEGER)
+ if (!ReadFile(hIndex, &val.u.HighPart, sizeof(DWORD), &read, nullptr) ||
+ (read != sizeof(DWORD)) ||
+ (val.u.LowPart != val.u.HighPart))
+ {
+ CloseHandle(hIndex);
+ this->Clear();
+ LogWarning("The index file %s is invalid: it appears to be missing the ending sentinel.", inputFileName);
+ return;
+ }
+
+ CloseHandle(hIndex);
+
+ if (validate)
+ {
+ int lastNum = -1;
+
+ // Quickly validate that the index is sorted
+ for (size_t i = 0; i < this->m_tocCount; i++)
+ {
+ int nextNum = this->m_tocArray[i].Number;
+ if (nextNum <= lastNum)
+ {
+ // It wasn't sorted: abort
+ this->Clear();
+ LogWarning("The index file %s is invalid: it is not sorted.", inputFileName);
+ return;
+ }
+ lastNum = nextNum;
+ }
+ }
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/tocfile.h b/src/ToolBox/superpmi/superpmi-shared/tocfile.h
new file mode 100644
index 0000000000..a0e7bde146
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/tocfile.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.
+//
+
+//----------------------------------------------------------
+// TOCFile.h - Abstraction for reading a TOC file
+//----------------------------------------------------------
+#ifndef _TOCFile
+#define _TOCFile
+
+#include "methodcontext.h"
+
+class TOCElement
+{
+public:
+ __int64 Offset;
+ int Number;
+ char Hash[MD5_HASH_BUFFER_SIZE];
+
+ TOCElement()
+ {
+ }
+
+ TOCElement(int number, __int64 offset)
+ : Offset(offset)
+ , Number(number)
+ {
+ }
+};
+
+class TOCFile
+{
+private:
+
+ TOCElement* m_tocArray;
+ size_t m_tocCount;
+
+public:
+
+ TOCFile()
+ : m_tocArray(nullptr)
+ , m_tocCount(0)
+ {
+ }
+
+ ~TOCFile()
+ {
+ Clear();
+ }
+
+ void Clear()
+ {
+ delete[] m_tocArray;
+ m_tocArray = nullptr;
+ m_tocCount = 0;
+ }
+
+ void LoadToc(const char *inputFileName, bool validate = true);
+
+ size_t GetTocCount()
+ {
+ return m_tocCount;
+ }
+
+ const TOCElement* GetElementPtr(size_t i)
+ {
+ if (i >= m_tocCount)
+ {
+ // error!
+ return nullptr;
+ }
+ return &m_tocArray[i];
+ }
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/typeutils.cpp b/src/ToolBox/superpmi/superpmi-shared/typeutils.cpp
new file mode 100644
index 0000000000..6cd4da4842
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/typeutils.cpp
@@ -0,0 +1,169 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// TypeUtils.cpp - Utility code for working with managed types
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "typeutils.h"
+#include "errorhandling.h"
+
+// Returns a string representation of the given CorInfoType. The naming scheme is based on JITtype2varType
+// in src/jit/ee_il_dll.hpp.
+const char *TypeUtils::GetCorInfoTypeName(CorInfoType type)
+{
+ switch (type)
+ {
+ case CORINFO_TYPE_VOID:
+ return "void";
+
+ case CORINFO_TYPE_BOOL:
+ return "bool";
+
+ case CORINFO_TYPE_CHAR:
+ return "char";
+
+ case CORINFO_TYPE_BYTE:
+ return "byte";
+
+ case CORINFO_TYPE_UBYTE:
+ return "ubyte";
+
+ case CORINFO_TYPE_SHORT:
+ return "short";
+
+ case CORINFO_TYPE_USHORT:
+ return "ushort";
+
+ case CORINFO_TYPE_INT:
+ return "int";
+
+ case CORINFO_TYPE_UINT:
+ return "uint";
+
+ case CORINFO_TYPE_LONG:
+ return "long";
+
+ case CORINFO_TYPE_ULONG:
+ return "ulong";
+
+ case CORINFO_TYPE_FLOAT:
+ return "float";
+
+ case CORINFO_TYPE_DOUBLE:
+ return "double";
+
+ case CORINFO_TYPE_BYREF:
+ return "byref";
+
+ case CORINFO_TYPE_VALUECLASS:
+ case CORINFO_TYPE_REFANY:
+ return "struct";
+
+ case CORINFO_TYPE_STRING:
+ case CORINFO_TYPE_CLASS:
+ case CORINFO_TYPE_VAR:
+ return "ref";
+
+ case CORINFO_TYPE_NATIVEINT:
+ case CORINFO_TYPE_NATIVEUINT:
+ // Emulates the JIT's concept of TYP_I_IMPL
+#if defined(_TARGET_AMD64_) // TODO: should be _TARGET_64BIT_
+ return "long";
+#else
+ return "int";
+#endif
+
+ case CORINFO_TYPE_PTR:
+ // The JIT just treats this as a TYP_I_IMPL because this isn't a GC root,
+ // but we don't care about GC-ness: we care about pointer-sized.
+ return "ptr";
+
+ default:
+ LogException(EXCEPTIONCODE_TYPEUTILS, "Unknown type passed into GetCorInfoTypeName (0x%x)", type);
+ return "UNKNOWN";
+ }
+}
+
+bool TypeUtils::IsFloatingPoint(CorInfoType type)
+{
+ return (type == CORINFO_TYPE_FLOAT || type == CORINFO_TYPE_DOUBLE);
+}
+
+bool TypeUtils::IsPointer(CorInfoType type)
+{
+ return (type == CORINFO_TYPE_STRING || type == CORINFO_TYPE_PTR ||
+ type == CORINFO_TYPE_BYREF || type == CORINFO_TYPE_CLASS);
+}
+
+bool TypeUtils::IsValueClass(CorInfoType type)
+{
+ return (type == CORINFO_TYPE_VALUECLASS || type == CORINFO_TYPE_REFANY);
+}
+
+// Determines if a value class, represented by the given class handle, is required to be passed
+// by reference (i.e. it cannot be stuffed as-is into a register or stack slot).
+bool TypeUtils::ValueClassRequiresByref(MethodContext *mc, CORINFO_CLASS_HANDLE clsHnd)
+{
+#if defined(_TARGET_AMD64_)
+ size_t size = mc->repGetClassSize(clsHnd);
+ return ((size > sizeof(void *)) || ((size & (size - 1)) != 0));
+#else
+ LogException(EXCEPTIONCODE_TYPEUTILS, "unsupported architecture");
+ return false;
+#endif
+}
+
+// Returns the size of the given CorInfoType. If there is no applicable size (e.g. CORINFO_TYPE_VOID,
+// CORINFO_TYPE_UNDEF), this throws an exception and returns -1. Taken largely from the MSDN documentation
+// for managed sizeof.
+size_t TypeUtils::SizeOfCorInfoType(CorInfoType type)
+{
+ switch (type)
+ {
+ case CORINFO_TYPE_BYTE:
+ case CORINFO_TYPE_UBYTE:
+ case CORINFO_TYPE_BOOL:
+ return sizeof(BYTE);
+
+ case CORINFO_TYPE_CHAR: // 2 bytes for Unicode
+ case CORINFO_TYPE_SHORT:
+ case CORINFO_TYPE_USHORT:
+ return sizeof(WORD);
+
+ case CORINFO_TYPE_INT:
+ case CORINFO_TYPE_UINT:
+ case CORINFO_TYPE_FLOAT:
+ return sizeof(DWORD);
+
+ case CORINFO_TYPE_LONG:
+ case CORINFO_TYPE_ULONG:
+ case CORINFO_TYPE_DOUBLE:
+ return sizeof(DWORDLONG);
+
+ case CORINFO_TYPE_NATIVEINT:
+ case CORINFO_TYPE_NATIVEUINT:
+ case CORINFO_TYPE_STRING:
+ case CORINFO_TYPE_PTR:
+ case CORINFO_TYPE_BYREF:
+ case CORINFO_TYPE_CLASS:
+ return sizeof(void *);
+
+ // This should be obtained via repGetClassSize
+ case CORINFO_TYPE_VALUECLASS:
+ case CORINFO_TYPE_REFANY:
+ LogException(EXCEPTIONCODE_TYPEUTILS,
+ "SizeOfCorInfoType does not support value types; use repGetClassSize instead (type: 0x%x)",
+ type);
+ return 0;
+
+ case CORINFO_TYPE_UNDEF:
+ case CORINFO_TYPE_VOID:
+ default:
+ LogException(EXCEPTIONCODE_TYPEUTILS, "Unsupported type (0x%x) passed into SizeOfCorInfoType", type);
+ return 0;
+ }
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/typeutils.h b/src/ToolBox/superpmi/superpmi-shared/typeutils.h
new file mode 100644
index 0000000000..31cb478020
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/typeutils.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// TypeUtils.h - Utility code for working with managed types
+//----------------------------------------------------------
+#ifndef _TypeUtils
+#define _TypeUtils
+
+#include "methodcontext.h"
+
+class TypeUtils
+{
+public:
+ static const char *GetCorInfoTypeName(CorInfoType type);
+ static bool IsFloatingPoint(CorInfoType type);
+ static bool IsPointer(CorInfoType type);
+ static bool IsValueClass(CorInfoType type);
+ static bool ValueClassRequiresByref(MethodContext *mc, CORINFO_CLASS_HANDLE clsHnd);
+ static size_t SizeOfCorInfoType(CorInfoType type);
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/.gitmirror b/src/ToolBox/superpmi/superpmi-shim-collector/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/.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-shim-collector/CMakeLists.txt b/src/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt
new file mode 100644
index 0000000000..ded4e9630a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt
@@ -0,0 +1,72 @@
+project(superpmi-shim-collector)
+
+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)
+
+set(SUPERPMI_SHIM_COLLECTOR_SOURCES
+ coreclrcallbacks.cpp
+ jithost.cpp
+ icorjitcompiler.cpp
+ icorjitinfo.cpp
+ ieememorymanager.cpp
+ iexecutionengine.cpp
+ superpmi-shim-collector.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_SHIM_COLLECTOR_SOURCES
+)
+
+if (WIN32)
+ preprocess_def_file(${CMAKE_CURRENT_SOURCE_DIR}/superpmi-shim-collector.def ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-collector.def)
+
+ list(APPEND SUPERPMI_SHIM_COLLECTOR_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-collector.def)
+endif (WIN32)
+
+add_library(superpmi-shim-collector
+ SHARED
+ ${SUPERPMI_SHIM_COLLECTOR_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(superpmi-shim-collector
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(superpmi-shim-collector
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/superpmi-shim-collector.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS superpmi-shim-collector DESTINATION .)
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp
new file mode 100644
index 0000000000..5b764f2fa5
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp
@@ -0,0 +1,62 @@
+//
+// 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 "coreclrcallbacks.h"
+#include "iexecutionengine.h"
+
+typedef LPVOID (__stdcall * pfnEEHeapAllocInProcessHeap)(DWORD dwFlags, SIZE_T dwBytes);
+typedef BOOL (__stdcall * pfnEEHeapFreeInProcessHeap)(DWORD dwFlags, LPVOID lpMem);
+
+CoreClrCallbacks *original_CoreClrCallbacks = nullptr;
+pfnEEHeapAllocInProcessHeap original_EEHeapAllocInProcessHeap = nullptr;
+pfnEEHeapFreeInProcessHeap original_EEHeapFreeInProcessHeap = nullptr;
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
+{
+ interceptor_IEE *iee = new interceptor_IEE();
+ iee->original_IEE = original_CoreClrCallbacks->m_pfnIEE();
+ return iee;
+}
+
+/*#pragma warning( suppress :4996 ) //deprecated
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength)
+{
+ DebugBreakorAV(131);
+ return 0;
+}
+*/
+
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes)
+{
+ if(original_EEHeapAllocInProcessHeap == nullptr)
+ __debugbreak();
+ return original_EEHeapAllocInProcessHeap(dwFlags, dwBytes);
+}
+
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem)
+{
+ if(original_EEHeapFreeInProcessHeap == nullptr)
+ __debugbreak();
+ return original_EEHeapFreeInProcessHeap(dwFlags, lpMem);
+}
+
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
+{
+ if(strcmp(functionName, "EEHeapAllocInProcessHeap")==0)
+ {
+ original_EEHeapAllocInProcessHeap =
+ (pfnEEHeapAllocInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapAllocInProcessHeap");
+ return (void*)EEHeapAllocInProcessHeap;
+ }
+ if(strcmp(functionName, "EEHeapFreeInProcessHeap")==0)
+ {
+ original_EEHeapFreeInProcessHeap =
+ (pfnEEHeapFreeInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapFreeInProcessHeap");
+ return (void*)EEHeapFreeInProcessHeap;
+ }
+ return original_CoreClrCallbacks->m_pfnGetCLRFunction(functionName);
+}
+
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.h b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.h
new file mode 100644
index 0000000000..c8c3e27c66
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.h
@@ -0,0 +1,19 @@
+//
+// 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);
+
+extern CoreClrCallbacks *original_CoreClrCallbacks;
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp
new file mode 100644
index 0000000000..e3f5ae2764
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.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 "spmiutil.h"
+#include "icorjitcompiler.h"
+#include "icorjitinfo.h"
+#include "jithost.h"
+#include "superpmi-shim-collector.h"
+
+#define fatMC //this is nice to have on so ildump works...
+interceptor_IEEMM *current_IEEMM = nullptr; //we want this to live beyond the scope of a single compileMethodCall
+
+CorJitResult __stdcall interceptor_ICJC::compileMethod (
+ ICorJitInfo *comp, /* IN */
+ struct CORINFO_METHOD_INFO *info, /* IN */
+ unsigned /* code:CorJitFlag */ flags, /* IN */
+ BYTE **nativeEntry, /* OUT */
+ ULONG *nativeSizeOfCode /* OUT */
+ )
+{
+ interceptor_ICJI our_ICorJitInfo;
+ our_ICorJitInfo.original_ICorJitInfo = comp;
+
+ if(current_IEEMM == nullptr)
+ current_IEEMM = new interceptor_IEEMM();
+
+ auto* mc = new MethodContext();
+ if (g_ourJitHost != nullptr)
+ {
+ g_ourJitHost->setMethodContext(mc);
+ }
+
+ our_ICorJitInfo.mc = mc;
+ our_ICorJitInfo.mc->cr->recProcessName(GetCommandLineA());
+
+ our_ICorJitInfo.mc->recCompileMethod(info, flags);
+
+//force some extra data into our tables..
+ //data probably not needed with RyuJIT, but needed in 4.5 and 4.5.1 to help with catching cached values
+ our_ICorJitInfo.getBuiltinClass(CLASSID_SYSTEM_OBJECT);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_TYPED_BYREF);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_TYPE_HANDLE);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_FIELD_HANDLE);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_METHOD_HANDLE);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_STRING);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_ARGUMENT_HANDLE);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_RUNTIME_TYPE);
+
+#ifdef fatMC
+ //to build up a fat mc
+ CORINFO_CLASS_HANDLE ourClass = our_ICorJitInfo.getMethodClass(info->ftn);
+ our_ICorJitInfo.getClassAttribs(ourClass);
+ our_ICorJitInfo.getClassName(ourClass);
+ our_ICorJitInfo.isValueClass(ourClass);
+ our_ICorJitInfo.asCorInfoType(ourClass);
+#endif
+
+ // Record data from the global context, if any
+ if (g_globalContext != nullptr)
+ {
+ our_ICorJitInfo.mc->recGlobalContext(*g_globalContext);
+ }
+
+ //Record a simple view of the environment
+ our_ICorJitInfo.mc->recEnvironment();
+
+ CorJitResult temp = original_ICorJitCompiler->compileMethod(&our_ICorJitInfo, info, flags, nativeEntry, nativeSizeOfCode);
+
+ if(temp == CORJIT_OK)
+ {
+ //capture the results of compilation
+ our_ICorJitInfo.mc->cr->recCompileMethod(nativeEntry, nativeSizeOfCode, temp);
+
+ our_ICorJitInfo.mc->cr->recAllocMemCapture();
+ our_ICorJitInfo.mc->cr->recAllocGCInfoCapture();
+ our_ICorJitInfo.mc->saveToFile(hFile);
+ }
+
+ delete mc;
+
+ if (g_ourJitHost != nullptr)
+ {
+ g_ourJitHost->setMethodContext(g_globalContext);
+ }
+
+ return temp;
+}
+
+void interceptor_ICJC::clearCache()
+{
+ original_ICorJitCompiler->clearCache();
+}
+
+BOOL interceptor_ICJC::isCacheCleanupRequired()
+{
+ return original_ICorJitCompiler->isCacheCleanupRequired();
+}
+
+void interceptor_ICJC::ProcessShutdownWork(ICorStaticInfo* info)
+{
+ original_ICorJitCompiler->ProcessShutdownWork(info);
+}
+
+void interceptor_ICJC::getVersionIdentifier(GUID* versionIdentifier /* OUT */)
+{
+ original_ICorJitCompiler->getVersionIdentifier(versionIdentifier);
+}
+
+unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags)
+{
+ return original_ICorJitCompiler->getMaxIntrinsicSIMDVectorLength(cpuCompileFlags);
+}
+
+void interceptor_ICJC::setRealJit(ICorJitCompiler* realJitCompiler)
+{
+ original_ICorJitCompiler->setRealJit(realJitCompiler);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.h b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.h
new file mode 100644
index 0000000000..97dbebd9a9
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitCompiler
+#define _ICorJitCompiler
+
+#include "runtimedetails.h"
+#include "ieememorymanager.h"
+
+class interceptor_ICJC : public ICorJitCompiler
+{
+
+#include "icorjitcompilerimpl.h"
+
+public:
+ // Added to help us track the original icjc and be able to easily indirect to it.
+ ICorJitCompiler *original_ICorJitCompiler;
+ HANDLE hFile;
+};
+
+extern interceptor_IEEMM *current_IEEMM; //we want this to live beyond the scope of a single compileMethodCall
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
new file mode 100644
index 0000000000..fb9163629d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
@@ -0,0 +1,2284 @@
+//
+// 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 "superpmi-shim-collector.h"
+#include "ieememorymanager.h"
+#include "icorjitcompiler.h"
+#include "methodcontext.h"
+#include "errorhandling.h"
+#include "logging.h"
+
+#define fatMC //this is nice to have on so ildump works...
+
+//Stuff on ICorStaticInfo
+/**********************************************************************************/
+//
+// ICorMethodInfo
+//
+/**********************************************************************************/
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getMethodAttribs (CORINFO_METHOD_HANDLE ftn /* IN */)
+{
+ mc->cr->AddCall("getMethodAttribs");
+ DWORD temp = original_ICorJitInfo->getMethodAttribs(ftn);
+ mc->recGetMethodAttribs(ftn, temp);
+ return temp;
+}
+
+// sets private JIT flags, which can be, retrieved using getAttrib.
+void interceptor_ICJI::setMethodAttribs (CORINFO_METHOD_HANDLE ftn,/* IN */
+ CorInfoMethodRuntimeFlags attribs/* IN */)
+{
+ mc->cr->AddCall("setMethodAttribs");
+ original_ICorJitInfo->setMethodAttribs(ftn, attribs);
+ 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 interceptor_ICJI::getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ mc->cr->AddCall("getMethodSig");
+ original_ICorJitInfo->getMethodSig(ftn, sig, memberParent);
+ mc->recGetMethodSig(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 interceptor_ICJI::getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_METHOD_HANDLE ftn;
+ CORINFO_METHOD_INFO* info;
+ bool temp;
+ } param;
+ param.pThis = this;
+ param.ftn = ftn;
+ param.info = info;
+ param.temp = false;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("getMethodInfo");
+ pParam->temp = pParam->pThis->original_ICorJitInfo->getMethodInfo(pParam->ftn, pParam->info);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recGetMethodInfo(ftn, info, param.temp, param.exceptionCode);
+ }
+ PAL_ENDTRY
+
+ return param.temp;
+ }
+
+// 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 interceptor_ICJI::canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_METHOD_HANDLE callerHnd;
+ CORINFO_METHOD_HANDLE calleeHnd;
+ DWORD* pRestrictions;
+ CorInfoInline temp;
+ } param;
+ param.pThis = this;
+ param.callerHnd = callerHnd;
+ param.calleeHnd = calleeHnd;
+ param.pRestrictions = pRestrictions;
+ param.temp = INLINE_NEVER;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("canInline");
+ pParam->temp = pParam->pThis->original_ICorJitInfo->canInline(pParam->callerHnd, pParam->calleeHnd, pParam->pRestrictions);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recCanInline(callerHnd, calleeHnd, pRestrictions, param.temp, param.exceptionCode);
+ }
+ PAL_ENDTRY
+
+ return param.temp;
+ }
+
+// 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 interceptor_ICJI::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * reason)
+{
+ mc->cr->AddCall("reportInliningDecision");
+ original_ICorJitInfo->reportInliningDecision(inlinerHnd, inlineeHnd, inlineResult, reason);
+ 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 interceptor_ICJI::canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ )
+{
+ mc->cr->AddCall("canTailCall");
+ bool temp = original_ICorJitInfo->canTailCall(callerHnd, declaredCalleeHnd, exactCalleeHnd, fIsTailPrefix);
+ mc->recCanTailCall(callerHnd, declaredCalleeHnd, exactCalleeHnd, fIsTailPrefix, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason)
+{
+ mc->cr->AddCall("reportTailCallDecision");
+ original_ICorJitInfo->reportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+ mc->cr->recReportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+}
+
+// get individual exception handler
+void interceptor_ICJI::getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ )
+{
+ mc->cr->AddCall("getEHinfo");
+ original_ICorJitInfo->getEHinfo(ftn, EHnumber, clause);
+ mc->recGetEHinfo(ftn, EHnumber, clause);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("getMethodClass");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getMethodClass(method);
+ mc->recGetMethodClass(method, temp);
+ return temp;
+}
+
+// return module it belongs to
+CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("getMethodModule");
+ return original_ICorJitInfo->getMethodModule(method);
+}
+
+// This function returns the offset of the specified method in the
+// vtable of it's owning class or interface.
+void interceptor_ICJI::getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ )
+{
+ mc->cr->AddCall("getMethodVTableOffset");
+ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+ mc->recGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+}
+
+// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+// getIntrinsicID() returns the intrinsic ID.
+CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand /* OUT */
+ )
+{
+ mc->cr->AddCall("getIntrinsicID");
+ CorInfoIntrinsics temp = original_ICorJitInfo->getIntrinsicID(method, pMustExpand);
+ mc->recGetIntrinsicID(method, pMustExpand, temp);
+ return temp;
+}
+
+// Is the given module the System.Numerics.Vectors module?
+bool interceptor_ICJI::isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ )
+{
+ mc->cr->AddCall("isInSIMDModule");
+ bool temp = original_ICorJitInfo->isInSIMDModule(classHnd);
+ mc->recIsInSIMDModule(classHnd, temp);
+ return temp;
+}
+
+// return the unmanaged calling convention for a PInvoke
+CorInfoUnmanagedCallConv interceptor_ICJI::getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("getUnmanagedCallConv");
+ CorInfoUnmanagedCallConv temp = original_ICorJitInfo->getUnmanagedCallConv(method);
+ mc->recGetUnmanagedCallConv(method, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ )
+{
+ mc->cr->AddCall("pInvokeMarshalingRequired");
+ BOOL temp = original_ICorJitInfo->pInvokeMarshalingRequired(method, callSiteSig);
+ mc->recPInvokeMarshalingRequired(method, callSiteSig, temp);
+ return temp;
+}
+
+// Check constraints on method type arguments (only).
+// The parent class should be checked separately using satisfiesClassConstraints(parent).
+BOOL interceptor_ICJI::satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("satisfiesMethodConstraints");
+ BOOL temp = original_ICorJitInfo->satisfiesMethodConstraints(parent, method);
+ mc->recSatisfiesMethodConstraints(parent, method, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::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 */
+ )
+{
+ mc->cr->AddCall("isCompatibleDelegate");
+ BOOL temp = original_ICorJitInfo->isCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate);
+ mc->recIsCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate, temp);
+ return temp;
+}
+
+// Determines whether the delegate creation obeys security transparency rules
+BOOL interceptor_ICJI::isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ )
+{
+ mc->cr->AddCall("isDelegateCreationAllowed");
+ BOOL temp = original_ICorJitInfo->isDelegateCreationAllowed(delegateHnd, calleeHnd);
+ mc->recIsDelegateCreationAllowed(delegateHnd, calleeHnd, temp);
+ return temp;
+}
+
+
+// Indicates if the method is an instance of the generic
+// method that passes (or has passed) verification
+CorInfoInstantiationVerification interceptor_ICJI::isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ )
+{
+ mc->cr->AddCall("isInstantiationOfVerifiedGeneric");
+ CorInfoInstantiationVerification temp = original_ICorJitInfo->isInstantiationOfVerifiedGeneric(method);
+ mc->recIsInstantiationOfVerifiedGeneric(method, temp);
+ return temp;
+}
+
+// Loads the constraints on a typical method definition, detecting cycles;
+// for use in verification.
+void interceptor_ICJI::initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ )
+{
+ mc->cr->AddCall("initConstraintsForVerification");
+ original_ICorJitInfo->initConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+ mc->recInitConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+}
+
+// Returns enum whether the method does not require verification
+// Also see ICorModuleInfo::canSkipVerification
+CorInfoCanSkipVerificationResult interceptor_ICJI::canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ )
+{
+ mc->cr->AddCall("canSkipMethodVerification");
+ CorInfoCanSkipVerificationResult temp = original_ICorJitInfo->canSkipMethodVerification(ftnHandle);
+ mc->recCanSkipMethodVerification(ftnHandle, FALSE, temp);
+ return temp;
+}
+
+// load and restore the method
+void interceptor_ICJI::methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("methodMustBeLoadedBeforeCodeIsRun");
+ original_ICorJitInfo->methodMustBeLoadedBeforeCodeIsRun(method);
+ mc->cr->recMethodMustBeLoadedBeforeCodeIsRun(method);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("mapMethodDeclToMethodImpl");
+ return original_ICorJitInfo->mapMethodDeclToMethodImpl(method);
+}
+
+// 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 interceptor_ICJI::getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ )
+{
+ mc->cr->AddCall("getGSCookie");
+ original_ICorJitInfo->getGSCookie(pCookieVal, ppCookieVal);
+ mc->recGetGSCookie(pCookieVal, ppCookieVal);
+}
+
+/**********************************************************************************/
+//
+// ICorModuleInfo
+//
+/**********************************************************************************/
+// Resolve metadata token into runtime method handles.
+void interceptor_ICJI::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_RESOLVED_TOKEN* pResolvedToken;
+ } param;
+ param.pThis = this;
+ param.pResolvedToken = pResolvedToken;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("resolveToken");
+ pParam->pThis->original_ICorJitInfo->resolveToken(pParam->pResolvedToken);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recResolveToken(param.pResolvedToken, param.exceptionCode);
+ }
+ PAL_ENDTRY
+}
+
+bool interceptor_ICJI::tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ mc->cr->AddCall("tryResolveToken");
+ bool success = original_ICorJitInfo->tryResolveToken(pResolvedToken);
+ mc->recResolveToken(pResolvedToken, success);
+ return success;
+}
+
+// Signature information about the call sig
+void interceptor_ICJI::findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ mc->cr->AddCall("findSig");
+ original_ICorJitInfo->findSig(module, sigTOK, context, sig);
+ mc->recFindSig(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 interceptor_ICJI::findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ mc->cr->AddCall("findCallSiteSig");
+ original_ICorJitInfo->findCallSiteSig(module, methTOK, context, sig);
+ mc->recFindCallSiteSig(module, methTOK, context, sig);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */)
+{
+ mc->cr->AddCall("getTokenTypeAsHandle");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getTokenTypeAsHandle(pResolvedToken);
+ mc->recGetTokenTypeAsHandle(pResolvedToken, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ )
+{
+ mc->cr->AddCall("canSkipVerification");
+ return original_ICorJitInfo->canSkipVerification(module);
+}
+
+// Checks if the given metadata token is valid
+BOOL interceptor_ICJI::isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ mc->cr->AddCall("isValidToken");
+ BOOL result = original_ICorJitInfo->isValidToken(module, metaTOK);
+ mc->recIsValidToken(module, metaTOK, result);
+ return result;
+}
+
+// Checks if the given metadata token is valid StringRef
+BOOL interceptor_ICJI::isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ mc->cr->AddCall("isValidStringRef");
+ BOOL temp = original_ICorJitInfo->isValidStringRef(module, metaTOK);
+ mc->recIsValidStringRef(module, metaTOK, temp);
+ return temp;
+}
+
+BOOL interceptor_ICJI::shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE scope
+ )
+{
+ mc->cr->AddCall("shouldEnforceCallvirtRestriction");
+ BOOL temp = original_ICorJitInfo->shouldEnforceCallvirtRestriction(scope);
+ mc->recShouldEnforceCallvirtRestriction(scope, temp);
+ return temp;
+}
+
+/**********************************************************************************/
+//
+// 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 interceptor_ICJI::asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("asCorInfoType");
+ CorInfoType temp = original_ICorJitInfo->asCorInfoType(cls);
+ mc->recAsCorInfoType(cls, temp);
+ return temp;
+}
+
+// for completeness
+const char* interceptor_ICJI::getClassName (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getClassName");
+ const char* result = original_ICorJitInfo->getClassName(cls);
+ mc->recGetClassName(cls, result);
+ 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 interceptor_ICJI::appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ )
+{
+ mc->cr->AddCall("appendClassName");
+ WCHAR* pBuf = *ppBuf;
+ int nLen = original_ICorJitInfo->appendClassName(ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly);
+ mc->recAppendClassName(cls, fNamespace, fFullInst, fAssembly, pBuf);
+ return nLen;
+}
+
+// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+BOOL interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ mc->cr->AddCall("isValueClass");
+ BOOL temp = original_ICorJitInfo->isValueClass(cls);
+ mc->recIsValueClass(cls, temp);
+ return temp;
+}
+
+// If this method returns true, JIT will do optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType()
+BOOL interceptor_ICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ mc->cr->AddCall("canInlineTypeCheckWithObjectVTable");
+ BOOL temp = original_ICorJitInfo->canInlineTypeCheckWithObjectVTable(cls);
+ mc->recCanInlineTypeCheckWithObjectVTable(cls, temp);
+ return temp;
+}
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getClassAttribs (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getClassAttribs");
+ DWORD temp = original_ICorJitInfo->getClassAttribs(cls);
+ mc->recGetClassAttribs(cls, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ mc->cr->AddCall("isStructRequiringStackAllocRetBuf");
+ BOOL temp = original_ICorJitInfo->isStructRequiringStackAllocRetBuf(cls);
+ mc->recIsStructRequiringStackAllocRetBuf(cls, temp);
+ return temp;
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getClassModule");
+ return original_ICorJitInfo->getClassModule(cls);
+}
+
+// Returns the assembly that contains the module "mod".
+CORINFO_ASSEMBLY_HANDLE interceptor_ICJI::getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ )
+{
+ mc->cr->AddCall("getModuleAssembly");
+ return original_ICorJitInfo->getModuleAssembly(mod);
+}
+
+// Returns the name of the assembly "assem".
+const char* interceptor_ICJI::getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE assem
+ )
+{
+ mc->cr->AddCall("getAssemblyName");
+ return original_ICorJitInfo->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* interceptor_ICJI::LongLifetimeMalloc(size_t sz)
+{
+ mc->cr->AddCall("LongLifetimeMalloc");
+ return original_ICorJitInfo->LongLifetimeMalloc(sz);
+}
+
+void interceptor_ICJI::LongLifetimeFree(void* obj)
+{
+ mc->cr->AddCall("LongLifetimeFree");
+ original_ICorJitInfo->LongLifetimeFree(obj);
+}
+
+size_t interceptor_ICJI::getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getClassModuleIdForStatics");
+ size_t temp = original_ICorJitInfo->getClassModuleIdForStatics(cls, pModule, ppIndirection);
+ mc->recGetClassModuleIdForStatics(cls, pModule, ppIndirection, temp);
+ return temp;
+}
+
+// return the number of bytes needed by an instance of the class
+unsigned interceptor_ICJI::getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getClassSize");
+ unsigned temp = original_ICorJitInfo->getClassSize(cls);
+ mc->recGetClassSize(cls, temp);
+ return temp;
+}
+
+unsigned interceptor_ICJI::getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint
+ )
+{
+ mc->cr->AddCall("getClassAlignmentRequirement");
+ unsigned temp = original_ICorJitInfo->getClassAlignmentRequirement(cls, fDoubleAlignHint);
+ mc->recGetClassAlignmentRequirement(cls, fDoubleAlignHint, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ )
+{
+ mc->cr->AddCall("getClassGClayout");
+ unsigned temp = original_ICorJitInfo->getClassGClayout(cls, gcPtrs);
+ unsigned len = (getClassSize(cls) + sizeof(void *) - 1)/sizeof(void*);
+ mc->recGetClassGClayout(cls, gcPtrs, len, temp);
+ return temp;
+}
+
+// returns the number of instance fields in a class
+unsigned interceptor_ICJI::getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ )
+{
+ mc->cr->AddCall("getClassNumInstanceFields");
+ unsigned temp = original_ICorJitInfo->getClassNumInstanceFields(cls);
+ mc->recGetClassNumInstanceFields(cls, temp);
+ return temp;
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ )
+{
+ mc->cr->AddCall("getFieldInClass");
+ CORINFO_FIELD_HANDLE temp = original_ICorJitInfo->getFieldInClass(clsHnd, num);
+ mc->recGetFieldInClass(clsHnd, num, temp);
+ return temp;
+}
+
+BOOL interceptor_ICJI::checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ )
+{
+ mc->cr->AddCall("checkMethodModifier");
+ BOOL result = original_ICorJitInfo->checkMethodModifier(hMethod, modifier, fOptional);
+ mc->recCheckMethodModifier(hMethod, modifier, fOptional, result);
+ return result;
+}
+
+// returns the "NEW" helper optimized for "newCls."
+CorInfoHelpFunc interceptor_ICJI::getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ )
+{
+ mc->cr->AddCall("getNewHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getNewHelper(pResolvedToken, callerHandle);
+ mc->recGetNewHelper(pResolvedToken, callerHandle, temp);
+ return temp;
+}
+
+// returns the newArr (1-Dim array) helper optimized for "arrayCls."
+CorInfoHelpFunc interceptor_ICJI::getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ )
+{
+ mc->cr->AddCall("getNewArrHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getNewArrHelper(arrayCls);
+ mc->recGetNewArrHelper(arrayCls, temp);
+ return temp;
+}
+
+// returns the optimized "IsInstanceOf" or "ChkCast" helper
+CorInfoHelpFunc interceptor_ICJI::getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ )
+{
+ mc->cr->AddCall("getCastingHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getCastingHelper(pResolvedToken, fThrowing);
+ mc->recGetCastingHelper(pResolvedToken, fThrowing, temp);
+ return temp;
+}
+
+// returns helper to trigger static constructor
+CorInfoHelpFunc interceptor_ICJI::getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ )
+{
+ mc->cr->AddCall("getSharedCCtorHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getSharedCCtorHelper(clsHnd);
+ mc->recGetSharedCCtorHelper(clsHnd, temp);
+ return temp;
+}
+
+CorInfoHelpFunc interceptor_ICJI::getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE ftn
+ )
+{
+ mc->cr->AddCall("getSecurityPrologHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getSecurityPrologHelper(ftn);
+ mc->recGetSecurityPrologHelper(ftn, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getTypeForBox(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getTypeForBox");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getTypeForBox(cls);
+ mc->recGetTypeForBox(cls, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getBoxHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getBoxHelper(cls);
+ mc->recGetBoxHelper(cls, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getUnBoxHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getUnBoxHelper(cls);
+ mc->recGetUnBoxHelper(cls, temp);
+ return temp;
+}
+
+bool interceptor_ICJI::getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ mc->cr->AddCall("getReadyToRunHelper");
+ bool result = original_ICorJitInfo->getReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup);
+ mc->recGetReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup, result);
+ return result;
+}
+
+void interceptor_ICJI::getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ mc->cr->AddCall("getReadyToRunDelegateCtorHelper");
+ original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+ mc->recGetReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+}
+
+const char* interceptor_ICJI::getHelperName(
+ CorInfoHelpFunc funcNum
+ )
+{
+ mc->cr->AddCall("getHelperName");
+ const char* temp = original_ICorJitInfo->getHelperName(funcNum);
+ mc->recGetHelperName(funcNum, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::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
+ )
+{
+ mc->cr->AddCall("initClass");
+ CorInfoInitClassResult temp = original_ICorJitInfo->initClass(field, method, context, speculative);
+ mc->recInitClass(field, method, context, speculative, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("classMustBeLoadedBeforeCodeIsRun");
+ original_ICorJitInfo->classMustBeLoadedBeforeCodeIsRun(cls);
+ mc->cr->recClassMustBeLoadedBeforeCodeIsRun(cls);
+}
+
+// returns the class handle for the special builtin classes
+CORINFO_CLASS_HANDLE interceptor_ICJI::getBuiltinClass (
+ CorInfoClassId classId
+ )
+{
+ mc->cr->AddCall("getBuiltinClass");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getBuiltinClass(classId);
+ mc->recGetBuiltinClass(classId, temp);
+ return temp;
+}
+
+// "System.Int32" ==> CORINFO_TYPE_INT..
+CorInfoType interceptor_ICJI::getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getTypeForPrimitiveValueClass");
+ CorInfoType temp = original_ICorJitInfo->getTypeForPrimitiveValueClass(cls);
+ mc->recGetTypeForPrimitiveValueClass(cls, temp);
+ return temp;
+}
+
+// TRUE if child is a subtype of parent
+// if parent is an interface, then does child implement / extend parent
+BOOL interceptor_ICJI::canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ )
+{
+ mc->cr->AddCall("canCast");
+ BOOL temp = original_ICorJitInfo->canCast(child, parent);
+ mc->recCanCast(child, parent, temp);
+ return temp;
+}
+
+// TRUE if cls1 and cls2 are considered equivalent types.
+BOOL interceptor_ICJI::areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ mc->cr->AddCall("areTypesEquivalent");
+ BOOL temp = original_ICorJitInfo->areTypesEquivalent(cls1, cls2);
+ mc->recAreTypesEquivalent(cls1, cls2, temp);
+ return temp;
+}
+
+// returns is the intersection of cls1 and cls2.
+CORINFO_CLASS_HANDLE interceptor_ICJI::mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ mc->cr->AddCall("mergeClasses");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->mergeClasses(cls1, cls2);
+ mc->recMergeClasses(cls1, cls2, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getParentType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getParentType");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getParentType(cls);
+ mc->recGetParentType(cls, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ )
+{
+ mc->cr->AddCall("getChildType");
+ CorInfoType temp = original_ICorJitInfo->getChildType(clsHnd, clsRet);
+ mc->recGetChildType(clsHnd, clsRet, temp);
+ return temp;
+}
+
+// Check constraints on type arguments of this class and parent classes
+BOOL interceptor_ICJI::satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("satisfiesClassConstraints");
+ BOOL temp = original_ICorJitInfo->satisfiesClassConstraints(cls);
+ mc->recSatisfiesClassConstraints(cls, temp);
+ return temp;
+
+}
+
+// Check if this is a single dimensional array type
+BOOL interceptor_ICJI::isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("isSDArray");
+ BOOL temp = original_ICorJitInfo->isSDArray(cls);
+ mc->recIsSDArray(cls, temp);
+ return temp;
+}
+
+// Get the numbmer of dimensions in an array
+unsigned interceptor_ICJI::getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getArrayRank");
+ unsigned result = original_ICorJitInfo->getArrayRank(cls);
+ mc->recGetArrayRank(cls, result);
+ return result;
+}
+
+// Get static field data for an array
+void * interceptor_ICJI::getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ )
+{
+ mc->cr->AddCall("getArrayInitializationData");
+ void *temp = original_ICorJitInfo->getArrayInitializationData(field, size);
+ mc->recGetArrayInitializationData(field, size, temp);
+ return temp;
+}
+
+// Check Visibility rules.
+CorInfoIsAccessAllowedResult interceptor_ICJI::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. */
+ )
+{
+ mc->cr->AddCall("canAccessClass");
+ CorInfoIsAccessAllowedResult temp = original_ICorJitInfo->canAccessClass(pResolvedToken, callerHandle, pAccessHelper);
+ mc->recCanAccessClass(pResolvedToken, callerHandle, pAccessHelper, temp);
+ return temp;
+}
+
+/**********************************************************************************/
+//
+// 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* interceptor_ICJI::getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ mc->cr->AddCall("getFieldName");
+ const char* temp = original_ICorJitInfo->getFieldName(ftn, moduleName);
+ mc->recGetFieldName(ftn, moduleName, temp);
+ return temp;
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getFieldClass (
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ mc->cr->AddCall("getFieldClass");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getFieldClass(field);
+ mc->recGetFieldClass(field, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ mc->cr->AddCall("getFieldType");
+ CorInfoType temp = original_ICorJitInfo->getFieldType(field, structType, memberParent);
+ mc->recGetFieldType(field, structType, memberParent, temp);
+ return temp;
+}
+
+// return the data member's instance offset
+unsigned interceptor_ICJI::getFieldOffset(
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ mc->cr->AddCall("getFieldOffset");
+ unsigned temp = original_ICorJitInfo->getFieldOffset(field);
+ mc->recGetFieldOffset(field, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field)
+{
+ mc->cr->AddCall("isWriteBarrierHelperRequired");
+ bool result = original_ICorJitInfo->isWriteBarrierHelperRequired(field);
+ mc->recIsWriteBarrierHelperRequired(field, result);
+ return result;
+}
+
+void interceptor_ICJI::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ )
+{
+ mc->cr->AddCall("getFieldInfo");
+ original_ICorJitInfo->getFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+ mc->recGetFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+}
+
+// Returns true iff "fldHnd" represents a static field.
+bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
+{
+ mc->cr->AddCall("isFieldStatic");
+ bool result = original_ICorJitInfo->isFieldStatic(fldHnd);
+ mc->recIsFieldStatic(fldHnd, result);
+ return result;
+}
+
+/*********************************************************************************/
+//
+// 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 interceptor_ICJI::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
+ )
+{
+ mc->cr->AddCall("getBoundaries");
+ original_ICorJitInfo->getBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+ mc->recGetBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+}
+
+// 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.
+//Note - Ownership of pMap is transfered with this call. We need to record it before its passed on to the EE.
+void interceptor_ICJI::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
+ )
+{
+ mc->cr->AddCall("setBoundaries");
+ mc->cr->recSetBoundaries(ftn, cMap, pMap); //Since the EE frees, we've gotta record before its sent to the EE.
+ original_ICorJitInfo->setBoundaries(ftn, cMap, pMap);
+}
+
+// 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 interceptor_ICJI::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
+ )
+{
+ mc->cr->AddCall("getVars");
+ original_ICorJitInfo->getVars(ftn, cVars, vars, extendOthers);
+ mc->recGetVars(ftn, cVars, vars, extendOthers);
+}
+
+// Report back to the EE the location of every variable.
+// note that the JIT might split lifetimes into different
+// locations etc.
+//Note - Ownership of vars is transfered with this call. We need to record it before its passed on to the EE.
+void interceptor_ICJI::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
+ )
+{
+ mc->cr->AddCall("setVars");
+ mc->cr->recSetVars(ftn, cVars, vars); //Since the EE frees, we've gotta record before its sent to the EE.
+ original_ICorJitInfo->setVars(ftn, cVars, vars);
+}
+
+/*-------------------------- 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 * interceptor_ICJI::allocateArray(
+ ULONG cBytes
+ )
+{
+ mc->cr->AddCall("allocateArray");
+ return original_ICorJitInfo->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 interceptor_ICJI::freeArray(
+ void *array
+ )
+{
+ mc->cr->AddCall("freeArray");
+ original_ICorJitInfo->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 interceptor_ICJI::getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ mc->cr->AddCall("getArgNext");
+ CORINFO_ARG_LIST_HANDLE temp = original_ICorJitInfo->getArgNext(args);
+ mc->recGetArgNext(args, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_SIG_INFO* sig;
+ CORINFO_ARG_LIST_HANDLE args;
+ CORINFO_CLASS_HANDLE* vcTypeRet;
+ CorInfoTypeWithMod temp;
+ } param;
+ param.pThis = this;
+ param.sig = sig;
+ param.args = args;
+ param.vcTypeRet = vcTypeRet;
+ param.temp = (CorInfoTypeWithMod)CORINFO_TYPE_UNDEF;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("getArgType");
+ pParam->temp = pParam->pThis->original_ICorJitInfo->getArgType(pParam->sig, pParam->args, pParam->vcTypeRet);
+
+#ifdef fatMC
+ CORINFO_CLASS_HANDLE temp3 = pParam->pThis->getArgClass(pParam->sig, pParam->args);
+#endif
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recGetArgType(sig, args, vcTypeRet, param.temp, param.exceptionCode);
+ }
+ PAL_ENDTRY
+
+ return param.temp;
+ }
+
+// If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_SIG_INFO* sig;
+ CORINFO_ARG_LIST_HANDLE args;
+ CORINFO_CLASS_HANDLE temp;
+ } param;
+ param.pThis = this;
+ param.sig = sig;
+ param.args = args;
+ param.temp = 0;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("getArgClass");
+ pParam->temp = pParam->pThis->original_ICorJitInfo->getArgClass(pParam->sig, pParam->args);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recGetArgClass(sig, args, param.temp, param.exceptionCode);
+
+ //to build up a fat mc
+ getClassName(param.temp);
+ }
+ PAL_ENDTRY
+
+ return param.temp;
+ }
+
+// Returns type of HFA for valuetype
+CorInfoType interceptor_ICJI::getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ )
+{
+ mc->cr->AddCall("getHFAType");
+ return original_ICorJitInfo->getHFAType(hClass);
+}
+
+/*****************************************************************************
+* 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 interceptor_ICJI::GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mc->cr->AddCall("GetErrorHRESULT");
+ return original_ICorJitInfo->GetErrorHRESULT(pExceptionPointers);
+}
+
+// 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 interceptor_ICJI::GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ )
+{
+ mc->cr->AddCall("GetErrorMessage");
+ return original_ICorJitInfo->GetErrorMessage(buffer, bufferLength);
+}
+
+// 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 interceptor_ICJI::FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mc->cr->AddCall("FilterException");
+ int temp = original_ICorJitInfo->FilterException(pExceptionPointers);
+ mc->recFilterException(pExceptionPointers, temp);
+ return temp;
+}
+
+// Cleans up internal EE tracking when an exception is caught.
+void interceptor_ICJI::HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ //bswHack?
+ mc->cr->AddCall("HandleException");
+ original_ICorJitInfo->HandleException(pExceptionPointers);
+ mc->recHandleException(pExceptionPointers);
+}
+
+void interceptor_ICJI::ThrowExceptionForJitResult(
+ HRESULT result)
+{
+ mc->cr->AddCall("ThrowExceptionForJitResult");
+ original_ICorJitInfo->ThrowExceptionForJitResult(result);
+}
+
+//Throws an exception defined by the given throw helper.
+void interceptor_ICJI::ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper)
+{
+ mc->cr->AddCall("ThrowExceptionForHelper");
+ original_ICorJitInfo->ThrowExceptionForHelper(throwHelper);
+}
+
+/*****************************************************************************
+ * 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 interceptor_ICJI::getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ )
+{
+ mc->cr->AddCall("getEEInfo");
+ original_ICorJitInfo->getEEInfo(pEEInfoOut);
+ mc->recGetEEInfo(pEEInfoOut);
+}
+
+// Returns name of the JIT timer log
+LPCWSTR interceptor_ICJI::getJitTimeLogFilename()
+{
+ mc->cr->AddCall("getJitTimeLogFilename");
+ LPCWSTR temp = original_ICorJitInfo->getJitTimeLogFilename();
+ mc->recGetJitTimeLogFilename(temp);
+ return temp;
+}
+
+/*********************************************************************************/
+//
+// Diagnostic methods
+//
+/*********************************************************************************/
+// this function is for debugging only. Returns method token.
+// Returns mdMethodDefNil for dynamic methods.
+mdMethodDef interceptor_ICJI::getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ )
+{
+ mc->cr->AddCall("getMethodDefFromMethod");
+ mdMethodDef result = original_ICorJitInfo->getMethodDefFromMethod(hMethod);
+ mc->recGetMethodDefFromMethod(hMethod, result);
+ 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* interceptor_ICJI::getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ mc->cr->AddCall("getMethodName");
+ const char* temp = original_ICorJitInfo->getMethodName(ftn, moduleName);
+ mc->recGetMethodName(ftn, (char *)temp, moduleName);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ )
+{
+ mc->cr->AddCall("getMethodHash");
+ unsigned temp = original_ICorJitInfo->getMethodHash(ftn);
+ mc->recGetMethodHash(ftn, temp);
+ return temp;
+}
+
+// this function is for debugging only.
+size_t interceptor_ICJI::findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ )
+{
+ mc->cr->AddCall("findNameOfToken");
+ size_t result = original_ICorJitInfo->findNameOfToken(module, metaTOK, szFQName, FQNameCapacity);
+ mc->recFindNameOfToken(module, metaTOK, szFQName, FQNameCapacity, result);
+ return result;
+}
+
+bool interceptor_ICJI::getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ )
+{
+ mc->cr->AddCall("getSystemVAmd64PassStructInRegisterDescriptor");
+ bool result = original_ICorJitInfo->getSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
+ mc->recGetSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr, result);
+ return result;
+}
+
+//Stuff on ICorDynamicInfo
+DWORD interceptor_ICJI::getThreadTLSIndex(
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getThreadTLSIndex");
+ DWORD temp = original_ICorJitInfo->getThreadTLSIndex(ppIndirection);
+ mc->recGetThreadTLSIndex(ppIndirection, temp);
+ return temp;
+}
+
+const void * interceptor_ICJI::getInlinedCallFrameVptr(
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getInlinedCallFrameVptr");
+ const void* temp = original_ICorJitInfo->getInlinedCallFrameVptr(ppIndirection);
+ mc->recGetInlinedCallFrameVptr(ppIndirection, temp);
+ return temp;
+}
+
+LONG * interceptor_ICJI::getAddrOfCaptureThreadGlobal(
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getAddrOfCaptureThreadGlobal");
+ LONG * temp = original_ICorJitInfo->getAddrOfCaptureThreadGlobal(ppIndirection);
+ mc->recGetAddrOfCaptureThreadGlobal(ppIndirection, temp);
+ return temp;
+}
+
+SIZE_T* interceptor_ICJI::getAddrModuleDomainID(CORINFO_MODULE_HANDLE module)
+{
+ mc->cr->AddCall("getAddrModuleDomainID");
+ return original_ICorJitInfo->getAddrModuleDomainID(module);
+}
+
+// return the native entry point to an EE helper (see CorInfoHelpFunc)
+void* interceptor_ICJI::getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getHelperFtn");
+ void *temp = original_ICorJitInfo->getHelperFtn(ftnNum, ppIndirection);
+ mc->recGetHelperFtn(ftnNum, ppIndirection, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags)
+{
+ mc->cr->AddCall("getFunctionEntryPoint");
+ original_ICorJitInfo->getFunctionEntryPoint(ftn, pResult, accessFlags);
+ mc->recGetFunctionEntryPoint(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 interceptor_ICJI::getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult)
+{
+ mc->cr->AddCall("getFunctionFixedEntryPoint");
+ original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, pResult);
+ mc->recGetFunctionFixedEntryPoint(ftn, pResult);
+}
+
+// get the synchronization handle that is passed to monXstatic function
+void* interceptor_ICJI::getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getMethodSync");
+ void *temp = original_ICorJitInfo->getMethodSync(ftn, ppIndirection);
+ mc->recGetMethodSync(ftn, ppIndirection, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ )
+{
+ mc->cr->AddCall("getLazyStringLiteralHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getLazyStringLiteralHelper(handle);
+ mc->recGetLazyStringLiteralHelper(handle, temp);
+ return temp;
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("embedModuleHandle");
+ CORINFO_MODULE_HANDLE temp = original_ICorJitInfo->embedModuleHandle(handle, ppIndirection);
+ mc->recEmbedModuleHandle(handle, ppIndirection, temp);
+ return temp;
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("embedClassHandle");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->embedClassHandle(handle, ppIndirection);
+ mc->recEmbedClassHandle(handle, ppIndirection, temp);
+ return temp;
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("embedMethodHandle");
+ CORINFO_METHOD_HANDLE temp = original_ICorJitInfo->embedMethodHandle(handle, ppIndirection);
+ mc->recEmbedMethodHandle(handle, ppIndirection, temp);
+ return temp;
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("embedFieldHandle");
+ CORINFO_FIELD_HANDLE temp = original_ICorJitInfo->embedFieldHandle(handle, ppIndirection);
+ mc->recEmbedFieldHandle(handle, ppIndirection, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * pResult)
+{
+ mc->cr->AddCall("embedGenericHandle");
+ original_ICorJitInfo->embedGenericHandle(pResolvedToken, fEmbedParent, pResult);
+ mc->recEmbedGenericHandle(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 interceptor_ICJI::getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ )
+{
+ mc->cr->AddCall("getLocationOfThisType");
+ CORINFO_LOOKUP_KIND temp = original_ICorJitInfo->getLocationOfThisType(context);
+ mc->recGetLocationOfThisType(context, &temp);
+ return temp;
+}
+
+// return the unmanaged target *if method has already been prelinked.*
+void* interceptor_ICJI::getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getPInvokeUnmanagedTarget");
+ void *result = original_ICorJitInfo->getPInvokeUnmanagedTarget(method, ppIndirection);
+ mc->recGetPInvokeUnmanagedTarget(method, ppIndirection, result);
+ return result;
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void* interceptor_ICJI::getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getAddressOfPInvokeFixup");
+ void *temp = original_ICorJitInfo->getAddressOfPInvokeFixup(method, ppIndirection);
+ mc->recGetAddressOfPInvokeFixup(method, ppIndirection, temp);
+ return temp;
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void interceptor_ICJI::getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ )
+{
+ mc->cr->AddCall("getAddressOfPInvokeTarget");
+ original_ICorJitInfo->getAddressOfPInvokeTarget(method, pLookup);
+ mc->recGetAddressOfPInvokeTarget(method, pLookup);
+}
+
+// Generate a cookie based on the signature that would needs to be passed
+// to CORINFO_HELP_PINVOKE_CALLI
+LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection
+ )
+{
+ mc->cr->AddCall("GetCookieForPInvokeCalliSig");
+ LPVOID temp = original_ICorJitInfo->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection);
+ mc->recGetCookieForPInvokeCalliSig(szMetaSig, ppIndirection, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ )
+{
+ mc->cr->AddCall("canGetCookieForPInvokeCalliSig");
+ bool temp = original_ICorJitInfo->canGetCookieForPInvokeCalliSig(szMetaSig);
+ mc->recCanGetCookieForPInvokeCalliSig(szMetaSig, temp);
+ return temp;
+}
+
+// Gets a handle that is checked to see if the current method is
+// included in "JustMyCode"
+CORINFO_JUST_MY_CODE_HANDLE interceptor_ICJI::getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection
+ )
+{
+ mc->cr->AddCall("getJustMyCodeHandle");
+ CORINFO_JUST_MY_CODE_HANDLE temp = original_ICorJitInfo->getJustMyCodeHandle(method, ppIndirection);
+ mc->recGetJustMyCodeHandle(method, ppIndirection, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ )
+{
+ mc->cr->AddCall("GetProfilingHandle");
+ original_ICorJitInfo->GetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+ mc->recGetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+}
+
+// Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+void interceptor_ICJI::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
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_RESOLVED_TOKEN* pResolvedToken;
+ CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken;
+ CORINFO_METHOD_HANDLE callerHandle;
+ CORINFO_CALLINFO_FLAGS flags;
+ CORINFO_CALL_INFO* pResult;
+ } param;
+ param.pThis = this;
+ param.pResolvedToken = pResolvedToken;
+ param.pConstrainedResolvedToken = pConstrainedResolvedToken;
+ param.callerHandle = callerHandle;
+ param.flags = flags;
+ param.pResult = pResult;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("getCallInfo");
+ pParam->pThis->original_ICorJitInfo->getCallInfo(pParam->pResolvedToken, pParam->pConstrainedResolvedToken, pParam->callerHandle, pParam->flags, pParam->pResult);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recGetCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult, param.exceptionCode);
+ }
+ PAL_ENDTRY
+ }
+
+BOOL interceptor_ICJI::canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE hInstanceType)
+{
+ mc->cr->AddCall("canAccessFamily");
+ BOOL temp = original_ICorJitInfo->canAccessFamily(hCaller, hInstanceType);
+ mc->recCanAccessFamily(hCaller, hInstanceType, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::isRIDClassDomainID(CORINFO_CLASS_HANDLE cls)
+{
+ mc->cr->AddCall("isRIDClassDomainID");
+ return original_ICorJitInfo->isRIDClassDomainID(cls);
+}
+
+// returns the class's domain ID for accessing shared statics
+unsigned interceptor_ICJI::getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getClassDomainID");
+ unsigned temp = original_ICorJitInfo->getClassDomainID(cls, ppIndirection);
+ mc->recGetClassDomainID(cls, ppIndirection, temp);
+ return temp;
+}
+
+// return the data's address (for static fields only)
+void* interceptor_ICJI::getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getFieldAddress");
+ void *temp = original_ICorJitInfo->getFieldAddress(field, ppIndirection);
+
+ //Figure out the element type so we know how much we can load
+ CORINFO_CLASS_HANDLE cch;
+ CorInfoType cit = getFieldType(field, &cch, NULL);
+ mc->recGetFieldAddress(field, ppIndirection, temp, cit);
+ return temp;
+}
+
+// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+CORINFO_VARARGS_HANDLE interceptor_ICJI::getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getVarArgsHandle");
+ CORINFO_VARARGS_HANDLE temp = original_ICorJitInfo->getVarArgsHandle(pSig, ppIndirection);
+ mc->recGetVarArgsHandle(pSig, ppIndirection, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ )
+{
+ mc->cr->AddCall("canGetVarArgsHandle");
+ bool temp = original_ICorJitInfo->canGetVarArgsHandle(pSig);
+ mc->recCanGetVarArgsHandle(pSig, temp);
+ return temp;
+}
+
+// Allocate a string literal on the heap and return a handle to it
+InfoAccessType interceptor_ICJI::constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ )
+{
+ mc->cr->AddCall("constructStringLiteral");
+ InfoAccessType temp = original_ICorJitInfo->constructStringLiteral(module, metaTok, ppValue);
+ mc->recConstructStringLiteral(module, metaTok, *ppValue, temp);
+ return temp;
+}
+
+InfoAccessType interceptor_ICJI::emptyStringLiteral(void **ppValue)
+{
+ mc->cr->AddCall("emptyStringLiteral");
+ InfoAccessType temp = original_ICorJitInfo->emptyStringLiteral(ppValue);
+ mc->recEmptyStringLiteral(ppValue, temp);
+ return temp;
+}
+
+// (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 interceptor_ICJI::getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getFieldThreadLocalStoreID");
+ DWORD temp = original_ICorJitInfo->getFieldThreadLocalStoreID(field, ppIndirection);
+ mc->recGetFieldThreadLocalStoreID(field, ppIndirection, temp);
+ return temp;
+}
+
+// Sets another object to intercept calls to "self" and current method being compiled
+void interceptor_ICJI::setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ )
+{
+ mc->cr->AddCall("setOverride");
+ original_ICorJitInfo->setOverride(pOverride, currentMethod);
+}
+
+// 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 interceptor_ICJI::addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ )
+{
+ mc->cr->AddCall("addActiveDependency");
+ original_ICorJitInfo->addActiveDependency(moduleFrom, moduleTo);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ )
+{
+ mc->cr->AddCall("GetDelegateCtor");
+ CORINFO_METHOD_HANDLE temp = original_ICorJitInfo->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData);
+ mc->recGetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData, temp);
+ return temp;
+}
+
+void interceptor_ICJI::MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ )
+{
+ mc->cr->AddCall("MethodCompileComplete");
+ original_ICorJitInfo->MethodCompileComplete(methHnd);
+}
+
+// return a thunk that will copy the arguments for the given signature.
+void* interceptor_ICJI::getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ )
+{
+ mc->cr->AddCall("getTailCallCopyArgsThunk");
+ void *result = original_ICorJitInfo->getTailCallCopyArgsThunk(pSig, flags);
+ mc->recGetTailCallCopyArgsThunk(pSig, flags, result);
+ return result;
+}
+
+//Stuff directly on ICorJitInfo
+
+// Returns extended flags for a particular compilation instance.
+DWORD interceptor_ICJI::getJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes)
+{
+ mc->cr->AddCall("getJitFlags");
+ DWORD result = original_ICorJitInfo->getJitFlags(jitFlags, sizeInBytes);
+ mc->recGetJitFlags(jitFlags, sizeInBytes, result);
+ return result;
+}
+
+// Runs the given function with the given parameter under an error trap
+// and returns true if the function completes successfully. We don't
+// record the results of the call: when this call gets played back,
+// its result will depend on whether or not `function` calls something
+// that throws at playback time rather than at capture time.
+bool interceptor_ICJI::runWithErrorTrap(void (*function)(void*), void *param)
+{
+ mc->cr->AddCall("runWithErrorTrap");
+ return original_ICorJitInfo->runWithErrorTrap(function, param);
+}
+
+// return memory manager that the JIT can use to allocate a regular memory
+IEEMemoryManager* interceptor_ICJI::getMemoryManager()
+{
+ mc->cr->AddCall("getMemoryManager");
+ if(current_IEEMM->original_IEEMM == nullptr)
+ current_IEEMM->original_IEEMM = original_ICorJitInfo->getMemoryManager();
+ return current_IEEMM;
+}
+
+// get a block of memory for the code, readonly data, and read-write data
+void interceptor_ICJI::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 */
+ )
+{
+ mc->cr->AddCall("allocMem");
+ original_ICorJitInfo->allocMem(hotCodeSize, coldCodeSize, roDataSize, xcptnsCount, flag, hotCodeBlock, coldCodeBlock, roDataBlock);
+ 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 interceptor_ICJI::reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ )
+{
+ mc->cr->AddCall("reserveUnwindInfo");
+ original_ICorJitInfo->reserveUnwindInfo(isFunclet, isColdCode, unwindSize);
+ 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 interceptor_ICJI::allocUnwindInfo(
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ )
+{
+ mc->cr->AddCall("allocUnwindInfo");
+ original_ICorJitInfo->allocUnwindInfo(pHotCode, pColdCode, startOffset, endOffset, unwindSize, pUnwindBlock, funcKind);
+ 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 *interceptor_ICJI::allocGCInfo(size_t size /* IN */)
+{
+ mc->cr->AddCall("allocGCInfo");
+ void *temp = original_ICorJitInfo->allocGCInfo(size);
+ mc->cr->recAllocGCInfo(size, temp);
+ return temp;
+}
+
+//only used on x64
+void interceptor_ICJI::yieldExecution()
+{
+ mc->cr->AddCall("yieldExecution"); //Nothing to record
+ original_ICorJitInfo->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 interceptor_ICJI::setEHcount (unsigned cEH /* IN */)
+{
+ mc->cr->AddCall("setEHcount");
+ original_ICorJitInfo->setEHcount(cEH);
+ 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 interceptor_ICJI::setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ )
+{
+ mc->cr->AddCall("setEHinfo");
+ original_ICorJitInfo->setEHinfo(EHnumber, clause);
+ 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 interceptor_ICJI::logMsg(unsigned level, const char* fmt, va_list args)
+{
+ mc->cr->AddCall("logMsg");
+ return original_ICorJitInfo->logMsg(level, fmt, args);
+}
+
+// do an assert. will return true if the code should retry (DebugBreak)
+// returns false, if the assert should be igored.
+int interceptor_ICJI::doAssert(const char* szFile, int iLine, const char* szExpr)
+{
+ mc->cr->AddCall("doAssert");
+ return original_ICorJitInfo->doAssert(szFile, iLine, szExpr);
+}
+
+void interceptor_ICJI::reportFatalError(CorJitResult result)
+{
+ mc->cr->AddCall("reportFatalError");
+ original_ICorJitInfo->reportFatalError(result);
+ mc->cr->recReportFatalError(result);
+}
+
+// allocate a basic block profile buffer where execution counts will be stored
+// for jitted basic blocks.
+HRESULT interceptor_ICJI::allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer **profileBuffer
+ )
+{
+ mc->cr->AddCall("allocBBProfileBuffer");
+ HRESULT result = original_ICorJitInfo->allocBBProfileBuffer(count, profileBuffer);
+ mc->cr->recAllocBBProfileBuffer(count, profileBuffer, result);
+ return result;
+}
+
+// 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 interceptor_ICJI::getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG *count, // The number of basic blocks that we have
+ ProfileBuffer **profileBuffer,
+ ULONG *numRuns
+ )
+{
+ mc->cr->AddCall("getBBProfileData");
+ HRESULT temp = original_ICorJitInfo->getBBProfileData(ftnHnd, count, profileBuffer, numRuns);
+ mc->recGetBBProfileData(ftnHnd, count, profileBuffer, numRuns, temp);
+ return temp;
+}
+
+// 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 interceptor_ICJI::recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ )
+{
+ mc->cr->AddCall("recordCallSite");
+ original_ICorJitInfo->recordCallSite(instrOffset, callSig, methodHandle);
+ mc->cr->recRecordCallSite(instrOffset, callSig, methodHandle);
+}
+
+// A relocation is recorded if we are pre-jitting.
+// A jump thunk may be inserted if we are jitting
+void interceptor_ICJI::recordRelocation(
+ void *location, /* IN */
+ void *target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ )
+{
+ mc->cr->AddCall("recordRelocation");
+ original_ICorJitInfo->recordRelocation(location, target, fRelocType, slotNum, addlDelta);
+ mc->cr->recRecordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+WORD interceptor_ICJI::getRelocTypeHint(void *target)
+{
+ mc->cr->AddCall("getRelocTypeHint");
+ WORD result = original_ICorJitInfo->getRelocTypeHint(target);
+ mc->recGetRelocTypeHint(target, result);
+ 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 interceptor_ICJI::getModuleNativeEntryPointRange(
+ void **pStart, /* OUT */
+ void **pEnd /* OUT */
+ )
+{
+ mc->cr->AddCall("getModuleNativeEntryPointRange");
+ original_ICorJitInfo->getModuleNativeEntryPointRange(pStart, pEnd);
+}
+
+// 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 interceptor_ICJI::getExpectedTargetArchitecture()
+{
+ return original_ICorJitInfo->getExpectedTargetArchitecture();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.h b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.h
new file mode 100644
index 0000000000..08d7643613
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.h
@@ -0,0 +1,30 @@
+//
+// 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"
+#include "methodcontext.h"
+
+class interceptor_ICJI : public ICorJitInfo
+{
+
+#include "icorjitinfoimpl.h"
+
+private:
+
+ void makeFatMC_ClassHandle(CORINFO_CLASS_HANDLE cls, bool getAttribs);
+
+public:
+
+ //Added to help us track the original icji and be able to easily indirect
+ //to it. And a simple way to keep one memory manager instance per instance.
+ ICorJitInfo *original_ICorJitInfo;
+ MethodContext *mc;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.cpp
new file mode 100644
index 0000000000..e0021244cd
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.cpp
@@ -0,0 +1,72 @@
+//
+// 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 "ieememorymanager.h"
+#include "superpmi-shim-collector.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEEMM::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEEMM->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::AddRef()
+{
+ return original_IEEMM->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::Release()
+{
+ return original_IEEMM->Release();
+}
+
+//***************************************************************************
+// IEEMemoryManager methods for locking
+//***************************************************************************
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+ return original_IEEMM->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return original_IEEMM->ClrVirtualFree(lpAddress, dwSize, dwFreeType);
+}
+SIZE_T STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ return original_IEEMM->ClrVirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ return original_IEEMM->ClrVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessHeap()
+{
+ return original_IEEMM->ClrGetProcessHeap();
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ return original_IEEMM->ClrHeapCreate(flOptions, dwInitialSize, dwMaximumSize);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapDestroy(HANDLE hHeap)
+{
+ return original_IEEMM->ClrHeapDestroy(hHeap);
+}
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
+{
+ return original_IEEMM->ClrHeapAlloc(hHeap, dwFlags, dwBytes);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ return original_IEEMM->ClrHeapFree(hHeap, dwFlags, lpMem);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ return original_IEEMM->ClrHeapValidate(hHeap, dwFlags, lpMem);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessExecutableHeap()
+{
+ return original_IEEMM->ClrGetProcessExecutableHeap();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.h b/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.h
new file mode 100644
index 0000000000..4855d383b5
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.h
@@ -0,0 +1,43 @@
+//
+// 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"
+
+class interceptor_IEEMM : public IEEMemoryManager
+{
+public:
+ interceptor_IEEMM()
+ : original_IEEMM(nullptr)
+ { }
+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:
+ IEEMemoryManager *original_IEEMM;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.cpp
new file mode 100644
index 0000000000..6bdde6ec8b
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.cpp
@@ -0,0 +1,154 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// IExecutionEngine.cpp - core shim implementation for IEE stuff
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "iexecutionengine.h"
+#include "superpmi-shim-collector.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEE::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEE->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::AddRef()
+{
+ return original_IEE->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::Release()
+{
+ return original_IEE->Release();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for TLS
+//***************************************************************************
+// Associate a callback for cleanup with a TLS slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ original_IEE->TLS_AssociateCallback(slot, callback);
+}
+// Get the TLS block for fast Get/Set operations
+LPVOID* STDMETHODCALLTYPE interceptor_IEE::TLS_GetDataBlock()
+{
+ return original_IEE->TLS_GetDataBlock();
+}
+// Get the value at a slot
+LPVOID STDMETHODCALLTYPE interceptor_IEE::TLS_GetValue(DWORD slot)
+{
+ return original_IEE->TLS_GetValue(slot);
+}
+// Get the value at a slot, return FALSE if TLS info block doesn't exist
+BOOL STDMETHODCALLTYPE interceptor_IEE::TLS_CheckValue(DWORD slot, LPVOID *pValue)
+{
+ return original_IEE->TLS_CheckValue(slot, pValue);
+}
+// Set the value at a slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_SetValue(DWORD slot, LPVOID pData)
+{
+ original_IEE->TLS_SetValue(slot, pData);
+}
+// Free TLS memory block and make callback
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_ThreadDetaching()
+{
+ original_IEE->TLS_ThreadDetaching();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for locking
+//***************************************************************************
+CRITSEC_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
+{
+ return original_IEE->CreateLock(szTag, level, flags);
+}
+void STDMETHODCALLTYPE interceptor_IEE::DestroyLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->DestroyLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::AcquireLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->AcquireLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ReleaseLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->ReleaseLock(lock);
+}
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateAutoEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateAutoEvent(bInitialState);
+}
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateManualEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateManualEvent(bInitialState);
+}
+void STDMETHODCALLTYPE interceptor_IEE::CloseEvent(EVENT_COOKIE event)
+{
+ original_IEE->CloseEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrSetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrSetEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrResetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrResetEvent(event);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->WaitForEvent(event, dwMilliseconds, bAlertable);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds)
+{
+ return original_IEE->WaitForSingleObject(handle, dwMilliseconds);
+}
+SEMAPHORE_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ return original_IEE->ClrCreateSemaphore(dwInitial, dwMax);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ original_IEE->ClrCloseSemaphore(semaphore);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForSemaphore(semaphore, dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ return original_IEE->ClrReleaseSemaphore(semaphore, lReleaseCount, lpPreviousCount);
+}
+MUTEX_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName)
+{
+ return original_IEE->ClrCreateMutex(lpMutexAttributes, bInitialOwner, lpName);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ original_IEE->ClrCloseMutex(mutex);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ return original_IEE->ClrReleaseMutex(mutex);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForMutex(MUTEX_COOKIE mutex, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForMutex(mutex, dwMilliseconds, bAlertable);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrSleepEx(dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrAllocationDisallowed()
+{
+ return original_IEE->ClrAllocationDisallowed();
+}
+void STDMETHODCALLTYPE interceptor_IEE::GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ original_IEE->GetLastThrownObjectExceptionFromThread(ppvException);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.h b/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.h
new file mode 100644
index 0000000000..49c247ff99
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.h
@@ -0,0 +1,70 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// IExecutionEngine.h - core shim implementation for IEE stuff
+//----------------------------------------------------------
+#ifndef _IExecutionEngine
+#define _IExecutionEngine
+
+#include "ieememorymanager.h"
+
+class interceptor_IEE : 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);
+
+public:
+ IExecutionEngine *original_IEE; //Our extra value that holds a pointer to the original IEE we'll pass calls along to
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/jithost.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/jithost.cpp
new file mode 100644
index 0000000000..5e63a76be7
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/jithost.cpp
@@ -0,0 +1,64 @@
+//
+// 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 "runtimedetails.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+JitHost* g_ourJitHost;
+
+JitHost::JitHost(ICorJitHost* wrappedHost, MethodContext* methodContext) : wrappedHost(wrappedHost), mc(methodContext)
+{
+}
+
+void JitHost::setMethodContext(MethodContext* methodContext)
+{
+ this->mc = methodContext;
+}
+
+void* JitHost::allocateMemory(size_t size, bool usePageAllocator)
+{
+ return wrappedHost->allocateMemory(size, usePageAllocator);
+}
+
+void JitHost::freeMemory(void* block, bool usePageAllocator)
+{
+ return wrappedHost->freeMemory(block, usePageAllocator);
+}
+
+int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
+{
+ mc->cr->AddCall("getIntConfigValue");
+ int result = wrappedHost->getIntConfigValue(key, defaultValue);
+
+ // The JIT eagerly asks about every config value. If we store all these
+ // queries, it takes almost half the MC file space. So only store the
+ // non-default answers.
+ if (result != defaultValue)
+ {
+ mc->recGetIntConfigValue(key, defaultValue, result);
+ }
+ return result;
+}
+
+const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
+{
+ mc->cr->AddCall("getStringConfigValue");
+ const wchar_t *result = wrappedHost->getStringConfigValue(key);
+
+ // Don't store null returns, which is the default
+ if (result != nullptr)
+ {
+ mc->recGetStringConfigValue(key, result);
+ }
+ return result;
+}
+
+void JitHost::freeStringConfigValue(const wchar_t* value)
+{
+ mc->cr->AddCall("freeStringConfigValue");
+ wrappedHost->freeStringConfigValue(value);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/jithost.h b/src/ToolBox/superpmi/superpmi-shim-collector/jithost.h
new file mode 100644
index 0000000000..2950a55682
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/jithost.h
@@ -0,0 +1,25 @@
+//
+// 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(ICorJitHost* wrappedHost, MethodContext* methodContext);
+
+ void setMethodContext(MethodContext* methodContext);
+
+#include "icorjithostimpl.h"
+
+private:
+ ICorJitHost* wrappedHost;
+ MethodContext* mc;
+};
+
+extern JitHost* g_ourJitHost;
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp
new file mode 100644
index 0000000000..1a6d73da0a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp
@@ -0,0 +1,309 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim-Collector.cpp - Shim that collects and yields .mc (method context) files.
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "coreclrcallbacks.h"
+#include "icorjitcompiler.h"
+#include "runtimedetails.h"
+#include "errorhandling.h"
+#include "logging.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+//Assumptions:
+// -We'll never be unloaded - we leak memory and have no facility to unload libraries
+// -printf output to console is okay
+
+HMODULE g_hRealJit = 0; //We leak this currently (could do the proper shutdown in process_detach)
+WCHAR* g_realJitPath = nullptr; //We leak this (could do the proper shutdown in process_detach)
+WCHAR* g_logPath = nullptr; //Again, we leak this one too...
+WCHAR* g_dataFileName = nullptr; //We leak this
+char* g_logFilePath = nullptr; //We *don't* leak this, hooray!
+WCHAR* g_HomeDirectory = nullptr;
+WCHAR* g_DefaultRealJitPath = nullptr;
+MethodContext* g_globalContext = nullptr;
+
+void SetDefaultPaths()
+{
+ if (g_HomeDirectory == nullptr)
+ {
+ g_HomeDirectory = GetEnvironmentVariableWithDefaultW(W("HOME"), W("."));
+ }
+
+ if (g_DefaultRealJitPath == nullptr)
+ {
+ size_t len = wcslen(g_HomeDirectory) + 1 + wcslen(DEFAULT_REAL_JIT_NAME_W) + 1;
+ g_DefaultRealJitPath = new WCHAR[len];
+ wcscpy_s(g_DefaultRealJitPath, len, g_HomeDirectory);
+ wcscat_s(g_DefaultRealJitPath, len, DIRECTORY_SEPARATOR_STR_W);
+ wcscat_s(g_DefaultRealJitPath, len, DEFAULT_REAL_JIT_NAME_W);
+ }
+}
+
+void SetLibName()
+{
+ if (g_realJitPath == nullptr)
+ {
+ g_realJitPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimPath"), g_DefaultRealJitPath);
+ }
+}
+
+void SetLogPath()
+{
+ if (g_logPath == nullptr)
+ {
+ g_logPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimLogPath"), g_HomeDirectory);
+ }
+}
+
+void SetLogPathName()
+{
+ // NOTE: under PAL, we don't get the comand line, so we depend on the random number generator to give us a unique filename.
+ WCHAR *OriginalExecutableName = GetCommandLineW();//TODO-Cleanup: not cool to write to the process view of commandline....
+ size_t len = wcslen(OriginalExecutableName);
+ WCHAR *ExecutableName = new WCHAR[len+1];
+ wcscpy_s(ExecutableName, len+1, OriginalExecutableName);
+ ExecutableName[len] = W('\0');
+ WCHAR *quote1 = NULL;
+
+ //if there are any quotes in filename convert them to spaces.
+ while ((quote1 = wcsstr(ExecutableName, W("\""))) != NULL)
+ *quote1 = W(' ');
+
+ //remove any illegal or annoying characters from file name by converting them to underscores
+ while ((quote1 = wcspbrk(ExecutableName, W("=<>:\"/\\|?! *.,"))) != NULL)
+ *quote1 = W('_');
+
+ const WCHAR *DataFileExtension = W(".mc");
+ size_t ExecutableNameLength = wcslen(ExecutableName);
+ size_t DataFileExtensionLength = wcslen(DataFileExtension);
+ size_t logPathLength = wcslen(g_logPath);
+
+ size_t dataFileNameLength = logPathLength + 1 + ExecutableNameLength + 1 + DataFileExtensionLength + 1;
+
+ const size_t MaxAcceptablePathLength = MAX_PATH - 20; // subtract 20 to leave buffer, for possible random number addition
+ if (dataFileNameLength >= MaxAcceptablePathLength)
+ {
+ // The path name is too long; creating the file will fail. This can happen because we use the command line,
+ // which for ngen includes lots of environment variables, for example.
+
+ // Assume (!) the extra space is all in the ExecutableName, so shorten that.
+ ExecutableNameLength -= dataFileNameLength - MaxAcceptablePathLength;
+
+ dataFileNameLength = MaxAcceptablePathLength;
+ }
+
+ // Always add a random number, just in case the above doesn't give us a unique filename.
+#ifdef FEATURE_PAL
+ unsigned __int64 randNumber = 0;
+ const size_t RandNumberLength = sizeof(randNumber) * 2 + 1; // 16 hex digits + null
+ WCHAR RandNumberString[RandNumberLength];
+ PAL_Random(/* bStrong */ FALSE, &randNumber, sizeof(randNumber));
+ swprintf_s(RandNumberString, RandNumberLength, W("%016llX"), randNumber);
+#else // !FEATURE_PAL
+ unsigned int randNumber = 0;
+ const size_t RandNumberLength = sizeof(randNumber) * 2 + 1; // 8 hex digits + null
+ WCHAR RandNumberString[RandNumberLength];
+ rand_s(&randNumber);
+ swprintf_s(RandNumberString, RandNumberLength, W("%08X"), randNumber);
+#endif // !FEATURE_PAL
+
+ dataFileNameLength += RandNumberLength - 1;
+
+ // Construct the full pathname we're going to use.
+ g_dataFileName = new WCHAR[dataFileNameLength];
+ g_dataFileName[0] = 0;
+ wcsncat_s(g_dataFileName, dataFileNameLength, g_logPath, logPathLength);
+ wcsncat_s(g_dataFileName, dataFileNameLength, DIRECTORY_SEPARATOR_STR_W, 1);
+ wcsncat_s(g_dataFileName, dataFileNameLength, ExecutableName, ExecutableNameLength);
+
+ if (RandNumberLength > 0)
+ {
+ wcsncat_s(g_dataFileName, dataFileNameLength, RandNumberString, RandNumberLength);
+ }
+
+ wcsncat_s(g_dataFileName, dataFileNameLength, DataFileExtension, DataFileExtensionLength);
+}
+
+// TODO: this only works for ANSI file paths...
+void SetLogFilePath()
+{
+ if (g_logFilePath == nullptr)
+ {
+ // If the environment variable isn't set, we don't enable file logging
+ g_logFilePath = GetEnvironmentVariableWithDefaultA("SuperPMIShimLogFilePath", nullptr);
+ }
+}
+
+extern "C"
+BOOL
+#ifndef FEATURE_PAL
+APIENTRY
+#endif // !FEATURE_PAL
+DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+#ifdef FEATURE_PAL
+ if (0 != PAL_InitializeDLL())
+ {
+ fprintf(stderr, "Error: Fail to PAL_InitializeDLL\n");
+ exit(1);
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+ SetLogFilePath();
+ Logger::OpenLogFile(g_logFilePath);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ Logger::Shutdown();
+
+ delete[] g_logFilePath;
+ g_logFilePath = nullptr;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+// Exported via def file
+extern "C"
+void __stdcall jitStartup(ICorJitHost* host)
+{
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if (g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if (g_hRealJit == 0)
+ {
+ LogError("jitStartup() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ // Get the required entrypoint
+ PjitStartup pnjitStartup = (PjitStartup)::GetProcAddress(g_hRealJit, "jitStartup");
+ if (pnjitStartup == nullptr)
+ {
+ // This portion of the interface is not used by the JIT under test.
+ return;
+ }
+
+ g_globalContext = new MethodContext();
+ g_ourJitHost = new JitHost(host, g_globalContext);
+ pnjitStartup(g_ourJitHost);
+}
+
+//Exported via def file
+extern "C"
+ICorJitCompiler* __stdcall getJit()
+{
+ DWORD dwRetVal = 0;
+ PgetJit pngetJit;
+ interceptor_ICJC *pJitInstance = nullptr;
+ ICorJitCompiler *tICJI = nullptr;
+
+ SetDefaultPaths();
+ SetLibName();
+ SetLogPath();
+ SetLogPathName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("getJit() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return nullptr;
+ }
+ }
+
+ //get the required entrypoints
+ pngetJit = (PgetJit)::GetProcAddress(g_hRealJit, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("getJit() - GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return nullptr;
+ }
+
+ tICJI = pngetJit();
+ if(tICJI == nullptr)
+ {
+ LogError("getJit() - pngetJit gave us null");
+ return nullptr;
+ }
+
+ pJitInstance = new interceptor_ICJC();
+ pJitInstance->original_ICorJitCompiler = tICJI;
+
+ //create our datafile
+ pJitInstance->hFile = CreateFileW(g_dataFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (pJitInstance->hFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Couldn't open file '%ws': error %d", g_dataFileName, GetLastError());
+ }
+
+ return pJitInstance;
+}
+
+//Exported via def file
+extern "C"
+void __stdcall sxsJitStartup(CoreClrCallbacks const & original_cccallbacks)
+{
+ PsxsJitStartup pnsxsJitStartup;
+
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("sxsJitStartup() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ //get entry point
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(g_hRealJit, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("sxsJitStartup() - GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return;
+ }
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ original_CoreClrCallbacks = new CoreClrCallbacks();
+ original_CoreClrCallbacks->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ original_CoreClrCallbacks->m_pfnIEE = original_cccallbacks.m_pfnIEE;
+ original_CoreClrCallbacks->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ original_CoreClrCallbacks->m_pfnGetCLRFunction = original_cccallbacks.m_pfnGetCLRFunction;
+
+ CoreClrCallbacks *temp = new CoreClrCallbacks();
+
+ temp->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ temp->m_pfnIEE = IEE_t;
+ temp->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ temp->m_pfnGetCLRFunction = GetCLRFunction;
+
+ pnsxsJitStartup(*temp);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.def b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.def
new file mode 100644
index 0000000000..436434c3de
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.def
@@ -0,0 +1,5 @@
+LIBRARY
+EXPORTS
+ getJit
+ jitStartup
+ sxsJitStartup
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.h b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.h
new file mode 100644
index 0000000000..62c813fae1
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim-Collector.h - Shim that collects and yields .mc (method context) files.
+//----------------------------------------------------------
+#ifndef _SuperPMIShim
+#define _SuperPMIShim
+
+class MethodContext;
+extern MethodContext* g_globalContext;
+
+void DebugBreakorAV(int val);
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/.gitmirror b/src/ToolBox/superpmi/superpmi-shim-counter/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/.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-shim-counter/CMakeLists.txt b/src/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt
new file mode 100644
index 0000000000..2243053b3f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt
@@ -0,0 +1,73 @@
+project(superpmi-shim-counter)
+
+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)
+
+set(SUPERPMI_SHIM_COUNTER_SOURCES
+ coreclrcallbacks.cpp
+ jithost.cpp
+ icorjitcompiler.cpp
+ icorjitinfo.cpp
+ ieememorymanager.cpp
+ iexecutionengine.cpp
+ methodcallsummarizer.cpp
+ superpmi-shim-counter.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_SHIM_COUNTER_SOURCES
+)
+
+if (WIN32)
+ preprocess_def_file(${CMAKE_CURRENT_SOURCE_DIR}/superpmi-shim-counter.def ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-counter.def)
+
+ list(APPEND SUPERPMI_SHIM_COUNTER_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-counter.def)
+endif (WIN32)
+
+add_library(superpmi-shim-counter
+ SHARED
+ ${SUPERPMI_SHIM_COUNTER_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(superpmi-shim-counter
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(superpmi-shim-counter
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/superpmi-shim-counter.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS superpmi-shim-counter DESTINATION .)
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp
new file mode 100644
index 0000000000..5b764f2fa5
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp
@@ -0,0 +1,62 @@
+//
+// 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 "coreclrcallbacks.h"
+#include "iexecutionengine.h"
+
+typedef LPVOID (__stdcall * pfnEEHeapAllocInProcessHeap)(DWORD dwFlags, SIZE_T dwBytes);
+typedef BOOL (__stdcall * pfnEEHeapFreeInProcessHeap)(DWORD dwFlags, LPVOID lpMem);
+
+CoreClrCallbacks *original_CoreClrCallbacks = nullptr;
+pfnEEHeapAllocInProcessHeap original_EEHeapAllocInProcessHeap = nullptr;
+pfnEEHeapFreeInProcessHeap original_EEHeapFreeInProcessHeap = nullptr;
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
+{
+ interceptor_IEE *iee = new interceptor_IEE();
+ iee->original_IEE = original_CoreClrCallbacks->m_pfnIEE();
+ return iee;
+}
+
+/*#pragma warning( suppress :4996 ) //deprecated
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength)
+{
+ DebugBreakorAV(131);
+ return 0;
+}
+*/
+
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes)
+{
+ if(original_EEHeapAllocInProcessHeap == nullptr)
+ __debugbreak();
+ return original_EEHeapAllocInProcessHeap(dwFlags, dwBytes);
+}
+
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem)
+{
+ if(original_EEHeapFreeInProcessHeap == nullptr)
+ __debugbreak();
+ return original_EEHeapFreeInProcessHeap(dwFlags, lpMem);
+}
+
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
+{
+ if(strcmp(functionName, "EEHeapAllocInProcessHeap")==0)
+ {
+ original_EEHeapAllocInProcessHeap =
+ (pfnEEHeapAllocInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapAllocInProcessHeap");
+ return (void*)EEHeapAllocInProcessHeap;
+ }
+ if(strcmp(functionName, "EEHeapFreeInProcessHeap")==0)
+ {
+ original_EEHeapFreeInProcessHeap =
+ (pfnEEHeapFreeInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapFreeInProcessHeap");
+ return (void*)EEHeapFreeInProcessHeap;
+ }
+ return original_CoreClrCallbacks->m_pfnGetCLRFunction(functionName);
+}
+
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.h b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.h
new file mode 100644
index 0000000000..c8c3e27c66
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.h
@@ -0,0 +1,19 @@
+//
+// 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);
+
+extern CoreClrCallbacks *original_CoreClrCallbacks;
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp
new file mode 100644
index 0000000000..da766cc51d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp
@@ -0,0 +1,68 @@
+//
+// 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 "icorjitcompiler.h"
+#include "icorjitinfo.h"
+
+interceptor_IEEMM *current_IEEMM = nullptr; //we want this to live beyond the scope of a single compileMethodCall
+
+CorJitResult __stdcall interceptor_ICJC::compileMethod (
+ ICorJitInfo *comp, /* IN */
+ struct CORINFO_METHOD_INFO *info, /* IN */
+ unsigned /* code:CorJitFlag */ flags, /* IN */
+ BYTE **nativeEntry, /* OUT */
+ ULONG *nativeSizeOfCode /* OUT */
+ )
+{
+ interceptor_ICJI our_ICorJitInfo;
+ our_ICorJitInfo.original_ICorJitInfo = comp;
+
+ if(current_IEEMM == nullptr)
+ current_IEEMM = new interceptor_IEEMM();
+ our_ICorJitInfo.mcs = mcs;
+
+ mcs->AddCall("compileMethod");
+ CorJitResult temp = original_ICorJitCompiler->compileMethod(&our_ICorJitInfo, info, flags, nativeEntry, nativeSizeOfCode);
+
+ mcs->SaveTextFile();
+ return temp;
+}
+
+void interceptor_ICJC::clearCache()
+{
+ mcs->AddCall("clearCache");
+ original_ICorJitCompiler->clearCache();
+}
+
+BOOL interceptor_ICJC::isCacheCleanupRequired()
+{
+ mcs->AddCall("isCacheCleanupRequired");
+ return original_ICorJitCompiler->isCacheCleanupRequired();
+}
+
+void interceptor_ICJC::ProcessShutdownWork(ICorStaticInfo* info)
+{
+ mcs->AddCall("ProcessShutdownWork");
+ original_ICorJitCompiler->ProcessShutdownWork(info);
+}
+
+void interceptor_ICJC::getVersionIdentifier(GUID* versionIdentifier /* OUT */)
+{
+ mcs->AddCall("getVersionIdentifier");
+ original_ICorJitCompiler->getVersionIdentifier(versionIdentifier);
+}
+
+unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags)
+{
+ mcs->AddCall("getMaxIntrinsicSIMDVectorLength");
+ return original_ICorJitCompiler->getMaxIntrinsicSIMDVectorLength(cpuCompileFlags);
+}
+
+void interceptor_ICJC::setRealJit(ICorJitCompiler* realJitCompiler)
+{
+ mcs->AddCall("setRealJit");
+ original_ICorJitCompiler->setRealJit(realJitCompiler);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.h b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.h
new file mode 100644
index 0000000000..1abcd0a77b
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.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 _ICorJitCompiler
+#define _ICorJitCompiler
+
+#include "runtimedetails.h"
+#include "methodcallsummarizer.h"
+#include "ieememorymanager.h"
+
+class interceptor_ICJC : public ICorJitCompiler
+{
+
+#include "icorjitcompilerimpl.h"
+
+public:
+ // Added to help us track the original icjc and be able to easily indirect to it.
+ ICorJitCompiler *original_ICorJitCompiler;
+ MethodCallSummarizer *mcs;
+};
+
+extern interceptor_IEEMM *current_IEEMM; //we want this to live beyond the scope of a single compileMethodCall
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
new file mode 100644
index 0000000000..77519caa84
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
@@ -0,0 +1,1901 @@
+//
+// 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 "superpmi-shim-counter.h"
+#include "ieememorymanager.h"
+#include "icorjitcompiler.h"
+#include "spmiutil.h"
+
+//Stuff on ICorStaticInfo
+/**********************************************************************************/
+//
+// ICorMethodInfo
+//
+/**********************************************************************************/
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getMethodAttribs (CORINFO_METHOD_HANDLE ftn /* IN */)
+{
+ mcs->AddCall("getMethodAttribs");
+ return original_ICorJitInfo->getMethodAttribs(ftn);
+}
+
+// sets private JIT flags, which can be, retrieved using getAttrib.
+void interceptor_ICJI::setMethodAttribs (CORINFO_METHOD_HANDLE ftn,/* IN */
+ CorInfoMethodRuntimeFlags attribs/* IN */)
+{
+ mcs->AddCall("setMethodAttribs");
+ original_ICorJitInfo->setMethodAttribs(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 interceptor_ICJI::getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ mcs->AddCall("getMethodSig");
+ original_ICorJitInfo->getMethodSig(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 interceptor_ICJI::getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ )
+{
+ mcs->AddCall("getMethodInfo");
+ return original_ICorJitInfo->getMethodInfo(ftn, info);
+}
+
+// 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 interceptor_ICJI::canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ )
+{
+ mcs->AddCall("canInline");
+ return original_ICorJitInfo->canInline(callerHnd, calleeHnd, pRestrictions);
+}
+
+// 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 interceptor_ICJI::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * reason)
+{
+ mcs->AddCall("reportInliningDecision");
+ original_ICorJitInfo->reportInliningDecision(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 interceptor_ICJI::canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ )
+{
+ mcs->AddCall("canTailCall");
+ return original_ICorJitInfo->canTailCall(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 interceptor_ICJI::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason)
+{
+ mcs->AddCall("reportTailCallDecision");
+ original_ICorJitInfo->reportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+}
+
+// get individual exception handler
+void interceptor_ICJI::getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ )
+{
+ mcs->AddCall("getEHinfo");
+ original_ICorJitInfo->getEHinfo(ftn, EHnumber, clause);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("getMethodClass");
+ return original_ICorJitInfo->getMethodClass(method);
+}
+
+// return module it belongs to
+CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("getMethodModule");
+ return original_ICorJitInfo->getMethodModule(method);
+}
+
+// This function returns the offset of the specified method in the
+// vtable of it's owning class or interface.
+void interceptor_ICJI::getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ )
+{
+ mcs->AddCall("getMethodVTableOffset");
+ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+}
+
+// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+// getIntrinsicID() returns the intrinsic ID.
+CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand /* OUT */
+ )
+{
+ mcs->AddCall("getIntrinsicID");
+ return original_ICorJitInfo->getIntrinsicID(method, pMustExpand);
+}
+
+// Is the given module the System.Numerics.Vectors module?
+bool interceptor_ICJI::isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ )
+{
+ mcs->AddCall("isInSIMDModule");
+ return original_ICorJitInfo->isInSIMDModule(classHnd);
+}
+
+// return the unmanaged calling convention for a PInvoke
+CorInfoUnmanagedCallConv interceptor_ICJI::getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("getUnmanagedCallConv");
+ return original_ICorJitInfo->getUnmanagedCallConv(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 interceptor_ICJI::pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ )
+{
+ mcs->AddCall("pInvokeMarshalingRequired");
+ return original_ICorJitInfo->pInvokeMarshalingRequired(method, callSiteSig);
+}
+
+// Check constraints on method type arguments (only).
+// The parent class should be checked separately using satisfiesClassConstraints(parent).
+BOOL interceptor_ICJI::satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("satisfiesMethodConstraints");
+ return original_ICorJitInfo->satisfiesMethodConstraints(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 interceptor_ICJI::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 */
+ )
+{
+ mcs->AddCall("isCompatibleDelegate");
+ return original_ICorJitInfo->isCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate);
+}
+
+// Determines whether the delegate creation obeys security transparency rules
+BOOL interceptor_ICJI::isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ )
+{
+ mcs->AddCall("isDelegateCreationAllowed");
+ return original_ICorJitInfo->isDelegateCreationAllowed(delegateHnd, calleeHnd);
+}
+
+
+// Indicates if the method is an instance of the generic
+// method that passes (or has passed) verification
+CorInfoInstantiationVerification interceptor_ICJI::isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ )
+{
+ mcs->AddCall("isInstantiationOfVerifiedGeneric");
+ return original_ICorJitInfo->isInstantiationOfVerifiedGeneric(method);
+}
+
+// Loads the constraints on a typical method definition, detecting cycles;
+// for use in verification.
+void interceptor_ICJI::initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ )
+{
+ mcs->AddCall("initConstraintsForVerification");
+ original_ICorJitInfo->initConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+}
+
+// Returns enum whether the method does not require verification
+// Also see ICorModuleInfo::canSkipVerification
+CorInfoCanSkipVerificationResult interceptor_ICJI::canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ )
+{
+ mcs->AddCall("canSkipMethodVerification");
+ return original_ICorJitInfo->canSkipMethodVerification(ftnHandle);
+}
+
+// load and restore the method
+void interceptor_ICJI::methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("methodMustBeLoadedBeforeCodeIsRun");
+ original_ICorJitInfo->methodMustBeLoadedBeforeCodeIsRun(method);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("mapMethodDeclToMethodImpl");
+ return original_ICorJitInfo->mapMethodDeclToMethodImpl(method);
+}
+
+// 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 interceptor_ICJI::getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ )
+{
+ mcs->AddCall("getGSCookie");
+ original_ICorJitInfo->getGSCookie(pCookieVal, ppCookieVal);
+}
+
+/**********************************************************************************/
+//
+// ICorModuleInfo
+//
+/**********************************************************************************/
+
+// Resolve metadata token into runtime method handles.
+void interceptor_ICJI::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ mcs->AddCall("resolveToken");
+ original_ICorJitInfo->resolveToken(pResolvedToken);
+}
+
+bool interceptor_ICJI::tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ mcs->AddCall("tryResolveToken");
+ return original_ICorJitInfo->tryResolveToken(pResolvedToken);
+}
+
+// Signature information about the call sig
+void interceptor_ICJI::findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ mcs->AddCall("findSig");
+ original_ICorJitInfo->findSig(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 interceptor_ICJI::findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ mcs->AddCall("findCallSiteSig");
+ original_ICorJitInfo->findCallSiteSig(module, methTOK, context, sig);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */)
+{
+ mcs->AddCall("getTokenTypeAsHandle");
+ return original_ICorJitInfo->getTokenTypeAsHandle(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 interceptor_ICJI::canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ )
+{
+ mcs->AddCall("canSkipVerification");
+ return original_ICorJitInfo->canSkipVerification(module);
+}
+
+// Checks if the given metadata token is valid
+BOOL interceptor_ICJI::isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ mcs->AddCall("isValidToken");
+ return original_ICorJitInfo->isValidToken(module, metaTOK);
+}
+
+// Checks if the given metadata token is valid StringRef
+BOOL interceptor_ICJI::isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ mcs->AddCall("isValidStringRef");
+ return original_ICorJitInfo->isValidStringRef(module, metaTOK);
+}
+
+BOOL interceptor_ICJI::shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE scope
+ )
+{
+ mcs->AddCall("shouldEnforceCallvirtRestriction");
+ return original_ICorJitInfo->shouldEnforceCallvirtRestriction(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 interceptor_ICJI::asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("asCorInfoType");
+ return original_ICorJitInfo->asCorInfoType(cls);
+}
+
+// for completeness
+const char* interceptor_ICJI::getClassName (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getClassName");
+ return original_ICorJitInfo->getClassName(cls);
+}
+
+
+// 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 interceptor_ICJI::appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ )
+{
+ mcs->AddCall("appendClassName");
+ return original_ICorJitInfo->appendClassName(ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly);
+}
+
+// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+BOOL interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ mcs->AddCall("isValueClass");
+ return original_ICorJitInfo->isValueClass(cls);
+}
+
+// If this method returns true, JIT will do optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType()
+BOOL interceptor_ICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ mcs->AddCall("canInlineTypeCheckWithObjectVTable");
+ return original_ICorJitInfo->canInlineTypeCheckWithObjectVTable(cls);
+}
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getClassAttribs (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getClassAttribs");
+ return original_ICorJitInfo->getClassAttribs(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 interceptor_ICJI::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ mcs->AddCall("isStructRequiringStackAllocRetBuf");
+ return original_ICorJitInfo->isStructRequiringStackAllocRetBuf(cls);
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getClassModule");
+ return original_ICorJitInfo->getClassModule(cls);
+}
+
+// Returns the assembly that contains the module "mod".
+CORINFO_ASSEMBLY_HANDLE interceptor_ICJI::getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ )
+{
+ mcs->AddCall("getModuleAssembly");
+ return original_ICorJitInfo->getModuleAssembly(mod);
+}
+
+// Returns the name of the assembly "assem".
+const char* interceptor_ICJI::getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE assem
+ )
+{
+ mcs->AddCall("getAssemblyName");
+ return original_ICorJitInfo->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* interceptor_ICJI::LongLifetimeMalloc(size_t sz)
+{
+ mcs->AddCall("LongLifetimeMalloc");
+ return original_ICorJitInfo->LongLifetimeMalloc(sz);
+}
+
+void interceptor_ICJI::LongLifetimeFree(void* obj)
+{
+ mcs->AddCall("LongLifetimeFree");
+ original_ICorJitInfo->LongLifetimeFree(obj);
+}
+
+size_t interceptor_ICJI::getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getClassModuleIdForStatics");
+ return original_ICorJitInfo->getClassModuleIdForStatics(cls, pModule, ppIndirection);
+}
+
+// return the number of bytes needed by an instance of the class
+unsigned interceptor_ICJI::getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getClassSize");
+ return original_ICorJitInfo->getClassSize(cls);
+}
+
+unsigned interceptor_ICJI::getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint
+ )
+{
+ mcs->AddCall("getClassAlignmentRequirement");
+ return original_ICorJitInfo->getClassAlignmentRequirement(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 interceptor_ICJI::getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ )
+{
+ mcs->AddCall("getClassGClayout");
+ return original_ICorJitInfo->getClassGClayout(cls, gcPtrs);
+}
+
+// returns the number of instance fields in a class
+unsigned interceptor_ICJI::getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ )
+{
+ mcs->AddCall("getClassNumInstanceFields");
+ return original_ICorJitInfo->getClassNumInstanceFields(cls);
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ )
+{
+ mcs->AddCall("getFieldInClass");
+ return original_ICorJitInfo->getFieldInClass(clsHnd, num);
+}
+
+BOOL interceptor_ICJI::checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ )
+{
+ mcs->AddCall("checkMethodModifier");
+ return original_ICorJitInfo->checkMethodModifier(hMethod, modifier, fOptional);
+}
+
+// returns the "NEW" helper optimized for "newCls."
+CorInfoHelpFunc interceptor_ICJI::getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ )
+{
+ mcs->AddCall("getNewHelper");
+ return original_ICorJitInfo->getNewHelper(pResolvedToken, callerHandle);
+}
+
+// returns the newArr (1-Dim array) helper optimized for "arrayCls."
+CorInfoHelpFunc interceptor_ICJI::getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ )
+{
+ mcs->AddCall("getNewArrHelper");
+ return original_ICorJitInfo->getNewArrHelper(arrayCls);
+}
+
+// returns the optimized "IsInstanceOf" or "ChkCast" helper
+CorInfoHelpFunc interceptor_ICJI::getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ )
+{
+ mcs->AddCall("getCastingHelper");
+ return original_ICorJitInfo->getCastingHelper(pResolvedToken, fThrowing);
+}
+
+// returns helper to trigger static constructor
+CorInfoHelpFunc interceptor_ICJI::getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ )
+{
+ mcs->AddCall("getSharedCCtorHelper");
+ return original_ICorJitInfo->getSharedCCtorHelper(clsHnd);
+}
+
+CorInfoHelpFunc interceptor_ICJI::getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE ftn
+ )
+{
+ mcs->AddCall("getSecurityPrologHelper");
+ return original_ICorJitInfo->getSecurityPrologHelper(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 interceptor_ICJI::getTypeForBox(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getTypeForBox");
+ return original_ICorJitInfo->getTypeForBox(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 interceptor_ICJI::getBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getBoxHelper");
+ return original_ICorJitInfo->getBoxHelper(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 interceptor_ICJI::getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getUnBoxHelper");
+ return original_ICorJitInfo->getUnBoxHelper(cls);
+}
+
+bool interceptor_ICJI::getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ mcs->AddCall("getReadyToRunHelper");
+ return original_ICorJitInfo->getReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup);
+}
+
+void interceptor_ICJI::getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ mcs->AddCall("getReadyToRunDelegateCtorHelper");
+ original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+}
+
+const char* interceptor_ICJI::getHelperName(
+ CorInfoHelpFunc funcNum
+ )
+{
+ mcs->AddCall("getHelperName");
+ return original_ICorJitInfo->getHelperName(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 interceptor_ICJI::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
+ )
+{
+ mcs->AddCall("initClass");
+ return original_ICorJitInfo->initClass(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 interceptor_ICJI::classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("classMustBeLoadedBeforeCodeIsRun");
+ original_ICorJitInfo->classMustBeLoadedBeforeCodeIsRun(cls);
+}
+
+// returns the class handle for the special builtin classes
+CORINFO_CLASS_HANDLE interceptor_ICJI::getBuiltinClass (
+ CorInfoClassId classId
+ )
+{
+ mcs->AddCall("getBuiltinClass");
+ return original_ICorJitInfo->getBuiltinClass(classId);
+}
+
+// "System.Int32" ==> CORINFO_TYPE_INT..
+CorInfoType interceptor_ICJI::getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getTypeForPrimitiveValueClass");
+ return original_ICorJitInfo->getTypeForPrimitiveValueClass(cls);
+}
+
+// TRUE if child is a subtype of parent
+// if parent is an interface, then does child implement / extend parent
+BOOL interceptor_ICJI::canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ )
+{
+ mcs->AddCall("canCast");
+ return original_ICorJitInfo->canCast(child, parent);
+}
+
+// TRUE if cls1 and cls2 are considered equivalent types.
+BOOL interceptor_ICJI::areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ mcs->AddCall("areTypesEquivalent");
+ return original_ICorJitInfo->areTypesEquivalent(cls1, cls2);
+}
+
+// returns is the intersection of cls1 and cls2.
+CORINFO_CLASS_HANDLE interceptor_ICJI::mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ mcs->AddCall("mergeClasses");
+ return original_ICorJitInfo->mergeClasses(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 interceptor_ICJI::getParentType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getParentType");
+ return original_ICorJitInfo->getParentType(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 interceptor_ICJI::getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ )
+{
+ mcs->AddCall("getChildType");
+ return original_ICorJitInfo->getChildType(clsHnd, clsRet);
+}
+
+// Check constraints on type arguments of this class and parent classes
+BOOL interceptor_ICJI::satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("satisfiesClassConstraints");
+ return original_ICorJitInfo->satisfiesClassConstraints(cls);
+}
+
+// Check if this is a single dimensional array type
+BOOL interceptor_ICJI::isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("isSDArray");
+ return original_ICorJitInfo->isSDArray(cls);
+}
+
+// Get the numbmer of dimensions in an array
+unsigned interceptor_ICJI::getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getArrayRank");
+ return original_ICorJitInfo->getArrayRank(cls);
+}
+
+// Get static field data for an array
+void * interceptor_ICJI::getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ )
+{
+ mcs->AddCall("getArrayInitializationData");
+ return original_ICorJitInfo->getArrayInitializationData(field, size);
+}
+
+// Check Visibility rules.
+CorInfoIsAccessAllowedResult interceptor_ICJI::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. */
+ )
+{
+ mcs->AddCall("canAccessClass");
+ return original_ICorJitInfo->canAccessClass(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* interceptor_ICJI::getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ mcs->AddCall("getFieldName");
+ return original_ICorJitInfo->getFieldName(ftn, moduleName);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getFieldClass (
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ mcs->AddCall("getFieldClass");
+ return original_ICorJitInfo->getFieldClass(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 interceptor_ICJI::getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ mcs->AddCall("getFieldType");
+ return original_ICorJitInfo->getFieldType(field, structType, memberParent);
+}
+
+// return the data member's instance offset
+unsigned interceptor_ICJI::getFieldOffset(
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ mcs->AddCall("getFieldOffset");
+ return original_ICorJitInfo->getFieldOffset(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 interceptor_ICJI::isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field)
+{
+ mcs->AddCall("isWriteBarrierHelperRequired");
+ return original_ICorJitInfo->isWriteBarrierHelperRequired(field);
+}
+
+void interceptor_ICJI::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ )
+{
+ mcs->AddCall("getFieldInfo");
+ original_ICorJitInfo->getFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+}
+
+// Returns true iff "fldHnd" represents a static field.
+bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
+{
+ mcs->AddCall("isFieldStatic");
+ return original_ICorJitInfo->isFieldStatic(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 interceptor_ICJI::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
+ )
+{
+ mcs->AddCall("getBoundaries");
+ original_ICorJitInfo->getBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+}
+
+// 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 interceptor_ICJI::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
+ )
+{
+ mcs->AddCall("setBoundaries");
+ original_ICorJitInfo->setBoundaries(ftn, cMap, pMap);
+}
+
+// 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 interceptor_ICJI::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
+ )
+{
+ mcs->AddCall("getVars");
+ original_ICorJitInfo->getVars(ftn, cVars, vars, extendOthers);
+}
+
+// Report back to the EE the location of every variable.
+// note that the JIT might split lifetimes into different
+// locations etc.
+
+void interceptor_ICJI::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
+ )
+{
+ mcs->AddCall("setVars");
+ original_ICorJitInfo->setVars(ftn, cVars, vars);
+}
+
+/*-------------------------- 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 * interceptor_ICJI::allocateArray(
+ ULONG cBytes
+ )
+{
+ mcs->AddCall("allocateArray");
+ return original_ICorJitInfo->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 interceptor_ICJI::freeArray(
+ void *array
+ )
+{
+ mcs->AddCall("freeArray");
+ original_ICorJitInfo->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 interceptor_ICJI::getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ mcs->AddCall("getArgNext");
+ return original_ICorJitInfo->getArgNext(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 interceptor_ICJI::getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ )
+{
+ mcs->AddCall("getArgType");
+ return original_ICorJitInfo->getArgType(sig, args, vcTypeRet);
+}
+
+// If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ mcs->AddCall("getArgClass");
+ return original_ICorJitInfo->getArgClass(sig, args);
+}
+
+// Returns type of HFA for valuetype
+CorInfoType interceptor_ICJI::getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ )
+{
+ mcs->AddCall("getHFAType");
+ return original_ICorJitInfo->getHFAType(hClass);
+}
+
+/*****************************************************************************
+* 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 interceptor_ICJI::GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mcs->AddCall("GetErrorHRESULT");
+ return original_ICorJitInfo->GetErrorHRESULT(pExceptionPointers);
+}
+
+// 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 interceptor_ICJI::GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ )
+{
+ mcs->AddCall("GetErrorMessage");
+ return original_ICorJitInfo->GetErrorMessage(buffer, bufferLength);
+}
+
+// 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 interceptor_ICJI::FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mcs->AddCall("FilterException");
+ return original_ICorJitInfo->FilterException(pExceptionPointers);
+}
+
+// Cleans up internal EE tracking when an exception is caught.
+void interceptor_ICJI::HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mcs->AddCall("HandleException");
+ original_ICorJitInfo->HandleException(pExceptionPointers);
+}
+
+void interceptor_ICJI::ThrowExceptionForJitResult(
+ HRESULT result)
+{
+ mcs->AddCall("ThrowExceptionForJitResult");
+ original_ICorJitInfo->ThrowExceptionForJitResult(result);
+}
+
+//Throws an exception defined by the given throw helper.
+void interceptor_ICJI::ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper)
+{
+ mcs->AddCall("ThrowExceptionForHelper");
+ original_ICorJitInfo->ThrowExceptionForHelper(throwHelper);
+}
+
+/*****************************************************************************
+ * 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 interceptor_ICJI::getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ )
+{
+ mcs->AddCall("getEEInfo");
+ original_ICorJitInfo->getEEInfo(pEEInfoOut);
+}
+
+// Returns name of the JIT timer log
+LPCWSTR interceptor_ICJI::getJitTimeLogFilename()
+{
+ mcs->AddCall("getJitTimeLogFilename");
+ return original_ICorJitInfo->getJitTimeLogFilename();
+}
+
+ /*********************************************************************************/
+ //
+ // Diagnostic methods
+ //
+ /*********************************************************************************/
+
+// this function is for debugging only. Returns method token.
+// Returns mdMethodDefNil for dynamic methods.
+mdMethodDef interceptor_ICJI::getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ )
+{
+ mcs->AddCall("getMethodDefFromMethod");
+ return original_ICorJitInfo->getMethodDefFromMethod(hMethod);
+}
+
+// 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* interceptor_ICJI::getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ mcs->AddCall("getMethodName");
+ return original_ICorJitInfo->getMethodName(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 interceptor_ICJI::getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ )
+{
+ mcs->AddCall("getMethodHash");
+ return original_ICorJitInfo->getMethodHash(ftn);
+}
+
+// this function is for debugging only.
+size_t interceptor_ICJI::findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ )
+{
+ mcs->AddCall("findNameOfToken");
+ return original_ICorJitInfo->findNameOfToken(module, metaTOK, szFQName, FQNameCapacity);
+}
+
+bool interceptor_ICJI::getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ )
+{
+ mcs->AddCall("getSystemVAmd64PassStructInRegisterDescriptor");
+ return original_ICorJitInfo->getSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
+}
+
+//Stuff on ICorDynamicInfo
+DWORD interceptor_ICJI::getThreadTLSIndex(
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getThreadTLSIndex");
+ return original_ICorJitInfo->getThreadTLSIndex(ppIndirection);
+}
+
+const void * interceptor_ICJI::getInlinedCallFrameVptr(
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getInlinedCallFrameVptr");
+ return original_ICorJitInfo->getInlinedCallFrameVptr(ppIndirection);
+}
+
+LONG * interceptor_ICJI::getAddrOfCaptureThreadGlobal(
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getAddrOfCaptureThreadGlobal");
+ return original_ICorJitInfo->getAddrOfCaptureThreadGlobal(ppIndirection);
+}
+
+SIZE_T* interceptor_ICJI::getAddrModuleDomainID(CORINFO_MODULE_HANDLE module)
+{
+ mcs->AddCall("getAddrModuleDomainID");
+ return original_ICorJitInfo->getAddrModuleDomainID(module);
+}
+
+// return the native entry point to an EE helper (see CorInfoHelpFunc)
+void* interceptor_ICJI::getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getHelperFtn");
+ return original_ICorJitInfo->getHelperFtn(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 interceptor_ICJI::getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags)
+{
+ mcs->AddCall("getFunctionEntryPoint");
+ original_ICorJitInfo->getFunctionEntryPoint(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 interceptor_ICJI::getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult)
+{
+ mcs->AddCall("getFunctionFixedEntryPoint");
+ original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, pResult);
+}
+
+// get the synchronization handle that is passed to monXstatic function
+void* interceptor_ICJI::getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getMethodSync");
+ return original_ICorJitInfo->getMethodSync(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 interceptor_ICJI::getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ )
+{
+ mcs->AddCall("getLazyStringLiteralHelper");
+ return original_ICorJitInfo->getLazyStringLiteralHelper(handle);
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("embedModuleHandle");
+ return original_ICorJitInfo->embedModuleHandle(handle, ppIndirection);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("embedClassHandle");
+ return original_ICorJitInfo->embedClassHandle(handle, ppIndirection);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("embedMethodHandle");
+ return original_ICorJitInfo->embedMethodHandle(handle, ppIndirection);
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("embedFieldHandle");
+ return original_ICorJitInfo->embedFieldHandle(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 interceptor_ICJI::embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * pResult)
+{
+ mcs->AddCall("embedGenericHandle");
+ original_ICorJitInfo->embedGenericHandle(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 interceptor_ICJI::getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ )
+{
+ mcs->AddCall("getLocationOfThisType");
+ return original_ICorJitInfo->getLocationOfThisType(context);
+}
+
+// return the unmanaged target *if method has already been prelinked.*
+void* interceptor_ICJI::getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getPInvokeUnmanagedTarget");
+ return original_ICorJitInfo->getPInvokeUnmanagedTarget(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void* interceptor_ICJI::getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getAddressOfPInvokeFixup");
+ return original_ICorJitInfo->getAddressOfPInvokeFixup(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void interceptor_ICJI::getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ )
+{
+ mcs->AddCall("getAddressOfPInvokeTarget");
+ original_ICorJitInfo->getAddressOfPInvokeTarget(method, pLookup);
+}
+
+// Generate a cookie based on the signature that would needs to be passed
+// to CORINFO_HELP_PINVOKE_CALLI
+LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection
+ )
+{
+ mcs->AddCall("GetCookieForPInvokeCalliSig");
+ return original_ICorJitInfo->GetCookieForPInvokeCalliSig(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 interceptor_ICJI::canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ )
+{
+ mcs->AddCall("canGetCookieForPInvokeCalliSig");
+ return original_ICorJitInfo->canGetCookieForPInvokeCalliSig(szMetaSig);
+}
+
+// Gets a handle that is checked to see if the current method is
+// included in "JustMyCode"
+CORINFO_JUST_MY_CODE_HANDLE interceptor_ICJI::getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection
+ )
+{
+ mcs->AddCall("getJustMyCodeHandle");
+ return original_ICorJitInfo->getJustMyCodeHandle(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 interceptor_ICJI::GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ )
+{
+ mcs->AddCall("GetProfilingHandle");
+ original_ICorJitInfo->GetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+}
+
+// Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+void interceptor_ICJI::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
+ )
+{
+ mcs->AddCall("getCallInfo");
+ original_ICorJitInfo->getCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult);
+}
+
+BOOL interceptor_ICJI::canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE hInstanceType)
+
+{
+ mcs->AddCall("canAccessFamily");
+ return original_ICorJitInfo->canAccessFamily(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 interceptor_ICJI::isRIDClassDomainID(CORINFO_CLASS_HANDLE cls)
+{
+ mcs->AddCall("isRIDClassDomainID");
+ return original_ICorJitInfo->isRIDClassDomainID(cls);
+}
+
+// returns the class's domain ID for accessing shared statics
+unsigned interceptor_ICJI::getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getClassDomainID");
+ return original_ICorJitInfo->getClassDomainID(cls, ppIndirection);
+}
+
+
+// return the data's address (for static fields only)
+void* interceptor_ICJI::getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getFieldAddress");
+ return original_ICorJitInfo->getFieldAddress(field, ppIndirection);
+}
+
+// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+CORINFO_VARARGS_HANDLE interceptor_ICJI::getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getVarArgsHandle");
+ return original_ICorJitInfo->getVarArgsHandle(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 interceptor_ICJI::canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ )
+{
+ mcs->AddCall("canGetVarArgsHandle");
+ return original_ICorJitInfo->canGetVarArgsHandle(pSig);
+}
+
+// Allocate a string literal on the heap and return a handle to it
+InfoAccessType interceptor_ICJI::constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ )
+{
+ mcs->AddCall("constructStringLiteral");
+ return original_ICorJitInfo->constructStringLiteral(module, metaTok, ppValue);
+}
+
+InfoAccessType interceptor_ICJI::emptyStringLiteral(
+ void **ppValue
+ )
+{
+ mcs->AddCall("emptyStringLiteral");
+ return original_ICorJitInfo->emptyStringLiteral(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 interceptor_ICJI::getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getFieldThreadLocalStoreID");
+ return original_ICorJitInfo->getFieldThreadLocalStoreID(field, ppIndirection);
+}
+
+// Sets another object to intercept calls to "self" and current method being compiled
+void interceptor_ICJI::setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ )
+{
+ mcs->AddCall("setOverride");
+ original_ICorJitInfo->setOverride(pOverride, currentMethod);
+}
+
+// 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 interceptor_ICJI::addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ )
+{
+ mcs->AddCall("addActiveDependency");
+ original_ICorJitInfo->addActiveDependency(moduleFrom, moduleTo);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ )
+{
+ mcs->AddCall("GetDelegateCtor");
+ return original_ICorJitInfo->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData);
+}
+
+void interceptor_ICJI::MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ )
+{
+ mcs->AddCall("MethodCompileComplete");
+ original_ICorJitInfo->MethodCompileComplete(methHnd);
+}
+
+// return a thunk that will copy the arguments for the given signature.
+void* interceptor_ICJI::getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ )
+{
+ mcs->AddCall("getTailCallCopyArgsThunk");
+ return original_ICorJitInfo->getTailCallCopyArgsThunk(pSig, flags);
+}
+
+//Stuff directly on ICorJitInfo
+
+// Returns extended flags for a particular compilation instance.
+DWORD interceptor_ICJI::getJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes)
+{
+ mcs->AddCall("getJitFlags");
+ return original_ICorJitInfo->getJitFlags(jitFlags, sizeInBytes);
+}
+
+// Runs the given function with the given parameter under an error trap
+// and returns true if the function completes successfully. We don't
+// record the results of the call: when this call gets played back,
+// its result will depend on whether or not `function` calls something
+// that throws at playback time rather than at capture time.
+bool interceptor_ICJI::runWithErrorTrap(void (*function)(void*), void *param)
+{
+ mcs->AddCall("runWithErrorTrap");
+ return original_ICorJitInfo->runWithErrorTrap(function, param);
+}
+
+// return memory manager that the JIT can use to allocate a regular memory
+IEEMemoryManager* interceptor_ICJI::getMemoryManager()
+{
+ mcs->AddCall("getMemoryManager");
+ if(current_IEEMM->original_IEEMM == nullptr)
+ current_IEEMM->original_IEEMM = original_ICorJitInfo->getMemoryManager();
+
+ return current_IEEMM;
+}
+
+// get a block of memory for the code, readonly data, and read-write data
+void interceptor_ICJI::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 */
+ )
+{
+ mcs->AddCall("allocMem");
+ return original_ICorJitInfo->allocMem(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 interceptor_ICJI::reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ )
+{
+ mcs->AddCall("reserveUnwindInfo");
+ original_ICorJitInfo->reserveUnwindInfo(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 interceptor_ICJI::allocUnwindInfo (
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ )
+{
+ mcs->AddCall("allocUnwindInfo");
+ original_ICorJitInfo->allocUnwindInfo(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 * interceptor_ICJI::allocGCInfo (
+ size_t size /* IN */
+ )
+{
+ mcs->AddCall("allocGCInfo");
+ return original_ICorJitInfo->allocGCInfo(size);
+}
+
+//only used on x64
+void interceptor_ICJI::yieldExecution()
+{
+ mcs->AddCall("yieldExecution");
+ original_ICorJitInfo->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 interceptor_ICJI::setEHcount (
+ unsigned cEH /* IN */
+ )
+{
+ mcs->AddCall("setEHcount");
+ original_ICorJitInfo->setEHcount(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 interceptor_ICJI::setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ )
+{
+ mcs->AddCall("setEHinfo");
+ original_ICorJitInfo->setEHinfo(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 interceptor_ICJI::logMsg(unsigned level, const char* fmt, va_list args)
+{
+ mcs->AddCall("logMsg");
+ return original_ICorJitInfo->logMsg(level, fmt, args);
+}
+
+// do an assert. will return true if the code should retry (DebugBreak)
+// returns false, if the assert should be igored.
+int interceptor_ICJI::doAssert(const char* szFile, int iLine, const char* szExpr)
+{
+ mcs->AddCall("doAssert");
+ return original_ICorJitInfo->doAssert(szFile, iLine, szExpr);
+}
+
+void interceptor_ICJI::reportFatalError(CorJitResult result)
+{
+ mcs->AddCall("reportFatalError");
+ original_ICorJitInfo->reportFatalError(result);
+}
+
+/*
+struct ProfileBuffer // Also defined here: code:CORBBTPROF_BLOCK_DATA
+{
+ ULONG ILOffset;
+ ULONG ExecutionCount;
+};
+*/
+
+// allocate a basic block profile buffer where execution counts will be stored
+// for jitted basic blocks.
+HRESULT interceptor_ICJI::allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer
+ )
+{
+ mcs->AddCall("allocBBProfileBuffer");
+ return original_ICorJitInfo->allocBBProfileBuffer(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 interceptor_ICJI::getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG * count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer,
+ ULONG * numRuns
+ )
+{
+ mcs->AddCall("getBBProfileData");
+ return original_ICorJitInfo->getBBProfileData(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 interceptor_ICJI::recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ )
+{
+ mcs->AddCall("recordCallSite");
+ return original_ICorJitInfo->recordCallSite(instrOffset, callSig, methodHandle);
+}
+
+// A relocation is recorded if we are pre-jitting.
+// A jump thunk may be inserted if we are jitting
+void interceptor_ICJI::recordRelocation(
+ void * location, /* IN */
+ void * target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ )
+{
+ mcs->AddCall("recordRelocation");
+ original_ICorJitInfo->recordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+WORD interceptor_ICJI::getRelocTypeHint(void * target)
+{
+ mcs->AddCall("getRelocTypeHint");
+ return original_ICorJitInfo->getRelocTypeHint(target);
+}
+
+// A callback to identify the range of address known to point to
+// compiler-generated native entry points that call back into
+// MSIL.
+void interceptor_ICJI::getModuleNativeEntryPointRange(
+ void ** pStart, /* OUT */
+ void ** pEnd /* OUT */
+ )
+{
+ mcs->AddCall("getModuleNativeEntryPointRange");
+ original_ICorJitInfo->getModuleNativeEntryPointRange(pStart, pEnd);
+}
+
+// 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 interceptor_ICJI::getExpectedTargetArchitecture()
+{
+ mcs->AddCall("getExpectedTargetArchitecture");
+ return original_ICorJitInfo->getExpectedTargetArchitecture();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.h b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.h
new file mode 100644
index 0000000000..ab90cfc646
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/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"
+#include "methodcallsummarizer.h"
+
+class interceptor_ICJI : public ICorJitInfo
+{
+
+#include "icorjitinfoimpl.h"
+
+public:
+
+ //Added to help us track the original icji and be able to easily indirect
+ //to it. And a simple way to keep one memory manager instance per instance.
+ ICorJitInfo *original_ICorJitInfo;
+ MethodCallSummarizer *mcs;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.cpp
new file mode 100644
index 0000000000..863d542202
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.cpp
@@ -0,0 +1,72 @@
+//
+// 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 "ieememorymanager.h"
+#include "superpmi-shim-counter.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEEMM::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEEMM->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::AddRef()
+{
+ return original_IEEMM->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::Release()
+{
+ return original_IEEMM->Release();
+}
+
+//***************************************************************************
+// IEEMemoryManager methods for locking
+//***************************************************************************
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+ return original_IEEMM->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return original_IEEMM->ClrVirtualFree(lpAddress, dwSize, dwFreeType);
+}
+SIZE_T STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ return original_IEEMM->ClrVirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ return original_IEEMM->ClrVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessHeap()
+{
+ return original_IEEMM->ClrGetProcessHeap();
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ return original_IEEMM->ClrHeapCreate(flOptions, dwInitialSize, dwMaximumSize);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapDestroy(HANDLE hHeap)
+{
+ return original_IEEMM->ClrHeapDestroy(hHeap);
+}
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
+{
+ return original_IEEMM->ClrHeapAlloc(hHeap, dwFlags, dwBytes);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ return original_IEEMM->ClrHeapFree(hHeap, dwFlags, lpMem);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ return original_IEEMM->ClrHeapValidate(hHeap, dwFlags, lpMem);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessExecutableHeap()
+{
+ return original_IEEMM->ClrGetProcessExecutableHeap();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.h b/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.h
new file mode 100644
index 0000000000..4a72e809ee
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.h
@@ -0,0 +1,107 @@
+//
+// 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"
+
+/*
+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
+
+*/
+
+class interceptor_IEEMM : 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:
+ IEEMemoryManager *original_IEEMM;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.cpp
new file mode 100644
index 0000000000..0e2a311ed3
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.cpp
@@ -0,0 +1,157 @@
+//
+// 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 "iexecutionengine.h"
+#include "superpmi-shim-counter.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEE::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEE->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::AddRef()
+{
+ return original_IEE->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::Release()
+{
+ return original_IEE->Release();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for TLS
+//***************************************************************************
+// Associate a callback for cleanup with a TLS slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ original_IEE->TLS_AssociateCallback(slot, callback);
+}
+// Get the TLS block for fast Get/Set operations
+LPVOID* STDMETHODCALLTYPE interceptor_IEE::TLS_GetDataBlock()
+{
+ return original_IEE->TLS_GetDataBlock();
+}
+// Get the value at a slot
+LPVOID STDMETHODCALLTYPE interceptor_IEE::TLS_GetValue(DWORD slot)
+{
+ return original_IEE->TLS_GetValue(slot);
+}
+
+// Get the value at a slot, return FALSE if TLS info block doesn't exist
+BOOL STDMETHODCALLTYPE interceptor_IEE::TLS_CheckValue(DWORD slot, LPVOID * pValue)
+{
+ return original_IEE->TLS_CheckValue(slot, pValue);
+}
+// Set the value at a slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_SetValue(DWORD slot, LPVOID pData)
+{
+ original_IEE->TLS_SetValue(slot, pData);
+}
+// Free TLS memory block and make callback
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_ThreadDetaching()
+{
+ original_IEE->TLS_ThreadDetaching();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for locking
+//***************************************************************************
+CRITSEC_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
+{
+ return original_IEE->CreateLock(szTag, level, flags);
+}
+void STDMETHODCALLTYPE interceptor_IEE::DestroyLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->DestroyLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::AcquireLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->AcquireLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ReleaseLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->ReleaseLock(lock);
+}
+
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateAutoEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateAutoEvent(bInitialState);
+}
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateManualEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateManualEvent(bInitialState);
+}
+void STDMETHODCALLTYPE interceptor_IEE::CloseEvent(EVENT_COOKIE event)
+{
+ original_IEE->CloseEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrSetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrSetEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrResetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrResetEvent(event);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->WaitForEvent(event, dwMilliseconds, bAlertable);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds)
+{
+ return original_IEE->WaitForSingleObject(handle, dwMilliseconds);
+}
+SEMAPHORE_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ return original_IEE->ClrCreateSemaphore(dwInitial, dwMax);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ original_IEE->ClrCloseSemaphore(semaphore);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForSemaphore(semaphore, dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ return original_IEE->ClrReleaseSemaphore(semaphore, lReleaseCount, lpPreviousCount);
+}
+MUTEX_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName)
+{
+ return original_IEE->ClrCreateMutex(lpMutexAttributes, bInitialOwner, lpName);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ original_IEE->ClrCloseMutex(mutex);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ return original_IEE->ClrReleaseMutex(mutex);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForMutex(mutex, dwMilliseconds, bAlertable);
+}
+
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrSleepEx(dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrAllocationDisallowed()
+{
+ return original_IEE->ClrAllocationDisallowed();
+}
+void STDMETHODCALLTYPE interceptor_IEE::GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ original_IEE->GetLastThrownObjectExceptionFromThread(ppvException);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.h b/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.h
new file mode 100644
index 0000000000..642c145374
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.h
@@ -0,0 +1,149 @@
+//
+// 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
+*/
+
+class interceptor_IEE : 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);
+
+public:
+ IExecutionEngine *original_IEE;
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/jithost.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/jithost.cpp
new file mode 100644
index 0000000000..d4efc33c69
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/jithost.cpp
@@ -0,0 +1,49 @@
+//
+// 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 "runtimedetails.h"
+#include "spmiutil.h"
+#include "methodcallsummarizer.h"
+#include "jithost.h"
+
+JitHost* g_ourJitHost;
+
+JitHost::JitHost(ICorJitHost* wrappedHost) : wrappedHost(wrappedHost), mcs(nullptr)
+{
+}
+
+void JitHost::setMethodCallSummarizer(MethodCallSummarizer* methodCallSummarizer)
+{
+ this->mcs = methodCallSummarizer;
+}
+
+void* JitHost::allocateMemory(size_t size, bool usePageAllocator)
+{
+ return wrappedHost->allocateMemory(size, usePageAllocator);
+}
+
+void JitHost::freeMemory(void* block, bool usePageAllocator)
+{
+ return wrappedHost->freeMemory(block, usePageAllocator);
+}
+
+int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
+{
+ mcs->AddCall("getIntConfigValue");
+ return wrappedHost->getIntConfigValue(key, defaultValue);
+}
+
+const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
+{
+ mcs->AddCall("getStringConfigValue");
+ return wrappedHost->getStringConfigValue(key);
+}
+
+void JitHost::freeStringConfigValue(const wchar_t* value)
+{
+ mcs->AddCall("freeStringConfigValue");
+ wrappedHost->freeStringConfigValue(value);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/jithost.h b/src/ToolBox/superpmi/superpmi-shim-counter/jithost.h
new file mode 100644
index 0000000000..06acf8bf91
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/jithost.h
@@ -0,0 +1,25 @@
+//
+// 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(ICorJitHost* wrappedHost);
+
+ void setMethodCallSummarizer(MethodCallSummarizer* methodCallSummarizer);
+
+#include "icorjithostimpl.h"
+
+private:
+ ICorJitHost* wrappedHost;
+ MethodCallSummarizer* mcs;
+};
+
+extern JitHost* g_ourJitHost;
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.cpp
new file mode 100644
index 0000000000..61f31e4237
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.cpp
@@ -0,0 +1,140 @@
+//
+// 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 "methodcallsummarizer.h"
+
+MethodCallSummarizer::MethodCallSummarizer(WCHAR *logPath)
+{
+ numNames = 0;
+ names = nullptr;
+ counts = nullptr;
+
+ WCHAR *ExecutableName = GetCommandLineW();
+ WCHAR *quote1 = NULL;
+
+ //if there are any quotes in filename convert them to spaces.
+ while ((quote1 = wcsstr(ExecutableName, W("\""))) != NULL)
+ *quote1 = W(' ');
+
+ //remove any illegal or annoying characters from file name by converting them to underscores
+ while ((quote1 = wcspbrk(ExecutableName, W("=<>:\"/\\|?! *.,"))) != NULL)
+ *quote1 = W('_');
+
+ const WCHAR *DataFileExtension = W(".csv");
+ size_t ExecutableNameLength = wcslen(ExecutableName);
+ size_t DataFileExtensionLength = wcslen(DataFileExtension);
+ size_t logPathLength = wcslen(logPath);
+
+ unsigned int randNumber = 0;
+ WCHAR RandNumberString[9];
+ RandNumberString[0] = L'\0';
+ size_t RandNumberLength = 0;
+
+ size_t dataFileNameLength = logPathLength + 1 + ExecutableNameLength + 1 + RandNumberLength + 1 + DataFileExtensionLength + 1;
+
+ const size_t MaxAcceptablePathLength = MAX_PATH - 20; // subtract 20 to leave buffer, for possible random number addition
+ if (dataFileNameLength >= MaxAcceptablePathLength)
+ {
+ // The path name is too long; creating the file will fail. This can happen because we use the command line,
+ // which for ngen includes lots of environment variables, for example.
+
+ // Assume (!) the extra space is all in the ExecutableName, so shorten that.
+ ExecutableNameLength -= dataFileNameLength - MaxAcceptablePathLength;
+
+ dataFileNameLength = MaxAcceptablePathLength;
+
+#ifdef FEATURE_PAL
+ PAL_Random(/* bStrong */ FALSE, &randNumber, sizeof(randNumber));
+#else // !FEATURE_PAL
+ rand_s(&randNumber);
+#endif // !FEATURE_PAL
+
+ RandNumberLength = 9; // 8 hex digits + null
+ swprintf_s(RandNumberString, RandNumberLength, W("%08X"), randNumber);
+
+ dataFileNameLength += RandNumberLength - 1;
+ }
+
+ dataFileName = new WCHAR[dataFileNameLength];
+ dataFileName[0] = 0;
+ wcsncat_s(dataFileName, dataFileNameLength, logPath, logPathLength);
+ wcsncat_s(dataFileName, dataFileNameLength, W("\\\0"), 1);
+ wcsncat_s(dataFileName, dataFileNameLength, ExecutableName, ExecutableNameLength);
+
+ if (RandNumberLength > 0)
+ {
+ wcsncat_s(dataFileName, dataFileNameLength, RandNumberString, RandNumberLength);
+ }
+
+ wcsncat_s(dataFileName, dataFileNameLength, DataFileExtension, DataFileExtensionLength);
+}
+
+//lots of ways will be faster.. this happens to be decently simple and good enough for the task at hand and nicely sorts the output.
+//in this approach the most commonly added items are at the top of the list... 60% landed in the first three slots in short runs
+void MethodCallSummarizer::AddCall(const char *name)
+{
+ //if we can find it already in our list, increment the count
+ for(int i=0;i<numNames;i++)
+ {
+ if(strcmp(name, names[i])==0)
+ {
+ counts[i]++;
+ for(i=1;i<numNames;i++)
+ if(counts[i]>counts[i-1])
+ {
+ unsigned int tempui = counts[i-1];
+ counts[i-1] = counts[i];
+ counts[i] = tempui;
+ char *tempc = names[i-1];
+ names[i-1] = names[i];
+ names[i] = tempc;
+ }
+ return;
+ }
+ }
+
+ //else we didn't find it, so add it
+ char **tnames = names;
+ unsigned int *tcounts = counts;
+
+ names = new char*[numNames+1];
+ if(tnames!=nullptr)
+ {
+ memcpy(names, tnames, numNames*sizeof(char*));
+ delete tnames;
+ }
+
+ size_t tlen = strlen(name);
+ names[numNames] = new char[tlen+1];
+ memcpy(names[numNames], name, tlen+1);
+
+ counts = new unsigned int[numNames+1];
+ if(tcounts!=nullptr)
+ {
+ memcpy(counts, tcounts, numNames*sizeof(unsigned int));
+ delete tcounts;
+ }
+ counts[numNames] = 1;
+
+ numNames++;
+}
+
+void MethodCallSummarizer::SaveTextFile()
+{
+ char buff[512];
+ DWORD bytesWritten = 0;
+ HANDLE hFile = CreateFileW(dataFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ DWORD len = (DWORD)sprintf_s(buff,512,"FunctionName,Count\n");
+ WriteFile(hFile, buff, len, &bytesWritten, NULL);
+
+ for(int i=0;i<numNames;i++)
+ {
+ len = sprintf_s(buff,512,"%s,%u\n", names[i], counts[i]);
+ WriteFile(hFile, buff, len, &bytesWritten, NULL);
+ }
+ CloseHandle(hFile);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.h b/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.h
new file mode 100644
index 0000000000..09eaaca46f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _MethodCallSummarizer
+#define _MethodCallSummarizer
+
+class MethodCallSummarizer
+{
+public:
+ MethodCallSummarizer(WCHAR *name);
+ void AddCall(const char *name);
+ void SaveTextFile();
+
+private:
+ char **names;
+ unsigned int *counts;
+ int numNames;
+ WCHAR *dataFileName;
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp
new file mode 100644
index 0000000000..ee47dd8c44
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp
@@ -0,0 +1,231 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim.cpp - thin shim for the jit
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "superpmi-shim-counter.h"
+#include "runtimedetails.h"
+#include "coreclrcallbacks.h"
+#include "icorjitcompiler.h"
+#include "errorhandling.h"
+#include "logging.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+HMODULE g_hRealJit = 0; //We leak this currently (could do the proper shutdown in process_detach)
+WCHAR* g_realJitPath = nullptr; //We leak this (could do the proper shutdown in process_detach)
+WCHAR* g_logPath = nullptr; //Again, we leak this one too...
+char* g_logFilePath = nullptr; //We *don't* leak this, hooray!
+WCHAR* g_HomeDirectory = nullptr;
+WCHAR* g_DefaultRealJitPath = nullptr;
+
+void SetDefaultPaths()
+{
+ if (g_HomeDirectory == nullptr)
+ {
+ g_HomeDirectory = GetEnvironmentVariableWithDefaultW(W("HOME"), W("."));
+ }
+
+ if (g_DefaultRealJitPath == nullptr)
+ {
+ size_t len = wcslen(g_HomeDirectory) + 1 + wcslen(DEFAULT_REAL_JIT_NAME_W) + 1;
+ g_DefaultRealJitPath = new WCHAR[len];
+ wcscpy_s(g_DefaultRealJitPath, len, g_HomeDirectory);
+ wcscat_s(g_DefaultRealJitPath, len, DIRECTORY_SEPARATOR_STR_W);
+ wcscat_s(g_DefaultRealJitPath, len, DEFAULT_REAL_JIT_NAME_W);
+ }
+}
+
+void SetLibName()
+{
+ if (g_realJitPath == nullptr)
+ {
+ g_realJitPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimPath"), g_DefaultRealJitPath);
+ }
+}
+
+void SetLogPath()
+{
+ if (g_logPath == nullptr)
+ {
+ g_logPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimLogPath"), g_HomeDirectory);
+ }
+}
+
+// TODO: this only works for ANSI file paths...
+void SetLogFilePath()
+{
+ if (g_logFilePath == nullptr)
+ {
+ // If the environment variable isn't set, we don't enable file logging
+ g_logFilePath = GetEnvironmentVariableWithDefaultA("SuperPMIShimLogFilePath", nullptr);
+ }
+}
+
+extern "C"
+BOOL
+#ifndef FEATURE_PAL
+APIENTRY
+#endif // !FEATURE_PAL
+DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+#ifdef FEATURE_PAL
+ if (0 != PAL_InitializeDLL())
+ {
+ fprintf(stderr, "Error: Fail to PAL_InitializeDLL\n");
+ exit(1);
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+ SetLogFilePath();
+ Logger::OpenLogFile(g_logFilePath);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ Logger::Shutdown();
+
+ delete[] g_logFilePath;
+ g_logFilePath = nullptr;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+// Exported via def file
+extern "C"
+void __stdcall jitStartup(ICorJitHost* host)
+{
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if (g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if (g_hRealJit == 0)
+ {
+ LogError("jitStartup - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ // Get the required entrypoint
+ PjitStartup pnjitStartup = (PjitStartup)::GetProcAddress(g_hRealJit, "jitStartup");
+ if (pnjitStartup == nullptr)
+ {
+ // This portion of the interface is not used by the JIT under test.
+ g_ourJitHost = nullptr;
+ return;
+ }
+
+ g_ourJitHost = new JitHost(host);
+ pnjitStartup(g_ourJitHost);
+}
+
+//Exported via def file
+extern "C"
+ICorJitCompiler* __stdcall getJit()
+{
+ DWORD dwRetVal = 0;
+ PgetJit pngetJit;
+ interceptor_ICJC *pJitInstance = nullptr;
+ ICorJitCompiler *tICJI = nullptr;
+
+ SetDefaultPaths();
+ SetLibName();
+ SetLogPath();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("getJit() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return nullptr;
+ }
+ }
+
+ //get the required entrypoints
+ pngetJit = (PgetJit)::GetProcAddress(g_hRealJit, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("getJit() - GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return nullptr;
+ }
+
+ tICJI = pngetJit();
+ if(tICJI == nullptr)
+ {
+ LogError("getJit() - pngetJit gave us null");
+ return nullptr;
+ }
+
+ pJitInstance = new interceptor_ICJC();
+ pJitInstance->original_ICorJitCompiler = tICJI;
+ pJitInstance->mcs = new MethodCallSummarizer(g_logPath);
+ if (g_ourJitHost != nullptr)
+ {
+ g_ourJitHost->setMethodCallSummarizer(pJitInstance->mcs);
+ }
+ return pJitInstance;
+}
+
+//Exported via def file
+extern "C"
+void __stdcall sxsJitStartup(CoreClrCallbacks const & original_cccallbacks)
+{
+ PsxsJitStartup pnsxsJitStartup;
+
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("sxsJitStartup() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ //get entry point
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(g_hRealJit, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("sxsJitStartup() - GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return;
+ }
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ original_CoreClrCallbacks = new CoreClrCallbacks();
+ original_CoreClrCallbacks->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ original_CoreClrCallbacks->m_pfnIEE = original_cccallbacks.m_pfnIEE;
+ original_CoreClrCallbacks->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ original_CoreClrCallbacks->m_pfnGetCLRFunction = original_cccallbacks.m_pfnGetCLRFunction;
+
+ CoreClrCallbacks *temp = new CoreClrCallbacks();
+
+ temp->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ temp->m_pfnIEE = IEE_t;
+ temp->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ temp->m_pfnGetCLRFunction = GetCLRFunction;
+
+ pnsxsJitStartup(*temp);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.def b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.def
new file mode 100644
index 0000000000..436434c3de
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.def
@@ -0,0 +1,5 @@
+LIBRARY
+EXPORTS
+ getJit
+ jitStartup
+ sxsJitStartup
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.h b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.h
new file mode 100644
index 0000000000..2068a02775
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.h
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim.h - thin shim for the jit
+//----------------------------------------------------------
+#ifndef _SuperPMIShim
+#define _SuperPMIShim
+
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/.gitmirror b/src/ToolBox/superpmi/superpmi-shim-simple/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/.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-shim-simple/CMakeLists.txt b/src/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt
new file mode 100644
index 0000000000..354e46d097
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt
@@ -0,0 +1,72 @@
+project(superpmi-shim-simple)
+
+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)
+
+set(SUPERPMI_SHIM_SIMPLE_SOURCES
+ coreclrcallbacks.cpp
+ jithost.cpp
+ icorjitcompiler.cpp
+ icorjitinfo.cpp
+ ieememorymanager.cpp
+ iexecutionengine.cpp
+ superpmi-shim-simple.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_SHIM_SIMPLE_SOURCES
+)
+
+if (WIN32)
+ preprocess_def_file(${CMAKE_CURRENT_SOURCE_DIR}/superpmi-shim-simple.def ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-simple.def)
+
+ list(APPEND SUPERPMI_SHIM_SIMPLE_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-simple.def)
+endif (WIN32)
+
+add_library(superpmi-shim-simple
+ SHARED
+ ${SUPERPMI_SHIM_SIMPLE_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(superpmi-shim-simple
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(superpmi-shim-simple
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/superpmi-shim-simple.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS superpmi-shim-simple DESTINATION .)
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp
new file mode 100644
index 0000000000..cf35748672
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp
@@ -0,0 +1,58 @@
+//
+// 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 "coreclrcallbacks.h"
+#include "iexecutionengine.h"
+
+typedef LPVOID (__stdcall * pfnEEHeapAllocInProcessHeap)(DWORD dwFlags, SIZE_T dwBytes);
+typedef BOOL (__stdcall * pfnEEHeapFreeInProcessHeap)(DWORD dwFlags, LPVOID lpMem);
+
+CoreClrCallbacks *original_CoreClrCallbacks = nullptr;
+pfnEEHeapAllocInProcessHeap original_EEHeapAllocInProcessHeap = nullptr;
+pfnEEHeapFreeInProcessHeap original_EEHeapFreeInProcessHeap = nullptr;
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
+{
+ interceptor_IEE *iee = new interceptor_IEE();
+ iee->original_IEE = original_CoreClrCallbacks->m_pfnIEE();
+ return iee;
+}
+
+/*#pragma warning( suppress :4996 ) //deprecated
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength)
+{
+ DebugBreakorAV(131);
+ return 0;
+}
+*/
+
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes)
+{
+ return original_EEHeapAllocInProcessHeap(dwFlags, dwBytes);
+}
+
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem)
+{
+ return original_EEHeapFreeInProcessHeap(dwFlags, lpMem);
+}
+
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
+{
+ if(strcmp(functionName, "EEHeapAllocInProcessHeap")==0)
+ {
+ original_EEHeapAllocInProcessHeap =
+ (pfnEEHeapAllocInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapAllocInProcessHeap");
+ return (void*)EEHeapAllocInProcessHeap;
+ }
+ if(strcmp(functionName, "EEHeapFreeInProcessHeap")==0)
+ {
+ original_EEHeapFreeInProcessHeap =
+ (pfnEEHeapFreeInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapFreeInProcessHeap");
+ return (void*)EEHeapFreeInProcessHeap;
+ }
+ return original_CoreClrCallbacks->m_pfnGetCLRFunction(functionName);
+}
+
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.h b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.h
new file mode 100644
index 0000000000..2e3a673f57
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.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 _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);
+
+// Added to allow us to persist a copy of the original callbacks
+extern CoreClrCallbacks *original_CoreClrCallbacks;
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp
new file mode 100644
index 0000000000..f6fceb2029
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp
@@ -0,0 +1,59 @@
+//
+// 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 "icorjitcompiler.h"
+#include "icorjitinfo.h"
+
+interceptor_IEEMM *current_IEEMM = nullptr; //we want this to live beyond the scope of a single compileMethodCall
+
+CorJitResult __stdcall interceptor_ICJC::compileMethod (
+ ICorJitInfo *comp, /* IN */
+ struct CORINFO_METHOD_INFO *info, /* IN */
+ unsigned /* code:CorJitFlag */ flags, /* IN */
+ BYTE **nativeEntry, /* OUT */
+ ULONG *nativeSizeOfCode /* OUT */
+ )
+{
+ interceptor_ICJI our_ICorJitInfo;
+ our_ICorJitInfo.original_ICorJitInfo = comp;
+
+ if(current_IEEMM == nullptr)
+ current_IEEMM = new interceptor_IEEMM();
+
+ CorJitResult temp = original_ICorJitCompiler->compileMethod(&our_ICorJitInfo, info, flags, nativeEntry, nativeSizeOfCode);
+
+ return temp;
+}
+
+void interceptor_ICJC::clearCache()
+{
+ original_ICorJitCompiler->clearCache();
+}
+
+BOOL interceptor_ICJC::isCacheCleanupRequired()
+{
+ return original_ICorJitCompiler->isCacheCleanupRequired();
+}
+
+void interceptor_ICJC::ProcessShutdownWork(ICorStaticInfo* info)
+{
+ original_ICorJitCompiler->ProcessShutdownWork(info);
+}
+
+void interceptor_ICJC::getVersionIdentifier(GUID* versionIdentifier /* OUT */)
+{
+ original_ICorJitCompiler->getVersionIdentifier(versionIdentifier);
+}
+
+unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags)
+{
+ return original_ICorJitCompiler->getMaxIntrinsicSIMDVectorLength(cpuCompileFlags);
+}
+
+void interceptor_ICJC::setRealJit(ICorJitCompiler* realJitCompiler)
+{
+ original_ICorJitCompiler->setRealJit(realJitCompiler);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.h b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.h
new file mode 100644
index 0000000000..a5eda6aa19
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitCompiler
+#define _ICorJitCompiler
+
+#include "runtimedetails.h"
+#include "ieememorymanager.h"
+
+class interceptor_ICJC : public ICorJitCompiler
+{
+
+#include "icorjitcompilerimpl.h"
+
+public:
+ // Added to help us track the original icjc and be able to easily indirect to it.
+ ICorJitCompiler *original_ICorJitCompiler;
+};
+
+extern interceptor_IEEMM *current_IEEMM; // we want a pointer to the memory manager to live beyond the scope of a single compileMethodCall (jit32 expects this)
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
new file mode 100644
index 0000000000..89b19d8754
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
@@ -0,0 +1,1726 @@
+//
+// 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 "superpmi-shim-simple.h"
+#include "ieememorymanager.h"
+#include "icorjitcompiler.h"
+#include "spmiutil.h"
+
+//Stuff on ICorStaticInfo
+/**********************************************************************************/
+//
+// ICorMethodInfo
+//
+/**********************************************************************************/
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getMethodAttribs (CORINFO_METHOD_HANDLE ftn /* IN */)
+{
+ return original_ICorJitInfo->getMethodAttribs(ftn);
+}
+
+// sets private JIT flags, which can be, retrieved using getAttrib.
+void interceptor_ICJI::setMethodAttribs (CORINFO_METHOD_HANDLE ftn,/* IN */
+ CorInfoMethodRuntimeFlags attribs/* IN */)
+{
+ original_ICorJitInfo->setMethodAttribs(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 interceptor_ICJI::getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ original_ICorJitInfo->getMethodSig(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 interceptor_ICJI::getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ )
+{
+ return original_ICorJitInfo->getMethodInfo(ftn, info);
+}
+
+// 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 interceptor_ICJI::canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ )
+{
+ return original_ICorJitInfo->canInline(callerHnd, calleeHnd, pRestrictions);
+}
+
+// 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 interceptor_ICJI::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * reason)
+{
+ original_ICorJitInfo->reportInliningDecision(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 interceptor_ICJI::canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ )
+{
+ return original_ICorJitInfo->canTailCall(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 interceptor_ICJI::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason)
+{
+ original_ICorJitInfo->reportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+}
+
+// get individual exception handler
+void interceptor_ICJI::getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ )
+{
+ original_ICorJitInfo->getEHinfo(ftn, EHnumber, clause);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->getMethodClass(method);
+}
+
+// return module it belongs to
+CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->getMethodModule(method);
+}
+
+// This function returns the offset of the specified method in the
+// vtable of it's owning class or interface.
+void interceptor_ICJI::getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ )
+{
+ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+}
+
+// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+// getIntrinsicID() returns the intrinsic ID.
+CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand /* OUT */
+ )
+{
+ return original_ICorJitInfo->getIntrinsicID(method, pMustExpand);
+}
+
+// Is the given module the System.Numerics.Vectors module?
+bool interceptor_ICJI::isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ )
+{
+ return original_ICorJitInfo->isInSIMDModule(classHnd);
+}
+
+// return the unmanaged calling convention for a PInvoke
+CorInfoUnmanagedCallConv interceptor_ICJI::getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->getUnmanagedCallConv(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 interceptor_ICJI::pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ )
+{
+ return original_ICorJitInfo->pInvokeMarshalingRequired(method, callSiteSig);
+}
+
+// Check constraints on method type arguments (only).
+// The parent class should be checked separately using satisfiesClassConstraints(parent).
+BOOL interceptor_ICJI::satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->satisfiesMethodConstraints(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 interceptor_ICJI::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 */
+ )
+{
+ return original_ICorJitInfo->isCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate);
+}
+
+// Determines whether the delegate creation obeys security transparency rules
+BOOL interceptor_ICJI::isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ )
+{
+ return original_ICorJitInfo->isDelegateCreationAllowed(delegateHnd, calleeHnd);
+}
+
+
+// Indicates if the method is an instance of the generic
+// method that passes (or has passed) verification
+CorInfoInstantiationVerification interceptor_ICJI::isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ )
+{
+ return original_ICorJitInfo->isInstantiationOfVerifiedGeneric(method);
+}
+
+// Loads the constraints on a typical method definition, detecting cycles;
+// for use in verification.
+void interceptor_ICJI::initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ )
+{
+ original_ICorJitInfo->initConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+}
+
+CorInfoCanSkipVerificationResult interceptor_ICJI::canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ )
+{
+ return original_ICorJitInfo->canSkipMethodVerification(ftnHandle);
+}
+
+// load and restore the method
+void interceptor_ICJI::methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ original_ICorJitInfo->methodMustBeLoadedBeforeCodeIsRun(method);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->mapMethodDeclToMethodImpl(method);
+}
+
+// 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 interceptor_ICJI::getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ )
+{
+ original_ICorJitInfo->getGSCookie(pCookieVal, ppCookieVal);
+}
+
+/**********************************************************************************/
+//
+// ICorModuleInfo
+//
+/**********************************************************************************/
+
+// Resolve metadata token into runtime method handles.
+void interceptor_ICJI::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ original_ICorJitInfo->resolveToken(pResolvedToken);
+}
+
+bool interceptor_ICJI::tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ return original_ICorJitInfo->tryResolveToken(pResolvedToken);
+}
+
+// Signature information about the call sig
+void interceptor_ICJI::findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ original_ICorJitInfo->findSig(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 interceptor_ICJI::findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ original_ICorJitInfo->findCallSiteSig(module, methTOK, context, sig);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */)
+{
+ return original_ICorJitInfo->getTokenTypeAsHandle(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 interceptor_ICJI::canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ )
+{
+ return original_ICorJitInfo->canSkipVerification(module);
+}
+
+// Checks if the given metadata token is valid
+BOOL interceptor_ICJI::isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ return original_ICorJitInfo->isValidToken(module, metaTOK);
+}
+
+// Checks if the given metadata token is valid StringRef
+BOOL interceptor_ICJI::isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ return original_ICorJitInfo->isValidStringRef(module, metaTOK);
+}
+
+BOOL interceptor_ICJI::shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE scope
+ )
+{
+ return original_ICorJitInfo->shouldEnforceCallvirtRestriction(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 interceptor_ICJI::asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->asCorInfoType(cls);
+}
+
+// for completeness
+const char* interceptor_ICJI::getClassName (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getClassName(cls);
+}
+
+
+// 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 interceptor_ICJI::appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ )
+{
+ return original_ICorJitInfo->appendClassName(ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly);
+}
+
+// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+BOOL interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ return original_ICorJitInfo->isValueClass(cls);
+}
+
+// If this method returns true, JIT will do optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType()
+BOOL interceptor_ICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ return original_ICorJitInfo->canInlineTypeCheckWithObjectVTable(cls);
+}
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getClassAttribs (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getClassAttribs(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 interceptor_ICJI::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ return original_ICorJitInfo->isStructRequiringStackAllocRetBuf(cls);
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getClassModule(cls);
+}
+
+// Returns the assembly that contains the module "mod".
+CORINFO_ASSEMBLY_HANDLE interceptor_ICJI::getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ )
+{
+ return original_ICorJitInfo->getModuleAssembly(mod);
+}
+
+// Returns the name of the assembly "assem".
+const char* interceptor_ICJI::getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE assem
+ )
+{
+ return original_ICorJitInfo->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* interceptor_ICJI::LongLifetimeMalloc(size_t sz)
+{
+ return original_ICorJitInfo->LongLifetimeMalloc(sz);
+}
+
+void interceptor_ICJI::LongLifetimeFree(void* obj)
+{
+ original_ICorJitInfo->LongLifetimeFree(obj);
+}
+
+size_t interceptor_ICJI::getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getClassModuleIdForStatics(cls, pModule, ppIndirection);
+}
+
+// return the number of bytes needed by an instance of the class
+unsigned interceptor_ICJI::getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getClassSize(cls);
+}
+
+unsigned interceptor_ICJI::getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint
+ )
+{
+ return original_ICorJitInfo->getClassAlignmentRequirement(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 interceptor_ICJI::getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ )
+{
+ return original_ICorJitInfo->getClassGClayout(cls, gcPtrs);
+}
+
+// returns the number of instance fields in a class
+unsigned interceptor_ICJI::getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ )
+{
+ return original_ICorJitInfo->getClassNumInstanceFields(cls);
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ )
+{
+ return original_ICorJitInfo->getFieldInClass(clsHnd, num);
+}
+
+BOOL interceptor_ICJI::checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ )
+{
+ return original_ICorJitInfo->checkMethodModifier(hMethod, modifier, fOptional);
+}
+
+// returns the "NEW" helper optimized for "newCls."
+CorInfoHelpFunc interceptor_ICJI::getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ )
+{
+ return original_ICorJitInfo->getNewHelper(pResolvedToken, callerHandle);
+}
+
+// returns the newArr (1-Dim array) helper optimized for "arrayCls."
+CorInfoHelpFunc interceptor_ICJI::getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ )
+{
+ return original_ICorJitInfo->getNewArrHelper(arrayCls);
+}
+
+// returns the optimized "IsInstanceOf" or "ChkCast" helper
+CorInfoHelpFunc interceptor_ICJI::getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ )
+{
+ return original_ICorJitInfo->getCastingHelper(pResolvedToken, fThrowing);
+}
+
+// returns helper to trigger static constructor
+CorInfoHelpFunc interceptor_ICJI::getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ )
+{
+ return original_ICorJitInfo->getSharedCCtorHelper(clsHnd);
+}
+
+CorInfoHelpFunc interceptor_ICJI::getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE ftn
+ )
+{
+ return original_ICorJitInfo->getSecurityPrologHelper(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 interceptor_ICJI::getTypeForBox(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getTypeForBox(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 interceptor_ICJI::getBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getBoxHelper(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 interceptor_ICJI::getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getUnBoxHelper(cls);
+}
+
+bool interceptor_ICJI::getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ return original_ICorJitInfo->getReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup);
+}
+
+void interceptor_ICJI::getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+}
+
+const char* interceptor_ICJI::getHelperName(
+ CorInfoHelpFunc funcNum
+ )
+{
+ return original_ICorJitInfo->getHelperName(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 interceptor_ICJI::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
+ )
+{
+ return original_ICorJitInfo->initClass(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 interceptor_ICJI::classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ original_ICorJitInfo->classMustBeLoadedBeforeCodeIsRun(cls);
+}
+
+// returns the class handle for the special builtin classes
+CORINFO_CLASS_HANDLE interceptor_ICJI::getBuiltinClass (
+ CorInfoClassId classId
+ )
+{
+ return original_ICorJitInfo->getBuiltinClass(classId);
+}
+
+// "System.Int32" ==> CORINFO_TYPE_INT..
+CorInfoType interceptor_ICJI::getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getTypeForPrimitiveValueClass(cls);
+}
+
+// TRUE if child is a subtype of parent
+// if parent is an interface, then does child implement / extend parent
+BOOL interceptor_ICJI::canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ )
+{
+ return original_ICorJitInfo->canCast(child, parent);
+}
+
+// TRUE if cls1 and cls2 are considered equivalent types.
+BOOL interceptor_ICJI::areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ return original_ICorJitInfo->areTypesEquivalent(cls1, cls2);
+}
+
+// returns is the intersection of cls1 and cls2.
+CORINFO_CLASS_HANDLE interceptor_ICJI::mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ return original_ICorJitInfo->mergeClasses(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 interceptor_ICJI::getParentType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getParentType(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 interceptor_ICJI::getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ )
+{
+ return original_ICorJitInfo->getChildType(clsHnd, clsRet);
+}
+
+// Check constraints on type arguments of this class and parent classes
+BOOL interceptor_ICJI::satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->satisfiesClassConstraints(cls);
+}
+
+// Check if this is a single dimensional array type
+BOOL interceptor_ICJI::isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->isSDArray(cls);
+}
+
+// Get the numbmer of dimensions in an array
+unsigned interceptor_ICJI::getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getArrayRank(cls);
+}
+
+// Get static field data for an array
+void * interceptor_ICJI::getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ )
+{
+ return original_ICorJitInfo->getArrayInitializationData(field, size);
+}
+
+// Check Visibility rules.
+CorInfoIsAccessAllowedResult interceptor_ICJI::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. */
+ )
+{
+ return original_ICorJitInfo->canAccessClass(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* interceptor_ICJI::getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ return original_ICorJitInfo->getFieldName(ftn, moduleName);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getFieldClass (
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ return original_ICorJitInfo->getFieldClass(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 interceptor_ICJI::getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ return original_ICorJitInfo->getFieldType(field, structType, memberParent);
+}
+
+// return the data member's instance offset
+unsigned interceptor_ICJI::getFieldOffset(
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ return original_ICorJitInfo->getFieldOffset(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 interceptor_ICJI::isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field)
+{
+ return original_ICorJitInfo->isWriteBarrierHelperRequired(field);
+}
+
+void interceptor_ICJI::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ )
+{
+ original_ICorJitInfo->getFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+}
+
+// Returns true iff "fldHnd" represents a static field.
+bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
+{
+ // this method does exist in some forms of the jit interface... if trip into one we'll know about it
+ DebugBreakorAV(67);
+ return true;
+}
+
+/*********************************************************************************/
+//
+// 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 interceptor_ICJI::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
+ )
+{
+ original_ICorJitInfo->getBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+}
+
+// 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 interceptor_ICJI::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
+ )
+{
+ original_ICorJitInfo->setBoundaries(ftn, cMap, pMap);
+}
+
+// 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 interceptor_ICJI::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
+ )
+{
+ original_ICorJitInfo->getVars(ftn, cVars, vars, extendOthers);
+}
+
+// Report back to the EE the location of every variable.
+// note that the JIT might split lifetimes into different
+// locations etc.
+
+void interceptor_ICJI::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
+ )
+{
+ original_ICorJitInfo->setVars(ftn, cVars, vars);
+}
+
+/*-------------------------- 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 * interceptor_ICJI::allocateArray(
+ ULONG cBytes
+ )
+{
+ return original_ICorJitInfo->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 interceptor_ICJI::freeArray(
+ void *array
+ )
+{
+ original_ICorJitInfo->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 interceptor_ICJI::getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ return original_ICorJitInfo->getArgNext(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 interceptor_ICJI::getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ )
+{
+ return original_ICorJitInfo->getArgType(sig, args, vcTypeRet);
+}
+
+// If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ return original_ICorJitInfo->getArgClass(sig, args);
+}
+
+// Returns type of HFA for valuetype
+CorInfoType interceptor_ICJI::getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ )
+{
+ return original_ICorJitInfo->getHFAType(hClass);
+}
+
+/*****************************************************************************
+* 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 interceptor_ICJI::GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ return original_ICorJitInfo->GetErrorHRESULT(pExceptionPointers);
+}
+
+// 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 interceptor_ICJI::GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ )
+{
+ return original_ICorJitInfo->GetErrorMessage(buffer, bufferLength);
+}
+
+// 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 interceptor_ICJI::FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ return original_ICorJitInfo->FilterException(pExceptionPointers);
+}
+
+// Cleans up internal EE tracking when an exception is caught.
+void interceptor_ICJI::HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ original_ICorJitInfo->HandleException(pExceptionPointers);
+}
+
+void interceptor_ICJI::ThrowExceptionForJitResult(
+ HRESULT result)
+{
+ original_ICorJitInfo->ThrowExceptionForJitResult(result);
+}
+
+//Throws an exception defined by the given throw helper.
+void interceptor_ICJI::ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper)
+{
+ original_ICorJitInfo->ThrowExceptionForHelper(throwHelper);
+}
+
+/*****************************************************************************
+ * 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 interceptor_ICJI::getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ )
+{
+ original_ICorJitInfo->getEEInfo(pEEInfoOut);
+}
+
+// Returns name of the JIT timer log
+LPCWSTR interceptor_ICJI::getJitTimeLogFilename()
+{
+ return original_ICorJitInfo->getJitTimeLogFilename();
+}
+
+
+ /*********************************************************************************/
+ //
+ // Diagnostic methods
+ //
+ /*********************************************************************************/
+
+// this function is for debugging only. Returns method token.
+// Returns mdMethodDefNil for dynamic methods.
+mdMethodDef interceptor_ICJI::getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ )
+{
+ return original_ICorJitInfo->getMethodDefFromMethod(hMethod);
+}
+
+// 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* interceptor_ICJI::getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ return original_ICorJitInfo->getMethodName(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 interceptor_ICJI::getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ )
+{
+ return original_ICorJitInfo->getMethodHash(ftn);
+}
+
+// this function is for debugging only.
+size_t interceptor_ICJI::findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ )
+{
+ return original_ICorJitInfo->findNameOfToken(module, metaTOK, szFQName, FQNameCapacity);
+}
+
+bool interceptor_ICJI::getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ )
+{
+ return original_ICorJitInfo->getSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
+}
+
+//Stuff on ICorDynamicInfo
+DWORD interceptor_ICJI::getThreadTLSIndex(
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getThreadTLSIndex(ppIndirection);
+}
+
+const void * interceptor_ICJI::getInlinedCallFrameVptr(
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getInlinedCallFrameVptr(ppIndirection);
+}
+
+LONG * interceptor_ICJI::getAddrOfCaptureThreadGlobal(
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getAddrOfCaptureThreadGlobal(ppIndirection);
+}
+
+SIZE_T* interceptor_ICJI::getAddrModuleDomainID(CORINFO_MODULE_HANDLE module)
+{
+ return original_ICorJitInfo->getAddrModuleDomainID(module);
+}
+
+// return the native entry point to an EE helper (see CorInfoHelpFunc)
+void* interceptor_ICJI::getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getHelperFtn(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 interceptor_ICJI::getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags)
+{
+ original_ICorJitInfo->getFunctionEntryPoint(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 interceptor_ICJI::getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult)
+{
+ original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, pResult);
+}
+
+// get the synchronization handle that is passed to monXstatic function
+void* interceptor_ICJI::getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getMethodSync(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 interceptor_ICJI::getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ )
+{
+ return original_ICorJitInfo->getLazyStringLiteralHelper(handle);
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->embedModuleHandle(handle, ppIndirection);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->embedClassHandle(handle, ppIndirection);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->embedMethodHandle(handle, ppIndirection);
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->embedFieldHandle(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 interceptor_ICJI::embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * pResult)
+{
+ original_ICorJitInfo->embedGenericHandle(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 interceptor_ICJI::getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ )
+{
+ return original_ICorJitInfo->getLocationOfThisType(context);
+}
+
+// return the unmanaged target *if method has already been prelinked.*
+void* interceptor_ICJI::getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getPInvokeUnmanagedTarget(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void* interceptor_ICJI::getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getAddressOfPInvokeFixup(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void interceptor_ICJI::getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ )
+{
+ original_ICorJitInfo->getAddressOfPInvokeTarget(method, pLookup);
+}
+
+// Generate a cookie based on the signature that would needs to be passed
+// to CORINFO_HELP_PINVOKE_CALLI
+LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection
+ )
+{
+ return original_ICorJitInfo->GetCookieForPInvokeCalliSig(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 interceptor_ICJI::canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ )
+{
+ return original_ICorJitInfo->canGetCookieForPInvokeCalliSig(szMetaSig);
+}
+
+// Gets a handle that is checked to see if the current method is
+// included in "JustMyCode"
+CORINFO_JUST_MY_CODE_HANDLE interceptor_ICJI::getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection
+ )
+{
+ return original_ICorJitInfo->getJustMyCodeHandle(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 interceptor_ICJI::GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ )
+{
+ original_ICorJitInfo->GetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+}
+
+// Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+void interceptor_ICJI::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
+ )
+{
+ original_ICorJitInfo->getCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult);
+}
+
+BOOL interceptor_ICJI::canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE hInstanceType)
+
+{
+ return original_ICorJitInfo->canAccessFamily(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 interceptor_ICJI::isRIDClassDomainID(CORINFO_CLASS_HANDLE cls)
+{
+ return original_ICorJitInfo->isRIDClassDomainID(cls);
+}
+
+// returns the class's domain ID for accessing shared statics
+unsigned interceptor_ICJI::getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getClassDomainID(cls, ppIndirection);
+}
+
+
+// return the data's address (for static fields only)
+void* interceptor_ICJI::getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getFieldAddress(field, ppIndirection);
+}
+
+// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+CORINFO_VARARGS_HANDLE interceptor_ICJI::getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getVarArgsHandle(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 interceptor_ICJI::canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ )
+{
+ return original_ICorJitInfo->canGetVarArgsHandle(pSig);
+}
+
+// Allocate a string literal on the heap and return a handle to it
+InfoAccessType interceptor_ICJI::constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ )
+{
+ return original_ICorJitInfo->constructStringLiteral(module, metaTok, ppValue);
+}
+
+InfoAccessType interceptor_ICJI::emptyStringLiteral(
+ void **ppValue
+ )
+{
+ return original_ICorJitInfo->emptyStringLiteral(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 interceptor_ICJI::getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getFieldThreadLocalStoreID(field, ppIndirection);
+}
+
+// Sets another object to intercept calls to "self" and current method being compiled
+void interceptor_ICJI::setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ )
+{
+ original_ICorJitInfo->setOverride(pOverride, currentMethod);
+}
+
+// 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 interceptor_ICJI::addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ )
+{
+ original_ICorJitInfo->addActiveDependency(moduleFrom, moduleTo);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ )
+{
+ return original_ICorJitInfo->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData);
+}
+
+void interceptor_ICJI::MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ )
+{
+ original_ICorJitInfo->MethodCompileComplete(methHnd);
+}
+
+// return a thunk that will copy the arguments for the given signature.
+void* interceptor_ICJI::getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ )
+{
+ return original_ICorJitInfo->getTailCallCopyArgsThunk(pSig, flags);
+}
+
+//Stuff directly on ICorJitInfo
+
+// Returns extended flags for a particular compilation instance.
+DWORD interceptor_ICJI::getJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes)
+{
+ return original_ICorJitInfo->getJitFlags(jitFlags, sizeInBytes);
+}
+
+// Runs the given function with the given parameter under an error trap
+// and returns true if the function completes successfully.
+bool interceptor_ICJI::runWithErrorTrap(void (*function)(void*), void *param)
+{
+ return original_ICorJitInfo->runWithErrorTrap(function, param);
+}
+
+// return memory manager that the JIT can use to allocate a regular memory
+IEEMemoryManager* interceptor_ICJI::getMemoryManager()
+{
+ if(current_IEEMM->original_IEEMM == nullptr)
+ current_IEEMM->original_IEEMM = original_ICorJitInfo->getMemoryManager();
+
+ return current_IEEMM;
+}
+
+// get a block of memory for the code, readonly data, and read-write data
+void interceptor_ICJI::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 */
+ )
+{
+ return original_ICorJitInfo->allocMem(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 interceptor_ICJI::reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ )
+{
+ original_ICorJitInfo->reserveUnwindInfo(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 interceptor_ICJI::allocUnwindInfo (
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ )
+{
+ original_ICorJitInfo->allocUnwindInfo(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 * interceptor_ICJI::allocGCInfo (
+ size_t size /* IN */
+ )
+{
+ return original_ICorJitInfo->allocGCInfo(size);
+}
+
+// only used on x64
+void interceptor_ICJI::yieldExecution()
+{
+ original_ICorJitInfo->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 interceptor_ICJI::setEHcount (
+ unsigned cEH /* IN */
+ )
+{
+ original_ICorJitInfo->setEHcount(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 interceptor_ICJI::setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ )
+{
+ original_ICorJitInfo->setEHinfo(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 interceptor_ICJI::logMsg(unsigned level, const char* fmt, va_list args)
+{
+ return original_ICorJitInfo->logMsg(level, fmt, args);
+}
+
+// do an assert. will return true if the code should retry (DebugBreak)
+// returns false, if the assert should be igored.
+int interceptor_ICJI::doAssert(const char* szFile, int iLine, const char* szExpr)
+{
+ return original_ICorJitInfo->doAssert(szFile, iLine, szExpr);
+}
+
+void interceptor_ICJI::reportFatalError(CorJitResult result)
+{
+ original_ICorJitInfo->reportFatalError(result);
+}
+
+// allocate a basic block profile buffer where execution counts will be stored
+// for jitted basic blocks.
+HRESULT interceptor_ICJI::allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer
+ )
+{
+ return original_ICorJitInfo->allocBBProfileBuffer(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 interceptor_ICJI::getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG * count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer,
+ ULONG * numRuns
+ )
+{
+ return original_ICorJitInfo->getBBProfileData(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 interceptor_ICJI::recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ )
+{
+ original_ICorJitInfo->recordCallSite(instrOffset, callSig, methodHandle);
+}
+
+// A relocation is recorded if we are pre-jitting.
+// A jump thunk may be inserted if we are jitting
+void interceptor_ICJI::recordRelocation(
+ void * location, /* IN */
+ void * target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ )
+{
+ original_ICorJitInfo->recordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+WORD interceptor_ICJI::getRelocTypeHint(void * target)
+{
+ return original_ICorJitInfo->getRelocTypeHint(target);
+}
+
+// A callback to identify the range of address known to point to
+// compiler-generated native entry points that call back into
+// MSIL.
+void interceptor_ICJI::getModuleNativeEntryPointRange(
+ void ** pStart, /* OUT */
+ void ** pEnd /* OUT */
+ )
+{
+ original_ICorJitInfo->getModuleNativeEntryPointRange(pStart, pEnd);
+}
+
+// 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 interceptor_ICJI::getExpectedTargetArchitecture()
+{
+ return original_ICorJitInfo->getExpectedTargetArchitecture();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.h b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.h
new file mode 100644
index 0000000000..c266f036fb
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.h
@@ -0,0 +1,24 @@
+//
+// 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"
+
+class interceptor_ICJI : public ICorJitInfo
+{
+
+#include "icorjitinfoimpl.h"
+
+public:
+
+ //Added to help us track the original icji and be able to easily indirect
+ //to it. And a simple way to keep one memory manager instance per instance.
+ ICorJitInfo *original_ICorJitInfo;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.cpp
new file mode 100644
index 0000000000..668b4b7728
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.cpp
@@ -0,0 +1,72 @@
+//
+// 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 "ieememorymanager.h"
+#include "superpmi-shim-simple.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEEMM::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEEMM->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::AddRef()
+{
+ return original_IEEMM->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::Release()
+{
+ return original_IEEMM->Release();
+}
+
+//***************************************************************************
+// IEEMemoryManager methods for locking
+//***************************************************************************
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+ return original_IEEMM->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return original_IEEMM->ClrVirtualFree(lpAddress, dwSize, dwFreeType);
+}
+SIZE_T STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ return original_IEEMM->ClrVirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ return original_IEEMM->ClrVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessHeap()
+{
+ return original_IEEMM->ClrGetProcessHeap();
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ return original_IEEMM->ClrHeapCreate(flOptions, dwInitialSize, dwMaximumSize);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapDestroy(HANDLE hHeap)
+{
+ return original_IEEMM->ClrHeapDestroy(hHeap);
+}
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
+{
+ return original_IEEMM->ClrHeapAlloc(hHeap, dwFlags, dwBytes);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ return original_IEEMM->ClrHeapFree(hHeap, dwFlags, lpMem);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ return original_IEEMM->ClrHeapValidate(hHeap, dwFlags, lpMem);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessExecutableHeap()
+{
+ return original_IEEMM->ClrGetProcessExecutableHeap();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.h b/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.h
new file mode 100644
index 0000000000..411c532b59
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.h
@@ -0,0 +1,108 @@
+//
+// 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"
+
+/*
+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
+
+*/
+
+class interceptor_IEEMM : 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 so we know where to make the real calls to.
+ IEEMemoryManager *original_IEEMM;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.cpp
new file mode 100644
index 0000000000..a137266751
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.cpp
@@ -0,0 +1,158 @@
+//
+// 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 "iexecutionengine.h"
+#include "superpmi-shim-simple.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEE::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEE->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::AddRef()
+{
+ return original_IEE->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::Release()
+{
+ return original_IEE->Release();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for TLS
+//***************************************************************************
+// Associate a callback for cleanup with a TLS slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ original_IEE->TLS_AssociateCallback(slot, callback);
+}
+// Get the TLS block for fast Get/Set operations
+LPVOID* STDMETHODCALLTYPE interceptor_IEE::TLS_GetDataBlock()
+{
+ return original_IEE->TLS_GetDataBlock();
+}
+
+// Get the value at a slot
+LPVOID STDMETHODCALLTYPE interceptor_IEE::TLS_GetValue(DWORD slot)
+{
+ return original_IEE->TLS_GetValue(slot);
+}
+
+// Get the value at a slot, return FALSE if TLS info block doesn't exist
+BOOL STDMETHODCALLTYPE interceptor_IEE::TLS_CheckValue(DWORD slot, LPVOID * pValue)
+{
+ return original_IEE->TLS_CheckValue(slot, pValue);
+}
+// Set the value at a slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_SetValue(DWORD slot, LPVOID pData)
+{
+ original_IEE->TLS_SetValue(slot, pData);
+}
+// Free TLS memory block and make callback
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_ThreadDetaching()
+{
+ original_IEE->TLS_ThreadDetaching();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for locking
+//***************************************************************************
+CRITSEC_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
+{
+ return original_IEE->CreateLock(szTag, level, flags);
+}
+void STDMETHODCALLTYPE interceptor_IEE::DestroyLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->DestroyLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::AcquireLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->AcquireLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ReleaseLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->ReleaseLock(lock);
+}
+
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateAutoEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateAutoEvent(bInitialState);
+}
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateManualEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateManualEvent(bInitialState);
+}
+void STDMETHODCALLTYPE interceptor_IEE::CloseEvent(EVENT_COOKIE event)
+{
+ original_IEE->CloseEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrSetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrSetEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrResetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrResetEvent(event);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->WaitForEvent(event, dwMilliseconds, bAlertable);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds)
+{
+ return original_IEE->WaitForSingleObject(handle, dwMilliseconds);
+}
+SEMAPHORE_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ return original_IEE->ClrCreateSemaphore(dwInitial, dwMax);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ original_IEE->ClrCloseSemaphore(semaphore);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForSemaphore(semaphore, dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ return original_IEE->ClrReleaseSemaphore(semaphore, lReleaseCount, lpPreviousCount);
+}
+MUTEX_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName)
+{
+ return original_IEE->ClrCreateMutex(lpMutexAttributes, bInitialOwner, lpName);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ original_IEE->ClrCloseMutex(mutex);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ return original_IEE->ClrReleaseMutex(mutex);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForMutex(mutex, dwMilliseconds, bAlertable);
+}
+
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrSleepEx(dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrAllocationDisallowed()
+{
+ return original_IEE->ClrAllocationDisallowed();
+}
+void STDMETHODCALLTYPE interceptor_IEE::GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ original_IEE->GetLastThrownObjectExceptionFromThread(ppvException);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.h b/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.h
new file mode 100644
index 0000000000..dd3d7ee1fa
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.h
@@ -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.
+//
+
+#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
+*/
+
+class interceptor_IEE : 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);
+
+public:
+ // Added so we know where to make the real calls to.
+ IExecutionEngine *original_IEE;
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/jithost.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/jithost.cpp
new file mode 100644
index 0000000000..01bff37a01
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/jithost.cpp
@@ -0,0 +1,40 @@
+//
+// 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 "runtimedetails.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+JitHost* g_ourJitHost;
+
+JitHost::JitHost(ICorJitHost* wrappedHost) : wrappedHost(wrappedHost)
+{
+}
+
+void* JitHost::allocateMemory(size_t size, bool usePageAllocator)
+{
+ return wrappedHost->allocateMemory(size, usePageAllocator);
+}
+
+void JitHost::freeMemory(void* block, bool usePageAllocator)
+{
+ return wrappedHost->freeMemory(block, usePageAllocator);
+}
+
+int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
+{
+ return wrappedHost->getIntConfigValue(key, defaultValue);
+}
+
+const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
+{
+ return wrappedHost->getStringConfigValue(key);
+}
+
+void JitHost::freeStringConfigValue(const wchar_t* value)
+{
+ wrappedHost->freeStringConfigValue(value);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/jithost.h b/src/ToolBox/superpmi/superpmi-shim-simple/jithost.h
new file mode 100644
index 0000000000..7df1c581dc
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/jithost.h
@@ -0,0 +1,22 @@
+//
+// 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(ICorJitHost* wrappedHost);
+
+#include "icorjithostimpl.h"
+
+private:
+ ICorJitHost* wrappedHost;
+};
+
+extern JitHost* g_ourJitHost;
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp
new file mode 100644
index 0000000000..2381d0fa38
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp
@@ -0,0 +1,217 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim.cpp - thin shim for the jit
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "superpmi-shim-simple.h"
+#include "runtimedetails.h"
+#include "coreclrcallbacks.h"
+#include "icorjitcompiler.h"
+#include "errorhandling.h"
+#include "logging.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+HMODULE g_hRealJit = 0; //We leak this currently (could do the proper shutdown in process_detach)
+WCHAR* g_realJitPath = nullptr; //We leak this (could do the proper shutdown in process_detach)
+char* g_logFilePath = nullptr; //We *don't* leak this, hooray!
+WCHAR* g_HomeDirectory = nullptr;
+WCHAR* g_DefaultRealJitPath = nullptr;
+
+void SetDefaultPaths()
+{
+ if (g_HomeDirectory == nullptr)
+ {
+ g_HomeDirectory = GetEnvironmentVariableWithDefaultW(W("HOME"), W("."));
+ }
+
+ if (g_DefaultRealJitPath == nullptr)
+ {
+ size_t len = wcslen(g_HomeDirectory) + 1 + wcslen(DEFAULT_REAL_JIT_NAME_W) + 1;
+ g_DefaultRealJitPath = new WCHAR[len];
+ wcscpy_s(g_DefaultRealJitPath, len, g_HomeDirectory);
+ wcscat_s(g_DefaultRealJitPath, len, DIRECTORY_SEPARATOR_STR_W);
+ wcscat_s(g_DefaultRealJitPath, len, DEFAULT_REAL_JIT_NAME_W);
+ }
+}
+
+void SetLibName()
+{
+ if (g_realJitPath == nullptr)
+ {
+ g_realJitPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimPath"), g_DefaultRealJitPath);
+ }
+}
+
+// TODO: this only works for ANSI file paths...
+void SetLogFilePath()
+{
+ if (g_logFilePath == nullptr)
+ {
+ // If the environment variable isn't set, we don't enable file logging
+ g_logFilePath = GetEnvironmentVariableWithDefaultA("SuperPMIShimLogFilePath", nullptr);
+ }
+}
+
+extern "C"
+BOOL
+#ifndef FEATURE_PAL
+APIENTRY
+#endif // !FEATURE_PAL
+DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+#ifdef FEATURE_PAL
+ if (0 != PAL_InitializeDLL())
+ {
+ fprintf(stderr, "Error: Fail to PAL_InitializeDLL\n");
+ exit(1);
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+ SetLogFilePath();
+ Logger::OpenLogFile(g_logFilePath);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ Logger::Shutdown();
+
+ delete[] g_logFilePath;
+ g_logFilePath = nullptr;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+// Exported via def file
+extern "C"
+void __stdcall jitStartup(ICorJitHost* host)
+{
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if (g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if (g_hRealJit == 0)
+ {
+ LogError("getJit() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ // Get the required entrypoint
+ PjitStartup pnjitStartup = (PjitStartup)::GetProcAddress(g_hRealJit, "jitStartup");
+ if (pnjitStartup == nullptr)
+ {
+ // This portion of the interface is not used by the JIT under test.
+ g_ourJitHost = nullptr;
+ return;
+ }
+
+ g_ourJitHost = new JitHost(host);
+ pnjitStartup(g_ourJitHost);
+}
+
+
+//Exported via def file
+extern "C"
+ICorJitCompiler* __stdcall getJit()
+{
+ DWORD dwRetVal = 0;
+ PgetJit pngetJit;
+ interceptor_ICJC *pJitInstance = nullptr;
+ ICorJitCompiler *tICJI = nullptr;
+
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("getJit() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return nullptr;
+ }
+ }
+
+ //get the required entrypoints
+ pngetJit = (PgetJit)::GetProcAddress(g_hRealJit, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("getJit() - GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return nullptr;
+ }
+
+ tICJI = pngetJit();
+ if(tICJI == nullptr)
+ {
+ LogError("getJit() - pngetJit gave us null");
+ return nullptr;
+ }
+
+ pJitInstance = new interceptor_ICJC();
+ pJitInstance->original_ICorJitCompiler = tICJI;
+ return pJitInstance;
+}
+
+//Exported via def file
+extern "C"
+void __stdcall sxsJitStartup(CoreClrCallbacks const & original_cccallbacks)
+{
+ PsxsJitStartup pnsxsJitStartup;
+
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("sxsJitStartup() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ //get entry point
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(g_hRealJit, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("sxsJitStartup() - GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return;
+ }
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ original_CoreClrCallbacks = new CoreClrCallbacks();
+ original_CoreClrCallbacks->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ original_CoreClrCallbacks->m_pfnIEE = original_cccallbacks.m_pfnIEE;
+ original_CoreClrCallbacks->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ original_CoreClrCallbacks->m_pfnGetCLRFunction = original_cccallbacks.m_pfnGetCLRFunction;
+
+ CoreClrCallbacks *temp = new CoreClrCallbacks();
+
+ temp->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ temp->m_pfnIEE = IEE_t;
+ temp->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ temp->m_pfnGetCLRFunction = GetCLRFunction;
+
+ pnsxsJitStartup(*temp);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.def b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.def
new file mode 100644
index 0000000000..436434c3de
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.def
@@ -0,0 +1,5 @@
+LIBRARY
+EXPORTS
+ getJit
+ jitStartup
+ sxsJitStartup
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.h b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.h
new file mode 100644
index 0000000000..2068a02775
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.h
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim.h - thin shim for the jit
+//----------------------------------------------------------
+#ifndef _SuperPMIShim
+#define _SuperPMIShim
+
+
+#endif
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..508ccc9348
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
@@ -0,0 +1,2039 @@
+//
+// 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()
+{
+#ifdef _TARGET_AMD64_
+ return IMAGE_FILE_MACHINE_AMD64;
+#endif
+#ifdef _TARGET_X86_
+ return IMAGE_FILE_MACHINE_I386;
+#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
diff --git a/tests/src/JIT/superpmi/app.config b/tests/src/JIT/superpmi/app.config
new file mode 100644
index 0000000000..8077c95440
--- /dev/null
+++ b/tests/src/JIT/superpmi/app.config
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <runtime>
+ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+ <dependentAssembly>
+ <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-4.0.20.0" newVersion="4.0.20.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="System.Text.Encoding" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-4.0.10.0" newVersion="4.0.10.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-4.0.10.0" newVersion="4.0.10.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="System.IO" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-4.0.10.0" newVersion="4.0.10.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="System.Reflection" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-4.0.10.0" newVersion="4.0.10.0" />
+ </dependentAssembly>
+ </assemblyBinding>
+ </runtime>
+</configuration> \ No newline at end of file
diff --git a/tests/src/JIT/superpmi/collect_alltests.cmd b/tests/src/JIT/superpmi/collect_alltests.cmd
new file mode 100644
index 0000000000..108b5ca938
--- /dev/null
+++ b/tests/src/JIT/superpmi/collect_alltests.cmd
@@ -0,0 +1,36 @@
+@echo off
+setlocal
+
+REM Do a .NET Core SuperPMI collection across all tests in the coreclr repo.
+
+REM Set the repo root.
+set _root=d:\src\coreclr
+
+REM Set the build flavor.
+set _flavor=Windows_NT.x64.Debug
+
+REM Everything else in this script is parameterized using the above two variables.
+
+if not exist %_root% echo Error: %_root% not found&goto :eof
+
+REM Where to put the resulting MCH file?
+set _mch=%_root%\bin\tests\alltests_win.mch
+
+set _testbuild=%_root%\bin\tests\%_flavor%
+if not exist %_testbuild% echo Error: %_testbuild% not found&goto :eof
+if not exist %_testbuild%\JIT\superpmi\superpmicollect\superpmicollect.exe echo Error: superpmicollect.exe not found&goto :eof
+
+set _collect_script=%_root%\tests\src\JIT\superpmi\collect_runtest.cmd
+if not exist %_collect_script% echo Error: %_collect_script% not found&goto :eof
+
+if not exist %_root%\tests\runtest.cmd echo Error: %_root%\tests\runtest.cmd not found&goto :eof
+
+if not defined CORE_ROOT echo ERROR: set CORE_ROOT before running this script&goto :eof
+if not exist %CORE_ROOT% echo Error: CORE_ROOT (%CORE_ROOT%) not found&goto :eof
+if not exist %CORE_ROOT%\coreclr.dll echo Error: coreclr.dll (%CORE_ROOT%\coreclr.dll) not found&goto :eof
+if not exist %CORE_ROOT%\corerun.exe echo Error: corerun.exe (%CORE_ROOT%\corerun.exe) not found&goto :eof
+
+REM Do the collection!
+pushd %_testbuild%
+%core_root%\corerun.exe %_testbuild%\JIT\superpmi\superpmicollect\superpmicollect.exe -mch %_mch% -run %_collect_script% %_root%\tests\runtest.cmd
+popd
diff --git a/tests/src/JIT/superpmi/collect_alltests.sh b/tests/src/JIT/superpmi/collect_alltests.sh
new file mode 100755
index 0000000000..ecb26e2151
--- /dev/null
+++ b/tests/src/JIT/superpmi/collect_alltests.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+CORECLRROOT=~/src/coreclr
+TESTROOT=~/test
+MCHFILE=${TESTROOT}/alltests_linux.mch
+FLAVOR=Windows_NT.x64.Debug
+
+pushd ${TESTROOT}/${FLAVOR}/superpmi/superpmicollect
+${TESTROOT}/${FLAVOR}/Tests/coreoverlay/corerun ${TESTROOT}/${FLAVOR}/JIT/superpmi/superpmicollect/superpmicollect.exe -mch ${MCHFILE} -run ${CORECLRROOT}/tests/src/JIT/superpmi/runtests.sh
+popd
diff --git a/tests/src/JIT/superpmi/collect_runtest.cmd b/tests/src/JIT/superpmi/collect_runtest.cmd
new file mode 100644
index 0000000000..3f573e01a2
--- /dev/null
+++ b/tests/src/JIT/superpmi/collect_runtest.cmd
@@ -0,0 +1,41 @@
+@echo off
+setlocal
+
+goto :start
+:usage
+echo Set up for individual test execution, with SuperPMI collection.
+echo.
+echo Usage: collect_runtest.cmd ^<path to runtest.cmd^>
+echo.
+echo We can't set the SuperPMI collection variables before runtest.cmd executes,
+echo because it runs other managed apps, including desktop apps, that are also
+echo affected by these variables. For SuperPMI collection, we only want the test
+echo itself to be collected. So, create a temporary script that just sets the
+echo SuperPMI collection variables, and pass that as the "TestEnv" argument to
+echo runtest.cmd, which uses it to set the environment for running an individual
+echo test.
+echo.
+echo Example usage of this script:
+echo %CORE_ROOT%\corerun superpmicollect.exe -mch alltests.mch -run collect_runtest.cmd c:\repos\coreclr\tests\runtest.cmd
+goto :eof
+
+:start
+if "%1"=="" echo ERROR: missing argument.&goto usage
+
+set runtestscript=%1
+
+set testenvfile=%TEMP%\superpmitestenv_%RANDOM%.cmd
+
+echo set SuperPMIShimLogPath=%SuperPMIShimLogPath%> %testenvfile%
+echo set SuperPMIShimPath=%SuperPMIShimPath%>> %testenvfile%
+echo set COMPlus_AltJit=%COMPlus_AltJit%>> %testenvfile%
+echo set COMPlus_AltJitName=%COMPlus_AltJitName%>> %testenvfile%
+
+set SuperPMIShimLogPath=
+set SuperPMIShimPath=
+set COMPlus_AltJit=
+set COMPlus_AltJitName=
+
+call %runtestscript% testEnv %testenvfile%
+
+del /f /q %testenvfile%
diff --git a/tests/src/JIT/superpmi/runtests.sh b/tests/src/JIT/superpmi/runtests.sh
new file mode 100755
index 0000000000..9fa5943821
--- /dev/null
+++ b/tests/src/JIT/superpmi/runtests.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Run CoreCLR OSS tests on Linux or Mac
+# Use the instructions here:
+# https://github.com/dotnet/coreclr/blob/master/Documentation/building/unix-test-instructions.md
+#
+# Summary:
+# 1. On Linux/Mac, in coreclr, ./build.sh
+# 2. On Linux/Mac, in corefx, ./build.sh
+# 3. On Linux/Mac, in corefx, "build-native.sh"
+# 4. On Windows, build Linux mscorlib: build.cmd linuxmscorlib
+# 5. Mount Windows shares on Linux
+# 6. Copy tests to Linux/Mac:
+# Linux: cp --recursive ~/brucefo1/ManagedCodeGen/bin/tests/Windows_NT.x64.Debug ~/test/Windows_NT.x64.Debug
+# Mac : cp -R ~/brucefo1/ManagedCodeGen/bin/tests/Windows_NT.x64.Debug ~/test/Windows_NT.x64.Debug
+# 7. Run this script
+#
+# If you pass "--testDir=NONE" to runtest.sh, it will create the "test overlay" (i.e., core_root directory)
+# and exit. This is a hack because runtest.sh doesn't have a separate argument to just do this.
+
+TESTROOT=~/test
+CORECLRROOT=~/src/coreclr
+COREFXROOT=~/src/corefx
+WINDOWSCORECLRROOT=~/WindowsMachine/coreclr
+WINDOWSFLAVOR=Windows_NT.x64.Debug
+UNIXANYFLAVOR=OSX.AnyCPU.Debug
+UNIXARCHFLAVOR=OSX.x64.Debug
+
+ARGS="\
+--testRootDir=${TESTROOT}/${WINDOWSFLAVOR} \
+--testNativeBinDir=${CORECLRROOT}/bin/obj/${UNIXARCHFLAVOR}/tests \
+--coreClrBinDir=${CORECLRROOT}/bin/Product/${UNIXARCHFLAVOR} \
+--mscorlibDir=${WINDOWSCORECLRROOT}/bin/Product/${UNIXARCHFLAVOR} \
+--coreFxBinDir=${COREFXROOT}/bin/${UNIXANYFLAVOR};${COREFXROOT}/bin/Unix.AnyCPU.Debug;${COREFXROOT}/bin/AnyOS.AnyCPU.Debug \
+--coreFxNativeBinDir=${COREFXROOT}/bin/${UNIXARCHFLAVOR}"
+
+pushd ${CORECLRROOT}
+echo ${CORECLRROOT}/tests/runtest.sh ${ARGS}
+#${CORECLRROOT}/tests/runtest.sh ${ARGS} --testDir=NONE
+${CORECLRROOT}/tests/runtest.sh ${ARGS}
+popd
diff --git a/tests/src/JIT/superpmi/superpmicollect.cs b/tests/src/JIT/superpmi/superpmicollect.cs
new file mode 100644
index 0000000000..02f15bb5c2
--- /dev/null
+++ b/tests/src/JIT/superpmi/superpmicollect.cs
@@ -0,0 +1,773 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace SuperPMICollection
+{
+
+ public class SpmiException : Exception
+ {
+ public SpmiException() : base()
+ { }
+
+ public SpmiException(string message)
+ : base(message)
+ { }
+
+ public SpmiException(string message, Exception innerException)
+ : base(message, innerException)
+ { }
+ }
+
+ internal class Global
+ {
+ // Arguments to the program. These should not be touched by Initialize(), as they are set earlier than that.
+ internal static bool SkipCleanup = false; // Should we skip all cleanup? That is, should we keep all temporary files? Useful for debugging.
+
+ // Computed values based on the environment and platform.
+ internal static bool IsWindows { get; private set; }
+ internal static bool IsOSX { get; private set; }
+ internal static bool IsLinux { get; private set; }
+
+ internal static string CoreRoot { get; private set; }
+ internal static string StandaloneJitName { get; private set; }
+ internal static string CollectorShimName { get; private set; }
+ internal static string SuperPmiToolName { get; private set; }
+ internal static string McsToolName { get; private set; }
+ internal static string JitPath { get; private set; } // Path to the standalone JIT
+ internal static string SuperPmiPath { get; private set; } // Path to superpmi.exe
+ internal static string McsPath { get; private set; } // Path to mcs.exe
+
+ // Initialize the global state. Don't use a class constructor, because we might throw exceptions
+ // that we want to catch.
+ public static void Initialize()
+ {
+ string core_root_raw = System.Environment.GetEnvironmentVariable("CORE_ROOT");
+ if (String.IsNullOrEmpty(core_root_raw))
+ {
+ throw new SpmiException("Environment variable CORE_ROOT is not set");
+ }
+
+ try
+ {
+ CoreRoot = System.IO.Path.GetFullPath(core_root_raw);
+ }
+ catch (Exception ex)
+ {
+ throw new SpmiException("Illegal CORE_ROOT environment variable (" + core_root_raw + "), exception: " + ex.Message);
+ }
+
+ IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ IsOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+ IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+
+ if (IsWindows)
+ {
+ StandaloneJitName = "clrjit.dll";
+ CollectorShimName = "superpmi-shim-collector.dll";
+ SuperPmiToolName = "superpmi.exe";
+ McsToolName = "mcs.exe";
+ }
+ else if (IsLinux)
+ {
+ StandaloneJitName = "libclrjit.so";
+ CollectorShimName = "libsuperpmi-shim-collector.so";
+ SuperPmiToolName = "superpmi";
+ McsToolName = "mcs";
+ }
+ else if (IsOSX)
+ {
+ StandaloneJitName = "libclrjit.dylib";
+ CollectorShimName = "libsuperpmi-shim-collector.dylib";
+ SuperPmiToolName = "superpmi";
+ McsToolName = "mcs";
+ }
+ else
+ {
+ throw new SpmiException("Unknown platform");
+ }
+
+ JitPath = Path.Combine(CoreRoot, StandaloneJitName);
+ SuperPmiPath = Path.Combine(CoreRoot, SuperPmiToolName);
+ McsPath = Path.Combine(CoreRoot, McsToolName);
+ }
+ }
+
+ internal class SuperPMICollectionClass
+ {
+ private static string s_tempDir = null; // Temporary directory where we will put the MC files, MCH files, MCL files, and TOC.
+ private static string s_baseFailMclFile = null; // Pathname for a temporary .MCL file used for noticing superpmi replay failures against base MCH.
+ private static string s_finalFailMclFile = null; // Pathname for a temporary .MCL file used for noticing superpmi replay failures against final MCH.
+ private static string s_baseMchFile = null; // The base .MCH file path
+ private static string s_cleanMchFile = null; // The clean .MCH file path
+ private static string s_finalMchFile = null; // The clean thin unique .MCH file path
+ private static string s_tocFile = null; // The .TOC file path for the clean thin unique .MCH file
+ private static string s_errors = ""; // Collect non-fatal file delete errors to display at the end of the collection process.
+
+ private static bool s_saveFinalMchFile = false; // Should we save the final MCH file, or delete it?
+
+ private static void SafeFileDelete(string filePath)
+ {
+ try
+ {
+ File.Delete(filePath);
+ }
+ catch(Exception ex)
+ {
+ string err = string.Format("Error deleting file \"{0}\": {1}", filePath, ex.Message);
+ s_errors += err + System.Environment.NewLine;
+ Console.Error.WriteLine(err);
+ }
+ }
+
+ private static void CreateTempDirectory(string tempPath)
+ {
+ if (tempPath == null)
+ {
+ tempPath = Path.GetTempPath();
+ }
+ s_tempDir = Path.Combine(tempPath, Path.GetRandomFileName() + "SPMI");
+ if (Directory.Exists(s_tempDir))
+ {
+ throw new SpmiException("temporary directory already exists: " + s_tempDir);
+ }
+ DirectoryInfo di = Directory.CreateDirectory(s_tempDir);
+ }
+
+ private static void ChooseFilePaths(string outputMchPath)
+ {
+ s_baseFailMclFile = Path.Combine(s_tempDir, "basefail.mcl");
+ s_finalFailMclFile = Path.Combine(s_tempDir, "finalfail.mcl");
+ s_baseMchFile = Path.Combine(s_tempDir, "base.mch");
+ s_cleanMchFile = Path.Combine(s_tempDir, "clean.mch");
+
+ if (outputMchPath == null)
+ {
+ s_saveFinalMchFile = false;
+ s_finalMchFile = Path.Combine(s_tempDir, "final.mch");
+ s_tocFile = Path.Combine(s_tempDir, "final.mch.mct");
+ }
+ else
+ {
+ s_saveFinalMchFile = true;
+ s_finalMchFile = Path.GetFullPath(outputMchPath);
+ s_tocFile = s_finalMchFile + ".mct";
+ }
+ }
+
+ private static int RunProgram(string program, string arguments)
+ {
+ // If the program is a script, move the program name into the arguments, and run it
+ // under the appropriate shell.
+ if (Global.IsWindows)
+ {
+ if ((program.LastIndexOf(".bat") != -1) || (program.LastIndexOf(".cmd") != -1))
+ {
+ string programArgumentSep = String.IsNullOrEmpty(arguments) ? "" : " ";
+ arguments = "/c " + program + programArgumentSep + arguments;
+ program = Environment.GetEnvironmentVariable("ComSpec"); // path to CMD.exe
+ }
+ }
+ else
+ {
+ if (program.LastIndexOf(".sh") != -1)
+ {
+ string programArgumentSep = String.IsNullOrEmpty(arguments) ? "" : " ";
+ arguments = "bash " + program + programArgumentSep + arguments;
+ program = "/usr/bin/env";
+ }
+ }
+
+ Console.WriteLine("Running: " + program + " " + arguments);
+ Process p = Process.Start(program, arguments);
+ p.WaitForExit();
+ return p.ExitCode;
+ }
+
+ // Run a single test from the coreclr test binary drop.
+ // This works even if given a test path in Windows file system format (e.g.,
+ // "c:\foo\bar\runit.cmd") when run on Unix. It converts to Unix path format and replaces
+ // the ".cmd" with ".sh" before attempting to run the script.
+ private static void RunTest(string testName)
+ {
+ string testDir;
+
+ if (Global.IsWindows)
+ {
+ int lastIndex = testName.LastIndexOf("\\");
+ if (lastIndex == -1)
+ {
+ throw new SpmiException("test path doesn't have any directory separators? " + testName);
+ }
+ testDir = testName.Substring(0, lastIndex);
+ }
+ else
+ {
+ // Just in case we've been given a test name in Windows format, convert it to Unix format here.
+
+ testName = testName.Replace("\\", "/");
+ testName = testName.Replace(".cmd", ".sh");
+ testName = testName.Replace(".bat", ".sh");
+
+ // The way tests are run on Linux, we might need to do some setup. In particular,
+ // if the test scripts are copied from Windows, we need to convert line endings
+ // to Unix line endings, and make the script executable. We can always do this
+ // more than once. This same transformation is done in runtest.sh.
+ // Review: RunProgram doesn't seem to work if the program isn't a full path.
+
+ RunProgram("/usr/bin/perl", @"-pi -e 's/\r\n|\n|\r/\n/g' " + "\"" + testName + "\"");
+ RunProgram("/bin/chmod", "+x \"" + testName + "\"");
+
+ // Now, figure out how to run the test.
+
+ int lastIndex = testName.LastIndexOf("/");
+ if (lastIndex == -1)
+ {
+ throw new SpmiException("test path doesn't have any directory separators? " + testName);
+ }
+ testDir = testName.Substring(0, lastIndex);
+ }
+
+ // Run the script in the same directory where the test lives.
+ string originalDir = Directory.GetCurrentDirectory();
+ Directory.SetCurrentDirectory(testDir);
+
+ try
+ {
+ RunProgram(testName, "");
+ }
+ finally
+ {
+ // Restore the original current directory from before the test run.
+ Directory.SetCurrentDirectory(originalDir);
+ }
+ }
+
+ // Run all the programs from the CoreCLR test binary drop we wish to run while collecting MC files.
+ private static void RunTestProgramsWhileCollecting()
+ {
+ // The list of all programs from the CoreCLR repo test binary drop that
+ // we should run when doing the SuperPMI collection. This is currently a
+ // hard-coded list of the relative paths within the test build binaries
+ // directory of the Windows .cmd files used to run a test. For non-Windows
+ // platforms, the .cmd is replaced by .sh, and the path separator character
+ // is changed.
+ //
+ // TODO: this should probably be loaded dynamically from a .json/.xml file.
+ string[] SuperPMICollectionTestProgramsList =
+ {
+ @"JIT\Performance\CodeQuality\Roslyn\CscBench\CscBench.cmd",
+ @"JIT\Methodical\fp\exgen\10w5d_cs_do\10w5d_cs_do.cmd",
+ @"JIT\Generics\Coverage\chaos65204782cs\chaos65204782cs.cmd"
+ };
+
+ // Figure out the root of the test binaries directory.
+ // Perhaps this (or something similar) would be a better way to figure out the binary root dir:
+ // testBinaryRootDir = System.IO.Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
+
+ string thisTestDir = Directory.GetCurrentDirectory();
+ int lastIndex = thisTestDir.LastIndexOf("JIT");
+ if (lastIndex == -1)
+ {
+ throw new SpmiException("we expect the current directory when the test is run to be within the JIT test binaries tree, but it is not: " + thisTestDir);
+ }
+ string testBinaryRootDir = thisTestDir.Substring(0, lastIndex);
+
+ // Run the tests
+ foreach (string test in SuperPMICollectionTestProgramsList)
+ {
+ string testFullPath = Path.Combine(testBinaryRootDir, test);
+ try
+ {
+ RunTest(testFullPath);
+ }
+ catch (SpmiException ex)
+ {
+ // Ignore failures running the test. We don't really care if they pass or not
+ // as long as they generate some .MC files. Plus, I'm not sure how confident
+ // we can be in getting a correct error code.
+
+ Console.Error.WriteLine("WARNING: test failed (ignoring): " + ex.Message);
+ }
+ }
+ }
+
+ // Run all the programs we wish to run while collecting MC files.
+ private static void RunProgramsWhileCollecting(string runProgramPath, string runProgramArguments)
+ {
+ if (runProgramPath == null)
+ {
+ // No program was given to use for collection, so use our default set.
+ RunTestProgramsWhileCollecting();
+ }
+ else
+ {
+ RunProgram(runProgramPath, runProgramArguments);
+ }
+ }
+
+ // Collect MC files:
+ // a. Set environment variables
+ // b. Run tests
+ // c. Un-set environment variables
+ // d. Check that something was generated
+ private static void CollectMCFiles(string runProgramPath, string runProgramArguments)
+ {
+ // Set environment variables.
+ Console.WriteLine("Setting environment variables:");
+ Console.WriteLine(" SuperPMIShimLogPath=" + s_tempDir);
+ Console.WriteLine(" SuperPMIShimPath=" + Global.JitPath);
+ Console.WriteLine(" COMPlus_AltJit=*");
+ Console.WriteLine(" COMPlus_AltJitName=" + Global.CollectorShimName);
+
+ Environment.SetEnvironmentVariable("SuperPMIShimLogPath", s_tempDir);
+ Environment.SetEnvironmentVariable("SuperPMIShimPath", Global.JitPath);
+ Environment.SetEnvironmentVariable("COMPlus_AltJit", "*");
+ Environment.SetEnvironmentVariable("COMPlus_AltJitName", Global.CollectorShimName);
+
+ RunProgramsWhileCollecting(runProgramPath, runProgramArguments);
+
+ // Un-set environment variables
+ Environment.SetEnvironmentVariable("SuperPMIShimLogPath", "");
+ Environment.SetEnvironmentVariable("SuperPMIShimPath", "");
+ Environment.SetEnvironmentVariable("COMPlus_AltJit", "");
+ Environment.SetEnvironmentVariable("COMPlus_AltJitName", "");
+
+ // Did any .mc files get generated?
+ string[] mcFiles = Directory.GetFiles(s_tempDir, "*.mc");
+ if (mcFiles.Length == 0)
+ {
+ throw new SpmiException("no .mc files generated");
+ }
+ }
+
+ // Merge MC files:
+ // mcs -merge <s_baseMchFile> <s_tempDir>\*.mc -recursive
+ private static void MergeMCFiles()
+ {
+ string pattern = Path.Combine(s_tempDir, "*.mc");
+ RunProgram(Global.McsPath, "-merge " + s_baseMchFile + " " + pattern + " -recursive");
+ if (!File.Exists(s_baseMchFile))
+ {
+ throw new SpmiException("file missing: " + s_baseMchFile);
+ }
+
+ if (!Global.SkipCleanup)
+ {
+ // All the individual MC files are no longer necessary, now that we've merged them into the base.mch. Delete them.
+ string[] mcFiles = Directory.GetFiles(s_tempDir, "*.mc");
+ foreach (string mcFile in mcFiles)
+ {
+ SafeFileDelete(mcFile);
+ }
+ }
+ }
+
+ // Create clean MCH file:
+ // <superPmiPath> -p -f <s_baseFailMclFile> <s_baseMchFile> <jitPath>
+ // if <s_baseFailMclFile> is non-empty:
+ // <mcl> -strip <s_baseFailMclFile> <s_baseMchFile> <s_cleanMchFile>
+ // else:
+ // s_cleanMchFile = s_baseMchFile // no need to copy; just change string names (and null out s_baseMchFile so we don't try to delete twice)
+ // del <s_baseFailMclFile>
+ private static void CreateCleanMCHFile()
+ {
+ RunProgram(Global.SuperPmiPath, "-p -f " + s_baseFailMclFile + " " + s_baseMchFile + " " + Global.JitPath);
+
+ if (File.Exists(s_baseFailMclFile) && !String.IsNullOrEmpty(File.ReadAllText(s_baseFailMclFile)))
+ {
+ RunProgram(Global.McsPath, "-strip " + s_baseMchFile + " " + s_cleanMchFile);
+ }
+ else
+ {
+ // Instead of stripping the file, just set s_cleanMchFile = s_baseMchFile and
+ // null out s_baseMchFile so we don't try to delete the same file twice.
+ // Note that we never use s_baseMchFile after this function is called.
+
+ s_cleanMchFile = s_baseMchFile;
+ s_baseMchFile = null;
+ }
+
+ if (!File.Exists(s_cleanMchFile))
+ {
+ throw new SpmiException("file missing: " + s_cleanMchFile);
+ }
+
+ if (!Global.SkipCleanup)
+ {
+ if (File.Exists(s_baseFailMclFile))
+ {
+ SafeFileDelete(s_baseFailMclFile);
+ s_baseFailMclFile = null;
+ }
+
+ // The base file is no longer used (unless there was no cleaning done, in which case
+ // s_baseMchFile has been null-ed and s_cleanMchFile points at the base file).
+ if ((s_baseMchFile != null) && File.Exists(s_baseMchFile))
+ {
+ SafeFileDelete(s_baseMchFile);
+ s_baseMchFile = null;
+ }
+ }
+ }
+
+ // Create a thin unique MCH:
+ // <mcl> -removeDup -thin <s_cleanMchFile> <s_finalMchFile>
+ private static void CreateThinUniqueMCH()
+ {
+ RunProgram(Global.McsPath, "-removeDup -thin " + s_cleanMchFile + " " + s_finalMchFile);
+
+ if (!File.Exists(s_finalMchFile))
+ {
+ throw new SpmiException("file missing: " + s_finalMchFile);
+ }
+
+ if (!Global.SkipCleanup)
+ {
+ // The clean file is no longer used; delete it.
+ if ((s_cleanMchFile != null) && File.Exists(s_cleanMchFile))
+ {
+ SafeFileDelete(s_cleanMchFile);
+ s_cleanMchFile = null;
+ }
+ }
+ }
+
+ // Create a TOC file:
+ // <mcl> -toc <s_finalMchFile>
+ // // check that .mct file was created
+ private static void CreateTOC()
+ {
+ RunProgram(Global.McsPath, "-toc " + s_finalMchFile);
+
+ if (!File.Exists(s_tocFile))
+ {
+ throw new SpmiException("file missing: " + s_tocFile);
+ }
+ }
+
+ // Verify the resulting MCH file is error-free when running superpmi against it with the same JIT used for collection.
+ // <superPmiPath> -p -f <s_finalFailMclFile> <s_finalMchFile> <jitPath>
+ // if <s_finalFailMclFile> is non-empty:
+ // // error!
+ private static void VerifyFinalMCH()
+ {
+ RunProgram(Global.SuperPmiPath, "-p -f " + s_finalFailMclFile + " " + s_finalMchFile + " " + Global.JitPath);
+
+ if (!File.Exists(s_finalFailMclFile) || !String.IsNullOrEmpty(File.ReadAllText(s_finalFailMclFile)))
+ {
+ throw new SpmiException("replay of final file is not error free");
+ }
+
+ if (!Global.SkipCleanup)
+ {
+ if (File.Exists(s_finalFailMclFile))
+ {
+ SafeFileDelete(s_finalFailMclFile);
+ s_finalFailMclFile = null;
+ }
+ }
+ }
+
+ // Cleanup. If we get here due to a failure of some kind, we want to do full cleanup. If we get here as part
+ // of normal shutdown processing, we want to keep the s_finalMchFile and s_tocFile if s_saveFinalMchFile == true.
+ // del <s_baseMchFile>
+ // del <s_cleanMchFile>
+ // del <s_finalMchFile>
+ // del <s_tocFile>
+ // rmdir <s_tempDir>
+ private static void Cleanup()
+ {
+ if (Global.SkipCleanup)
+ return;
+
+ try
+ {
+ if ((s_baseFailMclFile != null) && File.Exists(s_baseFailMclFile))
+ {
+ SafeFileDelete(s_baseFailMclFile);
+ s_baseFailMclFile = null;
+ }
+
+ if ((s_baseMchFile != null) && File.Exists(s_baseMchFile))
+ {
+ SafeFileDelete(s_baseMchFile);
+ s_baseMchFile = null;
+ }
+ if ((s_cleanMchFile != null) && File.Exists(s_cleanMchFile))
+ {
+ SafeFileDelete(s_cleanMchFile);
+ s_cleanMchFile = null;
+ }
+
+ if (!s_saveFinalMchFile)
+ {
+ // Note that if we fail to create the TOC, but we already
+ // successfully created the MCH file, and the user wants to
+ // keep the final result, then we will still keep the final
+ // MCH file. We'll also keep it if the verify pass fails.
+
+ if ((s_finalMchFile != null) && File.Exists(s_finalMchFile))
+ {
+ SafeFileDelete(s_finalMchFile);
+ }
+ if ((s_tocFile != null) && File.Exists(s_tocFile))
+ {
+ SafeFileDelete(s_tocFile);
+ }
+ }
+
+ if ((s_finalFailMclFile != null) && File.Exists(s_finalFailMclFile))
+ {
+ SafeFileDelete(s_finalFailMclFile);
+ s_finalFailMclFile = null;
+ }
+
+ if ((s_tempDir != null) && Directory.Exists(s_tempDir))
+ {
+ Directory.Delete(s_tempDir, /* delete recursively */ true);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine("ERROR during cleanup: " + ex.Message);
+ }
+ }
+
+ public static int Collect(string outputMchPath, string runProgramPath, string runProgramArguments, string tempPath)
+ {
+ // Do a basic SuperPMI collect and validation:
+ // 1. Collect MC files by running a set of sample apps.
+ // 2. Merge the MC files into a single MCH using "mcs -merge *.mc -recursive".
+ // 3. Create a clean MCH by running superpmi over the MCH, and using "mcs -strip" to filter
+ // out any failures (if any).
+ // 4. Create a thin unique MCH by using "mcs -removeDup -thin".
+ // 5. Create a TOC using "mcs -toc".
+ // 6. Verify the resulting MCH file is error-free when running superpmi against it with the
+ // same JIT used for collection.
+ //
+ // MCH files are big. If we don't need them anymore, clean them up right away to avoid
+ // running out of disk space in disk constrained situations.
+
+ string thisTask = "SuperPMI collection and playback";
+ Console.WriteLine(thisTask + " - BEGIN");
+
+ int result = 101; // assume error (!= 100)
+
+ try
+ {
+ CreateTempDirectory(tempPath);
+ ChooseFilePaths(outputMchPath);
+ CollectMCFiles(runProgramPath, runProgramArguments);
+ MergeMCFiles();
+ CreateCleanMCHFile();
+ CreateThinUniqueMCH();
+ CreateTOC();
+ VerifyFinalMCH();
+
+ // Success!
+ result = 100;
+ }
+ catch (SpmiException ex)
+ {
+ Console.Error.WriteLine("ERROR: " + ex.Message);
+ result = 101;
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine("ERROR: unknown exception running collection: " + ex.Message);
+ result = 101;
+ }
+ finally
+ {
+ Cleanup();
+ }
+
+ // Re-display the file delete errors, if any, in case they got lost in the output so far.
+ if (!String.IsNullOrEmpty(s_errors))
+ {
+ Console.Error.WriteLine("Non-fatal errors occurred during processing:");
+ Console.Error.Write(s_errors);
+ }
+
+ if (result == 100)
+ {
+ Console.WriteLine(thisTask + " - SUCCESS");
+ }
+ else
+ {
+ Console.WriteLine(thisTask + " - FAILED");
+ }
+
+ return result;
+ }
+ }
+
+ internal class Program
+ {
+ private static void Usage()
+ {
+ // Unfortunately, under CoreCLR, this just gets is the path to CoreRun.exe:
+ // string thisProgram = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
+
+ string thisProgram = "superpmicollect";
+
+ Console.WriteLine("Usage: {0} [arguments]", thisProgram);
+ Console.WriteLine(" where [arguments] is zero or more of:");
+ Console.WriteLine(" -? | -help : Display this help text.");
+ Console.WriteLine(" -mch <file> : Specify the name of the generated clean/thin/unique MCH file.");
+ Console.WriteLine(" The MCH file is retained (by default, the final MCH file is deleted).");
+ Console.WriteLine(" -run <program> [arguments...] : This program (or script) is invoked to run any number");
+ Console.WriteLine(" of programs during MC collection. All arguments after");
+ Console.WriteLine(" <program> are passed to <program> as its arguments.");
+ Console.WriteLine(" Thus, -run must be the last argument.");
+ Console.WriteLine(" -skipCleanup : Do not delete any intermediate files created during processing.");
+ Console.WriteLine(" -temp <dir> : A newly created, randomly-named, subdirectory of this");
+ Console.WriteLine(" directory will be used to store all temporary files.");
+ Console.WriteLine(" By default, the user temporary directory is used");
+ Console.WriteLine(" (%TEMP% on Windows, /tmp on Unix).");
+ Console.WriteLine(" Since SuperPMI collections generate a lot of data, this option");
+ Console.WriteLine(" is useful if the normal temporary directory doesn't have enough space.");
+ Console.WriteLine("");
+ Console.WriteLine("This program performs a collection of SuperPMI data. With no arguments, a hard-coded list of");
+ Console.WriteLine("programs are run during collection. With the -run argument, the user species which apps are run.");
+ Console.WriteLine("");
+ Console.WriteLine("If -mch is not given, all generated files are deleted, and the result is simply the exit code");
+ Console.WriteLine("indicating whether the collection succeeded. This is useful as a test.");
+ Console.WriteLine("");
+ Console.WriteLine("If the COMPlus_AltJit variable is already set, it is assumed SuperPMI collection is already happening,");
+ Console.WriteLine("and the program exits with success.");
+ Console.WriteLine("");
+ Console.WriteLine("On success, the return code is 100.");
+ }
+
+ private static int Main(string[] args)
+ {
+ string outputMchPath = null;
+ string runProgramPath = null;
+ string runProgramArguments = null;
+ string tempPath = null;
+
+ // Parse arguments
+ if (args.Length > 0)
+ {
+ for (int i = 0; i < args.Length; i++)
+ {
+ switch (args[i])
+ {
+ default:
+ Usage();
+ return 101;
+
+ case "-?":
+ Usage();
+ return 101;
+
+ case "-help":
+ Usage();
+ return 101;
+
+ case "-skipCleanup":
+ Global.SkipCleanup = true;
+ break;
+
+ case "-mch":
+ i++;
+ if (i >= args.Length)
+ {
+ Console.Error.WriteLine("Error: missing argument to -mch");
+ Usage();
+ return 101;
+ }
+ outputMchPath = args[i];
+ if (!outputMchPath.EndsWith(".mch"))
+ {
+ // We need the resulting file to end with ".mch". If the user didn't specify this, then simply add it.
+ // Thus, if the user specifies "-mch foo", we'll generate foo.mch (and TOC file foo.mch.mct).
+ outputMchPath += ".mch";
+ }
+ outputMchPath = Path.GetFullPath(outputMchPath);
+ break;
+
+ case "-run":
+ i++;
+ if (i >= args.Length)
+ {
+ Console.Error.WriteLine("Error: missing argument to -run");
+ Usage();
+ return 101;
+ }
+ runProgramPath = Path.GetFullPath(args[i]);
+ if (!File.Exists(runProgramPath))
+ {
+ Console.Error.WriteLine("Error: couldn't find program {0}", runProgramPath);
+ return 101;
+ }
+ // The rest of the arguments, if any, are passed as arguments to the run program.
+ i++;
+ if (i < args.Length)
+ {
+ string[] runArgumentsArray = new string[args.Length - i];
+ for (int j = 0; i < args.Length; i++, j++)
+ {
+ runArgumentsArray[j] = args[i];
+ }
+ runProgramArguments = string.Join(" ", runArgumentsArray);
+ }
+ break;
+
+ case "-temp":
+ i++;
+ if (i >= args.Length)
+ {
+ Console.Error.WriteLine("Error: missing argument to -temp");
+ Usage();
+ return 101;
+ }
+ tempPath = args[i];
+ break;
+ }
+ }
+ }
+
+ // Done with argument parsing.
+
+ string altjitvar = System.Environment.GetEnvironmentVariable("COMPlus_AltJit");
+ if (!String.IsNullOrEmpty(altjitvar))
+ {
+ // Someone already has the COMPlus_AltJit variable set. We don't want to override
+ // that. Perhaps someone is already doing a SuperPMI collection and invokes this
+ // program as part of a full test path in which this program exists.
+
+ Console.WriteLine("COMPlus_AltJit already exists: skipping SuperPMI collection and returning success");
+ return 100;
+ }
+
+ int result;
+
+ try
+ {
+ Global.Initialize();
+ result = SuperPMICollectionClass.Collect(outputMchPath, runProgramPath, runProgramArguments, tempPath);
+ }
+ catch (SpmiException ex)
+ {
+ Console.Error.WriteLine("ERROR: " + ex.Message);
+ result = 101;
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine("ERROR: unknown exception running collection: " + ex.Message);
+ result = 101;
+ }
+
+ return result;
+ }
+ }
+
+}
diff --git a/tests/src/JIT/superpmi/superpmicollect.csproj b/tests/src/JIT/superpmi/superpmicollect.csproj
new file mode 100644
index 0000000000..77463d534c
--- /dev/null
+++ b/tests/src/JIT/superpmi/superpmicollect.csproj
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <PropertyGroup>
+ <DebugType>Full</DebugType>
+ <Optimize>False</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="superpmicollect.cs" />
+
+ <!-- Add project references to those tests that we run as part of the test -->
+ <!-- Don't do this now; it simply builds and copies these EXE/PDB, but not the .cmd/.sh, which we use.
+ <ProjectReference Include="..\Performance\CodeQuality\Roslyn\CscBench.csproj" />
+ <ProjectReference Include="..\Methodical\fp\exgen\10w5d_cs_do.csproj" />
+ <ProjectReference Include="..\Generics\Coverage\chaos65204782cs.csproj" />
+ -->
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="$(JitPackagesConfigFileDirectory)extra\project.json" />
+ <None Include="app.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <PropertyGroup>
+ <ProjectJson>$(JitPackagesConfigFileDirectory)extra\project.json</ProjectJson>
+ <ProjectLockJson>$(JitPackagesConfigFileDirectory)extra\project.lock.json</ProjectLockJson>
+ </PropertyGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+ </PropertyGroup>
+</Project>