using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Scriban.Syntax; namespace Kalk.Core.Modules.HardwareIntrinsics { public abstract class IntrinsicsModuleBase : KalkModuleWithFunctions { public const string CategoryIntrinsics = "Vector Hardware Intrinsics"; protected IntrinsicsModuleBase(string name) : base(name) { } protected void ProcessAction(object arg1, Action func) where TArg1Base : unmanaged where TArg1: unmanaged { var nativeArg1 = ToArg(0, arg1); func(nativeArg1); } protected void ProcessAction(object arg1, object arg2, Action func) where TArg1Base : unmanaged where TArg1: unmanaged where TArg2Base : unmanaged where TArg2: unmanaged { var nativeArg1 = ToArg(0, arg1); var nativeArg2 = ToArg(1, arg2); func(nativeArg1, nativeArg2); } protected void ProcessAction(object arg1, int arg1Align, object arg2, Action func) where TArg1Base : unmanaged where TArg1: unmanaged where TArg2Base : unmanaged where TArg2: unmanaged { var nativeArg1 = ToArg(0, arg1, arg1Align); var nativeArg2 = ToArg(1, arg2); func(nativeArg1, nativeArg2); } protected void ProcessAction(object arg1, object arg2, object arg3, Action func) where TArg1Base : unmanaged where TArg1: unmanaged where TArg2Base : unmanaged where TArg2: unmanaged where TArg3Base : unmanaged where TArg3: unmanaged { var nativeArg1 = ToArg(0, arg1); var nativeArg2 = ToArg(1, arg2); var nativeArg3 = ToArg(2, arg3); func(nativeArg1, nativeArg2, nativeArg3); } protected object ProcessFunc(object arg1, Func func) where TArg1Base : unmanaged where TArg1: unmanaged where TResultBase : unmanaged where TResult: unmanaged { var nativeArg1 = ToArg(0, arg1); var nativeResult = func(nativeArg1); return ToResult(nativeResult); } protected object ProcessFunc(object arg1, int arg1Align, Func func) where TArg1Base : unmanaged where TArg1: unmanaged where TResultBase : unmanaged where TResult: unmanaged { var nativeArg1 = ToArg(0, arg1, arg1Align); var nativeResult = func(nativeArg1); return ToResult(nativeResult); } protected object ProcessFunc(object arg1, object arg2, Func func) where TArg1Base : unmanaged where TArg1: unmanaged where TArg2Base : unmanaged where TArg2: unmanaged where TResultBase : unmanaged where TResult: unmanaged { var nativeArg1 = ToArg(0, arg1); var nativeArg2 = ToArg(1, arg2); var nativeResult = func(nativeArg1, nativeArg2); return ToResult(nativeResult); } protected object ProcessFunc(object arg1, object arg2, object arg3, Func func) where TArg1Base : unmanaged where TArg1: unmanaged where TArg2Base : unmanaged where TArg2: unmanaged where TArg3Base : unmanaged where TArg3: unmanaged where TResultBase : unmanaged where TResult: unmanaged { var nativeArg1 = ToArg(0, arg1); var nativeArg2 = ToArg(1, arg2); var nativeArg3 = ToArg(2, arg3); var nativeResult = func(nativeArg1, nativeArg2, nativeArg3); return ToResult(nativeResult); } protected object ProcessFunc(object arg1, object arg2, object arg3, object arg4, Func func) where TArg1Base : unmanaged where TArg1: unmanaged where TArg2Base : unmanaged where TArg2: unmanaged where TArg3Base : unmanaged where TArg3: unmanaged where TArg4Base : unmanaged where TArg4: unmanaged where TResultBase : unmanaged where TResult: unmanaged { var nativeArg1 = ToArg(0, arg1); var nativeArg2 = ToArg(1, arg2); var nativeArg3 = ToArg(2, arg3); var nativeArg4 = ToArg(3, arg4); var nativeResult = func(nativeArg1, nativeArg2, nativeArg3, nativeArg4); return ToResult(nativeResult); } protected object ProcessFunc(object arg1, object arg2, object arg3, object arg4, object arg5, Func func) where TArg1Base : unmanaged where TArg1: unmanaged where TArg2Base : unmanaged where TArg2: unmanaged where TArg3Base : unmanaged where TArg3: unmanaged where TArg4Base : unmanaged where TArg4: unmanaged where TArg5Base : unmanaged where TArg5: unmanaged where TResultBase : unmanaged where TResult: unmanaged { var nativeArg1 = ToArg(0, arg1); var nativeArg2 = ToArg(1, arg2); var nativeArg3 = ToArg(2, arg3); var nativeArg4 = ToArg(3, arg4); var nativeArg5 = ToArg(4, arg5); var nativeResult = func(nativeArg1, nativeArg2, nativeArg3, nativeArg4, nativeArg5); return ToResult(nativeResult); } private T ToArg(int argIndex, object value, int align = 0) where T : unmanaged where TBase: unmanaged { if (typeof(T) == typeof(IntPtr)) { var buffer = value as KalkNativeBuffer; if (buffer == null) { throw new ScriptArgumentException(argIndex, "Expecting a byte buffer. Use malloc(size) to pass data to this argument."); } var ptr = buffer.GetPointer(); if (align != 0) { var remainder = (long) ptr & (long) align - 1; if (remainder != 0) { throw new ScriptArgumentException(argIndex, $"Invalid memory alignment. Expecting an alignment on {align} bytes, but the bytebuffer offset is off by {remainder} bytes"); } } var rawPtr = Unsafe.As(ref ptr); return rawPtr; } var targetSize = Unsafe.SizeOf(); var baseElementSize = Unsafe.SizeOf(); var dimension = targetSize / baseElementSize; if (dimension == 1) { // Handle Vector64 if (typeof(System.Runtime.Intrinsics.Vector64) == typeof(T)) { var tValue = Engine.ToObject(argIndex, value); return (T)(object)(System.Runtime.Intrinsics.Vector64.Create(tValue)); } if (typeof(System.Runtime.Intrinsics.Vector64) == typeof(T)) { var tValue = Engine.ToObject(argIndex, value); return (T)(object)(System.Runtime.Intrinsics.Vector64.Create(tValue)); } if (typeof(System.Runtime.Intrinsics.Vector64) == typeof(T)) { var tValue = Engine.ToObject(argIndex, value); return (T)(object)(System.Runtime.Intrinsics.Vector64.Create(tValue)); } return Engine.ToObject(argIndex, value); } else { Debug.Assert(dimension > 1); Span elements = stackalloc TBase[dimension]; if (value is KalkVector vec) { var span = vec.AsSpan(); var minSize = Math.Min(targetSize, span.Length); span = span.Slice(0, minSize); span.CopyTo(MemoryMarshal.Cast(elements)); } else { var leftValueT = Engine.ToObject(argIndex, value); for (int i = 0; i < dimension; i++) { elements[i] = leftValueT; } } return Unsafe.As(ref elements[0]); } } private object ToResult(T result) where T: unmanaged where TBase: unmanaged { var targetSize = Unsafe.SizeOf(); var baseElementSize = Unsafe.SizeOf(); var dimension = targetSize / baseElementSize; var span = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref result, 1)); if (dimension == 1) { return span[0]; } else { var vector = new KalkVector(dimension); for (int i = 0; i < dimension; i++) { vector[i] = span[i]; } return vector; } } } }