How Clang Compiles a Function
blog.regehr.org
blog.regehr.org
> we’re not going to look at any non-trivial C++ features
Becomes: we're only going to look at trivial C++ features.
Which is then recognized as a no-op and replaced with the null sentence ;-)
Nice article though.
Compilers work by taking a (computer-)linguistic analysis of the source code text, a syntax tree, converting it into an intermediate representation (IR) which is then progressively lowered to the raw machine code form by a series of transformations.
A connected graph of basic blocks (BBs) is generally how such IRs work. Basic blocks have a formal structure involving them being connected to other basic blocks only at their tops and bottoms, and not being multiply-connected internally. A perhaps simplistic way to visualize them is as the polygons on a flowchart.
So the article, which assumes that you know this background, is showing what kinds of basic blocks in Clang's intermediate representation result, before any lowering and optimizations, from the syntax tree. It's skipping over the part where a syntax tree is constructed from the source code shown.
In many compilers, you don't get to see the intermediate representation. In compilers like OpenWatcom C/C++ and GCC you can always go and look at the source code to see what the IR data structures are. But the IR is in memory (or internal temporary files) and not accessible outwith the compiler programs themselves. Clang's design involves a publicly specified intermediate representation that has documented serialization formats for writing to file and then accessing with other, potentially third party, tools.
You can also write compiler plugins in GCC. Not as fun as clang/llvm, but manageable.
I was wondering. When is this graph actually built? is it before creating the IR or after? The way I see it, you can generate IR without worrying about control flow graph. Then you can build the CFG from the IR in order to perform common optimization or register allocation. Is that correct?
In effect, the IR is exactly a control-flow graph represented in adjacency list form, so it's not possible to construct the IR without constructing the control flow graph. You could theoretically write the IR in textual form, but that's definitely not the common case, and you generally need to construct the CFG anyways to properly emit the IR (particularly since adding the phi nodes for SSA requires knowing the predecessors of every basic block).
[1] https://en.wikipedia.org/wiki/Static_single_assignment_form
https://www.cs.princeton.edu/~appel/modern/
There are other classical books, but this is one of the best ones, while containing quite a few modern stuff like GC implementations and SSA algorithms code code generation.
There are some well known complaints, but on my opinion none of them really hinders the subject of learning about compilers.
If you really want to learn more, you have to check out the well written LLVM docs. If you prefer a hands-on approach, follow the kaleidoscope tutorial.
The article is written to be pretty approachable, it doesn't assume any deep knowledge of compilers.
Which specific concepts are you unfamiliar with?