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

formalsec / smtml / 333

13 May 2025 04:22PM UTC coverage: 55.751% (-0.6%) from 56.313%
333

push

github

filipeom
correcting with Filipe's comments

0 of 14 new or added lines in 1 file covered. (0.0%)

139 existing lines in 1 file now uncovered.

950 of 1704 relevant lines covered (55.75%)

11.63 hits per line

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

32.15
/src/smtml/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
494✔
35
    | Val v1, Val v2 -> Value.equal v1 v2
468✔
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
6✔
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) ->
×
42
      Ty.equal t1 t2 && Ty.Unop.equal op1 op2 && phys_equal e1 e2
×
43
    | Binop (t1, op1, e1, e3), Binop (t2, op2, e2, e4) ->
12✔
44
      Ty.equal t1 t2 && Ty.Binop.equal op1 op2 && phys_equal e1 e2
12✔
45
      && phys_equal e3 e4
12✔
46
    | Relop (t1, op1, e1, e3), Relop (t2, op2, e2, e4) ->
1✔
47
      Ty.equal t1 t2 && Ty.Relop.equal op1 op2 && phys_equal e1 e2
1✔
48
      && phys_equal e3 e4
1✔
49
    | Triop (t1, op1, e1, e3, e5), Triop (t2, op2, e2, e4, e6) ->
×
50
      Ty.equal t1 t2 && Ty.Triop.equal op1 op2 && phys_equal e1 e2
×
51
      && phys_equal e3 e4 && phys_equal e5 e6
×
52
    | Cvtop (t1, op1, e1), Cvtop (t2, op2, e2) ->
×
53
      Ty.equal t1 t2 && Ty.Cvtop.equal op1 op2 && phys_equal e1 e2
×
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
964✔
68
    match e with
69
    | Val v -> h v
834✔
70
    | Ptr { base; offset } -> h (base, offset.tag)
13✔
71
    | Symbol s -> h s
36✔
72
    | List v -> h v
16✔
73
    | App (x, es) -> h (x, es)
×
74
    | Unop (ty, op, e) -> h (ty, op, e.tag)
4✔
75
    | Cvtop (ty, op, e) -> h (ty, op, e.tag)
6✔
76
    | Binop (ty, op, e1, e2) -> h (ty, op, e1.tag, e2.tag)
34✔
77
    | Relop (ty, op, e1, e2) -> h (ty, op, e1.tag, e2.tag)
3✔
78
    | Triop (ty, op, e1, e2, e3) -> h (ty, op, e1.tag, e2.tag, e3.tag)
×
79
    | Naryop (ty, op, es) -> h (ty, op, es)
×
80
    | Extract (e, hi, lo) -> h (e.tag, hi, lo)
14✔
81
    | Concat (e1, e2) -> h (e1.tag, e2.tag)
4✔
82
    | Binder (b, vars, e) -> h (b, vars, e.tag)
×
83
end
84

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

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

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

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

94
  let to_int hte = hash hte
×
95
end
96

97
let[@inline] make e = Hc.hashcons e
729✔
98

99
let[@inline] view (hte : t) = hte.node
510✔
100

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

103
let symbol s = make (Symbol s)
21✔
104

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

134
let rec is_symbolic (v : t) : bool =
135
  match view v with
×
136
  | Val _ -> false
×
137
  | Symbol _ -> true
×
138
  | Ptr { offset; _ } -> is_symbolic offset
×
139
  | List vs -> List.exists is_symbolic vs
×
140
  | App (_, vs) -> List.exists is_symbolic vs
×
141
  | Unop (_, _, v) -> is_symbolic v
×
142
  | Binop (_, _, v1, v2) -> is_symbolic v1 || is_symbolic v2
×
143
  | Triop (_, _, v1, v2, v3) ->
×
144
    is_symbolic v1 || is_symbolic v2 || is_symbolic v3
×
145
  | Cvtop (_, _, v) -> is_symbolic v
×
146
  | Relop (_, _, v1, v2) -> is_symbolic v1 || is_symbolic v2
×
147
  | Naryop (_, _, vs) -> List.exists is_symbolic vs
×
148
  | Extract (e, _, _) -> is_symbolic e
×
149
  | Concat (e1, e2) -> is_symbolic e1 || is_symbolic e2
×
150
  | Binder (_, _, e) -> is_symbolic e
×
151

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

185
module Set = struct
186
  include PatriciaTree.MakeHashconsedSet (Key) ()
187

188
  let hash = to_int
189

190
  let get_symbols (set : t) =
191
    let tbl = Hashtbl.create 64 in
