Skip to content

Commit 4df33d3

Browse files
Stevengrerv-auditorjberthold
authored
Write symbolic value at symbolic index (#151)
Co-authored-by: devops <devops@runtimeverification.com> Co-authored-by: Jost Berthold <jost.berthold@gmail.com>
1 parent 42176ce commit 4df33d3

8 files changed

Lines changed: 126 additions & 5 deletions

File tree

package/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.112
1+
0.1.113

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "kriscv"
7-
version = "0.1.112"
7+
version = "0.1.113"
88
description = "K tooling for the RISC-V architecture"
99
readme = "README.md"
1010
requires-python = "~=3.10"

src/kriscv/kdist/riscv-semantics/lemmas/sparse-bytes-simplifications.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,51 @@ For symbolic execution, we need to tackle the patterns of `#bytes(B +Bytes _) _`
3030
requires I >=Int lengthBytes(B) [simplification(45), preserves-definedness]
3131
```
3232

33+
## writeBytes
34+
35+
If the write index is concrete, we can directly call `writeBytesBF` or `writeBytesEF`.
36+
37+
```k
38+
rule writeBytes(I, V, NUM, BF:SparseBytesBF) => writeBytesBF(I, V, NUM, BF) [simplification(45), concrete(I)]
39+
rule writeBytes(I, V, NUM, EF:SparseBytesEF) => writeBytesEF(I, V, NUM, EF) [simplification(45), concrete(I)]
40+
```
41+
42+
If the write index is symbolic, we use `#WB` to wrap the write operation. Unlike `writeBytes`, `#WB` contains a boolean value to determine whether the write operation has been completed. `true` means completed, `false` means not completed.
43+
44+
```k
45+
syntax SparseBytes ::= #WB(Bool, Int, Int, Int, SparseBytes) [function, total]
46+
rule #WB(_, I, V, NUM, B:SparseBytes) => writeBytes(I, V, NUM, B) [concrete]
47+
rule writeBytes(I, V, NUM, B:SparseBytes) => #WB(false, I, V, NUM, B) [simplification]
48+
```
49+
50+
**Termination Control**: The boolean flag ensures that symbolic write operations eventually terminate by transitioning from `false` to `true` state, at which point concrete write functions can be applied when the index becomes concrete.
51+
52+
```k
53+
rule #WB(false, I, V, NUM, BF:SparseBytesBF) => #WB(true, I, V, NUM, BF) [simplification]
54+
rule #WB(false, I, V, NUM, EF:SparseBytesEF) => #WB(true, I, V, NUM, EF) [simplification]
55+
rule #WB(true, I, V, NUM, BF:SparseBytesBF) => writeBytesBF(I, V, NUM, BF) [simplification, concrete(I)]
56+
rule #WB(true, I, V, NUM, EF:SparseBytesEF) => writeBytesEF(I, V, NUM, EF) [simplification, concrete(I)]
57+
```
58+
59+
**Reordering for Optimization**: When multiple `#WB` operations are nested, the rules bring incomplete `#WB` operations (with `false` flag) to the terminal position, allowing them to traverse and find all possible merge opportunities.
60+
61+
```k
62+
rule #WB(false, I0, V0, NUM0, #WB(true, I1, V1, NUM1, B:SparseBytes)) => #WB(true, I1, V1, NUM1, #WB(false, I0, V0, NUM0, B))
63+
requires I0 +Int NUM0 <=Int I1 orBool I1 +Int NUM1 <=Int I0 [simplification]
64+
```
65+
66+
The rule below handles a termination edge case: it immediately marks the operation as complete with reduced priority to avoid infinite rewriting cycles.
67+
68+
```k
69+
rule #WB(false, I0, V0, NUM0, #WB(true, I1, V1, NUM1, B:SparseBytes)) => #WB(true, I0, V0, NUM0, #WB(true, I1, V1, NUM1, B)) [simplification(60)]
70+
```
71+
72+
**Write Consolidation**: When multiple write operations target the same symbolic index with equal byte counts (`NUM0 == NUM1`), the rules merge them by eliminating the older write operation. When the new write has fewer bytes than the existing one (`NUM1 < NUM0`), the smaller write is also eliminated to maintain simplicity.
73+
74+
```k
75+
rule #WB(false, I0, V0, NUM0, #WB(_, I1, _, NUM1, B:SparseBytes)) => #WB(false, I0, V0, NUM0, B)
76+
requires I0 <=Int I1 andBool I1 +Int NUM1 <=Int I0 +Int NUM0 [simplification(45)]
77+
```
3378

3479
## writeByteBF
3580

src/kriscv/kdist/riscv-semantics/sparse-bytes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ We provide helpers to prepend either data or an empty region to an existing `Spa
8787
| writeBytesEF(Int, Int, Int, SparseBytesEF) [function, total]
8888
| writeBytesBF(Int, Int, Int, SparseBytesBF) [function, total]
8989
90-
rule writeBytes(I, V, NUM, BF:SparseBytesBF) => writeBytesBF(I, V, NUM, BF)
91-
rule writeBytes(I, V, NUM, EF:SparseBytesEF) => writeBytesEF(I, V, NUM, EF)
90+
rule writeBytes(I, V, NUM, BF:SparseBytesBF) => writeBytesBF(I, V, NUM, BF) [concrete]
91+
rule writeBytes(I, V, NUM, EF:SparseBytesEF) => writeBytesEF(I, V, NUM, EF) [concrete]
9292
9393
rule writeBytesEF(I, _, _ , _ ) => .SparseBytes requires I <Int 0 // error case for totality
9494
rule writeBytesEF(I, V, NUM, .SparseBytes) => prependEmpty(I, #bytes(Int2Bytes(NUM, V, LE)) .SparseBytes) requires I >=Int 0
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module STORE-SYMBOLIC-INDEX-OVERLAP-COUNTER
2+
imports RISCV
3+
4+
claim [id]:
5+
<instrs> #CHECK_HALT => #HALT </instrs>
6+
<regs>
7+
.Map
8+
</regs>
9+
<pc> 0 </pc>
10+
<mem>
11+
// Perfect overlapping writes - I0's write completely covers I1's write
12+
storeBytes(I0, V1, 2,
13+
storeBytes(I1, V0, 1, #bytes ( b"\x00\x00" ) .SparseBytes))
14+
=>
15+
// Only I0's write remains since it completely covers I1's write
16+
#WB(true, I0, V1, 2, #bytes ( b"\x00\x00" ) .SparseBytes)
17+
</mem>
18+
<haltCond> ADDRESS ( 0 ) </haltCond>
19+
requires // Perfect overlapping case: I1 is completely within I0's write range
20+
I0 <=Int I1 andBool I1 +Int 1 <=Int I0 +Int 2 // I1's write [I1, I1+1) is within I0's write [I0, I0+2)
21+
endmodule
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module STORE-SYMBOLIC-INDEX-PARTIAL-OVERLAP
2+
imports RISCV
3+
4+
claim [id]:
5+
<instrs> #CHECK_HALT => #HALT </instrs>
6+
<regs>
7+
.Map
8+
</regs>
9+
<pc> 0 </pc>
10+
<mem>
11+
// Partial overlapping writes - only part of the writes overlap
12+
storeBytes(I0, V1, 2,
13+
storeBytes(I1, V0, 2, #bytes ( b"\x00\x00\x00\x00" ) .SparseBytes))
14+
=>
15+
// Ordering is preserved
16+
#WB(true, I0, V1, 2,
17+
#WB(true, I1, V0, 2, #bytes ( b"\x00\x00\x00\x00" ) .SparseBytes))
18+
</mem>
19+
<haltCond> ADDRESS ( 0 ) </haltCond>
20+
requires // Partial overlapping case: I0 starts 1 byte after I1, so they overlap by 1 byte
21+
I0 ==Int I1 +Int 1 // I1 writes [I1, I1+2), I0 writes [I1+1, I1+3), overlap at [I1+1, I1+2)
22+
endmodule
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module STORE-SYMBOLIC-INDEX-VALUE
2+
imports RISCV
3+
4+
claim [id]:
5+
<instrs> #CHECK_HALT => #HALT </instrs>
6+
<regs>
7+
.Map
8+
</regs>
9+
<pc> 0 </pc>
10+
<mem>
11+
storeBytes(0 , V6, 4,
12+
storeBytes(I2, V5, 4,
13+
storeBytes(I1, V4, 2,
14+
storeBytes(I0, V3, 4,
15+
storeBytes(I2, V2, 2,
16+
storeBytes(I1, V1, 4,
17+
storeBytes(I0, V0, 4, #bytes ( b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) .SparseBytes)))))))
18+
=> // No way to simplify this without any information about the index. Just eliminate the writes on the same index.
19+
#WB(true, I1, V4, 2,
20+
#WB(true, I1, V1, 4,
21+
#WB(true, I0, V3, 4,
22+
#WB(true, I2, V5, 4,
23+
#bytes (Int2Bytes(4, V6, LE) +Bytes b"\x00\x00\x00\x00\x00\x00\x00\x00") .SparseBytes))))
24+
</mem>
25+
<haltCond> ADDRESS ( 0 ) </haltCond>
26+
requires // Ensure truly non-overlapping writes
27+
// I0 writes 4 bytes [I0, I0+4), must not overlap with [0, 4)
28+
4 <=Int I0 andBool
29+
// I1 writes 4 bytes [I1, I1+4), must not overlap with I0 [I0, I0+4)
30+
I0 +Int 4 <=Int I1 andBool
31+
// I2 writes 4 bytes [I2, I2+4), must not overlap with I1 [I1, I1+4)
32+
I1 +Int 4 <=Int I2
33+
endmodule

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)