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

formalsec / smtml / 321

03 May 2025 06:14PM UTC coverage: 42.428% (-7.8%) from 50.195%
321

push

github

filipeom
Don't install z3 in deploy CI

720 of 1697 relevant lines covered (42.43%)

7.49 hits per line

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

55.62
/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
23✔
23
  | `Binop op -> Fmt.pf fmt "binop '%a'" Ty.Binop.pp op
151✔
24
  | `Relop op -> Fmt.pf fmt "relop '%a'" Ty.Relop.pp op
98✔
25
  | `Triop op -> Fmt.pf fmt "triop '%a'" Ty.Triop.pp op
11✔
26
  | `Cvtop op -> Fmt.pf fmt "cvtop '%a'" Ty.Cvtop.pp op
15✔
27
  | `Naryop op -> Fmt.pf fmt "naryop '%a'" Ty.Naryop.pp op
18✔
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 IndexOutOfBounds
50

51
(* FIXME: use snake case instead *)
52
exception ParseNumError
53

54
let of_arg f n v op msg =
55
  try f v
316✔
56
  with Value t -> raise (TypeError { index = n; value = v; ty = t; op; msg })
×
57
[@@inline]
58

59
let err_str n op ty_expected ty_actual =
60
  Fmt.str "Argument %d of %a expected type %a but got %a instead." n pp_op_type
316✔
61
    op Ty.pp ty_expected Ty.pp ty_actual
62

63
module Int = struct
64
  let to_value (i : int) : Value.t = Int i [@@inline]
25✔
65

66
  let of_value (n : int) (op : op_type) (v : Value.t) : int =
67
    of_arg
51✔
68
      (function Int i -> i | _ -> raise_notrace (Value Ty_int))
×
69
      n v op
70
      (err_str n op Ty_int (Value.type_of v))
51✔
71
  [@@inline]
72

73
  let str_value (n : int) (op : op_type) (v : Value.t) : string =
74
    of_arg
×
75
      (function Str str -> str | _ -> raise_notrace (Value Ty_str))
×
76
      n v op
77
      (err_str n op Ty_str (Value.type_of v))
×
78

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

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

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

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

137
  let of_bool : Value.t -> int = function
138
    | True -> 1
1✔
139
    | False -> 0
1✔
140
    | _ -> assert false
141
  [@@inline]
142

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

158
module Real = struct
159
  let to_value (v : float) : Value.t = Real v [@@inline]
17✔
160

161
  let of_value (n : int) (op : op_type) (v : Value.t) : float =
162
    of_arg
31✔
163
      (function Real v -> v | _ -> raise_notrace (Value Ty_int))
×
164
      n v op
165
      (err_str n op Ty_real (Value.type_of v))
31✔
166
  [@@inline]
167

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

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

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

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

229
module Bool = struct
230
  let to_value (b : bool) : Value.t = if b then True else False [@@inline]
4✔
231

232
  let of_value (n : int) (op : op_type) (v : Value.t) : bool =
233
    of_arg
15✔
234
      (function
235
        | True -> true | False -> false | _ -> raise_notrace (Value Ty_bool) )
×
236
      n v op
237
      (err_str n op Ty_bool (Value.type_of v))
15✔
238
  [@@inline]
239

240
  let unop (op : Ty.Unop.t) v =
241
    let b = of_value 1 (`Unop op) v in
1✔
242
    match op with
1✔
243
    | Not -> to_value (not b)
1✔
244
    | _ -> Fmt.failwith {|unop: Unsupported bool operator "%a"|} Ty.Unop.pp op
×
245

246
  let xor b1 b2 =
247
    match (b1, b2) with
×
248
    | true, true -> false
×
249
    | true, false -> true
×
250
    | false, true -> true
×
251
    | false, false -> false
×
252

253
  let binop (op : Ty.Binop.t) v1 v2 =
254
    let f =
1✔
255
      match op with
256
      | And -> ( && )
1✔
257
      | Or -> ( || )
×
258
      | Xor -> xor
×
259
      | _ ->
×
260
        Fmt.failwith {|binop: Unsupported bool operator "%a"|} Ty.Binop.pp op
×
261
    in
262
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
1✔
263

264
  let triop (op : Ty.Triop.t) c v1 v2 =
265
    match op with
×
266
    | Ite -> ( match of_value 1 (`Triop op) c with true -> v1 | false -> v2 )
×
267
    | _ -> Fmt.failwith {|triop: Unsupported bool operator "%a"|} Ty.Triop.pp op
×
268

269
  let relop (op : Ty.Relop.t) v1 v2 =
270
    match op with
16✔
271
    | Eq -> Value.equal v1 v2
9✔
272
    | Ne -> not (Value.equal v1 v2)
7✔
273
    | _ -> Fmt.failwith {|relop: Unsupported bool operator "%a"|} Ty.Relop.pp op
×
274

275
  let cvtop _ _ = assert false
×
276

277
  let naryop (op : Ty.Naryop.t) vs =
278
    let b =
4✔
279
      match op with
280
      | Logand ->
2✔
281
        List.fold_left ( && ) true
2✔
282
          (List.mapi (fun i -> of_value i (`Naryop op)) vs)
2✔
283
      | Logor ->
2✔
284
        List.fold_left ( || ) false
