Skip to content

[BugFix][Metal] Preserve pointer address spaces for byte offsets - #2925

Merged
LeiWang1999 merged 2 commits into
tile-ai:mainfrom
GY-Bai:fix/metal-pointer-byte-offsets
Aug 14, 2026
Merged

[BugFix][Metal] Preserve pointer address spaces for byte offsets#2925
LeiWang1999 merged 2 commits into
tile-ai:mainfrom
GY-Bai:fix/metal-pointer-byte-offsets

Conversation

@GY-Bai

@GY-Bai GY-Bai commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Problem

  1. Metal requires pointer declarations and pointer casts to carry an explicit address-space qualifier.

  2. Shared-memory aliases created through BindNode and handle_add_byte_offset could reach the MSL compiler as unqualified pointers. The current build path then tried to repair the generated source with five string-rewrite passes, including rules tied to names such as A_shared, B_shared, and _shared.

  3. Equivalent kernels could therefore compile or fail depending on buffer names. A failing kernel is rejected before GPU execution with pointer type must have explicit address space qualifier.

Root cause

  1. The shared-memory merge pass preserves the pointee type and storage scope on the alias variable.

  2. CodeGenTileLangMetal previously inherited generic C handling for pointer-valued BindNode statements, so that type information was not used when emitting the alias declaration.

  3. handle_add_byte_offset was hard-coded to threadgroup instead of deriving its address space from the source pointer.

Change

  1. Emit pointer-valued BindNode declarations from their PointerType, including pointee dtype and storage scope.

  2. Map TIR storage scopes to Metal device, threadgroup, and thread address spaces in one place.

  3. Trace pointer storage scope and pointee type through variables, address_of, handle_add_byte_offset, tvm_access_ptr, and reinterpret.

  4. Derive byte-offset casts from the source pointer and reject unresolved storage scopes.

  5. Remove the five generated-source string rewrites. Both TileLang Metal build paths now consume the same type-correct codegen output.

Upstream

  1. The corresponding fix was accepted and merged as Apache TVM #20101.

  2. This PR applies the same type-driven correction to TileLang own Metal codegen. It does not modify 3rdparty/tvm.

Validation

  1. Hardware validation used a MacBook Air Mac14,2 with Apple M2, macOS 15.6.1 24G90, Metal 3, Command Line Tools, and macOS SDK 15.5.

  2. A full local tilelang build completed successfully from the current main base.

  3. The focused regression passed through source-only codegen, compiled codegen, TVM FFI execution, and Torch execution on the M2 GPU:

python -m pytest testing/python/metal/test_metal_address_space.py -q -ra
3 passed
  1. The complete Metal test directory passed:
python -m pytest testing/python/metal -q -ra
38 passed, 3 skipped
  1. The three skips are the existing Metal 4 cooperative-tensor tests. They are not supported by the current M2 capability detector.

  2. Repository checks passed:

pre-commit run --all-files
All hooks passed

Scope

  1. The fix is independent of pointee dtype. The regression executes FP16 GEMM aliases, while address-space derivation is based on pointer storage scope.

  2. The change is limited to TileLang Metal code generation and its regression coverage.

  3. It does not change compilation pipelines, runtime dispatch, public APIs, serialized formats, or the 3rdparty/tvm revision.

Summary

  • Preserve explicit Metal address-space qualifiers during pointer byte-offset code generation.
  • Centralize TIR storage-scope to Metal address-space mapping.
  • Propagate storage scopes and pointee types through pointer operations.
  • Validate typed pointer bindings and reject unresolved address spaces.
  • Remove five generated-source string-rewrite passes.
  • Add regression tests for shared-memory pointer aliases across Metal compilation paths.

Validation

  • Focused regression tests: 3 passed.
  • Full Metal tests: 38 passed, 3 skipped for unsupported Metal 4 cooperative tensors.
  • Pre-commit hooks passed on Apple M2.

