• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

formalsec / smtml / 286

24 Feb 2025 11:56PM UTC coverage: 48.02% (-0.03%) from 48.051%
286

push

github

filipeom
Update `CHANGES.md`

1576 of 3282 relevant lines covered (48.02%)

33.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

44.61
/src/expr.ml
1
(* SPDX-License-Identifier: MIT *)
2
(* Copyright (C) 2023-2024 formalsec *)
3
(* Written by the Smtml programmers *)
4

5
type t = expr Hc.hash_consed
6

7
and expr =
8
  | Val of Value.t
9
  | Ptr of
10
      { base : int32
11
      ; offset : t
12
      }
13
  | Symbol of Symbol.t
14
  | List of t list
15
  | App of Symbol.t * t list
16
  | Unop of Ty.t * Ty.Unop.t * t
17
  | Binop of Ty.t * Ty.Binop.t * t * t
18
  | Triop of Ty.t * Ty.Triop.t * t * t * t
19
  | Relop of Ty.t * Ty.Relop.t * t * t
20
  | Cvtop of Ty.t * Ty.Cvtop.t * t
21
  | Naryop of Ty.t * Ty.Naryop.t * t list
22
  | Extract of t * int * int
23
  | Concat of t * t
24
  | Binder of Binder.t * t list * t
25

26
module Expr = struct
27
  type t = expr
28

29
  let list_eq (l1 : 'a list) (l2 : 'a list) : bool =
30
    if List.compare_lengths l1 l2 = 0 then List.for_all2 phys_equal l1 l2
4✔
31
    else false
×
32

33
  let equal (e1 : expr) (e2 : expr) : bool =
34
    match (e1, e2) with
1,437✔
35
    | Val v1, Val v2 -> Value.equal v1 v2
821✔
36
    | Ptr { base = b1; offset = o1 }, Ptr { base = b2; offset = o2 } ->
3✔
37
      Int32.equal b1 b2 && phys_equal o1 o2
3✔
38
    | Symbol s1, Symbol s2 -> Symbol.equal s1 s2
415✔
39
    | List l1, List l2 -> list_eq l1 l2
4✔
40
    | App (s1, l1), App (s2, l2) -> Symbol.equal s1 s2 && list_eq l1 l2
×
41
    | Unop (t1, op1, e1), Unop (t2, op2, e2) ->
31✔
42
      Ty.equal t1 t2 && Ty.Unop.equal op1 op2 && phys_equal e1 e2
31✔
43
    | Binop (t1, op1, e1, e3), Binop (t2, op2, e2, e4) ->
88✔
44
      Ty.equal t1 t2 && Ty.Binop.equal op1 op2 && phys_equal e1 e2
88✔
45
      && phys_equal e3 e4
88✔
46
    | Relop (t1, op1, e1, e3), Relop (t2, op2, e2, e4) ->
36✔
47
      Ty.equal t1 t2 && Ty.Relop.equal op1 op2 && phys_equal e1 e2
36✔
48
      && phys_equal e3 e4
36✔
49
    | Triop (t1, op1, e1, e3, e5), Triop (t2, op2, e2, e4, e6) ->
4✔
50
      Ty.equal t1 t2 && Ty.Triop.equal op1 op2 && phys_equal e1 e2
4✔
51
      && phys_equal e3 e4 && phys_equal e5 e6
4✔
52
    | Cvtop (t1, op1, e1), Cvtop (t2, op2, e2) ->
35✔
53
      Ty.equal t1 t2 && Ty.Cvtop.equal op1 op2 && phys_equal e1 e2
35✔
54
    | Naryop (t1, op1, l1), Naryop (t2, op2, l2) ->
×
55
      Ty.equal t1 t2 && Ty.Naryop.equal op1 op2 && list_eq l1 l2
×
56
    | Extract (e1, h1, l1), Extract (e2, h2, l2) ->
×
57
      phys_equal e1 e2 && h1 = h2 && l1 = l2
×
58
    | Concat (e1, e3), Concat (e2, e4) -> phys_equal e1 e2 && phys_equal e3 e4
×
59
    | Binder (binder1, vars1, e1), Binder (binder2, vars2, e2) ->
×
60
      Binder.equal binder1 binder2 && list_eq vars1 vars2 && phys_equal e1 e2
×
61
    | ( ( Val _ | Ptr _ | Symbol _ | List _ | App _ | Unop _ | Binop _ | Triop _
×
62
        | Relop _ | Cvtop _ | Naryop _ | Extract _ | Concat _ | Binder _ )
×
63
      , _ ) ->
64
      false
65

66
  let hash (e : expr) : int =
67
    let h x = Hashtbl.hash x in
5,394✔
68
    match e with
69
    | Val v -> h v
1,776✔
70
    | Ptr { base; offset } -> h (base, offset.tag)
13✔
71
    | Symbol s -> h s
1,081✔
72
    | List v -> h v
16✔
73
    | App (x, es) -> h (x, es)
24✔
74
    | Unop (ty, op, e) -> h (ty, op, e.tag)
155✔
75
    | Cvtop (ty, op, e) -> h (ty, op, e.tag)
83✔
76
    | Binop (ty, op, e1, e2) -> h (ty, op, e1.tag, e2.tag)
1,414✔
77
    | Relop (ty, op, e1, e2) -> h (ty, op, e1.tag, e2.tag)
740✔
78
    | Triop (ty, op, e1, e2, e3) -> h (ty, op, e1.tag, e2.tag, e3.tag)
46✔
79
    | Naryop (ty, op, es) -> h (ty, op, es)
×
80
    | Extract (e, hi, lo) -> h (e.tag, hi, lo)
16✔
81
    | Concat (e1, e2) -> h (e1.tag, e2.tag)
6✔
82
    | Binder (b, vars, e) -> h (b, vars, e.tag)
24✔
83
end
84

85
module Hc = Hc.Make [@inlined hint] (Expr)
86

87
let equal (hte1 : t) (hte2 : t) = phys_equal hte1 hte2 [@@inline]
208✔
88

89
let hash (hte : t) = hte.tag [@@inline]
23✔
90

91
module Key = struct
92
  type nonrec t = t
93

94
  let to_int hte = hash hte
7✔
95
end
96

97
module Set = struct
98
  include PatriciaTree.MakeHashconsedSet (Key) ()
99

100
  let hash = to_int
101
end
102

103
let[@inline] make e = Hc.hashcons e
3,411✔
104

105
let[@inline] view (hte : t) = hte.node
6,438✔
106

107
let[@inline] compare (hte1 : t) (hte2 : t) = compare hte1.tag hte2.tag
×
108

109
let symbol s = make (Symbol s)
748✔
110

111
let is_num (e : t) = match view e with Val (Num _) -> true | _ -> false
×
112

113
(** The return type of an expression *)
114
let rec ty (hte : t) : Ty.t =
115
  match view hte with
296✔
116
  | Val x -> Value.type_of x
54✔
117
  | Ptr _ -> Ty_bitv 32
×
118
  | Symbol x -> Symbol.type_of x
155✔
119
  | List _ -> Ty_list
×
120
  | App _ -> Ty_app
×
121
  | Unop (ty, _, _) -> ty
23✔
122
  | Binop (ty, _, _, _) -> ty
36✔
123
  | Triop (_, Ite, _, hte1, hte2) ->
×
124
    let ty1 = ty hte1 in
125
    let ty2 = ty hte2 in
×
126
    assert (Ty.equal ty1 ty2);
×
127
    ty1
128
  | Triop (ty, _, _, _, _) -> ty
×
129
  | Relop (ty, _, _, _) -> ty
8✔
130
  | Cvtop (_, (Zero_extend m | Sign_extend m), hte) -> (
×
131
    match ty hte with Ty_bitv n -> Ty_bitv (n + m) | _ -> assert false )
1✔
132
  | Cvtop (ty, _, _) -> ty
17✔
133
  | Naryop (ty, _, _) -> ty
×
134
  | Extract (_, h, l) -> Ty_bitv ((h - l) * 8)
2✔
135
  | Concat (e1, e2) -> (
×
136
    match (ty e1, ty e2) with
×
137
    | Ty_bitv n1, Ty_bitv n2 -> Ty_bitv (n1 + n2)
×
138
    | t1, t2 ->
×
139
      Fmt.failwith "Invalid concat of (%a) with (%a)" Ty.pp t1 Ty.pp t2 )
140
  | Binder (_, _, e) -> ty e
×
141

142
let rec is_symbolic (v : t) : bool =
143
  match view v with
×
144
  | Val _ -> false
×
145
  | Symbol _ -> true
×
146
  | Ptr { offset; _ } -> is_symbolic offset
×
147
  | List vs -> List.exists is_symbolic vs
×
148
  | App (_, vs) -> List.exists is_symbolic vs
×
149
  | Unop (_, _, v) -> is_symbolic v
×
150
  | Binop (_, _, v1, v2) -> is_symbolic v1 || is_symbolic v2
×
151
  | Triop (_, _, v1, v2, v3) ->
×
152
    is_symbolic v1 || is_symbolic v2 || is_symbolic v3
×
153
  | Cvtop (_, _, v) -> is_symbolic v
×
154
  | Relop (_, _, v1, v2) -> is_symbolic v1 || is_symbolic v2
×
155
  | Naryop (_, _, vs) -> List.exists is_symbolic vs
×
156
  | Extract (e, _, _) -> is_symbolic e
×
157
  | Concat (e1, e2) -> is_symbolic e1 || is_symbolic e2
×
158
  | Binder (_, _, e) -> is_symbolic e
×
159

160
let get_symbols (hte : t list) =
161
  let tbl = Hashtbl.create 64 in
×
162
  let rec symbols (hte : t) =
×
163
    match view hte with
×
164
    | Val _ -> ()
×
165
    | Ptr { offset; _ } -> symbols offset
×
166
    | Symbol s -> Hashtbl.replace tbl s ()
×
167
    | List es -> List.iter symbols es
×
168
    | App (_, es) -> List.iter symbols es
×
169
    | Unop (_, _, e1) -> symbols e1
×
170
    | Binop (_, _, e1, e2) ->
×
171
      symbols e1;
172
      symbols e2
×
173
    | Triop (_, _, e1, e2, e3) ->
×
174
      symbols e1;
175
      symbols e2;
×
176
      symbols e3
×
177
    | Relop (_, _, e1, e2) ->
×
178
      symbols e1;
179
      symbols e2
×
180
    | Cvtop (_, _, e) -> symbols e
×
181
    | Naryop (_, _, es) -> List.iter symbols es
×
182
    | Extract (e, _, _) -> symbols e
×
183
    | Concat (e1, e2) ->
×
184
      symbols e1;
185
      symbols e2
×
186
    | Binder (_, vars, e) ->
×
187
      List.iter symbols vars;
188
      symbols e
×
189
  in
190
  List.iter symbols hte;
191
  Hashtbl.fold (fun k () acc -> k :: acc) tbl []
×
192

193
let negate_relop (hte : t) : (t, string) Result.t =
194
  let e =
×
195
    match view hte with
196
    | Relop (ty, Eq, e1, e2) -> Ok (Relop (ty, Ne, e1, e2))
×
197
    | Relop (ty, Ne, e1, e2) -> Ok (Relop (ty, Eq, e1, e2))
×
198
    | Relop (ty, Lt, e1, e2) -> Ok (Relop (ty, Ge, e1, e2))
×
199
    | Relop (ty, LtU, e1, e2) -> Ok (Relop (ty, GeU, e1, e2))
×
200
    | Relop (ty, Le, e1, e2) -> Ok (Relop (ty, Gt, e1, e2))
×
201
    | Relop (ty, LeU, e1, e2) -> Ok (Relop (ty, GtU, e1, e2))
×
202
    | Relop (ty, Gt, e1, e2) -> Ok (Relop (ty, Le, e1, e2))
×
203
    | Relop (ty, GtU, e1, e2) -> Ok (Relop (ty, LeU, e1, e2))
×
204
    | Relop (ty, Ge, e1, e2) -> Ok (Relop (ty, Lt, e1, e2))
×
205
    | Relop (ty, GeU, e1, e2) -> Ok (Relop (ty, LtU, e1, e2))
×
206
    | _ -> Error "negate_relop: not a relop."
×
207
  in
208
  Result.map make e
209

210
module Pp = struct
211
  let rec pp fmt (hte : t) =
212
    match view hte with
×
213
    | Val v -> Value.pp fmt v
×
214
    | Ptr { base; offset } -> Fmt.pf fmt "(Ptr (i32 %ld) %a)" base pp offset
×
215
    | Symbol s -> Fmt.pf fmt "@[<hov 1>%a@]" Symbol.pp s
×
216
    | List v -> Fmt.pf fmt "@[<hov 1>[%a]@]" (Fmt.list ~sep:Fmt.comma pp) v
×
217
    | App (s, v) ->
×
218
      Fmt.pf fmt "@[<hov 1>(%a@ %a)@]" Symbol.pp s
219
        (Fmt.list ~sep:Fmt.comma pp)
×
220
        v
221
    | Unop (ty, op, e) ->
×
222
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Unop.pp op pp e
223
    | Binop (ty, op, e1, e2) ->
×
224
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Binop.pp op pp e1 pp
225
        e2
226
    | Triop (ty, op, e1, e2, e3) ->
×
227
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a@ %a)@]" Ty.pp ty Ty.Triop.pp op pp e1
228
        pp e2 pp e3
229
    | Relop (ty, op, e1, e2) ->
×
230
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Relop.pp op pp e1 pp
231
        e2
232
    | Cvtop (ty, op, e) ->
×
233
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Cvtop.pp op pp e
234
    | Naryop (ty, op, es) ->
×
235
      Fmt.pf fmt "@[<hov 1>(%a.%a@ (%a))@]" Ty.pp ty Ty.Naryop.pp op
