Skip to content

Commit

Permalink
Support static abstract interface members
Browse files Browse the repository at this point in the history
This replaces specific GenericMath support introduced in 7bf5578 ( #898 )  with generic support for static abstract interface members.

Support is implemented using the constrained flag on the call instruction the same way it used to be done on virtual method calls. The call instruction with constrained flag set contains concrete type, that implements given interface, which makes it possible to find concrete method implementation to be called.

fixes #929
  • Loading branch information
lostmsu committed Feb 14, 2023
1 parent 10b2fbe commit c8ef126
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 63 deletions.
2 changes: 1 addition & 1 deletion Src/ILGPU.Tests/Configurations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ DisassemblerTests
EnumValues
FixedBuffers
GetKernelTests
GenericMath
Indices
KernelEntryPoints
MemoryBufferOperations
Expand All @@ -14,6 +13,7 @@ ProfilingMarkers
SharedMemory
SizeOfValues
SpecializedKernels
StaticAbstractInterfaceMembers
StructureValues
ValueTuples
InteropTests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Copyright (c) 2022-2023 ILGPU Project
// www.ilgpu.net
//
// File: GenericMath.cs
// File: StaticAbstractInterfaceMembers.cs
//
// This file is part of ILGPU and is distributed under the University of Illinois Open
// Source License. See LICENSE.txt for details.
Expand All @@ -17,9 +17,11 @@

namespace ILGPU.Tests
{
public abstract class GenericMath : TestBase
public abstract class StaticAbstractInterfaceMembers : TestBase
{
protected GenericMath(ITestOutputHelper output, TestContext testContext)
protected StaticAbstractInterfaceMembers(
ITestOutputHelper output,
TestContext testContext)
: base(output, testContext)
{ }

Expand All @@ -34,6 +36,8 @@ public static T GeZeroIfBigger<T>(T value, T max) where T : INumber<T>
return value;
}

#region Generic math

internal static void GenericMathKernel<T>(
Index1D index,
ArrayView1D<T, Stride1D.Dense> input,
Expand Down Expand Up @@ -94,6 +98,56 @@ public void GenericMathDoubleTest()
TestGenericMathKernel(input, expected, MaxValue);
}

#endregion

internal static void IncrementingKernel<T>(
Index1D index,
ArrayView1D<int, Stride1D.Dense> input,
ArrayView1D<int, Stride1D.Dense> output)
where T : IStaticAbstract
{
output[index] = T.Inc(input[index]);
}

private void TestIncrementingKernel<T>(int[] inputValues, int[] expected)
where T : IStaticAbstract
{
using var input = Accelerator.Allocate1D<int>(inputValues);
using var output = Accelerator.Allocate1D<int>(Length);

using var start = Accelerator.DefaultStream.AddProfilingMarker();
Accelerator.LaunchAutoGrouped<
Index1D,
ArrayView1D<int, Stride1D.Dense>,
ArrayView1D<int, Stride1D.Dense>>(
IncrementingKernel<T>,
Accelerator.DefaultStream,
(int)input.Length,
input.View,
output.View);

Verify(output.View, expected);
}

public interface IStaticAbstract
{
static abstract int Inc(int x);
}

public class Incrementer: IStaticAbstract
{
public static int Inc(int x) => x + 1;
}

[Fact]
public void StaticInterfaceTest()
{
int[] input = Enumerable.Range(0, Length).ToArray();

int[] expected = input.Select(Incrementer.Inc).ToArray();

TestIncrementingKernel<Incrementer>(input, expected);
}
#endif
}
}
16 changes: 16 additions & 0 deletions Src/ILGPU/Frontend/CodeGenerator/Calls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ private void CreateCall(
}
}

/// <summary>
/// Realizes a call instruction.
/// </summary>
/// <param name="instruction">The instruction to realize.</param>
private void MakeCall(ILInstruction instruction)
{
var method = instruction.GetArgumentAs<MethodBase>();
if (instruction.HasFlags(ILInstructionFlags.Constrained)
&& method is MethodInfo methodInfo)
{
var constrainedType = instruction.FlagsContext.Argument as Type;
method = ResolveVirtualCallTarget(methodInfo, constrainedType);
}
MakeCall(method);
}

/// <summary>
/// Realizes a call instruction.
/// </summary>
Expand Down
3 changes: 1 addition & 2 deletions Src/ILGPU/Frontend/CodeGenerator/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ private void SetupVariables()
}

// Initialize locals
var methodBody = Disassembler.ExtractMethodBody(Method);
var localVariables = methodBody.LocalVariables;
var localVariables = Method.GetMethodBody().LocalVariables;
for (int i = 0, e = localVariables.Count; i < e; ++i)
{
var variable = localVariables[i];
Expand Down
2 changes: 1 addition & 1 deletion Src/ILGPU/Frontend/CodeGenerator/Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private bool TryGenerateCode(ILInstruction instruction)
MakeReturn();
return true;
case ILInstructionType.Call:
MakeCall(instruction.GetArgumentAs<MethodBase>());
MakeCall(instruction);
return true;
case ILInstructionType.Callvirt:
MakeVirtualCall(instruction);
Expand Down
58 changes: 2 additions & 56 deletions Src/ILGPU/Frontend/Disassembler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
using ILGPU.Util;
using System;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
Expand All @@ -28,60 +27,7 @@ namespace ILGPU.Frontend
/// <remarks>Members of this class are not thread safe.</remarks>
public sealed partial class Disassembler : ILocation
{
#region Static

/// <summary>
/// Indicates whether the .NET runtime has support for Static Abstract methods
/// in Interfaces.
/// </summary>
[SuppressMessage("Performance", "CA1802:Use literals where appropriate")]
private static readonly bool UsingStaticAbstractMethodsInInterfaces =
#if NET7_0_OR_GREATER
RuntimeFeature.IsSupported(RuntimeFeature.VirtualStaticsInInterfaces);
#else
false;
#endif

/// <summary>
/// Extracts the method body for the given method.
/// </summary>
/// <param name="method">The method.</param>
/// <returns>The method body.</returns>
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
#region Constants

/// <summary>
/// Represents the native pointer type that is used during the
Expand Down Expand Up @@ -152,7 +98,7 @@ public Disassembler(
? MethodBase.GetGenericArguments()
: Array.Empty<Type>();
TypeGenericArguments = MethodBase.DeclaringType.GetGenericArguments();
MethodBody = ExtractMethodBody(MethodBase);
MethodBody = MethodBase.GetMethodBody();
if (MethodBody == null)
{
throw new NotSupportedException(string.Format(
Expand Down

0 comments on commit c8ef126

Please sign in to comment.