×
192
    let rec symbols hte =
×
193
      match view hte with
×
194
      | Val _ -> ()
×
195
      | Ptr { offset; _ } -> symbols offset
×
196
      | Symbol s -> Hashtbl.replace tbl s ()
×
197
      | List es -> List.iter symbols es
×
198
      | App (_, es) -> List.iter symbols es
×
UNCOV
199
      | Unop (_, _, e1) -> symbols e1
×
UNCOV
200
      | Binop (_, _, e1, e2) ->
×
201
        symbols e1;
UNCOV
202
        symbols e2
×
UNCOV
203
      | Triop (_, _, e1, e2, e3) ->
×
204
        symbols e1;
UNCOV
205
        symbols e2;
×
UNCOV
206
        symbols e3
×
UNCOV
207
      | Relop (_, _, e1, e2) ->
×
208
        symbols e1;
209
        symbols e2
×
210
      | Cvtop (_, _, e) -> symbols e
×
211
      | Naryop (_, _, es) -> List.iter symbols es
×
212
      | Extract (e, _, _) -> symbols e
×
213
      | Concat (e1, e2) ->
×
214
        symbols e1;
215
        symbols e2
×
216
      | Binder (_, vars, e) ->
×
217
        List.iter symbols vars;
UNCOV
218
        symbols e
×
219
    in
220
    iter symbols set;
UNCOV
221
    Hashtbl.fold (fun k () acc -> k :: acc) tbl []
×
222
end
223

224
module Pp = struct
225
  let rec pp fmt (hte : t) =
226
    match view hte with
×
227
    | Val v -> Value.pp fmt v
×
228
    | Ptr { base; offset } -> Fmt.pf fmt "(Ptr (i32 %ld) %a)" base pp offset
×
229
    | Symbol s -> Fmt.pf fmt "@[<hov 1>%a@]" Symbol.pp s
×
230
    | List v -> Fmt.pf fmt "@[<hov 1>[%a]@]" (Fmt.list ~sep:Fmt.comma pp) v
×
UNCOV
231
    | App (s, v) ->
×
232
      Fmt.pf fmt "@[<hov 1>(%a@ %a)@]" Symbol.pp s
233
        (Fmt.list ~sep:Fmt.comma pp)
×
234
        v
235
    | Unop (ty, op, e) ->
×
236
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Unop.pp op pp e
UNCOV
237
    | Binop (ty, op, e1, e2) ->
×
238
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Binop.pp op pp e1 pp
239
        e2
UNCOV
240
    | Triop (ty, op, e1, e2, e3) ->
×
241
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a@ %a)@]" Ty.pp ty Ty.Triop.pp op pp e1
242
        pp e2 pp e3
243
    | Relop (ty, op, e1, e2) ->
×
244
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Relop.pp op pp e1 pp
245
        e2
246
    | Cvtop (ty, op, e) ->
×
247
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Cvtop.pp op pp e
248
    | Naryop (ty, op, es) ->
×
249
      Fmt.pf fmt "@[<hov 1>(%a.%a@ (%a))@]" Ty.pp ty Ty.Naryop.pp op
250
        (Fmt.list ~sep:Fmt.comma pp)
×
251
        es
252
    | Extract (e, h, l) ->
×
253
      Fmt.pf fmt "@[<hov 1>(extract@ %a@ %d@ %d)@]" pp e l h
254
    | Concat (e1, e2) -> Fmt.pf fmt "@[<hov 1>(++@ %a@ %a)@]" pp e1 pp e2
×
UNCOV
255
    | Binder (b, vars, e) ->
×
256
      Fmt.pf fmt "@[<hov 1>(%a@ (%a)@ %a)@]" Binder.pp b
257
        (Fmt.list ~sep:Fmt.sp pp) vars pp e
×
258

UNCOV
259
  let pp_list fmt (es : t list) = Fmt.hovbox (Fmt.list ~sep:Fmt.comma pp) fmt es
×
260

261
  let pp_smt fmt (es : t list) : unit =
UNCOV
262
    let pp_symbols fmt syms =
×
263
      Fmt.list ~sep:Fmt.semi
×
264
        (fun fmt sym ->
265
          let t = Symbol.type_of sym in
×
UNCOV
266
          Fmt.pf fmt "(let-const %a %a)" Symbol.pp sym Ty.pp t )
×
267
        fmt syms
268
    in
269
    let pp_asserts fmt es =
UNCOV
270
      Fmt.list ~sep:Fmt.semi
×
271
        (fun fmt e -> Fmt.pf fmt "(assert @[<h 2>%a@])" pp e)
×
272
        fmt es
