239 lines
6.9 KiB
Markdown
239 lines
6.9 KiB
Markdown
---
|
|
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<string, BB> {
|
|
const blocks = new Map<string, BB>();
|
|
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<string, BB>): Set<string> {
|
|
const reachable = new Set<string>(["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, BB>): 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<string, BB>): Map<string, Set<string>> {
|
|
const liveOut = new Map<string, Set<string>>();
|
|
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<string>();
|
|
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]] · [[Compiler]]
|
|
- 변형: [[Liveness Analysis]] · [[Constant Propagation]] · [[Available Expressions]]
|
|
- 응용: [[Dead Code Elimination]] · [[Register Allocation]] · [[Type Narrowing]]
|
|
- Adjacent: [[Control Flow Graph]] · [[Data Flow Analysis]] · [[Abstract Interpretation]]
|
|
|
|
## 🤖 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 |
|