Skip to content

Commit bfd913c

Browse files
authored
flambda-backend: Infer needed zero_alloc checks from signatures (#2742)
1 parent bdab106 commit bfd913c

25 files changed

+910
-368
lines changed

compilerlibs/Makefile.compilerlibs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ TYPING = \
8989
typing/jkind_types.cmo \
9090
typing/primitive.cmo \
9191
typing/shape.cmo \
92+
typing/zero_alloc.cmo \
9293
typing/types.cmo \
9394
typing/jkind.cmo \
9495
typing/btype.cmo \

dune

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@
8888
jane_syntax ; manual update: mli only files asttypes parsetree
8989

9090
;; TYPING
91-
ident path jkind primitive shape shape_reduce types btype oprint subst predef datarepr
91+
ident path jkind primitive shape shape_reduce zero_alloc types btype oprint subst
92+
predef datarepr
9293
global_module cmi_format persistent_env env errortrace mode jkind_types jkind_intf
9394
typedtree printtyped ctype printtyp includeclass mtype envaux includecore
9495
tast_iterator tast_mapper signature_group cmt_format cms_format untypeast
@@ -291,6 +292,7 @@
291292
(jkind.mli as compiler-libs/jkind.mli)
292293
(jkind_types.mli as compiler-libs/jkind_types.mli)
293294
(primitive.mli as compiler-libs/primitive.mli)
295+
(zero_alloc.mli as compiler-libs/zero_alloc.mli)
294296
(types.mli as compiler-libs/types.mli)
295297
(btype.mli as compiler-libs/btype.mli)
296298
(binutils.mli as compiler-libs/binutils.mli)

lambda/translcore.ml

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,38 @@ let can_apply_primitive p pmode pos args =
350350
end
351351
end
352352

353+
let zero_alloc_of_application
354+
~num_args (annotation : Zero_alloc.assume option) funct =
355+
match annotation, funct.exp_desc with
356+
| Some assume, _ ->
357+
(* The user wrote a zero_alloc attribute on the application - keep it. *)
358+
Builtin_attributes.assume_zero_alloc assume
359+
| None, Texp_ident (_, _, { val_zero_alloc; _ }, _, _) ->
360+
(* We assume the call is zero_alloc if the function is known to be
361+
zero_alloc. If the function is zero_alloc opt, then we need to be sure
362+
that the opt checks were run to license this assumption. We judge
363+
whether the opt checks were run based on the argument to the
364+
[-zero-alloc-check] command line flag. *)
365+
let use_opt =
366+
match !Clflags.zero_alloc_check with
367+
| Check_default | No_check -> false
368+
| Check_all | Check_opt_only -> true
369+
in
370+
begin match Zero_alloc.get val_zero_alloc with
371+
| Check c when c.arity = num_args && (use_opt || not c.opt) ->
372+
let assume : Zero_alloc.assume =
373+
{ strict = c.strict;
374+
never_returns_normally = false;
375+
never_raises = false;
376+
arity = c.arity;
377+
loc = c.loc }
378+
in
379+
Builtin_attributes.assume_zero_alloc assume
380+
| Check _ | Default_zero_alloc | Ignore_assert_all | Assume _ ->
381+
Zero_alloc_utils.Assume_info.none
382+
end
383+
| None, _ -> Zero_alloc_utils.Assume_info.none
384+
353385
let rec transl_exp ~scopes sort e =
354386
transl_exp1 ~scopes ~in_new_scope:false sort e
355387

@@ -387,7 +419,7 @@ and transl_exp0 ~in_new_scope ~scopes sort e =
387419
| Texp_apply({ exp_desc = Texp_ident(path, _, {val_kind = Val_prim p},
388420
Id_prim (pmode, psort), _);
389421
exp_type = prim_type; } as funct,
390-
oargs, pos, ap_mode, assume_zero_alloc)
422+
oargs, pos, ap_mode, zero_alloc)
391423
when can_apply_primitive p pmode pos oargs ->
392424
let rec cut_args prim_repr oargs =
393425
match prim_repr, oargs with
@@ -409,6 +441,11 @@ and transl_exp0 ~in_new_scope ~scopes sort e =
409441
if extra_args = [] then transl_apply_position pos
410442
else Rc_normal
411443
in
444+
let assume_zero_alloc =
445+
match zero_alloc with
446+
| None -> Zero_alloc_utils.Assume_info.none
447+
| Some assume -> Builtin_attributes.assume_zero_alloc assume
448+
in
412449
let lam =
413450
let loc =
414451
map_scopes (update_assume_zero_alloc ~assume_zero_alloc)
@@ -433,14 +470,17 @@ and transl_exp0 ~in_new_scope ~scopes sort e =
433470
~position ~mode
434471
~result_layout lam extra_args (of_location ~scopes e.exp_loc))
435472
end
436-
| Texp_apply(funct, oargs, position, ap_mode, assume_zero_alloc)
473+
| Texp_apply(funct, oargs, position, ap_mode, zero_alloc)
437474
->
438475
let tailcall = Translattribute.get_tailcall_attribute funct in
439476
let inlined = Translattribute.get_inlined_attribute funct in
440477
let specialised = Translattribute.get_specialised_attribute funct in
441478
let result_layout = layout_exp sort e in
442479
let position = transl_apply_position position in
443480
let mode = transl_locality_mode_l ap_mode in
481+
let assume_zero_alloc =
482+
zero_alloc_of_application ~num_args:(List.length oargs) zero_alloc funct
483+
in
444484
event_after ~scopes e
445485
(transl_apply ~scopes ~tailcall ~inlined ~specialised
446486
~assume_zero_alloc
@@ -1579,8 +1619,13 @@ and transl_function ~in_new_scope ~scopes e params body
15791619
~zero_alloc =
15801620
let attrs = e.exp_attributes in
15811621
let mode = transl_alloc_mode_r alloc_mode in
1622+
let zero_alloc = Zero_alloc.get zero_alloc in
15821623
let assume_zero_alloc =
1583-
Builtin_attributes.assume_zero_alloc ~is_check_allowed:true zero_alloc
1624+
match zero_alloc with
1625+
| Default_zero_alloc | Check _ | Ignore_assert_all ->
1626+
Zero_alloc_utils.Assume_info.none
1627+
| Assume assume ->
1628+
Builtin_attributes.assume_zero_alloc assume
15841629
in
15851630
let scopes =
15861631
if in_new_scope then

otherlibs/dynlink/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ COMPILERLIBS_SOURCES=\
121121
typing/shape.ml \
122122
typing/jkind_types.ml \
123123
typing/primitive.ml \
124+
typing/zero_alloc.ml \
124125
typing/types.ml \
125126
typing/jkind.ml \
126127
typing/typedtree.ml \

otherlibs/dynlink/dune

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
jkind_intf
9595
jkind_types
9696
primitive
97+
zero_alloc
9798
types
9899
jkind
99100
value_rec_types
@@ -198,6 +199,7 @@
198199
(copy_files ../../typing/solver.ml)
199200
(copy_files ../../typing/shape_reduce.ml)
200201
(copy_files ../../typing/mode.ml)
202+
(copy_files ../../typing/zero_alloc.ml)
201203
(copy_files ../../typing/types.ml)
202204
(copy_files ../../typing/btype.ml)
203205
(copy_files ../../typing/subst.ml)
@@ -267,6 +269,7 @@
267269
(copy_files ../../typing/solver.mli)
268270
(copy_files ../../typing/shape_reduce.mli)
269271
(copy_files ../../typing/mode.mli)
272+
(copy_files ../../typing/zero_alloc.mli)
270273
(copy_files ../../typing/types.mli)
271274
(copy_files ../../typing/btype.mli)
272275
(copy_files ../../typing/subst.mli)
@@ -379,6 +382,7 @@
379382
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Mode.cmo
380383
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Jkind_intf.cmo
381384
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Jkind_types.cmo
385+
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Zero_alloc.cmo
382386
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Types.cmo
383387
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Attr_helper.cmo
384388
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Primitive.cmo
@@ -462,6 +466,7 @@
462466
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Mode.cmx
463467
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Jkind_intf.cmx
464468
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Jkind_types.cmx
469+
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Zero_alloc.cmx
465470
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Types.cmx
466471
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Attr_helper.cmx
467472
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Primitive.cmx

parsing/builtin_attributes.ml

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -681,20 +681,26 @@ let error_message_attr l =
681681
| _ -> None in
682682
List.find_map inner l
683683

684+
type zero_alloc_check =
685+
{ strict: bool;
686+
opt: bool;
687+
arity: int;
688+
loc: Location.t;
689+
}
690+
691+
type zero_alloc_assume =
692+
{ strict: bool;
693+
never_returns_normally: bool;
694+
never_raises: bool;
695+
arity: int;
696+
loc: Location.t;
697+
}
698+
684699
type zero_alloc_attribute =
685700
| Default_zero_alloc
686701
| Ignore_assert_all
687-
| Check of { strict: bool;
688-
opt: bool;
689-
arity: int;
690-
loc: Location.t;
691-
}
692-
| Assume of { strict: bool;
693-
never_returns_normally: bool;
694-
never_raises: bool;
695-
arity: int;
696-
loc: Location.t;
697-
}
702+
| Check of zero_alloc_check
703+
| Assume of zero_alloc_assume
698704

699705
let is_zero_alloc_check_enabled ~opt =
700706
match !Clflags.zero_alloc_check with
@@ -923,25 +929,26 @@ let get_zero_alloc_attribute ~in_signature ~default_arity l =
923929
register_zero_alloc_attribute attr.attr_name);
924930
res
925931

