--- id: wiki-2026-0508-도달-가능성-분석-reachability-analysis title: 도달 가능성 분석 (Reachability Analysis) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Reachability Analysis, Control Flow Reachability, Dead Code Detection] duplicate_of: none source_trust_level: A confidence_score: 0.95 verification_status: applied tags: [static-analysis, compiler, control-flow, dead-code, type-narrowing] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: compiler-domain framework: TypeScript/Java/LLVM --- # 도달 가능성 분석 (Reachability Analysis) ## 매 한 줄 > **"매 program point 가 매 실행될 수 있는지 매 control flow graph 위에서 결정"**. compiler 의 매 dead code elimination, 매 unreachable warning, 매 exhaustive switch check, 매 type narrowing (TS) 의 공통 토대. 매 forward / backward CFG traversal + 매 lattice 위 fixed-point. ## 매 핵심 ### 매 정의 - **CFG (Control Flow Graph)**: 매 basic block 노드 + 매 control edge. - **Reachable**: 매 entry 에서 매 path 가 존재. - **Forward analysis**: 매 entry → 매 모든 노드. - **Backward analysis**: 매 exit ← 매 모든 노드 (liveness 등). ### 매 lattice - 매 `{Reachable, Unreachable}` — 매 simple Boolean lattice. - 매 transfer function: 매 block end 에서 매 successor 로 propagate. - 매 join: 매 OR (predecessor 중 하나라도 reachable 이면 reachable). ### 매 응용 1. Dead code elimination (compile-time). 2. Unreachable code warning (TS, Java, Rust). 3. Exhaustive switch check (TS `never`, Rust `!`). 4. Liveness analysis (register allocation). 5. Type narrowing (TypeScript control-flow analysis). ## 💻 패턴 ### CFG construction (simple imperative) ```ts type BB = { id: string; stmts: Stmt[]; succ: string[] }; function buildCFG(fn: FunctionAST): Map { const blocks = new Map(); let cur: BB = { id: "entry", stmts: [], succ: [] }; blocks.set("entry", cur); for (const s of fn.body) { if (s.kind === "if") { const thenId = freshId(), elseId = freshId(), joinId = freshId(); cur.succ = [thenId, elseId]; const thenB: BB = { id: thenId, stmts: s.then, succ: [joinId] }; const elseB: BB = { id: elseId, stmts: s.else ?? [], succ: [joinId] }; const joinB: BB = { id: joinId, stmts: [], succ: [] }; blocks.set(thenId, thenB); blocks.set(elseId, elseB); blocks.set(joinId, joinB); cur = joinB; } else if (s.kind === "return") { cur.stmts.push(s); cur.succ = []; cur = { id: freshId(), stmts: [], succ: [] }; blocks.set(cur.id, cur); } else { cur.stmts.push(s); } } return blocks; } ``` ### Forward reachability (worklist) ```ts function reachableSet(cfg: Map): Set { const reachable = new Set(["entry"]); const worklist: string[] = ["entry"]; while (worklist.length > 0) { const id = worklist.pop()!; const bb = cfg.get(id)!; for (const s of bb.succ) { if (!reachable.has(s)) { reachable.add(s); worklist.push(s); } } } return reachable; } function unreachableBlocks(cfg: Map): string[] { const r = reachableSet(cfg); return [...cfg.keys()].filter(id => !r.has(id)); } ``` ### TS exhaustive switch (`never` reach) ```ts type Shape = | { kind: "circle"; r: number } | { kind: "square"; side: number } | { kind: "triangle"; base: number; height: number }; function area(s: Shape): number { switch (s.kind) { case "circle": return Math.PI * s.r ** 2; case "square": return s.side ** 2; case "triangle": return s.base * s.height / 2; default: const _exhaustive: never = s; throw new Error(`unreachable: ${_exhaustive}`); } } ``` ### TS control-flow type narrowing (reachability + narrowing) ```ts function process(input: string | null) { if (input === null) { return "no input"; } return input.toUpperCase(); } function withReturn(x: number) { if (x < 0) { return -1; } return x * 2; } ``` ### Dead code after return ```ts function foo() { return 42; console.log("unreachable"); // 매 TS warning ts(7027) } ``` ### Java unreachable statement (JLS 14.21) ```java void example() { while (true) { if (cond()) break; } System.out.println("ok"); while (true) {} System.out.println("dead"); // 매 compile error } ``` ### LLVM dead-code elimination (conceptual) ```llvm ; 매 before define i32 @foo() { entry: ret i32 42 %dead = add i32 1, 2 ret i32 %dead } ; 매 after DCE pass define i32 @foo() { entry: ret i32 42 } ``` ### Liveness (backward reachability) ```ts function liveness(cfg: Map): Map> { const liveOut = new Map>(); for (const id of cfg.keys()) liveOut.set(id, new Set()); let changed = true; while (changed) { changed = false; for (const [id, bb] of cfg) { const live = new Set(); for (const s of bb.succ) { for (const v of liveOut.get(s) ?? []) live.add(v); } const def = defsOf(bb), use = usesOf(bb); const liveIn = new Set([...use, ...[...live].filter(v => !def.has(v))]); if (!eqSet(liveIn, liveOut.get(id)!)) { liveOut.set(id, liveIn); changed = true; } } } return liveOut; } ``` ## 매 결정 기준 | 목적 | 알고리즘 | |---|---| | Dead code elimination | Forward reachability + DCE pass | | Exhaustive switch | Type narrowing → `never` 도달 | | Unreachable warning | Forward reach + report unreachable BB | | Liveness (register alloc) | Backward dataflow | | Static deadlock / null check | Symbolic + reach | **기본값**: Forward worklist algorithm + Boolean lattice. 매 type narrowing 매 data-flow + reach 결합. ## 🔗 Graph - 부모: [[Static Analysis]] - 응용: [[Type Narrowing]] ## 🤖 LLM 활용 **언제**: 매 compiler 설계, 매 linter 작성, 매 IDE feature, 매 type system 분석. **언제 X**: 매 application code 작성 (매 compiler 가 자동 처리). ## ❌ 안티패턴 - **Path-sensitive 가정 X**: 매 conditional reach 무시 — 매 false positive 폭증. - **Loop fixed-point 누락**: 매 1-pass 만 — 매 cycle 미처리. - **Exception edge 무시**: 매 throw 후 reach 잘못 — 매 catch 매 edge. - **Switch fallthrough 모델링 X**: 매 C/Java fall-through case 매 단일 successor. ## 🧪 검증 / 중복 - Verified (Aho/Lam/Sethi/Ullman "Compilers" Ch. 9, Java Language Specification §14.21, TypeScript Compiler API). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — CFG + 8 reachability patterns |