MCPcopy Create free account
hub / github.com/bitsandbytes-foundation/bitsandbytes / neon_dequant_4bit_16values

Function neon_dequant_4bit_16values

csrc/cpu_ops.cpp:89–132  ·  view source on GitHub ↗

Vectorized 4-bit dequantization: process 8 packed bytes = 16 output values Each byte contains two 4-bit values: high nibble first, low nibble second

Source from the content-addressed store, hash-verified

87// Vectorized 4-bit dequantization: process 8 packed bytes = 16 output values
88// Each byte contains two 4-bit values: high nibble first, low nibble second
89static inline void
90 neon_dequant_4bit_16values(const uint8_t* packed, float scale, const float32x4_t lut[4], float* out) {
91 // Load 8 bytes = 16 x 4-bit values
92 uint8x8_t raw = vld1_u8(packed);
93
94 // Extract high and low nibbles
95 uint8x8_t mask4 = vdup_n_u8(0x0F);
96 uint8x8_t lo_nibbles = vand_u8(raw, mask4); // low nibble (second value)
97 uint8x8_t hi_nibbles = vshr_n_u8(raw, 4); // high nibble (first value)
98
99 // Interleave hi/lo into 16-element index array for output ordering
100 // output[2*i] = hi_nibble[i], output[2*i+1] = lo_nibble[i]
101 uint8x8x2_t interleaved = vzip_u8(hi_nibbles, lo_nibbles);
102 // interleaved.val[0] has elements 0-7, interleaved.val[1] has elements 8-15
103 uint8x16_t indices = vcombine_u8(interleaved.val[0], interleaved.val[1]);
104
105 // Reinterpret float LUT as 64-byte table for vqtbl4q_u8 lookup.
106 // Each 4-bit index i maps to bytes [i*4 .. i*4+3] in the table.
107 uint8x16x4_t lut_bytes = {
108 vreinterpretq_u8_f32(lut[0]), vreinterpretq_u8_f32(lut[1]), vreinterpretq_u8_f32(lut[2]),
109 vreinterpretq_u8_f32(lut[3])
110 };
111 // Multiply each index by 4 to get byte offset (max 15*4=60 < 64, safe)
112 uint8x16_t base = vshlq_n_u8(indices, 2);
113 // Expand each base offset to 4 consecutive bytes via zip
114 const uint8x16_t off = vreinterpretq_u8_u32(vdupq_n_u32(0x03020100));
115 uint8x8_t lo = vget_low_u8(base), hi = vget_high_u8(base);
116 uint8x8x2_t z0 = vzip_u8(lo, lo);
117 uint8x8x2_t z1 = vzip_u8(hi, hi);
118 uint8x8x2_t zlo = vzip_u8(z0.val[0], z0.val[0]);
119 uint8x8x2_t zhi = vzip_u8(z0.val[1], z0.val[1]);
120 uint8x8x2_t zlo2 = vzip_u8(z1.val[0], z1.val[0]);
121 uint8x8x2_t zhi2 = vzip_u8(z1.val[1], z1.val[1]);
122 float32x4_t vscale = vdupq_n_f32(scale);
123 float32x4_t v0 = vreinterpretq_f32_u8(vqtbl4q_u8(lut_bytes, vaddq_u8(vcombine_u8(zlo.val[0], zlo.val[1]), off)));
124 float32x4_t v1 = vreinterpretq_f32_u8(vqtbl4q_u8(lut_bytes, vaddq_u8(vcombine_u8(zhi.val[0], zhi.val[1]), off)));
125 float32x4_t v2 = vreinterpretq_f32_u8(vqtbl4q_u8(lut_bytes, vaddq_u8(vcombine_u8(zlo2.val[0], zlo2.val[1]), off)));
126 float32x4_t v3 = vreinterpretq_f32_u8(vqtbl4q_u8(lut_bytes, vaddq_u8(vcombine_u8(zhi2.val[0], zhi2.val[1]), off)));
127
128 vst1q_f32(out, vmulq_f32(v0, vscale));
129 vst1q_f32(out + 4, vmulq_f32(v1, vscale));
130 vst1q_f32(out + 8, vmulq_f32(v2, vscale));
131 vst1q_f32(out + 12, vmulq_f32(v3, vscale));
132}
133
134// NEON-optimized BF16 to float conversion (4 values at a time)
135static inline float32x4_t neon_bf16x4_to_f32(const bf16_t* src) {

Callers 1

Calls

no outgoing calls

Tested by

no test coverage detected