{caption}, '
+ f"as opt -passes={KINDS[self.kind][0]} drew it
"
+ f'
{_svg(self)}
'
+ )
+
+
+__all__ = ["Edge", "Graph", "GraphError", "Node", "callgraph", "cfg", "ddg", "dom", "graph"]
diff --git a/toolkit/irx/proc.py b/toolkit/irx/proc.py
index 47de6e4..3aa6283 100644
--- a/toolkit/irx/proc.py
+++ b/toolkit/irx/proc.py
@@ -117,8 +117,15 @@ def run(
stdin: str | None = None,
check: bool = True,
timeout: int = DEFAULT_TIMEOUT,
+ cwd: str | Path | None = None,
) -> Result:
- """Run one LLVM tool. `tool` is a bare name like `opt`, resolved against the pin."""
+ """Run one LLVM tool. `tool` is a bare name like `opt`, resolved against the pin.
+
+ `cwd` is for the handful of things LLVM writes to the working directory
+ rather than to stdout. The graph printers are the ones this repository
+ needs: `opt -passes=dot-cfg` writes `.f.dot` next to wherever you are
+ standing, and there is no flag that redirects it.
+ """
argv = [str(toolchain.path_to(tool)), *(str(a) for a in args)]
start = time.monotonic()
try:
@@ -128,6 +135,7 @@ def run(
capture_output=True,
text=True,
timeout=timeout,
+ cwd=None if cwd is None else str(cwd),
)
except subprocess.TimeoutExpired:
elapsed = time.monotonic() - start
diff --git a/toolkit/tests/test_graphs.py b/toolkit/tests/test_graphs.py
new file mode 100644
index 0000000..870924f
--- /dev/null
+++ b/toolkit/tests/test_graphs.py
@@ -0,0 +1,219 @@
+"""Tests for irx.graphs.
+
+Split the same way as the rest of the toolkit tests. The parser and the layout
+run against a fixture and need nothing installed, because they are text handling
+and should be covered on a machine with no compiler. The rest run `opt` and skip
+with a reason when there is no pinned toolchain.
+
+The fixture below is verbatim `opt -passes=dot-cfg` output, addresses and all.
+It has been left exactly as LLVM wrote it, because the point of these tests is
+that this module reads what LLVM actually produces rather than what would have
+been convenient.
+"""
+
+from __future__ import annotations
+
+import unittest
+import xml.etree.ElementTree as ET
+
+import irx
+from irx import graphs, toolchain
+
+DOT = r"""digraph "CFG for 'f' function" {
+ label="CFG for 'f' function";
+
+ Node0x600002f880a0 [shape=record,color="#3d50c3ff", style=filled, fillcolor="#d24b4070", fontname="Courier",label="{1:\l| %2 = icmp sgt i32 %0, 0\l br i1 %2, label %5, label %3\l|{