Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/coreclr/interpreter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ set(INTERPRETER_SOURCES
stackmap.cpp
naming.cpp
methodset.cpp
intrinsics.cpp
../../native/containers/dn-simdhash.c
../../native/containers/dn-simdhash-ght-compatible.c
../../native/containers/dn-simdhash-ptr-ptr.c)
Expand Down
69 changes: 60 additions & 9 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2180,6 +2180,44 @@ int32_t InterpCompiler::GetDataItemIndexForHelperFtn(CorInfoHelpFunc ftn)
return GetDataItemIndex(addr);
}

bool InterpCompiler::EmitNamedIntrinsicCall(NamedIntrinsic ni, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO sig)
{
bool mustExpand = (method == m_methodHnd);
if (!mustExpand && (ni == NI_Illegal))
return false;

switch (ni)
{
case NI_IsSupported_False:
case NI_IsSupported_True:
AddIns(INTOP_LDC_I4);
m_pLastNewIns->data[0] = ni == NI_IsSupported_True;
PushStackType(StackTypeI4, NULL);
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
return true;

case NI_Throw_PlatformNotSupportedException:
AddIns(INTOP_THROW_PNSE);
return true;

default:
{
#ifdef DEBUG
if (m_verbose)
{
const char* className = NULL;
const char* namespaceName = NULL;
const char* methodName = m_compHnd->getMethodNameFromMetadata(method, &className, &namespaceName, NULL, 0);
printf("WARNING: Intrinsic not implemented in EmitNamedIntrinsicCall: %d (for %s.%s.%s)\n", ni, namespaceName, className, methodName);
}
#endif
if (mustExpand)
NO_WAY("EmitNamedIntrinsicCall not implemented must-expand intrinsic");
return false;
}
}
}

