Beaver (beaver v0.2.16)

This module contains top level functions and macros for Beaver DSL for MLIR.

Here are some of the examples of most common forms of Beaver DSL:

  • single result
    res = TOSA.add(a, b) >>> res_t
  • multiple results
    [res1, res2] = TOSA.add(a, b) >>> [res_t, res_t]
    [res1, res2] = TOSA.add(a, b) >>> res_t_list
    res_list = TOSA.add(a, b) >>> res_t_list
  • infer results
    TOSA.add(a, b) >>> :infer
  • with op
    {op, res} = TOSA.add(a, b) >>> {:op, res_t}
  • with no result
    TOSA.add(a, b) >>> []

Link to this section Summary

Functions

This is a macro where Beaver's MLIR DSL expressions get transformed to MLIR API calls. This transformation will works on any expression of this form, so it is also possible to call any other function/macro rather than an Op creation function. There is one operator >>> for typing the result of the SSA or an argument of a block. It kind of works like the :: in specs and types of Elixir.

Link to this section Functions

Link to this function

call >>> results

Link to this macro

block(call, list)

(macro)
Link to this macro

mlir(list)

(macro)

This is a macro where Beaver's MLIR DSL expressions get transformed to MLIR API calls. This transformation will works on any expression of this form, so it is also possible to call any other function/macro rather than an Op creation function. There is one operator >>> for typing the result of the SSA or an argument of a block. It kind of works like the :: in specs and types of Elixir.

how-it-works-under-the-hood

How it works under the hood

mlir do
  [res0, res1] = TestDialect.some_op(operand0, operand1, attr0: ~a{11 : i32}) >>> ~t{f32}
end
# will be transform to:
[res0, res1] =
  %DSL.SSA{}
  |> DSL.SSA.arguments([operand0, operand1, attr0: ~a{11 : i32}, attr1: ~a{11 : i32}])
  |> DSL.SSA.results([~t{f32}])
  |> TestDialect.some_op()

The SSA form will return:

  • For op with multiple result: the results of this op in a list.
  • For op with one single result: the result
  • For op with no result: the op itself (for instance, module, func, and terminators)

If there is no returns, add a [] to make the transformation effective:

TestDialect.some_op(operand0) >>> []

To defer the creation of a terminator in case its successor block has not been created. You can pass an atom of the name in the block's call form.

CF.cond_br(cond0, :bb1, {:bb2, [v0]})  >>> []

To create region, call the op with a do block. The block macro works like the function definition in Elixir, and in the do block of block macro you can reference an argument by name. One caveat is that if it is a Op with region, it requires all arguments to be passed in one list to make it to call the macro version of the Op creation function.

TestDialect.op_with_region [operand0, attr0: ~a{1}i32] do
  region do
    block(arg >>> ~t{f32}) do
      TestDialect.some_op(arg) >>> ~t{f32}
    end
  end
end >>> ~t{f32}
Link to this macro

mlir(opts, list)

(macro)
Link to this macro

region(list)

(macro)