ARM64-SVE: `TestFirstTrue()` can be optimised away when using `CreateTrueMask()`
@a74nh is already working on this.
Since May 22, 2025.
Assessment
This issue has not been assessed yet.
Description
Consider the C++ code:
int16_t mutiplyadd(int16_t *a, int16_t *b, int length)
{
svint16_t res_vec = svdup_s16(0);
svbool_t ploop;
for (int i = 0; svptest_first(svptrue_b16(), ploop = svwhilelt_b16(i, length)); i+= (int)svcnth())
{
svint16_t a_vec = svld1(ploop, a + i);
svint16_t b_vec = svld1(ploop, b + i);
res_vec = svmla_m(ploop, res_vec, a_vec, b_vec);
}
return svaddv(svptrue_b16(), res_vec);
}
Compiled with gcc -O3, this gives:
0000000000000000 <_Z10mutiplyaddPsS_i>:
0: 256207e2 whilelt p2.h, wzr, w2
4: 2558e3e1 ptrue p1.h
8: 25824840 mov p0.b, p2.b
c: 2550c440 ptest p1, p2.b
10: 54000265 b.pl 5c <_Z10mutiplyaddPsS_i+0x5c> // b.nfrst
14: 0460e3e5 cnth x5
18: 52800003 mov w3, #0x0 // #0
1c: 937f7ca4 sbfiz x4, x5, #1, #32
20: 2538c000 mov z0.b, #0
24: d503201f nop
28: a4a0a002 ld1h {z2.h}, p0/z, [x0]
2c: a4a0a021 ld1h {z1.h}, p0/z, [x1]
30: 0b050063 add w3, w3, w5
34: 04414040 mla z0.h, p0/m, z2.h, z1.h
38: 25620461 whilelt p1.h, w3, w2
3c: 8b040000 add x0, x0, x4
40: 8b040021 add x1, x1, x4
44: 25814420 mov p0.b, p1.b
48: 54ffff04 b.mi 28 <_Z10mutiplyaddPsS_i+0x28> // b.first
4c: 2518e3e0 ptrue p0.b
50: 04402000 saddv d0, p0, z0.h
54: 9e660000 fmov x0, d0
58: d65f03c0 ret
5c: 2518e3e0 ptrue p0.b
60: 2538c000 mov z0.b, #0
64: 04402000 saddv d0, p0, z0.h
68: 9e660000 fmov x0, d0
6c: d65f03c0 ret
Note the C++ has a pfirst instruction but it is not in the assembly. It has been optimised away. whilelt sets the flags and then there is a branch via b.mi (same as b.first).
Consider the same in C#:
public static unsafe long mutiplyadd(ref ushort* a, ref ushort* b, int length)
{
Vector<ushort> res = Vector<ushort>.Zero;
Vector<ushort> ploop;
for (int i = 0; Sve.TestFirstTrue(Sve.CreateTrueMaskInt16(), ploop = (Vector<ushort>)Sve.CreateWhileLessThanMask16Bit(i, length)); i+= (int)Sve.Count16BitElements())
{
Vector<ushort> a_vec = Sve.LoadVector((Vector<ushort>)ploop, a+i);
Vector<ushort> b_vec = Sve.LoadVector((Vector<ushort>)ploop, b+i);
res = Sve.ConditionalSelect((Vector<ushort>)ploop, Sve.MultiplyAdd(res, a_vec, b_vec), res);
}
return Sve.AddAcross(res).ToScalar();
}
G_M65150_IG01: ;; offset=0x0000
stp fp, lr, [sp, #-0x10]!
mov fp, sp
;; size=8 bbWeight=1 PerfScore 1.50
G_M65150_IG02: ;; offset=0x0008
movi v16.4s, #0
mov w3, wzr
mov w4, wzr
whilelt p0.h, w4, w2
mov z17.h, p0/z, #1
ptrue p0.h
ptrue p1.h
cmpne p1.h, p1/z, z17.h, #0
ptest p0, p1.b
bge G_M65150_IG05
;; size=40 bbWeight=1 PerfScore 15.50
G_M65150_IG03: ;; offset=0x0030
ldr x0, [x0]
cnth x4, all
align [4 bytes for IG04]
align [4 bytes]
align [0 bytes]
align [0 bytes]
;; size=16 bbWeight=0.25 PerfScore 1.50
G_M65150_IG04: ;; offset=0x0040
ptrue p1.h
cmpne p1.h, p1/z, z17.h, #0
sbfiz x5, x3, #1, #32
add x6, x0, x5
ld1h { z17.h }, p1/z, [x6]
ldr x6, [x1]
add x5, x6, x5
ld1h { z18.h }, p1/z, [x5]
mla z16.h, p1/m, z17.h, z18.h
add w3, w3, w4
whilelt p1.h, w3, w2
mov z17.h, p1/z, #1
ptrue p1.h
cmpne p1.h, p1/z, z17.h, #0
ptest p0, p1.b
blt G_M65150_IG04
;; size=64 bbWeight=4 PerfScore 170.00
G_M65150_IG05: ;; offset=0x0080
ptrue p0.h
saddv d16, p0, z16.h
umov x0, v16.d[0]
;; size=12 bbWeight=1 PerfScore 6.00
G_M65150_IG06: ;; offset=0x008C
ldp fp, lr, [sp], #0x10
ret lr
;; size=8 bbWeight=1 PerfScore 2.00
As expected there is a pfirst instruction. This could be folded into the whilelt the same as C++.
(Issues to fix the extra mask conversions and the load addressing modes have already been raised.)
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/runtime
-
agentic-workflows untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
area-System.Reflection blocking-clean-ci-optional Known Build Error os-mac-os-x untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
area-CodeGen-coreclr untriaged
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
agentic-workflows untriaged
Difficulty 1/5 Under an hour Newbie friendliness 78/100
-
area-VM-meta-mono untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
:watch: Not Triaged 11.0 fundamentals/subsvc
Difficulty 2/5 1-3 hours Newbie friendliness 92/100
dotnet/AspNetCore.Docs#37699 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SubtitleEdit/subtitleedit#15108 · 1 comment ·
-
area/docs-content Bug pulumi/docs
Difficulty 1/5 1-3 hours Newbie friendliness 94/100
-
Create parent directories only after the containment check in InstallHelper.TryExtractToDirectory Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
PowerShell/PSResourceGet#2056 ·