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

formalsec / smtml / 322

03 May 2025 06:35PM UTC coverage: 42.529% (+0.1%) from 42.428%
322

push

github

filipeom
Standardize raw constructor naming and improve documentation

The constructors responsible for building raw nodes without simplification,
previously denoted by a trailing apostrophe (e.g., `unop'`), have been
renamed to follow the `raw_name` convention (e.g., `raw_unop`).

Furthermore, the documentation for these raw functions has been updated to
explicitly describe their behavior and provide examples that highlight their
distinction from the simplifying constructors.

18 of 35 new or added lines in 1 file covered. (51.43%)

71 existing lines in 1 file now uncovered.

723 of 1700 relevant lines covered (42.53%)

7.48 hits per line

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

29.53
/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
578✔
35
    | Val v1, Val v2 -> Value.equal v1 v2
545✔
36
    | Ptr { base = b1; offset = o1 }, Ptr { base = b2; offset = o2 } ->
4✔
37
      Int32.equal b1 b2 && phys_equal o1 o2
4✔
38
    | Symbol s1, Symbol s2 -> Symbol.equal s1 s2
11✔
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) ->
1✔
57
      phys_equal e1 e2 && h1 = h2 && l1 = l2
1✔
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
838✔
68
    match e with
69
    | Val v -> h v
723✔
70
    | Ptr { base; offset } -> h (base, offset.tag)
12✔
71
    | Symbol s -> h s
23✔
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)
2✔
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)
15✔
81
    | Concat (e1, e2) -> h (e1.tag, e2.tag)
6✔
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]
214✔
88

89
let hash (hte : t) = hte.tag [@@inline]
6✔
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
705✔
98

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

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

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

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

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

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

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

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

204
module Set = struct
205
  include PatriciaTree.MakeHashconsedSet (Key) ()
206

207
  let hash = to_int
208

209
  let get_symbols (set : t) =
210
    let tbl = Hashtbl.create 64 in
×
211
    let rec symbols hte =
×
212
      match view hte with
×
213
      | Val _ -> ()
×
214
      | Ptr { offset; _ } -> symbols offset
×
215
      | Symbol s -> Hashtbl.replace tbl s ()
×
216
      | List es -> List.iter symbols es
×
217
      | App (_, es) -> List.iter symbols es
×
218
      | Unop (_, _, e1) -> symbols e1
×
219
      | Binop (_, _, e1, e2) ->
×
220
        symbols e1;
221
        symbols e2
×
222
      | Triop (_, _, e1, e2, e3) ->
×
223
        symbols e1;
224
        symbols e2;
×
225
        symbols e3
×
226
      | Relop (_, _, e1, e2) ->
×
227
        symbols e1;
228
        symbols e2
×
229
      | Cvtop (_, _, e) -> symbols e
×
230
      | Naryop (_, _, es) -> List.iter symbols es
×
231
      | Extract (e, _, _) -> symbols e
×
232
      | Concat (e1, e2) ->
×
233
        symbols e1;
234
        symbols e2
×
235
      | Binder (_, vars, e) ->
×
236
        List.iter symbols vars;
237
        symbols e
×
238
    in
239
    iter symbols set;
240
    Hashtbl.fold (fun k () acc -> k :: acc) tbl []
×
241
end
242

243
module Pp = struct
244
  let rec pp fmt (hte : t) =
245
    match view hte with
×
246
    | Val v -> Value.pp fmt v
×
247
    | Ptr { base; offset } -> Fmt.pf fmt "(Ptr (i32 %ld) %a)" base pp offset
×
248
    | Symbol s -> Fmt.pf fmt "@[<hov 1>%a@]" Symbol.pp s
×
249
    | List v -> Fmt.pf fmt "@[<hov 1>[%a]@]" (Fmt.list ~sep:Fmt.comma pp) v
×
250
    | App (s, v) ->
×
251
      Fmt.pf fmt "@[<hov 1>(%a@ %a)@]" Symbol.pp s
252
        (Fmt.list ~sep:Fmt.comma pp)
×
253
        v
254
    | Unop (ty, op, e) ->
×
255
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Unop.pp op pp e
256
    | Binop (ty, op, e1, e2) ->
×
257
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Binop.pp op pp e1 pp
258
        e2
259
    | Triop (ty, op, e1, e2, e3) ->
×
260
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a@ %a)@]" Ty.pp ty Ty.Triop.pp op pp e1
261
        pp e2 pp e3
262
    | Relop (ty, op, e1, e2) ->
×
263
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Relop.pp op pp e1 pp
264
        e2
265
    | Cvtop (ty, op, e) ->
×
266
      Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Cvtop.pp op pp e
267
    | Naryop (ty, op, es) ->
×
268
      Fmt.pf fmt "@[<hov 1>(%a.%a@ (%a))@]" Ty.pp ty Ty.Naryop.pp op
269
        (Fmt.list ~sep:Fmt.comma pp)
×
270
        es
271
    | Extract (e, h, l) ->
×
272
      Fmt.pf fmt "@[<hov 1>(extract@ %a@ %d@ %d)@]" pp e l h
273
    | Concat (e1, e2) -> Fmt.pf fmt "@[<hov 1>(++@ %a@ %a)@]" pp e1 pp e2
×
274
    | Binder (b, vars, e) ->
×
275
      Fmt.pf fmt "@[<hov 1>(%a@ (%a)@ %a)@]" Binder.pp b
276
        (Fmt.list ~sep:Fmt.sp pp) vars pp e
×
277

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

280
  let pp_smt fmt (es : t list) : unit =
281
    let pp_symbols fmt syms =
×
282
      Fmt.list ~sep:Fmt.semi
×
283
        (fun fmt sym ->
284
          let t = Symbol.type_of sym in
×
285
          Fmt.pf fmt "(let-const %a %a)" Symbol.pp sym Ty.pp t )
×
286
        fmt syms
287
    in
288
    let pp_asserts fmt es =
289
      Fmt.list ~sep:Fmt.semi
×
290
        (fun fmt e -> Fmt.pf fmt "(assert @[<h 2>%a@])" pp e)
×
291
        fmt es
292
    in
293
    let syms = get_symbols es in
294
    if List.length syms > 0 then Fmt.pf fmt "%a@\n" pp_symbols syms;
×
295
    if List.length es > 0 then Fmt.pf fmt "%a@\n" pp_asserts es;
×
296
    Fmt.string fmt "(check-sat)"
×
297
end
298

299
let pp = Pp.pp
300

301
let pp_list = Pp.pp_list
302

303
let pp_smt = Pp.pp_smt
304

305
let to_string e = Fmt.str "%a" pp e
×
306

307
let value (v : Value.t) : t = make (Val v) [@@inline]
631✔
308

309
let ptr base offset = make (Ptr { base; offset })
8✔
310

311
let list l = make (List l)
5✔
312

313
let app symbol args = make (App (symbol, args))
×
314

315
let[@inline] binder bt vars expr = make (Binder (bt, vars, expr))
×
316

317
let let_in vars body = binder Let_in vars body
×
318

319
let forall vars body = binder Forall vars body
×
320

UNCOV
321
let exists vars body = binder Exists vars body
×
322

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

325
let unop ty op hte =
326
  match (op, view hte) with
32✔
NEW
327
  | Ty.Unop.(Regexp_loop _ | Regexp_star), _ -> raw_unop ty op hte
×
328
  | _, Val v -> value (Eval.unop ty op v)
23✔
329
  | Not, Unop (_, Not, hte') -> hte'
1✔
330
  | Neg, Unop (_, Neg, hte') -> hte'
1✔
UNCOV
331
  | Trim, Cvtop (Ty_real, ToString, _) -> hte
×
332
  | Head, List (hd :: _) -> hd
1✔
333
  | Tail, List (_ :: tl) -> make (List tl)
1✔
334
  | Reverse, List es -> make (List (List.rev es))
2✔
335
  | Length, List es -> value (Int (List.length es))
1✔
336
  | _ -> raw_unop ty op hte
2✔
337

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

340
let rec binop ty op hte1 hte2 =
341
  match (op, view hte1, view hte2) with
96✔
NEW
342
  | Ty.Binop.(String_in_re | Regexp_range), _, _ -> raw_binop ty op hte1 hte2
×
343
  | op, Val v1, Val v2 -> value (Eval.binop ty op v1 v2)
67✔
344
  | Sub, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
1✔
345
    if Int32.equal b1 b2 then binop ty Sub os1 os2
1✔
NEW
346
    else raw_binop ty op hte1 hte2
×
347
  | Add, Ptr { base; offset }, _ ->
1✔
348
    ptr base (binop (Ty_bitv 32) Add offset hte2)
1✔
349
  | Sub, Ptr { base; offset }, _ ->
1✔
350
    ptr base (binop (Ty_bitv 32) Sub offset hte2)
1✔
351
  | Rem, Ptr { base; offset }, _ ->
1✔
352
    let rhs = value (Num (I32 base)) in
353
    let addr = binop (Ty_bitv 32) Add rhs offset in
1✔
354
    binop ty Rem addr hte2
1✔
355
  | Add, _, Ptr { base; offset } ->
1✔
356
    ptr base (binop (Ty_bitv 32) Add offset hte1)
1✔
357
  | Sub, _, Ptr { base; offset } ->
×
358
    binop ty Sub hte1 (binop (Ty_bitv 32) Add (value (Num (I32 base))) offset)
×
359
  | (Add | Or), Val (Num (I32 0l)), _ -> hte2
×
360
  | (And | Div | DivU | Mul | Rem | RemU), Val (Num (I32 0l)), _ -> hte1
×
UNCOV
361
  | (Add | Or), _, Val (Num (I32 0l)) -> hte1
×
UNCOV
362
  | (And | Mul), _, Val (Num (I32 0l)) -> hte2
×
363
  | Add, Binop (ty, Add, x, { node = Val v1; _ }), Val v2 ->
1✔
364
    let v = value (Eval.binop ty Add v1 v2) in
1✔
365
    raw_binop ty Add x v
1✔
366
  | Sub, Binop (ty, Sub, x, { node = Val v1; _ }), Val v2 ->
1✔
367
    let v = value (Eval.binop ty Add v1 v2) in
1✔
368
    raw_binop ty Sub x v
1✔
369
  | Mul, Binop (ty, Mul, x, { node = Val v1; _ }), Val v2 ->
1✔
370
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
371
    raw_binop ty Mul x v
1✔
372
  | Add, Val v1, Binop (ty, Add, x, { node = Val v2; _ }) ->
1✔
373
    let v = value (Eval.binop ty Add v1 v2) in
1✔
374
    raw_binop ty Add v x
1✔
375
  | Mul, Val v1, Binop (ty, Mul, x, { node = Val v2; _ }) ->
1✔
376
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
377
    raw_binop ty Mul v x
1✔
378
  | At, List es, Val (Int n) ->
1✔
379
    (* TODO: use another datastructure? *)
380
    begin
381
      match List.nth_opt es n with None -> assert false | Some v -> v
1✔
382
    end
383
  | List_cons, _, List es -> make (List (hte1 :: es))
1✔
UNCOV
384
  | List_append, List _, (List [] | Val (List [])) -> hte1
×
385
  | List_append, (List [] | Val (List [])), List _ -> hte2
×
386
  | List_append, List l0, Val (List l1) -> make (List (l0 @ List.map value l1))
1✔
UNCOV
387
  | List_append, Val (List l0), List l1 -> make (List (List.map value l0 @ l1))
×
UNCOV
388
  | List_append, List l0, List l1 -> make (List (l0 @ l1))
×
389
  | _ -> raw_binop ty op hte1 hte2
12✔
390

NEW
391
let raw_triop ty op e1 e2 e3 = make (Triop (ty, op, e1, e2, e3)) [@@inline]
×
392

393
let triop ty op e1 e2 e3 =
394
  match (op, view e1, view e2, view e3) with
6✔
395
  | Ty.Triop.Ite, Val True, _, _ -> e2
1✔
396
  | Ite, Val False, _, _ -> e3
1✔
397
  | op, Val v1, Val v2, Val v3 -> value (Eval.triop ty op v1 v2 v3)
4✔
398
  | Ite, _, Triop (_, Ite, c2, r1, r2), Triop (_, Ite, _, _, _) ->
×
399
    let else_ = raw_triop ty Ite e1 r2 e3 in
400
    let cond = binop Ty_bool And e1 c2 in
×
NEW
401
    raw_triop ty Ite cond r1 else_
×
NEW
402
  | _ -> raw_triop ty op e1 e2 e3
×
403

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

406
let rec relop ty op hte1 hte2 =
407
  match (op, view hte1, view hte2) with
75✔
408
  | op, Val v1, Val v2 -> value (if Eval.relop ty op v1 v2 then True else False)
29✔
409
  | Ty.Relop.Ne, Val (Real v), _ | Ne, _, Val (Real v) ->
×
410
    if Float.is_nan v || Float.is_infinite v then value True
×
NEW
411
    else raw_relop ty op hte1 hte2
×
412
  | _, Val (Real v), _ | _, _, Val (Real v) ->
×
413
    if Float.is_nan v || Float.is_infinite v then value False
×
NEW
414
    else raw_relop ty op hte1 hte2
×
415
  | Eq, _, Val Nothing | Eq, Val Nothing, _ -> value False
×
416
  | Ne, _, Val Nothing | Ne, Val Nothing, _ -> value True
×
UNCOV
417
  | Eq, _, Val (App (`Op "symbol", [ Str _ ]))
×
418
  | Eq, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
419
    value False
UNCOV
420
  | Ne, _, Val (App (`Op "symbol", [ Str _ ]))
×
UNCOV
421
  | Ne, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
422
    value True
423
  | Eq, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
2✔
424
    if Int32.equal b1 b2 then relop Ty_bool Eq os1 os2 else value False
1✔
425
  | Ne, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
2✔
426
    if Int32.equal b1 b2 then relop Ty_bool Ne os1 os2 else value True
1✔
427
  | ( (LtU | LeU | GtU | GeU)
1✔
428
    , Ptr { base = b1; offset = os1 }
429
    , Ptr { base = b2; offset = os2 } ) ->
430
    if Int32.equal b1 b2 then relop ty op os1 os2
2✔
431
    else
432
      value
2✔
433
        (if Eval.relop ty op (Num (I32 b1)) (Num (I32 b2)) then True else False)
1✔
434
  | op, Val (Num _ as n), Ptr { base; offset = { node = Val (Num _ as o); _ } }
2✔
435
    ->
436
    let base = Eval.binop (Ty_bitv 32) Add (Num (I32 base)) o in
437
    value (if Eval.relop ty op n base then True else False)
1✔
438
  | op, Ptr { base; offset = { node = Val (Num _ as o); _ } }, Val (Num _ as n)
2✔
439
    ->
440
    let base = Eval.binop (Ty_bitv 32) Add (Num (I32 base)) o in
441
    value (if Eval.relop ty op base n then True else False)
1✔
UNCOV
442
  | op, List l1, List l2 -> relop_list op l1 l2
×
443
  | _, _, _ -> raw_relop ty op hte1 hte2
2✔
444

445
and relop_list op l1 l2 =
446
  match (op, l1, l2) with
×
447
  | Eq, [], [] -> value True
×
448
  | Eq, _, [] | Eq, [], _ -> value False
×
UNCOV
449
  | Eq, l1, l2 ->
×
450
    if not (List.compare_lengths l1 l2 = 0) then value False
×
451
    else
452
      List.fold_left2
×
453
        (fun acc a b ->
454
          binop Ty_bool And acc
×
455
          @@
456
          match (ty a, ty b) with
×
457
          | Ty_real, Ty_real -> relop Ty_real Eq a b
×
458
          | _ -> relop Ty_bool Eq a b )
×
UNCOV
459
        (value True) l1 l2
×
UNCOV
460
  | Ne, _, _ -> unop Ty_bool Not @@ relop_list Eq l1 l2
×
461
  | (Lt | LtU | Gt | GtU | Le | LeU | Ge | GeU), _, _ -> assert false
462

463
let raw_cvtop ty op hte = make (Cvtop (ty, op, hte)) [@@inline]
1✔
464

465
let cvtop ty op hte =
466
  match (op, view hte) with
21✔
NEW
467
  | Ty.Cvtop.String_to_re, _ -> raw_cvtop ty op hte
×
468
  | _, Val v -> value (Eval.cvtop ty op v)
20✔
UNCOV
469
  | String_to_float, Cvtop (Ty_real, ToString, real) -> real
×
470
  | _ -> raw_cvtop ty op hte
1✔
471

NEW
472
let raw_naryop ty op es = make (Naryop (ty, op, es)) [@@inline]
×
473

474
let naryop ty op es =
UNCOV
475
  if List.for_all (fun e -> match view e with Val _ -> true | _ -> false) es
×
476
  then
477
    let vs =
7✔
478
      List.map (fun e -> match view e with Val v -> v | _ -> assert false) es
18✔
479
    in
480
    value (Eval.naryop ty op vs)
7✔
481
  else
UNCOV
482
    match (ty, op, List.map view es) with
×
UNCOV
483
    | ( Ty_str
×
484
      , Concat
485
      , [ Naryop (Ty_str, Concat, l1); Naryop (Ty_str, Concat, l2) ] ) ->
486
      raw_naryop Ty_str Concat (l1 @ l2)
487
    | Ty_str, Concat, [ Naryop (Ty_str, Concat, htes); hte ] ->
×
NEW
488
      raw_naryop Ty_str Concat (htes @ [ make hte ])
×
489
    | Ty_str, Concat, [ hte; Naryop (Ty_str, Concat, htes) ] ->
×
NEW
490
      raw_naryop Ty_str Concat (make hte :: htes)
×
NEW
491
    | _ -> raw_naryop ty op es
×
492

493
let nland64 (x : int64) (n : int) =
494
  let rec loop x' n' acc =
×
UNCOV
495
    if n' = 0 then Int64.logand x' acc
×
UNCOV
496
    else loop x' (n' - 1) Int64.(logor (shift_left acc 8) 0xffL)
×
497
  in
498
  loop x n 0L
499

500
let nland32 (x : int32) (n : int) =
501
  let rec loop x' n' acc =
×
UNCOV
502
    if n' = 0 then Int32.logand x' acc
×
UNCOV
503
    else loop x' (n' - 1) Int32.(logor (shift_left acc 8) 0xffl)
