Files
mercury/compiler/det_util.m
Zoltan Somogyi 8547e1634b Fix some things reported by --warn-inconsistent-pred-order-clauses.
compiler/arg_info.m:
compiler/bytecode_data.m:
compiler/common.m:
compiler/compile_target_code.m:
compiler/delay_info.m:
compiler/det_util.m:
compiler/erl_call_gen.m:
compiler/erl_code_util.m:
compiler/from_ground_term_util.m:
compiler/hlds_out_goal.m:
compiler/inst_match.m:
compiler/inst_util.m:
compiler/mode_constraint_robdd.m:
compiler/ordering_mode_constraints.m:
compiler/simplify_info.m:
compiler/switch_detection.m:
compiler/type_util.m:
compiler/unique_modes.m:
    As above.

compiler/Mercury.options:
    Note a module that is not worth fixing this way.
2017-10-14 19:07:02 +11:00

389 lines
14 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1996-2000,2002-2012 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: det_util.m.
% Main authors: fjh, zs.
%
% Utility predicates used in two or more of the modules concerned with
% determinism: switch_detection, cse_detection, det_analysis, det_report
% and simplify.
%
%-----------------------------------------------------------------------------%
:- module check_hlds.det_util.
:- interface.
:- import_module hlds.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module hlds.instmap.
:- import_module hlds.vartypes.
:- import_module parse_tree.
:- import_module parse_tree.error_util.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.set_of_var.
:- import_module list.
%-----------------------------------------------------------------------------%
:- type maybe_changed
---> changed
; unchanged.
% Should we emit an error message about extra variables in the head
% of a promise_equivalent_solutions scope? Extra variables are
% those non-locals that are not further bound or (potentially) constrained
% by the goal inside the scope.
%
% We ignore such extra variables when re-running determinism
% analysis after optimisations such as inlining have been performed
% because not doing so results in spurious error messages.
% (Inlining can cause variables that had inst any to become ground.)
%
:- type report_pess_extra_vars
---> pess_extra_vars_report
% Emit an error message if the head of a
% promise_equivalent_solutions scope contains variables that
% are not further bound or (potentially) further constrained
% by the goal inside the scope.
; pess_extra_vars_ignore.
% Do not emit an error message if the above occurs.
% Does the predicate being analyzed contain a require_complete_switch
% or require_detism scope?
%
:- type contains_require_scope
---> does_not_contain_require_scope
; contains_require_scope.
% Does the predicate being analyzed contain an incomplete switch?
%
:- type contains_incomplete_switch
---> does_not_contain_incomplete_switch
; contains_incomplete_switch.
% Does the predicate being analyzed contain a call that can be optimized
% by format_call.m?
%
:- type contains_format_call
---> does_not_contain_format_call
; contains_format_call.
:- type det_info.
% delete_unreachable_cases(Cases0, ConsIds, ReachableCases,
% UnreachableGoals):
%
% Given Cases, and ConsIds, a list of the possible cons_ids that
% the switch variable could be bound to, return as ReachableCases
% the subset of Cases whose cons_id occurs in ConsIds.
%
% Return the goals of the unreachable cases in UnreachableGoals.
%
:- pred delete_unreachable_cases(list(case)::in, list(cons_id)::in,
list(case)::out, list(hlds_goal)::out) is det.
% Update the current substitution to account for the effects
% of the given unification.
%
:- pred interpret_unify(prog_var::in, unify_rhs::in,
prog_substitution::in, prog_substitution::out) is semidet.
% Look up the determinism of a procedure, and also return the pred_info
% containing the procedure. Doing both at once allows a small speedup.
%
:- pred det_lookup_pred_info_and_detism(det_info::in, pred_id::in, proc_id::in,
pred_info::out, determinism::out) is det.
:- pred det_get_proc_info(det_info::in, proc_info::out) is det.
:- pred det_lookup_var_type(module_info::in, proc_info::in, prog_var::in,
hlds_type_defn::out) is semidet.
:- pred det_no_output_vars(det_info::in, instmap::in, instmap_delta::in,
set_of_progvar::in) is semidet.
:- pred det_info_add_error_spec(error_spec::in, det_info::in, det_info::out)
is det.
:- pred det_info_init(module_info::in, pred_proc_id::in,
prog_varset::in, vartypes::in, report_pess_extra_vars::in,
list(error_spec)::in, det_info::out) is det.
:- pred det_info_get_module_info(det_info::in, module_info::out) is det.
:- pred det_info_get_pred_proc_id(det_info::in, pred_proc_id::out) is det.
:- pred det_info_get_varset(det_info::in, prog_varset::out) is det.
:- pred det_info_get_vartypes(det_info::in, vartypes::out) is det.
:- pred det_info_get_pess_extra_vars(det_info::in,
report_pess_extra_vars::out) is det.
:- pred det_info_get_has_format_call(det_info::in,
contains_format_call::out) is det.
:- pred det_info_get_has_req_scope(det_info::in,
contains_require_scope::out) is det.
:- pred det_info_get_has_incomplete_switch(det_info::in,
contains_incomplete_switch::out) is det.
:- pred det_info_get_error_specs(det_info::in, list(error_spec)::out) is det.
:- pred det_info_set_module_info(module_info::in, det_info::in, det_info::out)
is det.
:- pred det_info_set_has_format_call(det_info::in, det_info::out) is det.
:- pred det_info_set_has_req_scope(det_info::in, det_info::out) is det.
:- pred det_info_set_has_incomplete_switch(det_info::in, det_info::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.prog_util.
:- import_module map.
:- import_module set_tree234.
:- import_module term.
%-----------------------------------------------------------------------------%
delete_unreachable_cases(Cases0, PossibleConsIds, ReachableCases,
UnreachableGoals) :-
PossibleConsIdSet = set_tree234.list_to_set(PossibleConsIds),
% We use a reverse list accumulator because we want to avoid requiring
% O(n) stack space.
delete_unreachable_cases_acc(Cases0, PossibleConsIdSet,
[], RevReachableCases, [], RevUnreachableGoals),
list.reverse(RevReachableCases, ReachableCases),
list.reverse(RevUnreachableGoals, UnreachableGoals).
:- pred delete_unreachable_cases_acc(list(case)::in, set_tree234(cons_id)::in,
list(case)::in, list(case)::out,
list(hlds_goal)::in, list(hlds_goal)::out) is det.
delete_unreachable_cases_acc([], _PossibleConsIdSet,
!RevReachableCases, !RevUnreachableGoals).
delete_unreachable_cases_acc([Case0 | Cases0], PossibleConsIdSet,
!RevReachableCases, !RevUnreachableGoals) :-
Case0 = case(MainConsId0, OtherConsIds0, Goal),
( if set_tree234.contains(PossibleConsIdSet, MainConsId0) then
list.filter(set_tree234.contains(PossibleConsIdSet),
OtherConsIds0, OtherConsIds),
Case = case(MainConsId0, OtherConsIds, Goal),
!:RevReachableCases = [Case | !.RevReachableCases]
else
list.filter(set_tree234.contains(PossibleConsIdSet),
OtherConsIds0, OtherConsIds1),
(
OtherConsIds1 = [],
% We don't add Case to !RevReachableCases, effectively deleting it.
!:RevUnreachableGoals = [Goal | !.RevUnreachableGoals]
;
OtherConsIds1 = [MainConsId | OtherConsIds],
Case = case(MainConsId, OtherConsIds, Goal),
!:RevReachableCases = [Case | !.RevReachableCases]
)
),
delete_unreachable_cases_acc(Cases0, PossibleConsIdSet,
!RevReachableCases, !RevUnreachableGoals).
interpret_unify(X, rhs_var(Y), !Subst) :-
unify_term(variable(X, context_init), variable(Y, context_init), !Subst).
interpret_unify(X, rhs_functor(ConsId, _, ArgVars), !Subst) :-
term.var_list_to_term_list(ArgVars, ArgTerms),
cons_id_and_args_to_term(ConsId, ArgTerms, RhsTerm),
unify_term(variable(X, context_init), RhsTerm, !Subst).
interpret_unify(_X, rhs_lambda_goal(_, _, _, _, _, _, _, _, _), !Subst).
% For ease of implementation we just ignore unifications with lambda terms.
% This is a safe approximation, it just prevents us from optimizing them
% as well as we would like.
det_lookup_pred_info_and_detism(DetInfo, PredId, ModeId, PredInfo, Detism) :-
det_info_get_module_info(DetInfo, ModuleInfo),
module_info_get_preds(ModuleInfo, PredTable),
map.lookup(PredTable, PredId, PredInfo),
pred_info_get_proc_table(PredInfo, ProcTable),
map.lookup(ProcTable, ModeId, ProcInfo),
proc_info_interface_determinism(ProcInfo, Detism).
det_get_proc_info(DetInfo, ProcInfo) :-
det_info_get_module_info(DetInfo, ModuleInfo),
det_info_get_pred_proc_id(DetInfo, PredProcId),
module_info_proc_info(ModuleInfo, PredProcId, ProcInfo).
det_lookup_var_type(ModuleInfo, ProcInfo, Var, TypeDefn) :-
proc_info_get_vartypes(ProcInfo, VarTypes),
lookup_var_type(VarTypes, Var, Type),
type_to_ctor_det(Type, TypeCtor),
module_info_get_type_table(ModuleInfo, TypeTable),
search_type_ctor_defn(TypeTable, TypeCtor, TypeDefn).
det_no_output_vars(DetInfo, InstMap, InstMapDelta, Vars) :-
det_info_get_module_info(DetInfo, ModuleInfo),
VarTypes = DetInfo ^ di_vartypes,
instmap_delta_no_output_vars(ModuleInfo, VarTypes, InstMap, InstMapDelta,
Vars).
det_info_add_error_spec(Spec, !DetInfo) :-
det_info_get_error_specs(!.DetInfo, Specs0),
Specs = [Spec | Specs0],
det_info_set_error_specs(Specs, !DetInfo).
%-----------------------------------------------------------------------------%
:- type det_info
---> det_info(
di_module_info :: module_info,
% The id of the proc currently processed.
di_pred_proc_id :: pred_proc_id,
di_varset :: prog_varset,
di_vartypes :: vartypes,
di_pess_extra_vars :: report_pess_extra_vars,
di_has_format_call :: contains_format_call,
di_has_req_scope :: contains_require_scope,
di_has_incomplete_switch :: contains_incomplete_switch,
di_error_specs :: list(error_spec)
).
det_info_init(ModuleInfo, PredProcId, VarSet, VarTypes,
PessExtraVars, Specs, DetInfo) :-
DetInfo = det_info(ModuleInfo, PredProcId, VarSet, VarTypes,
PessExtraVars, does_not_contain_format_call,
does_not_contain_require_scope, does_not_contain_incomplete_switch,
Specs).
det_info_get_module_info(DetInfo, X) :-
X = DetInfo ^ di_module_info.
det_info_get_pred_proc_id(DetInfo, X) :-
X = DetInfo ^ di_pred_proc_id.
det_info_get_varset(DetInfo, X) :-
X = DetInfo ^ di_varset.
det_info_get_vartypes(DetInfo, X) :-
X = DetInfo ^ di_vartypes.
det_info_get_pess_extra_vars(DetInfo, X) :-
X = DetInfo ^ di_pess_extra_vars.
det_info_get_has_format_call(DetInfo, X) :-
X = DetInfo ^ di_has_format_call.
det_info_get_has_req_scope(DetInfo, X) :-
X = DetInfo ^ di_has_req_scope.
det_info_get_has_incomplete_switch(DetInfo, X) :-
X = DetInfo ^ di_has_incomplete_switch.
det_info_get_error_specs(DetInfo, X) :-
X = DetInfo ^ di_error_specs.
:- pred det_info_set_error_specs(list(error_spec)::in,
det_info::in, det_info::out) is det.
det_info_set_module_info(X, !DetInfo) :-
( if private_builtin.pointer_equal(X, !.DetInfo ^ di_module_info) then
true
else
!DetInfo ^ di_module_info := X
).
det_info_set_has_format_call(!DetInfo) :-
X = contains_format_call,
( if X = !.DetInfo ^ di_has_format_call then
true
else
!DetInfo ^ di_has_format_call := X
).
det_info_set_has_req_scope(!DetInfo) :-
X = contains_require_scope,
( if X = !.DetInfo ^ di_has_req_scope then
true
else
!DetInfo ^ di_has_req_scope := X
).
det_info_set_has_incomplete_switch(!DetInfo) :-
X = contains_incomplete_switch,
( if X = !.DetInfo ^ di_has_incomplete_switch then
true
else
!DetInfo ^ di_has_incomplete_switch := X
).
det_info_set_error_specs(X, !DetInfo) :-
!DetInfo ^ di_error_specs := X.
% Access stats for the det_info structure, derived using the commented-out
% code below:
%
% i read same diff same%
% 0 5135754 209308 2043 99.033% module_info
% 1 339264 0 0 pred_id
% 2 339264 0 0 proc_id
% 3 211938 0 0 vartypes
% 4 371 0 0 pess_extra_vars
% 5 299597 921 1381 40.009% has_format_call
% 6 299597 147 140 51.220% has_req_scope
% 7 300265 0 33 0.000% error_specs
% :- pragma foreign_decl("C", local,
% "
% #define MR_NUM_INFO_STATS 11
% unsigned long MR_stats_read[MR_NUM_INFO_STATS];
% unsigned long MR_stats_same[MR_NUM_INFO_STATS];
% unsigned long MR_stats_diff[MR_NUM_INFO_STATS];
% ").
%
% :- pred gather_info_read_stats(int::in,
% det_info::in, det_info::out) is det.
%
% :- pragma foreign_proc("C",
% gather_info_read_stats(N::in, Info0::in, Info::out),
% [will_not_call_mercury, promise_pure],
% "
% ++MR_stats_read[N];
% Info = Info0;
% ").
%
% :- pred gather_info_write_stats(int::in, T::in, T::in,
% det_info::in, det_info::out) is det.
%
% :- pragma foreign_proc("C",
% gather_info_write_stats(N::in, Old::in, New::in, Info0::in, Info::out),
% [will_not_call_mercury, promise_pure],
% "
% if (((MR_Unsigned) Old) == ((MR_Unsigned) New)) {
% ++MR_stats_same[N];
% } else {
% ++MR_stats_diff[N];
% }
%
% Info = Info0;
% ").
%
% :- interface.
% :- import_module io.
% :- pred write_det_info_stats(io::di, io::uo) is det.
% :- implementation.
%
% :- pragma foreign_proc("C",
% write_det_info_stats(IO0::di, IO::uo),
% [will_not_call_mercury, promise_pure],
% "
% FILE *fp;
%
% fp = fopen(""/tmp/DET_INFO_STATS"", ""a"");
% if (fp != NULL) {
% int i;
% for (i = 0; i < MR_NUM_INFO_STATS; i++) {
% fprintf(fp, ""stat_rsd %d %lu %lu %lu\\n"",
% i, MR_stats_read[i], MR_stats_same[i], MR_stats_diff[i]);
% }
% }
%
% IO = IO0;
% ").
%-----------------------------------------------------------------------------%
:- end_module check_hlds.det_util.
%-----------------------------------------------------------------------------%