Skip to content

Commit 69edaea

Browse files
committed
Added Cmm_helpers.Scalar_type. This provides utilities for converting between integers types of different widths and signedness. This is in preparation for adding unboxed small integer types.
1 parent d944235 commit 69edaea

File tree

4 files changed

+640
-239
lines changed

4 files changed

+640
-239
lines changed

backend/cmm_helpers.ml

+267
Original file line numberDiff line numberDiff line change
@@ -4691,3 +4691,270 @@ let reperform ~dbg ~eff ~cont ~last_fiber =
46914691
dbg )
46924692

46934693
let poll ~dbg = return_unit dbg (Cop (Cpoll, [], dbg))
4694+
4695+
module Scalar_type = struct
4696+
module Float_width = struct
4697+
type t = Cmm.float_width =
4698+
| Float64
4699+
| Float32
4700+
4701+
let[@inline] static_cast ~dbg ~src ~dst exp =
4702+
match src, dst with
4703+
| Float64, Float64 -> exp
4704+
| Float32, Float32 -> exp
4705+
| Float32, Float64 -> float_of_float32 ~dbg exp
4706+
| Float64, Float32 -> float32_of_float ~dbg exp
4707+
end
4708+
4709+
module Signedness = struct
4710+
type t =
4711+
| Signed
4712+
| Unsigned
4713+
4714+
let equal (x : t) (y : t) = x = y
4715+
4716+
let print ppf t =
4717+
match t with
4718+
| Signed -> Format.pp_print_string ppf "signed"
4719+
| Unsigned -> Format.pp_print_string ppf "unsigned"
4720+
end
4721+
4722+
module Bit_width_and_signedness : sig
4723+
(** An integer with signedness [signedness t] that fits into a general-purpose
4724+
register. It is canonically stored in twos-complement representation, in the lower
4725+
[bits] bits of its container (whether that be memory or a register), and is sign-
4726+
or zero-extended to fill the entire container. *)
4727+
type t [@@immediate]
4728+
4729+
val create_exn : bit_width:int -> signedness:Signedness.t -> t
4730+
4731+
val bit_width : t -> int
4732+
4733+
val signedness : t -> Signedness.t
4734+
4735+
val equal : t -> t -> bool
4736+
end = struct
4737+
(* [signedness t] is stored in the low bit of [t], and [bit_width t] is
4738+
stored in the remaining high bits of [t]. We use this encoding to fit [t]
4739+
into an immediate value. This is worth trying since we expect to create
4740+
one of these for ~every integer operation, so it should cut down on
4741+
garbage *)
4742+
type t = { bit_width_and_signedness : int } [@@unboxed]
4743+
4744+
let[@inline] equal { bit_width_and_signedness = x }
4745+
{ bit_width_and_signedness = y } =
4746+
Int.equal x y
4747+
4748+
let[@inline] bit_width { bit_width_and_signedness } =
4749+
bit_width_and_signedness lsr 1
4750+
4751+
let[@inline] signedness { bit_width_and_signedness } =
4752+
match bit_width_and_signedness land 1 with
4753+
| 0 -> Signedness.Signed
4754+
| 1 -> Signedness.Unsigned
4755+
| _ -> assert false
4756+
4757+
let[@inline] int_of_signedness : Signedness.t -> int = function
4758+
| Signed -> 0
4759+
| Unsigned -> 1
4760+
4761+
let[@inline] create_exn ~bit_width ~signedness =
4762+
assert (0 < bit_width && bit_width <= arch_bits);
4763+
{ bit_width_and_signedness =
4764+
(bit_width lsl 1) + int_of_signedness signedness
4765+
}
4766+
end
4767+
4768+
module Integral_type = struct
4769+
include Bit_width_and_signedness
4770+
4771+
let[@inline] with_signedness t ~signedness =
4772+
create_exn ~bit_width:(bit_width t) ~signedness
4773+
4774+
let[@inline] signed t = with_signedness t ~signedness:Signed
4775+
4776+
let[@inline] unsigned t = with_signedness t ~signedness:Unsigned
4777+
4778+
(** Determines whether [dst] can represent every value of [src], preserving sign *)
4779+
let[@inline] can_cast_without_losing_information ~src ~dst =
4780+
match signedness src, signedness dst with
4781+
| Signed, Signed | Unsigned, Unsigned -> bit_width src <= bit_width dst
4782+
| Unsigned, Signed -> bit_width src < bit_width dst
4783+
| Signed, Unsigned -> false
4784+
4785+
let[@inline] static_cast ~dbg ~src ~dst exp =
4786+
if can_cast_without_losing_information ~src ~dst
4787+
then
4788+
(* Since [Bit_width_and_signedness] represents sign- or zero-extended
4789+
expressions, this is a no-op *)
4790+
exp
4791+
else
4792+
match signedness dst with
4793+
| Signed -> sign_extend ~bits:(bit_width dst) exp ~dbg
4794+
| Unsigned -> zero_extend ~bits:(bit_width dst) exp ~dbg
4795+
4796+
let[@inline] conjugate ~outer ~inner ~dbg ~f x =
4797+
x
4798+
|> static_cast ~src:outer ~dst:inner ~dbg
4799+
|> f
4800+
|> static_cast ~src:inner ~dst:outer ~dbg
4801+
end
4802+
4803+
module Integer = struct
4804+
include Integral_type
4805+
4806+
let print ppf t =
4807+
Format.fprintf ppf "%a int%d" Signedness.print (signedness t)
4808+
(bit_width t)
4809+
4810+
let nativeint = create_exn ~bit_width:arch_bits ~signedness:Signed
4811+
end
4812+
4813+
(** An {!Integer.t} but with the additional stipulation that its container must
4814+
reserve its lowest bit to be 1. The [bit_width] field includes this bit. *)
4815+
module Tagged_integer = struct
4816+
include Integral_type
4817+
4818+
let[@inline] create_exn ~bit_width_including_tag_bit:bit_width ~signedness =
4819+
assert (bit_width > 1);
4820+
create_exn ~bit_width ~signedness
4821+
4822+
let immediate =
4823+
create_exn ~bit_width_including_tag_bit:arch_bits ~signedness:Signed
4824+
4825+
let[@inline] bit_width_including_tag_bit t = bit_width t
4826+
4827+
let[@inline] bit_width_excluding_tag_bit t = bit_width t - 1
4828+
4829+
let[@inline] untagged t =
4830+
Integer.create_exn
4831+
~bit_width:(bit_width_excluding_tag_bit t)
4832+
~signedness:(signedness t)
4833+
4834+
let[@inline] untag ~dbg t exp =
4835+
match signedness t with
4836+
| Signed -> asr_const exp 1 dbg
4837+
| Unsigned -> lsr_const exp 1 dbg
4838+
4839+
let print ppf t =
4840+
Format.fprintf ppf "tagged %a int%d" Signedness.print (signedness t)
4841+
(bit_width_excluding_tag_bit t)
4842+
end
4843+
4844+
module Integral = struct
4845+
type t =
4846+
| Untagged of Integer.t
4847+
| Tagged of Tagged_integer.t
4848+
4849+
let nativeint = Untagged Integer.nativeint
4850+
4851+
let[@inline] untagged_or_identity = function
4852+
| Untagged t -> t
4853+
| Tagged t -> Tagged_integer.untagged t
4854+
4855+
let signedness = function
4856+
| Untagged t -> Integer.signedness t
4857+
| Tagged t -> Tagged_integer.signedness t
4858+
4859+
let with_signedness t ~signedness =
4860+
match t with
4861+
| Untagged t -> Untagged (Integer.with_signedness t ~signedness)
4862+
| Tagged t -> Tagged (Tagged_integer.with_signedness t ~signedness)
4863+
4864+
let[@inline] signed t = with_signedness t ~signedness:Signed
4865+
4866+
let[@inline] unsigned t = with_signedness t ~signedness:Unsigned
4867+
4868+
let[@inline] equal x y =
4869+
match x, y with
4870+
| Untagged x, Untagged y -> Integer.equal x y
4871+
| Untagged _, _ -> false
4872+
| Tagged x, Tagged y -> Tagged_integer.equal x y
4873+
| Tagged _, _ -> false
4874+
4875+
let print ppf t =
4876+
match t with
4877+
| Untagged untagged -> Integer.print ppf untagged
4878+
| Tagged tagged -> Tagged_integer.print ppf tagged
4879+
4880+
let[@inline] can_cast_without_losing_information ~src ~dst =
4881+
Integer.can_cast_without_losing_information
4882+
~src:(untagged_or_identity src) ~dst:(untagged_or_identity dst)
4883+
4884+
let static_cast ~dbg ~src ~dst exp =
4885+
match src, dst with
4886+
| Untagged src, Untagged dst -> Integer.static_cast ~dbg ~src ~dst exp
4887+
| Tagged src, Tagged dst -> Tagged_integer.static_cast ~dbg ~src ~dst exp
4888+
| Untagged src, Tagged dst ->
4889+
tag_int
4890+
(Integer.static_cast ~dbg ~src ~dst:(Tagged_integer.untagged dst) exp)
4891+
dbg
4892+
| Tagged src, Untagged dst ->
4893+
Integer.static_cast ~dbg
4894+
~src:(Tagged_integer.untagged src)
4895+
~dst
4896+
(Tagged_integer.untag ~dbg src exp)
4897+
4898+
let[@inline] conjugate ~outer ~inner ~dbg ~f x =
4899+
x
4900+
|> static_cast ~src:outer ~dst:inner ~dbg
4901+
|> f
4902+
|> static_cast ~src:inner ~dst:outer ~dbg
4903+
end
4904+
4905+
type t =
4906+
| Integral of Integral.t
4907+
| Float of Float_width.t
4908+
4909+
let static_cast ~dbg ~src ~dst exp =
4910+
match src, dst with
4911+
| Integral src, Integral dst -> Integral.static_cast ~dbg ~src ~dst exp
4912+
| Float src, Float dst -> Float_width.static_cast ~dbg ~src ~dst exp
4913+
| Integral src, Float dst ->
4914+
let float_of_int_arg = Integral.nativeint in
4915+
if not
4916+
(Integral.can_cast_without_losing_information ~src
4917+
~dst:float_of_int_arg)
4918+
then
4919+
Misc.fatal_errorf "static_cast: casting %a to float is not implemented"
4920+
Integral.print src
4921+
else
4922+
unary (Cstatic_cast (Float_of_int dst)) ~dbg
4923+
(Integral.static_cast exp ~dbg ~src ~dst:float_of_int_arg)
4924+
| Float src, Integral dst -> (
4925+
match Integral.signedness dst with
4926+
| Unsigned ->
4927+
Misc.fatal_errorf
4928+
"static_cast: casting floats to unsigned values is not implemented"
4929+
| Signed ->
4930+
(* we can truncate because casting from float -> int is unspecified when
4931+
the rounded value doesn't fit in the integral type. We can't promote
4932+
since nativeint is already the largest integral type supported
4933+
here. *)
4934+
let exp = unary (Cstatic_cast (Int_of_float src)) exp ~dbg in
4935+
let src = Integral.nativeint in
4936+
(* assert that nativeint is indeed the largest integer width *)
4937+
assert (Integral.can_cast_without_losing_information ~src:dst ~dst:src);
4938+
Integral.static_cast exp ~dbg ~src ~dst)
4939+
4940+
let[@inline] conjugate ~outer ~inner ~dbg ~f x =
4941+
x
4942+
|> static_cast ~src:outer ~dst:inner ~dbg
4943+
|> f
4944+
|> static_cast ~src:inner ~dst:outer ~dbg
4945+
4946+
module Untagged = struct
4947+
type numeric = t
4948+
4949+
type t =
4950+
| Untagged of Integer.t
4951+
| Float of float_width
4952+
4953+
let to_numeric : t -> numeric = function
4954+
| Untagged width -> Integral (Untagged width)
4955+
| Float float -> Float float
4956+
4957+
let[@inline] static_cast ~dbg ~src ~dst exp =
4958+
static_cast ~dbg ~src:(to_numeric src) ~dst:(to_numeric dst) exp
4959+
end
4960+
end

0 commit comments

Comments
 (0)