Beaver (beaver v0.2.19)
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
call >>> results
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}