Skip to content

Commit 3c3f66c

Browse files
committed
flambda2-types: New n-way join algorithm
The existing join algorithm suffers from several drawbacks: - It can be slow due to the use of a quadratic algorithm, taking up to 60% of the total compilation time in -O3 mode in pathological cases (lambda_to_flambda_primitives.ml). See also ocaml-flambda#3300. - It is inefficient as it computes the join of all types appearing in *any* joined environment prior to filtering out the types that are not needed, instead of first computing the types whose join will be needed. - It is sensitive to the names of local variables that only exist in some of the joined environments but not in the target environment. - It relies on a global binding time of variables across all joined environments and the target environment that does not exist, as figured in ocaml-flambda#3278. Subsequently, it can lose aliasing information, and breaks typing env invariants by recording the same variable as defined multiple times (with dubious semantics). This patch implements a new join algorithm, based on a n-way join of types. The new algorithm is: - Faster, as it avoids quadratic complexity (outside of complex nesting of env extensions). Compared to the existing join algorithm (with advanced meet), on my machine, the new join algorithm is 30x faster on the pathological lambda_to_flambda_primitives.ml, taking only around 10% of the total compilation time and speeding up the compilation of the file by 3.5x. On camlinternalFormat.ml, the new join is about 2.5-3x faster, reducing the time spent in the join from 20% to less than 10% and speeding up the total compilation time by about 20%. - More efficient, as it only computes a join if it can possibly result in a more precise type, i.e. if the variable has been assigned a new type in all joined environments (otherwise the existing type in the target environment is already the most precise). - Independent of the names of local variables. - Only depends on a consistent binding time *order* of the shared variables (defined in both the target environment and all joined environments), which is respected. Since the result is independent of the binding times of local / existential variables, the typing env invariants are respected.
1 parent f937946 commit 3c3f66c

17 files changed

+5733
-1055
lines changed

driver/flambda_backend_flags.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ module Flambda2 = struct
134134
let cse_depth = 2
135135
let join_depth = 5
136136
let function_result_types = Never
137-
let meet_algorithm = Basic
137+
let meet_algorithm = Advanced
138138
let enable_reaper = false
139139
let unicode = true
140140
end

middle_end/flambda2/tests/meet_test.ml

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,153 @@ let meet_variants_don't_lose_aliases () =
139139
Format.eprintf "@[<hov 2>meet:@ %a@]@.@[<hov 2>env:@ %a@]@." T.print
140140
tag_meet_ty TE.print tag_meet_env)
141141

142+
let test_join_with_extensions () =
143+
let define ?(kind = K.value) env v =
144+
let v' = Bound_var.create v Name_mode.normal in
145+
TE.add_definition env (Bound_name.create_var v') kind
146+
in
147+
let env = create_env () in
148+
let y = Variable.create "y" in
149+
let x = Variable.create "x" in
150+
let a = Variable.create "a" in
151+
let b = Variable.create "b" in
152+
let env = define env y in
153+
let env = define env x in
154+
let env = define ~kind:K.naked_immediate env a in
155+
let env = define ~kind:K.naked_immediate env b in
156+
let tag_0 = Tag.Scannable.zero in
157+
let tag_1 = Option.get (Tag.Scannable.of_tag (Tag.create_exn 1)) in
158+
let make ty =
159+
T.variant
160+
~const_ctors:(T.bottom K.naked_immediate)
161+
~non_const_ctors:
162+
(Tag.Scannable.Map.of_list
163+
[ tag_0, (K.Block_shape.Scannable Value_only, [ty]);
164+
tag_1, (K.Block_shape.Scannable Value_only, []) ])
165+
Alloc_mode.For_types.heap
166+
in
167+
let env = TE.add_equation env (Name.var y) (make (T.unknown K.value)) in
168+
let scope = TE.current_scope env in
169+
let scoped_env = TE.increment_scope env in
170+
let left_env =
171+
TE.add_equation scoped_env (Name.var x)
172+
(T.tagged_immediate_alias_to ~naked_immediate:a)
173+
in
174+
let right_env =
175+
TE.add_equation scoped_env (Name.var x)
176+
(T.tagged_immediate_alias_to ~naked_immediate:b)
177+
in
178+
let ty_a = make (T.tagged_immediate_alias_to ~naked_immediate:a) in
179+
let ty_b = make (T.tagged_immediate_alias_to ~naked_immediate:b) in
180+
let left_env = TE.add_equation left_env (Name.var y) ty_a in
181+
let right_env =
182+
match T.meet right_env ty_a ty_b with
183+
| Ok (ty, right_env) -> TE.add_equation right_env (Name.var y) ty
184+
| Bottom -> assert false
185+
in
186+
Format.eprintf "Left:@.%a@." TE.print left_env;
187+
Format.eprintf "Right:@.%a@." TE.print right_env;
188+
let joined_env =
189+
T.cut_and_n_way_join scoped_env
190+
[ left_env, Apply_cont_rewrite_id.create (), Inlinable;
191+
right_env, Apply_cont_rewrite_id.create (), Inlinable ]
192+
~params:Bound_parameters.empty ~cut_after:scope
193+
~extra_allowed_names:Name_occurrences.empty
194+
~extra_lifted_consts_in_use_envs:Symbol.Set.empty
195+
in
196+
Format.eprintf "Res:@.%a@." TE.print joined_env
197+
198+
let test_join_with_complex_extensions () =
199+
let define ?(kind = K.value) env v =
200+
let v' = Bound_var.create v Name_mode.normal in
201+
TE.add_definition env (Bound_name.create_var v') kind
202+
in
203+
let env = create_env () in
204+
let y = Variable.create "y" in
205+
let x = Variable.create "x" in
206+
let w = Variable.create "w" in
207+
let z = Variable.create "z" in
208+
let a = Variable.create "a" in
209+
let b = Variable.create "b" in
210+
let c = Variable.create "c" in
211+
let d = Variable.create "d" in
212+
let env = define env z in
213+
let env = define env x in
214+
let env = define env y in
215+
let env = define env w in
216+
let env = define ~kind:K.naked_immediate env a in
217+
let env = define ~kind:K.naked_immediate env b in
218+
let env = define ~kind:K.naked_immediate env c in
219+
let env = define ~kind:K.naked_immediate env d in
220+
let tag_0 = Tag.Scannable.zero in
221+
let tag_1 = Option.get (Tag.Scannable.of_tag (Tag.create_exn 1)) in
222+
let make tys =
223+
T.variant
224+
~const_ctors:(T.bottom K.naked_immediate)
225+
~non_const_ctors:
226+
(Tag.Scannable.Map.of_list
227+
[ tag_0, (K.Block_shape.Scannable Value_only, tys);
228+
tag_1, (K.Block_shape.Scannable Value_only, []) ])
229+
Alloc_mode.For_types.heap
230+
in
231+
let env =
232+
TE.add_equation env (Name.var z)
233+
(make [T.unknown K.value; T.unknown K.value])
234+
in
235+
let scope = TE.current_scope env in
236+
let scoped_env = TE.increment_scope env in
237+
let left_env =
238+
TE.add_equation scoped_env (Name.var x)
239+
(T.tagged_immediate_alias_to ~naked_immediate:a)
240+
in
241+
let left_env =
242+
TE.add_equation left_env (Name.var y)
243+
(T.tagged_immediate_alias_to ~naked_immediate:a)
244+
in
245+
let left_env =
246+
TE.add_equation left_env (Name.var w)
247+
(T.tagged_immediate_alias_to ~naked_immediate:a)
248+
in
249+
let right_env =
250+
TE.add_equation scoped_env (Name.var x)
251+
(T.tagged_immediate_alias_to ~naked_immediate:b)
252+
in
253+
let right_env =
254+
TE.add_equation right_env (Name.var y)
255+
(T.tagged_immediate_alias_to ~naked_immediate:c)
256+
in
257+
let right_env =
258+
TE.add_equation right_env (Name.var w)
259+
(T.tagged_immediate_alias_to ~naked_immediate:d)
260+
in
261+
let ty_a =
262+
make
263+
[ T.tagged_immediate_alias_to ~naked_immediate:b;
264+
T.tagged_immediate_alias_to ~naked_immediate:b ]
265+
in
266+
let ty_b =
267+
make
268+
[ T.tagged_immediate_alias_to ~naked_immediate:c;
269+
T.tagged_immediate_alias_to ~naked_immediate:d ]
270+
in
271+
let left_env = TE.add_equation left_env (Name.var z) ty_a in
272+
let right_env =
273+
match T.meet right_env ty_a ty_b with
274+
| Ok (ty, right_env) -> TE.add_equation right_env (Name.var z) ty
275+
| Bottom -> assert false
276+
in
277+
Format.eprintf "Left:@.%a@." TE.print left_env;
278+
Format.eprintf "Right:@.%a@." TE.print right_env;
279+
let joined_env =
280+
T.cut_and_n_way_join scoped_env
281+
[ left_env, Apply_cont_rewrite_id.create (), Inlinable;
282+
right_env, Apply_cont_rewrite_id.create (), Inlinable ]
283+
~params:Bound_parameters.empty ~cut_after:scope
284+
~extra_allowed_names:Name_occurrences.empty
285+
~extra_lifted_consts_in_use_envs:Symbol.Set.empty
286+
in
287+
Format.eprintf "Res:@.%a@." TE.print joined_env
288+
142289
let test_meet_two_blocks () =
143290
let define env v =
144291
let v' = Bound_var.create v Name_mode.normal in
@@ -274,4 +421,8 @@ let () =
274421
Format.eprintf "@.MEET ALIAS TO RECOVER @\n@.";
275422
test_meet_recover_alias ();
276423
Format.eprintf "@.MEET BOTTOM AFTER ALIAS@\n@.";
277-
test_meet_bottom_after_alias ()
424+
test_meet_bottom_after_alias ();
425+
Format.eprintf "@.JOIN WITH EXTENSIONS@\n@.";
426+
test_join_with_extensions ();
427+
Format.eprintf "@.JOIN WITH COMPLEX EXTENSIONS@\n@.";
428+
test_join_with_complex_extensions ()

0 commit comments

Comments
 (0)