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

formalsec / smtml / 533

29 Apr 2026 09:07AM UTC coverage: 48.296% (+0.02%) from 48.274%
533

push

github

filipeom
Expose raw evaluation of binop and relop in eval.ml

40 of 42 new or added lines in 1 file covered. (95.24%)

3 existing lines in 2 files now uncovered.

2424 of 5019 relevant lines covered (48.3%)

41.11 hits per line

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

76.64
/src/smtml/eval.ml
1
(* SPDX-License-Identifier: MIT *)
2
(* Copyright (C) 2023-2026 formalsec *)
3
(* Written by the Smtml programmers *)
4

5
(* Adapted from: *)
6
(* - https://github.com/WebAssembly/spec/blob/main/interpreter/exec/ixx.ml, *)
7
(* - https://github.com/WebAssembly/spec/blob/main/interpreter/exec/fxx.ml, and *)
8
(* - https://github.com/WebAssembly/spec/blob/main/interpreter/exec *)
9

10
type op_type =
×
11
  [ `Unop of Ty.Unop.t
12✔
12
  | `Binop of Ty.Binop.t
13
  | `Relop of Ty.Relop.t
14
  | `Triop of Ty.Triop.t
15
  | `Cvtop of Ty.Cvtop.t
16
  | `Naryop of Ty.Naryop.t
17
  ]
18
[@@deriving show]
19

20
type type_error_info =
×
21
  { index : int
×
22
  ; value : Value.t
23
  ; ty : Ty.t
24
  ; op : op_type
25
  ; msg : string
×
26
  }
27
[@@deriving show]
×
28

29
type error_kind =
30
  [ `Divide_by_zero
31
  | `Conversion_to_integer
32
  | `Integer_overflow
33
  | `Index_out_of_bounds
34
  | `Invalid_format_conversion
35
  | `Unsupported_operator of op_type * Ty.t
36
  | `Unsupported_theory of Ty.t
37
  | `Type_error of type_error_info
38
  ]
39

40
let pp_error_kind fmt err =
41
  match err with
×
42
  | `Divide_by_zero -> Fmt.string fmt "Division by zero"
×
43
  | `Conversion_to_integer ->
×
44
    Fmt.string fmt "Failed to convert value to integer"
45
  | `Integer_overflow ->
×
46
    Fmt.string fmt "Integer overflow: result is outside the representable range"
47
  | `Index_out_of_bounds -> Fmt.string fmt "Index out of bounds"
×
48
  | `Invalid_format_conversion ->
×
49
    Fmt.string fmt "Invalid format conversion string"
50
  | `Unsupported_operator (op, ty) ->
×
51
    Fmt.pf fmt "The operator '%a' is not supported for type '%a'" pp_op_type op
52
      Ty.pp ty
53
  | `Unsupported_theory ty ->
×
54
    Fmt.pf fmt "The theory for type '%a' is not currently supported" Ty.pp ty
55
  | `Type_error { index; value; ty; op; _ } ->
×
56
    Fmt.pf fmt
57
      "@[<h>Type error: Argument %d of operation '%a' expected type '%a', but \
58
       received value '%a' instead.@]"
59
      index pp_op_type op Ty.pp ty Value.pp value
60

61
exception Eval_error of error_kind
62

63
exception Value of Ty.t
64

65
(* Exception helpers *)
66

67
let eval_error kind = raise (Eval_error kind)
13✔
68

69
let type_error n v ty op msg =
70
  eval_error (`Type_error { index = n; value = v; ty; op; msg })
9✔
71

72
let err_str n op ty_expected ty_actual =
73
  Fmt.str "Argument %d of %a expected type %a but got %a instead." n pp_op_type
6✔
74
    op Ty.pp ty_expected Ty.pp ty_actual
75

76
let raise_type_mismatch n op v expected_ty =
77
  let actual_ty = Value.type_of v in
6✔
78
  let msg = err_str n op expected_ty actual_ty in
6✔
79
  type_error n v expected_ty op msg
6✔
80

81
(* Coercion helpers *)
82

83
let[@inline] of_int n op v =
84
  match v with Value.Int x -> x | _ -> raise_type_mismatch n op v Ty_int
1✔
85

86
let[@inline] to_int x = Value.Int x
56✔
87

88
let[@inline] of_real n op v =
89
  match v with Value.Real x -> x | _ -> raise_type_mismatch n op v Ty_real
1✔
90

91
let[@inline] to_real x = Value.Real x
37✔
92

93
let[@inline] of_bool n op v =
94
  match v with
44✔
95
  | Value.True -> true
24✔
96
  | False -> false
19✔
97
  | _ -> raise_type_mismatch n op v Ty_bool
1✔
98

99
let[@inline] to_bool x = if x then Value.True else False
9✔
100

101
let[@inline] of_str n op v =
102
  match v with Value.Str x -> x | _ -> raise_type_mismatch n op v Ty_str
1✔
103

104
let[@inline] to_str x = Value.Str x
19✔
105

106
let[@inline] of_list n op v =
107
  match v with Value.List x -> x | _ -> raise_type_mismatch n op v Ty_list
×
108

109
let[@inline] of_bitv n op v =
110
  match v with Value.Bitv x -> x | _ -> raise_type_mismatch n op v (Ty_bitv 0)
×
111

112
let[@inline] int32_of_bitv n op v = of_bitv n op v |> Bitvector.to_int32
8✔
113

114
let[@inline] int64_of_bitv n op v = of_bitv n op v |> Bitvector.to_int64
12✔
115

116
let[@inline] to_bitv x = Value.Bitv x
76✔
117

118
let[@inline] bitv_of_int32 x = to_bitv (Bitvector.of_int32 x)
14✔
119

120
let[@inline] bitv_of_int64 x = to_bitv (Bitvector.of_int64 x)
11✔
121

122
let[@inline] of_fp32 n op v : int32 =
123
  match v with
96✔
124
  | Value.Num (F32 f) -> f
95✔
125
  | _ -> raise_type_mismatch n op v (Ty_fp 32)
1✔
126

127
let[@inline] to_fp32 (x : int32) = Value.Num (F32 x)
38✔
128

129
let[@inline] fp32_of_float (x : float) = to_fp32 (Int32.bits_of_float x)
17✔
130

131
let[@inline] of_fp64 n op v : int64 =
132
  match v with
89✔
133
  | Value.Num (F64 f) -> f
88✔
134
  | _ -> raise_type_mismatch n op v (Ty_fp 64)
1✔
135

136
let[@inline] to_fp64 (x : int64) = Value.Num (F64 x)
33✔
137

138
let[@inline] fp64_of_float (x : float) = to_fp64 (Int64.bits_of_float x)
16✔
139

140
(* Operator evaluation *)
141

142
module Int = struct
143
  let[@inline] raw_unop (op : Ty.Unop.t) (v : int) : int =
144
    match op with
5✔
145
    | Neg -> Int.neg v
2✔
146
    | Not -> Int.lognot v
1✔
147
    | Abs -> Int.abs v
2✔
UNCOV
148
    | _ -> eval_error (`Unsupported_operator (`Unop op, Ty_int))
×
149

150
  let[@inline] unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
151
    let v = of_int 1 (`Unop op) v in
6✔
152
    to_int (raw_unop op v)
5✔
153

154
  let exp_by_squaring x n =
155
    let rec exp_by_squaring2 y x n =
2✔
156
      if n < 0 then exp_by_squaring2 y (1 / x) ~-n
×
157
      else if n = 0 then y
2✔
158
      else if n mod 2 = 0 then exp_by_squaring2 y (x * x) (n / 2)
1✔
159
      else begin
3✔
160
        assert (n mod 2 = 1);
3✔
161
        exp_by_squaring2 (x * y) (x * x) ((n - 1) / 2)
162
      end
163
    in
164
    exp_by_squaring2 1 x n
165

166
  let[@inline] raw_binop (op : Ty.Binop.t) v1 v2 =
167
    match op with
33✔
168
    | Add -> Int.add v1 v2
4✔
169
    | Sub -> Int.sub v1 v2
2✔
170
    | Mul -> Int.mul v1 v2
2✔
171
    | Div -> Int.div v1 v2
3✔
172
    | Rem -> Int.rem v1 v2
2✔
173
    | Pow -> exp_by_squaring v1 v2
2✔
174
    | Min -> Int.min v1 v2
2✔
175
    | Max -> Int.max v1 v2
2✔
176
    | And -> Int.logand v1 v2
4✔
177
    | Or -> Int.logor v1 v2
2✔
178
    | Xor -> Int.logxor v1 v2
2✔
179
    | Shl -> Int.shift_left v1 v2
2✔
180
    | ShrL -> Int.shift_right_logical v1 v2
1✔
181
    | ShrA -> Int.shift_right v1 v2
3✔
NEW
182
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_int))
×
183

184
  let[@inline] binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
185
    let v1 = of_int 1 (`Binop op) v1 in
33✔
186
    let v2 = of_int 2 (`Binop op) v2 in
33✔
187
    to_int (raw_binop op v1 v2)
32✔
188

189
  let[@inline] raw_relop (op : Ty.Relop.t) a b =
190
    match op with
8✔
191
    | Lt -> a < b
4✔
192
    | Le -> a <= b
4✔
193
    | Eq -> Int.equal a b
×
194
    | Ne -> not (Int.equal a b)
×
195
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_int))
×
196

197
  let[@inline] relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
198
    let a = of_int 1 (`Relop op) v1 in
8✔
199
    let b = of_int 2 (`Relop op) v2 in
8✔
200
    raw_relop op a b
8✔
201

202
  let[@inline] int_of_bool v =
203
    match v with Value.True -> 1 | False -> 0 | _ -> assert false
1✔
204

205
  let[@inline] cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
206
    match op with
5✔
207
    | OfBool -> to_int (int_of_bool v)
3✔
208
    | Reinterpret_float -> Int (Int.of_float (of_real 1 (`Cvtop op) v))
2✔
209
    | _ -> eval_error (`Unsupported_operator (`Cvtop op, Ty_int))
×
210
end
211

212
module Real = struct
213
  let[@inline] unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
214
    let v = of_real 1 (`Unop op) v in
19✔
215
    match op with
18✔
216
    | Neg -> to_real @@ Float.neg v
2✔
217
    | Abs -> to_real @@ Float.abs v
2✔
218
    | Sqrt -> to_real @@ Float.sqrt v
2✔
219
    | Nearest -> to_real @@ Float.round v
3✔
220
    | Ceil -> to_real @@ Float.ceil v
2✔
221
    | Floor -> to_real @@ Float.floor v
2✔
222
    | Trunc -> to_real @@ Float.trunc v
2✔
223
    | Is_nan -> if Float.is_nan v then Value.True else Value.False
1✔
224
    | _ -> eval_error (`Unsupported_operator (`Unop op, Ty_real))
×
225

226
  let[@inline] raw_binop (op : Ty.Binop.t) a b =
227
    match op with
16✔
228
    | Add -> Float.add a b
2✔
229
    | Sub -> Float.sub a b
2✔
230
    | Mul -> Float.mul a b
2✔
231
    | Div -> Float.div a b
3✔
232
    | Rem -> Float.rem a b
2✔
233
    | Min -> Float.min a b
2✔
234
    | Max -> Float.max a b
2✔
235
    | Pow -> Float.pow a b
1✔
NEW
236
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_real))
×
237

238
  let[@inline] binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
239
    let a = of_real 1 (`Binop op) v1 in
16✔
240
    let b = of_real 2 (`Binop op) v2 in
16✔
241
    to_real (raw_binop op a b)
16✔
242

243
  let[@inline] raw_relop (op : Ty.Relop.t) a b =
244
    match op with
12✔
245
    | Lt -> Float.Infix.(a < b)
4✔
246
    | Le -> Float.Infix.(a <= b)
4✔
247
    | Eq -> Float.Infix.(a = b)
2✔
248
    | Ne -> Float.Infix.(a <> b)
2✔
249
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_real))
×
250

251
  let[@inline] relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
252
    let a = of_real 1 (`Relop op) v1 in
12✔
253
    let b = of_real 2 (`Relop op) v2 in
12✔
254
    raw_relop op a b
12✔
255

256
  let[@inline] cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
257
    let op' = `Cvtop op in
8✔
258
    match op with
259
    | ToString -> Str (Float.to_string (of_real 1 op' v))
2✔
260
    | OfString ->
3✔
261
      begin match float_of_string_opt (of_str 1 op' v) with
3✔
262
      | None -> eval_error `Invalid_format_conversion
1✔
263
      | Some v -> to_real v
2✔
264
      end
265
    | Reinterpret_int -> to_real (float_of_int (of_int 1 op' v))
2✔
266
    | Reinterpret_float -> to_int (Float.to_int (of_real 1 op' v))
1✔
267
    | _ -> eval_error (`Unsupported_operator (op', Ty_real))
×
268
end
269

270
module Bool = struct
271
  let[@inline] unop (op : Ty.Unop.t) v =
272
    let b = of_bool 1 (`Unop op) v in
3✔
273
    match op with
2✔
274
    | Not -> to_bool (not b)
2✔
275
    | _ -> eval_error (`Unsupported_operator (`Unop op, Ty_bool))
×
276

277
  let xor b1 b2 =
278
    match (b1, b2) with
4✔
279
    | true, true -> false
1✔
280
    | true, false -> true
1✔
281
    | false, true -> true
1✔
282
    | false, false -> false
1✔
283

284
  let[@inline] binop (op : Ty.Binop.t) v1 v2 =
285
    let a = of_bool 1 (`Binop op) v1 in
13✔
286
    let b = of_bool 2 (`Binop op) v2 in
13✔
287
    match op with
13✔
288
    | And -> to_bool (a && b)
3✔
289
    | Or -> to_bool (a || b)
1✔
290
    | Xor -> to_bool (xor a b)
4✔
291
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_bool))
×
292

293
  let[@inline] triop (op : Ty.Triop.t) c v1 v2 =
294
    match op with
2✔
295
    | Ite -> ( match of_bool 1 (`Triop op) c with true -> v1 | false -> v2 )
1✔
296
    | _ -> eval_error (`Unsupported_operator (`Triop op, Ty_bool))
×
297

298
  let[@inline] relop (op : Ty.Relop.t) v1 v2 =
299
    match op with
9✔
300
    | Eq -> Value.equal v1 v2
4✔
301
    | Ne -> not (Value.equal v1 v2)
5✔
302
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_bool))
×
303

304
  let[@inline] naryop (op : Ty.Naryop.t) vs =
305
    match op with
6✔
306
    | Logand ->
3✔
307
      let exists_false =
308
        let i = ref 0 in
309
        List.find_map
3✔
310
          (fun e ->
311
            incr i;
7✔
312
            let b = of_bool !i (`Naryop op) e in
7✔
313
            if not b then Some () else None )
2✔
314
          vs
315
      in
316
      if Option.is_some exists_false then Value.False else Value.True
1✔
317
    | Logor ->
3✔
318
      let exists_true =
319
        let i = ref 0 in
320
        List.find_map
3✔
321
          (fun e ->
322
            incr i;
6✔
323
            let b = of_bool !i (`Naryop op) e in
6✔
324
            if b then Some () else None )
2✔
325
          vs
326
      in
327
      if Option.is_some exists_true then Value.True else Value.False
1✔
328
    | _ -> eval_error (`Unsupported_operator (`Naryop op, Ty_bool))
×
329
end
330

331
module Str = struct
332
  let replace s t t' =
333
    let len_s = String.length s in
2✔
334
    let len_t = String.length t in
2✔
335
    let rec loop i =
2✔
336
      if i >= len_s then s
×
337
      else if i + len_t > len_s then s
×
338
      else if String.equal (String.sub s i len_t) t then
4✔
339
        let s' = Fmt.str "%s%s" (String.sub s 0 i) t' in
2✔
340
        let s'' = String.sub s (i + len_t) (len_s - i - len_t) in
2✔
341
        Fmt.str "%s%s" s' s''
2✔
342
      else loop (i + 1)
2✔
343
    in
344
    loop 0
345

346
  let indexof s sub start =
347
    let len_s = String.length s in
4✔
348
    let len_sub = String.length sub in
4✔
349
    let max_i = len_s - 1 in
4✔
350
    let rec loop i =
351
      if i > max_i then ~-1
×
352
      else if i + len_sub > len_s then ~-1
×
353
      else if String.equal sub (String.sub s i len_sub) then i
4✔
354
      else loop (i + 1)
4✔
355
    in
356
    if start <= 0 then loop 0 else loop start
×
357

358
  let contains s sub = if indexof s sub 0 < 0 then false else true
×
359

360
  let[@inline] unop (op : Ty.Unop.t) v =
