Skip to content

Commit 605f0e1

Browse files
committed
arm64: fast-forward loop for runs of non-interleaving blocks
When the disjoint check passes, stay in a small loop that selects blocks with a predicted branch instead of returning to the main loop's CSEL select. On run-structured data the branch predicts and the untaken cursor stays off the loop-carried dependency chain; the first interleaving block merges and rejoins the main loop. Run-structured unions improve about 2.5x over the base kernel; random data never enters the loop.
1 parent 4d673e0 commit 605f0e1

1 file changed

Lines changed: 44 additions & 14 deletions

File tree

setutil_neon_arm64.s

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@
3737
VZIP1 V5.H8, V4.H8, V2.H8 \
3838
VZIP2 V5.H8, V4.H8, V0.H8
3939

40-
// Store the lanes of V2 that differ from their predecessor (previous lane,
40+
// Store the lanes of Vin that differ from their predecessor (previous lane,
4141
// or last lane of V1 for lane 0) at (Rout), advancing Rout by 2 bytes per
4242
// lane kept. Writes a full 16 bytes; the caller guarantees slack.
4343
// Rcnt receives the number of lanes kept. Clobbers V3-V5, R14, R15, R16.
44-
#define STOREUNIQ(Rout, Rcnt) \
45-
VEXT $14, V2.B16, V1.B16, V3.B16 \
46-
VCMEQ V3.H8, V2.H8, V4.H8 \
44+
#define STOREUNIQ(Vin, Rout, Rcnt) \
45+
VEXT $14, Vin.B16, V1.B16, V3.B16 \
46+
VCMEQ V3.H8, Vin.H8, V4.H8 \
4747
VUZP1 V4.B16, V4.B16, V4.B16 \
4848
VMOV V4.D[0], R14 \
4949
AND R12, R14, R14 \
@@ -54,7 +54,7 @@
5454
SUB R14, R19, Rcnt \
5555
ADD R15<<4, R6, R16 \
5656
VLD1 (R16), [V5.B16] \
57-
VTBL V5.B16, [V2.B16], V3.B16 \
57+
VTBL V5.B16, [Vin.B16], V3.B16 \
5858
VST1 [V3.B16], (Rout) \
5959
ADD Rcnt<<1, Rout, Rout
6060

@@ -86,7 +86,7 @@ TEXT ·unionKernelNEON(SB), NOSPLIT, $0-120
8686
MERGE
8787
// laststore = all ones (never equal to a first stored lane)
8888
VCMEQ V0.H8, V0.H8, V1.H8
89-
STOREUNIQ(R2, R17)
89+
STOREUNIQ(V2, R2, R17)
9090
VORR V2.B16, V2.B16, V1.B16
9191

9292
loop:
@@ -111,22 +111,52 @@ loop:
111111
CMP R21, R20
112112
BHS disjoint
113113
MERGE
114-
STOREUNIQ(R2, R17)
114+
STOREUNIQ(V2, R2, R17)
115115
VORR V2.B16, V2.B16, V1.B16
116116
B loop
117117

118+
// Fast-forward loop for consecutive disjoint blocks. The head select is a
119+
// plain branch: on run-structured data it predicts, keeping the untaken
120+
// cursor off the loop-carried chain that the main loop's CSEL select
121+
// serializes. Interleaving input rejoins the main loop after one merge.
118122
disjoint:
119-
VORR V2.B16, V2.B16, V22.B16 // stash fresh
120-
VORR V0.B16, V0.B16, V2.B16 // emit old carry as this round's minimum
121-
STOREUNIQ(R2, R17)
122-
VORR V2.B16, V2.B16, V1.B16 // laststore = old carry
123-
VORR V22.B16, V22.B16, V0.B16 // carry = fresh
123+
STOREUNIQ(V0, R2, R17)
124+
VORR V0.B16, V0.B16, V1.B16 // laststore = old carry
125+
VORR V2.B16, V2.B16, V0.B16 // carry = fresh
126+
127+
ffloop:
128+
CMP R3, R0
129+
BHS done
130+
CMP R4, R1
131+
BHS done
132+
MOVHU (R0), R8
133+
MOVHU (R1), R9
134+
CMP R9, R8
135+
BHI fftake2
136+
VLD1.P 16(R0), [V2.H8] // take set1's block on tie/less
137+
MOVD R8, R20
138+
B ffcheck
139+
fftake2:
140+
VLD1.P 16(R1), [V2.H8]
141+
MOVD R9, R20
142+
ffcheck:
143+
VMOV V0.H[7], R21
144+
CMP R21, R20
145+
BLO ffmerge
146+
STOREUNIQ(V0, R2, R17)
147+
VORR V0.B16, V0.B16, V1.B16 // laststore = old carry
148+
VORR V2.B16, V2.B16, V0.B16 // carry = fresh
149+
B ffloop
150+
151+
ffmerge:
152+
MERGE
153+
STOREUNIQ(V2, R2, R17)
154+
VORR V2.B16, V2.B16, V1.B16
124155
B loop
125156

126157
done:
127158
// flush carry through the dedup into the leftover buffer
128-
VORR V0.B16, V0.B16, V2.B16
129-
STOREUNIQ(R7, R17)
159+
STOREUNIQ(V0, R7, R17)
130160
MOVD R17, leftoverLen+112(FP)
131161

132162
// outLen = (out - outBase) / 2

0 commit comments

Comments
 (0)