← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Control Flow Graph

Compiler Advanced
debt(d5/e3/b3/t5)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches' (d5). CFG-related issues like unreachable code, missing return paths, and dead code are caught by specialist tools listed in detection_hints.tools: phpstan, psalm, llvm, gcc, eslint. These aren't default linters but dedicated static analysers that build and traverse CFGs internally.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix suggests using early returns and running static analysers - this typically involves refactoring functions to have clearer exit points, which may touch multiple places within a single function/file but doesn't require cross-cutting architectural changes. Fixing unreachable code or adding missing returns is localised work.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). Understanding CFGs is primarily relevant when building/extending static analysers or debugging analyser output. The applies_to shows broad context (cli, web, library) but the concept itself is structural knowledge that aids interpretation rather than an architectural choice that shapes the codebase. It imposes cognitive load on developers debugging complex analyser reports but doesn't create ongoing maintenance burden across the system.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap' (t5). The misconception clearly states developers assume CFG nodes are single statements/lines when they're actually basic blocks. This is a documented gotcha that developers eventually learn when working with analysers. Additionally, common_mistakes includes confusing CFG with AST and not recognising that complex conditionals create exponential paths - these are moderate traps that trip up developers unfamiliar with compiler theory but are well-documented.

About DEBT scoring →

Also Known As

CFG flow graph program flow graph

TL;DR

A directed graph representation of a program's execution paths where nodes are basic blocks and edges represent possible control transfers like branches, jumps, and loops.

Explanation

A control flow graph (CFG) is a fundamental data structure in compiler design and static analysis that models all possible execution paths through a program or function. Each node in the graph represents a basic block - a straight-line sequence of instructions with no branches except at the end and no branch targets except at the beginning. Edges connect basic blocks to show how control can flow: a conditional branch creates two outgoing edges, a loop creates a back edge to an earlier block, and a function call may create edges to exception handlers. The CFG has a single entry node (the function's first block) and one or more exit nodes (returns, throws). Compilers construct CFGs after parsing to enable dataflow analysis: reaching definitions, live variable analysis, available expressions, and constant propagation all operate by propagating facts along CFG edges until a fixed point is reached. Optimisations like dead code elimination, loop-invariant code motion, and common subexpression elimination rely on the CFG structure. Static analysers like PHPStan, Psalm, and ESLint plugins build CFGs to detect unreachable code, guarantee return statements exist on all paths, and verify type narrowing persists through conditionals. The CFG also determines basic block ordering for register allocation and instruction scheduling. Cyclomatic complexity - a common code quality metric - is computed directly from the CFG as E - N + 2P, where E is edges, N is nodes, and P is connected components. Understanding CFGs helps developers reason about why analysers report certain warnings and how restructuring conditionals affects tool output.

Diagram

flowchart TD
    ENTRY[Entry Block] --> A[Block A: x = input]
    A --> B{Block B: x > 0?}
    B -->|true| C[Block C: y = x * 2]
    B -->|false| D[Block D: y = 0]
    C --> E[Block E: return y]
    D --> E
    E --> EXIT[Exit Block]
style ENTRY fill:#1f6feb,color:#fff
style EXIT fill:#238636,color:#fff
style B fill:#d29922,color:#fff

Common Misconception

A CFG node represents a single statement or line of code - in reality, each node is a basic block containing multiple sequential instructions; the graph structure captures control transfer points, not individual statements.

Why It Matters

CFGs enable compilers to perform dataflow analysis and optimisation passes, and static analysers use them to detect unreachable code, verify all paths return values, and track type narrowing through conditionals - understanding CFGs helps developers interpret analyser output and write analysis-friendly code.

Common Mistakes

  • Confusing CFG with AST - the AST represents syntactic structure while the CFG represents execution flow; they serve different purposes.
  • Assuming CFG edges only represent explicit goto/jump statements - conditionals, loops, exceptions, and fall-through all create edges.
  • Forgetting that unreachable code after return/throw creates disconnected nodes the analyser flags as dead code.
  • Not recognising that complex nested conditionals create exponentially more CFG paths, increasing analysis time and cyclomatic complexity.
  • Believing CFGs only matter for compiled languages - interpreted languages like PHP and JavaScript use CFGs in their static analysers and JIT compilers.

Avoid When

  • Explaining code structure to beginners who need to understand syntax before execution flow.
  • Analysing trivial functions with linear flow where CFG complexity adds no insight.
  • Working in contexts where the runtime provides no CFG-based tooling and manual analysis is impractical.

When To Use

  • Building or extending static analysers that need to track dataflow through branches and loops.
  • Debugging why an analyser reports unreachable code or missing return paths.
  • Measuring and reducing cyclomatic complexity in complex functions.
  • Understanding compiler optimisations that rely on basic block structure.

Code Examples

✗ Vulnerable
// PHP: complex control flow creates tangled CFG
function process($value) {
    if ($value > 0) {
        if ($value > 100) {
            return 'large';
        } else {
            $result = 'medium';
        }
    } else {
        if ($value < -100) {
            return 'very negative';
        }
        // Missing return path! PHPStan detects via CFG analysis
        $result = 'small negative';
    }
    echo 'processing'; // Unreachable after some paths
    return $result;    // $result may be undefined on some paths
}
✓ Fixed
// PHP: clear control flow with explicit paths
function process($value): string {
    if ($value > 100) {
        return 'large';           // Exit edge from this block
    }
    if ($value > 0) {
        return 'medium';          // Exit edge from this block
    }
    if ($value < -100) {
        return 'very negative';   // Exit edge from this block
    }
    return 'small negative';      // Final exit - all paths covered
}
// CFG has clear structure: 4 condition blocks, 4 exit edges, no dead code

Added 7 Jul 2026
Views 17
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 2 pings W 2 pings T 2 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W
No pings yet today
Brave Search 1
Google 2 ChatGPT 1 PetalBot 1 Applebot 1 Ahrefs 1 Meta AI 1 Brave Search 1
crawler 8
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
Compiler general A compiler is a program that translates your entire source code into machine-readable instructions before the program runs, catching errors upfront and producing an executable file.

Understanding compilation explains why some errors appear before your code runs, why compiled programs are often faster, and how tools like TypeScript catch bugs at build time rather than in production.

💡 When you see an error before your code runs, that's the compiler helping you—read the line number and fix the syntax first.

Ask Codex about Compiler →
DEV INTEL Tools & Severity
🔵 Info ⚙ Fix effort: Medium
⚡ Quick Fix
Use early returns to create clear CFG exit points and run static analysers that report unreachable code and missing return paths
📦 Applies To
any cli web library
🔗 Prerequisites
🔍 Detection Hints
Functions with deeply nested conditionals, multiple return points, or code after unconditional return/throw statements
Auto-detectable: ✓ Yes phpstan psalm llvm gcc eslint
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Low ✗ Manual fix Fix: Medium Context: Function


✓ schema.org compliant