2✔
285
          (List.mapi (fun i -> of_value i (`Naryop op)) vs)
2✔
286
      | _ ->
×
287
        Fmt.failwith {|naryop: Unsupported bool operator "%a"|} Ty.Naryop.pp op
×
288
    in
289
    to_value b
290
end
291

292
module Str = struct
293
  let to_value (str : string) : Value.t = Str str [@@inline]
9✔
294

295
  let of_value (n : int) (op : op_type) (v : Value.t) : string =
296
    of_arg
40✔
297
      (function Str str -> str | _ -> raise_notrace (Value Ty_str))
×
298
      n v op
299
      (err_str n op Ty_str (Value.type_of v))
40✔
300
  [@@inline]
301

302
  let replace s t t' =
303
    let len_s = String.length s in
1✔
304
    let len_t = String.length t in
1✔
305
    let rec loop i =
1✔
306
      if i >= len_s then s
×
307
      else if i + len_t > len_s then s
×
308
      else if String.equal (String.sub s i len_t) t then
2✔
309
        let s' = Fmt.str "%s%s" (String.sub s 0 i) t' in
1✔
310
        let s'' = String.sub s (i + len_t) (len_s - i - len_t) in
1✔
311
        Fmt.str "%s%s" s' s''
1✔
312
      else loop (i + 1)
1✔
313
    in
314
    loop 0
315

316
  let indexof s sub start =
317
    let len_s = String.length s in
2✔
318
    let len_sub = String.length sub in
2✔
319
    let max_i = len_s - 1 in
2✔
320
    let rec loop i =
321
      if i > max_i then ~-1
×
322
      else if i + len_sub > len_s then ~-1
×
323
      else if String.equal sub (String.sub s i len_sub) then i
2✔
324
      else loop (i + 1)
2✔
325
    in
326
    if start <= 0 then loop 0 else loop start
×
327

328
  let contains s sub = if indexof s sub 0 < 0 then false else true
×
329

330
  let unop (op : Ty.Unop.t) v =
331
    let str = of_value 1 (`Unop op) v in
2✔
332
    match op with
2✔
333
    | Length -> Int.to_value (String.length str)
1✔
334
    | Trim -> to_value (String.trim str)
1✔
335
    | _ -> Fmt.failwith {|unop: Unsupported str operator "%a"|} Ty.Unop.pp op
×
336

337
  let binop (op : Ty.Binop.t) v1 v2 =
338
    let op' = `Binop op in
4✔
339
    let str = of_value 1 op' v1 in
340
    match op with
4✔
341
    | At -> (
1✔
342
      let i = Int.of_value 2 op' v2 in
343
      try to_value (Fmt.str "%c" (String.get str i))
1✔
344
      with Invalid_argument _ -> raise IndexOutOfBounds )
×
345
    | String_prefix ->
1✔
346
      Bool.to_value (String.starts_with ~prefix:str (of_value 2 op' v2))
1✔
347
    | String_suffix ->
1✔
348
      Bool.to_value (String.ends_with ~suffix:str (of_value 2 op' v2))
1✔
349
    | String_contains -> Bool.to_value (contains str (of_value 2 op' v2))
1✔
350
    | _ -> Fmt.failwith {|binop: Unsupported str operator "%a"|} Ty.Binop.pp op
×
351

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

370
  let relop (op : Ty.Relop.t) v1 v2 =
371
    let f =
8✔
372
      match op with
373
      | Lt -> ( < )
1✔
374
      | Le -> ( <= )
1✔
375
      | Gt -> ( > )
1✔
376
      | Ge -> ( >= )
1✔
377
      | Eq -> ( = )
2✔
378
      | Ne -> ( <> )
2✔
379
      | _ ->
×
380
        Fmt.failwith {|relop: Unsupported string operator "%a"|} Ty.Relop.pp op
×
381
    in
382
    let f x y = f (String.compare x y) 0 in
8✔
383
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
8✔
384

385
  let cvtop (op : Ty.Cvtop.t) v =
386
    let op' = `Cvtop op in
5✔
387
    match op with
388
    | String_to_code ->
1✔
389
      let str = of_value 1 op' v in
390
      Int.to_value (Char.code str.[0])
1✔
391
    | String_from_code ->
1✔
392
      let code = Int.of_value 1 op' v in
393
      to_value (String.make 1 (Char.chr code))
1✔
394
    | String_to_int ->
1✔
395
      let s = of_value 1 op' v in
396
      let i =
1✔
397
        match int_of_string_opt s with
398
        | None -> raise ParseNumError
×
399
        | Some i -> i
1✔
400
      in
401
      Int.to_value i
402
    | String_from_int -> to_value (string_of_int (Int.of_value 1 op' v))
1✔
403
    | String_to_float ->
1✔
404
      let s = of_value 1 op' v in
405
      let f =
1✔
406
        match float_of_string_opt s with
407
        | None -> raise ParseNumError
×
408
        | Some f -> f
1✔
409
      in
410
      Real.to_value f
411
    | _ -> Fmt.failwith {|cvtop: Unsupported str operator "%a"|} Ty.Cvtop.pp op
×
412

413
  let naryop (op : Ty.Naryop.t) vs =
414
    let op' = `Naryop op in
3✔
415
    match op with
416
    | Concat -> to_value (String.concat "" (List.map (of_value 0 op') vs))
3✔
417
    | _ ->
×
418
      Fmt.failwith {|naryop: Unsupported str operator "%a"|} Ty.Naryop.pp op
419
end
420

421
module Lst = struct
422
  let of_value (n : int) (op : op_type) (v : Value.t) : Value.t list =
423
    of_arg
9✔
424
      (function List lst -> lst | _ -> raise_notrace (Value Ty_list))
×
425
      n v op
426
      (err_str n op Ty_list (Value.type_of v))
9✔
427
  [@@inline]
428

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

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

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

472
  let naryop (op : Ty.Naryop.t) (vs : Value.t list) : Value.t =
473
    let op' = `Naryop op in
×
474
    match op with
475
    | Concat -> List (List.concat_map (of_value 0 op') vs)
×
476
    | _ ->
×
477
      Fmt.failwith {|naryop: Unsupported list operator "%a"|} Ty.Naryop.pp op
478
end
479

480
module I32 = struct
481
  let to_value (i : int32) : Value.t = Num (I32 i) [@@inline]
29✔
482

483
  let of_value (n : int) (op : op_type) (v : Value.t) : int32 =
484
    of_arg
86✔
485
      (function Num (I32 i) -> i | _ -> raise_notrace (Value (Ty_bitv 32)))
×
486
      n v op
487
      (err_str n op (Ty_bitv 32) (Value.type_of v))
86✔
488
  [@@inline]
489

490
  let cmp_u x op y = op Int32.(add x min_int) Int32.(add y min_int) [@@inline]
8✔
491

492
  let lt_u x y = cmp_u x Int32.Infix.( < ) y [@@inline]
2✔
493

494
  let le_u x y = cmp_u x Int32.Infix.( <= ) y [@@inline]
2✔
495

496
  let gt_u x y = cmp_u x Int32.Infix.( > ) y [@@inline]
2✔
497

498
  let ge_u x y = cmp_u x Int32.Infix.( >= ) y [@@inline]
2✔
499

500
  let shift f x y = f x Int32.(to_int (logand y 31l)) [@@inline]
6✔
501

502
  let shl x y = shift Int32.shift_left x y [@@inline]
3✔
503

504
  let shr_s x y = shift Int32.shift_right x y [@@inline]
1✔
505

506
  let shr_u x y = shift Int32.shift_right_logical x y [@@inline]
2✔
507

508
  (* Stolen rotl and rotr from: *)
509
  (* https://github.com/OCamlPro/owi/blob/main/src/int32.ml *)
510
  (* We must mask the count to implement rotates via shifts. *)
511
  let clamp_rotate_count n = Int32.(to_int (logand n 31l)) [@@inline]
2✔
512

513
  let rotl x y =
514
    let n = clamp_rotate_count y in
1✔
515
    Int32.logor (shl x (Int32.of_int n)) (shr_u x (Int32.of_int (32 - n)))
1✔
516
  [@@inline]
517

518
  let rotr x y =
519
    let n = clamp_rotate_count y in
1✔
520
    Int32.logor (shr_u x (Int32.of_int n)) (shl x (Int32.of_int (32 - n)))
1✔
521
  [@@inline]
522

523
  let clz n =
524
    let n = Ocaml_intrinsics.Int32.count_leading_zeros n in
×
525
    Int32.of_int n
×
526

527
  let ctz n =
528
    let n = Ocaml_intrinsics.Int32.count_trailing_zeros n in
×
529
    Int32.of_int n
×
530

531
  let popcnt n =
532
    let n = Ocaml_intrinsics.Int32.count_set_bits n in
×
533
    Int32.of_int n
×
534

535
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
536
    let f =
2✔
537
      match op with
538
      | Neg -> Int32.neg
1✔
539
      | Not -> Int32.lognot
1✔
540
      | Clz -> clz
×
541
      | Ctz -> ctz
×
542
      | Popcnt -> popcnt
×
543
      | _ -> Fmt.failwith {|unop: Unsupported i32 operator "%a"|} Ty.Unop.pp op
×
544
    in
545
    to_value (f (of_value 1 (`Unop op) v))
2✔
546

547
  let binop op v1 v2 =
548
    let f =
25✔
549
      match op with
550
      | Ty.Binop.Add -> Int32.add
9✔
551
      | Sub -> Int32.sub
3✔
552
      | Mul -> Int32.mul
3✔
553
      | Div -> Int32.div
1✔
554
      | DivU -> Int32.unsigned_div
×
555
      | Rem -> Int32.rem
2✔
556
      | RemU -> Int32.unsigned_rem
×
557
      | And -> Int32.logand
1✔
558
      | Or -> Int32.logor
1✔
559
      | Xor -> Int32.logxor
1✔
560
      | Shl -> shl
1✔
561
      | ShrL -> shr_u
×
562
      | ShrA -> shr_s
1✔
563
      | Rotl -> rotl
1✔
564
      | Rotr -> rotr
1✔
565
      | _ ->
×
566
        Fmt.failwith {|binop: Unsupported i32 operator "%a"|} Ty.Binop.pp op
×
567
    in
568
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
25✔
569

570
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
571
    let f =
16✔
572
      match op with
573
      | Lt -> Int32.Infix.( < )
2✔
574
      | LtU -> lt_u
2✔
575
      | Le -> Int32.Infix.( <= )
2✔
576
      | LeU -> le_u
2✔
577
      | Gt -> Int32.Infix.( > )
2✔
578
      | GtU -> gt_u
2✔
579
      | Ge -> Int32.Infix.( >= )
2✔
580
      | GeU -> ge_u
2✔
581
      | Eq | Ne -> assert false
582
    in
583
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
16✔
584
end
585

586
module I64 = struct
587
  let to_value (i : int64) : Value.t = Num (I64 i) [@@inline]
16✔
588

589
  let of_value (n : int) (op : op_type) (v : Value.t) : int64 =
590
    of_arg
44✔
591
      (function Num (I64 i) -> i | _ -> raise_notrace (Value (Ty_bitv 64)))
×
592
      n v op
593
      (err_str n op (Ty_bitv 64) (Value.type_of v))
44✔
594
  [@@inline]
595

596
  let cmp_u x op y = op Int64.(add x min_int) Int64.(add y min_int) [@@inline]
4✔
597

598
  let lt_u x y = cmp_u x Int64.Infix.( < ) y [@@inline]
1✔
599

600
  let le_u x y = cmp_u x Int64.Infix.( <= ) y [@@inline]
1✔
601

602
  let gt_u x y = cmp_u x Int64.Infix.( > ) y [@@inline]
1✔
603

604
  let ge_u x y = cmp_u x Int64.Infix.( >= ) y [@@inline]
1✔
605

606
  let shift f x y = f x Int64.(to_int (logand y 63L)) [@@inline]
6✔
607

608
  let shl x y = shift Int64.shift_left x y [@@inline]
3✔
609

610
  let shr_s x y = shift Int64.shift_right x y [@@inline]
1✔
611

612
  let shr_u x y = shift Int64.shift_right_logical x y [@@inline]
2✔
613

614
  (* Stolen rotl and rotr from: *)
615
  (* https://github.com/OCamlPro/owi/blob/main/src/int64.ml *)
616
  (* We must mask the count to implement rotates via shifts. *)
617
  let clamp_rotate_count n = Int64.(to_int (logand n (of_int 63))) [@@inline]
2✔
618

619
  let rotl x y =
620
    let n = clamp_rotate_count y in
1✔
621
    Int64.logor (shl x (Int64.of_int n)) (shr_u x (Int64.of_int (64 - n)))
1✔
622
  [@@inline]
623

624
  let rotr x y =
625
    let n = clamp_rotate_count y in
1✔
626
    Int64.logor (shr_u x (Int64.of_int n)) (shl x (Int64.of_int (64 - n)))
1✔
627
  [@@inline]
628

629
  let clz n =
630
    let n = Ocaml_intrinsics.Int64.count_leading_zeros n in
×
631
    Int64.of_int n
×
632

633
  let ctz n =
634
    let n = Ocaml_intrinsics.Int64.count_trailing_zeros n in
×
635
    Int64.of_int n
×
636

637
  let popcnt n =
638
    let n = Ocaml_intrinsics.Int64.count_set_bits n in
×
639
    Int64.of_int n
×
640

641
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
642
    let f =
2✔
643
      match op with
644
      | Neg -> Int64.neg
1✔
645
      | Not -> Int64.lognot
1✔
646
      | Clz -> clz
×
647
      | Ctz -> ctz
×
648
      | Popcnt -> popcnt
×
649
      | _ -> Fmt.failwith {|unop: Unsupported i64 operator "%a"|} Ty.Unop.pp op
×
650
    in
651
    to_value (f (of_value 1 (`Unop op) v))
2✔
652

653
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
654
    let f =
12✔
655
      match op with
656
      | Add -> Int64.add
1✔
657
      | Sub -> Int64.sub
1✔
658
      | Mul -> Int64.mul
1✔
659
      | Div -> Int64.div
1✔
660
      | DivU -> Int64.unsigned_div
×
661
      | Rem -> Int64.rem
1✔
662
      | RemU -> Int64.unsigned_rem
×
663
      | And -> Int64.logand
1✔
664
      | Or -> Int64.logor
1✔
665
      | Xor -> Int64.logxor
1✔
666
      | Shl -> shl
1✔
667
      | ShrL -> shr_u
×
668
      | ShrA -> shr_s
1✔
669
      | Rotl -> rotl
1✔
670
      | Rotr -> rotr
1✔
671
      | _ ->
×
672
        Fmt.failwith {|binop: Unsupported i64 operator "%a"|} Ty.Binop.pp op
×
673
    in
674
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
12✔
675

676
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
677
    let f =
8✔
678
      match op with
679
      | Lt -> Int64.Infix.( < )
1✔
680
      | LtU -> lt_u
1✔
681
      | Le -> Int64.Infix.( <= )
1✔
682
      | LeU -> le_u
1✔
683
      | Gt -> Int64.Infix.( > )
1✔
684
      | GtU -> gt_u
1✔
685
      | Ge -> Int64.Infix.( >= )
1✔
686
      | GeU -> ge_u
1✔
687
      | Eq | Ne -> assert false
688
    in
689
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
8✔
690
end
691

692
module F32 = struct
693
  let to_float (v : int32) : float = Int32.float_of_bits v [@@inline]
21✔
694

695
  let of_float (v : float) : int32 = Int32.bits_of_float v [@@inline]
7✔
696

697
  let to_value (f : int32) : Value.t = Num (F32 f) [@@inline]
7✔
698

699
  let to_value' (f : float) : Value.t = to_value @@ of_float f [@@inline]
5✔
700

701
  let of_value (i : int) (op : op_type) (v : Value.t) : int32 =
702
    of_arg
21✔
703
      (function Num (F32 f) -> f | _ -> raise_notrace (Value (Ty_fp 32)))
×
704
      i v op
705
      (err_str i op (Ty_fp 32) (Value.type_of v))
21✔
706
  [@@inline]
707

708
  let of_value' (i : int) (op : op_type) (v : Value.t) : float =
709
    of_value i op v |> to_float
18✔
710
  [@@inline]
711

712
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
713
    let v = to_float @@ of_value 1 (`Unop op) v in
1✔
714
    match op with
1✔
715
    | Neg -> to_value' @@ Float.neg v
×
716
    | Abs -> to_value' @@ Float.abs v
×
717
    | Sqrt -> to_value' @@ Float.sqrt v
×
718
    | Nearest -> to_value' @@ Float.round v
×
719
    | Ceil -> to_value' @@ Float.ceil v
×
720
    | Floor -> to_value' @@ Float.floor v
×
721
    | Trunc -> to_value' @@ Float.trunc v
1✔
722
    | Is_nan -> if Float.is_nan v then Value.True else Value.False
×
723
    | _ -> Fmt.failwith {|unop: Unsupported f32 operator "%a"|} Ty.Unop.pp op
×
724

725
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
726
    let f =
4✔
727
      match op with
728
      | Add -> Float.add
×
729
      | Sub -> Float.sub
×
730
      | Mul -> Float.mul
×
731
      | Div -> Float.div
×
732
      | Rem -> Float.rem
×
733
      | Min -> Float.min
×
734
      | Max -> Float.max
×
735
      | Copysign -> Float.copy_sign
4✔
736
      | _ ->
×
737
        Fmt.failwith {|binop: Unsupported f32 operator "%a"|} Ty.Binop.pp op
×
738
    in
739
    to_value' (f (of_value' 1 (`Binop op) v1) (of_value' 2 (`Binop op) v2))
4✔
740

741
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
742
    let f =
5✔
743
      match op with
744
      | Eq -> Float.Infix.( = )
1✔
745
      | Ne -> Float.Infix.( <> )
×
746
      | Lt -> Float.Infix.( < )
1✔
747
      | Le -> Float.Infix.( <= )
1✔
748
      | Gt -> Float.Infix.( > )
1✔
749
      | Ge -> Float.Infix.( >= )
1✔
750
      | _ ->
×
751
        Fmt.failwith {|relop: Unsupported f32 operator "%a"|} Ty.Relop.pp op
×
752
    in
753
    f (of_value' 1 (`Relop op) v1) (of_value' 2 (`Relop op) v2)
5✔
754
end
755

756
module F64 = struct
757
  let to_float (v : int64) : float = Int64.float_of_bits v [@@inline]
19✔
758

759
  let of_float (v : float) : int64 = Int64.bits_of_float v [@@inline]
7✔
760

761
  let to_value (f : int64) : Value.t = Num (F64 f) [@@inline]
7✔
762

763
  let to_value' (f : float) : Value.t = to_value @@ of_float f [@@inline]
5✔
764

765
  let of_value (i : int) (op : op_type) (v : Value.t) : int64 =
766
    of_arg
19✔
767
      (function Num (F64 f) -> f | _ -> raise_notrace (Value (Ty_fp 64)))
×
768
      i v op
769
      (err_str i op (Ty_fp 64) (Value.type_of v))
19✔
770
  [@@inline]
771

772
  let of_value' (i : int) (op : op_type) (v : Value.t) : float =
773
    of_value i op v |> to_float
17✔
774
  [@@inline]
775

776
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
777
    let v = of_value' 1 (`Unop op) v in
1✔
778
    match op with
1✔
779
    | Neg -> to_value' @@ Float.neg v
×
780
    | Abs -> to_value' @@ Float.abs v
×
781
    | Sqrt -> to_value' @@ Float.sqrt v
×
782
    | Nearest -> to_value' @@ Float.round v
×
783
    | Ceil -> to_value' @@ Float.ceil v
×
784
    | Floor -> to_value' @@ Float.floor v
×
785
    | Trunc -> to_value' @@ Float.trunc v
1✔
786
    | Is_nan -> if Float.is_nan v then Value.True else Value.False
×
787
    | _ -> Fmt.failwith {|unop: Unsupported f32 operator "%a"|} Ty.Unop.pp op
×
788

789
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
790
    let f =
4✔
791
      match op with
792
      | Add -> Float.add
×
793
      | Sub -> Float.sub
×
794
      | Mul -> Float.mul
×
795
      | Div -> Float.div
×
796
      | Rem -> Float.rem
×
797
      | Min -> Float.min
×
798
      | Max -> Float.max
×
799
      | Copysign -> Float.copy_sign
4✔
800
      | _ ->
×
801
        Fmt.failwith {|binop: Unsupported f32 operator "%a"|} Ty.Binop.pp op
×
802
    in
803
    to_value' (f (of_value' 1 (`Binop op) v1) (of_value' 2 (`Binop op) v2))
4✔
804

805
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
806
    let f =
4✔
807
      match op with
808
      | Eq -> Float.Infix.( = )
×
809
      | Ne -> Float.Infix.( <> )
×
810
      | Lt -> Float.Infix.( < )
1✔
811
      | Le -> Float.Infix.( <= )
1✔
812
      | Gt -> Float.Infix.( > )
1✔
813
      | Ge -> Float.Infix.( >= )
1✔
814
      | _ ->
×
815
        Fmt.failwith {|relop: Unsupported f32 operator "%a"|} Ty.Relop.pp op
×
816
    in
817
    f (of_value' 1 (`Relop op) v1) (of_value' 2 (`Relop op) v2)
4✔
818
end
819

820
module I32CvtOp = struct
821
  let extend_s (n : int) (x : int32) : int32 =
822
    let shift = 32 - n in
×
823
    Int32.(shift_right (shift_left x shift) shift)
×
824

825
  let trunc_f32_s (x : int32) =
826
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
827
    else
828
      let xf = F32.to_float x in
1✔
829
      if
1✔
830
        Float.Infix.(
831
          xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) )
×
832
      then raise Integer_overflow
×
833
      else Int32.of_float xf
1✔
834

835
  let trunc_f32_u (x : int32) =
836
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
837
    else
838
      let xf = F32.to_float x in
×
839
      if Float.Infix.(xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0)
×
840
      then raise Integer_overflow
×
841
      else Int32.of_float xf
×
842

843
  let trunc_f64_s (x : int64) =
844
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
845
    else
846
      let xf = F64.to_float x in
1✔
847
      if
1✔
848
        Float.Infix.(
849
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
850
      then raise Integer_overflow
×
851
      else Int32.of_float xf
1✔
852

853
  let trunc_f64_u (x : int64) =
854
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
855
    else
856
      let xf = F64.to_float x in
×
857
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
858
      then raise Integer_overflow
×
859
      else Int32.of_float xf
×
860

861
  let trunc_sat_f32_s x =
862
    if Int32.Infix.(x <> x) then 0l
×
863
    else
864
      let xf = F32.to_float x in
×
865
      if Float.Infix.(xf < Int32.(to_float min_int)) then Int32.min_int
×
866
      else if Float.Infix.(xf >= -.Int32.(to_float min_int)) then Int32.max_int
×
867
      else Int32.of_float xf
×
868

869
  let trunc_sat_f32_u x =
870
    if Int32.Infix.(x <> x) then 0l
×
871
    else
872
      let xf = F32.to_float x in
×
873
      if Float.Infix.(xf <= -1.0) then 0l
×
874
      else if Float.Infix.(xf >= -.Int32.(to_float min_int) *. 2.0) then -1l
×
875
      else Int32.of_float xf
×
876

877
  let trunc_sat_f64_s x =
878
    if Int64.Infix.(x <> x) then 0l
×
879
    else
880
      let xf = F64.to_float x in
×
881
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int32.min_int
×
882
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int32.max_int
×
883
      else Int32.of_float xf
×
884

885
  let trunc_sat_f64_u x =
886
    if Int64.Infix.(x <> x) then 0l
×
887
    else
888
      let xf = F64.to_float x in
×
889
      if Float.Infix.(xf <= -1.0) then 0l
×
890
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1l
×
891
      else Int32.of_float xf
×
892

893
  let cvtop op v =
894
    let op' = `Cvtop op in
2✔
895
    match op with
896
    | Ty.Cvtop.WrapI64 -> I32.to_value (Int64.to_int32 (I64.of_value 1 op' v))
×
897
    | TruncSF32 -> I32.to_value (trunc_f32_s (F32.of_value 1 op' v))
1✔
898
    | TruncUF32 -> I32.to_value (trunc_f32_u (F32.of_value 1 op' v))
×
899
    | TruncSF64 -> I32.to_value (trunc_f64_s (F64.of_value 1 op' v))
1✔
900
    | TruncUF64 -> I32.to_value (trunc_f64_u (F64.of_value 1 op' v))
×
901
    | Trunc_sat_f32_s -> I32.to_value (trunc_sat_f32_s (F32.of_value 1 op' v))
×
902
    | Trunc_sat_f32_u -> I32.to_value (trunc_sat_f32_u (F32.of_value 1 op' v))
×
903
    | Trunc_sat_f64_s -> I32.to_value (trunc_sat_f64_s (F64.of_value 1 op' v))
×
904
    | Trunc_sat_f64_u -> I32.to_value (trunc_sat_f64_u (F64.of_value 1 op' v))
×
905
    | Reinterpret_float -> I32.to_value (F32.of_value 1 op' v)
×
906
    | Sign_extend n -> I32.to_value (extend_s n (I32.of_value 1 op' v))
×
907
    | Zero_extend _n -> I32.to_value (I32.of_value 1 op' v)
×
908
    | OfBool -> v (* already a num here *)
×
909
    | ToBool | _ ->
×
910
      Fmt.failwith {|cvtop: Unsupported i32 operator "%a"|} Ty.Cvtop.pp op
911
end
912

913
module I64CvtOp = struct
914
  (* let extend_s n x = *)
915
  (*   let shift = 64 - n in *)
916
  (*   Int64.(shift_right (shift_left x shift) shift) *)
917

918
  let extend_i32_u (x : int32) =
919
    Int64.(logand (of_int32 x) 0x0000_0000_ffff_ffffL)
×
920

921
  let trunc_f32_s (x : int32) =
922
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
923
    else
924
      let xf = F32.to_float x in
1✔
925
      if
1✔
926
        Float.Infix.(
927
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
928
      then raise Integer_overflow
×
929
      else Int64.of_float xf
1✔
930

931
  let trunc_f32_u (x : int32) =
932
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
933
    else
934
      let xf = F32.to_float x in
×
935
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
936
      then raise Integer_overflow
×
937
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
×
938
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
939
      else Int64.of_float xf
×
940

941
  let trunc_f64_s (x : int64) =
942
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
943
    else
944
      let xf = F64.to_float x in
1✔
945
      if
1✔
946
        Float.Infix.(
947
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
948
      then raise Integer_overflow
×
949
      else Int64.of_float xf
1✔
950

951
  let trunc_f64_u (x : int64) =
952
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
953
    else
954
      let xf = F64.to_float x in
×
955
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
956
      then raise Integer_overflow
×
957
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
×
958
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
959
      else Int64.of_float xf
×
960

961
  let trunc_sat_f32_s (x : int32) =
962
    if Int32.Infix.(x <> x) then 0L
×
963
    else
964
      let xf = F32.to_float x in
×
965
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
966
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
967
      else Int64.of_float xf
×
968

969
  let trunc_sat_f32_u (x : int32) =
970
    if Int32.Infix.(x <> x) then 0L
×
971
    else
972
      let xf = F32.to_float x in
×
973
      if Float.Infix.(xf <= -1.0) then 0L
×
974
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1L
×
975
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
×
976
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
977
      else Int64.of_float xf
×
978

979
  let trunc_sat_f64_s (x : int64) =
980
    if Int64.Infix.(x <> x) then 0L
×
981
    else
982
      let xf = F64.to_float x in
×
983
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
984
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
985
      else Int64.of_float xf
×
986

987
  let trunc_sat_f64_u (x : int64) =
988
    if Int64.Infix.(x <> x) then 0L
×
989
    else
990
      let xf = F64.to_float x in
×
991
      if Float.Infix.(xf <= -1.0) then 0L
×
992
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1L
×
993
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
×
994
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
995
      else Int64.of_float xf
×
996

997
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
998
    let op' = `Cvtop op in
2✔
999
    match op with
1000
    | Sign_extend 32 -> I64.to_value (Int64.of_int32 (I32.of_value 1 op' v))
×
1001
    | Zero_extend 32 -> I64.to_value (extend_i32_u (I32.of_value 1 op' v))
×
1002
    | TruncSF32 -> I64.to_value (trunc_f32_s (F32.of_value 1 op' v))
1✔
1003
    | TruncUF32 -> I64.to_value (trunc_f32_u (F32.of_value 1 op' v))
×
1004
    | TruncSF64 -> I64.to_value (trunc_f64_s (F64.of_value 1 op' v))
1✔
1005
    | TruncUF64 -> I64.to_value (trunc_f64_u (F64.of_value 1 op' v))
×
1006
    | Trunc_sat_f32_s -> I64.to_value (trunc_sat_f32_s (F32.of_value 1 op' v))
×
1007
    | Trunc_sat_f32_u -> I64.to_value (trunc_sat_f32_u (F32.of_value 1 op' v))
×
1008
    | Trunc_sat_f64_s -> I64.to_value (trunc_sat_f64_s (F64.of_value 1 op' v))
×
1009
    | Trunc_sat_f64_u -> I64.to_value (trunc_sat_f64_u (F64.of_value 1 op' v))
×
1010
    | Reinterpret_float -> I64.to_value (F64.of_value 1 op' v)
×
1011
    | WrapI64 ->
×
1012
      raise
1013
        (TypeError
1014
           { index = 1
1015
           ; value = v
1016
           ; ty = Ty_bitv 64
1017
           ; op = `Cvtop WrapI64
1018
           ; msg = "Cannot wrapI64 on an I64"
1019
           } )
1020
    | ToBool | OfBool | _ ->
×
1021
      Fmt.failwith {|cvtop: Unsupported i64 operator "%a"|} Ty.Cvtop.pp op
1022
end
1023

1024
module F32CvtOp = struct
1025
  let demote_f64 x =
1026
    let xf = F64.to_float x in
×
1027
    if Float.Infix.(xf = xf) then F32.of_float xf
×
1028
    else
1029
      let nan64bits = x in
×
1030
      let sign_field =
1031
        Int64.(shift_left (shift_right_logical nan64bits 63) 31)
×
1032
      in
1033
      let significand_field =
1034
        Int64.(shift_right_logical (shift_left nan64bits 12) 41)
×
1035
      in
1036
      let fields = Int64.logor sign_field significand_field in
1037
      Int32.logor 0x7fc0_0000l (Int64.to_int32 fields)
×
1038

1039
  let convert_i32_s x = F32.of_float (Int32.to_float x)
1✔
1040

1041
  let convert_i32_u x =
1042
    F32.of_float
×
1043
      Int32.(
1044
        Int32.Infix.(
1045
          if x >= 0l then to_float x
×
1046
          else to_float (logor (shift_right_logical x 1) (logand x 1l)) *. 2.0 ) )
×
1047

1048
  let convert_i64_s x =
1049
    F32.of_float
1✔
1050
      Int64.(
1051
        Int64.Infix.(
1052
          if abs x < 0x10_0000_0000_0000L then to_float x
1✔
1053
          else
1054
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
1055
            to_float (logor (shift_right x 12) r) *. 0x1p12 ) )
×
1056

1057
  let convert_i64_u x =
1058
    F32.of_float
×
1059
      Int64.(
1060
        Int64.Infix.(
1061
          if I64.lt_u x 0x10_0000_0000_0000L then to_float x
×
1062
          else
1063
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
1064
            to_float (logor (shift_right_logical x 12) r) *. 0x1p12 ) )
×
1065

1066
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
1067
    let op' = `Cvtop op in
2✔
1068
    match op with
1069
    | DemoteF64 -> F32.to_value (demote_f64 (F64.of_value 1 op' v))
×
1070
    | ConvertSI32 -> F32.to_value (convert_i32_s (I32.of_value 1 op' v))
1✔
1071
    | ConvertUI32 -> F32.to_value (convert_i32_u (I32.of_value 1 op' v))
×
1072
    | ConvertSI64 -> F32.to_value (convert_i64_s (I64.of_value 1 op' v))
1✔
1073
    | ConvertUI64 -> F32.to_value (convert_i64_u (I64.of_value 1 op' v))
×
1074
    | Reinterpret_int -> F32.to_value (I32.of_value 1 op' v)
×
1075
    | PromoteF32 ->
×
1076
      raise
1077
        (TypeError
1078
           { index = 1
1079
           ; value = v
1080
           ; ty = Ty_fp 32
1081
           ; op = `Cvtop PromoteF32
1082
           ; msg = "F64 must promote a F32"
1083
           } )
1084
    | ToString | OfString | _ ->
×
1085
      Fmt.failwith {|cvtop: Unsupported f32 operator "%a"|} Ty.Cvtop.pp op
1086
end
1087

1088
module F64CvtOp = struct
1089
  Float.is_nan
1090

1091
  let promote_f32 x =
1092
    let xf = F32.to_float x in
×
1093
    if Float.Infix.(xf = xf) then F64.of_float xf
×
1094
    else
1095
      let nan32bits = I64CvtOp.extend_i32_u x in
×
1096
      let sign_field =
×
1097
        Int64.(shift_left (shift_right_logical nan32bits 31) 63)
×
1098
      in
1099
      let significand_field =
1100
        Int64.(shift_right_logical (shift_left nan32bits 41) 12)
×
1101
      in
1102
      let fields = Int64.logor sign_field significand_field in
1103
      Int64.logor 0x7ff8_0000_0000_0000L fields
×
1104

1105
  let convert_i32_s x = F64.of_float (Int32.to_float x)
1✔
1106

1107
  (*
1108
   * Unlike the other convert_u functions, the high half of the i32 range is
1109
   * within the range where f32 can represent odd numbers, so we can't do the
1110
   * shift. Instead, we can use int64 signed arithmetic.
1111
   *)
1112
  let convert_i32_u x =
1113
    F64.of_float Int64.(to_float (logand (of_int32 x) 0x0000_0000_ffff_ffffL))
×
1114

1115
  let convert_i64_s x = F64.of_float (Int64.to_float x)
1✔
1116

1117
  (*
1118
   * Values in the low half of the int64 range can be converted with a signed
1119
   * conversion. The high half is beyond the range where f64 can represent odd
1120
   * numbers, so we can shift the value right, adjust the least significant
1121
   * bit to round correctly, do a conversion, and then scale it back up.
1122
   *)
1123
  let convert_i64_u (x : int64) =
1124
    F64.of_float
×
1125
      Int64.(
1126
        Int64.Infix.(
1127
          if x >= 0L then to_float x
×
1128
          else to_float (logor (shift_right_logical x 1) (logand x 1L)) *. 2.0 ) )
×
1129

1130
  let cvtop (op : Ty.Cvtop.t) v : Value.t =
1131
    let op' = `Cvtop op in
2✔
1132
    match op with
1133
    | PromoteF32 -> F64.to_value (promote_f32 (F32.of_value 1 op' v))
×
1134
    | ConvertSI32 -> F64.to_value (convert_i32_s (I32.of_value 1 op' v))
1✔
1135
    | ConvertUI32 -> F64.to_value (convert_i32_u (I32.of_value 1 op' v))
×
1136
    | ConvertSI64 -> F64.to_value (convert_i64_s (I64.of_value 1 op' v))
1✔
1137
    | ConvertUI64 -> F64.to_value (convert_i64_u (I64.of_value 1 op' v))
×
1138
    | Reinterpret_int -> F64.to_value (I64.of_value 1 op' v)
×
1139
    | DemoteF64 ->
×
1140
      raise
1141
        (TypeError
1142
           { index = 1
1143
           ; value = v
1144
           ; ty = Ty_bitv 64
1145
           ; op = `Cvtop DemoteF64
1146
           ; msg = "F32 must demote a F64"
1147
           } )
1148
    | ToString | OfString | _ ->
×
1149
      Fmt.failwith {|cvtop: Unsupported f64 operator "%a"|} Ty.Cvtop.pp op
1150
end
1151

1152
(* Dispatch *)
1153

1154
let op int real bool str lst i32 i64 f32 f64 ty op =
1155
  match ty with
99✔
1156
  | Ty.Ty_int -> int op
18✔
1157
  | Ty_real -> real op
15✔
1158
  | Ty_bool -> bool op
2✔
1159
  | Ty_str -> str op
6✔
1160
  | Ty_list -> lst op
7✔
1161
  | Ty_bitv 32 -> i32 op
27✔
1162
  | Ty_bitv 64 -> i64 op
14✔
1163
  | Ty_fp 32 -> f32 op
5✔
1164
  | Ty_fp 64 -> f64 op
5✔
1165
  | Ty_bitv _ | Ty_fp _ | Ty_app | Ty_unit | Ty_none | Ty_regexp -> assert false
1166
[@@inline]
1167

1168
let unop =
1169
  op Int.unop Real.unop Bool.unop Str.unop Lst.unop I32.unop I64.unop F32.unop
1✔
1170
    F64.unop
1171

1172
let binop =
1173
  op Int.binop Real.binop Bool.binop Str.binop Lst.binop I32.binop I64.binop
1✔
1174
    F32.binop F64.binop
1175

1176
let triop = function
1177
  | Ty.Ty_bool -> Bool.triop
×
1178
  | Ty_str -> Str.triop
3✔
1179
  | Ty_list -> Lst.triop
1✔
1180
  | _ -> assert false
1181

1182
let relop = function
1183
  | Ty.Ty_int -> Int.relop
4✔
1184
  | Ty_real -> Real.relop
4✔
1185
  | Ty_bool -> Bool.relop
16✔
1186
  | Ty_str -> Str.relop
8✔
1187
  | Ty_bitv 32 -> I32.relop
16✔
1188
  | Ty_bitv 64 -> I64.relop
8✔
1189
  | Ty_fp 32 -> F32.relop
5✔
1190
  | Ty_fp 64 -> F64.relop
4✔
1191
  | _ -> assert false
1192

1193
let cvtop = function
1194
  | Ty.Ty_int -> Int.cvtop
4✔
1195
  | Ty_real -> Real.cvtop
3✔
1196
  | Ty_bool -> Bool.cvtop
×
1197
  | Ty_str -> Str.cvtop
5✔
1198
  | Ty_bitv 32 -> I32CvtOp.cvtop
2✔
1199
  | Ty_bitv 64 -> I64CvtOp.cvtop
2✔
1200
  | Ty_fp 32 -> F32CvtOp.cvtop
2✔
1201
  | Ty_fp 64 -> F64CvtOp.cvtop
2✔
1202
  | _ -> assert false
1203

1204
let naryop = function
1205
  | Ty.Ty_bool -> Bool.naryop
4✔
1206
  | Ty_str -> Str.naryop
3✔
1207
  | Ty_list -> Lst.naryop
×
1208
  | _ -> 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