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

formalsec / smtml / 327

07 May 2025 05:57PM UTC coverage: 56.38% (+0.4%) from 56.0%
327

push

github

filipeom
Format code

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

53 existing lines in 1 file now uncovered.

950 of 1685 relevant lines covered (56.38%)

11.76 hits per line

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

79.9
/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
(* TODO: This module should be concrete or a part of the reducer *)
11

12
type op_type =
13
  [ `Unop of Ty.Unop.t
14
  | `Binop of Ty.Binop.t
15
  | `Relop of Ty.Relop.t
16
  | `Triop of Ty.Triop.t
17
  | `Cvtop of Ty.Cvtop.t
18
  | `Naryop of Ty.Naryop.t
19
  ]
20

21
let pp_op_type fmt = function
22
  | `Unop op -> Fmt.pf fmt "unop '%a'" Ty.Unop.pp op
71✔
23
  | `Binop op -> Fmt.pf fmt "binop '%a'" Ty.Binop.pp op
289✔
24
  | `Relop op -> Fmt.pf fmt "relop '%a'" Ty.Relop.pp op
168✔
25
  | `Triop op -> Fmt.pf fmt "triop '%a'" Ty.Triop.pp op
22✔
26
  | `Cvtop op -> Fmt.pf fmt "cvtop '%a'" Ty.Cvtop.pp op
72✔
27
  | `Naryop op -> Fmt.pf fmt "naryop '%a'" Ty.Naryop.pp op
30✔
28

29
exception Value of Ty.t
30

31
(* FIXME: use snake case instead *)
32
exception
33
  TypeError of
34
    { index : int
35
    ; value : Value.t
36
    ; ty : Ty.t
37
    ; op : op_type
38
    ; msg : string
39
    }
40

41
(* FIXME: use snake case instead *)
42
exception DivideByZero
43

44
exception Conversion_to_integer
45

46
exception Integer_overflow
47

48
(* FIXME: use snake case instead *)
49
exception Index_out_of_bounds
50

51
let of_arg f n v op msg =
52
  try f v
646✔
53
  with Value t -> raise (TypeError { index = n; value = v; ty = t; op; msg })
6✔
54
[@@inline]
55

56
let err_str n op ty_expected ty_actual =
57
  Fmt.str "Argument %d of %a expected type %a but got %a instead." n pp_op_type
652✔
58
    op Ty.pp ty_expected Ty.pp ty_actual
59

60
module Int = struct
61
  let to_value (i : int) : Value.t = Int i [@@inline]
47✔
62

63
  let of_value (n : int) (op : op_type) (v : Value.t) : int =
64
    of_arg
101✔
65
      (function Int i -> i | _ -> raise_notrace (Value Ty_int))
1✔
66
      n v op
67
      (err_str n op Ty_int (Value.type_of v))
101✔
68
  [@@inline]
69

70
  let str_value (n : int) (op : op_type) (v : Value.t) : string =
71
    of_arg
2✔
72
      (function Str str -> str | _ -> raise_notrace (Value Ty_str))
×
73
      n v op
74
      (err_str n op Ty_str (Value.type_of v))
2✔
75

76
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
77
    let f =
6✔
78
      match op with
79
      | Neg -> Int.neg
3✔
80
      | Not -> Int.lognot
1✔
81
      | Abs -> Int.abs
2✔
82
      | _ -> Fmt.failwith {|unop: Unsupported int operator "%a"|} Ty.Unop.pp op
×
83
    in
84
    to_value (f (of_value 1 (`Unop op) v))
5✔
85

86
  let exp_by_squaring x n =
87
    let rec exp_by_squaring2 y x n =
2✔
88
      if n < 0 then exp_by_squaring2 y (1 / x) ~-n
×
89
      else if n = 0 then y
2✔
90
      else if n mod 2 = 0 then exp_by_squaring2 y (x * x) (n / 2)
1✔
91
      else begin
3✔
92
        assert (n mod 2 = 1);
3✔
93
        exp_by_squaring2 (x * y) (x * x) ((n - 1) / 2)
94
      end
95
    in
96
    exp_by_squaring2 1 x n
97

98
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
99
    let f =
31✔
100
      match op with
101
      | Add -> Int.add
4✔
102
      | Sub -> Int.sub
2✔
103
      | Mul -> Int.mul
2✔
104
      | Div -> Int.div
3✔
105
      | Rem -> Int.rem
2✔
106
      | Pow -> exp_by_squaring
2✔
107
      | Min -> Int.min
2✔
108
      | Max -> Int.max
2✔
109
      | And -> Int.logand
2✔
110
      | Or -> Int.logor
2✔
111
      | Xor -> Int.logxor
2✔
112
      | Shl -> Int.shift_left
2✔
113
      | ShrL -> Int.shift_right_logical
1✔
114
      | ShrA -> Int.shift_right
3✔
115
      | _ ->
×
116
        Fmt.failwith {|binop: Unsupported int operator "%a"|} Ty.Binop.pp op
×
117
    in
118
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
30✔
119

120
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
121
    let f =
8✔
122
      match op with
123
      | Lt -> ( < )
2✔
124
      | Le -> ( <= )
2✔
125
      | Gt -> ( > )
2✔
126
      | Ge -> ( >= )
2✔
127
      | _ ->
×
128
        Fmt.failwith {|relop: Unsupported int operator "%a"|} Ty.Relop.pp op
×
129
    in
130
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
8✔
131

132
  let of_bool : Value.t -> int = function
133
    | True -> 1
2✔
134
    | False -> 0
1✔
135
    | _ -> assert false
136
  [@@inline]
137

138
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
139
    match op with
9✔
140
    | OfBool -> to_value (of_bool v)
3✔
141
    | Reinterpret_float ->
2✔
142
      Int (Int.of_float (match v with Real v -> v | _ -> assert false))
2✔
143
    | ToString -> Str (string_of_int (of_value 1 (`Cvtop op) v))
2✔
144
    | OfString -> begin
2✔
145
      let s = str_value 1 (`Cvtop op) v in
146
      match int_of_string_opt s with
2✔
147
      | None -> raise (Invalid_argument "int_of_string")
1✔
148
      | Some i -> Int i
1✔
149
    end
150
    | _ -> Fmt.failwith {|cvtop: Unsupported int operator "%a"|} Ty.Cvtop.pp op
×
151
end
152

153
module Real = struct
154
  let to_value (v : float) : Value.t = Real v [@@inline]
37✔
155

156
  let of_value (n : int) (op : op_type) (v : Value.t) : float =
157
    of_arg
78✔
158
      (function Real v -> v | _ -> raise_notrace (Value Ty_int))
1✔
159
      n v op
160
      (err_str n op Ty_real (Value.type_of v))
78✔
161
  [@@inline]
162

163
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
164
    let v = of_value 1 (`Unop op) v in
