Skip to content

Express element_arrangementin Triton kernel #114

Description

@airin711

Background

tt.spyre_tensor_layout (introduced in #74, "RewriteDescriptorLayout:
physicalize tensor descriptors for stick-tiled device layouts") lets a
Triton-Spyre kernel annotate a tensor descriptor with a physical coordinate
decomposition — three parallel arrays phys_src/phys_op/phys_arg
describing, per physical dim, which logical dim it derives from and whether
via identity, floordiv, or mod. This is the Triton-DSL analog of
TensorArg.device_coordinates in torch-spyre's OpSpec IR — it says where
each element physically lives.

What tt.spyre_tensor_layout does not express and what Triton's ordinary
tensor type system (tl.float16, tl.bfloat16, etc.) also does not express
is how elements are arranged/packed within a stick for certain hardware
conversions. torch-spyre tracks this separately as element_arrangement
(ElementArrangement enum: STANDARD, DL16_TO_FP32, FP32_TO_DL16,
QFP8CH, QFP8WT, EXX2) on SpyreTensorLayout/TensorArg. Certain
fp16/bf16↔fp32 hardware conversions produce a "staggered" physical layout,
elements interleaved/reordered within a stick rather than laid out in simple
row-major order, and this is orthogonal to dtype: it's about intra-stick
element ordering, not the element's storage format. A Triton kernel that
needs to consume or produce a staggered tensor currently has no way to
declare that.

Here is the example OpSpec using the element_arrangement field:

KERNEL_NAME = "sdsc_fused__to_copy_0"
POOL_SIZE = 0
BUNDLE_SYMBOLIC_ARGS = True
INPUTS_PT = os.path.splitext(os.path.abspath(__file__))[0] + ".inputs.pt"

# Host (shape, dtype) per kernel arg, in arg_index order.
SHAPES = [
    ([4, 128], torch.float16),
    ([4, 128], torch.float32)
]

# Exact device layout each arg had when the real graph ran.
LAYOUTS = [
    SpyreTensorLayout(
        device_size=[2, 4, 64],
        stride_map=[64, 128, 1],
        device_dtype=DataFormats.SEN169_FP16,
        element_arrangement=ElementArrangement.STANDARD,
    ),
    SpyreTensorLayout(
        device_size=[4, 4, 32],
        stride_map=[32, 128, 1],
        device_dtype=DataFormats.IEEE_FP32,
        element_arrangement=ElementArrangement.DL16_TO_FP32,
    ),
]

ops = [
    OpSpec(
        op='dl16tofp32',
        is_reduction=False,
        iteration_space={sympify('c0'): (sympify('4'), 4), sympify('c1'): (sympify('128'), 2)},
        op_info={},
        symbolic_dim_bounds={},
        debug_handle=DebugHandle(id=4224585593578919848, source=SourceLoc(file='staggered_ea.py', start_line=6, start_col=0, end_line=None, end_col=None), aten_op='aten._to_copy.default', ir_chain=('convert_element_type', 'buf0'), fused_from=(), transform_history=()),
        args=[
            TensorArg(
                is_input=True, arg_index=0, device_dtype=DataFormats.SEN169_FP16,
                device_size=[2, 4, 64],
                device_coordinates=[sympify('floor(c1/64)'), sympify('c0'), sympify('Mod(c1, 64)')],
                allocation={'hbm': 0},
            ),
            TensorArg(
                is_input=False, arg_index=1, device_dtype=DataFormats.IEEE_FP32,
                device_size=[4, 4, 32],
                device_coordinates=[sympify('floor(c1/32)'), sympify('c0'), sympify('Mod(c1, 32)')],
                allocation={'hbm': 1},
                element_arrangement=ElementArrangement.DL16_TO_FP32,
            ),
        ]
    ),
]

Proposal

Extend tt.spyre_tensor_layout (and its tl.spyre_tensor_layout frontend
builtin) with an element_arrangement attribute, applying to the whole
descriptor (not per-dimension, since staggering is not a coordinate
operation like phys_src/phys_op/phys_arg):

tl.spyre_tensor_layout(desc, layout, element_arrangement="DL16_TO_FP32")

lowering to an element_arrangement attribute on tt.spyre_tensor_layout
alongside the existing phys_src/phys_op/phys_arg arrays. Defaults to
STANDARD/omitted when not specified, matching the "omit if default"
convention already used throughout torch-spyre's own OpSpec serialization
(torch_spyre/_inductor/spyre_kernel.py).

Why this matters

Without this, any Triton-Spyre kernel that touches a staggered fp16↔fp32
conversion has no way to describe that at the descriptor level — the
information exists in torch-spyre's OpSpec IR today but has no Triton-DSL
counterpart, making it impossible to hand-write or lower to a Triton kernel
that participates in one of these conversions without out-of-band knowledge.

References

cc. @fabianlim

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions