Skip to content

Commit 5fdd91c

Browse files
authored
Direct conversion of lambda switches to ilambda (#429)
1 parent 7a0d56d commit 5fdd91c

File tree

6 files changed

+103
-142
lines changed

6 files changed

+103
-142
lines changed

lambda/lambda.ml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,6 @@ and lambda_switch =
467467
sw_numblocks: int;
468468
sw_blocks: (lambda_switch_block_key * lambda) list;
469469
sw_failaction : lambda option;
470-
sw_tags_to_sizes : Targetint.OCaml.t Tag.Scannable.Map.t;
471470
}
472471

473472
and lambda_switch_block_key =
@@ -948,7 +947,6 @@ let shallow_map f = function
948947
sw_numblocks = sw.sw_numblocks;
949948
sw_blocks = List.map (fun (n, e) -> (n, f e)) sw.sw_blocks;
950949
sw_failaction = Option.map f sw.sw_failaction;
951-
sw_tags_to_sizes = sw.sw_tags_to_sizes;
952950
},
953951
loc)
954952
| Lstringswitch (e, sw, default, loc) ->

lambda/lambda.mli

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,6 @@ and lambda_switch =
339339
sw_numblocks: int; (* Number of tag block cases *)
340340
sw_blocks: (lambda_switch_block_key * lambda) list; (* Tag block cases *)
341341
sw_failaction : lambda option; (* Action to take if failure *)
342-
sw_tags_to_sizes : Targetint.OCaml.t Tag.Scannable.Map.t;
343342
}
344343

345344
and lambda_switch_block_key =

lambda/matching.ml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,7 +1999,6 @@ let inline_lazy_force_switch arg loc =
19991999
} )
20002000
];
20012001
sw_failaction = Some varg;
2002-
sw_tags_to_sizes = Tag.Scannable.Map.empty;
20032002
},
20042003
loc ) ) )
20052004

@@ -2457,7 +2456,6 @@ module SArg = struct
24572456
sw_numblocks = 0;
24582457
sw_blocks = [];
24592458
sw_failaction = None;
2460-
sw_tags_to_sizes = Tag.Scannable.Map.empty;
24612459
},
24622460
loc )
24632461

@@ -2959,7 +2957,6 @@ let combine_constructor loc arg pat_env cstr partial ctx def
29592957
sw_numblocks = cstr.cstr_nonconsts;
29602958
sw_blocks = nonconsts;
29612959
sw_failaction = fail_opt;
2962-
sw_tags_to_sizes = Tag.Scannable.Map.empty;
29632960
}
29642961
in
29652962
let hs, sw = share_actions_sw sw in

lambda/simplif.ml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ let rec eliminate_ref id = function
5959
List.map (fun (n, e) -> (n, eliminate_ref id e)) sw.sw_blocks;
6060
sw_failaction =
6161
Option.map (eliminate_ref id) sw.sw_failaction;
62-
sw_tags_to_sizes = sw.sw_tags_to_sizes;
6362
},
6463
loc)
6564
| Lstringswitch(e, sw, default, loc) ->

middle_end/flambda/from_lambda/cps_conversion.ml

Lines changed: 93 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@ module I = Ilambda
2424
module L = Lambda
2525
module C = Lambda_conversions
2626

