On Tue, 4 Aug 2026 07:22:19 GMT, Eric Fang <[email protected]> wrote:
>> This PR introduces the basic Ideal/Identity transformations for
>> `VectorBlendNode`.
>>
>> The semantic of `VectorBlend(X, Y, M)` is: `M ? Y : X`.
>>
>> **Identity**:
>>
>> (VectorBlend X Y (Replicate -1)) => Y
>> (VectorBlend X Y (MaskAll -1)) => Y
>> (VectorBlend X Y (Replicate 0)) => X
>> (VectorBlend X Y (MaskAll 0)) => X
>>
>>
>> **Ideal**:
>>
>> (VectorBlend (VectorBlend X A M) B M) => (VectorBlend X B M)
>> (VectorBlend A (VectorBlend B X M) M) => (VectorBlend A X M)
>> (VectorBlend A B (XorV/XorVMask M -1)) => (VectorBlend B A M)
>>
>>
>> Also corrects the VectorBlendNode header comment: across all backends (X86
>> SSE/AVX, AArch64 NEON/SVE, RISC-V V) the active mask lane selects `vec2`
>> (in(2)), and the inactive lane selects `vec1` (in(1)).
>>
>> JTReg and JMH tests are also added for each optimization pattern. All tests
>> (tier1, tier2, and tier3) passed on AArch64 and X86 platforms.
>>
>> JMH benchmark test results:
>>
>> On a Nvidia Grace (Neoverse-V2) machine with 128-bit SVE2:
>>
>> Benchmark Unit Before Error After Error
>> Uplift
>> blendNegatedMaskInt ops/ms 7990.6 2.8 10215.2
>> 11.0 1.3
>> identityAllOnesInt ops/ms 3574.8 2.6 7967.1
>> 0.3 2.2
>> identityAllZerosLong ops/ms 3575.6 1.0 7966.0 3.6
>> 2.2
>> nestedBlendInnerLong ops/ms 3533.8 2.8 478573.0 3178.5 135.4
>> nestedBlendOuterInt ops/ms 3537.6 3.4 472242.2 3034.2
>> 133.5
>>
>>
>> On an AWS Graviton3 (Neoverse-V1) machine with 256-bit SVE1:
>>
>> Benchmark Unit Before Error After Error
>> Uplift
>> blendNegatedMaskInt ops/ms 5171.9 5.2 8129.0
>> 17.3 1.6
>> identityAllOnesInt ops/ms 2722.0 0.1 5891.3
>> 0.1 2.2
>> identityAllZerosLong ops/ms 2722.4 0.1 5891.1 0.3
>> 2.2
>> nestedBlendInnerLong ops/ms 2697.6 0.0 312148.7 2366.4 115.7
>> nestedBlendOuterInt ops/ms 2702.7 0.1 308686.0 2709.8
>> 114.2
>>
>>
>> On a Nvidia Grace (Neoverse-V2) machine with `-XX:UseSVE=0`:
>>
>> Benchmark Unit Before Error After Error
>> Uplift
>> blendNegatedMaskInt ops/ms 7718.1 1.9 9515.9
>> 54.0 1.2
>> identityAllOnesInt ops/ms 3581.9 0.6 8062.5
>> 0.5 2.3
>> identityAllZerosLong ops/ms 3582.7 0.6 8058.5 11.9
>> 2.2
>> nestedBlendInnerLong ops/ms 3529.6 1.4 476029.8 5190.2 134.9
>> nestedBlendOuterInt ops/ms 3536.9 2.1 486060.0 3442.1
>> 137.4
>>
>>
>> On an AMD EPYC 9124 16-Core Processor with option `-XX:UseAVX=3`:
>>
>> Benchmark Unit Before Error After Error
>> Uplift
>> blendNegatedMaskInt ops/ms...
>
> Eric Fang has updated the pull request with a new target base due to a merge
> or a rebase. The incremental webrev excludes the unrelated changes brought in
> by the merge/rebase. The pull request contains four additional commits since
> the last revision:
>
> - Merge branch 'master' into JDK-8384571-vector-blend-opt-pr1
> - Add JMH and JTReg tests for each optimization pattern and each type
> - Merge branch 'master' into JDK-8384571-vector-blend-opt-pr1
> - 8384571: C2: Add some basic IGVN optimization for VectorBlendNode
>
> This PR introduces the basic Ideal/Identity transformations for
> `VectorBlendNode`.
>
> The semantic of `VectorBlend(X, Y, M)` is: `M ? Y : X`.
>
> **Identity**:
> ```
> (VectorBlend X Y (Replicate -1)) => Y
> (VectorBlend X Y (MaskAll -1)) => Y
> (VectorBlend X Y (Replicate 0)) => X
> (VectorBlend X Y (MaskAll 0)) => X
> ```
>
> **Ideal**:
> ```
> (VectorBlend (VectorBlend X A M) B M) => (VectorBlend X B M)
> (VectorBlend A (VectorBlend B X M) M) => (VectorBlend A X M)
> (VectorBlend A B (XorV/XorVMask M -1)) => (VectorBlend B A M)
> ```
>
> Also corrects the VectorBlendNode header comment: across all backends
> (X86 SSE/AVX, AArch64 NEON/SVE, RISC-V V) the active mask lane selects
> `vec2` (in(2)), and the inactive lane selects `vec1` (in(1)).
>
> JTReg and JMH tests are also added for each optimization pattern. All
> tests (tier1, tier2, and tier3) passed on AArch64 and X86 platforms.
>
> JMH benchmark test results:
>
> On a Nvidia Grace (Neoverse-V2) machine with 128-bit SVE2:
> ```
> Benchmark Unit Before Error After Error Uplift
> blendNegatedMaskInt ops/ms 7990.6 2.8 10215.2 11.0
> 1.3
> identityAllOnesInt ops/ms 3574.8 2.6 7967.1 0.3 2.2
> identityAllZerosLong ops/ms 3575.6 1.0 7966.0 3.6
> 2.2
> nestedBlendInnerLong ops/ms 3533.8 2.8 478573.0 3178.5
> 135.4
> nestedBlendOuterInt ops/ms 3537.6 3.4 472242.2 3034.2
> 133.5
> ```
>
> On an AWS Graviton3 (Neoverse-V1) machine with 256-bit SVE1:
> ```
> Benchmark Unit Before Error After Error Uplift
> blendNegatedMaskInt ops/ms 5171.9 5.2 8129.0 17.3
> 1.6
> identityAllOnesInt ops/ms 2722.0 0.1 5891.3 0.1 2.2
> identityAllZerosLong ops/ms 2722.4 0.1 5891.1 0.3
> 2.2
> nestedBlendInnerLong ops/ms 2697.6 0.0 312148.7 2366.4
> 115.7
> nestedBlendOuterInt ops/ms 2702.7 0.1 308686.0 2709.8
> 114.2
> ```
>
> On a Nvidia Grace (Neoverse-V2) machine with `-XX:UseSVE=0`:
> ```
> Benchmark Unit Before Error After Error Upl...
Nice work, Eric.
src/hotspot/share/opto/vectornode.cpp line 2919:
> 2917:
> 2918: // (VectorBlend A B (XorV/XorVMask M -1)) => (VectorBlend B A M)
> 2919: Node* uncasted_mask = uncast_mask(mask);
A simpler alternative is to detect negated mask shape and swap arguments along
with double negating the mask:
VectorBlend A B (NotV M) ==> VectorBlend B A (NotV (NotV M)) ==> VectorBlend B
A M
src/hotspot/share/opto/vectornode.cpp line 2949:
> 2947: // (VectorBlend X Y (Replicate -1)) => Y
> 2948: // (VectorBlend X Y (MaskAll -1)) => Y
> 2949: if (VectorNode::is_all_ones_vector(uncast_mask(in(3)))) {
I suggest to either enhance `VectorNode::is_all_ones_vector` to cover vector
masks or introduce a helper method. Explicit vector mask uncasting
(`uncast_mask()`) is error-prone and harder to read.
-------------
PR Review: https://git.openjdk.org/jdk/pull/31333#pullrequestreview-5251119578
PR Review Comment: https://git.openjdk.org/jdk/pull/31333#discussion_r4049453604
PR Review Comment: https://git.openjdk.org/jdk/pull/31333#discussion_r4049483189