github.com/segmentio/parquet-go@v0.0.0-20230712180008-5d42db8f0d47/internal/bitpack/unpack_int64_amd64.s (about) 1 //go:build !purego 2 3 #include "funcdata.h" 4 #include "textflag.h" 5 6 // func unpackInt64Default(dst []int64, src []uint32, bitWidth uint) 7 TEXT ·unpackInt64Default(SB), NOSPLIT, $0-56 8 MOVQ dst_base+0(FP), AX 9 MOVQ dst_len+8(FP), DX 10 MOVQ src_base+24(FP), BX 11 MOVQ bitWidth+48(FP), CX 12 13 MOVQ $1, R8 // bitMask = (1 << bitWidth) - 1 14 SHLQ CX, R8, R8 15 DECQ R8 16 MOVQ CX, R9 // bitWidth 17 18 XORQ DI, DI // bitOffset 19 XORQ SI, SI // index 20 XORQ R10, R10 21 XORQ R11, R11 22 XORQ R14, R14 23 JMP test 24 loop: 25 MOVQ DI, R10 26 MOVQ DI, CX 27 SHRQ $5, R10 // i = bitOffset / 32 28 ANDQ $0b11111, CX // j = bitOffset % 32 29 30 MOVLQZX (BX)(R10*4), R11 31 MOVQ R8, R12 // d = bitMask 32 SHLQ CX, R12 // d = d << j 33 ANDQ R12, R11 // d = src[i] & d 34 SHRQ CX, R11 // d = d >> j 35 36 MOVQ CX, R13 37 ADDQ R9, R13 38 CMPQ R13, $32 39 JBE next // j+bitWidth <= 32 ? 40 MOVQ CX, R15 // j 41 42 MOVLQZX 4(BX)(R10*4), R14 43 MOVQ $32, CX 44 SUBQ R15, CX // k = 32 - j 45 MOVQ R8, R12 // c = bitMask 46 SHRQ CX, R12 // c = c >> k 47 ANDQ R12, R14 // c = src[i+1] & c 48 SHLQ CX, R14 // c = c << k 49 ORQ R14, R11 // d = d | c 50 51 CMPQ R13, $64 52 JBE next 53 54 MOVLQZX 8(BX)(R10*4), R14 55 MOVQ $64, CX 56 SUBQ R15, CX // k = 64 - j 57 MOVQ R8, R12 // c = bitMask 58 SHRQ CX, R12 // c = c >> k 59 ANDQ R12, R14 // c = src[i+2] & c 60 SHLQ CX, R14 // c = c << k 61 ORQ R14, R11 // d = d | c 62 next: 63 MOVQ R11, (AX)(SI*8) // dst[n] = d 64 ADDQ R9, DI // bitOffset += bitWidth 65 INCQ SI 66 test: 67 CMPQ SI, DX 68 JNE loop 69 RET 70 71 // This bit unpacking function was inspired from the 32 bit version, but 72 // adapted to account for the fact that eight 64 bit values span across 73 // two YMM registers, and across lanes of YMM registers. 74 // 75 // Because of the two lanes of YMM registers, we cannot use the VPSHUFB 76 // instruction to dispatch bytes of the input to the registers. Instead we use 77 // the VPERMD instruction, which has higher latency but supports dispatching 78 // bytes across register lanes. Measurable throughput gains remain despite the 79 // algorithm running on a few more CPU cycles per loop. 80 // 81 // The initialization phase of this algorithm generates masks for 82 // permutations and shifts used to decode the bit-packed values. 83 // 84 // The permutation masks are written to Y7 and Y8, and contain the results 85 // of this formula: 86 // 87 // temp[i] = (bitWidth * i) / 32 88 // mask[i] = temp[i] | ((temp[i] + 1) << 32) 89 // 90 // Since VPERMQ only supports reading the permutation combination from an 91 // immediate value, we use VPERMD and generate permutation for pairs of two 92 // consecutive 32 bit words, which is why we have the upper part of each 64 93 // bit word set with (x+1)<<32. 94 // 95 // The masks for right shifts are written to Y5 and Y6, and computed with 96 // this formula: 97 // 98 // shift[i] = (bitWidth * i) - (32 * ((bitWidth * i) / 32)) 99 // 100 // The amount to shift by is the number of values previously unpacked, offseted 101 // by the byte count of 32 bit words that we read from first bits from. 102 // 103 // Technically the masks could be precomputed and declared in global tables; 104 // however, declaring masks for all bit width is tedious and makes code 105 // maintenance more costly for no measurable benefits on production workloads. 106 // 107 // func unpackInt64x1to32bitsAVX2(dst []int64, src []byte, bitWidth uint) 108 TEXT ·unpackInt64x1to32bitsAVX2(SB), NOSPLIT, $56-56 109 NO_LOCAL_POINTERS 110 MOVQ dst_base+0(FP), AX 111 MOVQ dst_len+8(FP), DX 112 MOVQ src_base+24(FP), BX 113 MOVQ bitWidth+48(FP), CX 114 115 CMPQ DX, $8 116 JB tail 117 118 MOVQ DX, DI 119 SHRQ $3, DI 120 SHLQ $3, DI 121 XORQ SI, SI 122 123 MOVQ $1, R8 124 SHLQ CX, R8 125 DECQ R8 126 MOVQ R8, X0 127 VPBROADCASTQ X0, Y0 // bitMask = (1 << bitWidth) - 1 128 129 VPCMPEQQ Y1, Y1, Y1 130 VPSRLQ $63, Y1, Y1 // [1,1,1,1] 131 132 MOVQ CX, X2 133 VPBROADCASTQ X2, Y2 // [bitWidth] 134 135 VMOVDQU range0n7<>+0(SB), Y3 // [0,1,2,3] 136 VMOVDQU range0n7<>+32(SB), Y4 // [4,5,6,7] 137 138 VPMULLD Y2, Y3, Y5 // [bitWidth] * [0,1,2,3] 139 VPMULLD Y2, Y4, Y6 // [bitWidth] * [4,5,6,7] 140 141 VPSRLQ $5, Y5, Y7 // ([bitWidth] * [0,1,2,3]) / 32 142 VPSRLQ $5, Y6, Y8 // ([bitWidth] * [4,5,6,7]) / 32 143 144 VPSLLQ $5, Y7, Y9 // (([bitWidth] * [0,1,2,3]) / 32) * 32 145 VPSLLQ $5, Y8, Y10 // (([bitWidth] * [4,5,6,7]) / 32) * 32 146 147 VPADDQ Y1, Y7, Y11 148 VPADDQ Y1, Y8, Y12 149 VPSLLQ $32, Y11, Y11 150 VPSLLQ $32, Y12, Y12 151 VPOR Y11, Y7, Y7 // permutations[i] = [i | ((i + 1) << 32)] 152 VPOR Y12, Y8, Y8 // permutations[i] = [i | ((i + 1) << 32)] 153 154 VPSUBQ Y9, Y5, Y5 // shifts 155 VPSUBQ Y10, Y6, Y6 156 loop: 157 VMOVDQU (BX), Y1 158 159 VPERMD Y1, Y7, Y2 160 VPERMD Y1, Y8, Y3 161 162 VPSRLVQ Y5, Y2, Y2 163 VPSRLVQ Y6, Y3, Y3 164 165 VPAND Y0, Y2, Y2 166 VPAND Y0, Y3, Y3 167 168 VMOVDQU Y2, (AX)(SI*8) 169 VMOVDQU Y3, 32(AX)(SI*8) 170 171 ADDQ CX, BX 172 ADDQ $8, SI 173 CMPQ SI, DI 174 JNE loop 175 VZEROUPPER 176 177 CMPQ SI, DX 178 JE done 179 LEAQ (AX)(SI*8), AX 180 SUBQ SI, DX 181 tail: 182 MOVQ AX, dst_base-56(SP) 183 MOVQ DX, dst_len-48(SP) 184 MOVQ BX, src_base-32(SP) 185 MOVQ CX, bitWidth-8(SP) 186 CALL ·unpackInt64Default(SB) 187 done: 188 RET 189 190 GLOBL range0n7<>(SB), RODATA|NOPTR, $64 191 DATA range0n7<>+0(SB)/8, $0 192 DATA range0n7<>+8(SB)/8, $1 193 DATA range0n7<>+16(SB)/8, $2 194 DATA range0n7<>+24(SB)/8, $3 195 DATA range0n7<>+32(SB)/8, $4 196 DATA range0n7<>+40(SB)/8, $5 197 DATA range0n7<>+48(SB)/8, $6 198 DATA range0n7<>+56(SB)/8, $7