27-
type proto_switch = {
28-
numconsts : int;
29-
consts : (int * L.lambda) list;
30-
failaction : L.lambda option;
31-
}
32-
3327
type primitive_transform_result =
3428
| Primitive of L.primitive * L.lambda list * L.scoped_location
3529
| Transformed of L.lambda
@@ -133,7 +127,6 @@ let switch_for_if_then_else ~cond ~ifso ~ifnot =
133127
sw_numblocks = 0;
134128
sw_blocks = [];
135129
sw_failaction = None;
136-
sw_tags_to_sizes = Tag.Scannable.Map.empty;
137130
}
138131
in
139132
L.Lswitch (cond, switch, Loc_unknown)
@@ -449,23 +442,11 @@ let rec cps_non_tail (lam : L.lambda) (k : Ident.t -> Ilambda.t)
449442
k_exn
450443
| Transformed lam -> cps_non_tail lam k k_exn
451444
end
452-
| Lswitch (scrutinee,
453-
{ sw_numconsts; sw_consts; sw_numblocks = _; sw_blocks; sw_failaction;
454-
sw_tags_to_sizes = _; }, _loc) ->
455-
begin match sw_blocks with
456-
| [] -> ()
457-
| _ -> Misc.fatal_error "Lswitch `block' cases are forbidden"
458-
end;
445+
| Lswitch (scrutinee, switch, _loc) ->
459446
let after_switch = Continuation.create () in
460447
let result_var = Ident.create_local "switch_result" in
461448
let after = k result_var in
462-
let proto_switch : proto_switch =
463-
{ numconsts = sw_numconsts;
464-
consts = sw_consts;
465-
failaction = sw_failaction;
466-
}
467-
in
468-
let body = cps_switch proto_switch ~scrutinee after_switch k_exn in
449+
let body = cps_switch switch ~scrutinee after_switch k_exn in
469450
Let_cont {
470451
name = after_switch;
471452
is_exn_handler = false;
@@ -474,8 +455,9 @@ let rec cps_non_tail (lam : L.lambda) (k : Ident.t -> Ilambda.t)
474455
body;
475456
handler = after;
476457
}
477-
| Lstringswitch _ ->
478-
Misc.fatal_error "Lstringswitch must be expanded prior to CPS conversion"
458+
| Lstringswitch (scrutinee, cases, default, loc) ->
459+
cps_non_tail (Matching.expand_stringswitch loc scrutinee cases default)
460+
k k_exn
479461
| Lstaticraise (static_exn, args) ->
480462
let continuation =
481463
match Numbers.Int.Map.find static_exn !static_exn_env with
@@ -783,22 +765,10 @@ and cps_tail (lam : L.lambda) (k : Continuation.t) (k_exn : Continuation.t)
783765
Apply_cont (k, None, [Ilambda.Var result_var]))) k_exn
784766
| Transformed lam -> cps_tail lam k k_exn
785767
end
786-
| Lswitch (scrutinee,
787-
{ sw_numconsts; sw_consts; sw_numblocks = _; sw_blocks; sw_failaction;
788-
sw_tags_to_sizes = _; }, _loc) ->
789-
begin match sw_blocks with
790-
| [] -> ()
791-
| _ -> Misc.fatal_error "Lswitch `block' cases are forbidden"
792-
end;
793-
let proto_switch : proto_switch =
794-
{ numconsts = sw_numconsts;
795-
consts = sw_consts;
796-
failaction = sw_failaction;
797-
}
798-
in
799-
cps_switch proto_switch ~scrutinee k k_exn
800-
| Lstringswitch _ ->
801-
Misc.fatal_error "Lstringswitch must be expanded prior to CPS conversion"
768+
| Lswitch (scrutinee,switch, _loc) ->
769+
cps_switch switch ~scrutinee k k_exn
770+
| Lstringswitch (scrutinee, cases, default, loc) ->
771+
cps_tail (Matching.expand_stringswitch loc scrutinee cases default) k k_exn
802772
| Lstaticraise (static_exn, args) ->
803773
let continuation =
804774
match Numbers.Int.Map.find static_exn !static_exn_env with
@@ -958,9 +928,35 @@ and cps_function ({ kind; params; return; body; attr; loc; } : L.lfunction)
958928
stub = false;
959929
}
960930

961-
and cps_switch (switch : proto_switch) ~scrutinee (k : Continuation.t)
931+
and cps_switch (switch : L.lambda_switch) ~scrutinee (k : Continuation.t)
962932
(k_exn : Continuation.t) : Ilambda.t =
963-
let consts_rev, wrappers =
933+
let block_nums, sw_blocks = List.split switch.sw_blocks in
934+
let block_nums =
935+
List.map (fun ({ sw_tag; _ } : L.lambda_switch_block_key) ->
936+
begin match Tag.Scannable.create sw_tag with
937+
| Some tag ->
938+
let tag' = Tag.Scannable.to_tag tag in
939+
if Tag.is_structured_block_but_not_a_variant tag' then
940+
Misc.fatal_errorf "Bad tag %a in [Lswitch] (tag is that of a \
941+
scannable block, but not one treated like a variant; \
942+
[Lswitch] can only be used for variant matching)"
943+
Tag.print tag'
944+
| None ->
945+
Misc.fatal_errorf "Bad tag %d in [Lswitch] (not the tag \
946+
of a GC-scannable block)"
947+
sw_tag
948+
end;
949+
sw_tag)
950+
block_nums
951+
in
952+
if switch.sw_numblocks > Obj.last_non_constant_constructor_tag + 1
953+
then begin
954+
Misc.fatal_errorf "Too many blocks (%d) in [Lswitch], would \
955+
overlap into tag space for blocks that are not treated like variants; \
956+
[Lswitch] can only be used for variant matching"
957+
switch.sw_numblocks
958+
end;
959+
let convert_arms_rev cases wrappers =
964960
List.fold_left (fun (consts_rev, wrappers) (arm, (action : L.lambda)) ->
965961
match action with
966962
| Lvar var when not (Ident.Set.mem var !mutable_variables) ->
@@ -999,26 +995,76 @@ and cps_switch (switch : proto_switch) ~scrutinee (k : Continuation.t)
999995
let consts_rev = (arm, cont, None, []) :: consts_rev in
1000996
let wrappers = (cont, action) :: wrappers in
1001997
consts_rev, wrappers)
1002-
([], [])
1003-
switch.consts
998+
([], wrappers)
999+
cases
1000+
in
1001+
let consts_rev, wrappers = convert_arms_rev switch.sw_consts [] in
1002+
let blocks_rev, wrappers =
1003+
convert_arms_rev (List.combine block_nums sw_blocks) wrappers
10041004
in
10051005
let consts = List.rev consts_rev in
1006+
let blocks = List.rev blocks_rev in
10061007
let failaction, wrappers =
1007-
match switch.failaction with
1008+
match switch.sw_failaction with
10081009
| None -> None, wrappers
10091010
| Some action ->
10101011
let cont = Continuation.create () in
10111012
let action = cps_tail action k k_exn in
10121013
let wrappers = (cont, action) :: wrappers in
10131014
Some (cont, None, []), wrappers
10141015
in
1015-
let switch : I.switch =
1016-
{ numconsts = switch.numconsts;
1016+
let const_switch : I.switch =
1017+
{ numconsts = switch.sw_numconsts;
10171018
consts;
10181019
failaction;
10191020
}
10201021
in
1022+
let block_switch : I.switch =
1023+
{ numconsts = switch.sw_numblocks;
1024+
consts = blocks;
1025+
failaction;
1026+
}
1027+
in
1028+
let build_switch scrutinee wrappers =
1029+
let const_switch = I.Switch (scrutinee, const_switch) in
1030+
let scrutinee_tag = Ident.create_local "scrutinee_tag" in
1031+
let block_switch =
1032+
I.Let (scrutinee_tag,
1033+
Not_user_visible,
1034+
Pintval,
1035+
Prim { prim = Pgettag;
1036+
args = [Var scrutinee];
1037+
loc = Loc_unknown;
1038+
exn_continuation = None; },
1039+
I.Switch(scrutinee_tag, block_switch))
1040+
in
1041+
if switch.sw_numblocks = 0 then const_switch, wrappers
1042+
else if switch.sw_numconsts = 0 then block_switch, wrappers
1043+
else
1044+
let const_cont = Continuation.create () in
1045+
let block_cont = Continuation.create () in
1046+
let isint_switch : I.switch =
1047+
{ numconsts = 2;
1048+
consts = [ (0, block_cont, None, []); (1, const_cont, None, []) ];
1049+
failaction = None;
1050+
}
1051+
in
1052+
let is_scrutinee_int = Ident.create_local "is_scrutinee_int" in
1053+
let isint_switch =
1054+
I.Let (is_scrutinee_int,
1055+
Not_user_visible,
1056+
Pintval,
1057+
Prim { prim = Pflambda_isint;
1058+
args = [Var scrutinee];
1059+
loc = Loc_unknown;
1060+
exn_continuation = None; },
1061+
I.Switch(is_scrutinee_int, isint_switch))
1062+
in
1063+
isint_switch,
1064+
((const_cont, const_switch)::(block_cont, block_switch)::wrappers)
1065+
in
10211066
cps_non_tail scrutinee (fun scrutinee ->
1067+
let switch, wrappers = build_switch scrutinee wrappers in
10221068
List.fold_left (fun body (cont, action) ->
10231069
I.Let_cont {
10241070
name = cont;
@@ -1028,8 +1074,7 @@ and cps_switch (switch : proto_switch) ~scrutinee (k : Continuation.t)
10281074
body;
10291075
handler = action;
10301076
})
1031-
(I.Switch (scrutinee, switch))
1032-
wrappers)
1077+
switch wrappers)
10331078
k_exn
10341079

10351080
let lambda_to_ilambda lam : Ilambda.program =

middle_end/flambda/from_lambda/prepare_lambda.ml

