Charms.Defm.Expander (charms v0.1.4)
Expander is a module of functions to compile Elixir AST to MLIR.
In MLIR, the process of creating IR from source or AST is usually called "import". While in our case, we are following Elixir's convention to call it "expansion". While we are not just expanding Elixir's AST tree, in the end one Elixir function definition defm
will be compiled to one MLIR func.func
.
Working with Elixir's parallel compiler
Charms will try to work with Elixir's parallel compiler in the most well-integrated way. This allows us to compile multiple modules at the same time, which can speed up the compilation process. Another benefit is that all defm
functions can also be executed at compile time, just like vanilla Elixir functions defined by def
. Behind the scenes, func.func
generated from defm
will also be used as reference to infer and check types of a remote function calls when compiling another module.
To take deep dive, Elixir has an official blog post about parallel compiler.
Cyclic dependency
Charms will disallow cyclic dependency. If module A calls module B, module B cannot call module A. This is to prevent infinite recursion and streamline compile-time features. Although this is technically possible as long as function calls and function definitions are following the same signature (like the separate compilation of C/C++ files and linkage), we still make this trade-off for simplicity.
Compile an AST without enough type information
Being an dynamic language, it is possible for Elixir to have an AST that is valid in Elixir but not in MLIR. For example, Elixir allows to define a serial of nested function calls without any return type, like a(b(c()))
. In Elixir everything is expression so a function call is assumed to always return a value while in MLIR it is possible to have a function call that does not return anything (equivalent to a void function in C). In this case, Charms will generate a ub.poison
operation with result type none
. If the result value of created ub.poison
op will never be used, nothing will happen. If used, it will raise an error in later verification or passes. This is meant to allow Elixir code to work with the AST with interest only on Elixir semantic keep going without interruption as much as possible, and limit the error information to the type level, instead of leaking it to the syntax level.
Return type convention
MLIR allows multiple values as a function return, while Elixir only allows one. To keep the convention, internally Charms will always use a list to store function's return type. If the function has only one return, it will be a list with one element. If the function has no return, it will be an empty list. This should streamline the transformation and pattern matching on the function signature.
Summary
Functions
Decomposes a call into the call part and return type.
Expand an Elixir AST into MLIR.
Expand an AST into MLIR.
Functions
Decomposes a call into the call part and return type.
Expand an Elixir AST into MLIR.
Note that this function will not do any resource management, like destroying MLIR context or module. So this function should be used with care, and mainly for testing or debugging purpose.
Expand an AST into MLIR.