Skip to content

[TIRX] Represent buffers as typed variables - #20062

Closed
tqchen wants to merge 2 commits into
apache:mainfrom
tqchen:tvm-buffer-type-var-refactor
Closed

[TIRX] Represent buffers as typed variables#20062
tqchen wants to merge 2 commits into
apache:mainfrom
tqchen:tvm-buffer-type-var-refactor

Conversation

@tqchen

@tqchen tqchen commented Jul 28, 2026

Copy link
Copy Markdown
Member

This refactors TIRx buffers into ordinary ir.Var identities carrying an
immutable BufferType.

Rationale

The old representation kept a standalone Buffer object, its logical metadata,
and a separate pointer-typed data Var. Generic Var substitution and
buffer-specific rebuilding could therefore let identity, metadata, and pointer
type drift apart. In this change, BufferType is the single source of the
logical contract and buffer_data(buffer) is the explicit physical-pointer
projection; DeclBuffer records a view's binding.

Simplification

  • Generic Var scoping, substitution, undefined-variable analysis, and identity
    maps now apply directly to buffers.
  • The pointer type is derived from BufferType, so the physical pointer and
    buffer metadata cannot disagree.
  • The common IR path no longer needs a second Buffer object, duplicate
    constructor state, or a parallel buffer-only substitution mechanism.
  • Metadata-changing transforms create a fresh typed Var and bind it explicitly
    at the replacing AllocBuffer or DeclBuffer.

Physical-root tracking remains deliberately local to the passes whose
correctness depends on allocation ownership:

  1. Storage liveness/reuse attributes alias accesses to the owning allocation.
  2. Pointer vector-type inference and rewriting aggregate evidence at that root.
  3. LowerTIRx cleanup projects aliases to the remapped physical allocation.
  4. Thread-allreduce's deferred remapper replaces/removes the owning allocation.

Each is a plain pass-local Map<Var, Var> flattened when a DeclBuffer is
visited. Parameters, buffer_map, and AllocBuffer seed roots; an alias to an
unseen typed source is rejected rather than silently treated as a new root.
Simpler passes decode the one-step buffer_data projection locally.

Migration guide

C++

  • Treat BufferVar as a checked view over an ordinary Var.
  • Read logical metadata through buffer->...; use buffer.var() for identity.
  • Use buffer.data() or builtin::buffer_data() only when a physical pointer
    expression is required.
  • To change metadata, rebuild BufferType, create a fresh Var, and bind it at
    the replacement definition site.
  • A transformed DeclBuffer replaces its superseded declaration consistently;
    it must not leave a stale declaration beside the new binding.

Python

  • Buffer constructors return tvm.ir.Var; .ty contains BufferType.
  • Use tvm.tirx.is_buffer_var(value) for runtime discrimination.
  • tvm.tirx.Buffer remains an import/annotation compatibility alias for
    tvm.ir.Var, so isinstance(value, Buffer) matches every Var.
  • Importing tvm.tirx installs the legacy metadata properties on Var.
    Non-buffer Vars raise AttributeError; buffer .dtype keeps its historical
    DataType result.

Behavior-preserving follow-up

The second commit restores the original LowerTIRx, FlattenBuffer, and BF16
outputs; restores the alias-lifetime regression; replaces superseded
declarations; keeps ownership logic pass-local; and fixes tagged shared-memory
children after their parent allocation is rebuilt.

Validation

  • C++ build (USE_LLVM=OFF): passed.
  • Focused matrix: 239 passed, 3 skipped.
  • Complete unfiltered TIRx/S-TIR Python run:
    62 failed, 2248 passed, 1668 skipped, 11 xfailed, 1 xpassed,
    1 warning, 10 collection errors
    in 290.09s.
  • The broad run exposed 12 genuine Python dtype-compatibility failures; those
    were fixed and rerun green. The remaining failures and collection errors
    require LLVM/target globals, CUDA, NKI, Torch, or target-feature/target-build
    support unavailable on this node.
  • git diff --check: passed.

Make buffer identity an ordinary ir.Var carrying BufferType.  The previous
parallel Buffer object and pointer-typed data Var could diverge under generic
IR substitution and rebuilding; BufferType is now the single source of truth,
and buffer_data(buffer) is the explicit physical-pointer projection.

This lets standard Var scoping, substitution, undefined-variable analysis, and
identity maps handle buffers without a second object hierarchy.  It also
removes duplicate constructor state and representation-specific remapping from
the common path.

C++ migration: use BufferVar as a checked Var view, read logical metadata
through buffer->..., use buffer.var() for identity, and use buffer.data() or
builtin::buffer_data for the pointer projection.  Metadata changes rebuild the
BufferType and rebind the new Var through AllocBuffer or DeclBuffer.

Python migration: constructors return tvm.ir.Var, value.ty carries BufferType,
and is_buffer_var(value) is the runtime discriminator.  Buffer remains a
source-compatibility alias for imports and annotations, so isinstance(value,
Buffer) must not be used to distinguish buffer variables.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@tqchen

tqchen commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

@tvm-bot rerun

@tqchen
tqchen force-pushed the tvm-buffer-type-var-refactor branch 2 times, most recently from 072c9de to 81dc885 Compare July 28, 2026 17:21
@tqchen

tqchen commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

LLVM CI follow-up is published at 81dc8857da while preserving the exact two-commit series.

The repair keeps BufferType out of the general LLVM ABI mapper. Instead, debug emission translates a BufferVar to its physical DataPointerType only where LLVM already has the corresponding pointer value, including outlined compute-scope captures. Those captures are also treated as pointer-backed for the existing noalias, noinline, and alignment contracts.

Coverage adds a packed, parallel PrimFunc with parameter, declared, and allocated buffers. Validation includes an LLVM 18 build, all 377 tests in test_target_codegen_llvm.py, the packed parallel pipeline compiling and executing with numerical agreement, and all pre-commit hooks.

@tqchen
tqchen force-pushed the tvm-buffer-type-var-refactor branch from 81dc885 to 52557a2 Compare July 28, 2026 19:38
Keep physical-buffer ownership explicit after Buffer becomes a typed Var. Storage planning, vector type rewriting, cleanup, and all-reduce lowering now maintain pass-local alias-to-root maps only where liveness or allocation remapping requires them; simpler passes decode buffer_data projections locally.

Replace superseded DeclBuffer bindings instead of retaining stale declarations, reject aliases whose physical source has not been registered, and repair merged tagged-memory children after their parent allocation is rebuilt. Restore the alias-lifetime and original-output regressions that exercise these invariants.

Preserve the Python compatibility surface deliberately: Buffer remains an annotation/import alias, is_buffer_var is the runtime discriminator, legacy metadata properties reject non-buffer Vars with AttributeError, and dtype continues to return DataType.

Validated with a clean C++ build, 239 focused passing tests (3 skipped), and the complete TIRx/S-TIR Python suite; remaining broad-suite failures require unavailable LLVM, CUDA, NKI, Torch, or target-feature support.
@tqchen
tqchen force-pushed the tvm-buffer-type-var-refactor branch from 52557a2 to dfa2012 Compare July 28, 2026 20:04
@tqchen tqchen closed this Jul 28, 2026
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.

1 participant