236
        (Fmt.list ~sep:Fmt.comma pp)
×
237
        es
238
    | Extract (e, h, l) ->
×
239
      Fmt.pf fmt "@[<hov 1>(extract@ %a@ %d@ %d)@]" pp e l h
240
    | Concat (e1, e2) -> Fmt.pf fmt "@[<hov 1>(++@ %a@ %a)@]" pp e1 pp e2
×
241
    | Binder (b, vars, e) ->
×
242
      Fmt.pf fmt "@[<hov 1>(%a@ (%a)@ %a)@]" Binder.pp b
243
        (Fmt.list ~sep:Fmt.sp pp) vars pp e
×
244

245
  let pp_list fmt (es : t list) = Fmt.hovbox (Fmt.list ~sep:Fmt.comma pp) fmt es
×
246

247
  let pp_smt fmt (es : t list) : unit =
248
    let pp_symbols fmt syms =
×
249
      Fmt.list ~sep:Fmt.semi
×
250
        (fun fmt sym ->
251
          let t = Symbol.type_of sym in
×
252
          Fmt.pf fmt "(let-const %a %a)" Symbol.pp sym Ty.pp t )
×
253
        fmt syms
254
    in
255
    let pp_asserts fmt es =
256
      Fmt.list ~sep:Fmt.semi
×
257
        (fun fmt e -> Fmt.pf fmt "(assert @[<h 2>%a@])" pp e)
×
258
        fmt es
259
    in
260
    let syms = get_symbols es in
261
    if List.length syms > 0 then Fmt.pf fmt "%a@\n" pp_symbols syms;
×
262
    if List.length es > 0 then Fmt.pf fmt "%a@\n" pp_asserts es;
×
263
    Fmt.string fmt "(check-sat)"
×
264
end
265

266
let pp = Pp.pp
267

268
let pp_list = Pp.pp_list
269

270
let pp_smt = Pp.pp_smt
271

272
let to_string e = Fmt.str "%a" pp e
×
273

274
let value (v : Value.t) : t = make (Val v) [@@inline]
1,248✔
275

276
let ptr base offset = make (Ptr { base; offset })
8✔
277

278
let app symbol args = make (App (symbol, args))
12✔
279

280
let[@inline] binder bt vars expr = make (Binder (bt, vars, expr))
10✔
281

282
let let_in vars body = binder Let_in vars body
8✔
283

284
let forall vars body = binder Forall vars body
1✔
285

286
let exists vars body = binder Exists vars body
1✔
287

288
let unop' ty op hte = make (Unop (ty, op, hte)) [@@inline]
73✔
289

290
let unop ty op hte =
291
  match (op, view hte) with
81✔
292
  | Ty.Unop.(Regexp_loop _ | Regexp_star), _ -> unop' ty op hte
×
293
  | _, Val v -> value (Eval.unop ty op v)
