dialect: linalg+memref+func

difficulty: easy

nl: Write a function that performs a matrix multiplication of two 2-D f32 memrefs with dynamic shapes and writes the result into a pre-allocated output memref.

mlir:
module {
  func.func @mm(%A: memref<?x?xf32>, %B: memref<?x?xf32>, %C: memref<?x?xf32>) {
    linalg.matmul ins(%A, %B : memref<?x?xf32>, memref<?x?xf32>) outs(%C : memref<?x?xf32>)
    return
  }
}

notes: canonical linalg.matmul


nl: Write a function that performs a 2x2 matrix multiplication of f32 matrices. All inputs are statically-shaped memrefs.

mlir:
module {
  func.func @mm22(%A: memref<2x2xf32>, %B: memref<2x2xf32>, %C: memref<2x2xf32>) {
    linalg.matmul ins(%A, %B : memref<2x2xf32>, memref<2x2xf32>) outs(%C : memref<2x2xf32>)
    return
  }
}

notes: static-shape matmul


difficulty: medium

nl: Write a function that multiplies a 4x8xf32 matrix by an 8x2xf32 matrix, writing to a 4x2xf32 result memref.

mlir:
module {
  func.func @mm482(%A: memref<4x8xf32>, %B: memref<8x2xf32>, %C: memref<4x2xf32>) {
    linalg.matmul ins(%A, %B : memref<4x8xf32>, memref<8x2xf32>) outs(%C : memref<4x2xf32>)
    return
  }
}

notes: rectangular MxK * KxN


nl: Write a function that performs matmul on two f64 2-D memrefs into an f64 output memref.

mlir:
module {
  func.func @mmf64(%A: memref<?x?xf64>, %B: memref<?x?xf64>, %C: memref<?x?xf64>) {
    linalg.matmul ins(%A, %B : memref<?x?xf64>, memref<?x?xf64>) outs(%C : memref<?x?xf64>)
    return
  }
}

notes: f64 matmul


dialect: linalg+memref+arith+func

difficulty: hard

nl: Write a function that zeroes an f32 output memref using linalg.fill, then computes matmul of two input memrefs into it.

mlir:
module {
  func.func @fm(%A: memref<?x?xf32>, %B: memref<?x?xf32>, %C: memref<?x?xf32>) {
    %z = arith.constant 0.0 : f32
    linalg.fill ins(%z : f32) outs(%C : memref<?x?xf32>)
    linalg.matmul ins(%A, %B : memref<?x?xf32>, memref<?x?xf32>) outs(%C : memref<?x?xf32>)
    return
  }
}

notes: zero-then-matmul idiom


nl: Write a function that fills a 1-D f32 memref with zeros.

mlir:
module {
  func.func @f0(%m: memref<?xf32>) {
    %z = arith.constant 0.0 : f32
    linalg.fill ins(%z : f32) outs(%m : memref<?xf32>)
    return
  }
}

notes: zero-fill 1D


nl: Write a function that fills a 2-D f32 memref with a given f32 value passed as a parameter.

mlir:
module {
  func.func @fp(%v: f32, %m: memref<?x?xf32>) {
    linalg.fill ins(%v : f32) outs(%m : memref<?x?xf32>)
    return
  }
}

notes: fill with parameter value


nl: Write a function that fills a 1-D i32 memref with the integer constant 7.

mlir:
module {
  func.func @f7(%m: memref<?xi32>) {
    %c7 = arith.constant 7 : i32
    linalg.fill ins(%c7 : i32) outs(%m : memref<?xi32>)
    return
  }
}

notes: integer-typed fill


nl: Write a function that copies a 1-D f32 memref into another 1-D f32 memref of the same shape.

mlir:
module {
  func.func @c1(%s: memref<?xf32>, %d: memref<?xf32>) {
    linalg.copy ins(%s : memref<?xf32>) outs(%d : memref<?xf32>)
    return
  }
}

notes: 1D copy


nl: Write a function that copies a 4x4xf32 static memref into another 4x4xf32 memref.

mlir:
module {
  func.func @c2(%s: memref<4x4xf32>, %d: memref<4x4xf32>) {
    linalg.copy ins(%s : memref<4x4xf32>) outs(%d : memref<4x4xf32>)
    return
  }
}

