hst337
How to get an AST and Macro.Env of a function by name and arity?
Summary
Is there any way to write a function which can be invoked in compile time and which can return an AST and Macro.Env of given function.
Example
We have a module
defmodule MMM do
def fff(x), do: x + 1
end
And we can execute some function in compile time which will work like
iex> Macro.get_ast(MMM, :fff, 1)
{
{:def, [context: Elixir, import: Kernel],
[
{:f, [context: Elixir], [{:x, [], Elixir}]},
[do: {:+, [context: Elixir, import: Kernel], [{:x, [], Elixir}, 1]}]
]},
%Macro.Env{}
}
PS
I know there is no such function, but how can something like this be implemented? What problems should I expect while implementing this function?
Most Liked
ityonemo
this is possible using Elixir compiler tracer hooks. I am pretty sure it broadcasts the AST and __ENV__ of a function being compiled.
https://hexdocs.pm/elixir/Code.html#module-compilation-tracers
Worst case scenario, collect em’ all, serialize them using :erlang.term_to_binary, and then stash everything into some file in /priv
OvermindDL1
In the erlang side things like -compile({inline,24}). are specified to inline module-local functions with a given weight or less, 24 is the default weight, so most ‘shortish’ functions will get inlined directly and always. It can be overridden though with a different weight, like 1000 if you want to inline almost everything, or you can force inline specific functions by giving it a list.
Do note though, inlining larger can help with some optimizations, but overall it usually doesn’t and it’s best to leave it at default as the size overhead doesn’t help the speed at that point, and calling module-local functions require no indirected call so their only cost is a stack frame at that point (if that’s even needed).
OvermindDL1
Such a function like that doesn’t exist because the AST is long long gone at that point. You could always make your own macro, say defmodulex or so and just use it instead of defmodule and have it build a new hidden function called __ASTS__/1 where you pass it an option of what to return and it can return the AST for whatever you asked for. Basically compile the compile and add in that function that just returns the AST of the module that was passed in?
Better question though, ‘why’ do you want it?
hst337
I am trying to investigate ability to perform some simple automatic function inlining optimizations in compile time
I thought about defmodulex-style approach, but this looks ugly and can’t be acheived with functions in dependencies
I thought that there might be some way to override :elixir_module module (unload it before compilation and load modified version which stores AST per module), or create some compiler tracer, which will try to store all functions in some ets or process (but I don’t know if tracers are capable of doing this)
OvermindDL1
You actually can, but this is definitely more work… ^.^;
First task would be to fork that file, then make the changes you want, then load your ‘loader’ module first and replace the built-in module with that by forcing it to load via a call in the Code module (forget the name but it’s not hard to figure out).
Traces can’t really do that no, well, maybe if you hook the file loading but then you’d have to do a whole lot more work, so not recommended… ^.^;
EDIT1: Are you sure that inlining functions is really that necessary with the new OTP-24 JIT?
EDIT2: Also, what if a module gets hot-replaced, how will you update your inlined versions?







