Skip to content

Commit 6b2dd36

Browse files
authored
mlkem: optimize ringCompressAndEncode1NEON #479
1 parent da699a3 commit 6b2dd36

1 file changed

Lines changed: 64 additions & 38 deletions

File tree

mlkem/field_arm64.s

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,20 +2148,19 @@ compress_encode11_neon_loop:
21482148
// For each coefficient x in [0, q):
21492149
// compress(x, 1) = 1 if 833 <= x <= 2496, else 0.
21502150
//
2151-
// Vector strategy (8 coefficients -> 1 byte per iteration):
2152-
// 1) Load 8 x uint16 coefficients into V0.H8.
2153-
// 2) CMHS twice (cmgt_u_eq_8h_opcode = unsigned >=) to identify compress=1 range.
2154-
// Lower: V0 >= 833 -> compress=1 candidate.
2155-
// Upper: V0 >= 2497 -> compress=0 (excluded).
2156-
// 3) BIC to combine: 0xFFFF where 833 <= x <= 2496.
2157-
// 4) VUSHR $15 to reduce each lane to 0x0001 or 0x0000.
2151+
// Vector strategy (32 coefficients -> 4 bytes per iteration, 8 iterations total):
2152+
// 1) Load 4 x 8 coefficients into V0-V3.H8 (64 bytes).
2153+
// 2) CMHS twice per register (cmgt_u_eq_8h_opcode = unsigned >=) to identify range.
2154+
// 3) BIC per register: V20..V23 = 0xFFFF where 833 <= x <= 2496.
2155+
// 4) VUSHR $15: convert to 0x0001/0x0000 per lane.
21582156
// 5) MUL by bit-position weights {1,2,4,8,16,32,64,128}.
2159-
// 6) ADDV to sum all 8 weighted bits into one halfword = output byte.
2157+
// 6) ADDV: sum all 8 lanes into H20..H23 (four output bytes).
2158+
// 7) Extract byte 0 from each, pack into 32-bit word, store 4 bytes.
21602159
//
21612160
// Register allocation (setup once, outside loop):
21622161
// V27.H8 = {1, 2, 4, 8, 16, 32, 64, 128} bit-position weights
2163-
// V28.H8 = broadcast(833) lower threshold (CMHS: x >= 833)
2164-
// V29.H8 = broadcast(2497) upper threshold (CMHS: x >= 2497 means x not in range)
2162+
// V28.H8 = broadcast(833) lower threshold (CMHS: x >= 833)
2163+
// V29.H8 = broadcast(2497) upper threshold (CMHS: x >= 2497 means x out of range)
21652164
//
21662165
// func ringCompressAndEncode1NEON(out []byte, f *ringElement)
21672166
TEXT ·ringCompressAndEncode1NEON(SB), NOSPLIT, $0-32
@@ -2178,41 +2177,68 @@ TEXT ·ringCompressAndEncode1NEON(SB), NOSPLIT, $0-32
21782177

21792178
// V27 = {1, 2, 4, 8, 16, 32, 64, 128}: bit-position weights for packing.
21802179
// Low 64 bits (lanes 0-3): 0x0008_0004_0002_0001 (little-endian halfwords)
2181-
// High 64 bits (lanes 4-7): 0x0080_0040_0020_0010
2180+
// High 64 bits (lanes 4-7): low << 4 = 0x0080_0040_0020_0010
21822181
MOVD $0x0008000400020001, R2
21832182
VMOV R2, V27.D[0]
2184-
MOVD $0x0080004000200010, R2
2183+
LSL $4, R2, R2
21852184
VMOV R2, V27.D[1]
21862185

2187-
MOVD $32, R2 // 32 iterations: 8 coefficients each -> 256 total -> 32 bytes
2186+
MOVD $8, R2 // 8 iterations: 32 coefficients each -> 256 total -> 32 bytes
21882187

21892188
compress_encode1_neon_loop:
2190-
VLD1.P 16(R1), [V0.H8] // Load 8 coefficients (16 bytes)
2191-
2192-
// V20 = 0xFFFF where V0 >= 833 (coefficient in compress=1 range, lower bound)
2193-
WORD $0x6E7C3C14 // CMHS V20.H8, V0.H8, V28.H8
2194-
2195-
// V21 = 0xFFFF where V0 >= 2497 (coefficient above compress=1 range, upper bound)
2196-
WORD $0x6E7D3C15 // CMHS V21.H8, V0.H8, V29.H8
2197-
2198-
// V20 = 0xFFFF where 833 <= coefficient <= 2496 (compress=1)
2199-
// BIC V20.B16, V20.B16, V21.B16 => V20 = V20 AND NOT(V21)
2200-
WORD $0x4E751E94 // BIC V20.B16, V20.B16, V21.B16
2201-
2202-
// V20 = 0x0001 or 0x0000 per lane (1 = compress bit)
2189+
// Load 32 coefficients as 4 x 8 halfwords (64 bytes)
2190+
VLD1.P 64(R1), [V0.H8, V1.H8, V2.H8, V3.H8]
2191+
2192+
// V20 = (V0 >= 833) AND NOT (V0 >= 2497) = 0xFFFF where compress(coeff)=1
2193+
WORD $0x6E7C3C14 // CMHS V20.H8, V0.H8, V28.H8 (vd=20, vn=0, vm=28)
2194+
WORD $0x6E7D3C15 // CMHS V21.H8, V0.H8, V29.H8 (vd=21, vn=0, vm=29)
2195+
WORD $0x4E751E94 // BIC V20.B16, V20.B16, V21.B16
2196+
2197+
// V21 = (V1 >= 833) AND NOT (V1 >= 2497)
2198+
WORD $0x6E7C3C35 // CMHS V21.H8, V1.H8, V28.H8 (vd=21, vn=1, vm=28)
2199+
WORD $0x6E7D3C36 // CMHS V22.H8, V1.H8, V29.H8 (vd=22, vn=1, vm=29)
2200+
WORD $0x4E761EB5 // BIC V21.B16, V21.B16, V22.B16
2201+
2202+
// V22 = (V2 >= 833) AND NOT (V2 >= 2497)
2203+
WORD $0x6E7C3C56 // CMHS V22.H8, V2.H8, V28.H8 (vd=22, vn=2, vm=28)
2204+
WORD $0x6E7D3C57 // CMHS V23.H8, V2.H8, V29.H8 (vd=23, vn=2, vm=29)
2205+
WORD $0x4E771ED6 // BIC V22.B16, V22.B16, V23.B16
2206+
2207+
// V23 = (V3 >= 833) AND NOT (V3 >= 2497)
2208+
WORD $0x6E7C3C77 // CMHS V23.H8, V3.H8, V28.H8 (vd=23, vn=3, vm=28)
2209+
WORD $0x6E7D3C78 // CMHS V24.H8, V3.H8, V29.H8 (vd=24, vn=3, vm=29)
2210+
WORD $0x4E781EF7 // BIC V23.B16, V23.B16, V24.B16
2211+
2212+
// Convert 0xFFFF/0x0000 to 0x0001/0x0000 in all four registers
22032213
VUSHR $15, V20.H8, V20.H8
2204-
2205-
// V20 = {weight * bit} per lane, where weights are {1,2,4,8,16,32,64,128}
2206-
WORD $0x4E7B9E94 // MUL V20.H8, V20.H8, V27.H8
2207-
2208-
// H20 = sum of all 8 weighted lanes = packed output byte
2209-
WORD $0x4E71BA94 // ADDV H20, V20.8H
2210-
2211-
// Extract the 64-bit value from V20 and store the low byte.
2212-
// After ADDV, V20[15:0] holds the sum (0-255); V20 upper bits are MUL leftovers.
2213-
VMOV V20.D[0], R10
2214-
MOVB R10, (R0)
2215-
ADD $1, R0
2214+
VUSHR $15, V21.H8, V21.H8
2215+
VUSHR $15, V22.H8, V22.H8
2216+
VUSHR $15, V23.H8, V23.H8
2217+
2218+
// Multiply by bit-position weights {1,2,4,8,16,32,64,128}
2219+
WORD $0x4E7B9E94 // MUL V20.H8, V20.H8, V27.H8 (vd=20, vn=20, vm=27)
2220+
WORD $0x4E7B9EB5 // MUL V21.H8, V21.H8, V27.H8 (vd=21, vn=21, vm=27)
2221+
WORD $0x4E7B9ED6 // MUL V22.H8, V22.H8, V27.H8 (vd=22, vn=22, vm=27)
2222+
WORD $0x4E7B9EF7 // MUL V23.H8, V23.H8, V27.H8 (vd=23, vn=23, vm=27)
2223+
2224+
// Sum all 8 weighted lanes: output byte in H20..H23 (= byte[0] of V20..V23)
2225+
WORD $0x4E71BA94 // ADDV H20, V20.8H (vd=20, vn=20)
2226+
WORD $0x4E71BAB5 // ADDV H21, V21.8H (vd=21, vn=21)
2227+
WORD $0x4E71BAD6 // ADDV H22, V22.8H (vd=22, vn=22)
2228+
WORD $0x4E71BAF7 // ADDV H23, V23.8H (vd=23, vn=23)
2229+
2230+
// Extract byte 0 from each halfword result (sum <= 255, so byte[0] is the full result)
2231+
VMOV V20.B[0], R10
2232+
VMOV V21.B[0], R11
2233+
VMOV V22.B[0], R12
2234+
VMOV V23.B[0], R13
2235+
2236+
// Pack 4 bytes into a 32-bit word and store
2237+
ORR R11<<8, R10, R10
2238+
ORR R12<<16, R10, R10
2239+
ORR R13<<24, R10, R10
2240+
MOVW R10, (R0)
2241+
ADD $4, R0
22162242

22172243
SUB $1, R2, R2
22182244
CBNZ R2, compress_encode1_neon_loop

0 commit comments

Comments
 (0)