diff --git a/src/coreclr/interpreter/CMakeLists.txt b/src/coreclr/interpreter/CMakeLists.txt index dcde0cf2d43e2d..06536580d1a440 100644 --- a/src/coreclr/interpreter/CMakeLists.txt +++ b/src/coreclr/interpreter/CMakeLists.txt @@ -13,6 +13,7 @@ set(INTERPRETER_SOURCES naming.cpp methodset.cpp ../../native/containers/dn-simdhash.c + ../../native/containers/dn-simdhash-ght-compatible.c ../../native/containers/dn-simdhash-ptr-ptr.c) set(INTERPRETER_LINK_LIBRARIES diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index db036d1d11b039..8afa2302a10ae1 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -41,6 +41,8 @@ thread_local ICorJitInfo* t_InterpJitInfoTls = nullptr; static const char *g_stackTypeString[] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " }; +const char* CorInfoHelperToName(CorInfoHelpFunc helper); + /*****************************************************************************/ void AssertOpCodeNotImplemented(const uint8_t *ip, size_t offset) { @@ -87,6 +89,73 @@ class InterpIAllocator : public IAllocator } }; +size_t GetGenericLookupOffset(const CORINFO_RUNTIME_LOOKUP *pLookup, uint32_t index) +{ + if (pLookup->indirections == CORINFO_USEHELPER) + return 0; + else if (index < pLookup->indirections) + return pLookup->offsets[index]; + else + return 0; +} + +void InterpCompiler::CopyToInterpGenericLookup(InterpGenericLookup* dst, const CORINFO_RUNTIME_LOOKUP *src) +{ + if (src->testForNull || src->indirections == CORINFO_USEHELPER) + { + dst->signature = src->signature; + } + else + { + dst->signature = nullptr; + } + + if (src->indirections == CORINFO_USEHELPER) + dst->indirections = InterpGenericLookup_UseHelper; + else + dst->indirections = src->indirections; + + assert(CORINFO_MAXINDIRECTIONS == sizeof(dst->offsets)/sizeof(dst->offsets[0])); + + if (GetGenericLookupOffset(src, 0) > UINT16_MAX || GetGenericLookupOffset(src, 1) > UINT16_MAX || + GetGenericLookupOffset(src, 2) > UINT16_MAX || GetGenericLookupOffset(src, 3) > UINT16_MAX) + { +#ifdef DEBUG + if (m_verbose) + { + printf("CopyToInterpGenericLookup: Offsets too large for generic lookup, unable to compile\n"); + printf(" Indirections: %d\n", (int)src->indirections); + printf(" Offsets: %zu %zu %zu %zu\n", + src->offsets[0], src->offsets[1], src->offsets[2], src->offsets[3]); + } +#endif // DEBUG + NO_WAY("CopyToInterpGenericLookup: Offsets too large for generic lookup"); + } + + if (dst->indirections == InterpGenericLookup_UseHelper) + { + if (dst->signature == NULL) + { + NO_WAY("CopyToInterpGenericLookup: Helper lookup must have a signature"); + } + dst->sizeOffset = 0; + dst->offsets[0] = 0; + dst->offsets[1] = 0; + dst->offsets[2] = 0; + dst->offsets[3] = 0; + } + else + { + dst->sizeOffset = src->sizeOffset; + dst->offsets[0] = (uint16_t)GetGenericLookupOffset(src, 0); + dst->offsets[1] = (uint16_t)GetGenericLookupOffset(src, 1); + dst->offsets[2] = (uint16_t)GetGenericLookupOffset(src, 2); + dst->offsets[3] = (uint16_t)GetGenericLookupOffset(src, 3); + } + assert(!src->indirectFirstOffset); + assert(!src->indirectSecondOffset); +} + // Interpreter-FIXME Use specific allocators for their intended purpose // Allocator for data that is kept alive throughout application execution, // being freed only if the associated method gets freed. @@ -512,6 +581,11 @@ void InterpCompiler::InitBBStackState(InterpBasicBlock *pBB) int32_t InterpCompiler::CreateVarExplicit(InterpType interpType, CORINFO_CLASS_HANDLE clsHnd, int size) { + if (interpType == InterpTypeVT) + { + assert(clsHnd != NULL); + } + if (m_varsSize == m_varsCapacity) { m_varsCapacity *= 2; if (m_varsCapacity == 0) @@ -741,12 +815,12 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArraySetDVar(m_pStackPointer[-1].var); } -int32_t InterpCompiler::GetDataItemIndex(void *data) +void* InterpCompiler::GetDataItemAtIndex(int32_t index) { - int32_t index = m_dataItems.Find(data); - if (index != -1) - return index; - - return m_dataItems.Add(data); + if (index < 0 || index >= m_dataItems.GetSize()) + { + assert(!"Invalid data item index"); + return NULL; + } + return m_dataItems.Get(index); } -void* InterpCompiler::GetDataItemAtIndex(int32_t index) +void* InterpCompiler::GetAddrOfDataItemAtIndex(int32_t index) { if (index < 0 || index >= m_dataItems.GetSize()) { assert(!"Invalid data item index"); return NULL; } - return m_dataItems.Get(index); + return (void*)&m_dataItems.GetUnderlyingArray()[index]; } int32_t InterpCompiler::GetMethodDataItemIndex(CORINFO_METHOD_HANDLE mHandle) @@ -2091,6 +2168,15 @@ int32_t InterpCompiler::GetDataItemIndexForHelperFtn(CorInfoHelpFunc ftn) { addr = (void*)((size_t)addr | INTERP_INDIRECT_HELPER_TAG); } + +#ifdef DEBUG + if (!PointerInNameMap(addr)) + { + const char* name = CorInfoHelperToName(ftn); + if (name) + AddPointerToNameMap(addr, name); + } +#endif assert(ftnLookup.accessType == IAT_VALUE || ftnLookup.accessType == IAT_PVALUE); return GetDataItemIndex(addr); @@ -2206,27 +2292,190 @@ InterpCompiler::InterpEmbedGenericResult InterpCompiler::EmitGenericHandle(CORIN return result; } +InterpCompiler::GenericHandleData InterpCompiler::GenericHandleToGenericHandleData(const CORINFO_GENERICHANDLE_RESULT& embedInfo) +{ + if (embedInfo.lookup.lookupKind.needsRuntimeLookup) + { + CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind = embedInfo.lookup.lookupKind.runtimeLookupKind; + InterpGenericLookup runtimeLookup; + + if (runtimeLookupKind == CORINFO_LOOKUP_METHODPARAM) + { + runtimeLookup.lookupType = InterpGenericLookupType::Method; + } + else if (runtimeLookupKind == CORINFO_LOOKUP_THISOBJ) + { + runtimeLookup.lookupType = InterpGenericLookupType::This; + } + else + { + runtimeLookup.lookupType = InterpGenericLookupType::Class; + } + CopyToInterpGenericLookup(&runtimeLookup, &embedInfo.lookup.runtimeLookup); + return GenericHandleData(getParamArgIndex(), GetDataItemIndex(runtimeLookup)); + } + else + { + return GenericHandleData(GetDataItemIndex(embedInfo.lookup.constLookup.handle)); + } +} + +void InterpCompiler::EmitPushHelperCall_2(const CorInfoHelpFunc ftn, const CORINFO_GENERICHANDLE_RESULT& arg1, int arg2, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack) +{ + PushStackType(resultStackType, clsHndStack); + int resultVar = m_pStackPointer[-1].var; + + GenericHandleData handleData = GenericHandleToGenericHandleData(arg1); + + if (handleData.argType == HelperArgType::GenericResolution) + { + AddIns(INTOP_CALL_HELPER_P_GP); + m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(ftn); + m_pLastNewIns->data[1] = handleData.dataItemIndex; + + m_pLastNewIns->SetSVars2(handleData.genericVar, arg2); + m_pLastNewIns->SetDVar(resultVar); + } + else + { + AddIns(INTOP_CALL_HELPER_P_PP); + m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(ftn); + m_pLastNewIns->data[1] = handleData.dataItemIndex; + + m_pLastNewIns->SetSVar(arg2); + m_pLastNewIns->SetDVar(resultVar); + } +} + +void InterpCompiler::EmitPushUnboxAny(const CORINFO_GENERICHANDLE_RESULT& arg1, int arg2, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack) +{ + PushStackType(resultStackType, clsHndStack); + int resultVar = m_pStackPointer[-1].var; + + GenericHandleData handleData = GenericHandleToGenericHandleData(arg1); + + if (handleData.argType == HelperArgType::GenericResolution) + { + AddIns(INTOP_UNBOX_ANY_GENERIC); + m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(CORINFO_HELP_UNBOX); + m_pLastNewIns->data[1] = handleData.dataItemIndex; + + m_pLastNewIns->SetSVars2(handleData.genericVar, arg2); + m_pLastNewIns->SetDVar(resultVar); + } + else + { + AddIns(INTOP_UNBOX_ANY); + m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(CORINFO_HELP_UNBOX); + m_pLastNewIns->data[1] = handleData.dataItemIndex; + + m_pLastNewIns->SetSVar(arg2); + m_pLastNewIns->SetDVar(resultVar); + } +} + +void InterpCompiler::EmitPushUnboxAnyNullable(const CORINFO_GENERICHANDLE_RESULT& arg1, int arg2, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack) +{ + PushStackType(resultStackType, clsHndStack); + int resultVar = m_pStackPointer[-1].var; + + GenericHandleData handleData = GenericHandleToGenericHandleData(arg1); + + if (handleData.argType == HelperArgType::GenericResolution) + { + AddIns(INTOP_CALL_HELPER_V_AGP); + 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); + } + else + { + AddIns(INTOP_CALL_HELPER_V_APP); + m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(CORINFO_HELP_UNBOX_NULLABLE); + m_pLastNewIns->data[1] = handleData.dataItemIndex; + + m_pLastNewIns->SetSVar(arg2); + m_pLastNewIns->SetDVar(resultVar); + } +} + +void InterpCompiler::EmitPushHelperCall_Addr2(const CorInfoHelpFunc ftn, const CORINFO_GENERICHANDLE_RESULT& arg1, int arg2, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack) +{ + PushStackType(resultStackType, clsHndStack); + int resultVar = m_pStackPointer[-1].var; + + GenericHandleData handleData = GenericHandleToGenericHandleData(arg1); + + if (handleData.argType == HelperArgType::GenericResolution) + { + AddIns(INTOP_CALL_HELPER_P_GA); + m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(ftn); + m_pLastNewIns->data[1] = handleData.dataItemIndex; + + m_pLastNewIns->SetSVars2(handleData.genericVar, arg2); + m_pLastNewIns->SetDVar(resultVar); + } + else + { + AddIns(INTOP_CALL_HELPER_P_PA); + m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(ftn); + m_pLastNewIns->data[1] = handleData.dataItemIndex; + + m_pLastNewIns->SetSVar(arg2); + m_pLastNewIns->SetDVar(resultVar); + } +} + +void InterpCompiler::EmitPushHelperCall(const CorInfoHelpFunc ftn, const CORINFO_GENERICHANDLE_RESULT& arg1, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack) +{ + PushStackType(resultStackType, clsHndStack); + int resultVar = m_pStackPointer[-1].var; + + GenericHandleData handleData = GenericHandleToGenericHandleData(arg1); + + if (handleData.argType == HelperArgType::GenericResolution) + { + AddIns(INTOP_CALL_HELPER_P_G); + m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(ftn); + m_pLastNewIns->data[1] = handleData.dataItemIndex; + + m_pLastNewIns->SetSVar(handleData.genericVar); + m_pLastNewIns->SetDVar(resultVar); + } + else + { + AddIns(INTOP_CALL_HELPER_P_P); + m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(ftn); + m_pLastNewIns->data[1] = handleData.dataItemIndex; + + m_pLastNewIns->SetDVar(resultVar); + } +} + void InterpCompiler::EmitPushCORINFO_LOOKUP(const CORINFO_LOOKUP& lookup) { PushStackType(StackTypeI, NULL); int resultVar = m_pStackPointer[-1].var; CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind = lookup.lookupKind.runtimeLookupKind; + InterpGenericLookup runtimeLookup; if (runtimeLookupKind == CORINFO_LOOKUP_METHODPARAM) { - AddIns(INTOP_GENERICLOOKUP_METHOD); + runtimeLookup.lookupType = InterpGenericLookupType::Method; } else if (runtimeLookupKind == CORINFO_LOOKUP_THISOBJ) { - AddIns(INTOP_GENERICLOOKUP_THIS); + runtimeLookup.lookupType = InterpGenericLookupType::This; } else { - AddIns(INTOP_GENERICLOOKUP_CLASS); + runtimeLookup.lookupType = InterpGenericLookupType::Class; } - CORINFO_RUNTIME_LOOKUP *pRuntimeLookup = (CORINFO_RUNTIME_LOOKUP*)AllocMethodData(sizeof(CORINFO_RUNTIME_LOOKUP)); - *pRuntimeLookup = lookup.runtimeLookup; - m_pLastNewIns->data[0] = GetDataItemIndex(pRuntimeLookup); + CopyToInterpGenericLookup(&runtimeLookup, &lookup.runtimeLookup); + AddIns(INTOP_GENERICLOOKUP); + m_pLastNewIns->data[0] = GetDataItemIndex(runtimeLookup); m_pLastNewIns->SetSVar(getParamArgIndex()); m_pLastNewIns->SetDVar(resultVar); @@ -2237,37 +2486,25 @@ int InterpCompiler::EmitGenericHandleAsVar(const CORINFO_GENERICHANDLE_RESULT &e PushStackType(StackTypeI, NULL); int resultVar = m_pStackPointer[-1].var; m_pStackPointer--; + + GenericHandleData handleData = GenericHandleToGenericHandleData(embedInfo); - if (embedInfo.lookup.lookupKind.needsRuntimeLookup) + if (handleData.argType == HelperArgType::GenericResolution) { - CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind = embedInfo.lookup.lookupKind.runtimeLookupKind; - if (runtimeLookupKind == CORINFO_LOOKUP_METHODPARAM) - { - AddIns(INTOP_GENERICLOOKUP_METHOD); - } - else if (runtimeLookupKind == CORINFO_LOOKUP_THISOBJ) - { - AddIns(INTOP_GENERICLOOKUP_THIS); - } - else - { - AddIns(INTOP_GENERICLOOKUP_CLASS); - } - CORINFO_RUNTIME_LOOKUP *pRuntimeLookup = (CORINFO_RUNTIME_LOOKUP*)AllocMethodData(sizeof(CORINFO_RUNTIME_LOOKUP)); - *pRuntimeLookup = embedInfo.lookup.runtimeLookup; - m_pLastNewIns->data[0] = GetDataItemIndex(pRuntimeLookup); + AddIns(INTOP_GENERICLOOKUP); + m_pLastNewIns->data[0] = handleData.dataItemIndex; - m_pLastNewIns->SetSVar(getParamArgIndex()); + m_pLastNewIns->SetSVar(handleData.genericVar); m_pLastNewIns->SetDVar(resultVar); } else { AddIns(INTOP_LDPTR); - m_pLastNewIns->SetDVar(resultVar); + m_pLastNewIns->data[0] = handleData.dataItemIndex; - assert(embedInfo.lookup.constLookup.accessType == IAT_VALUE); - m_pLastNewIns->data[0] = GetDataItemIndex(embedInfo.lookup.constLookup.handle); + m_pLastNewIns->SetDVar(resultVar); } + return resultVar; } @@ -2361,7 +2598,9 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re StackInfo *pThisStackInfo = m_pStackPointer - callInfo.sig.numArgs - 1; if (callInfo.thisTransform == CORINFO_BOX_THIS) { - EmitBox(pThisStackInfo, pConstrainedToken->hClass, true); + CORINFO_GENERICHANDLE_RESULT embedInfo; + m_compHnd->embedGenericHandle(pConstrainedToken, false, m_methodInfo->ftn, &embedInfo); + EmitBox(pThisStackInfo, embedInfo, true); } else { @@ -2417,7 +2656,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re } callArgs[numArgs] = CALL_ARGS_TERMINATOR; - InterpEmbedGenericResult newObjType; + GenericHandleData newObjData; int32_t newObjThisVar = -1; int32_t newObjDVar = -1; InterpType ctorType = InterpTypeO; @@ -2437,7 +2676,9 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re PushInterpType(ctorType, resolvedCallToken.hClass); PushInterpType(ctorType, resolvedCallToken.hClass); - newObjType = EmitGenericHandle(&resolvedCallToken, GenericHandleEmbedOptions::EmbedParent); + CORINFO_GENERICHANDLE_RESULT newObjGenericHandleEmbedInfo; + m_compHnd->embedGenericHandle(&resolvedCallToken, true, m_methodInfo->ftn, &newObjGenericHandleEmbedInfo); + newObjData = GenericHandleToGenericHandleData(newObjGenericHandleEmbedInfo); } newObjDVar = m_pStackPointer[-2].var; newObjThisVar = m_pStackPointer[-1].var; @@ -2461,6 +2702,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re CORINFO_METHOD_HANDLE exactMethodHandle = (CORINFO_METHOD_HANDLE)((SIZE_T)exactContextHnd & ~CORINFO_CONTEXTFLAGS_MASK); + DeclarePointerIsMethod(exactMethodHandle); if (!callInfo.exactContextNeedsRuntimeLookup) { @@ -2483,6 +2725,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re { assert(((SIZE_T)exactContextHnd & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS); CORINFO_CLASS_HANDLE exactClassHandle = getClassFromContext(exactContextHnd); + DeclarePointerIsClass(exactClassHandle); if ((callInfo.classFlags & CORINFO_FLG_ARRAY) && readonly) { @@ -2563,26 +2806,42 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re } else { - if (newObjType.var != -1) + if (newObjData.argType == HelperArgType::GenericResolution) { // newobj of type known only through a generic dictionary lookup. - AddIns(INTOP_NEWOBJ_VAR); - m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, newObjType.var); + AddIns(INTOP_NEWOBJ_GENERIC); + m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, newObjData.genericVar); + m_pLastNewIns->data[1] = newObjData.dataItemIndex; } else { // Normal newobj call AddIns(INTOP_NEWOBJ); - m_pLastNewIns->data[1] = newObjType.dataItemIndex; + m_pLastNewIns->data[1] = newObjData.dataItemIndex; } } m_pLastNewIns->data[0] = GetDataItemIndex(callInfo.hMethod); } else if ((callInfo.classFlags & CORINFO_FLG_ARRAY) && newObj) { - AddIns(INTOP_NEWMDARR); - m_pLastNewIns->data[0] = GetDataItemIndex(resolvedCallToken.hClass); - m_pLastNewIns->data[1] = callInfo.sig.numArgs; + CORINFO_GENERICHANDLE_RESULT newObjGenericHandleEmbedInfo; + m_compHnd->embedGenericHandle(&resolvedCallToken, true, m_methodInfo->ftn, &newObjGenericHandleEmbedInfo); + newObjData = GenericHandleToGenericHandleData(newObjGenericHandleEmbedInfo); + + if (newObjData.argType == HelperArgType::GenericResolution) + { + AddIns(INTOP_NEWMDARR_GENERIC); + m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, newObjData.genericVar); + m_pLastNewIns->data[0] = newObjData.dataItemIndex; + m_pLastNewIns->data[1] = callInfo.sig.numArgs; + } + else + { + AddIns(INTOP_NEWMDARR); + DeclarePointerIsClass(resolvedCallToken.hClass); + m_pLastNewIns->data[0] = GetDataItemIndex(resolvedCallToken.hClass); + m_pLastNewIns->data[1] = callInfo.sig.numArgs; + } } else if (isCalli) { @@ -2854,7 +3113,7 @@ void InterpCompiler::EmitStaticFieldAddress(CORINFO_FIELD_INFO *pFieldInfo, CORI break; } // Call helper to obtain thread static base address - AddIns(INTOP_CALL_HELPER_PP); + AddIns(INTOP_CALL_HELPER_P_P); m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(pFieldInfo->helper); m_pLastNewIns->data[1] = GetDataItemIndex(helperArg); PushInterpType(InterpTypeByRef, NULL); @@ -2871,7 +3130,7 @@ void InterpCompiler::EmitStaticFieldAddress(CORINFO_FIELD_INFO *pFieldInfo, CORI } case CORINFO_FIELD_STATIC_GENERICS_STATIC_HELPER: { - AddIns(INTOP_CALL_HELPER_PP); + AddIns(INTOP_CALL_HELPER_P_P); m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(pFieldInfo->helper); m_pLastNewIns->data[1] = GetDataItemIndex(pResolvedToken->tokenContext); PushInterpType(InterpTypeByRef, NULL); @@ -2975,19 +3234,20 @@ void InterpCompiler::EmitLdLocA(int32_t var) m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); } -void InterpCompiler::EmitBox(StackInfo* pStackInfo, CORINFO_CLASS_HANDLE clsHnd, bool argByRef) +void InterpCompiler::EmitBox(StackInfo* pStackInfo, const CORINFO_GENERICHANDLE_RESULT &boxType, bool argByRef) { - CORINFO_CLASS_HANDLE boxedClsHnd = m_compHnd->getTypeForBox(clsHnd); - CorInfoHelpFunc helpFunc = m_compHnd->getBoxHelper(clsHnd); - AddIns(argByRef ? INTOP_BOX_PTR : INTOP_BOX); - m_pLastNewIns->SetSVar(pStackInfo->var); - - int32_t var = CreateVarExplicit(InterpTypeO, boxedClsHnd, INTERP_STACK_SLOT_SIZE); - new (pStackInfo) StackInfo(StackTypeO, boxedClsHnd, var); - - m_pLastNewIns->SetDVar(pStackInfo->var); - m_pLastNewIns->data[0] = GetDataItemIndex(clsHnd); - m_pLastNewIns->data[1] = GetDataItemIndexForHelperFtn(helpFunc); + CorInfoHelpFunc helpFunc = m_compHnd->getBoxHelper((CORINFO_CLASS_HANDLE)boxType.compileTimeHandle); + DeclarePointerIsClass((CORINFO_CLASS_HANDLE)boxType.compileTimeHandle); + if (argByRef) + { + EmitPushHelperCall_2(helpFunc, boxType, pStackInfo->var, StackTypeO, (CORINFO_CLASS_HANDLE)boxType.compileTimeHandle); + } + else + { + EmitPushHelperCall_Addr2(helpFunc, boxType, pStackInfo->var, StackTypeO, (CORINFO_CLASS_HANDLE)boxType.compileTimeHandle); + } + *pStackInfo = m_pStackPointer[-1]; + m_pStackPointer--; } void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) @@ -3254,6 +3514,7 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) int32_t token = getI4LittleEndian(m_ip + 1); void *str; InfoAccessType accessType = m_compHnd->constructStringLiteral(m_compScopeHnd, token, &str); + DeclarePointerIsString(str); assert(accessType == IAT_VALUE); // str should be forever pinned, so we can include its ref inside interpreter code AddIns(INTOP_LDPTR); @@ -4774,12 +5035,16 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) case CEE_BOX: { - CORINFO_CLASS_HANDLE clsHnd = ResolveClassToken(getU4LittleEndian(m_ip + 1)); + uint32_t token = getU4LittleEndian(m_ip + 1); CHECK_STACK(1); - if (m_compHnd->isValueClass(clsHnd)) + CORINFO_RESOLVED_TOKEN resolvedToken; + ResolveToken(token, CORINFO_TOKENKIND_Box, &resolvedToken); + if (m_compHnd->isValueClass(resolvedToken.hClass)) { + CORINFO_GENERICHANDLE_RESULT embedInfo; + m_compHnd->embedGenericHandle(&resolvedToken, false, m_methodInfo->ftn, &embedInfo); m_pStackPointer -= 1; - EmitBox(m_pStackPointer, clsHnd, false); + EmitBox(m_pStackPointer, embedInfo, false); m_pStackPointer++; } m_ip += 5; @@ -4789,19 +5054,67 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) case CEE_UNBOX: case CEE_UNBOX_ANY: { + uint32_t token = getU4LittleEndian(m_ip + 1); CHECK_STACK(1); - m_pStackPointer -= 1; - CORINFO_CLASS_HANDLE clsHnd = ResolveClassToken(getU4LittleEndian(m_ip + 1)); - CorInfoHelpFunc helpFunc = m_compHnd->getUnBoxHelper(clsHnd); - AddIns(opcode == CEE_UNBOX ? INTOP_UNBOX : INTOP_UNBOX_ANY); - m_pLastNewIns->SetSVar(m_pStackPointer[0].var); - if (opcode == CEE_UNBOX) - PushStackType(StackTypeI, NULL); + CORINFO_RESOLVED_TOKEN resolvedToken; + ResolveToken(token, CORINFO_TOKENKIND_Class, &resolvedToken); + CORINFO_GENERICHANDLE_RESULT embedInfo; + m_compHnd->embedGenericHandle(&resolvedToken, false, m_methodInfo->ftn, &embedInfo); + DeclarePointerIsClass((CORINFO_CLASS_HANDLE)embedInfo.compileTimeHandle); + + m_pStackPointer--; + if (*m_ip == CEE_UNBOX) + { + if (!m_compHnd->isValueClass(resolvedToken.hClass)) + { + NO_WAY("Unbox of a reference type is not valid"); + } + + CorInfoHelpFunc helpFunc = m_compHnd->getUnBoxHelper((CORINFO_CLASS_HANDLE)embedInfo.compileTimeHandle); + if (helpFunc == CORINFO_HELP_UNBOX) + { + EmitPushHelperCall_2(helpFunc, embedInfo, m_pStackPointer[0].var, StackTypeO, (CORINFO_CLASS_HANDLE)embedInfo.compileTimeHandle); + } + else + { + // NOTE: what we do here doesn't comply with the ECMA spec, see + // https://github.com/dotnet/runtime/issues/86203#issuecomment-1546709542 + + // Unbox nullable helper returns a struct type. + // We need to spill it to a temp so than can take the address of it. + + EmitPushUnboxAnyNullable(embedInfo, m_pStackPointer[0].var, g_stackTypeFromInterpType[GetInterpType(m_compHnd->asCorInfoType(resolvedToken.hClass))], resolvedToken.hClass); + m_pStackPointer--; + EmitLdLocA(m_pStackPointer[0].var); + } + } else - PushInterpType(GetInterpType(m_compHnd->asCorInfoType(clsHnd)), clsHnd); - m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); - m_pLastNewIns->data[0] = GetDataItemIndex(clsHnd); - m_pLastNewIns->data[1] = GetDataItemIndexForHelperFtn(helpFunc); + { + if (!m_compHnd->isValueClass(resolvedToken.hClass)) + { + // 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); + } + else + { + EmitPushUnboxAnyNullable(embedInfo, m_pStackPointer[0].var, g_stackTypeFromInterpType[GetInterpType(m_compHnd->asCorInfoType(resolvedToken.hClass))], resolvedToken.hClass); + } + } + } m_ip += 5; break; } @@ -4816,17 +5129,37 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) CORINFO_CLASS_HANDLE arrayClsHnd = resolvedToken.hClass; CorInfoHelpFunc helpFunc = m_compHnd->getNewArrHelper(arrayClsHnd); + DeclarePointerIsClass(arrayClsHnd); m_pStackPointer--; + int newArrLenVar = m_pStackPointer[0].var; + PushInterpType(InterpTypeO, NULL); - AddIns(INTOP_NEWARR); - m_pLastNewIns->SetSVar(m_pStackPointer[0].var); + if (m_compHnd->getClassAttribs(arrayClsHnd) & CORINFO_FLG_SHAREDINST) + { + CORINFO_GENERICHANDLE_RESULT embedInfo; + m_compHnd->embedGenericHandle(&resolvedToken, false, m_methodInfo->ftn, &embedInfo); - PushInterpType(InterpTypeO, NULL); - m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); + GenericHandleData handleData = GenericHandleToGenericHandleData(embedInfo); + assert(handleData.argType == HelperArgType::GenericResolution); - m_pLastNewIns->data[0] = GetDataItemIndex(arrayClsHnd); - m_pLastNewIns->data[1] = GetDataItemIndexForHelperFtn(helpFunc); + + 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); + } + else + { + AddIns(INTOP_NEWARR); + m_pLastNewIns->data[0] = GetDataItemIndex(arrayClsHnd); + m_pLastNewIns->data[1] = GetDataItemIndexForHelperFtn(helpFunc); + + m_pLastNewIns->SetSVar(newArrLenVar); + m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); + } m_ip += 5; break; @@ -5134,26 +5467,11 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) } case CEE_LDTOKEN: { - CORINFO_RESOLVED_TOKEN resolvedToken; ResolveToken(getU4LittleEndian(m_ip + 1), CORINFO_TOKENKIND_Ldtoken, &resolvedToken); - InterpEmbedGenericResult resolvedEmbedResult = EmitGenericHandle(&resolvedToken, GenericHandleEmbedOptions::None); - - if (resolvedEmbedResult.var != -1) - { - AddIns(INTOP_LDTOKEN_VAR); - m_pLastNewIns->SetSVar(resolvedEmbedResult.var); - } - else - { - AddIns(INTOP_LDTOKEN); - m_pLastNewIns->data[1] = resolvedEmbedResult.dataItemIndex; - } - - CORINFO_CLASS_HANDLE clsHnd = m_compHnd->getTokenTypeAsHandle(&resolvedToken); - PushStackType(StackTypeVT, clsHnd); - m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); + CORINFO_GENERICHANDLE_RESULT embedInfo; + m_compHnd->embedGenericHandle(&resolvedToken, false, m_methodInfo->ftn, &embedInfo); // see jit/importer.cpp CEE_LDTOKEN CorInfoHelpFunc helper; @@ -5163,10 +5481,12 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) } else if (resolvedToken.hMethod) { + DeclarePointerIsMethod(resolvedToken.hMethod); helper = CORINFO_HELP_METHODDESC_TO_STUBRUNTIMEMETHOD; } else if (resolvedToken.hClass) { + DeclarePointerIsClass(resolvedToken.hClass); helper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE; } else @@ -5174,8 +5494,9 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) helper = CORINFO_HELP_FAIL_FAST; assert(!"Token not resolved or resolved to unexpected type"); } - m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(helper); + CORINFO_CLASS_HANDLE clsHnd = m_compHnd->getTokenTypeAsHandle(&resolvedToken); + EmitPushHelperCall(helper, embedInfo, StackTypeVT, clsHnd); m_ip += 5; break; } @@ -5187,23 +5508,18 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) CORINFO_RESOLVED_TOKEN resolvedToken; ResolveToken(getU4LittleEndian(m_ip + 1), CORINFO_TOKENKIND_Casting, &resolvedToken); - bool isCastClass = (opcode == CEE_CASTCLASS); - - CorInfoHelpFunc castingHelper = m_compHnd->getCastingHelper(&resolvedToken, isCastClass); + CorInfoHelpFunc castingHelper = m_compHnd->getCastingHelper(&resolvedToken, *m_ip == CEE_CASTCLASS /* throwing */); - AddIns(INTOP_CALL_HELPER_PP_2); - m_pLastNewIns->data[0] = GetDataItemIndexForHelperFtn(castingHelper); - m_pLastNewIns->data[1] = GetDataItemIndex(resolvedToken.hClass); - m_pLastNewIns->SetSVar(m_pStackPointer[-1].var); + CORINFO_GENERICHANDLE_RESULT embedInfo; + InterpEmbedGenericResult result; + m_compHnd->embedGenericHandle(&resolvedToken, false, m_methodInfo->ftn, &embedInfo); m_pStackPointer--; - if (isCastClass) - PushInterpType(InterpTypeO, resolvedToken.hClass); - else - PushInterpType(InterpTypeI, NULL); - m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); + DeclarePointerIsClass((CORINFO_CLASS_HANDLE)embedInfo.compileTimeHandle); + EmitPushHelperCall_2(castingHelper, embedInfo, m_pStackPointer[0].var, g_stackTypeFromInterpType[*m_ip == CEE_CASTCLASS ? InterpTypeO : InterpTypeI], NULL); m_ip += 5; break; } + default: { AssertOpCodeNotImplemented(m_ip, m_ip - m_pILCode); @@ -5305,7 +5621,7 @@ void InterpCompiler::PrintMethodName(CORINFO_METHOD_HANDLE method) TArray methodName = ::PrintMethodName(m_compHnd, cls, method, &sig, /* includeAssembly */ false, - /* includeClass */ false, + /* includeClass */ true, /* includeClassInstantiation */ true, /* includeMethodInstantiation */ true, /* includeSignature */ true, @@ -5361,6 +5677,10 @@ void InterpCompiler::PrintIns(InterpInst *ins) callArgs++; } } + if (i + 1 < g_interpOpSVars[opcode]) + { + printf(":"); + } } else { @@ -5396,6 +5716,130 @@ const char* CorInfoHelperToName(CorInfoHelpFunc helper) return s_jitHelperNames[helper]; } +void PrintInterpGenericLookup(InterpGenericLookup* lookup) +{ + const char *lookupType; + if (lookup->lookupType == InterpGenericLookupType::Class) + lookupType = "Class"; + else if (lookup->lookupType == InterpGenericLookupType::Method) + lookupType = "Method"; + else if (lookup->lookupType == InterpGenericLookupType::This) + lookupType = "This"; + else + lookupType = "Unknown"; + + printf("%s,%p[", lookupType, lookup->signature); + if (lookup->indirections == 0) + { + printf("UseContextMethodTableOrMethodDescDirectly"); + } + else if (lookup->indirections == InterpGenericLookup_UseHelper) + { + printf("UseHelper"); + } + else + { + for (int i = 0; i < lookup->indirections; i++) + { + if (i > 0) + printf(","); + + printf("%d", (int)lookup->offsets[i]); + } + } + printf("]"); + if (lookup->sizeOffset != CORINFO_NO_SIZE_CHECK) + { + printf(" sizeOffset=%d", (int)lookup->sizeOffset); + } +} + +#ifdef DEBUG +void InterpCompiler::PrintNameInPointerMap(void* ptr) +{ + const char *name; + if (dn_simdhash_ptr_ptr_try_get_value(m_pointerToNameMap.GetValue(), ptr, (void**)&name)) + { + if (name == PointerIsMethodHandle) + { + printf("("); + PrintMethodName((CORINFO_METHOD_HANDLE)((size_t)ptr)); + printf(")"); + } + else if (name == PointerIsClassHandle) + { + printf("("); + PrintClassName((CORINFO_CLASS_HANDLE)((size_t)ptr)); + printf(")"); + } + else if (name == PointerIsStringLiteral) + { + printf("(\""); + CORINFO_String* stringPtr = (CORINFO_String*)ptr; + unsigned i = 0; + for (; i < stringPtr->stringLen && i < 50; i++) + { + char16_t c16 = stringPtr->chars[i]; + if (c16 > 0x7F) + { + printf("?"); + continue; + } + char c = (char)c16; + if (c == '\n') + printf("\\n"); + else if (c == '\r') + printf("\\r"); + else if (c == '\t') + printf("\\t"); + else if (c == '\"') + printf("\\\""); + else if (c == '\\') + printf("\\\\"); + else if (isprint((unsigned char)c)) + printf("%c", c); + else + printf("\\x%02x", (unsigned char)c); + } + printf("\""); + if (i < stringPtr->stringLen) + { + printf("..."); + } + printf(")"); + } + else + { + printf("(%s)", name); + } + } + return; +} +#endif + +void InterpCompiler::PrintPointer(void* pointer) +{ + printf("%p ", pointer); +#ifdef DEBUG + PrintNameInPointerMap(pointer); +#endif +} + +void InterpCompiler::PrintHelperFtn(void* helperDirectOrIndirect) +{ + void* helperAddr = helperDirectOrIndirect; + + if (((size_t)helperDirectOrIndirect) & INTERP_INDIRECT_HELPER_TAG) + { + helperAddr = (void*)(((size_t)helperDirectOrIndirect) & ~INTERP_INDIRECT_HELPER_TAG); + printf(" (indirect)"); + } + else + printf(" (direct)"); + + PrintPointer(helperAddr); +} + void InterpCompiler::PrintInsData(InterpInst *ins, int32_t insOffset, const int32_t *pData, int32_t opcode) { switch (g_interpOpArgType[opcode]) { @@ -5435,33 +5879,20 @@ void InterpCompiler::PrintInsData(InterpInst *ins, int32_t insOffset, const int3 break; case InterpOpLdPtr: { - printf("%p", (void*)GetDataItemAtIndex(pData[0])); + PrintPointer((void*)GetDataItemAtIndex(pData[0])); + break; + } + case InterpOpGenericHelperFtn: + { + PrintHelperFtn((void*)GetDataItemAtIndex(pData[0])); + InterpGenericLookup *pGenericLookup = (InterpGenericLookup*)GetAddrOfDataItemAtIndex(pData[1]); + PrintInterpGenericLookup(pGenericLookup); break; } case InterpOpGenericLookup: { - CORINFO_RUNTIME_LOOKUP *pGenericLookup = (CORINFO_RUNTIME_LOOKUP*)GetDataItemAtIndex(pData[0]); - printf("%s,%p[", CorInfoHelperToName(pGenericLookup->helper), pGenericLookup->signature); - for (int i = 0; i < pGenericLookup->indirections; i++) - { - if (i > 0) - printf(","); - - if (i == 0 && pGenericLookup->indirectFirstOffset) - printf("*"); - if (i == 1 && pGenericLookup->indirectSecondOffset) - printf("*"); - printf("%d", (int)pGenericLookup->offsets[i]); - } - printf("]"); - if (pGenericLookup->sizeOffset != CORINFO_NO_SIZE_CHECK) - { - printf(" sizeOffset=%d", (int)pGenericLookup->sizeOffset); - } - if (pGenericLookup->testForNull) - { - printf(" testForNull"); - } + InterpGenericLookup *pGenericLookup = (InterpGenericLookup*)GetAddrOfDataItemAtIndex(pData[0]); + PrintInterpGenericLookup(pGenericLookup); } break; case InterpOpSwitch: @@ -5497,11 +5928,29 @@ void InterpCompiler::PrintInsData(InterpInst *ins, int32_t insOffset, const int3 } case InterpOpHelperFtn: { - size_t helperDirectOrIndirect = (size_t)m_dataItems.Get(*pData); - if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG) - printf(" (indirect) %p", (void*)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG)); - else - printf(" (direct) %p", (void*)helperDirectOrIndirect); + PrintHelperFtn((void*)GetDataItemAtIndex(pData[0])); + printf(", "); + PrintPointer((void*)GetDataItemAtIndex(pData[1])); + break; + } + case InterpOpPointerHelperFtn: + { + PrintPointer((void*)GetDataItemAtIndex(pData[0])); + printf(", "); + PrintHelperFtn((void*)GetDataItemAtIndex(pData[1])); + break; + } + case InterpOpPointerInt: + { + PrintPointer((void*)GetDataItemAtIndex(pData[0])); + printf(", %d", pData[1]); + break; + } + case InterpOpGenericLookupInt: + { + InterpGenericLookup *pGenericLookup = (InterpGenericLookup*)GetAddrOfDataItemAtIndex(pData[0]); + PrintInterpGenericLookup(pGenericLookup); + printf(", %d", pData[1]); break; } default: diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index b8cbd333ef8dc9..5349da7a98f082 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -7,6 +7,53 @@ #include "intops.h" #include "datastructs.h" #include "enum_class_flags.h" +#include + +#include "../../native/containers/dn-simdhash.h" +#include "../../native/containers/dn-simdhash-specializations.h" +#include "../../native/containers/dn-simdhash-utils.h" + +class dn_simdhash_ptr_ptr_holder +{ + dn_simdhash_ptr_ptr_t *Value; +public: + dn_simdhash_ptr_ptr_holder() : + Value(nullptr) + { + } + + dn_simdhash_ptr_ptr_t* GetValue() + { + if (!Value) + Value = dn_simdhash_ptr_ptr_new(0, nullptr); + return Value; + } + + dn_simdhash_ptr_ptr_holder(const dn_simdhash_ptr_ptr_holder&) = delete; + dn_simdhash_ptr_ptr_holder& operator=(const dn_simdhash_ptr_ptr_holder&) = delete; + dn_simdhash_ptr_ptr_holder(dn_simdhash_ptr_ptr_holder&& other) + { + Value = other.Value; + other.Value = nullptr; + } + dn_simdhash_ptr_ptr_holder& operator=(dn_simdhash_ptr_ptr_holder&& other) + { + if (this != &other) + { + if (Value != nullptr) + dn_simdhash_free(Value); + Value = other.Value; + other.Value = nullptr; + } + return *this; + } + + ~dn_simdhash_ptr_ptr_holder() + { + if (Value != nullptr) + dn_simdhash_free(Value); + } +}; struct InterpException { @@ -29,6 +76,104 @@ INTERPRETER_NORETURN void NO_WAY(const char* message); INTERPRETER_NORETURN void BADCODE(const char* message); INTERPRETER_NORETURN void NOMEM(); +class InterpCompiler; + +class InterpDataItemIndexMap +{ + struct VarSizedData + { + VarSizedData(size_t size) : size(size) + { + } + + const size_t size; + uint32_t SizeOf() + { + return (uint32_t)(size * sizeof(void*)); + } + }; + + template + struct VarSizedDataWithPayload : public VarSizedData + { + VarSizedDataWithPayload() : VarSizedData(sizeof(VarSizedDataWithPayload)/sizeof(void*)) + { + assert(SizeOf() == sizeof(VarSizedDataWithPayload)); + } + T payload; + }; + + dn_simdhash_ght_t* _hash = nullptr; + TArray *_dataItems = nullptr; // Actual data items stored here, indexed by the value in the hash table. This pointer is owned by the InterpCompiler class. + InterpCompiler* _compiler = nullptr; + + static unsigned int HashVarSizedData(const void *voidKey) + { + VarSizedData* key = (VarSizedData*)voidKey; + return MurmurHash3_32((const uint8_t*)key, key->SizeOf(), 0); + } + + static int32_t KeyEqualVarSizeData(const void * aVoid, const void * bVoid) + { + VarSizedData* keyA = (VarSizedData*)aVoid; + VarSizedData* keyB = (VarSizedData*)bVoid; + + if (keyA->size != keyB->size) + return 0; + + if (memcmp(aVoid, bVoid, keyA->SizeOf()) == 0) + return 1; + else + return 0; + } + + dn_simdhash_ght_t* GetHash() + { + if (_hash == nullptr) + _hash = dn_simdhash_ght_new(HashVarSizedData, KeyEqualVarSizeData, 0, NULL); + if (_hash == nullptr) + NOMEM(); + + return _hash; + } + +public: + InterpDataItemIndexMap() = default; + InterpDataItemIndexMap(const InterpDataItemIndexMap&) = delete; + InterpDataItemIndexMap& operator=(const InterpDataItemIndexMap&) = delete; + + void Init(TArray *dataItems, InterpCompiler* compiler) + { + _compiler = compiler; + _dataItems = dataItems; + } + + int32_t GetDataItemIndex(const InterpGenericLookup& lookup) + { + size_t sizeOfFieldsConcatenated = sizeof(InterpGenericLookup::offsets) + + sizeof(InterpGenericLookup::indirections) + + sizeof(InterpGenericLookup::sizeOffset) + + sizeof(InterpGenericLookup::lookupType) + + sizeof(InterpGenericLookup::signature); + + size_t sizeOfStruct = sizeof(InterpGenericLookup); + + assert(sizeOfFieldsConcatenated == sizeOfStruct); // Assert that there is no padding in the struct, so a fixed size hash unaware of padding is safe to use + return GetDataItemIndexForT(lookup); + } + + int32_t GetDataItemIndex(void* lookup) + { + // TODO: this is a bit more expensive than necessary size we are allocating a full varsized struct for a single pointer + // Consider optimizing this to use a seperate hashtable like a dn_simdhash_ptr_ptr_t if it becomes a bottleneck + return GetDataItemIndexForT(lookup); + } + +private: + template + int32_t GetDataItemIndexForT(const T& lookup); +}; + TArray PrintMethodName(COMP_HANDLE comp, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE methHnd, @@ -348,10 +493,67 @@ class InterpCompiler CORINFO_MODULE_HANDLE m_compScopeHnd; COMP_HANDLE m_compHnd; CORINFO_METHOD_INFO* m_methodInfo; + + void DeclarePointerIsClass(CORINFO_CLASS_HANDLE clsHnd) + { +#ifdef DEBUG + void *ptr = (void*)clsHnd; + if (!PointerInNameMap(ptr)) + { + AddPointerToNameMap(ptr, PointerIsClassHandle); + } +#endif // DEBUG + } + + void DeclarePointerIsMethod(CORINFO_METHOD_HANDLE methodHnd) + { +#ifdef DEBUG + void *ptr = (void*)methodHnd; + if (!PointerInNameMap(ptr)) + { + AddPointerToNameMap(ptr, PointerIsMethodHandle); + } +#endif // DEBUG + } + + void DeclarePointerIsString(void* stringLiteral) + { +#ifdef DEBUG + void *ptr = (void*)stringLiteral; + if (!PointerInNameMap(ptr)) + { + AddPointerToNameMap(ptr, PointerIsStringLiteral); + } +#endif // DEBUG + } + #ifdef DEBUG CORINFO_CLASS_HANDLE m_classHnd; TArray m_methodName; bool m_verbose = false; + + const char* PointerIsClassHandle = (const char*)0x1; + const char* PointerIsMethodHandle = (const char*)0x2; + const char* PointerIsStringLiteral = (const char*)0x3; + + dn_simdhash_ptr_ptr_holder m_pointerToNameMap; + bool PointerInNameMap(void* ptr) + { + void* result; + if (dn_simdhash_ptr_ptr_try_get_value(m_pointerToNameMap.GetValue(), ptr, &result)) + { + return true; + } + return false; + } + void AddPointerToNameMap(void* ptr, const char* name) + { + if (!PointerInNameMap(ptr)) + { + dn_simdhash_ptr_ptr_try_add(m_pointerToNameMap.GetValue(), ptr, (void*)name); + } + } + void PrintNameInPointerMap(void* ptr); #endif static int32_t InterpGetMovForType(InterpType interpType, bool signExtend); @@ -370,10 +572,20 @@ class InterpCompiler // instruction can request an index for some data (like a MethodDesc pointer), that it // will then embed in the instruction stream. The data item table will be referenced // from the interpreter code header during execution. - // FIXME during compilation this should be a hashtable for fast lookup of duplicates TArray m_dataItems; - int32_t GetDataItemIndex(void* data); + + InterpDataItemIndexMap m_genericLookupToDataItemIndex; + int32_t GetDataItemIndex(void* data) + { + return m_genericLookupToDataItemIndex.GetDataItemIndex(data); + } + int32_t GetDataItemIndex(const InterpGenericLookup& data) + { + return m_genericLookupToDataItemIndex.GetDataItemIndex(data); + } + void* GetDataItemAtIndex(int32_t index); + void* GetAddrOfDataItemAtIndex(int32_t index); int32_t GetMethodDataItemIndex(CORINFO_METHOD_HANDLE mHandle); int32_t GetDataItemIndexForHelperFtn(CorInfoHelpFunc ftn); @@ -403,14 +615,49 @@ class InterpCompiler VarOnly = 1, EmbedParent = 2, }; + + enum class HelperArgType + { + GenericResolution, + Value + }; + + struct TokenArg + { + CORINFO_RESOLVED_TOKEN* token; + InterpCompiler::GenericHandleEmbedOptions options; + }; + + struct GenericHandleData + { + GenericHandleData(int genericVar, int dataItemIndex) + : argType(HelperArgType::GenericResolution), genericVar(genericVar), dataItemIndex(dataItemIndex) {} + + GenericHandleData(int dataItemIndex) + : argType(HelperArgType::Value), genericVar(-1), dataItemIndex(dataItemIndex) {} + + GenericHandleData() = default; + + HelperArgType argType = HelperArgType::Value; + int genericVar = -1; // This will be set to the var of the generic context argument if argType == HelperArgType::GenericResolution + int dataItemIndex = 0; + }; + + GenericHandleData GenericHandleToGenericHandleData(const CORINFO_GENERICHANDLE_RESULT& embedInfo); InterpEmbedGenericResult EmitGenericHandle(CORINFO_RESOLVED_TOKEN* resolvedToken, GenericHandleEmbedOptions options); // Do a generic handle lookup and acquire the result as either a var or a data item. int EmitGenericHandleAsVar(const CORINFO_GENERICHANDLE_RESULT &embedInfo); // Emit a generic dictionary lookup and push the result onto the interpreter stack + void CopyToInterpGenericLookup(InterpGenericLookup* dst, const CORINFO_RUNTIME_LOOKUP *src); void EmitPushCORINFO_LOOKUP(const CORINFO_LOOKUP& lookup); void EmitPushLdvirtftn(int thisVar, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo); + void EmitPushHelperCall_2(const CorInfoHelpFunc ftn, const CORINFO_GENERICHANDLE_RESULT& arg1, int arg2, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack); + void EmitPushHelperCall_Addr2(const CorInfoHelpFunc ftn, const CORINFO_GENERICHANDLE_RESULT& arg1, int arg2, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack); + void EmitPushHelperCall(const CorInfoHelpFunc ftn, const CORINFO_GENERICHANDLE_RESULT& arg1, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack); + void EmitPushUnboxAny(const CORINFO_GENERICHANDLE_RESULT& arg1, int arg2, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack); + void EmitPushUnboxAnyNullable(const CORINFO_GENERICHANDLE_RESULT& arg1, int arg2, StackType resultStackType, CORINFO_CLASS_HANDLE clsHndStack); void* AllocMethodData(size_t numBytes); public: @@ -517,7 +764,7 @@ class InterpCompiler void EmitStaticFieldAddress(CORINFO_FIELD_INFO *pFieldInfo, CORINFO_RESOLVED_TOKEN *pResolvedToken); void EmitStaticFieldAccess(InterpType interpFieldType, CORINFO_FIELD_INFO *pFieldInfo, CORINFO_RESOLVED_TOKEN *pResolvedToken, bool isLoad); void EmitLdLocA(int32_t var); - void EmitBox(StackInfo* pStackInfo, CORINFO_CLASS_HANDLE clsHnd, bool argByRef); + void EmitBox(StackInfo* pStackInfo, const CORINFO_GENERICHANDLE_RESULT &boxType, bool argByRef); // Var Offset allocator TArray *m_pActiveCalls; @@ -556,6 +803,8 @@ class InterpCompiler void PrintCode(); void PrintBBCode(InterpBasicBlock *pBB); void PrintIns(InterpInst *ins); + void PrintPointer(void* pointer); + void PrintHelperFtn(void* helperAddr); void PrintInsData(InterpInst *ins, int32_t offset, const int32_t *pData, int32_t opcode); void PrintCompiledCode(); void PrintCompiledIns(const int32_t *ip, const int32_t *start); @@ -586,4 +835,41 @@ class InterpCompiler return compiler->AllocMemPool0(sz); } +template +int32_t InterpDataItemIndexMap::GetDataItemIndexForT(const T& lookup) +{ + VarSizedDataWithPayload key; + key.payload = lookup; + + dn_simdhash_ght_t* hash = GetHash(); + + void* resultAsPtr = nullptr; + if (dn_simdhash_ght_try_get_value(hash, (void*)&key, &resultAsPtr)) + { + return (int32_t)(size_t)resultAsPtr; + } + + // Assert that there is no padding in the struct, so a fixed size hash unaware of padding is safe to use + assert(sizeof(VarSizedData) == sizeof(void*)); + assert(sizeof(T) % sizeof(void*) == 0); + assert(sizeof(VarSizedDataWithPayload) == sizeof(T) + sizeof(void*)); + + void** LookupAsPtrs = (void**)&lookup; + int32_t dataItemIndex = _dataItems->Add(LookupAsPtrs[0]); + for (unsigned i = 1; i < sizeof(T) / sizeof(void*); i++) + { + _dataItems->Add(LookupAsPtrs[i]); + } + + void* hashItemPayload = _compiler->AllocMemPool0(sizeof(VarSizedDataWithPayload)); + if (hashItemPayload == nullptr) + NOMEM(); + + VarSizedDataWithPayload* pLookup = new(hashItemPayload) VarSizedDataWithPayload(); + memcpy(&pLookup->payload, &lookup, sizeof(T)); + + dn_simdhash_ght_insert(hash, (void*)pLookup, (void*)(size_t)dataItemIndex); + return dataItemIndex; +} + #endif //_COMPILER_H_ diff --git a/src/coreclr/interpreter/datastructs.h b/src/coreclr/interpreter/datastructs.h index 706821f76c2505..3228ca7682ea30 100644 --- a/src/coreclr/interpreter/datastructs.h +++ b/src/coreclr/interpreter/datastructs.h @@ -78,7 +78,7 @@ class TArray free(m_array); } - int32_t GetSize() + int32_t GetSize() const { return m_size; } @@ -119,7 +119,7 @@ class TArray } // Returns a pointer to the element at the specified index. - T* GetUnderlyingArray() + T* GetUnderlyingArray() const { return m_array; } diff --git a/src/coreclr/interpreter/interpreter.h b/src/coreclr/interpreter/interpreter.h index 8ef12000884d7b..90c62b98af6dfe 100644 --- a/src/coreclr/interpreter/interpreter.h +++ b/src/coreclr/interpreter/interpreter.h @@ -25,8 +25,8 @@ #define ALIGN_UP_TO(val,align) ((((size_t)val) + (size_t)((align) - 1)) & (~((size_t)(align - 1)))) -#ifdef _DEBUG +#ifdef DEBUG extern "C" void assertAbort(const char* why, const char* file, unsigned line); #undef assert #define assert(p) (void)((p) || (assertAbort(#p, __FILE__, __LINE__), 0)) -#endif // _DEBUG +#endif // DEBUG diff --git a/src/coreclr/interpreter/interpretershared.h b/src/coreclr/interpreter/interpretershared.h index fe99ccb8ae6bc0..388fa3502e8065 100644 --- a/src/coreclr/interpreter/interpretershared.h +++ b/src/coreclr/interpreter/interpretershared.h @@ -120,4 +120,38 @@ const CORINFO_CLASS_HANDLE NO_CLASS_HANDLE = nullptr; const CORINFO_FIELD_HANDLE NO_FIELD_HANDLE = nullptr; const CORINFO_METHOD_HANDLE NO_METHOD_HANDLE = nullptr; +enum class InterpGenericLookupType : uint32_t +{ + This, + Method, + Class +}; + +const int InterpGenericLookup_MaxIndirections = 4; + +const uint16_t InterpGenericLookup_UseHelper = InterpGenericLookup_MaxIndirections + 1; + +struct InterpGenericLookup +{ + // This is signature you must pass back to the runtime lookup helper + void* signature; + + // Here is the helper you must call. It is one of CORINFO_HELP_RUNTIMEHANDLE_* helpers. + + InterpGenericLookupType lookupType; + + // Number of indirections to get there + // InterpGenericLookup_UseHelper = don't know how to get it, so use helper function at run-time instead + // 0 = use the this pointer itself (e.g. token is C inside code in sealed class C) + // or method desc itself (e.g. token is method void M::mymeth() inside code in M::mymeth) + // Otherwise, follow each byte-offset stored in the "offsets[]" array (may be negative) + uint16_t indirections; + + // If this is not CORINFO_NO_SIZE_CHECK, then the last indirection used needs to be checked + // against the size stored at this offset from the previous indirection pointer. The logic + // here is to allow for the generic dictionary to change in size without requiring any locks. + uint16_t sizeOffset; + uint16_t offsets[InterpGenericLookup_MaxIndirections]; +}; + #endif diff --git a/src/coreclr/interpreter/intops.def b/src/coreclr/interpreter/intops.def index 2aa99f248a58c5..54a0795ac07d85 100644 --- a/src/coreclr/interpreter/intops.def +++ b/src/coreclr/interpreter/intops.def @@ -23,8 +23,10 @@ OPDEF(INTOP_LDC_R8, "ldc.r8", 4, 1, 0, InterpOpDouble) OPDEF(INTOP_LDPTR, "ldptr", 3, 1, 0, InterpOpLdPtr) OPDEF(INTOP_LDPTR_DEREF, "ldptr.deref", 3, 1, 0, InterpOpLdPtr) -OPDEF(INTOP_NEWARR, "newarr", 5, 1, 1, InterpOpInt) -OPDEF(INTOP_NEWMDARR, "newmdarr", 5, 1, 1, InterpOpTwoInts) +OPDEF(INTOP_NEWARR, "newarr", 5, 1, 1, InterpOpPointerHelperFtn) +OPDEF(INTOP_NEWARR_GENERIC, "newarr.generic", 6, 1, 2, InterpOpGenericHelperFtn) +OPDEF(INTOP_NEWMDARR, "newmdarr", 5, 1, 1, InterpOpPointerInt) +OPDEF(INTOP_NEWMDARR_GENERIC, "newmdarr.generic", 6, 1, 2, InterpOpGenericLookupInt) OPDEF(INTOP_LDELEM_I1, "ldelem.i1", 4, 1, 2, InterpOpNoArgs) OPDEF(INTOP_LDELEM_U1, "ldelem.u1", 4, 1, 2, InterpOpNoArgs) @@ -52,9 +54,6 @@ OPDEF(INTOP_STELEM_VT_NOREF, "stelem.vt.noref", 5, 0, 3, InterpOpInt) OPDEF(INTOP_LDELEMA, "ldelema", 5, 1, 2, InterpOpInt) OPDEF(INTOP_LDELEMA_REF, "ldelema.ref", 6, 1, 2, InterpOpTwoInts) -OPDEF(INTOP_LDTOKEN, "ldtoken", 4, 1, 0, InterpOpTwoInts) // [token data item] [conversion helper func] -OPDEF(INTOP_LDTOKEN_VAR, "ldtoken.var", 4, 1, 1, InterpOpInt) // [var index] [conversion helper func] - OPDEF(INTOP_MOV_I4_I1, "mov.i4.i1", 3, 1, 1, InterpOpNoArgs) OPDEF(INTOP_MOV_I4_U1, "mov.i4.u1", 3, 1, 1, InterpOpNoArgs) OPDEF(INTOP_MOV_I4_I2, "mov.i4.i2", 3, 1, 1, InterpOpNoArgs) @@ -206,10 +205,8 @@ OPDEF(INTOP_CONV_OVF_I8_R8, "conv.ovf.i8.r8", 3, 1, 1, InterpOpNoArgs) OPDEF(INTOP_CONV_OVF_U8_R4, "conv.ovf.u8.r4", 3, 1, 1, InterpOpNoArgs) OPDEF(INTOP_CONV_OVF_U8_R8, "conv.ovf.u8.r8", 3, 1, 1, InterpOpNoArgs) -OPDEF(INTOP_BOX, "box", 5, 1, 1, InterpOpClassHandle) // [class handle data item] [helper data item] -OPDEF(INTOP_BOX_PTR, "box.ptr", 5, 1, 1, InterpOpClassHandle) // [class handle data item] [helper data item] -OPDEF(INTOP_UNBOX, "unbox", 5, 1, 1, InterpOpClassHandle) // [class handle data item] [helper data item] -OPDEF(INTOP_UNBOX_ANY, "unbox.any", 5, 1, 1, InterpOpClassHandle) // [class handle data item] [helper data item] +OPDEF(INTOP_UNBOX_ANY, "unbox.any", 5, 1, 1, InterpOpHelperFtn) // [class handle data item] [helper data item] +OPDEF(INTOP_UNBOX_ANY_GENERIC, "unbox.any.generic", 6, 1, 2, InterpOpGenericHelperFtn) // [class handle data item] [helper data item] // Unary operations end OPDEF(INTOP_ADD_I4_IMM, "add.i4.imm", 4, 1, 1, InterpOpInt) @@ -339,15 +336,30 @@ OPDEF(INTOP_CALL, "call", 4, 1, 1, InterpOpMethodHandle) OPDEF(INTOP_CALLI, "calli", 5, 1, 2, InterpOpLdPtr) OPDEF(INTOP_CALLVIRT, "callvirt", 4, 1, 1, InterpOpMethodHandle) OPDEF(INTOP_NEWOBJ, "newobj", 5, 1, 1, InterpOpMethodHandle) -OPDEF(INTOP_NEWOBJ_VAR, "newobj.var", 5, 1, 2, InterpOpMethodHandle) +OPDEF(INTOP_NEWOBJ_GENERIC, "newobj.generic", 6, 1, 2, InterpOpMethodHandle) OPDEF(INTOP_NEWOBJ_VT, "newobj.vt", 5, 1, 1, InterpOpMethodHandle) -OPDEF(INTOP_CALL_HELPER_PP, "call.helper.pp", 4, 1, 0, InterpOpHelperFtn) -OPDEF(INTOP_CALL_HELPER_PP_2, "call.helper.pp.2", 5, 1, 1, InterpOpHelperFtn) +// The following helper call instructions exist in 2 variants, one for normal methods, and one for cases where a shared generic lookup is needed. +// In the case where a shared generic lookup is needed an extra argument is passed as an svar, which is a pointer to the generic context. +// If there is a generic context argument it is always the first SVar to the instruction. + +// Call a helper function with a signature of PointerSizedDest helper(TypeOrGenericArg)) +OPDEF(INTOP_CALL_HELPER_P_P, "call.helper.p.p", 4, 1, 0, InterpOpHelperFtn) +OPDEF(INTOP_CALL_HELPER_P_PP, "call.helper.p.pp", 5, 1, 1, InterpOpHelperFtn) + +// Call a helper function with a signature of PointerSizedDest helper(TypeOrGenericArg, SVar)) +OPDEF(INTOP_CALL_HELPER_P_G, "call.helper.p.g", 5, 1, 1, InterpOpGenericHelperFtn) +OPDEF(INTOP_CALL_HELPER_P_GP, "call.helper.p.gp", 6, 1, 2, InterpOpGenericHelperFtn) + +// Call a helper function with a signature of PointerSizedDest helper(TypeOrGenericArg, AddrOfSVar)) +OPDEF(INTOP_CALL_HELPER_P_PA, "call.helper.p.pa", 5, 1, 1, InterpOpHelperFtn) +OPDEF(INTOP_CALL_HELPER_P_GA, "call.helper.p.ga", 6, 1, 2, InterpOpGenericHelperFtn) + +// Call a helper function with a signature of void helper(AddrOfDVar, TypeOrGenericArg, SVar)) +OPDEF(INTOP_CALL_HELPER_V_APP, "call.helper.v.app", 5, 1, 1, InterpOpHelperFtn) +OPDEF(INTOP_CALL_HELPER_V_AGP, "call.helper.v.agp", 6, 1, 2, InterpOpGenericHelperFtn) -OPDEF(INTOP_GENERICLOOKUP_METHOD, "generic.method", 4, 1, 1, InterpOpGenericLookup) -OPDEF(INTOP_GENERICLOOKUP_CLASS, "generic.class", 4, 1, 1, InterpOpGenericLookup) -OPDEF(INTOP_GENERICLOOKUP_THIS, "generic.this", 4, 1, 1, InterpOpGenericLookup) +OPDEF(INTOP_GENERICLOOKUP, "generic", 4, 1, 1, InterpOpGenericLookup) OPDEF(INTOP_CALL_FINALLY, "call.finally", 2, 0, 0, InterpOpBranch) diff --git a/src/coreclr/interpreter/intops.h b/src/coreclr/interpreter/intops.h index 82ab2f66bbfb55..4c79076ae2fa25 100644 --- a/src/coreclr/interpreter/intops.h +++ b/src/coreclr/interpreter/intops.h @@ -23,8 +23,12 @@ typedef enum InterpOpMethodHandle, InterpOpClassHandle, InterpOpGenericLookup, + InterpOpGenericHelperFtn, InterpOpLdPtr, InterpOpHelperFtn, + InterpOpPointerHelperFtn, + InterpOpPointerInt, + InterpOpGenericLookupInt, } InterpOpArgType; extern const uint8_t g_interpOpLen[]; diff --git a/src/coreclr/interpreter/stackmap.cpp b/src/coreclr/interpreter/stackmap.cpp index ec26d29fa7eea5..340b5dbb7fce76 100644 --- a/src/coreclr/interpreter/stackmap.cpp +++ b/src/coreclr/interpreter/stackmap.cpp @@ -6,19 +6,15 @@ #include "interpreter.h" #include "stackmap.h" -extern "C" { - #include "../../native/containers/dn-simdhash.h" - #include "../../native/containers/dn-simdhash-specializations.h" +#include "../../native/containers/dn-simdhash.h" +#include "../../native/containers/dn-simdhash-specializations.h" +#include "../../native/containers/dn-simdhash-utils.h" - void assertAbort(const char* why, const char* file, unsigned line); +extern "C" void assertAbort(const char* why, const char* file, unsigned line); - void - dn_simdhash_assert_fail (const char* file, int line, const char* condition); - - void - dn_simdhash_assert_fail (const char* file, int line, const char* condition) { - assertAbort(condition, file, line); - } +void +dn_simdhash_assert_fail (const char* file, int line, const char* condition) { + assertAbort(condition, file, line); } thread_local dn_simdhash_ptr_ptr_t *t_sharedStackMapLookup = nullptr; diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 372e8b2cfbafc6..723deb9ca447ab 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -109,6 +109,8 @@ typedef void* (*HELPER_FTN_PP)(void*); typedef void* (*HELPER_FTN_BOX_UNBOX)(MethodTable*, void*); typedef Object* (*HELPER_FTN_NEWARR)(MethodTable*, intptr_t); typedef void* (*HELPER_FTN_PP_2)(void*, void*); +typedef void (*HELPER_FTN_V_PPP)(void*, void*, void*); + InterpThreadContext::InterpThreadContext() { @@ -291,6 +293,79 @@ template void ConvOvfHelper(int8_t *stack, } } +void* DoGenericLookup(void* genericVarAsPtr, InterpGenericLookup* pLookup) +{ + // TODO! If this becomes a performance bottleneck, we could expand out the various permutations of this + // so that we have 24 versions of lookup (or 48 is we allow for avoiding the null check), do the only + // if check to figure out which one to use, and then have the rest of the logic be straight-line code. + MethodTable *pMT = nullptr; + MethodDesc* pMD = nullptr; + uint8_t* lookup; + if (pLookup->lookupType == InterpGenericLookupType::This) + { + OBJECTREF thisPtr = ObjectToOBJECTREF((Object*)genericVarAsPtr); + NULL_CHECK(thisPtr); + pMT = thisPtr->GetMethodTable(); + lookup = (uint8_t*)pMT; + } + else if (pLookup->lookupType == InterpGenericLookupType::Class) + { + pMT = (MethodTable*)genericVarAsPtr; + lookup = (uint8_t*)pMT; + } + else + { + assert(pLookup->lookupType == InterpGenericLookupType::Method); + pMD = (MethodDesc*)genericVarAsPtr; + lookup = (uint8_t*)pMD; + } + void* result = 0; + + uint16_t indirections = pLookup->indirections; + if (indirections == 0) + { + return lookup; + } + else if (indirections != InterpGenericLookup_UseHelper) + { + lookup = VolatileLoadWithoutBarrier((uint8_t**)(lookup + pLookup->offsets[0])); + if (indirections >= 3) + lookup = VolatileLoadWithoutBarrier((uint8_t**)(lookup + pLookup->offsets[1])); + if (indirections >= 4) + lookup = VolatileLoadWithoutBarrier((uint8_t**)(lookup + pLookup->offsets[2])); + do { + size_t lastOffset = pLookup->offsets[indirections - 1]; + if (pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK) + { + /* Last indirection to check is the size*/ + size_t size = VolatileLoadWithoutBarrier((size_t*)(lookup + pLookup->sizeOffset)); + if (size <= lastOffset) + { + result = GenericHandleCommon(pMD, pMT, pLookup->signature); + break; + } + } + lookup = VolatileLoadWithoutBarrier((uint8_t**)(lookup + lastOffset)); + + if (lookup == NULL) + { + result = GenericHandleCommon(pMD, pMT, pLookup->signature); + break; + } + result = lookup; + } while (0); + } + else + { + return GenericHandleCommon(pMD, pMT, pLookup->signature); + } + return result; +} + +#define DO_GENERIC_LOOKUP(genericVar, lookupIndex) \ + InterpGenericLookup *pLookup = (InterpGenericLookup*)&pMethod->pDataItems[ip[lookupIndex]]; \ + void* result = DoGenericLookup(LOCAL_VAR(genericVar, void*), pLookup) + void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFrame *pFrame, InterpThreadContext *pThreadContext, ExceptionClauseArgs *pExceptionClauseArgs) { CONTRACTL @@ -1512,10 +1587,10 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr break; } - case INTOP_CALL_HELPER_PP: - case INTOP_CALL_HELPER_PP_2: + case INTOP_CALL_HELPER_P_P: + case INTOP_CALL_HELPER_P_PP: { - int base = (*ip == INTOP_CALL_HELPER_PP) ? 2 : 3; + int base = (*ip == INTOP_CALL_HELPER_P_P) ? 2 : 3; HELPER_FTN_PP helperFtn = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[base]]); void* helperArg = pMethod->pDataItems[ip[base + 1]]; @@ -1523,7 +1598,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr // This can call either native or compiled managed code. For an interpreter // only configuration, this might be problematic, at least performance wise. - if (*ip == INTOP_CALL_HELPER_PP) + if (*ip == INTOP_CALL_HELPER_P_P) { LOCAL_VAR(ip[1], void*) = ((HELPER_FTN_PP)helperFtn)(helperArg); ip += 4; @@ -1533,9 +1608,81 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr LOCAL_VAR(ip[1], void*) = ((HELPER_FTN_PP_2)helperFtn)(helperArg, LOCAL_VAR(ip[2], void*)); ip += 5; } + break; + } + + case INTOP_CALL_HELPER_P_G: + case INTOP_CALL_HELPER_P_GP: + { + int base = (*ip == INTOP_CALL_HELPER_P_G) ? 3 : 4; + + DO_GENERIC_LOOKUP(ip[2], base + 1); + + HELPER_FTN_PP helperFtn = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[base]]); + void* helperArg = result; + + // This can call either native or compiled managed code. For an interpreter + // only configuration, this might be problematic, at least performance wise. + + if (*ip == INTOP_CALL_HELPER_P_G) + { + LOCAL_VAR(ip[1], void*) = ((HELPER_FTN_PP)helperFtn)(helperArg); + ip += 5; + } + else + { + LOCAL_VAR(ip[1], void*) = ((HELPER_FTN_PP_2)helperFtn)(helperArg, LOCAL_VAR(ip[3], void*)); + ip += 6; + } + break; + } + + case INTOP_CALL_HELPER_P_GA: + { + int base = 4; + DO_GENERIC_LOOKUP(ip[2], base + 1); + + HELPER_FTN_PP helperFtn = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[base]]); + void* helperArg = result; + LOCAL_VAR(ip[1], void*) = ((HELPER_FTN_PP_2)helperFtn)(helperArg, LOCAL_VAR_ADDR(ip[3], void*)); + ip += 6; + break; + } + + case INTOP_CALL_HELPER_P_PA: + { + int base = 3; + + HELPER_FTN_PP helperFtn = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[base]]); + void* helperArg = pMethod->pDataItems[ip[base + 1]]; + LOCAL_VAR(ip[1], void*) = ((HELPER_FTN_PP_2)helperFtn)(helperArg, LOCAL_VAR_ADDR(ip[2], void*)); + ip += 5; + break; + } + + case INTOP_CALL_HELPER_V_AGP: + { + int base = 4; + DO_GENERIC_LOOKUP(ip[2], base + 1); + + HELPER_FTN_PP helperFtn = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[base]]); + void* helperArg = result; + ((HELPER_FTN_V_PPP)helperFtn)(LOCAL_VAR_ADDR(ip[1], void*), helperArg, LOCAL_VAR(ip[3], void*)); + ip += 6; + break; + } + case INTOP_CALL_HELPER_V_APP: + { + int base = 3; + + HELPER_FTN_PP helperFtn = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[base]]); + void* helperArg = pMethod->pDataItems[ip[base + 1]]; + ((HELPER_FTN_V_PPP)helperFtn)(LOCAL_VAR_ADDR(ip[1], void*), helperArg, LOCAL_VAR(ip[2], void*)); + ip += 5; break; } + case INTOP_CALLVIRT: { returnOffset = ip[1]; @@ -1632,19 +1779,22 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr pThreadContext->pStackPointer = stack + pMethod->allocaSize; break; } - case INTOP_NEWOBJ_VAR: + case INTOP_NEWOBJ_GENERIC: { returnOffset = ip[1]; callArgsOffset = ip[2]; methodSlot = ip[4]; + DO_GENERIC_LOOKUP(ip[3], 5); + + MethodTable *pMTNewObj = (MethodTable*)result; // result comes from the generic lookup - OBJECTREF objRef = AllocateObject(LOCAL_VAR(ip[3], MethodTable*)); + OBJECTREF objRef = AllocateObject(pMTNewObj); // This is return value LOCAL_VAR(returnOffset, OBJECTREF) = objRef; // Set `this` arg for ctor call LOCAL_VAR (callArgsOffset, OBJECTREF) = objRef; - ip += 5; + ip += 6; goto CALL_INTERP_SLOT; } @@ -1670,6 +1820,14 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr ip += 5; break; } + case INTOP_NEWMDARR_GENERIC: + { + DO_GENERIC_LOOKUP(ip[3], 4); + MethodTable *pMTArray = (MethodTable*)result; // result comes from the generic lookup + LOCAL_VAR(ip[1], OBJECTREF) = CreateMultiDimArray(pMTArray, stack, ip[2], ip[5]); + ip += 6; + break; + } case INTOP_NEWOBJ_VT: { returnOffset = ip[1]; @@ -1753,38 +1911,40 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr LOCAL_VAR(ip[1], OBJECTREF) = pExceptionClauseArgs->throwable; ip += 2; break; - case INTOP_BOX: - case INTOP_BOX_PTR: - case INTOP_UNBOX: case INTOP_UNBOX_ANY: { int opcode = *ip; int dreg = ip[1]; int sreg = ip[2]; - MethodTable *pMT = (MethodTable*)pMethod->pDataItems[ip[3]]; - HELPER_FTN_BOX_UNBOX helper = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[4]]); + HELPER_FTN_BOX_UNBOX helper = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[3]]); + MethodTable *pMT = (MethodTable*)pMethod->pDataItems[ip[4]]; - if (opcode == INTOP_BOX || opcode == INTOP_BOX_PTR) { - // internal static object Box(MethodTable* typeMT, ref byte unboxedData) - void *unboxedData; - if (opcode == INTOP_BOX) - unboxedData = LOCAL_VAR_ADDR(sreg, void); - else - unboxedData = LOCAL_VAR(sreg, void*); - LOCAL_VAR(dreg, OBJECTREF) = ObjectToOBJECTREF((Object*)helper(pMT, unboxedData)); - } else { - // private static ref byte Unbox(MethodTable* toTypeHnd, object obj) - Object *src = LOCAL_VAR(sreg, Object*); - void *unboxedData = helper(pMT, src); - if (opcode == INTOP_UNBOX) - LOCAL_VAR(dreg, void*) = unboxedData; - else - CopyValueClassUnchecked(LOCAL_VAR_ADDR(dreg, void), unboxedData, pMT); - } + // private static ref byte Unbox(MethodTable* toTypeHnd, object obj) + Object *src = LOCAL_VAR(sreg, Object*); + void *unboxedData = helper(pMT, src); + CopyValueClassUnchecked(LOCAL_VAR_ADDR(dreg, void), unboxedData, pMT); ip += 5; break; } + + case INTOP_UNBOX_ANY_GENERIC: + { + int opcode = *ip; + int dreg = ip[1]; + int sreg = ip[3]; + DO_GENERIC_LOOKUP(ip[2], 5); + MethodTable *pMTBoxedObj = (MethodTable*)result; // result comes from the generic lookup + HELPER_FTN_BOX_UNBOX helper = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[4]]); + + // private static ref byte Unbox(MethodTable* toTypeHnd, object obj) + Object *src = LOCAL_VAR(sreg, Object*); + void *unboxedData = helper(pMTBoxedObj, src); + CopyValueClassUnchecked(LOCAL_VAR_ADDR(dreg, void), unboxedData, pMTBoxedObj->IsNullable() ? pMTBoxedObj->GetInstantiation()[0].AsMethodTable() : pMTBoxedObj); + + ip += 6; + break; + } case INTOP_NEWARR: { int32_t length = LOCAL_VAR(ip[2], int32_t); @@ -1800,6 +1960,23 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr ip += 5; break; } + case INTOP_NEWARR_GENERIC: + { + int32_t length = LOCAL_VAR(ip[3], int32_t); + if (length < 0) + COMPlusThrow(kArgumentOutOfRangeException); + + DO_GENERIC_LOOKUP(ip[2], 5); + + MethodTable* arrayClsHnd = (MethodTable*)result; + HELPER_FTN_NEWARR helper = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[4]]); + + Object* arr = helper(arrayClsHnd, (intptr_t)length); + LOCAL_VAR(ip[1], OBJECTREF) = ObjectToOBJECTREF(arr); + + ip += 6; + break; + } #define LDELEM(dtype,etype) \ do { \ BASEARRAYREF arrayRef = LOCAL_VAR(ip[2], BASEARRAYREF); \ @@ -2062,98 +2239,16 @@ do { \ ip += 6; break; } -#define DO_GENERIC_LOOKUP(mdParam, mtParam) \ - CORINFO_RUNTIME_LOOKUP *pLookup = (CORINFO_RUNTIME_LOOKUP*)pMethod->pDataItems[ip[3]]; \ - void* result = 0; \ - \ - assert(!pLookup->indirectFirstOffset); \ - assert(!pLookup->indirectSecondOffset); \ - lookup = *(uint8_t**)(lookup + pLookup->offsets[0]); \ - if (pLookup->indirections >= 3) \ - lookup = *(uint8_t**)(lookup + pLookup->offsets[1]); \ - if (pLookup->indirections >= 4) \ - lookup = *(uint8_t**)(lookup + pLookup->offsets[2]); \ - do { \ - size_t lastOffset = pLookup->offsets[pLookup->indirections - 1]; \ - if (pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK) \ - { \ - /* Last indirection is the size*/ \ - size_t size = *(size_t*)(lookup + pLookup->sizeOffset); \ - if (size <= lastOffset) \ - { \ - result = GenericHandleCommon(mdParam, mtParam, pLookup->signature); \ - break; \ - } \ - } \ - lookup = *(uint8_t**)(lookup + lastOffset); \ - \ - if (lookup == NULL) \ - { \ - result = GenericHandleCommon(mdParam, mtParam, pLookup->signature); \ - break; \ - } \ - result = lookup; \ - } while (0); \ - LOCAL_VAR(dreg, void*) = result; \ - ip += 4; - - case INTOP_GENERICLOOKUP_THIS: - { - int dreg = ip[1]; - int sreg = ip[2]; - OBJECTREF thisPtr = LOCAL_VAR(sreg, OBJECTREF); - if (thisPtr == NULL) - assert(0); // TODO: throw NullReferenceException - MethodTable *pMT = thisPtr->GetMethodTable(); - uint8_t *lookup = (uint8_t*)pMT; - DO_GENERIC_LOOKUP(NULL, pMT); - - break; - } - case INTOP_GENERICLOOKUP_METHOD: + case INTOP_GENERICLOOKUP: { int dreg = ip[1]; - int sreg = ip[2]; - MethodDesc *paramReg = LOCAL_VAR(sreg, MethodDesc*); - uint8_t *lookup = (uint8_t*)paramReg; - DO_GENERIC_LOOKUP(paramReg, NULL); - break; - } - case INTOP_GENERICLOOKUP_CLASS: - { - int dreg = ip[1]; - int sreg = ip[2]; - MethodTable *paramReg = LOCAL_VAR(sreg, MethodTable*); - uint8_t *lookup = (uint8_t*)paramReg; - DO_GENERIC_LOOKUP(NULL, paramReg); - break; - } - case INTOP_LDTOKEN_VAR: - { - int dreg = ip[1]; - void *nativeHandle = LOCAL_VAR(ip[2], void*); - size_t helperDirectOrIndirect = (size_t)pMethod->pDataItems[ip[3]]; - HELPER_FTN_PP helper = nullptr; - if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG) - helper = *(HELPER_FTN_PP *)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG); - else - helper = (HELPER_FTN_PP)helperDirectOrIndirect; - void *managedHandle = helper(nativeHandle); - LOCAL_VAR(dreg, void*) = managedHandle; - ip += 4; - break; - } - case INTOP_LDTOKEN: - { - int dreg = ip[1]; - void *nativeHandle = pMethod->pDataItems[ip[3]]; - HELPER_FTN_PP helper = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[2]]); - void *managedHandle = helper(nativeHandle); - LOCAL_VAR(dreg, void*) = managedHandle; + DO_GENERIC_LOOKUP(ip[2], 3); + LOCAL_VAR(dreg, void*) = result; ip += 4; break; } + case INTOP_CALL_FINALLY: { const int32_t* targetIp = ip + ip[1]; diff --git a/src/native/containers/dn-simdhash-ght-compatible.h b/src/native/containers/dn-simdhash-ght-compatible.h index 2b87f84b03363d..76f25ae09946c1 100644 --- a/src/native/containers/dn-simdhash-ght-compatible.h +++ b/src/native/containers/dn-simdhash-ght-compatible.h @@ -7,6 +7,10 @@ typedef void (*dn_simdhash_ght_destroy_func) (void * data); typedef unsigned int (*dn_simdhash_ght_hash_func) (const void * key); typedef int32_t (*dn_simdhash_ght_equal_func) (const void * a, const void * b); +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + dn_simdhash_ght_t * dn_simdhash_ght_new ( dn_simdhash_ght_hash_func hash_func, dn_simdhash_ght_equal_func key_equal_func, @@ -34,7 +38,11 @@ dn_simdhash_ght_get_value_or_default ( dn_simdhash_ght_t *hash, void * key ); +#ifdef __cplusplus +} +#endif // __cplusplus + // compatibility shims for the g_hash_table_ versions in glib.h -#define dn_simdhash_ght_insert(h,k,v) dn_simdhash_ght_insert_replace ((h),(k),(v),FALSE) -#define dn_simdhash_ght_replace(h,k,v) dn_simdhash_ght_insert_replace ((h),(k),(v),TRUE) -#define dn_simdhash_ght_add(h,k) dn_simdhash_ght_insert_replace ((h),(k),(k),TRUE) +#define dn_simdhash_ght_insert(h,k,v) dn_simdhash_ght_insert_replace ((h),(k),(v),0) +#define dn_simdhash_ght_replace(h,k,v) dn_simdhash_ght_insert_replace ((h),(k),(v),1) +#define dn_simdhash_ght_add(h,k) dn_simdhash_ght_insert_replace ((h),(k),(k),1) diff --git a/src/native/containers/dn-simdhash-specialization-declarations.h b/src/native/containers/dn-simdhash-specialization-declarations.h index 585f2094ee58c8..047727ebb50f73 100644 --- a/src/native/containers/dn-simdhash-specialization-declarations.h +++ b/src/native/containers/dn-simdhash-specialization-declarations.h @@ -1,6 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + // Gluing macro expansions together requires nested macro invocation :/ #ifndef DN_SIMDHASH_GLUE #define DN_SIMDHASH_GLUE_INNER(a, b) a ## b @@ -74,3 +78,7 @@ DN_SIMDHASH_TRY_REPLACE_VALUE_WITH_HASH (DN_SIMDHASH_T_PTR hash, DN_SIMDHASH_KEY void DN_SIMDHASH_FOREACH (DN_SIMDHASH_T_PTR hash, DN_SIMDHASH_FOREACH_FUNC func, void *user_data); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/src/native/containers/dn-simdhash-string-ptr.h b/src/native/containers/dn-simdhash-string-ptr.h index fc3e80fef5689a..e37acd06ec5816 100644 --- a/src/native/containers/dn-simdhash-string-ptr.h +++ b/src/native/containers/dn-simdhash-string-ptr.h @@ -1,3 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + uint8_t dn_simdhash_string_ptr_try_add (dn_simdhash_string_ptr_t *hash, const char *key, void *value); @@ -11,3 +18,7 @@ typedef void (*dn_simdhash_string_ptr_foreach_func) (const char *key, void *valu void dn_simdhash_string_ptr_foreach (dn_simdhash_string_ptr_t *hash, dn_simdhash_string_ptr_foreach_func func, void *user_data); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/src/native/containers/dn-simdhash-utils.h b/src/native/containers/dn-simdhash-utils.h index c158bb36504b07..21395f6f48856a 100644 --- a/src/native/containers/dn-simdhash-utils.h +++ b/src/native/containers/dn-simdhash-utils.h @@ -6,6 +6,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + #if defined(__clang__) || defined (__GNUC__) static DN_FORCEINLINE(uint32_t) next_power_of_two (uint32_t value) { @@ -218,4 +222,8 @@ dn_simdhash_assert_fail (const char *file, int line, const char *condition); dn_simdhash_assert_fail(__FILE__, __LINE__, #expr); \ } +#ifdef __cplusplus +} +#endif // __cplusplus + #endif // __DN_SIMDHASH_UTILS_H__ diff --git a/src/native/containers/dn-simdhash.h b/src/native/containers/dn-simdhash.h index cb00f2627ebb9f..af1afd8a910c7f 100644 --- a/src/native/containers/dn-simdhash.h +++ b/src/native/containers/dn-simdhash.h @@ -8,6 +8,10 @@ #include "dn-utils.h" #include "dn-allocator.h" +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + // We reserve the last two bytes of each suffix vector to store data #define DN_SIMDHASH_MAX_BUCKET_CAPACITY 14 // The ideal capacity depends on the size of your keys. For 4-byte keys, it is 12. @@ -161,4 +165,8 @@ dn_simdhash_overflow_count (dn_simdhash_t *hash); void dn_simdhash_ensure_capacity (dn_simdhash_t *hash, uint32_t capacity); +#ifdef __cplusplus +} +#endif // __cplusplus + #endif // __DN_SIMDHASH_H__ diff --git a/src/native/containers/dn-utils.h b/src/native/containers/dn-utils.h index dab4160852b1fa..f33b781aba72c1 100644 --- a/src/native/containers/dn-utils.h +++ b/src/native/containers/dn-utils.h @@ -19,6 +19,10 @@ // included unconditionally for static_assert macro on C11 #include +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + #if defined(_DEBUG) #define DN_ASSERT(x) assert(x) #else @@ -82,4 +86,8 @@ dn_safe_uint32_t_add (uint32_t lhs, uint32_t rhs, uint32_t *result) return true; } +#ifdef __cplusplus +} +#endif // __cplusplus + #endif /* __DN_UTILS_H__ */ diff --git a/src/tests/JIT/interpreter/Interpreter.cs b/src/tests/JIT/interpreter/Interpreter.cs index bd642706218bc3..53b6fa817145cb 100644 --- a/src/tests/JIT/interpreter/Interpreter.cs +++ b/src/tests/JIT/interpreter/Interpreter.cs @@ -1897,6 +1897,59 @@ public static Type GetTypeOfTStatic() } } + public static bool TestSharedGenerics_IsInst(object o) + { + return o is T; + } + + public static T[] TestSharedGenerics_CastClass(object o) + { + return (T[])o; + } + + public static T TestSharedGenerics_UnboxAny(object o) + { + T result = (T)o; + return result; + } + + public static T[] TestNewArr(int len) + { + return new T[len]; + } + + public static T[,,] TestNewMDArr(int len) + { + return new T[len,len-1,len-2]; + } + + public static object Box(T value) + { + return value; + } + + public static T TestUnboxInst(object o) + { + return ((GenericStruct)o).Value; + } + + struct GenericStruct + { + public T Value; + } + + class MyClass + { + public Type GetTypeOf() + { + return typeof(MyClass); + } + public static Type GetTypeOfStatic() + { + return typeof(MyClass); + } + } + public static bool TestSharedGenerics() { if (!TestSharedGenerics_CallsTo()) @@ -1909,6 +1962,154 @@ public static bool TestSharedGenerics() if (!TestGenerics_CallsFrom()) return false; + Console.WriteLine("Test isinst with shared generics (string)"); + if (!TestSharedGenerics_IsInst("hello")) + return false; + + if (TestSharedGenerics_IsInst(new object())) + return false; + + Console.WriteLine("Test castclass with shared generics (string)"); + if (TestSharedGenerics_CastClass(new string[] { "hello" }).GetType() != typeof(string[])) + return false; + + try + { + TestSharedGenerics_CastClass(new object()); + Console.WriteLine("Did not throw from casting object to string[]"); + return false; + } + catch (InvalidCastException) + { + Console.WriteLine("Expected InvalidCastException from casting object to string[]"); + } + + Console.WriteLine("Test unbox.any with shared generics"); + if (TestSharedGenerics_UnboxAny("hello") != "hello") + return false; + try + { + TestSharedGenerics_UnboxAny(new object()); + Console.WriteLine("Did not throw from casting object to string"); + return false; + } + catch (InvalidCastException) + { + Console.WriteLine("Expected InvalidCastException from casting object to string"); + } + + GenericStruct gs = new GenericStruct(); + gs.Value = "hello"; + + if (TestSharedGenerics_UnboxAny>(gs).Value != "hello") + { + return false; + } + + if (TestSharedGenerics_UnboxAny?>(gs).Value.Value != "hello") + { + return false; + } + + if (TestSharedGenerics_UnboxAny?>(null).HasValue) + { + return false; + } + Console.WriteLine("Test box with shared generics"); + + object objOriginal = new object(); + if (Box(objOriginal) != objOriginal) + { + return false; + } + + if (((int)Box(42)) != 42) + { + return false; + } + + if (((int)Box(42)) != 42) + { + return false; + } + + if ((Box(null)) != null) + { + return false; + } + + GenericStruct gsObj = new GenericStruct(); + gsObj.Value = objOriginal; + if (((GenericStruct)Box>(gsObj)).Value != objOriginal) + { + return false; + } + + if (((GenericStruct)Box?>(gsObj)).Value != objOriginal) + { + return false; + } + + if (Box?>(null) != null) + { + return false; + } + + Console.WriteLine("Test classic unbox instruction with shared generics"); + if (TestUnboxInst(Box>(gsObj)) != objOriginal) + { + return false; + } + + GenericStruct gsInt = new GenericStruct(); + gsInt.Value = 42; + + if (TestUnboxInst(Box>(gsInt)) != 42) + { + return false; + } + + Console.WriteLine("Test newarr"); + if (TestNewArr(5).GetType() != typeof(string[])) + { + return false; + } + + if (TestNewArr(5).GetType() != typeof(int[])) + { + return false; + } + + Console.WriteLine("Test new MD arr"); + if (TestNewMDArr(5).GetType() != typeof(string[,,])) + { + return false; + } + string[,,] mdStringArr = TestNewMDArr(5); + if (mdStringArr.GetLength(0) != 5 || mdStringArr.GetLength(1) != 4 || mdStringArr.GetLength(2) != 3) + { + return false; + } + + if (TestNewMDArr(5).GetType() != typeof(int[,,])) + { + return false; + } + int[,,] mdIntArr = TestNewMDArr(5); + if (mdIntArr.GetLength(0) != 5 || mdIntArr.GetLength(1) != 4 || mdIntArr.GetLength(2) != 3) + { + return false; + } + + MyClass mcString = new MyClass(); + if (mcString.GetTypeOf() != typeof(MyClass)) + { + return false; + } + if (MyClass.GetTypeOfStatic() != typeof(MyClass)) + { + return false; + } return true; }