Skip to content

[TIRx][Runtime] Rebase TVM to 0.26.dev0 for TileLang#50

Open
LeiWang1999 wants to merge 106 commits into
tilelang_mainfrom
rebase-tvm-0.26-dev0
Open

[TIRx][Runtime] Rebase TVM to 0.26.dev0 for TileLang#50
LeiWang1999 wants to merge 106 commits into
tilelang_mainfrom
rebase-tvm-0.26-dev0

Conversation

@LeiWang1999

Copy link
Copy Markdown
Member

Summary

  • Rebase the TileLang TVM fork from v0.25.dev0 to v0.26.dev0.
  • Add local compatibility fixes needed for TileLang on top of the upstream TVM changes.
  • Keep CUDA/TIRx launch, dtype, and FFI behavior aligned with TileLang's current test suite.

Changes

  • Cherry-pick the v0.25.dev0..v0.26.dev0 TVM commit range onto tilelang_main.
  • Add TileLang-specific compatibility fixes for TIRx floor div/mod dtype handling, sparse MMA dtype lowering, cluster launch dimensions, and launch-parameter validation.
  • Preserve fork-specific runtime and packaging behavior needed by the TileLang build.

Validation

  • TileLang integration CI from the TileLang repository root: ./maint/scripts/run_local_ci_test.sh 2>&1 | tee run.log
  • Examples: 75 passed, 5 skipped
  • Main test suite: 1519 passed, 196 skipped
  • Not run: TVM ./format.sh because this fork does not include that script

LeiWang1999 and others added 30 commits June 15, 2026 15:35
…che#19495)

This PR adds first-class `nn.ParameterList` and `nn.ParameterDict`
containers to the Relax frontend.

These containers provide PyTorch-like list/dict registration for raw
`nn.Parameter` objects while preserving Relax frontend semantics: values
must be explicit `nn.Parameter` instances, with no automatic
tensor-to-parameter conversion.

### Changes

- Add public `nn.ParameterList` and `nn.ParameterDict` exports.
- Support stable parameter names in traversal:
  - `params.0`, `params.1`
  - `params.foo`, `params.bar`
- Integrate the new containers with:
  - `named_parameters()`
  - `parameters()`
  - `state_dict()`
  - `load_state_dict()`
  - `to(dtype=...)`
  - `export_tvm()`
  - `nn.Mutator`
- Add focused tests for basic container behavior, type validation,
nested traversal, export parameter names, state loading, dtype
conversion, and mutator naming.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
(cherry picked from commit 86794e7)
## Summary

This PR adds Relax TFLite frontend support for the following segment
operators from apache#19412:

  - `SEGMENT_SUM`
  - `UNSORTED_SEGMENT_MIN`
  - `UNSORTED_SEGMENT_PROD`

These operators are lowered through `relax.op.scatter_nd` with the
corresponding reduction modes.

  ## Changes

  ### TFLite Frontend

  1. Add TFLite converter mappings for segment operators:
     - `SEGMENT_SUM` -> `scatter_nd(..., reduction="add")`
     - `UNSORTED_SEGMENT_MIN` -> `scatter_nd(..., reduction="min")`
     - `UNSORTED_SEGMENT_PROD` -> `scatter_nd(..., reduction="mul")`

  2. Add shared segment lowering logic:
     - Convert `segment_ids` into scatter indices via `expand_dims`.
- Build the output shape from `num_segments` or constant `segment_ids`.
- Initialize the scatter base tensor with the correct reduction
identity.

  ### Tests

  Add TFLite frontend tests for:

  - `test_segment_sum`
  - `test_unsorted_segment_min`
  - `test_unsorted_segment_prod`

Each test verifies the imported Relax IR lowers to `R.scatter_nd` with
the expected reduction mode and base tensor initialization.

  ## Testing

  All targeted tests pass:

  ```bash
  python -m pytest  \
    tests/python/relax/test_frontend_tflite.py::test_scatter_nd \
    tests/python/relax/test_frontend_tflite.py::test_segment_sum \
tests/python/relax/test_frontend_tflite.py::test_unsorted_segment_min \
tests/python/relax/test_frontend_tflite.py::test_unsorted_segment_prod \
    -q
```
  ## References

  - Issue apache#19412: TFLite Relax frontend operator support tracking
  - Related PR apache#19490: Adds SCATTER_ND support

(cherry picked from commit 8873a4c)
## Summary

The TIR CSE pass currently lifts bool-typed sub-expressions like `i < n`
or `a && b` into `cse_v: bool = ...` bindings whenever they appear
twice. Boolean expressions are almost always predicates feeding `if` /
`Select` / `assert`, where reading the condition inline is clearer than
going through a boolean temporary, and where downstream simplification
(ProveCondition, branch elimination) benefits from seeing the predicate
directly.

- Extend `CSEPlanner::IsEligible` in
`src/tirx/transform/common_subexpr_elim.cc` to reject any compound
expression whose result dtype is `bool`.
- Update the file-level `Eligibility rules` doc-comment and the
per-function `IsEligible` docstring to document the new rule.
- Add two regression tests (`test_no_lift_bool_predicate`,
`test_no_lift_bool_logical`) covering comparison predicates and
logical-And predicates respectively.

