File: bvdomain.cry

package info (click to toggle)
haskell-what4 1.5.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,240 kB
  • sloc: haskell: 34,630; makefile: 5
file content (287 lines) | stat: -rw-r--r-- 9,516 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*

This file gives Cryptol implementations for transferring between
the various bitvector domain representations and proofs of the
correctness of these operations.
*/

module bvdomain where

import arithdomain as A
import bitsdomain as B
import xordomain as X


// Precondition `x <= mask`.  Find the (arithmetically) smallest
//  `z` above `x` which is bitwise above `mask`.  In other words
// find the smallest `z` such that `x <= z` and `mask || z == z`.

bitwise_round_above : {n} (fin n, n >= 1) => [n] -> [n] -> [n]
bitwise_round_above x mask = (x && ~q) ^ (mask && q)
  where
  q = A::fillright_alt ((x || mask) ^ x)

bra_correct1 : {n} (fin n, n>=1) => [n] -> [n] -> Bit
bra_correct1 x mask = mask <= x ==> (x <= q /\ B::bitle mask q)
  where
  q = bitwise_round_above x mask

bra_correct2 : {n} (fin n, n>=1) => [n] -> [n] -> [n] -> Bit
bra_correct2 x mask q' = (x <= q' /\ B::bitle mask q') ==> q <= q'
  where
  q = bitwise_round_above x mask

property bra1 = bra_correct1`{64}
property bra2 = bra_correct2`{64}


// Precondition `lomask <= x <= himask` and `lomask || himask == himask`.
// Find the (arithmetically) smallest `z` above `x` which is bitwise between
// `lomask` and `himask`.  In otherwords, find the smallest `z` such that
//  `x <= z` and `lomask || z = z` and `z || himask == himask`.
bitwise_round_between : {n} (fin n, n >= 1) => [n] -> [n] -> [n] -> [n]
bitwise_round_between x lomask himask = if r == 0 then loup else final
  // Read these steps from the bottom up...
  where

  // Finally mask out the low bits and only set those requried by the lomask
  final = (upper && ~lowbits) || lomask

  // add the correcting bit and mask out any extraneous bits set in
  // the previous step
  upper = (z + highbit) && himask

  // set ourselves up so that when we add the high bit to correct,
  // the carry will ripple until it finds a bit position that we
  // are allowed to set.
  z = loup || ~himask

  // isolate just the highest incorrect bit
  highbit = rmask ^ lowbits

  // A mask for all the bits lower than the high bit of r
  lowbits = rmask >> 1

  // set all the bits to the right of the highest incorrect bit
  rmask = A::fillright_alt r

  // now compute all the bits that are set that are not allowed
  // to be set according to the himask
  r = loup && ~himask

  // first, round up to the lomask
  loup = bitwise_round_above x lomask


brb_correct1 : {n} (fin n, n>=1) => [n] -> [n] -> [n] -> Bit
brb_correct1 x lomask himask =
    (B::bitle lomask himask /\ lomask <= x /\ x <= himask) ==>
    (x <= q /\ B::bitle lomask q /\ B::bitle q himask)

  where
  q = bitwise_round_between x lomask himask

brb_correct2 : {n} (fin n, n>=1) => [n] -> [n] -> [n] -> [n] -> Bit
brb_correct2 x lomask himask q' = (x <= q' /\ B::bitle lomask q' /\ B::bitle q' himask) ==> q <= q'
  where
  q = bitwise_round_between x lomask himask

property brb1 = brb_correct1`{64}
property brb2 = brb_correct2`{64}

// Interesting fact about arithmetic domains: the low values of the two domains
// represent overlap candidates.  If neither low value is contained in the other domain,
// then they do not overlap.
arith_overlap_candidates : {n} (fin n, n >= 1) => A::Dom n -> A::Dom n -> [n] -> Bit
arith_overlap_candidates a b x =
  A::mem a x ==>
  A::mem b x ==>
  ((A::mem a b.lo /\ A::mem b b.lo) \/
   (A::mem a a.lo /\ A::mem b a.lo))

