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.contract to CPU dot-product instructions.
  • You want AVX512 BF16 instructions such as vdpbf16ps or cvtneps2bf16.
  • 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-x86 is active.

Avoid it when:

  • You want portable CPU IR.
  • A normal vector, arith, math, memref, or linalg operation 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:

  1. Which x86 feature family is this using?
  2. Did this come from vector.contract, a packed conversion sequence, a transpose lowering, or hand-written target IR?
  3. What vector or tile shape is required?
  4. Which LLVM intrinsic family will it lower to?
  5. 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

  • x86 is 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-llvm only uses X86-specific lowering when enable-x86 is 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.tile to LLVM AMX form.
  • High-level matrix code should normally reach x86 through vector.contract lowering, 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.