Skip to content

Commit ae1f27d

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.
# Squashed commit of the following: # commit c9d7aa6 # Author: Jacob Van Buren <[email protected]> # Date: Thu Jan 2 14:49:45 2025 -0500 # cleaned up div/mod # commit 4d9f427 # Author: Jacob Van Buren <[email protected]> # Date: Thu Jan 2 14:45:42 2025 -0500 # address feedback and simplify division interface # unified unboxed field getters/setters. This will be useful once we have unboxed integers of different sizes # formatted # updated cmm_helpers interface to be more amenable to adding other integer sizes # Separate test for vectorizer in the CI (#3414) # * Separate test for vectorizer in the CI # * Remove vectorizer from "gi" CI job # CI: simplify the regalloc jobs (#3389) # ASR 64-bit lane not available in sse instruction (#3413) # Fix case where parser drops attributes in packed module types. (#3262) # * Demonstrate dropped attributes in test. # Signed-off-by: Thomas Del Vecchio <[email protected]> # * Syntax error on misplaced attribute in packed module types. # Signed-off-by: Thomas Del Vecchio <[email protected]> # --------- # Signed-off-by: Thomas Del Vecchio <[email protected]> # Add attributes to (unsafely) skip jkind check (#3385) # * Add attributes to (unsafely) skip jkind check # Add a pair of attributes, [@@unsafe_allow_any_kind_in_intf] and # [@@unsafe_allow_any_kind_in_impl], which if set on both the impl and the intf # respectively, skip checking the jkind of the type in a signature against the # jkind of the type in a struct entirely. This is a more-selective version of the # `--allow-illegal-crossing` flag, and likely eventually subsumes it. # Signed-off-by: Aspen Smith <[email protected]> # * Emit a warning when unsafe_allow_any_kind is added unnecessarily # Note that this is /only/ done if the attribute is set in both signatures but not # used - also this is a little over-sensitive (sadly) since this is done during # sigature inclusion too. A new test covers the over-sensitivity. # Signed-off-by: Aspen Smith <[email protected]> # --------- # Signed-off-by: Aspen Smith <[email protected]> # Add `Variant_with_null` and `Null` variant constructors (#2870) # * `Variant_with_null` # * `Null` tagged constructors # * precise value kind # * No private re-export # --------- # Co-authored-by: Diana Kalinichenko <[email protected]> # Revert "Implement %makearray_dynamic{,_uninit}" (#3408) # Revert "Implement %makearray_dynamic{,_uninit} (#3317)" # This reverts commit 6da1dde. # Upload core files etc upon CI failure (#3405) # Fix IRC and Greedy allocators (arm64) (#3388) # Convert float32 constants to int32 in first stage compiler (#3371) # * convert float32 constants in bytecode output # * edit # * edit # * blocks + test # * compare against float64 constants # * tests check proper custom ops # --------- # Co-authored-by: Diana Kalinichenko <[email protected]>
1 parent 6392eea commit ae1f27d

File tree

3 files changed

+598
-237
lines changed

3 files changed

+598
-237
lines changed

Diff for: backend/cmm_helpers.ml

+258
Original file line numberDiff line numberDiff line change
@@ -4410,3 +4410,261 @@ let reperform ~dbg ~eff ~cont ~last_fiber =
44104410
dbg )
44114411

44124412
let poll ~dbg = return_unit dbg (Cop (Cpoll, [], dbg))
4413+
4414+
module Scalar_type = struct
4415+
module Float_width = struct
4416+
type t = Cmm.float_width =
4417+
| Float64
4418+
| Float32
4419+
4420+
let[@inline] static_cast ~dbg ~src ~dst exp =
4421+
match src, dst with
4422+
| Float64, Float64 -> exp
4423+
| Float32, Float32 -> exp
4424+
| Float32, Float64 -> float_of_float32 ~dbg exp
4425+
| Float64, Float32 -> float32_of_float ~dbg exp
4426+
end
4427+
4428+
module Signedness = struct
4429+
type t =
4430+
| Signed
4431+
| Unsigned
4432+
4433+
let equal (x : t) (y : t) = x = y
4434+
4435+
let print ppf t =
4436+
match t with
4437+
| Signed -> Format.pp_print_string ppf "signed"
4438+
| Unsigned -> Format.pp_print_string ppf "unsigned"
4439+
end
4440+
4441+
module Bit_width_and_signedness : sig
4442+
(** An integer with signedness [signedness t] that fits into a general-purpose
4443+
register. It is canonically stored in twos-complement representation, in the lower
4444+
[bits] bits of its container (whether that be memory or a register), and is sign-
4445+
or zero-extended to fill the entire container. *)
4446+
type t [@@immediate]
4447+
4448+
val create_exn : bit_width:int -> signedness:Signedness.t -> t
4449+
4450+
val bit_width : t -> int
4451+
4452+
val signedness : t -> Signedness.t
4453+
4454+
val equal : t -> t -> bool
4455+
end = struct
4456+
(* [signedness t] is stored in the low bit of [t], and [bit_width t] is
4457+
stored in the remaining high bits of [t]. We use this encoding to fit [t]
4458+
into an immediate value *)
4459+
type t = { bit_width_and_signedness : int } [@@unboxed]
4460+
4461+
let[@inline] equal { bit_width_and_signedness = x }
4462+
{ bit_width_and_signedness = y } =
4463+
Int.equal x y
4464+
4465+
let[@inline] bit_width { bit_width_and_signedness } =
4466+
bit_width_and_signedness lsr 1
4467+
4468+
let[@inline] signedness { bit_width_and_signedness } =
4469+
match (Obj.magic (bit_width_and_signedness land 1) : Signedness.t) with
4470+
| (Signed | Unsigned) as signedness ->
4471+
(* If [Signedness.t] ever changes, adjust the representation of [t]
4472+
accordingly *)
4473+
signedness
4474+
4475+
(** This type annotation proves that [int_of_signedness] is valid *)
4476+
type signedness_is_immediate = Signedness.t [@@immediate]
4477+
4478+
external int_of_signedness : signedness_is_immediate -> int = "%identity"
4479+
4480+
let[@inline] create_exn ~bit_width ~signedness =
4481+
assert (0 < bit_width && bit_width <= arch_bits);
4482+
{ bit_width_and_signedness =
4483+
(bit_width lsl 1) lor int_of_signedness signedness
4484+
}
4485+
end
4486+
4487+
module Integral_type = struct
4488+
include Bit_width_and_signedness
4489+
4490+
let[@inline] with_signedness t ~signedness =
4491+
create_exn ~bit_width:(bit_width t) ~signedness
4492+
4493+
let[@inline] signed t = with_signedness t ~signedness:Signed
4494+
4495+
let[@inline] unsigned t = with_signedness t ~signedness:Unsigned
4496+
4497+
(** Determines whether [dst] can represent every value of [src], preserving sign *)
4498+
let[@inline] is_promotable ~src ~dst =
4499+
match signedness src, signedness dst with
4500+
| Signed, Signed | Unsigned, Unsigned -> bit_width src <= bit_width dst
4501+
| Unsigned, Signed -> bit_width src < bit_width dst
4502+
| Signed, Unsigned -> false
4503+
4504+
let[@inline] static_cast ~dbg ~src ~dst exp =
4505+
if is_promotable ~src ~dst
4506+
then
4507+
(* since the values are already stored sign- or zero-extended, this is a
4508+
no-op. *)
4509+
exp
4510+
else
4511+
match signedness dst with
4512+
| Signed -> sign_extend ~bits:(bit_width dst) exp ~dbg
4513+
| Unsigned -> zero_extend ~bits:(bit_width dst) exp ~dbg
4514+
4515+
let[@inline] conjugate ~outer ~inner ~dbg ~f x =
4516+
x
4517+
|> static_cast ~src:outer ~dst:inner ~dbg
4518+
|> f
4519+
|> static_cast ~src:inner ~dst:outer ~dbg
4520+
end
4521+
4522+
module Integer = struct
4523+
include Integral_type
4524+
4525+
let print ppf t =
4526+
Format.fprintf ppf "%a int%d" Signedness.print (signedness t)
4527+
(bit_width t)
4528+
4529+
let nativeint = create_exn ~bit_width:arch_bits ~signedness:Signed
4530+
end
4531+
4532+
(** An {!Integer.t} but with the additional stipulation that its container must
4533+
reserve its lowest bit to be 1. The [bits] field does not include this bit. *)
4534+
module Tagged_integer = struct
4535+
include Integral_type
4536+
4537+
let[@inline] create_exn ~bit_width_including_tag_bit:bit_width ~signedness =
4538+
assert (bit_width > 1);
4539+
create_exn ~bit_width ~signedness
4540+
4541+
let immediate =
4542+
create_exn ~bit_width_including_tag_bit:arch_bits ~signedness:Signed
4543+
4544+
let[@inline] bit_width_including_tag_bit t = bit_width t
4545+
4546+
let[@inline] bit_width_excluding_tag_bit t = bit_width t - 1
4547+
4548+
let[@inline] untagged t =
4549+
Integer.create_exn
4550+
~bit_width:(bit_width_excluding_tag_bit t)
4551+
~signedness:(signedness t)
4552+
4553+
let[@inline] untag ~dbg t exp =
4554+
match signedness t with
4555+
| Signed -> asr_const exp 1 dbg
4556+
| Unsigned -> lsr_const exp 1 dbg
4557+
4558+
let print ppf t =
4559+
Format.fprintf ppf "tagged %a int%d" Signedness.print (signedness t)
4560+
(bit_width_excluding_tag_bit t)
4561+
end
4562+
4563+
module Integral = struct
4564+
type t =
4565+
| Untagged of Integer.t
4566+
| Tagged of Tagged_integer.t
4567+
4568+
let nativeint = Untagged Integer.nativeint
4569+
4570+
let[@inline] untagged = function
4571+
| Untagged t -> t
4572+
| Tagged t -> Tagged_integer.untagged t
4573+
4574+
let signedness = function
4575+
| Untagged t -> Integer.signedness t
4576+
| Tagged t -> Tagged_integer.signedness t
4577+
4578+
let with_signedness t ~signedness =
4579+
match t with
4580+
| Untagged t -> Untagged (Integer.with_signedness t ~signedness)
4581+
| Tagged t -> Tagged (Tagged_integer.with_signedness t ~signedness)
4582+
4583+
let[@inline] signed t = with_signedness t ~signedness:Signed
4584+
4585+
let[@inline] unsigned t = with_signedness t ~signedness:Signed
4586+
4587+
let[@inline] equal x y =
4588+
match x, y with
4589+
| Untagged x, Untagged y -> Integer.equal x y
4590+
| Untagged _, _ -> false
4591+
| Tagged x, Tagged y -> Tagged_integer.equal x y
4592+
| Tagged _, _ -> false
4593+
4594+
let print ppf t =
4595+
match t with
4596+
| Untagged untagged -> Integer.print ppf untagged
4597+
| Tagged tagged -> Tagged_integer.print ppf tagged
4598+
4599+
let[@inline] is_promotable ~src ~dst =
4600+
Integer.is_promotable ~src:(untagged src) ~dst:(untagged dst)
4601+
4602+
let static_cast ~dbg ~src ~dst exp =
4603+
match src, dst with
4604+
| Untagged src, Untagged dst -> Integer.static_cast ~dbg ~src ~dst exp
4605+
| Tagged src, Tagged dst -> Tagged_integer.static_cast ~dbg ~src ~dst exp
4606+
| Untagged src, Tagged dst ->
4607+
tag_int
4608+
(Integer.static_cast ~dbg ~src ~dst:(Tagged_integer.untagged dst) exp)
4609+
dbg
4610+
| Tagged src, Untagged dst ->
4611+
Integer.static_cast ~dbg
4612+
~src:(Tagged_integer.untagged src)
4613+
~dst
4614+
(Tagged_integer.untag ~dbg src exp)
4615+
4616+
let[@inline] conjugate ~outer ~inner ~dbg ~f x =
4617+
x
4618+
|> static_cast ~src:outer ~dst:inner ~dbg
4619+
|> f
4620+
|> static_cast ~src:inner ~dst:outer ~dbg
4621+
end
4622+
4623+
type t =
4624+
| Integral of Integral.t
4625+
| Float of Float_width.t
4626+
4627+
let static_cast ~dbg ~src ~dst exp =
4628+
match src, dst with
4629+
| Integral src, Integral dst -> Integral.static_cast ~dbg ~src ~dst exp
4630+
| Float src, Float dst -> Float_width.static_cast ~dbg ~src ~dst exp
4631+
| Integral src, Float dst ->
4632+
let float_of_int_arg = Integral.nativeint in
4633+
if not (Integral.is_promotable ~src ~dst:float_of_int_arg)
4634+
then
4635+
Misc.fatal_errorf "static_cast: casting %a to float is not implemented"
4636+
Integral.print src
4637+
else
4638+
unary (Cstatic_cast (Float_of_int dst)) ~dbg
4639+
(Integral.static_cast exp ~dbg ~src ~dst:float_of_int_arg)
4640+
| Float src, Integral dst -> (
4641+
match Integral.signedness dst with
4642+
| Unsigned ->
4643+
Misc.fatal_errorf
4644+
"static_cast: casting floats to unsigned values is undefined"
4645+
| Signed ->
4646+
(* we can truncate, but we don't want to promote *)
4647+
Integral.static_cast ~dbg ~src:Integral.nativeint ~dst
4648+
(unary (Cstatic_cast (Int_of_float src)) exp ~dbg))
4649+
4650+
let[@inline] conjugate ~outer ~inner ~dbg ~f x =
4651+
x
4652+
|> static_cast ~src:outer ~dst:inner ~dbg
4653+
|> f
4654+
|> static_cast ~src:inner ~dst:outer ~dbg
4655+
4656+
module Untagged = struct
4657+
type numeric = t
4658+
4659+
type t =
4660+
| Untagged of Integer.t
4661+
| Float of float_width
4662+
4663+
let to_numeric : t -> numeric = function
4664+
| Untagged width -> Integral (Untagged width)
4665+
| Float float -> Float float
4666+
4667+
let[@inline] static_cast ~dbg ~src ~dst exp =
4668+
static_cast ~dbg ~src:(to_numeric src) ~dst:(to_numeric dst) exp
4669+
end
4670+
end

Diff for: backend/cmm_helpers.mli

+132
Original file line numberDiff line numberDiff line change
@@ -1226,3 +1226,135 @@ val set_field_unboxed :
12261226
val dls_get : dbg:Debuginfo.t -> expression
12271227

12281228
val poll : dbg:Debuginfo.t -> expression
1229+
1230+
module Scalar_type : sig
1231+
type 'a static_cast :=
1232+
dbg:Debuginfo.t -> src:'a -> dst:'a -> expression -> expression
1233+
1234+
(** Conjugate f by [static_cast ~src:outer ~dst:inner].
1235+
1236+
Shorthand for:
1237+
- [static_cast] the argument from [outer] to [inner]
1238+
- apply [f]
1239+
- [static_cast] back from [inner] to [outer] *)
1240+
type 'a conjugate :=
1241+
outer:'a ->
1242+
inner:'a ->
1243+
dbg:Debuginfo.t ->
1244+
f:(expression -> expression) ->
1245+
expression ->
1246+
expression
1247+
1248+
(** An IEEE 754 floating-point number *)
1249+
module Float_width : sig
1250+
type t = Cmm.float_width =
1251+
| Float64
1252+
| Float32
1253+
1254+
val static_cast : t static_cast
1255+
end
1256+
1257+
module Signedness : sig
1258+
type t =
1259+
| Signed
1260+
| Unsigned
1261+
1262+
val equal : t -> t -> bool
1263+
1264+
val print : Format.formatter -> t -> unit
1265+
end
1266+
1267+
module type Integral_ops := sig
1268+
type t
1269+
1270+
val print : Format.formatter -> t -> unit
1271+
1272+
val equal : t -> t -> bool
1273+
1274+
val signedness : t -> Signedness.t
1275+
1276+
val with_signedness : t -> signedness:Signedness.t -> t
1277+
1278+
val signed : t -> t
1279+
1280+
val unsigned : t -> t
1281+
1282+
val is_promotable : src:t -> dst:t -> bool
1283+
1284+
val static_cast : t static_cast
1285+
1286+
val conjugate : t conjugate
1287+
end
1288+
1289+
(** An integer that fits into a general-purpose register. It is canonically stored in
1290+
twos-complement representation, in the lower [bits] bits of its container (whether
1291+
that be memory or a register), and is sign- or zero-extended as needed, according
1292+
to [signed]. *)
1293+
module Integer : sig
1294+
type t [@@immediate]
1295+
1296+
val nativeint : t
1297+
1298+
val create_exn : bit_width:int -> signedness:Signedness.t -> t
1299+
1300+
val bit_width : t -> int
1301+
1302+
include Integral_ops with type t := t
1303+
end
1304+
1305+
(** An {!Integer.t} but with the additional stipulation that its lowest bit is always
1306+
set to 1 and is not considered in mathematical operations on the numbers. *)
1307+
module Tagged_integer : sig
1308+
type t [@@immediate]
1309+
1310+
val immediate : t
1311+
1312+
val create_exn :
1313+
bit_width_including_tag_bit:int -> signedness:Signedness.t -> t
1314+
1315+
val bit_width_excluding_tag_bit : t -> int
1316+
1317+
val bit_width_including_tag_bit : t -> int
1318+
1319+
val untagged : t -> Integer.t
1320+
1321+
include Integral_ops with type t := t
1322+
end
1323+
1324+
module Integral : sig
1325+
type t =
1326+
| Untagged of Integer.t
1327+
| Tagged of Tagged_integer.t
1328+
1329+
val nativeint : t
1330+
1331+
(** Gets the integral resulting from untagging the integer (if it is tagged).
1332+
1333+
E.g., you can use [static_cast ~src ~dst:(Untagged (untagged src))] to untag a
1334+
value of type [src]
1335+
*)
1336+
val untagged : t -> Integer.t
1337+
1338+
include Integral_ops with type t := t
1339+
end
1340+
1341+
type t =
1342+
| Integral of Integral.t
1343+
| Float of Float_width.t
1344+
1345+
val static_cast : t static_cast
1346+
1347+
val conjugate : t conjugate
1348+
1349+
module Untagged : sig
1350+
type numeric = t
1351+
1352+
type t =
1353+
| Untagged of Integer.t
1354+
| Float of float_width
1355+
1356+
val to_numeric : t -> numeric
1357+
1358+
val static_cast : t static_cast
1359+
end
1360+
end

0 commit comments

Comments
 (0)