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