C++ style / lint notes

  • The PR changes C++ code and the CodeGenTileLangMetal declaration.
  • The PR does not change the documented rules in docs/developer_guide/cpp_style.md.
  • The C++ API Style Audit remains warning-only.
  • No correctness or build issues are identified. Advisory style warnings should not block this change unless they indicate a new API, FFI, or maintainability risk.

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the TileLang project.

Please remember to run pre-commit run --all-files in the root directory of the project to ensure your changes are properly linted and formatted. This will help ensure your contribution passes the format check.

We appreciate you taking this step! Our team will review your contribution, and we look forward to your awesome work! 🚀

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 773baf57-0cb6-4568-b553-c89458248aab

📥 Commits

Reviewing files that changed from the base of the PR and between bba2458 and 8e931ab.

📒 Files selected for processing (2)
  • src/metal/codegen/codegen_metal.cc
  • testing/python/metal/test_metal_address_space.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/metal/codegen/codegen_metal.cc
  • testing/python/metal/test_metal_address_space.py

📝 Walkthrough

Walkthrough

The Metal backend centralizes storage-scope mapping, validates typed pointer bindings, infers pointer metadata from supported expressions, and emits inferred address spaces for declarations and offsets. Tests cover source generation and MPS execution for shared-memory GEMM kernels.

Changes

Metal address-space-aware pointer code generation

Layer / File(s) Summary
Storage-scope mapping and bind handling
src/metal/codegen/codegen_metal.h, src/metal/codegen/codegen_metal.cc
BindNode handling maps supported storage scopes and emits validated, address-space-qualified pointer declarations.
Pointer inference and offset emission
src/metal/codegen/codegen_metal.cc
Pointer types and address spaces come from typed variables and supported pointer-producing expressions. Pointer offsets use the inferred address space, and source-rewrite passes were removed.
Metal source and execution validation
testing/python/metal/test_metal_address_space.py
Tests compare both Metal build paths, check qualified threadgroup aliases, and execute shared-memory GEMM through TVM FFI and Torch backends on MPS.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant MetalTest
  participant MetalCodegen
  participant MPSBackend
  MetalTest->>MetalCodegen: lower shared-memory GEMM
  MetalCodegen->>MetalTest: return address-space-qualified source
  MetalTest->>MPSBackend: execute through TVM FFI or Torch
  MPSBackend->>MetalTest: return numerical result
Loading

Suggested reviewers: leiwang1999, siriusneo

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preserving Metal pointer address spaces during byte-offset generation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
testing/python/metal/test_metal_address_space.py (2)

46-56: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert that the kernel source exists instead of substituting an empty string.

artifact.kernel_source or "" converts a missing source into an empty string. The equality assertion on Line 64 then compares "" with "" and passes. The later alias assertion still catches the case, but the failure message loses the cause.

♻️ Proposed change
-    return artifact.kernel_source or ""
+    source = artifact.kernel_source
+    assert source, f"empty Metal kernel source (enable_device_compile={enable_device_compile})"
+    return source
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@testing/python/metal/test_metal_address_space.py` around lines 46 - 56,
Update lower_to_metal so it asserts artifact.kernel_source is present before
returning it, rather than converting a missing value to an empty string.
Preserve the existing return type and ensure the assertion clearly identifies
missing kernel source; leave the downstream equality and alias assertions
unchanged.

84-92: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Seed the random inputs.

torch.randn uses the global RNG state. If this test fails, the inputs cannot be reproduced. Call torch.manual_seed at the start of the test.

♻️ Proposed change
+    torch.manual_seed(0)
     a = torch.randn(M, K, dtype=torch.float16, device="mps")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@testing/python/metal/test_metal_address_space.py` around lines 84 - 92, Seed
the test’s random inputs by calling torch.manual_seed at the beginning of the
test before the torch.randn calls that create a and b, using the project’s
established deterministic seeding convention if available.
src/metal/codegen/codegen_metal.cc (1)

484-496: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Report the enclosing function when a pointer type check fails.

The three checks print only op->var. A user who hits this error gets a bare variable name with no kernel name and no statement context. Add the value expression to the message so the failing bind is identifiable.

♻️ Proposed message improvement
   const auto *pointer_type = op->var->type_annotation.as<PointerTypeNode>();
   TVM_FFI_ICHECK(pointer_type)
-      << "Metal handle binding requires a typed pointer: " << op->var;
+      << "Metal handle binding requires a typed pointer: " << op->var
+      << " = " << op->value;
   const auto *element_type = pointer_type->element_type.as<PrimTypeNode>();
   TVM_FFI_ICHECK(element_type)
-      << "Metal handle binding requires a primitive pointee type: " << op->var;
+      << "Metal handle binding requires a primitive pointee type: " << op->var
+      << " = " << op->value;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/metal/codegen/codegen_metal.cc` around lines 484 - 496, Update the three
validation messages in the enclosing handle-binding function to include the
value expression alongside op->var, so failures identify the kernel statement
context. Preserve the existing checks and message distinctions for typed
pointers, primitive pointee types, and explicit storage scopes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/metal/codegen/codegen_metal.cc`:
- Around line 946-953: Update GetStorageScopeOf to recursively unwrap CastNode
expressions before inferring the pointer storage scope, while preserving
existing handling for other expression types. In GetAddrSpaceOf, include the
failing kernel context in the check failure message alongside the pointer
expression, and retain the existing storage-scope mapping for successfully
inferred scopes.
- Around line 988-990: Update the builtin::reinterpret() branch in
GetAddrSpaceOf and GetPointeeTypeOf to derive the Metal address space and
pointee type from the reinterpret call’s result dtype rather than recursively
using call->args[0]. Preserve the existing single-argument validation and ensure
cooperative_tensor_load/store receive the target dtype through these helpers.

In `@testing/python/metal/test_metal_address_space.py`:
- Around line 70-72: Update the per-alias assertions in the test loop to stop
requiring a specific vector type or offset syntax, and replace the ineffective
negative alias-cast check. Validate that every half-pointer cast in
compiled_source includes the expected address-space qualifier, using the
existing aliases loop and source inspection while remaining independent of
vectorization width and indexing form.
- Around line 59-62: The
test_both_metal_build_paths_use_type_driven_shared_aliases test must explicitly
apply the existing Metal build-availability gate before calling lower_to_metal.
Add the repository’s established skip mechanism so CUDA-only or non-Metal builds
skip the test, while preserving the current fallback environment setup and both
build-path invocations.

---

Nitpick comments:
In `@src/metal/codegen/codegen_metal.cc`:
- Around line 484-496: Update the three validation messages in the enclosing
handle-binding function to include the value expression alongside op->var, so
failures identify the kernel statement context. Preserve the existing checks and
message distinctions for typed pointers, primitive pointee types, and explicit
storage scopes.

In `@testing/python/metal/test_metal_address_space.py`:
- Around line 46-56: Update lower_to_metal so it asserts artifact.kernel_source
is present before returning it, rather than converting a missing value to an
empty string. Preserve the existing return type and ensure the assertion clearly
identifies missing kernel source; leave the downstream equality and alias
assertions unchanged.
- Around line 84-92: Seed the test’s random inputs by calling torch.manual_seed
at the beginning of the test before the torch.randn calls that create a and b,
using the project’s established deterministic seeding convention if available.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7e925cfc-672c-43fa-b7bb-f91431455571

📥 Commits

Reviewing files that changed from the base of the PR and between 5a9b2a5 and bba2458.

📒 Files selected for processing (3)
  • src/metal/codegen/codegen_metal.cc
  • src/metal/codegen/codegen_metal.h
  • testing/python/metal/test_metal_address_space.py

Comment thread src/metal/codegen/codegen_metal.cc
Comment thread src/metal/codegen/codegen_metal.cc Outdated
Comment thread testing/python/metal/test_metal_address_space.py
Comment thread testing/python/metal/test_metal_address_space.py Outdated
@LeiWang1999
LeiWang1999 merged commit 15b0670 into tile-ai:main Aug 14, 2026
7 checks passed
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.

2 participants