(cherry picked from commit e4c5b7c)
…d BATCH_TO_SPACE_ND (apache#19499)

**Changes**
Add tests in `test_frontend_tflite.py`.
Lower S`PACE_TO_BATCH_ND` / `BATCH_TO_SPACE_ND` through TOPI in
`tflite_frontend.py`.
Use tf.raw_ops.BatchToSpaceND in the test because tf.batch_to_space_nd
is not available in this TF build.

**Why the TFLite frontend changed**
The frontend was calling relax.op.nn.space_to_batch_nd /
relax.op.nn.batch_to_space_nd, which aren’t implemented in this
checkout. I updated the TFLite frontend to lower these ops via TOPI
packed calls so conversion works and the new tests can pass.

**Test:**
```
pytest test_frontend_tflite.py -k "test_space_to_batch_nd or test_batch_to_space_nd"
```
related to apache#18971

(cherry picked from commit 87bf302)
…pache#19497)

`topi.scatter_elements` and `topi.scatter_nd` emit bare `T.parallel`
loops in their te.extern IRBuilder bodies which trips `VerifyMemory` on
CUDA targets:

    RuntimeError: Memory verification failed
    ...
    Did you forget to bind?

CPU (LLVM) is unaffected.

This fix makes the IRBuilder body in both `topi/scatter_elements.py` and
`topi/scatter.py` target-aware. When `Target.current()` is a GPU target
it emits thread bindings instead of `T.parallel`.

Fixes apache#19451.

(cherry picked from commit fde09d2)
…ape/Tensor inputs (apache#19498)

## Description

When `from_onnx(model, keep_params_in_input=True)` is used, every ONNX
initializer becomes a `relax.Var` instead of a `relax.Constant`. The
`Concat` handler's `is_shape_like()` check only recognizes
`relax.ShapeExpr` and 1D-int64 `relax.Constant`, so a 1D-int64 shape
value loaded as a Var is no longer recognized.

When such a Var is concatenated with a `ShapeExpr` — the standard
pattern for dynamic-batch `Reshape` in PyTorch-exported ONNX models —
the heterogeneous `Tuple(ShapeExpr, Tensor)` is rejected by
`relax.op.concat` with:

```
InternalError: Op(relax.concat) expects the input to be a Tuple of Tensors.
However, the given input is R.Tuple(R.Shape([N]), R.Tensor((1,), dtype="int64"))
```

This effectively breaks `keep_params_in_input=True` for any model with
dynamic-batch `Reshape` (extremely common in PyTorch ONNX exports).

## Fix

Run each `Concat` input through the existing `get_constant` helper
before the `is_shape_like` check. This resolves any `Var` that maps to a
known param back to its baked `Constant`, restoring the all-shape-like
fast path.

## Minimal repro

An 8-node ONNX graph (`Shape` → `Slice` → `Concat([dyn_n, [12]])` →
`Reshape`) fails with `keep_params_in_input=True` before this PR and
passes after. A regression test (`test_concat_with_param_shape_value`)
covers this pattern.

## Testing

```
pytest tests/python/relax/test_frontend_onnx.py -k concat
```

9 passed (1 new + 8 existing).

(cherry picked from commit 82a37da)
…pache#19511)

## Motivation

PyTorch's `torch.flip(x, dims=[...])` reverses every listed axis. The
Relax converter `_flip` (`base_fx_graph_translator.py`) instead coerces
the list to a single integer:

```python
if isinstance(dims, list | tuple) and len(dims) > 0:
    dims = dims[0]
```

Only the first axis is forwarded to `relax.op.flip`, which is itself
single-axis. The remaining axes are silently dropped.

Minimal repro (vs PyTorch eager) on a `(3, 4)` input with
`dims=[-1, -2]`:

```
ref: [11, 10,  9,  8,  7,  6,  5,  4, ...]   # both axes flipped
tvm: [ 3,  2,  1,  0,  7,  6,  5,  4, ...]   # only last axis flipped
```

max_abs_diff = 8.0. Both the `torch.export` and legacy fx paths share
this converter, so both are affected.

## Fix

Iterate over `dims` in the converter and emit one `relax.op.flip` per
axis (flips along distinct axes commute, so the order is irrelevant).
A scalar `dims` is wrapped to a single-element list; non-int /
non-sequence arguments still raise `TypeError`.

`relax.op.flip` itself is unchanged: it is used elsewhere as a
single-axis op, and widening its signature would expand the scope of
this fix beyond the PyTorch frontend.

(cherry picked from commit 61b49bb)
…e#19512)

## Motivation

The PyTorch frontend's `_var` ignored the `correction` kwarg of
`aten.var.correction`. `torch.export.run_decompositions()` rewrites
both `aten.std.correction` and `aten.std.dim` into
`aten.var.correction(..., correction=<value>) → sqrt`, so every
`torch.std`/`torch.var` call lands in `_var` — but the correction
value was dropped on the floor. The variance was therefore always
divided by `n` regardless of what the user requested.

Minimal repro (vs PyTorch eager):

```
x = [[1, 2, 3, 4, 5], [2, 2, 2, 2, 2]]
torch.std(x, dim=1, unbiased=True)
  ref: [1.5811, 0.0]   # sqrt(2.5)
  tvm: [1.4142, 0.0]   # sqrt(2.0) -- correction silently set to 0
```

The same omission shows up for explicit `torch.var(x, correction=k)`
and any model that relies on the documented Bessel default.

## Fix

Route `aten.var.correction` (identified by `OpOverload._overloadname`,
not a substring match) to a new `_var_correction` helper. It reads
`correction` from `node.kwargs`, treats `None` as 1 to match the
overload's `Scalar? correction = None` schema, and scales the
existing `relax.op.variance` output by `n / (n - correction)` when
`correction != 0`.

When `n - correction <= 0`, the multiplier is set to NaN rather than
raising — this mirrors PyTorch's documented
`max(0, N - correction)` semantics (eager produces NaN with a warning,
not an error).

Reduction-axis sizes are read from `x.struct_info.shape`. Dynamic
sizes raise `NotImplementedError`; static-shape models cover the
real-world `torch.export` flow.

The legacy fx path through `_var` is intentionally left alone — it has
a separate preexisting bug (it reads `args[2]` as `keepdim` even when
that slot is `unbiased`), but fixing that here would expand the scope
of this PR beyond the `correction` semantics.

## Notes

- `_std` is also registered for `"std.correction"` but is unreachable on
  the default exported-program path because `aten.std.*` always
  decomposes to `var.correction + sqrt` before dispatch. Sparse-tensor
  exports that skip `run_decompositions` still hit the old `_std`; that
  path is out of scope for this fix.
- Existing `test_std`/`test_var` encoded the buggy `correction=0` IR
  for `torch.var(x)` (which defaults to Bessel) and have been updated
  to expect the correct `R.multiply(var, R.const(15/14))`. New
`test_var_correction` covers explicit `correction=2` and `correction=0`.

(cherry picked from commit 5a7da7a)
…d root-block crash (apache#19514)

## Problem

Closes apache#17873.

`DefaultGPUSchedule` crashes when a PrimFunc body is a bare
`SBlockRealize` (a fully-scalar op with no enclosing loops and no iter
vars):

```
ValueError: Check failed: (sref->parent != nullptr) is false:
  Cannot add loops on top of the root block
```

Minimal repro (TVMScript decorators are omitted in this snippet to
satisfy the PR-body lint; the regression test uses the regular
`T.prim_func` form):

```
ir_module:
  prim_func main(a: Buffer((), "float32"),
                 b: Buffer((), "float32"),
                 c: Buffer((), "float32")):
      func_attr({"target": target("nvidia/geforce-rtx-3080")})
      with sblock("scalar_add"):
          c[()] = a[()] + b[()]

s_tir.transform.DefaultGPUSchedule()(M)  # crashes
```

## Root Cause

The realized `scalar_add` block is itself the prim_func body's root
sref — it has no parent stmt to mutate. `ThreadBind`
(`src/s_tir/transform/default_gpu_schedule.cc`) reaches the
`loops.empty()` branch and calls `sch->AddUnitLoop(block)`, which fails
the `sref->parent != nullptr` check in `s_tir::AddUnitLoop`
(`src/s_tir/schedule/primitive/loop_transformation.cc:1166`).

The schedule infrastructure additionally requires the prim_func body
to be an `SBlockRealize` whose block is the function's root
(`GetRootPrimFunc` in `src/s_tir/schedule/analysis/analysis.cc:53`),
so the body cannot simply be wrapped in a top-level `For`.

## Fix

Before constructing the schedule, rewrite GPU-bound PrimFuncs whose
body is a bare-leaf `SBlockRealize` so the realized block is no longer
the root. The wrap conditions are intentionally narrow:

1. `func->body` is `SBlockRealize`,
2. the realized block has empty `iter_vars`, and
3. the block's body is not `For` or `SBlockRealize` (i.e. it is a leaf
   computation, not the well-formed implicit root that wraps a loop
   nest produced by the rest of the pipeline).

When all three hold, the body becomes:

```
SBlockRealize(
  block=SBlock("root", body=
    For(u, 0, 1, kSerial,
      SBlockRealize(iter_values=[u],
        block=<original block, iter_vars=[IterVar(0..1, vu, kDataPar)]>))))
```

The synthesised 1-extent data-parallel iter keeps
`iter_values.size() == iter_vars.size()` for downstream checks, and the
new For loop gives `ThreadBind` a real loop to bind to `blockIdx.x` /
`threadIdx.x`. Already-scheduled functions and host-only PrimFuncs are
skipped via the existing `IsScheduledOnGPU` / `kIsScheduled` gating.

## Testing

```
pytest tests/python/s_tir/transform/test_s_tir_transform_default_gpu_schedule.py
```

10 passed (9 existing + 1 new `test_scalar_block_no_loops`). End-to-end
compile + execute on RTX 3080 (sm_86): the scalar repro returns the
expected `2.0 + 3.0 = 5.0`.
…9516)

This adds explicit Expected IRModule coverage for TFLite GATHER and
GATHER_ND frontend conversion.

GATHER_ND uses Relax gather_nd with int64 indices, so the frontend now
casts int32 TFLite indices to int64 before emitting the Relax op. This
keeps the generated module well-typed and matches the expected Relax IR.

Testing:
- `python -m pytest tests/python/relax/test_frontend_tflite.py -k
"gather"`

related to apache#18971
…s index_put_ with tuple output (apache#19488)

Hi Committers,

This PR is trying to fix issues
apache#18363. Any suggestions would be
appreciated if you are available.

### Root Cause
- When an ExportedProgram's FX graph output node returns a **nested
Python tuple** (e.g., buffer mutation outputs + user-defined tuple
returns), `_translate_fx_graph()` passes the raw nested structure
directly to the Relax FFI Tuple constructor.
- The C++ Array<Expr> initializer cannot handle heterogeneous/nested
Python containers, causing a segmentation fault at `expr.cc`.
- Additionally, index_put_ (in-place write op) did not update self.env
to alias the source tensor to the mutated output, causing subsequent FX
nodes that read the same tensor to observe **stale pre-mutation
values**.

### Solution
- exported_program_translator.py
- Added static method `_flatten_output_args()` that recursively walks
any Python `tuple/list`, collects only `relax.Expr` leaves, and preserve
explicit None outputs as Relax null objects.
- Replaced the fragile `assert isinstance(output_args, tuple |
relax.Tuple)` guard with a call to `_flatten_output_args()`, producing a
clean flat tuple of `relax.Expr` before FFI construction.
- base_fx_graph_translator.py
- In `_index_put()`, after emitting the `relax.op.index_put(...)` call,
added an env alias update: `self.env[source_node] = output` when the
target op name starts with `index_put_`, preserving correct in-place
mutation semantics for downstream FX nodes.

---------

Co-authored-by: cchung100m <cchung100m@users.noreply.github.com>
Description
This PR adds support for the CONV_3D operator in the TFLite frontend for
Relax.

  Key Changes
- Operator Mapping: Added CONV_3D to the OperatorConverter mapping in
tflite_frontend.py.
   - Implementation:
- Implemented convert_conv3d to handle 3D convolution attributes such as
StrideD/H/W, DilationD/H/W, and Padding.
- Correctly handled the TFLite 3D kernel layout, which is expected to be
DHWIO (Depth, Height, Width, Input Channels, Output Channels).
- Integrated support for fused activation functions (ReLU, ReLU6, etc.)
directly following the convolution.
   - Unit Tests:
- Added comprehensive tests in
tests/python/relax/test_frontend_tflite.py covering:
           - VALID and SAME padding modes.
           - Various stride and dilation configurations.
           - Verification against expected Relax IR structure.

  Testing:
- `python3 -m pytest tests/python/relax/test_frontend_tflite.py -k
"test_conv3d"`

  Notes for Reviewers
The implementation follows the existing pattern used for CONV_2D but
extends it to the 5D case (NDHWC layout). I've ensured that the kernel
layout mapping aligns with TVM's R.nn.conv3d requirements.

Related to: apache#19519
## Summary

`AttrFunctor` is declared infrastructure with no remaining users.
An exhaustive search across `include/`, `src/`, `tests/`, `python/`,
`apps/`, `web/`, and `cmake/` confirms zero subclasses, zero friend
declarations, and zero macro callers outside the header itself —
its two internal macros (`ATTR_FUNCTOR_DEFAULT`,
`ATTR_FUNCTOR_DISPATCH`)
are only used inside `src/ir/attr_functor.h`. The one
`#include "attr_functor.h"` in `src/ir/attrs.cc` is a stale leftover
from a prior migration; that file references only `DictAttrs` and
`AttrFieldInfoNode` (from `tvm/ir/attrs.h`), not any `AttrFunctor`
symbols.

Removes `src/ir/attr_functor.h` (150 lines) and drops the stale include.
No build-system changes needed — the header was never enumerated in
any `CMakeLists.txt`.
…ther` operator (apache#19525)

Hi Committers,

This PR is trying to fix issues
apache#19436. Any suggestions would be
appreciated if you are available.

### Root Cause
1. ONNX `Gather` allows negative indices (counting from the end of the
target axis).
2. In the Relax ONNX importer, `Gather` was lowered directly to
`relax.op.take` without normalizing negative indices first.
3. This created semantic mismatch / incorrect behavior in downstream
lowering paths that assume non-negative indices.
4. Test failures were also caused by pytest parametrization issues:
  - using ONNX `TensorProto` enum values directly as NumPy dtypes,
- and tuple-style parametrization triggering fixture interpretation
errors.
  
### Solutions
1. Added conditional negative-index normalization in `Gather._impl_v13`:
  - apply only for signed index dtypes,
  - use: `idx < 0 ? idx + axis_extent : idx`,
- derive `axis_extent` from shape/runtime expression to support dynamic
shapes.
2. Skipped normalization for unsigned index dtypes to avoid redundant
graph ops/checks.

---------

Co-authored-by: cchung100m <cchung100m@users.noreply.github.com>
…pache#19530)

This commit adds support for the CONV_3D_TRANSPOSE operator in the Relax
TFLite frontend.

Key implementations:
- Registered CONV_3D_TRANSPOSE to the TFLite op map.
- Implemented convert_conv3d_transpose which shares Conv3DOptions with
regular Conv3D but handles the distinct tensor input layout
[output_shape, weight, data, bias] and the DHWOI kernel layout.
- Added calculation for SAME padding that correctly handles transposed
convolution semantics, computing padding and output_padding based on
dilated kernel and stride sizes.
- Added comprehensive unit tests for valid and same padding in
test_frontend_tflite.py.

Testing:
- `python3 -m pytest tests/python/relax/test_frontend_tflite.py -k
"test_conv3d_transpose"`

Related to: apache#19519
…orage scope (apache#19423)

part of tile-ai/tilelang#1869

## Summary
Add TIR builtins and storage scope for Metal cooperative_tensor
operations (MetalPerformancePrimitives / Metal 4).

## Motivation
Apple Metal 4 introduces MetalPerformancePrimitives (MPP) with
`matmul2d` using `cooperative_tensor` operands. On M5, this routes to
NAX tensor cores; on M1-M4, it falls back to simdgroup matrix
instructions. These TIR primitives enable backend codegen to emit MPP
calls.

## Changes

### New TIR builtins
- `cooperative_tensor_fill(d, index, value, rows, cols)`
- `cooperative_tensor_load(d, index, ptr, stride, rows, cols,
transpose)`
- `cooperative_tensor_store(d, index, ptr, stride, rows, cols,
transpose)`
- `cooperative_tensor_multiply_accumulate(d, di, a, ai, b, bi, c, ci, M,
N, K, trans_a, trans_b)`

### New storage scope
- `metal.cooperative_tensor` (`StorageRank::kMetalCooperativeTensor`)

### Files changed
- `include/tvm/tirx/builtin.h` — Op declarations
- `src/tirx/op/builtin.cc` — Op registrations
- `python/tvm/tirx/op.py` — Python wrappers
- `python/tvm/script/ir_builder/tirx/ir.py` — Script parser exports
- `src/runtime/thread_storage_scope.h` — StorageRank enum + scope
parsing

These builtins mirror the existing `simdgroup_*` builtins for the older
Metal simdgroup matrix API, extended with M/N/K dimension parameters for
the matmul2d descriptor.
…ort (apache#19536)

## Summary

This PR adds initial Relax TFLite frontend support for 29 StableHLO
builtin
operators from apache#19519 item I.

The covered subset includes pure elementwise ops, BuiltinOptions2 /
metadata-based ops, simple shape-manipulation ops, and a take-equivalent
subset
of `STABLEHLO_GATHER`.

StableHLO builtins carry no TFLite-specific quantization or
fused-activation
metadata, so the implementation uses dedicated converter helpers that
bypass the
existing TFLite elemwise/QNN code paths.

Relates to apache#19519.

## Changes

1. **Zero-attribute elementwise helpers**
   - Add `_convert_stablehlo_unary`, `_convert_stablehlo_binary`, and
     `_convert_stablehlo_ternary` for pure elementwise mapping.
- Register 20 ops: unary (`ABS`, `NEGATE`, `COSINE`, `EXPONENTIAL`,
`FLOOR`,
`LOG`, `LOGISTIC`, `RSQRT`, `TANH`), binary (`ADD`, `SUBTRACT`,
`MULTIPLY`,
`DIVIDE`, `MAXIMUM`, `MINIMUM`, `POWER`), ternary (`SELECT` →
`R.where`),
and dtype-dispatched bitwise/logical ops (`AND` / `OR` → logical ops for
bool or bitwise ops for integer, `SHIFT_LEFT` → `R.left_shift` for
integer).

2. **BuiltinOptions2 infrastructure**
- Add `_get_stablehlo_options` helper for parsing `BuiltinOptions2`
flatbuffers
with enum validation via `getattr(BuiltinOptions2,
options_cls.__name__)`.
   - Register 6 ops: `CONVERT` → `R.astype`, `CLAMP` →
     `R.minimum(R.maximum(...))`, `CONCATENATE` → `R.concat`,
     `BROADCAST_IN_DIM` → `R.reshape` + `R.broadcast_to`, `IOTA` →
`R.arange` + `R.broadcast_to`, and `COMPARE` → 6 comparison directions
     (`TOTALORDER` raises `OpNotImplemented`).

3. **Shape-manipulation ops**
   - `PAD` → `R.nn.pad` in constant mode. The initial PAD path supports
non-negative edge padding with zero interior padding and a constant
scalar
padding value. Interior padding, negative padding, and dynamic padding
     values raise `OpNotImplemented`.
- `DYNAMIC_SLICE` → `R.dynamic_strided_slice`. The initial path supports
     constant, in-bound start indices only. Runtime start indices and
     out-of-bounds StableHLO clamping semantics are deferred.

4. **Indexing op**
   - `GATHER` → `R.take` for the take-equivalent subset only.
- Parses the relevant `StablehloGatherOptions` attributes needed to
validate
this subset: `offset_dims`, `collapsed_slice_dims`, `start_index_map`,
     `index_vector_dim`, and `slice_sizes`.
- Validates the gather axis, collapsed dims, offset dims, slice sizes,
and
output shape against the expected `R.take` layout. Multi-dimensional and
     non-take-equivalent gather patterns raise `OpNotImplemented`.

5. **Not included**
- `STABLEHLO_RESHAPE`, `STABLEHLO_TRANSPOSE`, and `STABLEHLO_SLICE` are
left
     to another contributor who expressed interest in those ops.
- The remaining Issue apache#19519 StableHLO items are deferred to follow-up
PRs:
`CBRT`, `REMAINDER`, `SCATTER`, `CONVOLUTION`, `DOT_GENERAL`, `REDUCE`,
`REDUCE_WINDOW`, `DYNAMIC_UPDATE_SLICE`, `COMPOSITE`, `CUSTOM_CALL`,
     `RNG_BIT_GENERATOR`, `SORT`, and `WHILE`.
- More general or multi-dimensional `STABLEHLO_GATHER` patterns are also
     deferred to follow-up work.

## Testing

All tests use manually-built minimal TFLite flatbuffers with
`tvm.ir.assert_structural_equal`. BuiltinOptions2 ops construct their
options
via the FlatBuffers schema API, modeled after the existing DILATE test
pattern.

```bash
python -m pytest tests/python/relax/test_frontend_tflite.py -k stablehlo -q
```

## Result

- 29 StableHLO operators registered in the Relax TFLite frontend.
- 44 StableHLO test cases covering all registered ops, including
  structural-equal tests and unsupported/error-path checks:

  - `COMPARE` with `TOTALORDER`
- `PAD` with interior padding, negative padding, and dynamic padding
values
  - `DYNAMIC_SLICE` with runtime starts and out-of-bounds starts
  - non-take-equivalent or multi-dimensional `GATHER`
- All StableHLO TFLite frontend tests pass locally.

## References

- Issue apache#19519 item I: StableHLO operators in TFLite
- Related PR apache#19481: DILATE operator mapping, the first use of
BuiltinOptions2
  in the TFLite frontend tests
)

Six CUDA sources in src/runtime/contrib used LOG(FATAL) via transitive
includes that apache#19483 trimmed; add the explicit <tvm/runtime/logging.h>
include to thrust.cu, attention_kernels.cu, and the four cutlass kernel
headers (fp16/fp8 sm90/sm100, gemm_runner, fp8_groupwise_scaled_gemm).

cache_kernels.cu used the bare Array{...} alias that apache#19483 removed;
switch to ffi::Array<Tensor>{...}.

attention_kernels.cu registered FFI functions whose parameters were raw
DLTensor*; the new reflection registry requires TypeSchema, so wrap both
TVM_FFI_STATIC_INIT_BLOCK registrations to take Tensor and forward to
the unchanged launchers via GetDLTensorPtr() (with const_cast for the
output tensors, matching the mt_random_engine / cudnn pattern).
### Summary

- Respect the ONNX `reduction` attribute in the Relax ONNX frontend
`ScatterElements` converter.
- Preserve existing default behavior by mapping missing reduction and
ONNX `none` to Relax `update`.
- Add focused regression coverage for opset 11 default behavior, opset
16 `add`/`mul`, and opset 18 `none`/`min`/`max`.

### Changes

- Added a shared helper to normalize and validate ONNX reduction
attributes.
- Implemented `ScatterElements` opset 16 and opset 18 converters.
- Reused the existing `relax.op.scatter_elements(..., reduction=...)`
API.
- Reused the same reduction helper in `ScatterND` to keep behavior
consistent.

### Test Plan

- `python -m py_compile python/tvm/relax/frontend/onnx/onnx_frontend.py
tests/python/relax/test_frontend_onnx.py`
- `python -m pytest
tests/python/relax/test_frontend_onnx.py::test_gather_elements
tests/python/relax/test_frontend_onnx.py::test_scatter
tests/python/relax/test_frontend_onnx.py::test_scatter_elements_reduction
tests/python/relax/test_frontend_onnx.py::test_scatter_nd -q`

### Issue

Fixes apache#19435

## Local Verification Notes

- WSL conda environment: `/home/thinker/.cache/tvm-conda-onnx`
- TVM build directory: `/home/thinker/.cache/tvm-build-onnx`
- LLVM runtime check: `tvm.runtime.enabled("llvm") == True`
- Relevant ONNX frontend subset: `15 passed, 4 skipped, 2 warnings`
- Full `tests/python/relax/test_frontend_onnx.py` was also attempted. It
currently has 14 failures in unrelated `Reduce* axes input` and `TopK`
tests; running the same selected failures against `origin/main`
reproduces them, so they are not introduced by this PR.
apache#19535)

This PR fixes apache#19533:
- Sanitize floating tensor min/max: replace NaN with +inf/-inf before
topi max/min so bounds match ONNX "unbounded" semantics where NaN bounds
default to no constraint.
- After clamping, preserve NaNs from the input tensor on floating
dtypes.
- Extend check_correctness with equal_nan for float outputs containing
NaN.
- Add parametrized Clip opset-13 tests for NaN min/max tensor bounds.
This PR fixes apache#19552.

astral-sh/setup-uv is not on the ASF GitHub Enterprise action allowlist,
causing the Lint workflow to fail with "Startup failure" before any
pre-commit checks run. See
https://github.com/apache/tvm/actions/runs/25743684906 for the failed
reason.

This PR removes the uv setup and sync steps entirely; pre-commit/action
will install and manage pre-commit and all hook dependencies on its own.
This PR also corrected previous lint errors.

After the fix, the CI lint succeeded:
https://github.com/apache/tvm/actions/runs/25775499703/job/75707088129
… NonMaxSuppression (apache#19547)

Hi Committers,

This PR is trying to fix issues apache#19544. Any suggestions would be
appreciated if you are available.

---------

Co-authored-by: cchung100m <cchung100m@users.noreply.github.com>
…apache#19515)

## Summary

Introduce a test runner that reuses the official ONNX Backend Test Suite
to systematically verify the Relax ONNX importer. This complements the
existing hand-written tests in `test_frontend_onnx.py` by providing
spec-aligned coverage of standard ONNX operator semantics.

Towards apache#19505

## Motivation

The existing `test_frontend_onnx.py` has 187 hand-written tests that
validate TVM-specific importer behavior (parameter handling, name
sanitization, dynamic shapes, Relax IR structure). However, it relies on
ONNX Runtime as the reference and cannot systematically cover all edge
cases defined in the ONNX specification.

The ONNX Backend Test Suite provides 1653+ node-level tests with
protobuf reference inputs/outputs. It is the industry standard for
validating ONNX importers/exporters (used by ONNX Runtime, TensorFlow,
PyTorch). Reusing it gives Relax a living, upstream-aligned correctness
baseline.

## What this PR adds

- `tests/python/relax/test_frontend_onnx_backend.py` — a backend adapter
(`TVMRelaxBackend`) that implements the `onnx.backend.base.Backend`
interface, wiring `from_onnx()` → `DecomposeOpsForInference()` →
`LegalizeOps()` → `tvm.compile()` → `VirtualMachine`.

## Coverage

72 operators with 388 test cases, all passing. Only operators where
every ONNX node test passes are included — no xfail markers.

Operators not yet covered include: cast (exotic dtypes), reduce ops
(edge cases), reshape/resize/attention (complex behavior), quantization,
and several others with known importer gaps. These can be added
incrementally as the importer improves.

## Test results

388 passed, 3216 skipped (CUDA variants + operators not yet in
allowlist), 0 failed, 0 xfailed

## CI impact

- New test file is not added to any existing CI test shard by default
- Full suite (388 tests) is lightweight on CPU-only runners

## Design decisions

- **Coexistence with existing tests**: `test_frontend_onnx.py` remains
unchanged. Backend tests cover standard ONNX semantics; hand-written
tests continue to cover TVM-specific behavior (dynamic shapes, Relax IR
structure, importer options).
- **Public API only**: uses `backend_test.include()` with `^`-anchored
regex patterns. No access to private ONNX APIs.
- **No xfail**: only include operators that fully pass. Uncovered
operators are documented in code comments and this PR description.
Follow-up PRs can expand coverage as importer gaps are fixed.
- **Prefix conflict handling**: `include()` patterns use
`^test_{op}(?:_.*)?(?:_cpu|_cuda)$`, which can cause false matches when
a short op name is a prefix of a longer one (e.g. `log` vs
`log_softmax`). Affected ops (`log`, `max`, `relu`) are excluded until a
more precise matching strategy is adopted.
This PR fixed apache#19551.

Bool product has logical-AND semantics and cannot be lowered through TIR
Mul for LLVM codegen. Route bool prod through all() and add frontend and
legalization coverage for bool R.prod.
Hi Committers,

This PR is trying to fix issues apache#19541. Any suggestions would be
appreciated if you are available.

### Root cause:
The ONNX `Div` path in the Relax frontend did not separate two
integer-divisor cases: constant zero divisors and dynamic/unknown
divisors. As a result, constant integer zero divisors were not rejected
during import, and dynamic integer divisors could reach runtime without
a guard. When the divisor became zero at runtime, execution could
trigger SIGFPE and terminate the process instead of raising a controlled
error.

### Solution:
This PR applies a minimal, targeted fix in the ONNX frontend `Div`
conversion path. It introduces: import-time validation for constant
integer divisors containing zero, raising ValueError early.

---------

Co-authored-by: cchung100m <cchung100m@users.noreply.github.com>
This PR adds the initial TIRx support needed for low-level programming
of Blackwell-class GPU architectures. As part of the ongoing TIRx
refactor, it introduces TVMScript support for directly scripting
advanced hardware features without relying on scheduling as the primary
programming interface.

The change keeps existing `s_tir` script support intact while making
direct scripting a first-class path for TIRx programs.

- Add TIRx operator dispatch and layout infrastructure.
- Add TVMScript support for new low-level TIRx operations.
- Add analysis, transform, and lowering support for TIRx IR nodes.
- Add CUDA/Blackwell-oriented codegen and intrinsic coverage.
- Add Python and C++ integration points for TIRx scripting and runtime
support.

- `pre-commit run --all-files`
- `ninja -C build -j32`
- `CUDA_VISIBLE_DEVICES=2 pytest tests/python/tirx/ -n 16`
  - `1723 passed, 47 skipped, 32 warnings`
- `CUDA_VISIBLE_DEVICES=2 python -m pytest -v
tests/python/all-platform-minimal-test`
  - `37 passed, 105 skipped`
- `TVM_TEST_TARGETS=llvm python -m pytest -v tests/python/tirx-analysis
tests/python/tirx-base tests/python/tirx-transform -n 16`
  - `664 passed, 25 skipped, 9 xfailed, 1 xpassed`

Some full CI-equivalent jobs were not locally reproducible because this
machine is missing parts of the Apache TVM CI environment, including
`llvm-config-15/17`, Vulkan, ROCm, Maven, Sphinx, Doxygen, Emscripten,
and ARM/QEMU cross-toolchain components. Metal-specific tests were
skipped locally because no Metal runtime is available.
… Taylor (apache#19567)

## Summary

`tirx.asin`'s LLVM legalize used a 6-term Taylor series for `|x| < 0.5`
with wrong recurrence coefficients. The ratios in the code (`9/40`,
`25/112`, `1225/3456`, `3969/28160`) don't match the real asin series
(`9/20`, `25/42`, `49/72`, `81/110`), so mid-range inputs lose ~1e-3 of
precision — over 1000 float32 ULP. `acos` inherits it via `π/2 −
asin(x)`.

```
x=0.47  ORT=0.48929077  TVM(old)=0.48820966  err=-1.08e-3
```

The Taylor branch was added in apache#17945 as the initial implementation,
with no libm fallback. apache#18582 later patched only `|x| ≥ 0.5` by routing
to the libm extern, leaving the buggy mid-range in place. I see no
evidence the inline series was an intentional fast-path.

## Fix

Drop the inline series, route the whole domain through the existing
`asinf`/`acosf` extern, keep the out-of-range NaN guard. Max error over
`x ∈ [-1, 1]` drops to **2.4e-7** (ULP-grade).

## Tests

- Re-enable `Asin`/`Acos` in `test_unary` (they were commented out with
a TODO about Taylor precision loss).
- Existing `test_asin_acos_boundary_values` (apache#18582) still passes.

If the inline polynomial was intentional for some target/path, please
flag it — I'll restore it with corrected coefficients instead.

`Atan` is still disabled; that's a separate `x² + 1` overflow bug
(apache#19560).

Fixes apache#19563.
… option (apache#19565)

Fix CUDA lowering of standard TIR math intrinsics so they use precise
CUDA math functions by default instead of fast-math `__*f` functions.
This fixes the default behavior reported in apache#19546, where operators such
as `tirx.exp` could lower to `__expf` even though fast math was not
explicitly requested.

This change adds a CUDA target attribute, `enable_fast_math`, which
defaults to `false`. When the attribute is unset or false, standard math
intrinsics lower through the normal CUDA math rule, for example `expf`,
`logf`, `sinf`, `cosf`, `powf`, and `rsqrtf` for `float32`. When users
explicitly enable the attribute on the target, the lowering pass also
checks the `cuda.fastmath.FLowerIntrinsic` rules before the normal CUDA
lowering rules.

Users can opt in to fast math by constructing a CUDA target with the
attribute:

```py
tvm.target.Target({"kind": "cuda", "enable_fast_math": True})
target = tvm.target.Target({
    "tag": "nvidia/nvidia-a100",
    "enable_fast_math": True,
})
```

The fast-math lowering path currently covers the CUDA math operators
registered with `cuda.fastmath.FLowerIntrinsic`: `tirx.exp`,
`tirx.exp10`, `tirx.log`, `tirx.log2`, `tirx.log10`, `tirx.tan`,
`tirx.cos`, `tirx.sin`, `tirx.tanh`, and `tirx.pow`.

`tirx.rsqrt` is also registered for CUDA lowering so it maps to the CUDA
reciprocal-square-root intrinsic instead of being legalized as `1 /
sqrt(x)`.

Add CUDA codegen tests
`tests/python/codegen/test_target_codegen_cuda_fastmath.py` that check
the lowered IR, generated CUDA source, and runtime results for the
supported math intrinsics across floating point dtypes and both default
and fast-math targets.
ConvolutedDog and others added 30 commits June 15, 2026 19:43
…on (apache#19643)

This PR will fix apache#19592.

LayerNorm could produce NaN on large-value, small-variance inputs due to
catastrophic cancellation in var = E[x^2] - E[x]^2.

Switch to a numerically stable two-pass formulation:

  - pass1 computes mean via sum(x) / N
  - pass2 computes variance via sum((x - mean)^2) / N

(cherry picked from commit fca86a6)
… same tensor (apache#19644)

This PR will fix apache#19577.

In this issue, the IRModule before applying any pass looks like:

```
  %x: Tensor[(4,), float32]  // function param
  with R.dataflow():
    %lv  = expand_dims(%x, axis=1)     // (4, 1)
    %lv1 = expand_dims(%x, axis=1)     // (4, 1) second call, new Var
    %lv2 = multiply(%lv, %lv1)         // (4, 1)
    %lv3 = concat(%lv2, %lv1, axis=1)  // (4, 2)
    ...
```

When the users manually apply the `DataflowUseInplaceCalls` pass, the
pass will rewrite the statement `%lv2 = multiply(%lv, %lv1)` to be like
`%lv = multiply(%lv, %lv1); %lv3 = concat(%lv, %lv1, axis=1)`, which
reuses the %lv buffer to avoid storage waste.

But this rewrite will chang the buffer context of %lv, and also in LLVM
generated code, %lv1 shared the same storage with %lv, so when executing
`%lv = concat(%lv, %lv1, axis=1)`, the %lv1 context has also been
changed to `multiply(%lv, %lv1)`. So the failure is due to the shared
storage of different views of the same tensor %x.

During the execution, %lv1 holds `x^2` instead of `x` after `multiply`.
`concat` reads %lv1 for the right column and its result is
[[1,1],[4,4],[9,9],[16,16]] instead of [[1,1],[4,2],[9,3],[16,4]] (the
correct result should be : left col `x^2`, right col should stay `x`).

Change: View-like ops (expand_dims, squeeze, reshape, permute_dims,
memory.view, ensure_zero_offset) take the input's alias set in alias
analysis instead of a new id: %lv and %lv1 share alias with %x. Then the
pass rejects in-place of `multiply(%lv, %lv1)`: %lv and %lv1 are
different vars but alias ids intersect, so no operand may be reused
in-place.

(cherry picked from commit d705cb2)
## Summary

This PR adds conservative Relax TFLite frontend support for the TFLite
builtin
`STABLEHLO_CUSTOM_CALL` operator.

TFLite marks `STABLEHLO_CUSTOM_CALL` as having no runtime kernel.
Importing
general custom calls as executable Relax operators would therefore give
them
semantics that TFLite itself does not provide. This PR only supports the
metadata-only `Sharding` custom call target, which TensorFlow's
StableHLO
pipeline treats as an annotation that can be erased.

## Design

### Sharding Annotation Lowering

`STABLEHLO_CUSTOM_CALL` now parses `StablehloCustomCallOptions` from
`BuiltinOptions2` and reads the `call_target_name`.

For `call_target_name == "Sharding"`, the frontend lowers the op to
identity:
the output tensor is bound to the input expression. This mirrors
TensorFlow's
handling of Sharding custom calls as metadata annotations. The sharding
spec in
`backend_config` is intentionally dropped for single-device import.

The supported subset is guarded:

- exactly one input and one output
- input and output shape/dtype metadata must match
- `has_side_effect` must be false
- `called_computations` must be empty

All other custom-call targets raise `OpNotImplemented` with the target
name in
the diagnostic.

## Operator Support

| Operator | TFLite options | Relax lowering | Supported subset |
|---|---|---|---|
| `STABLEHLO_CUSTOM_CALL` | `StablehloCustomCallOptions` from
`BuiltinOptions2` | identity for `Sharding`; otherwise unsupported |
metadata-only `Sharding` annotations with unchanged tensor metadata |

## Tests

The tests manually build minimal StableHLO custom-call TFLite
flatbuffers and
compare the supported identity path with
`tvm.ir.assert_structural_equal`.
Unsupported patterns use `pytest.raises`.

| Test | Coverage |
|---|---|
| `test_stablehlo_custom_call_sharding` | `Sharding` annotation lowers
to identity |
| `test_stablehlo_custom_call_unsupported_target` | unknown external
target guard |
| `test_stablehlo_custom_call_sharding_side_effect_unsupported` |
side-effecting `Sharding` guard |
| `test_stablehlo_custom_call_sharding_metadata_mismatch_unsupported` |
input/output metadata guard |

Local validation:

```bash
python -m py_compile \
  python/tvm/relax/frontend/tflite/tflite_frontend.py \
  tests/python/relax/test_frontend_tflite.py

python -m ruff check \
  python/tvm/relax/frontend/tflite/tflite_frontend.py \
  tests/python/relax/test_frontend_tflite.py

python -m pytest \
  tests/python/relax/test_frontend_tflite.py \
  -k stablehlo_custom_call -q

python -m pytest \
  tests/python/relax/test_frontend_tflite.py \
  -k stablehlo -q
```

Result:

```text
py_compile: passed
ruff check: All checks passed
stablehlo_custom_call tests: 4 passed
stablehlo tests: 81 passed
```

## References

- Issue apache#19519 item I: remaining StableHLO operators in TFLite
- TensorFlow Lite schema marks `STABLEHLO_CUSTOM_CALL` as no runtime
support
- TensorFlow StableHLO pipeline erases `Sharding` custom calls as
metadata annotations

(cherry picked from commit cf859b9)
…pache#19653)

In continuation of apache#19624 this catches some unlifted entries.

Hope there is no more left, for consistency it now covers comments and
perhaps non-active (hotpath) parts.

(cherry picked from commit 349225a)
## Summary

Add Relax TFLite frontend support for `HASHTABLE_LOOKUP`.

This PR adds a converter for `HASHTABLE_LOOKUP` in the Relax TFLite
frontend. The implementation supports non-string value tensors and
lowers the lookup through `bucketize`, `take`, and `where` so that
missing keys return zero-filled values together with a `uint8` hits mask
matching TFLite semantics for the supported cases.

The PR also adds handcrafted TFLite frontend tests covering:
- 1D float value tensors
- 2D float value tensors
- the current unsupported string-value case

## Testing

Ran `tests/python/relax/test_frontend_tflite.py -k 'hashtable_lookup'`.

Part of apache#19519

(cherry picked from commit 066bf77)
…#19651)

## Summary

This PR adds Relax TFLite frontend support for the TFLite builtin
`STABLEHLO_RNG_BIT_GENERATOR` operator.

Unlike most StableHLO builtins, the TFLite runtime
(`tensorflow/lite/kernels/rng_bit_generator.cc`) implements this op as a
real,
deterministic counter-based PRNG, so the importer must reproduce it
bit-exactly
rather than map it to an existing op:

- one uint64 1-D `initial_state` input, two outputs — uint64
`output_state` and
  the random-bit `output` (int32 / int64 / uint32 / uint64);
- `algorithm` in `{DEFAULT, PHILOX, THREEFRY}`, where `DEFAULT` resolves
to
  `PHILOX`;
- Random123 Threefry2x32 (20 rounds) and Philox4x32 (10 rounds) with the
fixed
  constants from `rng_util.cc`;
- state-length constraints: `THREEFRY` requires `u64[2]`,
`PHILOX`/`DEFAULT`
  require `u64[2]` or `u64[3]`.

## Design

TVM/Relax has no matching RNG primitive, so the converter generates a
TIR kernel
that mirrors the runtime and emits it through `relax.call_tir` with two
outputs.
The kernel:

- reinterprets the uint64 state as uint32 words and advances a 64-bit
block
  counter (`final counter = initial_state[1] + num_blocks`);
- runs the selected algorithm per block with all round state
materialized into
local buffers, which keeps the generated IR linear instead of an
exponentially
  nested expression tree;
- packs the produced uint32 words back into the output dtype, and writes
the
updated state (key unchanged, counter advanced, Philox `u64[3]` tail
passed
  through) — the only state behaviour the runtime relies on.

The kernel is an `s_tir` PrimFunc wrapped in a single opaque structured
block so
it remains a well-formed block-structured function for the Relax
pipeline
(e.g. `HasReshapePattern`). `get_tensor_type_str` and the input
`_decode_type`
map are extended with uint32/uint64 so the uint64 state imports
correctly.

Unsupported inputs raise a precise `OpNotImplemented` (non-uint64 /
non-1-D
state, mismatched output-state shape, unsupported output dtype, unknown
algorithm, per-algorithm state-length violations).

## Operator Support

| Operator | TFLite options | Relax lowering | Supported subset |
|---|---|---|---|
| `STABLEHLO_RNG_BIT_GENERATOR` |
`StablehloRngBitGeneratorOptions.Algorithm()` from `BuiltinOptions2` |
`call_tir` to a generated bit-exact TIR kernel | THREEFRY (`u64[2]`) and
PHILOX/DEFAULT (`u64[2]`/`u64[3]`); int32/int64/uint32/uint64 output |

## Tests

Tests build minimal RNG flatbuffers, compile, and execute them,
comparing the
output and updated state against the verbatim expected vectors from the
TFLite
runtime kernel test (`rng_bit_generator_test.cc`).

| Test | Coverage |
|---|---|
| `test_stablehlo_rng_bit_generator_threefry` | THREEFRY bit-exact, all
4 output dtypes |
| `test_stablehlo_rng_bit_generator_philox` | PHILOX bit-exact, all 4
output dtypes |
| `test_stablehlo_rng_bit_generator_default_matches_philox` | DEFAULT
resolves to PHILOX |
| `test_stablehlo_rng_bit_generator_deterministic` | run-to-run
bit-identical output |
| `test_stablehlo_rng_bit_generator_unsupported_output_dtype` | output
dtype guard |
| `test_stablehlo_rng_bit_generator_threefry_invalid_state_unsupported`
| THREEFRY `u64[2]` state guard |
| `test_stablehlo_rng_bit_generator_non_uint64_state_unsupported` |
uint64 state guard |

Local validation:

```bash
python -m ruff check \
  python/tvm/relax/frontend/tflite/tflite_frontend.py \
  tests/python/relax/test_frontend_tflite.py

python -m pytest \
  tests/python/relax/test_frontend_tflite.py \
  -k rng_bit_generator -q

python -m pytest \
  tests/python/relax/test_frontend_tflite.py \
  -k stablehlo -q
```

Result:

```text
ruff check: All checks passed
rng_bit_generator tests: 13 passed
stablehlo tests: 96 passed
```

## References

- Issue apache#19519 item I: remaining StableHLO operators in TFLite
- `tensorflow/lite/kernels/rng_bit_generator.cc`, `rng_util.cc`,
  `rng_bit_generator_test.cc`

(cherry picked from commit 23a0ea8)
…ache#19648)

## Summary

This patch resolves a security vulnerability by explicitly setting the
`android:exported` flag within the `AndroidManifest.xml` file.
Previously, certain components were missing this required flag, which
could lead to incorrect permission handling and exposure, potentially
allowing unauthorized access to components of the application.

## Changes

* **Security:** Added explicit `android:exported="true"` or
`android:exported="false"` flags to relevant `<activity>`, `<receiver>`,
and `<provider>` tags in `AndroidManifest.xml`.
* **Safety:** Ensures that all exposed components properly define their
export status, adhering to modern Android best practices and mitigating
potential misconfigurations.
* **Compatibility:** Improves the application's security posture and
adherence to Android framework requirements regarding component
visibility.

## Testing
- Verified logic locally using Docker sandbox

Fixes #AUTO_SEMGREP

Co-authored-by: CodeMechanic <codemechanic@local.ai>
(cherry picked from commit 5649122)
apache#19645)

### Motivation

`torch.logical_not` accepts an input tensor of any dtype (treating any
nonzero
element as `True`) and always returns a `bool` tensor.

The PyTorch frontend previously lowered it with
`self._unary_op(relax.op.logical_not)`.
`relax.op.logical_not` is a unary arithmetic op that passes its input
dtype through,
so a non-bool input (for example `float32`) produced a `float32` result
instead of
the `bool` result PyTorch returns. This is a dtype mismatch against the
reference
PyTorch semantics for both the FX and ExportedProgram frontends.

### Changes

- Add a shared `_logical_not` converter in `BaseFXGraphImporter` that
casts non-bool
inputs to `bool` before applying `relax.op.logical_not`. Bool inputs are
passed
  through unchanged (no redundant cast).
- Point the `logical_not` (FX) and `logical_not.default`
(ExportedProgram)
  registrations at the new converter.
- Update the FX test and add a standalone ExportedProgram
`test_logical_not` to assert
the corrected IR (`astype` to bool, then `logical_not`, producing a
`bool` output).

### Notes

The cast to `bool` lowers to an elementwise nonzero test, so it matches
PyTorch's
"nonzero is True" semantics for float, integer, and NaN inputs.

(cherry picked from commit 9898909)
The CrossOriginStorage class was storing the URL→hash map only in the
module-level GLOBAL_HASH_CACHE. After a page reload that cache is empty,
and getFileHash() can only recover hashes for HuggingFace LFS files
(URLs containing /resolve/). This left several resource categories
uncacheable across sessions:

<img width="1197" height="279" alt="Screenshot 2026-05-15 at 17 43 15"
src="https://github.com/user-attachments/assets/c9943910-9002-4b06-afdd-6288b7e22ba6"
/>

- JSON files not stored in LFS (mlc-chat-config.json, tokenizer.json,
tensor-cache.json) — getFileHash returns null for their /resolve/ URLs
because the raw pointer is the actual file content, not an LFS pointer.
- .wasm files from GitHub raw URLs — no /resolve/ pattern at all.
- Any file whose hash was computed from blob content via getBlobHash.

Additionally, even for genuine LFS model shards, each page load was
re-fetching every shard's LFS pointer file over the network just to
re-derive the SHA-256 hash.

Fix: persist the URL→hash mapping to a dedicated Cache API store
(tvmjs-cos-hash-meta). Two write sites:

1. put() — after a file is stored in COS, persist its blob-derived hash.
This covers all non-LFS files and non-HuggingFace URLs.

2. resolveHashDescriptor() — after getFileHash() resolves a hash from
the LFS pointer, persist it immediately. This eliminates repeated
pointer-file network requests for model shards on subsequent visits.

Both write sites use a best-effort try/catch so storage quota errors are
silently ignored. loadPersistedHashEntry() similarly swallows errors.
The typeof caches === "undefined" guard keeps the code safe in Node.js
test environments.

(cherry picked from commit 8039963)
…ss (apache#19650)

Fix a crash (apache#19576) when
AdjustMatmulOrder encounters mixed-dimension matmul chains common in
transformer models (e.g. matmul(attn_output[B,S,D], W_o[D,D])). The pass
previously assumed all operands in a chained rewrite were 2D and
asserted shape_c.size() == 2, failing on 3D intermediate results.

Changes:
- Replace full 2D transpose with permute_last_two_dims for permuted
matmul patterns, swapping only the last two axes for ND tensors.
- Remove hard ndim==2 checks in the permuted rewrite path.
- Account for batch prefixes when comparing naive matmul FLOPs, so
reorder decisions reflect batched vs. weight-only inner matmuls.
- Skip reorder when neither evaluation order is provably cheaper.
- Add regression tests for symbolic/concrete batched LoRA shapes.
- Add a numerics test covering a minimal attention block with ND
permute_dims.

(cherry picked from commit dda158c)
…he#19652)

## Summary

Add Relax TFLite frontend support for `EMBEDDING_LOOKUP_SPARSE`.

This PR adds a converter for `EMBEDDING_LOOKUP_SPARSE` in the Relax
TFLite frontend. The implementation supports the `SUM`, `MEAN`, and
`SQRTN` combiners and handles higher-rank sparse indices. The sparse
aggregation is lowered through `scatter_nd` to match TFLite operator
semantics for the supported cases.

The PR also adds handcrafted TFLite frontend tests covering:
- `SUM`
- `MEAN`
- `SQRTN`
- a 3D indices case

## Testing

Ran `tests/python/relax/test_frontend_tflite.py -k
'embedding_lookup_sparse'`.

Part of apache#19519

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
(cherry picked from commit 4a688dd)
Add a workflow_dispatch pipeline that builds, tests, and publishes
manylinux/ macOS/Windows wheels via cibuildwheel with OIDC trusted
publishing.

(cherry picked from commit 5cbf506)
…che#19657)

## Summary

Follow-up work on top of the TIRx infrastructure bring-up (apache#19581). It
extends the TIRx operator-dispatch, codegen, and TVMScript surfaces with
the next batch of low-level programming features for Blackwell-class
GPUs, while keeping `s_tir` script support intact.

## Main Changes

- **op-dispatch**: warp `ldmatrix`/`stmatrix` copy dispatch; split CUDA
copy into register / gmem-smem / `ldgsts` paths; `tcgen05.ld/st`
`.16x{64,128,256}b` dispatch with a factory and M=128 layout;
element-wise broadcast at the layout level with a copy vec-alignment
fix.
- **gemm**: CUDA synchronous `mma.sync` tensor-core dispatch; accept a
Layout F C operand for M=64 MMAs.
- **op**: add the `permute_layout` primitive (replaces `permute_dims`).
- **tvmscript**: add the `Tx.jit` decorator, `Tx.constexpr` compile-time
params, and `Tx.wg_reg_tile`.
- **lower-tirx**: introduce the `Tx.device_entry()` marker (replacing
`ScopeKind::kKernel`); canonical thread filters that drop the
`Tx.filter` wrapper.
- **codegen**: add a typed-pointer byte-offset intrinsic; remove the
`entry_cluster_sync` codegen attribute.

## Validation

- `pre-commit run` (changed files) — clean
- `ninja -C build -j$(nproc)` — builds
- `pytest tests/python/tirx/ -n 16`
  - `1997 passed, 39 skipped, 3 xpassed`
- `python -m pytest tests/python/all-platform-minimal-test`
  - `37 passed, 105 skipped`
- `TVM_TEST_TARGETS=llvm pytest tests/python/tirx-analysis
tests/python/tirx-base tests/python/tirx-transform -n 16`
  - `630 passed, 25 skipped, 8 xfailed, 1 xpassed`

## Local CI Notes

Several full CI-equivalent jobs are not locally reproducible because
this machine is missing parts of the Apache TVM CI environment (e.g.,
specific `llvm-config` versions, Vulkan, ROCm, ARM/QEMU cross-toolchain,
and web/wasm components). The Blackwell/Trainium kernel tests are
maintained downstream and are intentionally not part of this PR.

(cherry picked from commit 57c638f)
Local tree already avoids importing tvm.testing on the import path by using the testing.object_use_count FFI lookup directly. Keep that equivalent behavior while preserving the upstream cherry-pick point.

(cherry picked from commit a979b2f)
Follow-ups to the cibuildwheel wheel-publishing flow (apache#19656):
- macOS: ad-hoc re-sign the wheel's Mach-O dylibs after delocate.
install_name_tool edits invalidate the arm64 code signature and dyld
SIGKILLs an invalidly-signed dylib on dlopen, so `import tvm` crashed
with no traceback. New ci/scripts/package/macos_repair_wheel.sh runs
delocate, ad-hoc re-signs every Mach-O, and repacks so RECORD matches.
- Simplify the per-platform CUDA extra-libs in the wheel CMAKE_ARGS:
macOS never bundles the CUDA sidecar (drop the always-empty arg);
Linux/Windows always do (pass -DTVM_PACKAGE_EXTRA_LIBS unconditionally).
- Move the wheel post-install checks into
tests/python/all-platform-minimal-test, gated behind
TVM_WHEEL_EXPECT_LLVM / TVM_WHEEL_EXPECT_CUDA_RUNTIME so they only
assert during wheel validation and skip in ordinary source-build CI; the
cibuildwheel test-command is now a single pytest invocation.
- Windows: collapse the two tvm_ffi DLL excludes into the delvewheel
glob --exclude "*tvm_ffi*.dll" and pin delvewheel>=1.12.0 (wildcards
need >=1.12.0).

(cherry picked from commit da9b580)
The host/device split flow already runs device-region annotation,
host/device function extraction, and device-kernel launch lowering as
one consecutive pipeline. Keeping those stages exposed as separate
public passes makes the API surface larger than the actual execution
model and leaves the stage dependencies spread across multiple files.

This change makes `tirx.transform.SplitHostDevice` the single public
entry point for that flow, while preserving the existing stage order
internally.

Changes:
- Merge the annotation, splitting, and kernel-launch lowering
implementations into `src/tirx/transform/split_host_device.cc` as
private sections.
- Remove the old public C++ declarations, FFI registrations, and Python
wrappers for `AnnotateDeviceRegions` and `LowerDeviceKernelLaunch`.
- Replace pipeline call sites that previously invoked the three-stage
sequence with one `SplitHostDevice()` call.
- Update TIRx and S-TIR tests to exercise the consolidated pass and the
reduced public API surface.

(cherry picked from commit dea2bf9)
TVM can rely on tvm-ffi's JSON graph serialization helpers directly
instead of routing through TVM-side `node.SaveJSON`/`node.LoadJSON`
registry entries.

This changes `tvm.ir` save/load to call `tvm_ffi.serialization` with
`tvm_version` metadata, removes the C++ registry wrapper, and moves the
disco debug object path to `ffi::ToJSONGraph`/`ffi::FromJSONGraph` plus
JSON parse/stringify.

The disco Python wrappers now declare Python attribute storage
explicitly for `DRef` and `Session` so `DPackedFunc`/`DModule` and
method caches continue to work with the current tvm-ffi object model.
The socket address helper also normalizes `localhost` consistently
across constructors so the disco socket debug round-trip can bind an
IPv4 socket when `localhost` resolves to IPv6 first.

Validated locally in an isolated worktree build with `ninja -C build
tvm_compiler tvm_runtime_extra`, targeted IR/target tests,
`tests/python/disco/test_session.py::test_string_obj`, import smoke, and
touched-file pre-commit.

(cherry picked from commit 1382707)
…pache#19660)

`torch.pow` on an integer tensor returns an integer result, but the
PyTorch frontend lowered it to `relax.op.power`, which fails
`LegalizeOps` with `power only applies to float` (TOPI `power` /
`tvm::pow` requires a floating-point input).

This decomposes an integer base raised to a constant non-negative
integer exponent into repeated multiplication, so the result stays
integral and matches PyTorch. Float bases and non-constant or tensor
exponents keep using `relax.op.power` unchanged. The ONNX frontend
already uses the same decomposition (`x**3 = x*x*x`).

Added structural tests covering both the FX and ExportedProgram import
paths.

Fixes apache#19550

(cherry picked from commit 5791239)
Replace manual version.py stamping with scikit-build-core's
setuptools_scm metadata provider, so local builds no longer call
version.py. The Python distribution/runtime version comes from the
generated python/tvm/_version.py (libinfo.py reads it with a fallback);
the C++ TVM_VERSION is injected from SKBUILD_PROJECT_VERSION_FULL with a
#ifndef default in base.h for bare cmake builds.

version.py is removed. The publish workflow's wheel build checks out
full history (fetch-depth: 0) so setuptools_scm can derive the version,
and drops the version.py stamping step. release_process.rst is updated
to the tag-driven release flow.

(cherry picked from commit a72d57c)
…pache#19664)

The single-line 'bash -c' form is hard to read and edit; rewrite it as a
readable multiline command. Functionally equivalent to the previous
inline version: delocate the wheel, then ad-hoc re-sign every bundled
Mach-O and repack.

The re-sign step is required because delocate's edits invalidate the
ad-hoc signature of tvm/lib/libtvm_runtime.dylib (it LC_LOADs the
excluded `rpath/libtvm_ffi.dylib`, which ships in the separate
apache-tvm-ffi package), and arm64 dyld SIGKILLs an invalidly-signed
Mach-O on import. 'wheel' is installed explicitly since it is absent
from cibuildwheel's repair venv, and the delocated wheel is located by
glob since delocate may retag it.

(cherry picked from commit 9d68a1d)
## Summary

Python callers should reach the canonical tvm-ffi structural helpers
directly instead of going through a TVM-side redirect layer. This makes
the public tvm.ir bindings exact aliases of the tvm_ffi APIs and exposes
get_first_structural_mismatch from tvm.ir.

Main changes:

- Import structural_equal, get_first_structural_mismatch, and
structural_hash directly from tvm_ffi
- Remove the pure wrappers from tvm.ir.base while keeping
assert_structural_equal's TVM-specific formatting
- Update mismatch tests and add identity coverage for the direct
bindings

(cherry picked from commit 1240649)
…blowup (apache#19670)

`Analyzer::Bind` could hang indefinitely (>300s, ~200% CPU, no GPU work)
while binding a small expression for one variable. The root cause is
general and lives in `src/arith/int_set.cc`.

Diagnosis: 100% of the time is spent in `arith::Analyzer::Bind` →
`IntSetAnalyzer` → `IntervalSetEvaluator`, evaluating a **5-node** bound
expression. A counter showed **>2^20 `VisitExpr` calls at recursion
depth 67** with no end in sight.

`IntervalSetEvaluator::VisitExpr_(VarNode)` relaxes a variable's bounds
by recursively evaluating **both** the `min` and `max` sub-expressions
of its mapped interval. For diamond-shaped variable dependency chains
(`a → {b, c}`, `b → {d, e}`, …) the shared sub-expressions are
re-expanded along every path, so cost is **O(2^depth)** in the length of
the dependency chain — bounded only by `dom_map_.size()` (~67
interdependent vars in the failing case).

Memoize the fully-relaxed interval **per variable** (`relax_memo_`) and
break cyclic dependencies with an in-progress set
(`relax_in_progress_`). A variable's relaxed interval is deterministic
for a given evaluator instance (`dom_map_`/`dom_constraints_` are
fixed), so memoizing collapses the diamonds to linear cost. Short chains
— the common case, which never reached the old `recur_depth_ >=
dom_map_.size()` cutoff — are unaffected, so the change is
behavior-preserving outside the pathological case.

New regression tests in `tests/python/arith/test_arith_intset.py`:
- `test_relax_deep_variable_dependency_chain` — a 64-deep diamond
(`O(2^64)` without the fix; verified to hang on a clean build), also
asserting the relaxed result is correct (`x0 → [-n, 100+n]`).
- `test_relax_cyclic_variable_dependency` — a cyclic `x↔y` dependency
must terminate.

- `tests/python/arith/test_arith_intset.py` — 20 passed (the deep-chain
test completes instantly).
- Full `tests/python/arith/` — 933 passed (1 pre-existing flaky
random-seed failure in `test_arith_solve_linear_equations.py` unrelated
to this change, passes on rerun).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 96b8257)
…e#19669)

## Summary

`CanonicalSimplifier::Impl::VisitExpr_(LTNode)` Case 2 rewrites

    S + xn < 0  ⇔  S/d + xn // d < 0      where d = gcd(scales)

The Case 1 derivation only works when `xn ≥ 0`. With `scale = -1` the
equivalence becomes `≤` rather than `<`, and the rewrite silently
strengthens the predicate by dropping the boundary `S/d == xn // d`.

After CSE/inlining, a comparison such as `2*(tx%4) < 16*warp +
(tx%32)//4` (where `row` and `col` are independent projections of the
same lane id) reaches canonical_simplify with the divided projection on
the LHS (scale = -1), and Case 2 folds it to a plain `0 < warp_id` —
zeroing every thread that should have written `val` in warp 0. The same
path also folds other configurations (e.g. `0 < (tx%32) - 8*warp`) all
the way to `False`.

The fix gates Case 2 with `extra->args[0]->scale == 1`. The original
target shape (`yn % m` with positive scale and `lower_factor=1`, plus
the `scale = +1 / lower_factor > 1` generalization) is unchanged;
truly-always-true comparisons still fold to `True`.

## Test plan

- New regression test `test_simplify_le_negative_scale_extra` in
`tests/python/arith/test_arith_canonical_simplify.py` — asserts on
simplified `PrimExpr`, no GPU required; pre-fix fails, post-fix passes.
It also pins the buggy `scale = -1` shapes to their unsimplified form,
confirms the `scale = +1` Case 2 path still optimizes, and re-asserts
the truly-always-true variant still folds to `True`.
- Existing `test_simplify_le` (the original Case 2 target with `scale =
+1`) still passes.
- `tests/python/arith/test_arith_canonical_simplify.py` — 16 passed.
- Full `tests/python/arith/` — 932 passed (1 pre-existing flaky
random-seed failure in `test_arith_solve_linear_equations.py` unrelated
to this change, passes on rerun).

(cherry picked from commit 913fc4b)
…9626)

Hi Committers,

This PR is trying to fix issues apache#19542. Any suggestions would be
appreciated if you are available.

### Root cause:
FP to INT lowering can be implementation-defined or UB for NaN/Inf and
extreme floats, producing backend-dependent results versus ONNX Runtime.

### Solution:
Apply a minimal, deterministic frontend sanitization for float to
integer Casts: map NaN and ±Inf to 0.0 before astype. This prevents
NaN/Inf from reaching backend fptosi/fptoui lowers and yields stable
behavior across targets.

---------

Co-authored-by: cchung100m <cchung100m@users.noreply.github.com>
(cherry picked from commit 4d9d129)
## Summary

- replace the block-structured TIRx exec-scope surface with
scope-qualified `Tx.<scope>.<op>` namespaces and migrate call sites
- split TIRx op namespaces and remove the unused dynamic generic-op
fallback
- add explicit CUDA launch bounds plumbing through TIRx attrs and
split-host-device lowering

## Validation

- `git diff --check apache/main..HEAD`
- `pre-commit run --from-ref apache/main --to-ref HEAD`

(cherry picked from commit 9db74c7)
…#19674)

Hi Committers,

This PR fixes issues apache#19543. Any
suggestions would be appreciated if you are available.

### Root cause:
The ONNX frontend `Sign` converter directly returned `relax.op.sign(x)`.
After legalization, this maps to `topi.sign`, which is implemented via
comparisons (x < 0 ? -1 : x > 0 ? 1 : 0). For `NaN`, both comparisons
are false, so TVM produced 0, while ONNX Runtime preserves NaN. This
created a frontend semantic mismatch for imported ONNX models.

### Solution:
Apply a minimal ONNX-frontend-only fix in `onnx_frontend.py`:
- For floating-point inputs, lower `Sign` as `where(isnan(x), x,
sign(x))`.
- Keep non-floating inputs unchanged (`sign(x)`).

---------

Co-authored-by: cchung100m <cchung100m@users.noreply.github.com>
[Bump] tvm-ffi to 59da4c0

Bumps `3rdparty/tvm-ffi` from 98d0029 to 59da4c0, picking up seven
commits from apache/tvm-ffi:

- [FEAT] Optimize Expected<T> for minimal compiled code and efficiency
(apache#599)
- [FEAT] Streamline AccessPath/AccessStep print format (apache#598)
- [CI] Use uv tool run for sdist build instead of pipx (apache#600)
- [CORE] Make AnyView trivially copyable — match C-ABI struct passing
(apache#602)
- feat(python): add tensor methods to align with C++ APIs (apache#604)
- [FIX] Drop test_empty_tensor_attributes (numpy dlpack stride change)
(apache#607)
- [TEST] Relax test_shared_dag_hash_scaling_not_exponential ratio to 4x
(apache#612)

The bump range is additive for almost all of the above (performance
improvements to Expected<T>, new Python tensor methods, AnyView ABI
alignment, CI toolchain switch). One commit required a TVM-side
migration:

apache/tvm-ffi#598 moved the `__ffi_repr__` hooks for
`ffi::reflection::AccessPath` and `AccessStep` into tvm-ffi itself
(src/ffi/extra/reflection_extra.cc, compiled into libtvm_ffi.so).
TVM already registered the same hooks in src/ir/access_path_repr.cc,
causing a double-registration abort at library load time. This commit
removes the duplicate AccessPath/AccessStep `__ffi_repr__` registrations
from access_path_repr.cc, keeping only the `node.AsRepr` global function
that tvm-ffi does not provide. The format emitted by tvm-ffi's hooks is
equivalent for common cases; missing-item steps now use the
`[<missing:...>]` notation from tvm-ffi rather than the `...?` suffix
that TVM used previously.
…ecords (apache#19673)

Add support for synchronous access handles in OPFS
(https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createSyncAccessHandle).
Sync mode uses FileSystemSyncAccessHandle where available. Replace
optional metadata with committed OPFS records written after payload.
Records store the URL, payload byte count, and content type. This allows
interrupted or partial writes to be treated as cache misses. Stale OPFS
directory handles are cleared on `InvalidStateError`.
This PR makes `arith::Analyzer` a first-class tvm-ffi object.

The implementation splits the previous concrete `Analyzer` class into:

- `AnalyzerObj`, the mutable object node that owns analyzer state,
sub-analyzers, caches, and bindings
- `Analyzer`, a reference-counted `ObjectRef` handle that can be passed
across the tvm-ffi boundary

This allows Python and C++ to share the same analyzer instance, so
bindings, constraints, and cached facts can persist across FFI calls.

Public APIs that accept an analyzer now use `const arith::Analyzer&`,
while internal helper APIs that only borrow the object continue to use
`AnalyzerObj*`.

---------

Co-authored-by: Ubospica <ubospica@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.