diff options
Diffstat (limited to 'src/vm/interpreter.hpp')
-rw-r--r-- | src/vm/interpreter.hpp | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/src/vm/interpreter.hpp b/src/vm/interpreter.hpp new file mode 100644 index 0000000000..b087930e39 --- /dev/null +++ b/src/vm/interpreter.hpp @@ -0,0 +1,481 @@ +// 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. + + + +// This file contains bodies of inline methods. + +#ifndef INTERPRETER_HPP_DEFINED +#define INTERPRETER_HPP_DEFINED 1 + +#include "interpreter.h" +#if INTERP_ILCYCLE_PROFILE +#include "cycletimer.h" +#endif // INTERP_ILCYCLE_PROFILE + +#if INTERP_TRACING +// static +FILE* Interpreter::GetLogFile() +{ + if (s_InterpreterLogFile == NULL) + { + static ConfigString fileName; + LPWSTR fn = fileName.val(CLRConfig::INTERNAL_InterpreterLogFile); + if (fn == NULL) + { + s_InterpreterLogFile = stdout; + } + else + { + s_InterpreterLogFile = _wfopen(fn, W("a")); + } + } + return s_InterpreterLogFile; +} +#endif // INTERP_TRACING + +inline void Interpreter::LdFromMemAddr(void* addr, InterpreterType tp) +{ + CONTRACTL { + SO_TOLERANT; + THROWS; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } CONTRACTL_END; + + unsigned stackHt = m_curStackHt; + + OpStackTypeSet(stackHt, tp.StackNormalize()); + + size_t sz = tp.Size(&m_interpCeeInfo); + if (tp.IsStruct()) + { + if (tp.IsLargeStruct(&m_interpCeeInfo)) + { + // Large struct case. + void* ptr = LargeStructOperandStackPush(sz); + memcpy(ptr, addr, sz); + OpStackSet<void*>(stackHt, ptr); + } + else + { + OpStackSet<INT64>(stackHt, GetSmallStructValue(addr, sz)); + } + m_curStackHt = stackHt + 1; + return; + } + + // Otherwise... + + // The compiler seems to compile this switch statement into an "if + // cascade" anyway, but in a non-optimal order (one that's good for + // code density, but doesn't match the actual usage frequency, + // which, in fairness, it would have no clue about. So we might + // as well do our own "if cascade" in the order we believe is + // likely to be optimal (at least on 32-bit systems). + if (sz == 4) + { + OpStackSet<INT32>(stackHt, *reinterpret_cast<INT32*>(addr)); + } + else if (sz == 1) + { + CorInfoType cit = tp.ToCorInfoType(); + if (CorInfoTypeIsUnsigned(cit)) + { + OpStackSet<UINT32>(stackHt, *reinterpret_cast<UINT8*>(addr)); + } + else + { + OpStackSet<INT32>(stackHt, *reinterpret_cast<INT8*>(addr)); + } + } + else if (sz == 8) + { + OpStackSet<INT64>(stackHt, *reinterpret_cast<INT64*>(addr)); + } + else + { + assert(sz == 2); // only remaining case. + CorInfoType cit = tp.ToCorInfoType(); + if (CorInfoTypeIsUnsigned(cit)) + { + OpStackSet<UINT32>(stackHt, *reinterpret_cast<UINT16*>(addr)); + } + else + { + OpStackSet<INT32>(stackHt, *reinterpret_cast<INT16*>(addr)); + } + } + m_curStackHt = stackHt + 1; +} + +inline void Interpreter::LdLoc(int locNum) +{ + CONTRACTL { + SO_TOLERANT; + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } CONTRACTL_END; + + if (locNum >= m_methInfo->m_numLocals) + { + COMPlusThrow(kVerificationException); + } + + unsigned stackHt = m_curStackHt; + GCX_FORBID(); + OpStackSet<INT64>(stackHt, *FixedSizeLocalSlot(locNum)); + InterpreterType tp = m_methInfo->m_localDescs[locNum].m_typeStackNormal; + OpStackTypeSet(stackHt, tp); + m_curStackHt = stackHt + 1; + m_orOfPushedInterpreterTypes |= static_cast<size_t>(tp.AsRaw()); +} + +void Interpreter::StLoc(int locNum) +{ + CONTRACTL { + SO_TOLERANT; + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } CONTRACTL_END; + + assert(m_curStackHt >= 1); + + if (locNum >= m_methInfo->m_numLocals) + { + COMPlusThrow(kVerificationException); + } + + // Don't decrement "m_curStackHt" early -- if we do, then we'll have a potential GC hole, if + // the top-of-stack value is a GC ref. + unsigned ind = m_curStackHt - 1; + InterpreterType tp = m_methInfo->m_localDescs[locNum].m_typeStackNormal; + +#ifdef _DEBUG + if (!tp.Matches(OpStackTypeGet(ind), &m_interpCeeInfo)) + { + if (!s_InterpreterLooseRules || + // We copy a 64 bit value, otherwise some of the below conditions should end up as casts. + !((tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_NATIVEINT && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_INT) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_INT && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_NATIVEINT) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_INT && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_LONG) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_LONG && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_INT) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_LONG && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_BYREF) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_INT && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_BYREF) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_NATIVEINT && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_BYREF) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_BYREF && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_LONG) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_BYREF && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_CLASS) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_FLOAT && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_DOUBLE) || + (tp.ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_DOUBLE && OpStackTypeGet(ind).ToCorInfoTypeShifted() == CORINFO_TYPE_SHIFTED_FLOAT))) + { + VerificationError("StLoc requires types to match."); + } + } +#endif + + if (tp.IsLargeStruct(&m_interpCeeInfo)) + { + size_t sz = tp.Size(&m_interpCeeInfo); // TODO: note that tp.IsLargeStruct() above just called tp.Size(), so this is duplicate work unless the optimizer inlines and CSEs the calls. + + // The operand stack entry is a pointer to a corresponding entry in the large struct stack. + // There will be a large struct location for the local as well. + BYTE* addr = LargeStructLocalSlot(locNum); + + // Now, before we copy from the large struct stack to "addr", we have a problem. + // We've optimized "ldloc" to just copy the fixed size entry for the local onto the ostack. + // But this might mean that there are pointers to "addr" already on the stack, as stand-ins for + // the value they point to. If we overwrite that value, we've inadvertently modified the ostack. + // So we first "normalize" the ostack wrt "addr", ensuring that any entries containing addr + // have large-struct slots allocated for them, and the values are copied there. + OpStackNormalize(); + + // Now we can do the copy. + void* srcAddr = OpStackGet<void*>(ind); + memcpy(addr, srcAddr, sz); + LargeStructOperandStackPop(sz, srcAddr); + } + else + { + // Otherwise, we just copy the full stack entry. + *FixedSizeLocalSlot(locNum) = OpStackGet<INT64>(ind); + } + + m_curStackHt = ind; + +#ifdef _DEBUG + // The value of the locals has changed; print them. + if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL)) + { + PrintLocals(); + } +#endif // _DEBUG +} + +void Interpreter::StToLocalMemAddr(void* addr, InterpreterType tp) +{ + CONTRACTL { + SO_TOLERANT; + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } CONTRACTL_END; + + assert(m_curStackHt >= 1); + m_curStackHt--; + + size_t sz = tp.Size(&m_interpCeeInfo); + if (tp.IsStruct()) + { + if (tp.IsLargeStruct(&m_interpCeeInfo)) + { + // Large struct case. + void* srcAddr = OpStackGet<void*>(m_curStackHt); + memcpy(addr, srcAddr, sz); + LargeStructOperandStackPop(sz, srcAddr); + } + else + { + memcpy(addr, OpStackGetAddr(m_curStackHt, sz), sz); + } + return; + } + + // Note: this implementation assumes a little-endian architecture. + if (sz == 4) + { + *reinterpret_cast<INT32*>(addr) = OpStackGet<INT32>(m_curStackHt); + } + else if (sz == 1) + { + *reinterpret_cast<INT8*>(addr) = OpStackGet<INT8>(m_curStackHt); + } + else if (sz == 8) + { + *reinterpret_cast<INT64*>(addr) = OpStackGet<INT64>(m_curStackHt); + } + else + { + assert(sz == 2); + *reinterpret_cast<INT16*>(addr) = OpStackGet<INT16>(m_curStackHt); + } +} + +template<int op, typename T, bool IsIntType, CorInfoType cit, bool TypeIsUnchanged> +void Interpreter::BinaryArithOpWork(T val1, T val2) +{ + T res; + if (op == BA_Add) + { + res = val1 + val2; + } + else if (op == BA_Sub) + { + res = val1 - val2; + } + else if (op == BA_Mul) + { + res = val1 * val2; + } + else + { + assert(op == BA_Div || op == BA_Rem); + if (IsIntType) + { + if (val2 == 0) + { + ThrowDivideByZero(); + } + else if (val2 == -1 && val1 == static_cast<T>(((UINT64)1) << (sizeof(T)*8 - 1))) // min int / -1 is not representable. + { + ThrowSysArithException(); + } + } + // Otherwise... + if (op == BA_Div) + { + res = val1 / val2; + } + else + { + res = RemFunc(val1, val2); + } + } + + unsigned residx = m_curStackHt - 2; + OpStackSet<T>(residx, res); + if (!TypeIsUnchanged) + { + OpStackTypeSet(residx, InterpreterType(cit)); + } +} + +void Interpreter::BrOnValueTakeBranch(bool shouldBranch, int targetLen) +{ + if (shouldBranch) + { + int offset; + if (targetLen == 1) + { + // BYTE is unsigned... + offset = getI1(m_ILCodePtr + 1); + } + else + { + offset = getI4LittleEndian(m_ILCodePtr + 1); + } + // 1 is the size of the current instruction; offset is relative to start of next. + if (offset < 0) + { + // Backwards branch; enable caching. + BackwardsBranchActions(offset); + } + ExecuteBranch(m_ILCodePtr + 1 + targetLen + offset); + } + else + { + m_ILCodePtr += targetLen + 1; + } +} + +extern size_t CorInfoTypeSizeArray[]; + +size_t CorInfoTypeSize(CorInfoType cit) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE_MSG(cit != CORINFO_TYPE_VALUECLASS, "Precondition"); + + size_t res = CorInfoTypeSizeArray[cit]; + + _ASSERTE_MSG(res != 0, "Other illegal input"); + + return res; +} + +void Interpreter::StaticFldAddr(CORINFO_ACCESS_FLAGS accessFlgs, + /*out (byref)*/void** pStaticFieldAddr, + /*out*/InterpreterType* pit, /*out*/UINT* pFldSize, /*out*/bool* pManagedMem) +{ + unsigned ilOffset = CurOffset(); + +#if INTERP_TRACING + InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_SFldAddr]); +#endif // INTERP_TRACING + + StaticFieldCacheEntry* cacheEntry = NULL; + if (s_InterpreterUseCaching) cacheEntry = GetCachedStaticField(ilOffset); + if (cacheEntry == NULL) + { + bool doCaching = StaticFldAddrWork(accessFlgs, pStaticFieldAddr, pit, pFldSize, pManagedMem); + if (s_InterpreterUseCaching && doCaching) + { + cacheEntry = new StaticFieldCacheEntry(*pStaticFieldAddr, *pFldSize, *pit); + CacheStaticField(ilOffset, cacheEntry); + } + } + else + { + // Reenable this if you want to check this (#ifdef _DEBUG). Was interfering with some statistics + // gathering I was doing in debug builds. +#if 0 + // Make sure the caching works correctly. + StaticFldAddrWork(accessFlgs, pStaticFieldAddr, pit, pFldSize, pManagedMem); + assert(*pStaticFieldAddr == cacheEntry->m_srcPtr && *pit == cacheEntry->m_it && *pFldSize == cacheEntry->m_sz); +#else + // If we do the call above, it takes care of this. + m_ILCodePtr += 5; // In the case above, the call to StaticFldAddr increments the code pointer. +#endif + *pStaticFieldAddr = cacheEntry->m_srcPtr; + *pit = cacheEntry->m_it; + *pFldSize = cacheEntry->m_sz; + *pManagedMem = true; // Or else it wouldn't have been cached. + } +} + +void Interpreter::ResolveToken(CORINFO_RESOLVED_TOKEN* resTok, mdToken token, CorInfoTokenKind tokenType InterpTracingArg(ResolveTokenKind rtk)) +{ + resTok->tokenContext = GetPreciseGenericsContext(); + resTok->tokenScope = m_methInfo->m_module; + resTok->token = token; + resTok->tokenType = tokenType; +#if INTERP_ILCYCLE_PROFILE + unsigned __int64 startCycles; + bool b = CycleTimer::GetThreadCyclesS(&startCycles); assert(b); +#endif // INTERP_ILCYCLE_PROFILE + m_interpCeeInfo.resolveToken(resTok); +#if 1 + if (resTok->tokenType == CORINFO_TOKENKIND_Method) + { + MethodDesc* pMD = reinterpret_cast<MethodDesc*>(resTok->hMethod); + MethodTable* pMT = GetMethodTableFromClsHnd(resTok->hClass); + + if (pMD->GetMethodTable() != pMT) + { + // Find the method on exactClass corresponding to methToCall. + pMD = MethodDesc::FindOrCreateAssociatedMethodDesc( + pMD, // pPrimaryMD + pMT, // pExactMT + FALSE, // forceBoxedEntryPoint + pMD->GetMethodInstantiation(), // methodInst + FALSE, // allowInstParam + TRUE); // forceRemotableMethod (to get maximally specific). + resTok->hMethod = reinterpret_cast<CORINFO_METHOD_HANDLE>(pMD); + } + } +#endif +#if INTERP_ILCYCLE_PROFILE + unsigned __int64 endCycles; + b = CycleTimer::GetThreadCyclesS(&endCycles); assert(b); + m_exemptCycles += (endCycles - startCycles); +#endif // INTERP_ILCYCLE_PROFILE + +#if INTERP_TRACING + InterlockedIncrement(&s_tokenResolutionCalls[rtk]); +#endif // INTERP_TRACING +} + +FieldDesc* Interpreter::FindField(unsigned metaTok InterpTracingArg(ResolveTokenKind rtk)) +{ + CORINFO_RESOLVED_TOKEN fldTok; + ResolveToken(&fldTok, metaTok, CORINFO_TOKENKIND_Field InterpTracingArg(rtk)); + return (FieldDesc*)fldTok.hField; +} + +CORINFO_CLASS_HANDLE Interpreter::FindClass(unsigned metaTok InterpTracingArg(ResolveTokenKind rtk)) +{ + CORINFO_RESOLVED_TOKEN clsTok; + ResolveToken(&clsTok, metaTok, CORINFO_TOKENKIND_Class InterpTracingArg(rtk)); + return clsTok.hClass; +} + +void Interpreter::ThrowOnInvalidPointer(void* ptr) +{ + if (ptr == NULL) + ThrowNullPointerException(); + + BOOL good = TRUE; + + EX_TRY + { + AVInRuntimeImplOkayHolder AVOkay; + good = *(BOOL*)ptr; + + // This conditional forces the dereference to occur; it also + // ensures that good == TRUE if the dereference succeeds. + if (!good) + good = TRUE; + } + EX_CATCH + { + good = FALSE; + } + EX_END_CATCH(SwallowAllExceptions) + + if (!good) + ThrowNullPointerException(); +} + +#endif // INTERPRETER_HPP_DEFINED |