@@ -24,12 +24,6 @@ module I = Ilambda
2424module L = Lambda
2525module C = Lambda_conversions
2626
27- type proto_switch = {
28- numconsts : int ;
29- consts : (int * L .lambda ) list ;
30- failaction : L .lambda option ;
31- }
32-
3327type 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
10351080let lambda_to_ilambda lam : Ilambda.program =
0 commit comments