This is the first issue of a series of blog posts about some Linux coding tricks I have collected in the last few years.
Folklore says that compilers are among the most complex computer programs written today. They incorporate many optimization algorithms, inline functions and fold constant expressions; all without changing output, correctness or side effects of the code. If you think about it, the work gcc
, llvm
and other compilers do is really amazing and mostly works just great.
Sometimes, however, you want to know exactly what a compiler does with your C/C++ code. Most straight-forward questions can be answered using a debugger. However, if you want to verify whether the compiler really applies those optimizations to your program, that your intuition expects it to do, then a debugger is usually not useful, because optimized programs can look very different from the original. Some example questions are:
- Is a local integer variable stored in a register and how long does it exist?
- Does the compiler use special instructions for a simple copy loop?
- Are special conditional instructions used for an
if
or switch
statement? - Is a specific function inlined or called each time?
These questions can be answered definitely by investigating the compiler's output. On the Net, there are multiple "online compilers," which can visualize the assembler output of popular compilers for small pieces of code: see the "GCC Explorer" or "C/C++ to Assembly v2". However, for inspecting parts of a larger project, these tools are unusable, because the interesting pieces are embedded in much larger source files.
Luckily, gcc
does not output binary machine code directly. Instead, it internally writes assembler code, which then is translated by as
into binary machine code (actually, gcc
creates more intermediate structures). This internal assembler code can be outputted to a file, with some annotation to make it easier to read.