5.3.11Build Systems & Toolchain

GCC - Clang flags — optimization (-O0 to -O3, -Os), warnings (-Wall, -Wextra), sanitizers

2,191 words10 min readdifficulty · medium1 backlinks

WHY do flags exist at all?

The flag is the student's lever on the invariant: same observable program behaviour, different machine code. Optimization is only legal because the C/C++ standard says the compiler may do anything as long as it preserves the "as-if" observable behaviour of a correct program. If your program has undefined behaviour (UB), that contract is void — and -O2 may "miscompile" it. That is the deep reason sanitizers exist.


Optimization levels: -O0 to -O3, -Os

Deriving why -O0 is slow — from first principles

Consider int s=0; for(int i=0;i<n;i++) s+=a[i];

Figure — GCC - Clang flags — optimization (-O0 to -O3, -Os), warnings (-Wall, -Wextra), sanitizers

Warnings: -Wall and -Wextra


Sanitizers: -fsanitize=...


Common Mistakes (Steel-manned)


Putting it together (the 80/20 recipes)


Flashcards

What is the default optimization level if no -O flag is given?
-O0 (no optimization).
Which optimization level is the recommended production default?
-O2.
Why can -O3 be slower than -O2?
Aggressive inlining/vectorization grows code and may thrash the instruction cache; the optimization "bets" don't always pay off.
What does -Os optimize for?
Binary size (like -O2 minus size-bloating passes).
Does -Wall enable all warnings?
No — it's a common-bug subset; use -Wall -Wextra (plus others) for fuller coverage.
What does -Werror do?
Promotes warnings to errors so the build fails.
Why does a program "work at -O0 but crash at -O2"?
Almost always undefined behaviour; the optimizer legally assumes UB never occurs. Detect with sanitizers.
How does AddressSanitizer detect overflows?
Shadow memory + poisoned redzones around allocations; every load/store checks the shadow byte.
What runtime cost does ASan add roughly?
~2× slower, ~3× more memory.
Which two sanitizers form the cheap default bug net?
-fsanitize=address and -fsanitize=undefined.
Why compile sanitizers at -O1 with -g rather than -O3?
-O3 may optimize away the UB you want to catch; -g and -O1 keep reports readable yet fast.
Must -fsanitize=address be passed at link time too?
Yes — it pulls in a runtime library.
What does -Og mean?
Optimize for debugging: faster than -O0 while keeping debug info usable.
Which legal principle lets the optimizer change machine code freely?
The "as-if" rule: preserve observable behaviour of a correct program only.

Recall Feynman: explain to a 12-year-old

Imagine you wrote instructions for building a Lego castle. -O0 follows your steps exactly, even silly ones, so it's easy to check but slow. -O2/-O3 is a clever builder who skips wasted moves to finish fast — but only because your instructions made sense. Warnings are a friend reading over your shoulder saying "hey, you forgot a piece." Sanitizers are a referee who paints the floor with invisible "no-step" paint around your Lego pile; the moment you step outside, an alarm rings and shows exactly where. The clever builder trusts you never step out of bounds — so if you do, things break weirdly. The referee catches you doing it.


Connections

  • Compilation Pipeline (preprocess-compile-assemble-link)
  • Undefined Behaviour in C/C++
  • Makefiles and CFLAGS/LDFLAGS
  • CPU Caches and Instruction Cache
  • SIMD and Auto-Vectorization
  • Debugging with GDB and -g
  • Continuous Integration pipelines

Concept Map

permits

voids

conflicts with

expose choice of

include

include

ladder

-O0 keeps vars in

causes

-O2 production

may blow

so -O3 not always

-Wall, -Wextra find

detect at runtime

C/C++ as-if rule

Optimization

Undefined Behaviour

Debuggability

Compiler flags

Warnings

Sanitizers

-O0 to -O3, -Os

Memory not registers

Slow binary, easy debug

Inlining, vectorization

Instruction cache

Faster

Static bug hints

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, compiler flags basically knobs hain jo batate hain ki tumhara C/C++ code machine code me kaise convert hoga. Sabse important teen family hain. Pehli optimization (-O0 se -O3, aur -Os): -O0 me compiler kuch optimize nahi karta — code seedha source jaisa rehta hai, debug karna easy, lekin binary slow. -O2 production ka default hai, achhi speed. -O3 aur aggressive hai par hamesha tez nahi — kabhi code itna bada ho jaata hai ki i-cache thrash ho jata hai aur ulta slow. -Os size ke liye optimize karta hai. Rule yaad rakho: measure karo, assume mat karo.

Doosri family warnings hai. -Wall -Wextra lagao — yeh free me bugs pakad leta hai bina program chalaye. Jaise unsigned n; n - 10 < 0 kabhi true nahi hoga kyunki unsigned wrap ho jata hai — -Wall ye flag kar deta hai. CI me -Werror daalo taaki warning aate hi build fail ho. Yeh sabse zyada "bug per second" dene wala tool hai, isliye 80/20 me top priority.

Teesri family sanitizers hai — yeh runtime detectors hain. -fsanitize=address (ASan) buffer overflow aur use-after-free pakadta hai shadow memory aur poisoned redzones laga ke; jaise hi tum array ke bahar likhte ho, alarm baj jaata hai with exact line. -fsanitize=undefined signed overflow, null deref waghera pakadta hai. Yaad rakho: sanitizer flag ko compile aur link dono me dena padta hai, aur -O1 -g ke saath chalao taaki report readable rahe aur UB optimize na ho jaye.

Sabse bada lesson: agar program -O0 pe chalta hai par -O2 pe crash karta hai, toh 99% baar tumhare code me undefined behaviour hai — compiler galat nahi hai, optimizer ko UB-free code assume karne ka legal haq hai. Solution: -fsanitize=address,undefined chala ke asli bug dhoondo. Dev me -Og -g -Wall -Wextra -fsanitize=address,undefined, release me -O2 -DNDEBUG — bas yahi do recipes 80% kaam kar dete hain.

Go deeper — visual, from zero

Test yourself — Build Systems & Toolchain

Connections