Getting each stage of Elixir's compilation all the way to the BEAM bytecode

So a quick answer here (more later) is the erlang compiler has 2 main entry points: :compile.file which compiles a text file; and :compile.forms which takes a list of pre-parsed forms. As you have seen you can specify how “far” in the compilation you want to go, whether to pre-expanded macros and parse transforms, core erlang, kernel erlang or just the BEAM instructions (without generating a .beam file). Try doing to_kernel, dkern and dlife for some more fun.

You can also specify what type the input should be, a little anyway, so for example the option :from_core means that the input, whether file of forms, is core erlang. This is what I use in the LFE compiler where I generated core erlang forms (there AST anyway) which I then compile with :compile.forms(forms, [:from_core|options]). I found this easier than generating erlang AST.

Almost all optimisation in the compiler is done on core erlang so I don’t “lose” anything by entering there.

The c("file.erl", 'S') compiles the file only to the BEAM instructions and prints them to a .S file while :beam_diasm:file/1 looks at the beam file and disassembles it. There is also a way which I can’t remember now where you can disassemble the actual code installed in the BEAM itself. This will be slightly different from what the other two give you as there is quite a lot of optimisation done at load time.

Most of the time you don’t need to know this except for the expansion of macros and parse transforms, but it is fun. About 12 min into my talk mentioned there is a slide on the passes of the compiler. It is a bit hard to see. IIRC there is another talk I gave on about the same thing where it is easier to see the slides.

13 Likes