blob: 9de08e1b7f233e483d24f80bbc41fe01a972ed80 [file] [log] [blame]
module type RepType =
sig
type t
val zero : t
val one : t
val minus_one : t
val max_int : t
val min_int : t
val neg : t -> t
val add : t -> t -> t
val sub : t -> t -> t
val mul : t -> t -> t
val div : t -> t -> t (* raises Division_by_zero *)
val rem : t -> t -> t (* raises Division_by_zero *)
val logand : t -> t -> t
val lognot : t -> t
val logor : t -> t -> t
val logxor : t -> t -> t
val shift_left : t -> int -> t
val shift_right : t -> int -> t
val shift_right_logical : t -> int -> t
val of_int : int -> t
val to_int : t -> int
val to_string : t -> string
val bitwidth : int
end
module type S =
sig
type t
type bits
val of_bits : bits -> t
val to_bits : t -> bits
val zero : t
val add : t -> t -> t
val sub : t -> t -> t
val mul : t -> t -> t
val div_s : t -> t -> t (* raises IntegerDivideByZero, IntegerOverflow *)
val div_u : t -> t -> t (* raises IntegerDivideByZero *)
val rem_s : t -> t -> t (* raises IntegerDivideByZero *)
val rem_u : t -> t -> t (* raises IntegerDivideByZero *)
val and_ : t -> t -> t
val or_ : t -> t -> t
val xor : t -> t -> t
val shl : t -> t -> t
val shr_s : t -> t -> t
val shr_u : t -> t -> t
val rotl : t -> t -> t
val rotr : t -> t -> t
val clz : t -> t
val ctz : t -> t
val popcnt : t -> t
val eqz : t -> bool
val eq : t -> t -> bool
val ne : t -> t -> bool
val lt_s : t -> t -> bool
val lt_u : t -> t -> bool
val le_s : t -> t -> bool
val le_u : t -> t -> bool
val gt_s : t -> t -> bool
val gt_u : t -> t -> bool
val ge_s : t -> t -> bool
val ge_u : t -> t -> bool
val of_int_s : int -> t
val of_int_u : int -> t
val of_string_s : string -> t
val of_string_u : string -> t
val of_string : string -> t
val to_string_s : t -> string
val to_string_u : t -> string
end
module Make (Rep : RepType) : S with type bits = Rep.t and type t = Rep.t =
struct
(*
* Unsigned comparison in terms of signed comparison.
*)
let cmp_u x op y =
op (Rep.add x Rep.min_int) (Rep.add y Rep.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 = Rep.zero then raise Numeric_error.IntegerDivideByZero else
let t = Rep.shift_right d (Rep.bitwidth - 1) in
let n' = Rep.logand n (Rep.lognot t) in
let q = Rep.shift_left (Rep.div (Rep.shift_right_logical n' 1) d) 1 in
let r = Rep.sub n (Rep.mul q d) in
if cmp_u r (<) d then
q, r
else
Rep.add q Rep.one, Rep.sub r d
type t = Rep.t
type bits = Rep.t
let of_bits x = x
let to_bits x = x
let zero = Rep.zero
let one = Rep.one
let ten = Rep.of_int 10
(* add, sub, and mul are sign-agnostic and do not trap on overflow. *)
let add = Rep.add
let sub = Rep.sub
let mul = Rep.mul
(* result is truncated toward zero *)
let div_s x y =
if y = Rep.zero then
raise Numeric_error.IntegerDivideByZero
else if x = Rep.min_int && y = Rep.minus_one then
raise Numeric_error.IntegerOverflow
else
Rep.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 = Rep.zero then
raise Numeric_error.IntegerDivideByZero
else
Rep.rem x y
let rem_u x y =
let q, r = divrem_u x y in r
let and_ = Rep.logand
let or_ = Rep.logor
let xor = Rep.logxor
(* WebAssembly's shifts mask the shift count according to the bitwidth. *)
let shift f x y =
f x (Rep.to_int (Rep.logand y (Rep.of_int (Rep.bitwidth - 1))))
let shl x y =
shift Rep.shift_left x y
let shr_s x y =
shift Rep.shift_right x y
let shr_u x y =
shift Rep.shift_right_logical x y
(* We must mask the count to implement rotates via shifts. *)
let clamp_rotate_count n =
Rep.to_int (Rep.logand n (Rep.of_int (Rep.bitwidth - 1)))
let rotl x y =
let n = clamp_rotate_count y in
or_ (Rep.shift_left x n) (Rep.shift_right_logical x (Rep.bitwidth - n))
let rotr x y =
let n = clamp_rotate_count y in
or_ (Rep.shift_right_logical x n) (Rep.shift_left x (Rep.bitwidth - n))
(* clz is defined for all values, including all-zeros. *)
let clz x =
let rec loop acc n =
if n = Rep.zero then
Rep.bitwidth
else if and_ n (Rep.shift_left Rep.one (Rep.bitwidth - 1)) = Rep.zero then
loop (1 + acc) (Rep.shift_left n 1)
else
acc
in Rep.of_int (loop 0 x)
(* ctz is defined for all values, including all-zeros. *)
let ctz x =
let rec loop acc n =
if n = Rep.zero then
Rep.bitwidth
else if and_ n Rep.one = Rep.one then
acc
else
loop (1 + acc) (Rep.shift_right_logical n 1)
in Rep.of_int (loop 0 x)
let popcnt x =
let rec loop acc i n =
if n = Rep.zero then
acc
else
let acc' = if and_ n Rep.one = Rep.one then acc + 1 else acc in
loop acc' (i - 1) (Rep.shift_right_logical n 1)
in Rep.of_int (loop 0 Rep.bitwidth x)
let eqz x = x = Rep.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 of_int_s = Rep.of_int
let of_int_u i = and_ (Rep.of_int i) (or_ (shl (Rep.of_int max_int) one) 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 Rep.minus_one ten
let of_string s =
let open Rep in
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);
match s.[0] with
| '+' -> parse_int 1
| '-' ->
let n = parse_int 1 in
require (ge_s (sub n one) minus_one);
Rep.neg n
| _ -> parse_int 0
let of_string_s s =
let n = of_string s in
require (s.[0] = '-' || ge_s n Rep.zero);
n
let of_string_u s =
let n = of_string s in
require (s.[0] != '+' && s.[0] != '-');
n
(* String conversion that groups digits for readability *)
let rec add_digits buf s i j k =
if i < j then begin
if k = 0 then Buffer.add_char buf '_';
Buffer.add_char buf s.[i];
add_digits buf s (i + 1) j ((k + 2) mod 3)
end
let group_digits s =
let len = String.length s in
let num = if s.[0] = '-' then 1 else 0 in
let buf = Buffer.create (len*4/3) in
Buffer.add_substring buf s 0 num;
add_digits buf s num len ((len - num) mod 3 + 3);
Buffer.contents buf
let to_string_s i = group_digits (Rep.to_string i)
let to_string_u i =
if i >= Rep.zero then
group_digits (Rep.to_string i)
else
group_digits (Rep.to_string (div_u i ten) ^ Rep.to_string (rem_u i ten))
end