mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
... and other minor fixes.
library/*.m:
library/LIB_FLAGS.in:
compiler/*.m:
mdbcomp/*.m:
Fix and update copyright notices.
Fix spelling.
Delete trailing whitespace.
603 lines
24 KiB
Mathematica
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.
|
|
%---------------------------------------------------------------------------%
|