FForge
← Labsxl-04Source to assembly●●●○

The same bug at -O0 vs -O2: UB deleted to unreachable

cross_level/opt_levels.c
evidence to leave with
-O0 crashes cleanly; -O2 main is a single unreachable; a local becomes <optimized out>
cross-level lesson 4

the SAME bug at -O0 vs -O2. ------------------------------------------------------------ One null-store bug, compiled two ways, to show WHY you debug at -O0 -g: -O0 : every local is a real stack slot. gdb shows `scaled = 49`, the line table is 1:1 with the source, single-stepping is sane. -O2 : the optimizer folds `scaled` into a constant, may inline `crash_after_math` into main, and reorders code. gdb prints `<optimized out>` (or "No symbol") and the reported line jumps around. Compare the IR and asm at both levels: clang -g -O0 -S -emit-llvm opt_levels.c -o opt_O0.ll clang -g -O2 -S -emit-llvm opt_levels.c -o opt_O2.ll clang -g -O0 -S opt_levels.c -o opt_O0.s clang -g -O2 -S opt_levels.c -o opt_O2.s and watch gdb lose `scaled`: clang -g -O0 opt_levels.c -o opt_O0 ; clang -g -O2 opt_levels.c -o opt_O2 gdb -q -batch -ex 'break crash_after_math' -ex run -ex 'print scaled' ./opt_O0 gdb -q -batch -ex 'break crash_after_math' -ex run -ex 'print scaled' ./opt_O2 (REAL output in cross_level/README.md.) ============================================================

read the source · cross_level/opt_levels.c
int crash_after_math(int *p, int a, int b) { int scaled = a * b + 7; /* real local at -O0; constant-folded away at -O2 */ *p = scaled; /* faults when p == NULL */ return scaled; } int main(void) { int *p = 0; return crash_after_math(p, 6, 7); /* scaled = 6*7+7 = 49 */ }
◇
Finish the lab: write the evidence note

A lab is done when you can say what it showed on your machine — not when you have read it.