How are loops that modify a state implemented in SSA form?

2026-09-03

Stack Overflow: View Question

Tags: compiler-construction, computer-science, ssa, advice

Score: 0 | Views: 207

The asker wrote a simple strlen loop and is puzzled by what they see in Clang's LLVM IR: a mysterious phi instruction. They understand that Static Single Assignment (SSA) means every variable is assigned exactly once — but their loop clearly reassigns len and str on every iteration. How can both things be true?

Why it's interesting: SSA is the backbone of every modern optimizing compiler (LLVM, GCC's GIMPLE, V8's TurboFan, Cranelift), and phi nodes are the single cleverest idea in the whole formalism. They're also the point where most people bounce off SSA on first contact, because the name is borrowed from mathematics but the meaning is completely operational.

The core idea: a phi node isn't a computation — it's a selector keyed on control flow. It says "the value of this SSA name depends on which predecessor block we came from." For the strlen loop, the IR conceptually looks like:

entry:
  br label %loop

loop:
  %len.i   = phi i32  [ 0,        %entry ], [ %len.next, %loop ]
  %str.i   = phi i8*  [ %str.arg, %entry ], [ %str.next, %loop ]
  %c       = load i8, i8* %str.i
  %str.next= getelementptr i8, i8* %str.i, i32 1
  %len.next= add i32 %len.i, 1
  %cond    = icmp ne i8 %c, 0
  br i1 %cond, label %loop, label %exit

exit:
  ret i32 %len.i

Each SSA name (%len.i, %len.next, etc.) is assigned exactly once — the invariant holds. The mutation-over-time that the C source expresses is now encoded as a graph edge: control flow from entry supplies the initial value, control flow from the backedge supplies the updated value.

Direction toward a solution:

Gotchas: phi nodes must be the first instructions in a block, must have exactly one entry per predecessor, and reason purely about the incoming edge, not the block. And crucially: memory is not in SSA in LLVM — only registers/values are. Loads and stores model the mutable heap. That's why the C pointer-walk survives as load instructions rather than as SSA renaming of the bytes themselves.

The challenge: Understanding that a phi node isn't arithmetic — it's a control-flow-aware selector that lets SSA preserve its "assigned once" invariant even in the presence of loops.

All newsletters