mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 14:57:03 +00:00
Estimated hours taken: 25 Branches: main Package the type_info_varmap and typeclass_info_varmap types into an ADT called rtti_varmaps. There are two main purposes for this: - We wish to extend this set of maps with new maps. Doing this will be a lot easier and less error prone if all of the maps are packaged in a single data structure. - Any new maps that we add may contain redundant information that just makes searching the maps more efficient. Therefore they must be kept consistent with the existing maps. Having all the maps inside an ADT makes it easier to ensure this. This change also includes two extensions to the maps. First, the typeclass_info_map is made reversible so that it is possible to efficiently look up the constraint for a given typeclass_info variable. Second, a new map from prog_vars to types makes it possible to efficiently look up the type that a given type_info variable is for. These two changes mean that it is no longer necessary to consult the argument of type_info/1 or typeclass_info/1 to find this information. (We still do put that information there, though; changing the RTTI is left for a separate change.) compiler/hlds_pred.m: Move items relating to type_infos and typeclass_infos into a section of their own. Add a type `rtti_var_info' to hold information about the contents of a type_info or typeclass_info variable. Define the rtti_varmaps abstract data type. This data structure consists of the type_info_varmap and the typeclass_info_varmap. Add a new map, type_info_type_map, which is like the inverse to the type_info_varmap. The difference is that the latter can point to locations that are inside a typeclass_info variable, whereas the former only refers to type_info variables. Note that the combined maps do not form a bijection, or even an injection, since it is possible for two different type variables to point to the same location (that is, if they are aliased). Make the typeclass_info_varmap reversible, by using the new module injection.m. Unlike the type_info_varmap, this map is always injective since the same typeclass_info cannot be used for two different constraints. The predicates rtti_det_insert_type_info_locn and set_type_info_locn, which update the type_info_varmap, contain sanity checks to ensure that only type variables that have already been registered with the type_info_type_map are used, and that the information in both maps is consistent. Use the rtti_varmaps structure in proc_info and clauses_info, in place of type_info_varmap and typeclass_info_varmap. compiler/polymorphism.m: Remove polymorphism__type_info_or_ctor_type/2 and polymorphism__typeclass_info_class_constraint/2, to ensure that nobody tries to use the information in the type argument. Replace them with two similar predicates that test if a type is type_info or typeclass_info, but that don't return the argument. Ensure that the new type_info_type_map in the rtti_varmaps is kept up to date by threading the rtti_varmaps through a few more places. Some of these places are exported, so this part of the change affects other modules as well. Fix a comment that referred to a non-existent predicate. compiler/type_util.m: Remove the predicates apply_substitutions_to_var_map/5 and apply_substitutions_to_typeclass_var_map/5. The functionality is now provided by the new ADT. compiler/cse_detection.m: Rewrite update_existential_data_structures/4 to use the interface provided by rtti_varmaps. The algorithm for doing this has changed in the following ways: - The first pass, which builds a map from changed locations in the first branch to the tvars concerned, is modified slightly to traverse over the keys instead of over key-value pairs. - The second pass, which previously calculated the induced type substitution and reconstructed the type_info_varmap now only does the former. - Applying the prog_var transformation and the induced type substitution is done at the end, using the interface to rtti_varmaps. compiler/goal_util.m: Rewrite goal_util__extra_nonlocal_typeinfos/6 to avoid the need for using map__member/3 on the typeclass_info_varmap (about which the existing comments say "this is probably not very efficient..."), and to be more efficient in general. Previously, we nondeterministically generated non-local type vars and then tested each constraint to see if it had the type var in it. Now, we go through each constraint one at a time and check if any of the type variables in it are non-local. This is more efficient because we only need to find one non-local type in order to include the typeclass_info in the non-locals -- the remaining (duplicate) solutions are pruned away. compiler/higher_order.m: Use the new maps instead of looking at the arguments of type_info/1 and typeclass_info/1 types. We plan to remove this information from type_info and typeclass_info types in future. Previously, this module used the type argument in order to update the varmaps when the curried arguments of a higher order call are added as arguments to the procedure in which the call occurs. We now look up this information at the point where the curried arg variables are known, and store this information in higher_order_arg alongside the types where it used to be stored. This structure is threaded through to the place where the information is needed. Fix a cut and paste bug in higher_order_arg_depth/1. It was previously calling higher_order_args_size/1 in the recursive call, instead of calling higher_order_args_depth/1. compiler/inlining.m: In inlining__do_inline_call, apply the substitutions to the entire rtti_varmaps structure, not just to the type_info_varmap. (XXX Is there a good reason why the substitution should _not_ be applied to the typeclass_info varmap?) compiler/magic_util.m: Avoid using polymorphism__type_info_or_ctor_type/2 and polymorphism__typeclass_info_class_constraint/2, as these are no longer supported. compiler/*.m: Straightforward changes to use the new ADT. library/injection.m: New library module. This provides an `injection' type which is similar to the existing `bimap' type in that it implements back-to-back maps, but doesn't have such stringent invariants imposed. In particular, the reverse map is not required to be injective. This type is used to model the relationship between prog_constraints and program variables that hold typeclass_infos for them. Namely, the typeclass_info for a constraint can be held in two different variables, but one variable can never hold the typeclass_info for two distinct constraints. library/library.m: Add the new library module. library/list.m: Add list__foldl_corresponding and list__foldl2_corresponding, which traverse two lists in parallel, which one or two accumulators, and abort if there is a length mismatch. NEWS: Mention the changes to the standard library.
284 lines
11 KiB
Mathematica
284 lines
11 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2001-2005 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: delay_construct.m
|
|
%
|
|
% Author: zs.
|
|
%
|
|
% This module transforms sequences of goals in procedure bodies.
|
|
% It looks for a unification that constructs a ground term followed by
|
|
% primitive goals, at least one of which can fail, and none of which take
|
|
% the variable representing the cell as their input. Such code sequences
|
|
% cause the cell to be constructed even if the following goal would fail,
|
|
% which is wasteful. This module therefore reorders the sequence, moving the
|
|
% construction unification past all the semidet primitives it can.
|
|
%
|
|
% The reason we don't move the construction past calls or composite goals
|
|
% is that this may require storing the input arguments of the construction on
|
|
% the stack, which may cause a slowdown bigger than the speedup available from
|
|
% not having to construct the cell on some execution paths.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module transform_hlds__delay_construct.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds__hlds_module.
|
|
:- import_module hlds__hlds_pred.
|
|
|
|
:- import_module io.
|
|
|
|
:- pred delay_construct_proc(pred_id::in, proc_id::in, module_info::in,
|
|
proc_info::in, proc_info::out, io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds__inst_match.
|
|
:- import_module hlds__hlds_data.
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module hlds__instmap.
|
|
:- import_module hlds__passes_aux.
|
|
:- import_module libs__globals.
|
|
:- import_module parse_tree__prog_data.
|
|
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module std_util.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
delay_construct_proc(PredId, ProcId, ModuleInfo, !ProcInfo, !IO) :-
|
|
write_proc_progress_message("% Delaying construction unifications in ",
|
|
PredId, ProcId, ModuleInfo, !IO),
|
|
globals__io_get_globals(Globals, !IO),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
delay_construct_proc_no_io(PredInfo, ModuleInfo, Globals,
|
|
!ProcInfo).
|
|
|
|
:- pred delay_construct_proc_no_io(pred_info::in, module_info::in, globals::in,
|
|
proc_info::in, proc_info::out) is det.
|
|
|
|
delay_construct_proc_no_io(PredInfo, ModuleInfo, Globals, !ProcInfo) :-
|
|
body_should_use_typeinfo_liveness(PredInfo, Globals,
|
|
BodyTypeinfoLiveness),
|
|
proc_info_vartypes(!.ProcInfo, VarTypes),
|
|
proc_info_rtti_varmaps(!.ProcInfo, RttiVarMaps),
|
|
proc_info_get_initial_instmap(!.ProcInfo, ModuleInfo, InstMap0),
|
|
DelayInfo = delay_construct_info(ModuleInfo, BodyTypeinfoLiveness,
|
|
VarTypes, RttiVarMaps),
|
|
proc_info_goal(!.ProcInfo, Goal0),
|
|
delay_construct_in_goal(Goal0, InstMap0, DelayInfo, Goal),
|
|
proc_info_set_goal(Goal, !ProcInfo).
|
|
|
|
:- type delay_construct_info
|
|
---> delay_construct_info(
|
|
module_info :: module_info,
|
|
body_typeinfo_liveness :: bool,
|
|
vartypes :: vartypes,
|
|
rtti_varmaps :: rtti_varmaps
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred delay_construct_in_goal(hlds_goal::in, instmap::in,
|
|
delay_construct_info::in, hlds_goal::out) is det.
|
|
|
|
delay_construct_in_goal(GoalExpr0 - GoalInfo0, InstMap0, DelayInfo, Goal) :-
|
|
(
|
|
GoalExpr0 = conj(Goals0),
|
|
goal_info_get_determinism(GoalInfo0, Detism),
|
|
determinism_components(Detism, CanFail, MaxSoln),
|
|
(
|
|
% If the conjunction cannot fail, then its conjuncts
|
|
% cannot fail either, so we have no hope of pushing a
|
|
% construction past a failing goal.
|
|
%
|
|
% If the conjuntion contains goals that can succeed
|
|
% more than once, which is possible if MaxSoln is
|
|
% at_most_many or at_most_many_cc, then moving a
|
|
% construction to the right may increase the number of
|
|
% times the construction is executed. We are therefore
|
|
% careful to make sure delay_construct_in_conj doesn't
|
|
% move constructions across goals that succeed more
|
|
% than once.
|
|
%
|
|
% If the conjunction cannot succeed, i.e. MaxSoln is
|
|
% at_most_zero, there is no point in trying to speed it
|
|
% up.
|
|
|
|
CanFail = can_fail,
|
|
MaxSoln \= at_most_zero
|
|
->
|
|
delay_construct_in_conj(Goals0, InstMap0, DelayInfo,
|
|
set__init, [], Goals1)
|
|
;
|
|
Goals1 = Goals0
|
|
),
|
|
delay_construct_in_goals(Goals1, InstMap0, DelayInfo, Goals),
|
|
Goal = conj(Goals) - GoalInfo0
|
|
;
|
|
GoalExpr0 = par_conj(Goals0),
|
|
delay_construct_in_goals(Goals0, InstMap0, DelayInfo, Goals),
|
|
Goal = par_conj(Goals) - GoalInfo0
|
|
;
|
|
GoalExpr0 = disj(Goals0),
|
|
delay_construct_in_goals(Goals0, InstMap0, DelayInfo, Goals),
|
|
Goal = disj(Goals) - GoalInfo0
|
|
;
|
|
GoalExpr0 = not(NegGoal0),
|
|
delay_construct_in_goal(NegGoal0, InstMap0, DelayInfo, NegGoal),
|
|
Goal = not(NegGoal) - GoalInfo0
|
|
;
|
|
GoalExpr0 = switch(Var, CanFail, Cases0),
|
|
delay_construct_in_cases(Cases0, InstMap0, DelayInfo, Cases),
|
|
Goal = switch(Var, CanFail, Cases) - GoalInfo0
|
|
;
|
|
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
|
|
Cond0 = _ - CondInfo0,
|
|
goal_info_get_instmap_delta(CondInfo0, CondInstMapDelta),
|
|
instmap__apply_instmap_delta(InstMap0, CondInstMapDelta,
|
|
InstMapThen),
|
|
delay_construct_in_goal(Cond0, InstMap0, DelayInfo, Cond),
|
|
delay_construct_in_goal(Then0, InstMapThen, DelayInfo, Then),
|
|
delay_construct_in_goal(Else0, InstMap0, DelayInfo, Else),
|
|
Goal = if_then_else(Vars, Cond, Then, Else) - GoalInfo0
|
|
;
|
|
GoalExpr0 = scope(Reason, SubGoal0),
|
|
delay_construct_in_goal(SubGoal0, InstMap0, DelayInfo, SubGoal),
|
|
Goal = scope(Reason, SubGoal) - GoalInfo0
|
|
;
|
|
GoalExpr0 = generic_call(_, _, _, _),
|
|
Goal = GoalExpr0 - GoalInfo0
|
|
;
|
|
GoalExpr0 = call(_, _, _, _, _, _),
|
|
Goal = GoalExpr0 - GoalInfo0
|
|
;
|
|
GoalExpr0 = unify(_, _, _, _, _),
|
|
Goal = GoalExpr0 - GoalInfo0
|
|
;
|
|
GoalExpr0 = foreign_proc(_, _, _, _, _, _),
|
|
Goal = GoalExpr0 - GoalInfo0
|
|
;
|
|
GoalExpr0 = shorthand(_),
|
|
% these should have been expanded out by now
|
|
error("delay_construct_in_goal: unexpected shorthand")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% We maintain a list of delayed construction unifications that construct ground
|
|
% terms, and the set of variables they define.
|
|
%
|
|
% When we find other construction unifications, we add them to the list.
|
|
% It does not matter if they depend on other delayed construction unifications;
|
|
% when we put them back into the conjunction, we do so in the original order.
|
|
%
|
|
% There are several reasons why we may not be able to delay a construction
|
|
% unification past a conjunct. The conjunct may not be a primitive goal,
|
|
% or it may be impure; in either case, we must insert all the delayed
|
|
% construction unifications before it. The conjunct may also require the value
|
|
% of a variable defined by a construction unification. In such cases, we could
|
|
% drop before that goal only the construction unifications that define the
|
|
% variables needed by the conjunct, either directly or indirectly through
|
|
% the values required by some of those construction unifications. However,
|
|
% separating out this set of delayed constructions from the others would
|
|
% require somewhat complex code, and it is not clear that there would be any
|
|
% significant benefit. We therefore insert *all* the delayed constructions
|
|
% before a goal if the goal requires *any* of the variables bound by the
|
|
% constructions.
|
|
%
|
|
% The instmap we pass around is the one that we construct from the original
|
|
% conjunction order. At each point, it reflects the bindings made by the
|
|
% conjuncts so far *plus* the bindings made by the delayed goals.
|
|
|
|
:- pred delay_construct_in_conj(list(hlds_goal)::in, instmap::in,
|
|
delay_construct_info::in, set(prog_var)::in, list(hlds_goal)::in,
|
|
list(hlds_goal)::out) is det.
|
|
|
|
delay_construct_in_conj([], _, _, _, RevDelayedGoals, DelayedGoals) :-
|
|
list__reverse(RevDelayedGoals, DelayedGoals).
|
|
delay_construct_in_conj([Goal0 | Goals0], InstMap0, DelayInfo,
|
|
ConstructedVars0, RevDelayedGoals0, Goals) :-
|
|
Goal0 = GoalExpr0 - GoalInfo0,
|
|
goal_info_get_instmap_delta(GoalInfo0, InstMapDelta0),
|
|
instmap__apply_instmap_delta(InstMap0, InstMapDelta0, InstMap1),
|
|
(
|
|
GoalExpr0 = unify(_, _, _, Unif, _),
|
|
Unif = construct(Var, _, Args, _, _, _, _),
|
|
Args = [_ | _], % We are constructing a cell, not a constant
|
|
instmap__lookup_var(InstMap0, Var, Inst0),
|
|
inst_is_free(DelayInfo ^ module_info, Inst0),
|
|
instmap__lookup_var(InstMap1, Var, Inst1),
|
|
inst_is_ground(DelayInfo ^ module_info, Inst1)
|
|
->
|
|
set__insert(ConstructedVars0, Var, ConstructedVars1),
|
|
RevDelayedGoals1 = [Goal0 | RevDelayedGoals0],
|
|
delay_construct_in_conj(Goals0, InstMap1, DelayInfo,
|
|
ConstructedVars1, RevDelayedGoals1, Goals)
|
|
;
|
|
Goal0 = GoalExpr0 - GoalInfo0,
|
|
delay_construct_skippable(GoalExpr0, GoalInfo0),
|
|
goal_info_get_nonlocals(GoalInfo0, NonLocals),
|
|
proc_info_maybe_complete_with_typeinfo_vars(NonLocals,
|
|
DelayInfo ^ body_typeinfo_liveness,
|
|
DelayInfo ^ vartypes,
|
|
DelayInfo ^ rtti_varmaps, CompletedNonLocals),
|
|
set__intersect(CompletedNonLocals, ConstructedVars0,
|
|
Intersection),
|
|
set__empty(Intersection),
|
|
\+ goal_info_has_feature(GoalInfo0, impure),
|
|
\+ goal_info_has_feature(GoalInfo0, semipure)
|
|
->
|
|
delay_construct_in_conj(Goals0, InstMap1, DelayInfo,
|
|
ConstructedVars0, RevDelayedGoals0, Goals1),
|
|
Goals = [Goal0 | Goals1]
|
|
;
|
|
list__reverse(RevDelayedGoals0, DelayedGoals),
|
|
delay_construct_in_conj(Goals0, InstMap1, DelayInfo,
|
|
set__init, [], Goals1),
|
|
list__append(DelayedGoals, [Goal0 | Goals1], Goals)
|
|
).
|
|
|
|
:- pred delay_construct_skippable(hlds_goal_expr::in, hlds_goal_info::in)
|
|
is semidet.
|
|
|
|
delay_construct_skippable(GoalExpr, GoalInfo) :-
|
|
(
|
|
GoalExpr = unify(_, _, _, _, _)
|
|
;
|
|
GoalExpr = call(_, _, _, inline_builtin, _, _)
|
|
),
|
|
goal_info_get_determinism(GoalInfo, Detism),
|
|
determinism_components(Detism, _CanFail, MaxSoln),
|
|
MaxSoln \= at_most_many.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred delay_construct_in_goals(list(hlds_goal)::in, instmap::in,
|
|
delay_construct_info::in, list(hlds_goal)::out) is det.
|
|
|
|
delay_construct_in_goals([], _, _, []).
|
|
delay_construct_in_goals([Goal0 | Goals0], InstMap0, DelayInfo,
|
|
[Goal | Goals]) :-
|
|
delay_construct_in_goal(Goal0, InstMap0, DelayInfo, Goal),
|
|
delay_construct_in_goals(Goals0, InstMap0, DelayInfo, Goals).
|
|
|
|
:- pred delay_construct_in_cases(list(case)::in, instmap::in,
|
|
delay_construct_info::in, list(case)::out) is det.
|
|
|
|
delay_construct_in_cases([], _, _, []).
|
|
delay_construct_in_cases([case(Cons, Goal0) | Cases0], InstMap0, DelayInfo,
|
|
[case(Cons, Goal) | Cases]) :-
|
|
delay_construct_in_goal(Goal0, InstMap0, DelayInfo, Goal),
|
|
delay_construct_in_cases(Cases0, InstMap0, DelayInfo, Cases).
|
|
|
|
%-----------------------------------------------------------------------------%
|