Skip to content

[API Proposal]: An official way to determine whether the given value is JIT-time constant or not #98153

@MineCake147E

Description

@MineCake147E

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions