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

formalsec / smtml / 309

03 Apr 2025 09:42AM UTC coverage: 48.166% (-1.0%) from 49.165%
309

push

github

filipeom
Prepare release v0.6.2

1589 of 3299 relevant lines covered (48.17%)

32.75 hits per line

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

55.45
/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
exception Value of Ty.t
22

23
(* FIXME: use snake case instead *)
24
exception
25
  TypeError of
26
    { index : int
27
    ; value : Value.t
28
    ; ty : Ty.t
29
    ; op : op_type
30
    }
31

32
(* FIXME: use snake case instead *)
33
exception DivideByZero
34

35
exception Conversion_to_integer
36

37
exception Integer_overflow
38

39
(* FIXME: use snake case instead *)
40
exception IndexOutOfBounds
41

42
(* FIXME: use snake case instead *)
43
exception ParseNumError
44

45
let of_arg f n v op =
46
  try f v
330✔
47
  with Value t -> raise (TypeError { index = n; value = v; ty = t; op })
×
48
[@@inline]
49

50
module Int = struct
51
  let to_value (i : int) : Value.t = Int i [@@inline]
31✔
52

53
  let of_value (n : int) (op : op_type) (v : Value.t) : int =
54
    of_arg (function Int i -> i | _ -> raise_notrace (Value Ty_int)) n v op
×
55
  [@@inline]
56

57
  let str_value (n : int) (op : op_type) (v : Value.t) : string =
58
    of_arg
×
59
      (function Str str -> str | _ -> raise_notrace (Value Ty_str))
×
60
      n v op
61

62
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
63
    let f =
2✔
64
      match op with
65
      | Neg -> Int.neg
1✔
66
      | Not -> Int.lognot
×
67
      | Abs -> Int.abs
1✔
68
      | _ -> Fmt.failwith {|unop: Unsupported int operator "%a"|} Ty.Unop.pp op
×
69
    in
70
    to_value (f (of_value 1 (`Unop op) v))
2✔
71

72
  let exp_by_squaring x n =
73
    let rec exp_by_squaring2 y x n =
1✔
74
      if n < 0 then exp_by_squaring2 y (1 / x) ~-n
×
75
      else if n = 0 then y
1✔
76
      else if n mod 2 = 0 then exp_by_squaring2 y (x * x) (n / 2)
1✔
77
      else begin
1✔
78
        assert (n mod 2 = 1);
1✔
79
        exp_by_squaring2 (x * y) (x * y) ((n - 1) / 2)
80
      end
81
    in
82
    exp_by_squaring2 1 x n
83

84
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
85
    let f =
16✔
86
      match op with
87
      | Add -> Int.add
3✔
88
      | Sub -> Int.sub
1✔
89
      | Mul -> Int.mul
1✔
90
      | Div -> Int.div
1✔
91
      | Rem -> Int.rem
1✔
92
      | Pow -> exp_by_squaring
1✔
93
      | Min -> Int.min
1✔
94
      | Max -> Int.max
1✔
95
      | And -> Int.logand
1✔
96
      | Or -> Int.logor
1✔
97
      | Xor -> Int.logxor
1✔
98
      | Shl -> Int.shift_left
1✔
99
      | ShrL -> Int.shift_right_logical
×
100
      | ShrA -> Int.shift_right
2✔
101
      | _ ->
×
102
        Fmt.failwith {|binop: Unsupported int operator "%a"|} Ty.Binop.pp op
×
103
    in
104
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
16✔
105

106
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
107
    let f =
4✔
108
      match op with
109
      | Lt -> ( < )
1✔
110
      | Le -> ( <= )
1✔
111
      | Gt -> ( > )
1✔
112
      | Ge -> ( >= )
1✔
113
      | Eq -> ( = )
×
114
      | Ne -> ( <> )
×
115
      | _ ->
×
116
        Fmt.failwith {|relop: Unsupported int operator "%a"|} Ty.Relop.pp op
×
117
    in
118
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
4✔
119

120
  let of_bool : Value.t -> int = function
121
    | True -> 1
1✔
122
    | False -> 0
1✔
123
    | _ -> assert false
124
  [@@inline]
125

126
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
127
    match op with
4✔
128
    | OfBool -> to_value (of_bool v)
2✔
129
    | Reinterpret_float ->
1✔
130
      Int (Int.of_float (match v with Real v -> v | _ -> assert false))
1✔
131
    | ToString -> Str (string_of_int (of_value 1 (`Cvtop op) v))
1✔
132
    | OfString -> begin
×
133
      let s = str_value 1 (`Cvtop op) v in
134
      match int_of_string_opt s with
×
135
      | None -> raise ParseNumError
×
136
      | Some i -> Int i
×
137
    end
138
    | _ -> Fmt.failwith {|cvtop: Unsupported int operator "%a"|} Ty.Cvtop.pp op
×
139
end
140

141
module Real = struct
142
  let to_value (v : float) : Value.t = Real v [@@inline]
17✔
143

144
  let of_value (n : int) (op : op_type) (v : Value.t) : float =
145
    of_arg (function Real v -> v | _ -> raise_notrace (Value Ty_int)) n v op
×
146
  [@@inline]
147

148
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
149
    let v = of_value 1 (`Unop op) v in
8✔
150
    match op with
8✔
151
    | Neg -> to_value @@ Float.neg v
1✔
152
    | Abs -> to_value @@ Float.abs v
1✔
153
    | Sqrt -> to_value @@ Float.sqrt v
1✔
154
    | Nearest -> to_value @@ Float.round v
1✔
155
    | Ceil -> to_value @@ Float.ceil v
1✔
156
    | Floor -> to_value @@ Float.floor v
1✔
157
    | Trunc -> to_value @@ Float.trunc v
1✔
158
    | Is_nan -> if Float.is_nan v then Value.True else Value.False
×
159
    | _ -> Fmt.failwith {|unop: Unsupported real operator "%a"|} Ty.Unop.pp op
×
160

161
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
162
    let f =
7✔
163
      match op with
164
      | Add -> Float.add
1✔
165
      | Sub -> Float.sub
1✔
166
      | Mul -> Float.mul
1✔
167
      | Div -> Float.div
1✔
168
      | Rem -> Float.rem
1✔
169
      | Min -> Float.min
1✔
170
      | Max -> Float.max
1✔
171
      | Pow -> Float.pow
×
172
      | _ ->
