From 7bf55786f96a8912dcfd21b9e86e03c5e270fcd5 Mon Sep 17 00:00:00 2001 From: MoFtZ Date: Mon, 5 Dec 2022 14:30:33 +1100 Subject: [PATCH] Added support for Generic Math using net70 SDK. --- Src/ILGPU.Tests/Configurations.txt | 1 + Src/ILGPU.Tests/GenericMath.cs | 99 +++++++++++++++++++ .../Frontend/CodeGenerator/CodeGenerator.cs | 3 +- Src/ILGPU/Frontend/Disassembler.cs | 60 ++++++++++- 4 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 Src/ILGPU.Tests/GenericMath.cs diff --git a/Src/ILGPU.Tests/Configurations.txt b/Src/ILGPU.Tests/Configurations.txt index 255e310f4..547d176e1 100644 --- a/Src/ILGPU.Tests/Configurations.txt +++ b/Src/ILGPU.Tests/Configurations.txt @@ -5,6 +5,7 @@ DisassemblerTests EnumValues FixedBuffers GetKernelTests +GenericMath Indices KernelEntryPoints MemoryBufferOperations diff --git a/Src/ILGPU.Tests/GenericMath.cs b/Src/ILGPU.Tests/GenericMath.cs new file mode 100644 index 000000000..62f3a7486 --- /dev/null +++ b/Src/ILGPU.Tests/GenericMath.cs @@ -0,0 +1,99 @@ +// --------------------------------------------------------------------------------------- +// ILGPU +// Copyright (c) 2022 ILGPU Project +// www.ilgpu.net +// +// File: GenericMath.cs +// +// This file is part of ILGPU and is distributed under the University of Illinois Open +// Source License. See LICENSE.txt for details. +// --------------------------------------------------------------------------------------- + +using ILGPU.Runtime; +using System.Linq; +using System.Numerics; +using Xunit; +using Xunit.Abstractions; + +namespace ILGPU.Tests +{ + public abstract class GenericMath : TestBase + { + protected GenericMath(ITestOutputHelper output, TestContext testContext) + : base(output, testContext) + { } + +#if NET7_0_OR_GREATER + + private const int Length = 1024; + + public static T GeZeroIfBigger(T value, T max) where T : INumber + { + if (value > max) + return T.Zero; + return value; + } + + internal static void GenericMathKernel( + Index1D index, + ArrayView1D input, + ArrayView1D output, + T maxValue) + where T : unmanaged, INumber + { + output[index] = GeZeroIfBigger(input[index], maxValue); + } + + private void TestGenericMathKernel(T[] inputValues, T[] expected, T maxValue) + where T : unmanaged, INumber + { + using var input = Accelerator.Allocate1D(inputValues); + using var output = Accelerator.Allocate1D(Length); + + using var start = Accelerator.DefaultStream.AddProfilingMarker(); + Accelerator.LaunchAutoGrouped< + Index1D, + ArrayView1D, + ArrayView1D, + T>( + GenericMathKernel, + Accelerator.DefaultStream, + (int)input.Length, + input.View, + output.View, + maxValue); + + Verify(output.View, expected); + } + + [Fact] + public void GenericMathIntTest() + { + const int MaxValue = 50; + var input = Enumerable.Range(0, Length).ToArray(); + + var expected = input + .Select(x => GeZeroIfBigger(x, MaxValue)) + .ToArray(); + + TestGenericMathKernel(input, expected, MaxValue); + } + + [Fact] + public void GenericMathDoubleTest() + { + const double MaxValue = 75.0; + var input = Enumerable.Range(0, Length) + .Select(x => (double)x) + .ToArray(); + + var expected = input + .Select(x => GeZeroIfBigger(x, MaxValue)) + .ToArray(); + + TestGenericMathKernel(input, expected, MaxValue); + } + +#endif + } +} diff --git a/Src/ILGPU/Frontend/CodeGenerator/CodeGenerator.cs b/Src/ILGPU/Frontend/CodeGenerator/CodeGenerator.cs index ac8b9151e..943458860 100644 --- a/Src/ILGPU/Frontend/CodeGenerator/CodeGenerator.cs +++ b/Src/ILGPU/Frontend/CodeGenerator/CodeGenerator.cs @@ -142,7 +142,8 @@ private void SetupVariables() } // Initialize locals - var localVariables = Method.GetMethodBody().LocalVariables; + var methodBody = Disassembler.ExtractMethodBody(Method); + var localVariables = methodBody.LocalVariables; for (int i = 0, e = localVariables.Count; i < e; ++i) { var variable = localVariables[i]; diff --git a/Src/ILGPU/Frontend/Disassembler.cs b/Src/ILGPU/Frontend/Disassembler.cs index cf4d4d67d..b9bf47786 100644 --- a/Src/ILGPU/Frontend/Disassembler.cs +++ b/Src/ILGPU/Frontend/Disassembler.cs @@ -1,6 +1,6 @@ // --------------------------------------------------------------------------------------- // ILGPU -// Copyright (c) 2018-2021 ILGPU Project +// Copyright (c) 2018-2022 ILGPU Project // www.ilgpu.net // // File: Disassembler.cs @@ -15,6 +15,7 @@ using ILGPU.Util; using System; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; @@ -27,7 +28,60 @@ namespace ILGPU.Frontend /// Members of this class are not thread safe. public sealed partial class Disassembler : ILocation { - #region Constants + #region Static + + /// + /// Indicates whether the .NET runtime has support for Static Abstract methods + /// in Interfaces. + /// + [SuppressMessage("Performance", "CA1802:Use literals where appropriate")] + private static readonly bool UsingStaticAbstractMethodsInInterfaces = +#if NET7_0_OR_GREATER + RuntimeFeature.IsSupported(RuntimeFeature.VirtualStaticsInInterfaces); +#else + false; +#endif + + /// + /// Extracts the method body for the given method. + /// + /// The method. + /// The method body. + public static MethodBody ExtractMethodBody(MethodBase method) + { + var methodBody = method.GetMethodBody(); + if (methodBody == null && + UsingStaticAbstractMethodsInInterfaces && + method.DeclaringType.IsInterface && + method.IsStatic && + method.IsAbstract) + { + // Support for Static Abstract methods in Interfaces was introduced in + // C# 11, in particular, adding support for Generic Math. The interface + // itself does not contain the method implementation itself. Instead, we + // need to find the concrete type that implements the interface, and find + // the matching method. + var concreteType = method.DeclaringType.GetGenericArguments()[0]; + var interfaceMap = concreteType.GetInterfaceMap(method.DeclaringType); + + for (int i = 0; i < interfaceMap.InterfaceMethods.Length; i++) + { + if (interfaceMap.InterfaceMethods[i].Name.Equals( + method.Name, + StringComparison.OrdinalIgnoreCase)) + { + methodBody = interfaceMap.TargetMethods[i].GetMethodBody(); + break; + } + } + } + + return methodBody; + } + +#endregion + +#region Constants /// /// Represents the native pointer type that is used during the @@ -98,7 +152,7 @@ public Disassembler( ? MethodBase.GetGenericArguments() : Array.Empty(); TypeGenericArguments = MethodBase.DeclaringType.GetGenericArguments(); - MethodBody = MethodBase.GetMethodBody(); + MethodBody = ExtractMethodBody(MethodBase); if (MethodBody == null) { throw new NotSupportedException(string.Format(