Skip to content

Commit 5f3896a

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 9d7883c commit 5f3896a

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
@@ -4684,3 +4684,270 @@ let reperform ~dbg ~eff ~cont ~last_fiber =
46844684
dbg )
46854685

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

0 commit comments

Comments
 (0)