From b250ac3923c86868e1a1694054825bc66c2b1553 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 15 Jan 2024 14:30:16 +1000 Subject: [PATCH] Introduce new utilities to replace poor performing runtime methods --- .../Helpers/Shuffle/IComponentShuffle.cs | 173 +------------ .../Common/Helpers/Shuffle/IPad3Shuffle4.cs | 26 +- .../Common/Helpers/Shuffle/IShuffle3.cs | 18 +- .../Common/Helpers/Shuffle/IShuffle4.cs | 178 ++++++++++++++ .../Common/Helpers/Shuffle/IShuffle4Slice3.cs | 28 +-- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 230 +++++++++--------- .../Common/Helpers/Vector128Utilities.cs | 83 ++++++- .../Common/Helpers/Vector256Utilities.cs | 78 ++++++ .../Common/Helpers/Vector512Utilities.cs | 80 ++++++ 9 files changed, 563 insertions(+), 331 deletions(-) create mode 100644 src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs create mode 100644 src/ImageSharp/Common/Helpers/Vector256Utilities.cs create mode 100644 src/ImageSharp/Common/Helpers/Vector512Utilities.cs diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs index d4ab8c6183..c856267db2 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs @@ -1,12 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Buffers.Binary; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using static SixLabors.ImageSharp.SimdUtils; - // The JIT can detect and optimize rotation idioms ROTL (Rotate Left) // and ROTR (Rotate Right) emitting efficient CPU instructions: // https://github.com/dotnet/coreclr/pull/1830 @@ -20,7 +14,7 @@ internal interface IComponentShuffle { /// /// Shuffles then slices 8-bit integers in - /// using the control and store the results in . + /// using a byte control and store the results in . /// If successful, this method will reduce the length of length /// by the shuffle amount. /// @@ -40,168 +34,3 @@ internal interface IComponentShuffle /// void Shuffle(ReadOnlySpan source, Span destination); } - -/// -internal interface IShuffle4 : IComponentShuffle -{ -} - -internal readonly struct DefaultShuffle4(byte control) : IShuffle4 -{ - public byte Control { get; } = control; - - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, this.Control); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(destination); - - SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); - - for (nuint i = 0; i < (uint)source.Length; i += 4) - { - Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i); - Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i); - Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i); - Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i); - } - } -} - -internal readonly struct WXYZShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle2103); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // ROTL(8, packed) = [Z Y X W] - Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24); - } - } -} - -internal readonly struct WZYXShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0123); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // REVERSE(packedArgb) = [X Y Z W] - Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed); - } - } -} - -internal readonly struct YZWXShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0321); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // ROTR(8, packedArgb) = [Y Z W X] - Unsafe.Add(ref dBase, i) = BitOperations.RotateRight(packed, 8); - } - } -} - -internal readonly struct ZYXWShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3012); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // tmp1 = [W 0 Y 0] - // tmp2 = [0 Z 0 X] - // tmp3=ROTL(16, tmp2) = [0 X 0 Z] - // tmp1 + tmp3 = [W X Y Z] - uint tmp1 = packed & 0xFF00FF00; - uint tmp2 = packed & 0x00FF00FF; - uint tmp3 = BitOperations.RotateLeft(tmp2, 16); - - Unsafe.Add(ref dBase, i) = tmp1 + tmp3; - } - } -} - -internal readonly struct XWZYShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle1230); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // tmp1 = [0 Z 0 X] - // tmp2 = [W 0 Y 0] - // tmp3=ROTL(16, tmp2) = [Y 0 W 0] - // tmp1 + tmp3 = [Y Z W X] - uint tmp1 = packed & 0x00FF00FF; - uint tmp2 = packed & 0xFF00FF00; - uint tmp3 = BitOperations.RotateLeft(tmp2, 16); - - Unsafe.Add(ref dBase, i) = tmp1 + tmp3; - } - } -} diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs index 255448d619..0f282c7f9a 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static SixLabors.ImageSharp.SimdUtils; @@ -12,22 +13,21 @@ internal interface IPad3Shuffle4 : IComponentShuffle { } -internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4 +internal readonly struct DefaultPad3Shuffle4([ConstantExpected] byte control) : IPad3Shuffle4 { - public DefaultPad3Shuffle4(byte control) - => this.Control = control; - - public byte Control { get; } + public byte Control { get; } = control; [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, this.Control); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) +#pragma warning disable CA1857 // A constant is expected for the parameter + => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref destination, this.Control); +#pragma warning restore CA1857 // A constant is expected for the parameter [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); + ref byte dBase = ref MemoryMarshal.GetReference(destination); SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); @@ -51,14 +51,14 @@ internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4 internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4 { [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, SimdUtils.Shuffle.MMShuffle3210); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3210); [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); + ref byte dBase = ref MemoryMarshal.GetReference(destination); ref byte sEnd = ref Unsafe.Add(ref sBase, (uint)source.Length); ref byte sLoopEnd = ref Unsafe.Subtract(ref sEnd, 4); diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs index 89faca2437..3c0973ad69 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static SixLabors.ImageSharp.SimdUtils; @@ -12,22 +13,21 @@ internal interface IShuffle3 : IComponentShuffle { } -internal readonly struct DefaultShuffle3 : IShuffle3 +internal readonly struct DefaultShuffle3([ConstantExpected] byte control) : IShuffle3 { - public DefaultShuffle3(byte control) - => this.Control = control; - - public byte Control { get; } + public byte Control { get; } = control; [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle3Reduce(ref source, ref dest, this.Control); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) +#pragma warning disable CA1857 // A constant is expected for the parameter + => HwIntrinsics.Shuffle3Reduce(ref source, ref destination, this.Control); +#pragma warning restore CA1857 // A constant is expected for the parameter [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); + ref byte dBase = ref MemoryMarshal.GetReference(destination); SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs new file mode 100644 index 0000000000..d5c6df2c8b --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs @@ -0,0 +1,178 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SixLabors.ImageSharp.SimdUtils; + +namespace SixLabors.ImageSharp; + +/// +internal interface IShuffle4 : IComponentShuffle +{ +} + +internal readonly struct DefaultShuffle4([ConstantExpected] byte control) : IShuffle4 +{ + public byte Control { get; } = control; + + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) +#pragma warning disable CA1857 // A constant is expected for the parameter + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, this.Control); +#pragma warning restore CA1857 // A constant is expected for the parameter + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref byte sBase = ref MemoryMarshal.GetReference(source); + ref byte dBase = ref MemoryMarshal.GetReference(destination); + + SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); + + for (nuint i = 0; i < (uint)source.Length; i += 4) + { + Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i); + Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i); + Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i); + Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i); + } + } +} + +internal readonly struct WXYZShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle2103); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // ROTL(8, packed) = [Z Y X W] + Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24); + } + } +} + +internal readonly struct WZYXShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0123); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // REVERSE(packedArgb) = [X Y Z W] + Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed); + } + } +} + +internal readonly struct YZWXShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0321); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // ROTR(8, packedArgb) = [Y Z W X] + Unsafe.Add(ref dBase, i) = BitOperations.RotateRight(packed, 8); + } + } +} + +internal readonly struct ZYXWShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3012); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // tmp1 = [W 0 Y 0] + // tmp2 = [0 Z 0 X] + // tmp3=ROTL(16, tmp2) = [0 X 0 Z] + // tmp1 + tmp3 = [W X Y Z] + uint tmp1 = packed & 0xFF00FF00; + uint tmp2 = packed & 0x00FF00FF; + uint tmp3 = BitOperations.RotateLeft(tmp2, 16); + + Unsafe.Add(ref dBase, i) = tmp1 + tmp3; + } + } +} + +internal readonly struct XWZYShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle1230); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // tmp1 = [0 Z 0 X] + // tmp2 = [W 0 Y 0] + // tmp3=ROTL(16, tmp2) = [Y 0 W 0] + // tmp1 + tmp3 = [Y Z W X] + uint tmp1 = packed & 0x00FF00FF; + uint tmp2 = packed & 0xFF00FF00; + uint tmp3 = BitOperations.RotateLeft(tmp2, 16); + + Unsafe.Add(ref dBase, i) = tmp1 + tmp3; + } + } +} diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs index 30fda7a8e1..3e7e440664 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static SixLabors.ImageSharp.SimdUtils; @@ -12,26 +13,25 @@ internal interface IShuffle4Slice3 : IComponentShuffle { } -internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3 +internal readonly struct DefaultShuffle4Slice3([ConstantExpected] byte control) : IShuffle4Slice3 { - public DefaultShuffle4Slice3(byte control) - => this.Control = control; - - public byte Control { get; } + public byte Control { get; } = control; [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, this.Control); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) +#pragma warning disable CA1857 // A constant is expected for the parameter + => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref destination, this.Control); +#pragma warning restore CA1857 // A constant is expected for the parameter [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); + ref byte dBase = ref MemoryMarshal.GetReference(destination); SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); - for (nuint i = 0, j = 0; i < (uint)dest.Length; i += 3, j += 4) + for (nuint i = 0, j = 0; i < (uint)destination.Length; i += 3, j += 4) { Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + j); Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + j); @@ -43,14 +43,14 @@ internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3 internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3 { [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, SimdUtils.Shuffle.MMShuffle3210); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3210); [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref Byte3 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + ref Byte3 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); nint n = (nint)(uint)source.Length / 4; nint m = Numerics.Modulo4(n); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 4b9a90a952..14d2f1c259 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -63,7 +63,9 @@ internal static partial class SimdUtils ref Span destination, [ConstantExpected] byte control) { - if (Vector512.IsHardwareAccelerated || Vector256.IsHardwareAccelerated || Vector128.IsHardwareAccelerated) + if ((Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleFloat) || + (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleFloat) || + (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleFloat)) { int remainder = 0; if (Vector512.IsHardwareAccelerated) @@ -105,9 +107,11 @@ internal static partial class SimdUtils public static void Shuffle4Reduce( ref ReadOnlySpan source, ref Span destination, - byte control) + [ConstantExpected] byte control) { - if (Vector512.IsHardwareAccelerated || Vector256.IsHardwareAccelerated || Vector128.IsHardwareAccelerated) + if ((Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleByte) || + (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleByte) || + (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte)) { int remainder = 0; if (Vector512.IsHardwareAccelerated) @@ -139,7 +143,7 @@ internal static partial class SimdUtils } /// - /// Shuffles 8-bit integer triplets within 128-bit lanes in + /// Shuffles 8-bit integer triplets in /// using the control and store the results in . /// /// The source span of bytes. @@ -149,9 +153,9 @@ internal static partial class SimdUtils public static void Shuffle3Reduce( ref ReadOnlySpan source, ref Span destination, - byte control) + [ConstantExpected] byte control) { - if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsRightShift) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsRightAlign) { int remainder = source.Length % (Vector128.Count * 3); @@ -171,67 +175,67 @@ internal static partial class SimdUtils } /// - /// Pads then shuffles 8-bit integers within 128-bit lanes in - /// using the control and store the results in . + /// Pads then shuffles 8-bit integers in + /// using the control and store the results in . /// /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The byte control. [MethodImpl(InliningOptions.ShortMethod)] public static void Pad3Shuffle4Reduce( ref ReadOnlySpan source, - ref Span dest, - byte control) + ref Span destination, + [ConstantExpected] byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte) { int remainder = source.Length % (Vector128.Count * 3); int sourceCount = source.Length - remainder; - int destCount = (int)((uint)sourceCount * 4 / 3); + int destinationCount = (int)((uint)sourceCount * 4 / 3); if (sourceCount > 0) { Pad3Shuffle4( source[..sourceCount], - dest[..destCount], + destination[..destinationCount], control); source = source[sourceCount..]; - dest = dest[destCount..]; + destination = destination[destinationCount..]; } } } /// - /// Shuffles then slices 8-bit integers within 128-bit lanes in - /// using the control and store the results in . + /// Shuffles then slices 8-bit integers in + /// using the control and store the results in . /// /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The byte control. [MethodImpl(InliningOptions.ShortMethod)] public static void Shuffle4Slice3Reduce( ref ReadOnlySpan source, - ref Span dest, - byte control) + ref Span destination, + [ConstantExpected] byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte) { int remainder = source.Length & ((Vector128.Count * 4) - 1); // bit-hack for modulo int sourceCount = source.Length - remainder; - int destCount = (int)((uint)sourceCount * 3 / 4); + int destinationCount = (int)((uint)sourceCount * 3 / 4); if (sourceCount > 0) { Shuffle4Slice3( source[..sourceCount], - dest[..destCount], + destination[..destinationCount], control); source = source[sourceCount..]; - dest = dest[destCount..]; + destination = destination[destinationCount..]; } } } @@ -242,12 +246,8 @@ internal static partial class SimdUtils Span destination, [ConstantExpected] byte control) { - if (Vector512.IsHardwareAccelerated) + if (Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleFloat) { - Span temp = stackalloc int[Vector512.Count]; - Shuffle.MMShuffleSpan(ref temp, control); - Vector512 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); @@ -260,26 +260,22 @@ internal static partial class SimdUtils ref Vector512 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector512 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector512.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector512Utilities.Shuffle(vs0, control); + Unsafe.Add(ref vd0, (nuint)1) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control); + Unsafe.Add(ref vd0, (nuint)2) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control); + Unsafe.Add(ref vd0, (nuint)3) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector512.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector512Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control); } } } - else if (Vector256.IsHardwareAccelerated) + else if (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleFloat) { - Span temp = stackalloc int[Vector256.Count]; - Shuffle.MMShuffleSpan(ref temp, control); - Vector256 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); @@ -292,26 +288,22 @@ internal static partial class SimdUtils ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector256 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector256.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector256Utilities.Shuffle(vs0, control); + Unsafe.Add(ref vd0, (nuint)1) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control); + Unsafe.Add(ref vd0, (nuint)2) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control); + Unsafe.Add(ref vd0, (nuint)3) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector256.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector256Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control); } } } - else if (Vector128.IsHardwareAccelerated) + else if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleFloat) { - Span temp = stackalloc int[Vector128.Count]; - Shuffle.MMShuffleSpan(ref temp, control); - Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); @@ -324,17 +316,17 @@ internal static partial class SimdUtils ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector128 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector128.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector128Utilities.Shuffle(vs0, control); + Unsafe.Add(ref vd0, (nuint)1) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control); + Unsafe.Add(ref vd0, (nuint)2) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control); + Unsafe.Add(ref vd0, (nuint)3) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector128.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector128Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control); } } } @@ -344,9 +336,9 @@ internal static partial class SimdUtils private static void Shuffle4( ReadOnlySpan source, Span destination, - byte control) + [ConstantExpected] byte control) { - if (Vector512.IsHardwareAccelerated) + if (Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleByte) { Span temp = stackalloc byte[Vector512.Count]; Shuffle.MMShuffleSpan(ref temp, control); @@ -364,21 +356,21 @@ internal static partial class SimdUtils ref Vector512 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector512 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector512.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector512Utilities.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector512.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector512Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } - else if (Vector256.IsHardwareAccelerated) + else if (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleByte) { Span temp = stackalloc byte[Vector256.Count]; Shuffle.MMShuffleSpan(ref temp, control); @@ -396,21 +388,21 @@ internal static partial class SimdUtils ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector256 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector256.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector256Utilities.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector256.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector256Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } - else if (Vector128.IsHardwareAccelerated) + else if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte) { Span temp = stackalloc byte[Vector128.Count]; Shuffle.MMShuffleSpan(ref temp, control); @@ -428,17 +420,17 @@ internal static partial class SimdUtils ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector128 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector128.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector128Utilities.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector128.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector128Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } @@ -448,9 +440,9 @@ internal static partial class SimdUtils private static void Shuffle3( ReadOnlySpan source, Span destination, - byte control) + [ConstantExpected] byte control) { - if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsRightShift) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsRightAlign) { Vector128 maskPad4Nx16 = ShuffleMaskPad4Nx16(); Vector128 maskSlice4Nx16 = ShuffleMaskSlice4Nx16(); @@ -477,15 +469,15 @@ internal static partial class SimdUtils v2 = Vector128Utilities.AlignRight(v2, v1, 8); v1 = Vector128Utilities.AlignRight(v1, v0, 12); - v0 = Vector128.Shuffle(Vector128.Shuffle(v0, maskPad4Nx16), mask); - v1 = Vector128.Shuffle(Vector128.Shuffle(v1, maskPad4Nx16), mask); - v2 = Vector128.Shuffle(Vector128.Shuffle(v2, maskPad4Nx16), mask); - v3 = Vector128.Shuffle(Vector128.Shuffle(v3, maskPad4Nx16), mask); + v0 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, maskPad4Nx16), mask); + v1 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, maskPad4Nx16), mask); + v2 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, maskPad4Nx16), mask); + v3 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, maskPad4Nx16), mask); - v0 = Vector128.Shuffle(v0, maskE); - v1 = Vector128.Shuffle(v1, maskSlice4Nx16); - v2 = Vector128.Shuffle(v2, maskE); - v3 = Vector128.Shuffle(v3, maskSlice4Nx16); + v0 = Vector128Utilities.Shuffle(v0, maskE); + v1 = Vector128Utilities.Shuffle(v1, maskSlice4Nx16); + v2 = Vector128Utilities.Shuffle(v2, maskE); + v3 = Vector128Utilities.Shuffle(v3, maskSlice4Nx16); v0 = Vector128Utilities.AlignRight(v1, v0, 4); v3 = Vector128Utilities.AlignRight(v3, v2, 12); @@ -508,21 +500,21 @@ internal static partial class SimdUtils private static void Pad3Shuffle4( ReadOnlySpan source, Span destination, - byte control) + [ConstantExpected] byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte) { - Vector128 vmask = ShuffleMaskPad4Nx16(); - Vector128 vfill = Vector128.Create(0xff000000ff000000ul).AsByte(); + Vector128 maskPad4Nx16 = ShuffleMaskPad4Nx16(); + Vector128 fill = Vector128.Create(0xff000000ff000000ul).AsByte(); - Span bytes = stackalloc byte[Vector128.Count]; - Shuffle.MMShuffleSpan(ref bytes, control); - Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); + Span temp = stackalloc byte[Vector128.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref Vector128 destBase = + ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); nuint n = source.Vector128Count(); @@ -532,17 +524,17 @@ internal static partial class SimdUtils ref Vector128 v0 = ref Unsafe.Add(ref sourceBase, i); Vector128 v1 = Unsafe.Add(ref v0, 1); Vector128 v2 = Unsafe.Add(ref v0, 2); - Vector128 v3 = Sse2.ShiftRightLogical128BitLane(v2, 4); + Vector128 v3 = Vector128Utilities.ShiftRightBytesInVector(v2, 4); - v2 = Ssse3.AlignRight(v2, v1, 8); - v1 = Ssse3.AlignRight(v1, v0, 12); + v2 = Vector128Utilities.AlignRight(v2, v1, 8); + v1 = Vector128Utilities.AlignRight(v1, v0, 12); - ref Vector128 vd = ref Unsafe.Add(ref destBase, j); + ref Vector128 vd = ref Unsafe.Add(ref destinationBase, j); - vd = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v0, vmask), vfill), vshuffle); - Unsafe.Add(ref vd, 1) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v1, vmask), vfill), vshuffle); - Unsafe.Add(ref vd, 2) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v2, vmask), vfill), vshuffle); - Unsafe.Add(ref vd, 3) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v3, vmask), vfill), vshuffle); + vd = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, maskPad4Nx16) | fill, mask); + Unsafe.Add(ref vd, 1) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, maskPad4Nx16) | fill, mask); + Unsafe.Add(ref vd, 2) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, maskPad4Nx16) | fill, mask); + Unsafe.Add(ref vd, 3) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, maskPad4Nx16) | fill, mask); } } } @@ -550,23 +542,23 @@ internal static partial class SimdUtils [MethodImpl(InliningOptions.ShortMethod)] private static void Shuffle4Slice3( ReadOnlySpan source, - Span dest, - byte control) + Span destination, + [ConstantExpected] byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte) { - Vector128 vmasko = ShuffleMaskSlice4Nx16(); - Vector128 vmaske = Ssse3.AlignRight(vmasko, vmasko, 12); + Vector128 maskSlice4Nx16 = ShuffleMaskSlice4Nx16(); + Vector128 maskE = Ssse3.AlignRight(maskSlice4Nx16, maskSlice4Nx16, 12); - Span bytes = stackalloc byte[Vector128.Count]; - Shuffle.MMShuffleSpan(ref bytes, control); - Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); + Span temp = stackalloc byte[Vector128.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref Vector128 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + ref Vector128 destinationBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); nuint n = source.Vector128Count(); @@ -579,20 +571,20 @@ internal static partial class SimdUtils Vector128 v2 = Unsafe.Add(ref vs, 2); Vector128 v3 = Unsafe.Add(ref vs, 3); - v0 = Ssse3.Shuffle(Ssse3.Shuffle(v0, vshuffle), vmaske); - v1 = Ssse3.Shuffle(Ssse3.Shuffle(v1, vshuffle), vmasko); - v2 = Ssse3.Shuffle(Ssse3.Shuffle(v2, vshuffle), vmaske); - v3 = Ssse3.Shuffle(Ssse3.Shuffle(v3, vshuffle), vmasko); + v0 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, mask), maskE); + v1 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, mask), maskSlice4Nx16); + v2 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, mask), maskE); + v3 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, mask), maskSlice4Nx16); - v0 = Ssse3.AlignRight(v1, v0, 4); - v3 = Ssse3.AlignRight(v3, v2, 12); + v0 = Vector128Utilities.AlignRight(v1, v0, 4); + v3 = Vector128Utilities.AlignRight(v3, v2, 12); - v1 = Sse2.ShiftLeftLogical128BitLane(v1, 4); - v2 = Sse2.ShiftRightLogical128BitLane(v2, 4); + v1 = Vector128Utilities.ShiftLeftBytesInVector(v1, 4); + v2 = Vector128Utilities.ShiftRightBytesInVector(v2, 4); - v1 = Ssse3.AlignRight(v2, v1, 8); + v1 = Vector128Utilities.AlignRight(v2, v1, 8); - ref Vector128 vd = ref Unsafe.Add(ref destBase, j); + ref Vector128 vd = ref Unsafe.Add(ref destinationBase, j); vd = v0; Unsafe.Add(ref vd, 1) = v1; diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index 829362da88..a272e459cb 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -11,20 +11,95 @@ using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.Common.Helpers; /// -/// Defines utility methods for that have not yet been normalized in the runtime. +/// Defines utility methods for that have either: +/// +/// Not yet been normalized in the runtime. +/// Produce codegen that is poorly optimized by the runtime. +/// /// Should only be used if the intrinsics are available. /// internal static class Vector128Utilities { /// - /// Gets a value indicating whether right shift operations are supported. + /// Gets a value indicating whether shuffle operations are supported. /// - public static bool SupportsRightShift + public static bool SupportsShuffleFloat + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Sse.IsSupported; + } + + /// + /// Gets a value indicating whether shuffle operations are supported. + /// + public static bool SupportsShuffleByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Ssse3.IsSupported || AdvSimd.Arm64.IsSupported; + } + + /// + /// Gets a value indicating whether right align operations are supported. + /// + public static bool SupportsRightAlign { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Ssse3.IsSupported || AdvSimd.IsSupported; } + /// + /// Gets a value indicating whether right or left byte shift operations are supported. + /// + public static bool SupportsShiftByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Sse2.IsSupported || AdvSimd.IsSupported; + } + + /// + /// Creates a new vector by selecting values from an input vector using the control. + /// + /// The input vector from which values are selected. + /// The shuffle control byte. + /// The . + public static Vector128 Shuffle(Vector128 vector, [ConstantExpected] byte control) + { + if (Sse.IsSupported) + { + return Sse.Shuffle(vector, vector, control); + } + + ThrowUnreachableException(); + return default; + } + + /// + /// Creates a new vector by selecting values from an input vector using a set of indices. + /// + /// + /// The input vector from which values are selected. + /// + /// The per-element indices used to select a value from . + /// + /// + /// A new vector containing the values from selected by the given . + /// + public static Vector128 Shuffle(Vector128 vector, Vector128 indices) + { + if (Ssse3.IsSupported) + { + return Ssse3.Shuffle(vector, indices); + } + + if (AdvSimd.Arm64.IsSupported) + { + return AdvSimd.Arm64.VectorTableLookup(vector, indices); + } + + ThrowUnreachableException(); + return default; + } + /// /// Shifts a 128-bit value right by a specified number of bytes while shifting in zeros. /// @@ -80,7 +155,7 @@ internal static class Vector128Utilities /// The . public static Vector128 AlignRight(Vector128 left, Vector128 right, [ConstantExpected(Max = (byte)15)] byte mask) { - if (Sse3.IsSupported) + if (Ssse3.IsSupported) { return Ssse3.AlignRight(left, right, mask); } diff --git a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs new file mode 100644 index 0000000000..14fa24b315 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Common.Helpers; + +/// +/// Defines utility methods for that have either: +/// +/// Not yet been normalized in the runtime. +/// Produce codegen that is poorly optimized by the runtime. +/// +/// Should only be used if the intrinsics are available. +/// +internal static class Vector256Utilities +{ + /// + /// Gets a value indicating whether shuffle byte operations are supported. + /// + public static bool SupportsShuffleFloat + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Avx.IsSupported; + } + + /// + /// Gets a value indicating whether shuffle byte operations are supported. + /// + public static bool SupportsShuffleByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Avx2.IsSupported; + } + + /// + /// Creates a new vector by selecting values from an input vector using a set of indices. + /// + /// The input vector from which values are selected. + /// The shuffle control byte. + /// The . + public static Vector256 Shuffle(Vector256 vector, [ConstantExpected] byte control) + { + if (Avx.IsSupported) + { + return Avx.Shuffle(vector, vector, control); + } + + ThrowUnreachableException(); + return default; + } + + /// + /// Creates a new vector by selecting values from an input vector using a set of indices. + /// + /// The input vector from which values are selected. + /// + /// The per-element indices used to select a value from . + /// + /// The . + public static Vector256 Shuffle(Vector256 vector, Vector256 indices) + { + if (Avx2.IsSupported) + { + return Avx2.Shuffle(vector, indices); + } + + ThrowUnreachableException(); + return default; + } + + [DoesNotReturn] + private static void ThrowUnreachableException() => throw new UnreachableException(); +} diff --git a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs new file mode 100644 index 0000000000..5488b40644 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Common.Helpers; + +/// +/// Defines utility methods for that have either: +/// +/// Not yet been normalized in the runtime. +/// Produce codegen that is poorly optimized by the runtime. +/// +/// Should only be used if the intrinsics are available. +/// +internal static class Vector512Utilities +{ + /// + /// Gets a value indicating whether shuffle float operations are supported. + /// + public static bool SupportsShuffleFloat + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Avx512F.IsSupported; + } + + /// + /// Gets a value indicating whether shuffle byte operations are supported. + /// + public static bool SupportsShuffleByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Avx512BW.IsSupported; + } + + /// + /// Creates a new vector by selecting values from an input vector using the control. + /// + /// The input vector from which values are selected. + /// The shuffle control byte. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Shuffle(Vector512 vector, [ConstantExpected] byte control) + { + if (Avx512F.IsSupported) + { + return Avx512F.Shuffle(vector, vector, control); + } + + ThrowUnreachableException(); + return default; + } + + /// + /// Creates a new vector by selecting values from an input vector using a set of indices. + /// + /// The input vector from which values are selected. + /// + /// The per-element indices used to select a value from . + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Shuffle(Vector512 vector, Vector512 indices) + { + if (Avx512BW.IsSupported) + { + return Avx512BW.Shuffle(vector, indices); + } + + ThrowUnreachableException(); + return default; + } + + [DoesNotReturn] + private static void ThrowUnreachableException() => throw new UnreachableException(); +}