IREZ is a bounded, provenance-preserving LLVM IR evidence layer for white-box audits by engineers and AI agents. It parses textual LLVM IR and bitcode through LLVM's C++ API, persists a selective structural graph in SQLite, and exposes the same response envelope through a JSON CLI and a thin MCP server.
Canonical repository: https://github.com/yehweihsu/irez.
llvm/llvm-project#186922:
a loop copying i32 values is incorrectly vectorized. The driver prints 100
at -O0 and 107 at -O2.
opt reduced.ll -S -o reduced_optimized.ll \
-passes='inline,loop-rotate,sroa,instcombine,loop-vectorize'
clang -O0 main.c reduced_optimized.ll && ./a.out # expect 100, got 107The main.c driver is twenty lines and obviously correct. A source-level
indexer has full visibility into it and still has nothing to say, because the
defect is introduced by a pass pipeline that runs after the source is gone.
The fix, when it landed, was in Loop Access Analysis — an IR-level analysis
reaching an IR-level conclusion.
Questions in that region — which pass changed the meaning of this loop, which operands actually feed this value in this build, is this call boundary opaque or did the analysis give up — need the IR itself as evidence. docs/WHY_IR.md develops this case and five more real ones, including the limits of the argument.
IREZ does not replace source-level tooling. For "where is this function called", use clangd.
Every response carries its own limits. This is trace-return on the bundled
fixtures/nonfloating.ll, abridged:
{
"command": "trace-return",
"capabilities_used": [
{ "name": "direct_calls", "precision": "exact", "status": "supported" },
{ "name": "operand_graph", "precision": "exact", "status": "supported" },
{ "name": "source_mapping", "precision": "partial", "status": "supported" }
],
"result": {
"function": "irez:5b72ac864e894652:llvm:function:f1",
"return_count": 1,
"sites": [
{
"call_boundaries": [
{
"target_name": "external",
"status": "external",
"reason": "declaration_only",
"precision": "exact",
"modality": "must",
"expandable": false
}
]
}
]
},
"diagnostics": [
{ "kind": "truncation", "scope": "per_sink",
"total_visited_nodes": 8, "truncated_sites": 0 }
],
"evidence_refs": ["artifact:5b72ac864e894652", "run:..."],
"unknowns": []
}precision separates exact structural facts from partial ones. A call that
leaves the indexed set is reported as a boundary with the reason it is one,
not silently dropped. Truncation is stated rather than implied by a short
answer. Nothing here claims a solved path condition.
bounded_queries.py measures bounded return traces and operand slices on synthetic modules up to 124,501 entities. It records warm-cache samples, binary/input hashes, query plans, and response equality when comparing two binaries:
python benchmarks/bounded_queries.py --binary /path/to/irez --output .cache/benchmarkFor a traversal comparison, build_ablation.py
builds the current CLI and a preload ablation with the same compiler and
dependencies. It requires Linux, an LLVM development SDK, system SQLite, and
SQLiteCpp source; run it with --help for build options. Measurement protocol
and validation results are recorded in PROGRESS.md.
The JAX driver exports CPU LLVM IR and records runtime observations. The query script ingests the dumps and traces return/store operands with explicit budgets and provenance. Using Python from an environment with JAX installed:
python demos/jax/generate.py --mode jit_f --output .cache/jax-demo
python demos/jax/query.py --binary /path/to/irez --artifacts .cache/jax-demo/dump --output .cache/jax-evidenceUse a new output directory for each run. JAX is only needed to generate the
example; IREZ itself has no Python or JAX runtime dependency. On Windows, pass
the Windows irez.exe as --binary.
Download and extract the Windows or Linux x86-64 bundle from the latest release.
On Linux/WSL, extract the .tar.gz from inside the Linux environment so its
POSIX executable bits are preserved. A Windows archive tool writing directly
into the WSL filesystem may create bin/* as non-executable; see
Quick start for the exact command and recovery step.
The supported golden path is:
./bin/irez-mcp install codex # or: opencode
./bin/irez-mcp doctor codexThe installer uses the sibling irez binary, copies both executables to a
versioned per-user directory, initializes state, registers absolute paths, and
installs the embedded investigation skill.
See Quick start, supported hosts, onboarding a new project, or the host-agnostic stdio contract. Updates and schema/skill compatibility are in UPGRADING.md; when something goes wrong, start at TROUBLESHOOTING.md.
For an extracted binary release (.exe is implied on Windows):
./bin/irez --state-dir /tmp/demo init --name demo
./bin/irez --state-dir /tmp/demo ingest llvm fixtures/nonfloating.ll --index catalog
./bin/irez --state-dir /tmp/demo functions --match choose
./bin/irez --state-dir /tmp/demo materialize function '<function-handle>'
./bin/irez --state-dir /tmp/demo show '<function-handle>'
./bin/irez --state-dir /tmp/demo trace-return '<function-handle>' \
--budget-nodes 50 --budget-depth 8From a source checkout, replace ./bin/irez with build/irez.
See docs/CLI.md for the full command reference, envelope
contract, handle format, and exit codes. The CLI provides bounded function
views, trace-return, refresh/reindex behavior, and explicit
capability/version evidence; --adapter is gone because the parser is
in-process.
irez-llvm-index (catalog/function/version subcommands, JSONL on stdout)
remains available for debugging.
User-visible release history is recorded in CHANGELOG.md.
- DB layout, API envelope, analysis semantics, adapter, and LLVM build are versioned independently. Unsupported old or future DB schemas are refused without modification in the prototype release.
- SQLite access goes through a vendored SQLiteCpp build using its bundled
sqlite3 amalgamation; tests use vendored GoogleTest. Both are referenced
through the
IREZ_DEPS_DIRCMake variable instead of being copied, so Linux/Windows and x64/ARM64 builds only need a C++20 compiler, CMake, LLVM 21+, and Cargo. irez-llvm-indexremains as a standalone JSONL binary withcatalog,function, andversionsubcommands for debugging and adapter cross-checks. The CLI itself has no--adapteroption because there is no external parser process.irez-mcpspawns theirezCLI per tool call and passes the JSON envelope through. No FFI, no duplicated logic; the CLI stdout contract is the IPC.- UUIDs are generated with
std::random_device(RFC 4122 v4); no dependency.
IREZ uses a C++20 core for LLVM parsing, storage, queries, and response envelopes. The Rust component is a thin MCP stdio adapter that invokes the CLI. Python is used only for tests and release tooling, never at runtime. See docs/ARCHITECTURE.md for the process boundaries and docs/PROGRESS.md for the development history.
src/ C++ core + CLI + standalone adapter (all of the logic)
mcp/ irez-mcp: Rust MCP server (rmcp, stdio), spawns the CLI
tests/ GoogleTest suite + checked-in CLI response golden
scripts/ Build, test, packaging, and release tooling
fixtures/ LLVM IR fixtures (including regression fixtures)
skills/ agent investigation skill (language-agnostic)
make mcp # or .\scripts\build-mcp.ps1 on Windows
IREZ_STATE_DIR=/absolute/path/to/state IREZ_CLI=/absolute/path/to/build/irez \
mcp/target/release/irez-mcpThe server speaks MCP (stdio transport, protocol versions up to 2026-07-28)
and exposes 14 query tools (irez_status, irez_artifacts, irez_functions,
irez_show, irez_graph, irez_slice, irez_uses, irez_guards,
irez_context, irez_source, irez_expand, irez_capabilities,
irez_trace_return, irez_trace_stores). It holds no state and contains no
query logic: each tool call spawns the CLI and returns its envelope. By design,
MCP offers no init or ingest tool; prepare the state directory with the CLI
first.
Host registration (OpenCode, Codex) is a one-shot step per platform:
scripts/setup-mcp-host.sh # Linux/WSL
powershell -File scripts\setup-mcp-host.ps1 # Windowswhich wraps the lower-level installer:
mcp/target/release/irez-mcp install codex --cli "$PWD/build/irez" # or opencode
mcp/target/release/irez-mcp doctor codex --cli "$PWD/build/irez"
mcp/target/release/irez-mcp uninstall codexThe installer initializes a per-host state directory
(~/.local/share/irez/<platform> by default), registers the server with the
host (Codex via codex mcp add, OpenCode via opencode.jsonc with a
timestamped backup and a real JSONC scanner — not regexes), and installs the
irez-investigation skill into the host's personal skill directory. An owned
skill is fully staged into a sibling temporary directory and then swapped in
with a rename after the old copy is removed — a crash in that window can leave
the skill absent (reinstall to recover); unowned content is never replaced.
See docs/MCP_SETUP.md for per-platform details, state
preparation, verification, and troubleshooting. Uninstall removes only what
the installer created; state and artifacts are kept.
Building from source needs an LLVM development SDK (21.x through 23.x) and a Rust toolchain. On Windows it additionally needs Visual Studio with the C++ workload and the LLVM archive that ships the development files — not the ordinary installer. If you just want to try IREZ, use the binary release above; that is the supported path, and the source build exists for contributors.
Linux / WSL:
make build # C++ core, CLI, standalone adapter, tests
make test # ctest: GoogleTest suite + Python-driven CLI golden test
make e2e # init + ingest --index full against fixtures/
make mcp # cargo build --release of mcp/ (irez-mcp)Windows (PowerShell + MSVC; Visual Studio is discovered via vswhere):
.\scripts\build-cpp.ps1
.\scripts\run-tests.ps1
.\scripts\build-mcp.ps1Dependencies:
- C++20 compiler (GCC, Clang, or MSVC), CMake >= 3.20; GNU Make on Linux/WSL
- LLVM 21+ development files (tested with 21.x through 23.x; not sensitive to the exact minor version)
- Rust toolchain (>= 1.88) for the MCP server
- GoogleTest 1.17.0, SQLiteCpp 3.3.3, and the Windows LLVM 23 zlib/zstd
fallbacks are found or fetched automatically;
IREZ_DEPS_DIRsupports pinned offline checkouts - Python 3 is required when tests are enabled; it is never used at runtime
The same sources build on Linux, WSL, and Windows; official binary releases currently target Windows/Linux x86-64 only. SQLite comes from SQLiteCpp's bundled amalgamation; the artifact store uses the native flush primitive on each platform before atomic publication.
See docs/BUILDING.md for prerequisites per platform, LLVM package notes (including which Windows archive ships the development files), configuration variables, and troubleshooting. Runtime and toolchain floors are in COMPATIBILITY.md; maintainers should also read the release procedure. For a bounded, copy-paste release experiment in a fresh Agent window, see EXPERIMENT.md.
guardsreports exact control dependence (post-dominator analysis) for artifacts ingested with the current adapter; older state directories without control-dependence evidence fall back to immediate CFG predecessor evidence flaggedpartial/conservative. Neither mode claims solved path conditions.- Indirect calls are unknown/conservative and memory dependencies are unsupported.
- Runtime observations are not collected from SSA. Context packets explicitly report them as unavailable unless a future external evidence importer supplies them.
- IREZ indexes the artifacts you give it. It does not diff pass pipelines or attribute a change to a pass; producing the before/after IR pair is your job.
Copyright 2026 Yewei Xu. Licensed under the Apache License 2.0. Third-party licenses and attributions are listed in THIRD_PARTY_NOTICES.md.