Skip to content

[Bug][Relax] ONNX Slice with runtime negative starts gives wrong results (zeros) — dynamic_strided_slice mishandles negative begin #20263

Description

@lrcyyds1

Expected behavior

An ONNX Slice whose starts is a runtime input (not an initializer) with a negative value — e.g. starts=[-2] to take the last 2 elements of a dynamic axis, the standard GPT-2-style pattern — should
return the last 2 elements. Negative indices are part of the ONNX Slice spec.

Actual behavior

Silent wrong results: the output has the correct shape but contains zeros (out-of-bounds memory that happens to be zeroed). Both official build pipelines (default and get_default_pipeline) produce the
same wrong values.

Root cause is op-level: the ONNX frontend lowers runtime (non-constant) starts to relax.dynamic_strided_slice without normalizing negative values (negative axes are normalized; starts/ends are
passed through raw), and dynamic_strided_slice itself does not handle negative begin — any negative begin, in-range or out-of-bound, static or symbolic dim, produces zeros/garbage, while the same inputs
through static strided_slice are correct. Negative end works. Constant starts take a frontend shortcut to the static op, which is why the bug only shows with runtime starts.

Environment

OS: Linux x86_64
Target: llvm
TVM commit: 2a2b293c02269f4d9f3526c5b03a7548578e78e8 (current main)

Steps to reproduce (end-to-end ONNX)

import numpy as np, onnx, tvm
from onnx import helper, TensorProto
from tvm import relax
from tvm.relax.frontend.onnx import from_onnx

X = helper.make_tensor_value_info('x', TensorProto.FLOAT, [8])
S = helper.make_tensor_value_info('starts', TensorProto.INT64, [1])
E = helper.make_tensor_value_info('ends', TensorProto.INT64, [1])
Y = helper.make_tensor_value_info('y', TensorProto.FLOAT, [2])
node = helper.make_node('Slice', ['x', 'starts', 'ends'], ['y'])
graph = helper.make_graph([node], 'g', [X, S, E], [Y])
model = helper.make_model(graph, opset_imports=[helper.make_opsetid('', 13)])
model.ir_version = 8

mod = from_onnx(model)
data = np.clip(np.random.RandomState(7).randn(8), -1, 1).astype("float32")
exe = tvm.relax.build(mod, target=tvm.target.Target("llvm"), exec_mode="compiled")
got = relax.VirtualMachine(exe, tvm.cpu())["main"](
    tvm.runtime.tensor(data, tvm.cpu()),
    tvm.runtime.tensor(np.array([-2], "int64"), tvm.cpu()),
    tvm.runtime.tensor(np.array([np.iinfo(np.int64).max], "int64"), tvm.cpu())).numpy()
print(got)        # [0., 0.]  — expected data[-2:]

Op-level isolation (no ONNX): relax.op.dynamic_strided_slice(x, const([-2]), const([8]), const([1])) on x: (8,) returns zeros; relax.op.strided_slice(x, [0], [-2], [8], [1]) returns the correct last-2
elements. Larger negative begins (e.g. -58) read garbage (5.7e+16) — out-of-bounds reads, potential segfault.

Fix direction: either normalize negative begin/end (add dim, then clip) inside dynamic_strided_slice, or normalize in the ONNX frontend before lowering (mirroring what it already does for negative
axes).

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

    needs-triagePRs or issues that need to be investigated by maintainers to find the right assignees to address ittype: bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions