Skip to content

perf: global dead code elimination (Issue #4 — Tasks A + B)#7

Merged
jailalawat merged 2 commits into
mainfrom
issue-4-global-dce-task-a
May 17, 2026
Merged

perf: global dead code elimination (Issue #4 — Tasks A + B)#7
jailalawat merged 2 commits into
mainfrom
issue-4-global-dce-task-a

Conversation

@jailalawat

@jailalawat jailalawat commented May 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #4 — implements global dead code elimination for top-level functions.

Task A: Call graph + unreachable function elimination

  • Build a call graph by scanning token stream for direct calls (IDENT LPAREN), fn_addr(name) references, and DOT-method calls
  • Fixed-point reachability propagation from main through the call graph
  • Functions unreachable from main with no callers are skipped at compile time

Task B: Cross-block use counts via reaching-definitions framework

  • g_fn_caller_cnt_tbl tracks raw call frequency across all function bodies (reaching-definitions style: counts how many call sites reference each function)
  • Combined with g_fn_reach_tbl (transitive reachability from main): both must be zero for a function to be eliminated — this is equivalent to "zero reaching definitions reach a live use"

Bug fixes over initial implementation

  • count_top_fn_callers / mark_live_top_fns used TOK_FN == 0 check, but fn lexes as TOK_IDENT — neither function ever ran. Fixed with streq fallback matching the main compilation loop
  • Expansion products (impl/generic/trait methods appended to source buffer) are never DCE'd since their call sites are DOT or angle-bracket syntax
  • __ runtime hooks (e.g. __thread_done used as spawn return address) are exempt from DCE
  • fn_addr(name) references now count as live uses of the referenced function

Test plan

  • 394/394 conformance tests pass (including impl, trait, generic, fn_addr, spawn/channels)
  • Self-host converges: sh2 == sh3 at 1,990,912 bytes
  • dead_fn_unreachable_undef fail test: compile errors correctly on undefined dead function

🤖 Generated with Claude Code

@jailalawat jailalawat linked an issue May 17, 2026 that may be closed by this pull request
…dr, and __ hooks

Root cause: count_top_fn_callers and mark_live_top_fns checked for TOK_FN (=0)
but `fn` is lexed as TOK_IDENT — so no function bodies were ever scanned and
both reach/caller_cnt tables stayed all-zero, eliminating ~400 of 816 functions
during self-compilation.

Fixes applied:
- Use streq "fn" fallback in DCE scans (mirrors main compilation loop pattern)
- Add should_skip_compile_fn helper; protect expansion products (impl/generic/
  trait methods have names appended to source buffer at offset >= g_orig_src_len)
- Protect __ runtime hooks (e.g. __thread_done used via spawn return address)
- Detect fn_addr(name) references in scan_top_fn_calls so fn_addr targets
  are marked live even without a direct call
- Remove debug print statements committed with initial DCE implementation

Result: 394/394 conformance tests pass, self-host converges at 1,990,912 bytes
@jailalawat jailalawat changed the title perf: eliminate unreachable top-level functions perf: global dead code elimination (Issue #4 — Tasks A + B) May 17, 2026
@jailalawat jailalawat merged commit 6090878 into main May 17, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Perf: No Global Dead Code Elimination

1 participant