×
504
  in
505
  loop x n 0l
506

507
let raw_extract (hte : t) ~(high : int) ~(low : int) : t =
508
  make (Extract (hte, high, low))
8✔
509
[@@inline]
510

511
let extract (hte : t) ~(high : int) ~(low : int) : t =
512
  match (view hte, high, low) with
2✔
513
  | Val (Num (I64 x)), high, low ->
×
514
    let x' = nland64 (Int64.shift_right x (low * 8)) (high - low) in
×
515
    value (Num (I64 x'))
×
UNCOV
516
  | Concat (_, e), 4, 0 when Ty.size (ty e) = 4 -> e
×
UNCOV
517
  | Concat (e, _), 8, 4 when Ty.size (ty e) = 4 -> e
×
518
  | _ ->
2✔
519
    if high - low = Ty.size (ty hte) then hte else raw_extract hte ~high ~low
1✔
520

521
let extract2 (hte : t) (pos : int) : t =
522
  match view hte with
×
UNCOV
523
  | Val (Num (I32 i)) ->
×
524
    let i' = Int32.(to_int @@ logand 0xffl @@ shift_right i (pos * 8)) in
×
525
    value (Num (I8 i'))
UNCOV
526
  | Val (Num (I64 i)) ->
×
527
    let i' = Int64.(to_int @@ logand 0xffL @@ shift_right i (pos * 8)) in
×
528
    value (Num (I8 i'))
529
  | Cvtop
×
530
      ( _
UNCOV
531
      , (Zero_extend 24 | Sign_extend 24)
×
532
      , ({ node = Symbol { ty = Ty_bitv 8; _ }; _ } as sym) ) ->
533
    sym
UNCOV
534
  | _ -> make (Extract (hte, pos + 1, pos))
×
535

536
let raw_concat (msb : t) (lsb : t) : t = make (Concat (msb, lsb)) [@@inline]
3✔
537

538
let concat (msb : t) (lsb : t) : t =
539
  match (view msb, view lsb) with
3✔
UNCOV
540
  | ( Extract ({ node = Val (Num (I64 x2)); _ }, h2, l2)
×
541
    , Extract ({ node = Val (Num (I64 x1)); _ }, h1, l1) ) ->
542
    let d1 = h1 - l1 in
543
    let d2 = h2 - l2 in
544
    let x1' = nland64 (Int64.shift_right x1 (l1 * 8)) d1 in
×
545
    let x2' = nland64 (Int64.shift_right x2 (l2 * 8)) d2 in
×
546
    let x = Int64.(logor (shift_left x2' (d1 * 8)) x1') in
×
NEW
547
    raw_extract (value (Num (I64 x))) ~high:(d1 + d2) ~low:0
×
UNCOV
548
  | ( Extract ({ node = Val (Num (I32 x2)); _ }, h2, l2)
×
549
    , Extract ({ node = Val (Num (I32 x1)); _ }, h1, l1) ) ->
550
    let d1 = h1 - l1 in
551
    let d2 = h2 - l2 in
552
    let x1' = nland32 (Int32.shift_right x1 (l1 * 8)) d1 in
×
553
    let x2' = nland32 (Int32.shift_right x2 (l2 * 8)) d2 in
×
UNCOV
554
    let x = Int32.(logor (shift_left x2' (d1 * 8)) x1') in
×
NEW
555
    raw_extract (value (Num (I32 x))) ~high:(d1 + d2) ~low:0
×
556
  | Extract (s1, h, m1), Extract (s2, m2, l) when equal s1 s2 && m1 = m2 ->
3✔
557
    raw_extract s1 ~high:h ~low:l
3✔
UNCOV
558
  | ( Extract ({ node = Val (Num (I64 x2)); _ }, h2, l2)
×
559
    , Concat
560
        ({ node = Extract ({ node = Val (Num (I64 x1)); _ }, h1, l1); _ }, se) )
UNCOV
561
    when not (is_num se) ->
×
562
    let d1 = h1 - l1 in
×
563
    let d2 = h2 - l2 in
564
    let x1' = nland64 (Int64.shift_right x1 (l1 * 8)) d1 in
×
565
    let x2' = nland64 (Int64.shift_right x2 (l2 * 8)) d2 in
×
566
    let x = Int64.(logor (shift_left x2' (d1 * 8)) x1') in
×
NEW
567
    raw_concat (raw_extract (value (Num (I64 x))) ~high:(d1 + d2) ~low:0) se
×
NEW
568
  | _ -> raw_concat msb lsb
×
569

570
(* TODO: don't rebuild so many values it generates unecessary hc lookups *)
571
let merge_extracts (e1, h, m1) (e2, m2, l) =
572
  let ty = ty e1 in
×
573
  if m1 = m2 && equal e1 e2 then
×
UNCOV
574
    if h - l = Ty.size ty then e1 else make (Extract (e1, h, l))
×
UNCOV
575
  else make (Concat (make (Extract (e1, h, m1)), make (Extract (e2, m2, l))))
×
576

577
let concat3 ~msb ~lsb offset =
578
  assert (offset > 0 && offset <= 8);
×
579
  match (view msb, view lsb) with
×
580
  | Val (Num (I8 i1)), Val (Num (I8 i2)) ->
×
UNCOV
581
    value (Num (I32 Int32.(logor (shift_left (of_int i1) 8) (of_int i2))))
×
UNCOV
582
  | Val (Num (I8 i1)), Val (Num (I32 i2)) ->
×
583
    let offset = offset * 8 in
584
    if offset < 32 then
585
      value (Num (I32 Int32.(logor (shift_left (of_int i1) offset) i2)))
×
586
    else
587
      let i1' = Int64.of_int i1 in
×
588
      let i2' = Int64.of_int32 i2 in
×
UNCOV
589
      value (Num (I64 Int64.(logor (shift_left i1' offset) i2')))
×
590
  | Val (Num (I8 i1)), Val (Num (I64 i2)) ->
×
591
    let offset = offset * 8 in
UNCOV
592
    value (Num (I64 Int64.(logor (shift_left (of_int i1) offset) i2)))
×
593
  | Extract (e1, h, m1), Extract (e2, m2, l) ->
×
594
    merge_extracts (e1, h, m1) (e2, m2, l)
595
  | Extract (e1, h, m1), Concat ({ node = Extract (e2, m2, l); _ }, e3) ->
×
UNCOV
596
    make (Concat (merge_extracts (e1, h, m1) (e2, m2, l), e3))
×
UNCOV
597
  | _ -> make (Concat (msb, lsb))
×
598

599
let rec simplify_expr ?(rm_extract = true) (hte : t) : t =
19✔
600
  match view hte with
25✔
601
  | Val _ | Symbol _ -> hte
5✔
602
  | Ptr { base; offset } -> ptr base (simplify_expr offset)
×
603
  | List es -> make @@ List (List.map simplify_expr es)
×
UNCOV
604
  | App (x, es) -> make @@ App (x, List.map simplify_expr es)
×
605
  | Unop (ty, op, e) ->
×
606
    let e = simplify_expr e in
UNCOV
607
    unop ty op e
×
608
  | Binop (ty, op, e1, e2) ->
6✔
609
    let e1 = simplify_expr e1 in
610
    let e2 = simplify_expr e2 in
6✔
611
    binop ty op e1 e2
6✔
612
  | Relop (ty, op, e1, e2) ->
×
613
    let e1 = simplify_expr e1 in
614
    let e2 = simplify_expr e2 in
×
UNCOV
615
    relop ty op e1 e2
×
616
  | Triop (ty, op, c, e1, e2) ->
×
617
    let c = simplify_expr c in
618
    let e1 = simplify_expr e1 in
×
619
    let e2 = simplify_expr e2 in
×
UNCOV
620
    triop ty op c e1 e2
×
621
  | Cvtop (ty, op, e) ->
×
622
    let e = simplify_expr e in
UNCOV
623
    cvtop ty op e
×
624
  | Naryop (ty, op, es) ->
×
625
    let es = List.map (simplify_expr ~rm_extract:false) es in
UNCOV
626
    naryop ty op es
×
627
  | Extract (s, high, low) ->
5✔
628
    if not rm_extract then hte else extract s ~high ~low
1✔
629
  | Concat (e1, e2) ->
3✔
630
    let msb = simplify_expr ~rm_extract:false e1 in
631
    let lsb = simplify_expr ~rm_extract:false e2 in
3✔
632
    concat msb lsb
3✔
UNCOV
633
  | Binder _ ->
×
634
    (* Not simplifying anything atm *)
635
    hte
636

637
module Cache = Hashtbl.Make (struct
638
  type nonrec t = t
639

640
  let hash = hash
641

642
  let equal = equal
643
end)
644

645
let simplify =
646
  (* TODO: it may make sense to share the cache with simplify_expr ? *)
647
  let cache = Cache.create 512 in
648
  fun e ->
1✔
649
    match Cache.find_opt cache e with
3✔
UNCOV
650
    | Some simplified -> simplified
×
651
    | None ->
3✔
652
      let rec loop x =
653
        let x' = simplify_expr x in
7✔
654
        if equal x x' then begin
3✔
655
          Cache.add cache e x';
656
          x'
3✔
657
        end
658
        else loop x'
4✔
659
      in
660
      loop e
661

662
module Bool = struct
663
  open Ty
664

665
  let of_val = function
666
    | Val True -> Some true
×
UNCOV
667
    | Val False -> Some false
×
UNCOV
668
    | _ -> None
×
669

670
  let true_ = value True
1✔
671

672
  let false_ = value False
1✔
673

674
  let to_val b = if b then true_ else false_
×
675

UNCOV
676
  let v b = to_val b [@@inline]
×
677

678
  let not b =
679
    let bexpr = view b in
×
680
    match of_val bexpr with
×
UNCOV
681
    | Some b -> to_val (not b)
×
682
    | None -> (
×
683
      match bexpr with
UNCOV
684
      | Unop (Ty_bool, Not, cond) -> cond
×
UNCOV
685
      | _ -> unop Ty_bool Not b )
×
686

687
  let equal b1 b2 =
688
    match (view b1, view b2) with
×
UNCOV
689
    | Val True, Val True | Val False, Val False -> true_
×
UNCOV
690
    | _ -> relop Ty_bool Eq b1 b2
×
691

692
  let distinct b1 b2 =
693
    match (view b1, view b2) with
×
UNCOV
694
    | Val True, Val False | Val False, Val True -> true_
×
UNCOV
695
    | _ -> relop Ty_bool Ne b1 b2
×
696

697
  let and_ b1 b2 =
698
    match (of_val (view b1), of_val (view b2)) with
×
699
    | Some true, _ -> b2
×
700
    | _, Some true -> b1
×
UNCOV
701
    | Some false, _ | _, Some false -> false_
×
UNCOV
702
    | _ -> binop Ty_bool And b1 b2
×
703

704
  let or_ b1 b2 =
705
    match (of_val (view b1), of_val (view b2)) with
×
706
    | Some false, _ -> b2
×
707
    | _, Some false -> b1
×
UNCOV
708
    | Some true, _ | _, Some true -> true_
×
709
    | _ -> binop Ty_bool Or b1 b2
×
710

UNCOV
711
  let ite c r1 r2 = triop Ty_bool Ite c r1 r2
×
712
end
713

714
module Make (T : sig
715
  type elt
716

717
  val ty : Ty.t
718

719
  val num : elt -> Num.t
720
end) =
721
struct
722
  open Ty
723

724
  let v i = value (Num (T.num i))
×
725

726
  let sym x = symbol Symbol.(x @: T.ty)
×
727

728
  let ( ~- ) e = unop T.ty Neg e
×
729

730
  let ( = ) e1 e2 = relop Ty_bool Eq e1 e2
×
731

732
  let ( != ) e1 e2 = relop Ty_bool Ne e1 e2
×
733

734
  let ( > ) e1 e2 = relop T.ty Gt e1 e2
×
735

736
  let ( >= ) e1 e2 = relop T.ty Ge e1 e2
×
737

738
  let ( < ) e1 e2 = relop T.ty Lt e1 e2
×
739

UNCOV
740
  let ( <= ) e1 e2 = relop T.ty Le e1 e2
×
741
end
742

743
module Bitv = struct
744
  open Ty
745

746
  module I8 = Make (struct
747
    type elt = int
748

749
    let ty = Ty_bitv 8
750

UNCOV
751
    let num i = Num.I8 i
×
752
  end)
753

754
  module I32 = Make (struct
755
    type elt = int32
756

757
    let ty = Ty_bitv 32
758

UNCOV
759
    let num i = Num.I32 i
×
760
  end)
761

762
  module I64 = Make (struct
763
    type elt = int64
764

765
    let ty = Ty_bitv 64
766

UNCOV
767
    let num i = Num.I64 i
×
768
  end)
769
end
770

771
module Fpa = struct
772
  open Ty
773

774
  module F32 = struct
775
    include Make (struct
776
      type elt = float
777

778
      let ty = Ty_fp 32
779

UNCOV
780
      let num f = Num.F32 (Int32.bits_of_float f)
×
781
    end)
782

783
    (* Redeclare equality due to incorrect theory annotation *)
784
    let ( = ) e1 e2 = relop (Ty_fp 32) Eq e1 e2
×
785

UNCOV
786
    let ( != ) e1 e2 = relop (Ty_fp 32) Ne e1 e2
×
787
  end
788

789
  module F64 = struct
790
    include Make (struct
791
      type elt = float
792

793
      let ty = Ty_fp 64
794

UNCOV
795
      let num f = Num.F64 (Int64.bits_of_float f)
×
796
    end)
797

798
    (* Redeclare equality due to incorrect theory annotation *)
799
    let ( = ) e1 e2 = relop (Ty_fp 64) Eq e1 e2
×
800

UNCOV
801
    let ( != ) e1 e2 = relop (Ty_fp 64) Ne e1 e2
×
802
  end
803
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