diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 8cff503d5eabe..129eae138116c 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -4197,12 +4197,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, switch (ni) { case NI_System_Type_get_IsValueType: - retNode = gtNewIconNode( - (eeIsValueClass(hClass) && - // pointers are not value types (e.g. typeof(int*).IsValueType is false) - info.compCompHnd->asCorInfoType(hClass) != CORINFO_TYPE_PTR) - ? 1 - : 0); + retNode = gtNewIconNode(eeIsValueClass(hClass) ? 1 : 0); break; case NI_System_Type_get_IsByRefLike: retNode = gtNewIconNode( @@ -13669,6 +13664,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) goto APPEND; case CEE_LDELEMA: + { assertImp(sz == sizeof(unsigned)); _impResolveToken(CORINFO_TOKENKIND_Class); @@ -13676,63 +13672,49 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %08X", resolvedToken.token); ldelemClsHnd = resolvedToken.hClass; + lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(ldelemClsHnd)); - // If it's a value class array we just do a simple address-of - if (eeIsValueClass(ldelemClsHnd)) - { - CorInfoType cit = info.compCompHnd->getTypeForPrimitiveValueClass(ldelemClsHnd); - if (cit == CORINFO_TYPE_UNDEF) - { - lclTyp = TYP_STRUCT; - } - else - { - lclTyp = JITtype2varType(cit); - } - goto ARR_LD; - } - - // Similarly, if its a readonly access, we can do a simple address-of - // without doing a runtime type-check - if (prefixFlags & PREFIX_READONLY) + // If it's a value class / pointer array, or a readonly access, we don't need a type check. + // TODO-CQ: adapt "impCanSkipCovariantStoreCheck" to handle "ldelema"s and call it here to + // skip using the helper in more cases. + if ((lclTyp != TYP_REF) || ((prefixFlags & PREFIX_READONLY) != 0)) { - lclTyp = TYP_REF; goto ARR_LD; } // Otherwise we need the full helper function with run-time type check - op1 = impTokenToHandle(&resolvedToken); - if (op1 == nullptr) - { // compDonotInline() + GenTree* type = impTokenToHandle(&resolvedToken); + if (type == nullptr) + { + assert(compDonotInline()); return; } - { - GenTree* type = op1; - GenTree* index = impPopStack().val; - GenTree* arr = impPopStack().val; + GenTree* index = impPopStack().val; + GenTree* arr = impPopStack().val; + #ifdef TARGET_64BIT - // The CLI Spec allows an array to be indexed by either an int32 or a native int. - // The array helper takes a native int for array length. - // So if we have an int, explicitly extend it to be a native int. - if (genActualType(index->TypeGet()) != TYP_I_IMPL) + // The CLI Spec allows an array to be indexed by either an int32 or a native int. + // The array helper takes a native int for array length. + // So if we have an int, explicitly extend it to be a native int. + if (genActualType(index->TypeGet()) != TYP_I_IMPL) + { + if (index->IsIntegralConst()) { - if (index->IsIntegralConst()) - { - index->gtType = TYP_I_IMPL; - } - else - { - bool isUnsigned = false; - index = gtNewCastNode(TYP_I_IMPL, index, isUnsigned, TYP_I_IMPL); - } + index->gtType = TYP_I_IMPL; + } + else + { + bool isUnsigned = false; + index = gtNewCastNode(TYP_I_IMPL, index, isUnsigned, TYP_I_IMPL); } -#endif // TARGET_64BIT - op1 = gtNewHelperCallNode(CORINFO_HELP_LDELEMA_REF, TYP_BYREF, arr, index, type); } +#endif // TARGET_64BIT + op1 = gtNewHelperCallNode(CORINFO_HELP_LDELEMA_REF, TYP_BYREF, arr, index, type); impPushOnStack(op1, tiRetVal); - break; + } + break; // ldelem for reference and value types case CEE_LDELEM: @@ -13743,21 +13725,9 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %08X", resolvedToken.token); ldelemClsHnd = resolvedToken.hClass; - - // If it's a reference type or generic variable type - // then just generate code as though it's a ldelem.ref instruction - if (!eeIsValueClass(ldelemClsHnd)) - { - lclTyp = TYP_REF; - opcode = CEE_LDELEM_REF; - } - else - { - CorInfoType jitTyp = info.compCompHnd->asCorInfoType(ldelemClsHnd); - lclTyp = JITtype2varType(jitTyp); - tiRetVal = verMakeTypeInfo(ldelemClsHnd); // precise type always needed for struct - tiRetVal.NormaliseForStack(); - } + lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(ldelemClsHnd)); + tiRetVal = verMakeTypeInfo(ldelemClsHnd); + tiRetVal.NormaliseForStack(); goto ARR_LD; case CEE_LDELEM_I1: @@ -13835,23 +13805,15 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %08X", resolvedToken.token); stelemClsHnd = resolvedToken.hClass; + lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(stelemClsHnd)); - // If it's a reference type just behave as though it's a stelem.ref instruction - if (!eeIsValueClass(stelemClsHnd)) + if (lclTyp != TYP_REF) { - goto STELEM_REF_POST_VERIFY; - } - - // Otherwise extract the type - { - CorInfoType jitTyp = info.compCompHnd->asCorInfoType(stelemClsHnd); - lclTyp = JITtype2varType(jitTyp); goto ARR_ST; } + FALLTHROUGH; case CEE_STELEM_REF: - STELEM_REF_POST_VERIFY: - { GenTree* value = impStackTop(0).val; GenTree* index = impStackTop(1).val; @@ -17350,20 +17312,16 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %08X", resolvedToken.token); - if (!eeIsValueClass(resolvedToken.hClass)) + lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(resolvedToken.hClass)); + + if (lclTyp != TYP_STRUCT) { op1 = impPopStack().val; // address to load from - impBashVarAddrsToI(op1); - - assertImp(genActualType(op1->gtType) == TYP_I_IMPL || op1->gtType == TYP_BYREF); - - op1 = gtNewOperNode(GT_IND, TYP_REF, op1); - op1->gtFlags |= GTF_EXCEPT | GTF_GLOB_REF; + op1 = gtNewIndir(lclTyp, op1); + op1->gtFlags |= GTF_GLOB_REF; impPushOnStack(op1, typeInfo()); - opcode = CEE_STIND_REF; - lclTyp = TYP_REF; goto STIND; } @@ -17380,25 +17338,10 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %08X", resolvedToken.token); - if (eeIsValueClass(resolvedToken.hClass)) - { - lclTyp = TYP_STRUCT; - } - else - { - lclTyp = TYP_REF; - } - - if (lclTyp == TYP_REF) - { - opcode = CEE_STIND_REF; - goto STIND; - } + lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(resolvedToken.hClass)); - CorInfoType jitTyp = info.compCompHnd->asCorInfoType(resolvedToken.hClass); - if (impIsPrimitive(jitTyp)) + if (lclTyp != TYP_STRUCT) { - lclTyp = JITtype2varType(jitTyp); goto STIND; } @@ -17465,39 +17408,19 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %08X", resolvedToken.token); OBJ: - + lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(resolvedToken.hClass)); tiRetVal = verMakeTypeInfo(resolvedToken.hClass); - if (eeIsValueClass(resolvedToken.hClass)) + if (lclTyp != TYP_STRUCT) { - lclTyp = TYP_STRUCT; - } - else - { - lclTyp = TYP_REF; - opcode = CEE_LDIND_REF; goto LDIND; } op1 = impPopStack().val; - assertImp(op1->TypeGet() == TYP_BYREF || op1->TypeGet() == TYP_I_IMPL); - - CorInfoType jitTyp = info.compCompHnd->asCorInfoType(resolvedToken.hClass); - if (impIsPrimitive(jitTyp)) - { - op1 = gtNewOperNode(GT_IND, JITtype2varType(jitTyp), op1); + assertImp((genActualType(op1) == TYP_I_IMPL) || op1->TypeIs(TYP_BYREF)); - // Could point anywhere, example a boxed class static int - op1->gtFlags |= GTF_GLOB_REF; - assertImp(varTypeIsArithmetic(op1->gtType)); - } - else - { - // OBJ returns a struct - // and an inline argument which is the class token of the loaded obj - op1 = gtNewObjNode(resolvedToken.hClass, op1); - } + op1 = gtNewObjNode(resolvedToken.hClass, op1); op1->gtFlags |= GTF_EXCEPT; if (prefixFlags & PREFIX_UNALIGNED) @@ -17810,10 +17733,10 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) impBashVarAddrsToI(op2); op2 = impImplicitIorI4Cast(op2, info.compRetType); op2 = impImplicitR4orR8Cast(op2, info.compRetType); - // Note that we allow TYP_I_IMPL<->TYP_BYREF transformation, but only TYP_I_IMPL<-TYP_REF. + assertImp((genActualType(op2->TypeGet()) == genActualType(info.compRetType)) || - ((op2->TypeGet() == TYP_I_IMPL) && TypeIs(info.compRetType, TYP_BYREF)) || - (op2->TypeIs(TYP_BYREF, TYP_REF) && (info.compRetType == TYP_I_IMPL)) || + ((op2->TypeGet() == TYP_I_IMPL) && (info.compRetType == TYP_BYREF)) || + ((op2->TypeGet() == TYP_BYREF) && (info.compRetType == TYP_I_IMPL)) || (varTypeIsFloating(op2->gtType) && varTypeIsFloating(info.compRetType)) || (varTypeIsStruct(op2) && varTypeIsStruct(info.compRetType))); @@ -17867,9 +17790,8 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) if (returnType != originalCallType) { // Allow TYP_BYREF to be returned as TYP_I_IMPL and vice versa. - // Allow TYP_REF to be returned as TYP_I_IMPL and NOT vice verse. - if ((TypeIs(returnType, TYP_BYREF, TYP_REF) && (originalCallType == TYP_I_IMPL)) || - ((returnType == TYP_I_IMPL) && TypeIs(originalCallType, TYP_BYREF))) + if (((returnType == TYP_BYREF) && (originalCallType == TYP_I_IMPL)) || + ((returnType == TYP_I_IMPL) && (originalCallType == TYP_BYREF))) { JITDUMP("Allowing return type mismatch: have %s, needed %s\n", varTypeName(returnType), varTypeName(originalCallType)); diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp index d420f9f57b1d0..be1fe4ec4f583 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -322,7 +322,7 @@ ClassLayout* Compiler::typGetObjLayout(CORINFO_CLASS_HANDLE classHandle) ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) { - bool isValueClass = compiler->info.compCompHnd->isValueClass(classHandle); + bool isValueClass = compiler->eeIsValueClass(classHandle); unsigned size; if (isValueClass) diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_71636/Runtime_71636.il b/src/tests/JIT/Regression/JitBlue/Runtime_71636/Runtime_71636.il new file mode 100644 index 0000000000000..dd6b1f2076d0d --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_71636/Runtime_71636.il @@ -0,0 +1,261 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime { } +.assembly extern System.Console { } + +.assembly Runtime_71636.dll { } + +.class Runtime_71636 extends [System.Runtime]System.Object +{ + .field private static native int s_globalCounter; + + .method public static int32 Main() + { + .entrypoint + + call bool Runtime_71636::ProblemWithLdElemA() + brtrue FAILURE_LDELEMA + + call bool Runtime_71636::ProblemWithLdElem() + brtrue FAILURE_LDELEM + + call bool Runtime_71636::ProblemWithStElem() + brtrue FAILURE_STELEM + + call bool Runtime_71636::ProblemWithCpObj() + brtrue FAILURE_CPOBJ + + call bool Runtime_71636::ProblemWithLdObj() + brtrue FAILURE_LDOBJ + + ldc.i4 100 + ret + + FAILURE_LDELEMA: + ldc.i4 101 + ret + + FAILURE_LDELEM: + ldc.i4 102 + ret + + FAILURE_STELEM: + ldc.i4 103 + ret + + FAILURE_CPOBJ: + ldc.i4 104 + ret + + FAILURE_LDOBJ: + ldc.i4 105 + ret + } + + .method private static bool ProblemWithLdElemA() noinlining + { + .locals (void* a, void* b, void*[] arr, int32 idx) + + call void*[] Runtime_71636::GetArray() + stloc arr + + ldc.i4 0 + stloc idx + + ldloc arr + ldloc idx + readonly. + ldelema void* + ldind.i + stloc a + + ldloc arr + ldloc idx + ldc.i4 10 + conv.i + stelem.i + + ldloc arr + ldloc idx + readonly. + ldelema void* + ldind.i + stloc b + + ldloc a + ldloc b + ceq + ret + } + + .method private static bool ProblemWithLdElem() noinlining + { + .locals (void* lcl, void*[] arr, int32 idx) + + call void*[] Runtime_71636::GetArray() + stloc arr + + ldc.i4 0 + stloc idx + + ldloc arr + ldloc idx + ldloca lcl + stelem.i + + ldloc arr + ldloc idx + ldelem void* + call void [System.Runtime]System.GC::Collect() + stloc lcl + + ldloc lcl + ldloca lcl + bne.un NOT_EQUAL + + ldc.i4 0 + ret + + NOT_EQUAL: + ldc.i4 1 + ret + } + + .method private static bool ProblemWithStElem() noinlining + { + .locals (void* lcl, void*[] arr, int32 idx) + + call void*[] Runtime_71636::GetArray() + stloc arr + + ldc.i4 0 + stloc idx + + ldloc arr + ldloc idx + ldloca lcl + stelem void* + + ldloc arr + ldloc idx + ldelem.i + stloc lcl + + ldloc lcl + ldloca lcl + bne.un NOT_EQUAL + + ldc.i4 0 + ret + + NOT_EQUAL: + ldc.i4 1 + ret + } + + .method private static bool ProblemWithCpObj() noinlining + { + .locals (void* lcl, void*[] arr, int32 idx, native int loopIdx) + + call void*[] Runtime_71636::GetArray() + stloc arr + + ldc.i4 0 + stloc idx + + ldloca lcl + stloc lcl + + // A loop to force fully interruptible code - this test case could only fail under GCStress(=0xC). + // + ldloc arr + ldloc idx + ldelem.i + stloc loopIdx + LOOP: + ldloc loopIdx + ldc.i4 -1 + add + stloc loopIdx + + ldsfld native int Runtime_71636::s_globalCounter + ldloc loopIdx + add + stsfld native int Runtime_71636::s_globalCounter + + ldloc loopIdx + ldc.i4 0 + bge LOOP + + ldloc arr + ldloc idx + ldelema void* + ldloca lcl + cpobj void* + + ldloc arr + ldloc idx + ldelem.i + ldloc lcl + bne.un NOT_EQUAL + + ldc.i4 0 + ret + + NOT_EQUAL: + ldc.i4 1 + ret + } + + .method private static bool ProblemWithLdObj() noinlining + { + .locals (void* lcl, void*[] arr, int32 idx) + + call void*[] Runtime_71636::GetArray() + stloc arr + + ldc.i4 0 + stloc idx + + ldloc arr + ldloc idx + ldloca lcl + stelem.i + + ldloc arr + ldloc idx + ldelema void* + ldobj void* + call void [System.Runtime]System.GC::Collect() + stloc lcl + + ldloc lcl + ldloca lcl + bne.un NOT_EQUAL + + ldc.i4 0 + ret + + NOT_EQUAL: + ldc.i4 1 + ret + } + + .method private static void*[] GetArray() noinlining + { + .locals (void*[] arr) + ldc.i4 1 + newarr void* + stloc arr + + ldloc arr + ldc.i4 0 + ldc.i4 0 + conv.i + stelem.i + + ldloc arr + ret + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_71636/Runtime_71636.ilproj b/src/tests/JIT/Regression/JitBlue/Runtime_71636/Runtime_71636.ilproj new file mode 100644 index 0000000000000..c61c0c5d312f4 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_71636/Runtime_71636.ilproj @@ -0,0 +1,11 @@ + + + Exe + + + True + + + + +