diff --git a/Misaki.HighPerformance.Mathematics.SPMD/ISPMD.cs b/Misaki.HighPerformance.Mathematics.SPMD/ISPMDLane.cs
similarity index 96%
rename from Misaki.HighPerformance.Mathematics.SPMD/ISPMD.cs
rename to Misaki.HighPerformance.Mathematics.SPMD/ISPMDLane.cs
index 15c3b27..e808cb9 100644
--- a/Misaki.HighPerformance.Mathematics.SPMD/ISPMD.cs
+++ b/Misaki.HighPerformance.Mathematics.SPMD/ISPMDLane.cs
@@ -5,7 +5,7 @@ namespace Misaki.HighPerformance.Mathematics.SPMD;
///
/// Common marker interface for SPMD lane types.
///
-public interface ISPMD
+public interface ISPMDLane
{
///
/// Gets the number of lanes (vector width) for the SPMD implementation.
@@ -29,8 +29,8 @@ public interface ISPMD
///
/// The concrete SPMD lane type implementing this interface.
/// The underlying numeric element type.
-public interface ISPMD : ISPMD, IEquatable
- where TSelf : ISPMD
+public unsafe interface ISPMDLane : ISPMDLane, IEquatable
+ where TSelf : ISPMDLane
where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators
{
///
@@ -116,7 +116,15 @@ public interface ISPMD : ISPMD, IEquatable
///
/// Unsafe pointer overloads are provided for scenarios where sequential lane data is already contiguous in memory.
///
- static abstract unsafe TSelf Load(TNumber* pValue);
+ static abstract TSelf Load(TNumber* pValue);
+
+ static abstract TSelf MaskLoad(TSelf mask, ref TNumber value);
+ static abstract TSelf MaskLoad(TSelf mask, TNumber* pValue);
+
+ static abstract TSelf Gather(TNumber* pData, TSelf indices, int scale);
+ static abstract TSelf Gather(TNumber* pData, int* pIndices, int scale);
+ static abstract TSelf Gather(ref TNumber baseAddress, TSelf indices, int scale);
+ static abstract TSelf Gather(ref TNumber baseAddress, ref int baseIndex, int scale);
///
/// Stores the lane value to the specified reference.
@@ -127,7 +135,7 @@ public interface ISPMD : ISPMD, IEquatable
/// Stores the lane value to the specified pointer.
///
/// The pointer to store to.
- unsafe void Store(TNumber* pDestination);
+ void Store(TNumber* pDestination);
///
/// Compresses the data specified by the given mask and stores the compressed result in the provided destination
/// variable.
@@ -149,7 +157,7 @@ public interface ISPMD : ISPMD, IEquatable
///
/// Implementations may use hardware-specific shuffle tables to reorder the selected lanes before storing, falling back to a scalar loop otherwise.
///
- unsafe int CompressStore(TSelf mask, TNumber* pDestination);
+ int CompressStore(TSelf mask, TNumber* pDestination);
///
/// Converts the lane value to a vector.
@@ -157,6 +165,8 @@ public interface ISPMD : ISPMD, IEquatable
/// The backing vector representation.
Vector AsVector();
+ TNumber* GetUnsafePtr();
+
///
/// Casts the lane value to another SPMD lane type with a different underlying numeric type.
///
@@ -164,7 +174,7 @@ public interface ISPMD : ISPMD, IEquatable
/// The underlying numeric type of the other SPMD lane.
/// The casted lane value.
TOther Cast()
- where TOther : ISPMD
+ where TOther : ISPMDLane
where TOtherNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators;
///
@@ -174,7 +184,7 @@ public interface ISPMD : ISPMD, IEquatable
/// The underlying numeric type of the other SPMD lane.
/// The bit-cast lane value.
TOther BitCast()
- where TOther : ISPMD
+ where TOther : ISPMDLane
where TOtherNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators;
///
diff --git a/Misaki.HighPerformance.Mathematics.SPMD/README.md b/Misaki.HighPerformance.Mathematics.SPMD/README.md
index 227f0ec..13c3ad9 100644
--- a/Misaki.HighPerformance.Mathematics.SPMD/README.md
+++ b/Misaki.HighPerformance.Mathematics.SPMD/README.md
@@ -21,8 +21,8 @@ This package is intended for code that wants to express vectorized work in a way
## Main types
-- `ISPMD`
-- `ISPMD`
+- `ISPMDLane`
+- `ISPMDLane`
- `ScalerLane`
- `WideLane`
- `IJobSPMD`
diff --git a/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs b/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs
index 7085eb5..9667066 100644
--- a/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs
+++ b/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs
@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
namespace Misaki.HighPerformance.Mathematics.SPMD;
[StructLayout(LayoutKind.Sequential)]
-public readonly unsafe struct ScalarLane : ISPMD, TNumber>
+public readonly unsafe struct ScalarLane : ISPMDLane, TNumber>
where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators
{
public readonly TNumber value;
@@ -19,25 +19,25 @@ public readonly unsafe struct ScalarLane : ISPMD, T
public static ScalarLane Zero
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => new(TNumber.Zero);
+ get => new ScalarLane(TNumber.Zero);
}
public static ScalarLane One
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => new(TNumber.One);
+ get => new ScalarLane(TNumber.One);
}
public static ScalarLane MinValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => new(TNumber.MinValue);
+ get => new ScalarLane(TNumber.MinValue);
}
public static ScalarLane MaxValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => new(TNumber.MaxValue);
+ get => new ScalarLane(TNumber.MaxValue);
}
public readonly TNumber this[int index]
@@ -54,39 +54,76 @@ public readonly unsafe struct ScalarLane : ISPMD, T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Create(TNumber value)
{
- return new(value);
+ return new ScalarLane(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Create(params ReadOnlySpan values)
{
- return new(values[0]);
+ return new ScalarLane(values[0]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Create(Vector value)
{
- return new(value[0]);
+ return new ScalarLane(value[0]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Sequence(TNumber start, TNumber step)
{
- return new(start);
+ return new ScalarLane(start);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Load(ref TNumber value)
{
- return new(value);
+ return new ScalarLane(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Load(TNumber* pValue)
{
- return new(*pValue);
+ return new ScalarLane(*pValue);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ScalarLane MaskLoad(ScalarLane mask, ref TNumber value)
+ {
+ return new ScalarLane(mask.value != TNumber.Zero ? value : TNumber.Zero);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ScalarLane MaskLoad(ScalarLane mask, TNumber* pValue)
+ {
+ return new ScalarLane(mask.value != TNumber.Zero ? *pValue : TNumber.Zero);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ScalarLane Gather(TNumber* pData, ScalarLane indices, int scale)
+ {
+ return new ScalarLane(pData[int.CreateTruncating(indices.value) * scale / sizeof(TNumber)]);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ScalarLane Gather(TNumber* pData, int* pIndices, int scale)
+ {
+ return new ScalarLane(pData[pIndices[0] * scale / sizeof(TNumber)]);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ScalarLane Gather(ref TNumber baseAddress, ScalarLane indices, int scale)
+ {
+ return new ScalarLane(Unsafe.Add(ref baseAddress, int.CreateTruncating(indices.value) * scale / sizeof(TNumber)));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ScalarLane Gather(ref TNumber baseAddress, ref int baseIndex, int scale)
+ {
+ return new ScalarLane(Unsafe.Add(ref baseAddress, int.CreateTruncating(baseIndex) * scale / sizeof(TNumber)));
+ }
+
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void Store(ref TNumber destination)
{
@@ -123,9 +160,15 @@ public readonly unsafe struct ScalarLane : ISPMD, T
return Vector.Create(value);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly TNumber* GetUnsafePtr()
+ {
+ return (TNumber*)Unsafe.AsPointer(ref Unsafe.AsRef(in value));
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TOther Cast()
- where TOther : ISPMD
+ where TOther : ISPMDLane
where TOtherNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators
{
return TOther.Create(TOtherNumber.CreateChecked(value));
@@ -133,7 +176,7 @@ public readonly unsafe struct ScalarLane : ISPMD, T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TOther BitCast()
- where TOther : ISPMD
+ where TOther : ISPMDLane
where TOtherNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators
{
return Unsafe.BitCast, TOther>(this);
@@ -142,61 +185,61 @@ public readonly unsafe struct ScalarLane : ISPMD, T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator +(ScalarLane a, ScalarLane b)
{
- return new(a.value + b.value);
+ return new ScalarLane(a.value + b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator -(ScalarLane a, ScalarLane b)
{
- return new(a.value - b.value);
+ return new ScalarLane(a.value - b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator *(ScalarLane a, ScalarLane b)
{
- return new(a.value * b.value);
+ return new ScalarLane(a.value * b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator /(ScalarLane a, ScalarLane b)
{
- return new(a.value / b.value);
+ return new ScalarLane(a.value / b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator %(ScalarLane a, ScalarLane b)
{
- return new(a.value % b.value);
+ return new ScalarLane(a.value % b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator -(ScalarLane a)
{
- return new(-a.value);
+ return new ScalarLane(-a.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator &(ScalarLane a, ScalarLane b)
{
- return new(a.value & b.value);
+ return new ScalarLane(a.value & b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator |(ScalarLane a, ScalarLane b)
{
- return new(a.value | b.value);
+ return new ScalarLane(a.value | b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator ^(ScalarLane a, ScalarLane b)
{
- return new(a.value ^ b.value);
+ return new ScalarLane(a.value ^ b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane operator ~(ScalarLane a)
{
- return new(~a.value);
+ return new ScalarLane(~a.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -240,7 +283,7 @@ public readonly unsafe struct ScalarLane : ISPMD, T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ScalarLane(TNumber value)
{
- return new(value);
+ return new ScalarLane(value);
}
@@ -248,7 +291,7 @@ public readonly unsafe struct ScalarLane : ISPMD, T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Abs(ScalarLane value)
{
- return new(TNumber.Abs(value.value));
+ return new ScalarLane(TNumber.Abs(value.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -276,7 +319,7 @@ public readonly unsafe struct ScalarLane : ISPMD, T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Frac(ScalarLane value)
{
- return new(value.value - TNumber.CreateTruncating(value.value));
+ return new ScalarLane(value.value - TNumber.CreateTruncating(value.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -301,37 +344,37 @@ public readonly unsafe struct ScalarLane : ISPMD, T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Lerp(ScalarLane a, ScalarLane b, ScalarLane t)
{
- return new(a.value + (b.value - a.value) * t.value);
+ return new ScalarLane(a.value + (b.value - a.value) * t.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane MultipleAdd(ScalarLane a, ScalarLane b, ScalarLane c)
{
- return new(TNumber.MultiplyAddEstimate(a.value, b.value, c.value));
+ return new ScalarLane(TNumber.MultiplyAddEstimate(a.value, b.value, c.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Min(ScalarLane a, ScalarLane b)
{
- return new(TNumber.Min(a.value, b.value));
+ return new ScalarLane(TNumber.Min(a.value, b.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Max(ScalarLane a, ScalarLane b)
{
- return new(TNumber.Max(a.value, b.value));
+ return new ScalarLane(TNumber.Max(a.value, b.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Clamp(ScalarLane value, ScalarLane min, ScalarLane max)
{
- return new(TNumber.Clamp(value.value, min.value, max.value));
+ return new ScalarLane(TNumber.Clamp(value.value, min.value, max.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Saturate(ScalarLane value)
{
- return Clamp(value, new(TNumber.Zero), new(TNumber.One));
+ return Clamp(value, new ScalarLane(TNumber.Zero), new ScalarLane(TNumber.One));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -638,19 +681,19 @@ public readonly unsafe struct ScalarLane : ISPMD, T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Sign(ScalarLane value)
{
- return new((value.value > TNumber.Zero) ? TNumber.One : (value.value < TNumber.Zero) ? ~TNumber.Zero : TNumber.Zero);
+ return new ScalarLane((value.value > TNumber.Zero) ? TNumber.One : (value.value < TNumber.Zero) ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane CopySign(ScalarLane magnitude, ScalarLane sign)
{
- return new(TNumber.CopySign(magnitude.value, sign.value));
+ return new ScalarLane(TNumber.CopySign(magnitude.value, sign.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Rcp(ScalarLane value)
{
- return new(TNumber.One / value.value);
+ return new ScalarLane(TNumber.One / value.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -662,37 +705,37 @@ public readonly unsafe struct ScalarLane : ISPMD, T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Select(ScalarLane conditionMask, ScalarLane ifTrue, ScalarLane ifFalse)
{
- return new(conditionMask.value != TNumber.Zero ? ifTrue.value : ifFalse.value);
+ return new ScalarLane(conditionMask.value != TNumber.Zero ? ifTrue.value : ifFalse.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane GreaterThan(ScalarLane a, ScalarLane b)
{
- return new(a.value > b.value ? ~TNumber.Zero : TNumber.Zero);
+ return new ScalarLane(a.value > b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane GreaterThanOrEqual(ScalarLane a, ScalarLane b)
{
- return new(a.value >= b.value ? ~TNumber.Zero : TNumber.Zero);
+ return new ScalarLane(a.value >= b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane LessThan(ScalarLane a, ScalarLane b)
{
- return new(a.value < b.value ? ~TNumber.Zero : TNumber.Zero);
+ return new ScalarLane(a.value < b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane LessThanOrEqual(ScalarLane a, ScalarLane b)
{
- return new(a.value <= b.value ? ~TNumber.Zero : TNumber.Zero);
+ return new ScalarLane(a.value <= b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane Equal(ScalarLane a, ScalarLane b)
{
- return new(a.value == b.value ? ~TNumber.Zero : TNumber.Zero);
+ return new ScalarLane(a.value == b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/Misaki.HighPerformance.Mathematics.SPMD/Templates/IJobSPMD.gen.cs b/Misaki.HighPerformance.Mathematics.SPMD/Templates/IJobSPMD.gen.cs
index 146994c..f873e2c 100644
--- a/Misaki.HighPerformance.Mathematics.SPMD/Templates/IJobSPMD.gen.cs
+++ b/Misaki.HighPerformance.Mathematics.SPMD/Templates/IJobSPMD.gen.cs
@@ -14,7 +14,7 @@ public interface IJobSPMD
where TNumber0 : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators
{
void Execute(int baseIndex, ref readonly JobExecutionContext ctx)
- where TLane0 : unmanaged, ISPMD;
+ where TLane0 : unmanaged, ISPMDLane;
}
internal struct SPMDJobWrapper : IJobParallelFor
@@ -69,8 +69,8 @@ public interface IJobSPMD
where TNumber1 : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators
{
void Execute(int baseIndex, ref readonly JobExecutionContext ctx)
- where TLane0 : unmanaged, ISPMD
- where TLane1 : unmanaged, ISPMD;
+ where TLane0 : unmanaged, ISPMDLane
+ where TLane1 : unmanaged, ISPMDLane;
}
internal struct SPMDJobWrapper : IJobParallelFor
@@ -129,9 +129,9 @@ public interface IJobSPMD
where TNumber2 : unmanaged, INumber