Skip to content

[IE] UnrollGroupQuantize: make unrolled slice locations unique per consumer#302

Open
blairducrayoppat wants to merge 1 commit into
openvinotoolkit:developfrom
blairducrayoppat:fix/unroll-group-quantize-duplicate-slice-locations
Open

[IE] UnrollGroupQuantize: make unrolled slice locations unique per consumer#302
blairducrayoppat wants to merge 1 commit into
openvinotoolkit:developfrom
blairducrayoppat:fix/unroll-group-quantize-duplicate-slice-locations

Conversation

@blairducrayoppat

@blairducrayoppat blairducrayoppat commented Jun 19, 2026

Copy link
Copy Markdown

Details:

Problem. UnrollGroupQuantize (via the shared GenericUnrollBase::splitValue) names each unrolled slice after the value being splitappendLoc(val.getLoc(), "slice_{idx}"). When a single value feeds more than one unrolled consumer — e.g. a grouped-INT4 per-group scale that is the dequantization scale of both the weight DynamicDequantize and the activation/matmul DynamicDequantize — the low-index slices of that shared value get byte-identical locations across the two consumers. StopLocationVerifierPass then aborts compilation: Found N duplicated names after full verification (N = 40 on a Qwen3-0.6B grouped-INT4 attention projection).

This is the root cause behind the zero-dim as_convolution symptom previously guarded in #265 / #266: on current develop the zero-dim shape no longer appears; the failure surfaces here, in a pass emitting non-unique locations — i.e. the "fix it where it appears" case.

Fix. Derive each slice's location from the consuming op plus an operand tag and chunk index — takeOpLoc(consumerOp, "{operandTag}_slice_{idx}") — instead of from the shared value. Slices of a shared value now stay unique because they are rooted at the (distinct) consumers. The per-chunk reduced ops (FakeQuantize / DynamicDequantize) keep their consumer-rooted slice_{idx} locations, already unique by construction. All splitValue call sites are updated to pass the consumer + operand tag.

Test. New LIT test tests/lit/NPU/dialect/IE/passes/unroll_group_quantize_shared_param_locations.mlir: a shared scale feeding two DynamicDequantize consumers; asserts each consumer's slices resolve to a fused location rooted at that consumer + operand tag (so the two consumers' scale_slice_0 / input_slice_0 are distinct). Pre-fix the slices alias and the test fails.

Verification.

  • LIT: vpux-opt --unroll-group-quantize --mlir-print-debuginfo | FileCheck passes on the new test.
  • End-to-end on the real model: compiling Qwen3-0.6B grouped-INT4 for NPU via the NPUW-LLM path — without this change the compile aborts immediately at StopLocationVerifierPass (Found 40 duplicated names); with it, the compile proceeds past that pass and continues through the pipeline. Confirms the fix resolves a genuine compile blocker on the supported INT4 path, not just the isolated LIT case.
  • Built and checked locally (RelWithDebInfo; Intel Core Ultra 7 258V / NPU 4000).

Tickets:

AI Assistance:

  • AI assistance used: yes.
  • How: AI (Claude Code) assisted with the location-uniqueness analysis, the splitValue refactor, and the LIT regression test.
  • Human validation: contributor directed and reviewed the change; validated locally with vpux-opt/FileCheck and an end-to-end NPU compile on an Intel Core Ultra 7 258V (NPU 4000) from a from-source build.

…nsumer

GenericUnrollBase::splitValue named each unrolled slice after the value being
split (appendLoc(val.getLoc(), "slice_{idx}")). When one value feeds more than
one unrolled consumer -- e.g. a grouped-INT4 per-group scale that is the
dequantization scale of both the weight and the activation DynamicDequantize --
the low-index slices of that shared value get byte-identical locations across
consumers, and StopLocationVerifierPass aborts with "Found N duplicated names
after full verification".

Derive each slice's location from the consuming op plus an operand tag and chunk
index (takeOpLoc(consumerOp, "{operandTag}_slice_{idx}")) so slices of a shared
value stay unique. The per-chunk reduced FakeQuantize/DynamicDequantize ops keep
their consumer-rooted "slice_{idx}" locations (already unique by construction).

Adds a LIT regression test (unroll_group_quantize_shared_param_locations.mlir)
covering a shared scale feeding two DynamicDequantize consumers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@blairducrayoppat

Copy link
Copy Markdown
Author

Follow-up: I filed #303 documenting the next VPUX compiler blocker the same model (Qwen3-0.6B, asymmetric per-group INT4) hits once this PR is applied — a sub-byte (u4) per-group zero-point SEGMENTED slice that ConvertViewOpsToDeclarations cannot lower to a byte offset (Can't convert 4 Bit to Byte). This PR is a hard precondition for reaching it. Cross-linking for context; I'd be glad to attempt that fix as a follow-up once this merges.

@blairducrayoppat

Copy link
Copy Markdown
Author

Note: I've closed #303 — it surfaces on per-group asymmetric INT4, which the dev team flagged as unsupported on NPU (openvinotoolkit/openvino#34450). This PR is narrower: it fixes a StopLocationVerifierPass invariant violation — UnrollGroupQuantize emitting non-unique unrolled slice locations. Whether that's worth addressing on its own — independent of the unsupported configuration that surfaces it — is of course the team's call; happy to align the PR with however you'd prefer to handle it.

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.

1 participant