Skip to content

Commit 1f7a273

Browse files
ncik-robertsccasin
andcommitted
Basic mixed blocks for float# in runtime 5 (#2380)
* Records mixing immediates, floats, and float64s Should be configured with: --enable-runtime5 --disable-naked-pointers --enable-reserved-header-bits=8 * Cleanup of names * Slightly more intelligible implementation of record shape detection * Small simplification + remove comment * More renames than before * Raise on poly compare and hash * Fix some, but not all, bugs in bytecomp: we segfault on the 100-generation * Fix off-by-one for zero value prefix len * No more runtime mixed blocks in bytecode Instead, just use normal blocks. This only "drops support" for 64 bit bytecode as 32 bit would just not work. (Also we don't have bits to spare in the header in 32 bit.) * Macroize things more * Support weak pointer shallow copy * A few more places where we need to check for mixed blocks * Most issues fixed * Rename more 'abstract' things to 'mixed' * Remove ability to mix boxed floats with unboxed floats * Fix bugs and more accurately track offsets * Fix bugs and more accurately track budgets * Get let-rec working with mixed blocks * Clarify comment * Use corrected-style tests and actually run the small generated examples * Fix recursive values test * Fix typo in generated TEST stanza * comment and format * Restore support for floats * Flesh out the test suite a bit to cover records with floats in the prefix * Fix bug * Small tweaks to comments / bugfixes in dead code * Fix up Chris's old tests * Cleanup and comments * Commit to storing floats flat in mixed-float-float# blocks * Actually test all floats mixed records * Finish resolving type errors related to conflicts after merge * Resolve some CRs * Resolve more CRs * Clarify that bytecode operations don't raise * Back out an unnecessary change to backend/cmm_helpers.ml * Back out probably unnecessary changes to cmmgen.ml * Add test for too many fields to show error message * Fix local test to actually test something. Use better macros. * Make polymorphic hash raise for mixed blocks * Fix updating of dummy blocks * Add some comments about mixed blocks * Revert unintentional changes to runtime4 * make fmt * Move mixed records to layouts alpha * Always set reserved header bits to 8 * Reenable support for enable-profinfo-width in runtime 4 * Fix segfault in printing + in no-allocness of hash * Most of stedolan's comments * Adopt stedolan's suggestion for structure of `oldify_one` and `oldify_mopup` * Accept TheNumbat's suggestions * Address rest of @TheNumbat's comments * Revert change to conflict markers irrelevant to this PR * no u * Segregate runtime 4 and 5 tests * Fix typo * Clarify comment * Clarify comment * Factor out a gnarly function * Use mixed_block version of primitives for getting/setting value fields * make fmt * Fix bug in all-float mixed records and fix accidental omission in tests * Segregate tests for all-float mixed records and mixed blocks * Correct comment in float64 tests * Rework test structure * Comment raisiness * Fix bug in printing * Fix confusing name * Flat_imm_element -> Imm_element * Reshuffle tests so we don't get error message clashes between runtime 4 and 5 * Stop unnecessarily numbering tests * Fix upstream build * 'Fix' upstream build * Respond to stedolan's comments * Respond to review of @TheNumbat and @lthls * Fix bug in oldify_one * Re-enable test of recursive value (accidentally disabled) and allow recursive mixed blocks * Simplify generated test code, and just check in full test * Remove unnecessary test.reference file * Fix printing bug in bytecode * Allow the Obj.double_field call in printing to work on mixed blocks * Fix tests that I accidentally broke * Continue rejecting mixed blocks from runtime 4 type-checker * Resolve hash CR: implement hash differently in native code vs. bytecode * Revert to hashing a constant for mixed blocks * Just take the hash of the scannable prefix * Minimize needless diff in runtime * Re-enable an accidentally disabled test and fix a bug related to Obj.with_tag * Slightly more consistent name (`caml_alloc_small_with_reserved`) * Add missing functionality and test for mixed block over young wosize limit (so is allocated via a different code path) * add new function to headers --------- Co-authored-by: Chris Casinghino <[email protected]> (cherry picked from commit 4fce201)
1 parent d49e464 commit 1f7a273

File tree

87 files changed

+10677
-335
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+10677
-335
lines changed

backend/cmm_helpers.ml

Lines changed: 153 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,97 @@ let mk_load_atomic memory_chunk =
6464

6565
let floatarray_tag dbg = Cconst_int (Obj.double_array_tag, dbg)
6666

67+
type t =
68+
| Scan_all
69+
| Scan_prefix of int
70+
71+
module Mixed_block_support : sig
72+
val assert_mixed_block_support : unit -> unit
73+
74+
val make_header : Nativeint.t -> scannable_prefix:int -> Nativeint.t
75+
end = struct
76+
(* CR mixed blocks v1: This "8" is duplicated in [typedecl.ml]. We should fix
77+
up this duplication when we make the "8" configurable. *)
78+
let required_reserved_header_bits = 8
79+
80+
let required_addr_size_bits = 64
81+
82+
(* Many of these checks are duplicated *)
83+
84+
(* CR mixed blocks v1: This is also duplicated in [typedecl.ml]. *)
85+
(* Why 2? We'd subtract 1 if the mixed block encoding could use all 8 bits of
86+
the prefix. But the all-0 prefix means "not a mixed block", so we can't use
87+
the all-0 pattern, and we must subtract 2 instead. *)
88+
let max_scannable_prefix = (1 lsl required_reserved_header_bits) - 2
89+
90+
let max_header =
91+
(1 lsl (required_addr_size_bits - required_reserved_header_bits)) - 1
92+
|> Nativeint.of_int
93+
94+
let assert_mixed_block_support =
95+
lazy
96+
(if not Config.runtime5
97+
then Misc.fatal_error "Mixed blocks are only supported in runtime5";
98+
if not Config.native_compiler
99+
then Misc.fatal_error "Mixed blocks are only supported in native code";
100+
let reserved_header_bits = Config.reserved_header_bits in
101+
let addr_size_bits = Arch.size_addr * 8 in
102+
match
103+
( reserved_header_bits = required_reserved_header_bits,
104+
addr_size_bits = required_addr_size_bits )
105+
with
106+
| true, true -> ()
107+
| false, true ->
108+
Misc.fatal_errorf
109+
"Need %d reserved header bits for mixed blocks; got %d"
110+
required_reserved_header_bits reserved_header_bits
111+
| _, false ->
112+
Misc.fatal_errorf
113+
"Mixed blocks only supported on %d bit platforms; got %d"
114+
required_addr_size_bits addr_size_bits)
115+
116+
let assert_mixed_block_support () = Lazy.force assert_mixed_block_support
117+
118+
let make_header header ~scannable_prefix =
119+
assert_mixed_block_support ();
120+
if scannable_prefix > max_scannable_prefix
121+
then
122+
Misc.fatal_errorf "Scannable prefix too big (%d > %d)" scannable_prefix
123+
max_scannable_prefix;
124+
(* This means we crash the compiler if someone tries to write a mixed record
125+
with too many fields, but you effectively can't: you'd need something
126+
like 2^46 fields. *)
127+
if header > max_header
128+
then
129+
Misc.fatal_errorf
130+
"Header too big for the mixed block encoding to be added (%nd > %nd)"
131+
header max_header;
132+
Nativeint.add
133+
(Nativeint.shift_left
134+
(Nativeint.of_int (scannable_prefix + 1))
135+
(required_addr_size_bits - required_reserved_header_bits))
136+
header
137+
end
138+
67139
(* CR mshinwell: update to use NOT_MARKABLE terminology *)
68-
let block_header tag sz =
69-
Nativeint.add
70-
(Nativeint.shift_left (Nativeint.of_int sz) 10)
71-
(Nativeint.of_int tag)
140+
let block_header ?(scannable_prefix = Scan_all) tag sz =
141+
let hdr =
142+
Nativeint.add
143+
(Nativeint.shift_left (Nativeint.of_int sz) 10)
144+
(Nativeint.of_int tag)
145+
in
146+
match scannable_prefix with
147+
| Scan_all -> hdr
148+
| Scan_prefix scannable_prefix ->
149+
Mixed_block_support.make_header hdr ~scannable_prefix
72150

73151
(* Static data corresponding to "value"s must be marked black in case we are in
74152
no-naked-pointers mode. See [caml_darken] and the code below that emits
75153
structured constants and static module definitions. *)
76154
let black_block_header tag sz = Nativeint.logor (block_header tag sz) caml_black
77155

78-
let local_block_header tag sz = Nativeint.logor (block_header tag sz) caml_local
156+
let local_block_header ?scannable_prefix tag sz =
157+
Nativeint.logor (block_header ?scannable_prefix tag sz) caml_local
79158

80159
let white_closure_header sz = block_header Obj.closure_tag sz
81160

@@ -1318,15 +1397,16 @@ let call_cached_method obj tag cache pos args args_type result (apos, mode) dbg
13181397

13191398
(* Allocation *)
13201399

1321-
let make_alloc_generic ~mode set_fn dbg tag wordsize args =
1400+
let make_alloc_generic ?(scannable_prefix = Scan_all) ~mode set_fn dbg tag
1401+
wordsize args =
13221402
(* allocs of size 0 must be statically allocated else the Gc will bug *)
13231403
assert (List.compare_length_with args 0 > 0);
13241404
if Lambda.is_local_mode mode || wordsize <= Config.max_young_wosize
13251405
then
13261406
let hdr =
13271407
match mode with
1328-
| Lambda.Alloc_local -> local_block_header tag wordsize
1329-
| Lambda.Alloc_heap -> block_header tag wordsize
1408+
| Lambda.Alloc_local -> local_block_header ~scannable_prefix tag wordsize
1409+
| Lambda.Alloc_heap -> block_header ~scannable_prefix tag wordsize
13301410
in
13311411
Cop (Calloc mode, Cconst_natint (hdr, dbg) :: args, dbg)
13321412
else
@@ -1335,17 +1415,26 @@ let make_alloc_generic ~mode set_fn dbg tag wordsize args =
13351415
| [] -> Cvar id
13361416
| e1 :: el ->
13371417
Csequence
1338-
( set_fn (Cvar id) (Cconst_int (idx, dbg)) e1 dbg,
1339-
fill_fields (idx + 2) el )
1418+
( set_fn idx (Cvar id) (int_const dbg idx) e1 dbg,
1419+
fill_fields (idx + 1) el )
1420+
in
1421+
let caml_alloc_func, caml_alloc_args =
1422+
match Config.runtime5, scannable_prefix with
1423+
| true, Scan_all -> "caml_alloc_shr_check_gc", [wordsize; tag]
1424+
| false, Scan_all -> "caml_alloc", [wordsize; tag]
1425+
| true, Scan_prefix prefix_len ->
1426+
Mixed_block_support.assert_mixed_block_support ();
1427+
"caml_alloc_mixed_shr_check_gc", [wordsize; tag; prefix_len]
1428+
| false, Scan_prefix _ ->
1429+
Misc.fatal_error
1430+
"mixed blocks not implemented for runtime 4. (It uses the PROFINFO \
1431+
configuration instead of HEADER_RESERVED_WORDS.)"
13401432
in
13411433
Clet
13421434
( VP.create id,
13431435
Cop
13441436
( Cextcall
1345-
{ func =
1346-
(if Config.runtime5
1347-
then "caml_alloc_shr_check_gc"
1348-
else "caml_alloc");
1437+
{ func = caml_alloc_func;
13491438
ty = typ_val;
13501439
alloc = true;
13511440
builtin = false;
@@ -1354,33 +1443,61 @@ let make_alloc_generic ~mode set_fn dbg tag wordsize args =
13541443
coeffects = Has_coeffects;
13551444
ty_args = []
13561445
},
1357-
[Cconst_int (wordsize, dbg); Cconst_int (tag, dbg)],
1446+
List.map (fun arg -> Cconst_int (arg, dbg)) caml_alloc_args,
13581447
dbg ),
1359-
fill_fields 1 args )
1448+
fill_fields 0 args )
1449+
1450+
let addr_array_init arr ofs newval dbg =
1451+
Cop
1452+
( Cextcall
1453+
{ func = "caml_initialize";
1454+
ty = typ_void;
1455+
alloc = false;
1456+
builtin = false;
1457+
returns = true;
1458+
effects = Arbitrary_effects;
1459+
coeffects = Has_coeffects;
1460+
ty_args = []
1461+
},
1462+
[array_indexing log2_size_addr arr ofs dbg; newval],
1463+
dbg )
13601464

13611465
let make_alloc ~mode dbg tag args =
1362-
let addr_array_init arr ofs newval dbg =
1363-
Cop
1364-
( Cextcall
1365-
{ func = "caml_initialize";
1366-
ty = typ_void;
1367-
alloc = false;
1368-
builtin = false;
1369-
returns = true;
1370-
effects = Arbitrary_effects;
1371-
coeffects = Has_coeffects;
1372-
ty_args = []
1373-
},
1374-
[array_indexing log2_size_addr arr ofs dbg; newval],
1375-
dbg )
1376-
in
1377-
make_alloc_generic ~mode addr_array_init dbg tag (List.length args) args
1466+
make_alloc_generic ~mode
1467+
(fun _ arr ofs newval dbg -> addr_array_init arr ofs newval dbg)
1468+
dbg tag (List.length args) args
13781469

13791470
let make_float_alloc ~mode dbg tag args =
1380-
make_alloc_generic ~mode float_array_set dbg tag
1471+
make_alloc_generic ~mode
1472+
(fun _ -> float_array_set)
1473+
dbg tag
13811474
(List.length args * size_float / size_addr)
13821475
args
13831476

1477+
let make_mixed_alloc ~mode dbg shape args =
1478+
let ({ value_prefix_len; flat_suffix } : Lambda.mixed_block_shape) = shape in
1479+
(* args with shape [Float] must already have been unboxed. *)
1480+
let set_fn idx arr ofs newval dbg =
1481+
if idx < value_prefix_len
1482+
then addr_array_init arr ofs newval dbg
1483+
else
1484+
match flat_suffix.(idx - value_prefix_len) with
1485+
| Imm -> int_array_set arr ofs newval dbg
1486+
| Float | Float64 -> float_array_set arr ofs newval dbg
1487+
in
1488+
let size =
1489+
let values, floats = Lambda.count_mixed_block_values_and_floats shape in
1490+
if size_float <> size_addr
1491+
then
1492+
Misc.fatal_error
1493+
"Unable to compile mixed blocks on a platform where a float is not the \
1494+
same width as a value.";
1495+
values + floats
1496+
in
1497+
make_alloc_generic ~scannable_prefix:(Scan_prefix value_prefix_len) ~mode
1498+
(* CR mixed blocks v1: Support inline record args to variants. *)
1499+
set_fn dbg Obj.first_non_constant_constructor_tag size args
1500+
13841501
(* Record application and currying functions *)
13851502

13861503
let apply_function_name arity result (mode : Lambda.alloc_mode) =
@@ -3738,3 +3855,6 @@ let allocate_unboxed_int64_array =
37383855

37393856
let allocate_unboxed_nativeint_array =
37403857
allocate_unboxed_int64_or_nativeint_array custom_ops_unboxed_nativeint_array
3858+
3859+
(* Drop internal optional arguments from exported interface *)
3860+
let block_header x y = block_header x y

backend/cmm_helpers.mli

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,15 @@ val make_alloc :
291291
val make_float_alloc :
292292
mode:Lambda.alloc_mode -> Debuginfo.t -> int -> expression list -> expression
293293

294+
(** Allocate an mixed block of the corresponding shape. Initial values of
295+
the flat suffix should be provided unboxed. *)
296+
val make_mixed_alloc :
297+
mode:Lambda.alloc_mode ->
298+
Debuginfo.t ->
299+
Lambda.mixed_block_shape ->
300+
expression list ->
301+
expression
302+
294303
(** Sys.opaque_identity *)
295304
val opaque : expression -> Debuginfo.t -> expression
296305

middle_end/flambda2/from_lambda/closure_conversion.ml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -795,8 +795,9 @@ let close_primitive acc env ~let_bound_ids_with_kinds named
795795
| Some exn_continuation -> exn_continuation
796796
in
797797
close_raise0 acc env ~raise_kind ~arg ~dbg exn_continuation
798-
| (Pmakeblock _ | Pmakefloatblock _ | Pmakeufloatblock _ | Pmakearray _), []
799-
->
798+
| ( ( Pmakeblock _ | Pmakefloatblock _ | Pmakeufloatblock _ | Pmakearray _
799+
| Pmakemixedblock _ ),
800+
[] ) ->
800801
(* Special case for liftable empty block or array *)
801802
let acc, sym =
802803
match prim with
@@ -814,6 +815,8 @@ let close_primitive acc env ~let_bound_ids_with_kinds named
814815
Misc.fatal_error "Unexpected empty float block in [Closure_conversion]"
815816
| Pmakeufloatblock _ ->
816817
Misc.fatal_error "Unexpected empty float# block in [Closure_conversion]"
818+
| Pmakemixedblock _ ->
819+
Misc.fatal_error "Unexpected empty mixed block in [Closure_conversion]"
817820
| Pmakearray (array_kind, _, _mode) ->
818821
let array_kind = Empty_array_kind.of_lambda array_kind in
819822
register_const0 acc (Static_const.empty_array array_kind) "empty_array"
@@ -822,10 +825,10 @@ let close_primitive acc env ~let_bound_ids_with_kinds named
822825
| Pfield _ | Pfield_computed _ | Psetfield _ | Psetfield_computed _
823826
| Pfloatfield _ | Psetfloatfield _ | Pduprecord _ | Pccall _ | Praise _
824827
| Pufloatfield _ | Psetufloatfield _ | Psequand | Psequor | Pnot | Pnegint
825-
| Paddint | Psubint | Pmulint | Pdivint _ | Pmodint _ | Pandint | Porint
826-
| Pxorint | Plslint | Plsrint | Pasrint | Pintcomp _ | Pcompare_ints
827-
| Pcompare_floats _ | Pcompare_bints _ | Poffsetint _ | Poffsetref _
828-
| Pintoffloat _
828+
| Pmixedfield _ | Psetmixedfield _ | Paddint | Psubint | Pmulint
829+
| Pdivint _ | Pmodint _ | Pandint | Porint | Pxorint | Plslint | Plsrint
830+
| Pasrint | Pintcomp _ | Pcompare_ints | Pcompare_floats _
831+
| Pcompare_bints _ | Poffsetint _ | Poffsetref _ | Pintoffloat _
829832
| Pfloatofint (_, _)
830833
| Pnegfloat (_, _)
831834
| Pabsfloat (_, _)

middle_end/flambda2/from_lambda/dissect_letrec.ml

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ type block_type =
121121
| Normal of int
122122
(* tag *)
123123
| Flat_float_record
124+
| Mixed of Lambda.mixed_block_shape
124125

125126
type block =
126127
{ block_type : block_type;
@@ -270,6 +271,11 @@ let rec prepare_letrec (recursive_set : Ident.Set.t)
270271
match current_let with
271272
| Some cl -> build_block cl (List.length args) Flat_float_record lam letrec
272273
| None -> dead_code lam letrec)
274+
| Lprim (Pmakemixedblock (_, shape, mode), args, _) -> (
275+
assert_not_local ~lam mode;
276+
match current_let with
277+
| Some cl -> build_block cl (List.length args) (Mixed shape) lam letrec
278+
| None -> dead_code lam letrec)
273279
| Lprim (Pduprecord (kind, size), args, _) -> (
274280
match current_let with
275281
| Some cl -> (
@@ -286,6 +292,9 @@ let rec prepare_letrec (recursive_set : Ident.Set.t)
286292
build_block cl (size + 1) (Normal 0) arg letrec
287293
| Record_float | Record_ufloat ->
288294
build_block cl size Flat_float_record arg letrec
295+
| Record_mixed mixed ->
296+
let mixed = Lambda.transl_mixed_record_shape mixed in
297+
build_block cl size (Mixed mixed) arg letrec
289298
| Record_inlined (Extension _, _)
290299
| Record_inlined (Ordinary _, (Variant_unboxed | Variant_extensible))
291300
| Record_unboxed ->
@@ -563,16 +572,29 @@ let dissect_letrec ~bindings ~body ~free_vars_kind =
563572
}
564573
in
565574
let preallocations =
575+
let alloc_normal_dummy cfun size =
576+
let desc = Lambda.simple_prim_on_values ~name:cfun ~arity:1 ~alloc:true in
577+
let size : lambda = Lconst (Const_base (Const_int size)) in
578+
Lprim (Pccall desc, [size], Loc_unknown)
579+
in
580+
let alloc_mixed_dummy cfun (shape : Lambda.mixed_block_shape) size =
581+
let size = Lconst (Const_base (Const_int size)) in
582+
let value_prefix_len =
583+
Lconst (Const_base (Const_int shape.value_prefix_len))
584+
in
585+
let desc = Lambda.simple_prim_on_values ~name:cfun ~arity:2 ~alloc:true in
586+
Lprim (Pccall desc, [size; value_prefix_len], Loc_unknown)
587+
in
566588
List.map
567589
(fun (id, { block_type; size }) ->
568-
let fn =
590+
let ccall =
569591
match block_type with
570-
| Normal _tag -> "caml_alloc_dummy"
571-
| Flat_float_record -> "caml_alloc_dummy_float"
592+
| Normal _tag -> alloc_normal_dummy "caml_alloc_dummy" size
593+
| Flat_float_record ->
594+
alloc_normal_dummy "caml_alloc_dummy_float" size
595+
| Mixed shape -> alloc_mixed_dummy "caml_alloc_dummy_mixed" shape size
572596
in
573-
let desc = Lambda.simple_prim_on_values ~name:fn ~arity:1 ~alloc:true in
574-
let size : lambda = Lconst (Const_base (Const_int size)) in
575-
id, Lprim (Pccall desc, [size], Loc_unknown))
597+
id, ccall)
576598
letrec.blocks
577599
in
578600
let body = if not letrec.needs_region then body else Lexclave body in

middle_end/flambda2/from_lambda/lambda_to_flambda.ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -622,9 +622,10 @@ let primitive_can_raise (prim : Lambda.primitive) =
622622
| Pmakefloatblock _ | Pfield _ | Pfield_computed _ | Psetfield _
623623
| Psetfield_computed _ | Pfloatfield _ | Psetfloatfield _ | Pduprecord _
624624
| Pmakeufloatblock _ | Pufloatfield _ | Psetufloatfield _ | Psequand | Psequor
625-
| Pnot | Pnegint | Paddint | Psubint | Pmulint | Pandint | Porint | Pxorint
626-
| Plslint | Plsrint | Pasrint | Pintcomp _ | Pcompare_ints | Pcompare_floats _
627-
| Pcompare_bints _ | Poffsetint _ | Poffsetref _ | Pintoffloat _
625+
| Pmixedfield _ | Psetmixedfield _ | Pmakemixedblock _ | Pnot | Pnegint
626+
| Paddint | Psubint | Pmulint | Pandint | Porint | Pxorint | Plslint | Plsrint
627+
| Pasrint | Pintcomp _ | Pcompare_ints | Pcompare_floats _ | Pcompare_bints _
628+
| Poffsetint _ | Poffsetref _ | Pintoffloat _
628629
| Pfloatofint (_, _)
629630
| Pnegfloat (_, _)
630631
| Pabsfloat (_, _)

0 commit comments

Comments
 (0)