iBoot/lib/cksum/arm/adler32vec.S

342 lines
12 KiB
ArmAsm

/*
* Copyright (C) 2012-2014 Apple Inc. All rights reserved.
*
* This document is the property of Apple Computer, Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#if defined(__arm__)
#define BASE 65521 /* largest prime smaller than 65536 */
#define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
// Note: buf should have been 16-byte aligned in the caller function,
// uLong adler32_vec(unsigned int adler, unsigned int sum2, const Bytef* buf, int len) {
// unsigned n;
// while (len >= NMAX) {
// len -= NMAX;
// n = NMAX / 16; /* NMAX is divisible by 16 */
// do {
// DO16(buf); /* 16 sums unrolled */
// buf += 16;
// } while (--n);
// MOD(adler);
// MOD(sum2);
// }
// if (len) { /* avoid modulos if none remaining */
// while (len >= 16) {
// len -= 16;
// DO16(buf);
// buf += 16;
// }
// while (len--) {
// adler += *buf++;
// sum2 += adler;
// }
// MOD(adler);
// MOD(sum2);
// }
// return adler | (sum2 << 16); /* return recombined sums */
// }
/*
DO16 vectorization:
given initial unsigned int sum2 and adler, and a new set of 16 input bytes (x[0:15]), it can be shown that
sum2 += (16*adler + 16*x[0] + 15*x[1] + ... + 1*x[15]);
adler += (x[0] + x[1] + ... + x[15]);
therefore, this is what can be done to vectorize the above computation
1. 16-byte aligned vector load into q2 (x[0:x15])
2. sum2 += (adler<<4);
3. vmull.u8 (q9,q8),q2,d2 where d2 = (1,1,1,1...,1), (q9,q8) + 16 16-bit elements x[0:15]
4. vmull.u8 (q11,q10),q2,q0 where q0 = (1,2,3,4...,16), (q11,q10) + 16 16-bit elements (16:1)*x[0:15]
5. parallel add (with once expansion to 32-bit) (q9,q8) and (q11,q10) all the way to accumulate to adler and sum2
In this revision, whenever possible, 2 DO16 loops are combined into a DO32 loop.
1. 32-byte aligned vector load into q2,q14 (x[0:x31])
2. sum2 += (adler<<5);
3. vmull.u8 (4 q registers),(q2,q14),d2 where d2 = (1,1,1,1...,1), (4 q registers) : 32 16-bit elements x[0:31]
4. vmull.u8 (4 q registers),(q2,q14),(q0,q15) where q0 = (1,...,32), (4 q regs) : 32 16-bit elements (32:1)*x[0:31]
5. parallel add (with once expansion to 32-bit) the pair of (4 q regs) all the way to accumulate to adler and sum2
This change improves the performance by ~ 0.55 cycle/uncompress byte on ARM Cortex-A8.
*/
/*
MOD implementation:
adler%BASE = adler - floor(adler*(1/BASE))*BASE; where (1/BASE) = 0x80078071 in Q47
1. vmull.u32 q2,(adler,sum2),(1/BASE) // *(1/BASE) in Q47
2. vshr.u64 q2,q2,#47 // floor function
3. vpadd.u32 d4,d4,d5 // merge into a double word in d4
4. vmls.u32 (adler,sum2),d4,d3[0] // (adler,sum2) -= floor[(adler,sum2)/BASE]*BASE
*/
.text
.align 2
.globl _adler32_vec
_adler32_vec:
#define adler r0
#define sum2 r1
#define buf r2
#define len r3
#define nmax r4
#define vecs lr // vecs = NMAX/16
#define n r5
#define t r12
#define sum2_coeff q0
#define sum2_coeff0 d0
#define sum2_coeff1 d1
#define alder_coeff q1
#define ones d2
#define x0_x15 q2
#define x0_x7 d4
#define x8_x15 d5
#define adlersum2 d6
#define adler16 d25
adr t, vec_table // address to vec_table[]
stmfd sp!, {r4, r5, lr}
vld1.32 {q0-q1},[t,:128]! // loading up coefficients for adler/sum2 computation
vld1.32 {q15},[t,:128]! // for sum2 computation
ldr nmax, [t] // NMAX
vmov adlersum2, sum2, adler // pack up adler/sum2 into a double register
cmp len, nmax // len vs NMAX
lsr vecs, nmax, #4 // vecs = NMAX/16;
blt L_len_lessthan_NMAX // if (len < NMAX) skip the while loop
sub len, nmax // pre-decrement len by NMAX
L_while_len_ge_NMAX_loop: // while (len>=NMAX) {
mov n, vecs, lsr #1 // n = NMAX/16;
L_do_loop: // do {
vshll.u32 q12, adlersum2, #5 // d25 = (0,32*adler) to be added into (adler,sum2)
vld1.32 {x0_x15},[buf,:128]! // 16-byte input x0:x15
vmull.u8 q8, x0_x7, ones // 16-bit x0-x7
vld1.32 {q14}, [buf,:128]! // x16:x31
vmull.u8 q9, x8_x15, ones // 16-bit x8-x15
vadd.u32 adlersum2,adler16 // sum2 += old adler*32;
vmull.u8 q12, d28, ones // 16-bit x16-x23
vmull.u8 q13, d29, ones // 16-bit x24-x31
vmull.u8 q10, d28, sum2_coeff0 // 16-bit x16*16, x17*15, ..., x23*9
vmull.u8 q11, d29, sum2_coeff1 // 16-bit x24*8, x25*7, ..., x31*1
vadd.u16 q8, q8, q9 // q8 = (x0+x8):(x7+x15) 8 16-bit elements for adler
vmull.u8 q9, x0_x7, d30 // 16-bit x0*32,...,x7*25
vmull.u8 q14, x8_x15, d31 // 16-bit x8*24,...,x15*17
vadd.u16 q12, q12, q13 // q12 = (x16+x24):(x23+x31) 8 16-bit elements for adler
vadd.u16 q10, q11 // 8 16-bit elements for sum2
vadd.u16 q8, q12 // 8 16-bit elements for adler
vadd.u16 q9, q14 // 8 16-bit elements for sum2
vadd.u16 q10, q9 // 8 16-bit elements for sum2
#if 0
vpaddl.u16 q8, q8 // 4 32-bit elements for adler
vpaddl.u16 q10, q10 // 4 32-bit elements for sum2
vpadd.u32 d16,d16,d17 // 2 32-bit elements for adler
vpadd.u32 d17,d20,d21 // 2 32-bit elements for sum2
#else
vadd.u16 d16, d17
vadd.u16 d20, d21
vpaddl.u16 d16, d16
vpaddl.u16 d17, d20
#endif
vpadd.u32 d4,d17,d16 // s8 : 32-bit elements for sum2, s9 : 32-bit element for adler
vadd.u32 adlersum2,d4 // update adler/sum2 with the new 16 bytes input
subs n, #1 // --n
bgt L_do_loop // } while (--n);
vshll.u32 q12, adlersum2, #4 // d25 = (0,16*adler) to be added into (adler,sum2)
vld1.32 {x0_x15},[buf,:128]! // 16-byte input
vmull.u8 q8, x0_x7, ones // 16-bit x0-x7
vmull.u8 q9, x8_x15, ones // 16-bit x8-x15
vmull.u8 q10, x0_x7, sum2_coeff0 // 16-bit x0*16, x1*15, ..., x7*9
vmull.u8 q11, x8_x15, sum2_coeff1 // 16-bit x8*8, x9*7, ..., x15*1
vadd.u16 q8, q8, q9 // 8 16-bit elements for adler
vadd.u16 q10, q10, q11 // 8 16-bit elements for sum2
#if 0
vpaddl.u16 q8, q8 // 4 32-bit elements for adler
vpaddl.u16 q10, q10 // 4 32-bit elements for sum2
vpadd.u32 d16,d16,d17 // 2 32-bit elements for adler
vpadd.u32 d17,d20,d21 // 2 32-bit elements for sum2
#else
vadd.u16 d16, d17
vadd.u16 d20, d21
vpaddl.u16 d16, d16
vpaddl.u16 d17, d20
#endif
vadd.u32 adlersum2,adler16 // sum2 += old adler;
vpadd.u32 d4,d17,d16 // s8 : 32-bit elements for sum2, s9 : 32-bit element for adler
vadd.u32 adlersum2,d4 // update adler/sum2 with the new 16 bytes input
// mod(alder,BASE); mod(sum2,BASE);
vmull.u32 q2,adlersum2,d3[1] // alder/BASE, sum2/BASE in Q47
vshr.u64 q2,q2,#47 // take the integer part
vpadd.u32 d4,d4,d5 // merge into a double word in d4
vmls.u32 adlersum2,d4,d3[0] // (adler,sum2) -= floor[(adler,sum2)/BASE]*BASE
subs len, nmax // len -= NMAX;
bge L_while_len_ge_NMAX_loop // repeat while len >= NMAX
add len, nmax // post-increment len by NMAX
L_len_lessthan_NMAX:
cmp len, #0
beq L_len_is_zero // if len==0, branch to skip the following
subs len, #32 // pre-decrement len by 32
blt L_len_lessthan_32 // if len < 32, branch to L_len16_loop
L_len32_loop:
vshll.u32 q12, adlersum2, #5 // d25 = (0,32*adler) to be added into (adler,sum2)
vld1.32 {x0_x15},[buf,:128]! // 16-byte input x0:x15
vmull.u8 q8, x0_x7, ones // 16-bit x0-x7
vld1.32 {q14}, [buf,:128]! // x16:x31
vmull.u8 q9, x8_x15, ones // 16-bit x8-x15
vadd.u32 adlersum2,adler16 // sum2 += old adler*32;
vmull.u8 q12, d28, ones // 16-bit x16-x23
vmull.u8 q13, d29, ones // 16-bit x24-x31
vmull.u8 q10, d28, sum2_coeff0 // 16-bit x16*16, x17*15, ..., x23*9
vmull.u8 q11, d29, sum2_coeff1 // 16-bit x24*8, x25*7, ..., x31*1
vadd.u16 q8, q8, q9 // q8 = (x0+x8):(x7+x15) 8 16-bit elements for adler
vmull.u8 q9, x0_x7, d30 // 16-bit x0*32,...,x7*25
vmull.u8 q14, x8_x15, d31 // 16-bit x8*24,...,x15*17
vadd.u16 q12, q12, q13 // q12 = (x16+x24):(x23+x31) 8 16-bit elements for adler
vadd.u16 q10, q11 // 8 16-bit elements for sum2
vadd.u16 q8, q12 // 8 16-bit elements for adler
vadd.u16 q9, q14 // 8 16-bit elements for sum2
vadd.u16 q10, q9 // 8 16-bit elements for sum2
#if 0
vpaddl.u16 q8, q8 // 4 32-bit elements for adler
vpaddl.u16 q10, q10 // 4 32-bit elements for sum2
vpadd.u32 d16,d16,d17 // 2 32-bit elements for adler
vpadd.u32 d17,d20,d21 // 2 32-bit elements for sum2
#else
vadd.u16 d16, d17
vadd.u16 d20, d21
vpaddl.u16 d16, d16
vpaddl.u16 d17, d20
#endif
vpadd.u32 d4,d17,d16 // s8 : 32-bit elements for sum2, s9 : 32-bit element for adler
vadd.u32 adlersum2,d4 // update adler/sum2 with the new 16 bytes input
subs len, #32 // len -= 32;
bge L_len32_loop
L_len_lessthan_32:
adds len, #(32-16) // post-increment len by 32, then pre-decrement by 16
blt L_len_lessthan_16 // if len < 16, branch to L_len_lessthan_16
vshll.u32 q12, adlersum2, #4 // d25 = (0,16*adler) to be added into (adler,sum2)
vld1.32 {x0_x15},[buf,:128]! // 16-byte input
vmull.u8 q8, x0_x7, ones // 16-bit x0-x7
vmull.u8 q9, x8_x15, ones // 16-bit x8-x15
vmull.u8 q10, x0_x7, sum2_coeff0 // 16-bit x0*16, x1*15, ..., x7*9
vmull.u8 q11, x8_x15, sum2_coeff1 // 16-bit x8*8, x9*7, ..., x15*1
vadd.u16 q8, q8, q9 // 8 16-bit elements for adler
vadd.u16 q10, q10, q11 // 8 16-bit elements for sum2
#if 0
vpaddl.u16 q8, q8 // 4 32-bit elements for adler
vpaddl.u16 q10, q10 // 4 32-bit elements for sum2
vpadd.u32 d16,d16,d17 // 2 32-bit elements for adler
vpadd.u32 d17,d20,d21 // 2 32-bit elements for sum2
#else
vadd.u16 d16, d17
vadd.u16 d20, d21
vpaddl.u16 d16, d16
vpaddl.u16 d17, d20
#endif
subs len, #16 // decrement len by 16
vadd.u32 adlersum2,adler16 // sum2 += old adler;
vpadd.u32 d4,d17,d16 // s8 : 32-bit elements for sum2, s9 : 32-bit element for adler
vadd.u32 adlersum2,d4 // update adler/sum2 with the new 16 bytes input
L_len_lessthan_16:
adds len, #16 // post-increment len by 16
beq L_len_is_zero_internal // if len==0, branch to len_is_zero_internal
// restore adler/sum2 into general registers for remaining (<16) bytes
vmov sum2, adler, adlersum2
L_remaining_len_loop:
ldrb t, [buf], #1 // *buf++;
subs len, #1 // len--;
add adler,t // adler += *buf
add sum2,adler // sum2 += adler
bgt L_remaining_len_loop // break if len<=0
vmov adlersum2, sum2, adler // move to double register for modulo operation
L_len_is_zero_internal:
// mod(alder,BASE); mod(sum2,BASE);
vmull.u32 q2,adlersum2,d3[1] // alder/BASE, sum2/BASE in Q47
vshr.u64 q2,q2,#47 // take the integer part
vpadd.u32 d4,d4,d5 // merge into a double word in d4
vmls.u32 adlersum2,d4,d3[0] // (adler,sum2) -= floor[(adler,sum2)/BASE]*BASE
L_len_is_zero:
vmov sum2, adler, adlersum2 // restore adler/sum2 from (s12=sum2, s13=adler)
add r0, adler, sum2, lsl #16 // to return adler | (sum2 << 16);
ldmfd sp!, {r4, r5, pc} // restore registers and return
// constants to be loaded into q registers
.align 4 // 16 byte aligned
vec_table:
// coefficients for computing sum2
.long 0x0d0e0f10 // s0
.long 0x090a0b0c // s1
.long 0x05060708 // s2
.long 0x01020304 // s3
// coefficients for computing adler
.long 0x01010101 // s4/d2
.long 0x01010101 // s5
.long BASE // s6 : BASE
.long 0x80078071 // s7 : 1/BASE in Q47
// q15 : d30.d31
.long 0x1d1e1f20 // s0
.long 0x191a1b1c // s1
.long 0x15161718 // s2
.long 0x11121314 // s3
NMAX_loc:
.long NMAX // NMAX
#endif // defined(__arm__)