85 arm_sme Dialect
85.1 Beginner Summary
arm_sme is an Arm CPU target dialect for the Arm Scalable Matrix Extension and its ZA matrix storage.
The dialect is used when a compiler wants to represent operations that fit the Arm SME programming model: scalable vectors, scalable matrix tiles, tile slices, and matrix outer-product instructions. It is more target-specific than linalg or vector, but more structured than raw LLVM IR intrinsics.
You normally do not start a program in arm_sme. A typical path starts with portable IR such as linalg, vector, memref, scf, and arith, then lowers matching vector operations to arm_sme once the compiler knows it is targeting an Arm CPU with SME support.
The important idea is simple: arm_sme is the bridge from portable matrix and vector IR to Arm SME hardware tiles and AArch64 SME intrinsics.
85.2 Why This Dialect Exists
Generic MLIR dialects intentionally avoid committing to one hardware target. That is useful for optimization, but Arm SME has concepts that generic IR cannot name precisely:
- ZA, the architectural matrix tile storage used by SME.
- Streaming SVE mode, controlled by the processor state bit
PSTATE.SM. - Scalable 2-D tile values such as
vector<[4]x[4]xf32>. - Horizontal and vertical tile slices.
- Matrix outer-product instructions such as MOPA and MOPS.
- Tile allocation, where virtual tile SSA values are assigned hardware tile IDs before lowering to intrinsics.
The arm_sme dialect makes those concepts explicit. That lets MLIR optimize and verify SME-shaped code before the final conversion to LLVM-compatible intrinsic operations.
85.3 When It Matters
arm_sme matters when a compiler is lowering matrix or vector code for Arm SME-capable AArch64 targets.
You are likely to see it when:
- lowering
linalg.matmulorvector.outerproducttoward Arm SME outer product instructions; - converting
vector.transfer_read,vector.transfer_write,vector.load, orvector.storeon SME-sized scalable tiles; - legalizing scalable vectors that are larger than one SME tile;
- lowering full tile loads and stores into loops over tile slices;
- attaching function attributes that describe streaming mode and ZA usage;
- translating to LLVM IR that contains AArch64 SME intrinsics.
It is not a general matrix dialect. Use linalg for target-independent matrix algorithms and vector for target-independent vectorization. Use arm_sme when you have crossed into the Arm SME lowering path.
85.4 When To Use It
Use arm_sme when all of these are true:
- the target is AArch64 with the required SME feature set;
- the computation maps naturally to SME tiles, tile slices, or outer products;
- the IR is late enough in the pipeline for target-specific choices;
- you want the final lowering to use Arm SME LLVM intrinsic operations.
Avoid using it as an early frontend dialect. Early IR should usually preserve portable structure in linalg, tensor, memref, scf, arith, and vector so generic transformations still apply.
85.5 Core Concepts
85.5.1 SME Is About Scalable Matrix Tiles
Arm SME extends the Arm scalable-vector model with matrix-oriented storage and instructions. The key storage is ZA, which can be viewed as a collection of hardware tiles. In MLIR, arm_sme models these tiles with 2-D scalable vector types:
vector<[16]x[16]xi8>
vector<[8]x[8]xi16>
vector<[4]x[4]xi32>
vector<[2]x[2]xi64>
vector<[1]x[1]xi128>
vector<[8]x[8]xf16>
vector<[8]x[8]xbf16>
vector<[4]x[4]xf32>
vector<[2]x[2]xf64>
The square brackets mean the dimensions are scalable. The physical number of lanes depends on the runtime vector length, not a fixed compile-time width.
85.5.2 SVE Vectors Feed SME Tiles
Many arm_sme operations take 1-D scalable vectors:
vector<[16]xi8>
vector<[8]xi16>
vector<[4]xi32>
vector<[2]xi64>
vector<[1]xi128>
vector<[8]xf16>
vector<[8]xbf16>
vector<[4]xf32>
vector<[2]xf64>
Predicate operands use matching scalable i1 vectors:
vector<[16]xi1>
vector<[8]xi1>
vector<[4]xi1>
vector<[2]xi1>
vector<[1]xi1>
This is why arm_sme sits near vector and arm_sve in lowering pipelines: SME tile operations are built out of scalable-vector rows, columns, masks, and outer products.
85.5.3 Virtual Tiles And Tile IDs
Most high-level arm_sme tile operations implement ArmSMETileOpInterface. They operate on virtual tile SSA values first. Later, tile allocation assigns a tile_id attribute that selects which ZA tile the operation uses.
Tile ID availability depends on element width:
| Tile type | Possible tile IDs |
|---|---|
vector<[16]x[16]xi8> |
0 |
vector<[8]x[8]xi16>, vector<[8]x[8]xf16>, vector<[8]x[8]xbf16> |
0, 1 |
vector<[4]x[4]xi32>, vector<[4]x[4]xf32> |
0 through 3 |
vector<[2]x[2]xi64>, vector<[2]x[2]xf64> |
0 through 7 |
vector<[1]x[1]xi128> |
0 through 15 |
The programmer usually does not write tile_id by hand. The convert-arm-sme-to-llvm pass runs tile allocation before lowering tile ops to intrinsic ops.
85.5.4 Tile Slices
SME tiles can be accessed one scalable vector slice at a time. A slice can be horizontal or vertical:
layout<horizontal>
layout<vertical>
The default is horizontal. Full tile loads and stores can be lowered into loops over slice loads and stores.
85.5.5 Outer Products
The central compute operation is an outer product. arm_sme.outerproduct is the generic form. It consumes two 1-D scalable vectors and produces a 2-D SME tile. It can optionally accumulate into an existing tile and optionally use operand masks.
SME also has fused widening outer-product instructions. The dialect represents them with operations such as arm_sme.fmopa_2way, arm_sme.smopa_4way, and arm_sme.usmops_4way.
In the names:
mopameans outer product and accumulate.mopsmeans outer product and subtract.fmeans floating point.smeans signed integer.umeans unsigned integer.summeans signed by unsigned.usmmeans unsigned by signed.2wayand4waydescribe how many widened outer products are fused.
85.5.6 Streaming Mode And ZA Mode
SME code may require Arm streaming SVE mode. The enable-arm-streaming pass annotates func.func operations with attributes that describe how streaming mode and ZA storage are managed.
The streaming mode choices are:
disabledstreamingstreaming-locallystreaming-compatible
The ZA mode choices are:
disablednew-zain-zaout-zainout-zapreserves-za
These are ABI-level choices. They affect how callers and callees agree about PSTATE.SM and ZA state, so they should be introduced deliberately and late in the target lowering path.
85.6 Operations
The arm_sme dialect defines 25 regular operations and 43 intrinsic wrapper operations.
85.6.1 Tile State Operations
| Operation | Purpose |
|---|---|
arm_sme.get_tile |
Creates a virtual tile with undefined contents. |
arm_sme.zero |
Creates a zero-initialized virtual tile. |
arm_sme.copy_tile |
Copies a virtual tile SSA value, mainly to normalize IR before tile allocation. |
arm_sme.streaming_vl |
Returns the streaming vector length as an index for a selected element size. |
arm_sme.get_tile is useful when an operation needs an existing tile value but the contents are not meaningful yet.
%tile = arm_sme.get_tile : vector<[4]x[4]xf32>
arm_sme.zero creates a tile whose contents are known to be zero.
%tile = arm_sme.zero : vector<[4]x[4]xf32>
arm_sme.copy_tile preserves tile dataflow while giving tile allocation a separate SSA value to reason about.
%copy = arm_sme.copy_tile %tile : vector<[4]x[4]xf32>
arm_sme.streaming_vl queries the streaming vector length for an element size.
%svl = arm_sme.streaming_vl <word>
The element-size attribute is one of byte, half, word, or double.
85.6.2 Full Tile Memory Operations
| Operation | Purpose |
|---|---|
arm_sme.tile_load |
Loads a full 2-D SME tile from a 2-D memref. |
arm_sme.tile_store |
Stores a full 2-D SME tile to a 2-D memref. |
These are structured operations. They describe a full tile transfer, but they can be lowered to loops over tile slices by convert-arm-sme-to-scf.
%tile = arm_sme.tile_load %src[%i, %j]
: memref<?x?xf32>, vector<[4]x[4]xf32>
arm_sme.tile_store %tile, %dst[%i, %j]
: memref<?x?xf32>, vector<[4]x[4]xf32>
Both operations accept an optional layout<horizontal> or layout<vertical>. arm_sme.tile_load can also take padding and a mask together. arm_sme.tile_store can take a mask.
85.6.3 Tile Slice Operations
| Operation | Purpose |
|---|---|
arm_sme.load_tile_slice |
Loads one scalable vector slice from memory into a tile. |
arm_sme.store_tile_slice |
Stores one scalable vector slice from a tile to memory. |
arm_sme.insert_tile_slice |
Inserts a 1-D scalable vector into a tile slice. |
arm_sme.extract_tile_slice |
Extracts a 1-D scalable vector from a tile slice. |
Tile-slice ops are the lower-level form of tile memory and tile manipulation. They make the slice index, predicate, and layout explicit.
%updated = arm_sme.insert_tile_slice %row, %tile[%k]
: vector<[4]xf32> into vector<[4]x[4]xf32>
%row2 = arm_sme.extract_tile_slice %updated[%k]
: vector<[4]xf32> from vector<[4]x[4]xf32>
Slice load and store operations use an explicit scalable predicate:
%next = arm_sme.load_tile_slice %src[%i, %j], %mask, %tile, %k
: memref<?x?xf32>, vector<[4]xi1>, vector<[4]x[4]xf32>
arm_sme.store_tile_slice %next, %k, %mask, %dst[%i, %j]
: memref<?x?xf32>, vector<[4]xi1>, vector<[4]x[4]xf32>
85.6.4 Generic Outer Product
| Operation | Purpose |
|---|---|
arm_sme.outerproduct |
Computes an SME-sized outer product, optionally with masks, an accumulator, and add/sub behavior. |
The generic outer product is close to vector.outerproduct, but its masking is on the operands. That matches how SME instructions use predicates.
%tile = arm_sme.outerproduct %lhs, %rhs
: vector<[4]xf32>, vector<[4]xf32>
An existing accumulator tile can be provided:
%tile2 = arm_sme.outerproduct %lhs, %rhs acc(%tile)
: vector<[4]xf32>, vector<[4]xf32>
The optional combining kind is kind<add> or kind<sub>. Add corresponds to MOPA-style accumulation. Sub corresponds to MOPS-style subtraction.
85.6.5 Fused Widening Outer Product Operations
| Operation | Input | Result | Meaning |
|---|---|---|---|
arm_sme.fmopa_2way |
vector<[8]xf16> or vector<[8]xbf16> |
vector<[4]x[4]xf32> |
Floating-point sum of two widened outer products and accumulate. |
arm_sme.fmops_2way |
vector<[8]xf16> or vector<[8]xbf16> |
vector<[4]x[4]xf32> |
Floating-point sum of two widened outer products and subtract. |
arm_sme.smopa_2way |
vector<[8]xi16> |
vector<[4]x[4]xi32> |
Signed integer sum of two widened outer products and accumulate. |
arm_sme.smops_2way |
vector<[8]xi16> |
vector<[4]x[4]xi32> |
Signed integer sum of two widened outer products and subtract. |
arm_sme.umopa_2way |
vector<[8]xi16> |
vector<[4]x[4]xi32> |
Unsigned integer sum of two widened outer products and accumulate. |
arm_sme.umops_2way |
vector<[8]xi16> |
vector<[4]x[4]xi32> |
Unsigned integer sum of two widened outer products and subtract. |
arm_sme.smopa_4way |
vector<[16]xi8> or vector<[8]xi16> |
vector<[4]x[4]xi32> or vector<[2]x[2]xi64> |
Signed integer sum of four widened outer products and accumulate. |
arm_sme.smops_4way |
vector<[16]xi8> or vector<[8]xi16> |
vector<[4]x[4]xi32> or vector<[2]x[2]xi64> |
Signed integer sum of four widened outer products and subtract. |
arm_sme.umopa_4way |
vector<[16]xi8> or vector<[8]xi16> |
vector<[4]x[4]xi32> or vector<[2]x[2]xi64> |
Unsigned integer sum of four widened outer products and accumulate. |
arm_sme.umops_4way |
vector<[16]xi8> or vector<[8]xi16> |
vector<[4]x[4]xi32> or vector<[2]x[2]xi64> |
Unsigned integer sum of four widened outer products and subtract. |
arm_sme.sumopa_4way |
vector<[16]xi8> or vector<[8]xi16> |
vector<[4]x[4]xi32> or vector<[2]x[2]xi64> |
Signed-by-unsigned sum of four widened outer products and accumulate. |
arm_sme.sumops_4way |
vector<[16]xi8> or vector<[8]xi16> |
vector<[4]x[4]xi32> or vector<[2]x[2]xi64> |
Signed-by-unsigned sum of four widened outer products and subtract. |
arm_sme.usmopa_4way |
vector<[16]xi8> or vector<[8]xi16> |
vector<[4]x[4]xi32> or vector<[2]x[2]xi64> |
Unsigned-by-signed sum of four widened outer products and accumulate. |
arm_sme.usmops_4way |
vector<[16]xi8> or vector<[8]xi16> |
vector<[4]x[4]xi32> or vector<[2]x[2]xi64> |
Unsigned-by-signed sum of four widened outer products and subtract. |
These operations are usually produced by arm-sme-outer-product-fusion, not by frontends. They are close to hardware instructions and lower directly to SME intrinsic wrappers.
85.6.6 LLVM Intrinsic Wrapper Operations
The arm_sme.intr.* operations are low-level wrappers around AArch64 SME LLVM intrinsics. They are still MLIR operations, but they are already close to LLVM IR and backend instruction selection.
Count and zero operations:
| Operation | Purpose |
|---|---|
arm_sme.intr.cntsd |
Counts streaming doubleword elements and is used to compute streaming vector length. |
arm_sme.intr.zero |
Zeroes ZA tiles selected by an immediate tile mask. |
Memory load operations:
| Operation | Purpose |
|---|---|
arm_sme.intr.ld1b.horiz |
Load byte elements into a horizontal tile slice. |
arm_sme.intr.ld1b.vert |
Load byte elements into a vertical tile slice. |
arm_sme.intr.ld1h.horiz |
Load halfword elements into a horizontal tile slice. |
arm_sme.intr.ld1h.vert |
Load halfword elements into a vertical tile slice. |
arm_sme.intr.ld1w.horiz |
Load word elements into a horizontal tile slice. |
arm_sme.intr.ld1w.vert |
Load word elements into a vertical tile slice. |
arm_sme.intr.ld1d.horiz |
Load doubleword elements into a horizontal tile slice. |
arm_sme.intr.ld1d.vert |
Load doubleword elements into a vertical tile slice. |
arm_sme.intr.ld1q.horiz |
Load quadword elements into a horizontal tile slice. |
arm_sme.intr.ld1q.vert |
Load quadword elements into a vertical tile slice. |
Memory store operations:
| Operation | Purpose |
|---|---|
arm_sme.intr.st1b.horiz |
Store byte elements from a horizontal tile slice. |
arm_sme.intr.st1b.vert |
Store byte elements from a vertical tile slice. |
arm_sme.intr.st1h.horiz |
Store halfword elements from a horizontal tile slice. |
arm_sme.intr.st1h.vert |
Store halfword elements from a vertical tile slice. |
arm_sme.intr.st1w.horiz |
Store word elements from a horizontal tile slice. |
arm_sme.intr.st1w.vert |
Store word elements from a vertical tile slice. |
arm_sme.intr.st1d.horiz |
Store doubleword elements from a horizontal tile slice. |
arm_sme.intr.st1d.vert |
Store doubleword elements from a vertical tile slice. |
arm_sme.intr.st1q.horiz |
Store quadword elements from a horizontal tile slice. |
arm_sme.intr.st1q.vert |
Store quadword elements from a vertical tile slice. |
arm_sme.intr.str |
Store ZA-related data using the SME STR intrinsic form. |
Tile slice read/write operations:
| Operation | Purpose |
|---|---|
arm_sme.intr.read.horiz |
Read a horizontal tile slice into an SVE vector. |
arm_sme.intr.read.vert |
Read a vertical tile slice into an SVE vector. |
arm_sme.intr.write.horiz |
Write an SVE vector into a horizontal tile slice. |
arm_sme.intr.write.vert |
Write an SVE vector into a vertical tile slice. |
Outer-product intrinsic operations:
| Operation | Purpose |
|---|---|
arm_sme.intr.mopa |
Non-widening outer product and accumulate. |
arm_sme.intr.mops |
Non-widening outer product and subtract. |
arm_sme.intr.mopa.wide |
Widening floating-point outer product and accumulate. |
arm_sme.intr.mops.wide |
Widening floating-point outer product and subtract. |
arm_sme.intr.smopa.wide |
Widening signed integer outer product and accumulate. |
arm_sme.intr.smops.wide |
Widening signed integer outer product and subtract. |
arm_sme.intr.umopa.wide |
Widening unsigned integer outer product and accumulate. |
arm_sme.intr.umops.wide |
Widening unsigned integer outer product and subtract. |
arm_sme.intr.sumopa.wide |
Widening signed-by-unsigned outer product and accumulate. |
arm_sme.intr.sumops.wide |
Widening signed-by-unsigned outer product and subtract. |
arm_sme.intr.usmopa.wide |
Widening unsigned-by-signed outer product and accumulate. |
arm_sme.intr.usmops.wide |
Widening unsigned-by-signed outer product and subtract. |
arm_sme.intr.smopa.za32 |
Signed 2-way outer product using a ZA32 tile. |
arm_sme.intr.smops.za32 |
Signed 2-way outer product/subtract using a ZA32 tile. |
arm_sme.intr.umopa.za32 |
Unsigned 2-way outer product using a ZA32 tile. |
arm_sme.intr.umops.za32 |
Unsigned 2-way outer product/subtract using a ZA32 tile. |
Most users should read intrinsic ops as “the target-specific form after lowering.” They are important for backend correctness, but not the best teaching entry point for SME programming in MLIR.
85.7 Transformations
85.7.1 enable-arm-streaming
enable-arm-streaming runs on func.func. It annotates functions with Arm streaming-mode and ZA-mode attributes.
Important options:
| Option | Meaning |
|---|---|
streaming-mode=disabled |
Do not enable streaming mode. |
streaming-mode=streaming |
Streaming mode is part of the function ABI; the caller manages PSTATE.SM. |
streaming-mode=streaming-locally |
The callee enters and exits streaming mode internally. |
streaming-mode=streaming-compatible |
The function can be entered in either streaming or non-streaming mode. |
za-mode=disabled |
Do not enable ZA state. |
za-mode=new-za |
The function creates ZA state on entry and destroys it on exit. |
za-mode=in-za |
ZA is an input to the function. |
za-mode=out-za |
ZA is an output of the function. |
za-mode=inout-za |
ZA is both input and output. |
za-mode=preserves-za |
The function returns with shared ZA unchanged. |
if-required-by-ops |
Apply the selected modes only when the function contains ArmSME tile-interface ops. |
if-scalable-and-supported |
Apply the selected modes only for supported scalable-vector operations. |
This pass is about function boundaries and ABI behavior. It does not by itself turn a matrix multiply into SME instructions.
85.7.2 arm-sme-outer-product-fusion
arm-sme-outer-product-fusion runs on func.func. It finds chains of arm_sme.outerproduct operations connected through an accumulator and fuses them into 2-way or 4-way widening outer-product operations.
For example, two f16 outer products widened to f32 can become one arm_sme.fmopa_2way. Four i8 or i16 outer products can become one of the 4-way integer operations.
This pass matters because the fused operations are closer to the hardware instructions and have direct lowering support in convert-arm-sme-to-llvm.
85.7.3 arm-sme-vector-legalization
arm-sme-vector-legalization runs on module. It rewrites vector operations so they can be lowered to ArmSME. The main job is decomposing vector types that are larger than a single SME tile into multiple tile-sized operations.
The current decomposition is limited to vector types that are exact multiples of SME tiles. That means both scalable dimensions must divide cleanly into the SME tile shape for the element type.
85.7.4 Tile Allocation
Tile allocation assigns concrete tile_id attributes to operations that implement ArmSMETileOpInterface.
There is a test-arm-sme-tile-allocation pass for tests and debugging, but normal pipelines rely on convert-arm-sme-to-llvm, which runs tile allocation internally before converting tile operations to intrinsic operations.
Tile allocation can introduce spills and fills when the virtual tile live ranges do not fit directly in available hardware tile IDs.
85.7.5 Canonicalization
Standard canonicalization patterns apply to some ArmSME operations. For example, tests cover simplifying redundant tile slice extraction and insertion patterns.
Canonicalization is useful before tile allocation because it can shorten live ranges and remove unnecessary tile operations.
85.8 Conversions / Lowering Paths
85.8.1 convert-arith-to-arm-sme
This pass converts arith.constant operations that produce valid SME tile vectors.
The important case is a zero dense constant:
%0 = arith.constant dense<0.0> : vector<[4]x[4]xf32>
It lowers to:
%0 = arm_sme.zero : vector<[4]x[4]xf32>
Nonzero dense tile constants are expanded by building tile slices, using arm_sme.get_tile and arm_sme.insert_tile_slice.
85.8.2 convert-vector-to-arm-sme
This pass converts matching vector operations to arm_sme.
Important patterns include:
| Vector operation pattern | ArmSME result |
|---|---|
2-D vector.transfer_read of a valid SME tile type |
arm_sme.tile_load |
2-D vector.transfer_write of a valid SME tile type |
arm_sme.tile_store |
vector.load of a valid SME tile type |
arm_sme.tile_load |
vector.store of a valid SME tile type |
arm_sme.tile_store |
vector.outerproduct with an SME-sized result |
arm_sme.outerproduct |
vector.broadcast to an SME tile |
A loop that inserts slices into a tile. |
vector.transpose of an SME tile |
Store/reload or folding into transfer-read layout when possible. |
vector.extract from an SME tile |
arm_sme.extract_tile_slice plus scalar/vector extraction. |
vector.insert into an SME tile |
Slice extraction/insertion and arm_sme.insert_tile_slice. |
vector.print of an SME tile |
A loop that extracts and prints tile slices. |
Not every vector operation lowers. The source must have a supported scalable tile shape, supported element type, and compatible indexing or masking form.
85.8.3 convert-arm-sme-to-scf
This pass lowers structured full-tile memory operations to loops over tile slices.
Typical rewrites:
| Input | Lowered form |
|---|---|
arm_sme.tile_load |
arm_sme.get_tile or arm_sme.zero, an scf.for loop, and arm_sme.load_tile_slice. |
arm_sme.tile_store |
An scf.for loop and arm_sme.store_tile_slice. |
This pass is useful because the final intrinsic lowering operates naturally on tile slices.
85.8.4 convert-arm-sme-to-llvm
This pass lowers ArmSME tile operations to LLVM-compatible intrinsic wrapper operations.
It performs tile allocation first. Then it lowers:
arm_sme.zerotoarm_sme.intr.zero;arm_sme.load_tile_sliceto anarm_sme.intr.ld1*op;arm_sme.store_tile_sliceto anarm_sme.intr.st1*op;arm_sme.insert_tile_slicetoarm_sme.intr.write.*;arm_sme.extract_tile_slicetoarm_sme.intr.read.*;arm_sme.outerproducttoarm_sme.intr.mopafor supported non-widening floating-point add cases;- fused widening outer products to
arm_sme.intr.*mopa*orarm_sme.intr.*mops*forms; arm_sme.streaming_vltoarm_sme.intr.cntsd, index casts, and scaling.
The pass also handles tile spills and fills when virtual tile allocation needs temporary memory. If an operation still has an SME tile type after conversion and is not one of the allowed pseudo operations, the pass reports an error.
85.8.5 Common Pipeline Shape
A simplified ArmSME lowering path looks like this:
linalg / vector / arith
-> vectorization and vector cleanup
-> arm-sme-vector-legalization
-> convert-arith-to-arm-sme
-> convert-vector-to-arm-sme
-> arm-sme-outer-product-fusion
-> convert-arm-sme-to-scf
-> enable-arm-streaming
-> convert-arm-sme-to-llvm
-> LLVM lowering and translation
Real pipelines may interleave canonicalization, CSE, bufferization, and other target-specific steps. The important conceptual order is: keep IR portable early, introduce SME tile operations when the shapes match, lower structured tile operations to slice operations, then allocate tiles and emit intrinsics.
85.9 Example IR
85.9.1 Zero A Tile
module {
func.func @zero_tile() -> vector<[4]x[4]xf32> {
%0 = arm_sme.zero : vector<[4]x[4]xf32>
return %0 : vector<[4]x[4]xf32>
}
}
This creates a virtual f32 tile with zero contents. Before LLVM conversion, it is still an SSA value. During convert-arm-sme-to-llvm, tile allocation chooses a ZA tile ID and the operation lowers to arm_sme.intr.zero.
85.9.2 Load And Store A Tile
module {
func.func @tile_load_store(
%src: memref<?x?xf32>, %dst: memref<?x?xf32>,
%i: index, %j: index) {
%tile = arm_sme.tile_load %src[%i, %j]
: memref<?x?xf32>, vector<[4]x[4]xf32>
arm_sme.tile_store %tile, %dst[%i, %j]
: memref<?x?xf32>, vector<[4]x[4]xf32>
return
}
}
This is the structured form of a full tile memory transfer. A later convert-arm-sme-to-scf pass can turn it into a loop over tile slices.
85.9.3 Compute An Outer Product
module {
func.func @outer(
%lhs: vector<[4]xf32>,
%rhs: vector<[4]xf32>) -> vector<[4]x[4]xf32> {
%tile = arm_sme.outerproduct %lhs, %rhs
: vector<[4]xf32>, vector<[4]xf32>
return %tile : vector<[4]x[4]xf32>
}
}
This is the direct SME-shaped representation of an outer product. If an accumulator is supplied, the result represents an accumulated update to that tile.
85.9.4 Use A Fused Widening Outer Product
module {
func.func @fused_f16_outer(
%lhs: vector<[8]xf16>,
%rhs: vector<[8]xf16>) -> vector<[4]x[4]xf32> {
%tile = arm_sme.fmopa_2way %lhs, %rhs
: vector<[8]xf16>, vector<[8]xf16> into vector<[4]x[4]xf32>
return %tile : vector<[4]x[4]xf32>
}
}
This is already close to an SME widening outer-product instruction. It is the kind of operation arm-sme-outer-product-fusion tries to create from generic outer-product chains.
85.9.5 Query Streaming Vector Length
module {
func.func @streaming_words() -> index {
%svl = arm_sme.streaming_vl <word>
return %svl : index
}
}
This returns the number of bytes, halfwords, words, or doublewords in the streaming vector length depending on the TypeSize attribute.
85.10 Mental Model
Think of arm_sme as the point where vectorized matrix code becomes “real Arm SME code.”
Before arm_sme, the compiler mostly describes mathematical structure: matrix multiplication, vector transfer, vector outer products, loops, and memory.
Inside arm_sme, the compiler describes SME hardware structure: virtual ZA tiles, scalable tile slices, streaming vector length, predicates, and outer-product instructions.
After arm_sme, the compiler describes LLVM-compatible intrinsic operations and eventually machine instructions.
The dialect is therefore not just a bag of intrinsics. Its higher-level ops are there so MLIR can still reason about tile dataflow before final instruction selection.
85.11 Gotchas
arm_smeis target-specific. Do not introduce it before generic transformations have had a chance to run.- Tile vector types must be valid SME tile shapes. A random 2-D scalable vector is not automatically legal.
- Most tile ops need tile allocation before LLVM conversion. Missing
tile_idattributes are expected before allocation and an error after allocation is required. arm_sme.tile_loadandarm_sme.tile_storeare structured full-tile ops. The lower-level intrinsic path is slice-based.arm_sme.outerproductmasks operands, not result elements. This differs from how many beginners first think about matrix masks.- Direct LLVM conversion of generic
arm_sme.outerproductcurrently supports non-widening floating-point add cases. Integer and subtract forms usually need earlier rewriting or fusion into supported widening operations. - The fused 2-way and 4-way operations encode packed input layouts. They are not just spelling variants of generic outer products.
- Streaming mode and ZA mode are ABI-level choices. Adding them changes the function contract.
- Intrinsic wrapper ops are not portable IR. They should appear late, close to LLVM conversion.
- Some lowering patterns only handle exact tile shapes, exact multiples of tile shapes, or specific indexing maps.
85.12 Source Map
Primary source files:
mlir/include/mlir/Dialect/ArmSME/IR/ArmSME.tdmlir/include/mlir/Dialect/ArmSME/IR/ArmSMEOps.tdmlir/include/mlir/Dialect/ArmSME/IR/ArmSMEIntrinsicOps.tdmlir/include/mlir/Dialect/ArmSME/IR/ArmSMEOpInterfaces.hmlir/include/mlir/Dialect/ArmSME/IR/ArmSMEEnums.hmlir/lib/Dialect/ArmSME/IR/ArmSME.cppmlir/lib/Dialect/ArmSME/Utils/Utils.cpp
Transform source files:
mlir/include/mlir/Dialect/ArmSME/Transforms/Passes.tdmlir/lib/Dialect/ArmSME/Transforms/EnableArmStreaming.cppmlir/lib/Dialect/ArmSME/Transforms/OuterProductFusion.cppmlir/lib/Dialect/ArmSME/Transforms/TileAllocation.cppmlir/lib/Dialect/ArmSME/Transforms/VectorLegalization.cpp
Conversion source files:
mlir/lib/Conversion/ArithToArmSME/ArithToArmSME.cppmlir/lib/Conversion/VectorToArmSME/VectorToArmSME.cppmlir/lib/Conversion/ArmSMEToSCF/ArmSMEToSCF.cppmlir/lib/Conversion/ArmSMEToLLVM/ArmSMEToLLVM.cppmlir/include/mlir/Conversion/Passes.td
Tests:
mlir/test/Dialect/ArmSMEmlir/test/Conversion/ArithToArmSMEmlir/test/Conversion/VectorToArmSMEmlir/test/Conversion/ArmSMEToSCFmlir/test/Conversion/ArmSMEToLLVMmlir/test/Integration/Dialect/Linalg/CPU/ArmSMEmlir/test/Integration/Dialect/Vector/CPU/ArmSME