27✔
294
  | Not, Unop (_, Not, hte') -> hte'
2✔
295
  | Neg, Unop (_, Neg, hte') -> hte'
1✔
296
  | Trim, Cvtop (Ty_real, ToString, _) -> hte
×
297
  | Head, List (hd :: _) -> hd
1✔
298
  | Tail, List (_ :: tl) -> make (List tl)
1✔
299
  | Reverse, List es -> make (List (List.rev es))
2✔
300
  | Length, List es -> value (Int (List.length es))
1✔
301
  | _ -> unop' ty op hte
46✔
302

303
let binop' ty op hte1 hte2 = make (Binop (ty, op, hte1, hte2)) [@@inline]
721✔
304

305
let rec binop ty op hte1 hte2 =
306
  match (op, view hte1, view hte2) with
748✔
307
  | Ty.Binop.(String_in_re | Regexp_range), _, _ -> binop' ty op hte1 hte2
×
308
  | op, Val v1, Val v2 -> value (Eval.binop ty op v1 v2)
69✔
309
  | Sub, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
1✔
310
    if Int32.equal b1 b2 then binop ty Sub os1 os2 else binop' ty op hte1 hte2
×
311
  | Add, Ptr { base; offset }, _ ->
1✔
312
    ptr base (binop (Ty_bitv 32) Add offset hte2)
1✔
313
  | Sub, Ptr { base; offset }, _ ->
1✔
314
    ptr base (binop (Ty_bitv 32) Sub offset hte2)
1✔
315
  | Rem, Ptr { base; offset }, _ ->
1✔
316
    let rhs = value (Num (I32 base)) in
317
    let addr = binop (Ty_bitv 32) Add rhs offset in
1✔
318
    binop ty Rem addr hte2
1✔
319
  | Add, _, Ptr { base; offset } ->
1✔
320
    ptr base (binop (Ty_bitv 32) Add offset hte1)
1✔
321
  | Sub, _, Ptr { base; offset } ->
×
322
    binop ty Sub hte1 (binop (Ty_bitv 32) Add (value (Num (I32 base))) offset)
×
323
  | (Add | Or), Val (Num (I32 0l)), _ -> hte2
×
324
  | (And | Div | DivU | Mul | Rem | RemU), Val (Num (I32 0l)), _ -> hte1
×
325
  | (Add | Or), _, Val (Num (I32 0l)) -> hte1
×
326
  | (And | Mul), _, Val (Num (I32 0l)) -> hte2
×
327
  | Add, Binop (ty, Add, x, { node = Val v1; _ }), Val v2 ->
1✔
328
    let v = value (Eval.binop ty Add v1 v2) in
1✔
329
    binop' ty Add x v
1✔
330
  | Sub, Binop (ty, Sub, x, { node = Val v1; _ }), Val v2 ->
1✔
331
    let v = value (Eval.binop ty Add v1 v2) in
1✔
332
    binop' ty Sub x v
1✔
333
  | Mul, Binop (ty, Mul, x, { node = Val v1; _ }), Val v2 ->
1✔
334
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
335
    binop' ty Mul x v
1✔
336
  | Add, Val v1, Binop (ty, Add, x, { node = Val v2; _ }) ->
1✔
337
    let v = value (Eval.binop ty Add v1 v2) in
1✔
338
    binop' ty Add v x
1✔
339
  | Mul, Val v1, Binop (ty, Mul, x, { node = Val v2; _ }) ->
1✔
340
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
341
    binop' ty Mul v x
1✔
342
  | At, List es, Val (Int n) ->
1✔
343
    (* TODO: use another datastructure? *)
344
    begin
345
      match List.nth_opt es n with None -> assert false | Some v -> v
1✔
346
    end
347
  | List_cons, _, List es -> make (List (hte1 :: es))
1✔
348
  | List_append, List _, (List [] | Val (List [])) -> hte1
×
349
  | List_append, (List [] | Val (List [])), List _ -> hte2
×
350
  | List_append, List l0, Val (List l1) -> make (List (l0 @ List.map value l1))
1✔
351
  | List_append, Val (List l0), List l1 -> make (List (List.map value l0 @ l1))
×
352
  | List_append, List l0, List l1 -> make (List (l0 @ l1))
×
353
  | _ -> binop' ty op hte1 hte2
662✔
354

355
let triop' ty op e1 e2 e3 = make (Triop (ty, op, e1, e2, e3)) [@@inline]
21✔
356

357
let triop ty op e1 e2 e3 =
358
  match (op, view e1, view e2, view e3) with
27✔
359
  | Ty.Triop.Ite, Val True, _, _ -> e2
2✔
360
  | Ite, Val False, _, _ -> e3
1✔
361
  | op, Val v1, Val v2, Val v3 -> value (Eval.triop ty op v1 v2 v3)
4✔
362
  | _ -> triop' ty op e1 e2 e3
20✔
363

364
let relop' ty op hte1 hte2 = make (Relop (ty, op, hte1, hte2)) [@@inline]
354✔
365

366
let rec relop ty op hte1 hte2 =
367
  match (op, view hte1, view hte2) with
380✔
368
  | op, Val v1, Val v2 -> value (if Eval.relop ty op v1 v2 then True else False)
33✔
369
  | Ty.Relop.Ne, Val (Real v), _ | Ne, _, Val (Real v) ->
×
370
    if Float.is_nan v || Float.is_infinite v then value True
×
371
    else relop' ty op hte1 hte2
×
372
  | _, Val (Real v), _ | _, _, Val (Real v) ->
×
373
    if Float.is_nan v || Float.is_infinite v then value False
×
374
    else relop' ty op hte1 hte2
3✔
375
  | Eq, _, Val Nothing | Eq, Val Nothing, _ -> value False
×
376
  | Ne, _, Val Nothing | Ne, Val Nothing, _ -> value True
×
377
  | Eq, _, Val (App (`Op "symbol", [ Str _ ]))
×
378
  | Eq, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
379
    value False
380
  | Ne, _, Val (App (`Op "symbol", [ Str _ ]))
×
381
  | Ne, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
382
    value True
383
  | Eq, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
2✔
384
    if Int32.equal b1 b2 then relop Ty_bool Eq os1 os2 else value False
1✔
385
  | Ne, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
2✔
386
    if Int32.equal b1 b2 then relop Ty_bool Ne os1 os2 else value True
1✔
387
  | ( (LtU | LeU | GtU | GeU)
1✔
388
    , Ptr { base = b1; offset = os1 }
389
    , Ptr { base = b2; offset = os2 } ) ->
390
    if Int32.equal b1 b2 then relop ty op os1 os2
2✔
391
    else
392
      value
2✔
393
        (if Eval.relop ty op (Num (I32 b1)) (Num (I32 b2)) then True else False)
1✔
394
  | op, Val (Num _ as n), Ptr { base; offset = { node = Val (Num _ as o); _ } }
2✔
395
    ->
396
    let base = Eval.binop (Ty_bitv 32) Add (Num (I32 base)) o in
397
    value (if Eval.relop ty op n base then True else False)
1✔
398
  | op, Ptr { base; offset = { node = Val (Num _ as o); _ } }, Val (Num _ as n)
2✔
399
    ->
400
    let base = Eval.binop (Ty_bitv 32) Add (Num (I32 base)) o in
401
    value (if Eval.relop ty op base n then True else False)
1✔
402
  | op, List l1, List l2 -> relop_list op l1 l2
×
403
  | _, _, _ -> relop' ty op hte1 hte2
292✔
404

405
and relop_list op l1 l2 =
406
  match (op, l1, l2) with
×
407
  | Eq, [], [] -> value True
×
408
  | Eq, _, [] | Eq, [], _ -> value False
×
409
  | Eq, l1, l2 ->
×
410
    if not (List.compare_lengths l1 l2 = 0) then value False
×
411
    else
412
      List.fold_left2
×
413
        (fun acc a b ->
414
          binop Ty_bool And acc
×
415
          @@
416
          match (ty a, ty b) with
×
417
          | Ty_real, Ty_real -> relop Ty_real Eq a b
×
418
          | _ -> relop Ty_bool Eq a b )
×
419
        (value True) l1 l2
×
420
  | Ne, _, _ -> unop Ty_bool Not @@ relop_list Eq l1 l2
×
421
  | (Lt | LtU | Gt | GtU | Le | LeU | Ge | GeU), _, _ -> assert false
422

423
let cvtop' ty op hte = make (Cvtop (ty, op, hte)) [@@inline]
39✔
424

425
let cvtop ty op hte =
426
  match (op, view hte) with
56✔
427
  | Ty.Cvtop.String_to_re, _ -> cvtop' ty op hte
×
428
  | _, Val v -> value (Eval.cvtop ty op v)
30✔
429
  | String_to_float, Cvtop (Ty_real, ToString, real) -> real
×
430
  | _ -> cvtop' ty op hte
26✔
431

432
let naryop' ty op es = make (Naryop (ty, op, es)) [@@inline]
×
433

434
let naryop ty op es =
435
  if List.for_all (fun e -> match view e with Val _ -> true | _ -> false) es
×
436
  then
437
    let vs =
7✔
438
      List.map (fun e -> match view e with Val v -> v | _ -> assert false) es
20✔
439
    in
440
    value (Eval.naryop ty op vs)
7✔
441
  else naryop' ty op es
×
442

443
let nland64 (x : int64) (n : int) =
444
  let rec loop x' n' acc =
×
445
    if n' = 0 then Int64.logand x' acc
×
446
    else loop x' (n' - 1) Int64.(logor (shift_left acc 8) 0xffL)
×
447
  in
448
  loop x n 0L
449

450
let nland32 (x : int32) (n : int) =
451
  let rec loop x' n' acc =
×
452
    if n' = 0 then Int32.logand x' acc
×
453
    else loop x' (n' - 1) Int32.(logor (shift_left acc 8) 0xffl)