×
173
        Fmt.failwith {|binop: Unsupported real operator "%a"|} Ty.Binop.pp op
×
174
    in
175
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
7✔
176

177
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
178
    let f =
4✔
179
      match op with
180
      | Lt -> Float.Infix.( < )
1✔
181
      | Le -> Float.Infix.( <= )
1✔
182
      | Gt -> Float.Infix.( > )
1✔
183
      | Ge -> Float.Infix.( >= )
1✔
184
      | Eq -> Float.Infix.( = )
×
185
      | Ne -> Float.Infix.( <> )
×
186
      | _ ->
×
187
        Fmt.failwith {|relop: Unsupported real operator "%a"|} Ty.Relop.pp op
×
188
    in
189
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
4✔
190

191
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
192
    let op' = `Cvtop op in
3✔
193
    match op with
194
    | ToString -> Str (Float.to_string (of_value 1 op' v))
1✔
195
    | OfString ->
1✔
196
      let v = match v with Str v -> v | _ -> raise_notrace (Value Ty_str) in
×
197
      begin
198
        match Float.of_string_opt v with
199
        | None -> assert false
200
        | Some v -> to_value v
1✔
201
      end
202
    | Reinterpret_int ->
1✔
203
      let v = match v with Int v -> v | _ -> raise_notrace (Value Ty_int) in
×
204
      to_value (float_of_int v)
1✔
205
    | Reinterpret_float -> Int (Float.to_int (of_value 1 op' v))
×
206
    | _ -> Fmt.failwith {|cvtop: Unsupported real operator "%a"|} Ty.Cvtop.pp op
×
207
end
208

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

212
  let of_value (n : int) (op : op_type) (v : Value.t) : bool =
213
    of_arg
15✔
214
      (function
215
        | True -> true | False -> false | _ -> raise_notrace (Value Ty_bool) )
×
216
      n v op
217
  [@@inline]
218

219
  let unop (op : Ty.Unop.t) v =
220
    let b = of_value 1 (`Unop op) v in
1✔
221
    match op with
1✔
222
    | Not -> to_value (not b)
1✔
223
    | _ -> Fmt.failwith {|unop: Unsupported bool operator "%a"|} Ty.Unop.pp op
×
224

225
  let xor b1 b2 =
226
    match (b1, b2) with
×
227
    | true, true -> false
×
228
    | true, false -> true
×
229
    | false, true -> true
×
230
    | false, false -> false
×
231

232
  let binop (op : Ty.Binop.t) v1 v2 =
233
    let f =
1✔
234
      match op with
235
      | And -> ( && )
1✔
236
      | Or -> ( || )
×
237
      | Xor -> xor
×
238
      | _ ->
×
239
        Fmt.failwith {|binop: Unsupported bool operator "%a"|} Ty.Binop.pp op
×
240
    in
241
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
1✔
242

243
  let triop (op : Ty.Triop.t) c v1 v2 =
244
    match op with
×
245
    | Ite -> ( match of_value 1 (`Triop op) c with true -> v1 | false -> v2 )
×
246
    | _ -> Fmt.failwith {|triop: Unsupported bool operator "%a"|} Ty.Triop.pp op
×
247

248
  let relop (op : Ty.Relop.t) v1 v2 =
249
    match op with
24✔
250
    | Eq -> Value.equal v1 v2
17✔
251
    | Ne -> not (Value.equal v1 v2)
7✔
252
    | _ -> Fmt.failwith {|relop: Unsupported bool operator "%a"|} Ty.Relop.pp op
×
253

254
  let cvtop _ _ = assert false
×
255

256
  let naryop (op : Ty.Naryop.t) vs =
257
    let b =
4✔
258
      match op with
259
      | Logand ->
2✔
260
        List.fold_left ( && ) true
2✔
261
          (List.mapi (fun i -> of_value i (`Naryop op)) vs)
2✔
262
      | Logor ->
2✔
263
        List.fold_left ( || ) false
2✔
264
          (List.mapi (fun i -> of_value i (`Naryop op)) vs)
2✔
265
      | _ ->
×
266
        Fmt.failwith {|naryop: Unsupported bool operator "%a"|} Ty.Naryop.pp op
×
267
    in
268
    to_value b
269
end
270

271
module Str = struct
272
  let to_value (str : string) : Value.t = Str str [@@inline]
12✔
273

274
  let of_value (n : int) (op : op_type) (v : Value.t) : string =
275
    of_arg
46✔
276
      (function Str str -> str | _ -> raise_notrace (Value Ty_str))
×
277
      n v op
278
  [@@inline]
279

280
  let replace s t t' =
281
    let len_s = String.length s in
1✔
282
    let len_t = String.length t in
1✔
283
    let rec loop i =
1✔
284
      if i >= len_s then s
×
285
      else if i + len_t > len_s then s
×
286
      else if String.equal (String.sub s i len_t) t then
2✔
287
        let s' = Fmt.str "%s%s" (String.sub s 0 i) t' in
1✔
288
        let s'' = String.sub s (i + len_t) (len_s - i - len_t) in
1✔
289
        Fmt.str "%s%s" s' s''
1✔
290
      else loop (i + 1)
1✔
291
    in
292
    loop 0
293

294
  let indexof s sub start =
295
    let len_s = String.length s in
2✔
296
    let len_sub = String.length sub in
2✔
297
    let max_i = len_s - 1 in
2✔
298
    let rec loop i =
299
      if i > max_i then ~-1
×
300
      else if i + len_sub > len_s then ~-1
×
301
      else if String.equal sub (String.sub s i len_sub) then i
2✔
302
      else loop (i + 1)
2✔
303
    in
304
    if start <= 0 then loop 0 else loop start
×
305

306
  let contains s sub = if indexof s sub 0 < 0 then false else true
×
307

308
  let unop (op : Ty.Unop.t) v =
309
    let str = of_value 1 (`Unop op) v in
2✔
310
    match op with
2✔
311
    | Length -> Int.to_value (String.length str)
1✔
312
    | Trim -> to_value (String.trim str)
1✔
313
    | _ -> Fmt.failwith {|unop: Unsupported str operator "%a"|} Ty.Unop.pp op
×
314

315
  let binop (op : Ty.Binop.t) v1 v2 =
316
    let op' = `Binop op in
4✔
317
    let str = of_value 1 op' v1 in
318
    match op with
4✔
319
    | At -> (
1✔
320
      let i = Int.of_value 2 op' v2 in
321
      try to_value (Fmt.str "%c" (String.get str i))
1✔
322
      with Invalid_argument _ -> raise IndexOutOfBounds )
×
323
    | String_prefix ->
1✔
324
      Bool.to_value (String.starts_with ~prefix:str (of_value 2 op' v2))
1✔
325
    | String_suffix ->
1✔
326
      Bool.to_value (String.ends_with ~suffix:str (of_value 2 op' v2))
1✔
327
    | String_contains -> Bool.to_value (contains str (of_value 2 op' v2))
1✔
328
    | _ -> Fmt.failwith {|binop: Unsupported str operator "%a"|} Ty.Binop.pp op
×
329

330
  let triop (op : Ty.Triop.t) v1 v2 v3 =
331
    let op' = `Triop op in
3✔
332
    let str = of_value 1 op' v1 in
333
    match op with
3✔
334
    | String_extract ->
1✔
335
      let i = Int.of_value 2 op' v2 in
336
      let len = Int.of_value 3 op' v3 in
1✔
337
      to_value (String.sub str i len)
1✔
338
    | String_replace ->
1✔
339
      let t = of_value 2 op' v2 in
340
      let t' = of_value 2 op' v3 in
1✔
341
      to_value (replace str t t')
1✔
342
    | String_index ->
1✔
343
      let t = of_value 2 op' v2 in
344
      let i = Int.of_value 3 op' v3 in
1✔
345
      Int.to_value (indexof str t i)
1✔
346
    | _ -> Fmt.failwith {|triop: Unsupported str operator "%a"|} Ty.Triop.pp op
×
347

348
  let relop (op : Ty.Relop.t) v1 v2 =
349
    let f =
8✔
350
      match op with
351
      | Lt -> ( < )
1✔
352
      | Le -> ( <= )
1✔
353
      | Gt -> ( > )
1✔
354
      | Ge -> ( >= )
1✔
355
      | Eq -> ( = )
2✔
356
      | Ne -> ( <> )
2✔
357
      | _ ->
×
358
        Fmt.failwith {|relop: Unsupported string operator "%a"|} Ty.Relop.pp op
×
359
    in
360
    let f x y = f (String.compare x y) 0 in
8✔
361
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
8✔
362

363
  let cvtop (op : Ty.Cvtop.t) v =
364
    let op' = `Cvtop op in
14✔
365
    match op with
366
    | String_to_code ->
1✔
367
      let str = of_value 1 op' v in
368
      Int.to_value (Char.code str.[0])
1✔
369
    | String_from_code ->
1✔
370
      let code = Int.of_value 1 op' v in
371
      to_value (String.make 1 (Char.chr code))
1✔
372
    | String_to_int ->
7✔
373
      let s = of_value 1 op' v in
374
      let i =
7✔
375
        match int_of_string_opt s with
376
        | None -> raise ParseNumError
×
377
        | Some i -> i
7✔
378
      in
379
      Int.to_value i
380
    | String_from_int -> to_value (string_of_int (Int.of_value 1 op' v))
4✔
381
    | String_to_float ->
1✔
382
      let s = of_value 1 op' v in
383
      let f =
1✔
384
        match float_of_string_opt s with
385
        | None -> raise ParseNumError
×
386
        | Some f -> f
1✔
387
      in
388
      Real.to_value f
389
    | _ -> Fmt.failwith {|cvtop: Unsupported str operator "%a"|} Ty.Cvtop.pp op
×
390

391
  let naryop (op : Ty.Naryop.t) vs =
392
    let op' = `Naryop op in
3✔
393
    match op with
394
    | Concat -> to_value (String.concat "" (List.map (of_value 0 op') vs))
3✔
395
    | _ ->
×
396
      Fmt.failwith {|naryop: Unsupported str operator "%a"|} Ty.Naryop.pp op
397
end
398

399
module Lst = struct
400
  let of_value (n : int) (op : op_type) (v : Value.t) : Value.t list =
401
    of_arg
9✔
402
      (function List lst -> lst | _ -> raise_notrace (Value Ty_list))
×
403
      n v op
404
  [@@inline]
405

406
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
407
    let lst = of_value 1 (`Unop op) v in
4✔
408
    match op with
4✔
409
    | Head -> begin match lst with hd :: _tl -> hd | [] -> assert false end
1✔
410
    | Tail -> begin
1✔
411
      match lst with _hd :: tl -> List tl | [] -> assert false
1✔
412
    end
413
    | Length -> Int.to_value (List.length lst)
1✔
414
    | Reverse -> List (List.rev lst)
1✔
415
    | _ -> Fmt.failwith {|unop: Unsupported list operator "%a"|} Ty.Unop.pp op
×
416

417
  let binop (op : Ty.Binop.t) v1 v2 =
418
    let op' = `Binop op in
3✔
419
    match op with
420
    | At ->
1✔
421
      let lst = of_value 1 op' v1 in
422
      let i = Int.of_value 2 op' v2 in
1✔
423
      begin
1✔
424
        (* TODO: change datastructure? *)
425
        match List.nth_opt lst i with
426
        | None -> raise IndexOutOfBounds
×
427
        | Some v -> v
1✔
428
      end
429
    | List_cons -> List (v1 :: of_value 1 op' v2)
1✔
430
    | List_append -> List (of_value 1 op' v1 @ of_value 2 op' v2)
1✔
431
    | _ -> Fmt.failwith {|binop: Unsupported list operator "%a"|} Ty.Binop.pp op
×
432

433
  let triop (op : Ty.Triop.t) (v1 : Value.t) (v2 : Value.t) (v3 : Value.t) :
434
    Value.t =
435
    let op' = `Triop op in
1✔
436
    match op with
437
    | List_set ->
1✔
438
      let lst = of_value 1 op' v1 in
439
      let i = Int.of_value 2 op' v2 in
1✔
440
      let rec set i lst v acc =
1✔
441
        match (i, lst) with
2✔
442
        | 0, _ :: tl -> List.rev_append acc (v :: tl)
1✔
443
        | i, hd :: tl -> set (i - 1) tl v (hd :: acc)
1✔
444
        | _, [] -> raise IndexOutOfBounds
×
445
      in
446
      List (set i lst v3 [])
1✔
447
    | _ -> Fmt.failwith {|triop: Unsupported list operator "%a"|} Ty.Triop.pp op
×
448

449
  let naryop (op : Ty.Naryop.t) (vs : Value.t list) : Value.t =
450
    let op' = `Naryop op in
×
451
    match op with
452
    | Concat -> List (List.concat_map (of_value 0 op') vs)
×
453
    | _ ->
×
454
      Fmt.failwith {|naryop: Unsupported list operator "%a"|} Ty.Naryop.pp op
455
end
456

457
module I32 = struct
458
  let to_value (i : int32) : Value.t = Num (I32 i) [@@inline]
29✔
459

460
  let of_value (n : int) (op : op_type) (v : Value.t) : int32 =
461
    of_arg
86✔
462
      (function Num (I32 i) -> i | _ -> raise_notrace (Value (Ty_bitv 32)))
×
463
      n v op
464
  [@@inline]
465

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

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

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

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

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

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

478
  let shl x y = shift Int32.shift_left x y [@@inline]
3✔
479

480
  let shr_s x y = shift Int32.shift_right x y [@@inline]
1✔
481

482
  let shr_u x y = shift Int32.shift_right_logical x y [@@inline]
2✔
483

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

489
  let rotl x y =
490
    let n = clamp_rotate_count y in
1✔
491
    Int32.logor (shl x (Int32.of_int n)) (shr_u x (Int32.of_int (32 - n)))
1✔
492
  [@@inline]
493

494
  let rotr x y =
495
    let n = clamp_rotate_count y in
1✔
496
    Int32.logor (shr_u x (Int32.of_int n)) (shl x (Int32.of_int (32 - n)))
1✔
497
  [@@inline]
498

499
  let clz n =
500
    let n = Ocaml_intrinsics.Int32.count_leading_zeros n in
×
501
    Int32.of_int n
×
502

503
  let ctz n =
504
    let n = Ocaml_intrinsics.Int32.count_trailing_zeros n in
×
505
    Int32.of_int n
×
506

507
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
508
    let f =
2✔
509
      match op with
510
      | Neg -> Int32.neg
1✔
511
      | Not -> Int32.lognot
1✔
512
      | Clz -> clz
×
513
      | Ctz -> ctz
×
514
      | _ -> Fmt.failwith {|unop: Unsupported i32 operator "%a"|} Ty.Unop.pp op
×
515
    in
516
    to_value (f (of_value 1 (`Unop op) v))
2✔
517

518
  let binop op v1 v2 =
519
    let f =
25✔
520
      match op with
521
      | Ty.Binop.Add -> Int32.add
9✔
522
      | Sub -> Int32.sub
3✔
523
      | Mul -> Int32.mul
3✔
524
      | Div -> Int32.div
1✔
525
      | DivU -> Int32.unsigned_div
×
526
      | Rem -> Int32.rem
2✔
527
      | RemU -> Int32.unsigned_rem
×
528
      | And -> Int32.logand
1✔
529
      | Or -> Int32.logor
1✔
530
      | Xor -> Int32.logxor
1✔
531
      | Shl -> shl
1✔
532
      | ShrL -> shr_u
×
533
      | ShrA -> shr_s
1✔
534
      | Rotl -> rotl
1✔
535
      | Rotr -> rotr
1✔
536
      | _ ->
×
537
        Fmt.failwith {|binop: Unsupported i32 operator "%a"|} Ty.Binop.pp op
×
538
    in
539
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
25✔
540

541
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
542
    let f =
16✔
543
      match op with
544
      | Lt -> Int32.Infix.( < )
2✔
545
      | LtU -> lt_u
2✔
546
      | Le -> Int32.Infix.( <= )
2✔
547
      | LeU -> le_u
2✔
548
      | Gt -> Int32.Infix.( > )
2✔
549
      | GtU -> gt_u
2✔
550
      | Ge -> Int32.Infix.( >= )
2✔
551
      | GeU -> ge_u
2✔
552
      | Eq | Ne -> assert false
553
    in
554
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
16✔
555
end
556

557
module I64 = struct
558
  let to_value (i : int64) : Value.t = Num (I64 i) [@@inline]
16✔
559

560
  let of_value (n : int) (op : op_type) (v : Value.t) : int64 =
561
    of_arg
44✔
562
      (function Num (I64 i) -> i | _ -> raise_notrace (Value (Ty_bitv 64)))
×
563
      n v op
564
  [@@inline]
565

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

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

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

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

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

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

578
  let shl x y = shift Int64.shift_left x y [@@inline]
3✔
579

580
  let shr_s x y = shift Int64.shift_right x y [@@inline]
1✔
581

582
  let shr_u x y = shift Int64.shift_right_logical x y [@@inline]
2✔
583

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

589
  let rotl x y =
590
    let n = clamp_rotate_count y in
1✔
591
    Int64.logor (shl x (Int64.of_int n)) (shr_u x (Int64.of_int (64 - n)))
1✔
592
  [@@inline]
593

594
  let rotr x y =
595
    let n = clamp_rotate_count y in
1✔
596
    Int64.logor (shr_u x (Int64.of_int n)) (shl x (Int64.of_int (64 - n)))
1✔
597
  [@@inline]
598

599
  let clz n =
600
    let n = Ocaml_intrinsics.Int64.count_leading_zeros n in
×
601
    Int64.of_int n
×
602

603
  let ctz n =
604
    let n = Ocaml_intrinsics.Int64.count_trailing_zeros n in
×
605
    Int64.of_int n
×
606

607
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
608
    let f =
2✔
609
      match op with
610
      | Neg -> Int64.neg
1✔
611
      | Not -> Int64.lognot
1✔
612
      | Clz -> clz
×
613
      | Ctz -> ctz
×
614
      | _ -> Fmt.failwith {|unop: Unsupported i64 operator "%a"|} Ty.Unop.pp op
×
615
    in
616
    to_value (f (of_value 1 (`Unop op) v))
2✔
617

618
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
619
    let f =
12✔
620
      match op with
621
      | Add -> Int64.add
1✔
622
      | Sub -> Int64.sub
1✔
623
      | Mul -> Int64.mul
1✔
624
      | Div -> Int64.div
1✔
625
      | DivU -> Int64.unsigned_div
×
626
      | Rem -> Int64.rem
1✔
627
      | RemU -> Int64.unsigned_rem
×
628
      | And -> Int64.logand
1✔
629
      | Or -> Int64.logor
1✔
630
      | Xor -> Int64.logxor
1✔
631
      | Shl -> shl
1✔
632
      | ShrL -> shr_u
×
633
      | ShrA -> shr_s
1✔
634
      | Rotl -> rotl
1✔
635
      | Rotr -> rotr
1✔
636
      | _ ->
×
637
        Fmt.failwith {|binop: Unsupported i64 operator "%a"|} Ty.Binop.pp op
×
638
    in
639
    to_value (f (of_value 1 (`Binop op) v1) (of_value 2 (`Binop op) v2))
12✔
640

641
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
642
    let f =
8✔
643
      match op with
644
      | Lt -> Int64.Infix.( < )
1✔
645
      | LtU -> lt_u
1✔
646
      | Le -> Int64.Infix.( <= )
1✔
647
      | LeU -> le_u
1✔
648
      | Gt -> Int64.Infix.( > )
1✔
649
      | GtU -> gt_u
1✔
650
      | Ge -> Int64.Infix.( >= )
1✔
651
      | GeU -> ge_u
1✔
652
      | Eq | Ne -> assert false
653
    in
654
    f (of_value 1 (`Relop op) v1) (of_value 2 (`Relop op) v2)
8✔
655
end
656

657
module F32 = struct
658
  let to_float (v : int32) : float = Int32.float_of_bits v [@@inline]
24✔
659

660
  let of_float (v : float) : int32 = Int32.bits_of_float v [@@inline]
10✔
661

662
  let to_value (f : int32) : Value.t = Num (F32 f) [@@inline]
10✔
663

664
  let to_value' (f : float) : Value.t = to_value @@ of_float f [@@inline]
8✔
665

666
  let of_value (i : int) (op : op_type) (v : Value.t) : int32 =
667
    of_arg
24✔
668
      (function Num (F32 f) -> f | _ -> raise_notrace (Value (Ty_fp 32)))
×
669
      i v op
670
  [@@inline]
671

672
  let of_value' (i : int) (op : op_type) (v : Value.t) : float =
673
    of_value i op v |> to_float
18✔
674
  [@@inline]
675

676
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
677
    let v = to_float @@ of_value 1 (`Unop op) v in
4✔
678
    match op with
4✔
679
    | Neg -> to_value' @@ Float.neg v
1✔
680
    | Abs -> to_value' @@ Float.abs v
×
681
    | Sqrt -> to_value' @@ Float.sqrt v
2✔
682
    | Nearest -> to_value' @@ Float.round v
×
683
    | Ceil -> to_value' @@ Float.ceil v
×
684
    | Floor -> to_value' @@ Float.floor v
×
685
    | Trunc -> to_value' @@ Float.trunc v
1✔
686
    | Is_nan -> if Float.is_nan v then Value.True else Value.False
×
687
    | _ -> Fmt.failwith {|unop: Unsupported f32 operator "%a"|} Ty.Unop.pp op
×
688

689
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
690
    let f =
4✔
691
      match op with
692
      | Add -> Float.add
×
693
      | Sub -> Float.sub
×
694
      | Mul -> Float.mul
×
695
      | Div -> Float.div
×
696
      | Rem -> Float.rem
×
697
      | Min -> Float.min
×
698
      | Max -> Float.max
×
699
      | Copysign -> Float.copy_sign
4✔
700
      | _ ->
×
701
        Fmt.failwith {|binop: Unsupported f32 operator "%a"|} Ty.Binop.pp op
×
702
    in
703
    to_value' (f (of_value' 1 (`Binop op) v1) (of_value' 2 (`Binop op) v2))
4✔
704

705
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
706
    let f =
5✔
707
      match op with
708
      | Eq -> Float.Infix.( = )
1✔
709
      | Ne -> Float.Infix.( <> )
×
710
      | Lt -> Float.Infix.( < )
1✔
711
      | Le -> Float.Infix.( <= )
1✔
712
      | Gt -> Float.Infix.( > )
1✔
713
      | Ge -> Float.Infix.( >= )
1✔
714
      | _ ->
×
715
        Fmt.failwith {|relop: Unsupported f32 operator "%a"|} Ty.Relop.pp op
×
716
    in
717
    f (of_value' 1 (`Relop op) v1) (of_value' 2 (`Relop op) v2)
5✔
718
end
719

720
module F64 = struct
721
  let to_float (v : int64) : float = Int64.float_of_bits v [@@inline]
21✔
722

723
  let of_float (v : float) : int64 = Int64.bits_of_float v [@@inline]
8✔
724

725
  let to_value (f : int64) : Value.t = Num (F64 f) [@@inline]
8✔
726

727
  let to_value' (f : float) : Value.t = to_value @@ of_float f [@@inline]
6✔
728

729
  let of_value (i : int) (op : op_type) (v : Value.t) : int64 =
730
    of_arg
21✔
731
      (function Num (F64 f) -> f | _ -> raise_notrace (Value (Ty_fp 64)))
×
732
      i v op
733
  [@@inline]
734

735
  let of_value' (i : int) (op : op_type) (v : Value.t) : float =
736
    of_value i op v |> to_float
19✔
737
  [@@inline]
738

739
  let unop (op : Ty.Unop.t) (v : Value.t) : Value.t =
740
    let v = of_value' 1 (`Unop op) v in
1✔
741
    match op with
1✔
742
    | Neg -> to_value' @@ Float.neg v
×
743
    | Abs -> to_value' @@ Float.abs v
×
744
    | Sqrt -> to_value' @@ Float.sqrt v
×
745
    | Nearest -> to_value' @@ Float.round v
×
746
    | Ceil -> to_value' @@ Float.ceil v
×
747
    | Floor -> to_value' @@ Float.floor v
×
748
    | Trunc -> to_value' @@ Float.trunc v
1✔
749
    | Is_nan -> if Float.is_nan v then Value.True else Value.False
×
750
    | _ -> Fmt.failwith {|unop: Unsupported f32 operator "%a"|} Ty.Unop.pp op
×
751

752
  let binop (op : Ty.Binop.t) (v1 : Value.t) (v2 : Value.t) : Value.t =
753
    let f =
5✔
754
      match op with
755
      | Add -> Float.add
×
756
      | Sub -> Float.sub
×
757
      | Mul -> Float.mul
×
758
      | Div -> Float.div
1✔
759
      | Rem -> Float.rem
×
760
      | Min -> Float.min
×
761
      | Max -> Float.max
×
762
      | Copysign -> Float.copy_sign
4✔
763
      | _ ->
×
764
        Fmt.failwith {|binop: Unsupported f32 operator "%a"|} Ty.Binop.pp op
×
765
    in
766
    to_value' (f (of_value' 1 (`Binop op) v1) (of_value' 2 (`Binop op) v2))
5✔
767

768
  let relop (op : Ty.Relop.t) (v1 : Value.t) (v2 : Value.t) : bool =
769
    let f =
4✔
770
      match op with
771
      | Eq -> Float.Infix.( = )
×
772
      | Ne -> Float.Infix.( <> )
×
773
      | Lt -> Float.Infix.( < )
1✔
774
      | Le -> Float.Infix.( <= )
1✔
775
      | Gt -> Float.Infix.( > )
1✔
776
      | Ge -> Float.Infix.( >= )
1✔
777
      | _ ->
×
778
        Fmt.failwith {|relop: Unsupported f32 operator "%a"|} Ty.Relop.pp op
×
779
    in
780
    f (of_value' 1 (`Relop op) v1) (of_value' 2 (`Relop op) v2)
4✔
781
end
782

783
module I32CvtOp = struct
784
  let extend_s (n : int) (x : int32) : int32 =
785
    let shift = 32 - n in
×
786
    Int32.(shift_right (shift_left x shift) shift)
×
787

788
  let trunc_f32_s (x : int32) =
789
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
790
    else
791
      let xf = F32.to_float x in
1✔
792
      if
1✔
793
        Float.Infix.(
794
          xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) )
×
795
      then raise Integer_overflow
×
796
      else Int32.of_float xf
1✔
797

798
  let trunc_f32_u (x : int32) =
799
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
800
    else
801
      let xf = F32.to_float x in
×
802
      if Float.Infix.(xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0)
×
803
      then raise Integer_overflow
×
804
      else Int32.of_float xf
×
805

806
  let trunc_f64_s (x : int64) =
807
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
808
    else
809
      let xf = F64.to_float x in
1✔
810
      if
1✔
811
        Float.Infix.(
812
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
813
      then raise Integer_overflow
×
814
      else Int32.of_float xf
1✔
815

816
  let trunc_f64_u (x : int64) =
817
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
818
    else
819
      let xf = F64.to_float x in
×
820
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
821
      then raise Integer_overflow
×
822
      else Int32.of_float xf
×
823

824
  let trunc_sat_f32_s x =
825
    if Int32.Infix.(x <> x) then 0l
×
826
    else
827
      let xf = F32.to_float x in
×
828
      if Float.Infix.(xf < Int32.(to_float min_int)) then Int32.min_int
×
829
      else if Float.Infix.(xf >= -.Int32.(to_float min_int)) then Int32.max_int
×
830
      else Int32.of_float xf
×
831

832
  let trunc_sat_f32_u x =
833
    if Int32.Infix.(x <> x) then 0l
×
834
    else
835
      let xf = F32.to_float x in
×
836
      if Float.Infix.(xf <= -1.0) then 0l
×
837
      else if Float.Infix.(xf >= -.Int32.(to_float min_int) *. 2.0) then -1l
×
838
      else Int32.of_float xf
×
839

840
  let trunc_sat_f64_s x =
841
    if Int64.Infix.(x <> x) then 0l
×
842
    else
843
      let xf = F64.to_float x in
×
844
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int32.min_int
×
845
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int32.max_int
×
846
      else Int32.of_float xf
×
847

848
  let trunc_sat_f64_u x =
849
    if Int64.Infix.(x <> x) then 0l
×
850
    else
851
      let xf = F64.to_float x in
×
852
      if Float.Infix.(xf <= -1.0) then 0l
×
853
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1l
×
854
      else Int32.of_float xf
×
855

856
  let cvtop op v =
857
    let op' = `Cvtop op in
2✔
858
    match op with
859
    | Ty.Cvtop.WrapI64 -> I32.to_value (Int64.to_int32 (I64.of_value 1 op' v))
×
860
    | TruncSF32 -> I32.to_value (trunc_f32_s (F32.of_value 1 op' v))
1✔
861
    | TruncUF32 -> I32.to_value (trunc_f32_u (F32.of_value 1 op' v))
×
862
    | TruncSF64 -> I32.to_value (trunc_f64_s (F64.of_value 1 op' v))
1✔
863
    | TruncUF64 -> I32.to_value (trunc_f64_u (F64.of_value 1 op' v))
×
864
    | Trunc_sat_f32_s -> I32.to_value (trunc_sat_f32_s (F32.of_value 1 op' v))
×
865
    | Trunc_sat_f32_u -> I32.to_value (trunc_sat_f32_u (F32.of_value 1 op' v))
×
866
    | Trunc_sat_f64_s -> I32.to_value (trunc_sat_f64_s (F64.of_value 1 op' v))
×
867
    | Trunc_sat_f64_u -> I32.to_value (trunc_sat_f64_u (F64.of_value 1 op' v))
×
868
    | Reinterpret_float -> I32.to_value (F32.of_value 1 op' v)
×
869
    | Sign_extend n -> I32.to_value (extend_s n (I32.of_value 1 op' v))
×
870
    | Zero_extend _n -> I32.to_value (I32.of_value 1 op' v)
×
871
    | OfBool -> v (* already a num here *)
×
872
    | ToBool | _ ->
×
873
      Fmt.failwith {|cvtop: Unsupported i32 operator "%a"|} Ty.Cvtop.pp op
874
end
875

876
module I64CvtOp = struct
877
  (* let extend_s n x = *)
878
  (*   let shift = 64 - n in *)
879
  (*   Int64.(shift_right (shift_left x shift) shift) *)
880

881
  let extend_i32_u (x : int32) =
882
    Int64.(logand (of_int32 x) 0x0000_0000_ffff_ffffL)
×
883

884
  let trunc_f32_s (x : int32) =
885
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
886
    else
887
      let xf = F32.to_float x in
1✔
888
      if
1✔
889
        Float.Infix.(
890
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
891
      then raise Integer_overflow
×
892
      else Int64.of_float xf
1✔
893

894
  let trunc_f32_u (x : int32) =
895
    if Int32.Infix.(x <> x) then raise Conversion_to_integer
×
896
    else
897
      let xf = F32.to_float x in
×
898
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
899
      then raise Integer_overflow
×
900
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
×
901
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
902
      else Int64.of_float xf
×
903

904
  let trunc_f64_s (x : int64) =
905
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
906
    else
907
      let xf = F64.to_float x in
1✔
908
      if
1✔
909
        Float.Infix.(
910
          xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) )
×
911
      then raise Integer_overflow
×
912
      else Int64.of_float xf
1✔
913

914
  let trunc_f64_u (x : int64) =
915
    if Int64.Infix.(x <> x) then raise Conversion_to_integer
×
916
    else
917
      let xf = F64.to_float x in
×
918
      if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0)
×
919
      then raise Integer_overflow
×
920
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
×
921
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
922
      else Int64.of_float xf
×
923

924
  let trunc_sat_f32_s (x : int32) =
925
    if Int32.Infix.(x <> x) then 0L
×
926
    else
927
      let xf = F32.to_float x in
×
928
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
929
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
930
      else Int64.of_float xf
×
931

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

942
  let trunc_sat_f64_s (x : int64) =
943
    if Int64.Infix.(x <> x) then 0L
×
944
    else
945
      let xf = F64.to_float x in
×
946
      if Float.Infix.(xf < Int64.(to_float min_int)) then Int64.min_int
×
947
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then Int64.max_int
×
948
      else Int64.of_float xf
×
949

950
  let trunc_sat_f64_u (x : int64) =
951
    if Int64.Infix.(x <> x) then 0L
×
952
    else
953
      let xf = F64.to_float x in
×
954
      if Float.Infix.(xf <= -1.0) then 0L
×
955
      else if Float.Infix.(xf >= -.Int64.(to_float min_int) *. 2.0) then -1L
×
956
      else if Float.Infix.(xf >= -.Int64.(to_float min_int)) then
×
957
        Int64.(logxor (of_float (xf -. 0x1p63)) min_int)
×
958
      else Int64.of_float xf
×
959

960
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
961
    let op' = `Cvtop op in
2✔
962
    match op with
963
    | Sign_extend 32 -> I64.to_value (Int64.of_int32 (I32.of_value 1 op' v))
×
964
    | Zero_extend 32 -> I64.to_value (extend_i32_u (I32.of_value 1 op' v))
×
965
    | TruncSF32 -> I64.to_value (trunc_f32_s (F32.of_value 1 op' v))
1✔
966
    | TruncUF32 -> I64.to_value (trunc_f32_u (F32.of_value 1 op' v))
×
967
    | TruncSF64 -> I64.to_value (trunc_f64_s (F64.of_value 1 op' v))
1✔
968
    | TruncUF64 -> I64.to_value (trunc_f64_u (F64.of_value 1 op' v))
×
969
    | Trunc_sat_f32_s -> I64.to_value (trunc_sat_f32_s (F32.of_value 1 op' v))
×
970
    | Trunc_sat_f32_u -> I64.to_value (trunc_sat_f32_u (F32.of_value 1 op' v))
×
971
    | Trunc_sat_f64_s -> I64.to_value (trunc_sat_f64_s (F64.of_value 1 op' v))
×
972
    | Trunc_sat_f64_u -> I64.to_value (trunc_sat_f64_u (F64.of_value 1 op' v))
×
973
    | Reinterpret_float -> I64.to_value (F64.of_value 1 op' v)
×
974
    | WrapI64 ->
×
975
      raise
976
        (TypeError
977
           { index = 1; value = v; ty = Ty_bitv 64; op = `Cvtop WrapI64 } )
978
    | ToBool | OfBool | _ ->
×
979
      Fmt.failwith {|cvtop: Unsupported i64 operator "%a"|} Ty.Cvtop.pp op
980
end
981

982
module F32CvtOp = struct
983
  let demote_f64 x =
984
    let xf = F64.to_float x in
×
985
    if Float.Infix.(xf = xf) then F32.of_float xf
×
986
    else
987
      let nan64bits = x in
×
988
      let sign_field =
989
        Int64.(shift_left (shift_right_logical nan64bits 63) 31)
×
990
      in
991
      let significand_field =
992
        Int64.(shift_right_logical (shift_left nan64bits 12) 41)
×
993
      in
994
      let fields = Int64.logor sign_field significand_field in
995
      Int32.logor 0x7fc0_0000l (Int64.to_int32 fields)
×
996

997
  let convert_i32_s x = F32.of_float (Int32.to_float x)
1✔
998

999
  let convert_i32_u x =
1000
    F32.of_float
×
1001
      Int32.(
1002
        Int32.Infix.(
1003
          if x >= 0l then to_float x
×
1004
          else to_float (logor (shift_right_logical x 1) (logand x 1l)) *. 2.0 ) )
×
1005

1006
  let convert_i64_s x =
1007
    F32.of_float
1✔
1008
      Int64.(
1009
        Int64.Infix.(
1010
          if abs x < 0x10_0000_0000_0000L then to_float x
1✔
1011
          else
1012
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
1013
            to_float (logor (shift_right x 12) r) *. 0x1p12 ) )
×
1014

1015
  let convert_i64_u x =
1016
    F32.of_float
×
1017
      Int64.(
1018
        Int64.Infix.(
1019
          if I64.lt_u x 0x10_0000_0000_0000L then to_float x
×
1020
          else
1021
            let r = if logand x 0xfffL = 0L then 0L else 1L in
×
1022
            to_float (logor (shift_right_logical x 12) r) *. 0x1p12 ) )
×
1023

1024
  let cvtop (op : Ty.Cvtop.t) (v : Value.t) : Value.t =
1025
    let op' = `Cvtop op in
2✔
1026
    match op with
1027
    | DemoteF64 -> F32.to_value (demote_f64 (F64.of_value 1 op' v))
×
1028
    | ConvertSI32 -> F32.to_value (convert_i32_s (I32.of_value 1 op' v))
1✔
1029
    | ConvertUI32 -> F32.to_value (convert_i32_u (I32.of_value 1 op' v))
×
1030
    | ConvertSI64 -> F32.to_value (convert_i64_s (I64.of_value 1 op' v))
1✔
1031
    | ConvertUI64 -> F32.to_value (convert_i64_u (I64.of_value 1 op' v))
×
1032
    | Reinterpret_int -> F32.to_value (I32.of_value 1 op' v)
×
1033
    | PromoteF32 ->
×
1034
      raise
1035
        (TypeError
1036
           { index = 1; value = v; ty = Ty_fp 32; op = `Cvtop PromoteF32 } )
1037
    | ToString | OfString | _ ->
×
1038
      Fmt.failwith {|cvtop: Unsupported f32 operator "%a"|} Ty.Cvtop.pp op
1039
end
1040

1041
module F64CvtOp = struct
1042
  Float.is_nan
1043

1044
  let promote_f32 x =
1045
    let xf = F32.to_float x in
×
1046
    if Float.Infix.(xf = xf) then F64.of_float xf
×
1047
    else
1048
      let nan32bits = I64CvtOp.extend_i32_u x in
×
1049
      let sign_field =
×
1050
        Int64.(shift_left (shift_right_logical nan32bits 31) 63)
×
1051
      in
1052
      let significand_field =
1053
        Int64.(shift_right_logical (shift_left nan32bits 41) 12)
×
1054
      in
1055
      let fields = Int64.logor sign_field significand_field in
1056
      Int64.logor 0x7ff8_0000_0000_0000L fields
×
1057

1058
  let convert_i32_s x = F64.of_float (Int32.to_float x)
1✔
1059

1060
  (*
1061
   * Unlike the other convert_u functions, the high half of the i32 range is
1062
   * within the range where f32 can represent odd numbers, so we can't do the
1063
   * shift. Instead, we can use int64 signed arithmetic.
1064
   *)
1065
  let convert_i32_u x =
1066
    F64.of_float Int64.(to_float (logand (of_int32 x) 0x0000_0000_ffff_ffffL))
×
1067

1068
  let convert_i64_s x = F64.of_float (Int64.to_float x)
1✔
1069

1070
  (*
1071
   * Values in the low half of the int64 range can be converted with a signed
1072
   * conversion. The high half is beyond the range where f64 can represent odd
1073
   * numbers, so we can shift the value right, adjust the least significant
1074
   * bit to round correctly, do a conversion, and then scale it back up.
1075
   *)
1076
  let convert_i64_u (x : int64) =
1077
    F64.of_float
×
1078
      Int64.(
1079
        Int64.Infix.(
1080
          if x >= 0L then to_float x
×
1081
          else to_float (logor (shift_right_logical x 1) (logand x 1L)) *. 2.0 ) )
×
1082

1083
  let cvtop (op : Ty.Cvtop.t) v : Value.t =
1084
    let op' = `Cvtop op in
2✔
1085
    match op with
1086
    | PromoteF32 -> F64.to_value (promote_f32 (F32.of_value 1 op' v))
×
1087
    | ConvertSI32 -> F64.to_value (convert_i32_s (I32.of_value 1 op' v))
1✔
1088
    | ConvertUI32 -> F64.to_value (convert_i32_u (I32.of_value 1 op' v))
×
1089
    | ConvertSI64 -> F64.to_value (convert_i64_s (I64.of_value 1 op' v))
1✔
1090
    | ConvertUI64 -> F64.to_value (convert_i64_u (I64.of_value 1 op' v))
×
1091
    | Reinterpret_int -> F64.to_value (I64.of_value 1 op' v)
×
1092
    | DemoteF64 ->
×
1093
      raise
1094
        (TypeError
1095
           { index = 1; value = v; ty = Ty_bitv 64; op = `Cvtop DemoteF64 } )
1096
    | ToString | OfString | _ ->
×
1097
      Fmt.failwith {|cvtop: Unsupported f64 operator "%a"|} Ty.Cvtop.pp op
1098
end
1099

1100
(* Dispatch *)
1101

1102
let op int real bool str lst i32 i64 f32 f64 ty op =
1103
  match ty with
103✔
1104
  | Ty.Ty_int -> int op
18✔
1105
  | Ty_real -> real op
15✔
1106
  | Ty_bool -> bool op
2✔
1107
  | Ty_str -> str op
6✔
1108
  | Ty_list -> lst op
7✔
1109
  | Ty_bitv 32 -> i32 op
27✔
1110
  | Ty_bitv 64 -> i64 op
14✔
1111
  | Ty_fp 32 -> f32 op
8✔
1112
  | Ty_fp 64 -> f64 op
6✔
1113
  | Ty_bitv _ | Ty_fp _ | Ty_app | Ty_unit | Ty_none | Ty_regexp -> assert false
1114
[@@inline]
1115

1116
let unop =
1117
  op Int.unop Real.unop Bool.unop Str.unop Lst.unop I32.unop I64.unop F32.unop
37✔
1118
    F64.unop
1119

1120
let binop =
1121
  op Int.binop Real.binop Bool.binop Str.binop Lst.binop I32.binop I64.binop
37✔
1122
    F32.binop F64.binop
1123

1124
let triop = function
1125
  | Ty.Ty_bool -> Bool.triop
×
1126
  | Ty_str -> Str.triop
3✔
1127
  | Ty_list -> Lst.triop
1✔
1128
  | _ -> assert false
1129

1130
let relop = function
1131
  | Ty.Ty_int -> Int.relop
4✔
1132
  | Ty_real -> Real.relop
4✔
1133
  | Ty_bool -> Bool.relop
24✔
1134
  | Ty_str -> Str.relop
8✔
1135
  | Ty_bitv 32 -> I32.relop
16✔
1136
  | Ty_bitv 64 -> I64.relop
8✔
1137
  | Ty_fp 32 -> F32.relop
5✔
1138
  | Ty_fp 64 -> F64.relop
4✔
1139
  | _ -> assert false
1140

1141
let cvtop = function
1142
  | Ty.Ty_int -> Int.cvtop
4✔
1143
  | Ty_real -> Real.cvtop
3✔
1144
  | Ty_bool -> Bool.cvtop
×
1145
  | Ty_str -> Str.cvtop
14✔
1146
  | Ty_bitv 32 -> I32CvtOp.cvtop
2✔
1147
  | Ty_bitv 64 -> I64CvtOp.cvtop
2✔
1148
  | Ty_fp 32 -> F32CvtOp.cvtop
2✔
1149
  | Ty_fp 64 -> F64CvtOp.cvtop
2✔
1150
  | _ -> assert false
1151

1152
let naryop = function
1153
  | Ty.Ty_bool -> Bool.naryop
4✔
1154
  | Ty_str -> Str.naryop
3✔
1155
  | Ty_list -> Lst.naryop
×
1156
  | _ -> 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