// Bitwise domains, if they overlap, must overlap in some specific points.  The bitwise
// union of the low bounds is one.
bitwise_overlap_candidates : {n} (fin n, n >= 1) => B::Dom n -> B::Dom n -> [n] -> Bit
bitwise_overlap_candidates a b x =
  B::mem a x ==>
  B::mem b x ==>
  (B::mem a witness /\ B::mem b witness)

 where
 witness = a.lomask || b.lomask

// If mixed domains have some common value, then they must definintely overlap at one
// of the following three listed candidate points.
mixed_overlap_candidates : {n} (fin n, n >= 1) => A::Dom n -> B::Dom n -> [n] -> Bit
mixed_overlap_candidates a b x =
  A::mem a x ==>
  B::mem b x ==>
  (A::mem a b.lomask /\ B::mem b b.lomask) \/
  (A::mem a b.himask /\ B::mem b b.himask) \/
  (A::mem a next     /\ B::mem b next)

 where
 next = bitwise_round_between a.lo b.lomask b.himask


// A mixed domain overlap test.  It relies on testing special candidate overlap values.
//
// If none of the overlap candidates are found in both domains, then the domains do not overlap.
// On the other hand, if any canadiate is in both domains, it is a constructive witness of
// overlap.
mixed_domain_overlap : {n} (fin n, n >= 1) => A::Dom n -> B::Dom n -> Bit
mixed_domain_overlap a b =
  A::mem a b.lomask \/ A::mem a b.himask \/ A::mem a (bitwise_round_between a.lo b.lomask b.himask)

// If mixed domains have a common element, the overlap test will be true.
correct_mixed_domain_overlap : {n} (fin n, n >= 1) => A::Dom n -> B::Dom n -> [n] -> Bit
correct_mixed_domain_overlap a b x =
  A::mem a x ==>
  B::mem b x ==>
  mixed_domain_overlap a b

// If the overlap test is true, then we can find some element they share in common,
// provided the bitwise domain is nonempty.
correct_mixed_domain_overlap_inv : {n} (fin n, n >= 1) => A::Dom n -> B::Dom n -> Bit
correct_mixed_domain_overlap_inv a b =
  B::nonempty b ==> mixed_domain_overlap a b ==> (A::mem a witness /\ B::mem b witness)

 where
 witness = if A::mem a b.lomask then b.lomask else
           if A::mem a b.himask then b.himask else
           bitwise_round_between a.lo b.lomask b.himask

property mx = correct_mixed_domain_overlap`{64}
property mx_inv = correct_mixed_domain_overlap_inv`{64}


// Operations that transfer between the domains

arithToBitDom : {n} (fin n, n >= 1) => A::Dom n -> B::Dom n
arithToBitDom a = { lomask = lo, himask = hi }
  where
  u  = A::unknowns a
  hi = a.lo || u
  lo = hi ^ u

bitToArithDom : {n} (fin n) => B::Dom n -> A::Dom n
bitToArithDom b = A::range b.lomask b.himask

bitToXorDom : {n} (fin n) => B::Dom n -> X::Dom n
bitToXorDom b = { val = b.himask, unknown = b.lomask ^ b.himask }

xorToBitDom : {n} (fin n) => X::Dom n -> B::Dom n
xorToBitDom x = { lomask = x.val ^ x.unknown, himask = x.val }

arithToXorDom : {n} (fin n, n >= 1) => A::Dom n -> X::Dom n
arithToXorDom a = { val = a.lo || u, unknown = u }
  where
  u = A::unknowns a

// A small collection of operations that start in one
// domain and end in the other