×
454
  in
455
  loop x n 0l
456

457
let extract' (hte : t) ~(high : int) ~(low : int) : t =
458
  make (Extract (hte, high, low))
8✔
459
[@@inline]
460

461
let extract (hte : t) ~(high : int) ~(low : int) : t =
462
  match view hte with
2✔
463
  | Val (Num (I64 x)) ->
×
464
    let x' = nland64 (Int64.shift_right x (low * 8)) (high - low) in
×
465
    value (Num (I64 x'))
×
466
  | _ -> if high - low = Ty.size (ty hte) then hte else extract' hte ~high ~low
1✔
467

468
let concat' (msb : t) (lsb : t) : t = make (Concat (msb, lsb)) [@@inline]
3✔
469

470
let concat (msb : t) (lsb : t) : t =
471
  match (view msb, view lsb) with
3✔
472
  | ( Extract ({ node = Val (Num (I64 x2)); _ }, h2, l2)
×
473
    , Extract ({ node = Val (Num (I64 x1)); _ }, h1, l1) ) ->
474
    let d1 = h1 - l1 in
475
    let d2 = h2 - l2 in
476
    let x1' = nland64 (Int64.shift_right x1 (l1 * 8)) d1 in
×
477
    let x2' = nland64 (Int64.shift_right x2 (l2 * 8)) d2 in
×
478
    let x = Int64.(logor (shift_left x2' (d1 * 8)) x1') in
×
479
    extract' (value (Num (I64 x))) ~high:(d1 + d2) ~low:0
×
480
  | ( Extract ({ node = Val (Num (I32 x2)); _ }, h2, l2)
×
481
    , Extract ({ node = Val (Num (I32 x1)); _ }, h1, l1) ) ->
482
    let d1 = h1 - l1 in
483
    let d2 = h2 - l2 in
484
    let x1' = nland32 (Int32.shift_right x1 (l1 * 8)) d1 in
×
485
    let x2' = nland32 (Int32.shift_right x2 (l2 * 8)) d2 in
×
486
    let x = Int32.(logor (shift_left x2' (d1 * 8)) x1') in
×
487
    extract' (value (Num (I32 x))) ~high:(d1 + d2) ~low:0
×
488
  | Extract (s1, h, m1), Extract (s2, m2, l) when equal s1 s2 && m1 = m2 ->
3✔
489
    extract' s1 ~high:h ~low:l
3✔
490
  | ( Extract ({ node = Val (Num (I64 x2)); _ }, h2, l2)
×
491
    , Concat
492
        ({ node = Extract ({ node = Val (Num (I64 x1)); _ }, h1, l1); _ }, se) )
493
    when not (is_num se) ->
×
494
    let d1 = h1 - l1 in
×
495
    let d2 = h2 - l2 in
496
    let x1' = nland64 (Int64.shift_right x1 (l1 * 8)) d1 in
×
497
    let x2' = nland64 (Int64.shift_right x2 (l2 * 8)) d2 in
×
498
    let x = Int64.(logor (shift_left x2' (d1 * 8)) x1') in
×
499
    concat' (extract' (value (Num (I64 x))) ~high:(d1 + d2) ~low:0) se
×
500
  | _ -> concat' msb lsb
×
501

502
let rec simplify_expr ?(rm_extract = true) (hte : t) : t =
38✔
503
  match view hte with
44✔
504
  | Val _ | Symbol _ -> hte
5✔
505
  | Ptr { base; offset } -> ptr base (simplify_expr offset)
×
506
  | List es -> make @@ List (List.map simplify_expr es)
×
507
  | App (x, es) -> make @@ App (x, List.map simplify_expr es)
×
508
  | Unop (ty, op, e) ->
1✔
509
    let e = simplify_expr e in
510
    unop ty op e
1✔
511
  | Binop (ty, op, e1, e2) ->
7✔
512
    let e1 = simplify_expr e1 in
513
    let e2 = simplify_expr e2 in
7✔
514
    binop ty op e1 e2
7✔
515
  | Relop (ty, op, e1, e2) ->
1✔
516
    let e1 = simplify_expr e1 in
517
    let e2 = simplify_expr e2 in
1✔
518
    relop ty op e1 e2
1✔
519
  | Triop (ty, op, c, e1, e2) ->
1✔
520
    let c = simplify_expr c in
521
    let e1 = simplify_expr e1 in
1✔
522
    let e2 = simplify_expr e2 in
1✔
523
    triop ty op c e1 e2
1✔
524
  | Cvtop (ty, op, e) ->
1✔
525
    let e = simplify_expr e in
526
    cvtop ty op e
1✔
527
  | Naryop (ty, op, es) ->
×
528
    let es = List.map (simplify_expr ~rm_extract:false) es in
529
    naryop ty op es
×
530
  | Extract (s, high, low) ->
5✔
531
    if not rm_extract then hte else extract s ~high ~low
1✔
532
  | Concat (e1, e2) ->
3✔
533
    let msb = simplify_expr ~rm_extract:false e1 in
534
    let lsb = simplify_expr ~rm_extract:false e2 in
3✔
535
    concat msb lsb
3✔
536
  | Binder _ ->
×
537
    (* Not simplifying anything atm *)
538
    hte
539

540
module Cache = Hashtbl.Make (struct
541
  type nonrec t = t
542

543
  let hash = hash
544

545
  let equal = equal
546
end)
547

548
let simplify =
549
  (* TODO: it may make sense to share the cache with simplify_expr ? *)
550
  let cache = Cache.create 512 in
551
  fun e ->
46✔
552
    match Cache.find_opt cache e with
8✔
553
    | Some simplified -> simplified
×
554
    | None ->
8✔
555
      let rec loop x =
556
        let x' = simplify_expr x in
17✔
557
        if equal x x' then begin
8✔
558
          Cache.add cache e x';
559
          x'
8✔
560
        end
561
        else loop x'
9✔
562
      in
563
      loop e
564

565
module Bool = struct
566
  open Ty
567

568
  let of_val = function
569
    | Val True -> Some true
×
570
    | Val False -> Some false
×
571
    | _ -> None
×
572

573
  let true_ = value True
46✔
574

575
  let false_ = value False
46✔
576

577
  let to_val b = if b then true_ else false_
×
578

579
  let v b = to_val b [@@inline]
×
580

581
  let not b =
582
    let bexpr = view b in
×
583
    match of_val bexpr with
×
584
    | Some b -> to_val (not b)
×
585
    | None -> (
×
586
      match bexpr with
587
      | Unop (Ty_bool, Not, cond) -> cond
×
588
      | _ -> unop Ty_bool Not b )
×
589

590
  let equal b1 b2 =
591
    match (view b1, view b2) with
×
592
    | Val True, Val True | Val False, Val False -> true_
×
593
    | _ -> relop Ty_bool Eq b1 b2
×
594

595
  let distinct b1 b2 =
596
    match (view b1, view b2) with
×
597
    | Val True, Val False | Val False, Val True -> true_
×
598
    | _ -> relop Ty_bool Ne b1 b2
×
599

600
  let and_ b1 b2 =
601
    match (of_val (view b1), of_val (view b2)) with
×
602
    | Some true, _ -> b2
×
603
    | _, Some true -> b1
×
604
    | Some false, _ | _, Some false -> false_
×
605
    | _ -> binop Ty_bool And b1 b2
×
606

607
  let or_ b1 b2 =
608
    match (of_val (view b1), of_val (view b2)) with
×
609
    | Some false, _ -> b2
×
610
    | _, Some false -> b1
×
611
    | Some true, _ | _, Some true -> true_
×
612
    | _ -> binop Ty_bool Or b1 b2
×
613

614
  let ite c r1 r2 = triop Ty_bool Ite c r1 r2
×
615
end
616

617
module Make (T : sig
618
  type elt
619

620
  val ty : Ty.t
621

622
  val num : elt -> Num.t
623
end) =
624
struct
625
  open Ty
626

627
  let v i = value (Num (T.num i))
30✔
628

629
  let sym x = symbol Symbol.(x @: T.ty)
16✔
630

631
  let ( ~- ) e = unop T.ty Neg e
×
632

633
  let ( = ) e1 e2 = relop Ty_bool Eq e1 e2
×
634

635
  let ( != ) e1 e2 = relop Ty_bool Ne e1 e2
×
636

637
  let ( > ) e1 e2 = relop T.ty Gt e1 e2
4✔
638

639
  let ( >= ) e1 e2 = relop T.ty Ge e1 e2
×
640

641
  let ( < ) e1 e2 = relop T.ty Lt e1 e2
9✔
642

643
  let ( <= ) e1 e2 = relop T.ty Le e1 e2
×
644
end
645

646
module Bitv = struct
647
  open Ty
648

649
  module I8 = Make (struct
650
    type elt = int
651

652
    let ty = Ty_bitv 8
653

654
    let num i = Num.I8 i
3✔
655
  end)
656

657
  module I32 = Make (struct
658
    type elt = int32
659

660
    let ty = Ty_bitv 32
661

662
    let num i = Num.I32 i
13✔
663
  end)
664

665
  module I64 = Make (struct
666
    type elt = int64
667

668
    let ty = Ty_bitv 64
669

670
    let num i = Num.I64 i
1✔
671
  end)
672
end
673

674
module Fpa = struct
675
  open Ty
676

677
  module F32 = struct
678
    include Make (struct
679
      type elt = float
680

681
      let ty = Ty_fp 32
682

683
      let num f = Num.F32 (Int32.bits_of_float f)
9✔
684
    end)
685

686
    (* Redeclare equality due to incorrect theory annotation *)
687
    let ( = ) e1 e2 = relop (Ty_fp 32) Eq e1 e2
3✔
688

689
    let ( != ) e1 e2 = relop (Ty_fp 32) Ne e1 e2
×
690
  end
691

692
  module F64 = struct
693
    include Make (struct
694
      type elt = float
695

696
      let ty = Ty_fp 64
697

698
      let num f = Num.F64 (Int64.bits_of_float f)
4✔
699
    end)
700

701
    (* Redeclare equality due to incorrect theory annotation *)
702
    let ( = ) e1 e2 = relop (Ty_fp 64) Eq e1 e2
1✔
703

704
    let ( != ) e1 e2 = relop (Ty_fp 64) Ne e1 e2
×
705
  end
706
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc