Skip to content

Commit 936833e

Browse files
authored
Add a few simplifications and definedness preservation attributes (#855)
* declares that `#intAsType` and `littleEndianFromBytes` equations preserve definedness (booster should apply them) * removes round-trips from (symbolic) `discriminant` to `variantIdx` and back * simplifies repeated bit shifts and asserts magnitude limit for byte-sliced values
1 parent 8713564 commit 936833e

File tree

3 files changed

+48
-0
lines changed

3 files changed

+48
-0
lines changed

kmir/src/kmir/kdist/mir-semantics/lemmas/kmir-lemmas.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,24 @@ Definedness of the list and list elements is also guaranteed.
7474
[simplification]
7575
```
7676

77+
## Simplifications for `enum` Discriminants and Variant Indexes
78+
79+
For symbolic enum values, the variant index remains unevaluated but the original (symbolic) discriminant can be restored:
80+
81+
```k
82+
rule #lookupDiscriminant(typeInfoEnumType(_, _, _, _, _), #findVariantIdxAux(DISCR, DISCRS, _IDX)) => DISCR
83+
requires isOneOf(DISCR, DISCRS)
84+
[simplification, preserves-definedness, symbolic(DISCR)]
85+
86+
syntax Bool ::= isOneOf ( Int , Discriminants ) [function, total]
87+
// --------------------------------------------------------------
88+
rule isOneOf( _, .Discriminants ) => false
89+
rule isOneOf( I, discriminant(D) .Discriminants ) => I ==Int D
90+
rule isOneOf( I, discriminant(mirInt(D)) .Discriminants ) => I ==Int D
91+
rule isOneOf( I, discriminant(D) ((discriminant(_) _MORE) #as REST)) => I ==Int D orBool isOneOf(I, REST)
92+
rule isOneOf( I, discriminant(mirInt(D)) ((discriminant(_) _MORE) #as REST)) => I ==Int D orBool isOneOf(I, REST)
93+
```
94+
7795
## Simplifications for Int
7896

7997
These are trivial simplifications driven by syntactic equality, which should be present upstream.
@@ -130,6 +148,14 @@ power of two but the semantics will always operate with these particular ones.
130148
rule VAL &Int bitmask128 => VAL requires 0 <=Int VAL andBool VAL <=Int bitmask128 [simplification, preserves-definedness, smt-lemma]
131149
```
132150

151+
Repeated bit-masking can be simplified in an even more general way:
152+
153+
```k
154+
rule (X &Int MASK &Int MASK) => X &Int MASK
155+
requires 0 <Int MASK
156+
[simplification, smt-lemma]
157+
```
158+
133159
Support for `transmute` between byte arrays and numbers (and vice-versa) uses computations involving bit masks with 255 and 8-bit shifts.
134160
To support simplifying round-trip conversion, the following simplifications are essential.
135161

@@ -165,6 +191,24 @@ To support simplifying round-trip conversion, the following simplifications are
165191
[simplification, preserves-definedness, symbolic(VAL)]
166192
```
167193

194+
More generally, a value which is composed of sliced bytes can generally be assumed to be in range of a suitable bitmask for the byte length.
195+
This avoids building up large expressions related to overflow checks and vacuous branches leading to overflow errors.
196+
197+
```k
198+
rule ((( _X0 ) &Int 255) +Int 256 *Int (
199+
(( _X1 >>Int 8) &Int 255) +Int 256 *Int (
200+
(( _X2 >>Int 8 >>Int 8) &Int 255) +Int 256 *Int (
201+
(( _X3 >>Int 8 >>Int 8 >>Int 8) &Int 255) +Int 256 *Int (
202+
(( _X4 >>Int 8 >>Int 8 >>Int 8 >>Int 8) &Int 255) +Int 256 *Int (
203+
(( _X5 >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8) &Int 255) +Int 256 *Int (
204+
(( _X6 >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8) &Int 255) +Int 256 *Int (
205+
(( _X7 >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8) &Int 255)))))))))
206+
<=Int bitmask64
207+
=> true
208+
[simplification, preserves-definedness, symbolic]
209+
```
210+
211+
168212
For the case where the (symbolic) byte values are first converted to a number, the round-trip simplification requires different matching.
169213
First, the bit-masking with `&Int 255` eliminates `Bytes2Int(Int2Bytes(1, ..) +Bytes ..)` enclosing a byte-valued variable:
170214

kmir/src/kmir/kdist/mir-semantics/rt/data.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,7 @@ Casting a byte array/slice to an integer reinterprets the bytes in little-endian
15061506
rule #littleEndianFromBytes(.List) => 0
15071507
rule #littleEndianFromBytes(ListItem(Integer(BYTE, 8, false)) REST)
15081508
=> BYTE +Int 256 *Int #littleEndianFromBytes(REST)
1509+
[preserves-definedness]
15091510
```
15101511

15111512
Casting an integer to a `[u8; N]` array materialises its little-endian bytes.

kmir/src/kmir/kdist/mir-semantics/rt/numbers.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,14 @@ This truncation function is instrumental in the implementation of Integer arithm
127127
true
128128
)
129129
requires #bitWidth(INTTYPE) <=Int WIDTH
130+
[preserves-definedness]
130131
131132
// widening: nothing to do: VAL does not change (enough bits to represent, no sign change possible)
132133
rule #intAsType(VAL, WIDTH, INTTYPE:IntTy)
133134
=>
134135
Integer(VAL, #bitWidth(INTTYPE), true)
135136
requires WIDTH <Int #bitWidth(INTTYPE)
137+
[preserves-definedness]
136138
137139
// converting to unsigned int types (simple bitmask)
138140
rule #intAsType(VAL, _, UINTTYPE:UintTy)
@@ -142,6 +144,7 @@ This truncation function is instrumental in the implementation of Integer arithm
142144
#bitWidth(UINTTYPE),
143145
false
144146
)
147+
[preserves-definedness]
145148
```
146149

147150
## Alignment of Primitives

0 commit comments

Comments
 (0)