Lines changed: 10 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -215,99 +215,22 @@ let rec prepare env (lam : L.lambda) (k : L.lambda -> L.lambda) =
215215
prepare_option env switch.sw_failaction (fun sw_failaction ->
216216
prepare_list env sw_consts (fun sw_consts ->
217217
prepare_list env sw_blocks (fun sw_blocks ->
218-
let sw_failaction, wrap_switch =
219-
match sw_failaction with
220-
| None -> None, (fun lam -> lam)
221-
| Some failaction ->
222-
let failaction_cont = L.next_raise_count () in
223-
let wrap_switch lam : L.lambda =
224-
Lstaticcatch (lam, (failaction_cont, []), failaction)
225-
in
226-
Some (L.Lstaticraise (failaction_cont, [])), wrap_switch
227-
in
228-
let consts_switch : L.lambda_switch =
218+
let switch : L.lambda_switch =
229219
{ sw_numconsts = switch.sw_numconsts;
230220
sw_consts = List.combine const_nums sw_consts;
231-
sw_numblocks = 0;
232-
sw_blocks = [];
233-
sw_failaction;
234-
sw_tags_to_sizes = Tag.Scannable.Map.empty;
235-
}
236-
in
237-
(* CR mshinwell: Merge this file into Cps_conversion then delete
238-
[sw_tags_to_sizes]. *)
239-
let tags_to_sizes =
240-
List.fold_left (fun tags_to_sizes
241-
({ sw_tag; sw_size; } : L.lambda_switch_block_key) ->
242-
match Tag.Scannable.create sw_tag with
243-
| Some tag ->
244-
let tag' = Tag.Scannable.to_tag tag in
245-
if Tag.is_structured_block_but_not_a_variant tag' then begin
246-
Misc.fatal_errorf "Bad tag %a in [Lswitch] (tag is that \
247-
of a scannable block, but not one treated like a \
248-
variant; [Lswitch] can only be used for variant \
249-
matching)"
250-
Tag.print tag'
251-
end;
252-
let size = Targetint.OCaml.of_int sw_size in
253-
Tag.Scannable.Map.add tag size tags_to_sizes
254-
| None ->
255-
Misc.fatal_errorf "Bad tag %d in [Lswitch] (not the tag \
256-
of a GC-scannable block)"
257-
sw_tag)
258-
Tag.Scannable.Map.empty
259-
block_nums
260-
in
261-
let block_nums =
262-
List.map (fun ({ sw_tag; _} : L.lambda_switch_block_key) ->
263-
sw_tag)
264-
block_nums
265-
in
266-
if switch.sw_numblocks > Obj.last_non_constant_constructor_tag + 1
267-
then begin
268-
Misc.fatal_errorf "Too many blocks (%d) in [Lswitch], would \
269-
overlap into tag space for blocks that are not treated \
270-
like variants; [Lswitch] can only be used for variant \
271-
matching"
272-
switch.sw_numblocks
273-
end;
274-
let blocks_switch : L.lambda_switch =
275-
{ sw_numconsts = switch.sw_numblocks;
276-
sw_consts = List.combine block_nums sw_blocks;
277-
sw_numblocks = 0;
278-
sw_blocks = [];
221+
sw_numblocks = switch.sw_numblocks;
222+
sw_blocks = List.combine block_nums sw_blocks;
279223
sw_failaction;
280-
(* XXX What about the size for the failaction? ... *)
281-
sw_tags_to_sizes = tags_to_sizes;
282224
}
283225
in
284-
let consts_switch : L.lambda =
285-
L.Lswitch (scrutinee, consts_switch, loc)
286-
in
287-
let blocks_switch : L.lambda =
288-
L.Lswitch (
289-
Lprim (Pgettag, [scrutinee], Loc_unknown),
290-
blocks_switch, loc)
291-
in
292-
let isint_switch : L.lambda_switch =
293-
{ sw_numconsts = 2;
294-
sw_consts = [0, blocks_switch; 1, consts_switch];
295-
sw_numblocks = 0;
296-
sw_blocks = [];
297-
sw_failaction = None;
298-
sw_tags_to_sizes = Tag.Scannable.Map.empty;
299-
}
300-
in
301-
let switch =
302-
if switch.sw_numconsts = 0 then blocks_switch
303-
else if switch.sw_numblocks = 0 then consts_switch
304-
else
305-
L.Lswitch (L.Lprim (Pflambda_isint, [scrutinee], Loc_unknown),
306-
isint_switch, loc)
307-
in
308-
k (wrap_switch switch)))))
226+
k (Lswitch (scrutinee, switch, loc))))))
309227
| Lstringswitch (scrutinee, cases, default, loc) ->
310-
prepare env (Matching.expand_stringswitch loc scrutinee cases default) k
228+
prepare env scrutinee (fun scrutinee ->
229+
let patterns, actions = List.split cases in
230+
prepare_list env actions (fun actions ->
231+
prepare_option env default (fun default ->
232+
let cases = List.combine patterns actions in
233+
k (L.Lstringswitch (scrutinee, cases, default, loc)))))
311234
| Lstaticraise (cont, args) ->
312235
prepare_list env args (fun args ->
313236
k (L.Lstaticraise (cont, args)))

0 commit comments

Comments
 (0)