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

formalsec / smtml / 456

04 Jan 2026 09:20PM UTC coverage: 45.359% (+0.7%) from 44.642%
456

push

github

filipeom
Export individual modules in Eval

904 of 1993 relevant lines covered (45.36%)

7.1 hits per line

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

77.42
/src/smtml/eval.ml
1
(* SPDX-License-Identifier: MIT *)
2
(* Copyright (C) 2023-2024 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
48✔
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
16✔
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
75✔
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
92✔
124
  | Value.Num (F32 f) -> f
91✔
125
  | _ -> raise_type_mismatch n op v (Ty_fp 32)
1✔
126

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

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

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

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

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

140
(* Operator evaluation *)
141

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

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

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

183
  let[@inline] relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
184
    let a = of_int 1 (`Relop op) v1 in
8✔
185
    let b = of_int 2 (`Relop op) v2 in
8✔
186
    match op with
8✔
187
    | Lt -> a < b
2✔
188
    | Le -> a <= b
2✔
189
    | Gt -> a > b
2✔
190
    | Ge -> a >= b
2✔
191
    | Eq -> Int.equal a b
×
192
    | Ne -> not (Int.equal a b)
×
193
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_int))
×
194

195
  let[@inline] int_of_bool v =
196
    match v with Value.True -> 1 | False -> 0 | _ -> assert false
1✔
197

198
  let[@inline] cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
199
    match op with
5✔
200
    | OfBool -> to_int (int_of_bool v)
