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.