* add support for ckfinite

* Using existing OverflowException helper.
This commit is contained in:
yowl 2020-04-21 05:21:33 -05:00 коммит произвёл GitHub
Родитель ec03519fc8
Коммит 4f165631bf
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 182 добавлений и 23 удалений

Просмотреть файл

@ -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;