Background and motivation
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool IsConstant(int value)
=> Vector128.CreateScalarUnsafe(value - ~value).GetElement(1) != 0;
L0000: vzeroupper
L0003: mov eax, ecx
L0005: not eax
L0007: sub ecx, eax
L0009: vmovd xmm0, ecx
L000d: vpextrd eax, xmm0, 1
L0013: test eax, eax
L0015: setne al
L0018: movzx eax, al
L001b: ret
The code above currently allows me to guess whether the value is a JIT-time constant in x86-64 environments.
The Vector128.CreateScalarUnsafe(value) usually does the same thing with Vector128.CreateScalar(value) (which is vmovd xmm*, r*d) which clears all upper elements with 0, but it does Vector128.Create(value) which broadcasts the value, when the value is a constant.
The value - ~value is guaranteed to be nonzero, as LLVM says, and it obviously differs by value.
By checking whether the second element is nonzero or not, it returns true when the value is a constant, false otherwise.
Inlining the IsConstant(constant) simply boils down to mov eax, 1 as shown here.
Such behavior is really useful when I deal with some SIMD optimization, as some instructions only take constant value.
It also helps me to replicate something like what System.Buffer.Memmove does for small constant lengths, without modifying the CoreCLR.
But it's an undefined behavior, and I'm not sure if it works with ARM-based processors, so I propose the APIs below for official support.
API Proposal
namespace System.Runtime.CompilerServices;
public static class Unsafe
{
public static bool IsConstant<T>(T value) where T : unmanaged;
}
API Usage
// The method takes `[ConstantExpected] int index` and `Vector512<ushort> zmm2` as parameter
if (Unsafe.IsConstant(index))
{
// some processing that needs the index to be a constant value
return zmm2.GetElement(index);
}
// some processing that don't need the index to be a constant value
return Avx512BW.PermuteVar32x16(zmm2, Vector512.CreateScalarUnsafe((ushort)index)).GetElement(0);
Alternative Designs
- There may be a better place for
IsConstant to be.
- There may be a better name for
IsConstant.
Risks
None
Background and motivation
The code above currently allows me to guess whether the
valueis a JIT-time constant in x86-64 environments.The
Vector128.CreateScalarUnsafe(value)usually does the same thing withVector128.CreateScalar(value)(which isvmovd xmm*, r*d) which clears all upper elements with 0, but it doesVector128.Create(value)which broadcasts thevalue, when thevalueis a constant.The
value - ~valueis guaranteed to be nonzero, as LLVM says, and it obviously differs byvalue.By checking whether the second element is nonzero or not, it returns
truewhen thevalueis a constant,falseotherwise.Inlining the
IsConstant(constant)simply boils down tomov eax, 1as shown here.Such behavior is really useful when I deal with some SIMD optimization, as some instructions only take constant value.
It also helps me to replicate something like what System.Buffer.Memmove does for small constant lengths, without modifying the CoreCLR.
But it's an undefined behavior, and I'm not sure if it works with ARM-based processors, so I propose the APIs below for official support.
API Proposal
API Usage
Alternative Designs
IsConstantto be.IsConstant.Risks
None