Skip to content

VitisAI EP (VAIML): native assertion crash ('Iteratees do not have equal length') on Slice pair with negative starts/ends + Concat (torch.roll export pattern) — 3-node repro #397

Description

@tomhda

Summary

Creating a VitisAIExecutionProvider session for a BF16 (cast-format) model that contains two Slice nodes with negative starts/ends literals feeding one Concat — the exact ONNX export form of torch.roll(x, shifts=+s) — kills the whole host process with a native assertion during VAIML's ONNX→ONNX-MLIR lowering:

Assertion failed: all_equal({range_size(t), range_size(u), range_size(args)...}) && "Iteratees do not have equal length",
file G:\xmc\src\common\llvm-aie\llvm\include\llvm/ADT/STLExtras.h, line 867
Exception Code: 0x80000003

The minimal reproducer is 3 nodes. The same topology with non-negative literals compiles and runs fine, and each negative-bound Slice compiles fine in isolation — the crash needs the combination.

Because this is the export pattern of torch.roll, it currently blocks every shifted-window transformer (SwinIR / Swin family) at session creation. Since it is a native assert, there is no exception to catch: the application embedding onnxruntime dies.

Environment

  • Ryzen AI 7 PRO 350 (XDNA2 NPU), Windows 11 Home 10.0.26200
  • NPU driver 32.0.203.329
  • Ryzen AI Software 1.8.0 (conda env), onnxruntime 1.27.0 (VitisAI EP)
  • amd-quark 0.11rc1, conversion: quark.onnx.tools.convert_fp32_to_bf16 --format with_cast

Minimal reproduction (3 nodes, no weights needed)

# make_repro.py — builds the fp32 graph
import numpy as np
import onnx
from onnx import helper, numpy_helper, TensorProto

INT64_MAX = 2**63 - 1
SHAPE = [1, 256, 256, 180]   # any 4-D shape reproduces; this matches SwinIR-M 256x256

def ini(name, vals):
    return numpy_helper.from_array(np.array(vals, dtype=np.int64), name)

graph = helper.make_graph(
    [
        helper.make_node("Slice", ["x", "sA", "eA", "ax"], ["a"], name="slice_a"),
        helper.make_node("Slice", ["x", "sB", "eB", "ax"], ["b"], name="slice_b"),
        helper.make_node("Concat", ["a", "b"], ["y"], name="concat", axis=1),
    ],
    "neg_slice_concat_repro",
    [helper.make_tensor_value_info("x", TensorProto.FLOAT, SHAPE)],
    [helper.make_tensor_value_info("y", TensorProto.FLOAT, SHAPE)],
    initializer=[ini("sA", [-4]), ini("eA", [INT64_MAX]),
                 ini("sB", [0]),  ini("eB", [-4]), ini("ax", [1])],
)
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 17)])
model.ir_version = 8
onnx.checker.check_model(model)
onnx.save(model, "neg_roll_fp32.onnx")
python -m quark.onnx.tools.convert_fp32_to_bf16 \
    --input neg_roll_fp32.onnx --output neg_roll_bf16.onnx --format with_cast
import onnxruntime as ort
s = ort.InferenceSession(
    "neg_roll_bf16.onnx",
    providers=["VitisAIExecutionProvider"],
    provider_options=[{"cache_dir": "cache", "cache_key": "neg_roll_repro"}])
# -> process dies here: assertion above, exit code 0x80000003

Observed crash happens ~0.6 s into session creation, before any partition report (context.json / assignment report) is written. Readable frames in the crash dump: flexml::compile::common::lowerONNXToONNXMLIRflexmlir::VResultNamesUpdaterllvm::zip_equal<SmallVector<std::string>> — i.e. a result-count vs result-name-count mismatch while normalizing the graph, consistent with a Slice decomposition/normalization step mishandling negative bounds.

Isolation evidence (all on the same machine, same conversion path)

Graph Result
Slice(-4..INT64_MAX) + Slice(0..-4) + Concat (this repro) native crash 0x80000003
Identical topology, non-negative literals (forward roll: 4..INT64_MAX / 0..4) compiles + runs
Identical topology, only the two negative literals replaced by positive equivalents (-4252) compiles + runs
Each negative-bound Slice alone (no Concat), both variants compiles + runs
SwinIR-M x4 full model (1697 nodes, 72 negative-literal Slice bounds) native crash, same assertion
Same full model with all negative bounds rewritten to positive equivalents (bit-exact, node count/order unchanged) compiles: 4971 ops / 2969 GOPs accepted as a single 100% VAIML subgraph; runs with PSNR 38.5 dB vs fp32 CPU

Standalone LayerNormalization / Softmax / 4-D MatMul / window-partition Transpose-Reshape chains / masked window attention all compile and run individually, so overall Swin-type support looks otherwise healthy — only the negative-literal handling in this pattern is broken.

Expected behavior

Normalize negative Slice starts/ends per the ONNX spec during lowering (they are fully determined for static shapes), or reject the graph gracefully (CPU fallback / catchable exception). A native assertion that terminates the host process should never be reachable from model input.

Workaround (for anyone hitting this)

Rewrite negative Slice bound literals to their positive equivalents before conversion (value + dim_size); the result is numerically identical and compiles. This unblocks SwinIR-family models completely.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions