[Bug][Relax] CPU default pipeline: fused PrimFunc keeps symbolic height/width unbound (MakePackedAPI ICHECK)
- Dominant language
- Python
- Stars
- 13.7k
- Forks
- 4k
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 112
Description
### Expected behavior
`relax.get_default_pipeline(Target("llvm"))` should compile a valid Relax function whose input has symbolic height/width, or reject it with a clean error. In particular, this minimal graph — conv (stride 2) →
conv → bias add — is well-typed and builds fine with the plain default build.
### Actual behavior
The CPU pipeline (the one returned by `relax.get_default_pipeline`, i.e. `cpu_generic`: `LegalizeOps → AnnotateTIROpPattern → FoldConstant → FuseOps → FuseTIR → …`) aborts **at compile time** with an internal
check while wrapping the fused function:
```text
InternalError: Check failed: undefined.size() == 0 (2 vs. 0) :
In PrimFunc fused_conv2d1_add variables (width, height) are used,
but are not passed in as API arguments
```
Classification: this is a compile-time abort of the compiler itself (an internal invariant violation), not a runtime crash of generated code and not a silent miscompilation — no executable is produced. The
input is a valid Relax module that another official build path accepts (see controls), so the failure is not a legitimate rejection of invalid input.
### Environment
```text
OS: Linux x86_64
Target: llvm
TVM commit: 5a8dae4d95c55c8fec9246a607a28c3ff54ffe05 (0.26.dev1)
```
### Steps to reproduce
```python
import tvm
from tvm import relax
from tvm.script import ir as I, relax as R
@I.ir_module
class M:
@R.function
def main(
x: R.Tensor(("batch", 3, "height", "width"), "float32"),
w1: R.Tensor((16, 3, 3, 3), "float32"),
w2: R.Tensor((16, 16, 1, 1), "float32"),
b2: R.Tensor((16,), "float32"),
):
with R.dataflow():
c1 = R.nn.conv2d(x, w1, strides=[2, 2], padding=[1, 1, 1, 1])
c2 = R.nn.conv2d(c1, w2)
bias = R.reshape(b2, R.shape([1, 16, 1, 1]))
out = R.add(c2, bias)
R.output(out)
return out
llvm = tvm.target.Target("llvm")
exe = tvm.relax.build(M, target=llvm, exec_mode="compiled",
relax_pipeline=relax.get_default_pipeline(llvm))
```
### Controls (each removes exactly one trigger condition)
- Trigger: symbolic H/W + conv stride 2 + fused conv+add — **ICHECK abort**
- First conv stride 1 (intermediate shapes stay raw `height`/`width`) — OK
- No bias add (conv→conv is not fused into one group) — OK
- Fully static shapes — OK
- Trigger under the plain default build (`relax.build` without `relax_pipeline`) — OK
### Diagnosis
After the stride-2 conv, intermediate shapes become *expressions* over the input SizeVars (`(height - 1) // 2 + 1`). When FuseOps groups the second conv with the elementwise add, the fused PrimFunc's buffers
carry those expression extents, but `height`/`width` themselves never enter the fused function's signature (not parameters, and not recoverable via `T.match_buffer` of the group input, whose dims are the
expressions — not the raw vars). `MakePackedAPI` (`src/tirx/transform/make_packed_api.cc:278`) then correctly rejects the function for free variables.
For contrast, when the first conv has stride 1, the fused function's input buffer has raw `height`/`width` dims, `match_buffer` binds them, and the build succeeds.
### Real-world impact
ONNX exports with dynamic H/W inputs (e.g. Hugging Face-hosted ResNet50 / ConvNeXt-Tiny ONNX, input `(batch_size, num_channels, height, width)`) fail out of the box with this pipeline:
Contributor guide
No contributing guide indexed for this repository
Research direction
Reproduce the failure with the minimal Relax module and relax.get_default_pipeline(Target("llvm")), then inspect src/tirx/transform/make_packed_api.cc around line 278 and the CPU pipeline stages. Compare the stride-1, no-bias, static-shape, and plain-build controls to isolate where the fused PrimFunc leaves height and width unbound. Done means the valid symbolic-shape graph either builds successfully or receives a clean compiler error instead of the MakePackedAPI ICHECK.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100