Files
Martin DisibioandGitHub 9aaf851965 Upgrade parquet-go (#6030)
* Upgrade parquet-go to latest for StructTag option

* Upgrade parquet-go to get latest alloc fix
2025-12-04 15:04:47 -05:00

733 lines
16 KiB
ArmAsm
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build !purego
#include "funcdata.h"
#include "textflag.h"
// func unpackInt32Default(dst []int32, src []byte, bitWidth uint)
TEXT ·unpackInt32Default(SB), NOSPLIT, $0-56
MOVD dst_base+0(FP), R0 // R0 = dst pointer
MOVD dst_len+8(FP), R1 // R1 = dst length
MOVD src_base+24(FP), R2 // R2 = src pointer
MOVD bitWidth+48(FP), R3 // R3 = bitWidth
MOVD $1, R4 // R4 = bitMask = (1 << bitWidth) - 1
LSL R3, R4, R4
SUB $1, R4, R4
MOVD $0, R5 // R5 = bitOffset
MOVD $0, R6 // R6 = index
B test
loop:
MOVD R5, R7 // R7 = i = bitOffset / 32
LSR $5, R7, R7
MOVD R5, R8 // R8 = j = bitOffset % 32
AND $31, R8, R8
LSL $2, R7, R16 // R16 = i * 4
MOVWU (R2)(R16), R9 // R9 = src[i]
MOVW R4, R10 // R10 = bitMask
LSL R8, R10, R10 // R10 = bitMask << j
AND R10, R9, R9 // R9 = src[i] & (bitMask << j)
LSR R8, R9, R9 // R9 = d = (src[i] & (bitMask << j)) >> j
ADD R3, R8, R11 // R11 = j + bitWidth
CMP $32, R11
BLE next // if j+bitWidth <= 32, skip to next
ADD $1, R7, R12 // R12 = i + 1
LSL $2, R12, R16 // R16 = (i + 1) * 4
MOVWU (R2)(R16), R13 // R13 = src[i+1]
MOVD $32, R14 // R14 = k = 32 - j
SUB R8, R14, R14
MOVW R4, R15 // R15 = bitMask
LSR R14, R15, R15 // R15 = bitMask >> k
AND R15, R13, R13 // R13 = src[i+1] & (bitMask >> k)
LSL R14, R13, R13 // R13 = (src[i+1] & (bitMask >> k)) << k
ORR R13, R9, R9 // R9 = d | c
next:
LSL $2, R6, R16 // R16 = index * 4
MOVW R9, (R0)(R16) // dst[index] = d
ADD R3, R5, R5 // bitOffset += bitWidth
ADD $1, R6, R6 // index++
test:
CMP R1, R6
BNE loop
RET
// unpackInt32x1to16bitsARM64 implements optimized unpacking for bit widths 1-16
// Uses optimized scalar ARM64 operations with batched processing
//
// func unpackInt32x1to16bitsARM64(dst []int32, src []byte, bitWidth uint)
TEXT ·unpackInt32x1to16bitsARM64(SB), NOSPLIT, $0-56
MOVD dst_base+0(FP), R0 // R0 = dst pointer
MOVD dst_len+8(FP), R1 // R1 = dst length
MOVD src_base+24(FP), R2 // R2 = src pointer
MOVD bitWidth+48(FP), R3 // R3 = bitWidth
// Check if we have at least 4 values to process
CMP $4, R1
BLT scalar_fallback
// Determine which NEON path to use based on bitWidth
CMP $1, R3
BEQ neon_1bit
CMP $2, R3
BEQ neon_2bit
CMP $3, R3
BEQ neon_3bit
CMP $4, R3
BEQ neon_4bit
CMP $5, R3
BEQ neon_5bit
CMP $6, R3
BEQ neon_6bit
CMP $7, R3
BEQ neon_7bit
CMP $8, R3
BEQ neon_8bit
CMP $16, R3
BEQ neon_16bit
// For other bit widths, fall back to scalar
B scalar_fallback
neon_1bit:
// BitWidth 1: 8 int32 values packed in 1 byte
// Process 8 values at a time using scalar operations
// Round down to multiple of 8 for processing
MOVD R1, R4
LSR $3, R4, R4 // R4 = len / 8
LSL $3, R4, R4 // R4 = aligned length (multiple of 8)
MOVD $0, R5 // R5 = index
CMP $0, R4
BEQ scalar_fallback
neon_1bit_loop:
// Load 1 byte (contains 8 values, 1 bit each)
MOVBU (R2), R6
// Extract 8 values manually (bits 0-7)
// Value 0: bit 0
AND $1, R6, R7
MOVW R7, (R0)
// Value 1: bit 1
LSR $1, R6, R7
AND $1, R7, R7
MOVW R7, 4(R0)
// Value 2: bit 2
LSR $2, R6, R7
AND $1, R7, R7
MOVW R7, 8(R0)
// Value 3: bit 3
LSR $3, R6, R7
AND $1, R7, R7
MOVW R7, 12(R0)
// Value 4: bit 4
LSR $4, R6, R7
AND $1, R7, R7
MOVW R7, 16(R0)
// Value 5: bit 5
LSR $5, R6, R7
AND $1, R7, R7
MOVW R7, 20(R0)
// Value 6: bit 6
LSR $6, R6, R7
AND $1, R7, R7
MOVW R7, 24(R0)
// Value 7: bit 7
LSR $7, R6, R7
AND $1, R7, R7
MOVW R7, 28(R0)
// Advance pointers
ADD $1, R2, R2 // src += 1 byte (8 values)
ADD $32, R0, R0 // dst += 8 int32 (32 bytes)
ADD $8, R5, R5 // index += 8
CMP R4, R5
BLT neon_1bit_loop
CMP R1, R5
BEQ neon_done
SUB R5, R1, R1
B scalar_fallback_entry
neon_2bit:
// BitWidth 2: 4 int32 values packed in 1 byte
// Process 4 values at a time using scalar operations
MOVD R1, R4
LSR $2, R4, R4 // R4 = len / 4
LSL $2, R4, R4 // R4 = aligned length (multiple of 4)
MOVD $0, R5
CMP $0, R4
BEQ scalar_fallback
neon_2bit_loop:
// Load 1 byte (contains 4 values, 2 bits each)
MOVBU (R2), R6
// Extract 4 values manually (bits 0-1, 2-3, 4-5, 6-7)
// Value 0: bits 0-1
AND $3, R6, R7
MOVW R7, (R0)
// Value 1: bits 2-3
LSR $2, R6, R7
AND $3, R7, R7
MOVW R7, 4(R0)
// Value 2: bits 4-5
LSR $4, R6, R7
AND $3, R7, R7
MOVW R7, 8(R0)
// Value 3: bits 6-7
LSR $6, R6, R7
AND $3, R7, R7
MOVW R7, 12(R0)
// Advance pointers
ADD $1, R2, R2 // src += 1 byte (4 values)
ADD $16, R0, R0 // dst += 4 int32 (16 bytes)
ADD $4, R5, R5 // index += 4
CMP R4, R5
BLT neon_2bit_loop
CMP R1, R5
BEQ neon_done
SUB R5, R1, R1
B scalar_fallback_entry
neon_3bit:
// BitWidth 3: 8 int32 values packed in 3 bytes
// Process 8 values at a time using scalar operations
// Round down to multiple of 8 for processing
MOVD R1, R4
LSR $3, R4, R4 // R4 = len / 8
LSL $3, R4, R4 // R4 = aligned length (multiple of 8)
MOVD $0, R5
CMP $0, R4
BEQ scalar_fallback
neon_3bit_loop:
// Load 3 bytes as 32-bit value (4th byte will be ignored)
// Bytes 0-2 contain: [val7:val6:val5:val4:val3:val2:val1:val0]
// Bits layout: [23:21][20:18][17:15][14:12][11:9][8:6][5:3][2:0]
MOVWU (R2), R6
// Value 0: bits 0-2
AND $7, R6, R7
MOVW R7, (R0)
// Value 1: bits 3-5
LSR $3, R6, R7
AND $7, R7, R7
MOVW R7, 4(R0)
// Value 2: bits 6-8
LSR $6, R6, R7
AND $7, R7, R7
MOVW R7, 8(R0)
// Value 3: bits 9-11
LSR $9, R6, R7
AND $7, R7, R7
MOVW R7, 12(R0)
// Value 4: bits 12-14
LSR $12, R6, R7
AND $7, R7, R7
MOVW R7, 16(R0)
// Value 5: bits 15-17
LSR $15, R6, R7
AND $7, R7, R7
MOVW R7, 20(R0)
// Value 6: bits 18-20
LSR $18, R6, R7
AND $7, R7, R7
MOVW R7, 24(R0)
// Value 7: bits 21-23
LSR $21, R6, R7
AND $7, R7, R7
MOVW R7, 28(R0)
// Advance pointers
ADD $3, R2, R2 // src += 3 bytes (8 values)
ADD $32, R0, R0 // dst += 8 int32 (32 bytes)
ADD $8, R5, R5 // index += 8
CMP R4, R5
BLT neon_3bit_loop
CMP R1, R5
BEQ neon_done
SUB R5, R1, R1
B scalar_fallback_entry
neon_4bit:
// BitWidth 4: 4 int32 values packed in 2 bytes
// Process 4 values at a time using scalar operations
MOVD R1, R4
LSR $2, R4, R4 // R4 = len / 4
LSL $2, R4, R4 // R4 = aligned length (multiple of 4)
MOVD $0, R5
CMP $0, R4
BEQ scalar_fallback
neon_4bit_loop:
// Load 2 bytes (contains 4 values, 4 bits each)
MOVHU (R2), R6
// Extract 4 values manually (nibbles)
// Value 0: bits 0-3
AND $15, R6, R7
MOVW R7, (R0)
// Value 1: bits 4-7
LSR $4, R6, R7
AND $15, R7, R7
MOVW R7, 4(R0)
// Value 2: bits 8-11
LSR $8, R6, R7
AND $15, R7, R7
MOVW R7, 8(R0)
// Value 3: bits 12-15
LSR $12, R6, R7
AND $15, R7, R7
MOVW R7, 12(R0)
// Advance pointers
ADD $2, R2, R2 // src += 2 bytes (4 values)
ADD $16, R0, R0 // dst += 4 int32 (16 bytes)
ADD $4, R5, R5 // index += 4
CMP R4, R5
BLT neon_4bit_loop
CMP R1, R5
BEQ neon_done
SUB R5, R1, R1
B scalar_fallback_entry
neon_5bit:
// BitWidth 5: 8 int32 values packed in 5 bytes
// Process 8 values at a time using scalar operations
// Round down to multiple of 8 for processing
MOVD R1, R4
LSR $3, R4, R4 // R4 = len / 8
LSL $3, R4, R4 // R4 = aligned length (multiple of 8)
MOVD $0, R5
CMP $0, R4
BEQ scalar_fallback
neon_5bit_loop:
// Load 5 bytes as 64-bit value (upper bytes will be ignored)
// 8 values × 5 bits = 40 bits = 5 bytes
// Bits layout: [39:35][34:30][29:25][24:20][19:15][14:10][9:5][4:0]
MOVD (R2), R6
// Value 0: bits 0-4
AND $31, R6, R7
MOVW R7, (R0)
// Value 1: bits 5-9
LSR $5, R6, R7
AND $31, R7, R7
MOVW R7, 4(R0)
// Value 2: bits 10-14
LSR $10, R6, R7
AND $31, R7, R7
MOVW R7, 8(R0)
// Value 3: bits 15-19
LSR $15, R6, R7
AND $31, R7, R7
MOVW R7, 12(R0)
// Value 4: bits 20-24
LSR $20, R6, R7
AND $31, R7, R7
MOVW R7, 16(R0)
// Value 5: bits 25-29
LSR $25, R6, R7
AND $31, R7, R7
MOVW R7, 20(R0)
// Value 6: bits 30-34
LSR $30, R6, R7
AND $31, R7, R7
MOVW R7, 24(R0)
// Value 7: bits 35-39
LSR $35, R6, R7
AND $31, R7, R7
MOVW R7, 28(R0)
// Advance pointers
ADD $5, R2, R2 // src += 5 bytes (8 values)
ADD $32, R0, R0 // dst += 8 int32 (32 bytes)
ADD $8, R5, R5 // index += 8
CMP R4, R5
BLT neon_5bit_loop
CMP R1, R5
BEQ neon_done
SUB R5, R1, R1
B scalar_fallback_entry
neon_6bit:
// BitWidth 6: 4 int32 values packed in 3 bytes
// Process 4 values at a time using scalar operations
MOVD R1, R4
LSR $2, R4, R4 // R4 = len / 4
LSL $2, R4, R4 // R4 = aligned length (multiple of 4)
MOVD $0, R5
CMP $0, R4
BEQ scalar_fallback
neon_6bit_loop:
// Load 3 bytes as 32-bit value (4th byte will be ignored)
// 4 values × 6 bits = 24 bits = 3 bytes
// Bits layout: [23:18][17:12][11:6][5:0]
MOVWU (R2), R6
// Value 0: bits 0-5
AND $63, R6, R7
MOVW R7, (R0)
// Value 1: bits 6-11
LSR $6, R6, R7
AND $63, R7, R7
MOVW R7, 4(R0)
// Value 2: bits 12-17
LSR $12, R6, R7
AND $63, R7, R7
MOVW R7, 8(R0)
// Value 3: bits 18-23
LSR $18, R6, R7
AND $63, R7, R7
MOVW R7, 12(R0)
// Advance pointers
ADD $3, R2, R2 // src += 3 bytes (4 values)
ADD $16, R0, R0 // dst += 4 int32 (16 bytes)
ADD $4, R5, R5 // index += 4
CMP R4, R5
BLT neon_6bit_loop
CMP R1, R5
BEQ neon_done
SUB R5, R1, R1
B scalar_fallback_entry
neon_7bit:
// BitWidth 7: 8 int32 values packed in 7 bytes
// Process 8 values at a time using scalar operations
// Round down to multiple of 8 for processing
MOVD R1, R4
LSR $3, R4, R4 // R4 = len / 8
LSL $3, R4, R4 // R4 = aligned length (multiple of 8)
MOVD $0, R5
CMP $0, R4
BEQ scalar_fallback
neon_7bit_loop:
// Load 7 bytes as 64-bit value (8th byte will be ignored)
// 8 values × 7 bits = 56 bits = 7 bytes
// Bits layout: [55:49][48:42][41:35][34:28][27:21][20:14][13:7][6:0]
MOVD (R2), R6
// Value 0: bits 0-6
AND $127, R6, R7
MOVW R7, (R0)
// Value 1: bits 7-13
LSR $7, R6, R7
AND $127, R7, R7
MOVW R7, 4(R0)
// Value 2: bits 14-20
LSR $14, R6, R7
AND $127, R7, R7
MOVW R7, 8(R0)
// Value 3: bits 21-27
LSR $21, R6, R7
AND $127, R7, R7
MOVW R7, 12(R0)
// Value 4: bits 28-34
LSR $28, R6, R7
AND $127, R7, R7
MOVW R7, 16(R0)
// Value 5: bits 35-41
LSR $35, R6, R7
AND $127, R7, R7
MOVW R7, 20(R0)
// Value 6: bits 42-48
LSR $42, R6, R7
AND $127, R7, R7
MOVW R7, 24(R0)
// Value 7: bits 49-55
LSR $49, R6, R7
AND $127, R7, R7
MOVW R7, 28(R0)
// Advance pointers
ADD $7, R2, R2 // src += 7 bytes (8 values)
ADD $32, R0, R0 // dst += 8 int32 (32 bytes)
ADD $8, R5, R5 // index += 8
CMP R4, R5
BLT neon_7bit_loop
CMP R1, R5
BEQ neon_done
SUB R5, R1, R1
B scalar_fallback_entry
neon_8bit:
// BitWidth 8: 4 int32 values packed in 4 bytes
// Process 4 values at a time using NEON
// Calculate how many full groups of 4 we can process
MOVD R1, R4
LSR $2, R4, R4 // R4 = len / 4
LSL $2, R4, R4 // R4 = (len / 4) * 4 = aligned length
MOVD $0, R5 // R5 = index
CMP $0, R4
BEQ scalar_fallback
neon_8bit_loop:
// Load 4 bytes as 4 uint8 values into lower part of V0
// We need to load bytes and zero-extend to 32-bit
// Load 4 bytes to W6
MOVWU (R2), R6
// Extract bytes and write as int32
// Byte 0
AND $0xFF, R6, R7
MOVW R7, (R0)
// Byte 1
LSR $8, R6, R7
AND $0xFF, R7, R7
MOVW R7, 4(R0)
// Byte 2
LSR $16, R6, R7
AND $0xFF, R7, R7
MOVW R7, 8(R0)
// Byte 3
LSR $24, R6, R7
MOVW R7, 12(R0)
// Advance pointers
ADD $4, R2, R2 // src += 4 bytes
ADD $16, R0, R0 // dst += 4 int32 (16 bytes)
ADD $4, R5, R5 // index += 4
CMP R4, R5
BLT neon_8bit_loop
// Handle tail with scalar
CMP R1, R5
BEQ neon_done
// Calculate remaining elements
SUB R5, R1, R1 // R1 = remaining elements
B scalar_fallback_entry
neon_16bit:
// BitWidth 16: 4 int32 values packed in 8 bytes
// Process 4 values at a time
MOVD R1, R4
LSR $2, R4, R4 // R4 = len / 4
LSL $2, R4, R4 // R4 = (len / 4) * 4
MOVD $0, R5 // R5 = index
CMP $0, R4
BEQ scalar_fallback
neon_16bit_loop:
// Load 8 bytes as 4 uint16 values
MOVD (R2), R6 // Load 8 bytes into R6
// Extract 16-bit values and write as int32
// Value 0 (bits 0-15)
AND $0xFFFF, R6, R7
MOVW R7, (R0)
// Value 1 (bits 16-31)
LSR $16, R6, R7
AND $0xFFFF, R7, R7
MOVW R7, 4(R0)
// Value 2 (bits 32-47)
LSR $32, R6, R7
AND $0xFFFF, R7, R7
MOVW R7, 8(R0)
// Value 3 (bits 48-63)
LSR $48, R6, R7
MOVW R7, 12(R0)
// Advance pointers
ADD $8, R2, R2 // src += 8 bytes
ADD $16, R0, R0 // dst += 4 int32 (16 bytes)
ADD $4, R5, R5 // index += 4
CMP R4, R5
BLT neon_16bit_loop
// Handle tail with scalar
CMP R1, R5
BEQ neon_done
SUB R5, R1, R1
B scalar_fallback_entry
neon_done:
RET
scalar_fallback:
MOVD $0, R5 // Start from beginning
// R0, R1, R2, R3 already set from function args
scalar_fallback_entry:
// R0 = current dst position (already advanced)
// R1 = remaining elements
// R2 = current src position (already advanced)
// R3 = bitWidth
// R5 = elements already processed
// Fall back to scalar implementation for remaining elements
CMP $0, R1
BEQ scalar_done // No remaining elements
MOVD $1, R4 // R4 = bitMask = (1 << bitWidth) - 1
LSL R3, R4, R4
SUB $1, R4, R4
// bitOffset starts from 0 relative to current R2 position
// (not total offset, since R2 is already advanced)
MOVD $0, R6 // R6 = bitOffset (relative to current R2)
MOVD $0, R7 // R7 = index (within remaining elements)
B scalar_test
scalar_loop:
MOVD R6, R8 // R8 = i = bitOffset / 32
LSR $5, R8, R8
MOVD R6, R9 // R9 = j = bitOffset % 32
AND $31, R9, R9
LSL $2, R8, R10 // R10 = i * 4
MOVWU (R2)(R10), R11 // R11 = src[i] (relative to current R2)
MOVW R4, R12 // R12 = bitMask
LSL R9, R12, R12 // R12 = bitMask << j
AND R12, R11, R11 // R11 = src[i] & (bitMask << j)
LSR R9, R11, R11 // R11 = d = (src[i] & (bitMask << j)) >> j
ADD R3, R9, R12 // R12 = j + bitWidth
CMP $32, R12
BLE scalar_next // if j+bitWidth <= 32, skip to next
ADD $1, R8, R13 // R13 = i + 1
LSL $2, R13, R10 // R10 = (i + 1) * 4
MOVWU (R2)(R10), R14 // R14 = src[i+1]
MOVD $32, R15 // R15 = k = 32 - j
SUB R9, R15, R15
MOVW R4, R16 // R16 = bitMask
LSR R15, R16, R16 // R16 = bitMask >> k
AND R16, R14, R14 // R14 = src[i+1] & (bitMask >> k)
LSL R15, R14, R14 // R14 = (src[i+1] & (bitMask >> k)) << k
ORR R14, R11, R11 // R11 = d | c
scalar_next:
LSL $2, R7, R10 // R10 = index * 4
MOVW R11, (R0)(R10) // dst[index] = d (relative to current R0)
ADD R3, R6, R6 // bitOffset += bitWidth
ADD $1, R7, R7 // index++
scalar_test:
CMP R1, R7
BLT scalar_loop
scalar_done:
RET
// Macro definitions for unsupported NEON instructions using WORD encodings
// USHLL Vd.8H, Vn.8B, #0 - widen 8x8-bit to 8x16-bit
#define USHLL_8H_8B(vd, vn) WORD $(0x2f08a400 | (vd) | ((vn)<<5))
// USHLL2 Vd.8H, Vn.16B, #0 - widen upper 8x8-bit to 8x16-bit
#define USHLL2_8H_16B(vd, vn) WORD $(0x6f08a400 | (vd) | ((vn)<<5))
// USHLL Vd.4S, Vn.4H, #0 - widen 4x16-bit to 4x32-bit
#define USHLL_4S_4H(vd, vn) WORD $(0x2f10a400 | (vd) | ((vn)<<5))
// USHLL2 Vd.4S, Vn.8H, #0 - widen upper 4x16-bit to 4x32-bit
#define USHLL2_4S_8H(vd, vn) WORD $(0x6f10a400 | (vd) | ((vn)<<5))
// USHLL Vd.2D, Vn.2S, #0 - widen 2x32-bit to 2x64-bit
#define USHLL_2D_2S(vd, vn) WORD $(0x2f20a400 | (vd) | ((vn)<<5))
// USHLL2 Vd.2D, Vn.4S, #0 - widen upper 2x32-bit to 2x64-bit
#define USHLL2_2D_4S(vd, vn) WORD $(0x6f20a400 | (vd) | ((vn)<<5))
// Bit expansion lookup table defined in bitexpand_table_arm64.s
// unpackInt32x1bitNEON implements table-based NEON unpacking for bitWidth=1
// Uses lookup tables for parallel bit expansion
//
// func unpackInt32x1bitNEON(dst []int32, src []byte, bitWidth uint)