Skip to content

Commit d658bac

Browse files
Stevengreclaudejberthold
authored
Implement std::intrinsics::raw_eq support in KMIR (#665)
Goal for this PR is not a complete implementation for `raw_eq`, but a very basic startup to continue the p-token verification. More see: #666 ## Summary Implements support for the `std::intrinsics::raw_eq` intrinsic function in KMIR, enabling byte-by-byte equality comparison of referenced values. ## Key Changes ### 1. Intrinsic Architecture Refactoring - **Modified intrinsic execution pattern**: Changed from `#execIntrinsic(INTRINSIC_NAME, ARGS, DEST)` to `#readOperands(ARGS) ~> #execIntrinsic(INTRINSIC_NAME, DEST)` - **Benefits**: Uniform operand evaluation using freeze/heat pattern, simplified intrinsic implementations ### 2. Raw Eq Implementation - **K Semantics (`kmir.md`)**: - Added execution rules for `raw_eq` intrinsic - Properly dereferences References using `projectionElemDeref` to access underlying values - Uses dedicated `#execRawEq` function to avoid recursion issues - Compares values with K's built-in equality operator (`==K`) ### 3. Black Box Updates - **Adapted to new architecture**: Updated `black_box` intrinsic to work with the new signature - **Maintains identity function behavior**: Simply passes argument to destination ### 4. Documentation - **Added comprehensive documentation** for `raw_eq` intrinsic including: - Function description and use cases - Current implementation limitations - Future enhancement requirements ### 5. Test Integration - **New test case**: `raw_eq_simple` - compares two references to equal integers - **Test files**: - `raw_eq_simple.rs`: Rust source with assertion - `raw_eq_simple.smir.json`: Generated SMIR representation - `raw_eq_simple.state`: Expected execution state showing `BoolVal(true)` result ## Implementation Details The implementation follows an elegant pattern: 1. `#setUpCalleeData` for intrinsics triggers `#readOperands(ARGS)` first 2. All operands are evaluated to values and placed on the K cell 3. `#execIntrinsic(symbol("raw_eq"), DEST)` matches Reference values 4. Creates dereferenced operands and calls `#readOperands` again 5. `#execRawEq` compares the dereferenced values and sets result ## Test Results - ✅ `raw_eq_simple` test passes in both LLVM and Haskell backends - ✅ Correct `BoolVal(true)` result for equal integer values (42 == 42) - ✅ Execution completes successfully with proper control flow ## Limitations & Future Work Current implementation only handles References to same-type values. See issue #666 for enhancement tracking: - Different types with same memory representation - Composite types (structs, arrays, enums) - Memory layout considerations (alignment, padding) ## Files Changed - `kmir/src/kmir/kdist/mir-semantics/kmir.md` - Core implementation and documentation - `kmir/src/tests/integration/data/exec-smir/intrinsic/raw_eq_simple.rs` - Test source - `kmir/src/tests/integration/data/exec-smir/intrinsic/raw_eq_simple.smir.json` - SMIR data - `kmir/src/tests/integration/data/exec-smir/intrinsic/raw_eq_simple.state` - Expected state - `kmir/src/tests/integration/test_integration.py` - Test configuration 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Jost Berthold <jost.berthold@gmail.com>
1 parent 1bf01ef commit d658bac

8 files changed

Lines changed: 369 additions & 66 deletions

File tree

CLAUDE.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,12 @@ The Python layer (`kmir.py`) bridges between SMIR JSON and K semantics:
9797
3. Executes via K framework's `KProve`/`KRun` interfaces
9898

9999
### Intrinsic Functions
100-
Intrinsic functions (like `black_box`) don't have regular function bodies. They're handled by:
100+
Intrinsic functions (like `black_box`, `raw_eq`) don't have regular function bodies. They're handled by:
101101
1. Python: `_make_function_map` adds `IntrinsicFunction` entries to function map
102102
2. K: Special rules in `kmir.md` execute intrinsics via `#execIntrinsic`
103103

104+
**See `docs/dev/adding-intrinsics.md` for detailed implementation guide.**
105+
104106
## Testing Patterns
105107

106108
### prove-rs Tests
@@ -118,9 +120,14 @@ Tests in `kmir/src/tests/integration/data/prove-rs/` follow this pattern:
118120
## Development Workflow
119121

120122
### Before Starting Any Task
121-
1. Read README and documentation in docs/ directory first
122-
2. Study existing development patterns and conventions
123-
3. Understand the codebase structure before making changes
123+
1. **Always read relevant documentation first**:
124+
- Check `docs/` directory for guides on specific topics
125+
- Review existing implementations of similar features
126+
- Study test patterns in `kmir/src/tests/`
127+
2. **Understand existing patterns**:
128+
- Look at recent PRs for implementation examples
129+
- Check how similar features are implemented
130+
- Follow established conventions in the codebase
124131

125132
### Modifying K Semantics
126133
1. Edit `.md` files in `kmir/src/kmir/kdist/mir-semantics/`
@@ -133,10 +140,7 @@ Tests in `kmir/src/tests/integration/data/prove-rs/` follow this pattern:
133140
3. Test with `make test-unit`
134141

135142
### Adding Intrinsic Support
136-
1. Update `_make_function_map` in `kmir.py` to recognize intrinsic
137-
2. Add `IntrinsicFunction` constructor in `mono.md`
138-
3. Add execution rules in `kmir.md` under `#execIntrinsic`
139-
4. Add test in `prove-rs/` directory
143+
See `docs/dev/adding-intrinsics.md` for complete guide with examples.
140144

141145
## Debugging Tips
142146

docs/dev/adding-intrinsics.md

Lines changed: 163 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,195 @@
11
# Adding Intrinsics
22

3+
## Overview
4+
5+
This guide explains how to add support for new intrinsic functions in KMIR. Intrinsics are compiler built-in functions that don't have regular MIR bodies and require special semantic rules.
6+
7+
## Architecture
8+
9+
Intrinsics use optimized execution:
10+
1. **Direct Execution**: `#execIntrinsic(IntrinsicFunction(symbol("name")), ARGS, DEST)` bypasses regular function call setup
11+
2. **Pattern Matching**: Rules match on specific operand patterns for each intrinsic
12+
3. **Operand Evaluation**: Each intrinsic rule handles its own operand evaluation as needed
13+
14+
See implementation in [kmir.md](../../kmir/src/kmir/kdist/mir-semantics/kmir.md)
15+
316
## Development Workflow
417

518
### Step 1: Create Test File
6-
Create `tests/rust/intrinsic/your_intrinsic.rs`:
719

8-
```rust
9-
fn main() {
10-
let result = your_intrinsic(args);
11-
assert_eq!(result, expected);
12-
}
20+
Create a Rust test file in `kmir/src/tests/integration/data/exec-smir/intrinsic/` that uses your intrinsic and verifies its behavior with assertions.
21+
22+
### Step 2: Add Test to Integration Suite
23+
24+
Add entry to `EXEC_DATA` in [test_integration.py](../../kmir/src/tests/integration/test_integration.py):
25+
26+
```python
27+
(
28+
'your_intrinsic',
29+
EXEC_DATA_DIR / 'intrinsic' / 'your_intrinsic.smir.json',
30+
EXEC_DATA_DIR / 'intrinsic' / 'your_intrinsic.state',
31+
65, # Start with small depth, increase if needed
32+
),
1333
```
1434

15-
### Step 2: Generate SMIR and Verify Intrinsic Detection
35+
### Step 3: Generate Initial State (Will Show Stuck Point)
36+
1637
```bash
17-
# Generate SMIR JSON
18-
make generate-tests-smir
38+
# Generate initial state showing where execution gets stuck
39+
make test-integration TEST_ARGS="-k 'exec_smir and your_intrinsic' --update-expected-output"
40+
41+
# This will create your_intrinsic.state showing execution stuck at:
42+
# #execIntrinsic(IntrinsicFunction(symbol("your_intrinsic")), ARGS, DEST)
1943

20-
# Update expected outputs and verify intrinsic is detected
21-
make test-unit TEST_ARGS="--update-expected-output"
44+
# Save this for comparison
45+
cp kmir/src/tests/integration/data/exec-smir/intrinsic/your_intrinsic.state \
46+
your_intrinsic.state.initial
2247
```
2348

24-
Check `tests/expected/unit/test_smir/test_function_symbols/your_intrinsic.expected.json` to confirm the intrinsic appears as `IntrinsicSym`.
49+
### Step 4: Implement K Semantics Rule
2550

26-
### Step 3: Run Initial Integration Test
51+
Add rules to [kmir.md](../../kmir/src/kmir/kdist/mir-semantics/kmir.md).
52+
53+
To find implementation patterns for your intrinsic:
2754
```bash
28-
# Run test and update expected output (will show stuck at intrinsic call)
29-
make test-integration TEST_ARGS="-k your_intrinsic --update-expected-output"
55+
# Search for existing intrinsic implementations
56+
grep -A10 "#execIntrinsic(IntrinsicFunction" kmir/src/kmir/kdist/mir-semantics/kmir.md
3057

31-
# Backup the initial state for comparison
32-
cp tests/expected/integration/test_exec_smir/intrinsic_your_intrinsic.state \
33-
tests/expected/integration/test_exec_smir/intrinsic_your_intrinsic.state.backup
58+
# Look for helper functions and evaluation patterns
59+
grep -B2 -A5 "seqstrict" kmir/src/kmir/kdist/mir-semantics/kmir.md
3460
```
3561

36-
### Step 4: Implement K Rule
37-
Edit `kmir/src/kmir/kdist/mir-semantics/kmir.md`:
62+
Study existing intrinsics like `black_box` (simple value operation) and `raw_eq` (reference dereferencing with helper functions) as reference implementations.
3863

39-
```k
40-
rule <k> #execIntrinsic(mirString("your_intrinsic"), ARGS, DEST) =>
41-
/* your implementation */
42-
... </k>
43-
```
64+
### Step 5: Add Documentation
65+
66+
Document your intrinsic in the K semantics file with its implementation.
67+
68+
### Step 6: Rebuild and Test
4469

45-
### Step 5: Rebuild and Test
4670
```bash
4771
# Rebuild K semantics
4872
make build
4973

50-
# Run test again
51-
make test-integration TEST_ARGS="-k your_intrinsic --update-expected-output"
74+
# Run test again and update the state with working implementation
75+
make test-integration TEST_ARGS="-k 'exec_smir and your_intrinsic' --update-expected-output"
5276

53-
# Compare results
54-
diff tests/expected/integration/test_exec_smir/intrinsic_your_intrinsic.state.backup \
55-
tests/expected/integration/test_exec_smir/intrinsic_your_intrinsic.state
77+
# Compare to see the progress
78+
diff your_intrinsic.state.initial \
79+
kmir/src/tests/integration/data/exec-smir/intrinsic/your_intrinsic.state
5680
```
5781

58-
The diff should show progress past the intrinsic call if implementation is correct.
82+
### Step 7: Verify Both Backends
5983

60-
### Step 6: Verify Results
61-
Ensure the test completes successfully and the intrinsic behaves as expected.
62-
63-
## Example: black_box
84+
```bash
85+
# Test with both LLVM and Haskell backends
86+
make test-integration TEST_ARGS="-k 'exec_smir and your_intrinsic'"
6487

65-
Initial state (before rule):
88+
# Both should pass with consistent results
89+
# If not, you may need backend-specific state files
6690
```
67-
#setUpCalleeData ( IntrinsicFunction ( mirString ( "black_box" ) ) , ...)
91+
92+
## Examples
93+
94+
For implementation examples, see existing intrinsics in [kmir.md](../../kmir/src/kmir/kdist/mir-semantics/kmir.md).
95+
96+
## Common Patterns
97+
98+
### Pattern 1: Direct Operand Evaluation
99+
Use when the intrinsic needs to evaluate its operands directly.
100+
101+
### Pattern 2: Reference Dereferencing with #withDeref
102+
Use the `#withDeref` helper function to add dereferencing to operands.
103+
104+
### Pattern 3: seqstrict for Automatic Evaluation
105+
Use `[seqstrict(2,3)]` attribute to automatically evaluate operand arguments.
106+
107+
### Pattern 4: Pattern Matching on Operands
108+
Match specific operand patterns directly in the `#execIntrinsic` rule.
109+
110+
## Testing Best Practices
111+
112+
1. **Start Simple**: Test with primitive types first
113+
2. **Save Initial State**: Keep the stuck state for comparison
114+
3. **Use Correct Test Filter**: Always use `exec_smir and your_intrinsic` to ensure correct test runs
115+
4. **Check Both Backends**: Ensure LLVM and Haskell produce same results
116+
5. **Document Limitations**: Note what cases aren't handled yet
117+
6. **Create Issue for Future Work**: Track enhancements needed (like #666 for `raw_eq`)
118+
119+
## Debugging Tips
120+
121+
### Check Execution State
122+
```bash
123+
# See where execution is stuck with verbose output
124+
make test-integration TEST_ARGS="-k 'exec_smir and your_intrinsic' -vv"
125+
126+
# Look for the K cell content to see what values are present
68127
```
69128

70-
After implementing rule:
129+
### Understanding the State File
130+
The state file shows the complete execution state. Key sections to check:
131+
- `<k>`: Shows current execution point
132+
- `<locals>`: Shows local variable values
133+
- `<functions>`: Should contain `IntrinsicFunction(symbol("your_intrinsic"))`
134+
135+
### Verify Intrinsic Recognition
136+
```bash
137+
# Check SMIR JSON to confirm intrinsic is recognized
138+
cat kmir/src/tests/integration/data/exec-smir/intrinsic/your_intrinsic.smir.json | grep -A5 your_intrinsic
71139
```
72-
Program continues execution with value 11 passed through
73-
```
140+
141+
## Common Issues
142+
143+
### Issue: Wrong test runs
144+
**Solution**: Use `-k 'exec_smir and your_intrinsic'` to ensure `test_exec_smir` runs, not other tests.
145+
146+
### Issue: "Function not found"
147+
**Solution**: The intrinsic should be automatically recognized if it appears in SMIR. Check the SMIR JSON to confirm.
148+
149+
### Issue: Execution stuck at `#execIntrinsic`
150+
**Solution**: Your rule pattern doesn't match. Check:
151+
- The exact intrinsic name in the symbol
152+
- Value types on K cell (use `ListItem(VAL:Value)` to match any value)
153+
- Number of arguments expected
154+
155+
### Issue: Recursion/infinite loop
156+
**Solution**: Use helper functions to separate evaluation stages, avoid calling `#execIntrinsic` within itself.
157+
158+
### Issue: Different backend results
159+
**Solution**:
160+
- Increase execution depth if needed
161+
- Check for backend-specific evaluation order issues
162+
- May need backend-specific expected files (`.llvm.state`, `.haskell.state`)
163+
164+
### Issue: Test timeout
165+
**Solution**:
166+
- Start with smaller depth (e.g., 65 instead of 1000)
167+
- Optimize your rule to avoid unnecessary computation
168+
- Check for infinite loops in your implementation
169+
170+
## Important Notes
171+
172+
### Direct Operand Passing
173+
The current architecture passes operands directly to intrinsic rules:
174+
- **Direct Access**: Rules receive operands directly in `#execIntrinsic`
175+
- **Custom Evaluation**: Each intrinsic controls its own operand evaluation
176+
- **Better Indexing**: K can better index rules with explicit operand patterns
177+
178+
### When to Use Helper Functions
179+
Always use a helper function when:
180+
- You need automatic operand evaluation (with `seqstrict`)
181+
- The logic is complex enough to benefit from separation
182+
- You want to transform operands before evaluation (like with `#withDeref`)
183+
184+
### Testing Strategy
185+
1. Write the test with expected behavior first
186+
2. Generate initial state to see where it gets stuck
187+
3. Implement the minimal rule needed
188+
4. Update state to verify progress
189+
5. Iterate to handle edge cases
190+
6. Document limitations for future work
191+
192+
## References
193+
194+
- Recent intrinsic PRs in repository history
195+
- [Rust Intrinsics Documentation](https://doc.rust-lang.org/std/intrinsics/)

0 commit comments

Comments
 (0)