Skip to content

Commit b22815e

Browse files
authored
flambda-backend: Represent mixed records as "faux mixed blocks" in bytecode (#2476)
* Runtime 4 * Runtime 5 * A little less weird behavior in runtime 4 * Move the documentation of 'faux' to a better place, and minimize diff * Fix bug where dummy argument wasn't being pushed * make boot * Fix classification of `interp.c` in upstream Makefile Previously, `interp.c` was classified as both a C source common to native/bytecode AND a C source that was only needed in bytecode. This is clearly confusing and wrong. Upstream just classifies it as a bytecode-only C source. The build passes if I change that. I'll check with Mark before I merge this.
1 parent 8784786 commit b22815e

25 files changed

+167
-95
lines changed

Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,6 @@ runtime4_COMMON_C_SOURCES = \
631631
hash \
632632
instrtrace \
633633
intern \
634-
interp \
635634
ints \
636635
io \
637636
lexing \

boot/ocamlc

659 Bytes
Binary file not shown.

bytecomp/bytegen.ml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ let rec push_dummies n k = match n with
183183

184184
type rhs_kind =
185185
| RHS_block of int
186+
| RHS_faux_mixedblock of int
187+
(* See [instruct.ml] for what the "faux" means. *)
186188
| RHS_infix of { blocksize : int; offset : int }
187189
| RHS_floatblock of int
188190
| RHS_nonrec
@@ -204,7 +206,7 @@ let rec size_of_lambda env = function
204206
| Llet (Strict, _k, id, Lprim (Pduprecord (kind, size), _, _), body)
205207
when check_recordwith_updates id body ->
206208
begin match kind with
207-
| Record_mixed _
209+
| Record_mixed _ -> RHS_faux_mixedblock size
208210
| Record_boxed _ | Record_inlined (_, Variant_boxed _) -> RHS_block size
209211
| Record_unboxed | Record_inlined (_, Variant_unboxed) -> assert false
210212
| Record_float | Record_ufloat -> RHS_floatblock size
@@ -237,7 +239,7 @@ let rec size_of_lambda env = function
237239
| Lprim (Pmakefloatblock _, args, _) ->
238240
RHS_floatblock (List.length args)
239241
| Lprim (Pmakemixedblock (_, _, _), args, _) ->
240-
RHS_block (List.length args)
242+
RHS_faux_mixedblock (List.length args)
241243
| Lprim (Pmakearray (Pgenarray, _, _), _, _) ->
242244
(* Pgenarray is excluded from recursive bindings by the
243245
check in Translcore.check_recursive_lambda *)
@@ -806,6 +808,16 @@ let rec comp_expr stack_info env exp sz cont =
806808
Kconst(Const_base(Const_int blocksize)) ::
807809
Kccall("caml_alloc_dummy_float", 1) :: Kpush ::
808810
comp_init (add_var id (sz+1) new_env) (sz+1) rem
811+
| (id, _exp, RHS_faux_mixedblock blocksize) :: rem ->
812+
(* The -1 argument is unused by [caml_alloc_dummy_mixed]
813+
in bytecode, except to check that it's been set to
814+
this sentinel -1 value.
815+
*)
816+
Kconst(Const_base(Const_int (-1))) ::
817+
Kpush ::
818+
Kconst(Const_base(Const_int blocksize)) ::
819+
Kccall("caml_alloc_dummy_mixed", 2) :: Kpush ::
820+
comp_init (add_var id (sz+1) new_env) (sz+1) rem
809821
| (id, _exp, RHS_block blocksize) :: rem ->
810822
Kconst(Const_base(Const_int blocksize)) ::
811823
Kccall("caml_alloc_dummy", 1) :: Kpush ::
@@ -829,7 +841,8 @@ let rec comp_expr stack_info env exp sz cont =
829841
| [] -> comp_rec new_env sz ndecl decl_size
830842
| (_id, _exp, (RHS_block _ | RHS_infix _ |
831843
RHS_floatblock _ |
832-
RHS_function _))
844+
RHS_function _ |
845+
RHS_faux_mixedblock _))
833846
:: rem ->
834847
comp_nonrec new_env sz (i-1) rem
835848
| (_id, exp, RHS_nonrec) :: rem ->
@@ -839,7 +852,8 @@ let rec comp_expr stack_info env exp sz cont =
839852
| [] -> comp_expr stack_info new_env body sz (add_pop ndecl cont)
840853
| (_id, exp, (RHS_block _ | RHS_infix _ |
841854
RHS_floatblock _ |
842-
RHS_function _))
855+
RHS_function _ |
856+
RHS_faux_mixedblock _))
843857
:: rem ->
844858
comp_expr stack_info new_env exp sz
845859
(Kpush :: Kacc i :: Kccall("caml_update_dummy", 2) ::
@@ -924,7 +938,7 @@ let rec comp_expr stack_info env exp sz cont =
924938
(* CR mixed blocks v1: We will need to use the actual tag instead of [0]
925939
once mixed blocks can have non-zero tags.
926940
*)
927-
(Kmakeblock (total_len, 0) :: cont)
941+
(Kmake_faux_mixedblock (total_len, 0) :: cont)
928942
| Lprim((Pmakearray (kind, _, _)) as p, args, loc) ->
929943
let cont = add_pseudo_event loc !compunit_name cont in
930944
begin match kind with

bytecomp/emitcode.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ let emit_instr = function
257257
if t = 0 then out opATOM0 else (out opATOM; out_int t)
258258
else if n < 4 then (out(opMAKEBLOCK1 + n - 1); out_int t)
259259
else (out opMAKEBLOCK; out_int n; out_int t)
260+
| Kmake_faux_mixedblock(n, t) ->
261+
assert (n > 0);
262+
out opMAKE_FAUX_MIXEDBLOCK; out_int n; out_int t
260263
| Kgetfield n ->
261264
if n < 4 then out(opGETFIELD0 + n) else (out opGETFIELD; out_int n)
262265
| Ksetfield n ->

bytecomp/instruct.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ type instruction =
7070
| Ksetglobal of Ident.t
7171
| Kconst of structured_constant
7272
| Kmakeblock of int * int (* size, tag *)
73+
| Kmake_faux_mixedblock of int * int (* size, tag *)
7374
| Kmakefloatblock of int
7475
| Kgetfield of int
7576
| Ksetfield of int

bytecomp/instruct.mli

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ type instruction =
7878
| Ksetglobal of Ident.t
7979
| Kconst of structured_constant
8080
| Kmakeblock of int * int (* size, tag *)
81+
| Kmake_faux_mixedblock of int * int (* size, tag *)
82+
(* A "faux" mixed block is not actually represented as a mixed block at
83+
runtime. It just has the top header byte sent to a sentinel value so
84+
bytecode knows that the block can't be marshaled to native code, where
85+
mixed records are represented as true mixed blocks.
86+
*)
8187
| Kmakefloatblock of int
8288
| Kgetfield of int
8389
| Ksetfield of int

bytecomp/printinstr.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ let instruction ppf = function
4646
fprintf ppf "@[<10>\tconst@ %a@]" Printlambda.structured_constant cst
4747
| Kmakeblock(n, m) ->
4848
fprintf ppf "\tmakeblock %i, %i" n m
49+
| Kmake_faux_mixedblock(n, m) ->
50+
fprintf ppf "\tmake_faux_mixedblock %i, %i" n m
4951
| Kmakefloatblock(n) ->
5052
fprintf ppf "\tmakefloatblock %i" n
5153
| Kgetfield n -> fprintf ppf "\tgetfield %i" n

runtime/alloc.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ CAMLexport value caml_alloc_with_reserved (mlsize_t wosize, tag_t tag,
3737
mlsize_t i;
3838

3939
// Optimization: for mixed blocks, don't fill in non-scannable fields
40-
mlsize_t scannable_wosize =
41-
Is_mixed_block_reserved(reserved)
42-
? Mixed_block_scannable_wosize_reserved(reserved)
43-
: wosize;
40+
mlsize_t scannable_wosize = Scannable_wosize_reserved(reserved, wosize);
4441

4542
CAMLassert (tag < Num_tags);
4643
CAMLassert (tag != Infix_tag);
@@ -364,18 +361,23 @@ CAMLprim value caml_alloc_dummy_float (value size)
364361
CAMLprim value caml_alloc_dummy_mixed (value size, value scannable_size)
365362
{
366363
mlsize_t wosize = Long_val(size);
364+
#ifdef NATIVE_CODE
367365
mlsize_t scannable_wosize = Long_val(scannable_size);
368-
#ifdef NATIVECODE
369366
/* The below code runs for bytecode and native code, and critically assumes
370367
that a double record field can be stored in one word. That's true both for
371368
32-bit and 64-bit bytecode (as a double record field in a mixed record is
372369
always boxed), and for 64-bit native code (as the double record field is
373370
stored flat, taking up 1 word).
374-
*/
371+
*/
375372
CAML_STATIC_ASSERT(Double_wosize == 1);
376-
#endif
377373
reserved_t reserved =
378374
Reserved_mixed_block_scannable_wosize(scannable_wosize);
375+
#else
376+
/* [scannable_size] can't be used meaningfully in bytecode */
377+
(void)scannable_size;
378+
CAMLassert(scannable_size == Val_int(-1));
379+
reserved_t reserved = Faux_mixed_block_sentinel;
380+
#endif // NATIVE_CODE
379381
return caml_alloc_with_reserved (wosize, 0, reserved);
380382
}
381383

runtime/caml/instruct.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ enum instructions {
6262
RERAISE, RAISE_NOTRACE,
6363
GETSTRINGCHAR,
6464
PERFORM, RESUME, RESUMETERM, REPERFORMTERM,
65+
MAKE_FAUX_MIXEDBLOCK,
6566
FIRST_UNIMPLEMENTED_OP};
6667

6768
#endif /* CAML_INTERNALS */

runtime/caml/mlvalues.h

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,18 @@ mixed blocks. In the upstream compiler, R is set with the
153153
/* Header bits reserved for mixed blocks */
154154

155155
#define Is_mixed_block_reserved(res) (((reserved_t)(res)) > 0)
156-
#define Mixed_block_scannable_wosize_reserved(res) (((reserved_t)(res)) - 1)
156+
157+
#ifdef NATIVE_CODE
158+
#define Scannable_wosize_val(val) (Scannable_wosize_hd (Hd_val (val)))
157159
#define Reserved_mixed_block_scannable_wosize(sz) (((mlsize_t)(sz)) + 1)
160+
#define Mixed_block_scannable_wosize_reserved_native(res) (((reserved_t)(res)) - 1)
161+
162+
Caml_inline mlsize_t Scannable_wosize_reserved(reserved_t res, mlsize_t sz) {
163+
return
164+
Is_mixed_block_reserved(res)
165+
? Mixed_block_scannable_wosize_reserved_native(res)
166+
: sz;
167+
}
158168

159169
/* The scannable size of a block is how many fields are values as opposed
160170
to flat floats/ints/etc. This is different than the (normal) size of a
@@ -164,10 +174,10 @@ mixed blocks. In the upstream compiler, R is set with the
164174
an OCaml value. (e.g. polymorphic comparison, GC marking/sweeping)
165175
All of these traversals must be written to have one of the following
166176
properties:
167-
- it's known that the input can never be a mixed block,
168-
- it raises an exception on mixed blocks, or
169-
- it uses the scannable size (not the normal size) to figure out which
170-
fields to recursively descend into.
177+
- it's known that the input can never be a mixed block,
178+
- it raises an exception on mixed blocks, or
179+
- it uses the scannable size (not the normal size) to figure out which
180+
fields to recursively descend into.
171181
172182
Otherwise, the traversal could attempt to recursively descend into
173183
a flat field, which could segfault (or worse).
@@ -176,10 +186,22 @@ Caml_inline mlsize_t Scannable_wosize_hd(header_t hd) {
176186
reserved_t res = Reserved_hd(hd);
177187
return
178188
Is_mixed_block_reserved(res)
179-
? Mixed_block_scannable_wosize_reserved(res)
189+
? Mixed_block_scannable_wosize_reserved_native(res)
180190
: Wosize_hd(hd);
181191
}
182192

193+
#else
194+
#define Scannable_wosize_hd(val) (Wosize_hd (val))
195+
#define Scannable_wosize_val(val) (Wosize_hd (Hd_val (val)))
196+
#define Faux_mixed_block_sentinel ((reserved_t) 0xff)
197+
198+
// In bytecode always use the size of the block as the scannable size
199+
Caml_inline mlsize_t Scannable_wosize_reserved(reserved_t res, mlsize_t size) {
200+
return size;
201+
}
202+
203+
#endif // NATIVE_CODE
204+
183205
/* Color values are pre-shifted */
184206

185207
#define Color_hd(hd) ((hd) & HEADER_COLOR_MASK)
@@ -233,8 +255,6 @@ Caml_inline mlsize_t Scannable_wosize_hd(header_t hd) {
233255

234256
#define Reserved_val(val) (Reserved_hd (Hd_val (val)))
235257

236-
#define Scannable_wosize_val(val) (Scannable_wosize_hd (Hd_val (val)))
237-
238258
#ifdef ARCH_BIG_ENDIAN
239259
#define Tag_val(val) (((volatile unsigned char *) (val)) [-1])
240260
/* Also an l-value. */

runtime/fix_code.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ int* caml_init_opcode_nargs(void)
117117

118118
/* Instructions with two operands */
119119
l[APPTERM] = l[CLOSURE] = l[PUSHGETGLOBALFIELD] =
120-
l[GETGLOBALFIELD] = l[MAKEBLOCK] = l[C_CALLN] =
120+
l[GETGLOBALFIELD] = l[MAKEBLOCK] = l[MAKE_FAUX_MIXEDBLOCK] = l[C_CALLN] =
121121
l[BEQ] = l[BNEQ] = l[BLTINT] = l[BLEINT] = l[BGTINT] = l[BGEINT] =
122122
l[BULTINT] = l[BUGEINT] = l[GETPUBMET] = 2;
123123

runtime/instrtrace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ void caml_disasm_instr(code_t pc)
8585
snprintf(buf, sizeof(buf), "%s %d\n", opbuf, pc[0]); break;
8686
/* Instructions with two operands */
8787
case APPTERM: case CLOSURE: case CLOSUREREC: case PUSHGETGLOBALFIELD:
88-
case GETGLOBALFIELD: case MAKEBLOCK:
88+
case GETGLOBALFIELD: case MAKEBLOCK: case MAKE_FAUX_MIXEDBLOCK:
8989
case BEQ: case BNEQ: case BLTINT: case BLEINT: case BGTINT: case BGEINT:
9090
case BULTINT: case BUGEINT:
9191
snprintf(buf, sizeof(buf), "%s %d, %d\n", opbuf, pc[0], pc[1]); break;

runtime/interp.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,25 @@ value caml_interprete(code_t prog, asize_t prog_size)
840840
accu = block;
841841
Next;
842842
}
843+
Instruct(MAKE_FAUX_MIXEDBLOCK): {
844+
mlsize_t wosize = *pc++;
845+
tag_t tag = *pc++;
846+
mlsize_t i;
847+
value block;
848+
if (wosize <= Max_young_wosize) {
849+
Alloc_small_with_reserved(block, wosize, tag, Enter_gc,
850+
Faux_mixed_block_sentinel);
851+
Field(block, 0) = accu;
852+
for (i = 1; i < wosize; i++) Field(block, i) = *sp++;
853+
} else {
854+
block =
855+
caml_alloc_shr_reserved(wosize, tag, Faux_mixed_block_sentinel);
856+
caml_initialize(&Field(block, 0), accu);
857+
for (i = 1; i < wosize; i++) caml_initialize(&Field(block, i), *sp++);
858+
}
859+
accu = block;
860+
Next;
861+
}
843862
Instruct(MAKEFLOATBLOCK): {
844863
mlsize_t size = *pc++;
845864
mlsize_t i;

runtime/major_gc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ Caml_noinline static intnat do_some_marking(struct mark_stack* stk,
920920
reserved_t reserved = Reserved_hd(hd);
921921
if (Is_mixed_block_reserved(reserved)) {
922922
uintnat scannable_wosize =
923-
Mixed_block_scannable_wosize_reserved(reserved);
923+
Scannable_wosize_reserved(reserved, Wosize_hd(hd));
924924
me.end = me.start + scannable_wosize;
925925
budget -= Wosize_hd(hd) - scannable_wosize; /* unscannable suffix */
926926
} else {

runtime4/alloc.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,7 @@ CAMLexport value caml_alloc_with_reserved (mlsize_t wosize, tag_t tag,
4040
mlsize_t i;
4141

4242
// Optimization: for mixed blocks, don't fill in non-scannable fields
43-
mlsize_t scannable_wosize =
44-
Is_mixed_block_reserved(reserved)
45-
? Mixed_block_scannable_wosize_reserved(reserved)
46-
: wosize;
43+
mlsize_t scannable_wosize = Scannable_wosize_reserved(reserved, wosize);
4744

4845
CAMLassert (tag < 256);
4946
CAMLassert (tag != Infix_tag);
@@ -70,12 +67,14 @@ CAMLexport value caml_alloc (mlsize_t wosize, tag_t tag) {
7067
return caml_alloc_with_reserved (wosize, tag, 0);
7168
}
7269

70+
#if NATIVE_CODE
7371
CAMLexport value caml_alloc_mixed (mlsize_t wosize, tag_t tag,
7472
mlsize_t scannable_prefix) {
7573
reserved_t reserved =
7674
Reserved_mixed_block_scannable_wosize(scannable_prefix);
7775
return caml_alloc_with_reserved (wosize, tag, reserved);
7876
}
77+
#endif // NATIVE_CODE
7978

8079
CAMLexport value caml_alloc_small_with_reserved (mlsize_t wosize, tag_t tag,
8180
reserved_t reserved)
@@ -254,18 +253,23 @@ CAMLprim value caml_alloc_dummy_float (value size)
254253
CAMLprim value caml_alloc_dummy_mixed (value size, value scannable_size)
255254
{
256255
mlsize_t wosize = Long_val(size);
256+
#ifdef NATIVE_CODE
257257
mlsize_t scannable_wosize = Long_val(scannable_size);
258-
#ifdef NATIVECODE
259258
/* The below code runs for bytecode and native code, and critically assumes
260259
that a double record field can be stored in one word. That's true both for
261260
32-bit and 64-bit bytecode (as a double record field in a mixed record is
262261
always boxed), and for 64-bit native code (as the double record field is
263262
stored flat, taking up 1 word).
264263
*/
265264
CAML_STATIC_ASSERT(Double_wosize == 1);
266-
#endif
267265
reserved_t reserved =
268266
Reserved_mixed_block_scannable_wosize(scannable_wosize);
267+
#else
268+
/* [scannable_size] can't be used meaningfully in bytecode */
269+
(void)scannable_size;
270+
CAMLassert(scannable_size == Val_int(-1));
271+
reserved_t reserved = Faux_mixed_block_sentinel;
272+
#endif // NATIVE_CODE
269273
return caml_alloc_with_reserved (wosize, 0, reserved);
270274
}
271275

runtime4/caml/instruct.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ enum instructions {
6161
EVENT, BREAK,
6262
RERAISE, RAISE_NOTRACE,
6363
GETSTRINGCHAR,
64+
MAKE_FAUX_MIXEDBLOCK,
6465
FIRST_UNIMPLEMENTED_OP};
6566

6667
#endif /* CAML_INTERNALS */

0 commit comments

Comments
 (0)