926-
let assume_zero_alloc ~is_check_allowed check : Zero_alloc_utils.Assume_info.t =
927-
match check with
928-
| Default_zero_alloc -> Zero_alloc_utils.Assume_info.none
929-
| Ignore_assert_all -> Zero_alloc_utils.Assume_info.none
930-
| Assume { strict; never_returns_normally; never_raises; } ->
931-
Zero_alloc_utils.Assume_info.create ~strict ~never_returns_normally ~never_raises
932+
let zero_alloc_attribute_only_assume_allowed za =
933+
match za with
934+
| Assume assume -> Some assume
935+
| Default_zero_alloc | Ignore_assert_all -> None
932936
| Check { loc; _ } ->
933-
if not is_check_allowed then begin
934-
let name = "zero_alloc" in
935-
let msg = "Only the following combinations are supported in this context: \
936-
'zero_alloc assume', \
937-
`zero_alloc assume strict`, \
938-
`zero_alloc assume error`,\
939-
`zero_alloc assume never_returns_normally`,\
940-
`zero_alloc assume never_returns_normally strict`."
941-
in
942-
Location.prerr_warning loc (Warnings.Attribute_payload (name, msg))
943-
end;
944-
Zero_alloc_utils.Assume_info.none
937+
let name = "zero_alloc" in
938+
let msg = "Only the following combinations are supported in this context: \
939+
'zero_alloc assume', \
940+
`zero_alloc assume strict`, \
941+
`zero_alloc assume error`,\
942+
`zero_alloc assume never_returns_normally`,\
943+
`zero_alloc assume never_returns_normally strict`."
944+
in
945+
Location.prerr_warning loc (Warnings.Attribute_payload (name, msg));
946+
None
947+
948+
let assume_zero_alloc assume : Zero_alloc_utils.Assume_info.t =
949+
match assume with
950+
| { strict; never_returns_normally; never_raises; } ->
951+
Zero_alloc_utils.Assume_info.create ~strict ~never_returns_normally ~never_raises
945952

