Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
617ebb3
feat(attention): NVFP4 KV cache read path for FA2 paged attention (SM…
jethac Jun 17, 2026
0dd5f72
fix(prefill): shared-memory-aware CtaTileQ; reject infeasible KV tiles
jethac Jun 17, 2026
9ec7b93
fix(prefill): move mask_indptr to custom mask device before segment_p…
jethac Jun 17, 2026
3bffad4
feat(sm121): enable DGX Spark (GB10) FP4 dispatch + heuristic test
jethac Jun 17, 2026
e496d24
fix(prefill): key CTA_TILE_Q dispatch on head_dim_qk for asymmetric h…
jethac Jun 21, 2026
910d409
fix(decode): derive NVFP4 output width from KV dtype, not sf presence
jethac Jun 23, 2026
308a4d8
fix(attention): disable split-KV for NVFP4 paged prefill/decode
jethac Jun 25, 2026
a69f6f9
fix(attention): harden NVFP4 gating, rope_freq bound, smem SF budget
jethac Jun 25, 2026
6cd9acb
fix(nvfp4-sm120): zero lse before fwd kernel to avoid uninitialized read
jethac Jun 25, 2026
9909bb4
test(jit): assert the emitted FP4 KV gate string
jethac Jul 11, 2026
5db6900
fix(decode): require equal K/V strides in paged decode run
jethac Jul 15, 2026
d029150
fix(prefill): route V page offsets through V strides in on-the-fly pr…
jethac Jul 15, 2026
2e1612a
revert(gemm): keep cutlass/cudnn as the mm_fp4 auto default on SM121
jethac Jul 15, 2026
13762ee
fix(prefill): align the CtaTileQ smem probe with the dispatcher guard
jethac Jul 15, 2026
68f1e97
docs(prefill): present the NVFP4 split-KV gate as an empirical workar…
jethac Jul 15, 2026
12d9b40
fix(prefill): name the missing key for underivable JIT scalars in run()
jethac Jul 15, 2026
1c87f68
refactor(jit): drop dead CTA_TILE_Q=32 instantiation for large-QK/sma…
jethac Jul 15, 2026
5f75b96
test(nvfp4): asymmetric qk/vo paged prefill correctness + decode uneq…
jethac Jul 15, 2026
1b9f63c
test(prefill): pin the CtaTileQ smem-probe selection at (448, 256)
jethac Jul 17, 2026
e9a7423
test(prefill): cover the shared-KV-smem V producer with unequal K/V s…
jethac Jul 17, 2026
d0865da
test(prefill): skip FP8 qk448/vo256 smem probe on pre-SM100
jethac Jul 24, 2026
2ed09bd
test(jit): pass NVFP4 SF tensors to the large-head prefill flag probe
jethac Jul 24, 2026
277325f
Re-enable split-KV for NVFP4 KV cache in prefill path
ch2lab Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/nightly-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ jobs:
- name: Build wheel in container
env:
DOCKER_IMAGE: ${{ matrix.arch == 'aarch64' && format('pytorch/manylinuxaarch64-builder:cuda{0}', matrix.cuda) || format('pytorch/manylinux2_28-builder:cuda{0}', matrix.cuda) }}
FLASHINFER_CUDA_ARCH_LIST: ${{ matrix.cuda < '12.9' && '7.5 8.0 8.9 9.0a 10.0a 12.0a' || (matrix.cuda < '13.0' && '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f' || (matrix.arch == 'aarch64' && '7.5 8.0 8.9 9.0a 10.0a 10.3a 11.0a 12.0f' || '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f')) }}
FLASHINFER_CUDA_ARCH_LIST: ${{ matrix.cuda < '12.9' && '7.5 8.0 8.9 9.0a 10.0a 12.0a' || (matrix.cuda < '13.0' && (matrix.arch == 'aarch64' && '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f 12.1a' || '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f') || (matrix.arch == 'aarch64' && '7.5 8.0 8.9 9.0a 10.0a 10.3a 11.0a 12.0f 12.1a' || '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f')) }}
FLASHINFER_DEV_RELEASE_SUFFIX: ${{ needs.setup.outputs.dev_suffix }}
run: |
# Extract CUDA major and minor versions
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ jobs:
- name: Build wheel in container
env:
DOCKER_IMAGE: ${{ matrix.arch == 'aarch64' && format('pytorch/manylinuxaarch64-builder:cuda{0}', matrix.cuda) || format('pytorch/manylinux2_28-builder:cuda{0}', matrix.cuda) }}
FLASHINFER_CUDA_ARCH_LIST: ${{ matrix.cuda < '12.9' && '7.5 8.0 8.9 9.0a 10.0a 12.0a' || (matrix.cuda < '13.0' && '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f' || (matrix.arch == 'aarch64' && '7.5 8.0 8.9 9.0a 10.0a 10.3a 11.0a 12.0f' || '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f')) }}
FLASHINFER_CUDA_ARCH_LIST: ${{ matrix.cuda < '12.9' && '7.5 8.0 8.9 9.0a 10.0a 12.0a' || (matrix.cuda < '13.0' && (matrix.arch == 'aarch64' && '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f 12.1a' || '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f') || (matrix.arch == 'aarch64' && '7.5 8.0 8.9 9.0a 10.0a 10.3a 11.0a 12.0f 12.1a' || '7.5 8.0 8.9 9.0a 10.0a 10.3a 12.0f')) }}
run: |
# Extract CUDA major and minor versions
CUDA_MAJOR=$(echo "${{ matrix.cuda }}" | cut -d'.' -f1)
Expand Down
12 changes: 7 additions & 5 deletions csrc/batch_decode.cu
Original file line number Diff line number Diff line change
Expand Up @@ -172,15 +172,17 @@ void BatchDecodeWithPagedKVCacheRun(TensorView float_workspace_buffer,
const auto q_stride_n = q.stride(0);
const auto q_stride_h = q.stride(1);

// get kv_cache_strides
const int64_t* kv_cache_strides = nullptr;
// get kv-cache strides
auto k_strides = paged_k_cache.strides();
auto v_strides = paged_v_cache.strides();
TVM_FFI_ICHECK_EQ(k_strides.size(), v_strides.size());
for (int i = 0; i < k_strides.size(); ++i) {
TVM_FFI_ICHECK_EQ(k_strides[i], v_strides[i]);
TVM_FFI_ICHECK_EQ(k_strides[i], v_strides[i])
<< "K/V strides differ at dim " << i
<< ": the FA2 decode kernel addresses both K and V through a single set of "
"(K) strides, so paged_k_cache and paged_v_cache must have identical strides; "
"NVFP4/asymmetric decode with independent K/V strides is not yet supported.";
}
kv_cache_strides = k_strides.data();

ffi::CUDADeviceGuard device_guard(q.device().device_id);
const cudaStream_t stream = get_stream(q.device());
Expand All @@ -191,7 +193,7 @@ void BatchDecodeWithPagedKVCacheRun(TensorView float_workspace_buffer,
paged_kv_t<DTypeKV, IdType> paged_kv(
num_kv_heads, page_size, HEAD_DIM_QK, batch_size, kv_layout,
static_cast<DTypeKV*>(paged_k_cache.data_ptr()),
static_cast<DTypeKV*>(paged_v_cache.data_ptr()), kv_cache_strides,
static_cast<DTypeKV*>(paged_v_cache.data_ptr()), k_strides.data(), v_strides.data(),
static_cast<IdType*>(paged_kv_indices.data_ptr()),
static_cast<IdType*>(paged_kv_indptr.data_ptr()),
static_cast<IdType*>(paged_kv_last_page_len.data_ptr()));
Expand Down
16 changes: 7 additions & 9 deletions csrc/batch_prefill.cu
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Array<int64_t> BatchPrefillWithKVCachePlan(
static_cast<IdType*>(kv_indptr.data_ptr()), total_num_rows, batch_size, num_qo_heads,
num_kv_heads, head_dim_qk, head_dim_vo, page_size, enable_cuda_graph,
/*sizeof_dtype_o=*/2, window_left, fixed_split_size, disable_split_kv, num_colocated_ctas,
uniform_q_len, stream);
uniform_q_len, stream, /*kv_dtype_bytes=*/sizeof(DTypeKV));

TVM_FFI_ICHECK(status == cudaSuccess)
<< "Failed to plan prefill with error: " << cudaGetErrorString(status);
Expand All @@ -93,7 +93,7 @@ Array<int64_t> BatchPrefillWithKVCacheWorkspaceSize(
static_cast<IdType*>(qo_indptr.data_ptr()), static_cast<IdType*>(kv_indptr.data_ptr()),
total_num_rows, batch_size, num_qo_heads, num_kv_heads, head_dim_qk, head_dim_vo, page_size,
enable_cuda_graph, /*sizeof_dtype_o=*/2, window_left, fixed_split_size, disable_split_kv,
num_colocated_ctas, uniform_q_len, stream);
num_colocated_ctas, uniform_q_len, stream, /*kv_dtype_bytes=*/sizeof(DTypeKV));

TVM_FFI_ICHECK(status == cudaSuccess)
<< "Failed to calculate prefill workspace size with error: " << cudaGetErrorString(status);
Expand Down Expand Up @@ -275,13 +275,10 @@ void BatchPrefillWithPagedKVCacheRun(TensorView float_workspace_buffer,
const auto q_stride_n = q.stride(0);
const auto q_stride_h = q.stride(1);

// get kv_cache_strides
const int64_t* kv_cache_strides = paged_k_cache.strides().data();
// get kv-cache strides
auto k_cache_strides = paged_k_cache.strides();
auto v_cache_strides = paged_v_cache.strides();
TVM_FFI_ICHECK_EQ(paged_k_cache.ndim(), paged_v_cache.ndim());
for (int i = 0; i < paged_k_cache.ndim(); ++i) {
TVM_FFI_ICHECK_EQ(paged_k_cache.stride(i), paged_v_cache.stride(i))
<< "k/v strides differs at " << i;
}

ffi::CUDADeviceGuard device_guard(float_workspace_buffer.device().device_id);
const cudaStream_t stream = get_stream(float_workspace_buffer.device());
Expand All @@ -296,7 +293,8 @@ void BatchPrefillWithPagedKVCacheRun(TensorView float_workspace_buffer,
paged_kv_t<DTypeKV, IdType> paged_kv(
num_kv_heads, page_size, HEAD_DIM_VO, batch_size, kv_layout,
static_cast<DTypeKV*>(paged_k_cache.data_ptr()),
static_cast<DTypeKV*>(paged_v_cache.data_ptr()), kv_cache_strides,
static_cast<DTypeKV*>(paged_v_cache.data_ptr()), k_cache_strides.data(),
v_cache_strides.data(),
static_cast<IdType*>(paged_kv_indices.data_ptr()),
static_cast<IdType*>(paged_kv_indptr.data_ptr()),
static_cast<IdType*>(paged_kv_last_page_len.data_ptr()));
Expand Down
8 changes: 8 additions & 0 deletions csrc/batch_prefill_customize_config.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <flashinfer/pos_enc.cuh>
#include <flashinfer/fastdiv.cuh>
#include <flashinfer/attention/variant_helper.cuh>
#include <type_traits>

#define ADDITIONAL_FUNC_PARAMS {{ additional_func_params }}
#define ADDITIONAL_PARAMS_SETTER {{ additional_params_setter }}
Expand All @@ -23,6 +24,13 @@ using DTypeQ = {{ dtype_q }};
using DTypeKV = {{ dtype_kv }};
using DTypeO = {{ dtype_o }};
using IdType = {{ idtype }};
{% if require_fp4_kv_cache %}
#ifndef FLASHINFER_ENABLE_FP4_E2M1
#error "NVFP4 KV paged prefill compiled without FLASHINFER_ENABLE_FP4_E2M1"
#endif
static_assert(std::is_same_v<DTypeKV, __nv_fp4x2_e2m1>,
"NVFP4 KV paged prefill must build with the packed FP4 KV container type");
{% endif %}
constexpr int HEAD_DIM_QK = {{ head_dim_qk }};
constexpr int HEAD_DIM_VO = {{ head_dim_vo }};
constexpr bool USE_FP16_QK_REDUCTION = {{ use_fp16_qk_reduction }};
Expand Down
8 changes: 5 additions & 3 deletions csrc/batch_prefill_paged_kernel_inst.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ namespace flashinfer {

constexpr auto use_custom_mask = {{ mask_mode }} == MaskMode::kCustom;

{# FA2DetermineCtaTileQ only selects {16, 32} for head_dim_vo >= 512 and
{16, 64, 128} otherwise; don't instantiate unreachable variants. #}
{% for cta_tile_q in ([16, 32] if head_dim_vo | int >= 512 else [16, 64, 128]) %}
{# FA2DetermineCtaTileQ only selects {16, 32} for head_dim_vo >= 512 (VO-split),
{16} for head_dim_qk >= 512 with a smaller head_dim_vo (o_frag register
pressure permits only CTA16 there), and {16, 64, 128} otherwise; don't
instantiate unreachable variants. #}
{% for cta_tile_q in ([16, 32] if head_dim_vo | int >= 512 else ([16] if head_dim_qk | int >= 512 else [16, 64, 128])) %}
template cudaError_t BatchPrefillWithPagedKVCacheDispatched<
/*CTA_TILE_Q=*/{{cta_tile_q}}, {{head_dim_qk}}, {{head_dim_vo}}, {{pos_encoding_mode}}, {{use_fp16_qk_reduction}}, {{mask_mode}},
{{ variant_name }}, PagedParams>(PagedParams params, {{ dtype_o }}* tmp_v, float* tmp_s, bool enable_pdl, cudaStream_t stream);
Expand Down
8 changes: 5 additions & 3 deletions csrc/batch_prefill_ragged_kernel_inst.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ namespace flashinfer {

constexpr auto use_custom_mask = {{ mask_mode }} == MaskMode::kCustom;

{# FA2DetermineCtaTileQ only selects {16, 32} for head_dim_vo >= 512 and
{16, 64, 128} otherwise; don't instantiate unreachable variants. #}
{% for cta_tile_q in ([16, 32] if head_dim_vo | int >= 512 else [16, 64, 128]) %}
{# FA2DetermineCtaTileQ only selects {16, 32} for head_dim_vo >= 512 (VO-split),
{16} for head_dim_qk >= 512 with a smaller head_dim_vo (o_frag register
pressure permits only CTA16 there), and {16, 64, 128} otherwise; don't
instantiate unreachable variants. #}
{% for cta_tile_q in ([16, 32] if head_dim_vo | int >= 512 else ([16] if head_dim_qk | int >= 512 else [16, 64, 128])) %}
template cudaError_t BatchPrefillWithRaggedKVCacheDispatched<
/*CTA_TILE_Q=*/{{cta_tile_q}}, {{head_dim_qk}}, {{head_dim_vo}}, {{pos_encoding_mode}}, {{use_fp16_qk_reduction}}, {{mask_mode}},
{{ variant_name }}, RaggedParams>(RaggedParams params, {{ dtype_o }}* tmp_v, float* tmp_s, bool enable_pdl, cudaStream_t stream);
Expand Down
10 changes: 10 additions & 0 deletions csrc/nvfp4_attention_sm120/nvfp4_attention_sm120_binding.cu
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,16 @@ void fwd(TensorView q_fp4, TensorView k_fp4, TensorView v_fp4_t, TensorView q_sc
return;
}

// lse is allocated uninitialized by the caller (torch.empty). The fwd kernel
// writes `out` for every query row but does not guarantee writing every
// (batch, head, seq) entry of lse, so reading it back can surface whatever
// garbage the allocator handed out (observed as flaky NaNs in lse under a
// dirty allocator, depending on test/run ordering). Zero it first, mirroring
// the seq_len==0 branch above, so unwritten entries are a defined 0.
status = cudaMemsetAsync(lse.data_ptr(), 0, numel(lse) * get_element_size(lse), stream);
TVM_FFI_ICHECK(status == cudaSuccess)
<< "cudaMemsetAsync(lse) failed: " << cudaGetErrorString(status);

Flash_fwd_params params;
set_params_fprop(params, q_fp4, k_fp4, v_fp4_t, q_scale, k_scale, v_scale_t, qk_correction, out,
lse, static_cast<float>(sm_scale), causal, per_block_mean);
Expand Down
7 changes: 7 additions & 0 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ You can follow the steps below to install FlashInfer from source code:
.. code-block:: bash

export FLASHINFER_CUDA_ARCH_LIST="7.5 8.0 8.9 9.0a 10.0a 10.3a 11.0a 12.0f"

For DGX Spark / GB10, add the arch-specific SM121 target so JIT-cache
wheels include native ``121a`` artifacts for NVFP4/MXFP4 MMA:

.. code-block:: bash

export FLASHINFER_CUDA_ARCH_LIST="7.5 8.0 8.9 9.0a 10.0a 10.3a 11.0a 12.0f 12.1a"
cd flashinfer-jit-cache
python -m build --no-isolation --wheel
python -m pip install dist/*.whl
Expand Down
6 changes: 6 additions & 0 deletions flashinfer/cute_dsl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
from cutlass.cutlass_dsl import dsl_user_op
from cutlass.cute.typing import AddressSpace, Numeric, Pointer, Type

if not hasattr(cute.nvgpu, "OperandMajorMode"):
try:
cute.nvgpu.OperandMajorMode = cute.nvgpu.tcgen05.OperandMajorMode
except AttributeError:
pass


def ceil_div(a: int, b: int) -> int:
"""Ceiling division."""
Expand Down
12 changes: 11 additions & 1 deletion flashinfer/decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -2029,7 +2029,17 @@ def run(
out_dtype = getattr(self, "_cached_o_data_type", None) or q.dtype
# For NVFP4 KV (uint8 packed), v_cache last dim is head_dim//2;
# use q's head_dim for output instead
out_head_dim = q.shape[-1] if kv_cache_sf is not None else v_cache.shape[-1]
# NVFP4 packed: unpacked VO width is packed bytes * 2 (supports
# asymmetric QK/VO plans; q.shape[-1] assumed QK == VO).
# Only the NVFP4 packed path (uint8 KV) stores VO at half width;
# derive the doubled output width from the KV dtype, not merely from
# kv_cache_sf being present, so a stray scale-factor tensor on a
# non-uint8 cache can't silently miscompute the output shape.
out_head_dim = (
v_cache.shape[-1] * 2
if kv_cache_sf is not None and v_cache.dtype == torch.uint8
else v_cache.shape[-1]
)
out = torch.empty(
q.shape[:-1] + (out_head_dim,), dtype=out_dtype, device=q.device
)
Expand Down
48 changes: 35 additions & 13 deletions flashinfer/jit/attention/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from .. import env as jit_env
from ..core import (
JitSpec,
common_nvcc_flags,
gen_jit_spec,
logger,
sm90a_nvcc_flags,
Expand All @@ -33,6 +34,7 @@
dtype_map,
dtype_map_kv,
filename_safe_dtype_map,
filename_safe_dtype_map_kv,
mask_mode_literal,
pos_encoding_mode_literal,
write_if_different,
Expand All @@ -54,7 +56,7 @@ def get_single_decode_uri(
) -> str:
return (
f"single_decode_with_kv_cache_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
f"head_dim_qk_{head_dim_qk}_"
f"head_dim_vo_{head_dim_vo}_"
Expand All @@ -77,7 +79,7 @@ def get_batch_decode_uri(
) -> str:
return (
f"batch_decode_with_kv_cache_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
f"dtype_idx_{filename_safe_dtype_map[dtype_idx]}_"
f"head_dim_qk_{head_dim_qk}_"
Expand All @@ -100,7 +102,7 @@ def get_batch_mla_uri(
) -> str:
return (
f"batch_mla_attention_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
f"dtype_idx_{filename_safe_dtype_map[dtype_idx]}_"
f"head_dim_ckv_{head_dim_ckv}_"
Expand Down Expand Up @@ -217,7 +219,7 @@ def get_batch_decode_mla_uri(
) -> str:
return (
f"batch_decode_mla_with_kv_cache_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
f"dtype_idx_{filename_safe_dtype_map[dtype_idx]}_"
f"head_dim_ckv{head_dim_ckv}_"
Expand Down Expand Up @@ -329,7 +331,7 @@ def get_single_prefill_uri(
) -> str:
return (
f"single_prefill_with_kv_cache_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
f"head_dim_qk_{head_dim_qk}_"
f"head_dim_vo_{head_dim_vo}_"
Expand All @@ -356,7 +358,7 @@ def get_pod_uri(
) -> str:
return (
f"pod_with_kv_cache_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
f"head_dim_{head_dim}_"
f"posenc_p_{pos_encoding_mode_p}_"
Expand Down Expand Up @@ -385,7 +387,7 @@ def get_batch_prefill_uri(
) -> str:
return (
f"batch_prefill_with_kv_cache_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
f"dtype_idx_{filename_safe_dtype_map[dtype_idx]}_"
f"head_dim_qk_{head_dim_qk}_"
Expand All @@ -410,7 +412,7 @@ def get_batch_prefill_attention_sink_uri(
) -> str:
return (
f"batch_prefill_with_attention_sink_kv_cache_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
f"dtype_idx_{filename_safe_dtype_map[dtype_idx]}_"
f"head_dim_qk_{head_dim_qk}_"
Expand All @@ -432,7 +434,7 @@ def get_batch_attention_uri(
) -> str:
return (
f"batch_attention_with_kv_cache_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
f"dtype_idx_{filename_safe_dtype_map[dtype_idx]}_"
f"head_dim_qk_{head_dim_qk}_"
Expand Down Expand Up @@ -1617,13 +1619,28 @@ def gen_customize_batch_prefill_module(
use_fp16_qk_reduction: bool = False,
fp8_enabled: bool = False,
) -> JitSpec:
require_fp4_kv_cache = dtype_map_kv[dtype_kv] == "__nv_fp4x2_e2m1"
if require_fp4_kv_cache:
missing_sf_tensors = [
name
for name in ("maybe_k_cache_sf", "maybe_v_cache_sf")
if name not in additional_tensor_names
]
if missing_sf_tensors:
raise ValueError(
"NVFP4 KV paged prefill JIT modules require scale-factor tensors "
f"{missing_sf_tensors}; pass maybe_k_cache_sf and maybe_v_cache_sf "
"as additional tensors."
)

kwargs = {
"variant_decl": variant_decl,
"variant_name": variant_name,
"dtype_q": dtype_map[dtype_q],
"dtype_kv": dtype_map_kv[dtype_kv],
"dtype_o": dtype_map[dtype_o],
"idtype": dtype_map[idtype],
"require_fp4_kv_cache": require_fp4_kv_cache,
"head_dim_qk": head_dim_qk,
"head_dim_vo": head_dim_vo,
"pos_encoding_mode": pos_encoding_mode_literal[pos_encoding_mode],
Expand Down Expand Up @@ -1708,12 +1725,17 @@ def gen_customize_batch_prefill_module(

generated_config_path = gen_directory / "batch_prefill_config.inc"
write_if_different(generated_config_path, generated_inc_str)
extra_cuda_cflags = _fa2_prefill_head_dim_nvcc_flags(
head_dim_qk, head_dim_vo, dtype_kv
)
if kwargs["require_fp4_kv_cache"]:
# NVFP4 KV kernels need FLASHINFER_ENABLE_FP4_E2M1 (common flags) even
# when the head_dim helper returns no arch-specific flags.
extra_cuda_cflags = (extra_cuda_cflags or []) + common_nvcc_flags
return gen_jit_spec(
uri,
source_paths,
extra_cuda_cflags=_fa2_prefill_head_dim_nvcc_flags(
head_dim_qk, head_dim_vo, dtype_kv
),
extra_cuda_cflags=extra_cuda_cflags,
)
elif backend == "fa3":
gen_directory = jit_env.FLASHINFER_GEN_SRC_DIR / uri
Expand Down Expand Up @@ -1810,7 +1832,7 @@ def get_fmha_cutlass_sm100a_uri(
return "fmha_cutlass_sm100a"
# return (
# f"fmha_cutlass_sm100a_dtype_q_{filename_safe_dtype_map[dtype_q]}_"
# f"dtype_kv_{filename_safe_dtype_map[dtype_kv]}_"
# f"dtype_kv_{filename_safe_dtype_map_kv(dtype_kv)}_"
# f"dtype_o_{filename_safe_dtype_map[dtype_o]}_"
# f"dtype_idx_{filename_safe_dtype_map[dtype_idx]}_"
# f"head_dim_qk_{head_dim_qk}_"
Expand Down
9 changes: 9 additions & 0 deletions flashinfer/jit/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ def write_if_different(path: pathlib.Path, content: str) -> None:
if hasattr(torch, "float4_e2m1fn_x2"):
filename_safe_dtype_map[torch.float4_e2m1fn_x2] = "fp4_e2m1"


def filename_safe_dtype_map_kv(dtype: torch.dtype) -> str:
"""Return the cache-key dtype name for KV cache kernels."""

if dtype_map_kv[dtype] == "__nv_fp4x2_e2m1":
return "fp4x2_e2m1"
return filename_safe_dtype_map[dtype]


pos_encoding_mode_literal = {
0: "PosEncodingMode::kNone",
1: "PosEncodingMode::kRoPELlama",
Expand Down
Loading
Loading