main
h 609 lines 21.8 KB
Raw
1 /*
2 Fast Dequantization (Converting INT4/INT8/FP4/FP8 to FP16/BF16)
3
4 The process of fast dequantization can be summarized as a combination
5 of bitwise operations and floating-point computations:
6
7 weight =>(bit_op / bitwise operations)=>
8 f16_value =>(flop / floating-point computation)=>
9 dequantized_weight
10
11 Since the dequantized weights typically require subtracting the zero point and
12 applying a scale factor, the floating-point computation step can be fused with
13 the zero-point subtraction and scaling operations.
14
15 The following are the parts that need to be modified for the fused operation
16 of zero-point subtraction and scaling.
17
18 ## INT4 => FP16/BF16 or INT8 => FP16
19
20 The floating-point computation is `__hsub2`
21
22 If has zero points:
23
24 flop(bit_op(weight)) - flop(bit_op(zp))
25 = sub(bit_op(weight), bias) - sub(bit_op(zp), bias)
26 = bit_op(weight) - bit_op(zp)
27
28 so we don't need additional modification.
29
30 If has float zero points:
31
32 flop(bit_op(weight)) - fzp
33 = sub(bit_op(weight), bias) - fzp
34 = bit_op(weight) - (fzp + bias)
35
36 where the `fzp + bias` can be computed at weight loading. But this
37 may have accuracy issue, so we should not use this in most cases.
38
39 If has not zero points:
40
41 scale(flop(bit_op(weight)))
42 = scale(sub(bit_op(weight), bias))
43 = scale(bit_op(weight)) - scale(bias)
44 = fma(bit_op(weight), scale_factor, scale(bias))
45
46 where the `scale(bias)` can be cached. But this may have accuracy issue,
47 so we should not use this in most cases.
48
49
50 ## INT8 => BF16
51
52 INT8 => BF16 is a special case, it use byte_perm instead of flop.
53 We cannot fused byte_perm with scaling.
54
55
56 ## FP4/FP8 => FP16/BF16
57
58 scale(flop(bit_op(weight)))
59 = scale(mul(bit_op(weight), multiplier))
60 = mul(bit_op(weight), scale_factor * multiplier)
61
62 where `scale_factor * multiplier` can be computed at weight loading.
63
64 */
65
66 #include "marlin_dtypes.cuh"
67
68 namespace MARLIN_NAMESPACE_NAME {
69
70 #if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 750
71 // Lookup-table based 3-input logical operation; explicitly used for
72 // dequantization as the compiler does not seem to automatically recognize it in
73 // all cases.
74 template <int lut>
75 __device__ inline int lop3(int a, int b, int c) {
76 int res;
77 asm volatile("lop3.b32 %0, %1, %2, %3, %4;\n"
78 : "=r"(res)
79 : "r"(a), "r"(b), "r"(c), "n"(lut));
80 return res;
81 }
82
83 // Constructs destination register by taking bytes from 2 sources (based on
84 // mask)
85 template <int start_byte, int mask>
86 __device__ inline uint32_t prmt(uint32_t a) {
87 uint32_t res;
88 asm volatile("prmt.b32 %0, %1, %2, %3;\n"
89 : "=r"(res)
90 : "r"(a), "n"(start_byte), "n"(mask));
91 return res;
92 }
93
94 template <typename scalar_t2, vllm::ScalarTypeId w_type_id,
95 bool skip_flop = false>
96 __device__ inline void dequant(int q, scalar_t2* frag_b);
97
98 //
99 // Efficiently dequantize 4bit values packed in an int32 value into a full
100 // B-fragment of 4 fp16 values. We mostly follow the strategy in the link below,
101 // with some small changes:
102 // - FP16:
103 // https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/cutlass_extensions/include/cutlass_extensions/interleaved_numeric_conversion.h#L215-L287
104 // - BF16:
105 // https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/cutlass_extensions/include/cutlass_extensions/interleaved_numeric_conversion.h#L327-L385
106 //
107 template <>
108 __device__ inline void dequant<half2, vllm::kU4B8.id(), true>(int q,
109 half2* frag_b) {
110 const int MASK = 0x000f000f;
111 const int EX = 0x64006400;
112 // Guarantee that the `(a & b) | c` operations are LOP3s.
113 int lo = lop3<(0xf0 & 0xcc) | 0xaa>(q, MASK, EX);
114 q >>= 4;
115 int hi = lop3<(0xf0 & 0xcc) | 0xaa>(q, MASK, EX);
116
117 frag_b[0] = *reinterpret_cast<half2*>(&lo);
118 frag_b[1] = *reinterpret_cast<half2*>(&hi);
119 }
120
121 template <>
122 __device__ inline void dequant<half2, vllm::kU4B8.id(), false>(int q,
123 half2* frag_b) {
124 const int LO = 0x000f000f;
125 const int HI = 0x00f000f0;
126 const int EX = 0x64006400;
127 // Guarantee that the `(a & b) | c` operations are LOP3s.
128 // clang-format off
129 int lo = lop3<(0xf0 & 0xcc) | 0xaa>(q, LO, EX);
130 int hi = lop3<(0xf0 & 0xcc) | 0xaa>(q, HI, EX);
131 // clang-format on
132 // We want signed int4 outputs, hence we fuse the `-8` symmetric zero point
133 // directly into `SUB` and `ADD`.
134 const int SUB = 0x64086408;
135 const int MUL = 0x2c002c00;
136 const int ADD = 0xd480d480;
137 frag_b[0] = __hsub2(*reinterpret_cast<half2*>(&lo),
138 *reinterpret_cast<const half2*>(&SUB));
139 frag_b[1] = __hfma2(*reinterpret_cast<half2*>(&hi),
140 *reinterpret_cast<const half2*>(&MUL),
141 *reinterpret_cast<const half2*>(&ADD));
142 }
143
144 template <>
145 __device__ inline void dequant<half2, vllm::kU4.id(), true>(int q,
146 half2* frag_b) {
147 dequant<half2, vllm::kU4B8.id(), true>(q, frag_b);
148 }
149
150 template <>
151 __device__ inline void dequant<half2, vllm::kU4.id(), false>(int q,
152 half2* frag_b) {
153 const int LO = 0x000f000f;
154 const int HI = 0x00f000f0;
155 const int EX = 0x64006400;
156 // Guarantee that the `(a & b) | c` operations are LOP3s.
157 // clang-format off
158 int lo = lop3<(0xf0 & 0xcc) | 0xaa>(q, LO, EX);
159 int hi = lop3<(0xf0 & 0xcc) | 0xaa>(q, HI, EX);
160 // clang-format on
161 // We want signed int4 outputs, hence we fuse the `-8` symmetric zero point
162 // directly into `SUB` and `ADD`.
163 const int SUB = 0x64006400;
164 const int MUL = 0x2c002c00;
165 const int ADD = 0xd400d400;
166 frag_b[0] = __hsub2(*reinterpret_cast<half2*>(&lo),
167 *reinterpret_cast<const half2*>(&SUB));
168 frag_b[1] = __hfma2(*reinterpret_cast<half2*>(&hi),
169 *reinterpret_cast<const half2*>(&MUL),
170 *reinterpret_cast<const half2*>(&ADD));
171 }
172
173 template <>
174 __device__ inline void dequant<nv_bfloat162, vllm::kU4B8.id(), true>(
175 int q, nv_bfloat162* frag_b) {
176 static constexpr uint32_t MASK = 0x000f000f;
177 static constexpr uint32_t EX = 0x43004300;
178
179 // Guarantee that the `(a & b) | c` operations are LOP3s.
180 // clang-format off
181 int lo = lop3<(0xf0 & 0xcc) | 0xaa>(q, MASK, EX);
182 q >>= 4;
183 int hi = lop3<(0xf0 & 0xcc) | 0xaa>(q, MASK, EX);
184 // clang-format on
185
186 frag_b[0] = *reinterpret_cast<nv_bfloat162*>(&lo);
187 frag_b[1] = *reinterpret_cast<nv_bfloat162*>(&hi);
188 }
189
190 template <>
191 __device__ inline void dequant<nv_bfloat162, vllm::kU4B8.id(), false>(
192 int q, nv_bfloat162* frag_b) {
193 dequant<nv_bfloat162, vllm::kU4B8.id(), true>(q, frag_b);
194
195 static constexpr uint32_t SUB = 0x43084308;
196
197 frag_b[0] = __hsub2(frag_b[0], *reinterpret_cast<const nv_bfloat162*>(&SUB));
198 frag_b[1] = __hsub2(frag_b[1], *reinterpret_cast<const nv_bfloat162*>(&SUB));
199 }
200
201 template <>
202 __device__ inline void dequant<nv_bfloat162, vllm::kU4.id(), true>(
203 int q, nv_bfloat162* frag_b) {
204 dequant<nv_bfloat162, vllm::kU4B8.id(), true>(q, frag_b);
205 }
206
207 template <>
208 __device__ inline void dequant<nv_bfloat162, vllm::kU4.id(), false>(
209 int q, nv_bfloat162* frag_b) {
210 dequant<nv_bfloat162, vllm::kU4.id(), true>(q, frag_b);
211
212 static constexpr uint32_t SUB = 0x43004300;
213
214 frag_b[0] = __hsub2(frag_b[0], *reinterpret_cast<const nv_bfloat162*>(&SUB));
215 frag_b[1] = __hsub2(frag_b[1], *reinterpret_cast<const nv_bfloat162*>(&SUB));
216 }
217
218 //
219 // Fast Int8ToFp16/Int8ToBf16: Efficiently dequantize 8bit int values to fp16 or
220 // bf16 Reference:
221 // - FP16:
222 // https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/cutlass_extensions/include/cutlass_extensions/interleaved_numeric_conversion.h#L53-L85
223 // - BF16:
224 // https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/cutlass_extensions/include/cutlass_extensions/interleaved_numeric_conversion.h#L125-L175
225 //
226 template <>
227 __device__ inline void dequant<half2, vllm::kU8B128.id(), true>(int q,
228 half2* frag_b) {
229 static constexpr uint32_t mask_for_elt_01 = 0x5250;
230 static constexpr uint32_t mask_for_elt_23 = 0x5351;
231 static constexpr uint32_t start_byte_for_fp16 = 0x64646464;
232
233 uint32_t lo = prmt<start_byte_for_fp16, mask_for_elt_01>(q);
234 uint32_t hi = prmt<start_byte_for_fp16, mask_for_elt_23>(q);
235
236 frag_b[0] = *reinterpret_cast<half2*>(&lo);
237 frag_b[1] = *reinterpret_cast<half2*>(&hi);
238 }
239
240 template <>
241 __device__ inline void dequant<half2, vllm::kU8B128.id(), false>(
242 int q, half2* frag_b) {
243 dequant<half2, vllm::kU8B128.id(), true>(q, frag_b);
244
245 static constexpr uint32_t I8s_TO_F16s_MAGIC_NUM = 0x64806480;
246 frag_b[0] = __hsub2(frag_b[0],
247 *reinterpret_cast<const half2*>(&I8s_TO_F16s_MAGIC_NUM));
248 frag_b[1] = __hsub2(frag_b[1],
249 *reinterpret_cast<const half2*>(&I8s_TO_F16s_MAGIC_NUM));
250 }
251
252 template <>
253 __device__ inline void dequant<half2, vllm::kU8.id(), true>(int q,
254 half2* frag_b) {
255 dequant<half2, vllm::kU8B128.id(), true>(q, frag_b);
256 }
257
258 template <>
259 __device__ inline void dequant<half2, vllm::kU8.id(), false>(int q,
260 half2* frag_b) {
261 dequant<half2, vllm::kU8.id(), true>(q, frag_b);
262
263 static constexpr uint32_t I8s_TO_F16s_MAGIC_NUM = 0x64006400;
264 frag_b[0] = __hsub2(frag_b[0],
265 *reinterpret_cast<const half2*>(&I8s_TO_F16s_MAGIC_NUM));
266 frag_b[1] = __hsub2(frag_b[1],
267 *reinterpret_cast<const half2*>(&I8s_TO_F16s_MAGIC_NUM));
268 }
269
270 template <>
271 __device__ inline void dequant<nv_bfloat162, vllm::kU8B128.id(), false>(
272 int q, nv_bfloat162* frag_b) {
273 float fp32_intermediates[4];
274 uint32_t* fp32_intermediates_casted =
275 reinterpret_cast<uint32_t*>(fp32_intermediates);
276
277 static constexpr uint32_t fp32_base = 0x4B000000;
278 fp32_intermediates_casted[0] = __byte_perm(q, fp32_base, 0x7650);
279 fp32_intermediates_casted[1] = __byte_perm(q, fp32_base, 0x7652);
280 fp32_intermediates_casted[2] = __byte_perm(q, fp32_base, 0x7651);
281 fp32_intermediates_casted[3] = __byte_perm(q, fp32_base, 0x7653);
282
283 fp32_intermediates[0] -= 8388736.f;
284 fp32_intermediates[1] -= 8388736.f;
285 fp32_intermediates[2] -= 8388736.f;
286 fp32_intermediates[3] -= 8388736.f;
287
288 uint32_t* bf16_result_ptr = reinterpret_cast<uint32_t*>(frag_b);
289 bf16_result_ptr[0] = __byte_perm(fp32_intermediates_casted[0],
290 fp32_intermediates_casted[1], 0x7632);
291 bf16_result_ptr[1] = __byte_perm(fp32_intermediates_casted[2],
292 fp32_intermediates_casted[3], 0x7632);
293 }
294
295 template <>
296 __device__ inline void dequant<nv_bfloat162, vllm::kU8.id(), false>(
297 int q, nv_bfloat162* frag_b) {
298 float fp32_intermediates[4];
299 uint32_t* fp32_intermediates_casted =
300 reinterpret_cast<uint32_t*>(fp32_intermediates);
301
302 static constexpr uint32_t fp32_base = 0x4B000000;
303 fp32_intermediates_casted[0] = __byte_perm(q, fp32_base, 0x7650);
304 fp32_intermediates_casted[1] = __byte_perm(q, fp32_base, 0x7652);
305 fp32_intermediates_casted[2] = __byte_perm(q, fp32_base, 0x7651);
306 fp32_intermediates_casted[3] = __byte_perm(q, fp32_base, 0x7653);
307
308 fp32_intermediates[0] -= 8388608.f;
309 fp32_intermediates[1] -= 8388608.f;
310 fp32_intermediates[2] -= 8388608.f;
311 fp32_intermediates[3] -= 8388608.f;
312
313 uint32_t* bf16_result_ptr = reinterpret_cast<uint32_t*>(frag_b);
314 bf16_result_ptr[0] = __byte_perm(fp32_intermediates_casted[0],
315 fp32_intermediates_casted[1], 0x7632);
316 bf16_result_ptr[1] = __byte_perm(fp32_intermediates_casted[2],
317 fp32_intermediates_casted[3], 0x7632);
318 }
319
320 template <>
321 __device__ inline void dequant<half2, vllm::kFE4M3fn.id(), true>(
322 int q, half2* frag_b) {
323 // Constants for FP8 (E4M3) and FP16 formats
324 constexpr int FP8_EXPONENT = 4, FP16_EXPONENT = 5;
325 constexpr int RIGHT_SHIFT = FP16_EXPONENT - FP8_EXPONENT;
326 constexpr int MASK = 0x7F007F00;
327
328 // Extract and shift FP8 values to FP16 format
329 int Out1 = (q & 0x80008000) | ((q & MASK) >> RIGHT_SHIFT);
330 q <<= 8;
331 int Out2 = (q & 0x80008000) | ((q & MASK) >> RIGHT_SHIFT);
332
333 // Note: reverse indexing is intentional because weights are permuted
334 frag_b[1] = *reinterpret_cast<const half2*>(&Out1);
335 frag_b[0] = *reinterpret_cast<const half2*>(&Out2);
336 }
337
338 template <>
339 __device__ inline void dequant<half2, vllm::kFE4M3fn.id(), false>(
340 int q, half2* frag_b) {
341 dequant<half2, vllm::kFE4M3fn.id(), true>(q, frag_b);
342
343 // Constants for FP8 (E4M3) and FP16 formats
344 constexpr int FP8_EXPONENT = 4, FP16_EXPONENT = 5;
345
346 // Construct and apply exponent bias
347 constexpr int BIAS_OFFSET =
348 (1 << (FP16_EXPONENT - 1)) - (1 << (FP8_EXPONENT - 1));
349 const half2 bias_reg = __float2half2_rn(float(1 << BIAS_OFFSET));
350
351 // Convert to half2 and apply bias
352 frag_b[1] = __hmul2(frag_b[1], bias_reg);
353 frag_b[0] = __hmul2(frag_b[0], bias_reg);
354 }
355
356 template <>
357 __device__ inline void dequant<nv_bfloat162, vllm::kFE4M3fn.id(), true>(
358 int q, nv_bfloat162* frag_b) {
359 // Constants for FP8 (E4M3) and BF16 formats
360 constexpr int FP8_EXPONENT = 4, BF16_EXPONENT = 8;
361 constexpr int RIGHT_SHIFT = BF16_EXPONENT - FP8_EXPONENT;
362
363 constexpr int MASK = 0x7F007F00;
364
365 // Extract and shift FP8 values to BF16 format
366 int Out1 = (q & 0x80008000) | ((q & MASK) >> RIGHT_SHIFT);
367 q <<= 8;
368 int Out2 = (q & 0x80008000) | ((q & MASK) >> RIGHT_SHIFT);
369
370 // Note: reverse indexing is intentional because weights are permuted
371 frag_b[1] = *reinterpret_cast<const nv_bfloat162*>(&Out1);
372 frag_b[0] = *reinterpret_cast<const nv_bfloat162*>(&Out2);
373 }
374
375 template <>
376 __device__ inline void dequant<nv_bfloat162, vllm::kFE4M3fn.id(), false>(
377 int q, nv_bfloat162* frag_b) {
378 dequant<nv_bfloat162, vllm::kFE4M3fn.id(), true>(q, frag_b);
379
380 // Constants for FP8 (E4M3) and BF16 formats
381 constexpr int FP8_EXPONENT = 4, BF16_EXPONENT = 8;
382
383 // Construct and apply exponent bias
384 constexpr int BIAS_OFFSET =
385 (1 << (BF16_EXPONENT - 1)) - (1 << (FP8_EXPONENT - 1));
386 // Add 127 (float exponent bias) to BIAS_OFFSET and shift to float exponent
387 // position
388 constexpr uint32_t BIAS = (BIAS_OFFSET + 127) << 23;
389 const nv_bfloat162 bias_reg =
390 __float2bfloat162_rn(*reinterpret_cast<const float*>(&BIAS));
391
392 // Convert to bfloat162 and apply bias
393 frag_b[1] = __hmul2(frag_b[1], bias_reg);
394 frag_b[0] = __hmul2(frag_b[0], bias_reg);
395 }
396
397 template <>
398 __device__ inline void dequant<half2, vllm::kFE2M1f.id(), true>(int q,
399 half2* frag_b) {
400 // Constants for FP4 (E2M1) and FP16 formats
401 constexpr int FP4_EXPONENT = 2, FP16_EXPONENT = 5;
402 constexpr int RIGHT_SHIFT = FP16_EXPONENT - FP4_EXPONENT;
403 constexpr int MASK = 0x70007000;
404
405 // Extract and shift FP4 values to FP16 format
406 int Out1 = (q & 0x80008000) | ((q & MASK) >> RIGHT_SHIFT);
407 q <<= 4;
408 int Out2 = (q & 0x80008000) | ((q & MASK) >> RIGHT_SHIFT);
409
410 // Note: reverse indexing is intentional because weights are permuted
411 frag_b[1] = *reinterpret_cast<const half2*>(&Out1);
412 frag_b[0] = *reinterpret_cast<const half2*>(&Out2);
413 }
414
415 template <>
416 __device__ inline void dequant<half2, vllm::kFE2M1f.id(), false>(
417 int q, half2* frag_b) {
418 dequant<half2, vllm::kFE2M1f.id(), true>(q, frag_b);
419
420 // Constants for FP4 (E2M1) and FP16 formats
421 constexpr int FP4_EXPONENT = 2, FP16_EXPONENT = 5;
422
423 // Construct and apply exponent bias
424 constexpr int BIAS_OFFSET =
425 (1 << (FP16_EXPONENT - 1)) - (1 << (FP4_EXPONENT - 1));
426 const half2 bias_reg = __float2half2_rn(float(1 << BIAS_OFFSET));
427
428 // Convert to half2 and apply bias
429 frag_b[1] = __hmul2(frag_b[1], bias_reg);
430 frag_b[0] = __hmul2(frag_b[0], bias_reg);
431 }
432
433 template <>
434 __device__ inline void dequant<nv_bfloat162, vllm::kFE2M1f.id(), true>(
435 int q, nv_bfloat162* frag_b) {
436 // Constants for FP4 (E2M1) and FP16 formats
437 constexpr int FP4_EXPONENT = 2, BF16_EXPONENT = 8;
438 constexpr int RIGHT_SHIFT = BF16_EXPONENT - FP4_EXPONENT;
439 constexpr int MASK = 0x70007000;
440
441 // Extract and shift FP4 values to FP16 format
442 int Out1 = (q & 0x80008000) | ((q & MASK) >> RIGHT_SHIFT);
443 q <<= 4;
444 int Out2 = (q & 0x80008000) | ((q & MASK) >> RIGHT_SHIFT);
445
446 // Note: reverse indexing is intentional because weights are permuted
447 frag_b[1] = *reinterpret_cast<const nv_bfloat162*>(&Out1);
448 frag_b[0] = *reinterpret_cast<const nv_bfloat162*>(&Out2);
449 }
450
451 template <>
452 __device__ inline void dequant<nv_bfloat162, vllm::kFE2M1f.id(), false>(
453 int q, nv_bfloat162* frag_b) {
454 dequant<nv_bfloat162, vllm::kFE2M1f.id(), true>(q, frag_b);
455
456 // Constants for FP4 (E2M1) and BF16 formats
457 constexpr int FP4_EXPONENT = 2, BF16_EXPONENT = 8;
458
459 // Construct and apply exponent bias
460 constexpr int BIAS_OFFSET =
461 (1 << (BF16_EXPONENT - 1)) - (1 << (FP4_EXPONENT - 1));
462 // Add 127 (float exponent bias) to BIAS_OFFSET and shift to float exponent
463 // position
464 constexpr uint32_t BIAS = (BIAS_OFFSET + 127) << 23;
465 const nv_bfloat162 bias_reg =
466 __float2bfloat162_rn(*reinterpret_cast<const float*>(&BIAS));
467
468 // Convert to half2 and apply bias
469 frag_b[1] = __hmul2(frag_b[1], bias_reg);
470 frag_b[0] = __hmul2(frag_b[0], bias_reg);
471 }
472
473 template <>
474 __device__ inline void dequant<__nv_fp8x4_e4m3, vllm::kFE2M1f.id(), true>(
475 int q, __nv_fp8x4_e4m3* frag_b) {
476 // Constants for FP4 (E2M1) and FP16 formats
477 constexpr int FP4_EXPONENT = 2, FP8_EXPONENT = 4;
478 constexpr int RIGHT_SHIFT = FP8_EXPONENT - FP4_EXPONENT;
479 constexpr int MASK = 0x70707070;
480
481 // Extract and shift FP4 values to FP16 format
482 int Out1 = (q & 0x80808080) | ((q & MASK) >> RIGHT_SHIFT);
483 q <<= 4;
484 int Out2 = (q & 0x80808080) | ((q & MASK) >> RIGHT_SHIFT);
485
486 // Note1: reverse indexing is intentional because weights are permuted
487 // Note2: when dequant to 8bit type, we write to `frag_b[2]` instead of
488 // `frag_b[1]` to fit the layout of tensorcore
489 frag_b[1] = *reinterpret_cast<const __nv_fp8x4_e4m3*>(&Out1);
490 frag_b[0] = *reinterpret_cast<const __nv_fp8x4_e4m3*>(&Out2);
491 }
492
493 template <>
494 __device__ inline void dequant<int32_t, vllm::kU4B8.id(), true>(
495 int q, int32_t* frag_b) {
496 constexpr int repeated_zp = 0x08080808;
497 constexpr int MASK = 0x80808080;
498
499 frag_b[0] = ((q & 0x0F0F0F0F | MASK) - repeated_zp) ^ MASK;
500 q >>= 4;
501 frag_b[1] = ((q & 0x0F0F0F0F | MASK) - repeated_zp) ^ MASK;
502 }
503
504 template <>
505 __device__ inline void dequant<__nv_fp8x4_e4m3, vllm::kU4B8.id(), true>(
506 int q, __nv_fp8x4_e4m3* frag_b) {
507 int s = q & 0x08080808;
508 int Out1 = ((q & 0x07070707) | (s << 4)) + (s >> 3);
509 q >>= 4;
510 s = q & 0x08080808;
511 int Out2 = ((q & 0x07070707) | (s << 4)) + (s >> 3);
512
513 frag_b[0] = *reinterpret_cast<const __nv_fp8x4_e4m3*>(&Out1);
514 frag_b[1] = *reinterpret_cast<const __nv_fp8x4_e4m3*>(&Out2);
515 }
516
517 template <typename scalar_t2, vllm::ScalarTypeId s_type_id>
518 __device__ inline void dequant_fp8_scales(int q, scalar_t2* frag_b);
519
520 template <>
521 __device__ inline void dequant_fp8_scales<half2, vllm::kFE4M3fn.id()>(
522 int q, half2* frag_b) {
523 int Out1 = (q & 0xFF00FF00) >> 1;
524 ;
525 q <<= 8;
526 int Out2 = (q & 0xFF00FF00) >> 1;
527
528 // Note: reverse indexing is intentional because weights are permuted
529 frag_b[1] = *reinterpret_cast<const half2*>(&Out1);
530 frag_b[0] = *reinterpret_cast<const half2*>(&Out2);
531 };
532
533 template <>
534 __device__ inline void dequant_fp8_scales<nv_bfloat162, vllm::kFE4M3fn.id()>(
535 int q, nv_bfloat162* frag_b) {
536 constexpr int FP8_EXPONENT = 4, BF16_EXPONENT = 8;
537 constexpr int RIGHT_SHIFT = BF16_EXPONENT - FP8_EXPONENT;
538 constexpr int MASK = 0x7F007F00;
539
540 // Extract and shift FP8 values to BF16 format
541 int Out1 = ((q & 0x80008000) >> 1) | ((q & MASK) >> RIGHT_SHIFT);
542 q <<= 8;
543 int Out2 = ((q & 0x80008000) >> 1) | ((q & MASK) >> RIGHT_SHIFT);
544
545 // Note: reverse indexing is intentional because weights are permuted
546 frag_b[1] = *reinterpret_cast<const nv_bfloat162*>(&Out1);
547 frag_b[0] = *reinterpret_cast<const nv_bfloat162*>(&Out2);
548 }
549
550 template <>
551 __device__ inline void dequant_fp8_scales<nv_bfloat162, vllm::kFE8M0fnu.id()>(
552 int q, nv_bfloat162* frag_b) {
553 // In this conversion, 2 ** -127 in FP8E8M0 would become 0 in BF16,
554 // but we assume that such a extreme value would not occur in real models.
555 int Out1 = (q & 0xFF00FF00) >> 1;
556 q <<= 7;
557 int Out2 = q & 0x7F807F80;
558
559 // Note: reverse indexing is intentional because weights are permuted
560 frag_b[1] = *reinterpret_cast<const nv_bfloat162*>(&Out1);
561 frag_b[0] = *reinterpret_cast<const nv_bfloat162*>(&Out2);
562 };
563
564 // subtract zero point in quanted format and then dequant
565 template <typename scalar_t2, vllm::ScalarTypeId w_type_id,
566 bool skip_flop = false>
567 __device__ inline void sub_zp_and_dequant(int q, scalar_t2* frag_b, int zp);
568
569 template <>
570 __device__ inline void sub_zp_and_dequant<int32_t, vllm::kU4.id(), true>(
571 int q, int32_t* frag_b, int zp) {
572 // INT4 with zp -> INT8
573 // see https://github.com/vllm-project/vllm/pull/24722
574 int repeated_zp = 0x01010101 * zp;
575 int MASK = 0x80808080;
576
577 frag_b[0] = ((q & 0x0F0F0F0F | MASK) - repeated_zp) ^ MASK;
578 q >>= 4;
579 frag_b[1] = ((q & 0x0F0F0F0F | MASK) - repeated_zp) ^ MASK;
580 }
581
582 template <>
583 __device__ inline void sub_zp_and_dequant<__nv_fp8x4_e4m3, vllm::kU4.id(),
584 true>(int q, __nv_fp8x4_e4m3* frag_b,
585 int zp) {
586 // INT4 with zp -> FP8
587 // see https://github.com/vllm-project/vllm/pull/24722
588 uint32_t u_q = *reinterpret_cast<uint32_t*>(&q);
589 uint32_t u_zp = *reinterpret_cast<uint32_t*>(&zp);
590 uint32_t u_zp1 = u_zp + 1;
591 uint32_t repeated_zp = 0x01010101 * u_zp;
592
593 uint32_t q0, s;
594 q0 = (u_q & 0x0F0F0F0F) | 0x70707070;
595 s = (q0 + repeated_zp) & 0x80808080;
596 uint32_t Out1 = (q0 + (s >> 7) * u_zp1) & 0x0F0F0F0F | s;
597
598 u_q >>= 4;
599 q0 = (u_q & 0x0F0F0F0F) | 0x70707070;
600 s = (q0 + repeated_zp) & 0x80808080;
601 uint32_t Out2 = (q0 + (s >> 7) * u_zp1) & 0x0F0F0F0F | s;
602
603 frag_b[0] = *reinterpret_cast<const __nv_fp8x4_e4m3*>(&Out1);
604 frag_b[1] = *reinterpret_cast<const __nv_fp8x4_e4m3*>(&Out2);
605 }
606
607 #endif
608
609 } // namespace MARLIN_NAMESPACE_NAME