Skip to content

Commit

Permalink
Saturating floating point to integer conversions on Arm32 (dotnet#100682
Browse files Browse the repository at this point in the history
)

* Saturating floating point to integer conversions on Arm32

Follow up on dotnet#97529 (comment)

* Fixes, cleanup
  • Loading branch information
jkotas authored and matouskozak committed Apr 30, 2024
1 parent dc62901 commit 70b8754
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 142 deletions.
25 changes: 7 additions & 18 deletions src/coreclr/nativeaot/Runtime/MathHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,20 @@ FCIMPL1_D(uint64_t, RhpDbl2ULng, double val)
const double uint64_max_plus_1 = 4294967296.0 * 4294967296.0;
return (val > 0) ? ((val >= uint64_max_plus_1) ? UINT64_MAX : (uint64_t)val) : 0;
#else
const double two63 = 2147483648.0 * 4294967296.0;
uint64_t ret;
if (val < two63)
{
ret = (int64_t)(val);
}
else
{
// subtract 0x8000000000000000, do the convert then add it back again
ret = (int64_t)(val - two63) + I64(0x8000000000000000);
}
return ret;
#endif //HOST_X86 || HOST_AMD64
return (uint64_t)val;
#endif
}
FCIMPLEND

FCIMPL1_D(int64_t, RhpDbl2Lng, double val)
{
#if defined(HOST_X86) || defined(HOST_AMD64)
#if defined(HOST_X86) || defined(HOST_AMD64) || defined(HOST_ARM)
const double int64_min = -2147483648.0 * 4294967296.0;
const double int64_max = 2147483648.0 * 4294967296.0;
return (val != val) ? 0 : (val <= int64_min) ? INT64_MIN : (val >= int64_max) ? INT64_MAX : (int64_t)val;
#else
return (int64_t)val;
#endif //HOST_X86 || HOST_AMD64
#endif
}
FCIMPLEND

Expand All @@ -51,7 +40,7 @@ FCIMPL1_D(int32_t, RhpDbl2Int, double val)
return (val != val) ? 0 : (val <= int32_min) ? INT32_MIN : (val >= int32_max_plus_1) ? INT32_MAX : (int32_t)val;
#else
return (int32_t)val;
#endif //HOST_X86 || HOST_AMD64
#endif
}
FCIMPLEND

Expand All @@ -62,7 +51,7 @@ FCIMPL1_D(uint32_t, RhpDbl2UInt, double val)
return (val > 0) ? ((val >= uint_max) ? UINT32_MAX : (uint32_t)val) : 0;
#else
return (uint32_t)val;
#endif //HOST_X86 || HOST_AMD64
#endif
}
FCIMPLEND

Expand Down Expand Up @@ -358,4 +347,4 @@ FCIMPL2_FI(float, modff, float x, float* intptr)
return std::modff(x, intptr);
FCIMPLEND

#endif
#endif
73 changes: 28 additions & 45 deletions src/coreclr/vm/jithelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,51 +491,44 @@ HCIMPLEND
#include <optsmallperfcritical.h>

/*********************************************************************/
//
HCIMPL1_V(double, JIT_ULng2Dbl, UINT64 val)
HCIMPL1_V(double, JIT_ULng2Dbl, uint64_t val)
{
FCALL_CONTRACT;

double conv = (double) ((INT64) val);
if (conv < 0)
conv += (4294967296.0 * 4294967296.0); // add 2^64
_ASSERTE(conv >= 0);
return(conv);
return (double)val;
}
HCIMPLEND

/*********************************************************************/
// needed for ARM and RyuJIT-x86
HCIMPL1_V(double, JIT_Lng2Dbl, INT64 val)
HCIMPL1_V(double, JIT_Lng2Dbl, int64_t val)
{
FCALL_CONTRACT;
return double(val);
return (double)val;
}
HCIMPLEND

/*********************************************************************/
HCIMPL1_V(INT64, JIT_Dbl2Lng, double val)
HCIMPL1_V(int64_t, JIT_Dbl2Lng, double val)
{
FCALL_CONTRACT;

#if defined(TARGET_X86) || defined(TARGET_AMD64)
#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM)
const double int64_min = -2147483648.0 * 4294967296.0;
const double int64_max = 2147483648.0 * 4294967296.0;
return (val != val) ? 0 : (val <= int64_min) ? INT64_MIN : (val >= int64_max) ? INT64_MAX : (INT64)val;
return (val != val) ? 0 : (val <= int64_min) ? INT64_MIN : (val >= int64_max) ? INT64_MAX : (int64_t)val;
#else
return((INT64)val);
#endif // TARGET_X86 || TARGET_AMD64
return (int64_t)val;
#endif
}
HCIMPLEND

