Skip to content

Commit 0559555

Browse files
authored
Distinguish between "light" and "heavy" flambda2 invariants (#3205)
* Distinguish between "light" and "heavy" flambda2 invariants Light invariants can be checked in the CI, while heavy invariants are intended for local debugging. More precisely: - Light invariants must be fast to check and have low complexity. A guideline is that it is acceptable to call `find` or `mem` functions, but they should not use any kind of iterations iterations (`iter`, `fold`, `exists`, etc.). It must be viable to enable light invariants in regular user workflows without significantly impacting the performance of the compiler. Light invariants are enabled by the pre-existing `-dflambda-invariants` compiler flag, which is also enabled with `--enable-flambda-invariants` configure flag. To determine in code whether light invariants should be checked, call the function `Flambda_features.check_light_invariants ()`. Light invariants are checked on CI jobs, which should prevent drifts between the expressed invariants and the actual invariants. - Heavy invariants are allowed to be (almost) unbounded in complexity. They are not intended to be enabled during regular workflows, but rather to be used to help identify the source of a bug once it has been identified that one exists. It is fine if enabling heavy invariants drastically slow down the compiler on some inputs. Heavy invariants are enabled by a new `-dflambda-heavy-invariants` compiler flag. This flag implies `-dflambda-invariants`, so light invariants are always checked when heavy invariants are checked. To determine in code whether heavy invariants should be checked, call the function `Flambda_features.check_invariants ()`. Heavy invariants are *not* checked on CI jobs at the moment. They currently include invariants that are known to be broken and should be investigated before being moved to the light invariants. The name of the compiler flags (invariants/heavy invariants) and feature checks (light invariants/invariants) is chosen to reflect that users should usually enable light checks only, but calling `check_light_invariants ()` is a reminder to make sure the invariant does not do any expensive computation. A few invariants related to the typing env and aliases that currently hold and perform no iterations are moved to the set of light invariants. The goal is that invariants are slowly moved from the heavy set to the light set over time when it seems useful. * Support OCAMLPARAM=flambda-invariants=heavy
1 parent 21614a5 commit 0559555

File tree

10 files changed

+54
-14
lines changed

10 files changed

+54
-14
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
ocamlparam: '_,O3=1'
5656

5757
- name: flambda2_o3_advanced_meet_frame_pointers_runtime5_polling
58-
config: --enable-middle-end=flambda2 --enable-frame-pointers --enable-runtime5 --enable-poll-insertion
58+
config: --enable-middle-end=flambda2 --enable-frame-pointers --enable-runtime5 --enable-poll-insertion --enable-flambda-invariants
5959
os: ubuntu-latest
6060
build_ocamlparam: ''
6161
ocamlparam: '_,O3=1,flambda2-meet-algorithm=advanced,flambda2-expert-cont-lifting-budget=200'
@@ -68,7 +68,7 @@ jobs:
6868
ocamlparam: '_,O3=1,flambda2-meet-algorithm=advanced,flambda2-expert-cont-lifting-budget=200'
6969

7070
- name: flambda2_frame_pointers_oclassic_polling
71-
config: --enable-middle-end=flambda2 --enable-frame-pointers --enable-poll-insertion
71+
config: --enable-middle-end=flambda2 --enable-frame-pointers --enable-poll-insertion --enable-flambda-invariants
7272
os: ubuntu-latest
7373
build_ocamlparam: ''
7474
ocamlparam: '_,Oclassic=1'

driver/compenv.ml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,21 @@ let set_compiler_pass ppf ~name v flag ~filter =
188188
"Please specify at most one %s <pass>." name
189189
end
190190

191+
let decode_flambda_invariants ppf v ~name =
192+
match v with
193+
| "0" -> Some No_checks
194+
| "1" -> Some Light_checks
195+
| "heavy" -> Some Heavy_checks
196+
| _ ->
197+
Printf.ksprintf (print_error ppf)
198+
"bad value %s for %s" v name;
199+
None
200+
201+
let set_flambda_invariants ppf ~name v flag =
202+
match decode_flambda_invariants ppf v ~name with
203+
| None -> ()
204+
| Some checks -> flag := checks
205+
191206
(* 'can-discard=' specifies which arguments can be discarded without warning
192207
because they are not understood by some versions of OCaml. *)
193208
let can_discard = ref []
@@ -353,7 +368,7 @@ let read_one_param ppf position name v =
353368
| "flambda-verbose" ->
354369
set "flambda-verbose" [ dump_flambda_verbose ] v
355370
| "flambda-invariants" ->
356-
set "flambda-invariants" [ flambda_invariant_checks ] v
371+
set_flambda_invariants ppf ~name v flambda_invariant_checks
357372
| "cmm-invariants" ->
358373
set "cmm-invariants" [ cmm_invariants ] v
359374
| "linscan" ->

driver/main_args.ml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,10 @@ let mk_dflambda_invariants f =
809809
"-dflambda-invariants", Arg.Unit f, " Check Flambda (1 and 2) invariants"
810810
;;
811811

812+
let mk_dflambda_heavy_invariants f =
813+
"-dflambda-heavy-invariants", Arg.Unit f, " Check Flambda 2 heavy invariants"
814+
;;
815+
812816
let mk_dflambda_no_invariants f =
813817
"-dflambda-no-invariants", Arg.Unit f, " Do not check Flambda (1 and 2) \
814818
invariants"
@@ -1132,6 +1136,7 @@ module type Optcommon_options = sig
11321136
val _dflambda : unit -> unit
11331137
val _drawflambda : unit -> unit
11341138
val _dflambda_invariants : unit -> unit
1139+
val _dflambda_heavy_invariants : unit -> unit
11351140
val _dflambda_no_invariants : unit -> unit
11361141
val _dflambda_let : int -> unit
11371142
val _dflambda_verbose : unit -> unit
@@ -1573,6 +1578,7 @@ struct
15731578
mk_dflambda F._dflambda;
15741579
mk_drawflambda F._drawflambda;
15751580
mk_dflambda_invariants F._dflambda_invariants;
1581+
mk_dflambda_heavy_invariants F._dflambda_heavy_invariants;
15761582
mk_dflambda_no_invariants F._dflambda_no_invariants;
15771583
mk_dflambda_let F._dflambda_let;
15781584
mk_dflambda_verbose F._dflambda_verbose;
@@ -1937,9 +1943,13 @@ module Default = struct
19371943
let _dcombine = set dump_combine
19381944
let _dcse = set dump_cse
19391945
let _dflambda = set dump_flambda
1940-
let _dflambda_invariants = set flambda_invariant_checks
1946+
let _dflambda_heavy_invariants () =
1947+
flambda_invariant_checks := Heavy_checks
1948+
let _dflambda_invariants () =
1949+
flambda_invariant_checks := Light_checks
19411950
let _dflambda_let stamp = dump_flambda_let := (Some stamp)
1942-
let _dflambda_no_invariants = clear flambda_invariant_checks
1951+
let _dflambda_no_invariants () =
1952+
flambda_invariant_checks := No_checks
19431953
let _dflambda_verbose () =
19441954
set dump_flambda (); set dump_flambda_verbose ()
19451955
let _dinterval = set dump_interval

driver/main_args.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ module type Optcommon_options = sig
225225
val _dflambda : unit -> unit
226226
val _drawflambda : unit -> unit
227227
val _dflambda_invariants : unit -> unit
228+
val _dflambda_heavy_invariants : unit -> unit
228229
val _dflambda_no_invariants : unit -> unit
229230
val _dflambda_let : int -> unit
230231
val _dflambda_verbose : unit -> unit

middle_end/flambda2/types/env/aliases.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ end = struct
152152
(function
153153
| None -> Some (Name.Map.singleton elt coercion_to_canonical)
154154
| Some elts ->
155-
if Flambda_features.check_invariants ()
155+
if Flambda_features.check_light_invariants ()
156156
then assert (not (Name.Map.mem elt elts));
157157
Some (Name.Map.add elt coercion_to_canonical elts))
158158
t.aliases
@@ -646,7 +646,7 @@ let add_alias_between_canonical_elements ~binding_time_resolver
646646
let aliases_of_to_be_demoted =
647647
get_aliases_of_canonical_element t ~canonical_element:to_be_demoted
648648
in
649-
if Flambda_features.check_invariants ()
649+
if Flambda_features.check_light_invariants ()
650650
then
651651
Simple.pattern_match canonical_element
652652
~const:(fun _ -> ())
@@ -808,7 +808,7 @@ let add ~binding_time_resolver ~binding_times_and_modes t
808808
(Simple.coercion element2_with_coercion)
809809
~then_:(Coercion.inverse (Simple.coercion element1_with_coercion))
810810
in
811-
if Flambda_features.check_invariants ()
811+
if Flambda_features.check_light_invariants ()
812812
then (
813813
if Simple.equal canonical_element1 canonical_element2
814814
then

middle_end/flambda2/types/env/typing_env.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ let add_definition t (name : Bound_name.t) kind =
708708

709709
let invariant_for_alias (t : t) name ty =
710710
(* Check that no canonical element gets an [Equals] type *)
711-
if Flambda_features.check_invariants () || true
711+
if Flambda_features.check_light_invariants ()
712712
then
713713
match TG.get_alias_exn ty with
714714
| exception Not_found -> ()
@@ -798,7 +798,7 @@ let rec add_equation0 (t : t) name ty =
798798
with_current_level t ~current_level
799799

800800
and add_equation1 ~raise_on_bottom t name ty ~(meet_type : meet_type) =
801-
(if Flambda_features.check_invariants ()
801+
(if Flambda_features.check_light_invariants ()
802802
then
803803
let existing_ty = find t name None in
804804
if not (K.equal (TG.kind existing_ty) (TG.kind ty))
@@ -807,7 +807,7 @@ and add_equation1 ~raise_on_bottom t name ty ~(meet_type : meet_type) =
807807
"Cannot add equation %a = %a@ given existing binding %a = %a@ whose \
808808
type is of a different kind:@ %a"
809809
Name.print name TG.print ty Name.print name TG.print existing_ty print t);
810-
(if Flambda_features.check_invariants ()
810+
(if Flambda_features.check_light_invariants ()
811811
then
812812
match TG.get_alias_exn ty with
813813
| exception Not_found -> ()

middle_end/flambda2/ui/flambda_features.ml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,15 @@ let unicode () =
9999
!Flambda_backend_flags.Flambda2.unicode
100100
|> with_default ~f:(fun d -> d.unicode)
101101

102-
let check_invariants () = !Clflags.flambda_invariant_checks
102+
let check_invariants () =
103+
match !Clflags.flambda_invariant_checks with
104+
| No_checks | Light_checks -> false
105+
| Heavy_checks -> true
106+
107+
let check_light_invariants () =
108+
match !Clflags.flambda_invariant_checks with
109+
| No_checks -> false
110+
| Light_checks | Heavy_checks -> true
103111

104112
type dump_target = Flambda_backend_flags.Flambda2.Dump.target =
105113
| Nowhere

middle_end/flambda2/ui/flambda_features.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@ val colour : unit -> Misc.Color.setting option
6060

6161
val unicode : unit -> bool
6262

63+
(** Check all invariants (light and heavy). *)
6364
val check_invariants : unit -> bool
6465

66+
val check_light_invariants : unit -> bool
67+
6568
type dump_target = Flambda_backend_flags.Flambda2.Dump.target =
6669
| Nowhere
6770
| Main_dump_stream

utils/clflags.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ end
5454

5555
type profile_column = [ `Time | `Alloc | `Top_heap | `Abs_top_heap | `Counters ]
5656
type profile_granularity_level = File_level | Function_level | Block_level
57+
type flambda_invariant_checks = No_checks | Light_checks | Heavy_checks
5758

5859
let compile_only = ref false (* -c *)
5960
and output_name = ref (None : string option) (* -o *)
@@ -183,7 +184,8 @@ let cmm_invariants =
183184
ref Config.with_cmm_invariants (* -dcmm-invariants *)
184185

185186
let flambda_invariant_checks =
186-
ref Config.with_flambda_invariants (* -flambda-(no-)invariants *)
187+
let v = if Config.with_flambda_invariants then Light_checks else No_checks in
188+
ref v (* -flambda-(no-)invariants *)
187189

188190
let dont_write_files = ref false (* set to true under ocamldoc *)
189191

utils/clflags.mli

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ end
6161

6262
type profile_column = [ `Time | `Alloc | `Top_heap | `Abs_top_heap | `Counters ]
6363
type profile_granularity_level = File_level | Function_level | Block_level
64+
type flambda_invariant_checks = No_checks | Light_checks | Heavy_checks
6465

6566
val objfiles : string list ref
6667
val ccobjs : string list ref
@@ -207,7 +208,7 @@ val profile_columns : profile_column list ref
207208
val profile_granularity : profile_granularity_level ref
208209
val all_profile_granularity_levels : string list
209210
val set_profile_granularity : string -> unit
210-
val flambda_invariant_checks : bool ref
211+
val flambda_invariant_checks : flambda_invariant_checks ref
211212
val unbox_closures : bool ref
212213
val unbox_closures_factor : int ref
213214
val default_unbox_closures_factor : int

0 commit comments

Comments
 (0)