Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/Dialect/TritonGPU/Transforms/AccelerateMatmul.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#include "triton/Analysis/Utility.h"
#include "triton/Conversion/MLIRTypes.h"
#include "triton/Dialect/Triton/IR/Dialect.h"
#ifdef __TLE__
#include "tle/dialect/include/IR/Dialect.h"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

#ifdef __TLE__

#endif
#include "triton/Dialect/Triton/IR/OpInterfaces.h"
#include "triton/Dialect/Triton/IR/Utility.h"
#include "triton/Dialect/TritonGPU/IR/Attributes.h"
Expand Down Expand Up @@ -67,6 +70,10 @@ static Value getUnderlyingMemDesc(Value value) {
value = subslice.getSrc();
continue;
}
if (auto alias = value.getDefiningOp<tle::MemDescAliasOp>()) {
value = alias.getSrc();
continue;
}
if (auto trans = value.getDefiningOp<MemDescTransOp>()) {
value = trans.getSrc();
continue;
Expand Down
4 changes: 4 additions & 0 deletions lib/Dialect/TritonGPU/Transforms/OptimizeDotOperands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ static Value stripMemDescViews(Value value) {
value = subslice.getSrc();
continue;
}
if (auto alias = value.getDefiningOp<tle::MemDescAliasOp>()) {
value = alias.getSrc();
continue;
}
Comment thread
sunnycase marked this conversation as resolved.
if (auto trans = value.getDefiningOp<MemDescTransOp>()) {
value = trans.getSrc();
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ static Value stripMemDescViewsForLocalAlloc(Value value) {
value = subslice.getSrc();
continue;
}
if (auto alias = value.getDefiningOp<tle::MemDescAliasOp>()) {
value = alias.getSrc();
continue;
}
if (auto trans = value.getDefiningOp<ttg::MemDescTransOp>()) {
value = trans.getSrc();
continue;
Expand Down Expand Up @@ -523,6 +527,10 @@ static MemDescResource getMemDescResource(Value value) {
value = subslice.getSrc();
continue;
}
if (auto alias = value.getDefiningOp<tle::MemDescAliasOp>()) {
value = alias.getSrc();
continue;
}
if (auto trans = value.getDefiningOp<ttg::MemDescTransOp>()) {
value = trans.getSrc();
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "triton/Dialect/Triton/IR/Utility.h"
#include "triton/Dialect/TritonGPU/IR/Attributes.h"
#include "triton/Dialect/TritonGPU/IR/Dialect.h"
#include "tle/dialect/include/IR/Dialect.h"
#include "triton/Dialect/TritonGPU/Transforms/PipelineExpander.h"
#include "triton/Dialect/TritonGPU/Transforms/PipeliningUtility.h"
#include "triton/Dialect/TritonGPU/Transforms/Schedule.h"
Expand Down Expand Up @@ -454,7 +455,8 @@ static std::optional<int> dotCanBeProperlyAsync(ttng::WarpGroupDotOp dotOp,
// allowed in between.
Value transitiveOperand = operand;
while (isa_and_nonnull<ttg::ConvertLayoutOp, ttg::MemDescTransOp,
ttg::MemDescReshapeOp, ttg::MemDescSubsliceOp>(
ttg::MemDescReshapeOp, ttg::MemDescSubsliceOp,
mlir::triton::tle::MemDescAliasOp>(
Comment thread
sunnycase marked this conversation as resolved.
transitiveOperand.getDefiningOp()) ||
isa<BlockArgument>(transitiveOperand)) {
auto blockArg = dyn_cast<BlockArgument>(transitiveOperand);
Expand Down
3 changes: 3 additions & 0 deletions lib/Dialect/TritonGPU/Transforms/Prefetch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#include "mlir/Support/LLVM.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "triton/Dialect/TritonGPU/IR/Dialect.h"
#ifdef __TLE__
#include "tle/dialect/include/IR/Dialect.h"
#endif
#include "triton/Dialect/TritonGPU/Transforms/Passes.h"
#include "llvm/Support/Debug.h"

Expand Down
18 changes: 18 additions & 0 deletions lib/Dialect/TritonGPU/Transforms/Utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1389,10 +1389,18 @@ ttg::LocalAllocOp findShmemAlloc(Value operand) {
// come from an MemDescIndex op. Only ConvertLayout and MemdescView ops are
// allowed in between.
Value transitiveOperand = operand;
#ifdef __TLE__
while (isa_and_nonnull<ttg::ConvertLayoutOp, tt::TransOp, ttg::MemDescTransOp,
ttg::MemDescReshapeOp, ttg::MemDescSubsliceOp,
tle::MemDescAliasOp>(
transitiveOperand.getDefiningOp()) ||
isa<BlockArgument>(transitiveOperand)) {
#else
while (isa_and_nonnull<ttg::ConvertLayoutOp, tt::TransOp, ttg::MemDescTransOp,
ttg::MemDescReshapeOp, ttg::MemDescSubsliceOp>(
transitiveOperand.getDefiningOp()) ||
isa<BlockArgument>(transitiveOperand)) {
#endif
if (auto blockArg = dyn_cast<BlockArgument>(transitiveOperand)) {
assert(isa<scf::ForOp>(blockArg.getOwner()->getParentOp()) &&
"Block argument must come from a for loop");
Expand Down Expand Up @@ -1585,6 +1593,16 @@ void replaceUsesAndPropagateType(
oldType.getMemorySpace(), isMutable, oldType.getAllocShape());
newVal = ttg::MemDescSubsliceOp::create(
builder, subslice.getLoc(), newDstType, val, subslice.getOffsets());
#ifdef __TLE__
} else if (auto alias = dyn_cast<tle::MemDescAliasOp>(user)) {
ttg::MemDescType oldType = alias.getType();
bool isMutable = cast<ttg::MemDescType>(val.getType()).getMutableMemory();
Type newDstType = ttg::MemDescType::get(
oldType.getShape(), oldType.getElementType(), oldType.getEncoding(),
oldType.getMemorySpace(), isMutable, oldType.getAllocShape());
newVal = tle::MemDescAliasOp::create(
builder, alias.getLoc(), newDstType, val, alias.getOffsetBytesAttr());
#endif
} else if (auto trans = dyn_cast<ttg::MemDescTransOp>(user)) {
newVal = ttg::MemDescTransOp::create(builder, trans.getLoc(), val,
trans.getOrder());
Expand Down
62 changes: 62 additions & 0 deletions python/test/tle/integration/test_tle_alias_e2e.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The test file cannot be placed here.

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""E2E test: verify alloc alias shares the same physical smem via modify-and-observe."""
import torch
import triton
import triton.language as tl
import triton.experimental.tle.language as tle

BLOCK = 128


@triton.jit
def alias_e2e_kernel(in_ptr, out_ptr, N, BLOCK: tl.constexpr):
pid = tl.program_id(0)
offs = pid * BLOCK + tl.arange(0, BLOCK)
mask = offs < N

# Phase 1: allocate v_smem, write the first batch of data (original input)
v_smem = tle.gpu.alloc([BLOCK], dtype=tl.float32, scope=tle.gpu.smem)
tle.gpu.copy(in_ptr + offs, v_smem, [BLOCK])

# Phase 2: alias — o_smem reuses v_smem's physical memory
o_smem = tle.gpu.alloc(
[BLOCK], dtype=tl.float32, scope=tle.gpu.smem,
alias=v_smem, alias_offset_bytes=0,
)

# Phase 3: critical — write second batch of data via v_smem (input + BLOCK offset),
# overwriting the same physical memory. If alias is correct, o_smem
# should see the overwritten values.
offs2 = offs + BLOCK
mask2 = offs2 < N
tle.gpu.copy(in_ptr + offs2, v_smem, [BLOCK])

# Phase 4: read from o_smem
tle.gpu.copy(o_smem, out_ptr + offs, [BLOCK])


if __name__ == "__main__":
N = 2048 # enough for two BLOCK-sized chunks
x = torch.randn(N, device="cuda", dtype=torch.float32)
y = torch.zeros(1024, device="cuda", dtype=torch.float32) # only first 1024

grid = (triton.cdiv(1024, BLOCK),)
alias_e2e_kernel[grid](x, y, 1024, BLOCK=BLOCK)
torch.cuda.synchronize()

# Verify: y should equal x[128:1152] (second batch), not x[0:1024] (first batch).
# This confirms that v_smem's overwrite was observed by o_smem.
expected = x[128:1152]
max_diff_alias = (expected - y).abs().max().item()

# Control: y should NOT equal the first batch
first_batch = x[:1024]
max_diff_original = (first_batch - y).abs().max().item()

if max_diff_alias < 1e-5 and max_diff_original > 1e-5:
print("PASSED: alias E2E verified")
print(f" - o_smem matches overwritten data (second batch): max diff = {max_diff_alias:.2e}")
print(f" - o_smem differs from original data (first batch): max diff = {max_diff_original:.2e}")
else:
print(f"FAILED:")
print(f" diff vs overwritten (should be 0): {max_diff_alias:.2e}")
print(f" diff vs original (should be >0): {max_diff_original:.2e}")
130 changes: 130 additions & 0 deletions python/test/tle/unit/test_tle_alloc_alias.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# flagtree tle
"""TLE alloc alias unit tests — mock-based, no GPU required."""

import pytest
import triton.language as tl
import triton.experimental.tle.language as tle


class TestAllocAlias:

class _FakeTensor:
def __init__(self, handle, ty):
self.handle = handle
self.type = ty

class _FakeBuilder:

def __init__(self):
self.memdesc_type_args = None
self.memdesc_alias_args = None
self.swizzled_encoding_args = None

def get_half_ty(self):
return "fp16"

def make_swizzled_shared_encoding_attr(self, vector_size, per_phase, max_phase, order, ctas_per_cga,
cta_split_num, cta_order):
self.swizzled_encoding_args = (
vector_size, per_phase, max_phase,
list(order), list(ctas_per_cga), list(cta_split_num), list(cta_order),
)
return "fake_layout"

def get_memdesc_type(self, shape, element_ty, layout, space, alloc_shape=None):
self.memdesc_type_args = (list(shape), element_ty, layout, space, alloc_shape)
return ("memdesc", tuple(shape), element_ty, layout, space,
None if alloc_shape is None else tuple(alloc_shape))

def create_local_alloc(self, *args):
return "alloc_handle"

def create_memdesc_alias(self, result_ty, src, offset_bytes):
self.memdesc_alias_args = (result_ty, src, offset_bytes)
return "alias_handle"

class _FakeSemantic:

def __init__(self):
self.builder = TestAllocAlias._FakeBuilder()

def to_tensor(self, value):
if isinstance(value, TestAllocAlias._FakeTensor):
return value
if isinstance(value, bool):
return TestAllocAlias._FakeTensor(f"pred_{value}", tl.int1)
if isinstance(value, int):
return TestAllocAlias._FakeTensor(f"stage_{value}", tl.int32)
raise TypeError(f"unsupported fake tensor input: {value!r}")

def _make_buffer(self, shape):
semantic = self._FakeSemantic()
layout = tle.gpu.swizzled_shared_layout.make_default(len(shape))
return (
tle.gpu.buffered_tensor("base", tl.float16, shape, tle.gpu.smem, layout, semantic),
semantic,
)

def test_alloc_alias_creates_typed_memdesc_alias_view(self):
"""alloc(alias=...) returns a typed view without creating a new allocation."""
buffer, semantic = self._make_buffer([4, 16, 32])
alias = tle.gpu.alloc(
(2, 16, 16),
tl.float16,
layout=buffer.type.layout,
alias=buffer,
alias_offset_bytes=64,
_semantic=semantic,
)

assert isinstance(alias, tle.gpu.buffered_tensor)
assert alias.handle == "alias_handle"
assert alias.shape == [2, 16, 16]
assert alias.dtype == tl.float16
assert alias.type.storage is tle.gpu.smem
assert semantic.builder.memdesc_alias_args == (
("memdesc", (2, 16, 16), "fp16", "fake_layout", "smem", None),
"base",
64,
)

def test_alloc_alias_rejects_init_value(self):
buffer, semantic = self._make_buffer([4, 16, 32])
init = self._FakeTensor("init", tl.float16)

with pytest.raises(ValueError, match="alias mode cannot be combined"):
tle.gpu.alloc(
(2, 16, 16),
tl.float16,
layout=buffer.type.layout,
init_value=init,
alias=buffer,
_semantic=semantic,
)

def test_alloc_alias_rejects_non_smem_buffer(self):
"""alias source must be a shared-memory buffered_tensor."""
semantic = self._FakeSemantic()
fake_buffer = self._FakeTensor("tmem_buf", tl.float16)

with pytest.raises(ValueError, match="tle.buffered_tensor"):
tle.gpu.alloc(
(2, 16, 16),
tl.float16,
alias=fake_buffer,
_semantic=semantic,
)

def test_alloc_alias_invalid_offset_type(self):
"""Bytes are a compile-time argument."""
buffer, semantic = self._make_buffer([4, 16, 32])

with pytest.raises(ValueError, match="compile-time integer"):
tle.gpu.alloc(
(2, 16, 16),
tl.float16,
layout=buffer.type.layout,
alias=buffer,
alias_offset_bytes=b"not_an_int",
_semantic=semantic,
)
25 changes: 24 additions & 1 deletion python/triton/experimental/tle/language/gpu/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ def alloc(
layout: Optional[tle.shared_layout] = None,
scope: tle.scope = tle.smem,
init_value: Optional[tl.tensor] = None,
alias: Optional[tle.buffered_tensor] = None,
alias_offset_bytes: int = 0,
nv_mma_shared_layout=True,
_semantic=None,
) -> tle.buffered_tensor:
Expand All @@ -245,6 +247,9 @@ def alloc(
dtype: Data type
layout: Memory layout encoding (optional)
scope: Storage type (default to shared memory)
init_value: Optional initial register tensor for a new allocation
alias: Optional source shared-memory buffer to alias instead of allocating
alias_offset_bytes: Static byte offset from alias source view base
_semantic: Semantic analyzer (internal use)

Returns:
Expand All @@ -268,6 +273,20 @@ def alloc(
if not isinstance(scope, tle.scope):
raise ValueError(f"Storage type must be tle.scope, but got {type(scope)}")

alias = tl._unwrap_if_constexpr(alias)
alias_offset_bytes = tl._unwrap_if_constexpr(alias_offset_bytes)
if alias is not None:
if init_value is not None:
raise ValueError("alloc alias mode cannot be combined with init_value")
if not isinstance(alias, tle.buffered_tensor):
raise ValueError(f"alias must be a tle.buffered_tensor, but got {type(alias)}")
if scope is not tle.smem or alias.type.storage is not tle.smem:
raise ValueError("alloc alias mode currently supports only smem buffers")
if isinstance(alias_offset_bytes, bool) or not isinstance(alias_offset_bytes, int):
raise ValueError("alias_offset_bytes must be a compile-time integer")
if alias_offset_bytes < 0:
raise ValueError("alias_offset_bytes must be non-negative")

layout = tl._unwrap_if_constexpr(layout)
if layout is not None and not isinstance(layout, tle.shared_layout):
# Handle constexpr None
Expand Down Expand Up @@ -335,7 +354,11 @@ def alloc(
layout_handle = layout.to_ir(_semantic.builder)

if storage == tle.smem:
if init_value is not None:
if alias is not None:
alias_ty = _semantic.builder.get_memdesc_type(full_shape, elem_type, layout_handle, "smem")
tensor_handle = _semantic.builder.create_memdesc_alias(
alias_ty, alias.handle, alias_offset_bytes)
elif init_value is not None:
mutable_ty = _semantic.builder.get_memdesc_type(full_shape, elem_type, layout_handle, "smem")
tensor_handle = _semantic.builder.create_local_alloc(mutable_ty, init_value.handle)
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ struct ConvertTritonGPUToLLVM
typeConverter, patterns, targetInfo, benefit);
mlir::triton::tle::populateMemDescWGMMAViewOpToLLVMPatterns(
typeConverter, patterns, benefit);
mlir::triton::tle::populateMemDescAliasOpToLLVMPatterns(
typeConverter, patterns, benefit);
Comment thread
sunnycase marked this conversation as resolved.
mlir::triton::tle::populateExclusiveCumsumOpToLLVMPatterns(
typeConverter, targetInfo, patterns, benefit);
mlir::triton::tle::populateWGMMASharedOperandFenceOpToLLVMPatterns(
Expand Down
Loading
Loading