Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
10957a8
Clarify compilation and testing requirements in copilot-instructions …
stephentoub Jan 26, 2026
5e389de
Add test coverage for DateTime.ParseExact with different DateTimeStyl…
Copilot Jan 26, 2026
aa3a647
FileSystemWatcher.Linux: use a single inotify instance and refactor w…
tmds Jan 26, 2026
a854bc5
Improve ConcurrentDictionary test coverage by asserting callback argu…
Copilot Jan 26, 2026
86224c3
Use Array.FindAll in ComEventMethods (#123582)
Henr1k80 Jan 26, 2026
257a9d2
Analyze reaching PHI in GetRangeFromAssertions (more branch foldings)…
EgorBo Jan 26, 2026
21d48d1
Fix Base64.DecodeFromUtf8 consuming whitespace in partial final quant…
Copilot Jan 26, 2026
4bab919
[RyuJit Wasm] don't home locals if none are referenced (#123569)
AndyAyersMS Jan 26, 2026
5d86fc4
JIT: Fix mask AND with zero incorrectly folded (#123603)
Copilot Jan 26, 2026
2353f6d
Add FileCheck test for elided bounds checks (#123585)
Copilot Jan 26, 2026
bb5ea9b
Reduce unsafe usage in TextInfo.cs (#122116)
GrabYourPitchforks Jan 26, 2026
861226a
Add performance-benchmark skill for ad hoc benchmarking with EgorBot …
Copilot Jan 26, 2026
6316c17
Optimize Directory.GetFiles by passing safe patterns to NtQueryDirect…
Copilot Jan 26, 2026
1b15c7c
Emit task returning thunks in crossgen2 (#122651)
jtschuster Jan 26, 2026
b37f7ad
Remove unreachable thread pool priority alternation (#123111)
eduardo-vp Jan 27, 2026
7bfd8c4
Fix STJ source generator for partial class contexts with JsonSerializ…
Copilot Jan 27, 2026
bad839b
Use IndexOfAny in BaseConfigurationRecord (#123637)
stephentoub Jan 27, 2026
061f153
Port alternation switch optimization from source generator to RegexCo…
Copilot Jan 27, 2026
871446d
Fix up hijacking on arm32 (preserve async continuation register) (#12…
eduardo-vp Jan 27, 2026
6b70e93
Vectorize BitIncrement and BitDecrement for float/double in TensorPri…
Copilot Jan 27, 2026
94023c1
Add allocMem support for allocating multiple data chunks (#123378)
jakobbotsch Jan 27, 2026
76e0255
Skip crypto tests requiring elevation when not elevated (#123636)
stephentoub Jan 27, 2026
6450687
[Wasm RyuJit] proper fix for register homing (#123631)
AndyAyersMS Jan 27, 2026
e83b37c
Add documentation links for ByRef fields and byref-like generics (#12…
Copilot Jan 27, 2026
bd21f74
Object Writer V1 Demo Features (#123511)
adamperlin Jan 27, 2026
5097397
Fix failures in internal validation pipeline (#123452)
jkoritzinsky Jan 27, 2026
7b4be60
Revert "Bring a few jithelpers to new unwind plan" (#123696)
jkotas Jan 28, 2026
46786f7
Revert "Reduce unsafe usage in TextInfo.cs" (#123692)
jkotas Jan 28, 2026
752c000
Convert BlazorWebWasm solution file to new format (#123287)
kasperk81 Jan 28, 2026
cb70f35
Use unoptimized array sort helper on iOS (#123358)
BrzVlad Jan 28, 2026
7c4a413
[browser][MonoAOT] use response file for llvm-size (#123548)
pavelsavara Jan 28, 2026
fbff0d2
SPMI: Fix aligned allocation size computation (#123707)
jakobbotsch Jan 28, 2026
090d98d
[browser][wbt] Remove wasm platform from test app slnx (#123710)
maraf Jan 28, 2026
8fd9106
Replace deprecated wmic with MSBuild parallelism properties (#122386)
Copilot Jan 28, 2026
104aaa0
[RISC-V] Enhance the utilization of sh1/2/3add(.uw) instructions (#12…
namu-lee Jan 28, 2026
72a32ab
Revert "Bring a few jithelpers to new unwind plan (#123307)"
jkotas Jan 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Vectorize BitIncrement and BitDecrement for float/double in TensorPri…
…mitives (#123610)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Co-authored-by: tannergooding <10487869+tannergooding@users.noreply.github.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>
  • Loading branch information
4 people authored Jan 27, 2026
commit 6b70e930b4b7568286ba54f2b8e70a901c8ac7b5
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;

namespace System.Numerics.Tensors
Expand All @@ -26,11 +27,162 @@ public static void BitDecrement<T>(ReadOnlySpan<T> x, Span<T> destination)
private readonly struct BitDecrementOperator<T> : IUnaryOperator<T, T>
where T : IFloatingPointIeee754<T>
{
public static bool Vectorizable => false; // TODO: Vectorize
public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double);

public static T Invoke(T x) => T.BitDecrement(x);
public static Vector128<T> Invoke(Vector128<T> x) => throw new NotSupportedException();
public static Vector256<T> Invoke(Vector256<T> x) => throw new NotSupportedException();
public static Vector512<T> Invoke(Vector512<T> x) => throw new NotSupportedException();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<T> Invoke(Vector128<T> x)
{
if (typeof(T) == typeof(float))
{
Vector128<float> xFloat = x.AsSingle();
Vector128<uint> bits = xFloat.AsUInt32();

// General case: negative -> increment, positive -> decrement
Vector128<uint> result = Vector128.ConditionalSelect(
Vector128.IsNegative(xFloat).AsUInt32(),
bits + Vector128<uint>.One,
bits - Vector128<uint>.One);

// Handle special cases with a single conditional select
Vector128<uint> isPositiveZero = Vector128.IsZero(xFloat).AsUInt32();
Vector128<uint> specialValue = Vector128.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero;

Vector128<uint> isNaNOrNegInf = (Vector128.IsNaN(xFloat) | Vector128.IsNegativeInfinity(xFloat)).AsUInt32();
specialValue |= bits & isNaNOrNegInf;

Vector128<uint> specialMask = isPositiveZero | isNaNOrNegInf;
return Vector128.ConditionalSelect(specialMask, specialValue, result).AsSingle().As<float, T>();
}

if (typeof(T) == typeof(double))
{
Vector128<double> xDouble = x.AsDouble();
Vector128<ulong> bits = xDouble.AsUInt64();

// General case: negative -> increment, positive -> decrement
Vector128<ulong> result = Vector128.ConditionalSelect(
Vector128.IsNegative(xDouble).AsUInt64(),
bits + Vector128<ulong>.One,
bits - Vector128<ulong>.One);

// Handle special cases with a single conditional select
Vector128<ulong> isPositiveZero = Vector128.IsZero(xDouble).AsUInt64();
Vector128<ulong> specialValue = Vector128.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero;

Vector128<ulong> isNaNOrNegInf = (Vector128.IsNaN(xDouble) | Vector128.IsNegativeInfinity(xDouble)).AsUInt64();
specialValue |= bits & isNaNOrNegInf;

Vector128<ulong> specialMask = isPositiveZero | isNaNOrNegInf;
return Vector128.ConditionalSelect(specialMask, specialValue, result).AsDouble().As<double, T>();
}

// Fallback for unsupported types - should not be reached since Vectorizable returns false
throw new NotSupportedException();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<T> Invoke(Vector256<T> x)
{
if (typeof(T) == typeof(float))
{
Vector256<float> xFloat = x.AsSingle();
Vector256<uint> bits = xFloat.AsUInt32();

// General case: negative -> increment, positive -> decrement
Vector256<uint> result = Vector256.ConditionalSelect(
Vector256.IsNegative(xFloat).AsUInt32(),
bits + Vector256<uint>.One,
bits - Vector256<uint>.One);

// Handle special cases with a single conditional select
Vector256<uint> isPositiveZero = Vector256.IsZero(xFloat).AsUInt32();
Vector256<uint> specialValue = Vector256.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero;

Vector256<uint> isNaNOrNegInf = (Vector256.IsNaN(xFloat) | Vector256.IsNegativeInfinity(xFloat)).AsUInt32();
specialValue |= bits & isNaNOrNegInf;

Vector256<uint> specialMask = isPositiveZero | isNaNOrNegInf;
return Vector256.ConditionalSelect(specialMask, specialValue, result).AsSingle().As<float, T>();
}

if (typeof(T) == typeof(double))
{
Vector256<double> xDouble = x.AsDouble();
Vector256<ulong> bits = xDouble.AsUInt64();

// General case: negative -> increment, positive -> decrement
Vector256<ulong> result = Vector256.ConditionalSelect(
Vector256.IsNegative(xDouble).AsUInt64(),
bits + Vector256<ulong>.One,
bits - Vector256<ulong>.One);

// Handle special cases with a single conditional select
Vector256<ulong> isPositiveZero = Vector256.IsZero(xDouble).AsUInt64();
Vector256<ulong> specialValue = Vector256.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero;

Vector256<ulong> isNaNOrNegInf = (Vector256.IsNaN(xDouble) | Vector256.IsNegativeInfinity(xDouble)).AsUInt64();
specialValue |= bits & isNaNOrNegInf;

Vector256<ulong> specialMask = isPositiveZero | isNaNOrNegInf;
return Vector256.ConditionalSelect(specialMask, specialValue, result).AsDouble().As<double, T>();
}

// Fallback for unsupported types - should not be reached since Vectorizable returns false
throw new NotSupportedException();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<T> Invoke(Vector512<T> x)
{
if (typeof(T) == typeof(float))
{
Vector512<float> xFloat = x.AsSingle();
Vector512<uint> bits = xFloat.AsUInt32();

// General case: negative -> increment, positive -> decrement
Vector512<uint> result = Vector512.ConditionalSelect(
Vector512.IsNegative(xFloat).AsUInt32(),
bits + Vector512<uint>.One,
bits - Vector512<uint>.One);

// Handle special cases with a single conditional select
Vector512<uint> isPositiveZero = Vector512.IsZero(xFloat).AsUInt32();
Vector512<uint> specialValue = Vector512.Create(BitConverter.SingleToUInt32Bits(-float.Epsilon)) & isPositiveZero;

Vector512<uint> isNaNOrNegInf = (Vector512.IsNaN(xFloat) | Vector512.IsNegativeInfinity(xFloat)).AsUInt32();
specialValue |= bits & isNaNOrNegInf;

Vector512<uint> specialMask = isPositiveZero | isNaNOrNegInf;
return Vector512.ConditionalSelect(specialMask, specialValue, result).AsSingle().As<float, T>();
}

if (typeof(T) == typeof(double))
{
Vector512<double> xDouble = x.AsDouble();
Vector512<ulong> bits = xDouble.AsUInt64();

// General case: negative -> increment, positive -> decrement
Vector512<ulong> result = Vector512.ConditionalSelect(
Vector512.IsNegative(xDouble).AsUInt64(),
bits + Vector512<ulong>.One,
bits - Vector512<ulong>.One);

// Handle special cases with a single conditional select
Vector512<ulong> isPositiveZero = Vector512.IsZero(xDouble).AsUInt64();
Vector512<ulong> specialValue = Vector512.Create(BitConverter.DoubleToUInt64Bits(-double.Epsilon)) & isPositiveZero;

Vector512<ulong> isNaNOrNegInf = (Vector512.IsNaN(xDouble) | Vector512.IsNegativeInfinity(xDouble)).AsUInt64();
specialValue |= bits & isNaNOrNegInf;

Vector512<ulong> specialMask = isPositiveZero | isNaNOrNegInf;
return Vector512.ConditionalSelect(specialMask, specialValue, result).AsDouble().As<double, T>();
}

// Fallback for unsupported types - should not be reached since Vectorizable returns false
throw new NotSupportedException();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;

namespace System.Numerics.Tensors
Expand All @@ -26,11 +27,162 @@ public static void BitIncrement<T>(ReadOnlySpan<T> x, Span<T> destination)
private readonly struct BitIncrementOperator<T> : IUnaryOperator<T, T>
where T : IFloatingPointIeee754<T>
{
public static bool Vectorizable => false; // TODO: Vectorize
public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double);

public static T Invoke(T x) => T.BitIncrement(x);
public static Vector128<T> Invoke(Vector128<T> x) => throw new NotSupportedException();
public static Vector256<T> Invoke(Vector256<T> x) => throw new NotSupportedException();
public static Vector512<T> Invoke(Vector512<T> x) => throw new NotSupportedException();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<T> Invoke(Vector128<T> x)
{
if (typeof(T) == typeof(float))
{
Vector128<float> xFloat = x.AsSingle();
Vector128<uint> bits = xFloat.AsUInt32();

// General case: negative -> decrement, positive -> increment
Vector128<uint> result = Vector128.ConditionalSelect(
Vector128.IsNegative(xFloat).AsUInt32(),
bits - Vector128<uint>.One,
bits + Vector128<uint>.One);

// Handle special cases with a single conditional select
Vector128<uint> isNegativeZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000u));
Vector128<uint> specialValue = Vector128.Create(0x0000_0001u) & isNegativeZero;

Vector128<uint> isNaNOrPosInf = (Vector128.IsNaN(xFloat) | Vector128.IsPositiveInfinity(xFloat)).AsUInt32();
specialValue |= bits & isNaNOrPosInf;

Vector128<uint> specialMask = isNegativeZero | isNaNOrPosInf;
return Vector128.ConditionalSelect(specialMask, specialValue, result).AsSingle().As<float, T>();
}

if (typeof(T) == typeof(double))
{
Vector128<double> xDouble = x.AsDouble();
Vector128<ulong> bits = xDouble.AsUInt64();

// General case: negative -> decrement, positive -> increment
Vector128<ulong> result = Vector128.ConditionalSelect(
Vector128.IsNegative(xDouble).AsUInt64(),
bits - Vector128<ulong>.One,
bits + Vector128<ulong>.One);

// Handle special cases with a single conditional select
Vector128<ulong> isNegativeZero = Vector128.Equals(bits, Vector128.Create(0x8000_0000_0000_0000ul));
Vector128<ulong> specialValue = Vector128.Create(0x0000_0000_0000_0001ul) & isNegativeZero;

Vector128<ulong> isNaNOrPosInf = (Vector128.IsNaN(xDouble) | Vector128.IsPositiveInfinity(xDouble)).AsUInt64();
specialValue |= bits & isNaNOrPosInf;

Vector128<ulong> specialMask = isNegativeZero | isNaNOrPosInf;
return Vector128.ConditionalSelect(specialMask, specialValue, result).AsDouble().As<double, T>();
}

// Fallback for unsupported types - should not be reached since Vectorizable returns false
throw new NotSupportedException();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<T> Invoke(Vector256<T> x)
{
if (typeof(T) == typeof(float))
{
Vector256<float> xFloat = x.AsSingle();
Vector256<uint> bits = xFloat.AsUInt32();

// General case: negative -> decrement, positive -> increment
Vector256<uint> result = Vector256.ConditionalSelect(
Vector256.IsNegative(xFloat).AsUInt32(),
bits - Vector256<uint>.One,
bits + Vector256<uint>.One);

// Handle special cases with a single conditional select
Vector256<uint> isNegativeZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000u));
Vector256<uint> specialValue = Vector256.Create(0x0000_0001u) & isNegativeZero;

Vector256<uint> isNaNOrPosInf = (Vector256.IsNaN(xFloat) | Vector256.IsPositiveInfinity(xFloat)).AsUInt32();
specialValue |= bits & isNaNOrPosInf;

Vector256<uint> specialMask = isNegativeZero | isNaNOrPosInf;
return Vector256.ConditionalSelect(specialMask, specialValue, result).AsSingle().As<float, T>();
}

if (typeof(T) == typeof(double))
{
Vector256<double> xDouble = x.AsDouble();
Vector256<ulong> bits = xDouble.AsUInt64();

// General case: negative -> decrement, positive -> increment
Vector256<ulong> result = Vector256.ConditionalSelect(
Vector256.IsNegative(xDouble).AsUInt64(),
bits - Vector256<ulong>.One,
bits + Vector256<ulong>.One);

// Handle special cases with a single conditional select
Vector256<ulong> isNegativeZero = Vector256.Equals(bits, Vector256.Create(0x8000_0000_0000_0000ul));
Vector256<ulong> specialValue = Vector256.Create(0x0000_0000_0000_0001ul) & isNegativeZero;

Vector256<ulong> isNaNOrPosInf = (Vector256.IsNaN(xDouble) | Vector256.IsPositiveInfinity(xDouble)).AsUInt64();
specialValue |= bits & isNaNOrPosInf;

Vector256<ulong> specialMask = isNegativeZero | isNaNOrPosInf;
return Vector256.ConditionalSelect(specialMask, specialValue, result).AsDouble().As<double, T>();
}

// Fallback for unsupported types - should not be reached since Vectorizable returns false
throw new NotSupportedException();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<T> Invoke(Vector512<T> x)
{
if (typeof(T) == typeof(float))
{
Vector512<float> xFloat = x.AsSingle();
Vector512<uint> bits = xFloat.AsUInt32();

// General case: negative -> decrement, positive -> increment
Vector512<uint> result = Vector512.ConditionalSelect(
Vector512.IsNegative(xFloat).AsUInt32(),
bits - Vector512<uint>.One,
bits + Vector512<uint>.One);

// Handle special cases with a single conditional select
Vector512<uint> isNegativeZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000u));
Vector512<uint> specialValue = Vector512.Create(0x0000_0001u) & isNegativeZero;

Vector512<uint> isNaNOrPosInf = (Vector512.IsNaN(xFloat) | Vector512.IsPositiveInfinity(xFloat)).AsUInt32();
specialValue |= bits & isNaNOrPosInf;

Vector512<uint> specialMask = isNegativeZero | isNaNOrPosInf;
return Vector512.ConditionalSelect(specialMask, specialValue, result).AsSingle().As<float, T>();
}

if (typeof(T) == typeof(double))
{
Vector512<double> xDouble = x.AsDouble();
Vector512<ulong> bits = xDouble.AsUInt64();

// General case: negative -> decrement, positive -> increment
Vector512<ulong> result = Vector512.ConditionalSelect(
Vector512.IsNegative(xDouble).AsUInt64(),
bits - Vector512<ulong>.One,
bits + Vector512<ulong>.One);

// Handle special cases with a single conditional select
Vector512<ulong> isNegativeZero = Vector512.Equals(bits, Vector512.Create(0x8000_0000_0000_0000ul));
Vector512<ulong> specialValue = Vector512.Create(0x0000_0000_0000_0001ul) & isNegativeZero;

Vector512<ulong> isNaNOrPosInf = (Vector512.IsNaN(xDouble) | Vector512.IsPositiveInfinity(xDouble)).AsUInt64();
specialValue |= bits & isNaNOrPosInf;

Vector512<ulong> specialMask = isNegativeZero | isNaNOrPosInf;
return Vector512.ConditionalSelect(specialMask, specialValue, result).AsDouble().As<double, T>();
}

// Fallback for unsupported types - should not be reached since Vectorizable returns false
throw new NotSupportedException();
}
}
}
}