Skip to content

[Bug] Matmul and reductions with zero-size dimension return uninitialized memory #19578

@wuyii8941

Description

@wuyii8941

Expected behavior

matmul(A[M,0], B[0,N]) should return an M×N matrix of zeros (sum of zero terms = 0), consistent with NumPy, PyTorch, and ONNX Runtime.

Actual behavior

The output buffer is not zeroed, so the result contains uninitialized memory (garbage values, NaN). The output is non-deterministic across runs.

Reproducer

import numpy as np
import tvm
from tvm import relax
import tvm.relax.op as R
from tvm.relax.transform import LegalizeOps

bb = relax.BlockBuilder()
a = relax.Var("a", relax.TensorStructInfo((4, 0), "float32"))
b = relax.Var("b", relax.TensorStructInfo((0, 4), "float32"))
with bb.function("main", [a, b]):
    with bb.dataflow():
        gv = bb.emit_output(bb.emit(R.matmul(a, b)))
    bb.emit_func_output(gv)
mod = bb.finalize()

pipeline = tvm.ir.transform.Sequential([LegalizeOps()])
exe = tvm.relax.build(pipeline(mod), target="llvm")
vm = tvm.relax.VirtualMachine(exe, device=tvm.cpu())

out = vm["main"](
    tvm.runtime.tensor(np.zeros((4, 0), np.float32), device=tvm.cpu()),
    tvm.runtime.tensor(np.zeros((0, 4), np.float32), device=tvm.cpu()),
).numpy()

print(out)
# Non-deterministic garbage, e.g.:
# [[ 6.19e+21  3.07e-41  5.25e+21  3.07e-41]
#  [ 6.19e+21  3.07e-41  5.25e+21  3.07e-41]
#  [       nan -7.43e-44        nan -1.40e-44]
#  [       nan  7.57e-44        nan  4.48e-44]]

print(np.zeros((4, 0)) @ np.zeros((0, 4)))
# [[0. 0. 0. 0.]
#  [0. 0. 0. 0.]
#  [0. 0. 0. 0.]
#  [0. 0. 0. 0.]]

Scope

The same issue affects other reduction operations with a zero-size reduction axis:

# All return uninitialized memory instead of the identity element:
R.sum(X[4, 0], axis=1)               # expected: zeros(4,1)
R.prod(X[4, 0], axis=1)              # expected: ones(4,1)
R.matmul(A[2,3,4,0], B[2,3,0,5])    # expected: zeros(2,3,4,5)  [batched]
R.nn.conv2d(X[1,0,8,8], W[4,0,3,3]) # expected: zeros(1,4,8,8)  [0 in_channels]
R.einsum([A[4,0], B[0,8]], 'ik,kj->ij')  # expected: zeros(4,8)

Notably, R.max and R.min over size-0 axes are handled correctly (returning -inf / +inf).

Why this matters

The garbage output propagates silently. For example:

mm = R.matmul(A[4,0], B[0,8])   # garbage
sm = R.nn.softmax(mm, axis=-1)   # produces [1,0,0,...,0] instead of uniform

In dynamic-shape models, K=0 can occur naturally when a filter removes all elements. The result is silent, non-deterministic incorrect output with no crash or warning.

Environment

  • TVM commit: 0b0afd8 (main, 2026-04-24)
  • OS: Ubuntu 20.04
  • Target: llvm (CPU)

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions