Skip to content

Commit 8fa9539

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 e1c12d7 commit 8fa9539

File tree

4 files changed

+673
-284
lines changed

4 files changed

+673
-284
lines changed

backend/cmm_helpers.ml

+267
Original file line numberDiff line numberDiff line change
@@ -4476,3 +4476,270 @@ let reperform ~dbg ~eff ~cont ~last_fiber =
44764476
dbg )
44774477

44784478
let poll ~dbg = return_unit dbg (Cop (Cpoll, [], dbg))
4479+
4480+
module Scalar_type = struct
4481+
module Float_width = struct
4482+
type t = Cmm.float_width =
4483+
| Float64
4484+
| Float32
4485+
4486+
let[@inline] static_cast ~dbg ~src ~dst exp =
4487+
match src, dst with
4488+
| Float64, Float64 -> exp
4489+
| Float32, Float32 -> exp
4490+
| Float32, Float64 -> float_of_float32 ~dbg exp
4491+
| Float64, Float32 -> float32_of_float ~dbg exp
4492+
end
4493+
4494+
module Signedness = struct
4495+
type t =
4496+
| Signed
4497+
| Unsigned
4498+
4499+
let equal (x : t) (y : t) = x = y
4500+
4501+
let print ppf t =
4502+
match t with
4503+
| Signed -> Format.pp_print_string ppf "signed"
4504+
| Unsigned -> Format.pp_print_string ppf "unsigned"
4505+
end
4506+
4507+
module Bit_width_and_signedness : sig
4508+
(** An integer with signedness [signedness t] that fits into a general-purpose
4509+
register. It is canonically stored in twos-complement representation, in the lower
4510+
[bits] bits of its container (whether that be memory or a register), and is sign-
4511+
or zero-extended to fill the entire container. *)
4512+
type t [@@immediate]
4513+
4514+
val create_exn : bit_width:int -> signedness:Signedness.t -> t
4515+
4516+
val bit_width : t -> int
4517+
4518+
val signedness : t -> Signedness.t
4519+
4520+
val equal : t -> t -> bool
4521+
end = struct
4522+
(* [signedness t] is stored in the low bit of [t], and [bit_width t] is
4523+
stored in the remaining high bits of [t]. We use this encoding to fit [t]
4524+
into an immediate value. This is worth trying since we expect to create
4525+
one of these for ~every integer operation, so it should cut down on
4526+
garbage *)
4527+
type t = { bit_width_and_signedness : int } [@@unboxed]
4528+
4529+
let[@inline] equal { bit_width_and_signedness = x }
4530+
{ bit_width_and_signedness = y } =
4531+
Int.equal x y
4532+
4533+
let[@inline] bit_width { bit_width_and_signedness } =
4534+
bit_width_and_signedness lsr 1
4535+
4536+
let[@inline] signedness { bit_width_and_signedness } =
4537+
match bit_width_and_signedness land 1 with
4538+
| 0 -> Signedness.Signed
4539+
| 1 -> Signedness.Unsigned
4540+
| _ -> assert false
4541+
4542+
let[@inline] int_of_signedness : Signedness.t -> int = function
4543+
| Signed -> 0
4544+
| Unsigned -> 1
4545+
4546+
let[@inline] create_exn ~bit_width ~signedness =
4547+
assert (0 < bit_width && bit_width <= arch_bits);
4548+
{ bit_width_and_signedness =
4549+
(bit_width lsl 1) + int_of_signedness signedness
4550+
}
4551+
end
4552+
4553+
module Integral_type = struct
4554+
include Bit_width_and_signedness
4555+
4556+
let[@inline] with_signedness t ~signedness =
4557+
create_exn ~bit_width:(bit_width t) ~signedness
4558+
4559+
let[@inline] signed t = with_signedness t ~signedness:Signed
4560+
4561+
let[@inline] unsigned t = with_signedness t ~signedness:Unsigned
4562+
4563+
(** Determines whether [dst] can represent every value of [src], preserving sign *)
4564+
let[@inline] can_cast_without_losing_information ~src ~dst =
4565+
match signedness src, signedness dst with
4566+
| Signed, Signed | Unsigned, Unsigned -> bit_width src <= bit_width dst
4567+
| Unsigned, Signed -> bit_width src < bit_width dst
4568+
| Signed, Unsigned -> false
4569+
4570+
let[@inline] static_cast ~dbg ~src ~dst exp =
4571+
if can_cast_without_losing_information ~src ~dst
4572+
then
4573+
(* Since [Bit_width_and_signedness] represents sign- or zero-extended
4574+
expressions, this is a no-op *)
4575+
exp
4576+
else
4577+
match signedness dst with
4578+
| Signed -> sign_extend ~bits:(bit_width dst) exp ~dbg
4579+
| Unsigned -> zero_extend ~bits:(bit_width dst) exp ~dbg
4580+
4581+
let[@inline] conjugate ~outer ~inner ~dbg ~f x =
4582+
x
4583+
|> static_cast ~src:outer ~dst:inner ~dbg
4584+
|> f
4585+
|> static_cast ~src:inner ~dst:outer ~dbg
4586+
end
4587+
4588+
module Integer = struct
4589+
include Integral_type
4590+
4591+
let print ppf t =
4592+
Format.fprintf ppf "%a int%d" Signedness.print (signedness t)
4593+
(bit_width t)
4594+
4595+
let nativeint = create_exn ~bit_width:arch_bits ~signedness:Signed
4596+
end
4597+
4598+
(** An {!Integer.t} but with the additional stipulation that its container must
4599+
reserve its lowest bit to be 1. The [bit_width] field includes this bit. *)
4600+
module Tagged_integer = struct
4601+
include Integral_type
4602+
4603+
let[@inline] create_exn ~bit_width_including_tag_bit:bit_width ~signedness =
4604+
assert (bit_width > 1);
4605+
create_exn ~bit_width ~signedness
4606+
4607+
let immediate =
4608+
create_exn ~bit_width_including_tag_bit:arch_bits ~signedness:Signed
4609+
4610+
let[@inline] bit_width_including_tag_bit t = bit_width t
4611+
4612+
let[@inline] bit_width_excluding_tag_bit t = bit_width t - 1
4613+
4614+
let[@inline] untagged t =
4615+
Integer.create_exn
4616+
~bit_width:(bit_width_excluding_tag_bit t)
4617+
~signedness:(signedness t)
4618+
4619+
let[@inline] untag ~dbg t exp =
4620+
match signedness t with
4621+
| Signed -> asr_const exp 1 dbg
4622+
| Unsigned -> lsr_const exp 1 dbg
4623+
4624+
let print ppf t =
4625+
Format.fprintf ppf "tagged %a int%d" Signedness.print (signedness t)
4626+
(bit_width_excluding_tag_bit t)
4627+
end
4628+
4629+
module Integral = struct
4630+
type t =
4631+
| Untagged of Integer.t
4632+
| Tagged of Tagged_integer.t
4633+
4634+
let nativeint = Untagged Integer.nativeint
4635+
4636+
let[@inline] untagged_or_identity = function
4637+
| Untagged t -> t
4638+
| Tagged t -> Tagged_integer.untagged t
4639+
4640+
let signedness = function
4641+
| Untagged t -> Integer.signedness t
4642+
| Tagged t -> Tagged_integer.signedness t
4643+
4644+
let with_signedness t ~signedness =
4645+
match t with
4646+
| Untagged t -> Untagged (Integer.with_signedness t ~signedness)
4647+
| Tagged t -> Tagged (Tagged_integer.with_signedness t ~signedness)
4648+
4649+
let[@inline] signed t = with_signedness t ~signedness:Signed
4650+
4651+
let[@inline] unsigned t = with_signedness t ~signedness:Unsigned
4652+
4653+
let[@inline] equal x y =
4654+
match x, y with
4655+
| Untagged x, Untagged y -> Integer.equal x y
4656+
| Untagged _, _ -> false
4657+
| Tagged x, Tagged y -> Tagged_integer.equal x y
4658+
| Tagged _, _ -> false
4659+
4660+
let print ppf t =
4661+
match t with
4662+
| Untagged untagged -> Integer.print ppf untagged
4663+
| Tagged tagged -> Tagged_integer.print ppf tagged
4664+
4665+
let[@inline] can_cast_without_losing_information ~src ~dst =
4666+
Integer.can_cast_without_losing_information
4667+
~src:(untagged_or_identity src) ~dst:(untagged_or_identity dst)
4668+
4669+
let static_cast ~dbg ~src ~dst exp =
4670+
match src, dst with
4671+
| Untagged src, Untagged dst -> Integer.static_cast ~dbg ~src ~dst exp
4672+
| Tagged src, Tagged dst -> Tagged_integer.static_cast ~dbg ~src ~dst exp
4673+
| Untagged src, Tagged dst ->
4674+
tag_int
4675+
(Integer.static_cast ~dbg ~src ~dst:(Tagged_integer.untagged dst) exp)
4676+
dbg
4677+
| Tagged src, Untagged dst ->
4678+
Integer.static_cast ~dbg
4679+
~src:(Tagged_integer.untagged src)
4680+
~dst
4681+
(Tagged_integer.untag ~dbg src exp)
4682+
4683+
let[@inline] conjugate ~outer ~inner ~dbg ~f x =
4684+
x
4685+
|> static_cast ~src:outer ~dst:inner ~dbg
4686+
|> f
4687+
|> static_cast ~src:inner ~dst:outer ~dbg
4688+
end
4689+
4690+
type t =
4691+
| Integral of Integral.t
4692+
| Float of Float_width.t
4693+
4694+
let static_cast ~dbg ~src ~dst exp =
4695+
match src, dst with
4696+
| Integral src, Integral dst -> Integral.static_cast ~dbg ~src ~dst exp
4697+
| Float src, Float dst -> Float_width.static_cast ~dbg ~src ~dst exp
4698+
| Integral src, Float dst ->
4699+
let float_of_int_arg = Integral.nativeint in
4700+
if not
4701+
(Integral.can_cast_without_losing_information ~src
4702+
~dst:float_of_int_arg)
4703+
then
4704+
Misc.fatal_errorf "static_cast: casting %a to float is not implemented"
4705+
Integral.print src
4706+
else
4707+
unary (Cstatic_cast (Float_of_int dst)) ~dbg
4708+
(Integral.static_cast exp ~dbg ~src ~dst:float_of_int_arg)
4709+
| Float src, Integral dst -> (
4710+
match Integral.signedness dst with
4711+
| Unsigned ->
4712+
Misc.fatal_errorf
4713+
"static_cast: casting floats to unsigned values is not implemented"
4714+
| Signed ->
4715+
(* we can truncate because casting from float -> int is unspecified when
4716+
the rounded value doesn't fit in the integral type. We can't promote
4717+
since nativeint is already the largest integral type supported
4718+
here. *)
4719+
let exp = unary (Cstatic_cast (Int_of_float src)) exp ~dbg in
4720+
let src = Integral.nativeint in
4721+
(* assert that nativeint is indeed the largest integer width *)
4722+
assert (Integral.can_cast_without_losing_information ~src:dst ~dst:src);
4723+
Integral.static_cast exp ~dbg ~src ~dst)
4724+
4725+
let[@inline] conjugate ~outer ~inner ~dbg ~f x =
4726+
x
4727+
|> static_cast ~src:outer ~dst:inner ~dbg
4728+
|> f
4729+
|> static_cast ~src:inner ~dst:outer ~dbg
4730+
4731+
module Untagged = struct
4732+
type numeric = t
4733+
4734+
type t =
4735+
| Untagged of Integer.t
4736+
| Float of float_width
4737+
4738+
let to_numeric : t -> numeric = function
4739+
| Untagged width -> Integral (Untagged width)
4740+
| Float float -> Float float
4741+
4742+
let[@inline] static_cast ~dbg ~src ~dst exp =
4743+
static_cast ~dbg ~src:(to_numeric src) ~dst:(to_numeric dst) exp
4744+
end
4745+
end

0 commit comments

Comments
 (0)