popcount : {n} (fin n, n>=1) => [n] -> [n]
popcount bs = sum [ zero#[b] | b <- bs ]

countLeadingZeros : {n} (fin n, n>=1) => [n] -> [n]
countLeadingZeros x = loop 0
 where
 loop n =
   if n >= length x then
     length x
   else
     if x@n then n else loop (n+1)

countTrailingZeros : {n} (fin n, n>=1) => [n] -> [n]
countTrailingZeros xs = countLeadingZeros (reverse xs)



popcnt : {n} (fin n, n>=1) => B::Dom n -> A::Dom n
popcnt b = A::range lo hi
  where
  lo = popcount b.lomask
  hi = popcount b.himask

clz : {n} (fin n, n>=1) => B::Dom n -> A::Dom n
clz b = A::range lo hi
 where
 lo = countLeadingZeros b.himask
 hi = countLeadingZeros b.lomask

ctz : {n} (fin n, n>=1) => B::Dom n -> A::Dom n
ctz b = A::range lo hi
 where
 lo = countTrailingZeros b.himask
 hi = countTrailingZeros b.lomask


//////////////////////////////////////////////////////////////
// Correctness properties

correct_arithToBitDom : {n} (fin n, n >= 1) => A::Dom n -> [n] -> Bit
correct_arithToBitDom a x =
  A::mem a x ==> B::mem (arithToBitDom a) x

correct_bitToArithDom : {n} (fin n) => B::Dom n -> [n] -> Bit
correct_bitToArithDom b x =
  B::mem b x ==> A::mem (bitToArithDom b) x

correct_bitToXorDom : {n} (fin n) => B::Dom n -> [n] -> Bit
correct_bitToXorDom b x =
  B::mem b x == X::mem (bitToXorDom b) x

correct_xorToBitDom : {n} (fin n) => X::Dom n -> [n] -> Bit
correct_xorToBitDom b x =
  X::mem b x == B::mem (xorToBitDom b) x

correct_arithToXorDom : {n} (fin n, n >= 1) => A::Dom n -> [n] -> Bit
correct_arithToXorDom a x =
  A::mem a x ==> X::mem (arithToXorDom a) x

property t1 = correct_arithToBitDom`{16}
property t2 = correct_bitToArithDom`{16}
property t3 = correct_bitToXorDom`{16}
property t4 = correct_xorToBitDom`{16}
property t5 = correct_arithToXorDom`{16}

correct_popcnt : {n} (fin n, n>=1) => B::Dom n -> [n] -> Bit
correct_popcnt a x =
  B::mem a x ==> A::mem (popcnt a) (popcount x)

correct_clz : {n} (fin n, n>=1) => B::Dom n -> [n] -> Bit
correct_clz a x =
  B::mem a x ==> A::mem (clz a) (countLeadingZeros x)

correct_ctz : {n} (fin n, n>=1) => B::Dom n -> [n] -> Bit
correct_ctz a x =
  B::mem a x ==> A::mem (ctz a) (countTrailingZeros x)

property w1 = correct_popcnt`{16}
property w2 = correct_clz`{16}
property w3 = correct_ctz`{16}

////////////////////////////////////////////////////////////////
// Proofs that the XOR domain is really just an alternate way
// to compute the same thing as the bitsdomain operations.
// For "band" this requires the input domains to be nonempty,
// which should be the case for all actual values of interest.

equiv_bxor : {n} (fin n) => B::Dom n -> B::Dom n -> Bit
equiv_bxor a b =
  B::bxor a b == xorToBitDom (X::bxor (bitToXorDom a) (bitToXorDom b))

equiv_band : {n} (fin n) => B::Dom n -> B::Dom n -> Bit
equiv_band a b =
  B::nonempty a /\ B::nonempty b ==>
  B::band a b == xorToBitDom (X::band (bitToXorDom a) (bitToXorDom b))

equiv_band_scalar : {n} (fin n) => B::Dom n -> [n] -> Bit
equiv_band_scalar a x =
  B::band a (B::singleton x) == xorToBitDom (X::band_scalar (bitToXorDom a) x)


property e1 = equiv_bxor`{16}
property e2 = equiv_band`{16}
property e3 = equiv_band_scalar`{16}