Part 3 · Reading compiler output
Compiler Explorer as an instrument
The best tool in this field, and the ways it will mislead you if you mistake it for your machine.
What this lesson is trying to break (3)
- Godbolt shows what my binary does.
- The source-to-assembly colour mapping is exact.
- A function compiled alone behaves the same as one compiled in a program.
You have been pressing buttons for twelve lessons. This one is about what those buttons actually measure — because in this field the instrument misleads more often than the subject does.
What it does
Compiler Explorer runs a real compiler on your source and shows you the assembly,
with the noise filtered out. That is all, and it is enormous: before it existed
this workflow meant local toolchains, cross-compilers, and a lot of objdump.
The filters this site enables by default, and why:
| Filter | Effect |
|---|---|
| Intel syntax | mov dst, src. AT&T reverses the operands — see Setup |
| Directives | Hides .cfi_*, .section, .size — metadata, not instructions |
| Comments | Hides compiler annotations |
| Demangle | Turns _ZN7example1aE back into example::a |
| Labels | Removes unreferenced labels |
Turning directives back on is occasionally worth it: .p2align before a loop
tells you the compiler cared about alignment, and .cfi_* entries are the unwind
tables that make profiling work without frame pointers
(2.3).
Three ways it will mislead you
1. It is not your machine. The default target is a generic x86-64 or ARM64.
Your M1 has instructions and performance characteristics that a generic ARM64
target does not assume. Compile with -mcpu=apple-m1 and the output can change
materially. Any claim of the form “this compiles to N instructions” is
target-specific.
2. It compiles one file with nothing around it. No link-time optimisation, no other translation units, no real callers. In a real program your function may be inlined into three call sites and specialised differently in each. Godbolt shows you the standalone version, which may not exist in your binary at all.
3. Isolation changes the answer. This is the one that produces wrong conclusions most often.
static int f(int x) {
return x * 2;
}A static function, defined and never called, on its own.
Nothing. Empty output.
static means internal to this translation unit. Nothing calls it. So it is
unreachable, and the compiler deletes it — correctly.
This is the first thing that confuses everyone using Godbolt, and the fix is to
make the function externally visible: drop static (in C/C++), or use
pub extern "C" in Rust, or reference it from something that is exported.
The deeper point is that you must ask for the thing you want to see. A
function compiled in isolation and forced to be ABI-conforming is a specific
artefact, useful for learning the ABI and the arithmetic. It is not what your
program contains. When you want the latter, disassemble the actual binary — which
is why Setup lists objdump alongside Godbolt with a column for what
each one lies about.
Using it honestly
A short protocol that avoids most self-deception:
Make the function externally visible. Otherwise you may be reading a deletion.
Pass the flags you actually ship. -O2 versus -O3 versus -Os produce
genuinely different code, and so does -march=. Comparing your source against
someone’s blog post at different flags is comparing nothing.
Change one thing at a time. This is the whole method of this course. Two sources, one differing token, and diff the output — Compiler Explorer has a diff view for exactly this.
Treat the colour mapping as a hint. The source↔assembly highlighting is
best-effort attribution. After inlining and reordering, an instruction may belong
to several source lines or to none. In -O0 it is nearly exact; at -O2 it is
suggestive.
Never conclude anything about speed from the listing alone. You can conclude what was emitted. Speed requires Part 4 and, ultimately, measurement.
A colleague sends you a Godbolt link showing that their new version compiles to six fewer instructions, and proposes merging it as a performance improvement.
Write your reply. Name at least two things the link does not establish, and say what you would want to see instead. Do not be dismissive — they may well be right.
What you should now be able to say
- Godbolt shows what a compiler emitted for one file, for a generic target.
- It is not your machine, has no LTO or surrounding program, and shows a standalone function that may not exist in your binary.
staticand unused means deleted — make things externally visible.- The colour mapping is a hint, not ground truth, in optimised code.
- The listing establishes what was emitted, never how fast it is.