Skip to content

Commit 42c2362

Browse files
authored
Move checked math helpers to C# (#102129)
* Move checked math helpers to C# - Eliminate a few more FCThrow - Take advantage of ConvertToNative to make helpers faster on current hardware * Use collection expressions in more places
1 parent 4b87c13 commit 42c2362

File tree

13 files changed

+264
-392
lines changed

13 files changed

+264
-392
lines changed

src/coreclr/inc/jithelpers.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,22 @@
4747
JITHELPER(CORINFO_HELP_LRSZ, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB)
4848
#endif // TARGET_64BIT
4949
JITHELPER(CORINFO_HELP_LMUL, JIT_LMul, CORINFO_HELP_SIG_16_STACK)
50-
JITHELPER(CORINFO_HELP_LMUL_OVF, JIT_LMulOvf, CORINFO_HELP_SIG_16_STACK)
51-
JITHELPER(CORINFO_HELP_ULMUL_OVF, JIT_ULMulOvf, CORINFO_HELP_SIG_16_STACK)
50+
DYNAMICJITHELPER(CORINFO_HELP_LMUL_OVF, NULL, CORINFO_HELP_SIG_16_STACK)
51+
DYNAMICJITHELPER(CORINFO_HELP_ULMUL_OVF, NULL, CORINFO_HELP_SIG_16_STACK)
5252
JITHELPER(CORINFO_HELP_LDIV, JIT_LDiv, CORINFO_HELP_SIG_16_STACK)
5353
JITHELPER(CORINFO_HELP_LMOD, JIT_LMod, CORINFO_HELP_SIG_16_STACK)
5454
JITHELPER(CORINFO_HELP_ULDIV, JIT_ULDiv, CORINFO_HELP_SIG_16_STACK)
5555
JITHELPER(CORINFO_HELP_ULMOD, JIT_ULMod, CORINFO_HELP_SIG_16_STACK)
5656
JITHELPER(CORINFO_HELP_LNG2DBL, JIT_Lng2Dbl, CORINFO_HELP_SIG_8_STACK)
5757
JITHELPER(CORINFO_HELP_ULNG2DBL, JIT_ULng2Dbl, CORINFO_HELP_SIG_8_STACK)
5858
JITHELPER(CORINFO_HELP_DBL2INT, JIT_Dbl2Int, CORINFO_HELP_SIG_8_STACK)
59-
JITHELPER(CORINFO_HELP_DBL2INT_OVF, JIT_Dbl2IntOvf, CORINFO_HELP_SIG_8_STACK)
59+
DYNAMICJITHELPER(CORINFO_HELP_DBL2INT_OVF, NULL, CORINFO_HELP_SIG_8_STACK)
6060
JITHELPER(CORINFO_HELP_DBL2LNG, JIT_Dbl2Lng, CORINFO_HELP_SIG_8_STACK)
61-
JITHELPER(CORINFO_HELP_DBL2LNG_OVF, JIT_Dbl2LngOvf, CORINFO_HELP_SIG_8_STACK)
61+
DYNAMICJITHELPER(CORINFO_HELP_DBL2LNG_OVF, NULL, CORINFO_HELP_SIG_8_STACK)
6262
JITHELPER(CORINFO_HELP_DBL2UINT, JIT_Dbl2UInt, CORINFO_HELP_SIG_8_STACK)
63-
JITHELPER(CORINFO_HELP_DBL2UINT_OVF, JIT_Dbl2UIntOvf, CORINFO_HELP_SIG_8_STACK)
63+
DYNAMICJITHELPER(CORINFO_HELP_DBL2UINT_OVF, NULL, CORINFO_HELP_SIG_8_STACK)
6464
JITHELPER(CORINFO_HELP_DBL2ULNG, JIT_Dbl2ULng, CORINFO_HELP_SIG_8_STACK)
65-
JITHELPER(CORINFO_HELP_DBL2ULNG_OVF, JIT_Dbl2ULngOvf, CORINFO_HELP_SIG_8_STACK)
65+
DYNAMICJITHELPER(CORINFO_HELP_DBL2ULNG_OVF, NULL, CORINFO_HELP_SIG_8_STACK)
6666
JITHELPER(CORINFO_HELP_FLTREM, JIT_FltRem, CORINFO_HELP_SIG_8_STACK)
6767
JITHELPER(CORINFO_HELP_DBLREM, JIT_DblRem, CORINFO_HELP_SIG_16_STACK)
6868
DYNAMICJITHELPER(CORINFO_HELP_FLTROUND, NULL, CORINFO_HELP_SIG_8_STACK)

src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs

-186
Original file line numberDiff line numberDiff line change
@@ -15,195 +15,9 @@ namespace Internal.Runtime.CompilerHelpers
1515
[StackTraceHidden]
1616
internal static partial class MathHelpers
1717
{
18-
private const double Int32MaxValueOffset = (double)int.MaxValue + 1;
19-
private const double UInt32MaxValueOffset = (double)uint.MaxValue + 1;
20-
21-
[RuntimeExport("Dbl2IntOvf")]
22-
public static int Dbl2IntOvf(double value)
23-
{
24-
// Note that this expression also works properly for val = NaN case
25-
if (value is > -Int32MaxValueOffset - 1 and < Int32MaxValueOffset)
26-
{
27-
return (int)value;
28-
}
29-
30-
ThrowHelper.ThrowOverflowException();
31-
return 0;
32-
}
33-
34-
[RuntimeExport("Dbl2UIntOvf")]
35-
public static uint Dbl2UIntOvf(double value)
36-
{
37-
// Note that this expression also works properly for val = NaN case
38-
if (value is > -1.0 and < UInt32MaxValueOffset)
39-
{
40-
return (uint)value;
41-
}
42-
43-
ThrowHelper.ThrowOverflowException();
44-
return 0;
45-
}
46-
47-
[RuntimeExport("Dbl2LngOvf")]
48-
public static long Dbl2LngOvf(double value)
49-
{
50-
const double two63 = Int32MaxValueOffset * UInt32MaxValueOffset;
51-
52-
// Note that this expression also works properly for val = NaN case
53-
// We need to compare with the very next double to two63. 0x402 is epsilon to get us there.
54-
if (value is > -two63 - 0x402 and < two63)
55-
{
56-
return (long)value;
57-
}
58-
59-
ThrowHelper.ThrowOverflowException();
60-
return 0;
61-
}
62-
63-
[RuntimeExport("Dbl2ULngOvf")]
64-
public static ulong Dbl2ULngOvf(double value)
65-
{
66-
const double two64 = UInt32MaxValueOffset * UInt32MaxValueOffset;
67-
// Note that this expression also works properly for val = NaN case
68-
if (value is > -1.0 and < two64)
69-
{
70-
return (ulong)value;
71-
}
72-
73-
ThrowHelper.ThrowOverflowException();
74-
return 0;
75-
}
76-
7718
#if !TARGET_64BIT
78-
//
79-
// 64-bit checked multiplication for 32-bit platforms
80-
//
81-
8219
private const string RuntimeLibrary = "*";
8320

84-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
85-
private static uint High32Bits(ulong a)
86-
{
87-
return (uint)(a >> 32);
88-
}
89-
90-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
91-
private static ulong BigMul(uint left, uint right)
92-
{
93-
return (ulong)left * right;
94-
}
95-
96-
[RuntimeExport("LMulOvf")]
97-
public static long LMulOvf(long left, long right)
98-
{
99-
#if DEBUG
100-
long result = left * right;
101-
#endif
102-
103-
// Remember the sign of the result
104-
int sign = (int)(High32Bits((ulong)left) ^ High32Bits((ulong)right));
105-
106-
// Convert to unsigned multiplication
107-
if (left < 0)
108-
left = -left;
109-
if (right < 0)
110-
right = -right;
111-
112-
// Get the upper 32 bits of the numbers
113-
uint val1High = High32Bits((ulong)left);
114-
uint val2High = High32Bits((ulong)right);
115-
116-
ulong valMid;
117-
118-
if (val1High == 0)
119-
{
120-
// Compute the 'middle' bits of the long multiplication
121-
valMid = BigMul(val2High, (uint)left);
122-
}
123-
else
124-
{
125-
if (val2High != 0)
126-
goto Overflow;
127-
// Compute the 'middle' bits of the long multiplication
128-
valMid = BigMul(val1High, (uint)right);
129-
}
130-
131-
// See if any bits after bit 32 are set
132-
if (High32Bits(valMid) != 0)
133-
goto Overflow;
134-
135-
long ret = (long)(BigMul((uint)left, (uint)right) + (valMid << 32));
136-
137-
// check for overflow
138-
if (High32Bits((ulong)ret) < (uint)valMid)
139-
goto Overflow;
140-
141-
if (sign >= 0)
142-
{
143-
// have we spilled into the sign bit?
144-
if (ret < 0)
145-
goto Overflow;
146-
}
147-
else
148-
{
149-
ret = -ret;
150-
// have we spilled into the sign bit?
151-
if (ret > 0)
152-
goto Overflow;
153-
}
154-
155-
#if DEBUG
156-
Debug.Assert(ret == result, $"Multiply overflow got: {ret}, expected: {result}");
157-
#endif
158-
return ret;
159-
160-
Overflow:
161-
ThrowHelper.ThrowOverflowException();
162-
return 0;
163-
}
164-
165-
[RuntimeExport("ULMulOvf")]
166-
public static ulong ULMulOvf(ulong left, ulong right)
167-
{
168-
// Get the upper 32 bits of the numbers
169-
uint val1High = High32Bits(left);
170-
uint val2High = High32Bits(right);
171-
172-
ulong valMid;
173-
174-
if (val1High == 0)
175-
{
176-
if (val2High == 0)
177-
return (ulong)(uint)left * (uint)right;
178-
// Compute the 'middle' bits of the long multiplication
179-
valMid = BigMul(val2High, (uint)left);
180-
}
181-
else
182-
{
183-
if (val2High != 0)
184-
goto Overflow;
185-
// Compute the 'middle' bits of the long multiplication
186-
valMid = BigMul(val1High, (uint)right);
187-
}
188-
189-
// See if any bits after bit 32 are set
190-
if (High32Bits(valMid) != 0)
191-
goto Overflow;
192-
193-
ulong ret = BigMul((uint)left, (uint)right) + (valMid << 32);
194-
195-
// check for overflow
196-
if (High32Bits(ret) < (uint)valMid)
197-
goto Overflow;
198-
199-
Debug.Assert(ret == left * right, $"Multiply overflow got: {ret}, expected: {left * right}");
200-
return ret;
201-
202-
Overflow:
203-
ThrowHelper.ThrowOverflowException();
204-
return 0;
205-
}
206-
20721
[LibraryImport(RuntimeLibrary)]
20822
[SuppressGCTransition]
20923
private static partial ulong RhpULMod(ulong dividend, ulong divisor);

src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ public override MethodSignature Signature
686686
TypeDesc intPtrType = context.GetWellKnownType(WellKnownType.IntPtr);
687687
TypeDesc int32Type = context.GetWellKnownType(WellKnownType.Int32);
688688

689-
_signature = new MethodSignature(0, 0, intPtrType, new[] { int32Type });
689+
_signature = new MethodSignature(0, 0, intPtrType, [ int32Type ]);
690690
}
691691