19✔
165
    match op with
18✔
166
    | Neg -> to_value @@ Float.neg v
2✔
167
    | Abs -> to_value @@ Float.abs v
2✔
168
    | Sqrt -> to_value @@ Float.sqrt v
2✔
169
    | Nearest -> to_value @@ Float.round v
3✔
170
    | Ceil -> to_value @@ Float.ceil v
2✔
171
    | Floor -> to_value @@ Float.floor v
2✔
172
    | Trunc -> to_value @@ Float.trunc v
2✔
173
    | Is_nan -> if Float.is_nan v then Value.True else Value.False
1✔
174
    | _ -> Fmt.failwith {|unop: Unsupported real operator "%a"|} Ty.Unop.pp op
×
175

176
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
177
    let f =
16✔
178
      match op with
179
      | Add -> Float.add
2✔
180
      | Sub -> Float.sub
2✔
181
      | Mul -> Float.mul
2✔
182
      | Div -> Float.div
3✔
183
      | Rem -> Float.rem
2✔
184
      | Min -> Float.min
2✔
185
      | Max -> Float.max
2✔
186
      | Pow -> Float.pow
1✔
187
      | _ ->
×
188
        Fmt.failwith {|binop: Unsupported real operator "%a"|} Ty.Binop.pp op
×
189
    in
190
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
16✔
191

192
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
193
    let f =
12✔
194
      match op with
195
      | Lt -> Float.Infix.( < )
2✔
196
      | Le -> Float.Infix.( <= )
2✔
197
      | Gt -> Float.Infix.( > )
2✔
198
      | Ge -> Float.Infix.( >= )
2✔
199
      | Eq -> Float.Infix.( = )
2✔
200
      | Ne -> Float.Infix.( <> )
2✔
201
      | _ ->
×
202
        Fmt.failwith {|relop: Unsupported real operator "%a"|} Ty.Relop.pp op
×
203
    in
204
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
12✔
205

206
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
207
    let op' = `Cvtop op in
8✔
208
    match op with
