ILGPU icon indicating copy to clipboard operation
ILGPU copied to clipboard

[BUG]: Internal Compiler Error With Vector3.Transform After .NET 8 Update

Open rhoadscm opened this issue 11 months ago • 4 comments

Describe the bug

I have some code that applies a basic point cloud transformation using the Transform function of System.Numerics.Vector3 inside a kernel. This has been working for a long time until a recent update from .NET 6 to .NET 8. It now throws a runtime exception:

An internal compiler error has been detected
- in method System.Runtime.Intrinsics.Vector128`1[System.Single] AsVector128(System.Numerics.Vector4) declared in type System.Runtime.Intrinsics.Vector128
- in method System.Numerics.Vector3 Transform(System.Numerics.Vector3, System.Numerics.Matrix4x4) declared in type System.Numerics.Vector3
- in method Void Transform(ILGPU.Index1D, ILGPU.ArrayView`1[System.Numerics.Vector3], System.Numerics.Matrix4x4) declared in type <insert my assembly here>
- in method Void Transform(ILGPU.Index1D, ILGPU.ArrayView`1[System.Numerics.Vector3], System.Numerics.Matrix4x4) declared in type <insert my assembly here>"

Environment

  • ILGPU version: Tested 1.0, 1.3, 1.4, and 1.5.2
  • .NET version: .NET 8
  • Operating system: Windows 10 and 11
  • Hardware (if GPU-related): a few different workstation CUDA devices, both laptop (NVidia T1200) and desktop (NVidia A2000)

Steps to reproduce

Here's a test method:

        public void TestTransformKernel()
        {
            // Create the required ILGPU context
            using Context context = Context.CreateDefault();
            Device d = context.Devices[context.Devices.IndexOf(context.Devices.FirstOrDefault(d => d.AcceleratorType == AcceleratorType.Cuda))];
            using var accelerator = d.CreateAccelerator(context);
            var transformKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<Vector3>, Matrix4x4>(Transform);    // <- throws internal compiler error
        }

And here is the Transform kernel method:

        public static void Transform(Index1D index, ArrayView<Vector3> cloud, Matrix4x4 transform)
        {
            if (cloud[index] == Vector3.Zero)
                return;
            cloud[index] = Vector3.Transform(cloud[index], transform);
        }

Expected behavior

Expected that the kernel would load with no runtime exception.

Additional context

To mitigate this we have taken the source code from System.Numerics.Vector3 and replaced the call to Vector3.Transform with the matrix math as follows:

        public static void Transform(Index1D index, ArrayView<Vector3> cloud, Matrix4x4 transform)
        {
            if (cloud[index] == Vector3.Zero)
                return;
            cloud[index] = new Vector3(
                cloud[index].X * transform.M11 + cloud[index].Y * transform.M21 + cloud[index].Z * transform.M31 + transform.M41,
                cloud[index].X * transform.M12 + cloud[index].Y * transform.M22 + cloud[index].Z * transform.M32 + transform.M42,
                cloud[index].X * transform.M13 + cloud[index].Y * transform.M23 + cloud[index].Z * transform.M33 + transform.M43);
        }

The above kernel seems to compile just fine. It would appear that there is some sort of optimization going on in the background that is new in .NET 8 (or 7, I didn't test 7) that causes ILGPU compilation errors. Here is the relevant source from Microsoft and it does not reference any of the new intrinsic types directly, so I guess it must be something in the background.

rhoadscm avatar Apr 21 '25 22:04 rhoadscm

hi @rhoadscm.

It looks like the Transform method in net8.0 now makes use of an intrinsic.

In net8.0, the implementation makes use of AsVector128(), which is an intrinsic (source).

For reference, net7.0 had the same implementation as net6.0.

MoFtZ avatar Apr 22 '25 01:04 MoFtZ

I see that now, I was looking at the older .NET 6 source apparently. My mistake. Are the intrinsics expected to be supported in ILGPU?

rhoadscm avatar Apr 22 '25 01:04 rhoadscm

ILGPU will currently read the MSIL code for most methods, and transform that into GPU code.

However, ILGPU will ignore methods in the System.Runtime and System.Reflection namespaces, because these typically contain calls to native code that cannot be transformed.

Unfortunately, AsVector128() is part of the System.Runtime.Intrinsics namespace, so ILGPU assumes that it is not safe.

Having said that, there is currently an exception for System.Runtime.CompilerServices, so maybe additional namespaces need to be added to this list? This probably needs some internal discussion on how best to proceed.

For now, your workaround is the correct step.

MoFtZ avatar Apr 22 '25 02:04 MoFtZ

That all makes sense, thank you. We'll watch for this in future kernels since we rely on a lot of built in .NET math.

rhoadscm avatar Apr 22 '25 02:04 rhoadscm