/*********************************************************************/
HCIMPL1_V(UINT32, JIT_Dbl2UIntOvf, double val)
HCIMPL1_V(uint32_t, JIT_Dbl2UIntOvf, double val)
{
FCALL_CONTRACT;

// Note that this expression also works properly for val = NaN case
if (val > -1.0 && val < 4294967296.0)
return((UINT32)val);
return (uint32_t)val;

FCThrow(kOverflowException);
}
Expand All @@ -549,14 +542,14 @@ HCIMPL1_V(int, JIT_Dbl2IntOvf, double val)
const double two31 = 2147483648.0;
// Note that this expression also works properly for val = NaN case
if (val > -two31 - 1 && val < two31)
return((INT32)val);
return (int32_t)val;

FCThrow(kOverflowException);
}
HCIMPLEND

/*********************************************************************/
HCIMPL1_V(INT64, JIT_Dbl2LngOvf, double val)
HCIMPL1_V(int64_t, JIT_Dbl2LngOvf, double val)
{
FCALL_CONTRACT;

Expand All @@ -565,77 +558,67 @@ HCIMPL1_V(INT64, JIT_Dbl2LngOvf, double val)
// Note that this expression also works properly for val = NaN case
// We need to compare with the very next double to two63. 0x402 is epsilon to get us there.
if (val > -two63 - 0x402 && val < two63)
return((INT64)val);
return (int64_t)val;

FCThrow(kOverflowException);
}
HCIMPLEND

/*********************************************************************/
HCIMPL1_V(UINT64, JIT_Dbl2ULngOvf, double val)
HCIMPL1_V(uint64_t, JIT_Dbl2ULngOvf, double val)
{
FCALL_CONTRACT;

const double two64 = 4294967296.0 * 4294967296.0;
// Note that this expression also works properly for val = NaN case
if (val > -1.0 && val < two64)
return (UINT64)val;
return (uint64_t)val;

FCThrow(kOverflowException);
}
HCIMPLEND

HCIMPL1_V(UINT32, JIT_Dbl2UInt, double val)
HCIMPL1_V(uint32_t, JIT_Dbl2UInt, double val)
{
FCALL_CONTRACT;

#if defined(TARGET_X86) || defined(TARGET_AMD64)
const double uint_max = 4294967295.0;
// Note that this expression also works properly for val = NaN case
return (val >= 0) ? ((val >= uint_max) ? UINT32_MAX : (UINT32)val) : 0;
return (val >= 0) ? ((val >= uint_max) ? UINT32_MAX : (uint32_t)val) : 0;
#else
return((UINT32)val);
#endif //TARGET_X86 || TARGET_AMD64
return (uint32_t)val;
#endif
}
HCIMPLEND

/*********************************************************************/
HCIMPL1_V(INT32, JIT_Dbl2Int, double val)
HCIMPL1_V(int32_t, JIT_Dbl2Int, double val)
{
FCALL_CONTRACT;

#if defined(TARGET_X86) || defined(TARGET_AMD64)
const double int32_min = -2147483648.0;
const double int32_max_plus_1 = 2147483648.0;
return (val != val) ? 0 : (val <= int32_min) ? INT32_MIN : (val >= int32_max_plus_1) ? INT32_MAX : (INT32)val;
return (val != val) ? 0 : (val <= int32_min) ? INT32_MIN : (val >= int32_max_plus_1) ? INT32_MAX : (int32_t)val;
#else
return((INT32)val);
#endif // TARGET_X86 || TARGET_AMD64
return (int32_t)val;
#endif
}
HCIMPLEND

/*********************************************************************/
HCIMPL1_V(UINT64, JIT_Dbl2ULng, double val)
HCIMPL1_V(uint64_t, JIT_Dbl2ULng, double val)
{
FCALL_CONTRACT;

#if defined(TARGET_X86) || defined(TARGET_AMD64)
const double uint64_max_plus_1 = 4294967296.0 * 4294967296.0;
// Note that this expression also works properly for val = NaN case
return (val >= 0) ? ((val >= uint64_max_plus_1) ? UINT64_MAX : (UINT64)val) : 0;

return (val >= 0) ? ((val >= uint64_max_plus_1) ? UINT64_MAX : (uint64_t)val) : 0;
#else
const double two63 = 2147483648.0 * 4294967296.0;
UINT64 ret;
if (val < two63) {
ret = (INT64)(val);
}
else {
// subtract 0x8000000000000000, do the convert then add it back again
ret = (INT64)(val - two63) + I64(0x8000000000000000);
}
return ret;
#endif // TARGET_X86 || TARGET_AMD64
return (uint64_t)val;
#endif
}
HCIMPLEND

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ typedef enum {
CONVERT_SENTINEL,
CONVERT_SATURATING,
CONVERT_NATIVECOMPILERBEHAVIOR,
CONVERT_MANAGED_BACKWARD_COMPATIBLE_ARM32,
} FPtoIntegerConversionType;