3✔
201
    | Reinterpret_float -> Int (Int.of_float (of_real 1 (`Cvtop op) v))
2✔
202
    | _ -> eval_error (`Unsupported_operator (`Cvtop op, Ty_int))
×
203
end
204

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

219
  let[@inline] binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
220
    let a = of_real 1 (`Binop op) v1 in
16✔
221
    let b = of_real 2 (`Binop op) v2 in
16✔
222
    match op with
16✔
223
    | Add -> to_real (Float.add a b)
2✔
224
    | Sub -> to_real (Float.sub a b)
2✔
225
    | Mul -> to_real (Float.mul a b)
2✔
226
    | Div -> to_real (Float.div a b)
3✔
227
    | Rem -> to_real (Float.rem a b)
2✔
228
    | Min -> to_real (Float.min a b)
2✔
229
    | Max -> to_real (Float.max a b)
2✔
230
    | Pow -> to_real (Float.pow a b)
1✔
231
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_real))
×
232

233
  let[@inline] relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
234
    let a = of_real 1 (`Relop op) v1 in
12✔
235
    let b = of_real 2 (`Relop op) v2 in
12✔
236
    match op with
12✔
237
    | Lt -> Float.Infix.(a < b)
2✔
238
    | Le -> Float.Infix.(a <= b)
2✔
239
    | Gt -> Float.Infix.(a > b)
2✔
240
    | Ge -> Float.Infix.(a >= b)
2✔
241
    | Eq -> Float.Infix.(a = b)
2✔
242
    | Ne -> Float.Infix.(a <> b)
2✔
243
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_real))
×
244

245
  let[@inline] cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
246
    let op' = `Cvtop op in
8✔
247
    match op with
248
    | ToString -> Str (Float.to_string (of_real 1 op' v))
2✔
249
    | OfString -> begin
3✔
250
      match float_of_string_opt (of_str 1 op' v) with
3✔
251
      | None -> eval_error `Invalid_format_conversion
1✔
252
      | Some v -> to_real v
2✔
253
    end
254
    | Reinterpret_int -> to_real (float_of_int (of_int 1 op' v))
2✔
255
    | Reinterpret_float -> to_int (Float.to_int (of_real 1 op' v))
1✔
256
    | _ -> eval_error (`Unsupported_operator (op', Ty_real))
×
257
end
258

259
module Bool = struct
260
  let[@inline] unop (op : Ty.Unop.t) v =
261
    let b = of_bool 1 (`Unop op) v in
3✔
262
    match op with
2✔
263
    | Not -> to_bool (not b)
2✔
264
    | _ -> eval_error (`Unsupported_operator (`Unop op, Ty_bool))
×
265

266
  let xor b1 b2 =
267
    match (b1, b2) with
4✔
268
    | true, true -> false
1✔
269
    | true, false -> true
1✔
270
    | false, true -> true
1✔
271
    | false, false -> false
1✔
272

273
  let[@inline] binop (op : Ty.Binop.t) v1 v2 =
274
    let a = of_bool 1 (`Binop op) v1 in
13✔
275
    let b = of_bool 2 (`Binop op) v2 in
13✔
276
    match op with
13✔
277
    | And -> to_bool (a && b)
3✔
278
    | Or -> to_bool (a || b)
1✔
279
    | Xor -> to_bool (xor a b)
4✔
280
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_bool))
×
281

282
  let[@inline] triop (op : Ty.Triop.t) c v1 v2 =
283
    match op with
2✔
284
    | Ite -> ( match of_bool 1 (`Triop op) c with true -> v1 | false -> v2 )
1✔
285
    | _ -> eval_error (`Unsupported_operator (`Triop op, Ty_bool))
×
286

287
  let[@inline] relop (op : Ty.Relop.t) v1 v2 =
288
    match op with
9✔
289
    | Eq -> Value.equal v1 v2
4✔
290
    | Ne -> not (Value.equal v1 v2)
5✔
291
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_bool))
×
292

293
  let[@inline] naryop (op : Ty.Naryop.t) vs =
294
    match op with
6✔
295
    | Logand ->
3✔
296
      let exists_false =
297
        let i = ref 0 in
298
        List.find_map
3✔
299
          (fun e ->
300
            incr i;
7✔
301
            let b = of_bool !i (`Naryop op) e in
7✔
302
            if not b then Some () else None )
2✔
303
          vs
304
      in
305
      if Option.is_some exists_false then Value.False else Value.True
1✔
306
    | Logor ->
3✔
307
      let exists_true =
308
        let i = ref 0 in
309
        List.find_map
3✔
310
          (fun e ->
311
            incr i;
6✔
312
            let b = of_bool !i (`Naryop op) e in
6✔
313
            if b then Some () else None )
2✔
314
          vs
315
      in
316
      if Option.is_some exists_true then Value.True else Value.False
1✔
317
    | _ -> eval_error (`Unsupported_operator (`Naryop op, Ty_bool))
×
318
end
319

320
module Str = struct
321
  let replace s t t' =
322
    let len_s = String.length s in
2✔
323
    let len_t = String.length t in
2✔
324
    let rec loop i =
2✔
325
      if i >= len_s then s
×
326
      else if i + len_t > len_s then s
×
327
      else if String.equal (String.sub s i len_t) t then
4✔
328
        let s' = Fmt.str "%s%s" (String.sub s 0 i) t' in
2✔
329
        let s'' = String.sub s (i + len_t) (len_s - i - len_t) in
2✔
330
        Fmt.str "%s%s" s' s''
2✔
331
      else loop (i + 1)
2✔
332
    in
333
    loop 0
334

335
  let indexof s sub start =
336
    let len_s = String.length s in
4✔
337
    let len_sub = String.length sub in
4✔
338
    let max_i = len_s - 1 in
4✔
339
    let rec loop i =
340
      if i > max_i then ~-1
×
341
      else if i + len_sub > len_s then ~-1
×
342
      else if String.equal sub (String.sub s i len_sub) then i
4✔
343
      else loop (i + 1)
4✔
344
    in
345
    if start <= 0 then loop 0 else loop start
×
346

347
  let contains s sub = if indexof s sub 0 < 0 then false else true
×
348

349
  let[@inline] unop (op : Ty.Unop.t) v =
350
    let str = of_str 1 (`Unop op) v in
5✔
351
    match op with
4✔
352
    | Length -> to_int (String.length str)
2✔
353
    | Trim -> to_str (String.trim str)
2✔
354
    | _ -> eval_error (`Unsupported_operator (`Unop op, Ty_str))
×
355

356
  let[@inline] binop (op : Ty.Binop.t) v1 v2 =
357
    let op' = `Binop op in
9✔
358
    let str = of_str 1 op' v1 in
359
    match op with
9✔
360
    | At -> begin
3✔
361
      let i = of_int 2 op' v2 in
362
      try to_str (Fmt.str "%c" (String.get str i))
2✔
363
      with Invalid_argument _ -> eval_error `Index_out_of_bounds
1✔
364
    end
365
    | String_prefix ->
2✔
366
      to_bool (String.starts_with ~prefix:str (of_str 2 op' v2))
2✔
367
    | String_suffix -> to_bool (String.ends_with ~suffix:str (of_str 2 op' v2))
2✔
368
    | String_contains -> to_bool (contains str (of_str 2 op' v2))
2✔
369
    | _ -> eval_error (`Unsupported_operator (op', Ty_str))
×
370

371
  let[@inline] triop (op : Ty.Triop.t) v1 v2 v3 =
372
    let op' = `Triop op in
6✔
373
    let str = of_str 1 op' v1 in
374
    match op with
6✔
375
    | String_extract -> begin
2✔
376
      let i = of_int 2 op' v2 in
377
      let len = of_int 3 op' v3 in
2✔
378
      try to_str (String.sub str i len)
2✔
379
      with Invalid_argument _ -> eval_error `Index_out_of_bounds
×
380
    end
381
    | String_replace ->
2✔
382
      let t = of_str 2 op' v2 in
383
      let t' = of_str 2 op' v3 in
2✔
384
      to_str (replace str t t')
2✔
385
    | String_index ->
2✔
386
      let t = of_str 2 op' v2 in
387
      let i = of_int 3 op' v3 in
2✔
388
      to_int (indexof str t i)
2✔
389
    | _ -> eval_error (`Unsupported_operator (`Triop op, Ty_str))
×
390

391
  let[@inline] relop (op : Ty.Relop.t) v1 v2 =
392
    let a = of_str 1 (`Relop op) v1 in
14✔
393
    let b = of_str 2 (`Relop op) v2 in
14✔
394
    let cmp = String.compare a b in
14✔
395
    match op with
14✔
396
    | Lt -> cmp < 0
2✔
397
    | Le -> cmp <= 0
2✔
398
    | Gt -> cmp > 0
2✔
399
    | Ge -> cmp >= 0
2✔
400
    | Eq -> cmp = 0
3✔
401
    | Ne -> cmp <> 0
3✔
402
    | _ -> eval_error (`Unsupported_operator (`Relop op, Ty_str))
×
403

404
  let[@inline] cvtop (op : Ty.Cvtop.t) v =
405
    let op' = `Cvtop op in
12✔
406
    match op with
407
    | String_to_code ->
2✔
408
      let str = of_str 1 op' v in
409
      to_int (Char.code str.[0])
2✔
410
    | String_from_code ->
2✔
411
      let code = of_int 1 op' v in
412
      to_str (String.make 1 (Char.chr code))
2✔
413
    | String_to_int -> begin
3✔
414
      let s = of_str 1 op' v in
415
      match int_of_string_opt s with
3✔
416
      | None -> eval_error `Invalid_format_conversion
1✔
417
      | Some x -> to_int x
2✔
418
    end
419
    | String_from_int -> to_str (string_of_int (of_int 1 op' v))
2✔
420
    | String_to_float -> begin
3✔
421
      let s = of_str 1 op' v in
422
      match float_of_string_opt s with
3✔
423
      | None -> eval_error `Invalid_format_conversion
1✔
424
      | Some f -> to_real f
2✔
425
    end
426
    | _ -> eval_error (`Unsupported_operator (`Cvtop op, Ty_str))
×
427

428
  let[@inline] naryop (op : Ty.Naryop.t) vs =
429
    let op' = `Naryop op in
4✔
430
    match op with
431
    | Concat ->
4✔
432
      let _, s =
433
        List.fold_left
434
          (fun (i, acc) v ->
435
            let s = of_str i op' v in
10✔
436
            (i + 1, String.cat acc s) )
10✔
437
          (0, "") vs
438
      in
439
      to_str s
4✔
440
    | _ -> eval_error (`Unsupported_operator (`Naryop op, Ty_str))
×
441
end
442

443
module Lst = struct
444
  let[@inline] unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
445
    let lst = of_list 1 (`Unop op) v in
4✔
446
    match op with
4✔
447
    | Head -> begin
1✔
448
      (* FIXME: Exception handling *)
449
      match lst with
450
      | hd :: _tl -> hd
1✔
451
      | [] -> assert false
452
    end
453
    | Tail -> begin
1✔
454
      (* FIXME: Exception handling *)
455
      match lst with
456
      | _hd :: tl -> List tl
1✔
457
      | [] -> assert false
458
    end
459
    | Length -> to_int (List.length lst)
1✔
460
    | Reverse -> List (List.rev lst)
1✔
461
    | _ -> eval_error (`Unsupported_operator (`Unop op, Ty_list))
×
462

463
  let[@inline] binop (op : Ty.Binop.t) v1 v2 =
464
    let op' = `Binop op in
3✔
465
    match op with
466
    | At ->
1✔
467
      let lst = of_list 1 op' v1 in
468
      let i = of_int 2 op' v2 in
1✔
469
      (* TODO: change datastructure? *)
470
      begin match List.nth_opt lst i with
1✔
471
      | None -> eval_error `Index_out_of_bounds
×
472
      | Some v -> v
1✔
473
      end
474
    | List_cons -> List (v1 :: of_list 1 op' v2)
1✔
475
    | List_append -> List (of_list 1 op' v1 @ of_list 2 op' v2)
1✔
476
    | _ -> eval_error (`Unsupported_operator (`Binop op, Ty_list))
×
477

478
  let[@inline] triop (op : Ty.Triop.t) (v1 : Value.t) (v2 : Value.t)
479
    (v3 : Value.t) : Value.t =
480
    let op' = `Triop op in
1✔
481
    match op with
482
    | List_set ->
1✔
483
      let lst = of_list 1 op' v1 in
484
      let i = of_int 2 op' v2 in
1✔
485
      let rec set i lst v acc =
1✔
486
        match (i, lst) with
2✔
487
        | 0, _ :: tl -> List.rev_append acc (v :: tl)
1✔
488
        | i, hd :: tl -> set (i - 1) tl v (hd :: acc)
1✔
489
        | _, [] -> eval_error `Index_out_of_bounds
×
490
      in
491
      List (set i lst v3 [])
1✔
492
    | _ -> eval_error (`Unsupported_operator (`Triop op, Ty_list))
×
493

494
  let[@inline] naryop (op : Ty.Naryop.t) (vs : Value.t list) : Value.t =
495
    let op' = `Naryop op in
×
496
    match op with
497
    | Concat -> List (List.concat_map (of_list 0 op') vs)
×
498
    | _ -> eval_error (`Unsupported_operator (`Naryop op, Ty_list))
×
499
end
500

501
module I64 = struct
502
  let cmp_u x op y = op Int64.(add x min_int) Int64.(add y min_int) [@@inline]
2✔
503

504
  let lt_u x y = cmp_u x Int64.Infix.( < ) y [@@inline]
2✔
505
end
506

507
module Bitv = struct
508
  let[@inline] unop op bv =
509
    let bv = of_bitv 1 (`Unop op) bv in
4✔
510
    match op with
4✔
511
    | Ty.Unop.Neg -> to_bitv (Bitvector.neg bv)
2✔
512
    | Not -> to_bitv (Bitvector.lognot bv)
2✔
513
    | Clz -> to_bitv (Bitvector.clz bv)
×
514
    | Ctz -> to_bitv (Bitvector.ctz bv)
×
515
    | Popcnt -> to_bitv (Bitvector.popcnt bv)
×
516
    | _ ->
×
517
      eval_error
518
        (`Unsupported_operator (`Unop op, Ty_bitv (Bitvector.numbits bv)))
×
519

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

541
  let[@inline] relop op bv1 bv2 =
542
    let bv1 = of_bitv 1 (`Relop op) bv1 in
23✔
543
    let bv2 = of_bitv 2 (`Relop op) bv2 in
23✔
544
    match op with
23✔
545
    | Ty.Relop.Lt -> Bitvector.lt bv1 bv2
3✔
546
    | LtU -> Bitvector.lt_u bv1 bv2
3✔
547
    | Le -> Bitvector.le bv1 bv2
3✔
548
    | LeU -> Bitvector.le_u bv1 bv2
3✔
549
    | Gt -> Bitvector.gt bv1 bv2
3✔
550
    | GtU -> Bitvector.gt_u bv1 bv2
2✔
551
    | Ge -> Bitvector.ge bv1 bv2
3✔
552
    | GeU -> Bitvector.ge_u bv1 bv2
2✔
553
    | Eq -> Bitvector.equal bv1 bv2
1✔
554
    | Ne -> not @@ Bitvector.equal bv1 bv2
×
555

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

566
module F32 = struct
567
  (* Stolen from Owi *)
568
  let[@inline] abs x = Int32.logand x Int32.max_int
6✔
569

570
  let[@inline] neg x = Int32.logxor x Int32.min_int
2✔
571

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

585
  (* Stolen from Owi *)
586
  let[@inline] copy_sign x y =
587
    Int32.logor (abs x) (Int32.logand y Int32.min_int)
5✔
588

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

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

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

623
  let[@inline] neg x = Int64.logxor x Int64.min_int
1✔
624

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

638
  let copy_sign x y = Int64.logor (abs x) (Int64.logand y Int64.min_int)
5✔
639

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

657
  let[@inline] relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
658
    let a = Int64.float_of_bits @@ of_fp64 1 (`Relop op) v1 in
12✔
659
    let b = Int64.float_of_bits @@ of_fp64 2 (`Relop op) v2 in
12✔
660
    match op with
12✔
661
    | Eq -> Float.Infix.(a = b)
2✔
662
    | Ne -> Float.Infix.(a <> b)
2✔
663
    | Lt -> Float.Infix.(a < b)
2✔
664
    | Le -> Float.Infix.(a <= b)
2✔
665
    | Gt -> Float.Infix.(a > b)
2✔
666
    | Ge -> Float.Infix.(a >= b)
2✔
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 -> v (* v is already a number here *)
×
755
    | ToBool | _ -> eval_error (`Unsupported_operator (op', Ty_bitv 32))
×
756
end
757

758
module I64CvtOp = struct
759
  let extend_i32_u (x : int32) =
760
    Int64.(logand (of_int32 x) 0x0000_0000_ffff_ffffL)
1✔
761

762
  let trunc_f32_s (x : int32) =
763
    if Int32.Infix.(x <> x) then eval_error `Conversion_to_integer
×
764
    else
765
      let xf = Int32.float_of_bits x in
2✔
766
      if
2✔
767
        Float.Infix.(
768
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
769
      then eval_error `Integer_overflow
×
770
      else Int64.of_float xf
2✔
771

772
  let trunc_f32_u (x : int32) =
773
    if Int32.Infix.(x <> x) then eval_error `Conversion_to_integer
×
774
    else
775
      let xf = Int32.float_of_bits x in
1✔
776
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
777
      then eval_error `Integer_overflow
×
778
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
779
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
780
      else Int64.of_float xf
1✔
781

782
  let trunc_f64_s (x : int64) =
783
    if Int64.Infix.(x <> x) then eval_error `Conversion_to_integer
×
784
    else
785
      let xf = Int64.float_of_bits x in
2✔
786
      if
2✔
787
        Float.Infix.(
788
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
789
      then eval_error `Integer_overflow
×
790
      else Int64.of_float xf
2✔
791

792
  let trunc_f64_u (x : int64) =
793
    if Int64.Infix.(x <> x) then eval_error `Conversion_to_integer
×
794
    else
795
      let xf = Int64.float_of_bits x in
1✔
796
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
797
      then eval_error `Integer_overflow
×
798
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
799
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
800
      else Int64.of_float xf
1✔
801

802
  let trunc_sat_f32_s (x : int32) =
803
    if Int32.Infix.(x <> x) then 0L
×
804
    else
805
      let xf = Int32.float_of_bits x in
1✔
806
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
807
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
808
      else Int64.of_float xf
1✔
809

810
  let trunc_sat_f32_u (x : int32) =
811
    if Int32.Infix.(x <> x) then 0L
×
812
    else
813
      let xf = Int32.float_of_bits x in
1✔
814
      if Float.Infix.(xf <= -1.0) then 0L
×
815
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1L
×
816
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
817
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
818
      else Int64.of_float xf
1✔
819

820
  let trunc_sat_f64_s (x : int64) =
821
    if Int64.Infix.(x <> x) then 0L
×
822
    else
823
      let xf = Int64.float_of_bits x in
1✔
824
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
825
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
826
      else Int64.of_float xf
1✔
827

828
  let trunc_sat_f64_u (x : int64) =
829
    if Int64.Infix.(x <> x) then 0L
×
830
    else
831
      let xf = Int64.float_of_bits x in
1✔
832
      if Float.Infix.(xf <= -1.0) then 0L
×
833
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1L
×
834
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
835
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
836
      else Int64.of_float xf
1✔
837

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

856
module F32CvtOp = struct
857
  let demote_f64 x =
858
    let xf = Int64.float_of_bits x in
2✔
859
    if Float.Infix.(xf = xf) then Int32.bits_of_float xf
1✔
860
    else
861
      let nan64bits = x in
1✔
862
      let sign_field =
863
        Int64.(shift_left (shift_right_logical nan64bits 63) 31)
1✔
864
      in
865
      let significand_field =
866
        Int64.(shift_right_logical (shift_left nan64bits 12) 41)
1✔
867
      in
868
      let fields = Int64.logor sign_field significand_field in
869
      Int32.logor 0x7fc0_0000l (Int64.to_int32 fields)
1✔
870

871
  let convert_i32_s x = Int32.bits_of_float (Int32.to_float x)
2✔
872

873
  let convert_i32_u x =
874
    Int32.bits_of_float
2✔
875
      Int32.(
876
        Int32.Infix.(
877
          if x >= 0l then to_float x
1✔
878
          else to_float (logor (shift_right_logical x 1) (logand x 1l)) *. 2.0 ) )
1✔
879

880
  let convert_i64_s x =
881
    Int32.bits_of_float
3✔
882
      Int64.(
883
        Int64.Infix.(
884
          if abs x < 0x10_0000_0000_0000L then to_float x
2✔
885
          else
886
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
887
            to_float (logor (shift_right x 12) r) *. 0x1p12 ) )
1✔
888

889
  let convert_i64_u x =
890
    Int32.bits_of_float
2✔
891
      Int64.(
892
        Int64.Infix.(
893
          if I64.lt_u x 0x10_0000_0000_0000L then to_float x
1✔
894
          else
895
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
896
            to_float (logor (shift_right_logical x 12) r) *. 0x1p12 ) )
1✔
897

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

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

927
  let convert_i32_s x = Int64.bits_of_float (Int32.to_float x)
2✔
928

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

938
  let convert_i64_s x = Int64.bits_of_float (Int64.to_float x)
2✔
939

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

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

967
(* Dispatch *)
968

969
let unop ty op v =
970
  match ty with
66✔
971
  | Ty.Ty_int -> Int.unop op v
6✔
972
  | Ty_real -> Real.unop op v
19✔
973
  | Ty_bool -> Bool.unop op v
3✔
974
  | Ty_str -> Str.unop op v
5✔
975
  | Ty_list -> Lst.unop op v
4✔
976
  | Ty_bitv _ -> Bitv.unop op v
4✔
977
  | Ty_fp 32 -> F32.unop op v
13✔
978
  | Ty_fp 64 -> F64.unop op v
12✔
979
  | Ty_fp _ | Ty_app | Ty_unit | Ty_none | Ty_regexp | Ty_roundingMode ->
×
980
    eval_error (`Unsupported_theory ty)
981

982
let binop ty op v1 v2 =
983
  match ty with
136✔
984
  | Ty.Ty_int -> Int.binop op v1 v2
31✔
985
  | Ty_real -> Real.binop op v1 v2
16✔
986
  | Ty_bool -> Bool.binop op v1 v2
13✔
987
  | Ty_str -> Str.binop op v1 v2
9✔
988
  | Ty_list -> Lst.binop op v1 v2
3✔
989
  | Ty_bitv _ -> Bitv.binop op v1 v2
38✔
990
  | Ty_fp 32 -> F32.binop op v1 v2
13✔
991
  | Ty_fp 64 -> F64.binop op v1 v2
13✔
992
  | Ty_fp _ | Ty_app | Ty_unit | Ty_none | Ty_regexp | Ty_roundingMode ->
×
993
    eval_error (`Unsupported_theory ty)
994

995
let triop ty op v1 v2 v3 =
996
  match ty with
9✔
997
  | Ty.Ty_bool -> Bool.triop op v1 v2 v3
2✔
998
  | Ty_str -> Str.triop op v1 v2 v3
6✔
999
  | Ty_list -> Lst.triop op v1 v2 v3
1✔
1000
  | ty -> eval_error (`Unsupported_theory ty)
×
1001

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

1013
let cvtop ty op v =
1014
  match ty with
83✔
1015
  | Ty.Ty_int -> Int.cvtop op v
5✔
1016
  | Ty_real -> Real.cvtop op v
8✔
1017
  | Ty_str -> Str.cvtop op v
12✔
1018
  | Ty_bitv 32 -> I32CvtOp.cvtop op v
17✔
1019
  | Ty_bitv 64 -> I64CvtOp.cvtop op v
17✔
1020
  (* Remaining fall into arbitrary-width bv cvtop operations *)
1021
  | Ty_bitv _m -> Bitv.cvtop op v
×
1022
  | Ty_fp 32 -> F32CvtOp.cvtop op v
13✔
1023
  | Ty_fp 64 -> F64CvtOp.cvtop op v
11✔
1024
  | ty -> eval_error (`Unsupported_theory ty)
×
1025

1026
let naryop ty op vs =
1027
  match ty with
10✔
1028
  | Ty.Ty_bool -> Bool.naryop op vs
6✔
1029
  | Ty_str -> Str.naryop op vs
4✔
1030
  | Ty_list -> Lst.naryop op vs
×
1031
  | 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