Skip to content

Commit a6dc6f1

Browse files
authored
Update from master (#857)
* `assert_inhabited` implementation * `stable-mir-json` upgrade * simplifications for bit-shifts/masks in conversions
2 parents d869ae3 + 4997e79 commit a6dc6f1

24 files changed

+255
-132
lines changed

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-
cf0410fba16002276f6fcdc195b7003e145558e9
1+
20a1bbdfc1fb36fb19539d0d4d91f5a437be7de6

flake.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
flake-utils.url = "github:numtide/flake-utils";
88

9-
stable-mir-json-flake.url = "github:runtimeverification/stable-mir-json/cf0410fba16002276f6fcdc195b7003e145558e9";
9+
stable-mir-json-flake.url = "github:runtimeverification/stable-mir-json/20a1bbdfc1fb36fb19539d0d4d91f5a437be7de6";
1010
stable-mir-json-flake = {
1111
inputs.nixpkgs.follows = "nixpkgs";
1212
inputs.flake-utils.follows = "flake-utils";

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,29 @@ before it's used. They have no effect on program semantics, and are implemented
5353
rule <k> #execIntrinsic(IntrinsicFunction(symbol("prefetch_write_instruction")), _ARG1:Operand _ARG2:Operand .Operands, _DEST) => .K ... </k>
5454
```
5555

56+
#### Assert Inhabited (`std::intrinsics::assert_inhabited`)
57+
58+
The `assert_inhabited` instrinsic asserts that a type is "inhabited" (able to be instantiated). Types such as
59+
`Never` (`!`) cannot be instantiated and are uninhabited types. The target / codegen decides how to handle this
60+
intrinsic, it is not required to panic if the type is not inhabited, it could also perform a NO OP. We have witnessed
61+
in the case that there is an uninhabited type that the following basic block is `noBasicBlockIdx`, and so we
62+
error with `#AssertInhabitedFailure` if we see that following the intrinsic. Otherwise, we perform a NO OP.
63+
64+
```k
65+
syntax MIRError ::= "AssertInhabitedFailure"
66+
rule <k> #execIntrinsic(IntrinsicFunction(symbol("assert_inhabited")), .Operands, _DEST)
67+
~> #continueAt(noBasicBlockIdx)
68+
=> AssertInhabitedFailure
69+
...
70+
</k>
71+
72+
rule <k> #execIntrinsic(IntrinsicFunction(symbol("assert_inhabited")), .Operands, _DEST)
73+
=> .K
74+
...
75+
</k>
76+
[owise]
77+
```
78+
5679
#### Raw Eq (`std::intrinsics::raw_eq`)
5780

5881
The `raw_eq` intrinsic performs byte-by-byte equality comparison of the memory contents pointed to by two references.

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

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file contains basic lemmas required for symbolic execution of MIR programs
44

55
Lemmas are simpliciations of symbolic function application that aims to confirm conditions for rewrite rules to avoid spurious branching on symbolic program parts.
66

7-
Some of the lemmas relate to the control flow implementation in `kmir.md` and will be needed in various proofs (for instance the simplification of list size for partially-symbolic lists of locals or stack frames).
7+
Some of the lemmas relate to the control flow implementation in `kmir.md` and will be needed in various proofs (for instance the simplification of list size for partially-symbolic lists of locals or stack frames).
88
Others are related to helper functions used for integer arithmetic.
99

1010
```k
@@ -20,7 +20,7 @@ module KMIR-LEMMAS
2020
```
2121
## Simplifications for lists to avoid spurious branching on error cases in control flow
2222

23-
Rewrite rules that look up locals or stack frames require that an index into the respective `List`s in the configuration be within the bounds of the locals list/stack. Therefore, the `size` function on lists needs to be computed. The following simplifications allow for locals and stacks to have concrete values in the beginning but a symbolic rest (of unknown size).
23+
Rewrite rules that look up locals or stack frames require that an index into the respective `List`s in the configuration be within the bounds of the locals list/stack. Therefore, the `size` function on lists needs to be computed. The following simplifications allow for locals and stacks to have concrete values in the beginning but a symbolic rest (of unknown size).
2424
The lists used in the semantics are cons-lists, so only rules with a head element match are required.
2525

2626
```k
@@ -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
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![feature(core_intrinsics)]
2+
#![feature(never_type)]
3+
use std::intrinsics::assert_inhabited;
4+
5+
fn main() {
6+
std::hint::black_box(
7+
assert_inhabited::<!>() // Up to compiler/CodegenBackend to panic or NOOP
8+
);
9+
}

kmir/src/tests/integration/data/prove-rs/intrinsics.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
#![feature(core_intrinsics)]
2-
use std::intrinsics;
2+
use std::intrinsics::{self, assert_inhabited};
33

44
fn main() {
55
intrinsics::cold_path();
66
let b = true;
77
intrinsics::likely(b);
88
intrinsics::unlikely(b);
99
prefetch();
10+
assert_inhabited::<i32>(); // Up to compiler/CodegenBackend to panic or NOOP
11+
std::hint::black_box(
12+
assert_inhabited::<()>() // Trying to stop being optimised away
13+
);
1014
}
1115

1216
fn prefetch() {

0 commit comments

Comments
 (0)