Skip to content

fix: eliminate non-constant Slice ends in sliding-window CB subfunction - #1179

Open
quic-amitraj wants to merge 3 commits into
quic:mainfrom
quic-amitraj:fix/gemma4-sliding-window-subfunction-slice-main
Open

fix: eliminate non-constant Slice ends in sliding-window CB subfunction#1179
quic-amitraj wants to merge 3 commits into
quic:mainfrom
quic-amitraj:fix/gemma4-sliding-window-subfunction-slice-main

Conversation

@quic-amitraj

@quic-amitraj quic-amitraj commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Problem

When compiling Gemma4-E2B or Gemma4-E4B with use_onnx_subfunctions=True in qaic-vllm disaggregated serving, the decode QPC compilation consistently fails:

QAIC_ERROR:
Error message:  [Operator-'Slice_2461'] : Slice: Non-constant ends tensor not supported.
Unable to AddNodesToGraphFromModel

The encoder QPC compiles successfully; only the decoder (with -sub-functions) fails.

JIRA:

Root Cause

In QEffHybridCache, QEffHybridChunkedCache, and QEffGemma4DynamicLayer (cache_utils.py), the sliding-window rolling-index computation was:

all_indices     = torch.arange(layer_ctx_len) + kv_position_ids.max() + 1
rolling_indices = torch.where(all_indices > layer_ctx_len - 1, all_indices % layer_ctx_len, all_indices)
rolling_indices = rolling_indices[:ctx_len]   # ← generates Slice node

The rolling_indices[:ctx_len] slice becomes an ONNX Slice node inside the QEffGemma4TextDecoderLayer subfunction. Its ends tensor is ctx_len = Shape(past_key)[2] (= sliding_window), which is pre-computed in the outer ONNX graph as Gather(Shape(past_key), 2) and injected into the subfunction as an opaque INT32 argument. Inside the subfunction body, the QAIC compiler cannot resolve this as a compile-time constant and rejects the graph.

This is the same class of bug as the CtxGatherCB comp_ctx_len fix in commit c89c4f71.

Fix

Replace torch.arange(layer_ctx_len)[:ctx_len] with torch.arange(ctx_len) directly:

# Before
all_indices     = torch.arange(layer_ctx_len) + kv_position_ids.max() + 1
rolling_indices = torch.where(all_indices > layer_ctx_len - 1, all_indices % layer_ctx_len, all_indices)
rolling_indices = rolling_indices[:ctx_len]

# After
all_indices     = torch.arange(ctx_len) + kv_position_ids.max() + 1
rolling_indices = torch.where(all_indices > layer_ctx_len - 1, all_indices % layer_ctx_len, all_indices)
# rolling_indices already has ctx_len elements — no Slice needed

arange(n)[:n] == arange(n) identically — no behavioural change. The resulting Range op with a dynamic upper bound is already permitted in QAIC subfunction lowering (the existing ctx_indices = torch.arange(ctx_len) in the same function demonstrates this). The Slice node is eliminated entirely.

The fix is also correct for the CCL case: arange(min(sliding_window, CCL)) naturally produces CCL elements without slicing.

Changes

File Change
QEfficient/transformers/cache_utils.py Replace arange(layer_ctx_len)[:ctx_len] with arange(ctx_len) in QEffHybridCache.update(), QEffHybridChunkedCache.update(), and QEffGemma4DynamicLayer.update()

Test Plan

  • gemma4_cb.py with use_onnx_subfunctions=True, continuous_batching=True, full_batch_size=4, 2-layer reduced config — compiles and runs end-to-end on AI100
  • gemma4_cb.py with use_onnx_subfunctions=False — no regression
  • Verified Slice_520 absent from re-exported subfunction ONNX body
  • Corresponding fix already merged to release/v1.22.0 as PR fix: eliminate non-constant Slice ends in Gemma4 sliding-window CB subfunction #1170

Sdk: 1.22.1.18

@quic-amitraj quic-amitraj changed the title fix: eliminate non-constant Slice ends in sliding-window CB subfunction (QRANIUMSW-62851) fix: eliminate non-constant Slice ends in sliding-window CB subfunction Jul 13, 2026
@quic-amitraj
quic-amitraj force-pushed the fix/gemma4-sliding-window-subfunction-slice-main branch 2 times, most recently from 828d727 to f4d0bc9 Compare July 16, 2026 10:11
@quic-amitraj
quic-amitraj force-pushed the fix/gemma4-sliding-window-subfunction-slice-main branch from b95f6ff to 0493804 Compare July 21, 2026 17:58
Comment thread QEfficient/transformers/cache_utils.py Outdated
ctx_indices = torch.where(invalid_mask, invalid_idx_value, ctx_indices)

all_indices = torch.arange(layer_ctx_len) + kv_position_ids.max() + 1
all_indices = torch.arange(ctx_len) + kv_position_ids.max() + 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this working for the other models if we're using the same logic in the cache utilities?
all_indices = torch.arange(layer_ctx_len) + kv_position_ids.max() + 1.
The same change is already present in both HybridChunkedCache and HybridCache.

…on (QRANIUMSW-62851)

In QEffHybridCache, QEffHybridChunkedCache, and QEffGemma4DynamicLayer,
the rolling-index computation for sliding-window CB caches was:

    all_indices     = torch.arange(layer_ctx_len) + kv_position_ids.max() + 1
    rolling_indices = torch.where(...)
    rolling_indices = rolling_indices[:ctx_len]          # <- Slice node

When compiled with -sub-functions, QAIC subfunction lowering rejects
Slice operators whose `ends` input is a non-constant function argument.
ctx_len (= Shape(past_key)[2] = sliding_window) is pre-computed in the
outer ONNX graph and injected into the subfunction as an opaque INT32
parameter.  The compiler raises:

  [Operator-'Slice_N']: Slice: Non-constant ends tensor not supported.

Fix: replace torch.arange(layer_ctx_len)[:ctx_len] with torch.arange(ctx_len)
directly.  arange(n)[:n] == arange(n) identically; no behavioural change.
The Range op with a dynamic upper bound is already supported in QAIC
subfunction lowering.  The Slice node is eliminated entirely.

Same class of bug as the CtxGatherCB comp_ctx_len fix in commit c89c4f7.

Affects: Gemma4-E2B, Gemma4-E4B with use_onnx_subfunctions=True in
qaic-vllm disaggregated serving (decode QPC compilation).

Signed-off-by: Amit Raj <amitraj@qti.qualcomm.com>
Signed-off-by: Amit Raj <amitraj@qti.qualcomm.com>
@quic-amitraj
quic-amitraj force-pushed the fix/gemma4-sliding-window-subfunction-slice-main branch from ccc7829 to a0f8083 Compare July 28, 2026 10:10
Signed-off-by: Amit Raj <amitraj@qti.qualcomm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants