66 shard Dialect
66.1 Beginner Summary
The shard dialect describes how tensor values are split, replicated, moved, and synchronized across a logical grid of devices or processes.
It is easiest to think of shard as the MLIR dialect for distributed tensor placement. A compiler can use it to say:
- this tensor is split across these device-grid axes;
- this operation should run in SPMD form;
- this result needs an all-gather, all-reduce, all-to-all, halo exchange, or other communication step;
- this distributed program can later be lowered to a message-passing runtime such as MPI.
The dialect is inspired by GSPMD-style compilation. The important beginner idea is that shard does not start from explicit send and receive buffers. It starts from tensor sharding and lets compiler passes derive the communication needed to make the program correct on many devices.
66.2 Why This Dialect Exists
Large tensor programs often cannot run efficiently on one device. They need model parallelism, data parallelism, tensor parallelism, or domain decomposition. Those strategies all require the compiler to know where pieces of a tensor live and how operations communicate between devices.
Without a dialect like shard, this information tends to be hidden in runtime calls, framework-specific metadata, or opaque attributes. That makes it hard for MLIR passes to propagate layouts, change layouts, optimize collectives, or lower distributed work in stages.
The shard dialect gives the compiler first-class IR for:
- device/process grids;
- tensor sharding annotations;
- current process indices;
- distributed tensor shapes;
- collective communication;
- halo exchange;
- SPMD partitioning;
- lowering to MPI.
66.3 When It Matters
The shard dialect matters when a program is being compiled for multiple devices or processes and the tensor distribution is part of the compiler’s job.
It is especially important for:
- distributed ML workloads where tensors are partitioned across accelerators;
- SPMD lowering, where every process runs the same program on a local shard;
- resharding between different tensor layouts;
- reductions that cross device boundaries;
- halo exchange in stencil-like or domain-decomposed computations;
- lowering high-level tensor communication into MPI operations.
It is less relevant for a single-device compiler pipeline. A single GPU kernel, for example, usually uses gpu, nvgpu, nvvm, amdgpu, rocdl, xegpu, or xevm rather than shard.
66.4 When To Use It
Use shard when the IR still talks about tensors, but those tensors already have distributed placement semantics.
Typical uses are:
- represent a logical device grid with
shard.grid; - annotate tensor values with
shard.shardingandshard.shard; - propagate those annotations through supported operations with
sharding-propagation; - partition a tensor function into SPMD form with
shard-partition; - simplify communication with
shard-simplify; - lower implemented communication patterns with
convert-shard-to-mpi.
Do not use shard as a generic replacement for memref or mpi. shard explains distributed tensor intent. mpi models the lower-level communication runtime. memref models concrete buffers.
66.5 Core Concepts
66.5.1 Device Grids
A shard.grid operation defines a logical grid of devices or processes:
shard.grid @grid(shape = 2x4)
This means there are two grid axes. Axis 0 has size 2, and axis 1 has size 4. Dynamic grid dimensions are allowed with ?:
shard.grid @dynamic_grid(shape = 2x?)
Most other shard operations refer to a grid by symbol, for example @grid.
66.5.2 Grid Axes And Device Groups
Communication operations use grid_axes to say which axes participate in a collective. Devices with the same coordinates outside those axes form one communication group.
For example, on a grid with shape 2x3x4, grid_axes = [1] forms groups along axis 1 while fixing axes 0 and 2. grid_axes = [0, 2] forms groups over axes 0 and 2 while fixing axis 1.
The order of axes can matter for operations such as all_to_all.
66.5.5 SPMD Meaning
The execution model is SPMD: every process runs the same program, but operations act on the local shard owned by that process. Collective operations require all processes in their group to participate consistently.
That is why the dialect has both high-level tensor communication ops such as shard.all_reduce and process-query ops such as shard.process_multi_index.
66.5.6 Halos And Uneven Shards
shard.sharding can include halo_sizes for data that overlaps neighboring shards:
%s = shard.sharding @grid split_axes = [[0]]
halo_sizes = [1, 2] : !shard.sharding
It can also include sharded_dims_offsets to describe uneven shard boundaries:
%s = shard.sharding @grid split_axes = [[0]]
sharded_dims_offsets = [0, 3, 7, 10] : !shard.sharding
Those two forms are mutually exclusive in the same shard.sharding.
66.6 Operations
The local LLVM checkout defines 22 shard operations.
66.6.1 Placement And Grid Operations
| Operation | Purpose |
|---|---|
shard.grid |
Defines a named device/process grid. |
shard.grid_shape |
Returns selected grid dimension sizes as index values. |
shard.process_linear_index |
Returns the current process as one linear index in the grid. |
shard.process_multi_index |
Returns the current process coordinates over selected grid axes. |
shard.neighbors_linear_indices |
Returns previous and next neighbor process indices along split axes. |
Use these operations when the compiler needs to materialize process identity, grid sizes, or neighbor relationships in SPMD IR.
66.6.3 Collective Communication Operations
| Operation | Purpose |
|---|---|
shard.all_gather |
Concatenates pieces from all devices in a group and replicates the gathered result. |
shard.all_reduce |
Reduces values across a group and gives every participant the reduced result. |
shard.all_slice |
Slices a replicated value according to the process position; this has no inter-device communication. |
shard.all_to_all |
Splits each participant’s input, exchanges pieces, and concatenates received pieces. |
shard.broadcast |
Copies the root device’s value to the rest of the group. |
shard.gather |
Gathers pieces to the root device; non-root results are undefined. |
shard.reduce |
Reduces values to the root device; non-root results are undefined. |
shard.reduce_scatter |
Reduces across the group and scatters pieces of the reduced value. |
shard.scatter |
Splits a root value and distributes pieces to the group. |
shard.shift |
Shifts tensor values along a grid axis, optionally rotating. |
The reduction kind for all_reduce, reduce, and reduce_scatter is one of: sum, max, min, product, average, bitwise_and, bitwise_or, bitwise_xor, or generic.
66.6.4 Point-To-Point And Halo Operations
| Operation | Purpose |
|---|---|
shard.send |
Sends a tensor to a destination in-group device. |
shard.recv |
Receives a tensor from a source in-group device. |
shard.update_halo |
Exchanges halo regions with neighboring shards. |
send and recv are not collective pure operations. They model explicit point-to-point movement. update_halo is a higher-level halo exchange that the MPI lowering expands into neighbor communication and subview copies.
66.7 Attributes, Types, And Interfaces
66.7.1 Type
| Type | Meaning |
|---|---|
!shard.sharding |
A sharding definition value produced by shard.sharding. |
66.7.2 Attributes
| Attribute family | Meaning |
|---|---|
DenseI16ArrayAttr grid axes |
Stores lists such as grid_axes = [0, 1]. |
#shard.axisarray / GridAxesArrayAttr |
Stores nested split-axis arrays such as [[0], []]. |
ReductionKindAttr |
Stores reduction kinds such as sum, max, and generic. |
DenseI64ArrayAttr shapes, halos, offsets, roots |
Stores grid shapes, halo sizes, sharded dimension offsets, and root coordinates. |
66.8 Transformations
66.8.4 Lowering Patterns Used By Passes
The Shard transform library also provides rewrite patterns, used by tests and by the MPI conversion:
| Pattern group | Purpose |
|---|---|
populateProcessMultiIndexOpLoweringPatterns |
Rewrites shard.process_multi_index using shard.process_linear_index, shard.grid_shape, and affine delinearization. |
populateAllSliceOpLoweringPatterns |
Rewrites shard.all_slice into process-index queries, shape checks, and tensor slicing. |
populateAllOpLoweringPatterns |
Adds both of the above groups. |
66.9 Conversions And Lowering Paths
66.9.2 Rank Specialization With DLTI
If a module contains the DLTI attribute MPI:comm_world-rank, the convert-shard-to-mpi pass can use that integer value as the current MPI rank instead of emitting an MPI_Comm_rank query. That can expose constants for shape propagation and fusion.
66.9.3 After MPI
After convert-shard-to-mpi, the next stage is usually the MPI dialect’s own lowering path, eventually reaching LLVM-compatible IR and runtime calls. That belongs to the mpi chapter, not the shard chapter.
66.10 Example IR
This example declares a 1D grid, says that the first dimension of a tensor is split across that grid, and annotates the tensor:
module {
shard.grid @grid_1d(shape = 4)
func.func @annotate(%arg0: tensor<8x16xf32>) -> tensor<8x16xf32> {
%s = shard.sharding @grid_1d split_axes = [[0], []] : !shard.sharding
%sharded = shard.shard %arg0 to %s : tensor<8x16xf32>
return %sharded : tensor<8x16xf32>
}
}
This example shows communication over the same grid:
module {
shard.grid @grid_1d(shape = 4)
func.func @communicate(%arg0: tensor<2x16xf32>) -> tensor<8x16xf32> {
%gathered = shard.all_gather %arg0 on @grid_1d
grid_axes = [0] gather_axis = 0
: tensor<2x16xf32> -> tensor<8x16xf32>
return %gathered : tensor<8x16xf32>
}
}
This example computes the current process coordinate:
module {
shard.grid @grid_2d(shape = 2x4)
func.func @where_am_i() -> (index, index) {
%i, %j = shard.process_multi_index on @grid_2d : index, index
return %i, %j : index, index
}
}
66.11 Mental Model
Think of shard as a contract between tensor IR and distributed execution.
At the top of the contract, tensors have global shapes and sharding annotations. The compiler can propagate those annotations, reason about how operations map to local shards, and insert communication when layouts do not line up.
At the bottom of the contract, communication becomes explicit: MPI ranks, communicators, buffers, reductions, gathers, and halo sends/receives.
The dialect is valuable because it keeps distribution visible long enough for compiler transformations to optimize it.
66.12 Gotchas
shard.shardis an annotation operation, not a data copy by itself.annotate_for_userschanges whether a sharding describes a value’s producer side or consumer side.split_axesis organized by tensor dimension, whilegrid_axesis organized by device-grid dimension. Mixing those up is the most common reading error.grid_axesdefines communication groups by fixing all other grid coordinates.- Collective ops assume SPMD participation. Removing, duplicating, or moving a collective on only some paths can make the runtime program invalid.
shard.all_sliceis named like a collective, but it does not communicate; it slices based on process position.halo_sizesandsharded_dims_offsetsare mutually exclusive inshard.sharding.convert-shard-to-mpicurrently covers a specific set of Shard operations. Unsupported high-level communication may need partitioning, simplification, or additional lowering first.- Dynamic grid sizes and uneven shards often require shape computations to stay in the IR until enough constants are available.
66.13 Source Map
Use these files in the LLVM repo when you need exact behavior:
| Topic | Files |
|---|---|
| Dialect base, type, attributes, reduction enum | mlir/include/mlir/Dialect/Shard/IR/ShardBase.td |
| Operation definitions | mlir/include/mlir/Dialect/Shard/IR/ShardOps.td |
| Operation implementation | mlir/lib/Dialect/Shard/IR/ShardOps.cpp |
| Main dialect documentation | mlir/docs/Dialects/Shard.md |
| Sharding interface | mlir/include/mlir/Dialect/Shard/Interfaces/ShardingInterface.td, mlir/lib/Dialect/Shard/Interfaces/ShardingInterface.cpp |
| Transform pass definitions | mlir/include/mlir/Dialect/Shard/Transforms/Passes.td |
| Partition pass and resharding patterns | mlir/lib/Dialect/Shard/Transforms/Partition.cpp, mlir/include/mlir/Dialect/Shard/Transforms/ReshardingPartitionDoc.md |
| Sharding propagation | mlir/lib/Dialect/Shard/Transforms/ShardingPropagation.cpp |
| Simplification patterns | mlir/lib/Dialect/Shard/Transforms/Simplify.cpp |
| Lowering helper patterns | mlir/lib/Dialect/Shard/Transforms/Transforms.cpp |
| Shard to MPI pass declaration | mlir/include/mlir/Conversion/Passes.td |
| Shard to MPI implementation | mlir/lib/Conversion/ShardToMPI/ShardToMPI.cpp |
| Dialect tests | mlir/test/Dialect/Shard |
| Conversion tests | mlir/test/Conversion/ShardToMPI |