Files
mercury/compiler/split_switch_arms.m
Julien Fischer 4cd5c17f61 Fix more copyright notices ...
... and other minor fixes.

library/*.m:
library/LIB_FLAGS.in:
compiler/*.m:
mdbcomp/*.m:
    Fix and update copyright notices.

    Fix spelling.

    Delete trailing whitespace.
2024-02-20 17:18:52 +11:00

603 lines
24 KiB
Mathematica

%----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%----------------------------------------------------------------------------%
% Copyright (C) 2023-2024 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%----------------------------------------------------------------------------%
%
% File: split_switch_arms.m.
%
% This module implements a program transformation that replaces code like this:
%
% (
% ( X = f1
% ; X = f2
% ; X = f3
% ; X = f4
% ),
% <initial code to handle f1,f2,f3,f4>,
% (
% X = f1,
% <code to handle f1>
% ;
% ( X = f2
% ; X = f3
% ),
% <code to handle f2,f3>
% ;
% X = f4,
% <code to handle f4>
% ),
% <final code to handle f1,f2,f3,f4>
% ;
% X = f5,
% <code to handle f5>
% )
%
% with code like this:
% (
% X = f1,
% <initial code to handle f1,f2,f3,f4>,
% <code to handle f1>,
% <final code to handle f1,f2,f3,f4>
% ;
% ( X = f2
% ; X = f3
% ),
% <initial code to handle f1,f2,f3,f4>,
% <code to handle f2,f3>,
% <final code to handle f1,f2,f3,f4>
% ;
% X = f4,
% <initial code to handle f1,f2,f3,f4>,
% <code to handle f4>,
% <final code to handle f1,f2,f3,f4>
% ;
% X = f5,
% <code to handle f5>
% )
%
% The idea is that if inside a switch on a variable, there are other switches
% on that same variable that subdivide the set of cons_ids even further, then
% we do the following.
%
% - We partition the cons_ids of the top-level switch arm to make all the
% distinctions between its cons_ids that any of the (one or more) switches
% on the same variable inside the top-level switch arm make,
%
% In this case, we would partition {f1,f2,f3,f4} into three sets:
% {f1}, {f2,f3} and {f4}.
%
% - We replace the original switch arm with a separate switch arm for each
% partition.
%
% In this case, this would replace the arm for {f1,f2,f3,f4}
% with three arms for {f1}, {f2,f3} and {f4} respectively.
% The three arms would initially contain the same code as the original
% switch arm. This would mean that each would contain the whole of
% nested switch on X with the three arms for {f1}, {f2,f3} and {f4}.
%
% - We restrict the goal in each of the resulting switch arms to refer to
% only the cons_ids of its own partition in any nested switches on the
% original switched-on variable, and we then simplify away any trivial
% switches generated by this process.
%
% In this case, in the new {f1}-only arm of the top switch, would replace
% the inner switch on X with the arms for {f1}, {f2,f3} and {f4}
% with just the arm for {f1}, and then optimize away the now-unnecessary
% switch wrapper around the goal inside that arm. We would do the same
% with the other two arms.
%
% The code resulting from the above steps will include some code duplication
% (most of the pieces of code denoted by <code to handle ...> in the example
% above would be duplicated), but it will need to execute fewer transfers
% of control. This is worthwhile because
%
% - the branch instructions used to implement switches are hard to predict
% unless most paths through the nested switches are rarely if ever taken, and
%
% - the pipeline breaks caused by branches that are not correctly predicted
% are one of the two major contributors to the runtime of Mercury programs.
% (The other major contributors are data cache misses.)
%
% Nevertheless, the main point of this transformation is not performance,
% since the transformation performed by this module could also be done
% manually by humans. Instead, the main point is that having the transformation
% done by a machine is better from a software engineering point of view.
% One software engineering advantage is that the compiler can do the
% transformation more quickly, more cheaply, and more reliably than
% programmers can. Another advantage is that it allows programmers to maintain
% the pre-transform version of the code. The pre-transform version may be
% clearer and easier to maintain than the post-transform version, due to
% the code duplication required by the transform.
%
%----------------------------------------------------------------------------%
:- module check_hlds.simplify.split_switch_arms.
:- interface.
:- import_module check_hlds.simplify.simplify_info.
:- import_module hlds.
:- import_module hlds.hlds_goal.
:- import_module set.
% split_switch_arms_in_goal(ToSplitArms, Goal0, Goal):
%
% This predicate looks for the top-level switch arms listed in ToSplitArms,
% and when it finds them, it calls
%
% - the predicates that partition the cons_ids of the arm
% as described above, and
%
% - the predicates that replace the one original arm with several arms,
% one for each partition.
%
:- pred split_switch_arms_in_goal(set(switch_arm)::in,
hlds_goal::in, hlds_goal::out) is det.
%----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.make_goal.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module cord.
:- import_module list.
:- import_module require.
split_switch_arms_in_goal(ToSplitArms, Goal0, Goal) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
(
( GoalExpr0 = unify(_, _, _, _, _)
; GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
),
GoalExpr = GoalExpr0
;
GoalExpr0 = switch(Var, CanFail, Cases0),
split_switch_arms_in_cases(ToSplitArms, Var, Cases0,
cord.init, CasesCord),
Cases = cord.list(CasesCord),
GoalExpr = switch(Var, CanFail, Cases)
;
GoalExpr0 = conj(ConjType, Goals0),
split_switch_arms_in_goals(ToSplitArms, Goals0, Goals),
GoalExpr = conj(ConjType, Goals)
;
GoalExpr0 = disj(Goals0),
split_switch_arms_in_goals(ToSplitArms, Goals0, Goals),
GoalExpr = disj(Goals)
;
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
split_switch_arms_in_goal(ToSplitArms, Cond0, Cond),
split_switch_arms_in_goal(ToSplitArms, Then0, Then),
split_switch_arms_in_goal(ToSplitArms, Else0, Else),
GoalExpr = if_then_else(Vars, Cond, Then, Else)
;
GoalExpr0 = negation(SubGoal0),
split_switch_arms_in_goal(ToSplitArms, SubGoal0, SubGoal),
GoalExpr = negation(SubGoal)
;
GoalExpr0 = scope(Reason, SubGoal0),
split_switch_arms_in_goal(ToSplitArms, SubGoal0, SubGoal),
GoalExpr = scope(Reason, SubGoal)
;
GoalExpr0 = shorthand(ShortHand0),
(
ShortHand0 = atomic_goal(GoalType, Outer, Inner,
MaybeOutputVars, MainGoal0, OrElseGoals0, OrElseInners),
split_switch_arms_in_goal(ToSplitArms, MainGoal0, MainGoal),
split_switch_arms_in_goals(ToSplitArms, OrElseGoals0, OrElseGoals),
ShortHand = atomic_goal(GoalType, Outer, Inner,
MaybeOutputVars, MainGoal, OrElseGoals, OrElseInners)
;
ShortHand0 = try_goal(_, _, _),
% These should have been expanded out by now.
unexpected($pred, "try_goal")
;
ShortHand0 = bi_implication(_, _),
% These should have been expanded out by now.
unexpected($pred, "bi_implication")
),
GoalExpr = shorthand(ShortHand)
),
Goal = hlds_goal(GoalExpr, GoalInfo0).
:- pred split_switch_arms_in_goals(set(switch_arm)::in,
list(hlds_goal)::in, list(hlds_goal)::out) is det.
split_switch_arms_in_goals(_ToSplitArms, [], []).
split_switch_arms_in_goals(ToSplitArms, [Goal0 | Goals0], [Goal | Goals]) :-
split_switch_arms_in_goal(ToSplitArms, Goal0, Goal),
split_switch_arms_in_goals(ToSplitArms, Goals0, Goals).
:- pred split_switch_arms_in_cases(set(switch_arm)::in, prog_var::in,
list(case)::in, cord(case)::in, cord(case)::out) is det.
split_switch_arms_in_cases(_ToSplitArms, _Var, [], !CasesCord).
split_switch_arms_in_cases(ToSplitArms, Var, [Case0 | Cases0], !CasesCord) :-
split_switch_arms_in_case(ToSplitArms, Var, Case0, !CasesCord),
split_switch_arms_in_cases(ToSplitArms, Var, Cases0, !CasesCord).
%---------------------------------------------------------------------------%
% split_switch_arms_in_case(ToSplitArms, Var, Case0, !CasesCord):
%
% This is the active part of the split_switch_arms_in_X family
% of predicates; the rest of the code in that family is needed
% only to let us arrive here.
%
% If this case represents a switch arm that we should split, then
%
% - find out how its cons_ids should be partitioned,
% - perform the partitioning itself into several switch arms, and
% - restrict the code each resulting switch arms to refer to only
% the cons_id in its own partition.
%
% The first step is done by partition_cons_ids_for_var_in_goal,
% the second and third are done by gather_partition_restricted_cases.
%
:- pred split_switch_arms_in_case(set(switch_arm)::in, prog_var::in,
case::in, cord(case)::in, cord(case)::out) is det.
split_switch_arms_in_case(ToSplitArms, Var, Case0, !CasesCord) :-
Case0 = case(MainConsId, OtherConsIds, Goal0),
set.list_to_set([MainConsId | OtherConsIds], ConsIds),
Arm = switch_arm(Var, ConsIds),
( if set.contains(ToSplitArms, Arm) then
% Split up any switch arms that we can split up on *other* variables.
% It is better to do so before this arm is split up, because
% we can do so once and copy the results, rather than having to
% repeat the same work N times if Partitions below has N elements.
%
% The switch arms on Var itself inside Goal0 will be broken up
% as finely as possibly by gather_partition_restricted_cases below.
set.filter(switch_arm_is_on_var(Var), ToSplitArms,
_ToSplitArmsOnVar, ToSplitArmsNotOnVar),
( if set.is_empty(ToSplitArmsNotOnVar) then
Goal = Goal0
else
split_switch_arms_in_goal(ToSplitArmsNotOnVar, Goal0, Goal)
),
% Start with the initial set of partitions containing only one set,
% the set of all cons_ids in this arm. Then refine this partitioning
% based on the switches on Var found in Goal0.
Partitions0 = set.make_singleton_set(ConsIds),
partition_cons_ids_for_var_in_goal(Var, Goal, Partitions0, Partitions),
( if set.count(Partitions, 1) then
% Normally, an inner switch on Var inside an arm of a outer switch
% on Var makes distinctions that the outer switch does not,
% but it does not *have* to, and in this case, it doesn't.
% In this case, there is no basis on which to split up this arm.
%
% This code gets the same result as the else part would,
% only faster.
Case = case(MainConsId, OtherConsIds, Goal),
cord.snoc(Case, !CasesCord)
else
% Replace the original Case with one case for each partition in
% Partitions.
set.fold(gather_partition_restricted_cases(Var, Goal), Partitions,
!CasesCord)
)
else
split_switch_arms_in_goal(ToSplitArms, Goal0, Goal),
Case = case(MainConsId, OtherConsIds, Goal),
cord.snoc(Case, !CasesCord)
).
:- pred switch_arm_is_on_var(prog_var::in, switch_arm::in) is semidet.
switch_arm_is_on_var(Var, Arm) :-
Arm = switch_arm(ArmVar, _),
ArmVar = Var.
%---------------------------------------------------------------------------%
% partition_cons_ids_for_var_in_goal(Var, Goal, !Partitions):
%
% Refine !Partitions based on all the distinctions made between its
% cons_ids by switches on Var inside Goal.
%
:- pred partition_cons_ids_for_var_in_goal(prog_var::in, hlds_goal::in,
set(set(cons_id))::in, set(set(cons_id))::out) is det.
partition_cons_ids_for_var_in_goal(Var, Goal, !Partitions) :-
Goal = hlds_goal(GoalExpr, _GoalInfo),
(
( GoalExpr = unify(_, _, _, _, _)
; GoalExpr = plain_call(_, _, _, _, _, _)
; GoalExpr = generic_call(_, _, _, _, _)
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
)
;
GoalExpr = switch(SwitchVar, _CanFail, Cases),
( if Var = SwitchVar then
partition_cons_ids_of_cases(Cases, !Partitions)
else
true
),
partition_cons_ids_for_var_in_cases(Var, Cases, !Partitions)
;
GoalExpr = conj(_ConjType, Goals),
partition_cons_ids_for_var_in_goals(Var, Goals, !Partitions)
;
GoalExpr = disj(Goals),
partition_cons_ids_for_var_in_goals(Var, Goals, !Partitions)
;
GoalExpr = if_then_else(_Vars, Cond, Then, Else),
partition_cons_ids_for_var_in_goal(Var, Cond, !Partitions),
partition_cons_ids_for_var_in_goal(Var, Then, !Partitions),
partition_cons_ids_for_var_in_goal(Var, Else, !Partitions)
;
GoalExpr = negation(SubGoal),
partition_cons_ids_for_var_in_goal(Var, SubGoal, !Partitions)
;
GoalExpr = scope(_Reason, SubGoal),
partition_cons_ids_for_var_in_goal(Var, SubGoal, !Partitions)
;
GoalExpr = shorthand(ShortHand),
(
ShortHand = atomic_goal(_GoalType, _Outer, _Inner,
_MaybeOutputVars, MainGoal, OrElseGoals, _OrElseInners),
partition_cons_ids_for_var_in_goal(Var, MainGoal, !Partitions),
partition_cons_ids_for_var_in_goals(Var, OrElseGoals, !Partitions)
;
ShortHand = try_goal(_, _, _),
% These should have been expanded out by now.
unexpected($pred, "try_goal")
;
ShortHand = bi_implication(_, _),
% These should have been expanded out by now.
unexpected($pred, "bi_implication")
)
).
:- pred partition_cons_ids_for_var_in_goals(prog_var::in, list(hlds_goal)::in,
set(set(cons_id))::in, set(set(cons_id))::out) is det.
partition_cons_ids_for_var_in_goals(_Var, [], !Partitions).
partition_cons_ids_for_var_in_goals(Var, [Goal | Goals], !Partitions) :-
partition_cons_ids_for_var_in_goal(Var, Goal, !Partitions),
partition_cons_ids_for_var_in_goals(Var, Goals, !Partitions).
:- pred partition_cons_ids_for_var_in_cases(prog_var::in, list(case)::in,
set(set(cons_id))::in, set(set(cons_id))::out) is det.
partition_cons_ids_for_var_in_cases(_Var, [], !Partitions).
partition_cons_ids_for_var_in_cases(Var, [Case | Cases], !Partitions) :-
Case = case(_MainConsId, _OtherConsIds, Goal),
partition_cons_ids_for_var_in_goal(Var, Goal, !Partitions),
partition_cons_ids_for_var_in_cases(Var, Cases, !Partitions).
%---------------------------------------------------------------------------%
% partition_cons_ids_of_cases(Cases, !Partitions):
%
% Refine !Partitions by making all the distinctions between its cons_ids
% that Cases makes. In other words, if two cons_ids are in different cases,
% then ensure that those two cons_ids are in different partitions in
% !:Partitions, even if they were in the same partition in !.Partitions.
%
:- pred partition_cons_ids_of_cases(list(case)::in,
set(set(cons_id))::in, set(set(cons_id))::out) is det.
partition_cons_ids_of_cases([], !Partitions).
partition_cons_ids_of_cases([Case | Cases], !Partitions) :-
Case = case(MainConsId, OtherConsIds, _Goal),
set.list_to_set([MainConsId | OtherConsIds], ArmConsIds),
set.fold(add_in_and_or_out_cons_ids(ArmConsIds), !.Partitions,
set.init, !:Partitions),
partition_cons_ids_of_cases(Cases, !Partitions).
:- pred add_in_and_or_out_cons_ids(set(cons_id)::in,set(cons_id)::in,
set(set(cons_id))::in, set(set(cons_id))::out) is det.
add_in_and_or_out_cons_ids(ArmConsIds, Partition0, !Partitions) :-
divide_by_set(ArmConsIds, Partition0, PartitionIn, PartitionOut),
% Since Partition0 has to be nonempty, at least one of Partition{In,Out}
% also has to be nonempty, but both can be nonempty, and the partitioning
% process makes progress only if they both *are* nonempty.
( if set.is_empty(PartitionIn) then
true
else
set.insert(PartitionIn, !Partitions)
),
( if set.is_empty(PartitionOut) then
true
else
set.insert(PartitionOut, !Partitions)
).
%---------------------------------------------------------------------------%
% gather_partition_restricted_cases(Var, Goal0, Partition, !CasesCord):
%
% Restrict Goal0 to refer only to the cons_ids in Partition in its
% switches on Var, and add the resulting goal as a switch arm
% to !CasesCord for the cons_ids in Partition.
%
:- pred gather_partition_restricted_cases(prog_var::in, hlds_goal::in,
set(cons_id)::in, cord(case)::in, cord(case)::out) is det.
gather_partition_restricted_cases(Var, Goal0, Partition, !CasesCord) :-
set.to_sorted_list(Partition, ConsIds),
(
ConsIds = [],
unexpected($pred, "Partition is empty")
;
ConsIds = [MainConsId | OtherConsIds],
restrict_switches_on_var_in_goal(Var, Partition, Goal0, Goal),
Case = case(MainConsId, OtherConsIds, Goal),
cord.snoc(Case, !CasesCord)
).
% restrict_switches_on_var_in_goal(Var, ConsIds, !Goal):
%
% Restrict any switches on Var in !.Goal to refer to only the cons_ids
% in ConsIds in it cases.
%
:- pred restrict_switches_on_var_in_goal(prog_var::in, set(cons_id)::in,
hlds_goal::in, hlds_goal::out) is det.
restrict_switches_on_var_in_goal(Var, Partition, Goal0, Goal) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
(
( GoalExpr0 = unify(_, _, _, _, _)
; GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
),
GoalExpr = GoalExpr0
;
GoalExpr0 = switch(SwitchVar, CanFail, Cases0),
( if Var = SwitchVar then
restrict_switches_on_var_in_restricted_cases(Var, Partition,
Cases0, cord.init, CasesCord),
Cases = cord.list(CasesCord),
% This optimization effectively deletes the switch construct,
% leaving either one of its arms, or none.
(
Cases = [],
GoalExpr = fail_goal_expr
;
Cases = [Case],
Case = case(_, _, CaseGoal),
CaseGoal = hlds_goal(GoalExpr, _)
;
Cases = [_, _ | _],
% Every distinction made between the cons_ids of Cases
% should have been made by the top-level switch on Var
% found by split_switch_arms_in_goal, and each case
% that replaced that original top-level switch arm
% should be for a set of cons_ids that wholly fits
% within the cons_ids of ONE case. If execution gets here,
% then something in the above process must have gone wrong.
unexpected($pred, "Cases = [_, _ | _]")
)
else
restrict_switches_on_var_in_cases(Var, Partition, Cases0, Cases),
GoalExpr = switch(SwitchVar, CanFail, Cases)
)
;
GoalExpr0 = conj(ConjType, Goals0),
(
ConjType = plain_conj,
restrict_switches_on_var_in_conj(Var, Partition, Goals0, Goals)
;
ConjType = parallel_conj,
restrict_switches_on_var_in_goals(Var, Partition, Goals0, Goals)
),
GoalExpr = conj(ConjType, Goals)
;
GoalExpr0 = disj(Goals0),
restrict_switches_on_var_in_goals(Var, Partition, Goals0, Goals),
GoalExpr = disj(Goals)
;
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
restrict_switches_on_var_in_goal(Var, Partition, Cond0, Cond),
restrict_switches_on_var_in_goal(Var, Partition, Then0, Then),
restrict_switches_on_var_in_goal(Var, Partition, Else0, Else),
GoalExpr = if_then_else(Vars, Cond, Then, Else)
;
GoalExpr0 = negation(SubGoal0),
restrict_switches_on_var_in_goal(Var, Partition, SubGoal0, SubGoal),
GoalExpr = negation(SubGoal)
;
GoalExpr0 = scope(Reason, SubGoal0),
restrict_switches_on_var_in_goal(Var, Partition, SubGoal0, SubGoal),
GoalExpr = scope(Reason, SubGoal)
;
GoalExpr0 = shorthand(ShortHand0),
(
ShortHand0 = atomic_goal(GoalType, Outer, Inner,
MaybeOutputVars, MainGoal0, OrElseGoals0, OrElseInners),
restrict_switches_on_var_in_goal(Var, Partition,
MainGoal0, MainGoal),
restrict_switches_on_var_in_goals(Var, Partition,
OrElseGoals0, OrElseGoals),
ShortHand = atomic_goal(GoalType, Outer, Inner,
MaybeOutputVars, MainGoal, OrElseGoals, OrElseInners)
;
ShortHand0 = try_goal(_, _, _),
% These should have been expanded out by now.
unexpected($pred, "try_goal")
;
ShortHand0 = bi_implication(_, _),
% These should have been expanded out by now.
unexpected($pred, "bi_implication")
),
GoalExpr = shorthand(ShortHand)
),
Goal = hlds_goal(GoalExpr, GoalInfo0).
:- pred restrict_switches_on_var_in_conj(prog_var::in, set(cons_id)::in,
list(hlds_goal)::in, list(hlds_goal)::out) is det.
restrict_switches_on_var_in_conj(_Var, _Partition, [], []).
restrict_switches_on_var_in_conj(Var, Partition,
[Goal0 | Goals0], Goals) :-
restrict_switches_on_var_in_goal(Var, Partition, Goal0, HeadGoal),
restrict_switches_on_var_in_conj(Var, Partition, Goals0, TailGoals),
% Flatten sequential conjunctions.
( if HeadGoal = hlds_goal(conj(plain_conj, HeadConjuncts), _GoalInfo) then
Goals = HeadConjuncts ++ TailGoals
else
Goals = [HeadGoal | TailGoals]
).
:- pred restrict_switches_on_var_in_goals(prog_var::in, set(cons_id)::in,
list(hlds_goal)::in, list(hlds_goal)::out) is det.
restrict_switches_on_var_in_goals(_Var, _Partition, [], []).
restrict_switches_on_var_in_goals(Var, Partition,
[Goal0 | Goals0], [Goal | Goals]) :-
restrict_switches_on_var_in_goal(Var, Partition, Goal0, Goal),
restrict_switches_on_var_in_goals(Var, Partition, Goals0, Goals).
:- pred restrict_switches_on_var_in_cases(prog_var::in, set(cons_id)::in,
list(case)::in, list(case)::out) is det.
restrict_switches_on_var_in_cases(_Var, _Partition, [], []).
restrict_switches_on_var_in_cases(Var, Partition,
[Case0 | Cases0], [Case | Cases]) :-
Case0 = case(MainConsId, OtherPartition, Goal0),
restrict_switches_on_var_in_goal(Var, Partition, Goal0, Goal),
Case = case(MainConsId, OtherPartition, Goal),
restrict_switches_on_var_in_cases(Var, Partition, Cases0, Cases).
:- pred restrict_switches_on_var_in_restricted_cases(prog_var::in,
set(cons_id)::in, list(case)::in, cord(case)::in, cord(case)::out) is det.
restrict_switches_on_var_in_restricted_cases(_Var, _Partition, [], !CasesCord).
restrict_switches_on_var_in_restricted_cases(Var, Partition,
[Case0 | Cases0], !CasesCord) :-
Case0 = case(MainConsId0, OtherConsIds0, Goal0),
set.list_to_set([MainConsId0 | OtherConsIds0], ArmConsIds0),
set.intersect(Partition, ArmConsIds0, ArmConsIds),
set.to_sorted_list(ArmConsIds, ArmConsIdsList),
(
ArmConsIdsList = []
% Since none of the cons_ids of this case are allowed by the partition
% of the top switch arm for Var that we are in, delete this case
% by not adding it to !CasesCord.
;
ArmConsIdsList = [MainConsId | OtherConsIds],
restrict_switches_on_var_in_goal(Var, Partition, Goal0, Goal),
Case = case(MainConsId, OtherConsIds, Goal),
cord.snoc(Case, !CasesCord)
),
restrict_switches_on_var_in_restricted_cases(Var, Partition, Cases0,
!CasesCord).
%---------------------------------------------------------------------------%
:- end_module check_hlds.simplify.split_switch_arms.
%---------------------------------------------------------------------------%