Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
// Copyright (c) 2012 DotNetAnywhere
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "Compat.h"
#include "Sys.h"
#include "JIT.h"
#include "JIT_OpCodes.h"
#include "CIL_OpCodes.h"
#include "MetaData.h"
#include "Types.h"
#include "Type.h"
#include "InternalCall.h"
#include "Heap.h"
#include "PInvoke.h"
#define CorILMethod_TinyFormat 0x02
#define CorILMethod_MoreSects 0x08
#define CorILMethod_Sect_EHTable 0x01
#define CorILMethod_Sect_FatFormat 0x40
#define CorILMethod_Sect_MoreSects 0x80
#define DYNAMIC_OK 0x100
#define DYNAMIC_JUMP_TARGET 0x200
#define DYNAMIC_EX_START 0x400
#define DYNAMIC_EX_END 0x800
#define DYNAMIC_BYTE_COUNT_MASK 0xff
typedef struct tOps_ tOps;
struct tOps_ {
U32 *p;
U32 capacity;
U32 ofs;
};
typedef struct tTypeStack_ tTypeStack;
struct tTypeStack_ {
tMD_TypeDef **ppTypes;
U32 ofs;
U32 maxBytes; // The max size of the stack in bytes
};
#define InitOps(ops_, initialCapacity) ops_.capacity = initialCapacity; ops_.ofs = 0; ops_.p = malloc((initialCapacity) * sizeof(I32));
#define DeleteOps(ops_) free(ops_.p)
// Turn this into a MACRO at some point?
static U32 Translate(U32 op, U32 getDynamic) {
if (op >= JIT_OPCODE_MAXNUM) {
Crash("Illegal opcode: %d", op);
}
if (jitCodeInfo[op].pEnd == NULL) {
Crash("Opcode not available: 0x%08x", op);
}
if (getDynamic) {
return (U32)jitCodeInfo[op].isDynamic;
} else {
return (U32)jitCodeInfo[op].pStart;
}
}
#ifdef GEN_COMBINED_OPCODES
#define PushU32(v) PushU32_(&ops, (U32)(v)); PushU32_(&isDynamic, 0)
#define PushI32(v) PushU32_(&ops, (U32)(v)); PushU32_(&isDynamic, 0)
#define PushFloat(v) convFloat.f=(float)(v); PushU32_(&ops, convFloat.u32); PushU32_(&isDynamic, 0)
#define PushDouble(v) convDouble.d=(double)(v); PushU32_(&ops, convDouble.u32.a); PushU32_(&ops, convDouble.u32.b); PushU32_(&isDynamic, 0); PushU32_(&isDynamic, 0)
#define PushPTR(ptr) PushU32_(&ops, (U32)(ptr)); PushU32_(&isDynamic, 0)
#define PushOp(op) PushU32_(&ops, Translate((U32)(op), 0)); PushU32_(&isDynamic, Translate((U32)(op), 1))
#define PushOpParam(op, param) PushOp(op); PushU32_(&ops, (U32)(param)); PushU32_(&isDynamic, 0)
#else
#define PushU32(v) PushU32_(&ops, (U32)(v))
#define PushI32(v) PushU32_(&ops, (U32)(v))
#define PushFloat(v) convFloat.f=(float)(v); PushU32_(&ops, convFloat.u32)
#define PushDouble(v) convDouble.d=(double)(v); PushU32_(&ops, convDouble.u32.a); PushU32_(&ops, convDouble.u32.b)
#define PushPTR(ptr) PushU32_(&ops, (U32)(ptr))
#define PushOp(op) PushU32_(&ops, Translate((U32)(op), 0))
#define PushOpParam(op, param) PushOp(op); PushU32_(&ops, (U32)(param))
#endif
#define PushBranch() PushU32_(&branchOffsets, ops.ofs)
#define PushStackType(type) PushStackType_(&typeStack, type);
#define PopStackType() (typeStack.ppTypes[--typeStack.ofs])
#define PopStackTypeDontCare() typeStack.ofs--
#define PopStackTypeMulti(number) typeStack.ofs -= number
#define PopStackTypeAll() typeStack.ofs = 0;
#define MayCopyTypeStack() if (u32Value > cilOfs) ppTypeStacks[u32Value] = DeepCopyTypeStack(&typeStack)
static void PushStackType_(tTypeStack *pTypeStack, tMD_TypeDef *pType) {
U32 i, size;
MetaData_Fill_TypeDef(pType, NULL, NULL);
pTypeStack->ppTypes[pTypeStack->ofs++] = pType;
// Count current stack size in bytes
size = 0;
for (i=0; i<pTypeStack->ofs; i++) {
size += pTypeStack->ppTypes[i]->stackSize;
}
if (size > pTypeStack->maxBytes) {
pTypeStack->maxBytes = size;
}
//printf("Stack ofs = %d; Max stack size: %d (0x%x)\n", pTypeStack->ofs, size, size);
}
static void PushU32_(tOps *pOps, U32 v) {
if (pOps->ofs >= pOps->capacity) {
pOps->capacity <<= 1;
// printf("a.pOps->p = 0x%08x size=%d\n", pOps->p, pOps->capacity * sizeof(U32));
pOps->p = realloc(pOps->p, pOps->capacity * sizeof(U32));
}
pOps->p[pOps->ofs++] = v;
}
static U32 GetUnalignedU32(U8 *pCIL, U32 *pCILOfs) {
U32 a,b,c,d;
a = pCIL[(*pCILOfs)++];
b = pCIL[(*pCILOfs)++];
c = pCIL[(*pCILOfs)++];
d = pCIL[(*pCILOfs)++];
return a | (b << 8) | (c << 16) | (d << 24);
}
static tTypeStack* DeepCopyTypeStack(tTypeStack *pToCopy) {
tTypeStack *pCopy;
pCopy = TMALLOC(tTypeStack);
pCopy->maxBytes = pToCopy->maxBytes;
pCopy->ofs = pToCopy->ofs;
if (pToCopy->ofs > 0) {
pCopy->ppTypes = malloc(pToCopy->ofs * sizeof(tMD_TypeDef*));
memcpy(pCopy->ppTypes, pToCopy->ppTypes, pToCopy->ofs * sizeof(tMD_TypeDef*));
} else {
pCopy->ppTypes = NULL;
}
return pCopy;
}
static void RestoreTypeStack(tTypeStack *pMainStack, tTypeStack *pCopyFrom) {
// This does not effect maxBytes, as the current value will always be equal
// or greater than the value being copied from.
if (pCopyFrom == NULL) {
pMainStack->ofs = 0;
} else {
pMainStack->ofs = pCopyFrom->ofs;
if (pCopyFrom->ppTypes != NULL) {
memcpy(pMainStack->ppTypes, pCopyFrom->ppTypes, pCopyFrom->ofs * sizeof(tMD_TypeDef*));
}
}
}
#ifdef GEN_COMBINED_OPCODES
static U32 FindOpCode(void *pAddr) {
U32 i;
for (i=0; i<JIT_OPCODE_MAXNUM; i++) {
if (jitCodeInfo[i].pStart == pAddr) {
return i;
}
}
Crash("Cannot find opcode for address: 0x%08x", (U32)pAddr);
FAKE_RETURN;
}
static U32 combinedMemSize = 0;
static U32 GenCombined(tOps *pOps, tOps *pIsDynamic, U32 startOfs, U32 count, U32 *pCombinedSize, void **ppMem) {
U32 memSize;
U32 ofs;
void *pCombined;
U32 opCopyToOfs;
U32 shrinkOpsBy;
U32 goNextSize = (U32)((char*)jitCodeGoNext.pEnd - (char*)jitCodeGoNext.pStart);
// Get length of final combined code chunk
memSize = 0;
for (ofs=0; ofs < count; ofs++) {
U32 opcode = FindOpCode((void*)pOps->p[startOfs + ofs]);
U32 size = (U32)((char*)jitCodeInfo[opcode].pEnd - (char*)jitCodeInfo[opcode].pStart);
memSize += size;
ofs += (pIsDynamic->p[startOfs + ofs] & DYNAMIC_BYTE_COUNT_MASK) >> 2;
}
// Add length of GoNext code
memSize += goNextSize;
pCombined = malloc(memSize);
*ppMem = pCombined;
combinedMemSize += memSize;
*pCombinedSize = memSize;
//log_f(0, "Combined JIT size: %d\n", combinedMemSize);
// Copy the bits of code into place
memSize = 0;
opCopyToOfs = 1;
for (ofs=0; ofs < count; ofs++) {
U32 extraOpBytes;
U32 opcode = FindOpCode((void*)pOps->p[startOfs + ofs]);
U32 size = (U32)((char*)jitCodeInfo[opcode].pEnd - (char*)jitCodeInfo[opcode].pStart);
memcpy((char*)pCombined + memSize, jitCodeInfo[opcode].pStart, size);
memSize += size;
extraOpBytes = pIsDynamic->p[startOfs + ofs] & DYNAMIC_BYTE_COUNT_MASK;
memmove(&pOps->p[startOfs + opCopyToOfs], &pOps->p[startOfs + ofs + 1], extraOpBytes);
opCopyToOfs += extraOpBytes >> 2;
ofs += extraOpBytes >> 2;
}
shrinkOpsBy = ofs - opCopyToOfs;
// Add GoNext code
memcpy((char*)pCombined + memSize, jitCodeGoNext.pStart, goNextSize);
pOps->p[startOfs] = (U32)pCombined;
return shrinkOpsBy;
}
#endif
static U32* JITit(tMD_MethodDef *pMethodDef, U8 *pCIL, U32 codeSize, tParameter *pLocals, tJITted *pJITted, U32 genCombinedOpcodes) {
U32 maxStack = pJITted->maxStack;
U32 i;
U32 cilOfs;
tOps ops; // The JITted op-codes
tOps branchOffsets; // Filled with all the branch instructions that need offsets fixing
U32 *pJITOffsets; // To store the JITted code offset of each CIL byte.
// Only CIL bytes that are the first byte of an instruction will have meaningful data
tTypeStack **ppTypeStacks; // To store the evaluation stack state for forward jumps
U32 *pFinalOps;
tMD_TypeDef *pStackType;
tTypeStack typeStack;
#ifdef GEN_COMBINED_OPCODES
tOps isDynamic;
#endif
I32 i32Value;
U32 u32Value, u32Value2, ofs;
uConvFloat convFloat;
uConvDouble convDouble;
tMD_TypeDef *pTypeA, *pTypeB;
PTR pMem;
tMetaData *pMetaData;
pMetaData = pMethodDef->pMetaData;
pJITOffsets = malloc(codeSize * sizeof(U32));
// + 1 to handle cases where the stack is being restored at the last instruction in a method
ppTypeStacks = malloc((codeSize + 1) * sizeof(tTypeStack*));
memset(ppTypeStacks, 0, (codeSize + 1) * sizeof(tTypeStack*));
typeStack.maxBytes = 0;
typeStack.ofs = 0;
typeStack.ppTypes = malloc(maxStack * sizeof(tMD_TypeDef*));
// Set up all exception 'catch' blocks with the correct stack information,
// So they'll have just the exception type on the stack when entered
for (i=0; i<pJITted->numExceptionHandlers; i++) {
tExceptionHeader *pEx;
pEx = &pJITted->pExceptionHeaders[i];
if (pEx->flags == COR_ILEXCEPTION_CLAUSE_EXCEPTION) {
tTypeStack *pTypeStack;
ppTypeStacks[pEx->handlerStart] = pTypeStack = TMALLOC(tTypeStack);
pTypeStack->maxBytes = 4;
pTypeStack->ofs = 1;
pTypeStack->ppTypes = TMALLOC(tMD_TypeDef*);
pTypeStack->ppTypes[0] = pEx->u.pCatchTypeDef;
}
}
InitOps(ops, 32);
InitOps(branchOffsets, 16);
#ifdef GEN_COMBINED_OPCODES
InitOps(isDynamic, 32);
#endif
cilOfs = 0;
do {
U8 op;
// Set the JIT offset for this CIL opcode
pJITOffsets[cilOfs] = ops.ofs;
op = pCIL[cilOfs++];
//printf("Opcode: 0x%02x\n", op);
switch (op) {
case CIL_NOP:
PushOp(JIT_NOP);
break;
case CIL_LDNULL:
PushOp(JIT_LOAD_NULL);
PushStackType(types[TYPE_SYSTEM_OBJECT]);
break;
case CIL_DUP:
pStackType = PopStackType();
PushStackType(pStackType);
PushStackType(pStackType);
switch (pStackType->stackSize) {
case 4:
PushOp(JIT_DUP_4);
break;
case 8:
PushOp(JIT_DUP_8);
break;
default:
PushOpParam(JIT_DUP_GENERAL, pStackType->stackSize);
break;
}
break;
case CIL_POP:
pStackType = PopStackType();
if (pStackType->stackSize == 4) {
PushOp(JIT_POP_4);
} else {
PushOpParam(JIT_POP, pStackType->stackSize);
}
break;
case CIL_LDC_I4_M1:
case CIL_LDC_I4_0:
case CIL_LDC_I4_1:
case CIL_LDC_I4_2:
case CIL_LDC_I4_3:
case CIL_LDC_I4_4:
case CIL_LDC_I4_5:
case CIL_LDC_I4_6:
case CIL_LDC_I4_7:
case CIL_LDC_I4_8:
i32Value = (I8)op - (I8)CIL_LDC_I4_0;
goto cilLdcI4;
case CIL_LDC_I4_S:
i32Value = (I8)pCIL[cilOfs++];
goto cilLdcI4;
case CIL_LDC_I4:
i32Value = (I32)GetUnalignedU32(pCIL, &cilOfs);
cilLdcI4:
if (i32Value >= -1 && i32Value <= 2) {
PushOp(JIT_LOAD_I4_0 + i32Value);
} else {
PushOp(JIT_LOAD_I32);
PushI32(i32Value);
}
PushStackType(types[TYPE_SYSTEM_INT32]);
break;
case CIL_LDC_I8:
PushOp(JIT_LOAD_I64);
u32Value = GetUnalignedU32(pCIL, &cilOfs);
PushU32(u32Value);
u32Value = GetUnalignedU32(pCIL, &cilOfs);
PushU32(u32Value);
PushStackType(types[TYPE_SYSTEM_INT64]);
break;
case CIL_LDC_R4:
convFloat.u32 = GetUnalignedU32(pCIL, &cilOfs);
PushStackType(types[TYPE_SYSTEM_SINGLE]);
PushOp(JIT_LOAD_F32);
PushFloat(convFloat.f);
break;
case CIL_LDC_R8:
convDouble.u32.a = GetUnalignedU32(pCIL, &cilOfs);
convDouble.u32.b = GetUnalignedU32(pCIL, &cilOfs);
PushStackType(types[TYPE_SYSTEM_DOUBLE]);
PushOp(JIT_LOAD_F64);
PushDouble(convDouble.d);
break;
case CIL_LDARG_0:
case CIL_LDARG_1:
case CIL_LDARG_2:
case CIL_LDARG_3:
u32Value = op - CIL_LDARG_0;
goto cilLdArg;
case CIL_LDARG_S:
u32Value = pCIL[cilOfs++];
cilLdArg:
pStackType = pMethodDef->pParams[u32Value].pTypeDef;
ofs = pMethodDef->pParams[u32Value].offset;
if (pStackType->stackSize == 4 && ofs < 32) {
PushOp(JIT_LOADPARAMLOCAL_0 + (ofs >> 2));
} else {
PushOpParam(JIT_LOADPARAMLOCAL_TYPEID + pStackType->stackType, ofs);
// if it's a valuetype then push the TypeDef of it afterwards
if (pStackType->stackType == EVALSTACK_VALUETYPE) {
PushPTR(pStackType);
}
}
PushStackType(pStackType);
break;
case CIL_LDARGA_S:
// Get the argument number to load the address of
u32Value = pCIL[cilOfs++];
PushOpParam(JIT_LOAD_PARAMLOCAL_ADDR, pMethodDef->pParams[u32Value].offset);
PushStackType(types[TYPE_SYSTEM_INTPTR]);
break;
case CIL_STARG_S:
// Get the argument number to store the arg of
u32Value = pCIL[cilOfs++];
pStackType = PopStackType();
ofs = pMethodDef->pParams[u32Value].offset;
if (pStackType->stackSize == 4 && ofs < 32) {
PushOp(JIT_STOREPARAMLOCAL_0 + (ofs >> 2));
} else {
PushOpParam(JIT_STOREPARAMLOCAL_TYPEID + pStackType->stackType, ofs);
// if it's a valuetype then push the TypeDef of it afterwards
if (pStackType->stackType == EVALSTACK_VALUETYPE) {
PushPTR(pStackType);
}
}
break;
case CIL_LDLOC_0:
case CIL_LDLOC_1:
case CIL_LDLOC_2:
case CIL_LDLOC_3:
// Push opcode and offset into locals memory
u32Value = op - CIL_LDLOC_0;
goto cilLdLoc;
case CIL_LDLOC_S:
// Push opcode and offset into locals memory
u32Value = pCIL[cilOfs++];
cilLdLoc:
pStackType = pLocals[u32Value].pTypeDef;
ofs = pMethodDef->parameterStackSize + pLocals[u32Value].offset;
if (pStackType->stackSize == 4 && ofs < 32) {
PushOp(JIT_LOADPARAMLOCAL_0 + (ofs >> 2));
} else {
PushOpParam(JIT_LOADPARAMLOCAL_TYPEID + pStackType->stackType, ofs);
// if it's a valuetype then push the TypeDef of it afterwards
if (pStackType->stackType == EVALSTACK_VALUETYPE) {
PushPTR(pStackType);
}
}
PushStackType(pStackType);
break;
case CIL_STLOC_0:
case CIL_STLOC_1:
case CIL_STLOC_2:
case CIL_STLOC_3:
u32Value = op - CIL_STLOC_0;
goto cilStLoc;
case CIL_STLOC_S:
u32Value = pCIL[cilOfs++];
cilStLoc:
pStackType = PopStackType();
ofs = pMethodDef->parameterStackSize + pLocals[u32Value].offset;
if (pStackType->stackSize == 4 && ofs < 32) {
PushOp(JIT_STOREPARAMLOCAL_0 + (ofs >> 2));
} else {
PushOpParam(JIT_STOREPARAMLOCAL_TYPEID + pStackType->stackType, ofs);
// if it's a valuetype then push the TypeDef of it afterwards
if (pStackType->stackType == EVALSTACK_VALUETYPE) {
PushPTR(pStackType);
}
}
break;
case CIL_LDLOCA_S:
// Get the local number to load the address of
u32Value = pCIL[cilOfs++];
PushOpParam(JIT_LOAD_PARAMLOCAL_ADDR, pMethodDef->parameterStackSize + pLocals[u32Value].offset);
PushStackType(types[TYPE_SYSTEM_INTPTR]);
break;
case CIL_LDIND_I1:
u32Value = TYPE_SYSTEM_SBYTE;
goto cilLdInd;
case CIL_LDIND_U1:
u32Value = TYPE_SYSTEM_BYTE;
goto cilLdInd;
case CIL_LDIND_I2:
u32Value = TYPE_SYSTEM_INT16;
goto cilLdInd;
case CIL_LDIND_U2:
u32Value = TYPE_SYSTEM_UINT16;
goto cilLdInd;
case CIL_LDIND_I4:
u32Value = TYPE_SYSTEM_INT32;
goto cilLdInd;
case CIL_LDIND_U4:
u32Value = TYPE_SYSTEM_UINT32;
goto cilLdInd;
case CIL_LDIND_I8:
u32Value = TYPE_SYSTEM_INT64;
goto cilLdInd;
case CIL_LDIND_R4:
u32Value = TYPE_SYSTEM_SINGLE;
goto cilLdInd;
case CIL_LDIND_R8:
u32Value = TYPE_SYSTEM_DOUBLE;
goto cilLdInd;
case CIL_LDIND_REF:
u32Value = TYPE_SYSTEM_OBJECT;
goto cilLdInd;
case CIL_LDIND_I:
u32Value = TYPE_SYSTEM_INTPTR;
cilLdInd:
PopStackTypeDontCare(); // don't care what it is
PushOp(JIT_LOADINDIRECT_I8 + (op - CIL_LDIND_I1));
PushStackType(types[u32Value]);
break;
case CIL_STIND_REF:
case CIL_STIND_I1:
case CIL_STIND_I2:
case CIL_STIND_I4:
PopStackTypeMulti(2); // Don't care what they are
PushOp(JIT_STOREINDIRECT_REF + (op - CIL_STIND_REF));
break;
case CIL_RET:
PushOp(JIT_RETURN);
RestoreTypeStack(&typeStack, ppTypeStacks[cilOfs]);
break;
case CIL_CALL:
case CIL_CALLVIRT:
{
tMD_MethodDef *pCallMethod;
tMD_TypeDef *pBoxCallType;
U32 derefRefType;
u32Value2 = 0;
cilCallVirtConstrained:
pBoxCallType = NULL;
derefRefType = 0;
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pCallMethod = MetaData_GetMethodDefFromDefRefOrSpec(pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
if (pCallMethod->isFilled == 0) {
tMD_TypeDef *pTypeDef;
pTypeDef = MetaData_GetTypeDefFromMethodDef(pCallMethod);
MetaData_Fill_TypeDef(pTypeDef, NULL, NULL);
}
if (u32Value2 != 0) {
// There is a 'constrained' prefix
tMD_TypeDef *pConstrainedType;
pConstrainedType = MetaData_GetTypeDefFromDefRefOrSpec(pMetaData, u32Value2, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
if (TYPE_ISINTERFACE(pCallMethod->pParentType)) {
u32Value2 = 0xffffffff;
// Find the interface that we're dealing with
for (i=0; i<pConstrainedType->numInterfaces; i++) {
if (pConstrainedType->pInterfaceMaps[i].pInterface == pCallMethod->pParentType) {
u32Value2 = pConstrainedType->pInterfaceMaps[i].pVTableLookup[pCallMethod->vTableOfs];
break;
}
}
Assert(u32Value2 != 0xffffffff);
if (pConstrainedType->pVTable[u32Value2]->pParentType == pConstrainedType) {
// This method is implemented on this class, so make it a normal CALL op
op = CIL_CALL;
pCallMethod = pConstrainedType->pVTable[u32Value2];
}
} else {
if (pConstrainedType->isValueType) {
tMD_MethodDef *pImplMethod;
// If pConstraintedType directly implements the call then don't do anything
// otherwise the 'this' pointer must be boxed (BoxedCall)
pImplMethod = pConstrainedType->pVTable[pCallMethod->vTableOfs];
if (pImplMethod->pParentType == pConstrainedType) {
op = CIL_CALL;
pCallMethod = pImplMethod;
} else {
pBoxCallType = pConstrainedType;
}
} else {
// Reference-type, so dereference the PTR to 'this' and use that for the 'this' for the call.
derefRefType = 1;
}
}
}
// Pop stack type for each argument. Don't actually care what these are,
// except the last one which will be the 'this' object type of a non-static method
//printf("Call %s() - popping %d stack args\n", pCallMethod->name, pCallMethod->numberOfParameters);
for (i=0; i<pCallMethod->numberOfParameters; i++) {
pStackType = PopStackType();
}
// the stack type of the 'this' object will now be in stackType (if there is one)
if (METHOD_ISSTATIC(pCallMethod)) {
pStackType = types[TYPE_SYSTEM_OBJECT];
}
MetaData_Fill_TypeDef(pStackType, NULL, NULL);
if (TYPE_ISINTERFACE(pCallMethod->pParentType) && op == CIL_CALLVIRT) {
PushOp(JIT_CALL_INTERFACE);
} else if (pCallMethod->pParentType->pParent == types[TYPE_SYSTEM_MULTICASTDELEGATE]) {
PushOp(JIT_INVOKE_DELEGATE);
} else {
switch (pStackType->stackType)
{
case EVALSTACK_INTNATIVE: // Not really right, but it'll work on 32-bit
case EVALSTACK_O:
if (derefRefType) {
PushOp(JIT_DEREF_CALLVIRT);
} else {
if (pBoxCallType != NULL) {
PushOp(JIT_BOX_CALLVIRT);
PushPTR(pBoxCallType);
} else {
PushOp((op == CIL_CALL)?JIT_CALL_O:JIT_CALLVIRT_O);
}
}
break;
case EVALSTACK_PTR:
case EVALSTACK_VALUETYPE:
if (derefRefType) {
PushOp(JIT_DEREF_CALLVIRT);
} else if (pBoxCallType != NULL) {
PushOp(JIT_BOX_CALLVIRT);
PushPTR(pBoxCallType);
} else {
PushOp(JIT_CALL_PTR);
}
break;
default:
Crash("JITit(): Cannot CALL or CALLVIRT with stack type: %d", pStackType->stackType);
}
}
PushPTR(pCallMethod);
if (pCallMethod->pReturnType != NULL) {
PushStackType(pCallMethod->pReturnType);
}
}
break;
case CIL_BR_S: // unconditional branch
u32Value = (I8)pCIL[cilOfs++];
goto cilBr;
case CIL_BR:
u32Value = GetUnalignedU32(pCIL, &cilOfs);
cilBr:
// Put a temporary CIL offset value into the JITted code. This will be updated later
u32Value = cilOfs + (I32)u32Value;
MayCopyTypeStack();
PushOp(JIT_BRANCH);
PushBranch();
PushU32(u32Value);
// Restore the stack state
RestoreTypeStack(&typeStack, ppTypeStacks[cilOfs]);
break;
case CIL_SWITCH:
// This is the int containing the switch value. Don't care what it is.
PopStackTypeDontCare();
// The number of switch jump targets
i32Value = (I32)GetUnalignedU32(pCIL, &cilOfs);
// Set up the offset from which the jump offsets are calculated
u32Value2 = cilOfs + (i32Value << 2);
PushOpParam(JIT_SWITCH, i32Value);
for (i=0; i<(U32)i32Value; i++) {
// A jump target
u32Value = u32Value2 + (I32)GetUnalignedU32(pCIL, &cilOfs);
PushBranch();
MayCopyTypeStack();
// Push the jump target.
// It is needed to allow the branch offset to be correctly updated later.
PushU32(u32Value);
}
break;
case CIL_BRFALSE_S:
case CIL_BRTRUE_S:
u32Value = (I8)pCIL[cilOfs++];
u32Value2 = JIT_BRANCH_FALSE + (op - CIL_BRFALSE_S);
goto cilBrFalseTrue;
case CIL_BRFALSE:
case CIL_BRTRUE:
u32Value = GetUnalignedU32(pCIL, &cilOfs);
u32Value2 = JIT_BRANCH_FALSE + (op - CIL_BRFALSE);
cilBrFalseTrue:
PopStackTypeDontCare(); // Don't care what it is
// Put a temporary CIL offset value into the JITted code. This will be updated later
u32Value = cilOfs + (I32)u32Value;
MayCopyTypeStack();
PushOp(u32Value2);
PushBranch();
PushU32(u32Value);
break;
case CIL_BEQ_S:
case CIL_BGE_S:
case CIL_BGT_S:
case CIL_BLE_S:
case CIL_BLT_S:
case CIL_BNE_UN_S:
case CIL_BGE_UN_S:
case CIL_BGT_UN_S:
case CIL_BLE_UN_S:
case CIL_BLT_UN_S:
u32Value = (I8)pCIL[cilOfs++];
u32Value2 = CIL_BEQ_S;
goto cilBrCond;
case CIL_BEQ:
case CIL_BGE:
case CIL_BGT:
case CIL_BLE:
case CIL_BLT:
case CIL_BNE_UN:
case CIL_BGE_UN:
case CIL_BGT_UN:
case CIL_BLE_UN:
case CIL_BLT_UN:
u32Value = GetUnalignedU32(pCIL, &cilOfs);
u32Value2 = CIL_BEQ;
cilBrCond:
pTypeB = PopStackType();
pTypeA = PopStackType();
u32Value = cilOfs + (I32)u32Value;
MayCopyTypeStack();
if ((pTypeA->stackType == EVALSTACK_INT32 && pTypeB->stackType == EVALSTACK_INT32) ||
(pTypeA->stackType == EVALSTACK_O && pTypeB->stackType == EVALSTACK_O)) {
PushOp(JIT_BEQ_I32I32 + (op - u32Value2));
} else if (pTypeA->stackType == EVALSTACK_INT64 && pTypeB->stackType == EVALSTACK_INT64) {
PushOp(JIT_BEQ_I64I64 + (op - u32Value2));
} else if (pTypeA->stackType == EVALSTACK_F32 && pTypeB->stackType == EVALSTACK_F32) {
PushOp(JIT_BEQ_F32F32 + (op - u32Value2));
} else if (pTypeA->stackType == EVALSTACK_F64 && pTypeB->stackType == EVALSTACK_F64) {
PushOp(JIT_BEQ_F64F64 + (op - u32Value2));
} else {
Crash("JITit(): Cannot perform conditional branch on stack types: %d and %d", pTypeA->stackType, pTypeB->stackType);
}
PushBranch();
PushU32(u32Value);
break;
case CIL_ADD_OVF:
case CIL_ADD_OVF_UN:
case CIL_MUL_OVF:
case CIL_MUL_OVF_UN:
case CIL_SUB_OVF:
case CIL_SUB_OVF_UN:
u32Value = (CIL_ADD_OVF - CIL_ADD) + (JIT_ADD_I32I32 - JIT_ADD_OVF_I32I32);
goto cilBinaryArithOp;
case CIL_ADD:
case CIL_SUB:
case CIL_MUL:
case CIL_DIV:
case CIL_DIV_UN:
case CIL_REM:
case CIL_REM_UN:
case CIL_AND:
case CIL_OR:
case CIL_XOR:
u32Value = 0;
cilBinaryArithOp:
pTypeB = PopStackType();
pTypeA = PopStackType();
if (pTypeA->stackType == EVALSTACK_INT32 && pTypeB->stackType == EVALSTACK_INT32) {
PushOp(JIT_ADD_I32I32 + (op - CIL_ADD) - u32Value);
PushStackType(types[TYPE_SYSTEM_INT32]);
} else if (pTypeA->stackType == EVALSTACK_INT64 && pTypeB->stackType == EVALSTACK_INT64) {
PushOp(JIT_ADD_I64I64 + (op - CIL_ADD) - u32Value);
PushStackType(types[TYPE_SYSTEM_INT64]);
} else if (pTypeA->stackType == EVALSTACK_F32 && pTypeB->stackType == EVALSTACK_F32) {
PushOp(JIT_ADD_F32F32 + (op - CIL_ADD) - u32Value);
PushStackType(pTypeA);
} else if (pTypeA->stackType == EVALSTACK_F64 && pTypeB->stackType == EVALSTACK_F64) {
PushOp(JIT_ADD_F64F64 + (op - CIL_ADD) - u32Value);
PushStackType(pTypeA);
} else {
Crash("JITit(): Cannot perform binary numeric operand on stack types: %d and %d", pTypeA->stackType, pTypeB->stackType);
}
break;
case CIL_NEG:
case CIL_NOT:
pTypeA = PopStackType();
if (pTypeA->stackType == EVALSTACK_INT32) {
PushOp(JIT_NEG_I32 + (op - CIL_NEG));
PushStackType(types[TYPE_SYSTEM_INT32]);
} else if (pTypeA->stackType == EVALSTACK_INT64) {
PushOp(JIT_NEG_I64 + (op - CIL_NEG));
PushStackType(types[TYPE_SYSTEM_INT64]);
} else {
Crash("JITit(): Cannot perform unary operand on stack types: %d", pTypeA->stackType);
}
break;
case CIL_SHL:
case CIL_SHR:
case CIL_SHR_UN:
PopStackTypeDontCare(); // Don't care about the shift amount
pTypeA = PopStackType(); // Do care about the value to shift
if (pTypeA->stackType == EVALSTACK_INT32) {
PushOp(JIT_SHL_I32 - CIL_SHL + op);
PushStackType(types[TYPE_SYSTEM_INT32]);
} else if (pTypeA->stackType == EVALSTACK_INT64) {
PushOp(JIT_SHL_I64 - CIL_SHL + op);
PushStackType(types[TYPE_SYSTEM_INT64]);
} else {
Crash("JITit(): Cannot perform shift operation on type: %s", pTypeA->name);
}
break;
// Conversion operations
{
U32 toType;
U32 toBitCount;
U32 convOpOffset;
case CIL_CONV_I1:
case CIL_CONV_OVF_I1: // Fix this later - will never overflow
case CIL_CONV_OVF_I1_UN: // Fix this later - will never overflow
toBitCount = 8;
toType = TYPE_SYSTEM_SBYTE;
goto cilConvInt32;
case CIL_CONV_I2:
case CIL_CONV_OVF_I2: // Fix this later - will never overflow
case CIL_CONV_OVF_I2_UN: // Fix this later - will never overflow
toBitCount = 16;
toType = TYPE_SYSTEM_INT16;
goto cilConvInt32;
case CIL_CONV_I4:
case CIL_CONV_OVF_I4: // Fix this later - will never overflow
case CIL_CONV_OVF_I4_UN: // Fix this later - will never overflow
case CIL_CONV_I: // Only on 32-bit
case CIL_CONV_OVF_I_UN: // Only on 32-bit; Fix this later - will never overflow
toBitCount = 32;
toType = TYPE_SYSTEM_INT32;
cilConvInt32:
convOpOffset = JIT_CONV_OFFSET_I32;
goto cilConv;
case CIL_CONV_U1:
case CIL_CONV_OVF_U1: // Fix this later - will never overflow
case CIL_CONV_OVF_U1_UN: // Fix this later - will never overflow
toBitCount = 8;
toType = TYPE_SYSTEM_BYTE;
goto cilConvUInt32;
case CIL_CONV_U2:
case CIL_CONV_OVF_U2: // Fix this later - will never overflow
case CIL_CONV_OVF_U2_UN: // Fix this later - will never overflow
toBitCount = 16;
toType = TYPE_SYSTEM_UINT16;
goto cilConvUInt32;
case CIL_CONV_U4:
case CIL_CONV_OVF_U4: // Fix this later - will never overflow
case CIL_CONV_OVF_U4_UN: // Fix this later - will never overflow
case CIL_CONV_U: // Only on 32-bit
case CIL_CONV_OVF_U_UN: // Only on 32-bit; Fix this later - will never overflow
toBitCount = 32;
toType = TYPE_SYSTEM_UINT32;
cilConvUInt32:
convOpOffset = JIT_CONV_OFFSET_U32;
goto cilConv;
case CIL_CONV_I8:
case CIL_CONV_OVF_I8: // Fix this later - will never overflow
case CIL_CONV_OVF_I8_UN: // Fix this later - will never overflow
toType = TYPE_SYSTEM_INT64;
convOpOffset = JIT_CONV_OFFSET_I64;
goto cilConv;
case CIL_CONV_U8:
case CIL_CONV_OVF_U8: // Fix this later - will never overflow
case CIL_CONV_OVF_U8_UN: // Fix this later - will never overflow
toType = TYPE_SYSTEM_UINT64;
convOpOffset = JIT_CONV_OFFSET_U64;
goto cilConv;
case CIL_CONV_R4:
toType = TYPE_SYSTEM_SINGLE;
convOpOffset = JIT_CONV_OFFSET_R32;
goto cilConv;
case CIL_CONV_R8:
case CIL_CONV_R_UN:
toType = TYPE_SYSTEM_DOUBLE;
convOpOffset = JIT_CONV_OFFSET_R64;
goto cilConv;
cilConv:
pStackType = PopStackType();
{
U32 opCodeBase;
U32 useParam = 0, param;
// This is the types that the conversion is from.
switch (pStackType->stackType) {
case EVALSTACK_INT64:
opCodeBase = (pStackType == types[TYPE_SYSTEM_INT64])?JIT_CONV_FROM_I64:JIT_CONV_FROM_U64;
break;
case EVALSTACK_INT32:
case EVALSTACK_PTR: // Only on 32-bit
opCodeBase =
(pStackType == types[TYPE_SYSTEM_BYTE] ||
pStackType == types[TYPE_SYSTEM_UINT16] ||
pStackType == types[TYPE_SYSTEM_UINT32] ||
pStackType == types[TYPE_SYSTEM_UINTPTR])?JIT_CONV_FROM_U32:JIT_CONV_FROM_I32;
break;
case EVALSTACK_F64:
opCodeBase = JIT_CONV_FROM_R64;
break;
case EVALSTACK_F32:
opCodeBase = JIT_CONV_FROM_R32;
break;
default:
Crash("JITit() Conv cannot handle stack type %d", pStackType->stackType);
}
// This is the types that the conversion is to.
switch (convOpOffset) {
case JIT_CONV_OFFSET_I32:
useParam = 1;
param = 32 - toBitCount;
break;
case JIT_CONV_OFFSET_U32:
useParam = 1;
// Next line is really (1 << toBitCount) - 1
// But it's done like this to work when toBitCount == 32
param = (((1 << (toBitCount - 1)) - 1) << 1) + 1;
break;
case JIT_CONV_OFFSET_I64:
case JIT_CONV_OFFSET_U64:
case JIT_CONV_OFFSET_R32:
case JIT_CONV_OFFSET_R64:
break;
default:
Crash("JITit() Conv cannot handle convOpOffset %d", convOpOffset);
}
PushOp(opCodeBase + convOpOffset);
if (useParam) {
PushU32(param);
}
}
PushStackType(types[toType]);
break;
}
#ifdef OLD_CONV
case CIL_CONV_OVF_I1:
case CIL_CONV_OVF_I2:
case CIL_CONV_OVF_I4:
u32Value = TYPE_SYSTEM_INT32;
goto convOvf;
case CIL_CONV_OVF_I8:
u32Value = TYPE_SYSTEM_INT64;
goto convOvf;
case CIL_CONV_OVF_U1:
case CIL_CONV_OVF_U2:
case CIL_CONV_OVF_U4:
u32Value = TYPE_SYSTEM_UINT32;
goto convOvf;
case CIL_CONV_OVF_U8:
u32Value = TYPE_SYSTEM_UINT64;
convOvf:
pStackType = PopStackType();
PushOpParam(JIT_CONV_OVF_I1 + (op - CIL_CONV_OVF_I1), pStackType->stackType);
PushStackType(types[u32Value]);
break;
case CIL_CONV_I1:
case CIL_CONV_I2:
case CIL_CONV_I4:
u32Value = TYPE_SYSTEM_INT32;
goto conv1;
case CIL_CONV_I8:
u32Value = TYPE_SYSTEM_INT64;
goto conv1;
case CIL_CONV_R4:
u32Value = TYPE_SYSTEM_SINGLE;
goto conv1;
case CIL_CONV_R8:
u32Value = TYPE_SYSTEM_DOUBLE;
goto conv1;
case CIL_CONV_U4:
u32Value = TYPE_SYSTEM_UINT32;
goto conv1;
case CIL_CONV_U8:
u32Value = TYPE_SYSTEM_UINT64;
conv1:
pStackType = PopStackType();
PushOpParam(JIT_CONV_I1 + (op - CIL_CONV_I1), pStackType->stackType);
PushStackType(types[u32Value]);
break;
case CIL_CONV_U2:
case CIL_CONV_U1:
u32Value = TYPE_SYSTEM_UINT32;
goto conv2;
case CIL_CONV_I:
u32Value = TYPE_SYSTEM_INT32; // Only on 32-bit
conv2:
pStackType = PopStackType();
PushOpParam(JIT_CONV_U2 + (op - CIL_CONV_U2), pStackType->stackType);
PushStackType(types[u32Value]);
break;
case CIL_CONV_U:
pStackType = PopStackType();
PushOpParam(JIT_CONV_U_NATIVE, pStackType->stackType);
PushStackType(types[TYPE_SYSTEM_UINTPTR]);
break;
#endif
case CIL_LDOBJ:
{
tMD_TypeDef *pTypeDef;
PopStackTypeDontCare(); // Don't care what this is
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pTypeDef = MetaData_GetTypeDefFromDefRefOrSpec(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PushOp(JIT_LOADOBJECT);
PushPTR(pTypeDef);
PushStackType(pTypeDef);
}
break;
case CIL_STOBJ:
{
tMD_TypeDef *pTypeDef;
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pTypeDef = MetaData_GetTypeDefFromDefRefOrSpec(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PopStackTypeMulti(2);
if (pTypeDef->isValueType && pTypeDef->arrayElementSize != 4) {
// If it's a value-type then do this
PushOpParam(JIT_STORE_OBJECT_VALUETYPE, pTypeDef->arrayElementSize);
} else {
// If it's a ref type, or a value-type with size 4, then can do this instead
// (it executes faster)
PushOp(JIT_STOREINDIRECT_REF);
}
break;
}
case CIL_LDSTR:
u32Value = GetUnalignedU32(pCIL, &cilOfs) & 0x00ffffff;
PushOpParam(JIT_LOAD_STRING, u32Value);
PushStackType(types[TYPE_SYSTEM_STRING]);
break;
case CIL_NEWOBJ:
{
tMD_MethodDef *pConstructorDef;
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pConstructorDef = MetaData_GetMethodDefFromDefRefOrSpec(pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
if (pConstructorDef->isFilled == 0) {
tMD_TypeDef *pTypeDef;
pTypeDef = MetaData_GetTypeDefFromMethodDef(pConstructorDef);
MetaData_Fill_TypeDef(pTypeDef, NULL, NULL);
}
if (pConstructorDef->pParentType->isValueType) {
PushOp(JIT_NEWOBJECT_VALUETYPE);
} else {
PushOp(JIT_NEWOBJECT);
}
// -1 because the param count includes the 'this' parameter that is sent to the constructor
PopStackTypeMulti(pConstructorDef->numberOfParameters - 1);
PushPTR(pConstructorDef);
PushStackType(pConstructorDef->pParentType);
}
break;
case CIL_CASTCLASS:
{
tMD_TypeDef *pCastToType;
PushOp(JIT_CAST_CLASS);
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pCastToType = MetaData_GetTypeDefFromDefRefOrSpec(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PushPTR(pCastToType);
}
break;
case CIL_ISINST:
{
tMD_TypeDef *pIsInstanceOfType;
PushOp(JIT_IS_INSTANCE);
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pIsInstanceOfType = MetaData_GetTypeDefFromDefRefOrSpec(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PushPTR(pIsInstanceOfType);
}
break;
case CIL_NEWARR:
{
tMD_TypeDef *pTypeDef;
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pTypeDef = MetaData_GetTypeDefFromDefRefOrSpec(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PopStackTypeDontCare(); // Don't care what it is
PushOp(JIT_NEW_VECTOR);
MetaData_Fill_TypeDef(pTypeDef, NULL, NULL);
pTypeDef = Type_GetArrayTypeDef(pTypeDef, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PushPTR(pTypeDef);
PushStackType(pTypeDef);
}
break;
case CIL_LDLEN:
PopStackTypeDontCare(); // Don't care what it is
PushOp(JIT_LOAD_VECTOR_LEN);
PushStackType(types[TYPE_SYSTEM_INT32]);
break;
case CIL_LDELEM_I1:
case CIL_LDELEM_U1:
case CIL_LDELEM_I2:
case CIL_LDELEM_U2:
case CIL_LDELEM_I4:
case CIL_LDELEM_U4:
PopStackTypeMulti(2); // Don't care what any of these are
PushOp(JIT_LOAD_ELEMENT_I8 + (op - CIL_LDELEM_I1));
PushStackType(types[TYPE_SYSTEM_INT32]);
break;
case CIL_LDELEM_I8:
PopStackTypeMulti(2); // Don't care what any of these are
PushOp(JIT_LOAD_ELEMENT_I64);
PushStackType(types[TYPE_SYSTEM_INT64]);
break;
case CIL_LDELEM_R4:
PopStackTypeMulti(2); // Don't care what any of these are
PushOp(JIT_LOAD_ELEMENT_R32);
PushStackType(types[TYPE_SYSTEM_SINGLE]);
break;
case CIL_LDELEM_R8:
PopStackTypeMulti(2); // Don't care what any of these are
PushOp(JIT_LOAD_ELEMENT_R64);
PushStackType(types[TYPE_SYSTEM_DOUBLE]);
break;
case CIL_LDELEM_REF:
PopStackTypeMulti(2); // Don't care what any of these are
PushOp(JIT_LOAD_ELEMENT_U32);
PushStackType(types[TYPE_SYSTEM_OBJECT]);
break;
case CIL_LDELEM_ANY:
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pStackType = (tMD_TypeDef*)MetaData_GetTypeDefFromDefRefOrSpec(pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PopStackTypeMulti(2); // Don't care what these are
PushOpParam(JIT_LOAD_ELEMENT, pStackType->stackSize);
PushStackType(pStackType);
break;
case CIL_LDELEMA:
PopStackTypeMulti(2); // Don't care what any of these are
GetUnalignedU32(pCIL, &cilOfs); // Don't care what this is
PushOp(JIT_LOAD_ELEMENT_ADDR);
PushStackType(types[TYPE_SYSTEM_INTPTR]);
break;
case CIL_STELEM_I1:
case CIL_STELEM_I2:
case CIL_STELEM_I4:
case CIL_STELEM_R4:
case CIL_STELEM_REF:
PopStackTypeMulti(3); // Don't care what any of these are
PushOp(JIT_STORE_ELEMENT_32);
break;
case CIL_STELEM_I8:
case CIL_STELEM_R8:
PopStackTypeMulti(3); // Don't care what any of these are
PushOp(JIT_STORE_ELEMENT_64);
break;
case CIL_STELEM_ANY:
GetUnalignedU32(pCIL, &cilOfs); // Don't need this token, as the type stack will contain the same type
pStackType = PopStackType(); // This is the type to store
PopStackTypeMulti(2); // Don't care what these are
PushOpParam(JIT_STORE_ELEMENT, pStackType->stackSize);
break;
case CIL_STFLD:
{
tMD_FieldDef *pFieldDef;
// Get the stack type of the value to store
pStackType = PopStackType();
PushOp(JIT_STOREFIELD_TYPEID + pStackType->stackType);
// Get the FieldRef or FieldDef of the field to store
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pFieldDef = MetaData_GetFieldDefFromDefOrRef(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PushPTR(pFieldDef);
// Pop the object/valuetype on which to store the field. Don't care what it is
PopStackTypeDontCare();
}
break;
case CIL_LDFLD:
{
tMD_FieldDef *pFieldDef;
// Get the FieldRef or FieldDef of the field to load
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pFieldDef = MetaData_GetFieldDefFromDefOrRef(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
// Pop the object/valuetype on which to load the field.
pStackType = PopStackType();
if (pStackType->stackType == EVALSTACK_VALUETYPE) {
PushOpParam(JIT_LOADFIELD_VALUETYPE, pStackType->stackSize);
PushPTR(pFieldDef);
} else {
if (pFieldDef->memSize <= 4) {
PushOp(JIT_LOADFIELD_4);
PushU32(pFieldDef->memOffset);
} else {
PushOp(JIT_LOADFIELD);
PushPTR(pFieldDef);
}
}
// Push the stack type of the just-read field
PushStackType(pFieldDef->pType);
}
break;
case CIL_LDFLDA:
{
tMD_FieldDef *pFieldDef;
tMD_TypeDef *pTypeDef;
// Get the FieldRef or FieldDef of the field to load
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pFieldDef = MetaData_GetFieldDefFromDefOrRef(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
// Sometimes, the type def will not have been filled, so ensure it's filled.
pTypeDef = MetaData_GetTypeDefFromFieldDef(pFieldDef);
MetaData_Fill_TypeDef(pTypeDef, NULL, NULL);
PopStackTypeDontCare(); // Don't care what it is
PushOpParam(JIT_LOAD_FIELD_ADDR, pFieldDef->memOffset);
PushStackType(types[TYPE_SYSTEM_INTPTR]);
}
break;
case CIL_STSFLD: // Store static field
{
tMD_FieldDef *pFieldDef;
tMD_TypeDef *pTypeDef;
// Get the FieldRef or FieldDef of the static field to store
PopStackTypeDontCare(); // Don't care what it is
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pFieldDef = MetaData_GetFieldDefFromDefOrRef(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
// Sometimes, the type def will not have been filled, so ensure it's filled.
pTypeDef = MetaData_GetTypeDefFromFieldDef(pFieldDef);
MetaData_Fill_TypeDef(pTypeDef, NULL, NULL);
pStackType = pFieldDef->pType;
PushOp(JIT_STORESTATICFIELD_TYPEID + pStackType->stackType);
PushPTR(pFieldDef);
}
break;
case CIL_LDSFLD: // Load static field
{
tMD_FieldDef *pFieldDef;
tMD_TypeDef *pTypeDef;
// Get the FieldRef or FieldDef of the static field to load
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pFieldDef = MetaData_GetFieldDefFromDefOrRef(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
// Sometimes, the type def will not have been filled, so ensure it's filled.
pTypeDef = MetaData_GetTypeDefFromFieldDef(pFieldDef);
MetaData_Fill_TypeDef(pTypeDef, NULL, NULL);
pStackType = pFieldDef->pType;
PushOp(JIT_LOADSTATICFIELD_CHECKTYPEINIT_TYPEID + pStackType->stackType);
PushPTR(pFieldDef);
PushStackType(pStackType);
}
break;
case CIL_LDSFLDA: // Load static field address
{
tMD_FieldDef *pFieldDef;
tMD_TypeDef *pTypeDef;
// Get the FieldRef or FieldDef of the field to load
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pFieldDef = MetaData_GetFieldDefFromDefOrRef(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
// Sometimes, the type def will not have been filled, so ensure it's filled.
pTypeDef = MetaData_GetTypeDefFromFieldDef(pFieldDef);
MetaData_Fill_TypeDef(pTypeDef, NULL, NULL);
PushOp(JIT_LOADSTATICFIELDADDRESS_CHECKTYPEINIT);
PushPTR(pFieldDef);
PushStackType(types[TYPE_SYSTEM_INTPTR]);
}
break;
case CIL_BOX:
{
tMD_TypeDef *pTypeDef;
pStackType = PopStackType();
// Get the TypeDef(or Ref) token of the valuetype to box
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pTypeDef = MetaData_GetTypeDefFromDefRefOrSpec(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
MetaData_Fill_TypeDef(pTypeDef, NULL, NULL);
if (pTypeDef->pGenericDefinition == types[TYPE_SYSTEM_NULLABLE]) {
// This is a nullable type, so special boxing code is needed.
PushOp(JIT_BOX_NULLABLE);
// Push the underlying type of the nullable type, not the nullable type itself
PushPTR(pTypeDef->ppClassTypeArgs[0]);
} else {
PushOp(JIT_BOX_TYPEID + pStackType->stackType);
PushPTR(pTypeDef);
}
// This is correct - cannot push underlying type, as then references are treated as value-types
PushStackType(types[TYPE_SYSTEM_OBJECT]);
}
break;
case CIL_UNBOX_ANY:
{
tMD_TypeDef *pTypeDef;
PopStackTypeDontCare(); // Don't care what it is
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pTypeDef = MetaData_GetTypeDefFromDefRefOrSpec(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
if (pTypeDef->pGenericDefinition == types[TYPE_SYSTEM_NULLABLE]) {
// This is a nullable type, so special unboxing is required.
PushOp(JIT_UNBOX_NULLABLE);
// For nullable types, push the underlying type
PushPTR(pTypeDef->ppClassTypeArgs[0]);
} else if (pTypeDef->isValueType) {
PushOp(JIT_UNBOX2VALUETYPE);
} else {
PushOp(JIT_UNBOX2OBJECT);
}
PushStackType(pTypeDef);
}
break;
case CIL_LDTOKEN:
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pMem = MetaData_GetTypeMethodField(pMethodDef->pMetaData, u32Value, &u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PushOp(JIT_LOADTOKEN_BASE + u32Value);
PushPTR(pMem);
PushStackType(types[
(u32Value==0)?TYPE_SYSTEM_RUNTIMETYPEHANDLE:
((u32Value==1)?TYPE_SYSTEM_RUNTIMEFIELDHANDLE:TYPE_SYSTEM_RUNTIMEMETHODHANDLE)
]);
break;
case CIL_THROW:
PopStackTypeDontCare(); // Don't care what it is
PushOp(JIT_THROW);
RestoreTypeStack(&typeStack, ppTypeStacks[cilOfs]);
break;
case CIL_LEAVE_S:
u32Value = (I8)pCIL[cilOfs++];
goto cilLeave;
case CIL_LEAVE:
u32Value = GetUnalignedU32(pCIL, &cilOfs);
cilLeave:
// Put a temporary CIL offset value into the JITted code. This will be updated later
u32Value = cilOfs + (I32)u32Value;
MayCopyTypeStack();
RestoreTypeStack(&typeStack, ppTypeStacks[cilOfs]);
PushOp(JIT_LEAVE);
PushBranch();
PushU32(u32Value);
break;
case CIL_ENDFINALLY:
PushOp(JIT_END_FINALLY);
RestoreTypeStack(&typeStack, ppTypeStacks[cilOfs]);
break;
case CIL_EXTENDED:
op = pCIL[cilOfs++];
switch (op)
{
case CILX_INITOBJ:
{
tMD_TypeDef *pTypeDef;
PopStackTypeDontCare(); // Don't care what it is
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pTypeDef = MetaData_GetTypeDefFromDefRefOrSpec(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
if (pTypeDef->isValueType) {
PushOp(JIT_INIT_VALUETYPE);
PushPTR(pTypeDef);
} else {
PushOp(JIT_INIT_OBJECT);
}
}
break;
case CILX_LOADFUNCTION:
{
tMD_MethodDef *pFuncMethodDef;
u32Value = GetUnalignedU32(pCIL, &cilOfs);
pFuncMethodDef = MetaData_GetMethodDefFromDefRefOrSpec(pMethodDef->pMetaData, u32Value, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
PushOp(JIT_LOADFUNCTION);
PushPTR(pFuncMethodDef);
PushStackType(types[TYPE_SYSTEM_INTPTR]);
}
break;
case CILX_CEQ:
case CILX_CGT:
case CILX_CGT_UN:
case CILX_CLT:
case CILX_CLT_UN:
pTypeB = PopStackType();
pTypeA = PopStackType();
if ((pTypeA->stackType == EVALSTACK_INT32 && pTypeB->stackType == EVALSTACK_INT32) ||
(pTypeA->stackType == EVALSTACK_O && pTypeB->stackType == EVALSTACK_O) ||
// Next line: only on 32-bit
(pTypeA->stackType == EVALSTACK_PTR && pTypeB->stackType == EVALSTACK_PTR)) {
PushOp(JIT_CEQ_I32I32 + (op - CILX_CEQ));
} else if (pTypeA->stackType == EVALSTACK_INT64 && pTypeB->stackType == EVALSTACK_INT64) {
PushOp(JIT_CEQ_I64I64 + (op - CILX_CEQ));
} else if (pTypeA->stackType == EVALSTACK_F32 && pTypeB->stackType == EVALSTACK_F32) {
PushOp(JIT_CEQ_F32F32 + (op - CILX_CEQ));
} else if (pTypeA->stackType == EVALSTACK_F64 && pTypeB->stackType == EVALSTACK_F64) {
PushOp(JIT_CEQ_F64F64 + (op - CILX_CEQ));
} else {
Crash("JITit(): Cannot perform comparison operand on stack types: %s and %s", pTypeA->name, pTypeB->name);
}
PushStackType(types[TYPE_SYSTEM_INT32]);
break;
case CILX_RETHROW:
PushOp(JIT_RETHROW);
break;
case CILX_CONSTRAINED:
u32Value2 = GetUnalignedU32(pCIL, &cilOfs);
cilOfs++;
goto cilCallVirtConstrained;
case CILX_READONLY:
// Do nothing
break;
default:
Crash("JITit(): JITter cannot handle extended op-code:0x%02x", op);
}
break;
default:
Crash("JITit(): JITter cannot handle op-code: 0x%02x", op);
}
} while (cilOfs < codeSize);
// Apply branch offset fixes
for (i=0; i<branchOffsets.ofs; i++) {
U32 ofs, jumpTarget;
ofs = branchOffsets.p[i];
jumpTarget = ops.p[ofs];
// Rewrite the branch offset
jumpTarget = pJITOffsets[jumpTarget];
ops.p[ofs] = jumpTarget;
#ifdef GEN_COMBINED_OPCODES
isDynamic.p[jumpTarget] |= DYNAMIC_JUMP_TARGET;
#endif
}
// Apply expection handler offset fixes
for (i=0; i<pJITted->numExceptionHandlers; i++) {
tExceptionHeader *pEx;
pEx = &pJITted->pExceptionHeaders[i];
pEx->tryEnd = pJITOffsets[pEx->tryStart + pEx->tryEnd];
pEx->tryStart = pJITOffsets[pEx->tryStart];
pEx->handlerEnd = pJITOffsets[pEx->handlerStart + pEx->handlerEnd];
pEx->handlerStart = pJITOffsets[pEx->handlerStart];
#ifdef GEN_COMBINED_OPCODES
isDynamic.p[pEx->tryStart] |= DYNAMIC_EX_START | DYNAMIC_JUMP_TARGET;
isDynamic.p[pEx->tryEnd] |= DYNAMIC_EX_END | DYNAMIC_JUMP_TARGET;
isDynamic.p[pEx->handlerStart] |= DYNAMIC_EX_START | DYNAMIC_JUMP_TARGET;
isDynamic.p[pEx->handlerEnd] |= DYNAMIC_EX_END | DYNAMIC_JUMP_TARGET;
#endif
}
#ifdef GEN_COMBINED_OPCODES
// Find any candidates for instruction combining
if (genCombinedOpcodes) {
U32 inst0 = 0;
while (inst0 < ops.ofs) {
U32 opCodeCount = 0;
U32 instCount = 0;
U32 shrinkOpsBy;
U32 isFirstInst;
while (!(isDynamic.p[inst0] & DYNAMIC_OK)) {
inst0++;
if (inst0 >= ops.ofs) {
goto combineDone;
}
}
isFirstInst = 1;
while (isDynamic.p[inst0 + instCount] & DYNAMIC_OK) {
if (isFirstInst) {
isFirstInst = 0;
} else {
if (isDynamic.p[inst0 + instCount] & DYNAMIC_JUMP_TARGET) {
// Cannot span a jump target
break;
}
}
instCount += 1 + ((isDynamic.p[inst0 + instCount] & DYNAMIC_BYTE_COUNT_MASK) >> 2);
opCodeCount++;
}
shrinkOpsBy = 0;
if (opCodeCount > 1) {
U32 combinedSize;
tCombinedOpcodesMem *pCOMem = TMALLOC(tCombinedOpcodesMem);
shrinkOpsBy = GenCombined(&ops, &isDynamic, inst0, instCount, &combinedSize, &pCOMem->pMem);
pCOMem->pNext = pJITted->pCombinedOpcodesMem;
pJITted->pCombinedOpcodesMem = pCOMem;
pJITted->opsMemSize += combinedSize;
memmove(&ops.p[inst0 + instCount - shrinkOpsBy], &ops.p[inst0 + instCount], (ops.ofs - inst0 - instCount) << 2);
memmove(&isDynamic.p[inst0 + instCount - shrinkOpsBy], &isDynamic.p[inst0 + instCount], (ops.ofs - inst0 - instCount) << 2);
ops.ofs -= shrinkOpsBy;
isDynamic.ofs -= shrinkOpsBy;
for (i=0; i<branchOffsets.ofs; i++) {
U32 ofs;
if (branchOffsets.p[i] > inst0) {
branchOffsets.p[i] -= shrinkOpsBy;
}
ofs = branchOffsets.p[i];
if (ops.p[ofs] > inst0) {
ops.p[ofs] -= shrinkOpsBy;
}
}
for (i=0; i<pJITted->numExceptionHandlers; i++) {
tExceptionHeader *pEx;
pEx = &pJITted->pExceptionHeaders[i];
if (pEx->tryStart > inst0) {
pEx->tryStart -= shrinkOpsBy;
}
if (pEx->tryEnd > inst0) {
pEx->tryEnd -= shrinkOpsBy;
}
if (pEx->handlerStart > inst0) {
pEx->handlerStart -= shrinkOpsBy;
}
if (pEx->handlerEnd > inst0) {
pEx->handlerEnd -= shrinkOpsBy;
}
}
}
inst0 += instCount - shrinkOpsBy;
}
}
combineDone:
#endif
// Change maxStack to indicate the number of bytes needed on the evaluation stack.
// This is the largest number of bytes needed by all objects/value-types on the stack,
pJITted->maxStack = typeStack.maxBytes;
free(typeStack.ppTypes);
for (i=0; i<codeSize; i++) {
if (ppTypeStacks[i] != NULL) {
free(ppTypeStacks[i]->ppTypes);
}
}
free(ppTypeStacks);
DeleteOps(branchOffsets);
free(pJITOffsets);
// Copy ops to some memory of exactly the correct size. To not waste memory.
u32Value = ops.ofs * sizeof(U32);
pFinalOps = genCombinedOpcodes?malloc(u32Value):mallocForever(u32Value);
memcpy(pFinalOps, ops.p, u32Value);
DeleteOps(ops);
#ifdef GEN_COMBINED_OPCODES
pJITted->opsMemSize += u32Value;
DeleteOps(isDynamic);
#endif
return pFinalOps;
}
// Prepare a method for execution
// This makes sure that the method has been JITed.
void JIT_Prepare(tMD_MethodDef *pMethodDef, U32 genCombinedOpcodes) {
tMetaData *pMetaData;
U8 *pMethodHeader;
tJITted *pJITted;
FLAGS16 flags;
U32 codeSize;
IDX_TABLE localsToken;
U8 *pCIL;
SIG sig;
U32 i, sigLength, numLocals;
tParameter *pLocals;
log_f(2, "JIT: %s\n", Sys_GetMethodDesc(pMethodDef));
pMetaData = pMethodDef->pMetaData;
pJITted = (genCombinedOpcodes)?TMALLOC(tJITted):TMALLOCFOREVER(tJITted);
#ifdef GEN_COMBINED_OPCODES
pJITted->pCombinedOpcodesMem = NULL;
pJITted->opsMemSize = 0;
if (genCombinedOpcodes) {
pMethodDef->pJITtedCombined = pJITted;
} else {
pMethodDef->pJITted = pJITted;
}
#else
pMethodDef->pJITted = pJITted;
#endif
if ((pMethodDef->implFlags & METHODIMPLATTRIBUTES_INTERNALCALL) ||
((pMethodDef->implFlags & METHODIMPLATTRIBUTES_CODETYPE_MASK) == METHODIMPLATTRIBUTES_CODETYPE_RUNTIME)) {
tJITCallNative *pCallNative;
// Internal call
if (strcmp(pMethodDef->name, ".ctor") == 0) {
// Internal constructor needs enough evaluation stack space to return itself
pJITted->maxStack = pMethodDef->pParentType->stackSize;
} else {
pJITted->maxStack = (pMethodDef->pReturnType == NULL)?0:pMethodDef->pReturnType->stackSize; // For return value
}
pCallNative = TMALLOCFOREVER(tJITCallNative);
pCallNative->opCode = Translate(JIT_CALL_NATIVE, 0);
pCallNative->pMethodDef = pMethodDef;
pCallNative->fn = InternalCall_Map(pMethodDef);
pCallNative->retOpCode = Translate(JIT_RETURN, 0);
pJITted->localsStackSize = 0;
pJITted->pOps = (U32*)pCallNative;
return;
}
if (pMethodDef->flags & METHODATTRIBUTES_PINVOKEIMPL) {
tJITCallPInvoke *pCallPInvoke;
// PInvoke call
tMD_ImplMap *pImplMap = MetaData_GetImplMap(pMetaData, pMethodDef->tableIndex);
fnPInvoke fn = PInvoke_GetFunction(pMetaData, pImplMap);
if (fn == NULL) {
Crash("PInvoke library or function not found: %s()", pImplMap->importName);
}
pCallPInvoke = TMALLOCFOREVER(tJITCallPInvoke);
pCallPInvoke->opCode = Translate(JIT_CALL_PINVOKE, 0);
pCallPInvoke->fn = fn;
pCallPInvoke->pMethod = pMethodDef;
pCallPInvoke->pImplMap = pImplMap;
pJITted->localsStackSize = 0;
pJITted->maxStack = (pMethodDef->pReturnType == NULL)?0:pMethodDef->pReturnType->stackSize; // For return value
pJITted->pOps = (U32*)pCallPInvoke;
return;
}
pMethodHeader = (U8*)pMethodDef->pCIL;
if ((*pMethodHeader & 0x3) == CorILMethod_TinyFormat) {
// Tiny header
flags = *pMethodHeader & 0x3;
pJITted->maxStack = 8;
codeSize = (*pMethodHeader & 0xfc) >> 2;
localsToken = 0;
pCIL = pMethodHeader + 1;
} else {
// Fat header
flags = *(U16*)pMethodHeader & 0x0fff;
pJITted->maxStack = *(U16*)&pMethodHeader[2];
codeSize = *(U32*)&pMethodHeader[4];
localsToken = *(IDX_TABLE*)&pMethodHeader[8];
pCIL = pMethodHeader + ((pMethodHeader[1] & 0xf0) >> 2);
}
if (flags & CorILMethod_MoreSects) {
U32 numClauses;
pMethodHeader = pCIL + ((codeSize + 3) & (~0x3));
if (*pMethodHeader & CorILMethod_Sect_FatFormat) {
U32 exSize;
// Fat header
numClauses = ((*(U32*)pMethodHeader >> 8) - 4) / 24;
//pJITted->pExceptionHeaders = (tExceptionHeader*)(pMethodHeader + 4);
exSize = numClauses * sizeof(tExceptionHeader);
pJITted->pExceptionHeaders =
(tExceptionHeader*)(genCombinedOpcodes?malloc(exSize):mallocForever(exSize));
memcpy(pJITted->pExceptionHeaders, pMethodHeader + 4, exSize);
} else {
// Thin header
tExceptionHeader *pExHeaders;
U32 exSize;
numClauses = (((U8*)pMethodHeader)[1] - 4) / 12;
exSize = numClauses * sizeof(tExceptionHeader);
pMethodHeader += 4;
//pExHeaders = pJITted->pExceptionHeaders = (tExceptionHeader*)mallocForever(numClauses * sizeof(tExceptionHeader));
pExHeaders = pJITted->pExceptionHeaders =
(tExceptionHeader*)(genCombinedOpcodes?malloc(exSize):mallocForever(exSize));
for (i=0; i<numClauses; i++) {
pExHeaders[i].flags = ((U16*)pMethodHeader)[0];
pExHeaders[i].tryStart = ((U16*)pMethodHeader)[1];
pExHeaders[i].tryEnd = ((U8*)pMethodHeader)[4];
pExHeaders[i].handlerStart = ((U8*)pMethodHeader)[5] | (((U8*)pMethodHeader)[6] << 8);
pExHeaders[i].handlerEnd = ((U8*)pMethodHeader)[7];
pExHeaders[i].u.classToken = ((U32*)pMethodHeader)[2];
pMethodHeader += 12;
}
}
pJITted->numExceptionHandlers = numClauses;
// replace all classToken's with the actual tMD_TypeDef*
for (i=0; i<numClauses; i++) {
if (pJITted->pExceptionHeaders[i].flags == COR_ILEXCEPTION_CLAUSE_EXCEPTION) {
pJITted->pExceptionHeaders[i].u.pCatchTypeDef =
MetaData_GetTypeDefFromDefRefOrSpec(pMethodDef->pMetaData, pJITted->pExceptionHeaders[i].u.classToken, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
}
}
} else {
pJITted->numExceptionHandlers = 0;
pJITted->pExceptionHeaders = NULL;
}
// Analyse the locals
if (localsToken == 0) {
// No locals
pJITted->localsStackSize = 0;
pLocals = NULL;
} else {
tMD_StandAloneSig *pStandAloneSig;
U32 i, totalSize;
pStandAloneSig = (tMD_StandAloneSig*)MetaData_GetTableRow(pMethodDef->pMetaData, localsToken);
sig = MetaData_GetBlob(pStandAloneSig->signature, &sigLength);
MetaData_DecodeSigEntry(&sig); // Always 0x07
numLocals = MetaData_DecodeSigEntry(&sig);
pLocals = (tParameter*)malloc(numLocals * sizeof(tParameter));
totalSize = 0;
for (i=0; i<numLocals; i++) {
tMD_TypeDef *pTypeDef;
pTypeDef = Type_GetTypeFromSig(pMethodDef->pMetaData, &sig, pMethodDef->pParentType->ppClassTypeArgs, pMethodDef->ppMethodTypeArgs);
MetaData_Fill_TypeDef(pTypeDef, NULL, NULL);
pLocals[i].pTypeDef = pTypeDef;
pLocals[i].offset = totalSize;
pLocals[i].size = pTypeDef->stackSize;
totalSize += pTypeDef->stackSize;
}
pJITted->localsStackSize = totalSize;
}
// JIT the CIL code
pJITted->pOps = JITit(pMethodDef, pCIL, codeSize, pLocals, pJITted, genCombinedOpcodes);
free(pLocals);
}