361
    let str = of_str 1 (`Unop op) v in
5✔
362
    match op with
4✔
363
    | Length -> to_int (String.length str)
2✔
364
    | Trim -> to_str (String.trim str)
2✔
365
    | _ -> eval_error (`Unsupported_operator (`Unop op, Ty_str))
×
366

367
  let[@inline] binop (op : Ty.Binop.t) v1 v2 =
368
    let op' = `Binop op in
9✔
369
    let str = of_str 1 op' v1 in
370
    match op with
9✔
371
    | At -> begin
3✔
372
      let i = of_int 2 op' v2 in
373
      try to_str (Fmt.str "%c" (String.get str i))
2✔
374
      with Invalid_argument _ -> eval_error `Index_out_of_bounds
1✔
375
      end
376
    | String_prefix ->
2✔
377
      to_bool (String.starts_with ~prefix:str (of_str 2 op' v2))
2✔
378
    | String_suffix -> to_bool (String.ends_with ~suffix:str (of_str 2 op' v2))
2✔
379
    | String_contains -> to_bool (contains str (of_str 2 op' v2))
2✔
380
    | _ -> eval_error (`Unsupported_operator (op', Ty_str))
×
381

382
  let[@inline] triop (op : Ty.Triop.t) v1 v2 v3 =
383
    let op' = `Triop op in
6✔
384
    let str = of_str 1 op' v1 in
385
    match op with
6✔
386
    | String_extract -> begin
2✔
387
      let i = of_int 2 op' v2 in
388
      let len = of_int 3 op' v3 in
2✔
389
      try to_str (String.sub str i len)
2✔
390
      with Invalid_argument _ -> eval_error `Index_out_of_bounds
×
391
      end
392
    | String_replace ->
2✔
393
      let t = of_str 2 op' v2 in
394
      let t' = of_str 2 op' v3 in
2✔
395
      to_str (replace str t t')
2✔
396
    | String_index ->
2✔
397
      let t = of_str 2 op' v2 in
398
      let i = of_int 3 op' v3 in
2✔
399
      to_int (indexof str t i)
2✔
400
    | _ -> eval_error (`Unsupported_operator (`Triop op, Ty_str))
×
401

402
  let[@inline] raw_relop (op : Ty.Relop.t) a b =
403
    let cmp = String.compare a b in
14✔
404
    match op with
14✔
405
    | Lt -> cmp < 0
4✔
406
    | Le -> cmp <= 0
4✔
407
    | Eq -> cmp = 0
3✔
408
    | Ne -> cmp <> 0
3✔
409
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_str))
×
410

411
  let[@inline] relop (op : Ty.Relop.t) v1 v2 =
412
    let a = of_str 1 (`Relop op) v1 in
14✔
413
    let b = of_str 2 (`Relop op) v2 in
14✔
414
    raw_relop op a b
14✔
415

416
  let[@inline] cvtop (op : Ty.Cvtop.t) v =
417
    let op' = `Cvtop op in
21✔
418
    match op with
419
    | String_to_code ->
2✔
420
      let str = of_str 1 op' v in
421
      to_int (Char.code str.[0])
2✔
422
    | String_from_code ->
2✔
423
      let code = of_int 1 op' v in
424
      to_str (String.make 1 (Char.chr code))
2✔
425
    | String_to_int -> begin
9✔
426
      let s = of_str 1 op' v in
427
      match int_of_string_opt s with
9✔
428
      | None -> eval_error `Invalid_format_conversion
1✔
429
      | Some x -> to_int x
8✔
430
      end
431
    | String_from_int -> to_str (string_of_int (of_int 1 op' v))
5✔
432
    | String_to_float -> begin
3✔
433
      let s = of_str 1 op' v in
434
      match float_of_string_opt s with
3✔
435
      | None -> eval_error `Invalid_format_conversion
1✔
436
      | Some f -> to_real f
2✔
437
      end
438
    | _ -> eval_error (`Unsupported_operator (`Cvtop op, Ty_str))
×
439

440
  let[@inline] naryop (op : Ty.Naryop.t) vs =
441
    let op' = `Naryop op in
4✔
442
    match op with
443
    | Concat ->
4✔
444
      let _, s =
445
        List.fold_left
446
          (fun (i, acc) v ->
447
            let s = of_str i op' v in
10✔
448
            (i + 1, String.cat acc s) )
10✔
449
          (0, "") vs
450
      in
451
      to_str s
4✔
452
    | _ -> eval_error (`Unsupported_operator (`Naryop op, Ty_str))
×
453
end
454

455
module Lst = struct
456
  let[@inline] unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
457
    let lst = of_list 1 (`Unop op) v in
4✔
458
    match op with
4✔
459
    | Head ->
1✔
460
      (* FIXME: Exception handling *)
461
      begin match lst with hd :: _tl -> hd | [] -> assert false
1✔
462
      end
463
    | Tail ->
1✔
464
      (* FIXME: Exception handling *)
465
      begin match lst with _hd :: tl -> List tl | [] -> assert false
1✔
466
      end
467
    | Length -> to_int (List.length lst)
1✔
468
    | Reverse -> List (List.rev lst)
1✔
469
    | _ -> eval_error (`Unsupported_operator (`Unop op, Ty_list))
×
470

471
  let[@inline] binop (op : Ty.Binop.t) v1 v2 =
472
    let op' = `Binop op in
3✔
473
    match op with
474
    | At ->
1✔
475
      let lst = of_list 1 op' v1 in
476
      let i = of_int 2 op' v2 in
1✔
477
      (* TODO: change datastructure? *)
478
      begin match List.nth_opt lst i with
1✔
479
      | None -> eval_error `Index_out_of_bounds
×
480
      | Some v -> v
1✔
481
      end
482
    | List_cons -> List (v1 :: of_list 1 op' v2)
1✔
483
    | List_append -> List (of_list 1 op' v1 @ of_list 2 op' v2)
1✔
484
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_list))
×
485

486
  let[@inline] triop (op : Ty.Triop.t) (v1 : Value.t) (v2 : Value.t)
487
    (v3 : Value.t) : Value.t =
488
    let op' = `Triop op in
1✔
489
    match op with
490
    | List_set ->
1✔
491
      let lst = of_list 1 op' v1 in
492
      let i = of_int 2 op' v2 in
1✔
493
      let rec set i lst v acc =
1✔
494
        match (i, lst) with
2✔
495
        | 0, _ :: tl -> List.rev_append acc (v :: tl)
1✔
496
        | i, hd :: tl -> set (i - 1) tl v (hd :: acc)
1✔
497
        | _, [] -> eval_error `Index_out_of_bounds
×
498
      in
499
      List (set i lst v3 [])
1✔
500
    | _ -> eval_error (`Unsupported_operator (`Triop op, Ty_list))
×
501

502
  let[@inline] naryop (op : Ty.Naryop.t) (vs : Value.t list) : Value.t =
503
    let op' = `Naryop op in
×
504
    match op with
505
    | Concat -> List (List.concat_map (of_list 0 op') vs)
×
506
    | _ -> eval_error (`Unsupported_operator (`Naryop op, Ty_list))
×
507
end
508

509
module I64 = struct
510
  let cmp_u x op y = op Int64.(add x min_int) Int64.(add y min_int) [@@inline]
2✔
511

512
  let lt_u x y = cmp_u x Int64.Infix.( < ) y [@@inline]
2✔
513
end
514

515
module Bitv = struct
516
  let[@inline] unop op bv =
517
    let bv = of_bitv 1 (`Unop op) bv in
5✔
518
    match op with
5✔
519
    | Ty.Unop.Neg -> to_bitv (Bitvector.neg bv)
2✔
520
    | Not -> to_bitv (Bitvector.lognot bv)
3✔
521
    | Clz -> to_bitv (Bitvector.clz bv)
×
522
    | Ctz -> to_bitv (Bitvector.ctz bv)
×
523
    | Popcnt -> to_bitv (Bitvector.popcnt bv)
×
524
    | _ ->
×
525
      eval_error
526
        (`Unsupported_operator (`Unop op, Ty_bitv (Bitvector.numbits bv)))
×
527

528
  let[@inline] binop op bv1 bv2 =
529
    let bv1 = of_bitv 1 (`Binop op) bv1 in
38✔
530
    let bv2 = of_bitv 2 (`Binop op) bv2 in
38✔
531
    match op with
38✔
532
    | Ty.Binop.Add -> to_bitv (Bitvector.add bv1 bv2)
11✔
533
    | Sub -> to_bitv (Bitvector.sub bv1 bv2)
4✔
534
    | Mul -> to_bitv (Bitvector.mul bv1 bv2)
4✔
535
    | Div -> to_bitv (Bitvector.div bv1 bv2)
2✔
536
    | DivU -> to_bitv (Bitvector.div_u bv1 bv2)
×
537
    | Rem -> to_bitv (Bitvector.rem bv1 bv2)
3✔
538
    | RemU -> to_bitv (Bitvector.rem_u bv1 bv2)
×
539
    | And -> to_bitv (Bitvector.logand bv1 bv2)
2✔
540
    | Or -> to_bitv (Bitvector.logor bv1 bv2)
2✔
541
    | Xor -> to_bitv (Bitvector.logxor bv1 bv2)
2✔
542
    | Shl -> to_bitv (Bitvector.shl bv1 bv2)
2✔
543
    | ShrL -> to_bitv (Bitvector.lshr bv1 bv2)
×
544
    | ShrA -> to_bitv (Bitvector.ashr bv1 bv2)
2✔
545
    | Rotl -> to_bitv (Bitvector.rotate_left bv1 bv2)
2✔
546
    | Rotr -> to_bitv (Bitvector.rotate_right bv1 bv2)
2✔
547
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_bitv 0))
×
548

549
  let[@inline] relop op bv1 bv2 =
550
    let bv1 = of_bitv 1 (`Relop op) bv1 in
23✔
551
    let bv2 = of_bitv 2 (`Relop op) bv2 in
23✔
552
    match op with
23✔
553
    | Ty.Relop.Lt -> Bitvector.lt bv1 bv2
6✔
554
    | LtU -> Bitvector.lt_u bv1 bv2
5✔
555
    | Le -> Bitvector.le bv1 bv2
6✔
556
    | LeU -> Bitvector.le_u bv1 bv2
5✔
557
    | Eq -> Bitvector.equal bv1 bv2
1✔
558
    | Ne -> not @@ Bitvector.equal bv1 bv2
×
559

560
  let[@inline] cvtop op bv =
561
    let bv = of_bitv 1 (`Cvtop op) bv in
×
562
    match op with
×
563
    | Ty.Cvtop.Sign_extend m -> to_bitv (Bitvector.sign_extend m bv)
×
564
    | Ty.Cvtop.Zero_extend m -> to_bitv (Bitvector.zero_extend m bv)
×
565
    | _ ->
×
566
      eval_error
567
        (`Unsupported_operator (`Cvtop op, Ty_bitv (Bitvector.numbits bv)))
×
568
end
569

570
module F32 = struct
571
  (* Stolen from Owi *)
572
  let[@inline] abs x = Int32.logand x Int32.max_int
6✔
573

574
  let[@inline] neg x = Int32.logxor x Int32.min_int
3✔
575

576
  let[@inline] unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
577
    let f = Int32.float_of_bits (of_fp32 1 (`Unop op) v) in
15✔
578
    match op with
15✔
579
    | Neg -> to_fp32 @@ neg @@ of_fp32 1 (`Unop op) v
3✔
580
    | Abs -> to_fp32 @@ abs @@ of_fp32 1 (`Unop op) v
1✔
581
    | Sqrt -> fp32_of_float @@ Float.sqrt f
3✔
582
    | Nearest -> fp32_of_float @@ Float.round f
2✔
583
    | Ceil -> fp32_of_float @@ Float.ceil f
1✔
584
    | Floor -> fp32_of_float @@ Float.floor f
1✔
585
    | Trunc -> fp32_of_float @@ Float.trunc f
2✔
586
    | Is_nan -> if Float.is_nan f then Value.True else Value.False
1✔
587
    | _ -> eval_error (`Unsupported_operator (`Unop op, Ty_fp 32))
×
588

589
  (* Stolen from Owi *)
590
  let[@inline] copy_sign x y =
591
    Int32.logor (abs x) (Int32.logand y Int32.min_int)
5✔
592

593
  let[@inline] binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
594
    let a = Int32.float_of_bits @@ of_fp32 1 (`Binop op) v1 in
13✔
595
    let b = Int32.float_of_bits @@ of_fp32 1 (`Binop op) v2 in
13✔
596
    match op with
13✔
597
    | Add -> fp32_of_float @@ Float.add a b
1✔
598
    | Sub -> fp32_of_float @@ Float.sub a b
1✔
599
    | Mul -> fp32_of_float @@ Float.mul a b
1✔
600
    | Div -> fp32_of_float @@ Float.div a b
2✔
601
    | Rem -> fp32_of_float @@ Float.rem a b
1✔
602
    | Min -> fp32_of_float @@ Float.min a b
1✔
603
    | Max -> fp32_of_float @@ Float.max a b
1✔
604
    | Copysign ->
5✔
605
      let a = of_fp32 1 (`Binop op) v1 in
606
      let b = of_fp32 1 (`Binop op) v2 in
5✔
607
      to_fp32 (copy_sign a b)
5✔
608
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_fp 32))
×
609

610
  let[@inline] relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
611
    let a = Int32.float_of_bits @@ of_fp32 1 (`Relop op) v1 in
13✔
612
    let b = Int32.float_of_bits @@ of_fp32 2 (`Relop op) v2 in
13✔
613
    match op with
13✔
614
    | Eq -> Float.Infix.(a = b)
3✔
615
    | Ne -> Float.Infix.(a <> b)
2✔
616
    | Lt -> Float.Infix.(a < b)
4✔
617
    | Le -> Float.Infix.(a <= b)
4✔
618
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_fp 32))
×
619
end
620

621
module F64 = struct
622
  (* Stolen from owi *)
623
  let[@inline] abs x = Int64.logand x Int64.max_int
6✔
624

625
  let[@inline] neg x = Int64.logxor x Int64.min_int
1✔
626

627
  let[@inline] unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
628
    let f = Int64.float_of_bits @@ of_fp64 1 (`Unop op) v in
11✔
629
    match op with
11✔
630
    | Neg -> to_fp64 @@ neg @@ of_fp64 1 (`Unop op) v
1✔
631
    | Abs -> to_fp64 @@ abs @@ of_fp64 1 (`Unop op) v
1✔
632
    | Sqrt -> fp64_of_float @@ Float.sqrt f
1✔
633
    | Nearest -> fp64_of_float @@ Float.round f
2✔
634
    | Ceil -> fp64_of_float @@ Float.ceil f
1✔
635
    | Floor -> fp64_of_float @@ Float.floor f
1✔
636
    | Trunc -> fp64_of_float @@ Float.trunc f
2✔
637
    | Is_nan -> if Float.is_nan f then Value.True else Value.False
1✔
638
    | _ -> Fmt.failwith {|unop: Unsupported f32 operator "%a"|} Ty.Unop.pp op
×
639

640
  let copy_sign x y = Int64.logor (abs x) (Int64.logand y Int64.min_int)
5✔
641

642
  let[@inline] binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
643
    let a = Int64.float_of_bits @@ of_fp64 1 (`Binop op) v1 in
14✔
644
    let b = Int64.float_of_bits @@ of_fp64 2 (`Binop op) v2 in
14✔
645
    match op with
14✔
646
    | Add -> fp64_of_float @@ Float.add a b
1✔
647
    | Sub -> fp64_of_float @@ Float.sub a b
1✔
648
    | Mul -> fp64_of_float @@ Float.mul a b
1✔
649
    | Div -> fp64_of_float @@ Float.div a b
3✔
650
    | Rem -> fp64_of_float @@ Float.rem a b
1✔
651
    | Min -> fp64_of_float @@ Float.min a b
1✔
652
    | Max -> fp64_of_float @@ Float.max a b
1✔
653
    | Copysign ->
5✔
654
      let a = of_fp64 1 (`Binop op) v1 in
655
      let b = of_fp64 2 (`Binop op) v2 in
5✔
656
      to_fp64 @@ copy_sign a b
5✔
657
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_fp 64))
×
658

659
  let[@inline] relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
660
    let a = Int64.float_of_bits @@ of_fp64 1 (`Relop op) v1 in
12✔
661
    let b = Int64.float_of_bits @@ of_fp64 2 (`Relop op) v2 in
12✔
662
    match op with
12✔
663
    | Eq -> Float.Infix.(a = b)
2✔
664
    | Ne -> Float.Infix.(a <> b)
2✔
665
    | Lt -> Float.Infix.(a < b)
4✔
666
    | Le -> Float.Infix.(a <= b)
4✔
667
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_fp 64))
×
668
end
669

670
module I32CvtOp = struct
671
  let trunc_f32_s (x : int32) =
672
    if Int32.Infix.(x <> x) then eval_error `Conversion_to_integer
×
673
    else
674
      let xf = Int32.float_of_bits x in
2✔
675
      if
2✔
676
        Float.Infix.(
677
          xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) )
×
678
      then eval_error `Integer_overflow
×
679
      else Int32.of_float xf
2✔
680

681
  let trunc_f32_u (x : int32) =
682
    if Int32.Infix.(x <> x) then eval_error `Conversion_to_integer
×
683
    else
684
      let xf = Int32.float_of_bits x in
1✔
685
      if Float.Infix.(xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0)
×
686
      then eval_error `Integer_overflow
×
687
      else Int32.of_float xf
1✔
688

689
  let trunc_f64_s (x : int64) =
690
    if Int64.Infix.(x <> x) then eval_error `Conversion_to_integer
×
691
    else
692
      let xf = Int64.float_of_bits x in
2✔
693
      if
2✔
694
        Float.Infix.(
695
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
696
      then eval_error `Integer_overflow
×
697
      else Int32.of_float xf
2✔
698

699
  let trunc_f64_u (x : int64) =
700
    if Int64.Infix.(x <> x) then eval_error `Conversion_to_integer
×
701
    else
702
      let xf = Int64.float_of_bits x in
1✔
703
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
704
      then eval_error `Integer_overflow
×
705
      else Int32.of_float xf
1✔
706

707
  let trunc_sat_f32_s x =
708
    if Int32.Infix.(x <> x) then 0l
×
709
    else
710
      let xf = Int32.float_of_bits x in
1✔
711
      if Float.Infix.(xf < Int32.(to_float min_int)) then Int32.min_int
×
712
      else if Float.Infix.(xf >= -.Int32.(to_float min_int)) then Int32.max_int
×
713
      else Int32.of_float xf
1✔
714

715
  let trunc_sat_f32_u x =
716
    if Int32.Infix.(x <> x) then 0l
×
717
    else
718
      let xf = Int32.float_of_bits x in
1✔
719
      if Float.Infix.(xf <= -1.0) then 0l
×
720
      else if Float.Infix.(xf >= -.Int32.(to_float min_int) *. 2.0) then -1l
×
721
      else Int32.of_float xf
1✔
722

723
  let trunc_sat_f64_s x =
724
    if Int64.Infix.(x <> x) then 0l
×
725
    else
726
      let xf = Int64.float_of_bits x in
1✔
727
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int32.min_int
×
728
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int32.max_int
×
729
      else Int32.of_float xf
1✔
730

731
  let trunc_sat_f64_u x =
732
    if Int64.Infix.(x <> x) then 0l
×
733
    else
734
      let xf = Int64.float_of_bits x in
1✔
735
      if Float.Infix.(xf <= -1.0) then 0l
×
736
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1l
×
737
      else Int32.of_float xf
1✔
738

739
  let cvtop op v =
740
    let op' = `Cvtop op in
17✔
741
    match op with
742
    | Ty.Cvtop.WrapI64 -> bitv_of_int32 (Int64.to_int32 (int64_of_bitv 1 op' v))
2✔
743
    | TruncSF32 -> bitv_of_int32 (trunc_f32_s (of_fp32 1 op' v))
2✔
744
    | TruncUF32 -> bitv_of_int32 (trunc_f32_u (of_fp32 1 op' v))
1✔
745
    | TruncSF64 -> bitv_of_int32 (trunc_f64_s (of_fp64 1 op' v))
2✔
746
    | TruncUF64 -> bitv_of_int32 (trunc_f64_u (of_fp64 1 op' v))
1✔
747
    | Trunc_sat_f32_s -> bitv_of_int32 (trunc_sat_f32_s (of_fp32 1 op' v))
1✔
748
    | Trunc_sat_f32_u -> bitv_of_int32 (trunc_sat_f32_u (of_fp32 1 op' v))
1✔
749
    | Trunc_sat_f64_s -> bitv_of_int32 (trunc_sat_f64_s (of_fp64 1 op' v))
1✔
750
    | Trunc_sat_f64_u -> bitv_of_int32 (trunc_sat_f64_u (of_fp64 1 op' v))
1✔
751
    | Reinterpret_float -> bitv_of_int32 (of_fp32 1 op' v)
2✔
752
    | Sign_extend n -> to_bitv (Bitvector.sign_extend n (of_bitv 1 op' v))
2✔
753
    | Zero_extend n -> to_bitv (Bitvector.zero_extend n (of_bitv 1 op' v))
1✔
754
    | OfBool ->
×
755
      let b = of_bool 1 (`Cvtop OfBool) v in
756
      if b then to_bitv (Bitvector.make Z.one 32)
×
757
      else to_bitv (Bitvector.make Z.zero 32)
×
758
    | ToBool ->
×
759
      let bv = of_bitv 1 (`Cvtop ToBool) v in
760
      if not (Bitvector.eqz bv) then Value.True else False
×
761
    | _ -> eval_error (`Unsupported_operator (op', Ty_bitv 32))
×
762
end
763

764
module I64CvtOp = struct
765
  let extend_i32_u (x : int32) =
766
    Int64.(logand (of_int32 x) 0x0000_0000_ffff_ffffL)
1✔
767

768
  let trunc_f32_s (x : int32) =
769
    if Int32.Infix.(x <> x) then eval_error `Conversion_to_integer
×
770
    else
771
      let xf = Int32.float_of_bits x in
2✔
772
      if
2✔
773
        Float.Infix.(
774
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
775
      then eval_error `Integer_overflow
×
776
      else Int64.of_float xf
2✔
777

778
  let trunc_f32_u (x : int32) =
779
    if Int32.Infix.(x <> x) then eval_error `Conversion_to_integer
×
780
    else
781
      let xf = Int32.float_of_bits x in
1✔
782
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
783
      then eval_error `Integer_overflow
×
784
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
785
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
786
      else Int64.of_float xf
1✔
787

788
  let trunc_f64_s (x : int64) =
789
    if Int64.Infix.(x <> x) then eval_error `Conversion_to_integer
×
790
    else
791
      let xf = Int64.float_of_bits x in
2✔
792
      if
2✔
793
        Float.Infix.(
794
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
795
      then eval_error `Integer_overflow
×
796
      else Int64.of_float xf
2✔
797

798
  let trunc_f64_u (x : int64) =
799
    if Int64.Infix.(x <> x) then eval_error `Conversion_to_integer
×
800
    else
801
      let xf = Int64.float_of_bits x in
1✔
802
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
803
      then eval_error `Integer_overflow
×
804
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
805
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
806
      else Int64.of_float xf
1✔
807

808
  let trunc_sat_f32_s (x : int32) =
809
    if Int32.Infix.(x <> x) then 0L
×
810
    else
811
      let xf = Int32.float_of_bits x in
1✔
812
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
813
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
814
      else Int64.of_float xf
1✔
815

816
  let trunc_sat_f32_u (x : int32) =
817
    if Int32.Infix.(x <> x) then 0L
×
818
    else
819
      let xf = Int32.float_of_bits x in
1✔
820
      if Float.Infix.(xf <= -1.0) then 0L
×
821
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1L
×
822
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
823
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
824
      else Int64.of_float xf
1✔
825

826
  let trunc_sat_f64_s (x : int64) =
827
    if Int64.Infix.(x <> x) then 0L
×
828
    else
829
      let xf = Int64.float_of_bits x in
1✔
830
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
831
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
832
      else Int64.of_float xf
1✔
833

834
  let trunc_sat_f64_u (x : int64) =
835
    if Int64.Infix.(x <> x) then 0L
×
836
    else
837
      let xf = Int64.float_of_bits x in
1✔
838
      if Float.Infix.(xf <= -1.0) then 0L
×
839
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1L
×
840
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
841
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
842
      else Int64.of_float xf
1✔
843

844
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
845
    let op' = `Cvtop op in
17✔
846
    match op with
847
    | Sign_extend n -> to_bitv (Bitvector.sign_extend n (of_bitv 1 op' v))
3✔
848
    | Zero_extend n -> to_bitv (Bitvector.zero_extend n (of_bitv 1 op' v))
2✔
849
    | TruncSF32 -> bitv_of_int64 (trunc_f32_s (of_fp32 1 op' v))
2✔
850
    | TruncUF32 -> bitv_of_int64 (trunc_f32_u (of_fp32 1 op' v))
1✔
851
    | TruncSF64 -> bitv_of_int64 (trunc_f64_s (of_fp64 1 op' v))
2✔
852
    | TruncUF64 -> bitv_of_int64 (trunc_f64_u (of_fp64 1 op' v))
1✔
853
    | Trunc_sat_f32_s -> bitv_of_int64 (trunc_sat_f32_s (of_fp32 1 op' v))
1✔
854
    | Trunc_sat_f32_u -> bitv_of_int64 (trunc_sat_f32_u (of_fp32 1 op' v))
1✔
855
    | Trunc_sat_f64_s -> bitv_of_int64 (trunc_sat_f64_s (of_fp64 1 op' v))
1✔
856
    | Trunc_sat_f64_u -> bitv_of_int64 (trunc_sat_f64_u (of_fp64 1 op' v))
1✔
857
    | Reinterpret_float -> bitv_of_int64 (of_fp64 1 op' v)
1✔
858
    | WrapI64 -> type_error 1 v (Ty_bitv 64) op' "Cannot wrapI64 on an I64"
1✔
859
    | ToBool | OfBool | _ -> eval_error (`Unsupported_operator (op', Ty_bitv 64))
×
860
end
861

862
module F32CvtOp = struct
863
  let demote_f64 x =
864
    let xf = Int64.float_of_bits x in
2✔
865
    if Float.Infix.(xf = xf) then Int32.bits_of_float xf
1✔
866
    else
867
      let nan64bits = x in
1✔
868
      let sign_field =
869
        Int64.(shift_left (shift_right_logical nan64bits 63) 31)
1✔
870
      in
871
      let significand_field =
872
        Int64.(shift_right_logical (shift_left nan64bits 12) 41)
1✔
873
      in
874
      let fields = Int64.logor sign_field significand_field in
875
      Int32.logor 0x7fc0_0000l (Int64.to_int32 fields)
1✔
876

877
  let convert_i32_s x = Int32.bits_of_float (Int32.to_float x)
2✔
878

879
  let convert_i32_u x =
880
    Int32.bits_of_float
2✔
881
      Int32.(
882
        Int32.Infix.(
883
          if x >= 0l then to_float x
1✔
884
          else to_float (logor (shift_right_logical x 1) (logand x 1l)) *. 2.0 ) )
1✔
885

886
  let convert_i64_s x =
887
    Int32.bits_of_float
3✔
888
      Int64.(
889
        Int64.Infix.(
890
          if abs x < 0x10_0000_0000_0000L then to_float x
2✔
891
          else
892
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
893
            to_float (logor (shift_right x 12) r) *. 0x1p12 ) )
1✔
894

895
  let convert_i64_u x =
896
    Int32.bits_of_float
2✔
897
      Int64.(
898
        Int64.Infix.(
899
          if I64.lt_u x 0x10_0000_0000_0000L then to_float x
1✔
900
          else
901
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
902
            to_float (logor (shift_right_logical x 12) r) *. 0x1p12 ) )
1✔
903

904
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
905
    let op' = `Cvtop op in
13✔
906
    match op with
907
    | DemoteF64 -> to_fp32 (demote_f64 (of_fp64 1 op' v))
2✔
908
    | ConvertSI32 -> to_fp32 (convert_i32_s (int32_of_bitv 1 op' v))
2✔
909
    | ConvertUI32 -> to_fp32 (convert_i32_u (int32_of_bitv 1 op' v))
2✔
910
    | ConvertSI64 -> to_fp32 (convert_i64_s (int64_of_bitv 1 op' v))
3✔
911
    | ConvertUI64 -> to_fp32 (convert_i64_u (int64_of_bitv 1 op' v))
2✔
912
    | Reinterpret_int -> to_fp32 (int32_of_bitv 1 op' v)
1✔
913
    | PromoteF32 -> type_error 1 v (Ty_fp 32) op' "F64 must promote F32"
1✔
914
    | ToString | OfString | _ ->
×
915
      eval_error (`Unsupported_operator (op', Ty_fp 32))
916
end
917

918
module F64CvtOp = struct
919
  let promote_f32 x =
920
    let xf = Int32.float_of_bits x in
2✔
921
    if Float.Infix.(xf = xf) then Int64.bits_of_float xf
1✔
922
    else
923
      let nan32bits = I64CvtOp.extend_i32_u x in
1✔
924
      let sign_field =
1✔
925
        Int64.(shift_left (shift_right_logical nan32bits 31) 63)
1✔
926
      in
927
      let significand_field =
928
        Int64.(shift_right_logical (shift_left nan32bits 41) 12)
1✔
929
      in
930
      let fields = Int64.logor sign_field significand_field in
931
      Int64.logor 0x7ff8_0000_0000_0000L fields
1✔
932

933
  let convert_i32_s x = Int64.bits_of_float (Int32.to_float x)
2✔
934

935
  (*
936
   * Unlike the other convert_u functions, the high half of the i32 range is
937
   * within the range where f32 can represent odd numbers, so we can't do the
938
   * shift. Instead, we can use int64 signed arithmetic.
939
   *)
940
  let convert_i32_u x =
941
    Int64.bits_of_float
1✔
942
      Int64.(to_float (logand (of_int32 x) 0x0000_0000_ffff_ffffL))
1✔
943

944
  let convert_i64_s x = Int64.bits_of_float (Int64.to_float x)
2✔
945

946
  (*
947
   * Values in the low half of the int64 range can be converted with a signed
948
   * conversion. The high half is beyond the range where f64 can represent odd
949
   * numbers, so we can shift the value right, adjust the least significant
950
   * bit to round correctly, do a conversion, and then scale it back up.
951
   *)
952
  let convert_i64_u (x : int64) =
953
    Int64.bits_of_float
2✔
954
      Int64.(
955
        Int64.Infix.(
956
          if x >= 0L then to_float x
1✔
957
          else to_float (logor (shift_right_logical x 1) (logand x 1L)) *. 2.0 ) )
1✔
958

959
  let cvtop (op : Ty.Cvtop.t) v : Value.t =
960
    let op' = `Cvtop op in
11✔
961
    match op with
962
    | PromoteF32 -> to_fp64 (promote_f32 (of_fp32 1 op' v))
2✔
963
    | ConvertSI32 -> to_fp64 (convert_i32_s (int32_of_bitv 1 op' v))
2✔
964
    | ConvertUI32 -> to_fp64 (convert_i32_u (int32_of_bitv 1 op' v))
1✔
965
    | ConvertSI64 -> to_fp64 (convert_i64_s (int64_of_bitv 1 op' v))
2✔
966
    | ConvertUI64 -> to_fp64 (convert_i64_u (int64_of_bitv 1 op' v))
2✔
967
    | Reinterpret_int -> to_fp64 (int64_of_bitv 1 op' v)
1✔
968
    | DemoteF64 -> type_error 1 v (Ty_fp 64) op' "F32 must demote a F64"
1✔
969
    | ToString | OfString | _ ->
×
970
      eval_error (`Unsupported_operator (op', Ty_fp 64))
971
end
972

973
(* Dispatch *)
974

975
let unop ty op v =
976
  match ty with
70✔
977
  | Ty.Ty_int -> Int.unop op v
6✔
978
  | Ty_real -> Real.unop op v
19✔
979
  | Ty_bool -> Bool.unop op v
3✔
980
  | Ty_str -> Str.unop op v
5✔
981
  | Ty_list -> Lst.unop op v
4✔
982
  | Ty_bitv _ -> Bitv.unop op v
5✔
983
  | Ty_fp 32 -> F32.unop op v
16✔
984
  | Ty_fp 64 -> F64.unop op v
12✔
985
  | Ty_fp _ | Ty_app | Ty_unit | Ty_none | Ty_regexp | Ty_roundingMode ->
×
986
    eval_error (`Unsupported_theory ty)
987

988
let binop ty op v1 v2 =
989
  match ty with
139✔
990
  | Ty.Ty_int -> Int.binop op v1 v2
33✔
991
  | Ty_real -> Real.binop op v1 v2
16✔
992
  | Ty_bool -> Bool.binop op v1 v2
13✔
993
  | Ty_str -> Str.binop op v1 v2
9✔
994
  | Ty_list -> Lst.binop op v1 v2
3✔
995
  | Ty_bitv _ -> Bitv.binop op v1 v2
38✔
996
  | Ty_fp 32 -> F32.binop op v1 v2
13✔
997
  | Ty_fp 64 -> F64.binop op v1 v2
14✔
998
  | Ty_fp _ | Ty_app | Ty_unit | Ty_none | Ty_regexp | Ty_roundingMode ->
×
999
    eval_error (`Unsupported_theory ty)
1000

1001
let triop ty op v1 v2 v3 =
1002
  match ty with
9✔
1003
  | Ty.Ty_bool -> Bool.triop op v1 v2 v3
2✔
1004
  | Ty_str -> Str.triop op v1 v2 v3
6✔
1005
  | Ty_list -> Lst.triop op v1 v2 v3
1✔
1006
  | ty -> eval_error (`Unsupported_theory ty)
×
1007

1008
let relop ty op v1 v2 =
1009
  match ty with
91✔
1010
  | Ty.Ty_int -> Int.relop op v1 v2
8✔
1011
  | Ty_real -> Real.relop op v1 v2
12✔
1012
  | Ty_bool -> Bool.relop op v1 v2
9✔
1013
  | Ty_str -> Str.relop op v1 v2
14✔
1014
  | Ty_bitv _ -> Bitv.relop op v1 v2
23✔
1015
  | Ty_fp 32 -> F32.relop op v1 v2
13✔
1016
  | Ty_fp 64 -> F64.relop op v1 v2
12✔
1017
  | ty -> eval_error (`Unsupported_theory ty)
×
1018

1019
let cvtop ty op v =
1020
  match ty with
92✔
1021
  | Ty.Ty_int -> Int.cvtop op v
5✔
1022
  | Ty_real -> Real.cvtop op v
8✔
1023
  | Ty_str -> Str.cvtop op v
21✔
1024
  | Ty_bitv 32 -> I32CvtOp.cvtop op v
17✔
1025
  | Ty_bitv 64 -> I64CvtOp.cvtop op v
17✔
1026
  (* Remaining fall into arbitrary-width bv cvtop operations *)
1027
  | Ty_bitv _m -> Bitv.cvtop op v
×
1028
  | Ty_fp 32 -> F32CvtOp.cvtop op v
13✔
1029
  | Ty_fp 64 -> F64CvtOp.cvtop op v
11✔
1030
  | ty -> eval_error (`Unsupported_theory ty)
×
1031

1032
let naryop ty op vs =
1033
  match ty with
10✔
1034
  | Ty.Ty_bool -> Bool.naryop op vs
6✔
1035
  | Ty_str -> Str.naryop op vs
4✔
1036
  | Ty_list -> Lst.naryop op vs
×
1037
  | ty -> eval_error (`Unsupported_theory ty)
×
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