-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathops.h
More file actions
2842 lines (2678 loc) · 173 KB
/
Copy pathops.h
File metadata and controls
2842 lines (2678 loc) · 173 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// vllm.cpp original (vt runtime, inventory deviation §9.1); no upstream mirror.
#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <type_traits>
#include "vt/fp8_kv.h"
#include "vt/fused_recipe.h"
#include "vt/op_provider.h"
#include "vt/tensor.h"
namespace vt {
// Upstream-compatible vllm::ScalarType IDs without importing the Marlin-only
// scalar_type.hpp into the backend-neutral public surface. The bit packing is
// ported from csrc/core/scalar_type.hpp:80-151 @ e24d1b24. Storage DType and
// semantic type are deliberately separate: DType::kI8 never guesses whether
// its bytes contain int8, FP4, FP8, or a block scale.
using ScalarTypeId = int64_t;
namespace scalar_type {
enum class NanRepr : uint8_t { kNone = 0, kIeee754 = 1, kExtendedRangeMaxMin = 2 };
constexpr ScalarTypeId Make(uint8_t exponent, uint8_t mantissa, bool is_signed,
int32_t bias, bool finite_values_only, NanRepr nan_repr) {
const uint64_t bias_bits = static_cast<uint32_t>(bias);
return static_cast<ScalarTypeId>(
static_cast<uint64_t>(exponent) |
(static_cast<uint64_t>(mantissa) << 8) |
(static_cast<uint64_t>(is_signed) << 16) |
(bias_bits << 17) |
(static_cast<uint64_t>(finite_values_only) << 49) |
(static_cast<uint64_t>(nan_repr) << 50));
}
inline constexpr ScalarTypeId kF32 = Make(8, 23, true, 0, false, NanRepr::kIeee754);
inline constexpr ScalarTypeId kF16 = Make(5, 10, true, 0, false, NanRepr::kIeee754);
inline constexpr ScalarTypeId kBF16 = Make(8, 7, true, 0, false, NanRepr::kIeee754);
inline constexpr ScalarTypeId kI8 = Make(0, 7, true, 0, false, NanRepr::kIeee754);
inline constexpr ScalarTypeId kI32 = Make(0, 31, true, 0, false, NanRepr::kIeee754);
inline constexpr ScalarTypeId kI64 = Make(0, 63, true, 0, false, NanRepr::kIeee754);
inline constexpr ScalarTypeId kI4 = Make(0, 3, true, 0, false, NanRepr::kIeee754);
inline constexpr ScalarTypeId kU4 = Make(0, 4, false, 0, false, NanRepr::kIeee754);
inline constexpr ScalarTypeId kFE2M1f = Make(2, 1, true, 0, true, NanRepr::kNone);
inline constexpr ScalarTypeId kFE4M3fn =
Make(4, 3, true, 0, true, NanRepr::kExtendedRangeMaxMin);
inline constexpr ScalarTypeId kFE8M0fnu =
Make(8, 0, false, 0, true, NanRepr::kExtendedRangeMaxMin);
} // namespace scalar_type
ScalarTypeId ToScalarType(DType dtype);
enum class KernelLayout : uint8_t {
kStrided = 0,
kPackedTwoFp4PerByte,
kBlockScaleLinear,
kBlockScaleSwizzled,
kMarlinInterleaved,
};
// Explicit output layout for dynamic NVFP4 activation block scales. Keep this
// separate from KernelLayout: it is an op argument which selects how a producer
// addresses its output, not metadata inferred from a Tensor's shape. Aligned
// linear and CUTLASS-swizzled buffers can have the same dimensions.
enum class Fp4ScaleLayout : uint8_t {
kLinear = 0,
kCutlassSwizzled,
};
struct KernelTensorDesc {
void* data = nullptr;
DType storage_dtype = DType::kF32;
ScalarTypeId scalar_type = vt::scalar_type::kF32;
Device device;
int rank = 0;
int64_t shape[kMaxRank] = {0, 0, 0, 0};
int64_t stride[kMaxRank] = {0, 0, 0, 0};
KernelLayout layout = KernelLayout::kStrided;
};
KernelTensorDesc Describe(const Tensor& tensor, ScalarTypeId scalar_type,
KernelLayout layout);
enum class OpId : uint8_t {
kMatmul,
kRmsNorm,
kSiluAndMul,
kRopeNeox,
kEmbedding,
kCausalConv1dFwd,
kCausalConv1dUpdate,
kCausalConv1dSpecUpdate,
kL2Norm,
kRmsNormGated,
kGdnPrefill,
kGdnDecode,
kGdnSpecDecode,
kGdnPackedDecode,
kKdaGatedDeltaRule,
kKdaChunkPrefill,
kMoeRouterTopK,
kMoeCombine,
kAttention,
kAttentionDenseFast,
kAttentionDenseFlash,
kDFlashBlockAttention,
kDFlashPagedBlockAttention,
kReshapeAndCache,
kConcatAndCacheMla,
kMlaDecodeAttention,
kMlaPrefillAttention,
kGatherMlaCache,
kMergeAttnStates,
kPagedAttention,
kApplyTemperature,
kGreedyArgmax,
kApplyTopKTopP,
kComputeProbs,
kComputeLogprobs,
kRandomSample,
kApplyPenalties,
kApplyMinP,
kApplyLogitBias,
kApplyTokenMask,
kApplyAllowedTokenIds,
kMatmulNvfp4,
kScaledFp4Quant,
kSiluMulFp4Quant,
kSiluAndMulFp4Quant,
kSigmoidGateFp4Quant,
kMatmulNvfp4Fp4,
kMatmulNvfp4Cutlass,
kMatmulFp8Cutlass,
kMatmulFp8CublasLt,
kQuantFp8Static,
kSwizzleBlockscale,
kMoeGroupedGemmNvfp4,
kMoeSiluMul,
kCastBf16,
kCastF32,
kMulColVecF32,
kAttnGateSplit,
kSigmoidGateBf16,
kGdnGBeta,
kGdnConvSplit,
kQkvSplit,
kSharedExpertGate,
kMoeCombineGate,
kMoeGroupedGemmNvfp4Marlin,
kGdnPostConv,
kRopeCosSinCache,
kAttnQkNormRopeGate,
// Gate-FREE sibling: per-head standard RMSNorm(q)+RMSNorm(k)+partial NeoX RoPE,
// the Qwen3-DENSE preamble. Backends may register it as kAttnQkNormRope's
// fast_op; those that do not keep the byte-exact composite automatically.
kAttnQkNormRope,
kFusedChain,
kRmsNormQuantFp8,
kRmsNormGatedQuantFp8,
kMatmulBT,
// kMatmulBT with a BLOCK-QUANTIZED [N,K] weight kept in its native ggml
// encoding — llama.cpp's `ggml_compute_forward_mul_mat`
// (ggml/src/ggml-cpu/ggml-cpu.c:1245-1443 @ 237ad9b96).
kMatmulBTQuant,
// GROUPED keep-quant GEMM over an expert-index list: out[p,:] = act[p,:] .
// weight[expert_ids[p]*N .. +N]. Collapses the DeepSeek-V4 MoE's 6 routed
// experts x {gate,up,down} = 18 tiny T=1 kMatmulBTQuant matvecs/layer into 3
// grouped kernels (fewer host launches + higher GPU occupancy). Same arithmetic
// as the per-expert loop, so BYTE-IDENTICAL on the CPU provider (which loops the
// kMatmulBTQuant kernel per group). Mirrors kMoeGroupedGemmBf16's expert-index
// structure for the keep-quant (IQ2_XXS/Q2_K/Q8_0) tower.
kMatmulBTQuantGrouped,
// W0-only raw-signature probe for the shared drop-in adapter boundary. It is
// not a production kernel-family migration.
kDropinProbe,
// In-place base/MRoPE rotation from a supplied dtype-specific global cache.
kRopeFromCache,
// Indexed persistent GDN cache boundary: one launch replaces the former
// per-row copies plus a separate BF16<->F32 cast.
kGdnStateGather,
kGdnStateScatter,
// Row gather/scatter over dim 0 (torch index_select / index_copy_). Additive
// op powering the MIXED spec+non-spec GDN batch split/merge (SPEC-MTP): gather
// the per-token conv inputs into compact spec / non-spec working buffers and
// scatter the per-group core outputs back to their original row positions.
kIndexSelect,
kIndexCopy,
// BF16 grouped-MoE GEMM: the dtype-native analog of kMoeGroupedGemmNvfp4 (no
// fp4 decode). Powers the Qwen3-Coder (Qwen3MoeForCausalLM) fast bf16 MoE path.
kMoeGroupedGemmBf16,
// Cross-family dense primitives introduced by the OPT (`OPTForCausalLM`)
// bring-up — the pre-RMSNorm/pre-SwiGLU transformer vocabulary every
// non-Qwen family needs. All three mirror torch/vLLM semantics exactly:
// kLayerNorm — `nn.LayerNorm` (mean+variance normalization with a BIAS
// term), as used by opt.py:146-148,164-166,248-251. Distinct
// from kRmsNorm, which subtracts no mean and has no bias.
// kRelu — `get_act_fn("relu")` (opt.py:156), i.e. ReLU rather than the
// SwiGLU every Qwen model uses.
// kAdd — elementwise add, plus the rank-1 row-BROADCAST form that
// applies a `nn.Linear` bias (opt.py:90-104,149-163: OPT's
// q/k/v/out/fc1/fc2 all carry `enable_bias` bias terms, which
// the bias-free Qwen projections never needed).
kLayerNorm,
kRelu,
// Elementwise GELU activations (NEW for the Qwen3-VL vision tower). Both are
// plain (non-gated) elementwise ops, may alias in-place, f32 compute →
// out-dtype store.
// kGeluTanh — `gelu_pytorch_tanh` / F.gelu(approximate="tanh"): the vision
// MLP act_fn (qwen3_vl.py::Qwen3_VisionMLP, hidden_act
// "gelu_pytorch_tanh"). Same constant as kGeluAndMul's gate.
// kGeluErf — exact erf GELU / nn.GELU(): the patch-merger act_fn
// (qwen3_vl.py::Qwen3_VisionPatchMerger, self.act_fn=nn.GELU()).
kGeluTanh,
kGeluErf,
kAdd,
// Batched dense GEMM (`torch.bmm`). The primitive MLA weight absorption is
// expressed in — mla_attention.py:789 (q-side W_UK fold) and :1034 (W_UV
// v-up-projection). See vt::BatchedMatmul.
kBatchedMatmul,
// MLA nope|rope head concatenation — upstream `concat_mla_q`
// (csrc/libtorch_stable/concat_mla_q.cuh) and `_concat_k_nope_k_pe`
// (mla_attention.py:2063-2092). See vt::ConcatMlaNopeRope.
kConcatMlaNopeRope,
// Gemma GeGLU activation: gelu_pytorch_tanh(gate) * up — the tanh-approx GELU
// on the gate half elementwise-multiplied by the up half. The GeGLU analog of
// kSiluAndMul, mirroring vLLM GeluAndMul(approximate="tanh") (activation.py).
// NEW for the Gemma family (Gemma 1/2/3/4 MLP).
kGeluAndMul,
// Elementwise multiply by a runtime scalar: out[i] = x[i] * scalar (f32
// compute, out-dtype store). The Gemma embedding normalizer
// `embed_tokens(ids) * sqrt(hidden_size)` (gemma3.py:328-341). Additive; no
// Qwen/Llama/OPT/GLM model sets it.
kMulScalar,
// Logit soft-cap: out[i] = cap * tanh(x[i] / cap) (f32 compute, out-dtype
// store). The Gemma-2 final logit soft-cap (gemma2.py:344-345,
// LogitsProcessor(soft_cap=final_logit_softcapping)) — a monotone squashing of
// the logits. NEW for the Gemma-2 family. Additive; default-unused otherwise.
kSoftCap,
// Greedy speculative-decoding rejection sampling over the EXPANDED verify
// logits `[Σ(1+k_i), vocab]` — the accept-iff-draft-equals-target-argmax rule
// plus the bonus/replacement token. Mirrors the greedy branch of vLLM's
// `_rejection_kernel` (vllm/v1/worker/gpu/spec_decode/rejection_sampler_utils.py:
// 564-585,628) + the greedy short-circuit of `_resample_kernel` (:846-861) and
// `_insert_resampled_kernel` (:828-841). See vt::GreedyRejectionSample.
// Additive: nothing on the non-speculative path calls it.
kGreedyRejectionSample,
// --- Collective transport ops (BACKEND-DISTRIBUTED-COMM W2) -----------------
// The vt::Communicator collectives, deferred from W1 (a direct method was the
// cleaner W1 gate; W2 routes them through OpProvider so a backend SUPPLIES the
// transport — CPU in-process reduce, NCCL on kCUDA, MLX-ring on kMETAL). Each
// is dispatched on the queue's DeviceType and hands the bound Communicator its
// device-specific data plane. Mirrors DeviceCommunicatorBase.all_reduce:215 /
// all_gather:219 / send:321 / recv:328 (base_device_communicator.py). See
// vt::CommAllReduceFn (include/vt/communicator.h). Additive: nothing on the
// world_size==1 single-GPU path dispatches them (the collective returns before
// the lookup — parallel_state.py:638 bypass).
kAllReduce,
kAllGather,
kSend,
kRecv,
// --- DeepSeek-V4-Flash device kernels (W7-device) --------------------------
// The four NEW V4 op families' CUDA kernels (MHC Sinkhorn+pre/post+head, DSA
// Lightning-Indexer+seams, Compressor pool+fp8_ds_mla KV, sqrtsoftplus/hash
// router+clamped SwiGLU), registered through the OpProvider seam so
// DeepseekV4Model::ForwardDevice can dispatch them. Each OpId's `fn` points at
// a family kernels-struct (deepseek_v4_device.h) of typed device launchers,
// each a 1:1 CUDA port of the landed portable HOST reference
// (deepseek_v4_{mhc,dsa,compressor,moe}.{h,cpp}) it is unit-gated against. The
// 512-wide MLA attention + expert grouped-GEMM REUSE the existing kernels
// (kMlaDecodeAttention / kMoeGroupedGemmNvfp4) and are NOT re-ported. Additive:
// nothing outside the V4 device forward dispatches them.
kDeepseekV4Mhc,
kDeepseekV4Dsa,
kDeepseekV4Compressor,
kDeepseekV4Moe,
// fp8 KV-cache STORE (KV-FP8 W1). The fp8 sibling of kReshapeAndCache: the
// paged K/V cache pages are 1-byte fp8-e4m3fn (DType::kI8 storage) and the
// write quantizes each K/V element as Quantize(hp / k_scale|v_scale). Mirrors
// reshape_and_cache_flash's fp8 branch (cache_kernels.cu:314-401,
// CopyWithScaleOp :241-252). Kept a SEPARATE op so every float-cache caller
// stays byte-identical. Appended before kCount so no existing op's id shifts.
kReshapeAndCacheFp8,
// SHARED fused routed-MoE gate+up+SwiGLU keep-quant epilogue. Promoted from the
// DeepSeek-V4-private MoeDeviceKernels::moe_gate_up_swiglu seam
// (deepseek_v4_device.h) into a first-class vt:: op so EVERY keep-quant MoE arch
// inherits the tuned single-launch kernel — the contraction-tier sibling of the
// grouped keep-quant GEMM kMatmulBTQuantGrouped. ONE launch computes, per
// (expert-slot p, mid-row j): gate=gate_w[e,j]·xq, up=up_w[e,j]·xq (shared Q8_K
// act, broadcast), then adown[p*N+j] = silu(min(gate,limit))·clamp(up,±limit) —
// gate/up never touch HBM. BIT-IDENTICAL to {2× kMatmulBTQuantGrouped +
// clamped-SwiGLU}; the CPU provider runs exactly that composite as the golden.
// Appended before kCount so no existing op's id shifts.
kMoeGateUpSwiGLUGrouped,
// SHARED fused MLA norm-rope (Tier-A5 fold; ground: DeepSeek-V4-private
// NormRopeRowsKernel, deepseek_v4.cpp Brick-7). ONE launch over the merged
// kv_a projection row [T, off+rot] computes BOTH DeepSeek-MLA decoupled-rope
// halves: latent_out[t,:off] = RmsNorm(x[t,:off]) with norm_weight (the
// kv_a_layernorm over kv_lora_rank), and pe_out[t,:rot] = RopeFromCache-rotate
// x[t,off:off+rot] (the UNNORMED, UNWEIGHTED decoupled k_pe). The two halves
// are DISJOINT dims (latent normed, rope part not), so this is BIT-IDENTICAL
// to {vt::RmsNorm(x[:,:off]); vt::RopeFromCache(x[:,off:])} — the CPU provider
// runs exactly that composite as the golden. NOTE this reads the precomputed
// cos|sin CACHE (like the MLA rope it replaces), NOT ds4's in-kernel analytic
// recompute — DeepSeek-V2/kimi rope from a cache, so the cache path is what is
// byte-exact here; ds4's own NormRopeRows stays its per-head analytic form.
// Appended before kCount so no existing op's id shifts.
kFusedNormRope,
// SHARED fused BF16 grouped-MoE gate+up+SwiGLU (Tier-A4 fold). The bf16-native
// arm of the routed-MoE gate+up+SwiGLU family (keep-quant arm is
// kMoeGateUpSwiGLUGrouped): ONE vt entry replaces the {gate grouped GEMM; up
// grouped GEMM; SiluAndMul} triplet the bf16 grouped-MoE archs (Qwen3-Coder,
// DeepSeek-V2, kimi) ran. gate_w/up_w travel as the SAME per-expert bf16
// device-pointer arrays [E] i64 kMoeGroupedGemmBf16 consumes (NOT a contiguous
// [E*N,K] tensor — the bf16 experts are separate resident allocations), plus
// the optional pair->token row_map. In the decode/non-WMMA regime the fused
// kernels compute gate+up in ONE grouped launch (reusing the exact split-K
// sequential-k accumulation) and reduce+SwiGLU in a second, dropping the two
// f32 [P,I] HBM round-trips; in the WMMA regime it reuses LaunchGroupedBf16
// twice + the byte-identical silu-mul. BIT-IDENTICAL to {2x kMoeGroupedGemmBf16
// (f32 out) + kMoeSiluMul (bf16 out)} in every regime — that composite is the
// golden the A/B unit test gates against. CUDA-only (like kMoeGroupedGemmBf16).
// Appended before kCount so no existing op's id shifts.
kMoeGroupedGemmBf16GateUpSilu,
// Laguna-S-2.1 device-resident-decode glue table (the 5 small host ops the
// NVFP4/Marlin arm still ran on the host: sequential RMSNorm, partial-NeoX RoPE,
// GQA T=1 decode attention, per-head softplus out-gate, sigmoid-noaux top-k).
// Registered on kCUDA by cuda_laguna.cu; resolved via laguna::LagunaDevice().
// BYTE-EXACT (sequential reductions) to the host Laguna forward. Additive: only
// LagunaForwardResidentDecode dispatches it. Appended before kCount (no id shift).
kLaguna,
// DENSE Marlin W4A16 GEMM (lift of vLLM's own dense marlin.cu marlin_gemm; see
// MarlinDenseGemm below). Byte-preserving replacement for the single-expert
// MoE-marlin route the dense E=1 NVFP4/MXFP4 projections use today — direct-A,
// tile-per-CTA, vLLM's own dense fp32-C_tmp reduce (no par regrouping ULP).
// CUDA-only (Blackwell sm_12xa; vendored dense marlin TUs, VT_MARLIN_NVFP4).
// Appended before kCount (no existing op's id shifts).
kMarlinDenseGemm,
// MiniMax-H3 DiT device-resident-forward glue table (brick H3-2b). Only the 3
// small ops the shared vt:: surface does NOT already cover: the two indexed
// AdaLN modulates and plain elementwise SiLU. Everything else in the DiT
// forward reuses tuned shared ops (kMatmulBT, kRmsNorm, kQkvSplit, kSiluAndMul,
// kAdd, kIndexSelect, kIndexCopy, kRopeFromCache, kDFlashBlockAttention), so
// this table stays deliberately tiny. Registered on BOTH kCPU and kCUDA
// (cpu_ops.cpp / cuda_minimax_h3.cu) so the device forward is exercised in CPU
// CI too; resolved via minimax_h3::MiniMaxH3Device(). Additive: only
// MiniMaxH3DitForwardDevice dispatches it. Appended before kCount (no id shift).
kMiniMaxH3,
// --- Conformer / FastConformer audio-encoder kernels (spike
// .agents/specs/parakeet-conformer-encoder.md rows P1/P2/P3). Three primitives
// the tree had no device op for, each mirroring a transformers 5.3.0
// `transformers/models/parakeet/modeling_parakeet.py` module and, where the
// structure is identical, vLLM's own native conformer
// (`vllm/model_executor/models/conformer_encoder.py`). See vt::Conv2d,
// vt::DepthwiseConv1d and vt::AttentionRelPos below for the exact contracts.
// Additive: nothing outside the audio-encoder path dispatches them. Appended
// before kCount so no existing op's id shifts.
kConv2d,
kDepthwiseConv1d,
kAttentionRelPos,
kCount
};
enum class WorkspaceSlot : uint8_t {
kWorkspace = 0,
kOutput,
kLse,
kSemaphore,
kDeviceScalar0,
kDeviceScalar1,
};
enum class WorkspaceInit : uint8_t {
kUninitialized = 0,
kZeroOnFirstUse,
kZeroEachUse,
};
struct WorkspaceKey {
Device device;
uint64_t queue_id = 0;
uintptr_t native_handle = 0;
OpId op = OpId::kMatmul;
WorkspaceSlot slot = WorkspaceSlot::kWorkspace;
friend bool operator==(const WorkspaceKey& a, const WorkspaceKey& b) {
return a.device == b.device && a.queue_id == b.queue_id &&
a.native_handle == b.native_handle && a.op == b.op && a.slot == b.slot;
}
};
WorkspaceKey MakeWorkspaceKey(const Queue& q, OpId op, WorkspaceSlot slot);
struct DropinProbeArgs {
ScalarTypeId scalar_type = vt::scalar_type::kF32;
KernelLayout layout = KernelLayout::kStrided;
size_t workspace_bytes = sizeof(uint32_t);
size_t workspace_alignment = alignof(uint32_t);
WorkspaceInit workspace_init = WorkspaceInit::kZeroEachUse;
WorkspaceSlot workspace_slot = WorkspaceSlot::kWorkspace;
WorkspaceSlot scalar_slot = WorkspaceSlot::kDeviceScalar0;
float scalar = 0.0f;
};
struct RmsNormArgs {
float eps = 1e-6f;
bool gemma = false; // weight applied as (1 + w), GemmaRMSNorm style
};
// torch `nn.LayerNorm` arguments (opt.py:146-148,164-166,248-251 construct it
// with the default eps=1e-5 and `elementwise_affine=config.
// layer_norm_elementwise_affine`). Unlike RmsNormArgs there is no gemma variant:
// LayerNorm subtracts the mean and applies weight AND bias.
struct LayerNormArgs {
float eps = 1e-5f;
};
struct RopeArgs {
float base = 10000.0f;
int rotary_dim = 0; // <= head_dim; even
bool is_neox_style = true;
// Empty (all zero) for 1-D RoPE. For positions[3,T], the entries are the
// temporal/height/width counts in the half-rotary frequency dimension.
std::array<int32_t, 3> mrope_section = {0, 0, 0};
bool mrope_interleaved = false;
// Llama-3 rope frequency rescaling (rope_type=="llama3", e.g. Llama-3.2). When
// llama3_scaling_factor <= 0 (the default) NO rescale is applied and the RoPE
// is byte-identical to plain RoPE — so every existing caller (Qwen, the gate
// models) that leaves these zero is UNCHANGED. When set, the base inv_freq
// (base^(-2i/rotary_dim)) is rescaled per frequency by a piecewise low/high
// wavelength interpolation, mirroring vLLM Llama3RotaryEmbedding._compute_inv_freq
// (vllm/model_executor/layers/rotary_embedding/llama3_rope.py:33-54). Consumed
// by RopeNeox + RopeCosSinCache (the cache feeds RopeFromCache, so no extra
// field is needed there).
float llama3_scaling_factor = 0.0f; // rope_scaling "factor" (0 => disabled)
float llama3_low_freq_factor = 0.0f; // rope_scaling "low_freq_factor"
float llama3_high_freq_factor = 0.0f; // rope_scaling "high_freq_factor"
float llama3_orig_max_position = 0.0f; // "original_max_position_embeddings"
};
// GDN op args (.agents/specs/gdn-semantics.md is the formula reference; sections
// cited on each op below).
struct CausalConv1dArgs {
// Upstream `activation` is "silu"/"swish" (→ silu) or None (→ identity);
// Qwen GDN always uses silu (gdn-semantics.md §2).
bool silu_activation = true;
};
struct L2NormArgs {
float eps = 1e-6f; // upstream default (gdn-semantics.md §4)
};
struct RmsNormGatedArgs {
float eps = 1e-6f;
// Gate activation: silu by default; "sigmoid" allowed by upstream
// output_gate_type (gdn-semantics.md §5). norm_before_gate=True and
// group_size=None (the only configuration Qwen GDN uses) are baked in.
bool sigmoid_gate = false;
};
struct GdnArgs {
// q scale, applied to q only after l2norm; upstream default Dk^-0.5
// (gdn-semantics.md §1). Must be set explicitly (> 0).
float scale = 0.0f;
// OPTIONAL host-resident query_start_loc[N+1] (same values as the device
// `query_start_loc` tensor). When set, the CUDA chunked-prefill path builds
// its chunk layout from these host values + a device meta-kernel, avoiding
// the per-layer D2H copy + cudaStreamSynchronize (the prefill host-tax — it
// forced host↔GPU lockstep every GDN layer, ~67% GPU-idle). nullptr => the
// path falls back to the D2H+sync (op tests / callers without host qsl).
// Mirrors the decode StepDevInputs device-resident metadata pattern.
const int32_t* query_start_loc_host = nullptr;
};
// Dense causal attention args (.agents/specs/qwen36-forward-notes.md §5 is the
// formula reference — Qwen3NextAttention's core scaled-dot-product).
struct AttentionArgs {
// Softmax scale, applied to the qk dot product. Upstream sets it to
// head_dim^-0.5 (Qwen3NextAttention.scaling). Must be set explicitly (> 0).
float scale = 0.0f;
// Causal masking: key position j attends only when j <= query position i.
// Always true for the M0.9 decoder path (bidirectional is a M1.6+ concern).
bool causal = true;
};
// --- Conformer / FastConformer audio-encoder op args (spike
// .agents/specs/parakeet-conformer-encoder.md P1/P2/P3). ------------------------
// torch `nn.Conv2d` arguments. Mirrors the constructor keywords 1:1 so a reader
// of `ParakeetEncoderSubsamplingConv2D.__init__`
// (transformers 5.3.0 transformers/models/parakeet/modeling_parakeet.py:357-390)
// can map every field by name. `groups == in_channels == out_channels` is the
// DEPTHWISE form that module's inner layers use (:377-386); `groups == 1` with
// a 1x1 kernel is its pointwise layer (:388).
struct Conv2dArgs {
int64_t stride_h = 1;
int64_t stride_w = 1;
int64_t pad_h = 0;
int64_t pad_w = 0;
int64_t dilation_h = 1;
int64_t dilation_w = 1;
int64_t groups = 1;
};
// torch `nn.Conv1d(C, C, K, stride, padding, groups=C)` arguments — the
// NON-CAUSAL depthwise conv1d of `ParakeetEncoderConvolutionModule`
// (modeling_parakeet.py:116, ctor :138-146; padding = (K-1)//2 at :136). Not to
// be confused with CausalConv1dArgs, which drives the Mamba/GDN CAUSAL conv and
// carries a persistent conv_state; this op is stateless and centre-padded.
struct DepthwiseConv1dArgs {
int64_t stride = 1;
int64_t padding = 0;
int64_t dilation = 1;
};
// Transformer-XL relative-position self-attention args — the conformer
// attention of `ParakeetEncoderAttention` (modeling_parakeet.py:259, forward
// :302-347, `_rel_shift` :349-355) and of vLLM's own native
// `RelPosMultiHeadAttention` (conformer_encoder.py:170, forward :188-217,
// `_rel_shift` :179-186). See vt::AttentionRelPos for the full formula.
struct AttentionRelPosArgs {
// Softmax scale. Upstream sets it to head_dim^-0.5 (ParakeetEncoderAttention
// .scaling :271; RelPosMultiHeadAttention.scale :174).
float scale = 0.0f;
// WHERE the scale is applied, the one arithmetic difference between the two
// upstreams. false (default) = HF's form: `matrix_bd *= scaling` (:322) and
// `attn_weights = q@k^T * scaling + matrix_bd` (eager_attention_forward :247),
// i.e. `s = ac*scale + bd*scale`. true = vLLM's native form:
// `attn_scores = (matrix_ac + matrix_bd); attn_scores.mul_(self.scale)`
// (conformer_encoder.py:212-213), i.e. `s = (ac + bd) * scale`. The two agree
// in exact arithmetic and differ in f32 rounding, so the flag is exposed
// rather than chosen, and each upstream gets its own byte-exact path.
bool scale_after_sum = false;
};
// Arguments for vt::DFlashBlockAttention — the DFlash draft's IN-BLOCK attention
// (SPEC-DFLASH D2, DF-DRAFT-MODEL; the project's FIRST bidirectional/non-causal
// attention primitive). Ported from the semantics of DFlashQwen3Attention +
// _resolve_layer_attention (vllm/model_executor/models/qwen3_dflash.py:86-146,
// 149-263 @ 555967922) and grounded in flashinfer's non-causal attention path
// (the #48167 Blackwell non-causal kernel now in-pin). This op computes attention
// for the uniform (1+k)-token QUERY block of each request over the keys IN THAT
// SAME block only (the context K/V is pre-inserted separately by the D3
// context-KV precompute; D2 isolates the block forward). Kept a SEPARATE op from
// vt::Attention / vt::PagedAttention so every CAUSAL model stays byte-identical.
//
// Per-request block boundaries come from `cu_seqlens` (host, length num_reqs+1):
// request r owns query/key rows [cu_seqlens[r], cu_seqlens[r+1]). Each query i in
// the block attends to keys j in the same block subject to:
// - full-attention layer (causal=false): ALL j in the block (BIDIRECTIONAL);
// - sliding-window layer (causal=true): j <= i AND j >= i-(window-1)
// (window<=0 means plain causal). Positions are the intra-block offsets, which
// matches DFlash's contiguous (1+k) block; the z-lab 27B window (2048) >> 17
// so the SWA layer degenerates to plain causal over the block — the mask still
// computes the true window bound for fidelity to other DFlash checkpoints.
// f32 softmax accumulation (max-subtracted), matching vLLM. GQA broadcast as in
// vt::Attention. query [T,Hq,D], key/value [T,Hkv,D], out [T,Hq,D], T = ΣblockLen.
struct DFlashBlockAttentionArgs {
float scale = 0.0f; // head_dim^-0.5 (DFlashQwen3Attention.scaling)
bool causal = false; // per-layer: false=full(non-causal), true=SWA
int64_t sliding_window = 0; // SWA window (>0); 0 = full causal when causal
const int32_t* cu_seqlens = nullptr; // host, length num_reqs+1 (block bounds)
int num_reqs = 1; // number of query blocks
};
// SPEC-DFLASH D12 Part B — CAPTURE-SAFE paged variant of DFlashBlockAttention.
// The (1+k) block queries attend over [persistent PAGED context ; their own (1+k)
// block] exactly as DFlashBlockAttention does over a materialized [context; block]
// combined buffer, but the growing context enters as DATA (a paged K/V cache +
// per-request block_table + seq_lens) instead of a variable-size combined buffer —
// so the launch grid is STATIC over the fixed (1+k)*num_reqs query rows and every
// metadata input is a persistent DEVICE tensor read in place (NO function-local
// host upload, the cudagraph-capture-bakes-stack-addresses UAF class the eager
// DFlashBlockAttention launcher had). Same f32 online-softmax recurrence + the D2
// in-block mask (full/non-causal or causal-SWA), applied over the COMBINED index
// (context rows [0,C_r) then block rows [C_r, C_r+blen_r)); the context is always
// position-ordered (ascending), so the combined mask matches the materialized one
// bit-for-bit. CUDA is capture-safe; CPU is the reference; a unit test cross-checks
// both against DFlashBlockAttention over an explicit combined buffer.
struct DFlashPagedBlockAttentionArgs {
float scale = 0.0f; // head_dim^-0.5
bool causal = false; // false=full(non-causal); true=causal-SWA
int64_t sliding_window = 0; // SWA window (>0); 0 = full causal when causal
int num_reqs = 1; // number of (1+k) query blocks
int64_t block_size = 0; // rows per paged context page (>0)
};
// Backend-neutral local-attention window, matching FlashAttention's
// `window_size=(left, right)` convention. The bounds are inclusive distances
// from the bottom-right-aligned absolute query position: (W-1, 0) is a causal
// decoder window of W tokens and (W-1, W-1) is the symmetric encoder form.
// Full attention is represented by std::nullopt on PagedAttentionArgs, never by
// a backend-specific sentinel pair.
struct AttentionWindow {
int32_t left = 0;
int32_t right = 0;
};
// Paged attention args (M1.6). Same softmax convention as AttentionArgs — the
// paged op generalizes the dense M0.9 attention to the varlen/batched/paged
// case and MUST agree with it on the single-sequence contiguous read.
struct PagedAttentionArgs {
// Softmax scale, applied to the qk dot product (upstream FlashAttentionImpl
// self.scale = head_size^-0.5). Must be set explicitly (> 0).
float scale = 0.0f;
// Causal masking: a query token at absolute position p attends only to key
// positions j <= p. True for the decoder path; non-causal carried for
// fidelity (matches AttentionArgs.causal).
bool causal = true;
// OPTIONAL local-attention bounds. For an absolute query position p, visible
// keys are intersected with [p-left, p+right] after the causal/full bound is
// applied. Query positions use FlashAttention's bottom-right alignment:
// p = seq_len - query_len + local_query_index. std::nullopt preserves the
// existing full causal/non-causal behavior exactly.
std::optional<AttentionWindow> window_size = std::nullopt;
// OPTIONAL attention logit soft-cap (vLLM Attention(logits_soft_cap=...),
// gemma2.py:202 attn_logit_softcapping). When > 0 each pre-softmax score S is
// replaced by cap * tanh(S / cap) before the online softmax. 0.0 (default)
// leaves the plain scaled-dot path byte-identical — every existing model uses
// the default, so this is diff-inert for them. Gemma-2/4 set it (50.0).
float logits_soft_cap = 0.0f;
// OPTIONAL host-resident query_start_loc[num_reqs+1] (same values as the
// device `query_start_loc` tensor). When set, the CUDA prefill flash/WMMA
// launchers size the per-request query-tile grid from these host values and
// build the device tile array with a device meta-kernel, avoiding the
// per-layer D2H copy + cudaStreamSynchronize that drained the pipeline every
// full-attention prefill layer (~10-12 syncs/step; prefill only 43.7%
// GPU-busy). nullptr => the launcher falls back to the D2H+sync (op unit tests
// / callers without a host qsl). Mirrors GdnArgs::query_start_loc_host and the
// decode StepDevInputs device-resident metadata pattern.
const int32_t* query_start_loc_host = nullptr;
// OPTIONAL host-known max context length in this batch (max over the device
// `seq_lens` values = CommonAttentionMetadata::max_seq_len; an upper bound is
// safe — it only sizes grids/rounded dims, per-request geometry stays on the
// device values). When > 0 the FA-2 prefill launcher sizes its grid without a
// device read (companion to query_start_loc_host). 0 => that launcher falls
// back to the D2H+sync.
int32_t max_seq_len = 0;
// OPTIONAL fp8 KV-cache read (KV-FP8 W1). kAuto (default) => the cache holds
// the model float dtype and is read directly — every existing caller is
// byte-identical. When != kAuto the K/V cache pages are 1-byte fp8 (DType::kI8
// storage) and each read is DEQUANTIZED as Dequant(fp8) * k_scale|v_scale
// before entering the f32 softmax, mirroring the fp8 attention read path
// (scaled_vec_conversion<float,uint8_t>, quant_utils.cuh:302-308). k_scale /
// v_scale are the per-tensor scales from BaseKVCacheMethod (kv_cache.py:108-191)
// — 1.0 is the uncalibrated default. Per-head scales are a later brick.
Fp8KVCacheDataType kv_cache_dtype = Fp8KVCacheDataType::kAuto;
float k_scale = 1.0f;
float v_scale = 1.0f;
};
// Arguments for vt::MlaDecodeAttention (MLA campaign W4). Mirrors the scalar
// arguments `TritonMLAImpl.forward_mqa` passes to `decode_attention_fwd`
// (vllm/v1/attention/backends/mla/triton_mla.py:242-259 @ e24d1b24).
struct MlaDecodeAttentionArgs {
// `self.scale` — for DeepSeek this is head_dim^-0.5 TIMES the YaRN mscale^2
// correction (mla_attention.py computes it once and hands it to the kernel as
// a plain float; the kernel itself knows nothing about mscale). Must be > 0.
float scale = 0.0f;
// NUM_KV_SPLITS. 0 (the default) => the impl computes it exactly like
// `_compute_num_kv_splits` (triton_mla.py:40-47):
// min(next_pow2(max(1, max_seq_len // 512)), sm_count * 2)
// from `max_seq_len` below. 1 forces the single-split (batch-invariant)
// reduction upstream uses under VLLM_BATCH_INVARIANT (triton_mla.py:212-213).
int32_t num_kv_splits = 0;
// Host-known max over `seq_lens` (CommonAttentionMetadata::max_seq_len). Only
// used to derive `num_kv_splits` when that is 0; an upper bound is safe. When
// both are 0 the impl falls back to 1 split.
int32_t max_seq_len = 0;
};
// Arguments for vt::MlaPrefillAttention (MLA campaign W5). Mirrors the scalar
// arguments `FlashAttnPrefillBackend` passes to `flash_attn_varlen_func`
// (vllm/v1/attention/backends/mla/prefill/flash_attn.py:205-248 @ e24d1b24).
struct MlaPrefillAttentionArgs {
// `self.scale` — head_dim^-0.5 TIMES the YaRN mscale^2 correction for
// DeepSeek, handed to the kernel as a plain float (`flash_attn.py:222,245`).
float scale = 0.0f;
// `causal=True` for the NEW-TOKENS call (`flash_attn.py:223`), `causal=False`
// for every CONTEXT-CHUNK call (`:246`, "Context is unmasked"). Causal here is
// FlashAttention's BOTTOM-RIGHT alignment: query index i of a request whose
// query length is Lq and key length is Lk sees keys j <= i + (Lk - Lq).
bool causal = true;
// Host-known max over the per-request query / key lengths
// (`max_seqlen_q` / `max_seqlen_k`, `flash_attn.py:220-221,243-244`). Used for
// GRID SIZING and the rounded dims only — the per-request geometry reads the
// DEVICE cu_seqlens, so an UPPER BOUND is safe. 0 => the launcher falls back
// to a small D2H + sync (op unit tests / callers without host lengths), the
// same fallback the FA-2 paged prefill launcher uses.
int32_t max_seqlen_q = 0;
int32_t max_seqlen_k = 0;
};
// Router SCORING function. softmax over all E is the Qwen3.6 / DeepSeek-V2
// behavior; sigmoid (elementwise, NOT normalized across experts) is what
// DeepSeek-V3 / R1 use with `topk_method == "noaux_tc"`. Mirrors
// vllm/model_executor/layers/fused_moe/router/grouped_topk_router.py:110-117.
enum class MoeScoringFunc {
kSoftmax, // scores = softmax(gating_output, dim=-1) (:111-112)
kSigmoid, // scores = gating_output.sigmoid() (:113-114)
};
// MoE router top-k args (.agents/specs/moe-semantics.md §3 is the formula reference).
//
// The four fields below `renormalize` are the W3 GROUPED-TOPK (`noaux_tc`)
// extension, ported from grouped_topk_router.py:80-161 @ pin e24d1b24. They are
// ADDITIVE: every field defaults to the pre-W3 behavior, and `num_expert_group
// == 0` selects the original ungrouped softmax+top-k path VERBATIM (a separate
// kernel — the existing one is not touched), so the 27B / 35B / Coder / dense
// routers stay byte-identical.
struct MoeRouterTopKArgs {
// Number of experts selected per token (top_k = num_experts_per_tok).
int top_k = 0;
// renormalize = norm_topk_prob (True for Qwen3.6, moe-semantics.md §1/§3):
// divide the k selected softmax probs by their sum (denom>0 guard).
bool renormalize = true;
// --- grouped-topk (`noaux_tc`) extension ---------------------------------
// scoring_func: softmax (V2 / Qwen) vs sigmoid (V3 / R1).
MoeScoringFunc scoring_func = MoeScoringFunc::kSoftmax;
// num_expert_group == config.n_group. 0 DISABLES grouping entirely (the
// pre-W3 path). When > 0 it must divide num_experts exactly.
int num_expert_group = 0;
// topk_group == config.topk_group: how many expert GROUPS survive the
// first-level mask. Must be in [1, num_expert_group] when grouping is on.
int topk_group = 0;
// routed_scaling_factor: a final multiply on the routing weights
// (grouped_topk_router.py:159-160; deepseek_v2.py:288). 1.0 == no-op.
float routed_scaling_factor = 1.0f;
};
// Kernel registration contract. Backends register one kernel per (OpId,
// DeviceType); the kernel's signature must match the alias for its op
// exactly. Register with a static_cast against the alias so signature drift
// is a compile error:
// RegisterOp(OpId::kMatmul, DeviceType::kCPU,
// reinterpret_cast<void*>(static_cast<MatmulFn>(&MatmulKernel)));
// The public op functions below validate arguments, then dispatch through
// these types. A kernel that does not support a validated dtype combination
// must throw loudly, never silently truncate.
using MatmulFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
using MatmulNvfp4Fn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&, float);
using ScaledFp4QuantFn =
void (*)(Queue&, Tensor&, Tensor&, const Tensor&, float, Fp4ScaleLayout);
using SiluMulFp4QuantFn =
void (*)(Queue&, Tensor&, Tensor&, const Tensor&, const Tensor&, float,
Fp4ScaleLayout);
using SiluAndMulFp4QuantFn =
void (*)(Queue&, Tensor&, Tensor&, const Tensor&, float, Fp4ScaleLayout);
using SigmoidGateFp4QuantFn =
void (*)(Queue&, Tensor&, Tensor&, const Tensor&, const Tensor&, float,
Fp4ScaleLayout);
using MatmulNvfp4Fp4Fn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&, const Tensor&, float);
using MatmulNvfp4CutlassFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor*, float);
using MatmulFp8CutlassFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, float);
using MatmulFp8CublasLtFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, float);
using QuantFp8StaticFn = void (*)(Queue&, Tensor&, const Tensor&, float);
using RmsNormQuantFp8Fn = void (*)(Queue&, Tensor& /*out_fp8*/, Tensor* /*out_bf16*/,
const Tensor& /*x*/, const Tensor& /*weight*/,
const RmsNormArgs&, Tensor* /*residual*/, float /*input_scale*/);
using RmsNormGatedQuantFp8Fn = void (*)(Queue&, Tensor& /*out_fp8*/, const Tensor& /*x*/,
const Tensor& /*gate*/, const Tensor& /*weight*/,
const RmsNormGatedArgs&, float /*input_scale*/);
using SwizzleBlockscaleFn = void (*)(Queue&, Tensor&, const Tensor&);
// vt::BatchedMatmul (`torch.bmm`) — same shape as MatmulFn but rank-3 and
// stride-driven; a distinct alias so registrations read unambiguously.
using BatchedMatmulFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
// vt::ConcatMlaNopeRope — out[..., :Dn] = nope, out[..., Dn:] = rope.
using ConcatMlaNopeRopeFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
using MoeGroupedGemmNvfp4Fn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor*, const Tensor&,
const Tensor&, const Tensor&);
using MoeGroupedGemmBf16Fn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor*, const Tensor&);
// kMoeGroupedGemmBf16GateUpSilu: out[P,N] bf16 = silu(gate)*up, gate/up = grouped
// bf16 GEMM of act[T|P,K] against per-expert weight-pointer arrays gate_ptrs/
// up_ptrs [E] i64, expert_ids[P] i32, optional row_map[P] i32. Same convention as
// MoeGroupedGemmBf16 with a SECOND weight-pointer array + the fused SwiGLU epilogue.
using MoeGroupedGemmBf16GateUpSiluFn =
void (*)(Queue&, Tensor& /*out*/, const Tensor& /*act*/, const Tensor& /*expert_ids*/,
const Tensor* /*row_map*/, const Tensor& /*gate_ptrs*/, const Tensor& /*up_ptrs*/);
// kMatmulBTQuantGrouped: out[P,N], act[P,K] (f32/bf16), weight[E*N,K] block-quant,
// expert_ids[P] i32 — weight row for (p,n) is expert_ids[p]*N + n.
using MatmulBTQuantGroupedFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&);
// kMoeGateUpSwiGLUGrouped: out[P,N] f32 adown, act[Pa,K] (Pa==1 broadcast),
// gate_w/up_w[E*N,K] SAME block-quant dtype, expert_ids[P] i32, float limit. See
// vt::MoeGateUpSwiGLUGrouped / OpId::kMoeGateUpSwiGLUGrouped.
using MoeGateUpSwiGLUGroupedFn =
void (*)(Queue&, Tensor& /*out*/, const Tensor& /*act*/, const Tensor& /*gate_w*/,
const Tensor& /*up_w*/, const Tensor& /*expert_ids*/, float /*limit*/);
// Marlin NVFP4 W4A16 grouped-MoE GEMM (lift of vLLM moe_wna16_marlin_gemm; see
// MoeGroupedGemmNvfp4Marlin below). Scalar params travel in MoeMarlinArgs.
struct MoeMarlinArgs {
int moe_block_size = 0; // vLLM moe_align_block_size block (16..64, or 8)
int top_k = 0;
int size_m = 0; // number of tokens (rows of `a`)
int size_n = 0; // output features
int size_k = 0; // input features (contraction; multiple of 16)
bool mul_topk_weights = false; // fold topk_weights into the output (down proj)
// Block-scale format selector. Default = NVFP4 (fp8-e4m3 scales, group 16,
// per-tensor global scale). group_size 32 + mxfp4=true selects the MXFP4 path
// (E8M0/UE8M0 scales => s_type kFE8M0fnu, group_blocks 2, NO global scale;
// the `global_scale` tensor is ignored). Mirrors vLLM's is_nvfp4 branch.
int group_size = 16;
bool mxfp4 = false;
};
using MoeGroupedGemmNvfp4MarlinFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&, const Tensor&, Tensor&,
const Tensor&, const Tensor&, const Tensor&, const Tensor&, const MoeMarlinArgs&);
// DENSE Marlin W4A16 GEMM (lift of vLLM's own dense marlin_gemm; see
// MarlinDenseGemm below). Scalar params travel in MarlinDenseArgs. Unlike the
// MoE path there is NO moe_align gather (sorted_token_ids/expert_ids/top_k):
// `a` is a plain [size_m, size_k] contiguous activation (lda = size_k).
struct MarlinDenseArgs {
int size_m = 0; // number of tokens (rows of `a`)
int size_n = 0; // output features
int size_k = 0; // input features (contraction; multiple of 16)
// Block-scale format selector, identical semantics to MoeMarlinArgs: default =
// NVFP4 (fp8-e4m3 scales, group 16, per-tensor global scale). group_size 32 +
// mxfp4=true selects the MXFP4 path (E8M0 scales, group_blocks 2, NO global
// scale; the `global_scale` tensor is ignored). Mirrors vLLM's is_nvfp4 branch.
int group_size = 16;
bool mxfp4 = false;
};
using MarlinDenseGemmFn =
void (*)(Queue&, Tensor& /*c*/, const Tensor& /*a*/, const Tensor& /*b_q_weight*/,
const Tensor& /*b_scales*/, const Tensor& /*global_scale*/, Tensor& /*workspace*/,
const MarlinDenseArgs&);
using MoeSiluMulFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
// --- Qwen3.6 elementwise "glue" ops (M0.9 forward). These replace host-side
// loops so the decode step can run entirely on-device (CUDA-graph capture).
// All math in f32; dims are inferred from the tensor shapes (no args structs).
using CastBf16Fn = void (*)(Queue&, Tensor&, const Tensor&);
using CastF32Fn = void (*)(Queue&, Tensor&, const Tensor&);
using MulColVecF32Fn = void (*)(Queue&, Tensor&, const Tensor&);
using AttnGateSplitFn = void (*)(Queue&, Tensor&, Tensor&, const Tensor&);
using SigmoidGateBf16Fn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
using GdnGBetaFn = void (*)(Queue&, Tensor&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&);
using GdnConvSplitFn = void (*)(Queue&, Tensor&, Tensor&, Tensor&, const Tensor&);
using QkvSplitFn = void (*)(Queue&, Tensor&, Tensor&, Tensor&, const Tensor&);
// Fused GDN post-conv prep (mirror of fla fused_gdn_prefill_post_conv):
// conv-split + q/k l2norm + g/beta gating in ONE launch. eps travels in
// L2NormArgs (the q/k l2norm eps; softplus threshold 20 baked in as in GdnGBeta).
using GdnPostConvFn = void (*)(Queue&, Tensor&, Tensor&, Tensor&, Tensor&, Tensor&, const Tensor&,
const Tensor&, const Tensor&, const Tensor&, const Tensor&,
const L2NormArgs&);
// Per-step RoPE cos|sin cache fill (fused-attn-preamble prep): cos_sin[T,rot] f32
// from positions[T] (RopeArgs.base/rotary_dim). Cols [0,rot/2)=cos, [rot/2,rot)=sin.
using RopeCosSinCacheFn = void (*)(Queue&, Tensor&, const Tensor&, const RopeArgs&);
// Fused full-attention preamble (split q|gate + gemma qk-RMSNorm + partial NeoX
// RoPE-from-cache + gate passthrough) in ONE launch. See AttnQkNormRopeGate below.
using AttnQkNormRopeGateFn =
void (*)(Queue&, Tensor&, Tensor&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, const RmsNormArgs&, const RopeArgs&);
// Gate-free fused preamble (kAttnQkNormRope): q3/k3 are normed and rotated IN
// PLACE, so the op takes no separate outputs. See the recipe in recipes.h.
using AttnQkNormRopeFn =
void (*)(Queue&, Tensor&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const RmsNormArgs&, const RopeArgs&);
using SharedExpertGateFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
using RmsNormFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const RmsNormArgs&, Tensor*);
using SiluAndMulFn = void (*)(Queue&, Tensor&, const Tensor&);
using GeluAndMulFn = void (*)(Queue&, Tensor&, const Tensor&);
using MulScalarFn = void (*)(Queue&, Tensor&, const Tensor&, double);
using SoftCapFn = void (*)(Queue&, Tensor&, const Tensor&, double);
using LayerNormFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor*, const Tensor*,
const LayerNormArgs&);
using ReluFn = void (*)(Queue&, Tensor&, const Tensor&);
using AddFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
using EmbeddingFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
using RopeFn = void (*)(Queue&, Tensor&, Tensor&, const Tensor&, const RopeArgs&);
using RopeFromCacheFn = void (*)(Queue&, Tensor&, Tensor*, const Tensor&,
const Tensor&, const RopeArgs&);
// Fused MLA norm-rope (kFusedNormRope): latent RmsNorm + decoupled-pe
// RopeFromCache over one merged kv_a row, in ONE launch. See vt::FusedNormRope.
using FusedNormRopeFn = void (*)(Queue&, Tensor&, Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, const RmsNormArgs&,
const RopeArgs&);
using CausalConv1dFwdFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor*,
Tensor&, const Tensor&, const Tensor&,
const CausalConv1dArgs&);
using CausalConv1dUpdateFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&,
const Tensor*, Tensor&, const Tensor*,
const CausalConv1dArgs&);
using L2NormFn = void (*)(Queue&, Tensor&, const Tensor&, const L2NormArgs&);
using RmsNormGatedFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const RmsNormGatedArgs&);
using GdnPrefillFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, Tensor&, const Tensor&,
const GdnArgs&);
using GdnDecodeFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, Tensor&, const Tensor*,
const GdnArgs&);
using GdnSpecDecodeFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, Tensor&, const Tensor&,
const Tensor&, const Tensor&, const GdnArgs&);
using CausalConv1dSpecUpdateFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&,
const Tensor*, Tensor&, const Tensor&, const Tensor&,
const Tensor&, const CausalConv1dArgs&);
using GdnPackedDecodeFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, Tensor&, const Tensor&,
const GdnArgs&);
// Per-k-channel-decay gated-delta recurrence (KDA). Same shape as GdnPrefillFn;
// the ONLY difference is g is [T,Hv,Dk] (per-channel) not [T,Hv] (per-head).
using KdaGatedDeltaRuleFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, Tensor&, const Tensor&,
const GdnArgs&);
// KDA CHUNK-PREFILL: the chunked (WY-representation) forward of the SAME
// per-K-channel gated-delta linear attention as KdaGatedDeltaRule, but processing
// the whole prompt in BT=64 chunks through the vendored FLA Triton-AOT cubins
// (vLLM's actual prefill kernels) instead of the token-sequential recurrence.
// Takes the RAW gate projection g_raw + a_log + dt_bias (the gate is fused
// on-device by kda_gate_cumsum), NOT a pre-gated per-channel decay.
using KdaChunkPrefillFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, const Tensor&, const Tensor&,
Tensor&, const Tensor&, const GdnArgs&);
using GdnStateGatherFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor*);
using GdnStateScatterFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
using IndexSelectFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
using IndexCopyFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
using MoeRouterTopKFn = void (*)(Queue&, Tensor&, Tensor&, const Tensor&,
const MoeRouterTopKArgs&, const Tensor*);
using MoeCombineFn =
void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor*);
using MoeCombineGateFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&);
using AttentionFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const AttentionArgs&);
// Conformer / FastConformer audio-encoder kernels (spike P1/P2/P3).
using Conv2dFn = void (*)(Queue&, Tensor& /*out*/, const Tensor& /*x*/, const Tensor& /*weight*/,
const Tensor* /*bias*/, const Conv2dArgs&);
using DepthwiseConv1dFn = void (*)(Queue&, Tensor& /*out*/, const Tensor& /*x*/,
const Tensor& /*weight*/, const Tensor* /*bias*/,
const DepthwiseConv1dArgs&);
using AttentionRelPosFn = void (*)(Queue&, Tensor& /*out*/, const Tensor& /*query*/,
const Tensor& /*key*/, const Tensor& /*value*/,
const Tensor& /*rel_key*/, const Tensor* /*bias_u*/,
const Tensor* /*bias_v*/, const Tensor* /*key_mask*/,
const AttentionRelPosArgs&);
using DFlashBlockAttentionFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&,
const Tensor&, const DFlashBlockAttentionArgs&);
using DFlashPagedBlockAttentionFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, const Tensor&,
const DFlashPagedBlockAttentionArgs&);
using ReshapeAndCacheFn = void (*)(Queue&, const Tensor&, const Tensor&, Tensor&, Tensor&,
const Tensor&);
// fp8 KV-cache store (KV-FP8 W1). k_cache/v_cache are 1-byte fp8 (DType::kI8);
// each element is stored as Quantize(hp / k_scale|v_scale). `kind` selects the
// fp8 interpretation (kFp8E4M3 landed; kFp8E5M2 is a later brick).
using ReshapeAndCacheFp8Fn = void (*)(Queue&, const Tensor& /*k*/, const Tensor& /*v*/,
Tensor& /*k_cache*/, Tensor& /*v_cache*/,
const Tensor& /*slot_mapping*/, Fp8KVCacheDataType /*kind*/,
float /*k_scale*/, float /*v_scale*/);
using ConcatAndCacheMlaFn =
void (*)(Queue&, const Tensor&, const Tensor&, Tensor&, const Tensor&);
using MlaDecodeAttentionFn = void (*)(Queue&, Tensor&, Tensor*, const Tensor&, const Tensor&,
const Tensor&, const Tensor&,
const MlaDecodeAttentionArgs&);
using MlaPrefillAttentionFn = void (*)(Queue&, Tensor&, Tensor*, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, const Tensor&,
const MlaPrefillAttentionArgs&);
using GatherMlaCacheFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor*, int64_t);
using MergeAttnStatesFn = void (*)(Queue&, Tensor&, Tensor*, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, int64_t);
using PagedAttentionFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, const Tensor&,
const PagedAttentionArgs&);
// --- V1 sampling ops (M1.7 Task 2). See the sampling-op section at the bottom.
using ApplyTemperatureFn = void (*)(Queue&, Tensor&, const Tensor&, bool);
using GreedyArgmaxFn = void (*)(Queue&, Tensor&, const Tensor&);
using ApplyTopKTopPFn = void (*)(Queue&, Tensor&, const Tensor*, const Tensor*);
using ComputeProbsFn = void (*)(Queue&, Tensor&, const Tensor&);
using ComputeLogprobsFn = void (*)(Queue&, Tensor&, const Tensor&);
using RandomSampleFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&);
// --- Greedy spec-decode rejection sampling (SPEC-REJECTION I3).
using GreedyRejectionSampleFn = void (*)(Queue&, Tensor&, Tensor&, const Tensor&, const Tensor&,
const Tensor&);
// --- V1 penalty / mask / builtin-proc ops (M1.7 Task 3). See the section at the
// bottom of this header for the full contracts.
using ApplyPenaltiesFn = void (*)(Queue&, Tensor&, const Tensor&, const Tensor&, const Tensor&,
const Tensor&, const Tensor&, const Tensor&);