MLIR Lowering

Lowering Pipeline

The pipeline starts from the canonical IRModule data shape, which has two stable serialisations: mic@1 textual IR and mic@3 binary IR (RFC 0021). Both round-trip equivalently; lowering is invariant to which serialisation was on disk.

  1. MIND IRModulemic@1 text / mic@3 binary (RFC 0021)
  2. mind dialecthigh-level tensor ops
  3. linalg dialectloop-based tensor ops
  4. scf dialectstructured control flow
  5. arith + memref dialects
  6. llvm dialect
  7. LLVM IR → Machine Code
Top to bottom: from high-level tensor operations down to machine code. Each rung is a lower level of abstraction.

MIND Dialect

The custom MIND dialect represents high-level tensor operations:

// MIND dialect example
%result = mind.matmul %a, %b : tensor<2x3xf32>, tensor<3x4xf32> -> tensor<2x4xf32>
%activated = mind.relu %result : tensor<2x4xf32>
%reduced = mind.sum %activated {axis = 1} : tensor<2x4xf32> -> tensor<2xf32>

Optimization Passes

  • Operator Fusion

    Combine sequential ops to reduce memory traffic
  • Layout Optimization

    Select optimal memory layouts (row-major, column-major)
  • Tiling

    Break large operations into cache-friendly tiles
  • Vectorization

    Use SIMD instructions where available
  • Buffer Placement

    Optimize memory allocation and reuse

GPU Lowering Path

For GPU targets, additional dialects are used. GPU code generation ships in the commercial mind-runtime; deterministic GPU execution is roadmap (see Backend Targets):

GPU codegen: commercial mind-runtimeDeterministic GPU execution: roadmap

  1. mind dialect
  2. linalg on tensors
  3. linalg on buffers
  4. gpu dialectkernel outline
  5. nvvm dialect (NVIDIA) or spirv dialect (Vulkan/OpenCL)
  6. PTX / SPIR-V

Example Transformation

Before fusion

Three ops, three memory passes

%0 = mind.relu %input : tensor<1024xf32>
%1 = mind.mul %0, %scale : tensor<1024xf32>
%2 = mind.add %1, %bias : tensor<1024xf32>

After fusion (conceptual example, actual fused operations may vary)

One op, one memory pass

// Hypothetical fused operation for illustration
%0 = mind.fused_relu_scale_bias %input, %scale, %bias : tensor<1024xf32>
// Single memory pass instead of three

Inspecting IR

Use compiler flags to inspect intermediate representations:

# Dump MIND dialect
mindc --emit=mind-dialect model.mind

# Dump linalg dialect
mindc --emit=linalg model.mind

# Dump LLVM IR
mindc --emit=llvm model.mind

# Dump assembly
mindc --emit=asm model.mind

On mindc 0.10.2

The released mindc 0.10.2 does not accept the per-dialect --emit= flags above (it answers unexpected argument '--emit' found). Its shipped MLIR flag is --emit-mlir, which prints MLIR text for the canonical IR (requires the mlir-lowering feature). Captured on examples/hello_tensor.mind:

captured: mindc 0.10.2
$ mindc hello_tensor.mind --emit-mlir
module {
  func.func @main() -> f64 {
    %0 = arith.constant 3.0 : f64
    %1 = arith.mulf %0, %0 : f64
    %2 = arith.constant 2.0 : f64
    %3 = arith.mulf %2, %0 : f64
    %4 = arith.addf %1, %3 : f64
    %5 = arith.constant 1.0 : f64
    %6 = arith.addf %4, %5 : f64
    return %6 : f64
  }
}

Learn More

See the full MLIR lowering specification at mind-spec/mlir-lowering.md.