273
    in
274
    let syms = get_symbols es in
UNCOV
275
    if List.length syms > 0 then Fmt.pf fmt "%a@\n" pp_symbols syms;
×
276
    if List.length es > 0 then Fmt.pf fmt "%a@\n" pp_asserts es;
×
UNCOV
277
    Fmt.string fmt "(check-sat)"
×
278
end
279

280
let pp = Pp.pp
281

282
let pp_list = Pp.pp_list
283

284
let pp_smt = Pp.pp_smt
285

UNCOV
286
let to_string e = Fmt.str "%a" pp e
×
287

288
let value (v : Value.t) : t = make (Val v) [@@inline]
651✔
289

290
let ptr base offset = make (Ptr { base; offset })
8✔
291

292
let list l = make (List l)
5✔
293

294
let app symbol args = make (App (symbol, args))
×
295

UNCOV
296
let[@inline] binder bt vars expr = make (Binder (bt, vars, expr))
×
297

UNCOV
298
let let_in vars body = binder Let_in vars body
×
299

UNCOV
300
let forall vars body = binder Forall vars body
×
301

UNCOV
302
let exists vars body = binder Exists vars body
×
303

304
let raw_unop ty op hte = make (Unop (ty, op, hte)) [@@inline]
2✔
305

306
let normalize_eq_or_ne op (ty', e1, e2) =
UNCOV
307
  let make_relop lhs rhs = Relop (ty', op, lhs, rhs) in
×
NEW
308
  let ty, ty2 = (ty e1, ty e2) in
×
NEW
309
  assert (Ty.equal ty ty2);
×
310
  match ty with
NEW
311
  | Ty_bitv m ->
×
312
    let binop = make (Binop (ty, Sub, e1, e2)) in
NEW
313
    let zero = make (Val (Bitv (Bitvector.make Z.zero m))) in
×
NEW
314
    make_relop binop zero
×
UNCOV
315
  | Ty_int ->
×
316
    let binop = make (Binop (ty, Sub, e1, e2)) in
NEW
317
    let zero = make (Val (Int Int.zero)) in
×
NEW
318
    make_relop binop zero
×
NEW
319
  | Ty_real ->
×
320
    let binop = make (Binop (ty, Sub, e1, e2)) in
NEW
321
    let zero = make (Val (Real 0.)) in
×
NEW
322
    make_relop binop zero
×
NEW
323
  | _ -> make_relop e1 e2
×
324

325
let negate_relop (hte : t) : t =
UNCOV
326
  let e =
×
327
    match view hte with
328
    | Relop (ty, Eq, e1, e2) -> normalize_eq_or_ne Ne (ty, e1, e2)
×
UNCOV
329
    | Relop (ty, Ne, e1, e2) -> normalize_eq_or_ne Eq (ty, e1, e2)
×
UNCOV
330
    | Relop (ty, Lt, e1, e2) -> Relop (ty, Le, e2, e1)
×
UNCOV
331
    | Relop (ty, LtU, e1, e2) -> Relop (ty, LeU, e2, e1)
×
UNCOV
332
    | Relop (ty, Le, e1, e2) -> Relop (ty, Lt, e2, e1)
×
UNCOV
333
    | Relop (ty, LeU, e1, e2) -> Relop (ty, LtU, e2, e1)
×
UNCOV
334
    | Relop (ty, Gt, e1, e2) -> Relop (ty, Le, e1, e2)
×
UNCOV
335
    | Relop (ty, GtU, e1, e2) -> Relop (ty, LeU, e1, e2)
×
UNCOV
336
    | Relop (ty, Ge, e1, e2) -> Relop (ty, Lt, e1, e2)
×
UNCOV
337
    | Relop (ty, GeU, e1, e2) -> Relop (ty, LtU, e1, e2)
×
UNCOV
338
    | _ -> Fmt.failwith "negate_relop: not a relop."
×
339
  in
340
  make e
341

342
let unop ty op hte =
343
  match (op, view hte) with
32✔
UNCOV
344
  | Ty.Unop.(Regexp_loop _ | Regexp_star), _ -> raw_unop ty op hte
×
345
  | _, Val v -> value (Eval.unop ty op v)
23✔
346
  | Not, Unop (_, Not, hte') -> hte'
1✔
UNCOV
347
  | Not, Relop (_, _, _, _) -> negate_relop hte
×
348
  | Neg, Unop (_, Neg, hte') -> hte'
1✔
UNCOV
349
  | Trim, Cvtop (Ty_real, ToString, _) -> hte
×
350
  | Head, List (hd :: _) -> hd
1✔
351
  | Tail, List (_ :: tl) -> make (List tl)
1✔
352
  | Reverse, List es -> make (List (List.rev es))
2✔
353
  | Length, List es -> value (Int (List.length es))
1✔
354
  | _ -> raw_unop ty op hte
2✔
355

356
let raw_binop ty op hte1 hte2 = make (Binop (ty, op, hte1, hte2)) [@@inline]
23✔
357

358
let rec binop ty op hte1 hte2 =
359
  match (op, view hte1, view hte2) with
96✔
UNCOV
360
  | Ty.Binop.(String_in_re | Regexp_range), _, _ -> raw_binop ty op hte1 hte2
×
361
  | op, Val v1, Val v2 -> value (Eval.binop ty op v1 v2)
67✔
362
  | Sub, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
1✔
363
    if Int32.equal b1 b2 then binop ty Sub os1 os2
1✔
UNCOV
364
    else raw_binop ty op hte1 hte2
×
365
  | Add, Ptr { base; offset }, _ ->
1✔
366
    ptr base (binop (Ty_bitv 32) Add offset hte2)
1✔
367
  | Sub, Ptr { base; offset }, _ ->
1✔
368
    ptr base (binop (Ty_bitv 32) Sub offset hte2)
1✔
369
  | Rem, Ptr { base; offset }, _ ->
1✔
370
    let rhs = value (Bitv (Bitvector.of_int32 base)) in
1✔
371
    let addr = binop (Ty_bitv 32) Add rhs offset in
1✔
372
    binop ty Rem addr hte2
1✔
373
  | Add, _, Ptr { base; offset } ->
1✔
374
    ptr base (binop (Ty_bitv 32) Add offset hte1)
1✔
UNCOV
375
  | Sub, _, Ptr { base; offset } ->
×
376
    let base = value (Bitv (Bitvector.of_int32 base)) in
×
UNCOV
377
    binop ty Sub hte1 (binop (Ty_bitv 32) Add base offset)
×
UNCOV
378
  | (Add | Or), Val (Bitv bv), _ when Bitvector.eqz bv -> hte2
×
UNCOV
379
  | (And | Div | DivU | Mul | Rem | RemU), Val (Bitv bv), _
×
380
    when Bitvector.eqz bv ->
3✔
381
    hte1
1✔
UNCOV
382
  | (Add | Or), _, Val (Bitv bv) when Bitvector.eqz bv -> hte1
×
383
  | (And | Mul), _, Val (Bitv bv) when Bitvector.eqz bv -> hte2
1✔
384
  | Add, Binop (ty, Add, x, { node = Val v1; _ }), Val v2 ->
1✔
385
    let v = value (Eval.binop ty Add v1 v2) in
1✔
386
    raw_binop ty Add x v
1✔
387
  | Sub, Binop (ty, Sub, x, { node = Val v1; _ }), Val v2 ->
1✔
388
    let v = value (Eval.binop ty Add v1 v2) in
1✔
389
    raw_binop ty Sub x v
1✔
UNCOV
390
  | Mul, Val (Bitv bv), _ when Bitvector.eq_one bv -> hte2
×
UNCOV
391
  | Mul, _, Val (Bitv bv) when Bitvector.eq_one bv -> hte1
×
392
  | Mul, Binop (ty, Mul, x, { node = Val v1; _ }), Val v2 ->
1✔
393
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
394
    raw_binop ty Mul x v
1✔
395
  | Add, Val v1, Binop (ty, Add, x, { node = Val v2; _ }) ->
1✔
396
    let v = value (Eval.binop ty Add v1 v2) in
1✔
397
    raw_binop ty Add v x
1✔
398
  | Mul, Val v1, Binop (ty, Mul, x, { node = Val v2; _ }) ->
1✔
399
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
400
    raw_binop ty Mul v x
1✔
401
  | At, List es, Val (Int n) ->
1✔
402
    (* TODO: use another datastructure? *)
403
    begin
404
      match List.nth_opt es n with None -> assert false | Some v -> v
1✔
405
    end
406
  | List_cons, _, List es -> make (List (hte1 :: es))
1✔
UNCOV
407
  | List_append, List _, (List [] | Val (List [])) -> hte1
×
UNCOV
408
  | List_append, (List [] | Val (List [])), List _ -> hte2
×
409
  | List_append, List l0, Val (List l1) -> make (List (l0 @ List.map value l1))
1✔
UNCOV
410
  | List_append, Val (List l0), List l1 -> make (List (List.map value l0 @ l1))
×
UNCOV
411
  | List_append, List l0, List l1 -> make (List (l0 @ l1))
×
412
  | _ -> raw_binop ty op hte1 hte2
12✔
413

UNCOV
414
let raw_triop ty op e1 e2 e3 = make (Triop (ty, op, e1, e2, e3)) [@@inline]
×
415

416
let triop ty op e1 e2 e3 =
417
  match (op, view e1, view e2, view e3) with
6✔
418
  | Ty.Triop.Ite, Val True, _, _ -> e2
1✔
419
  | Ite, Val False, _, _ -> e3
1✔
420
  | op, Val v1, Val v2, Val v3 -> value (Eval.triop ty op v1 v2 v3)
4✔
UNCOV
421
  | Ite, _, Triop (_, Ite, c2, r1, r2), Triop (_, Ite, _, _, _) ->
×
422
    let else_ = raw_triop ty Ite e1 r2 e3 in
UNCOV
423
    let cond = binop Ty_bool And e1 c2 in
×
UNCOV
424
    raw_triop ty Ite cond r1 else_
×
UNCOV
425
  | _ -> raw_triop ty op e1 e2 e3
×
426

427
let raw_relop ty op hte1 hte2 = make (Relop (ty, op, hte1, hte2)) [@@inline]
2✔
428

429
let rec relop ty op hte1 hte2 =
430
  match (op, view hte1, view hte2) with
77✔
431
  | op, Val v1, Val v2 -> value (if Eval.relop ty op v1 v2 then True else False)
29✔
432
  | Ty.Relop.Ne, Val (Real v), _ | Ne, _, Val (Real v) ->
×
UNCOV
433
    if Float.is_nan v || Float.is_infinite v then value True
×
UNCOV
434
    else raw_relop ty op hte1 hte2
×
UNCOV
435
  | _, Val (Real v), _ | _, _, Val (Real v) ->
×
UNCOV
436
    if Float.is_nan v || Float.is_infinite v then value False
×
437
    else raw_relop ty op hte1 hte2
×
438
  | Eq, _, Val Nothing | Eq, Val Nothing, _ -> value False
×
439
  | Ne, _, Val Nothing | Ne, Val Nothing, _ -> value True
×
440
  | Eq, _, Val (App (`Op "symbol", [ Str _ ]))
×
441
  | Eq, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
442
    value False
443
  | Ne, _, Val (App (`Op "symbol", [ Str _ ]))
×
UNCOV
444
  | Ne, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
445
    value True
NEW
446
  | ( Eq
×
447
    , Symbol ({ ty = Ty_fp prec1; _ } as s1)
448
    , Symbol ({ ty = Ty_fp prec2; _ } as s2) )
NEW
449
    when prec1 = prec2 && Symbol.equal s1 s2 ->
×
NEW
450
    raw_unop Ty_bool Not (raw_unop (Ty_fp prec1) Is_nan hte1)
×
451
  | Eq, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
2✔
452
    if Int32.equal b1 b2 then relop Ty_bool Eq os1 os2 else value False
1✔
453
  | Ne, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
2✔
454
    if Int32.equal b1 b2 then relop Ty_bool Ne os1 os2 else value True
1✔
455
  | ( (LtU | LeU)
2✔
456
    , Ptr { base = b1; offset = os1 }
457
    , Ptr { base = b2; offset = os2 } ) ->
458
    if Int32.equal b1 b2 then relop ty op os1 os2
2✔
459
    else
460
      let b1 = Value.Bitv (Bitvector.of_int32 b1) in
2✔
461
      let b2 = Value.Bitv (Bitvector.of_int32 b2) in
2✔
462
      value (if Eval.relop ty op b1 b2 then True else False)
1✔
463
  | ( op
2✔
464
    , Val (Bitv _ as n)
465
    , Ptr { base; offset = { node = Val (Bitv _ as o); _ } } ) ->
466
    let base = Eval.binop (Ty_bitv 32) Add (Bitv (Bitvector.of_int32 base)) o in
2✔
467
    value (if Eval.relop ty op n base then True else False)
1✔
468
  | op, Ptr { base; offset = { node = Val (Bitv _ as o); _ } }, Val (Bitv _ as n)
2✔
469
    ->
470
    let base = Eval.binop (Ty_bitv 32) Add (Bitv (Bitvector.of_int32 base)) o in
2✔
471
    value (if Eval.relop ty op base n then True else False)
1✔
UNCOV
472
  | op, List l1, List l2 -> relop_list op l1 l2
×
473
  | Gt, _, _ -> relop ty Lt hte2 hte1
×
474
  | GtU, _, _ -> relop ty LtU hte2 hte1
1✔
UNCOV
475
  | Ge, _, _ -> relop ty Le hte2 hte1
×
476
  | GeU, _, _ -> relop ty LeU hte2 hte1
1✔
477
  | _, _, _ -> raw_relop ty op hte1 hte2
2✔
478

479
and relop_list op l1 l2 =
480
  match (op, l1, l2) with
×
481
  | Eq, [], [] -> value True
×
482
  | Eq, _, [] | Eq, [], _ -> value False
×
UNCOV
483
  | Eq, l1, l2 ->
×
UNCOV
484
    if not (List.compare_lengths l1 l2 = 0) then value False
×
485
    else
UNCOV
486
      List.fold_left2
×
487
        (fun acc a b ->
UNCOV
488
          binop Ty_bool And acc
×
489
          @@
UNCOV
490
          match (ty a, ty b) with
×
UNCOV
491
          | Ty_real, Ty_real -> relop Ty_real Eq a b
×
UNCOV
492
          | _ -> relop Ty_bool Eq a b )
×
UNCOV
493
        (value True) l1 l2
×
UNCOV
494
  | Ne, _, _ -> unop Ty_bool Not @@ relop_list Eq l1 l2
×
495
  | (Lt | LtU | Gt | GtU | Le | LeU | Ge | GeU), _, _ -> assert false
496

497
let raw_cvtop ty op hte = make (Cvtop (ty, op, hte)) [@@inline]
3✔
498

499
let cvtop ty op hte =
500
  match (op, view hte) with
25✔
501
  | Ty.Cvtop.String_to_re, _ -> raw_cvtop ty op hte
×
502
  | _, Val v -> value (Eval.cvtop ty op v)
22✔
503
  | String_to_float, Cvtop (Ty_real, ToString, real) -> real
×
504
  | _ -> raw_cvtop ty op hte
3✔
505

UNCOV
506
let raw_naryop ty op es = make (Naryop (ty, op, es)) [@@inline]
×
507

508
let naryop ty op es =
UNCOV
509
  if List.for_all (fun e -> match view e with Val _ -> true | _ -> false) es
×
510
  then
511
    let vs =
7✔
512
      List.map (fun e -> match view e with Val v -> v | _ -> assert false) es
18✔
513
    in
514
    value (Eval.naryop ty op vs)
7✔
515
  else
516
    match (ty, op, List.map view es) with
×
UNCOV
517
    | ( Ty_str
×
518
      , Concat
519
      , [ Naryop (Ty_str, Concat, l1); Naryop (Ty_str, Concat, l2) ] ) ->
520
      raw_naryop Ty_str Concat (l1 @ l2)
UNCOV
521
    | Ty_str, Concat, [ Naryop (Ty_str, Concat, htes); hte ] ->
×
522
      raw_naryop Ty_str Concat (htes @ [ make hte ])
×
UNCOV
523
    | Ty_str, Concat, [ hte; Naryop (Ty_str, Concat, htes) ] ->
×
524
      raw_naryop Ty_str Concat (make hte :: htes)
×
525
    | _ -> raw_naryop ty op es
×
526

527
let[@inline] raw_extract (hte : t) ~(high : int) ~(low : int) : t =
528
  make (Extract (hte, high, low))
7✔
529

530
let extract (hte : t) ~(high : int) ~(low : int) : t =
531
  match (view hte, high, low) with
12✔
532
  | Val (Bitv bv), high, low ->
3✔
533
    let high = (high * 8) - 1 in
534
    let low = low * 8 in
535
    value (Bitv (Bitvector.extract bv ~high ~low))
3✔
536
  | ( Cvtop
2✔
537
        ( _
538
        , (Zero_extend 24 | Sign_extend 24)
1✔
539
        , ({ node = Symbol { ty = Ty_bitv 8; _ }; _ } as sym) )
540
    , 1
541
    , 0 ) ->
542
    sym
543
  | Concat (_, e), h, l when Ty.size (ty e) = h - l -> e
2✔
544
  | Concat (e, _), 8, 4 when Ty.size (ty e) = 4 -> e
×
545
  | _ ->
5✔
546
    if high - low = Ty.size (ty hte) then hte else raw_extract hte ~high ~low
×
547

548
let raw_concat (msb : t) (lsb : t) : t = make (Concat (msb, lsb)) [@@inline]
2✔
549

550
(* TODO: don't rebuild so many values it generates unecessary hc lookups *)
551
let rec concat (msb : t) (lsb : t) : t =
552
  match (view msb, view lsb) with
6✔
553
  | Val (Bitv a), Val (Bitv b) -> value (Bitv (Bitvector.concat a b))
1✔
UNCOV
554
  | Val (Bitv _), Concat (({ node = Val (Bitv _); _ } as b), se) ->
×
555
    raw_concat (concat msb b) se
×
556
  | Extract (s1, h, m1), Extract (s2, m2, l) when equal s1 s2 && m1 = m2 ->
3✔
557
    if h - l = Ty.size (ty s1) then s1 else raw_extract s1 ~high:h ~low:l
1✔
UNCOV
558
  | Extract (_, _, _), Concat (({ node = Extract (_, _, _); _ } as e2), e3) ->
×
UNCOV
559
    raw_concat (concat msb e2) e3
×
560
  | _ -> raw_concat msb lsb
2✔
561

562
let rec simplify_expr ?(in_relop = false) (hte : t) : t =
4✔
563
  match view hte with
16✔
564
  | Val _ | Symbol _ -> hte
4✔
UNCOV
565
  | Ptr { base; offset } ->
×
566
    let offset = simplify_expr ~in_relop offset in
UNCOV
567
    if not in_relop then ptr base offset
×
UNCOV
568
    else binop (Ty_bitv 32) Add (value (Bitv (Bitvector.of_int32 base))) offset
×
UNCOV
569
  | List es -> make @@ List (List.map (simplify_expr ~in_relop) es)
×
UNCOV
570
  | App (x, es) -> make @@ App (x, List.map (simplify_expr ~in_relop) es)
×
UNCOV
571
  | Unop (ty, op, e) ->
×
572
    let e = simplify_expr ~in_relop e in
UNCOV
573
    unop ty op e
×
574
  | Binop (ty, op, e1, e2) ->
6✔
575
    let e1 = simplify_expr ~in_relop e1 in
576
    let e2 = simplify_expr ~in_relop e2 in
6✔
577
    binop ty op e1 e2
6✔
UNCOV
578
  | Relop (ty, op, e1, e2) ->
×
579
    let e1 = simplify_expr ~in_relop:true e1 in
UNCOV
580
    let e2 = simplify_expr ~in_relop:true e2 in
×
UNCOV
581
    relop ty op e1 e2
×
UNCOV
582
  | Triop (ty, op, c, e1, e2) ->
×
583
    let c = simplify_expr ~in_relop c in
UNCOV
584
    let e1 = simplify_expr ~in_relop e1 in
×
UNCOV
585
    let e2 = simplify_expr ~in_relop e2 in
×
UNCOV
586
    triop ty op c e1 e2
×
UNCOV
587
  | Cvtop (ty, op, e) ->
×
588
    let e = simplify_expr ~in_relop e in
UNCOV
589
    cvtop ty op e
×
590
  | Naryop (ty, op, es) ->
×
591
    let es = List.map (simplify_expr ~in_relop) es in
592
    naryop ty op es
×
UNCOV
593
  | Extract (s, high, low) ->
×
594
    let s = simplify_expr ~in_relop s in
UNCOV
595
    extract s ~high ~low
×
UNCOV
596
  | Concat (e1, e2) ->
×
597
    let msb = simplify_expr ~in_relop e1 in
598
    let lsb = simplify_expr ~in_relop e2 in
×
UNCOV
599
    concat msb lsb
×
600
  | Binder _ ->
×
601
    (* Not simplifying anything atm *)
602
    hte
603

604
module Cache = Hashtbl.Make (struct
605
  type nonrec t = t
606

607
  let hash = hash
608

609
  let equal = equal
610
end)
611

612
let simplify =
613
  (* TODO: it may make sense to share the cache with simplify_expr ? *)
614
  let cache = Cache.create 512 in
615
  fun e ->
3✔
616
    match Cache.find_opt cache e with
2✔
617
    | Some simplified -> simplified
×
618
    | None ->
2✔
619
      let rec loop x =
620
        let x' = simplify_expr x in
4✔
621
        if equal x x' then begin
2✔
622
          Cache.add cache e x';
623
          x'
2✔
624
        end
625
        else loop x'
2✔
626
      in
627
      loop e
628

629
module Bool = struct
630
  open Ty
631

632
  let of_val = function
633
    | Val True -> Some true
×
UNCOV
634
    | Val False -> Some false
×
635
    | _ -> None
×
636

637
  let true_ = value True
3✔
638

639
  let false_ = value False
3✔
640

UNCOV
641
  let to_val b = if b then true_ else false_
×
642

UNCOV
643
  let v b = to_val b [@@inline]
×
644

645
  let not b =
UNCOV
646
    let bexpr = view b in
×
UNCOV
647
    match of_val bexpr with
×
648
    | Some b -> to_val (not b)
×
UNCOV
649
    | None -> (
×
650
      match bexpr with
UNCOV
651
      | Unop (Ty_bool, Not, cond) -> cond
×
652
      | _ -> unop Ty_bool Not b )
×
653

654
  let equal b1 b2 =
UNCOV
655
    match (view b1, view b2) with
×
656
    | Val True, Val True | Val False, Val False -> true_
×
UNCOV
657
    | _ -> relop Ty_bool Eq b1 b2
×
658

659
  let distinct b1 b2 =
660
    match (view b1, view b2) with
×
UNCOV
661
    | Val True, Val False | Val False, Val True -> true_
×
662
    | _ -> relop Ty_bool Ne b1 b2
×
663

664
  let and_ b1 b2 =
UNCOV
665
    match (of_val (view b1), of_val (view b2)) with
×
UNCOV
666
    | Some true, _ -> b2
×
UNCOV
667
    | _, Some true -> b1
×
UNCOV
668
    | Some false, _ | _, Some false -> false_
×
UNCOV
669
    | _ -> binop Ty_bool And b1 b2
×
670

671
  let or_ b1 b2 =
UNCOV
672
    match (of_val (view b1), of_val (view b2)) with
×
UNCOV
673
    | Some false, _ -> b2
×
UNCOV
674
    | _, Some false -> b1
×
675
    | Some true, _ | _, Some true -> true_
×
UNCOV
676
    | _ -> binop Ty_bool Or b1 b2
×
677

UNCOV
678
  let ite c r1 r2 = triop Ty_bool Ite c r1 r2
×
679
end
680

681
module Make (T : sig
682
  type elt
683

684
  val ty : Ty.t
685

686
  val value : elt -> Value.t
687
end) =
688
struct
689
  open Ty
690

691
  let v i = value (T.value i)
×
692

UNCOV
693
  let sym x = symbol Symbol.(x @: T.ty)
×
694

UNCOV
695
  let ( ~- ) e = unop T.ty Neg e
×
696

UNCOV
697
  let ( = ) e1 e2 = relop Ty_bool Eq e1 e2
×
698

UNCOV
699
  let ( != ) e1 e2 = relop Ty_bool Ne e1 e2
×
700

UNCOV
701
  let ( > ) e1 e2 = relop T.ty Gt e1 e2
×
702

UNCOV
703
  let ( >= ) e1 e2 = relop T.ty Ge e1 e2
×
704

UNCOV
705
  let ( < ) e1 e2 = relop T.ty Lt e1 e2
×
706

UNCOV
707
  let ( <= ) e1 e2 = relop T.ty Le e1 e2
×
708
end
709

710
module Bitv = struct
711
  open Ty
712

713
  module I8 = Make (struct
714
    type elt = int
715

716
    let ty = Ty_bitv 8
717

UNCOV
718
    let value i = Value.Bitv (Bitvector.of_int8 i)
×
719
  end)
720

721
  module I32 = Make (struct
722
    type elt = int32
723

724
    let ty = Ty_bitv 32
725

UNCOV
726
    let value i = Value.Bitv (Bitvector.of_int32 i)
×
727
  end)
728

729
  module I64 = Make (struct
730
    type elt = int64
731

732
    let ty = Ty_bitv 64
733

UNCOV
734
    let value i = Value.Bitv (Bitvector.of_int64 i)
×
735
  end)
736
end
737

738
module Fpa = struct
739
  open Ty
740

741
  module F32 = struct
742
    include Make (struct
743
      type elt = float
744

745
      let ty = Ty_fp 32
746

UNCOV
747
      let value f = Value.Num (F32 (Int32.bits_of_float f))
×
748
    end)
749

750
    (* Redeclare equality due to incorrect theory annotation *)
UNCOV
751
    let ( = ) e1 e2 = relop (Ty_fp 32) Eq e1 e2
×
752

UNCOV
753
    let ( != ) e1 e2 = relop (Ty_fp 32) Ne e1 e2
×
754
  end
755

756
  module F64 = struct
757
    include Make (struct
758
      type elt = float
759

760
      let ty = Ty_fp 64
761

UNCOV
762
      let value f = Value.Num (F64 (Int64.bits_of_float f))
×
763
    end)
764

765
    (* Redeclare equality due to incorrect theory annotation *)
UNCOV
766
    let ( = ) e1 e2 = relop (Ty_fp 64) Eq e1 e2
×
767

UNCOV
768
    let ( != ) e1 e2 = relop (Ty_fp 64) Ne e1 e2
×
769
  end
770
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