notes: 2D static copy


nl: Write a function that copies a 1-D i32 memref into another 1-D i32 memref of the same shape.

mlir:
module {
  func.func @ci(%s: memref<?xi32>, %d: memref<?xi32>) {
    linalg.copy ins(%s : memref<?xi32>) outs(%d : memref<?xi32>)
    return
  }
}

notes: i32 copy


nl: Write a function that transposes a 2-D f32 memref into another 2-D f32 memref using permutation [1, 0].

mlir:
module {
  func.func @t2(%s: memref<?x?xf32>, %d: memref<?x?xf32>) {
    linalg.transpose ins(%s : memref<?x?xf32>) outs(%d : memref<?x?xf32>) permutation = [1, 0]
    return
  }
}

notes: 2D transpose


nl: Write a function that transposes a 3-D f32 memref using permutation [0, 2, 1] (swap the last two dimensions).

mlir:
module {
  func.func @t3(%s: memref<?x?x?xf32>, %d: memref<?x?x?xf32>) {
    linalg.transpose ins(%s : memref<?x?x?xf32>) outs(%d : memref<?x?x?xf32>) permutation = [0, 2, 1]
    return
  }
}

notes: 3D transpose


nl: Write a function that transposes a 3x5xf32 memref to a 5x3xf32 memref using permutation [1, 0].

mlir:
module {
  func.func @ts(%s: memref<3x5xf32>, %d: memref<5x3xf32>) {
    linalg.transpose ins(%s : memref<3x5xf32>) outs(%d : memref<5x3xf32>) permutation = [1, 0]
    return
  }
}

notes: static-shape transpose


nl: Write a function that broadcasts a 1-D f32 memref to a 2-D f32 memref along the 0th dimension.

mlir:
module {
  func.func @b1(%s: memref<?xf32>, %d: memref<?x?xf32>) {
    linalg.broadcast ins(%s : memref<?xf32>) outs(%d : memref<?x?xf32>) dimensions = [0]
    return
  }
}

notes: 1D to 2D broadcast, dim 0


nl: Write a function that broadcasts a 1-D f32 memref to a 2-D f32 memref along the 1st dimension.

mlir:
module {
  func.func @b2(%s: memref<?xf32>, %d: memref<?x?xf32>) {
    linalg.broadcast ins(%s : memref<?xf32>) outs(%d : memref<?x?xf32>) dimensions = [1]
    return
  }
}

notes: 1D to 2D broadcast, dim 1


nl: Write a function that performs a matrix-vector multiplication: input is a 2-D f32 memref and a 1-D f32 memref; output is a 1-D f32 memref.

mlir:
module {
  func.func @mv(%A: memref<?x?xf32>, %x: memref<?xf32>, %y: memref<?xf32>) {
    linalg.matvec ins(%A, %x : memref<?x?xf32>, memref<?xf32>) outs(%y : memref<?xf32>)
    return
  }
}

notes: canonical matvec


nl: Write a function that multiplies a 4x8xf32 matrix by an 8xf32 vector, writing to a 4xf32 result memref.

mlir:
module {
  func.func @mvs(%A: memref<4x8xf32>, %x: memref<8xf32>, %y: memref<4xf32>) {
    linalg.matvec ins(%A, %x : memref<4x8xf32>, memref<8xf32>) outs(%y : memref<4xf32>)
    return
  }
}

notes: static matvec 4x8 by 8


nl: Write a function that performs a matvec on f64 inputs.

mlir:
module {
  func.func @mvd(%A: memref<?x?xf64>, %x: memref<?xf64>, %y: memref<?xf64>) {
    linalg.matvec ins(%A, %x : memref<?x?xf64>, memref<?xf64>) outs(%y : memref<?xf64>)
    return
  }
}

notes: f64 matvec


nl: Write a function that adds two 1-D f32 memrefs element-wise into a third output memref.

mlir:
module {
  func.func @ae(%a: memref<?xf32>, %b: memref<?xf32>, %c: memref<?xf32>) {
    linalg.add ins(%a, %b : memref<?xf32>, memref<?xf32>) outs(%c : memref<?xf32>)
    return
  }
}

notes: elemwise add


nl: Write a function that subtracts two 1-D f32 memrefs element-wise.

mlir:
module {
  func.func @se(%a: memref<?xf32>, %b: memref<?xf32>, %c: memref<?xf32>) {
    linalg.sub ins(%a, %b : memref<?xf32>, memref<?xf32>) outs(%c : memref<?xf32>)
    return
  }
}

notes: elemwise sub


nl: Write a function that multiplies two 2-D f32 memrefs element-wise (Hadamard product) into an output memref.

mlir:
module {
  func.func @me(%a: memref<?x?xf32>, %b: memref<?x?xf32>, %c: memref<?x?xf32>) {
    linalg.mul ins(%a, %b : memref<?x?xf32>, memref<?x?xf32>) outs(%c : memref<?x?xf32>)
    return
  }
}

notes: Hadamard product


nl: Write a function that divides two 1-D f32 memrefs element-wise.

mlir:
module {
  func.func @de(%a: memref<?xf32>, %b: memref<?xf32>, %c: memref<?xf32>) {
    linalg.div ins(%a, %b : memref<?xf32>, memref<?xf32>) outs(%c : memref<?xf32>)
    return
  }
}

notes: elemwise div


nl: Write a function that applies the elementwise exponential to a 1-D f32 memref, writing the result into another memref.

mlir:
module {
  func.func @ee(%x: memref<?xf32>, %y: memref<?xf32>) {
    linalg.exp ins(%x : memref<?xf32>) outs(%y : memref<?xf32>)
    return
  }
}

notes: elemwise exp


nl: Write a function that applies elementwise exp to a 2-D f32 memref, writing into an output memref of the same shape.

mlir:
module {
  func.func @e2(%x: memref<?x?xf32>, %y: memref<?x?xf32>) {
    linalg.exp ins(%x : memref<?x?xf32>) outs(%y : memref<?x?xf32>)
    return
  }
}

notes: 2D exp


nl: Write a function that applies the elementwise absolute value to a 1-D f32 memref, writing the result into another memref.

mlir:
module {
  func.func @ae(%x: memref<?xf32>, %y: memref<?xf32>) {
    linalg.abs ins(%x : memref<?xf32>) outs(%y : memref<?xf32>)
    return
  }
}

notes: elemwise abs


nl: Write a function that applies elementwise abs to a 2-D f32 memref.

mlir:
module {
  func.func @a2(%x: memref<?x?xf32>, %y: memref<?x?xf32>) {
    linalg.abs ins(%x : memref<?x?xf32>) outs(%y : memref<?x?xf32>)
    return
  }
}

notes: 2D abs


nl: Write a function that fills a 1-D f32 memref with 1.0 and then copies its contents into a second memref.

mlir:
module {
  func.func @fc(%m: memref<?xf32>, %d: memref<?xf32>) {
    %one = arith.constant 1.0 : f32
    linalg.fill ins(%one : f32) outs(%m : memref<?xf32>)
    linalg.copy ins(%m : memref<?xf32>) outs(%d : memref<?xf32>)
    return
  }
}

notes: fill-then-copy chain


nl: Write a function that adds two 1-D f32 memrefs element-wise into a temporary buffer, then applies exp to the buffer into the final output.

mlir:
module {
  func.func @ax(%a: memref<?xf32>, %b: memref<?xf32>, %t: memref<?xf32>, %y: memref<?xf32>) {
    linalg.add ins(%a, %b : memref<?xf32>, memref<?xf32>) outs(%t : memref<?xf32>)
    linalg.exp ins(%t : memref<?xf32>) outs(%y : memref<?xf32>)
    return
  }
}

notes: add-then-exp chain


nl: Write a function that transposes A (2-D f32) into a temp memref and then performs matmul of transposed A and B.

mlir:
module {
  func.func @tm(%A: memref<?x?xf32>, %At: memref<?x?xf32>, %B: memref<?x?xf32>, %C: memref<?x?xf32>) {
    linalg.transpose ins(%A : memref<?x?xf32>) outs(%At : memref<?x?xf32>) permutation = [1, 0]
    linalg.matmul ins(%At, %B : memref<?x?xf32>, memref<?x?xf32>) outs(%C : memref<?x?xf32>)
    return
  }
}

notes: transpose-then-matmul