bool InterpCompiler::EmitCallIntrinsics(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO sig)
{
const char *className = NULL;
Expand Down Expand Up @@ -2384,7 +2422,7 @@ void InterpCompiler::EmitPushUnboxAnyNullable(const CORINFO_GENERICHANDLE_RESULT
AddIns(INTOP_CALL_HELPER_V_AGS);
m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(CORINFO_HELP_UNBOX_NULLABLE);
m_pLastNewIns->data[1] = handleData.dataItemIndex;

m_pLastNewIns->SetSVars2(handleData.genericVar, arg2);
m_pLastNewIns->SetDVar(resultVar);
}
Expand Down Expand Up @@ -2484,7 +2522,7 @@ int InterpCompiler::EmitGenericHandleAsVar(const CORINFO_GENERICHANDLE_RESULT &e
PushStackType(StackTypeI, NULL);
int resultVar = m_pStackPointer[-1].var;
m_pStackPointer--;

GenericHandleData handleData = GenericHandleToGenericHandleData(embedInfo);

if (handleData.argType == HelperArgType::GenericResolution)
Expand Down Expand Up @@ -2584,6 +2622,19 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
flags = (CORINFO_CALLINFO_FLAGS)(flags | CORINFO_CALLINFO_CALLVIRT);

m_compHnd->getCallInfo(&resolvedCallToken, pConstrainedToken, m_methodInfo->ftn, flags, &callInfo);
if (callInfo.methodFlags & CORINFO_FLG_INTRINSIC)
{
if (InterpConfig.InterpMode() >= 3)
{
NamedIntrinsic ni = GetNamedIntrinsic(m_compHnd, m_methodHnd, callInfo.hMethod);
if (EmitNamedIntrinsicCall(ni, resolvedCallToken.hClass, callInfo.hMethod, callInfo.sig))
{
m_ip += 5;
return;
}
}
}

if (EmitCallIntrinsics(callInfo.hMethod, callInfo.sig))
{
m_ip += 5;
Expand Down Expand Up @@ -2675,7 +2726,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
PushInterpType(ctorType, resolvedCallToken.hClass);

CORINFO_GENERICHANDLE_RESULT newObjGenericHandleEmbedInfo;
m_compHnd->embedGenericHandle(&resolvedCallToken, true, m_methodInfo->ftn, &newObjGenericHandleEmbedInfo);
m_compHnd->embedGenericHandle(&resolvedCallToken, true, m_methodInfo->ftn, &newObjGenericHandleEmbedInfo);
newObjData = GenericHandleToGenericHandleData(newObjGenericHandleEmbedInfo);
}
newObjDVar = m_pStackPointer[-2].var;
Expand Down Expand Up @@ -2823,9 +2874,9 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
else if ((callInfo.classFlags & CORINFO_FLG_ARRAY) && newObj)
{
CORINFO_GENERICHANDLE_RESULT newObjGenericHandleEmbedInfo;
m_compHnd->embedGenericHandle(&resolvedCallToken, true, m_methodInfo->ftn, &newObjGenericHandleEmbedInfo);
m_compHnd->embedGenericHandle(&resolvedCallToken, true, m_methodInfo->ftn, &newObjGenericHandleEmbedInfo);
newObjData = GenericHandleToGenericHandleData(newObjGenericHandleEmbedInfo);

if (newObjData.argType == HelperArgType::GenericResolution)
{
AddIns(INTOP_NEWMDARR_GENERIC);
Expand Down Expand Up @@ -5378,17 +5429,17 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
{
// Unbox.any of a reference type is just a cast
CorInfoHelpFunc castingHelper = m_compHnd->getCastingHelper(&resolvedToken, true /* throwing */);

CORINFO_GENERICHANDLE_RESULT embedInfo;
InterpEmbedGenericResult result;
m_compHnd->embedGenericHandle(&resolvedToken, false, m_methodInfo->ftn, &embedInfo);

EmitPushHelperCall_2(castingHelper, embedInfo, m_pStackPointer[0].var, g_stackTypeFromInterpType[InterpTypeO], NULL);
}
else
{
CorInfoHelpFunc helpFunc = m_compHnd->getUnBoxHelper((CORINFO_CLASS_HANDLE)embedInfo.compileTimeHandle);

if (helpFunc == CORINFO_HELP_UNBOX)
{
EmitPushUnboxAny(embedInfo, m_pStackPointer[0].var, g_stackTypeFromInterpType[GetInterpType(m_compHnd->asCorInfoType(resolvedToken.hClass))], resolvedToken.hClass);
Expand Down Expand Up @@ -5431,7 +5482,7 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
AddIns(INTOP_NEWARR_GENERIC);
m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(helpFunc);
m_pLastNewIns->data[1] = handleData.dataItemIndex;

m_pLastNewIns->SetSVars2(handleData.genericVar, newArrLenVar);
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
}
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/interpreter/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <new>
#include "failures.h"
#include "simdhash.h"
#include "intrinsics.h"

struct InterpException
{
Expand Down Expand Up @@ -695,6 +696,7 @@ class InterpCompiler
void EmitShiftOp(int32_t opBase);
void EmitCompareOp(int32_t opBase);
void EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool readonly, bool tailcall, bool newObj, bool isCalli);
bool EmitNamedIntrinsicCall(NamedIntrinsic ni, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO sig);
bool EmitCallIntrinsics(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO sig);
void EmitLdind(InterpType type, CORINFO_CLASS_HANDLE clsHnd, int32_t offset);
void EmitStind(InterpType type, CORINFO_CLASS_HANDLE clsHnd, int32_t offset, bool reverseSVarOrder);
Expand Down
56 changes: 42 additions & 14 deletions src/coreclr/interpreter/eeinterp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,27 +48,55 @@ CorJitResult CILInterp::compileMethod(ICorJitInfo* compHnd,
uint32_t* nativeSizeOfCode)
{

bool doInterpret;
bool doInterpret = false;

if ((g_interpModule != NULL) && (methodInfo->scope == g_interpModule))
doInterpret = true;

if (g_interpModule != NULL)
{
if (methodInfo->scope == g_interpModule)
doInterpret = true;
else
doInterpret = false;
}
else
{
const char *methodName = compHnd->getMethodNameFromMetadata(methodInfo->ftn, nullptr, nullptr, nullptr, 0);
switch (InterpConfig.InterpMode())
{
// 0: default, do not use interpreter except explicit opt-in via DOTNET_Interpreter
case 0:
break;

// 1: use interpreter for everything except (1) methods that have R2R compiled code and (2) all code in System.Private.CoreLib. All code in System.Private.CoreLib falls back to JIT if there is no R2R available for it.
case 1:
{
doInterpret = true;
const char *assemblyName = compHnd->getClassAssemblyName(compHnd->getMethodClass(methodInfo->ftn));
if (assemblyName && !strcmp(assemblyName, "System.Private.CoreLib"))
doInterpret = false;
break;
}

// 2: use interpreter for everything except intrinsics. All intrinsics fallback to JIT. Implies DOTNET_ReadyToRun=0.
case 2:
doInterpret = !(compHnd->getMethodAttribs(methodInfo->ftn) & CORINFO_FLG_INTRINSIC);
break;

// 3: use interpreter for everything, the full interpreter-only mode, no fallbacks to R2R or JIT whatsoever. Implies DOTNET_ReadyToRun=0, DOTNET_EnableHWIntrinsic=0
case 3:
doInterpret = true;
break;

default:
NO_WAY("Unsupported value for DOTNET_InterpMode");
break;
}

#ifdef TARGET_WASM
// interpret everything on wasm
doInterpret = true;
#else
doInterpret = (InterpConfig.Interpreter().contains(compHnd, methodInfo->ftn, compHnd->getMethodClass(methodInfo->ftn), &methodInfo->args));
#endif

if (doInterpret)
// NOTE: We do this check even if doInterpret==true in order to populate g_interpModule
const char *methodName = compHnd->getMethodNameFromMetadata(methodInfo->ftn, nullptr, nullptr, nullptr, 0);
if (InterpConfig.Interpreter().contains(compHnd, methodInfo->ftn, compHnd->getMethodClass(methodInfo->ftn), &methodInfo->args))
{
doInterpret = true;
g_interpModule = methodInfo->scope;
}
#endif
}

if (!doInterpret)
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/interpreter/interpconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ RELEASE_CONFIG_METHODSET(Interpreter, "Interpreter")
CONFIG_METHODSET(InterpHalt, "InterpHalt");
CONFIG_METHODSET(InterpDump, "InterpDump");
CONFIG_INTEGER(InterpList, "InterpList", 0); // List the methods which are compiled by the interpreter JIT
RELEASE_CONFIG_INTEGER(InterpMode, "InterpMode", 0); // Interpreter mode, one of the following:
// 0: default, do not use interpreter except explicit opt-in via DOTNET_Interpreter
// 1: use interpreter for everything except (1) methods that have R2R compiled code and (2) all code in System.Private.CoreLib. All code in System.Private.CoreLib falls back to JIT if there is no R2R available for it.
// 2: use interpreter for everything except intrinsics. All intrinsics fallback to JIT. Implies DOTNET_ReadyToRun=0.
// 3: use interpreter for everything, the full interpreter-only mode, no fallbacks to R2R or JIT whatsoever. Implies DOTNET_ReadyToRun=0, DOTNET_EnableHWIntrinsic=0

#undef CONFIG_STRING
#undef RELEASE_CONFIG_STRING
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/interpreter/intops.def
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ OPDEF(INTOP_LEAVE_FILTER, "leavefilter", 2, 0, 1, InterpOpNoArgs)
OPDEF(INTOP_LEAVE_CATCH, "leavecatch", 2, 0, 0, InterpOpBranch)
OPDEF(INTOP_LOAD_EXCEPTION, "load.exception", 2, 1, 0, InterpOpNoArgs)

OPDEF(INTOP_THROW_PNSE, "throw.pnse", 1, 0, 0, InterpOpNoArgs)
OPDEF(INTOP_FAILFAST, "failfast", 1, 0, 0, InterpOpNoArgs)
OPDEF(INTOP_GC_COLLECT, "gc.collect", 1, 0, 0, InterpOpNoArgs)

Expand Down
114 changes: 114 additions & 0 deletions src/coreclr/interpreter/intrinsics.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "interpreter.h"
#include "intrinsics.h"

#define HAS_PREFIX(haystack, needle) \
(strncmp(haystack, needle, strlen(needle)) == 0)

NamedIntrinsic GetNamedIntrinsic(COMP_HANDLE compHnd, CORINFO_METHOD_HANDLE compMethod, CORINFO_METHOD_HANDLE method)
{
const char* className = NULL;
const char* namespaceName = NULL;
const char* methodName = compHnd->getMethodNameFromMetadata(method, &className, &namespaceName, NULL, 0);

// Array methods don't have metadata
if (!namespaceName)
return NI_Illegal;

if (!HAS_PREFIX(namespaceName, "System"))
return NI_Illegal;

if (!strcmp(namespaceName, "System"))
{
if (!strcmp(className, "Double") || !strcmp(className, "Single"))
{
if (!strcmp(methodName, "ConvertToIntegerNative"))
return NI_PRIMITIVE_ConvertToIntegerNative;
else if (!strcmp(methodName, "MultiplyAddEstimate"))
return NI_System_Math_MultiplyAddEstimate;
}
else if (!strcmp(className, "Math") || !strcmp(className, "MathF"))
{
if (!strcmp(methodName, "ReciprocalEstimate"))
return NI_System_Math_ReciprocalEstimate;
else if (!strcmp(methodName, "ReciprocalSqrtEstimate"))
return NI_System_Math_ReciprocalSqrtEstimate;
}
}
else if (!strcmp(namespaceName, "System.Numerics"))
{
if (!strcmp(className, "Vector") && !strcmp(methodName, "get_IsHardwareAccelerated"))
return NI_IsSupported_False;

// Fall back to managed implementation for everything else.
return NI_Illegal;
}
else if (!strcmp(namespaceName, "System.Runtime.Intrinsics"))
{
// Vector128<T> etc
if (HAS_PREFIX(className, "Vector") && !strcmp(methodName, "get_IsHardwareAccelerated"))
return NI_IsSupported_False;

// Fall back to managed implementation for everything else.
return NI_Illegal;
}
else if (HAS_PREFIX(namespaceName, "System.Runtime.Intrinsics"))
{
// Architecture-specific intrinsics.
if (!strcmp(methodName, "get_IsSupported"))
return NI_IsSupported_False;

// Every intrinsic except IsSupported is PNSE in interpreted-only mode.
return NI_Throw_PlatformNotSupportedException;
}
else if (HAS_PREFIX(namespaceName, "System.Runtime"))
{
if (!strcmp(namespaceName, "System.Runtime.CompilerServices"))
{
if (!strcmp(className, "StaticsHelpers"))
{
if (!strcmp(methodName, "VolatileReadAsByref"))
return NI_System_Runtime_CompilerServices_StaticsHelpers_VolatileReadAsByref;
}
else if (!strcmp(className, "RuntimeHelpers"))
{
if (!strcmp(methodName, "IsReferenceOrContainsReferences"))
return NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences;
}
}
else if (!strcmp(namespaceName, "System.Runtime.InteropServices"))
{
if (!strcmp(className, "MemoryMarshal"))
{
if (!strcmp(methodName, "GetArrayDataReference`1"))
return NI_System_Runtime_InteropService_MemoryMarshal_GetArrayDataReference;
}
}
}
else if (!strcmp(namespaceName, "System.Threading"))
{
if (!strcmp(className, "Interlocked"))
{
if (!strcmp(methodName, "CompareExchange"))
return NI_System_Threading_Interlocked_CompareExchange;
else if (!strcmp(methodName, "MemoryBarrier"))
return NI_System_Threading_Interlocked_MemoryBarrier;
}
else if (!strcmp(className, "Thread"))
{
if (!strcmp(methodName, "FastPollGC"))
return NI_System_Threading_Thread_FastPollGC;
}
else if (!strcmp(className, "Volatile"))
{
if (!strcmp(methodName, "ReadBarrier"))
return NI_System_Threading_Volatile_ReadBarrier;
else if (!strcmp(methodName, "WriteBarrier"))
return NI_System_Threading_Volatile_WriteBarrier;
}
}

return NI_Illegal;
}
11 changes: 11 additions & 0 deletions src/coreclr/interpreter/intrinsics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#ifndef __INTERPRETER_INTRINSICS_H__
#define __INTERPRETER_INTRINSICS_H__

#include "../jit/namedintrinsiclist.h"

NamedIntrinsic GetNamedIntrinsic(COMP_HANDLE compHnd, CORINFO_METHOD_HANDLE compMethod, CORINFO_METHOD_HANDLE method);

#endif // __INTERPRETER_INTRINSICS_H__
5 changes: 4 additions & 1 deletion src/coreclr/vm/interpexec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet)
}
}

pHeader->SetTarget(pMD->GetMultiCallableAddrOfCode()); // The method to call
pHeader->SetTarget(pMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY)); // The method to call

pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
}
Expand Down Expand Up @@ -2391,6 +2391,9 @@ do { \
case INTOP_LEAVE_CATCH:
*(const int32_t**)pFrame->pRetVal = ip + ip[1];
goto EXIT_FRAME;
case INTOP_THROW_PNSE:
COMPlusThrow(kPlatformNotSupportedException);
break;
case INTOP_FAILFAST:
assert(0);
break;
Expand Down
Loading
Loading