lrcyyds1 opened a new issue, #20263:
URL: https://github.com/apache/tvm/issues/20263
### 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
```text
OS: Linux x86_64
Target: llvm
TVM commit: 2a2b293c02269f4d9f3526c5b03a7548578e78e8 (current main)
```
### Steps to reproduce (end-to-end ONNX)
```python
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`).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]