692692
return _signature;

src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,7 @@ public override MethodSignature Signature
4848
TypeDesc int32Type = context.GetWellKnownType(WellKnownType.Int32);
4949
TypeDesc eeTypePtrType = context.SystemModule.GetKnownType("Internal.Runtime", "MethodTable").MakePointerType();
5050

51-
_signature = new MethodSignature(0, 0, int32Type, new[] {
52-
int32Type,
53-
eeTypePtrType.MakeByRefType()
54-
});
51+
_signature = new MethodSignature(0, 0, int32Type, [ int32Type, eeTypePtrType.MakeByRefType() ]);
5552
}
5653

5754
return _signature;

src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.Aot.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1170,7 +1170,7 @@ private ILLocalVariable InitializeMarshallerVariable()
11701170
var customMarshallerType = Context.SystemModule.GetKnownType("System.Runtime.InteropServices", "ICustomMarshaler");
11711171
var getInstanceMethod = marshallerType.GetMethod(
11721172
"GetInstance",
1173-
new MethodSignature(MethodSignatureFlags.Static, 0, customMarshallerType, new[] { Context.GetWellKnownType(WellKnownType.String) }));
1173+
new MethodSignature(MethodSignatureFlags.Static, 0, customMarshallerType, [ Context.GetWellKnownType(WellKnownType.String) ]));
11741174
if (ManagedType.IsValueType || ManagedType.IsPointer || ManagedType.IsFunctionPointer)
11751175
{
11761176
ThrowHelper.ThrowMarshalDirectiveException();
@@ -1210,7 +1210,7 @@ protected override void TransformManagedToNative(ILCodeStream codeStream)
12101210
ILEmitter emitter = _ilCodeStreams.Emitter;
12111211
var manageToNativeMethod = customMarshallerType.GetKnownMethod(
12121212
"MarshalManagedToNative",
1213-
new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.IntPtr), new[] { Context.GetWellKnownType(WellKnownType.Object) }));
1213+
new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.IntPtr), [ Context.GetWellKnownType(WellKnownType.Object) ]));
12141214

12151215
codeStream.EmitLdLoc(lMarshaller);
12161216
LoadManagedValue(codeStream);
@@ -1239,7 +1239,7 @@ protected override void TransformNativeToManaged(ILCodeStream codeStream)
12391239
ILEmitter emitter = _ilCodeStreams.Emitter;
12401240
var marshalNativeToManagedMethod = customMarshallerType.GetKnownMethod(
12411241
"MarshalNativeToManaged",
1242-
new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.Object), new[] { Context.GetWellKnownType(WellKnownType.IntPtr) }));
1242+
new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.Object), [ Context.GetWellKnownType(WellKnownType.IntPtr) ]));
12431243

12441244
codeStream.EmitLdLoc(lMarshaller);
12451245
LoadNativeValue(codeStream);
@@ -1257,7 +1257,7 @@ protected void EmitCleanUpManagedData(ILCodeStream codeStream)
12571257
// Call CleanUpManagedData on cleanup code stream.
12581258
var cleanupManagedDataMethod = customMarshallerType.GetKnownMethod(
12591259
"CleanUpManagedData",
1260-
new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.Void), new[] { Context.GetWellKnownType(WellKnownType.Object) }));
1260+
new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.Void), [ Context.GetWellKnownType(WellKnownType.Object) ]));
12611261

12621262
codeStream.EmitLdLoc(lMarshaller);
12631263
LoadManagedValue(codeStream);
@@ -1274,7 +1274,7 @@ protected void EmitCleanUpNativeData(ILCodeStream codeStream)
12741274
// Call CleanUpNativeData on cleanup code stream.
12751275
var cleanupNativeDataMethod = customMarshallerType.GetKnownMethod(
12761276
"CleanUpNativeData",
1277-
new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.Void), new[] { Context.GetWellKnownType(WellKnownType.IntPtr) }));
1277+
new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.Void), [ Context.GetWellKnownType(WellKnownType.IntPtr) ]));
12781278

12791279
codeStream.EmitLdLoc(lMarshaller);
12801280
LoadNativeValue(codeStream);

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs

+14-6
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,16 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id,
188188
break;
189189

190190
case ReadyToRunHelper.Dbl2IntOvf:
191-
methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2IntOvf");
191+
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("ConvertToInt32Checked", null);
192192
break;
193193
case ReadyToRunHelper.Dbl2UIntOvf:
194-
methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2UIntOvf");
194+
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("ConvertToUInt32Checked", null);
195195
break;
196196
case ReadyToRunHelper.Dbl2LngOvf:
197-
methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2LngOvf");
197+
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("ConvertToInt64Checked", null);
198198
break;
199199
case ReadyToRunHelper.Dbl2ULngOvf:
200-
methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2ULngOvf");
200+
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("ConvertToUInt64Checked", null);
201201
break;
202202