946953
type tracing_probe =
947954
{ name : string;

parsing/builtin_attributes.mli

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -237,30 +237,36 @@ val parse_optional_id_payload :
237237
string -> Location.t -> empty:'a -> (string * 'a) list ->
238238
Parsetree.payload -> ('a,unit) Result.t
239239

240-
(* Support for property attributes like zero_alloc *)
240+
(* Support for zero_alloc *)
241+
type zero_alloc_check =
242+
{ strict: bool;
243+
(* [strict=true] property holds on all paths.
244+
[strict=false] if the function returns normally,
245+
then the property holds (but property violations on
246+
exceptional returns or diverging loops are ignored).
247+
This definition may not be applicable to new properties. *)
248+
opt: bool;
249+
arity: int;
250+
loc: Location.t;
251+
}
252+
253+
type zero_alloc_assume =
254+
{ strict: bool;
255+
never_returns_normally: bool;
256+
never_raises: bool;
257+
(* [never_raises=true] the function never returns
258+
via an exception. The function (directly or transitively)
259+
may raise exceptions that do not escape, i.e.,
260+
handled before the function returns. *)
261+
arity: int;
262+
loc: Location.t;
263+
}
264+
241265
type zero_alloc_attribute =
242266
| Default_zero_alloc
243267
| Ignore_assert_all
244-
| Check of { strict: bool;
245-
(* [strict=true] property holds on all paths.
246-
[strict=false] if the function returns normally,
247-
then the property holds (but property violations on
248-
exceptional returns or diverging loops are ignored).
249-
This definition may not be applicable to new properties. *)
250-
opt: bool;
251-
arity: int;
252-
loc: Location.t;
253-
}
254-
| Assume of { strict: bool;
255-
never_returns_normally: bool;
256-
never_raises: bool;
257-
(* [never_raises=true] the function never returns
258-
via an exception. The function (directly or transitively)
259-
may raise exceptions that do not escape, i.e.,
260-
handled before the function returns. *)
261-
arity: int;
262-
loc: Location.t;
263-
}
268+
| Check of zero_alloc_check
269+
| Assume of zero_alloc_assume
264270

265271
val is_zero_alloc_check_enabled : opt:bool -> bool
266272

@@ -271,8 +277,12 @@ val get_zero_alloc_attribute :
271277
in_signature:bool -> default_arity:int -> Parsetree.attributes ->
272278
zero_alloc_attribute
273279

274-
val assume_zero_alloc :
275-
is_check_allowed:bool -> zero_alloc_attribute -> Zero_alloc_utils.Assume_info.t
280+
(* This returns the [zero_alloc_assume] if the input is an assume. Otherwise,
281+
it returns None. If the input attribute is [Check], this issues a warning. *)
282+
val zero_alloc_attribute_only_assume_allowed :
283+
zero_alloc_attribute -> zero_alloc_assume option
284+
285+
val assume_zero_alloc : zero_alloc_assume -> Zero_alloc_utils.Assume_info.t
276286

277287
type tracing_probe =
278288
{ name : string;

0 commit comments

Comments
 (0)