82 x86 Dialect
82.1 Beginner Summary
The x86 dialect represents x86-specific vector and matrix operations inside MLIR. It is used when a compiler has already decided that part of the program should use Intel/AMD x86 CPU instructions such as AVX, AVX512, AVX10, or AMX.
Most beginners should not write x86 IR first. Start with portable dialects such as linalg, vector, arith, memref, and scf. The x86 dialect appears later, when the compiler chooses concrete CPU instructions for fast dot products, BF16/F16 conversion, mask operations, approximate reciprocal square root, or AMX tiled matrix multiplication.
The most important mental model is:
linalg / vector / arith / memref
-> target-specific vector lowering chooses x86 operations
x86
-> LLVM dialect intrinsics
llvm
-> LLVM IR / machine code
x86 is not a general CPU dialect. It is a collection of wrappers and intermediate forms for specific x86 instruction families.
82.2 Why This Dialect Exists
Portable MLIR vector operations are intentionally target-independent. That is good for optimization, but it is not enough to express every CPU instruction cleanly. x86 has instruction families with very specific semantics:
- AVX512 masked vector operations.
- AVX512 BF16 dot products and conversions.
- AVX/AVX2 packed BF16/F16 conversion helpers.
- AVX and AVX10 int8 dot-product instructions.
- AMX tile registers and tile dot-product instructions.
- Specialized AVX2 transpose lowering patterns.
The x86 dialect exists so MLIR can name these target-specific choices before final LLVM lowering. It gives the compiler a typed MLIR representation for operations that will eventually become LLVM x86 intrinsics.
It is especially useful because some x86 instructions are not just “ordinary vector arithmetic.” They have masks, implicit packing rules, tile register types, memory forms, special source layouts, or target-feature requirements.
82.3 When It Matters
The x86 dialect matters when an MLIR pipeline targets x86 CPUs and wants to use specific x86 vector or matrix hardware.
You care about it when:
- You are lowering
vector.contractto CPU dot-product instructions. - You want AVX512 BF16 instructions such as
vdpbf16psorcvtneps2bf16. - You want AVX/AVX2 packed BF16/F16 conversion and broadcast helpers.
- You want AVX512 mask operations such as compress, roundscale, scalef, or VP2INTERSECT.
- You want AVX10 int8 dot products.
- You want AMX tile operations for BF16, F16, F8, or int8 matrix kernels.
- You are debugging why
convert-vector-to-llvm="enable-x86"did or did not produce x86 intrinsics. - You are writing Transform dialect scripts that apply x86-specific vector contraction patterns.
It usually does not matter in frontend IR. At that level, use linalg.matmul, vector.contract, vector.transfer_read, arith, and memref first.
82.4 When To Use It
Use x86 when the IR is intentionally x86-specific and close to instruction selection.
Good uses:
- Writing tests for x86 vector lowering.
- Representing AVX512 BF16 dot or conversion operations before LLVM lowering.
- Representing AVX/AVX2 packed BF16/F16 conversion helpers.
- Representing AMX tile loads, stores, zeroing, and tile multiply operations.
- Applying x86-specific Transform dialect pattern descriptors.
- Inspecting the output of vector lowering when
enable-x86is active.
Avoid it when:
- You want portable CPU IR.
- A normal
vector,arith,math,memref, orlinalgoperation still expresses the computation adequately. - You do not know which x86 feature set is required.
- You are modeling source-level C/C++ or runtime APIs.
82.5 Core Concepts
82.5.1 X86 Is A Target-Specific Vector Dialect
The dialect mostly wraps operations that lower to LLVM x86 intrinsics. It is not an alternative to the vector dialect. Instead, it is a target-specific way to represent some vector lowering decisions.
For example, a portable dot product may begin as:
vector.contract
After x86-specific pattern selection, that may become:
x86.avx512.dot
x86.avx.dot.i8
x86.avx10.dot.i8
x86.amx.tile_mulf
x86.amx.tile_muli
The exact result depends on element type, vector shape, packing layout, and the target feature set.
82.5.2 X86 Operations Are Grouped By Instruction Family
The operation names carry the instruction-family group:
| Prefix | Meaning |
|---|---|
x86.avx512.* |
AVX512 and AVX512-BF16 style operations. |
x86.avx10.* |
AVX10 operations. |
x86.avx.* |
AVX/AVX2 helper operations and lower-level intrinsic forms. |
x86.avx.intr.* |
Lower-level AVX intrinsic-shaped helper operation. |
x86.amx.* |
AMX tile operations. |
This naming is important. x86.avx512.dot and x86.amx.tile_mulf are both matrix-kernel-related, but they target very different hardware mechanisms.
82.5.3 AMX Uses A Dialect Type
AMX operations use a special type:
!x86.amx.tile<16x32xbf16>
This is not a normal vector type. It represents a value in AMX tile-register space. The dialect provides shape and element-type information so MLIR can verify the operation before lowering to LLVM’s AMX type.
82.5.4 Final Lowering Is Through LLVM Intrinsics
Most x86 operations implement the dialect’s intrinsic operation interface. The lowering path converts them to LLVM dialect calls or operations that map to LLVM x86 intrinsics.
Typical final lowering uses:
convert-vector-to-llvm="enable-x86"
convert-to-llvm
reconcile-unrealized-casts
There is no ordinary beginner-facing “convert the entire x86 dialect by itself” story. The X86 dialect plugs into vector-to-LLVM and convert-to-LLVM infrastructure.
82.6 Type
The current local LLVM checkout defines one X86 type.
| Type | Syntax | Meaning |
|---|---|---|
AMXTileType |
!x86.amx.tile<16x32xbf16> |
A 2-D AMX tile-register value with shape and element type. |
Valid AMX tile element types in this checkout include:
f32
f16
bf16
i32
i8
f8E4M3FN
f8E5M2
AMX tiles are for AMX operations only. They are not general tensors or vectors.
82.7 Operations
The current local LLVM checkout defines 18 x86 operations.
82.7.1 AVX512 Operations
| Operation | What it does |
|---|---|
x86.avx512.mask.compress |
Compresses active vector elements according to an AVX512 mask, optionally passing through a source vector or constant source. |
x86.avx512.mask.rndscale |
AVX512 masked roundscale for packed f32 or f64 vectors. |
x86.avx512.mask.scalef |
AVX512 masked scalef for packed f32 or f64 vectors. |
x86.avx512.vp2intersect |
Computes intersection masks for packed i32 or i64 vectors. |
x86.avx512.dot |
AVX512-BF16 dot product, lowering toward llvm.x86.avx512bf16.dpbf16ps.*. |
x86.avx512.cvt.packed.f32_to_bf16 |
Converts packed f32 vectors to packed bf16 vectors. |
These operations are already hardware-specific. For example, x86.avx512.dot is not a general dot product; it models AVX512-BF16 dot semantics.
82.7.2 AVX10 Operation
| Operation | What it does |
|---|---|
x86.avx10.dot.i8 |
AVX10 signed int8 dot product for vector<64xi8> inputs accumulating into vector<16xi32>. |
82.7.3 AVX And AVX2 Operations
| Operation | What it does |
|---|---|
x86.avx.rsqrt |
AVX reciprocal square root for vector<8xf32>. |
x86.avx.intr.dot |
Lower-level AVX floating dot-product helper for vector<8xf32>. |
x86.avx.dot.i8 |
AVX/AVX2 int8 dot product for packed signed int8 inputs. |
x86.avx.bcst_to_f32.packed |
Loads one BF16 or F16 value from memory, converts it to F32, and broadcasts to a packed F32 vector. |
x86.avx.cvt.packed.even.indexed_to_f32 |
Loads even-indexed BF16/F16 packed memory elements and converts them to F32. |
x86.avx.cvt.packed.odd.indexed_to_f32 |
Loads odd-indexed BF16/F16 packed memory elements and converts them to F32. |
The packed conversion operations are useful in BF16/F16 vector-contract lowerings that emulate dot products with FMA sequences.
82.7.4 AMX Operations
| Operation | What it does |
|---|---|
x86.amx.tile_zero |
Creates an AMX tile filled with zero values. |
x86.amx.tile_load |
Loads an AMX tile from a memref, with implicit or explicit row stride. |
x86.amx.tile_store |
Stores an AMX tile to a memref, with implicit or explicit row stride. |
x86.amx.tile_mulf |
Floating tile multiply-accumulate. Supports BF16, F16, and F8 inputs accumulating into F32 tiles. |
x86.amx.tile_muli |
Integer tile multiply-accumulate for int8 inputs accumulating into I32 tiles, with optional zero-extension annotations. |
AMX operations expose tile-register behavior. They are usually generated from structured vector contractions rather than written by hand.
82.8 Transformations
The X86 dialect has important transformation entry points, but they are mostly pattern collections rather than standalone mlir-opt passes with x86-* names.
82.8.1 Pattern Population APIs
The local Transforms.h exposes these x86-specific pattern groups:
| Pattern group | What it lowers or rewrites |
|---|---|
populateVectorContractToFMAPatterns |
Lowers selected vector.contract operations to vector.fma. |
populateVectorContractToPackedTypeDotProductPatterns |
Lowers packed BF16/int8 contractions to x86 packed dot-product operations such as x86.avx512.dot, x86.avx.dot.i8, or x86.avx10.dot.i8. |
populateVectorContractBF16ToFMAPatterns |
Lowers BF16 contractions to FMA sequences using packed conversion helpers. |
populateSinkVectorProducerOpsPatterns |
Sinks vector producer operations closer to their first legal use to reduce live ranges. |
populateShuffleVectorFMAOpsPatterns |
Reorders FMA operations using x86 packed conversion operands so odd/even indexed work is grouped. |
populateVectorContractToAMXDotProductPatterns |
Lowers packed vector contractions to AMX tiled dot products. |
populateSpecializedTransposeLoweringPatterns |
Adds AVX2-specialized lowering for selected 2-D vector transpose shapes. |
These APIs are used by passes, transform extensions, or pipelines that want x86-specific lowering behavior.
82.8.2 Transform Dialect Extension
The X86 dialect also provides Transform dialect pattern descriptor operations. They do not perform computation themselves. They tell the Transform dialect to collect specific x86 lowering patterns.
| Transform op | Purpose |
|---|---|
transform.apply_patterns.x86.vector_contract_to_fma |
Collect F32 vector-contract to FMA patterns. |
transform.apply_patterns.x86.vector_contract_to_packed_type_dot_product |
Collect BF16/int8 vector-contract to packed dot-product patterns. |
transform.apply_patterns.x86.vector_contract_bf16_to_fma |
Collect BF16 vector-contract to FMA-emulation patterns. |
transform.apply_patterns.x86.sink_vector_producer_ops |
Collect vector producer sinking patterns. |
transform.apply_patterns.x86.shuffle_vector_fma_ops |
Collect FMA shuffling patterns for x86 packed conversion operands. |
transform.apply_patterns.x86.vector_contract_to_amx_dot_product |
Collect vector-contract to AMX tiled dot-product patterns. |
This matters when you see a transform script controlling exactly which x86 lowering patterns are applied.
82.9 Conversions And Lowering Paths
82.9.1 convert-vector-to-llvm With enable-x86
The convert-vector-to-llvm pass has an enable-x86 option. When enabled, the pass inserts the X86 dialect and uses X86 legalization patterns while lowering vector operations to LLVM.
Typical command shape:
mlir-opt input.mlir -convert-vector-to-llvm="enable-x86"
This is the main path for lowering existing x86 vector intrinsic operations toward LLVM dialect intrinsics.
82.9.2 convert-vector-to-amx
The convert-vector-to-amx pass lowers suitable vector operations into X86 AMX operations.
It depends on affine, arith, memref, scf, vector, and x86 because AMX lowering often needs loops, memory movement, affine maps, vector contractions, and tile operations together.
Typical direction:
vector.contract / vector.transfer_read / vector.transfer_write
-> x86.amx.tile_load
-> x86.amx.tile_mulf or x86.amx.tile_muli
-> x86.amx.tile_store
82.9.3 convert-to-llvm
The X86 dialect registers a ConvertToLLVM interface. The conversion configures the X86 dialect as illegal and replaces X86 intrinsic operations with LLVM dialect forms that map to LLVM x86 intrinsics.
For AMX, !x86.amx.tile<...> lowers to LLVM’s x86 AMX type.
82.9.4 reconcile-unrealized-casts
As with many multi-step conversions, partial lowering may introduce builtin.unrealized_conversion_cast. Use reconcile-unrealized-casts after the relevant conversion passes to clean up type-system glue.
82.10 Example IR
82.10.1 AVX512 BF16 Dot
func.func @avx512bf16_dot_256(
%src: vector<8xf32>,
%a: vector<16xbf16>,
%b: vector<16xbf16>) -> vector<8xf32> {
%0 = x86.avx512.dot %src, %a, %b : vector<16xbf16> -> vector<8xf32>
func.return %0 : vector<8xf32>
}
This represents an AVX512-BF16 dot product, not a portable vector dot product.
82.10.2 AVX512 Mask Compress
func.func @avx512_mask_compress(
%k: vector<16xi1>,
%a: vector<16xf32>) -> vector<16xf32> {
%0 = x86.avx512.mask.compress %k, %a
{constant_src = dense<5.0> : vector<16xf32>} : vector<16xf32>
func.return %0 : vector<16xf32>
}
The mask controls which elements are compressed. The constant source supplies the pass-through elements.
82.10.3 AVX Packed BF16 Conversion
func.func @avx_bf16_even_to_f32(%a: memref<16xbf16>) -> vector<8xf32> {
%0 = x86.avx.cvt.packed.even.indexed_to_f32 %a
: memref<16xbf16> -> vector<8xf32>
func.return %0 : vector<8xf32>
}
This helper is used by BF16 lowering sequences that split packed even and odd elements.
82.10.4 AVX10 Int8 Dot
func.func @avx10_dot_i8(
%w: vector<16xi32>,
%a: vector<64xi8>,
%b: vector<64xi8>) -> vector<16xi32> {
%0 = x86.avx10.dot.i8 %w, %a, %b : vector<64xi8> -> vector<16xi32>
func.return %0 : vector<16xi32>
}
This is a signed int8 dot-product instruction form with int32 accumulation.
82.10.5 AMX Tile Zero And Store
func.func @amx_zero_store(%dst: memref<?x?xbf16>) {
%c0 = arith.constant 0 : index
%tile = x86.amx.tile_zero : !x86.amx.tile<16x16xbf16>
x86.amx.tile_store %dst[%c0, %c0], %tile
: memref<?x?xbf16>, !x86.amx.tile<16x16xbf16>
func.return
}
This example shows that AMX works with tile values, not ordinary vector values.
82.10.6 AMX Integer Tile Multiply
func.func @amx_muli(
%lhs_mem: memref<?x?xi8>,
%rhs_mem: memref<?x?xi8>,
%acc_mem: memref<?x?xi32>) {
%c0 = arith.constant 0 : index
%lhs = x86.amx.tile_load %lhs_mem[%c0, %c0]
: memref<?x?xi8> into !x86.amx.tile<16x64xi8>
%rhs = x86.amx.tile_load %rhs_mem[%c0, %c0]
: memref<?x?xi8> into !x86.amx.tile<16x64xi8>
%acc = x86.amx.tile_load %acc_mem[%c0, %c0]
: memref<?x?xi32> into !x86.amx.tile<16x16xi32>
%out = x86.amx.tile_muli %lhs zext, %rhs zext, %acc
: !x86.amx.tile<16x64xi8>, !x86.amx.tile<16x64xi8>,
!x86.amx.tile<16x16xi32>
x86.amx.tile_store %acc_mem[%c0, %c0], %out
: memref<?x?xi32>, !x86.amx.tile<16x16xi32>
func.return
}
The zext markers say the int8 inputs are interpreted with zero extension. Without them, the operation defaults to signed extension.
82.11 How To Read X86 IR
When reading x86 IR, ask:
- Which x86 feature family is this using?
- Did this come from
vector.contract, a packed conversion sequence, a transpose lowering, or hand-written target IR? - What vector or tile shape is required?
- Which LLVM intrinsic family will it lower to?
- Is the target CPU configured to support the instruction?
Examples:
| If you see | Read it as |
|---|---|
x86.avx512.dot |
AVX512-BF16 dot product selected. |
x86.avx512.mask.compress |
AVX512 mask-register behavior selected. |
x86.avx.cvt.packed.even.indexed_to_f32 |
Packed BF16/F16 memory conversion helper selected. |
x86.avx10.dot.i8 |
AVX10 int8 dot-product form selected. |
x86.amx.tile_load |
Data is moving from memory into AMX tile-register form. |
x86.amx.tile_mulf |
Floating AMX tile dot-product accumulation selected. |
x86.amx.tile_muli |
Integer AMX tile dot-product accumulation selected. |
82.12 Gotchas
x86is not portable. It commits the IR to x86 CPU feature assumptions.- The dialect does not model all x86 instructions. It contains selected operations used by MLIR lowering pipelines.
- AVX, AVX512, AVX10, and AMX are different feature families. Do not assume a CPU that supports one supports the others.
- AMX tile values are not normal vectors. They use
!x86.amx.tile<...>and are intended only for AMX operations. - Many transformations are pattern sets, not standalone command-line passes. They may be applied through Transform dialect scripts or by broader conversion pipelines.
convert-vector-to-llvmonly uses X86-specific lowering whenenable-x86is set.- Final code generation still depends on LLVM target features and the chosen CPU. MLIR can represent an operation that the final target should not use.
- Packed BF16/F16 helpers rely on specific memory layouts. Shape and layout mismatches can prevent the expected rewrite.
82.13 What It Implies In A Compiler Pipeline
Introducing x86 means the pipeline has made a CPU-target-specific decision.
Practical consequences:
- The target should be an x86 CPU with the required feature set.
- Further lowering should include vector-to-LLVM and LLVM conversion paths that know about the X86 dialect.
- If AMX is used, the pipeline must preserve tile shapes and eventually lower
!x86.amx.tileto LLVM AMX form. - High-level matrix code should normally reach
x86throughvector.contractlowering, not by direct frontend emission. - If transform scripts are used, the X86 Transform dialect extension can select the relevant pattern groups explicitly.
For beginners, the main lesson is that x86 is a late-middle-end or backend dialect. It records “we chose this x86 instruction family” while still keeping the program in MLIR before final LLVM emission.
82.14 Source Map
Primary source files in the local LLVM checkout:
| File | What to look for |
|---|---|
mlir/include/mlir/Dialect/X86/X86.td |
Dialect definition, all X86 operations, and AMXTileType. |
mlir/include/mlir/Dialect/X86/X86Interfaces.td |
X86 intrinsic operation interface. |
mlir/include/mlir/Dialect/X86/Transforms.h |
X86 pattern population APIs and LLVM export hooks. |
mlir/include/mlir/Dialect/X86/TransformOps/X86TransformOps.td |
Transform dialect pattern descriptor operations for X86. |
mlir/lib/Dialect/X86/IR/X86Dialect.cpp |
Operation verification, parsing, printing, type implementation, and intrinsic operand lowering helpers. |
mlir/lib/Dialect/X86/Transforms/ |
X86-specific vector contraction, AMX, FMA, transpose, producer sinking, and LLVM export implementations. |
mlir/lib/Conversion/VectorToAMX/VectorToAMX.cpp |
convert-vector-to-amx implementation. |
mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp |
convert-vector-to-llvm integration with enable-x86. |
mlir/test/Dialect/X86/ |
X86 parser, printer, transformation, and LLVM legalization tests. |
mlir/test/Integration/Dialect/Vector/CPU/X86/ |
End-to-end CPU vector integration tests using x86 lowerings. |
mlir/test/Target/LLVMIR/x86.mlir |
LLVM IR translation coverage for x86-related forms. |