extern "C" DLLEXPORT int32_t ConvertDoubleToInt32(double x, FPtoIntegerConversionType t)
Expand All @@ -32,7 +31,6 @@ extern "C" DLLEXPORT int32_t ConvertDoubleToInt32(double x, FPtoIntegerConversio
case CONVERT_SENTINEL:
return ((x != x) || (x < INT32_MIN) || (x > INT32_MAX)) ? INT32_MIN : (int32_t)x;

case CONVERT_MANAGED_BACKWARD_COMPATIBLE_ARM32:
case CONVERT_SATURATING:
return (x != x) ? 0 : (x < INT32_MIN) ? INT32_MIN : (x > INT32_MAX) ? INT32_MAX : (int32_t)x;
case CONVERT_NATIVECOMPILERBEHAVIOR: // handled above, but add case to silence warning
Expand All @@ -57,7 +55,6 @@ extern "C" DLLEXPORT uint32_t ConvertDoubleToUInt32(double x, FPtoIntegerConvers
case CONVERT_SENTINEL:
return ((x != x) || (x < 0) || (x > UINT32_MAX)) ? UINT32_MAX : (uint32_t)x;

case CONVERT_MANAGED_BACKWARD_COMPATIBLE_ARM32:
case CONVERT_SATURATING:
return ((x != x) || (x < 0)) ? 0 : (x > UINT32_MAX) ? UINT32_MAX : (uint32_t)x;
case CONVERT_NATIVECOMPILERBEHAVIOR: // handled above, but add case to silence warning
Expand All @@ -67,14 +64,6 @@ extern "C" DLLEXPORT uint32_t ConvertDoubleToUInt32(double x, FPtoIntegerConvers
return 0;
}

static uint64_t CppNativeArm32ConvertDoubleToUInt64(double y)
{
const double uintmax_plus_1 = -2.0 * (double)INT32_MIN;
uint32_t hi32Bits = ConvertDoubleToUInt32(y / uintmax_plus_1, CONVERT_SATURATING);
uint32_t lo32Bits = ConvertDoubleToUInt32(y - (((double)hi32Bits) * uintmax_plus_1), CONVERT_SATURATING);
return (((uint64_t)hi32Bits) << 32) + lo32Bits;
}

extern "C" DLLEXPORT int64_t ConvertDoubleToInt64(double x, FPtoIntegerConversionType t)
{
if (t == CONVERT_NATIVECOMPILERBEHAVIOR)
Expand All @@ -96,16 +85,6 @@ extern "C" DLLEXPORT int64_t ConvertDoubleToInt64(double x, FPtoIntegerConversio
case CONVERT_SENTINEL:
return ((x != x) || (x < INT64_MIN) || (x >= int64_max_plus_1)) ? INT64_MIN : (int64_t)x;

case CONVERT_MANAGED_BACKWARD_COMPATIBLE_ARM32:
if (x > 0)
{
return (int64_t)CppNativeArm32ConvertDoubleToUInt64(x);
}
else
{
return -(int64_t)CppNativeArm32ConvertDoubleToUInt64(-x);
}

case CONVERT_SATURATING:
return (x != x) ? 0 : (x < INT64_MIN) ? INT64_MIN : (x >= int64_max_plus_1) ? INT64_MAX : (int64_t)x;
case CONVERT_NATIVECOMPILERBEHAVIOR: // handled above, but add case to silence warning
Expand Down Expand Up @@ -138,18 +117,6 @@ extern "C" DLLEXPORT uint64_t ConvertDoubleToUInt64(double x, FPtoIntegerConver
case CONVERT_SATURATING:
return ((x != x) || (x < 0)) ? 0 : (x >= uint64_max_plus_1) ? UINT64_MAX : (uint64_t)x;

case CONVERT_MANAGED_BACKWARD_COMPATIBLE_ARM32:
{
if (x < int64_max_plus_1)
{
return (uint64_t)ConvertDoubleToInt64(x, CONVERT_MANAGED_BACKWARD_COMPATIBLE_ARM32);
}
else
{
return (uint64_t)ConvertDoubleToInt64(x - int64_max_plus_1, CONVERT_MANAGED_BACKWARD_COMPATIBLE_ARM32) + (0x8000000000000000);
}
}

case CONVERT_NATIVECOMPILERBEHAVIOR: // handled above, but add case to silence warning
return 0;
}
Expand Down

0 comments on commit 70b8754

Please sign in to comment.