Skip to content

Commit 0d43647

Browse files
Nikhil Thomasmeta-codesync[bot]
authored andcommitted
Add tcprint SSA Graph Inspector for agent-driven JIT analysis
Summary: Add a Python tool that queries Hive for HHVM tcprint data (SSA IR from the JIT translation cache), parses the JSON-encoded IR, and presents it in a clear format for LLM agents to inspect and identify optimization opportunities. The tool supports: - Listing available tcprint runs from Hive - Ranking translations by profCount to find hottest code - Detailed SSA IR output with block structure, phi nodes, type flow, and profile execution counts - Summary mode with automatic anomaly flagging (hot-in-cold, dead blocks) - Machine code disassembly output - Local trace file loading for offline analysis Includes a README documenting the agent workflow and HHIR opcode reference for effective LLM-driven analysis. Reviewed By: ricklavoie Differential Revision: D94459371 fbshipit-source-id: a5179685062cffca96671b6c1bb108686ad8967c
1 parent b3e359d commit 0d43647

7 files changed

Lines changed: 1133 additions & 0 deletions

File tree

hphp/tools/ssa-inspector/README.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# SSA Inspector — HHVM tcprint IR Analysis Tool
2+
3+
## What This Tool Does
4+
5+
This tool queries HHVM's daily tcprint Dataswarm pipeline, which dumps the JIT
6+
translation cache as JSON-encoded SSA IR for production web tenants. It parses
7+
the IR and presents it in a format optimized for analysis by an LLM agent.
8+
9+
The data lives in Hive at `infrastructure.hhvm_tcprint_data`, partitioned by
10+
date (`ds`) and run UUID. Each row contains one JIT translation's full SSA IR:
11+
blocks, instructions with HHIR opcodes/types/SSATmps, phi nodes, inlining
12+
decisions, machine code ranges with disassembly, and profile execution counts.
13+
14+
## Quick Start
15+
16+
```bash
17+
# 1. List available runs (find recent data)
18+
buck run fbcode//hphp/tools/ssa-inspector:main -- list-runs --days 7
19+
20+
# 2. Find the hottest translations in a run
21+
buck run fbcode//hphp/tools/ssa-inspector:main -- \
22+
top --date 2026-02-24 --run-uuid "2026-02-24.prod.web.cln.0-1" --top-n 10
23+
24+
# 3. Inspect a specific translation in detail
25+
buck run fbcode//hphp/tools/ssa-inspector:main -- \
26+
inspect --trans-id 90516 \
27+
--date 2026-02-24 --run-uuid "2026-02-24.prod.web.cln.0-1" \
28+
--format detail
29+
30+
# 4. Search by function name
31+
buck run fbcode//hphp/tools/ssa-inspector:main -- \
32+
inspect --function "contains_key" \
33+
--date 2026-02-24 --run-uuid "2026-02-24.prod.web.cln.0-1" \
34+
--format summary
35+
36+
# 5. Include machine code disassembly
37+
buck run fbcode//hphp/tools/ssa-inspector:main -- \
38+
inspect --trans-id 90544 \
39+
--date 2026-02-24 --run-uuid "2026-02-24.prod.web.cln.0-1" \
40+
--format detail --disasm
41+
42+
# 6. Load from a local trace file (no Hive needed)
43+
buck run fbcode//hphp/tools/ssa-inspector:main -- \
44+
inspect --local-file /tmp/trace.out --function "someFunc" --format detail
45+
```
46+
47+
## Agent Workflow
48+
49+
When analyzing HHVM JIT output for optimization opportunities, follow this
50+
sequence:
51+
52+
### Step 1: Find Hot Code
53+
54+
Run `top` to identify the hottest translations by execution count (profCount).
55+
Focus on `TransOptimize` translations — these are the fully optimized JIT output
56+
and represent the code that actually runs in production.
57+
58+
### Step 2: Inspect in Summary Mode
59+
60+
Use `--format summary` to quickly scan a translation's structure. The summary
61+
flags anomalies automatically:
62+
- **"hot in cold area"** — Block has high profCount but is placed in the Cold
63+
code area. This means the code is frequently executed but was predicted to be
64+
rare, causing instruction cache pressure.
65+
- **"hot but Unlikely"** — Block has high profCount but its hint says Unlikely.
66+
The hint drives code layout; a wrong hint means hot code is placed far from
67+
the main path.
68+
- **"dead?"** — Block has zero profCount in a TransOptimize translation,
69+
meaning it was never executed in the profiling run that guided optimization.
70+
71+
### Step 3: Inspect in Detail Mode
72+
73+
Use `--format detail` to see the full SSA IR. The output format is:
74+
75+
```
76+
Block B0 [Main, Neither] (profCount=138,075,990, preds=[], next->B1):
77+
(000) t0:FramePtr = DefFuncEntryFP
78+
(001) t1:Int = LdLoc<0> t0
79+
(002) CheckType<Bool> t1 -> B5
80+
(003) t3:Bool = AssertType<Bool> t1
81+
```
82+
83+
Each line is one SSA instruction:
84+
- `(NNN)` — instruction ID
85+
- `tN:Type` — SSA temporary with its type
86+
- `= Opcode<TypeParam, Extra>` — the HHIR opcode with optional parameters
87+
- `tN, tM` — source operands (SSA temporaries)
88+
- `-> BN` — taken branch (for conditional/guard instructions)
89+
90+
Phi nodes appear at block entry on `DefLabel` instructions:
91+
```
92+
phi t10:Bool = [t5@B2, t8@B3]
93+
(020) t10:Bool = DefLabel
94+
```
95+
96+
### Step 4: What to Look For
97+
98+
These are the main optimization patterns to identify:
99+
100+
#### 1. Redundant Type Checks
101+
A `CheckType<T>` guards that a value has type T, branching to a side exit if
102+
not. If the value's SSA type already satisfies the guard (e.g., checking
103+
`CheckType<Bool>` on a value already typed `Bool`), the check is redundant.
104+
105+
#### 2. Hot Code in Cold Blocks
106+
Blocks in the "Cold" area with high profCount values. The JIT places Cold blocks
107+
far from Main blocks in memory. If these blocks are frequently executed, it
108+
causes instruction cache misses. Look for blocks flagged "hot in cold area" in
109+
summary mode.
110+
111+
#### 3. Missed Inlining
112+
Check the "Inlining Decisions" section. Functions marked "not inlined" with a
113+
reason like "too large" or "too deep" in a hot path may benefit from raising
114+
inlining thresholds or refactoring.
115+
116+
#### 4. Dead Code
117+
Blocks with profCount=0 in TransOptimize translations were never executed during
118+
profiling. If these blocks contain significant code (not just catch handlers),
119+
they may represent over-specialization.
120+
121+
#### 5. Suboptimal Type Specialization
122+
Long chains of `CheckType` instructions testing multiple types sequentially
123+
(e.g., Dict → Vec → Keyset → Obj) suggest the JIT isn't specializing well for
124+
the dominant type. If profCount shows one path is overwhelmingly hot, the
125+
compiler could potentially specialize for just that type.
126+
127+
#### 6. Excessive Phi Nodes
128+
Large phi nodes at block merge points can indicate the JIT is maintaining too
129+
many live values across branches, increasing register pressure.
130+
131+
#### 7. Instruction Patterns
132+
- `DecRefNZ` / `DecRef` clusters: excessive reference counting overhead
133+
- `StLoc` immediately followed by `LdLoc` of the same local: unnecessary
134+
store-load pairs
135+
- `AssertType` after `CheckType` where the assert is trivially true
136+
- Multiple `BespokeGet<Unknown>` calls: array access without specialization
137+
138+
## HHIR Reference
139+
140+
Key opcodes and their meanings:
141+
- `CheckType<T>` — Guard: check value has type T, side-exit if not
142+
- `AssertType<T>` — Assert value has type T (no runtime check, just narrows SSA type)
143+
- `LdLoc<N>` — Load local variable N from the frame
144+
- `StLoc<N>` — Store to local variable N
145+
- `DefLabel` — Block entry point (may have phi nodes)
146+
- `Jmp` — Unconditional jump
147+
- `JmpZero` / `JmpNZero` — Conditional jump on zero/non-zero
148+
- `CheckSurpriseFlagsEnter` — Check for async signals/stack overflow at function entry
149+
- `EnterInlineFrame` / `LeaveInlineFrame` — Inline call boundaries
150+
- `InlineCall` / `InlineSideExit` — Inlining machinery
151+
- `RetCtrl` — Return from translation
152+
- `ReqBindJmp` — Request the JIT to compile and bind a new translation
153+
- `DecRef` / `DecRefNZ` — Decrement reference count (NZ = known non-zero)
154+
- `AKExistsDict` / `AKExistsKeyset` — Array key existence checks
155+
- `BespokeGet` — Generic array element access
156+
- `InstanceOfBitmask` — Fast class hierarchy check
157+
- `VerifyParamFailHard` — Type constraint violation (always throws)
158+
159+
## File Structure
160+
161+
| File | Purpose |
162+
|------|---------|
163+
| `ir_model.py` | Dataclasses matching `hphp/doc/printir_json_schema.ts` |
164+
| `data_fetcher.py` | Presto/Hive queries + local file loading |
165+
| `ir_parser.py` | JSON blob → dataclass parsing |
166+
| `formatter.py` | Summary and detail output formatting |
167+
| `main.py` | CLI entry point (argparse) |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.

0 commit comments

Comments
 (0)