VectorMask.lastTrue() returns the index of the last (highest-numbered) lane 
that is set in the mask, or -1 if no lane is set.

Currently on AArch64 with SVE enabled, this API is implemented on a predicate 
register via the sequence `rev + brkb + cntp + movw + subw`. There is no native 
SVE predicate instruction for "last active lane index". This implementation 
doesn't perform very well because it heavily relies on the M pipeline, and the 
dependency chain between instructions is too long.

There is a SVE `lastb` instruction [1] that can extract the last active element 
from a vector register, and we can use this instruction to implement this API. 
As follow:

// vtmp = 0, 1, 2, ...
sve_index(vtmp, size, 0, 1);
// dst = last true or the highest-numbered element if src is all false
sve_lastb(dst, size, src, vtmp);
// Zero flag = 1 iff no active lane
sve_ptest(ptrue, src);
// active: keep; else -1
csinvw(dst, dst, zr, Assembler::NE);

This implementation is more evenly distributed across the different pipelines 
and exhibits better parallelism. Thus has better performance.

On a Nvidia Grace (Neoverse-V2) machine with 128-bit SVE2:

Benchmark               inputs  Unit    Before          Error   After           
Error   Uplift
testLastTrueByte        1       ops/ms  1047092.6       33773.0 1132866.9       
29053.6 1.08
testLastTrueByte        2       ops/ms  1032945.0       18573.7 1152164.6       
21749.0 1.12
testLastTrueByte        3       ops/ms  1032476.9       25744.7 1136212.3       
33285.8 1.10
testLastTrueDouble      1       ops/ms  987207.0        6460.4  1126175.6       
5973.0  1.14
testLastTrueDouble      2       ops/ms  987724.9        5854.1  1126033.5       
3558.7  1.14
testLastTrueDouble      3       ops/ms  985976.2        3525.5  1128522.9       
2284.2  1.14
testLastTrueFloat       1       ops/ms  978225.9        12308.3 1129334.8       
7655.7  1.15
testLastTrueFloat       2       ops/ms  980388.5        15776.9 1119524.3       
25849.4 1.14
testLastTrueFloat       3       ops/ms  988192.5        3414.6  1129177.4       
7393.8  1.14
testLastTrueInt         1       ops/ms  979679.8        9878.7  1125093.3       
7802.7  1.15
testLastTrueInt         2       ops/ms  987964.7        3320.0  1116457.0       
12490.9 1.13
testLastTrueInt         3       ops/ms  982718.4        6455.4  1131456.3       
8357.6  1.15
testLastTrueLong        1       ops/ms  1009693.7       3160.9  1113229.0       
18412.3 1.10
testLastTrueLong        2       ops/ms  1003385.8       6314.5  1126162.2       
6872.6  1.12
testLastTrueLong        3       ops/ms  998882.0        10596.3 1126631.1       
2575.0  1.13
testLastTrueShort       1       ops/ms  981657.4        16531.1 1110119.0       
22422.8 1.13
testLastTrueShort       2       ops/ms  989732.1        5418.9  1120666.7       
14837.0 1.13
testLastTrueShort       3       ops/ms  993479.7        20890.1 1113419.3       
9783.8  1.12


On an AWS Graviton3 (Neoverse-V1) machine with 256-bit SVE1:

Benchmark             bits inputs Unit  Before          Error   After           
Error   Uplift
testLastTrueByte        128     1       ops/ms  445974.1        1525.2  
647775.8        13.1    1.45
testLastTrueByte        128     2       ops/ms  443361.3        2249.4  
647751.6        34.2    1.46
testLastTrueByte        128     3       ops/ms  445180.4        2419.5  
647798.5        67.1    1.46
testLastTrueByte        256     1       ops/ms  430972.7        2091.7  
709610.2        3545.5  1.65
testLastTrueByte        256     2       ops/ms  434646.2        4654.8  
703740.4        8657.4  1.62
testLastTrueByte        256     3       ops/ms  433378.6        1629.0  
699756.6        8893.5  1.61
testLastTrueDouble      128     1       ops/ms  431387.5        2016.3  
684815.0        570.4   1.59
testLastTrueDouble      128     2       ops/ms  430707.8        1670.4  
684788.3        835.0   1.59
testLastTrueDouble      128     3       ops/ms  431187.9        1737.9  
684886.4        1032.1  1.59
testLastTrueDouble      256     1       ops/ms  428769.1        3339.2  
685239.2        1258.6  1.60
testLastTrueDouble      256     2       ops/ms  431413.0        1205.8  
684380.6        521.3   1.59
testLastTrueDouble      256     3       ops/ms  432058.8        741.4   
685003.7        763.4   1.59
testLastTrueFloat       128     1       ops/ms  431664.6        2875.1  
715284.2        1210.6  1.66
testLastTrueFloat       128     2       ops/ms  431359.6        1871.1  
712020.0        5298.5  1.65
testLastTrueFloat       128     3       ops/ms  430751.3        1616.7  
712175.1        4505.1  1.65
testLastTrueFloat       256     1       ops/ms  431454.4        1973.3  
714586.1        1183.1  1.66
testLastTrueFloat       256     2       ops/ms  430352.6        2919.5  
712560.2        4639.9  1.66
testLastTrueFloat       256     3       ops/ms  428985.3        1762.1  
709483.1        5855.8  1.65
testLastTrueInt         128     1       ops/ms  429454.2        3556.6  
713159.9        3430.3  1.66
testLastTrueInt         128     2       ops/ms  433534.6        4908.3  
712899.1        4633.5  1.64
testLastTrueInt         128     3       ops/ms  430257.9        4886.4  
712564.1        4133.5  1.66
testLastTrueInt         256     1       ops/ms  432123.2        1580.9  
714012.6        1416.8  1.65
testLastTrueInt         256     2       ops/ms  427013.2        3038.4  
708945.6        5333.5  1.66
testLastTrueInt         256     3       ops/ms  430151.5        1701.9  
714170.5        1453.8  1.66
testLastTrueLong        128     1       ops/ms  429703.3        4660.0  
684914.0        75.2    1.59
testLastTrueLong        128     2       ops/ms  431717.5        141.9   
685667.9        1165.0  1.59
testLastTrueLong        128     3       ops/ms  429200.2        3863.7  
686609.5        3335.1  1.60
testLastTrueLong        256     1       ops/ms  429981.8        4764.9  
685534.2        835.2   1.59
testLastTrueLong        256     2       ops/ms  432154.7        857.5   
685474.0        1072.2  1.59
testLastTrueLong        256     3       ops/ms  429434.8        4160.6  
684560.6        1190.1  1.59
testLastTrueShort       128     1       ops/ms  429497.6        3419.0  
711893.5        5202.0  1.66
testLastTrueShort       128     2       ops/ms  429492.9        1844.0  
715391.7        1220.4  1.67
testLastTrueShort       128     3       ops/ms  430892.4        2249.5  
714658.7        1342.4  1.66
testLastTrueShort       256     1       ops/ms  430080.9        4055.1  
713465.0        4053.0  1.66
testLastTrueShort       256     2       ops/ms  432013.2        2702.1  
714416.3        1344.3  1.65
testLastTrueShort       256     3       ops/ms  430957.7        1902.1  
711586.9        5171.2  1.65


The PR was tested on AArch64 (with SVE2, SVE1 and NEON) and X86 (AVX3, AVX2) 
platforms, all tests (tier1, tier2 and tier3) passed.

[1] 
https://developer.arm.com/documentation/ddi0602/2025-12/SVE-Instructions/LASTB--scalar---Extract-last-element-to-general-purpose-register-?lang=en




---------
- [x] I confirm that I make this contribution in accordance with the [OpenJDK 
Interim AI Policy](https://openjdk.org/legal/ai).

-------------

Commit messages:
 - 8387748: VectorAPI: AArch64: Improve VectorMask.lastTrue() SVE implementation

Changes: https://git.openjdk.org/jdk/pull/32094/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=32094&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8387748
  Stats: 37 lines in 5 files changed: 21 ins; 0 del; 16 mod
  Patch: https://git.openjdk.org/jdk/pull/32094.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/32094/head:pull/32094

PR: https://git.openjdk.org/jdk/pull/32094

Reply via email to