203203
case ReadyToRunHelper.DblRem:
@@ -211,10 +211,18 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id,
211211
mangledName = "RhpLMul";
212212
break;
213213
case ReadyToRunHelper.LMulOfv:
214-
methodDesc = context.GetHelperEntryPoint("MathHelpers", "LMulOvf");
214+
{
215+
TypeDesc t = context.GetWellKnownType(WellKnownType.Int64);
216+
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("MultiplyChecked",
217+
new MethodSignature(MethodSignatureFlags.Static, 0, t, [t, t]));
218+
}
215219
break;
216220
case ReadyToRunHelper.ULMulOvf:
217-
methodDesc = context.GetHelperEntryPoint("MathHelpers", "ULMulOvf");
221+
{
222+
TypeDesc t = context.GetWellKnownType(WellKnownType.UInt64);
223+
methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("MultiplyChecked",
224+
new MethodSignature(MethodSignatureFlags.Static, 0, t, [t, t]));
225+
}
218226
break;
219227

220228
case ReadyToRunHelper.Mod:

src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,19 @@ private void ImportBinaryOperation(ILOpcode opcode)
12781278
}
12791279
}
12801280

1281+
private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned)
1282+
{
1283+
if (checkOverflow)
1284+
{
1285+
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Dbl2IntOvf), "_dbl2intovf");
1286+
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Dbl2UIntOvf), "_dbl2uintovf");
1287+
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Dbl2LngOvf), "_dbl2lngovf");
1288+
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Dbl2ULngOvf), "_dbl2ulngovf");
1289+
1290+
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Overflow), "_ovf");
1291+
}
1292+
}
1293+
12811294
private void ImportFallthrough(BasicBlock next)
12821295
{
12831296
MarkBasicBlock(next);
@@ -1403,7 +1416,6 @@ private static void ImportStoreIndirect(int token) { }
14031416
private static void ImportStoreIndirect(TypeDesc type) { }
14041417
private static void ImportShiftOperation(ILOpcode opcode) { }
14051418
private static void ImportCompareOperation(ILOpcode opcode) { }
1406-
private static void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) { }
14071419
private static void ImportUnaryOperation(ILOpcode opCode) { }
14081420
private static void ImportCpOpj(int token) { }
14091421
private static void ImportCkFinite() { }

src/coreclr/vm/corelib.h

+8
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,14 @@ DEFINE_CLASS(UINT128, System, UInt128)
261261

262262
DEFINE_CLASS(MATH, System, Math)
263263
DEFINE_METHOD(MATH, ROUND, Round, SM_Dbl_RetDbl)
264+
#ifndef TARGET_64BIT
265+
DEFINE_METHOD(MATH, MULTIPLY_CHECKED_INT64, MultiplyChecked, SM_Long_Long_RetLong)
266+
DEFINE_METHOD(MATH, MULTIPLY_CHECKED_UINT64, MultiplyChecked, SM_ULong_ULong_RetULong)
267+
#endif
268+
DEFINE_METHOD(MATH, CONVERT_TO_INT32_CHECKED, ConvertToInt32Checked, NoSig)
269+
DEFINE_METHOD(MATH, CONVERT_TO_UINT32_CHECKED, ConvertToUInt32Checked, NoSig)
270+
DEFINE_METHOD(MATH, CONVERT_TO_INT64_CHECKED, ConvertToInt64Checked, NoSig)
271+
DEFINE_METHOD(MATH, CONVERT_TO_UINT64_CHECKED, ConvertToUInt64Checked, NoSig)
264272

265273
DEFINE_CLASS(MATHF, System, MathF)
266274
DEFINE_METHOD(MATHF, ROUND, Round, SM_Flt_RetFlt)

0 commit comments

Comments
 (0)