nbxtensor 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. nbxtensor/THIRD_PARTY_NOTICES.md +287 -0
  2. nbxtensor/__init__.py +17 -0
  3. nbxtensor/backend/__init__.py +1 -0
  4. nbxtensor/backend/capabilities.py +207 -0
  5. nbxtensor/backend/cpu/__init__.py +1 -0
  6. nbxtensor/backend/cpu/blas.py +102 -0
  7. nbxtensor/backend/cpu/pool.py +204 -0
  8. nbxtensor/backend/cpu_backend.py +162 -0
  9. nbxtensor/backend/device_facts.py +105 -0
  10. nbxtensor/backend/device_files.py +129 -0
  11. nbxtensor/backend/device_names.py +18 -0
  12. nbxtensor/backend/devices/amd/cdna.yml +228 -0
  13. nbxtensor/backend/devices/amd/cdna2.yml +228 -0
  14. nbxtensor/backend/devices/amd/cdna3.yml +227 -0
  15. nbxtensor/backend/devices/apple/apple_m1.yml +266 -0
  16. nbxtensor/backend/devices/apple/apple_m1_max.yml +266 -0
  17. nbxtensor/backend/devices/apple/apple_m1_pro.yml +266 -0
  18. nbxtensor/backend/devices/apple/apple_m1_ultra.yml +266 -0
  19. nbxtensor/backend/devices/apple/apple_m2.yml +266 -0
  20. nbxtensor/backend/devices/apple/apple_m2_max.yml +266 -0
  21. nbxtensor/backend/devices/apple/apple_m2_pro.yml +266 -0
  22. nbxtensor/backend/devices/apple/apple_m2_ultra.yml +266 -0
  23. nbxtensor/backend/devices/apple/apple_m3.yml +266 -0
  24. nbxtensor/backend/devices/apple/apple_m3_max.yml +266 -0
  25. nbxtensor/backend/devices/apple/apple_m3_pro.yml +266 -0
  26. nbxtensor/backend/devices/apple/apple_m3_ultra.yml +266 -0
  27. nbxtensor/backend/devices/apple/apple_m4.yml +266 -0
  28. nbxtensor/backend/devices/apple/apple_m4_max.yml +266 -0
  29. nbxtensor/backend/devices/apple/apple_m4_pro.yml +308 -0
  30. nbxtensor/backend/devices/apple/apple_m5.yml +269 -0
  31. nbxtensor/backend/devices/apple/apple_m5_max.yml +269 -0
  32. nbxtensor/backend/devices/apple/apple_m5_pro.yml +269 -0
  33. nbxtensor/backend/devices/apple/apple_m5_ultra.yml +269 -0
  34. nbxtensor/backend/devices/apple/apple_m6.yml +269 -0
  35. nbxtensor/backend/devices/apple/apple_silicon.yml +263 -0
  36. nbxtensor/backend/devices/nvidia/ampere.yml +214 -0
  37. nbxtensor/backend/devices/nvidia/hopper.yml +213 -0
  38. nbxtensor/backend/devices/nvidia/volta.yml +411 -0
  39. nbxtensor/backend/faults.py +151 -0
  40. nbxtensor/backend/host_memory.py +185 -0
  41. nbxtensor/backend/launcher.py +2350 -0
  42. nbxtensor/backend/launcher_contract.py +429 -0
  43. nbxtensor/backend/metal_backend.py +661 -0
  44. nbxtensor/backend/metal_device.py +1274 -0
  45. nbxtensor/backend/process_memory.py +73 -0
  46. nbxtensor/backend/replay_graph.py +322 -0
  47. nbxtensor/backend/runtime.py +409 -0
  48. nbxtensor/backend/triton_ext_driver.py +735 -0
  49. nbxtensor/dtype/__init__.py +1 -0
  50. nbxtensor/dtype/convert.py +107 -0
  51. nbxtensor/dtype/dtypes.yml +70 -0
  52. nbxtensor/dtype/itemsize.py +166 -0
  53. nbxtensor/dtype/nbx_dtype.py +72 -0
  54. nbxtensor/dtype/operands.py +158 -0
  55. nbxtensor/dtype/outputs.py +64 -0
  56. nbxtensor/dtype/policy.py +155 -0
  57. nbxtensor/dtype/policy.yml +51 -0
  58. nbxtensor/dtype/promotion.py +112 -0
  59. nbxtensor/host.py +55 -0
  60. nbxtensor/io/__init__.py +1 -0
  61. nbxtensor/io/read_rate.py +197 -0
  62. nbxtensor/io/safetensors.py +314 -0
  63. nbxtensor/memory/__init__.py +9 -0
  64. nbxtensor/memory/allocator.py +1988 -0
  65. nbxtensor/memory/arena.py +75 -0
  66. nbxtensor/memory/device_memory.py +150 -0
  67. nbxtensor/memory/device_transfer.py +154 -0
  68. nbxtensor/memory/layout.py +43 -0
  69. nbxtensor/memory/memory_pool.py +191 -0
  70. nbxtensor/memory/metal.py +96 -0
  71. nbxtensor/memory/oom_advice.py +91 -0
  72. nbxtensor/memory/pointer_tables.py +323 -0
  73. nbxtensor/memory/pool.py +507 -0
  74. nbxtensor/memory/shadow.py +43 -0
  75. nbxtensor/memory/slab.py +273 -0
  76. nbxtensor/memory/trace_replay.py +122 -0
  77. nbxtensor/memory/traces/sana-video-2b-720p.16g.2026-11-02.tsv +13610 -0
  78. nbxtensor/memory/vmm.py +118 -0
  79. nbxtensor/ops/__init__.py +1 -0
  80. nbxtensor/ops/_autotune_policy.py +90 -0
  81. nbxtensor/ops/_common.py +32 -0
  82. nbxtensor/ops/_configs.py +1041 -0
  83. nbxtensor/ops/_cost.py +231 -0
  84. nbxtensor/ops/abs.py +17 -0
  85. nbxtensor/ops/adaptive_avg_pool2d.py +161 -0
  86. nbxtensor/ops/add.py +96 -0
  87. nbxtensor/ops/addcdiv.py +29 -0
  88. nbxtensor/ops/addcmul.py +29 -0
  89. nbxtensor/ops/addmv_op.py +64 -0
  90. nbxtensor/ops/addr.py +68 -0
  91. nbxtensor/ops/all_reduce.py +83 -0
  92. nbxtensor/ops/amax.py +40 -0
  93. nbxtensor/ops/any_reduce.py +83 -0
  94. nbxtensor/ops/arange_op.py +27 -0
  95. nbxtensor/ops/argmax.py +107 -0
  96. nbxtensor/ops/argmin.py +93 -0
  97. nbxtensor/ops/atan2.py +71 -0
  98. nbxtensor/ops/attention_wrappers.py +1648 -0
  99. nbxtensor/ops/autotune_bucket.py +137 -0
  100. nbxtensor/ops/autotune_cache.py +430 -0
  101. nbxtensor/ops/autotune_certified.py +1262 -0
  102. nbxtensor/ops/autotune_refusals.py +380 -0
  103. nbxtensor/ops/autotune_settings.py +58 -0
  104. nbxtensor/ops/avg_pool2d.py +73 -0
  105. nbxtensor/ops/baddbmm_op.py +132 -0
  106. nbxtensor/ops/batch_norm.py +163 -0
  107. nbxtensor/ops/binary_strided.py +196 -0
  108. nbxtensor/ops/bitwise_and.py +26 -0
  109. nbxtensor/ops/bitwise_left_shift.py +47 -0
  110. nbxtensor/ops/bitwise_not.py +24 -0
  111. nbxtensor/ops/bitwise_or.py +26 -0
  112. nbxtensor/ops/bitwise_right_shift.py +47 -0
  113. nbxtensor/ops/bucketize.py +37 -0
  114. nbxtensor/ops/cat_op.py +87 -0
  115. nbxtensor/ops/celu.py +20 -0
  116. nbxtensor/ops/clamp.py +24 -0
  117. nbxtensor/ops/clamp_min.py +22 -0
  118. nbxtensor/ops/conv2d.py +265 -0
  119. nbxtensor/ops/conv2d_m8n8k4.py +165 -0
  120. nbxtensor/ops/conv_depthwise2d.py +154 -0
  121. nbxtensor/ops/conv_transpose2d.py +148 -0
  122. nbxtensor/ops/conv_wrappers.py +1482 -0
  123. nbxtensor/ops/copy.py +18 -0
  124. nbxtensor/ops/copy_op.py +39 -0
  125. nbxtensor/ops/cos.py +17 -0
  126. nbxtensor/ops/count_nonzero.py +64 -0
  127. nbxtensor/ops/cross_entropy.py +67 -0
  128. nbxtensor/ops/cummax.py +109 -0
  129. nbxtensor/ops/cummin.py +105 -0
  130. nbxtensor/ops/cumsum.py +244 -0
  131. nbxtensor/ops/decode_attn_vec.py +162 -0
  132. nbxtensor/ops/depthwise_conv2d.py +132 -0
  133. nbxtensor/ops/dequant_gemv.py +190 -0
  134. nbxtensor/ops/dft.py +67 -0
  135. nbxtensor/ops/dispatch.py +1228 -0
  136. nbxtensor/ops/div.py +75 -0
  137. nbxtensor/ops/dot_op.py +67 -0
  138. nbxtensor/ops/dropout.py +23 -0
  139. nbxtensor/ops/dtype_convert.py +42 -0
  140. nbxtensor/ops/elementwise_launch.py +81 -0
  141. nbxtensor/ops/elementwise_wrappers.py +2315 -0
  142. nbxtensor/ops/elu.py +20 -0
  143. nbxtensor/ops/embedding.py +69 -0
  144. nbxtensor/ops/eq.py +32 -0
  145. nbxtensor/ops/erf.py +18 -0
  146. nbxtensor/ops/exp.py +17 -0
  147. nbxtensor/ops/exp2.py +18 -0
  148. nbxtensor/ops/fft_op.py +125 -0
  149. nbxtensor/ops/fill_op.py +20 -0
  150. nbxtensor/ops/flash_attention.py +307 -0
  151. nbxtensor/ops/flash_attention_m8n8k4.py +384 -0
  152. nbxtensor/ops/flash_decode.py +78 -0
  153. nbxtensor/ops/flip_op.py +60 -0
  154. nbxtensor/ops/floor.py +90 -0
  155. nbxtensor/ops/full_op.py +24 -0
  156. nbxtensor/ops/fused_moe.py +417 -0
  157. nbxtensor/ops/fused_moe_m8n8k4.py +99 -0
  158. nbxtensor/ops/fused_upsample_conv.py +413 -0
  159. nbxtensor/ops/gather_op.py +52 -0
  160. nbxtensor/ops/ge.py +32 -0
  161. nbxtensor/ops/gelu.py +31 -0
  162. nbxtensor/ops/gemv_vec.py +80 -0
  163. nbxtensor/ops/glu.py +43 -0
  164. nbxtensor/ops/grid_sampler.py +145 -0
  165. nbxtensor/ops/group_norm_tiled.py +67 -0
  166. nbxtensor/ops/groupnorm.py +123 -0
  167. nbxtensor/ops/gt.py +32 -0
  168. nbxtensor/ops/hardsigmoid.py +18 -0
  169. nbxtensor/ops/hardswish.py +19 -0
  170. nbxtensor/ops/im2col.py +57 -0
  171. nbxtensor/ops/index_add.py +95 -0
  172. nbxtensor/ops/index_put_op.py +92 -0
  173. nbxtensor/ops/index_select.py +121 -0
  174. nbxtensor/ops/index_wrappers.py +770 -0
  175. nbxtensor/ops/isfinite.py +20 -0
  176. nbxtensor/ops/isinf.py +20 -0
  177. nbxtensor/ops/isnan.py +18 -0
  178. nbxtensor/ops/launch_keys.py +1054 -0
  179. nbxtensor/ops/launch_observer.py +54 -0
  180. nbxtensor/ops/layernorm.py +71 -0
  181. nbxtensor/ops/le.py +35 -0
  182. nbxtensor/ops/leaky_relu.py +19 -0
  183. nbxtensor/ops/lerp.py +76 -0
  184. nbxtensor/ops/linalg_wrappers.py +1370 -0
  185. nbxtensor/ops/linspace_op.py +47 -0
  186. nbxtensor/ops/log.py +17 -0
  187. nbxtensor/ops/log_sigmoid.py +23 -0
  188. nbxtensor/ops/logical_and.py +26 -0
  189. nbxtensor/ops/logical_not.py +24 -0
  190. nbxtensor/ops/logical_or.py +26 -0
  191. nbxtensor/ops/logical_xor.py +37 -0
  192. nbxtensor/ops/loss_wrappers.py +282 -0
  193. nbxtensor/ops/lt.py +32 -0
  194. nbxtensor/ops/masked_fill.py +21 -0
  195. nbxtensor/ops/matmul.py +377 -0
  196. nbxtensor/ops/matmul_m8n8k4.py +235 -0
  197. nbxtensor/ops/max_pool2d.py +74 -0
  198. nbxtensor/ops/max_pool3d.py +93 -0
  199. nbxtensor/ops/max_reduce.py +91 -0
  200. nbxtensor/ops/maximum.py +18 -0
  201. nbxtensor/ops/mean.py +40 -0
  202. nbxtensor/ops/min_reduce.py +91 -0
  203. nbxtensor/ops/minimum.py +18 -0
  204. nbxtensor/ops/mish.py +22 -0
  205. nbxtensor/ops/mma_m8n8k4.py +152 -0
  206. nbxtensor/ops/moe_align.py +195 -0
  207. nbxtensor/ops/moe_decode_vec.py +374 -0
  208. nbxtensor/ops/moe_device_residency.py +141 -0
  209. nbxtensor/ops/moe_wrappers.py +345 -0
  210. nbxtensor/ops/mse_loss.py +79 -0
  211. nbxtensor/ops/mul.py +51 -0
  212. nbxtensor/ops/nan_to_num.py +23 -0
  213. nbxtensor/ops/ne.py +32 -0
  214. nbxtensor/ops/neg.py +17 -0
  215. nbxtensor/ops/nllloss.py +124 -0
  216. nbxtensor/ops/nonzero.py +33 -0
  217. nbxtensor/ops/nonzero_op.py +62 -0
  218. nbxtensor/ops/norm_wrappers.py +579 -0
  219. nbxtensor/ops/ones_op.py +20 -0
  220. nbxtensor/ops/oracles/__init__.py +6 -0
  221. nbxtensor/ops/oracles/conv2d_fp64.py +450 -0
  222. nbxtensor/ops/pad_op.py +159 -0
  223. nbxtensor/ops/paged_decode_attn.py +215 -0
  224. nbxtensor/ops/pixel_shuffle.py +144 -0
  225. nbxtensor/ops/pixel_unshuffle.py +72 -0
  226. nbxtensor/ops/pow.py +137 -0
  227. nbxtensor/ops/prod.py +81 -0
  228. nbxtensor/ops/rand_op.py +52 -0
  229. nbxtensor/ops/random_wrappers.py +366 -0
  230. nbxtensor/ops/reciprocal.py +17 -0
  231. nbxtensor/ops/reduction_wrappers.py +845 -0
  232. nbxtensor/ops/relu.py +18 -0
  233. nbxtensor/ops/remainder.py +46 -0
  234. nbxtensor/ops/repeat_interleave.py +48 -0
  235. nbxtensor/ops/repeat_op.py +132 -0
  236. nbxtensor/ops/residual_chain.py +374 -0
  237. nbxtensor/ops/rmsnorm.py +68 -0
  238. nbxtensor/ops/rope.py +142 -0
  239. nbxtensor/ops/rsqrt.py +17 -0
  240. nbxtensor/ops/scaled_softmax.py +96 -0
  241. nbxtensor/ops/scatter_op.py +139 -0
  242. nbxtensor/ops/screen_oracle.py +479 -0
  243. nbxtensor/ops/selu.py +22 -0
  244. nbxtensor/ops/shape_wrappers.py +1032 -0
  245. nbxtensor/ops/sigmoid.py +19 -0
  246. nbxtensor/ops/silu.py +27 -0
  247. nbxtensor/ops/sin.py +17 -0
  248. nbxtensor/ops/softmax.py +169 -0
  249. nbxtensor/ops/softplus.py +27 -0
  250. nbxtensor/ops/sort_op.py +304 -0
  251. nbxtensor/ops/spectral_wrappers.py +481 -0
  252. nbxtensor/ops/sqrt.py +17 -0
  253. nbxtensor/ops/stack_op.py +84 -0
  254. nbxtensor/ops/std.py +113 -0
  255. nbxtensor/ops/strided_copy.py +200 -0
  256. nbxtensor/ops/sub.py +35 -0
  257. nbxtensor/ops/sum.py +40 -0
  258. nbxtensor/ops/tan.py +19 -0
  259. nbxtensor/ops/tanh.py +21 -0
  260. nbxtensor/ops/threshold.py +19 -0
  261. nbxtensor/ops/tiling.yml +91 -0
  262. nbxtensor/ops/tiling_policy.py +43 -0
  263. nbxtensor/ops/tiling_sizes.py +370 -0
  264. nbxtensor/ops/topk.py +267 -0
  265. nbxtensor/ops/trace_op.py +64 -0
  266. nbxtensor/ops/tril.py +64 -0
  267. nbxtensor/ops/triu.py +62 -0
  268. nbxtensor/ops/unfold_op.py +37 -0
  269. nbxtensor/ops/unique_op.py +97 -0
  270. nbxtensor/ops/upsample_bilinear2d.py +106 -0
  271. nbxtensor/ops/upsample_nearest2d.py +52 -0
  272. nbxtensor/ops/upsample_nearest3d.py +61 -0
  273. nbxtensor/ops/var.py +136 -0
  274. nbxtensor/ops/vector_norm.py +216 -0
  275. nbxtensor/ops/weight_norm.py +104 -0
  276. nbxtensor/ops/where.py +39 -0
  277. nbxtensor/ops/wrappers.py +825 -0
  278. nbxtensor/ops/zeros_op.py +20 -0
  279. nbxtensor/quant/__init__.py +1 -0
  280. nbxtensor/quant/quantized_tensor.py +187 -0
  281. nbxtensor/random/__init__.py +1 -0
  282. nbxtensor/random/mt_randn.py +154 -0
  283. nbxtensor/random/rng_pin.py +59 -0
  284. nbxtensor/random/rng_stream.py +118 -0
  285. nbxtensor/random/seeded_draw.py +223 -0
  286. nbxtensor/shape/__init__.py +157 -0
  287. nbxtensor/tensor.py +1867 -0
  288. nbxtensor-0.0.1.dist-info/METADATA +379 -0
  289. nbxtensor-0.0.1.dist-info/RECORD +291 -0
  290. nbxtensor-0.0.1.dist-info/WHEEL +4 -0
  291. nbxtensor-0.0.1.dist-info/licenses/LICENSE +189 -0
