Beaver (beaver v0.4.0)
This module contains top level functions and macros for Beaver DSL for MLIR.
use Beaver
will import and alias the essential modules and functions for Beaver'DSL for MLIR. It also imports the sigils used in the DSL.
Basic usage
To create an MLIR operation and insert it to a block, you can use the mlir
macro. Here is an example of creating a constant operation:
iex> use Beaver
iex> {ctx, blk} = {MLIR.Context.create(), MLIR.Block.create()}
iex> alias Beaver.MLIR.Dialect.Arith
iex> const = mlir ctx: ctx, blk: blk do
iex> Arith.constant(value: ~a{64: i32}) >>> ~t{i32}
iex> end
iex> const |> MLIR.Value.owner!() |> MLIR.verify!()
iex> MLIR.Block.destroy(blk)
iex> MLIR.Context.destroy(ctx)
Naming conventions
There are some concepts in Elixir and MLIR shared the same name, so it is encouraged to use the following naming conventions for variables:
- use
ctx
for MLIR context - use
blk
for MLIR block
Lazy creation of MLIR entities
In Beaver, various functions and sigils might return a function with a signature like MLIR.Context.t() -> MLIR.Type.t()
, if the context is not provided. When a creator gets passed to a SSA expression, it will be called with the context to create the entity or later the operation. Deferring the creation of the entity until context available is intended to keep the DSL code clean and succinct. For more information, see the docs of module Beaver.Deferred
.
Summary
Functions
Create an MLIR SSA expression with the given arguments and results.
Create an anonymous block.
Create a named block.
Transform the given DSL block but without specifying the context and block.
Macro where Beaver's MLIR DSL expressions get transformed to MLIR API calls.
Create MLIR region. Calling the macro within a do block will create an operation with the region.
Functions
Create an MLIR SSA expression with the given arguments and results.
The right hand of operator >>>
is used to typing the result of the SSA or an argument of a block. It kind of works like the ::
in specs and types of Elixir.
The return of SSA expression
By default SSA expression will return the MLIR values or the operation created.
- 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)
Different result declarations
There are several ways to declare the result of an SSA expression. The most common cases are single result and multi-results.
Single result
res = Foo.bar(a, b) >>> res_t
Multiple results
[res1, res2] = Foo.bar(a, b) >>> [res_t, res_t] [res1, res2] = Foo.bar(a, b) >>> res_t_list res_list = Foo.bar(a, b) >>> res_t_list
Zero result
Foo.bar(a, b) >>> []
Enable type inference,
Foo.bar(a, b) >>> :infer
To return the op together with the result
{op, res} = Foo.bar(a, b) >>> {:op, types}
Create an anonymous block.
The block can be used to call CAPI.
Create a named 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.
It should follow Elixir's lexical scoping rules and can be referenced by Beaver.Env.block/1
MLIR doesn't have named block
Note that the idea of named block is Beaver's concept. MLIR doesn't have it.
Transform the given DSL block but without specifying the context and block.
This is useful when you want to generate partials of quoted code and doesn't want to pass around the context and block.
Macro where Beaver's MLIR DSL expressions get transformed to MLIR API calls.
This transformation will works on any expression of the >>>
form, so it is also possible to call any other vanilla Elixir function/macro.
How it works under the hood
[res0, res1] = TestDialect.some_op(operand0, operand1, attr0: ~a{11 : i32}) >>> ~t{f32}
will be transform to:
[res0, res1] =
%SSA{}
|> SSA.arguments([operand0, operand1, attr0: ~a{11 : i32}, attr1: ~a{11 : i32}])
|> SSA.results([~t{f32}])
|> TestDialect.some_op()
and TestDialect.some_op()
is an Elixir function to actually create the MLIR operation. So it is possible to replace the left hand of the >>>
operator with any Elixir function.
Create MLIR region. Calling the macro within a do block will create an operation with the region.
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}