beaver

MLIR Toolkit in Elixir and Zig.

https://github.com/beaver-lodge/beaver

Science Score: 44.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (8.9%) to scientific vocabulary

Keywords

compiler elixir gpu mlir zig
Last synced: 6 months ago · JSON representation ·

Repository

MLIR Toolkit in Elixir and Zig.

Basic Info
  • Host: GitHub
  • Owner: beaver-lodge
  • License: mit
  • Language: Elixir
  • Default Branch: main
  • Homepage:
  • Size: 1.59 MB
Statistics
  • Stars: 206
  • Watchers: 3
  • Forks: 9
  • Open Issues: 6
  • Releases: 0
Topics
compiler elixir gpu mlir zig
Created almost 4 years ago · Last pushed 6 months ago
Metadata Files
Readme Contributing License Citation

README.md

Run in Livebook

Beaver 🦫

Package Documentation Check Upstream

Boost the almighty blue-silver dragon with some magical elixir! 🧙🧙‍♀️🧙‍♂️

Motivation

In the de-facto way of using MLIR, we need to work with C/C++, TableGen, CMake and Python (in most of cases). Each language or tool here has some functionalities and convenience we want to leverage. There is nothing wrong choosing the most popular and upstream-supported solution, but having alternative ways to build MLIR-based projects is still valuable or at least worth trying.

Elixir could actually be a good fit as a MLIR front end. Elixir has SSA, pattern-matching, pipe-operator. We can use these language features to define MLIR patterns and pass pipeline in a natural and uniformed way. Elixir is strong-typed but not static-typed which makes it a great choice for quickly building prototypes to validate and explore new ideas.

To build a piece of IR in Beaver:

```elixir Func.func somefunc(functiontype: Type.function([], [Type.i(32)])) do region do block () do v0 = Arith.constant(value: Attribute.integer(Type.i(32), 0)) >>> Type.i(32) cond0 = Arith.constant(true) >>> Type.i(1) CF.condbr(cond0, Beaver.Env.block(bb1), {Beaver.Env.block(bb2), [v0]}) >>> [] end

block bb1() do
  v1 = Arith.constant(value: Attribute.integer(Type.i(32), 0)) >>> Type.i(32)
  _add = Arith.addi(v0, v0) >>> Type.i(32)
  CF.br({Beaver.Env.block(bb2), [v1]}) >>> []
end

block bb2(arg >>> Type.i(32)) do
  v2 = Arith.constant(value: Attribute.integer(Type.i(32), 0)) >>> Type.i(32)
  add = Arith.addi(arg, v2) >>> Type.i(32)
  Func.return(add) >>> []
end

end end ```

And a small example to showcase what it is like to define and run a pass in Beaver (with some monad magic):

```elixir defmodule ToyPass do @moduledoc false use Beaver alias MLIR.Dialect.{Func, TOSA} require Func import Beaver.Pattern use MLIR.Pass, on: "builtin.module"

defpat replaceaddop() do a = value() b = value() res = type() {op, _t} = TOSA.add(a, b) >>> {:op, [res]}

rewrite op do
  {r, _} = TOSA.sub(a, b) >>> {:op, [res]}
  replace(op, with: r)
end

end

def run(%MLIR.Operation{} = operation, state) do with 1 <- Beaver.Walker.regions(operation) |> Enum.count(), {:ok, _} <- MLIR.apply(MLIR.Module.fromoperation(operation), [replaceadd_op(benefit: 2)]) do :ok else _ -> raise "unreachable" end end end

use Beaver import MLIR.Transform ctx = MLIR.Context.create() ~m""" module { func.func @tosa_add(%arg0: tensor<1x3xf32>, %arg1: tensor<2x1xf32>) -> tensor<2x3xf32> { %0 = "tosa.add"(%arg0, %arg1) : (tensor<1x3xf32>, tensor<2x1xf32>) -> tensor<2x3xf32> return %0 : tensor<2x3xf32> } } """.(ctx) |> Beaver.Composer.append(ToyPass) |> canonicalize |> Beaver.Composer.run!() ```

Goals

  • Powered by Elixir's composable modularity and meta-programming features, provide a simple, intuitive, and extensible interface for MLIR.
  • Edit-Build-Test-Debug Loop at seconds. Everything in Elixir and Zig are compiled in parallel.
  • Compile Elixir to native/WASM/GPU with the help from MLIR.
  • Revisit and reincarnate symbolic AI in the HW-accelerated world. Erlang/Elixir has a Prolog root!
  • Introduce a new stack to machine learning.
    • Higher-level: Elixir
    • Representation: MLIR
    • Lower-level: Zig

Why is it called Beaver?

Beaver is an umbrella species increase biodiversity. We hope this project could enable other compilers and applications in the way a beaver pond becomes the habitat of many other creatures. Many Elixir projects also use animal names as their package names and it is often about raising awareness of endangered species. To read more about why beavers are important to our planet, check out this National Geographic article.

Quick introduction

Beaver is essentially LLVM/MLIR on Erlang/Elixir. It is kind of interesting to see a crossover of two well established communities and four sub-communities. Here are some brief information about each of them.

For Erlang/Elixir forks

  • Explain this MLIR thing to me in one sentence

MLIR could be regarded as the XML for compilers and an MLIR dialect acts like HTTP standard which gives the generic format real-world semantics and functionalities.

For LLVM/MLIR forks

  • What's so good about this programming language Elixir?

    • It gets compiled to Erlang and runs on BEAM (Erlang's VM). So it has all the fault-tolerance and concurrency features of Erlang.
    • As a Lisp, Elixir has all the good stuff of a Lisp-y language including hygienic macro, protocol-based polymorphism.
    • Elixir has a powerful module system to persist compile-time data and this allows library users to easily adjust runtime behavior.
    • Minimum, very few keywords. Most of the language is built with itself.

Getting started

Installation

The package can be installed by adding beaver to your list of dependencies in mix.exs:

elixir def deps do [ {:beaver, "~> 0.4.0"} ] end

Add this to your .formatter.exs will have the formatter properly transform the macros introduced by beaver elixir import_deps: [:beaver],

Projects built on top of Beaver

How it works?

To implement a MLIR toolkit, we at least need these group of APIs:

  • IR API, to create and update Ops and blocks in the IR
  • Pass API, to create and run passes
  • Pattern API, in which you declare the transformation of a specific structure of Ops

We implement the IR API and Pass API with the help of the MLIR C API. There are both lower level APIs generated from the C headers and higher level APIs that are more idiomatic in Elixir. The Pattern API is implemented with the help from the PDL dialect. We are using the lower level IR APIs to compile your Elixir code to PDL. Another way to look at this is that Elixir/Erlang pattern matching is serving as a frontend alternative to PDLL.

Design principles

Transformation over builder

It is very common to use builder pattern to construct IR, especially in an OO programming language like C++/Python. One problem this approach has is that the compiler code looks very different from the code it is generating. Because Erlang/Elixir is SSA by its nature, in Beaver a MLIR Op's creation is very declarative and its container will transform it with the correct contextual information. By doing this, we could:

  • Keep compiler code's structure as close as possible to the generated code, with less noise and more readability.
  • Allow dialects of different targets and semantic to introduce different DSL. For instance, CPU, SIMD, GPU could all have their specialized transformation tailored for their own unique concepts.

One example:

```elixir module do v2 = Arith.constant(1) >>> ~t end

module/1 is a macro, it will transformed the SSA v2= Arith.constant.. to:

v2 = %Beaver.SSA{} |> Beaver.SSA.putarguments(value: ~a{1}) |> Beaver.SSA.putblock(Beaver.Env.block()) |> Beaver.SSA.putctx(Beaver.Env.context()) |> Beaver.SSA.putresults(~t) |> Arith.constant() ```

Also, using the declarative way to construct IR, proper dominance and operand reference is formed naturally.

```elixir SomeDialect.some_op do region do block entry() do x = Arith.constant(1) >>> ~t y = Arith.constant(1) >>> ~t end end region do block entry() do z = Arith.addi(x, y) >>> ~t end end end

will be transformed to:

SomeDialect.some_op( fn -> do region = Beaver.Env.region() # first region created block = Beaver.Env.block() x = Arith.constant(...) y = Arith.constant(...)

region = Beaver.Env.region() # second region created
block = Beaver.Env.block()
z = Arith.addi([x, y, ...]) # x and y dominate z

end ) ```

Beaver DSL as higher level AST for MLIR

There should be a 1:1 mapping between Beaver SSA DSL to MLIR SSA. It is possible to do a roundtrip parsing MLIR text format and dump it to Beaver DSL which is Elixir AST essentially. This makes it possible to easily debug a piece of IR in a more programmable and readable way.

In Beaver, working with MLIR should be in one format, no matter it is generating, transforming, debugging.

High level API in Erlang/Elixir idiom

When possible, lower level C APIs should be wrapped as Elixir struct with support to common Elixir protocols. For instance the iteration over one MLIR operation's operands, results, successors, attributes, regions should be implemented in Elixir's Enumerable protocol. This enable the possibility to use the rich collection of functions in Elixir standard libraries and Hex packages.

Is Beaver a compiler or binding to LLVM/MLIR?

Elixir is a programming language built for all purposes. There are multiple sub-ecosystems in the general Erlang/Elixir ecosystem. Each sub-ecosystem appears distinct/unrelated to each other, but they actually complement each other in the real world production. To name a few:

Each of these sub-ecosystems starts with a seed project/library. Beaver should evolve to become a sub-ecosystem for compilers built with Elixir and MLIR.

MLIR context management

When calling higher-level APIs, it is ideal not to have MLIR context passing around everywhere. If no MLIR context provided, an attribute and type getter should return an anonymous function with MLIR context as argument. In Erlang, all values are copied, so it is very safe to pass around these anonymous functions. When creating an operation, these functions will be called with the MLIR context in an operation state. With this approach we achieve both succinctness and modularity, not having a global MLIR context. Usually a function accepting a MLIR context to create an operation or type is called a "creator" in Beaver.

Development

Please refer to Beaver's contributing guide

Owner

  • Name: Beaver
  • Login: beaver-lodge
  • Kind: organization

Build ML with LLVM/MLIR/Elixir/Zig

Citation (CITATION.cff)

cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Cai"
  given-names: "Shenghang"
  orcid: "https://orcid.org/0009-0003-7397-1203"
title: "Beaver: MLIR Toolkit in Elixir and Zig"
version: 0.4.0
date-released: 2024-07-27
url: "https://github.com/beaver-lodge/beaver"

GitHub Events

Total
  • Issues event: 1
  • Watch event: 42
  • Delete event: 46
  • Issue comment event: 48
  • Push event: 221
  • Pull request review event: 130
  • Pull request review comment event: 146
  • Pull request event: 82
  • Fork event: 2
  • Create event: 44
Last Year
  • Issues event: 1
  • Watch event: 42
  • Delete event: 46
  • Issue comment event: 48
  • Push event: 221
  • Pull request review event: 130
  • Pull request review comment event: 146
  • Pull request event: 82
  • Fork event: 2
  • Create event: 44

Committers

Last synced: 7 months ago

All Time
  • Total Commits: 374
  • Total Committers: 2
  • Avg Commits per committer: 187.0
  • Development Distribution Score (DDS): 0.003
Past Year
  • Commits: 75
  • Committers: 1
  • Avg Commits per committer: 75.0
  • Development Distribution Score (DDS): 0.0
Top Committers
Name Email Commits
Shenghang Tsai j****r@g****m 373
Kian-Meng Ang k****g@g****m 1

Issues and Pull Requests

Last synced: 6 months ago

All Time
  • Total issues: 17
  • Total pull requests: 313
  • Average time to close issues: about 1 month
  • Average time to close pull requests: 3 days
  • Total issue authors: 8
  • Total pull request authors: 4
  • Average comments per issue: 1.65
  • Average comments per pull request: 0.31
  • Merged pull requests: 291
  • Bot issues: 0
  • Bot pull requests: 2
Past Year
  • Issues: 1
  • Pull requests: 81
  • Average time to close issues: N/A
  • Average time to close pull requests: about 23 hours
  • Issue authors: 1
  • Pull request authors: 2
  • Average comments per issue: 1.0
  • Average comments per pull request: 1.0
  • Merged pull requests: 75
  • Bot issues: 0
  • Bot pull requests: 2
Top Authors
Issue Authors
  • jackalcooper (9)
  • glyh (2)
  • kianmeng (1)
  • nofe1248 (1)
  • jumerckx (1)
  • MaPePeR (1)
  • guitcastro (1)
  • JackWolfard (1)
Pull Request Authors
  • jackalcooper (408)
  • coderabbitai[bot] (2)
  • polvalente (1)
  • kianmeng (1)
Top Labels
Issue Labels
help wanted (1)
Pull Request Labels
use-latest-mlir (3)

Packages

  • Total packages: 1
  • Total downloads:
    • hex 5,542 total
  • Total dependent packages: 1
  • Total dependent repositories: 0
  • Total versions: 39
  • Total maintainers: 1
hex.pm: beaver

Beaver, a MLIR Toolkit in Elixir

  • Versions: 39
  • Dependent Packages: 1
  • Dependent Repositories: 0
  • Downloads: 5,542 Total
Rankings
Dependent packages count: 10.5%
Stargazers count: 12.3%
Average: 26.4%
Forks count: 32.7%
Dependent repos count: 34.5%
Downloads: 42.1%
Maintainers (1)
Last synced: 6 months ago

Dependencies

.github/workflows/elixir.yml actions
  • actions/cache v3 composite
  • actions/checkout v3 composite
  • actions/upload-artifact v3 composite
  • erlef/setup-beam v1 composite
  • goto-bus-stop/setup-zig v1 composite
  • seanmiddleditch/gha-setup-ninja master composite
.github/workflows/livebook-image.yml actions
  • actions/checkout v2 composite
  • docker/build-push-action v3 composite
  • docker/login-action v2 composite
  • docker/setup-buildx-action v2 composite
  • pierotofy/set-swap-space v1.0 composite
.github/workflows/release.yml actions
  • actions/cache v3 composite
  • actions/checkout v3 composite
  • erlef/setup-beam 988e02bfe678367a02564f65ca2e37726dc0268f composite
  • goto-bus-stop/setup-zig v1 composite
  • seanmiddleditch/gha-setup-ninja master composite
  • softprops/action-gh-release v1 composite
Dockerfile docker
  • jackalcooper/beaver-livebook latest build
mix.exs hex
  • credo ~> 1.6
  • doctor ~> 0.21.0
  • ex_doc >= 0.0.0
  • gradient HEAD
  • kinda ~> 0.2.0
  • llvm_config ~> 0.1.0
  • mix_test_watch ~> 1.0