@@ -0,0 +1,417 @@
1
+ """Fused MoE kernels — pure @triton.jit, zero torch.
2
+
3
+ fused_moe_kernel: Grouped GEMM for all experts in one launch.
4
+ Expert weights accessed via absolute pointer table (int64 per expert).
5
+ Same approach works for scattered arena allocations — no offset arithmetic.
6
+
7
+ silu_and_mul_kernel: Fused SwiGLU activation.
8
+ """
9
+
10
+ import triton
11
+ import triton.language as tl
12
+
13
+ from nbxtensor.ops._configs import nbx_autotune
14
+
15
+ # The tile of a fused MoE launch beyond its row block: BLOCK_SIZE_M is the alignment's block (`moe_align_block_size`,
16
+ # the caller's), every other parameter a candidate the certification measures per key — the former fixed tile
17
+ # (64 x 32, 4 warps, 2 stages: the best of a nine-tile grid on DeepSeek and Qwen3 prefills, 2026-11-03) among them.
18
+ # BLOCK_SIZE_K divides the int4 kernel's quantisation group (128), so a K tile never straddles two groups.
19
+ _FUSED_MOE_CONFIGS = [
20
+ triton.Config({'BLOCK_SIZE_N': bn, 'BLOCK_SIZE_K': bk, 'GROUP_SIZE_M': 8}, num_warps=w, num_stages=st)
21
+ for bn in (32, 64, 128) for bk in (32, 64) for w in (4, 8) for st in (2, 3)
22
+ ]
23
+ #: the autotune key of a fused MoE launch: the GEMM's dims, the valid (token, expert) pairs' bucket — the prefill's
24
+ #: length class, the same for the gate/up and the down launch of a layer — the experts the pointer table holds (how
25
+ #: many rows each expert's blocks get), and the epilogue flags
26
+ _FUSED_MOE_KEY = ['N', 'K', 'NV_BUCKET', 'NUM_EXPERTS', 'MUL_ROUTED_WEIGHT', 'TOPK_DIVIDE']
27
+
28
+
29
+ @nbx_autotune(configs=_FUSED_MOE_CONFIGS, key=_FUSED_MOE_KEY, cache_results=True)
30
+ @triton.jit
31
+ def fused_moe_kernel(
32
+ # Pointers to matrices
33
+ a_ptr, # activations [M, K]
34
+ expert_ptrs_ptr, # [E] int64 — absolute weight pointers
35
+ c_ptr, # output [num_tokens_post_padded, N]
36
+ topk_weights_ptr, # routing scores [M * top_k]
37
+ sorted_token_ids_ptr, # sorted token indices
38
+ expert_ids_ptr, # expert id per BLOCK_M group
39
+ num_tokens_post_padded_ptr, # [1] total tokens after padding
40
+ # Matrix dimensions
41
+ N, # output feature dim
42
+ K, # input feature dim (reduction)
43
+ EM, # total sorted entries (with padding)
44
+ num_valid_tokens, # M * top_k (before padding)
45
+ NV_BUCKET, # num_valid_tokens' bucket: the autotune key's length class (unused in the body)
46
+ NUM_EXPERTS, # the pointer table's experts: the autotune key's (unused in the body)
47
+ # Strides (shared across all experts — same shape)
48
+ stride_am, stride_ak,
49
+ stride_bk, stride_bn,
50
+ stride_cm, stride_cn,
51
+ # Meta-parameters
52
+ BLOCK_SIZE_M: tl.constexpr,
53
+ MUL_ROUTED_WEIGHT: tl.constexpr,
54
+ top_k: tl.constexpr,
55
+ compute_type: tl.constexpr,
56
+ TOPK_DIVIDE: tl.constexpr = True,
57
+ BLOCK_SIZE_N: tl.constexpr = 64,
58
+ BLOCK_SIZE_K: tl.constexpr = 32,
59
+ GROUP_SIZE_M: tl.constexpr = 8,
60
+ ):
61
+ """Grouped GEMM with absolute expert pointer table.
62
+
63
+ Each BLOCK_M block of sorted tokens shares one expert. The kernel loads
64
+ that expert's absolute weight pointer from the table and uses it directly.
65
+ """
66
+ pid = tl.program_id(axis=0).to(tl.int64)
67
+ num_pid_m = tl.cdiv(EM, BLOCK_SIZE_M)
68
+ num_pid_n = tl.cdiv(N, BLOCK_SIZE_N)
69
+ num_pid_in_group = GROUP_SIZE_M * num_pid_n
70
+ group_id = pid // num_pid_in_group
71
+ first_pid_m = group_id * GROUP_SIZE_M
72
+ group_size_m = min(num_pid_m - first_pid_m, GROUP_SIZE_M)
73
+ pid_m = first_pid_m + ((pid % num_pid_in_group) % group_size_m)
74
+ pid_n = (pid % num_pid_in_group) // group_size_m
75
+
76
+ num_tokens_post_padded = tl.load(num_tokens_post_padded_ptr)
77
+ # A block beyond the padded token count reads and writes nothing: its
78
+ # sorted-token and expert entries are not written by moe_align, so every
79
+ # access below carries `block_valid` (no early exit — unstructured
80
+ # control flow has no lowering on every backend).
81
+ block_valid = pid_m * BLOCK_SIZE_M < num_tokens_post_padded
82
+
83
+ # Load sorted token IDs for this block
84
+ offs_token_id = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
85
+ offs_token = tl.load(sorted_token_ids_ptr + offs_token_id, mask=block_valid, other=0).to(tl.int64)
86
+ token_mask = (offs_token < num_valid_tokens) & block_valid
87
+
88
+ # Load expert id → load absolute weight pointer from table
89
+ off_experts = tl.load(expert_ids_ptr + pid_m, mask=block_valid, other=0)
90
+ expert_ptr_int = tl.load(expert_ptrs_ptr + off_experts, mask=block_valid, other=0)
91
+ # Bitcast int64 to pointer — reinterpret bits as address
92
+ b_base = tl.cast(expert_ptr_int, tl.pointer_type(compute_type), bitcast=True)
93
+
94
+ # INT64 offsets for pointer arithmetic within the expert weight matrix
95
+ offs_bn = (pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N).to(tl.int64)) % N
96
+ offs_k = tl.arange(0, BLOCK_SIZE_K).to(tl.int64)
97
+
98
+ if TOPK_DIVIDE:
99
+ a_idx = offs_token[:, None] // top_k
100
+ else:
101
+ a_idx = offs_token[:, None]
102
+ a_ptrs = a_ptr + (a_idx * stride_am + offs_k[None, :] * stride_ak)
103
+
104
+ # B pointers: absolute expert pointer + in-matrix offsets
105
+ b_ptrs = b_base + (offs_k[:, None] * stride_bk + offs_bn[None, :] * stride_bn)
106
+
107
+ accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
108
+ for k in range(0, tl.cdiv(K, BLOCK_SIZE_K)):
109
+ a = tl.load(
110
+ a_ptrs,
111
+ mask=token_mask[:, None] & (offs_k[None, :] < K - k * BLOCK_SIZE_K),
112
+ other=0.0,
113
+ )
114
+ b = tl.load(
115
+ b_ptrs,
116
+ mask=(offs_k[:, None] < K - k * BLOCK_SIZE_K) & block_valid,
117
+ other=0.0,
118
+ )
119
+ # NOTE: Phase 1.5 Step 1 (2026-05) tested 3-arg `tl.dot(a, b, acc)`
120
+ # form — measured 0% gain on V100 sm_70. Reverted to 2-arg form.
121
+ # See kernels/ops/matmul.py:matmul_kernel docstring for full audit.
122
+ accumulator += tl.dot(a, b)
123
+ a_ptrs += BLOCK_SIZE_K * stride_ak
124
+ b_ptrs += BLOCK_SIZE_K * stride_bk
125
+
126
+ if MUL_ROUTED_WEIGHT:
127
+ moe_weight = tl.load(
128
+ topk_weights_ptr + offs_token,
129
+ mask=token_mask,
130
+ other=0,
131
+ )
132
+ accumulator = accumulator * moe_weight[:, None]
133
+
134
+ accumulator = accumulator.to(compute_type)
135
+ offs_cn = (pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)).to(tl.int64)
136
+ c_ptrs = c_ptr + stride_cm * offs_token[:, None] + stride_cn * offs_cn[None, :]
137
+ c_mask = token_mask[:, None] & (offs_cn[None, :] < N)
138
+ tl.store(c_ptrs, accumulator, mask=c_mask)
139
+
140
+
141
+ @triton.jit
142
+ def fused_moe_wna16_kernel(
143
+ a_ptr, # activations [M, K]
144
+ qw_ptrs_ptr, # [E] int64 — packed int4 [K//8, N] per expert
145
+ sc_ptrs_ptr, # [E] int64 — scales fp16 [K//G, N]
146
+ mn_ptrs_ptr, # [E] int64 — mins fp16 [K//G, N]
147
+ c_ptr,
148
+ topk_weights_ptr,
149
+ sorted_token_ids_ptr,
150
+ expert_ids_ptr,
151
+ num_tokens_post_padded_ptr,
152
+ N, K, EM, num_valid_tokens,
153
+ stride_am, stride_ak,
154
+ stride_qk, stride_qn, # packed strides (rows of int32)
155
+ stride_sg, stride_sn, # scales/mins strides
156
+ stride_cm, stride_cn,
157
+ BLOCK_SIZE_M: tl.constexpr,
158
+ BLOCK_SIZE_N: tl.constexpr,
159
+ BLOCK_SIZE_K: tl.constexpr,
160
+ GROUP_SIZE_M: tl.constexpr,
161
+ MUL_ROUTED_WEIGHT: tl.constexpr,
162
+ top_k: tl.constexpr,
163
+ compute_type: tl.constexpr,
164
+ QGROUP: tl.constexpr, # quant group size along K (128)
165
+ QPACK: tl.constexpr, # nibbles per int32 (8)
166
+ TOPK_DIVIDE: tl.constexpr = True,
167
+ ):
168
+ """Grouped GEMM over int4-g128-asym experts — in-register dequant.
169
+
170
+ Structure mirrors fused_moe_kernel exactly (same pid mapping, same
171
+ K-loop, same routed-weight epilogue); the B tile is rebuilt each
172
+ iteration from the expert's packed triplet with the tier's
173
+ CANONICAL dequant (pure fp32: q*scale+min — contraction-immune,
174
+ int4 x fp16 products exact in fp32) and the dot runs fp32 x fp32
175
+ (on sm_70 fp16 tl.dot lowers to FMA fp32 anyway — Phase 1.5
176
+ audit). BLOCK_SIZE_K must divide QGROUP so a K-tile never crosses
177
+ a quant-group boundary (one scales/mins row vector per
178
+ iteration). SPLIT_K = 1, no atomics (tier byte contract). The
179
+ byte oracle is fused_moe_fp32b_kernel below — textually identical
180
+ minus the unpack.
181
+ """
182
+ pid = tl.program_id(axis=0).to(tl.int64)
183
+ num_pid_m = tl.cdiv(EM, BLOCK_SIZE_M)
184
+ num_pid_n = tl.cdiv(N, BLOCK_SIZE_N)
185
+ num_pid_in_group = GROUP_SIZE_M * num_pid_n
186
+ group_id = pid // num_pid_in_group
187
+ first_pid_m = group_id * GROUP_SIZE_M
188
+ group_size_m = min(num_pid_m - first_pid_m, GROUP_SIZE_M)
189
+ pid_m = first_pid_m + ((pid % num_pid_in_group) % group_size_m)
190
+ pid_n = (pid % num_pid_in_group) // group_size_m
191
+
192
+ num_tokens_post_padded = tl.load(num_tokens_post_padded_ptr)
193
+ # A block beyond the padded token count reads and writes nothing: its
194
+ # sorted-token and expert entries are not written by moe_align, so every
195
+ # access below carries `block_valid` (no early exit — unstructured
196
+ # control flow has no lowering on every backend).
197
+ block_valid = pid_m * BLOCK_SIZE_M < num_tokens_post_padded
198
+
199
+ offs_token_id = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
200
+ offs_token = tl.load(sorted_token_ids_ptr + offs_token_id, mask=block_valid, other=0).to(tl.int64)
201
+ token_mask = (offs_token < num_valid_tokens) & block_valid
202
+
203
+ off_experts = tl.load(expert_ids_ptr + pid_m, mask=block_valid, other=0)
204
+ qw_base = tl.cast(tl.load(qw_ptrs_ptr + off_experts, mask=block_valid, other=0),
205
+ tl.pointer_type(tl.int32), bitcast=True)
206
+ sc_base = tl.cast(tl.load(sc_ptrs_ptr + off_experts, mask=block_valid, other=0),
207
+ tl.pointer_type(tl.float16), bitcast=True)
208
+ mn_base = tl.cast(tl.load(mn_ptrs_ptr + off_experts, mask=block_valid, other=0),
209
+ tl.pointer_type(tl.float16), bitcast=True)
210
+
211
+ offs_bn = (pid_n * BLOCK_SIZE_N
212
+ + tl.arange(0, BLOCK_SIZE_N).to(tl.int64)) % N
213
+ offs_k = tl.arange(0, BLOCK_SIZE_K).to(tl.int64)
214
+
215
+ if TOPK_DIVIDE:
216
+ a_idx = offs_token[:, None] // top_k
217
+ else:
218
+ a_idx = offs_token[:, None]
219
+ a_ptrs = a_ptr + (a_idx * stride_am + offs_k[None, :] * stride_ak)
220
+
221
+ accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
222
+ for k in range(0, tl.cdiv(K, BLOCK_SIZE_K)):
223
+ k_rem = K - k * BLOCK_SIZE_K
224
+ a = tl.load(
225
+ a_ptrs,
226
+ mask=token_mask[:, None] & (offs_k[None, :] < k_rem),
227
+ other=0.0,
228
+ )
229
+ cur_k = k * BLOCK_SIZE_K + offs_k
230
+ packed = tl.load(
231
+ qw_base + (cur_k[:, None] // QPACK) * stride_qk
232
+ + offs_bn[None, :] * stride_qn,
233
+ mask=(offs_k[:, None] < k_rem) & block_valid, other=0)
234
+ q = (packed >> ((cur_k[:, None] % QPACK) * 4)) & 0xF
235
+ g = k * BLOCK_SIZE_K // QGROUP # BLOCK_SIZE_K divides QGROUP
236
+ scale = tl.load(sc_base + g * stride_sg + offs_bn * stride_sn, mask=block_valid, other=0.0)
237
+ mn = tl.load(mn_base + g * stride_sg + offs_bn * stride_sn, mask=block_valid, other=0.0)
238
+ # Canonical dequant — the tier's contraction-immune fp32 form,
239
+ # textually identical to the GEMV family's.
240
+ b = (q.to(tl.float32) * scale[None, :].to(tl.float32)
241
+ + mn[None, :].to(tl.float32))
242
+ b = tl.where(offs_k[:, None] < k_rem, b, 0.0)
243
+ accumulator += tl.dot(a.to(tl.float32), b)
244
+ a_ptrs += BLOCK_SIZE_K * stride_ak
245
+
246
+ if MUL_ROUTED_WEIGHT:
247
+ moe_weight = tl.load(
248
+ topk_weights_ptr + offs_token,
249
+ mask=token_mask,
250
+ other=0,
251
+ )
252
+ accumulator = accumulator * moe_weight[:, None]
253
+
254
+ accumulator = accumulator.to(compute_type)
255
+ offs_cn = (pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)).to(tl.int64)
256
+ c_ptrs = c_ptr + stride_cm * offs_token[:, None] + stride_cn * offs_cn[None, :]
257
+ c_mask = token_mask[:, None] & (offs_cn[None, :] < N)
258
+ tl.store(c_ptrs, accumulator, mask=c_mask)
259
+
260
+
261
+ @triton.jit
262
+ def fused_moe_fp32b_kernel(
263
+ a_ptr,
264
+ expert_ptrs_ptr, # [E] int64 — dense FP32 [K, N] per expert
265
+ c_ptr,
266
+ topk_weights_ptr,
267
+ sorted_token_ids_ptr,
268
+ expert_ids_ptr,
269
+ num_tokens_post_padded_ptr,
270
+ N, K, EM, num_valid_tokens,
271
+ stride_am, stride_ak,
272
+ stride_bk, stride_bn,
273
+ stride_cm, stride_cn,
274
+ BLOCK_SIZE_M: tl.constexpr,
275
+ BLOCK_SIZE_N: tl.constexpr,
276
+ BLOCK_SIZE_K: tl.constexpr,
277
+ GROUP_SIZE_M: tl.constexpr,
278
+ MUL_ROUTED_WEIGHT: tl.constexpr,
279
+ top_k: tl.constexpr,
280
+ compute_type: tl.constexpr,
281
+ TOPK_DIVIDE: tl.constexpr = True,
282
+ ):
283
+ """The wna16 kernel's BYTE ORACLE: dense fp32 expert weights (the
284
+ family's standalone dequant output), identical pid mapping, K-loop,
285
+ fp32 dot and epilogue — textually the kernel above minus the
286
+ unpack. Never used on a hot path; exists so the fused W4 kernel is
287
+ provably byte-identical to dequantize-then-grouped-GEMM."""
288
+ pid = tl.program_id(axis=0).to(tl.int64)
289
+ num_pid_m = tl.cdiv(EM, BLOCK_SIZE_M)
290
+ num_pid_n = tl.cdiv(N, BLOCK_SIZE_N)
291
+ num_pid_in_group = GROUP_SIZE_M * num_pid_n
292
+ group_id = pid // num_pid_in_group
293
+ first_pid_m = group_id * GROUP_SIZE_M
294
+ group_size_m = min(num_pid_m - first_pid_m, GROUP_SIZE_M)
295
+ pid_m = first_pid_m + ((pid % num_pid_in_group) % group_size_m)
296
+ pid_n = (pid % num_pid_in_group) // group_size_m
297
+
298
+ num_tokens_post_padded = tl.load(num_tokens_post_padded_ptr)
299
+ # A block beyond the padded token count reads and writes nothing: its
300
+ # sorted-token and expert entries are not written by moe_align, so every
301
+ # access below carries `block_valid` (no early exit — unstructured
302
+ # control flow has no lowering on every backend).
303
+ block_valid = pid_m * BLOCK_SIZE_M < num_tokens_post_padded
304
+
305
+ offs_token_id = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
306
+ offs_token = tl.load(sorted_token_ids_ptr + offs_token_id, mask=block_valid, other=0).to(tl.int64)
307
+ token_mask = (offs_token < num_valid_tokens) & block_valid
308
+
309
+ off_experts = tl.load(expert_ids_ptr + pid_m, mask=block_valid, other=0)
310
+ b_base = tl.cast(tl.load(expert_ptrs_ptr + off_experts, mask=block_valid, other=0),
311
+ tl.pointer_type(tl.float32), bitcast=True)
312
+
313
+ offs_bn = (pid_n * BLOCK_SIZE_N
314
+ + tl.arange(0, BLOCK_SIZE_N).to(tl.int64)) % N
315
+ offs_k = tl.arange(0, BLOCK_SIZE_K).to(tl.int64)
316
+
317
+ if TOPK_DIVIDE:
318
+ a_idx = offs_token[:, None] // top_k
319
+ else:
320
+ a_idx = offs_token[:, None]
321
+ a_ptrs = a_ptr + (a_idx * stride_am + offs_k[None, :] * stride_ak)
322
+ b_ptrs = b_base + (offs_k[:, None] * stride_bk
323
+ + offs_bn[None, :] * stride_bn)
324
+
325
+ accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
326
+ for k in range(0, tl.cdiv(K, BLOCK_SIZE_K)):
327
+ k_rem = K - k * BLOCK_SIZE_K
328
+ a = tl.load(
329
+ a_ptrs,
330
+ mask=token_mask[:, None] & (offs_k[None, :] < k_rem),
331
+ other=0.0,
332
+ )
333
+ b = tl.load(
334
+ b_ptrs,
335
+ mask=(offs_k[:, None] < k_rem) & block_valid,
336
+ other=0.0,
337
+ )
338
+ accumulator += tl.dot(a.to(tl.float32), b)
339
+ a_ptrs += BLOCK_SIZE_K * stride_ak
340
+ b_ptrs += BLOCK_SIZE_K * stride_bk
341
+
342
+ if MUL_ROUTED_WEIGHT:
343
+ moe_weight = tl.load(
344
+ topk_weights_ptr + offs_token,
345
+ mask=token_mask,
346
+ other=0,
347
+ )
348
+ accumulator = accumulator * moe_weight[:, None]
349
+
350
+ accumulator = accumulator.to(compute_type)
351
+ offs_cn = (pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)).to(tl.int64)
352
+ c_ptrs = c_ptr + stride_cm * offs_token[:, None] + stride_cn * offs_cn[None, :]
353
+ c_mask = token_mask[:, None] & (offs_cn[None, :] < N)
354
+ tl.store(c_ptrs, accumulator, mask=c_mask)
355
+
356
+
357
+ @triton.jit
358
+ def silu_and_mul_kernel(
359
+ input_ptr, output_ptr,
360
+ M, N,
361
+ stride_im, stride_in,
362
+ stride_om, stride_on,
363
+ BLOCK_M: tl.constexpr,
364
+ BLOCK_N: tl.constexpr,
365
+ ):
366
+ """Fused SwiGLU: output = silu(input[:, :N]) * input[:, N:2*N]."""
367
+ pid_m = tl.program_id(0).to(tl.int64)
368
+ pid_n = tl.program_id(1).to(tl.int64)
369
+ offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
370
+ offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
371
+ mask = (offs_m[:, None] < M) & (offs_n[None, :] < N)
372
+
373
+ gate_ptrs = input_ptr + offs_m[:, None] * stride_im + offs_n[None, :] * stride_in
374
+ up_ptrs = input_ptr + offs_m[:, None] * stride_im + (offs_n[None, :] + N) * stride_in
375
+
376
+ gate = tl.load(gate_ptrs, mask=mask, other=0.0).to(tl.float32)
377
+ up = tl.load(up_ptrs, mask=mask, other=0.0).to(tl.float32)
378
+
379
+ silu_gate = gate * tl.sigmoid(gate)
380
+ result = silu_gate * up
381
+
382
+ out_ptrs = output_ptr + offs_m[:, None] * stride_om + offs_n[None, :] * stride_on
383
+ tl.store(out_ptrs, result, mask=mask)
384
+
385
+
386
+ @triton.jit
387
+ def silu_mul_split_kernel(
388
+ gate_ptr, up_ptr, output_ptr,
389
+ M, N,
390
+ stride_gm, stride_gn,
391
+ stride_um, stride_un,
392
+ stride_om, stride_on,
393
+ BLOCK_M: tl.constexpr,
394
+ BLOCK_N: tl.constexpr,
395
+ ):
396
+ """Fused SwiGLU with split gate/up tensors: output = silu(gate) * up.
397
+
398
+ Variant of silu_and_mul_kernel for the common case where gate and up
399
+ are produced as two separate tensors (no concat needed).
400
+ """
401
+ pid_m = tl.program_id(0).to(tl.int64)
402
+ pid_n = tl.program_id(1).to(tl.int64)
403
+ offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
404
+ offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
405
+ mask = (offs_m[:, None] < M) & (offs_n[None, :] < N)
406
+
407
+ gate_ptrs = gate_ptr + offs_m[:, None] * stride_gm + offs_n[None, :] * stride_gn
408
+ up_ptrs = up_ptr + offs_m[:, None] * stride_um + offs_n[None, :] * stride_un
409
+
410
+ gate = tl.load(gate_ptrs, mask=mask, other=0.0).to(tl.float32)
411
+ up = tl.load(up_ptrs, mask=mask, other=0.0).to(tl.float32)
412
+
413
+ silu_gate = gate * tl.sigmoid(gate)
414
+ result = silu_gate * up
415
+
416
+ out_ptrs = output_ptr + offs_m[:, None] * stride_om + offs_n[None, :] * stride_on
417
+ tl.store(out_ptrs, result, mask=mask)
@@ -0,0 +1,99 @@
1
+ """The fused mixture-of-experts grouped GEMM on the m8n8k4 matrix unit — `fused_moe_kernel`'s contract where the
2
+ hardware profile declares `matrix_unit` and the experts are the unit's operand dtype in memory.
3
+
4
+ Same contract as `fused_moe_kernel` (ops/fused_moe.py): the routing alignment's blocks of sorted (token, expert)
5
+ pairs, one expert per block whose weights are read through the absolute pointer table, the activation row of each
6
+ pair (the token, or the pair itself without the top-k division), the routed weight on the down projection, the output
7
+ row written at the pair. The block of rows is the alignment's and the unit tile's at once: the rows of the
8
+ `matrix_unit.mm` row the certificate chose (`moe_wrappers.invoke_fused_moe`, the routing aligned at that block).
9
+
10
+ The products of two fp16 operands are exact in fp32; the unit sums one K tile into a zeroed accumulator and the tiles
11
+ are summed on the FMA units with round to nearest, as `matmul_m8n8k4_kernel` does (the same staging, fragments and
12
+ HMMA, ops/mma_m8n8k4.py).
13
+ """
14
+
15
+ from triton.experimental import gluon
16
+ from triton.experimental.gluon import language as gl
17
+
18
+ from .mma_m8n8k4 import acc_rows_cols, mma_k4, stage_k_inner, stage_k_outer
19
+
20
+
21
+ @gluon.jit
22
+ def fused_moe_m8n8k4_kernel(
23
+ A, expert_ptrs, C, topk_w, sorted_ids, expert_ids, num_post_padded,
24
+ N, K, EM, num_valid,
25
+ stride_am, stride_ak, stride_bk, stride_bn, stride_cm, stride_cn,
26
+ MUL_ROUTED_WEIGHT: gl.constexpr, TOP_K: gl.constexpr, TOPK_DIVIDE: gl.constexpr, B_K_CONTIG: gl.constexpr,
27
+ MB: gl.constexpr, NB: gl.constexpr, BK: gl.constexpr, GROUP_M: gl.constexpr,
28
+ L: gl.constexpr, LA: gl.constexpr, LB: gl.constexpr, GA: gl.constexpr, GB: gl.constexpr,
29
+ ):
30
+ BM: gl.constexpr = 16 * MB
31
+ BN: gl.constexpr = 16 * NB
32
+ J: gl.constexpr = BK // 4
33
+ SH: gl.constexpr = gl.SwizzledSharedLayout(1, 1, 1, order=[1, 0])
34
+
35
+ pid = gl.program_id(0).to(gl.int64)
36
+ num_pid_m = gl.cdiv(EM, BM)
37
+ num_pid_n = gl.cdiv(N, BN)
38
+ num_pid_in_group = GROUP_M * num_pid_n
39
+ first_pid_m = (pid // num_pid_in_group) * GROUP_M
40
+ group_size_m = gl.minimum(num_pid_m - first_pid_m, GROUP_M)
41
+ pid_m = first_pid_m + ((pid % num_pid_in_group) % group_size_m)
42
+ pid_n = (pid % num_pid_in_group) // group_size_m
43
+ m0 = pid_m * BM
44
+ n0 = pid_n * BN
45
+ # a block past the padded pair count reads and writes nothing (its alignment entries are not written)
46
+ block_valid = m0 < gl.load(num_post_padded)
47
+
48
+ sa = gl.allocate_shared_memory(gl.float16, [J * BM, 4], SH)
49
+ sb = gl.allocate_shared_memory(gl.float16, [J * BN, 4], SH)
50
+
51
+ # A: the block's pairs' activation rows [BM, BK], k along the columns
52
+ ra = gl.arange(0, BM, layout=gl.SliceLayout(1, GA))
53
+ pair_a = gl.load(sorted_ids + m0 + ra, mask=(ra >= 0) & block_valid, other=0).to(gl.int64)
54
+ ok_a = (pair_a < num_valid) & block_valid
55
+ if TOPK_DIVIDE:
56
+ row_a = pair_a // TOP_K
57
+ else:
58
+ row_a = pair_a
59
+ ka = gl.arange(0, BK, layout=gl.SliceLayout(0, GA))[None, :]
60
+ pa = A + row_a[:, None] * stride_am + ka * stride_ak
61
+ ma = ok_a[:, None]
62
+ # B: the block's expert, through the pointer table
63
+ expert = gl.load(expert_ids + pid_m, mask=block_valid, other=0)
64
+ B = gl.cast(gl.load(expert_ptrs + expert, mask=block_valid, other=0), gl.pointer_type(gl.float16), bitcast=True)
65
+ if B_K_CONTIG:
66
+ nb_ = gl.arange(0, BN, layout=gl.SliceLayout(1, GB))[:, None]
67
+ kb = gl.arange(0, BK, layout=gl.SliceLayout(0, GB))[None, :]
68
+ else:
69
+ kb = gl.arange(0, BK, layout=gl.SliceLayout(1, GB))[:, None]
70
+ nb_ = gl.arange(0, BN, layout=gl.SliceLayout(0, GB))[None, :]
71
+ pb = B + kb * stride_bk + (n0 + nb_) * stride_bn
72
+ mb_ = ((n0 + nb_) < N) & block_valid
73
+
74
+ acc = gl.zeros([MB, 2, 8, NB, 2, 2, 4], gl.float32, layout=L)
75
+ ta = gl.load(pa, mask=ma & (ka < K), other=0.0)
76
+ tb = gl.load(pb, mask=mb_ & (kb < K), other=0.0)
77
+ for k0 in range(0, K, BK):
78
+ gl.barrier() # every warp is done reading the previous tile
79
+ stage_k_inner(sa, ta, BM, BK)
80
+ if B_K_CONTIG:
81
+ stage_k_inner(sb, tb, BN, BK)
82
+ else:
83
+ stage_k_outer(sb, tb, BK, BN)
84
+ gl.barrier()
85
+ if k0 + BK < K: # the next tile loads while the HMMAs run
86
+ ta = gl.load(pa + (k0 + BK) * stride_ak, mask=ma & (ka + k0 + BK < K), other=0.0)
87
+ tb = gl.load(pb + (k0 + BK) * stride_bk, mask=mb_ & (kb + k0 + BK < K), other=0.0)
88
+ t = gl.zeros([MB, 2, 8, NB, 2, 2, 4], gl.float32, layout=L)
89
+ for j in gl.static_range(J):
90
+ t = mma_k4(sa, j * BM, sb, j * BN, t, MB, NB, LA, LB, L)
91
+ acc = acc + t # across tiles on the FMA units, round to nearest
92
+
93
+ rows, cols = acc_rows_cols(L, MB, NB)
94
+ pair = gl.load(sorted_ids + m0 + rows, mask=(rows >= 0) & block_valid, other=0).to(gl.int64)
95
+ ok = (pair < num_valid) & block_valid
96
+ cols = n0 + cols
97
+ if MUL_ROUTED_WEIGHT:
98
+ acc = acc * gl.load(topk_w + pair, mask=ok, other=0.0).to(gl.float32)
99
+ gl.store(C + pair * stride_cm + cols.to(gl.int64) * stride_cn, acc.to(C.dtype.element_ty), mask=ok & (cols < N))