Skip to content

Commit

Permalink
Move checked math helpers to C#
Browse files Browse the repository at this point in the history
- Eliminate a few more FCThrow
- Take advantage of ConvertToNative to make helpers faster on current hardware
  • Loading branch information
jkotas committed May 12, 2024
1 parent bdfbed6 commit dfa72ea
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 381 deletions.
12 changes: 6 additions & 6 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,22 @@
JITHELPER(CORINFO_HELP_LRSZ, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB)
#endif // TARGET_64BIT
JITHELPER(CORINFO_HELP_LMUL, JIT_LMul, CORINFO_HELP_SIG_16_STACK)
JITHELPER(CORINFO_HELP_LMUL_OVF, JIT_LMulOvf, CORINFO_HELP_SIG_16_STACK)
JITHELPER(CORINFO_HELP_ULMUL_OVF, JIT_ULMulOvf, CORINFO_HELP_SIG_16_STACK)
DYNAMICJITHELPER(CORINFO_HELP_LMUL_OVF, NULL, CORINFO_HELP_SIG_16_STACK)
DYNAMICJITHELPER(CORINFO_HELP_ULMUL_OVF, NULL, CORINFO_HELP_SIG_16_STACK)
JITHELPER(CORINFO_HELP_LDIV, JIT_LDiv, CORINFO_HELP_SIG_16_STACK)
JITHELPER(CORINFO_HELP_LMOD, JIT_LMod, CORINFO_HELP_SIG_16_STACK)
JITHELPER(CORINFO_HELP_ULDIV, JIT_ULDiv, CORINFO_HELP_SIG_16_STACK)
JITHELPER(CORINFO_HELP_ULMOD, JIT_ULMod, CORINFO_HELP_SIG_16_STACK)
JITHELPER(CORINFO_HELP_LNG2DBL, JIT_Lng2Dbl, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_ULNG2DBL, JIT_ULng2Dbl, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_DBL2INT, JIT_Dbl2Int, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_DBL2INT_OVF, JIT_Dbl2IntOvf, CORINFO_HELP_SIG_8_STACK)
DYNAMICJITHELPER(CORINFO_HELP_DBL2INT_OVF, NULL, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_DBL2LNG, JIT_Dbl2Lng, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_DBL2LNG_OVF, JIT_Dbl2LngOvf, CORINFO_HELP_SIG_8_STACK)
DYNAMICJITHELPER(CORINFO_HELP_DBL2LNG_OVF, NULL, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_DBL2UINT, JIT_Dbl2UInt, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_DBL2UINT_OVF, JIT_Dbl2UIntOvf, CORINFO_HELP_SIG_8_STACK)
DYNAMICJITHELPER(CORINFO_HELP_DBL2UINT_OVF, NULL, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_DBL2ULNG, JIT_Dbl2ULng, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_DBL2ULNG_OVF, JIT_Dbl2ULngOvf, CORINFO_HELP_SIG_8_STACK)
DYNAMICJITHELPER(CORINFO_HELP_DBL2ULNG_OVF, NULL, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_FLTREM, JIT_FltRem, CORINFO_HELP_SIG_8_STACK)
JITHELPER(CORINFO_HELP_DBLREM, JIT_DblRem, CORINFO_HELP_SIG_16_STACK)
DYNAMICJITHELPER(CORINFO_HELP_FLTROUND, NULL, CORINFO_HELP_SIG_8_STACK)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,195 +15,9 @@ namespace Internal.Runtime.CompilerHelpers
[StackTraceHidden]
internal static partial class MathHelpers
{
private const double Int32MaxValueOffset = (double)int.MaxValue + 1;
private const double UInt32MaxValueOffset = (double)uint.MaxValue + 1;

[RuntimeExport("Dbl2IntOvf")]
public static int Dbl2IntOvf(double value)
{
// Note that this expression also works properly for val = NaN case
if (value is > -Int32MaxValueOffset - 1 and < Int32MaxValueOffset)
{
return (int)value;
}

ThrowHelper.ThrowOverflowException();
return 0;
}

[RuntimeExport("Dbl2UIntOvf")]
public static uint Dbl2UIntOvf(double value)
{
// Note that this expression also works properly for val = NaN case
if (value is > -1.0 and < UInt32MaxValueOffset)
{
return (uint)value;
}

ThrowHelper.ThrowOverflowException();
return 0;
}

[RuntimeExport("Dbl2LngOvf")]
public static long Dbl2LngOvf(double value)
{
const double two63 = Int32MaxValueOffset * UInt32MaxValueOffset;

// 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 (value is > -two63 - 0x402 and < two63)
{
return (long)value;
}

ThrowHelper.ThrowOverflowException();
return 0;
}

[RuntimeExport("Dbl2ULngOvf")]
public static ulong Dbl2ULngOvf(double value)
{
const double two64 = UInt32MaxValueOffset * UInt32MaxValueOffset;
// Note that this expression also works properly for val = NaN case
if (value is > -1.0 and < two64)
{
return (ulong)value;
}

ThrowHelper.ThrowOverflowException();
return 0;
}

#if !TARGET_64BIT
//
// 64-bit checked multiplication for 32-bit platforms
//

private const string RuntimeLibrary = "*";

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint High32Bits(ulong a)
{
return (uint)(a >> 32);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong BigMul(uint left, uint right)
{
return (ulong)left * right;
}

[RuntimeExport("LMulOvf")]
public static long LMulOvf(long left, long right)
{
#if DEBUG
long result = left * right;
#endif

// Remember the sign of the result
int sign = (int)(High32Bits((ulong)left) ^ High32Bits((ulong)right));

// Convert to unsigned multiplication
if (left < 0)
left = -left;
if (right < 0)
right = -right;

// Get the upper 32 bits of the numbers
uint val1High = High32Bits((ulong)left);
uint val2High = High32Bits((ulong)right);

ulong valMid;

if (val1High == 0)
{
// Compute the 'middle' bits of the long multiplication
valMid = BigMul(val2High, (uint)left);
}
else
{
if (val2High != 0)
goto Overflow;
// Compute the 'middle' bits of the long multiplication
valMid = BigMul(val1High, (uint)right);
}

// See if any bits after bit 32 are set
if (High32Bits(valMid) != 0)
goto Overflow;

long ret = (long)(BigMul((uint)left, (uint)right) + (valMid << 32));

// check for overflow
if (High32Bits((ulong)ret) < (uint)valMid)
goto Overflow;

if (sign >= 0)
{
// have we spilled into the sign bit?
if (ret < 0)
goto Overflow;
}
else
{
ret = -ret;
// have we spilled into the sign bit?
if (ret > 0)
goto Overflow;
}

#if DEBUG
Debug.Assert(ret == result, $"Multiply overflow got: {ret}, expected: {result}");
#endif
return ret;

Overflow:
ThrowHelper.ThrowOverflowException();
return 0;
}

[RuntimeExport("ULMulOvf")]
public static ulong ULMulOvf(ulong left, ulong right)
{
// Get the upper 32 bits of the numbers
uint val1High = High32Bits(left);
uint val2High = High32Bits(right);

ulong valMid;

if (val1High == 0)
{
if (val2High == 0)
return (ulong)(uint)left * (uint)right;
// Compute the 'middle' bits of the long multiplication
valMid = BigMul(val2High, (uint)left);
}
else
{
if (val2High != 0)
goto Overflow;
// Compute the 'middle' bits of the long multiplication
valMid = BigMul(val1High, (uint)right);
}

// See if any bits after bit 32 are set
if (High32Bits(valMid) != 0)
goto Overflow;

ulong ret = BigMul((uint)left, (uint)right) + (valMid << 32);

// check for overflow
if (High32Bits(ret) < (uint)valMid)
goto Overflow;

Debug.Assert(ret == left * right, $"Multiply overflow got: {ret}, expected: {left * right}");
return ret;

Overflow:
ThrowHelper.ThrowOverflowException();
return 0;
}

[LibraryImport(RuntimeLibrary)]
[SuppressGCTransition]
private static partial ulong RhpULMod(ulong dividend, ulong divisor);
Expand Down
12 changes: 6 additions & 6 deletions src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,16 +188,16 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id,
break;

case ReadyToRunHelper.Dbl2IntOvf:
methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2IntOvf");
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("ConvertToInt32Checked", null);
break;
case ReadyToRunHelper.Dbl2UIntOvf:
methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2UIntOvf");
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("ConvertToUInt32Checked", null);
break;
case ReadyToRunHelper.Dbl2LngOvf:
methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2LngOvf");
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("ConvertToInt64Checked", null);
break;
case ReadyToRunHelper.Dbl2ULngOvf:
methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2ULngOvf");
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("ConvertToUInt64Checked", null);
break;

case ReadyToRunHelper.DblRem:
Expand All @@ -211,10 +211,10 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id,
mangledName = "RhpLMul";
break;
case ReadyToRunHelper.LMulOfv:
methodDesc = context.GetHelperEntryPoint("MathHelpers", "LMulOvf");
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("MultiplyInt64Checked", null);
break;
case ReadyToRunHelper.ULMulOvf:
methodDesc = context.GetHelperEntryPoint("MathHelpers", "ULMulOvf");
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("MultiplyUInt64Checked", null);
break;

case ReadyToRunHelper.Mod:
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,14 @@ DEFINE_CLASS(UINT128, System, UInt128)

DEFINE_CLASS(MATH, System, Math)
DEFINE_METHOD(MATH, ROUND, Round, SM_Dbl_RetDbl)
#ifndef TARGET_64BIT
DEFINE_METHOD(MATH, MULTIPLY_INT64_CHECKED, MultiplyInt64Checked, NoSig)
DEFINE_METHOD(MATH, MULTIPLY_UINT64_CHECKED, MultiplyUInt64Checked, NoSig)
#endif
DEFINE_METHOD(MATH, CONVERT_TO_INT32_CHECKED, ConvertToInt32Checked, NoSig)
DEFINE_METHOD(MATH, CONVERT_TO_UINT32_CHECKED, ConvertToUInt32Checked, NoSig)
DEFINE_METHOD(MATH, CONVERT_TO_INT64_CHECKED, ConvertToInt64Checked, NoSig)
DEFINE_METHOD(MATH, CONVERT_TO_UINT64_CHECKED, ConvertToUInt64Checked, NoSig)

DEFINE_CLASS(MATHF, System, MathF)
DEFINE_METHOD(MATHF, ROUND, Round, SM_Flt_RetFlt)
Expand Down
26 changes: 26 additions & 0 deletions src/coreclr/vm/ecall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,32 @@ void ECall::PopulateManagedHelpers()
pDest = pMD->GetMultiCallableAddrOfCode();
SetJitHelperFunction(CORINFO_HELP_BULK_WRITEBARRIER, pDest);

#ifndef TARGET_64BIT
pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATH__INT64_MULTIPLY_CHECKED));
pDest = pMD->GetMultiCallableAddrOfCode();
SetJitHelperFunction(CORINFO_HELP_LMUL_OVF, pDest);

pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATH__UINT64_MULTIPLY_CHECKED));
pDest = pMD->GetMultiCallableAddrOfCode();
SetJitHelperFunction(CORINFO_HELP_ULMUL_OVF, pDest);
#endif

pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATH__CONVERT_TO_INT32_CHECKED));
pDest = pMD->GetMultiCallableAddrOfCode();
SetJitHelperFunction(CORINFO_HELP_DBL2INT_OVF, pDest);

pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATH__CONVERT_TO_UINT32_CHECKED));
pDest = pMD->GetMultiCallableAddrOfCode();
SetJitHelperFunction(CORINFO_HELP_DBL2UINT_OVF, pDest);

pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATH__CONVERT_TO_INT64_CHECKED));
pDest = pMD->GetMultiCallableAddrOfCode();
SetJitHelperFunction(CORINFO_HELP_DBL2LNG_OVF, pDest);

pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATH__CONVERT_TO_UINT64_CHECKED));
pDest = pMD->GetMultiCallableAddrOfCode();
SetJitHelperFunction(CORINFO_HELP_DBL2ULNG_OVF, pDest);

pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATH__ROUND));
pDest = pMD->GetMultiCallableAddrOfCode();
SetJitHelperFunction(CORINFO_HELP_DBLROUND, pDest);
Expand Down

0 comments on commit dfa72ea

Please sign in to comment.