mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 09:53:36 +00:00
368 lines
15 KiB
Mathematica
368 lines
15 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2012 The University of Melbourne.
|
|
% Copyright (C) 2015-2018 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: from_ground_term_util.m.
|
|
% Author: zs.
|
|
%
|
|
% This module provides utility types and predicates that are useful
|
|
% when compiler modules process from_ground_term scopes.
|
|
%
|
|
% Specifically, the contents of this module are designed
|
|
% to help test whether after a program transformation,
|
|
% a from_ground_term_{initial,construct} scope goal can still remain
|
|
% a from_ground_term scope of the same kind, and if not, to wrap smaller
|
|
% from_ground_term_{initial,construct} scopes around subsequences of the
|
|
% original conjuncts whereever the status of the invariants permit it.
|
|
%
|
|
|
|
:- module hlds.from_ground_term_util.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
:- import_module map.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Describe whether a goal or sequence of goals inside a from_ground_term
|
|
% scope of the initial or construct kind still obeys the invariants
|
|
% required of such scopes after a program transformation.
|
|
%
|
|
:- type fgt_invariants_status
|
|
---> fgt_invariants_kept
|
|
; fgt_invariants_broken.
|
|
|
|
% A conjunct inside a from_ground_term_{initial,construct} scope must be
|
|
% a unification of the form X = f(Y1, ..., Yn). The X is constructed
|
|
% by this unification and by the goals that construct the Yi.
|
|
%
|
|
% Values of this type record both the relevant information about the input
|
|
% of a program transformation that operates on such conjuncts (the second
|
|
% and third fields of each functor), the result of the transformation
|
|
% (the first field of each functor) and whether the transformation
|
|
% breaks the scope's invariants (the functor itself).
|
|
%
|
|
:- type fgt_marked_goal
|
|
---> fgt_kept_goal(
|
|
% Either the original goal, or a version that is transformed
|
|
% in a way that keeps the from_ground_term_{initial,construct}
|
|
% invariants.
|
|
hlds_goal,
|
|
|
|
% The variable from the lhs of the original unification (X).
|
|
prog_var,
|
|
|
|
% The variables from the rhs of the original unification (Yi).
|
|
list(prog_var)
|
|
)
|
|
; fgt_broken_goal(
|
|
% A version of the original goal that is transformed
|
|
% in a way that breaks some of the
|
|
% from_ground_term_{initial,construct} invariants.
|
|
hlds_goal,
|
|
|
|
% The variable from the lhs of the original unification (X).
|
|
prog_var,
|
|
|
|
% The variables from the rhs of the original unification (Yi).
|
|
list(prog_var)
|
|
).
|
|
|
|
% Return the goal from a goal marked as kept. Abort if the goal is marked
|
|
% as broken.
|
|
%
|
|
:- pred project_kept_goal(fgt_marked_goal::in, hlds_goal::out) is det.
|
|
|
|
% A value of type fgt_build_info maps a variable to the code needed
|
|
% to build it, and to some other information that is of interest
|
|
% only to this module.
|
|
%
|
|
:- type fgt_build_info.
|
|
:- type fgt_build_info_map == map(prog_var, fgt_build_info).
|
|
|
|
:- type goal_order
|
|
---> construct_bottom_up
|
|
; deconstruct_top_down.
|
|
|
|
% introduce_partial_fgt_scopes(GoalInfo0, SubGoalInfo0,
|
|
% ConstructOrderMarkedSubGoals, Order, SubGoal):
|
|
%
|
|
% The inputs to this predicate are:
|
|
%
|
|
% - The original goal infos for a fgt{i,c} goal and for its subgoal
|
|
% (GoalInfo0 and SubGoalInfo0).
|
|
%
|
|
% - A list (ConstructOrderMarkedSubGoals) of the transformed versions
|
|
% of the conjuncts that were originally in that scope, in bottom up
|
|
% (construct) order, marked up in the way required by the comment
|
|
% on the definition of the fgt_marked_goal type). This predicate
|
|
% assumes that this list contains some violations of the fgt{i,c}
|
|
% invariants (other than the order invariant for initial scopes);
|
|
% if it doesn't, then the WHOLE conjunction can have a fgt{i,c} scope
|
|
% wrapped around it, using code much simpler than what is in this
|
|
% predicate.
|
|
%
|
|
% - The order into which the goals should be put (Order).
|
|
%
|
|
% The SubGoal returned by this predicate will contain the code in the
|
|
% MarkedSubGoals in the desired order, but with any sequence of goals that
|
|
% does obey the fgt{i,c} invariants will be wrapped in a fgt scope;
|
|
% initial if Order = deconstruct_top_down, construct if Order =
|
|
% construct_bottom_up.
|
|
%
|
|
% This allows us to preserve the effect of the compiler optimizations of
|
|
% fgt{i,c} scopes for as large a portion of the original scope as possible.
|
|
%
|
|
:- pred introduce_partial_fgt_scopes(hlds_goal_info::in, hlds_goal_info::in,
|
|
list(fgt_marked_goal)::in, goal_order::in, hlds_goal::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.goal_util.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
project_kept_goal(MarkedGoal, Goal) :-
|
|
(
|
|
MarkedGoal = fgt_kept_goal(Goal, _, _)
|
|
;
|
|
MarkedGoal = fgt_broken_goal(_, _, _),
|
|
unexpected($pred, "broken goal")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Has a goal or sequence of goals broken the fgt{i,c} invariants?
|
|
%
|
|
:- type maybe_kept
|
|
---> kept
|
|
; broken.
|
|
|
|
% Has a goal kept the fgt{i,c} invariants?
|
|
%
|
|
:- type maybe_kept_goal_info
|
|
---> kept_old_gi(
|
|
% Yes. But if its parent or one of its siblings has not,
|
|
% then we want to wrap it in an fgt scope if it is big
|
|
% enough (which is why we record the size of the goal,
|
|
% as a count of the unifications in it). The wrapping process
|
|
% needs the goal info of the unification that puts
|
|
% the top function symbol of the ground term in place.
|
|
mkgi_size :: int,
|
|
mkgi_old_goal_info :: hlds_goal_info
|
|
)
|
|
; broken_no_gi.
|
|
% No, the goal has not kept the fgt{i,c} invariants.
|
|
|
|
% The code needed to build a variable, and whether it breaks the fgt{i,c}
|
|
% invariants.
|
|
%
|
|
:- type fgt_build_info
|
|
---> fgt_build_info(
|
|
maybe_kept_goal_info,
|
|
cord(hlds_goal)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
introduce_partial_fgt_scopes(GoalInfo0, SubGoalInfo0, RevMarkedSubGoals,
|
|
Order, SubGoal) :-
|
|
introduce_partial_fgt_scopes_loop(RevMarkedSubGoals, Order,
|
|
map.init, BuildInfoMap),
|
|
map.values(BuildInfoMap, BuildInfos),
|
|
list.map(project_build_info_goal_cord, BuildInfos, BuildGoalCords),
|
|
BuildGoalCord = cord_list_to_cord(BuildGoalCords),
|
|
BuildGoals = cord.list(BuildGoalCord),
|
|
(
|
|
BuildGoals = [],
|
|
unexpected($pred, "BuildGoals = []")
|
|
;
|
|
BuildGoals = [SubGoal1]
|
|
;
|
|
BuildGoals = [_, _ | _],
|
|
SubGoalExpr1 = conj(plain_conj, BuildGoals),
|
|
SubGoal1 = hlds_goal(SubGoalExpr1, SubGoalInfo0)
|
|
),
|
|
( if goal_info_has_feature(GoalInfo0, feature_from_head) then
|
|
attach_features_to_all_goals([feature_from_head],
|
|
attach_in_from_ground_term, SubGoal1, SubGoal)
|
|
else
|
|
SubGoal = SubGoal1
|
|
).
|
|
|
|
% The main input to this predicate is a list of the goals in an fgt{i,c}
|
|
% scope, as transformed and marked up by a compiler pass.
|
|
% All the original goals were of the form X = f(Y1, ..., Yn), and
|
|
% the list is in construct order, so that the code constructing the Yi
|
|
% precedes the code constructing X. Each of the Yi will have appeared
|
|
% exactly twice in the original goal sequence: once when constructed,
|
|
% and once when used.
|
|
%
|
|
% Process this sequence by creating an entry for a variable in
|
|
% !BuildInfoMap when it is constructed, and removing it when it is used.
|
|
% At the end, the final BuildInfoMap should contain exactly one entry,
|
|
% for the variable that the fgt scope is for.
|
|
%
|
|
% Each entry gives the code sequence required to build the variable
|
|
% and all its components, and says whether that code obeys the required
|
|
% fgt{i,c} invariants. In process of building the code for an X that
|
|
% breaks these invariants, we wrap fgt{i,c} scopes around the code
|
|
% subsequences that construct any of the Yi that keep those invariants.
|
|
%
|
|
:- pred introduce_partial_fgt_scopes_loop(list(fgt_marked_goal)::in,
|
|
goal_order::in, fgt_build_info_map::in, fgt_build_info_map::out) is det.
|
|
|
|
introduce_partial_fgt_scopes_loop([], _Order, !BuildInfoMap).
|
|
introduce_partial_fgt_scopes_loop([RevMarkedGoal | RevMarkedGoals], Order,
|
|
!BuildInfoMap) :-
|
|
(
|
|
RevMarkedGoal = fgt_kept_goal(Goal, Var, ArgVars),
|
|
SavedBuildInfoMap = !.BuildInfoMap,
|
|
lookup_and_remove_arg_vars(ArgVars, cord.init, ArgsGoalCord0,
|
|
kept, Kept, 0, TotalArgSize, !BuildInfoMap),
|
|
(
|
|
Kept = kept,
|
|
ArgsGoalCord = ArgsGoalCord0,
|
|
Goal = hlds_goal(_, OldGoalInfo),
|
|
TotalSize = TotalArgSize + 1,
|
|
KeptGI = kept_old_gi(TotalSize, OldGoalInfo)
|
|
;
|
|
Kept = broken,
|
|
!:BuildInfoMap = SavedBuildInfoMap,
|
|
lookup_and_remove_arg_vars_insert_fgt(ArgVars, Order,
|
|
cord.init, ArgsGoalCord, !BuildInfoMap),
|
|
KeptGI = broken_no_gi
|
|
)
|
|
;
|
|
RevMarkedGoal = fgt_broken_goal(Goal, Var, ArgVars),
|
|
lookup_and_remove_arg_vars_insert_fgt(ArgVars, Order,
|
|
cord.init, ArgsGoalCord, !BuildInfoMap),
|
|
KeptGI = broken_no_gi
|
|
),
|
|
(
|
|
Order = construct_bottom_up,
|
|
GoalCord = cord.snoc(ArgsGoalCord, Goal)
|
|
;
|
|
Order = deconstruct_top_down,
|
|
GoalCord = cord.cons(Goal, ArgsGoalCord)
|
|
),
|
|
VarBuildInfo = fgt_build_info(KeptGI, GoalCord),
|
|
map.det_insert(Var, VarBuildInfo, !BuildInfoMap),
|
|
introduce_partial_fgt_scopes_loop(RevMarkedGoals, Order, !BuildInfoMap).
|
|
|
|
% Loop over all the Yi in a unification of the form X = f(Y1, ..., Yn),
|
|
% and report the goal cord needed to build all the Yi (!:GoalCord),
|
|
% whether all parts of that code keep the fgt{i,c} invariants (!:Kept).
|
|
% In the process, remove all the Yi from !:BuildInfoMap, since they
|
|
% should never appear in the reverse goal sequence again.
|
|
%
|
|
:- pred lookup_and_remove_arg_vars(list(prog_var)::in,
|
|
cord(hlds_goal)::in, cord(hlds_goal)::out,
|
|
maybe_kept::in, maybe_kept::out, int::in, int::out,
|
|
fgt_build_info_map::in, fgt_build_info_map::out) is det.
|
|
|
|
lookup_and_remove_arg_vars([], !GoalCord, !Kept, !TotalSize, !BuildInfoMap).
|
|
lookup_and_remove_arg_vars([Var | Vars], !GoalCord, !Kept, !TotalSize,
|
|
!BuildInfoMap) :-
|
|
map.det_remove(Var, BuildInfo, !BuildInfoMap),
|
|
BuildInfo = fgt_build_info(VarKept, VarGoalCord),
|
|
(
|
|
VarKept = kept_old_gi(Size, _),
|
|
!:TotalSize = !.TotalSize + Size
|
|
;
|
|
VarKept = broken_no_gi,
|
|
% The final TotalSize won't be consulted, so it is OK not to
|
|
% update it here.
|
|
!:Kept = broken
|
|
),
|
|
!:GoalCord = !.GoalCord ++ VarGoalCord,
|
|
lookup_and_remove_arg_vars(Vars, !GoalCord, !Kept, !TotalSize,
|
|
!BuildInfoMap).
|
|
|
|
% Loop over all the Yi in a unification of the form X = f(Y1, ..., Yn).
|
|
% We know that either the code that builds the top functor of X or
|
|
% the code that builds one or more of the Yi breaks the fgt{i,c}
|
|
% invariants, so wrap the codes that build the OTHER Yi in fgt{i,c}
|
|
% scopes. Return the code needed to build all Yi (in !:GoalCord).
|
|
% In the process, remove all the Yi from !:BuildInfoMap, since they
|
|
% should never appear in the reverse goal sequence again.
|
|
%
|
|
:- pred lookup_and_remove_arg_vars_insert_fgt(list(prog_var)::in,
|
|
goal_order::in, cord(hlds_goal)::in, cord(hlds_goal)::out,
|
|
fgt_build_info_map::in, fgt_build_info_map::out) is det.
|
|
|
|
lookup_and_remove_arg_vars_insert_fgt([], _Order, !GoalCord, !BuildInfoMap).
|
|
lookup_and_remove_arg_vars_insert_fgt([Var | Vars], Order, !GoalCord,
|
|
!BuildInfoMap) :-
|
|
map.det_remove(Var, BuildInfo, !BuildInfoMap),
|
|
BuildInfo = fgt_build_info(VarKept, VarGoalCord0),
|
|
(
|
|
VarKept = kept_old_gi(Size0, GoalInfo0),
|
|
( if cord.is_empty(VarGoalCord0) then
|
|
unexpected($pred, "VarGoalCord0 is empty")
|
|
else
|
|
MaybeThreshold = get_maybe_from_ground_term_threshold,
|
|
( if
|
|
MaybeThreshold = yes(Threshold),
|
|
Size0 >= Threshold
|
|
then
|
|
goal_info_set_nonlocals(set_of_var.make_singleton(Var),
|
|
GoalInfo0, GoalInfo),
|
|
VarGoals0 = cord.list(VarGoalCord0),
|
|
ConjGoalExpr = conj(plain_conj, VarGoals0),
|
|
ConjGoal = hlds_goal(ConjGoalExpr, GoalInfo),
|
|
(
|
|
Order = deconstruct_top_down,
|
|
Kind = from_ground_term_initial
|
|
;
|
|
Order = construct_bottom_up,
|
|
Kind = from_ground_term_construct
|
|
),
|
|
Reason = from_ground_term(Var, Kind),
|
|
ScopeGoalExpr = scope(Reason, ConjGoal),
|
|
ScopeGoal = hlds_goal(ScopeGoalExpr, GoalInfo),
|
|
VarGoalCord = cord.singleton(ScopeGoal)
|
|
else
|
|
VarGoalCord = VarGoalCord0
|
|
)
|
|
)
|
|
;
|
|
VarKept = broken_no_gi,
|
|
VarGoalCord = VarGoalCord0
|
|
),
|
|
!:GoalCord = !.GoalCord ++ VarGoalCord,
|
|
lookup_and_remove_arg_vars_insert_fgt(Vars, Order, !GoalCord,
|
|
!BuildInfoMap).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Return the hlds goal cord part of a fgt_build_info.
|
|
%
|
|
:- pred project_build_info_goal_cord(fgt_build_info::in, cord(hlds_goal)::out)
|
|
is det.
|
|
|
|
project_build_info_goal_cord(fgt_build_info(_, GoalCord), GoalCord).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module hlds.from_ground_term_util.
|
|
%---------------------------------------------------------------------------%
|