diff options
Diffstat (limited to 'src/inc/corjit.h')
-rw-r--r-- | src/inc/corjit.h | 575 |
1 files changed, 575 insertions, 0 deletions
diff --git a/src/inc/corjit.h b/src/inc/corjit.h new file mode 100644 index 0000000000..e4deabd0e1 --- /dev/null +++ b/src/inc/corjit.h @@ -0,0 +1,575 @@ +// 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. + +/*****************************************************************************\ +* * +* CorJit.h - EE / JIT interface * +* * +* Version 1.0 * +******************************************************************************* +* * +* * +* * +\*****************************************************************************/ + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE +// +// The JIT/EE interface is versioned. By "interface", we mean mean any and all communication between the +// JIT and the EE. Any time a change is made to the interface, the JIT/EE interface version identifier +// must be updated. See code:JITEEVersionIdentifier for more information. +// +// NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE +// +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef _COR_JIT_H_ +#define _COR_JIT_H_ + +#include <corinfo.h> + +#include <stdarg.h> + +#define CORINFO_STACKPROBE_DEPTH 256*sizeof(UINT_PTR) // Guaranteed stack until an fcall/unmanaged + // code can set up a frame. Please make sure + // this is less than a page. This is due to + // 2 reasons: + // + // If we need to probe more than a page + // size, we need one instruction per page + // (7 bytes per instruction) + // + // The JIT wants some safe space so it doesn't + // have to put a probe on every call site. It achieves + // this by probing n bytes more than CORINFO_STACKPROBE_DEPTH + // If it hasn't used more than n for its own stuff, it + // can do a call without doing any other probe + // + // In any case, we do really expect this define to be + // small, as setting up a frame should be only pushing + // a couple of bytes on the stack + // + // There is a compile time assert + // in the x86 jit to protect you from this + // + + + + +/*****************************************************************************/ + // These are error codes returned by CompileMethod +enum CorJitResult +{ + // Note that I dont use FACILITY_NULL for the facility number, + // we may want to get a 'real' facility number + CORJIT_OK = NO_ERROR, + CORJIT_BADCODE = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 1), + CORJIT_OUTOFMEM = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 2), + CORJIT_INTERNALERROR = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 3), + CORJIT_SKIPPED = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 4), + CORJIT_RECOVERABLEERROR = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 5), +}; + + +/* values for flags in compileMethod */ + +enum CorJitFlag +{ + CORJIT_FLG_SPEED_OPT = 0x00000001, + CORJIT_FLG_SIZE_OPT = 0x00000002, + CORJIT_FLG_DEBUG_CODE = 0x00000004, // generate "debuggable" code (no code-mangling optimizations) + CORJIT_FLG_DEBUG_EnC = 0x00000008, // We are in Edit-n-Continue mode + CORJIT_FLG_DEBUG_INFO = 0x00000010, // generate line and local-var info + CORJIT_FLG_MIN_OPT = 0x00000020, // disable all jit optimizations (not necesarily debuggable code) + CORJIT_FLG_GCPOLL_CALLS = 0x00000040, // Emit calls to JIT_POLLGC for thread suspension. + CORJIT_FLG_MCJIT_BACKGROUND = 0x00000080, // Calling from multicore JIT background thread, do not call JitComplete + + CORJIT_FLG_UNUSED1 = 0x00000100, + +#if defined(_TARGET_X86_) + + CORJIT_FLG_PINVOKE_RESTORE_ESP = 0x00000200, // Restore ESP after returning from inlined PInvoke + CORJIT_FLG_TARGET_P4 = 0x00000400, + CORJIT_FLG_USE_FCOMI = 0x00000800, // Generated code may use fcomi(p) instruction + CORJIT_FLG_USE_CMOV = 0x00001000, // Generated code may use cmov instruction + CORJIT_FLG_USE_SSE2 = 0x00002000, // Generated code may use SSE-2 instructions + +#elif defined(_TARGET_AMD64_) + + CORJIT_FLG_USE_SSE3_4 = 0x00000200, + CORJIT_FLG_USE_AVX = 0x00000400, + CORJIT_FLG_USE_AVX2 = 0x00000800, + CORJIT_FLG_USE_AVX_512 = 0x00001000, + CORJIT_FLG_FEATURE_SIMD = 0x00002000, + +#else // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) + + CORJIT_FLG_UNUSED2 = 0x00000200, + CORJIT_FLG_UNUSED3 = 0x00000400, + CORJIT_FLG_UNUSED4 = 0x00000800, + CORJIT_FLG_UNUSED5 = 0x00001000, + CORJIT_FLG_UNUSED6 = 0x00002000, + +#endif // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) + + CORJIT_FLG_MAKEFINALCODE = 0x00008000, // Use the final code generator, i.e., not the interpreter. + CORJIT_FLG_READYTORUN = 0x00010000, // Use version-resilient code generation + + CORJIT_FLG_PROF_ENTERLEAVE = 0x00020000, // Instrument prologues/epilogues + CORJIT_FLG_PROF_REJIT_NOPS = 0x00040000, // Insert NOPs to ensure code is re-jitable + CORJIT_FLG_PROF_NO_PINVOKE_INLINE + = 0x00080000, // Disables PInvoke inlining + CORJIT_FLG_SKIP_VERIFICATION = 0x00100000, // (lazy) skip verification - determined without doing a full resolve. See comment below + CORJIT_FLG_PREJIT = 0x00200000, // jit or prejit is the execution engine. + CORJIT_FLG_RELOC = 0x00400000, // Generate relocatable code + CORJIT_FLG_IMPORT_ONLY = 0x00800000, // Only import the function + CORJIT_FLG_IL_STUB = 0x01000000, // method is an IL stub + CORJIT_FLG_PROCSPLIT = 0x02000000, // JIT should separate code into hot and cold sections + CORJIT_FLG_BBINSTR = 0x04000000, // Collect basic block profile information + CORJIT_FLG_BBOPT = 0x08000000, // Optimize method based on profile information + CORJIT_FLG_FRAMED = 0x10000000, // All methods have an EBP frame + CORJIT_FLG_ALIGN_LOOPS = 0x20000000, // add NOPs before loops to align them at 16 byte boundaries + CORJIT_FLG_PUBLISH_SECRET_PARAM= 0x40000000, // JIT must place stub secret param into local 0. (used by IL stubs) + CORJIT_FLG_GCPOLL_INLINE = 0x80000000, // JIT must inline calls to GCPoll when possible + +#if COR_JIT_EE_VERSION > 460 + CORJIT_FLG_CALL_GETJITFLAGS = 0xffffffff, // Indicates that the JIT should retrieve flags in the form of a + // pointer to a CORJIT_FLAGS value via ICorJitInfo::getJitFlags(). +#endif +}; + +enum CorJitFlag2 +{ + CORJIT_FLG2_SAMPLING_JIT_BACKGROUND = 0x00000001, // JIT is being invoked as a result of stack sampling for hot methods in the background +#if COR_JIT_EE_VERSION > 460 + CORJIT_FLG2_USE_PINVOKE_HELPERS = 0x00000002, // The JIT should use the PINVOKE_{BEGIN,END} helpers instead of emitting inline transitions + CORJIT_FLG2_REVERSE_PINVOKE = 0x00000004, // The JIT should insert REVERSE_PINVOKE_{ENTER,EXIT} helpers into method prolog/epilog + CORJIT_FLG2_DESKTOP_QUIRKS = 0x00000008, // The JIT should generate desktop-quirk-compatible code +#endif +}; + +struct CORJIT_FLAGS +{ + unsigned corJitFlags; // Values are from CorJitFlag + unsigned corJitFlags2; // Values are from CorJitFlag2 +}; + +/***************************************************************************** +Here is how CORJIT_FLG_SKIP_VERIFICATION should be interepreted. +Note that even if any method is inlined, it need not be verified. + +if (CORJIT_FLG_SKIP_VERIFICATION is passed in to ICorJitCompiler::compileMethod()) +{ + No verification needs to be done. + Just compile the method, generating unverifiable code if necessary +} +else +{ + switch(ICorMethodInfo::isInstantiationOfVerifiedGeneric()) + { + case INSTVER_NOT_INSTANTIATION: + + // + // Non-generic case, or open generic instantiation + // + + switch(canSkipMethodVerification()) + { + case CORINFO_VERIFICATION_CANNOT_SKIP: + { + ICorMethodInfo::initConstraintsForVerification(&circularConstraints) + if (circularConstraints) + { + Just emit code to call CORINFO_HELP_VERIFICATION + The IL will not be compiled + } + else + { + Verify the method. + if (unverifiable code is detected) + { + In place of branches with unverifiable code, emit code to call CORINFO_HELP_VERIFICATION + Mark the method (and any of its instantiations) as unverifiable + } + Compile the rest of the verifiable code + } + } + + case CORINFO_VERIFICATION_CAN_SKIP: + { + No verification needs to be done. + Just compile the method, generating unverifiable code if necessary + } + + case CORINFO_VERIFICATION_RUNTIME_CHECK: + { + ICorMethodInfo::initConstraintsForVerification(&circularConstraints) + if (circularConstraints) + { + Just emit code to call CORINFO_HELP_VERIFICATION + The IL will not be compiled + + TODO: This could be changed to call CORINFO_HELP_VERIFICATION_RUNTIME_CHECK + } + else + { + Verify the method. + if (unverifiable code is detected) + { + In the prolog, emit code to call CORINFO_HELP_VERIFICATION_RUNTIME_CHECK + Mark the method (and any of its instantiations) as unverifiable + } + Compile the method, generating unverifiable code if necessary + } + } + case CORINFO_VERIFICATION_DONT_JIT: + { + ICorMethodInfo::initConstraintsForVerification(&circularConstraints) + if (circularConstraints) + { + Just emit code to call CORINFO_HELP_VERIFICATION + The IL will not be compiled + } + else + { + Verify the method. + if (unverifiable code is detected) + { + Fail the jit + } + } + } + } + + case INSTVER_GENERIC_PASSED_VERIFICATION: + { + This cannot ever happen because the VM would pass in CORJIT_FLG_SKIP_VERIFICATION. + } + + case INSTVER_GENERIC_FAILED_VERIFICATION: + + switch(canSkipMethodVerification()) + { + case CORINFO_VERIFICATION_CANNOT_SKIP: + { + This cannot be supported because the compiler does not know which branches should call CORINFO_HELP_VERIFICATION. + The CLR will throw a VerificationException instead of trying to compile this method + } + + case CORINFO_VERIFICATION_CAN_SKIP: + { + This cannot ever happen because the CLR would pass in CORJIT_FLG_SKIP_VERIFICATION. + } + + case CORINFO_VERIFICATION_RUNTIME_CHECK: + { + No verification needs to be done. + In the prolog, emit code to call CORINFO_HELP_VERIFICATION_RUNTIME_CHECK + Compile the method, generating unverifiable code if necessary + } + case CORINFO_VERIFICATION_DONT_JIT: + { + Fail the jit + } + } + } +} + +*/ + +/*****************************************************************************/ +// These are flags passed to ICorJitInfo::allocMem +// to guide the memory allocation for the code, readonly data, and read-write data +enum CorJitAllocMemFlag +{ + CORJIT_ALLOCMEM_DEFAULT_CODE_ALIGN = 0x00000000, // The code will be use the normal alignment + CORJIT_ALLOCMEM_FLG_16BYTE_ALIGN = 0x00000001, // The code will be 16-byte aligned + CORJIT_ALLOCMEM_FLG_RODATA_16BYTE_ALIGN = 0x00000002, // The read-only data will be 16-byte aligned +}; + +inline CorJitAllocMemFlag operator |(CorJitAllocMemFlag a, CorJitAllocMemFlag b) +{ + return static_cast<CorJitAllocMemFlag>(static_cast<int>(a) | static_cast<int>(b)); +} + +enum CorJitFuncKind +{ + CORJIT_FUNC_ROOT, // The main/root function (always id==0) + CORJIT_FUNC_HANDLER, // a funclet associated with an EH handler (finally, fault, catch, filter handler) + CORJIT_FUNC_FILTER // a funclet associated with an EH filter +}; + +// We have a performance-investigation mode (defined by the FEATURE_USE_ASM_GC_WRITE_BARRIERS and +// FEATURE_COUNT_GC_WRITE_BARRIER preprocessor symbols) in which the JIT adds an argument of this +// enumeration to checked write barrier calls in order to classify them. +enum CheckedWriteBarrierKinds { + CWBKind_Unclassified, // Not one of the ones below. + CWBKind_RetBuf, // Store through a return buffer pointer argument. + CWBKind_ByRefArg, // Store through a by-ref argument (not an implicit return buffer). + CWBKind_OtherByRefLocal, // Store through a by-ref local variable. + CWBKind_AddrOfLocal, // Store through the address of a local (arguably a bug that this happens at all). +}; + +#if COR_JIT_EE_VERSION > 460 + +#include "corjithost.h" + +extern "C" void __stdcall jitStartup(ICorJitHost* host); + +#endif + +class ICorJitCompiler; +class ICorJitInfo; +struct IEEMemoryManager; + +extern "C" ICorJitCompiler* __stdcall getJit(); + +// #EEToJitInterface +// ICorJitCompiler is the interface that the EE uses to get IL bytecode converted to native code. Note that +// to accomplish this the JIT has to call back to the EE to get symbolic information. The code:ICorJitInfo +// type passed as 'comp' to compileMethod is the mechanism to get this information. This is often the more +// interesting interface. +// +// +class ICorJitCompiler +{ +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 + virtual CorJitResult __stdcall compileMethod ( + ICorJitInfo *comp, /* IN */ + struct CORINFO_METHOD_INFO *info, /* IN */ + unsigned /* code:CorJitFlag */ flags, /* IN */ + BYTE **nativeEntry, /* OUT */ + ULONG *nativeSizeOfCode /* OUT */ + ) = 0; + + // 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) + virtual void clearCache() = 0; + virtual BOOL isCacheCleanupRequired() = 0; + + // Do any appropriate work at process shutdown. Default impl is to do nothing. + virtual 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. + // + virtual void getVersionIdentifier( + GUID* versionIdentifier /* OUT */ + ) = 0; + + // 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). + virtual 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. + virtual void setRealJit(ICorJitCompiler* realJitCompiler) { } + +}; + +//------------------------------------------------------------------------------------------ +// #JitToEEInterface +// +// ICorJitInfo is the main interface that the JIT uses to call back to the EE and get information. It is +// the companion to code:ICorJitCompiler#EEToJitInterface. The concrete implementation of this in the +// runtime is the code:CEEJitInfo type. There is also a version of this for the NGEN case. +// +// See code:ICorMethodInfo#EEJitContractDetails for subtle conventions used by this interface. +// +// There is more information on the JIT in the book of the runtime entry +// http://devdiv/sites/CLR/Product%20Documentation/2.0/BookOfTheRuntime/JIT/JIT%20Design.doc +// +class ICorJitInfo : public ICorDynamicInfo +{ +public: + // return memory manager that the JIT can use to allocate a regular memory + virtual IEEMemoryManager* getMemoryManager() = 0; + + // get a block of memory for the code, readonly data, and read-write data + virtual 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 */ + ) = 0; + + // 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. + // + virtual void reserveUnwindInfo ( + BOOL isFunclet, /* IN */ + BOOL isColdCode, /* IN */ + ULONG unwindSize /* IN */ + ) = 0; + + // 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) + // + virtual void allocUnwindInfo ( + BYTE * pHotCode, /* IN */ + BYTE * pColdCode, /* IN */ + ULONG startOffset, /* IN */ + ULONG endOffset, /* IN */ + ULONG unwindSize, /* IN */ + BYTE * pUnwindBlock, /* IN */ + CorJitFuncKind funcKind /* IN */ + ) = 0; + + // 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 + virtual void * allocGCInfo ( + size_t size /* IN */ + ) = 0; + + virtual void yieldExecution() = 0; + + // 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. + virtual void setEHcount ( + unsigned cEH /* IN */ + ) = 0; + + // 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. + virtual void setEHinfo ( + unsigned EHnumber, /* IN */ + const CORINFO_EH_CLAUSE *clause /* IN */ + ) = 0; + + // 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 + virtual BOOL logMsg(unsigned level, const char* fmt, va_list args) = 0; + + // do an assert. will return true if the code should retry (DebugBreak) + // returns false, if the assert should be igored. + virtual int doAssert(const char* szFile, int iLine, const char* szExpr) = 0; + + virtual void reportFatalError(CorJitResult result) = 0; + + 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. + virtual HRESULT allocBBProfileBuffer ( + ULONG count, // The number of basic blocks that we have + ProfileBuffer ** profileBuffer + ) = 0; + + // 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. + virtual HRESULT getBBProfileData( + CORINFO_METHOD_HANDLE ftnHnd, + ULONG * count, // The number of basic blocks that we have + ProfileBuffer ** profileBuffer, + ULONG * numRuns + ) = 0; + + // 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. + virtual void recordCallSite( + ULONG instrOffset, /* IN */ + CORINFO_SIG_INFO * callSig, /* IN */ + CORINFO_METHOD_HANDLE methodHandle /* IN */ + ) = 0; + + // A relocation is recorded if we are pre-jitting. + // A jump thunk may be inserted if we are jitting + virtual void recordRelocation( + void * location, /* IN */ + void * target, /* IN */ + WORD fRelocType, /* IN */ + WORD slotNum = 0, /* IN */ + INT32 addlDelta = 0 /* IN */ + ) = 0; + + virtual WORD getRelocTypeHint(void * target) = 0; + + // A callback to identify the range of address known to point to + // compiler-generated native entry points that call back into + // MSIL. + virtual void getModuleNativeEntryPointRange( + void ** pStart, /* OUT */ + void ** pEnd /* OUT */ + ) = 0; + + // 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. + // + virtual DWORD getExpectedTargetArchitecture() = 0; + +#if COR_JIT_EE_VERSION > 460 + // Fetches extended flags for a particular compilation instance. Returns + // the number of bytes written to the provided buffer. + virtual 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. */ + ) = 0; +#endif +}; + +/**********************************************************************************/ +#endif // _COR_CORJIT_H_ |