209
    | ToString -> Str (Float.to_string (of_value 1 op' v))
2✔
210
    | OfString ->
3✔
211
      let v = match v with Str v -> v | _ -> raise_notrace (Value Ty_str) in
×
212
      begin
213
        match Float.of_string_opt v with
214
        | None -> raise (Invalid_argument "float_of_int")
1✔
215
        | Some v -> to_value v
2✔
216
      end
217
    | Reinterpret_int ->
2✔
218
      let v = match v with Int v -> v | _ -> raise_notrace (Value Ty_int) in
×
219
      to_value (float_of_int v)
2✔
220
    | Reinterpret_float -> Int (Float.to_int (of_value 1 op' v))
1✔
221
    | _ -> Fmt.failwith {|cvtop: Unsupported real operator "%a"|} Ty.Cvtop.pp op
×
222
end
223

224
module Bool = struct
225
  let to_value (b : bool) : Value.t = if b then True else False [@@inline]
12✔
226

227
  let of_value (n : int) (op : op_type) (v : Value.t) : bool =
228
    of_arg
51✔
229
      (function
230
        | True -> true | False -> false | _ -> raise_notrace (Value Ty_bool) )
1✔
231
      n v op
232
      (err_str n op Ty_bool (Value.type_of v))
51✔
233
  [@@inline]
234

235
  let unop (op : Ty.Unop.t) v =
236
    let b = of_value 1 (`Unop op) v in
3✔
237
    match op with
2✔
238
    | Not -> to_value (not b)
2✔
239
    | _ -> Fmt.failwith {|unop: Unsupported bool operator "%a"|} Ty.Unop.pp op
×
240

241
  let xor b1 b2 =
242
    match (b1, b2) with
4✔
243
    | true, true -> false
1✔
244
    | true, false -> true
1✔
245
    | false, true -> true
1✔
246
    | false, false -> false
1✔
247

248
  let binop (op : Ty.Binop.t) v1 v2 =
249
    let f =
13✔
250
      match op with
251
      | And -> ( && )
5✔
252
      | Or -> ( || )
4✔
253
      | Xor -> xor
4✔
254
      | _ ->
×
255
        Fmt.failwith {|binop: Unsupported bool operator "%a"|} Ty.Binop.pp op
×
256
    in
257
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
13✔
258

259
  let triop (op : Ty.Triop.t) c v1 v2 =
260
    match op with
2✔
261
    | Ite -> ( match of_value 1 (`Triop op) c with true -> v1 | false -> v2 )
1✔
262
    | _ -> Fmt.failwith {|triop: Unsupported bool operator "%a"|} Ty.Triop.pp op
×
263

264
  let relop (op : Ty.Relop.t) v1 v2 =
265
    match op with
23✔
266
    | Eq -> Value.equal v1 v2
12✔
267
    | Ne -> not (Value.equal v1 v2)
11✔
268
    | _ -> Fmt.failwith {|relop: Unsupported bool operator "%a"|} Ty.Relop.pp op
×
269

270
  let naryop (op : Ty.Naryop.t) vs =
271
    let b =
6✔
272
      match op with
273
      | Logand ->
3✔
274
        List.fold_left ( && ) true
3✔
275
          (List.mapi (fun i -> of_value i (`Naryop op)) vs)
3✔
276
      | Logor ->
3✔
277
        List.fold_left ( || ) false
3✔
278
          (List.mapi (fun i -> of_value i (`Naryop op)) vs)
3✔
279
      | _ ->
×
280
        Fmt.failwith {|naryop: Unsupported bool operator "%a"|} Ty.Naryop.pp op
×
281
    in
282
    to_value b
283
end
284

285
module Str = struct
286
  let to_value (str : string) : Value.t = Str str [@@inline]
16✔
287

288
  let of_value (n : int) (op : op_type) (v : Value.t) : string =
289
    of_arg
78✔
290
      (function Str str -> str | _ -> raise_notrace (Value Ty_str))
1✔
291
      n v op
292
      (err_str n op Ty_str (Value.type_of v))
78✔
293
  [@@inline]
294

295
  let replace s t t' =
296
    let len_s = String.length s in
2✔
297
    let len_t = String.length t in
2✔
298
    let rec loop i =
2✔
299
      if i >= len_s then s
×
300
      else if i + len_t > len_s then s
×
301
      else if String.equal (String.sub s i len_t) t then
4✔
302
        let s' = Fmt.str "%s%s" (String.sub s 0 i) t' in
2✔
303
        let s'' = String.sub s (i + len_t) (len_s - i - len_t) in
2✔
304
        Fmt.str "%s%s" s' s''
2✔
305
      else loop (i + 1)
2✔
306
    in
307
    loop 0
308

309
  let indexof s sub start =
310
    let len_s = String.length s in
4✔
311
    let len_sub = String.length sub in
4✔
312
    let max_i = len_s - 1 in
4✔
313
    let rec loop i =
314
      if i > max_i then ~-1
×
315
      else if i + len_sub > len_s then ~-1
×
316
      else if String.equal sub (String.sub s i len_sub) then i
4✔
317
      else loop (i + 1)
4✔
318
    in
319
    if start <= 0 then loop 0 else loop start
×
320

321
  let contains s sub = if indexof s sub 0 < 0 then false else true
×
322

323
  let unop (op : Ty.Unop.t) v =
324
    let str = of_value 1 (`Unop op) v in
5✔
325
    match op with
4✔
326
    | Length -> Int.to_value (String.length str)
2✔
327
    | Trim -> to_value (String.trim str)
2✔
328
    | _ -> Fmt.failwith {|unop: Unsupported str operator "%a"|} Ty.Unop.pp op
×
329

330
  let binop (op : Ty.Binop.t) v1 v2 =
331
    let op' = `Binop op in
9✔
332
    let str = of_value 1 op' v1 in
333
    match op with
9✔
334
    | At -> (
3✔
335
      let i = Int.of_value 2 op' v2 in
336
      try to_value (Fmt.str "%c" (String.get str i))
2✔
337
      with Invalid_argument _ -> raise Index_out_of_bounds )
1✔
338
    | String_prefix ->
2✔
339
      Bool.to_value (String.starts_with ~prefix:str (of_value 2 op' v2))
2✔
340
    | String_suffix ->
2✔
341
      Bool.to_value (String.ends_with ~suffix:str (of_value 2 op' v2))
2✔
342
    | String_contains -> Bool.to_value (contains str (of_value 2 op' v2))
2✔
343
    | _ -> Fmt.failwith {|binop: Unsupported str operator "%a"|} Ty.Binop.pp op
×
344

345
  let triop (op : Ty.Triop.t) v1 v2 v3 =
346
    let op' = `Triop op in
6✔
347
    let str = of_value 1 op' v1 in
348
    match op with
6✔
349
    | String_extract ->
2✔
350
      let i = Int.of_value 2 op' v2 in
351
      let len = Int.of_value 3 op' v3 in
2✔
352
      to_value (String.sub str i len)
2✔
353
    | String_replace ->
2✔
354
      let t = of_value 2 op' v2 in
355
      let t' = of_value 2 op' v3 in
2✔
356
      to_value (replace str t t')
2✔
357
    | String_index ->
2✔
358
      let t = of_value 2 op' v2 in
359
      let i = Int.of_value 3 op' v3 in
2✔
360
      Int.to_value (indexof str t i)
2✔
361
    | _ -> Fmt.failwith {|triop: Unsupported str operator "%a"|} Ty.Triop.pp op
×
362

363
  let relop (op : Ty.Relop.t) v1 v2 =
364
    let f =
14✔
365
      match op with
366
      | Lt -> ( < )
2✔
367
      | Le -> ( <= )
2✔
368
      | Gt -> ( > )
2✔
369
      | Ge -> ( >= )
2✔
370
      | Eq -> ( = )
3✔
371
      | Ne -> ( <> )
3✔
372
      | _ ->
×
373
        Fmt.failwith {|relop: Unsupported string operator "%a"|} Ty.Relop.pp op
×
374
    in
375
    let f x y = f (String.compare x y) 0 in
14✔
376
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
14✔
377

378
  let cvtop (op : Ty.Cvtop.t) v =
379
    let op' = `Cvtop op in
12✔
380
    match op with
381
    | String_to_code ->
2✔
382
      let str = of_value 1 op' v in
383
      Int.to_value (Char.code str.[0])
2✔
384
    | String_from_code ->
2✔
385
      let code = Int.of_value 1 op' v in
386
      to_value (String.make 1 (Char.chr code))
2✔
387
    | String_to_int ->
3✔
388
      let s = of_value 1 op' v in
389
      let i =
3✔
390
        match int_of_string_opt s with
391
        | None -> raise (Invalid_argument "string_to_int")
1✔
392
        | Some i -> i
2✔
393
      in
394
      Int.to_value i
395
    | String_from_int -> to_value (string_of_int (Int.of_value 1 op' v))
2✔
396
    | String_to_float ->
3✔
397
      let s = of_value 1 op' v in
398
      let f =
3✔
399
        match float_of_string_opt s with
400
        | None -> raise (Invalid_argument "string_to_float")
1✔
401
        | Some f -> f
2✔
402
      in
403
      Real.to_value f
404
    | _ -> Fmt.failwith {|cvtop: Unsupported str operator "%a"|} Ty.Cvtop.pp op
×
405

406
  let naryop (op : Ty.Naryop.t) vs =
407
    let op' = `Naryop op in
4✔
408
    match op with
409
    | Concat -> to_value (String.concat "" (List.map (of_value 0 op') vs))
4✔
410
    | _ ->
×
411
      Fmt.failwith {|naryop: Unsupported str operator "%a"|} Ty.Naryop.pp op
412
end
413

414
module Lst = struct
415
  let of_value (n : int) (op : op_type) (v : Value.t) : Value.t list =
416
    of_arg
9✔
417
      (function List lst -> lst | _ -> raise_notrace (Value Ty_list))
×
418
      n v op
419
      (err_str n op Ty_list (Value.type_of v))
9✔
420
  [@@inline]
421

422
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
423
    let lst = of_value 1 (`Unop op) v in
4✔
424
    match op with
4✔
425
    | Head -> begin match lst with hd :: _tl -> hd | [] -> assert false end
1✔
426
    | Tail -> begin
1✔
427
      match lst with _hd :: tl -> List tl | [] -> assert false
1✔
428
    end
429
    | Length -> Int.to_value (List.length lst)
1✔
430
    | Reverse -> List (List.rev lst)
1✔
431
    | _ -> Fmt.failwith {|unop: Unsupported list operator "%a"|} Ty.Unop.pp op
×
432

433
  let binop (op : Ty.Binop.t) v1 v2 =
434
    let op' = `Binop op in
3✔
435
    match op with
436
    | At ->
1✔
437
      let lst = of_value 1 op' v1 in
438
      let i = Int.of_value 2 op' v2 in
1✔
439
      begin
1✔
440
        (* TODO: change datastructure? *)
441
        match List.nth_opt lst i with
442
        | None -> raise Index_out_of_bounds
×
443
        | Some v -> v
1✔
444
      end
445
    | List_cons -> List (v1 :: of_value 1 op' v2)
1✔
446
    | List_append -> List (of_value 1 op' v1 @ of_value 2 op' v2)
1✔
447
    | _ -> Fmt.failwith {|binop: Unsupported list operator "%a"|} Ty.Binop.pp op
×
448

449
  let triop (op : Ty.Triop.t) (v1 : Value.t) (v2 : Value.t) (v3 : Value.t) :
450
    Value.t =
451
    let op' = `Triop op in
1✔
452
    match op with
453
    | List_set ->
1✔
454
      let lst = of_value 1 op' v1 in
455
      let i = Int.of_value 2 op' v2 in
1✔
456
      let rec set i lst v acc =
1✔
457
        match (i, lst) with
2✔
458
        | 0, _ :: tl -> List.rev_append acc (v :: tl)
1✔
459
        | i, hd :: tl -> set (i - 1) tl v (hd :: acc)
1✔
460
        | _, [] -> raise Index_out_of_bounds
×
461
      in
462
      List (set i lst v3 [])
1✔
463
    | _ -> Fmt.failwith {|triop: Unsupported list operator "%a"|} Ty.Triop.pp op
×
464

465
  let naryop (op : Ty.Naryop.t) (vs : Value.t list) : Value.t =
466
    let op' = `Naryop op in
×
467
    match op with
468
    | Concat -> List (List.concat_map (of_value 0 op') vs)
×
469
    | _ ->
×
470
      Fmt.failwith {|naryop: Unsupported list operator "%a"|} Ty.Naryop.pp op
471
end
472

473
module I64 = struct
474
  let cmp_u x op y = op Int64.(add x min_int) Int64.(add y min_int) [@@inline]
2✔
475

476
  let lt_u x y = cmp_u x Int64.Infix.( < ) y [@@inline]
2✔
477
end
478

479
module Bitv = struct
480
  let to_value bv : Value.t = Bitv bv [@@inline]
72✔
481

482
  let i32_to_value v = to_value @@ Bitvector.of_int32 v
13✔
483

484
  let i64_to_value v = to_value @@ Bitvector.of_int64 v
11✔
485

486
  let of_value (n : int) (op : op_type) (v : Value.t) : Bitvector.t =
487
    let todo = Ty.Ty_bitv 32 in
154✔
488
    of_arg
489
      (function Bitv bv -> bv | _ -> raise_notrace (Value todo))
×
490
      n v op
491
      (err_str n op todo (Value.type_of v))
154✔
492

493
  let i32_of_value n op v = of_value n op v |> Bitvector.to_int32
8✔
494

495
  let i64_of_value n op v = of_value n op v |> Bitvector.to_int64
11✔
496

497
  let unop op bv =
498
    let bv = of_value 1 (`Unop op) bv in
4✔
499
    to_value
4✔
500
    @@
501
    match op with
502
    | Ty.Unop.Neg -> Bitvector.neg bv
2✔
503
    | Not -> Bitvector.lognot bv
2✔
504
    | Clz -> Bitvector.clz bv
×
505
    | Ctz -> Bitvector.ctz bv
×
506
    | Popcnt -> Bitvector.popcnt bv
×
507
    | _ ->
×
508
      Fmt.failwith {|unop: Unsupported bitvectore operator "%a"|} Ty.Unop.pp op
×
509

510
  let binop op bv1 bv2 =
511
    let bv1 = of_value 1 (`Binop op) bv1 in
37✔
512
    let bv2 = of_value 2 (`Binop op) bv2 in
37✔
513
    to_value
37✔
514
    @@
515
    match op with
516
    | Ty.Binop.Add -> Bitvector.add bv1 bv2
10✔
517
    | Sub -> Bitvector.sub bv1 bv2
4✔
518
    | Mul -> Bitvector.mul bv1 bv2
4✔
519
    | Div -> Bitvector.div bv1 bv2
2✔
520
    | DivU -> Bitvector.div_u bv1 bv2
×
521
    | Rem -> Bitvector.rem bv1 bv2
3✔
522
    | RemU -> Bitvector.rem_u bv1 bv2
×
523
    | And -> Bitvector.logand bv1 bv2
2✔
524
    | Or -> Bitvector.logor bv1 bv2
2✔
525
    | Xor -> Bitvector.logxor bv1 bv2
2✔
526
    | Shl -> Bitvector.shl bv1 bv2
2✔
527
    | ShrL -> Bitvector.lshr bv1 bv2
×
528
    | ShrA -> Bitvector.ashr bv1 bv2
2✔
529
    | Rotl -> Bitvector.rotate_left bv1 bv2
2✔
530
    | Rotr -> Bitvector.rotate_right bv1 bv2
2✔
531
    | _ ->
×
532
      Fmt.failwith {|binop: unsupported bitvector operator "%a"|} Ty.Binop.pp op
×
533

534
  let relop op bv1 bv2 =
535
    let bv1 = of_value 1 (`Relop op) bv1 in
25✔
536
    let bv2 = of_value 2 (`Relop op) bv2 in
25✔
537
    match op with
25✔
538
    | Ty.Relop.Lt -> Bitvector.lt bv1 bv2
3✔
539
    | LtU -> Bitvector.lt_u bv1 bv2
4✔
540
    | Le -> Bitvector.le bv1 bv2
3✔
541
    | LeU -> Bitvector.le_u bv1 bv2
4✔
542
    | Gt -> Bitvector.gt bv1 bv2
3✔
543
    | GtU -> Bitvector.gt_u bv1 bv2
2✔
544
    | Ge -> Bitvector.ge bv1 bv2
3✔
545
    | GeU -> Bitvector.ge_u bv1 bv2
2✔
546
    | Eq -> Bitvector.equal bv1 bv2
1✔
547
    | Ne -> not @@ Bitvector.equal bv1 bv2
×
548
end
549

550
module F32 = struct
551
  let to_float (v : int32) : float = Int32.float_of_bits v [@@inline]
76✔
552

553
  let of_float (v : float) : int32 = Int32.bits_of_float v [@@inline]
25✔
554

555
  let to_value (f : int32) : Value.t = Num (F32 f) [@@inline]
35✔
556

557
  let to_value' (f : float) : Value.t = to_value @@ of_float f [@@inline]
15✔
558

559
  let of_value (i : int) (op : op_type) (v : Value.t) : int32 =
560
    of_arg
92✔
561
      (function Num (F32 f) -> f | _ -> raise_notrace (Value (Ty_fp 32)))
1✔
562
      i v op
563
      (err_str i op (Ty_fp 32) (Value.type_of v))
92✔
564
  [@@inline]
565

566
  let of_value' (i : int) (op : op_type) (v : Value.t) : float =
567
    of_value i op v |> to_float
52✔
568
  [@@inline]
569

570
  (* Stolen from Owi *)
571
  let abs x = Int32.logand x Int32.max_int
6✔
572

573
  let neg x = Int32.logxor x Int32.min_int
2✔
574

575
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
576
    let f = to_float @@ of_value 1 (`Unop op) v in
12✔
577
    match op with
12✔
578
    | Neg -> to_value @@ neg @@ of_value 1 (`Unop op) v
2✔
579
    | Abs -> to_value @@ abs @@ of_value 1 (`Unop op) v
1✔
580
    | Sqrt -> to_value' @@ Float.sqrt f
1✔
581
    | Nearest -> to_value' @@ Float.round f
2✔
582
    | Ceil -> to_value' @@ Float.ceil f
1✔
583
    | Floor -> to_value' @@ Float.floor f
1✔
584
    | Trunc -> to_value' @@ Float.trunc f
2✔
585
    | Is_nan -> if Float.is_nan f then Value.True else Value.False
1✔
UNCOV
586
    | _ -> Fmt.failwith {|unop: Unsupported f32 operator "%a"|} Ty.Unop.pp op
×
587

588
  (* Stolen from Owi *)
589
  let copy_sign x y = Int32.logor (abs x) (Int32.logand y Int32.min_int)
5✔
590

591
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
592
    let a = of_value' 1 (`Binop op) v1 in
13✔
593
    let b = of_value' 1 (`Binop op) v2 in
13✔
594
    match op with
13✔
595
    | Add -> to_value' @@ Float.add a b
1✔
596
    | Sub -> to_value' @@ Float.sub a b
1✔
597
    | Mul -> to_value' @@ Float.mul a b
1✔
598
    | Div -> to_value' @@ Float.div a b
2✔
599
    | Rem -> to_value' @@ Float.rem a b
1✔
600
    | Min -> to_value' @@ Float.min a b
1✔
601
    | Max -> to_value' @@ Float.max a b
1✔
602
    | Copysign ->
5✔
603
      let a = of_value 1 (`Binop op) v1 in
604
      let b = of_value 1 (`Binop op) v2 in
5✔
605
      to_value (copy_sign a b)
5✔
UNCOV
606
    | _ -> Fmt.failwith {|binop: Unsupported f32 operator "%a"|} Ty.Binop.pp op
×
607

608
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
609
    let f =
13✔
610
      match op with
611
      | Eq -> Float.Infix.( = )
3✔
612
      | Ne -> Float.Infix.( <> )
2✔
613
      | Lt -> Float.Infix.( < )
2✔
614
      | Le -> Float.Infix.( <= )
2✔
615
      | Gt -> Float.Infix.( > )
2✔
616
      | Ge -> Float.Infix.( >= )
2✔
UNCOV
617
      | _ ->
×
UNCOV
618
        Fmt.failwith {|relop: Unsupported f32 operator "%a"|} Ty.Relop.pp op
×
619
    in
620
    f (of_value' 1 (`Relop op) v1) (of_value' 2 (`Relop op) v2)
13✔
621
end
622

623
module F64 = struct
624
  let to_float (v : int64) : float = Int64.float_of_bits v [@@inline]
73✔
625

626
  let of_float (v : float) : int64 = Int64.bits_of_float v [@@inline]
23✔
627

628
  let to_value (f : int64) : Value.t = Num (F64 f) [@@inline]
32✔
629

630
  let to_value' (f : float) : Value.t = to_value @@ of_float f [@@inline]
15✔
631

632
  let of_value (i : int) (op : op_type) (v : Value.t) : int64 =
633
    of_arg
87✔
634
      (function Num (F64 f) -> f | _ -> raise_notrace (Value (Ty_fp 64)))
1✔
635
      i v op
636
      (err_str i op (Ty_fp 64) (Value.type_of v))
87✔
637
  [@@inline]
638

639
  let of_value' (i : int) (op : op_type) (v : Value.t) : float =
640
    of_value i op v |> to_float
61✔
641
  [@@inline]
642

643
  (* Stolen from owi *)
644
  let abs x = Int64.logand x Int64.max_int
6✔
645

646
  let neg x = Int64.logxor x Int64.min_int
1✔
647

648
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
649
    let f = of_value' 1 (`Unop op) v in
12✔
650
    match op with
11✔
651
    | Neg -> to_value @@ neg @@ of_value 1 (`Unop op) v
1✔
652
    | Abs -> to_value @@ abs @@ of_value 1 (`Unop op) v
1✔
653
    | Sqrt -> to_value' @@ Float.sqrt f
1✔
654
    | Nearest -> to_value' @@ Float.round f
2✔
655
    | Ceil -> to_value' @@ Float.ceil f
1✔
656
    | Floor -> to_value' @@ Float.floor f
1✔
657
    | Trunc -> to_value' @@ Float.trunc f
2✔
658
    | Is_nan -> if Float.is_nan f then Value.True else Value.False
1✔
659
    | _ -> Fmt.failwith {|unop: Unsupported f32 operator "%a"|} Ty.Unop.pp op
×
660

661
  let copy_sign x y = Int64.logor (abs x) (Int64.logand y Int64.min_int)
5✔
662

663
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
664
    let a = of_value' 1 (`Binop op) v1 in
13✔
665
    let b = of_value' 1 (`Binop op) v2 in
13✔
666
    match op with
13✔
667
    | Add -> to_value' @@ Float.add a b
1✔
668
    | Sub -> to_value' @@ Float.sub a b
1✔
669
    | Mul -> to_value' @@ Float.mul a b
1✔
670
    | Div -> to_value' @@ Float.div a b
2✔
671
    | Rem -> to_value' @@ Float.rem a b
1✔
672
    | Min -> to_value' @@ Float.min a b
1✔
673
    | Max -> to_value' @@ Float.max a b
1✔
674
    | Copysign ->
5✔
675
      let a = of_value 1 (`Binop op) v1 in
676
      let b = of_value 1 (`Binop op) v2 in
5✔
677
      to_value @@ copy_sign a b
5✔
UNCOV
678
    | _ -> Fmt.failwith {|binop: Unsupported f32 operator "%a"|} Ty.Binop.pp op
×
679

680
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
681
    let f =
12✔
682
      match op with
683
      | Eq -> Float.Infix.( = )
2✔
684
      | Ne -> Float.Infix.( <> )
2✔
685
      | Lt -> Float.Infix.( < )
2✔
686
      | Le -> Float.Infix.( <= )
2✔
687
      | Gt -> Float.Infix.( > )
2✔
688
      | Ge -> Float.Infix.( >= )
2✔
689
      | _ ->
×
690
        Fmt.failwith {|relop: Unsupported f32 operator "%a"|} Ty.Relop.pp op
×
691
    in
692
    f (of_value' 1 (`Relop op) v1) (of_value' 2 (`Relop op) v2)
12✔
693
end
694

695
module I32CvtOp = struct
696
  (* let extend_s (n : int) (x : int32) : int32 = *)
697
  (*   let shift = 32 - n in *)
698
  (*   Int32.(shift_right (shift_left x shift) shift) *)
699

700
  let trunc_f32_s (x : int32) =
UNCOV
701
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
702
    else
703
      let xf = F32.to_float x in
2✔
704
      if
2✔
705
        Float.Infix.(
UNCOV
706
          xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) )
×
707
      then raise Integer_overflow
×
708
      else Int32.of_float xf
2✔
709

710
  let trunc_f32_u (x : int32) =
UNCOV
711
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
712
    else
713
      let xf = F32.to_float x in
1✔
UNCOV
714
      if Float.Infix.(xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0)
×
715
      then raise Integer_overflow
×
716
      else Int32.of_float xf
1✔
717

718
  let trunc_f64_s (x : int64) =
UNCOV
719
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
720
    else
721
      let xf = F64.to_float x in
2✔
722
      if
2✔
723
        Float.Infix.(
724
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
UNCOV
725
      then raise Integer_overflow
×
726
      else Int32.of_float xf
2✔
727

728
  let trunc_f64_u (x : int64) =
UNCOV
729
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
730
    else
731
      let xf = F64.to_float x in
1✔
732
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
UNCOV
733
      then raise Integer_overflow
×
734
      else Int32.of_float xf
1✔
735

736
  let trunc_sat_f32_s x =
UNCOV
737
    if Int32.Infix.(x <> x) then 0l
×
738
    else
739
      let xf = F32.to_float x in
1✔
740
      if Float.Infix.(xf < Int32.(to_float min_int)) then Int32.min_int
×
UNCOV
741
      else if Float.Infix.(xf >= -.Int32.(to_float min_int)) then Int32.max_int
×
742
      else Int32.of_float xf
1✔
743

744
  let trunc_sat_f32_u x =
UNCOV
745
    if Int32.Infix.(x <> x) then 0l
×
746
    else
747
      let xf = F32.to_float x in
1✔
748
      if Float.Infix.(xf <= -1.0) then 0l
×
UNCOV
749
      else if Float.Infix.(xf >= -.Int32.(to_float min_int) *. 2.0) then -1l
×
750
      else Int32.of_float xf
1✔
751

752
  let trunc_sat_f64_s x =
UNCOV
753
    if Int64.Infix.(x <> x) then 0l
×
754
    else
755
      let xf = F64.to_float x in
1✔
UNCOV
756
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int32.min_int
×
UNCOV
757
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int32.max_int
×
758
      else Int32.of_float xf
1✔
759

760
  let trunc_sat_f64_u x =
UNCOV
761
    if Int64.Infix.(x <> x) then 0l
×
762
    else
763
      let xf = F64.to_float x in
1✔
UNCOV
764
      if Float.Infix.(xf <= -1.0) then 0l
×
UNCOV
765
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1l
×
766
      else Int32.of_float xf
1✔
767

768
  let cvtop op v =
769
    let op' = `Cvtop op in
16✔
770
    match op with
771
    | Ty.Cvtop.WrapI64 ->
1✔
772
      Bitv.i32_to_value (Int64.to_int32 (Bitv.i64_of_value 1 op' v))
1✔
773
    | TruncSF32 -> Bitv.i32_to_value (trunc_f32_s (F32.of_value 1 op' v))
2✔
774
    | TruncUF32 -> Bitv.i32_to_value (trunc_f32_u (F32.of_value 1 op' v))
1✔
775
    | TruncSF64 -> Bitv.i32_to_value (trunc_f64_s (F64.of_value 1 op' v))
2✔
776
    | TruncUF64 -> Bitv.i32_to_value (trunc_f64_u (F64.of_value 1 op' v))
1✔
777
    | Trunc_sat_f32_s ->
1✔
778
      Bitv.i32_to_value (trunc_sat_f32_s (F32.of_value 1 op' v))
1✔
779
    | Trunc_sat_f32_u ->
1✔
780
      Bitv.i32_to_value (trunc_sat_f32_u (F32.of_value 1 op' v))
1✔
781
    | Trunc_sat_f64_s ->
1✔
782
      Bitv.i32_to_value (trunc_sat_f64_s (F64.of_value 1 op' v))
1✔
783
    | Trunc_sat_f64_u ->
1✔
784
      Bitv.i32_to_value (trunc_sat_f64_u (F64.of_value 1 op' v))
1✔
785
    | Reinterpret_float -> Bitv.i32_to_value (F32.of_value 1 op' v)
2✔
786
    | Sign_extend n ->
2✔
787
      Bitv.to_value (Bitvector.sign_extend n (Bitv.of_value 1 op' v))
2✔
788
    | Zero_extend n ->
1✔
789
      Bitv.to_value (Bitvector.zero_extend n (Bitv.of_value 1 op' v))
1✔
UNCOV
790
    | OfBool -> v (* already a num here *)
×
UNCOV
791
    | ToBool | _ ->
×
792
      Fmt.failwith {|cvtop: Unsupported i32 operator "%a"|} Ty.Cvtop.pp op
793
end
794

795
module I64CvtOp = struct
796
  (* let extend_s n x = *)
797
  (*   let shift = 64 - n in *)
798
  (*   Int64.(shift_right (shift_left x shift) shift) *)
799

800
  let extend_i32_u (x : int32) =
801
    Int64.(logand (of_int32 x) 0x0000_0000_ffff_ffffL)
1✔
802

803
  let trunc_f32_s (x : int32) =
UNCOV
804
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
805
    else
806
      let xf = F32.to_float x in
2✔
807
      if
2✔
808
        Float.Infix.(
UNCOV
809
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
UNCOV
810
      then raise Integer_overflow
×
811
      else Int64.of_float xf
2✔
812

813
  let trunc_f32_u (x : int32) =
UNCOV
814
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
815
    else
816
      let xf = F32.to_float x in
1✔
817
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
UNCOV
818
      then raise Integer_overflow
×
819
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
820
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
821
      else Int64.of_float xf
1✔
822

823
  let trunc_f64_s (x : int64) =
UNCOV
824
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
825
    else
826
      let xf = F64.to_float x in
2✔
827
      if
2✔
828
        Float.Infix.(
UNCOV
829
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
830
      then raise Integer_overflow
×
831
      else Int64.of_float xf
2✔
832

833
  let trunc_f64_u (x : int64) =
UNCOV
834
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
835
    else
836
      let xf = F64.to_float x in
1✔
UNCOV
837
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
838
      then raise Integer_overflow
×
839
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
UNCOV
840
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
841
      else Int64.of_float xf
1✔
842

843
  let trunc_sat_f32_s (x : int32) =
UNCOV
844
    if Int32.Infix.(x <> x) then 0L
×
845
    else
846
      let xf = F32.to_float x in
1✔
UNCOV
847
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
848
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
849
      else Int64.of_float xf
1✔
850

851
  let trunc_sat_f32_u (x : int32) =
UNCOV
852
    if Int32.Infix.(x <> x) then 0L
×
853
    else
854
      let xf = F32.to_float x in
1✔
UNCOV
855
      if Float.Infix.(xf <= -1.0) then 0L
×
856
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1L
×
857
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
UNCOV
858
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
859
      else Int64.of_float xf
1✔
860

861
  let trunc_sat_f64_s (x : int64) =
UNCOV
862
    if Int64.Infix.(x <> x) then 0L
×
863
    else
864
      let xf = F64.to_float x in
1✔
UNCOV
865
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
UNCOV
866
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
867
      else Int64.of_float xf
1✔
868

869
  let trunc_sat_f64_u (x : int64) =
UNCOV
870
    if Int64.Infix.(x <> x) then 0L
×
871
    else
872
      let xf = F64.to_float x in
1✔
UNCOV
873
      if Float.Infix.(xf <= -1.0) then 0L
×
UNCOV
874
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1L
×
875
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
1✔
UNCOV
876
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
877
      else Int64.of_float xf
1✔
878

879
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
880
    let op' = `Cvtop op in
16✔
881
    match op with
882
    | Sign_extend n ->
3✔
883
      Bitv.to_value (Bitvector.sign_extend n (Bitv.of_value 1 op' v))
3✔
884
    | Zero_extend n ->
1✔
885
      Bitv.to_value (Bitvector.zero_extend n (Bitv.of_value 1 op' v))
1✔
886
    | TruncSF32 -> Bitv.i64_to_value (trunc_f32_s (F32.of_value 1 op' v))
2✔
887
    | TruncUF32 -> Bitv.i64_to_value (trunc_f32_u (F32.of_value 1 op' v))
1✔
888
    | TruncSF64 -> Bitv.i64_to_value (trunc_f64_s (F64.of_value 1 op' v))
2✔
889
    | TruncUF64 -> Bitv.i64_to_value (trunc_f64_u (F64.of_value 1 op' v))
1✔
890
    | Trunc_sat_f32_s ->
1✔
891
      Bitv.i64_to_value (trunc_sat_f32_s (F32.of_value 1 op' v))
1✔
892
    | Trunc_sat_f32_u ->
1✔
893
      Bitv.i64_to_value (trunc_sat_f32_u (F32.of_value 1 op' v))
1✔
894
    | Trunc_sat_f64_s ->
1✔
895
      Bitv.i64_to_value (trunc_sat_f64_s (F64.of_value 1 op' v))
1✔
896
    | Trunc_sat_f64_u ->
1✔
897
      Bitv.i64_to_value (trunc_sat_f64_u (F64.of_value 1 op' v))
1✔
898
    | Reinterpret_float -> Bitv.i64_to_value (F64.of_value 1 op' v)
1✔
899
    | WrapI64 ->
1✔
900
      raise
901
        (TypeError
902
           { index = 1
903
           ; value = v
904
           ; ty = Ty_bitv 64
905
           ; op = `Cvtop WrapI64
906
           ; msg = "Cannot wrapI64 on an I64"
907
           } )
UNCOV
908
    | ToBool | OfBool | _ ->
×
909
      Fmt.failwith {|cvtop: Unsupported i64 operator "%a"|} Ty.Cvtop.pp op
910
end
911

912
module F32CvtOp = struct
913
  let demote_f64 x =
914
    let xf = F64.to_float x in
2✔
915
    if Float.Infix.(xf = xf) then F32.of_float xf
1✔
916
    else
917
      let nan64bits = x in
1✔
918
      let sign_field =
919
        Int64.(shift_left (shift_right_logical nan64bits 63) 31)
1✔
920
      in
921
      let significand_field =
922
        Int64.(shift_right_logical (shift_left nan64bits 12) 41)
1✔
923
      in
924
      let fields = Int64.logor sign_field significand_field in
925
      Int32.logor 0x7fc0_0000l (Int64.to_int32 fields)
1✔
926

927
  let convert_i32_s x = F32.of_float (Int32.to_float x)
2✔
928

929
  let convert_i32_u x =
930
    F32.of_float
2✔
931
      Int32.(
932
        Int32.Infix.(
933
          if x >= 0l then to_float x
1✔
934
          else to_float (logor (shift_right_logical x 1) (logand x 1l)) *. 2.0 ) )
1✔
935

936
  let convert_i64_s x =
937
    F32.of_float
3✔
938
      Int64.(
939
        Int64.Infix.(
940
          if abs x < 0x10_0000_0000_0000L then to_float x
2✔
941
          else
UNCOV
942
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
943
            to_float (logor (shift_right x 12) r) *. 0x1p12 ) )
1✔
944

945
  let convert_i64_u x =
946
    F32.of_float
2✔
947
      Int64.(
948
        Int64.Infix.(
949
          if I64.lt_u x 0x10_0000_0000_0000L then to_float x
1✔
950
          else
UNCOV
951
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
952
            to_float (logor (shift_right_logical x 12) r) *. 0x1p12 ) )
1✔
953

954
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
955
    let op' = `Cvtop op in
13✔
956
    match op with
957
    | DemoteF64 -> F32.to_value (demote_f64 (F64.of_value 1 op' v))
2✔
958
    | ConvertSI32 -> F32.to_value (convert_i32_s (Bitv.i32_of_value 1 op' v))
2✔
959
    | ConvertUI32 -> F32.to_value (convert_i32_u (Bitv.i32_of_value 1 op' v))
2✔
960
    | ConvertSI64 -> F32.to_value (convert_i64_s (Bitv.i64_of_value 1 op' v))
3✔
961
    | ConvertUI64 -> F32.to_value (convert_i64_u (Bitv.i64_of_value 1 op' v))
2✔
962
    | Reinterpret_int -> F32.to_value (Bitv.i32_of_value 1 op' v)
1✔
963
    | PromoteF32 ->
1✔
964
      raise
965
        (TypeError
966
           { index = 1
967
           ; value = v
968
           ; ty = Ty_fp 32
969
           ; op = `Cvtop PromoteF32
970
           ; msg = "F64 must promote a F32"
971
           } )
UNCOV
972
    | ToString | OfString | _ ->
×
973
      Fmt.failwith {|cvtop: Unsupported f32 operator "%a"|} Ty.Cvtop.pp op
974
end
975

976
module F64CvtOp = struct
977
  Float.is_nan
978

979
  let promote_f32 x =
980
    let xf = F32.to_float x in
2✔
981
    if Float.Infix.(xf = xf) then F64.of_float xf
1✔
982
    else
983
      let nan32bits = I64CvtOp.extend_i32_u x in
1✔
984
      let sign_field =
1✔
985
        Int64.(shift_left (shift_right_logical nan32bits 31) 63)
1✔
986
      in
987
      let significand_field =
988
        Int64.(shift_right_logical (shift_left nan32bits 41) 12)
1✔
989
      in
990
      let fields = Int64.logor sign_field significand_field in
991
      Int64.logor 0x7ff8_0000_0000_0000L fields
1✔
992

993
  let convert_i32_s x = F64.of_float (Int32.to_float x)
2✔
994

995
  (*
996
   * Unlike the other convert_u functions, the high half of the i32 range is
997
   * within the range where f32 can represent odd numbers, so we can't do the
998
   * shift. Instead, we can use int64 signed arithmetic.
999
   *)
1000
  let convert_i32_u x =
1001
    F64.of_float Int64.(to_float (logand (of_int32 x) 0x0000_0000_ffff_ffffL))
1✔
1002

1003
  let convert_i64_s x = F64.of_float (Int64.to_float x)
2✔
1004

1005
  (*
1006
   * Values in the low half of the int64 range can be converted with a signed
1007
   * conversion. The high half is beyond the range where f64 can represent odd
1008
   * numbers, so we can shift the value right, adjust the least significant
1009
   * bit to round correctly, do a conversion, and then scale it back up.
1010
   *)
1011
  let convert_i64_u (x : int64) =
1012
    F64.of_float
2✔
1013
      Int64.(
1014
        Int64.Infix.(
1015
          if x >= 0L then to_float x
1✔
1016
          else to_float (logor (shift_right_logical x 1) (logand x 1L)) *. 2.0 ) )
1✔
1017

1018
  let cvtop (op : Ty.Cvtop.t) v : Value.t =
1019
    let op' = `Cvtop op in
11✔
1020
    match op with
1021
    | PromoteF32 -> F64.to_value (promote_f32 (F32.of_value 1 op' v))
2✔
1022
    | ConvertSI32 -> F64.to_value (convert_i32_s (Bitv.i32_of_value 1 op' v))
2✔
1023
    | ConvertUI32 -> F64.to_value (convert_i32_u (Bitv.i32_of_value 1 op' v))
1✔
1024
    | ConvertSI64 -> F64.to_value (convert_i64_s (Bitv.i64_of_value 1 op' v))
2✔
1025
    | ConvertUI64 -> F64.to_value (convert_i64_u (Bitv.i64_of_value 1 op' v))
2✔
1026
    | Reinterpret_int -> F64.to_value (Bitv.i64_of_value 1 op' v)
1✔
1027
    | DemoteF64 ->
1✔
1028
      raise
1029
        (TypeError
1030
           { index = 1
1031
           ; value = v
1032
           ; ty = Ty_bitv 64
1033
           ; op = `Cvtop DemoteF64
1034
           ; msg = "F32 must demote a F64"
1035
           } )
UNCOV
1036
    | ToString | OfString | _ ->
×
1037
      Fmt.failwith {|cvtop: Unsupported f64 operator "%a"|} Ty.Cvtop.pp op
1038
end
1039

1040
(* Dispatch *)
1041

1042
let op int real bool str lst bv f32 f64 ty op =
1043
  match ty with
201✔
1044
  | Ty.Ty_int -> int op
37✔
1045
  | Ty_real -> real op
35✔
1046
  | Ty_bool -> bool op
16✔
1047
  | Ty_str -> str op
14✔
1048
  | Ty_list -> lst op
7✔
1049
  | Ty_bitv _ -> bv op
41✔
1050
  | Ty_fp 32 -> f32 op
26✔
1051
  | Ty_fp 64 -> f64 op
25✔
1052
  | Ty_fp _ | Ty_app | Ty_unit | Ty_none | Ty_regexp -> assert false
1053
[@@inline]
1054

1055
let unop =
1056
  op Int.unop Real.unop Bool.unop Str.unop Lst.unop Bitv.unop F32.unop F64.unop
6✔
1057

1058
let binop =
1059
  op Int.binop Real.binop Bool.binop Str.binop Lst.binop Bitv.binop F32.binop
6✔
1060
    F64.binop
1061

1062
let triop = function
1063
  | Ty.Ty_bool -> Bool.triop
2✔
1064
  | Ty_str -> Str.triop
6✔
1065
  | Ty_list -> Lst.triop
1✔
1066
  | _ -> assert false
1067

1068
let relop = function
1069
  | Ty.Ty_int -> Int.relop
8✔
1070
  | Ty_real -> Real.relop
12✔
1071
  | Ty_bool -> Bool.relop
23✔
1072
  | Ty_str -> Str.relop
14✔
1073
  | Ty_bitv _ -> Bitv.relop
25✔
1074
  | Ty_fp 32 -> F32.relop
13✔
1075
  | Ty_fp 64 -> F64.relop
12✔
1076
  | _ -> assert false
1077

1078
let cvtop = function
1079
  | Ty.Ty_int -> Int.cvtop
9✔
1080
  | Ty_real -> Real.cvtop
8✔
1081
  | Ty_str -> Str.cvtop
12✔
1082
  | Ty_bitv 32 -> I32CvtOp.cvtop
16✔
1083
  | Ty_bitv 64 -> I64CvtOp.cvtop
16✔
1084
  | Ty_fp 32 -> F32CvtOp.cvtop
13✔
1085
  | Ty_fp 64 -> F64CvtOp.cvtop
11✔
1086
  | _ -> assert false
1087

1088
let naryop = function
1089
  | Ty.Ty_bool -> Bool.naryop
6✔
1090
  | Ty_str -> Str.naryop
4✔
UNCOV
1091
  | Ty_list -> Lst.naryop
×
1092
  | _ -> assert false
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