mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-05-01 17:24:34 +00:00
Estimated hours taken: 12 Four loosely related changes to improve mode inference. -------------------- (1) Fix a problem with inference of unique modes. Previously it was sufficient to just look at the final insts of the mode and check whether they were `clobbered' or not in order to tell which arguments will be dead on exit from this procedure. However, that doesn't work for inferred modes, where the final inst will initially be `not_reached'. Previously mode inference just assumed that all arguments to a predicate were live, but that prevents it from being able to infer `di' modes. So I've added a new field to the proc_info to record which arguments are required to be dead on exit. I've also changed mode analysis so that it checks that such arguments really are dead. This means you can declare both `di' and `ui' modes of a predicate, and so long as you declare the `di' one first, mode checking should handle this overloading. It also means that errors such as passing a variable which is live to a procedure with a `di' mode argument are caught earlier, with a potentially better error message; mode checking may also be able to reorder such calls, so that the `di' mode pred is scheduled last. Mode inference now notices if an argument is dead, and if so will try infering a `di' mode. hlds_pred.m: Add a new field to the proc_info, the `maybe_arglives', of type `maybe(list(is_live))'. If set, this records whether or not the arguments are required to be dead after the procedure or not. Add an access predicate which checks whether this is set, and returns either the value set, or if no value special value is set, looks up a value based on the modes by calling get_arg_lives from mode_util.m. make_hlds.m: Add a MaybeArgLives parameter to add_new_pred. (Also change the error messages about pragmas to reflect the new pragma syntax.) clause_to_proc.m, unify_proc.m: Pass MaybeArgLives = no to add_new_pred. modes.m, mode_errors.m: Use the new arglives field. When mode-checking a call (or higher-order call), check that all the variables which are required to be dead really are dead. unique_modes.m: Use the new arglives field. mode_util.m: Add predicate `get_arg_lives', for use by modes.m and hlds_pred.m. -------------------- (2) Fix a problem with mode inference looping. It was checking whether a fixpoint had been reached by comparing the (un-normalised) inferred final insts with the previously recorded final insts (which had been normalised). Instead, it has to normalise the insts before the comparison. modes.m: When doing mode analysis of an inferred mode, normalise the insts before checking whether they've changed, rather than vice versa. -------------------- (3) Fix a problem with the computation of liveness for mode-analysis of if-then-elses that was detected when I tried to bootcheck the above changes. modes.m, unique_modes.m: When mode-checking if-then-elses, the variables in the "else" should not be considered live when checking the condition. (They're nondet-live, but not live with regard to forward execution.) -------------------- (4) Reduce the occurrence of mode inference inferring spurious `in(not_reached)' modes. modes.m: If the top-level inst of a variable becomes `not_reached', set the whole instmap to `unreachable'.
180 lines
6.6 KiB
Mathematica
180 lines
6.6 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1995 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.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module clause_to_proc.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds_pred, hlds_module.
|
|
|
|
% In the hlds, we initially record the clauses for a predicate
|
|
% in the clauses_info data structure which is part of the
|
|
% pred_info data structure. But once the clauses have been
|
|
% type-checked, we want to have a separate copy of each clause
|
|
% for each different mode of the predicate, since we may
|
|
% end up reordering the clauses differently in different modes.
|
|
% Here we copy the clauses from the clause_info data structure
|
|
% into the proc_info data structure. Each clause is marked
|
|
% with a list of the modes for which it applies, so that
|
|
% there can be different code to implement different modes
|
|
% of a predicate (e.g. sort). For each mode of the predicate,
|
|
% we select the clauses for that mode, disjoin them together,
|
|
% and save this in the proc_info.
|
|
|
|
:- pred copy_module_clauses_to_procs(list(pred_id), module_info, module_info).
|
|
:- mode copy_module_clauses_to_procs(in, in, out) is det.
|
|
|
|
:- pred copy_clauses_to_procs(pred_info, pred_info).
|
|
:- mode copy_clauses_to_procs(in, out) is det.
|
|
|
|
:- pred copy_clauses_to_proc(proc_id, clauses_info, proc_info, proc_info).
|
|
:- mode copy_clauses_to_proc(in, in, in, out) is det.
|
|
|
|
% Before copying the clauses to the procs, we need to add
|
|
% a default mode of `:- mode foo(in, in, ..., in) = out.'
|
|
% for functions that don't have an explicit mode declaration.
|
|
|
|
:- pred maybe_add_default_modes(list(pred_id), pred_table, pred_table).
|
|
:- mode maybe_add_default_modes(in, in, out) is det.
|
|
|
|
:- pred maybe_add_default_mode(pred_info, pred_info).
|
|
:- mode maybe_add_default_mode(in, out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds_goal, hlds_data, prog_data, make_hlds.
|
|
:- import_module int, list, set, map, std_util.
|
|
|
|
maybe_add_default_modes([], Preds, Preds).
|
|
maybe_add_default_modes([PredId | PredIds], Preds0, Preds) :-
|
|
map__lookup(Preds0, PredId, PredInfo0),
|
|
maybe_add_default_mode(PredInfo0, PredInfo),
|
|
map__set(Preds0, PredId, PredInfo, Preds1),
|
|
maybe_add_default_modes(PredIds, Preds1, Preds).
|
|
|
|
maybe_add_default_mode(PredInfo0, PredInfo) :-
|
|
pred_info_procedures(PredInfo0, Procs0),
|
|
pred_info_get_is_pred_or_func(PredInfo0, PredOrFunc),
|
|
(
|
|
%
|
|
% Is this a function with no modes?
|
|
%
|
|
PredOrFunc = function,
|
|
map__is_empty(Procs0)
|
|
->
|
|
%
|
|
% If so, add a default mode of
|
|
%
|
|
% :- mode foo(in, in, ..., in) = out is det.
|
|
%
|
|
% for this function. (N.B. functions which can
|
|
% fail must be explicitly declared as semidet.)
|
|
%
|
|
pred_info_arity(PredInfo0, PredArity),
|
|
FuncArity is PredArity - 1,
|
|
InMode = user_defined_mode(unqualified("in"), []),
|
|
OutMode = user_defined_mode(unqualified("out"), []),
|
|
list__duplicate(FuncArity, InMode, FuncArgModes),
|
|
FuncRetMode = OutMode,
|
|
list__append(FuncArgModes, [FuncRetMode], PredArgModes),
|
|
Determinism = det,
|
|
pred_info_context(PredInfo0, Context),
|
|
MaybePredArgLives = no,
|
|
add_new_proc(PredInfo0, PredArity, PredArgModes,
|
|
MaybePredArgLives, yes(Determinism), Context,
|
|
PredInfo, _ProcId)
|
|
;
|
|
PredInfo = PredInfo0
|
|
).
|
|
|
|
copy_module_clauses_to_procs(PredIds, ModuleInfo0, ModuleInfo) :-
|
|
module_info_preds(ModuleInfo0, Preds0),
|
|
copy_module_clauses_to_procs_2(PredIds, Preds0, Preds),
|
|
module_info_set_preds(ModuleInfo0, Preds, ModuleInfo).
|
|
|
|
:- pred copy_module_clauses_to_procs_2(list(pred_id), pred_table, pred_table).
|
|
:- mode copy_module_clauses_to_procs_2(in, in, out) is det.
|
|
|
|
copy_module_clauses_to_procs_2([], Preds, Preds).
|
|
copy_module_clauses_to_procs_2([PredId | PredIds], Preds0, Preds) :-
|
|
map__lookup(Preds0, PredId, PredInfo0),
|
|
copy_clauses_to_procs(PredInfo0, PredInfo),
|
|
map__det_update(Preds0, PredId, PredInfo, Preds1),
|
|
copy_module_clauses_to_procs_2(PredIds, Preds1, Preds).
|
|
|
|
|
|
copy_clauses_to_procs(PredInfo0, PredInfo) :-
|
|
pred_info_procedures(PredInfo0, Procs0),
|
|
pred_info_clauses_info(PredInfo0, ClausesInfo),
|
|
pred_info_non_imported_procids(PredInfo0, ProcIds),
|
|
copy_clauses_to_procs_2(ProcIds, ClausesInfo, Procs0, Procs),
|
|
pred_info_set_procedures(PredInfo0, Procs, PredInfo).
|
|
|
|
:- pred copy_clauses_to_procs_2(list(proc_id), clauses_info,
|
|
proc_table, proc_table).
|
|
:- mode copy_clauses_to_procs_2(in, in, in, out) is det.
|
|
|
|
copy_clauses_to_procs_2([], _, Procs, Procs).
|
|
copy_clauses_to_procs_2([ProcId | ProcIds], ClausesInfo, Procs0, Procs) :-
|
|
map__lookup(Procs0, ProcId, Proc0),
|
|
copy_clauses_to_proc(ProcId, ClausesInfo, Proc0, Proc),
|
|
map__set(Procs0, ProcId, Proc, Procs1),
|
|
copy_clauses_to_procs_2(ProcIds, ClausesInfo, Procs1, Procs).
|
|
|
|
copy_clauses_to_proc(ProcId, ClausesInfo, Proc0, Proc) :-
|
|
ClausesInfo = clauses_info(VarSet, VarTypes, HeadVars, Clauses),
|
|
select_matching_clauses(Clauses, ProcId, MatchingClauses),
|
|
get_clause_goals(MatchingClauses, GoalList),
|
|
( GoalList = [SingleGoal] ->
|
|
Goal = SingleGoal
|
|
;
|
|
% Construct a goal_info for the disjunction.
|
|
% We use the context of the first clause, unless
|
|
% there weren't any clauses at all, in which case
|
|
% we use the context of the mode declaration.
|
|
% The non-local vars are just the head variables.
|
|
goal_info_init(GoalInfo0),
|
|
( GoalList = [FirstGoal | _] ->
|
|
FirstGoal = _ - FirstGoalInfo,
|
|
goal_info_context(FirstGoalInfo, Context)
|
|
;
|
|
proc_info_context(Proc0, Context)
|
|
),
|
|
goal_info_set_context(GoalInfo0, Context, GoalInfo1),
|
|
set__list_to_set(HeadVars, NonLocalVars),
|
|
goal_info_set_nonlocals(GoalInfo1, NonLocalVars, GoalInfo),
|
|
map__init(Empty),
|
|
Goal = disj(GoalList, Empty) - GoalInfo
|
|
),
|
|
proc_info_set_body(Proc0, VarSet, VarTypes, HeadVars, Goal, Proc).
|
|
|
|
:- pred select_matching_clauses(list(clause), proc_id, list(clause)).
|
|
:- mode select_matching_clauses(in, in, out) is det.
|
|
|
|
select_matching_clauses([], _, []).
|
|
select_matching_clauses([Clause | Clauses], ProcId, MatchingClauses) :-
|
|
Clause = clause(ProcIds, _, _),
|
|
( ProcIds = [] ->
|
|
MatchingClauses = [Clause | MatchingClauses1]
|
|
; list__member(ProcId, ProcIds) ->
|
|
MatchingClauses = [Clause | MatchingClauses1]
|
|
;
|
|
MatchingClauses = MatchingClauses1
|
|
),
|
|
select_matching_clauses(Clauses, ProcId, MatchingClauses1).
|
|
|
|
:- pred get_clause_goals(list(clause)::in, list(hlds__goal)::out) is det.
|
|
|
|
get_clause_goals([], []).
|
|
get_clause_goals([Clause | Clauses], Goals) :-
|
|
Clause = clause(_, Goal, _),
|
|
goal_to_disj_list(Goal, GoalList),
|
|
list__append(GoalList, Goals1, Goals),
|
|
get_clause_goals(Clauses, Goals1).
|
|
|