You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

237 lines
6.3 KiB

include Stdlib.Int32
let clz = Ocaml_intrinsics.Int32.count_leading_zeros
let ctz = Ocaml_intrinsics.Int32.count_trailing_zeros
(* Taken from Base *)
let popcnt =
let mask = 0xffff_ffffL in
fun [@inline] x -> Int64.popcnt (Int64.logand (Int64.of_int32 x) mask)
let to_hex_string = Printf.sprintf "%lx"
let of_int64 = Int64.to_int32
let to_int64 = Int64.of_int32
exception Overflow
(*
* Unsigned comparison in terms of signed comparison.
*)
let cmp_u x op y = op (add x min_int) (add y min_int)
(*
* Unsigned division and remainder in terms of signed division; algorithm from
* Hacker's Delight, Second Edition, by Henry S. Warren, Jr., section 9-3
* "Unsigned Short Division from Signed Division".
*)
let divrem_u n d =
if d = zero then raise Division_by_zero
else
let t = shift_right d (32 - 1) in
let n' = logand n (lognot t) in
let q = shift_left (div (shift_right_logical n' 1) d) 1 in
let r = sub n (mul q d) in
if cmp_u r ( < ) d then (q, r) else (add q one, sub r d)
let of_bits x = x
let to_bits x = x
let ten = of_int 10
(* If bit (32 - 1) is set, sx will sign-extend t to maintain the
* invariant that small ints are stored sign-extended inside a wider int. *)
let sx x =
Int64.to_int32 @@ Int64.(shift_right (shift_left (Int64.of_int32 x) 32) 32)
(* We don't override min_int and max_int since those are used
* by other functions (like parsing), and rely on it being
* min/max for int32 *)
(* The smallest signed |32|-bits int. *)
let low_int = shift_left minus_one 31
(* The largest signed |32|-bits int. *)
let high_int = logxor low_int minus_one
(* result is truncated toward zero *)
let div_s x y =
if y = zero then raise Division_by_zero
else if x = low_int && y = minus_one then raise Overflow
else div x y
(* result is floored (which is the same as truncating for unsigned values) *)
let div_u x y =
let q, _r = divrem_u x y in
q
(* result has the sign of the dividend *)
let rem_s x y = if y = zero then raise Division_by_zero else rem x y
let rem_u x y =
let _q, r = divrem_u x y in
r
let avgr_u x y =
let open Int64 in
(* Mask with bottom #32 bits set *)
let mask = shift_right_logical minus_one 32 in
let x64 = logand mask (to_int64 x) in
let y64 = logand mask (to_int64 y) in
of_int64 (div (add (add x64 y64) one) 2L)
let and_ = logand
let or_ = logor
let xor = logxor
(* WebAssembly's shifts mask the shift count according to the 32. *)
let shift f x y = f x (to_int (logand y 31l))
let shl x y = sx (shift shift_left x y)
let shr_s x y = shift shift_right x y
(* Check if we are storing smaller ints. *)
let needs_extend = shl one 31l <> min_int
(*
* When Int is used to store a smaller int, it is stored in signed extended
* form. Some instructions require the unsigned form, which requires masking
* away the top 32-32 bits.
*)
let as_unsigned x =
if not needs_extend then x
else
(* Mask with bottom #32 bits set *)
let mask = shift_right_logical minus_one 0 in
logand x mask
let shr_u x y = sx (shift shift_right_logical (as_unsigned x) y)
(* We must mask the count to implement rotates via shifts. *)
let clamp_rotate_count n = to_int (logand n 31l)
let rotl x y =
let n = clamp_rotate_count y in
or_ (shl x (of_int n)) (shr_u x (of_int (32 - n)))
let rotr x y =
let n = clamp_rotate_count y in
or_ (shr_u x (of_int n)) (shl x (of_int (32 - n)))
let extend_s n x =
let shift = 32 - n in
shift_right (shift_left x shift) shift
let eqz x = x = zero
let eq x y = x = y
let ne x y = x <> y
let lt_s x y = x < y
let lt_u x y = cmp_u x ( < ) y
let le_s x y = x <= y
let le_u x y = cmp_u x ( <= ) y
let gt_s x y = x > y
let gt_u x y = cmp_u x ( > ) y
let ge_s x y = x >= y
let ge_u x y = cmp_u x ( >= ) y
let saturate_s x = sx (min (max x low_int) high_int)
let saturate_u x = sx (min (max x zero) (as_unsigned minus_one))
(* String conversion that allows leading signs and unsigned values *)
let require b = if not b then failwith "of_string"
let dec_digit = function
| '0' .. '9' as c -> Char.code c - Char.code '0'
| _ -> failwith "of_string"
let hex_digit = function
| '0' .. '9' as c -> Char.code c - Char.code '0'
| 'a' .. 'f' as c -> 0xa + Char.code c - Char.code 'a'
| 'A' .. 'F' as c -> 0xa + Char.code c - Char.code 'A'
| _ -> failwith "of_string"
let max_upper, max_lower = divrem_u minus_one ten
let sign_extend i =
(* This module is used with I32 and I64, but the 32 can be less
* than that, e.g. for I16. When used for smaller integers, the stored value
* needs to be signed extended, e.g. parsing -1 into a I16 (backed by Int32)
* should have all high bits set. We can do that by logor with a mask,
* where the mask is minus_one left shifted by 32. But if 32
* matches the number of bits of Rep, the shift will be incorrect.
* -1 (Int32) << 32 = -1
* Then the logor will be also wrong. So we check and bail out early.
* *)
if not needs_extend then i
else
let sign_bit = logand (of_int (1 lsl (32 - 1))) i in
if sign_bit = zero then i
else
(* Build a sign-extension mask *)
let sign_mask = shift_left minus_one 32 in
logor sign_mask i
let of_string s =
let len = String.length s in
let rec parse_hex i num =
if i = len then num
else if s.[i] = '_' then parse_hex (i + 1) num
else
let digit = of_int (hex_digit s.[i]) in
require (le_u num (shr_u minus_one (of_int 4)));
parse_hex (i + 1) (logor (shift_left num 4) digit)
in
let rec parse_dec i num =
if i = len then num
else if s.[i] = '_' then parse_dec (i + 1) num
else
let digit = of_int (dec_digit s.[i]) in
require (lt_u num max_upper || (num = max_upper && le_u digit max_lower));
parse_dec (i + 1) (add (mul num ten) digit)
in
let parse_int i =
require (len - i > 0);
if i + 2 <= len && s.[i] = '0' && s.[i + 1] = 'x' then
parse_hex (i + 2) zero
else parse_dec i zero
in
require (len > 0);
let parsed =
match s.[0] with
| '+' -> parse_int 1
| '-' ->
let n = parse_int 1 in
require (ge_s (sub n one) minus_one);
neg n
| _ -> parse_int 0
in
let n = sign_extend parsed in
require (low_int <= n && n <= high_int);
n
let of_string_s s =
let n = of_string s in
require (s.[0] = '-' || ge_s n zero);
n
let of_string_u s =
let n = of_string s in
require (s.[0] <> '+' && s.[0] <> '-');
n