diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index f4e007d06dfc28..5f6b789b696519 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -1287,6 +1287,8 @@ void InterpCompiler::EmitConv(StackInfo *sp, StackType type, InterpOpcode convOp int32_t var = CreateVarExplicit(g_interpTypeFromStackType[type], NULL, INTERP_STACK_SLOT_SIZE); sp->var = var; newInst->SetDVar(var); + + // NOTE: We rely on m_pLastNewIns == newInst upon return from this function. Make sure you preserve that if you change anything. } static InterpType GetInterpType(CorInfoType corInfoType) @@ -2120,6 +2122,7 @@ int32_t InterpCompiler::GetMethodDataItemIndex(CORINFO_METHOD_HANDLE mHandle) int32_t InterpCompiler::GetDataItemIndexForHelperFtn(CorInfoHelpFunc ftn) { + // Interpreter-TODO: Find an existing data item index for this helper if possible and reuse it void *indirect; void *direct = m_compHnd->getHelperFtn(ftn, &indirect); size_t data = !direct @@ -3526,6 +3529,245 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) } m_ip++; break; + case CEE_CONV_OVF_I1: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I1_R4); + break; + case StackTypeR8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I1_R8); + break; + case StackTypeI4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I1_I4); + break; + case StackTypeI8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I1_I8); + break; + default: + assert(0); + } + m_ip++; + break; + case CEE_CONV_OVF_U1: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U1_R4); + break; + case StackTypeR8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U1_R8); + break; + case StackTypeI4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U1_I4); + break; + case StackTypeI8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U1_I8); + break; + default: + assert(0); + } + m_ip++; + break; + case CEE_CONV_OVF_I2: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I2_R4); + break; + case StackTypeR8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I2_R8); + break; + case StackTypeI4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I2_I4); + break; + case StackTypeI8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I2_I8); + break; + default: + assert(0); + } + m_ip++; + break; + case CEE_CONV_OVF_U2: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U2_R4); + break; + case StackTypeR8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U2_R8); + break; + case StackTypeI4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U2_I4); + break; + case StackTypeI8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U2_I8); + break; + default: + assert(0); + } + m_ip++; + break; + case CEE_CONV_OVF_I4: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I4_R4); + break; + case StackTypeR8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I4_R8); + break; + case StackTypeI4: + break; + case StackTypeI8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_I4_I8); + break; + default: + assert(0); + } + m_ip++; + break; + case CEE_CONV_OVF_U4: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U4_R4); + break; + case StackTypeR8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U4_R8); + break; + case StackTypeI4: + break; + case StackTypeI8: + EmitConv(m_pStackPointer - 1, StackTypeI4, INTOP_CONV_OVF_U4_I8); + break; + default: + assert(0); + } + m_ip++; + break; + case CEE_CONV_OVF_I8: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: + EmitConv(m_pStackPointer - 1, StackTypeI8, INTOP_CONV_OVF_I8_R4); + break; + case StackTypeR8: + EmitConv(m_pStackPointer - 1, StackTypeI8, INTOP_CONV_OVF_I8_R8); + break; + case StackTypeI4: + EmitConv(m_pStackPointer - 1, StackTypeI8, INTOP_CONV_I8_I4); + break; + case StackTypeI8: + break; + default: + assert(0); + } + m_ip++; + break; + case CEE_CONV_OVF_U8: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: + EmitConv(m_pStackPointer - 1, StackTypeI8, INTOP_CONV_OVF_U8_R4); + break; + case StackTypeR8: + EmitConv(m_pStackPointer - 1, StackTypeI8, INTOP_CONV_OVF_U8_R8); + break; + case StackTypeI4: + EmitConv(m_pStackPointer - 1, StackTypeI8, INTOP_CONV_I8_U4); + break; + case StackTypeI8: + break; + default: + assert(0); + } + m_ip++; + break; + case CEE_CONV_OVF_I: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: +#ifdef TARGET_64BIT + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_I8_R4); +#else + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_I4_R4); +#endif + break; + case StackTypeR8: +#ifdef TARGET_64BIT + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_I8_R8); +#else + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_I4_R8); +#endif + break; + case StackTypeI4: +#ifdef TARGET_64BIT + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_I8_I4); +#else + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_MOV_4); +#endif + break; + case StackTypeI8: +#ifdef TARGET_64BIT + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_MOV_8); +#else + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_I4_I8); +#endif + break; + default: + assert(0); + } + m_ip++; + break; + case CEE_CONV_OVF_U: + CHECK_STACK(1); + switch (m_pStackPointer[-1].type) + { + case StackTypeR4: +#ifdef TARGET_64BIT + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_U8_R4); +#else + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_U4_R4); +#endif + break; + case StackTypeR8: +#ifdef TARGET_64BIT + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_U8_R8); +#else + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_U4_R8); +#endif + break; + case StackTypeI4: +#ifdef TARGET_64BIT + // FIXME: Is this the right conv opcode? + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_I8_I4); +#else + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_MOV_4); +#endif + break; + case StackTypeI8: +#ifdef TARGET_64BIT + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_MOV_8); +#else + EmitConv(m_pStackPointer - 1, StackTypeI, INTOP_CONV_OVF_U4_I8); +#endif + break; + default: + assert(0); + } + m_ip++; + break; case CEE_SWITCH: { m_ip++; @@ -5009,6 +5251,15 @@ void InterpCompiler::PrintInsData(InterpInst *ins, int32_t insOffset, const int3 PrintClassName(ch); break; } + 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); + break; + } default: assert(0); break; diff --git a/src/coreclr/interpreter/intops.def b/src/coreclr/interpreter/intops.def index 6a5bffd5b2639e..22147c95d26aa8 100644 --- a/src/coreclr/interpreter/intops.def +++ b/src/coreclr/interpreter/intops.def @@ -171,6 +171,40 @@ OPDEF(INTOP_CONV_R8_R4, "conv.r8.r4", 3, 1, 1, InterpOpNoArgs) OPDEF(INTOP_CONV_U8_R4, "conv.u8.r4", 3, 1, 1, InterpOpNoArgs) OPDEF(INTOP_CONV_U8_R8, "conv.u8.r8", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_I1_I4, "conv.ovf.i1.i4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_I1_I8, "conv.ovf.i1.i8", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_I1_R4, "conv.ovf.i1.r4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_I1_R8, "conv.ovf.i1.r8", 3, 1, 1, InterpOpNoArgs) + +OPDEF(INTOP_CONV_OVF_U1_I4, "conv.ovf.u1.i4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_U1_I8, "conv.ovf.u1.i8", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_U1_R4, "conv.ovf.u1.r4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_U1_R8, "conv.ovf.u1.r8", 3, 1, 1, InterpOpNoArgs) + +OPDEF(INTOP_CONV_OVF_I2_I4, "conv.ovf.i2.i4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_I2_I8, "conv.ovf.i2.i8", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_I2_R4, "conv.ovf.i2.r4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_I2_R8, "conv.ovf.i2.r8", 3, 1, 1, InterpOpNoArgs) + +OPDEF(INTOP_CONV_OVF_U2_I4, "conv.ovf.u2.i4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_U2_I8, "conv.ovf.u2.i8", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_U2_R4, "conv.ovf.u2.r4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_U2_R8, "conv.ovf.u2.r8", 3, 1, 1, InterpOpNoArgs) + +OPDEF(INTOP_CONV_OVF_I4_I8, "conv.ovf.i4.i8", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_I4_R4, "conv.ovf.i4.r4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_I4_R8, "conv.ovf.i4.r8", 3, 1, 1, InterpOpNoArgs) + +OPDEF(INTOP_CONV_OVF_U4_I8, "conv.ovf.u4.i8", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_U4_R4, "conv.ovf.u4.r4", 3, 1, 1, InterpOpNoArgs) +OPDEF(INTOP_CONV_OVF_U4_R8, "conv.ovf.u4.r8", 3, 1, 1, InterpOpNoArgs) + +OPDEF(INTOP_CONV_OVF_I8_R4, "conv.ovf.i8.r4", 3, 1, 1, InterpOpNoArgs) +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_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] @@ -293,8 +327,8 @@ OPDEF(INTOP_NEWOBJ, "newobj", 5, 1, 1, InterpOpMethodHandle) OPDEF(INTOP_NEWOBJ_VAR, "newobj.var", 5, 1, 2, InterpOpMethodHandle) OPDEF(INTOP_NEWOBJ_VT, "newobj.vt", 5, 1, 1, InterpOpMethodHandle) -OPDEF(INTOP_CALL_HELPER_PP, "call.helper.pp", 4, 1, 0, InterpOpTwoInts) -OPDEF(INTOP_CALL_HELPER_PP_2, "call.helper.pp.2", 5, 1, 1, InterpOpTwoInts) +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) OPDEF(INTOP_GENERICLOOKUP_METHOD, "generic.method", 4, 1, 1, InterpOpGenericLookup) OPDEF(INTOP_GENERICLOOKUP_CLASS, "generic.class", 4, 1, 1, InterpOpGenericLookup) diff --git a/src/coreclr/interpreter/intops.h b/src/coreclr/interpreter/intops.h index 5ff775b2b0d896..82ab2f66bbfb55 100644 --- a/src/coreclr/interpreter/intops.h +++ b/src/coreclr/interpreter/intops.h @@ -24,6 +24,7 @@ typedef enum InterpOpClassHandle, InterpOpGenericLookup, InterpOpLdPtr, + InterpOpHelperFtn, } InterpOpArgType; extern const uint8_t g_interpOpLen[]; diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index c29294c2da23c5..4e9d559425c164 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -8,10 +8,14 @@ #include "interpexec.h" #include "callstubgenerator.h" -FCDECL1(uint64_t, JIT_Dbl2ULng, double); -FCDECL1(int64_t, JIT_Dbl2Lng, double); -FCDECL1(uint32_t, JIT_Dbl2UInt, double); -FCDECL1(int32_t, JIT_Dbl2Int, double); +// HACK: debugreturn.h breaks constexpr which is used by +#if defined(debug_instrumented_return) || defined(_DEBUGRETURN_H_) +#undef return +#endif // debug_instrumented_return + +// for numeric_limits +#include + FCDECL1(float, JIT_ULng2Flt, uint64_t val); FCDECL1(double, JIT_ULng2Dbl, uint64_t val); FCDECL1(float, JIT_Lng2Flt, int64_t val); @@ -66,7 +70,7 @@ CallStubHeader *CreateNativeToInterpreterCallStub(InterpMethod* pInterpMethod) if (pHeader == NULL) { // Ensure that there is an interpreter thread context instance and thus an interpreter stack - // allocated for this thread. This allows us to not to have to check and allocate it from + // allocated for this thread. This allows us to not to have to check and allocate it from // the interpreter stub right after this call. GetThread()->GetInterpThreadContext(); @@ -143,6 +147,139 @@ static OBJECTREF CreateMultiDimArray(MethodTable* arrayClass, int8_t* stack, int #define LOCAL_VAR(offset,type) (*LOCAL_VAR_ADDR(offset, type)) #define NULL_CHECK(o) do { if ((o) == NULL) { COMPlusThrow(kNullReferenceException); } } while (0) +template static THelper GetPossiblyIndirectHelper(void* dataItem) +{ + size_t helperDirectOrIndirect = (size_t)dataItem; + if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG) + return *(THelper *)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG); + else + return (THelper)helperDirectOrIndirect; +} + +template static void ConvFpHelper(int8_t *stack, const int32_t *ip) +{ + static_assert(!std::numeric_limits::is_integer, "ConvFpHelper is only for use on floats and doubles"); + static_assert(std::numeric_limits::is_integer, "ConvFpHelper is only for use on floats and doubles to be converted to integers"); + + // First, promote the source value to double + double src = LOCAL_VAR(ip[2], TSource), + minValue = (double)std::numeric_limits::lowest(), + maxValue = (double)std::numeric_limits::max(); + + // (src != src) checks for NaN, then we check whether the min and max values (as represented by their closest double) + // properly bound the source value so that when it is truncated it will be in range + // We assume that we are in round-towards-zero mode. For NaN we want to return 0, and for out of range values, saturate. + TResult result; + if (src != src) + result = 0; + else if (src >= maxValue) + result = std::numeric_limits::max(); + else if (!std::numeric_limits::is_signed && (src <= -1)) + result = 0; + else if (std::numeric_limits::is_signed && (src < minValue)) + result = std::numeric_limits::lowest(); + else + result = (TResult)src; + + // According to spec, for result types smaller than int32, we store them on the stack as int32 + if (sizeof(TResult) >= 4) + LOCAL_VAR(ip[1], TResult) = result; + else + LOCAL_VAR(ip[1], int32_t) = (int32_t)result; +} + +template static void ConvOvfFpHelper(int8_t *stack, const int32_t *ip) +{ + static_assert(!std::numeric_limits::is_integer, "ConvOvfFpHelper is only for use on floats and doubles"); + static_assert(std::numeric_limits::is_integer, "ConvOvfFpHelper is only for use on floats and doubles to be converted to integers"); + static_assert(sizeof(TResult) <= 4, "ConvOvfFpHelper's generic version is only for use on results <= 4 bytes in size"); + + // First, promote the source value to double + double src = LOCAL_VAR(ip[2], TSource), + minValue = (double)std::numeric_limits::lowest() - 1, + maxValue = (double)std::numeric_limits::max() + 1; + + // We assume that we are in round-towards-zero mode + bool inRange = (src > minValue) && (src < maxValue); + + if (!inRange) + COMPlusThrow(kOverflowException); + + TResult truncated = (TResult)src; + // According to spec, for result types smaller than int32, we store them on the stack as int32 + LOCAL_VAR(ip[1], int32_t) = (int32_t)truncated; +} + +// I64 and U64 versions based on mono_math.h + +template static void ConvOvfFpHelperI64(int8_t *stack, const int32_t *ip) +{ + static_assert(!std::numeric_limits::is_integer, "ConvOvfFpHelper is only for use on floats and doubles"); + + const double two63 = 2147483648.0 * 4294967296.0; + // First, promote the source value to double + double src = LOCAL_VAR(ip[2], TSource), + // Define the boundary values we need to be between (see System.Math.ConvertToInt64Checked) + minValue = (-two63 - 0x402), + maxValue = two63; + + bool inRange = (src > minValue) && (src < maxValue); + if (!inRange) + COMPlusThrow(kOverflowException); + + int64_t truncated = (int64_t)src; + LOCAL_VAR(ip[1], int64_t) = truncated; +} + +template static void ConvOvfFpHelperU64(int8_t *stack, const int32_t *ip) +{ + static_assert(!std::numeric_limits::is_integer, "ConvOvfFpHelper is only for use on floats and doubles"); + + // First, promote the source value to double + double src = LOCAL_VAR(ip[2], TSource), + // Define the boundary values we need to be between (see System.Math.ConvertToUInt64Checked) + minValue = -1.0, + maxValue = 4294967296.0 * 4294967296.0; + + bool inRange = (src > minValue) && (src < maxValue); + if (!inRange) + COMPlusThrow(kOverflowException); + + uint64_t truncated = (uint64_t)src; + LOCAL_VAR(ip[1], uint64_t) = truncated; +} + +template void ConvOvfHelper(int8_t *stack, const int32_t *ip) +{ + static_assert(sizeof(TResult) < sizeof(TSource), "ConvOvfHelper only can convert to results smaller than the source value"); + static_assert(std::numeric_limits::is_integer, "ConvOvfHelper is only for use on integers"); + + TSource src = LOCAL_VAR(ip[2], TSource); + bool inRange; + if (std::numeric_limits::is_signed) + { + inRange = (src >= (TSource)std::numeric_limits::lowest()) && (src <= (TSource)std::numeric_limits::max()); + } + else + { + inRange = (src <= (TSource)std::numeric_limits::max()); + } + + if (inRange) + { + // conv_ovf with floating point inputs is handled in ConvOvfFpHelper, so all possible conv_ovfs in this function are + // from int64 into int32-or-smaller, or int32-or-smaller into int16-or-smaller. + // The spec says that conversion results are stored on the evaluation stack as signed, so we always want to convert + // the final truncated result into int32_t before storing it on the stack, even if the truncated result is unsigned. + TResult result = (TResult)src; + LOCAL_VAR(ip[1], int32_t) = (int32_t)result; + } + else + { + COMPlusThrow(kOverflowException); + } +} + void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFrame *pFrame, InterpThreadContext *pThreadContext, ExceptionClauseArgs *pExceptionClauseArgs) { CONTRACTL @@ -307,11 +444,11 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr ip += 3; break; case INTOP_CONV_I1_R4: - LOCAL_VAR(ip[1], int32_t) = (int8_t)HCCALL1(JIT_Dbl2Int, (double)LOCAL_VAR(ip[2], float)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_I1_R8: - LOCAL_VAR(ip[1], int32_t) = (int8_t)HCCALL1(JIT_Dbl2Int, LOCAL_VAR(ip[2], double)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_U1_I4: @@ -323,11 +460,11 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr ip += 3; break; case INTOP_CONV_U1_R4: - LOCAL_VAR(ip[1], int32_t) = (uint8_t)HCCALL1(JIT_Dbl2UInt, (double)LOCAL_VAR(ip[2], float)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_U1_R8: - LOCAL_VAR(ip[1], int32_t) = (uint8_t)HCCALL1(JIT_Dbl2UInt, LOCAL_VAR(ip[2], double)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_I2_I4: @@ -339,11 +476,11 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr ip += 3; break; case INTOP_CONV_I2_R4: - LOCAL_VAR(ip[1], int32_t) = (int16_t)HCCALL1(JIT_Dbl2Int, (double)LOCAL_VAR(ip[2], float)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_I2_R8: - LOCAL_VAR(ip[1], int32_t) = (int16_t)HCCALL1(JIT_Dbl2Int, LOCAL_VAR(ip[2], double)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_U2_I4: @@ -355,27 +492,27 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr ip += 3; break; case INTOP_CONV_U2_R4: - LOCAL_VAR(ip[1], int32_t) = (uint16_t)HCCALL1(JIT_Dbl2UInt, (double)LOCAL_VAR(ip[2], float)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_U2_R8: - LOCAL_VAR(ip[1], int32_t) = (uint16_t)HCCALL1(JIT_Dbl2UInt, LOCAL_VAR(ip[2], double)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_I4_R4: - LOCAL_VAR(ip[1], int32_t) = HCCALL1(JIT_Dbl2Int, (double)LOCAL_VAR(ip[2], float)); + ConvFpHelper(stack, ip); ip += 3; break;; case INTOP_CONV_I4_R8: - LOCAL_VAR(ip[1], int32_t) = HCCALL1(JIT_Dbl2Int, LOCAL_VAR(ip[2], double)); + ConvFpHelper(stack, ip); ip += 3; break;; case INTOP_CONV_U4_R4: - LOCAL_VAR(ip[1], uint32_t) = HCCALL1(JIT_Dbl2UInt, (double)LOCAL_VAR(ip[2], float)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_U4_R8: - LOCAL_VAR(ip[1], uint32_t) = HCCALL1(JIT_Dbl2UInt, LOCAL_VAR(ip[2], double)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_I8_I4: @@ -387,11 +524,11 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr ip += 3; break;; case INTOP_CONV_I8_R4: - LOCAL_VAR(ip[1], int64_t) = HCCALL1(JIT_Dbl2Lng, (double)LOCAL_VAR(ip[2], float)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_I8_R8: - LOCAL_VAR(ip[1], int64_t) = HCCALL1(JIT_Dbl2Lng, LOCAL_VAR(ip[2], double)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_R4_I4: @@ -419,11 +556,123 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr ip += 3; break; case INTOP_CONV_U8_R4: - LOCAL_VAR(ip[1], uint64_t) = HCCALL1(JIT_Dbl2ULng, (double)LOCAL_VAR(ip[2], float)); + ConvFpHelper(stack, ip); ip += 3; break; case INTOP_CONV_U8_R8: - LOCAL_VAR(ip[1], uint64_t) = HCCALL1(JIT_Dbl2ULng, LOCAL_VAR(ip[2], double)); + ConvFpHelper(stack, ip); + ip += 3; + break; + + case INTOP_CONV_OVF_I1_I4: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_I1_I8: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_I1_R4: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_I1_R8: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + + case INTOP_CONV_OVF_U1_I4: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_U1_I8: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_U1_R4: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_U1_R8: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + + case INTOP_CONV_OVF_I2_I4: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_I2_I8: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_I2_R4: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_I2_R8: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + + case INTOP_CONV_OVF_U2_I4: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_U2_I8: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_U2_R4: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_U2_R8: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + + case INTOP_CONV_OVF_I4_I8: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_I4_R4: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_I4_R8: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + + case INTOP_CONV_OVF_U4_I8: + ConvOvfHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_U4_R4: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_U4_R8: + ConvOvfFpHelper(stack, ip); + ip += 3; + break; + + case INTOP_CONV_OVF_I8_R4: + ConvOvfFpHelperI64(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_I8_R8: + ConvOvfFpHelperI64(stack, ip); + ip += 3; + break; + + case INTOP_CONV_OVF_U8_R4: + ConvOvfFpHelperU64(stack, ip); + ip += 3; + break; + case INTOP_CONV_OVF_U8_R8: + ConvOvfFpHelperU64(stack, ip); ip += 3; break; @@ -1164,13 +1413,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr { int base = (*ip == INTOP_CALL_HELPER_PP) ? 2 : 3; - size_t helperDirectOrIndirect = (size_t)pMethod->pDataItems[ip[base]]; - HELPER_FTN_PP helperFtn = nullptr; - if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG) - helperFtn = *(HELPER_FTN_PP *)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG); - else - helperFtn = (HELPER_FTN_PP)helperDirectOrIndirect; - + HELPER_FTN_PP helperFtn = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[base]]); void* helperArg = pMethod->pDataItems[ip[base + 1]]; // This can call either native or compiled managed code. For an interpreter @@ -1400,12 +1643,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr int dreg = ip[1]; int sreg = ip[2]; MethodTable *pMT = (MethodTable*)pMethod->pDataItems[ip[3]]; - size_t helperDirectOrIndirect = (size_t)pMethod->pDataItems[ip[4]]; - HELPER_FTN_BOX_UNBOX helper = nullptr; - if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG) - helper = *(HELPER_FTN_BOX_UNBOX *)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG); - else - helper = (HELPER_FTN_BOX_UNBOX)helperDirectOrIndirect; + HELPER_FTN_BOX_UNBOX helper = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[4]]); if (opcode == INTOP_BOX) { // internal static object Box(MethodTable* typeMT, ref byte unboxedData) @@ -1431,12 +1669,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr COMPlusThrow(kArgumentOutOfRangeException); CORINFO_CLASS_HANDLE arrayClsHnd = (CORINFO_CLASS_HANDLE)pMethod->pDataItems[ip[3]]; - size_t helperDirectOrIndirect = (size_t)pMethod->pDataItems[ip[4]]; - HELPER_FTN_NEWARR helper = nullptr; - if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG) - helper = *(HELPER_FTN_NEWARR *)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG); - else - helper = (HELPER_FTN_NEWARR)helperDirectOrIndirect; + HELPER_FTN_NEWARR helper = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[4]]); Object* arr = helper(arrayClsHnd, (intptr_t)length); LOCAL_VAR(ip[1], OBJECTREF) = ObjectToOBJECTREF(arr); @@ -1792,12 +2025,7 @@ do { \ { int dreg = ip[1]; void *nativeHandle = pMethod->pDataItems[ip[3]]; - size_t helperDirectOrIndirect = (size_t)pMethod->pDataItems[ip[2]]; - 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; + HELPER_FTN_PP helper = GetPossiblyIndirectHelper(pMethod->pDataItems[ip[2]]); void *managedHandle = helper(nativeHandle); LOCAL_VAR(dreg, void*) = managedHandle; ip += 4; diff --git a/src/tests/JIT/interpreter/Interpreter.cs b/src/tests/JIT/interpreter/Interpreter.cs index 01812ec3708ba2..61b40a7d97988d 100644 --- a/src/tests/JIT/interpreter/Interpreter.cs +++ b/src/tests/JIT/interpreter/Interpreter.cs @@ -799,6 +799,25 @@ public static void RunInterpreterTests() if (!TestFloat()) Environment.FailFast(null); + // Unchecked to ensure that the divide-by-zero here doesn't throw since we're using it to generate a NaN + unchecked + { + if (!TestConvOvf(1, 2, 3, 4, 1.0 / 0.0, -32, 1234567890)) + Environment.FailFast(null); + + if (!TestConvBoundaries( + 32767.999999999996, 32768.00000000001, + 2147483647.9999998, 2147483648.0000005 + )) + Environment.FailFast(null); + + if (!TestConvBoundaries( + -32768.99999999999, -32769.00000000001, + -2147483648.9999995, -2147483649.0000005 + )) + Environment.FailFast(null); + } + Console.WriteLine("TestLocalloc"); if (!TestLocalloc()) Environment.FailFast(null); @@ -1464,6 +1483,79 @@ public static bool PowLoop(int n, long nr, int expected) return (int)ret == expected; } + public static bool TestConvOvf(float r4, double r8, int i4, long i8, double nan, int negativeInt, long hugeInt) + { + checked + { + byte a = (byte)r4, + b = (byte)r8, + c = (byte)i4, + d = (byte)i8; + + if (a != r4) + return false; + if (b != r8) + return false; + if (c != i4) + return false; + if (d != i8) + return false; + + try { + a = (byte)nan; + return false; + } catch (OverflowException) { + } + + try { + b = (byte)hugeInt; + return false; + } catch (OverflowException) { + } + + try { + c = (byte)negativeInt; + return false; + } catch (OverflowException) { + } + } + + return true; + } + + public static bool TestConvBoundaries (double inRangeShort, double outOfRangeShort, double inRangeInt, double outOfRangeInt) { + // In unchecked mode, the interpreter saturates on float->int conversions if the value is out of range + unchecked { + short a = (short)inRangeShort, + b = (short)outOfRangeShort; + int c = (int)inRangeInt, + d = (int)outOfRangeInt; + + if (a != b) + return false; + if (c != d) + return false; + } + + checked { + short tempA = (short)inRangeShort; + try { + tempA = (short)outOfRangeShort; + return false; + } catch (OverflowException) { + } + + int tempB = (int)inRangeInt; + try { + tempB = (int)outOfRangeInt; + return false; + } catch (OverflowException) { + } + } + + return true; + } + public static int jitField1; [ThreadStatic] public static int jitField2;