Wasm: implement ckfinite (#8097)
* add support for ckfinite * Using existing OverflowException helper.
This commit is contained in:
Родитель
ec03519fc8
Коммит
4f165631bf
|
@ -3999,6 +3999,17 @@ namespace Internal.IL
|
|||
|
||||
private void ImportCkFinite()
|
||||
{
|
||||
StackEntry value = _stack.Pop();
|
||||
if (value.Type == GetWellKnownType(WellKnownType.Single))
|
||||
{
|
||||
ThrowCkFinite(value.ValueForStackKind(value.Kind, _builder, false), 32, ref CkFinite32Function);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowCkFinite(value.ValueForStackKind(value.Kind, _builder, false), 64, ref CkFinite64Function);
|
||||
}
|
||||
|
||||
_stack.Push(value);
|
||||
}
|
||||
|
||||
private void ImportMkRefAny(int token)
|
||||
|
@ -4201,25 +4212,9 @@ namespace Internal.IL
|
|||
builder.BuildCondBr(builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, NullRefFunction.GetParam(1), LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)), "nullCheck"),
|
||||
throwBlock, retBlock);
|
||||
builder.PositionAtEnd(throwBlock);
|
||||
MetadataType nullRefType = _compilation.NodeFactory.TypeSystemContext.SystemModule.GetType("System", "NullReferenceException");
|
||||
|
||||
var arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(nullRefType, true), GetEETypePtrTypeDesc()) };
|
||||
ThrowException(builder, "ThrowHelpers", "ThrowNullReferenceException", NullRefFunction);
|
||||
|
||||
MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System.Runtime", RuntimeExport);
|
||||
MethodDesc helperMethod = helperType.GetKnownMethod("RhNewObject", null);
|
||||
var resultAddress = builder.BuildIntCast(builder.BuildAlloca(LLVMTypeRef.Int32, "resultAddress"), LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "castResultAddress");
|
||||
HandleDirectCall(helperMethod, helperMethod.Signature, arguments, null, default(LLVMValueRef), 0, NullRefFunction.GetParam(0), builder, true, resultAddress, helperMethod);
|
||||
|
||||
var exceptionEntry = new LoadExpressionEntry(GetStackValueKind(nullRefType), "RhNewObject_return", resultAddress, nullRefType);
|
||||
|
||||
var ctorDef = nullRefType.GetDefaultConstructor();
|
||||
|
||||
HandleDirectCall(ctorDef, ctorDef.Signature, new StackEntry[] { exceptionEntry }, null, default(LLVMValueRef), 0, NullRefFunction.GetParam(0), builder, false, default(LLVMValueRef), ctorDef);
|
||||
|
||||
EnsureRhpThrowEx();
|
||||
LLVMValueRef[] args = new LLVMValueRef[] { exceptionEntry.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), builder) };
|
||||
builder.BuildCall(RhpThrowEx, args, "");
|
||||
builder.BuildUnreachable();
|
||||
builder.PositionAtEnd(retBlock);
|
||||
builder.BuildRetVoid();
|
||||
}
|
||||
|
@ -4228,12 +4223,56 @@ namespace Internal.IL
|
|||
CallOrInvoke(false, _builder, GetCurrentTryRegion(), NullRefFunction, new List<LLVMValueRef> { GetShadowStack(), entry }, ref nextInstrBlock);
|
||||
}
|
||||
|
||||
void EnsureRhpThrowEx()
|
||||
private void ThrowCkFinite(LLVMValueRef value, int size, ref LLVMValueRef llvmCheckFunction)
|
||||
{
|
||||
if (RhpThrowEx.Handle.Equals(IntPtr.Zero))
|
||||
if (llvmCheckFunction.Handle == IntPtr.Zero)
|
||||
{
|
||||
RhpThrowEx = Module.AddFunction("RhpThrowEx", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }, false));
|
||||
llvmCheckFunction = Module.AddFunction("corert.throwckfinite" + size, LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), size == 32 ? LLVMTypeRef.Float : LLVMTypeRef.Double }, false));
|
||||
LLVMValueRef exponentMask;
|
||||
LLVMTypeRef intTypeRef;
|
||||
var builder = Context.CreateBuilder();
|
||||
var block = llvmCheckFunction.AppendBasicBlock("Block");
|
||||
builder.PositionAtEnd(block);
|
||||
|
||||
if (size == 32)
|
||||
{
|
||||
intTypeRef = LLVMTypeRef.Int32;
|
||||
exponentMask = LLVMValueRef.CreateConstInt(intTypeRef, 0x7F800000, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
intTypeRef = LLVMTypeRef.Int64;
|
||||
exponentMask = LLVMValueRef.CreateConstInt(intTypeRef, 0x7FF0000000000000, false);
|
||||
}
|
||||
|
||||
var valRef = builder.BuildBitCast(llvmCheckFunction.GetParam(1), intTypeRef);
|
||||
LLVMValueRef exponentBits = builder.BuildAnd(valRef, exponentMask, "and");
|
||||
LLVMValueRef isFinite = builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, exponentBits, exponentMask, "isfinite");
|
||||
|
||||
LLVMBasicBlockRef throwBlock = llvmCheckFunction.AppendBasicBlock("Throw");
|
||||
LLVMBasicBlockRef afterIf = llvmCheckFunction.AppendBasicBlock("AfterIf");
|
||||
builder.BuildCondBr(isFinite, throwBlock, afterIf);
|
||||
|
||||
builder.PositionAtEnd(throwBlock);
|
||||
|
||||
ThrowException(builder, "ThrowHelpers", "ThrowOverflowException", llvmCheckFunction);
|
||||
|
||||
afterIf.MoveAfter(llvmCheckFunction.LastBasicBlock);
|
||||
builder.PositionAtEnd(afterIf);
|
||||
builder.BuildRetVoid();
|
||||
}
|
||||
|
||||
LLVMBasicBlockRef nextInstrBlock = default;
|
||||
CallOrInvoke(false, _builder, GetCurrentTryRegion(), llvmCheckFunction, new List<LLVMValueRef> { GetShadowStack(), value }, ref nextInstrBlock);
|
||||
}
|
||||
|
||||
private void ThrowException(LLVMBuilderRef builder, string helperClass, string helperMethodName, LLVMValueRef throwingFunction)
|
||||
{
|
||||
MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime.CompilerHelpers", helperClass);
|
||||
MethodDesc helperMethod = helperType.GetKnownMethod(helperMethodName, null);
|
||||
LLVMValueRef fn = LLVMFunctionForMethod(helperMethod, helperMethod, null, false, null, null, out bool hasHiddenParam, out LLVMValueRef dictPtrPtrStore, out LLVMValueRef fatFunctionPtr);
|
||||
builder.BuildCall(fn, new LLVMValueRef[] {throwingFunction.GetParam(0) }, string.Empty);
|
||||
builder.BuildUnreachable();
|
||||
}
|
||||
|
||||
private LLVMValueRef GetInstanceFieldAddress(StackEntry objectEntry, FieldDesc field)
|
||||
|
|
|
@ -117,6 +117,8 @@ namespace Internal.IL
|
|||
static LLVMValueRef LlvmCatchFunclet = default(LLVMValueRef);
|
||||
static LLVMValueRef LlvmFinallyFunclet = default(LLVMValueRef);
|
||||
static LLVMValueRef NullRefFunction = default(LLVMValueRef);
|
||||
static LLVMValueRef CkFinite32Function = default(LLVMValueRef);
|
||||
static LLVMValueRef CkFinite64Function = default(LLVMValueRef);
|
||||
public static LLVMValueRef GxxPersonality = default(LLVMValueRef);
|
||||
public static LLVMTypeRef GxxPersonalityType = default(LLVMTypeRef);
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
.assembly extern mscorlib
|
||||
{
|
||||
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
|
||||
.ver 4:0:0:0
|
||||
}
|
||||
|
||||
.assembly CkFinite { }
|
||||
|
||||
.class public abstract sealed CkFinite.CkFiniteTest {
|
||||
|
||||
.method public static bool CkFinite32(float32) {
|
||||
.maxstack 5
|
||||
try_start:
|
||||
ldarg 0
|
||||
ckfinite
|
||||
pop //remove the value from the stack
|
||||
leave try_end
|
||||
try_end:
|
||||
ldc.i4 0x00000001
|
||||
ret
|
||||
handler_start:
|
||||
pop //remove the exception ref from the stack
|
||||
leave done
|
||||
handler_end:
|
||||
done:
|
||||
ldc.i4 0x00000000
|
||||
ret
|
||||
.try try_start to try_end catch [mscorlib]System.OverflowException handler handler_start to handler_end
|
||||
}
|
||||
|
||||
.method public static bool CkFinite64(float64) {
|
||||
.maxstack 5
|
||||
try_start:
|
||||
ldarg 0
|
||||
ckfinite
|
||||
pop //remove the value from the stack
|
||||
leave try_end
|
||||
try_end:
|
||||
ldc.i4 0x00000001
|
||||
ret
|
||||
handler_start:
|
||||
pop //remove the exception ref from the stack
|
||||
leave done
|
||||
handler_end:
|
||||
done:
|
||||
ldc.i4 0x00000000
|
||||
ret
|
||||
.try try_start to try_end catch [mscorlib]System.OverflowException handler handler_start to handler_end
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<DebugType>portable</DebugType>
|
||||
<OutputPath>$(MSBuildProjectDirectory)\bin\$(Configuration)\$(Platform)\</OutputPath>
|
||||
<IntermediateOutputPath>$(MSBuildProjectDirectory)\obj\$(Configuration)\$(Platform)\</IntermediateOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="CkFinite.il" />
|
||||
<PackageReference Include="Microsoft.NETCore.App">
|
||||
<Version>$(MicrosoftNETCoreAppPackageVersion)</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
|
||||
</Project>
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
<ProjectReference Include="CpObj.ilproj" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
<IlcArg Include="-r:$(IntermediateOutputPath)\CpObj.dll" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
|
||||
<ProjectReference Include="CkFinite.ilproj" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
<IlcArg Include="-r:$(IntermediateOutputPath)\CkFinite.dll" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
<ProjectReference Include="ILHelpers.ilproj" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
<IlcArg Include="-r:$(IntermediateOutputPath)\ILHelpers.dll" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.Reflection;
|
|||
|
||||
#if TARGET_WINDOWS
|
||||
using CpObj;
|
||||
using CkFinite;
|
||||
#endif
|
||||
internal static class Program
|
||||
{
|
||||
|
@ -344,6 +345,10 @@ internal static class Program
|
|||
|
||||
TestThrowIfNull();
|
||||
|
||||
#if TARGET_WINDOWS
|
||||
TestCkFinite();
|
||||
#endif
|
||||
|
||||
// This test should remain last to get other results before stopping the debugger
|
||||
PrintLine("Debugger.Break() test: Ok if debugger is open and breaks.");
|
||||
System.Diagnostics.Debugger.Break();
|
||||
|
@ -1671,6 +1676,43 @@ internal static class Program
|
|||
EndTest(success);
|
||||
}
|
||||
|
||||
#if TARGET_WINDOWS
|
||||
private static void TestCkFinite()
|
||||
{
|
||||
// includes tests from https://github.com/dotnet/coreclr/blob/9b0a9fd623/tests/src/JIT/IL_Conformance/Old/Base/ckfinite.il4
|
||||
StartTest("CkFiniteTests");
|
||||
if (!CkFiniteTest.CkFinite32(0) || !CkFiniteTest.CkFinite32(1) ||
|
||||
!CkFiniteTest.CkFinite32(100) || !CkFiniteTest.CkFinite32(-100) ||
|
||||
!CkFinite32(0x7F7FFFC0) || CkFinite32(0xFF800000) || // use converter function to get the float equivalent of this bits
|
||||
CkFinite32(0x7FC00000) && !CkFinite32(0xFF7FFFFF) ||
|
||||
CkFinite32(0x7F800000))
|
||||
{
|
||||
FailTest("one or more 32 bit tests failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CkFiniteTest.CkFinite64(0) || !CkFiniteTest.CkFinite64(1) ||
|
||||
!CkFiniteTest.CkFinite64(100) || !CkFiniteTest.CkFinite64(-100) ||
|
||||
CkFinite64(0x7FF0000000000000) || CkFinite64(0xFFF0000000000000) ||
|
||||
CkFinite64(0x7FF8000000000000) || !CkFinite64(0xFFEFFFFFFFFFFFFF))
|
||||
{
|
||||
FailTest("one or more 64 bit tests failed.");
|
||||
return;
|
||||
}
|
||||
PassTest();
|
||||
}
|
||||
|
||||
private static unsafe bool CkFinite32(uint value)
|
||||
{
|
||||
return CkFiniteTest.CkFinite32 (* (float*)(&value));
|
||||
}
|
||||
|
||||
private static unsafe bool CkFinite64(ulong value)
|
||||
{
|
||||
return CkFiniteTest.CkFinite64(*(double*)(&value));
|
||||
}
|
||||
#endif
|
||||
|
||||
static ushort ReadUInt16()
|
||||
{
|
||||
// something with MSB set
|
||||
|
@ -1692,6 +1734,7 @@ public class ClassForNre
|
|||
public int F;
|
||||
}
|
||||
|
||||
|
||||
public class ClassWithFloat
|
||||
{
|
||||
public static float F;
|
||||
|
|
Загрузка…
Ссылка в новой задаче