C Mastery / UB-Based Optimization and Compiler Assumptions
Part 6 — Debugging and Optimization

UB-Based Optimization and Compiler Assumptions

This chapter explains how compilers exploit undefined behavior to optimize — and why UB is therefore far more dangerous than "the hardware does something weird."

Why This Matters

When you write UB, the compiler is allowed to assume it never happens. It can then delete your bounds checks, reorder your code, or remove entire branches. This is why UB programs behave *differently under optimization*, not just "incorrectly."

Prerequisites

Core Concept

A compiler optimizes under the assumption that the program is defined. If the program contains UB, the compiler's reasoning is unsound, and the generated code may do anything — including things impossible in the source.

The classic example:

int table[4];

int lookup(int i)
{
    if (i < 0 || i >= 4) return -1;
    return table[i];   /* the compiler may assume i is in bounds */
}

Because table[i] would be UB if i were out of bounds, the compiler may assume the check i < 0 || i >= 4 is always false and delete it. The "check" provides no protection against UB.

Examples

Signed overflow enabling elimination

int f(int x)
{
    if (x > 0 && x + 1 > 0)   /* compiler assumes x+1 doesn't overflow */
        return 1;
    return 0;
}

The compiler may assume x + 1 never overflows, so x > 0 implies x + 1 > 0 and the whole function returns 1.

Uninitialized read enabling branch removal

int f(void)
{
    int x;
    if (x) return 1;   /* reading x is UB, so the branch is unreachable */
    return 0;
}

The compiler may assume the if (x) is unreachable and return 0 always.

How It Works

Optimization passes (range analysis, dead-code elimination, alias analysis) use the assumption "no UB" to prune possibilities. A signed overflow or out-of-bounds access becomes a license to assume a path is impossible.

Variations

Sanitizers vs. UB-based optimization

UBSan inserts checks that make the UB *detectable*, but it changes the code. Without it, the optimizer exploits the UB.

The "as-if" rule

The compiler may do anything as long as observable behavior is preserved *for defined programs*. UB is outside the contract.

Common Mistakes

help.

control flow.

Undefined Behavior

examples illustrate specific cases.

Portability

why UB programs vary across compilers and flags.

Under the Hood

Range analysis propagates invariants; if an operation is UB outside a range, the range is assumed. Dead-code elimination removes paths that would require UB. Alias analysis (strict aliasing) assumes non-aliasing and reorders.

Practical Usage

Exercises

1. Write the table[i] example and compare assembly at -O2 vs. -O0. 2. Write a signed-overflow example and observe the optimized result. 3. Use UBSan to catch the UB in each example.

Deep Challenge

Construct a program where an out-of-bounds access causes the optimizer to remove a security-relevant check, and explain the exact reasoning. Then show the correct, defined version and prove the check survives optimization.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.opt.ub07