Skip to content

Commit 29b43ff

Browse files
Stevengredkcumming
andauthored
Add show-rule command & docs related to kmir (#642)
## Summary Adds a new `show-rules` command to KMIR and improves documentation for MIR semantics analysis. ## Changes ### New Features - **`kmir show-rules`**: Display rules applied on edges between proof nodes - **`generate-smir-json.sh`**: Multi-format SMIR JSON generation (JSON/DOT/PNG/PDF) - **Enhanced README**: Comprehensive usage guide with examples and workflow ### Documentation - Add MIR execution steps documentation with rule references - Update all documentation to English for internationalization - Remove macOS-specific setup instructions ### Technical - Fix code style issues (quotes, unused variables) - Update stable-mir-json submodules - Update .gitignore for new files ## Usage ```bash # Show rules on edge kmir show-rules proof_id 1 3 --proof-dir ./proof_dir # Generate SMIR JSON ./scripts/generate-smir-json.sh your_file.rs . png ``` ## Testing - All code passes formatting checks (flake8, mypy, black) - Scripts tested with various input formats --------- Co-authored-by: Daniel Cumming <124537596+dkcumming@users.noreply.github.com>
1 parent b6445e6 commit 29b43ff

9 files changed

Lines changed: 362 additions & 25 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ __pycache__/
44
/deps/.stable-mir-json
55
/.vscode/
66
kmir/src/tests/integration/data/**/target
7+
.DS_Store

README.md

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,144 @@ Also included is the `kmir` tool, a python script that acts as a front-end to th
99

1010
### KMIR Setup
1111

12-
Pre-requisites: `python >= 3.10`, `pip >= 20.0.2`, `poetry >= 1.3.2`, `gcc >= 11.4.0`, `cargo == nightly-2024-11-29`, `k >= v7.1.205`. To install K, follow the steps available in [K's Quick Start instructions](https://github.com/runtimeverification/k?tab=readme-ov-file#quick-start).
12+
Pre-requisites: `python >= 3.10`, `pip >= 20.0.2`, `uv >= 0.7.11`, `gcc >= 11.4.0`, `cargo == nightly-2024-11-29`, `k >= v7.1.205`. To install K, follow the steps available in [K's Quick Start instructions](https://github.com/runtimeverification/k?tab=readme-ov-file#quick-start).
1313

1414
```bash
1515
make build
1616
```
1717

1818
Use `make` to run common tasks (see the [Makefile](Makefile) for a complete list of available targets).
1919

20-
For interactive use, spawn a shell with `poetry -C kmir/ shell` (after `poetry -C kmir/ install`), then run an interpreter. Or directly run from `mir-semantics` root with `poetry run -C kmir kmir <COMMAND>`
20+
For interactive use, first sync the environment with `uv --directory kmir sync`, then either:
21+
- Run Python directly: `uv --directory kmir run python`
22+
- Activate the virtual environment: `source kmir/.venv/bin/activate` (on Unix/macOS) or `kmir\.venv\Scripts\activate` (on Windows)
23+
- Or directly run commands from `mir-semantics` root: `uv --directory kmir run kmir <COMMAND>`
2124

2225
### Stable-MIR-JSON Setup
2326

24-
To interact with some of KMIR functionalities, it is necessary to provide the tool with a serialized JSON of a Rust program's Stable MIR. To be able to extract these serialized SMIR JSONs, you can use the `Stable-MIR-JSON` tool, setting it up with the following commands:
27+
To interact with some of KMIR functionalities, it is necessary to provide the tool with a serialized JSON of a Rust program's Stable MIR. To be able to extract these serialized SMIR JSONs, you can use the `Stable-MIR-JSON` tool.
2528

26-
```Rust
29+
#### Quick Start
30+
```bash
2731
git submodule update --init --recursive
2832
make stable-mir-json
2933
```
3034

35+
#### Generating SMIR JSON Files
36+
37+
After setting up stable-mir-json, you can generate SMIR JSON files from Rust source code:
38+
39+
**Using the stable-mir-json tool directly:**
40+
```bash
41+
# For single files
42+
deps/.stable-mir-json/debug.sh -Zno-codegen your_file.rs
43+
44+
# For cargo projects
45+
RUSTC=deps/.stable-mir-json/debug.sh cargo build
46+
```
47+
48+
**Using the convenience script:**
49+
```bash
50+
# Generate JSON file
51+
./scripts/generate-smir-json.sh your_file.rs .
52+
53+
# Generate with visualization (PNG, PDF, DOT)
54+
./scripts/generate-smir-json.sh your_file.rs . png
55+
./scripts/generate-smir-json.sh your_file.rs . pdf
56+
./scripts/generate-smir-json.sh your_file.rs . dot
57+
```
58+
3159
For more information on testing, installation, and general usage of this tool, please check [Stable-MIR-JSON's repository](https://github.com/runtimeverification/stable-mir-json/).
3260

3361
## Usage
3462

3563
Use `--help` with each command for more details.
3664

37-
`parse` to parse a Stable MIR JSON file (`*.smir.json`) file to a K AST
65+
### Basic Commands
66+
67+
**`kmir run`** - Execute a Rust program from SMIR JSON or directly from source
68+
```bash
69+
# Run from SMIR JSON file
70+
uv --project kmir run kmir run --file path/to/program.smir.json
71+
72+
# Run with verbose output
73+
uv --project kmir run kmir run --file path/to/program.smir.json --verbose
74+
```
75+
76+
**`kmir prove-rs`** - Directly prove properties of Rust source code (recommended)
77+
```bash
78+
# Basic proof
79+
uv --project kmir run kmir prove-rs path/to/program.rs
80+
81+
# Detailed proof with output
82+
uv --project kmir run kmir prove-rs path/to/program.rs --verbose --proof-dir ./proof_dir
83+
```
84+
85+
**`kmir gen-spec`** - Generate K specification from SMIR JSON
86+
```bash
87+
uv --project kmir run kmir gen-spec path/to/program.smir.json --output-file path/to/spec.k
88+
```
89+
90+
**`kmir link`** - Link together multiple SMIR JSON files
91+
```bash
92+
# Link multiple SMIR JSON files into a single output file
93+
uv --project kmir run kmir link file1.smir.json file2.smir.json file3.smir.json --output-file linked.smir.json
94+
95+
# Use default output filename (linker_output.smir.json)
96+
uv --project kmir run kmir link file1.smir.json file2.smir.json
97+
```
98+
99+
### Analysis Commands
100+
101+
**`kmir show`** - Display proof information
102+
```bash
103+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir
104+
```
105+
106+
**`kmir view`** - Detailed view of proof results
107+
```bash
108+
uv --project kmir run kmir view proof_id --proof-dir ./proof_dir --verbose
109+
```
110+
111+
**`kmir show-rules`** - Show rules applied between nodes
112+
```bash
113+
uv --project kmir run kmir show-rules proof_id source_node target_node --proof-dir ./proof_dir
114+
```
115+
116+
### Recommended Workflow
117+
118+
1. **Setup Environment**:
119+
```bash
120+
make stable-mir-json # Setup stable-mir-json
121+
make build # Build K definitions
122+
```
123+
124+
2. **Direct Proof** (Recommended):
125+
```bash
126+
uv --project kmir run kmir prove-rs your_file.rs --verbose --proof-dir ./proof_dir
127+
```
128+
129+
3. **View Results**:
130+
```bash
131+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir
132+
uv --project kmir run kmir view proof_id --proof-dir ./proof_dir --verbose
133+
```
38134

39-
`kmir run` to run a binary target in your cargo project (or a specific smir json provided with --file). The SMIR JSON will need to have been already built with `stable-mir-json`, or you will need the `RUSTC` environment variable set to your installation of `stable-mir-json`.
135+
4. **Analyze Rules**:
136+
```bash
137+
uv --project kmir run kmir show-rules proof_id 1 3 --proof-dir ./proof_dir
138+
```
40139

41-
`kmir gen-spec` to take a SMIR JSON and create a K specification module that ensures termination of the program.
140+
### Command Options
42141

43-
`kmir prove run` to run the prover on a spec generated by `gen-spec`.
142+
Most commands support:
143+
- `--verbose, -v`: Detailed output
144+
- `--debug`: Debug information
145+
- `--proof-dir DIR`: Directory for proof results
146+
- `--max-depth DEPTH`: Maximum execution depth
147+
- `--max-iterations ITERATIONS`: Maximum iterations
44148

45-
`kmir prove view` to run the KCFG visualizer and inspect the proof steps.
149+
For complete options, use `--help` with each command.
46150

47151
### Supporters
48152

deps/stable-mir-json

deps/stable-mir-json_release

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
62a239d7102b4871a8d24eb921aab3630b7bd3a9
1+
89a011e5a590635892fffae9ffd16885106c14e3

docs/example-mir-execution-flow.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# MIR Execution Steps
2+
3+
**Last Updated: 2025-08-05**
4+
5+
Execution steps based on `assert_eq.rs`, applying a total of 156 rules, transforming from node 1 to node 3.
6+
7+
## Execution Steps List
8+
9+
### 1. Function Call Initialization Phase
10+
- [Function Call Start](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L461)
11+
- [Setup Callee Data](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L494)
12+
- [Complete Argument Setup](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L519)
13+
14+
### 2. Basic Block Execution Phase
15+
- [Execute Basic Block](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L256)
16+
- [Execute Statement Sequence](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L264)
17+
18+
### 3. Statement Execution Phase
19+
- [Handle Assignment Statement](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L279)
20+
- [Set Local Variable Value (Hot Rule)](../kmir/src/kmir/kdist/mir-semantics/rt/data.md#L173)
21+
22+
### 4. Operand Processing Phase
23+
- [Handle RValue Use](../kmir/src/kmir/kdist/mir-semantics/rt/data.md#L608)
24+
- [Handle Constant Operand](../kmir/src/kmir/kdist/mir-semantics/rt/data.md#L122)
25+
- [Decode Allocated Constant](../kmir/src/kmir/kdist/mir-semantics/rt/data.md#L1073)
26+
27+
### 5. Arithmetic Operation Phase
28+
- [Binary Operation Processing](../kmir/src/kmir/kdist/mir-semantics/rt/data.md#L1109)
29+
- [Binary Operation Result](../kmir/src/kmir/kdist/mir-semantics/rt/data.md#L1174)
30+
31+
### 6. Assertion Check Phase
32+
- [Assertion Check Start](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L563)
33+
- [Expectation Check (Hot Rule)](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L568)
34+
- [Move Operand Processing](../kmir/src/kmir/kdist/mir-semantics/rt/data.md#L154)
35+
- [Projection Traversal and Move Marking](../kmir/src/kmir/kdist/mir-semantics/rt/data.md#L357)
36+
- [Expectation Check (Cold Rule)](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L568)
37+
38+
### 7. Control Flow Processing Phase
39+
- [Assertion Success Processing](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L570)
40+
- [Execute Target Basic Block](../kmir/src/kmir/kdist/mir-semantics/kmir.md#L245)

docs/semantics-of-mir.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The _args_, _destination_, _unwind_, and (optional!) _target_ are
1616
supplied by either the `Terminator` kind `Call` (within a program), or
1717
unnecessary (when calling `main`).
1818

19-
The execution of a body consists of
19+
The execution of a function call consists of
2020
* setting up a stack frame for this call
2121
- with reserved space for all _locals_: return value, arg.s, and local
2222
variables

kmir/src/kmir/__main__.py

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,17 @@
1818
from .cargo import CargoProject
1919
from .kmir import KMIR, KMIRAPRNodePrinter
2020
from .linker import link
21-
from .options import GenSpecOpts, LinkOpts, ProveRawOpts, ProveRSOpts, PruneOpts, RunOpts, ShowOpts, ViewOpts
21+
from .options import (
22+
GenSpecOpts,
23+
LinkOpts,
24+
ProveRawOpts,
25+
ProveRSOpts,
26+
PruneOpts,
27+
RunOpts,
28+
ShowOpts,
29+
ShowRulesOpts,
30+
ViewOpts,
31+
)
2232
from .parse.parser import parse_json
2333
from .smir import SMIRInfo
2434

@@ -78,7 +88,7 @@ def _kmir_gen_spec(opts: GenSpecOpts) -> None:
7888
output_file = opts.output_file
7989
if output_file is None:
8090
suffixes = ''.join(opts.input_file.suffixes)
81-
base = opts.input_file.name.removesuffix(suffixes)
91+
base = opts.input_file.name.removesuffix(suffixes).replace('_', '-')
8292
output_file = Path(f'{base}-spec.k')
8393

8494
module_name = output_file.stem.upper().replace('_', '-')
@@ -131,6 +141,22 @@ def _kmir_show(opts: ShowOpts) -> None:
131141
print('\n'.join(lines))
132142

133143

144+
def _kmir_show_rules(opts: ShowRulesOpts) -> None:
145+
"""Show rules applied on the edge from source to target node."""
146+
proof = APRProof.read_proof_data(opts.proof_dir, opts.id)
147+
edge = proof.kcfg.edge(opts.source, opts.target)
148+
if edge is None:
149+
print(f'Error: No edge found from node {opts.source} to node {opts.target}')
150+
return
151+
rules = edge.rules
152+
print(f'Rules applied on edge {opts.source} -> {opts.target}:')
153+
print(f'Total rules: {len(rules)}')
154+
print('-' * 80)
155+
for rule in rules:
156+
print(rule)
157+
print('-' * 80)
158+
159+
134160
def _kmir_prune(opts: PruneOpts) -> None:
135161
proof = APRProof.read_proof_data(opts.proof_dir, opts.id)
136162
pruned_nodes = proof.prune(opts.node_id)
@@ -158,6 +184,8 @@ def kmir(args: Sequence[str]) -> None:
158184
_kmir_view(opts)
159185
case ShowOpts():
160186
_kmir_show(opts)
187+
case ShowRulesOpts():
188+
_kmir_show_rules(opts)
161189
case PruneOpts():
162190
_kmir_prune(opts)
163191
case ProveRSOpts():
@@ -241,11 +269,17 @@ def _arg_parser() -> ArgumentParser:
241269
)
242270

243271
command_parser.add_parser(
244-
'show', help='Show a saved proof', parents=[kcli_args.logging_args, proof_args, display_args]
272+
'show', help='Show proof information', parents=[kcli_args.logging_args, proof_args, display_args]
273+
)
274+
275+
show_rules_parser = command_parser.add_parser(
276+
'show-rules', help='Show rules applied on a specific edge', parents=[kcli_args.logging_args, proof_args]
245277
)
278+
show_rules_parser.add_argument('source', type=int, metavar='SOURCE', help='Source node ID')
279+
show_rules_parser.add_argument('target', type=int, metavar='TARGET', help='Target node ID')
246280

247281
command_parser.add_parser(
248-
'view', help='View a saved proof', parents=[kcli_args.logging_args, proof_args, display_args]
282+
'view', help='View proof information', parents=[kcli_args.logging_args, proof_args, display_args]
249283
)
250284

251285
prune_parser = command_parser.add_parser(
@@ -302,6 +336,21 @@ def _parse_args(ns: Namespace) -> KMirOpts:
302336
reload=ns.reload,
303337
max_iterations=ns.max_iterations,
304338
)
339+
case 'show':
340+
return ShowOpts(
341+
proof_dir=Path(ns.proof_dir),
342+
id=ns.id,
343+
full_printer=ns.full_printer,
344+
smir_info=Path(ns.smir_info) if ns.smir_info else None,
345+
omit_current_body=ns.omit_current_body,
346+
)
347+
case 'show-rules':
348+
return ShowRulesOpts(
349+
proof_dir=Path(ns.proof_dir),
350+
id=ns.id,
351+
source=ns.source,
352+
target=ns.target,
353+
)
305354
case 'view':
306355
proof_dir = Path(ns.proof_dir)
307356
return ViewOpts(
@@ -314,15 +363,6 @@ def _parse_args(ns: Namespace) -> KMirOpts:
314363
case 'prune':
315364
proof_dir = Path(ns.proof_dir)
316365
return PruneOpts(proof_dir, ns.id, ns.node_id)
317-
case 'show':
318-
proof_dir = Path(ns.proof_dir)
319-
return ShowOpts(
320-
proof_dir,
321-
ns.id,
322-
full_printer=ns.full_printer,
323-
smir_info=ns.smir_info,
324-
omit_current_body=ns.omit_current_body,
325-
)
326366
case 'prove-rs':
327367
return ProveRSOpts(
328368
rs_file=Path(ns.rs_file),

kmir/src/kmir/options.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,24 @@ class ShowOpts(DisplayOpts): ...
157157
class ViewOpts(DisplayOpts): ...
158158

159159

160+
@dataclass
161+
class ShowRulesOpts(ProofOpts):
162+
source: int
163+
target: int
164+
165+
def __init__(
166+
self,
167+
proof_dir: Path | str,
168+
id: str,
169+
source: int,
170+
target: int,
171+
) -> None:
172+
self.proof_dir = Path(proof_dir).resolve() if proof_dir is not None else None
173+
self.id = id
174+
self.source = source
175+
self.target = target
176+
177+
160178
@dataclass
161179
class PruneOpts(ProofOpts):
162180
node_id: int

0 commit comments

Comments
 (0)