mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-18 23:35:25 +00:00
Estimated hours taken: 20 Branches: main Fix a problem with from_ground_term scopes. When they are built, the scopes are tentantively marked as from_ground_term_construct scopes, and the unifications inside them are in a top down order. Mode analysis therefore expected the unifications inside from_ground_term_construct scopes to have that order. The problem was that mode analysis, when it confirmed that a from_ground_term scope is indeed a from_ground_term_construct scope, itself reversed the order of the unifications, putting them in a bottom up order. When mode analysis is reinvoked, either for unique mode checking, or after cse_detection finds common subexpressions, this meant that mode analysis found the unifications in the "wrong" order, and therefore disregarded the scope, discarding all its performance benefits. This diff separates out the two notions that we previously conflated. The scope kind from_ground_term_construct now refers only to scopes which are definitely known to construct ground terms. We can know that only after mode analysis. Until then, from_ground_term scopes are now marked as from_ground_term_initial. The two kinds have different though overlapping sets of invariants; in particular, they promise different orderings of the unifications in the scope. This diff reduces the time needed to compile mas_objects.data.m from about 221 seconds to about 8. compiler/hlds_goal.m: Add the from_ground_term_initial kind. Document the invariants that each kind of from_ground_term scope promises. compiler/superhomogeneous.m: Mark from_ground_term scopes initially as from_ground_term_initial, not from_ground_term_construct. compiler/post_typecheck.m: Make the predicate that converts function calls that look like unifications (such as X = int.min) into actual function calls say whether it performed such a conversion. compiler/purity.m: Use the new functionality in post_typecheck.m to convert from_ground_term_initial scopes into from_ground_term_other scopes if the conversion of a unification into a call means that we have to break an invariant expected of from_ground_term_initial scopes. compiler/cse_detection.m: compiler/switch_detection.m: Maintain the invariants we now expect of from_ground_term_deconstruct scopes. compiler/modecheck_goal.m: Maintain the invariants we now expect of the different from_ground_term scopes. Avoid traversing such scopes if a previous invocation of mode analysis says we can. Optimize away from_ground_term_construct scopes if the variable being constructed is not needed later. compiler/quantification.m: If the variable named in a from_ground_term_initial or from_ground_term_construct scope is not referred to outside the scope, set the nonlocals set of the scope to empty, which allows later compiler passes to optimize it away. Avoid some unnecessary work by the compiler. compiler/add_trail_ops.m: compiler/closure_analysis.m: compiler/constraint.m: compiler/dead_proc_elim.m: compiler/deep_profile.m: compiler/deforest.m: compiler/delay_construct.m: compiler/delay_partial_inst.m: compiler/dep_par_conj.m: compiler/dependency_graph.m: compiler/exception_analysis.m: compiler/follow_code.m: compiler/follow_vars.m: compiler/goal_form.m: compiler/goal_util.m: compiler/granularity.m: compiler/inlining.m: compiler/interval.m: compiler/lambda.m: compiler/lco.m: compiler/middle_rec.m: compiler/mode_util.m: compiler/parallel_to_plain.m: compiler/simplify.m: compiler/stm_expand.m: compiler/stratify.m: compiler/tabling_analysis.m: compiler/term_pass1.m: compiler/try_expand.m: compiler/tupling.m: compiler/untupling.m: compiler/unused_args.m: Avoid traversing from_ground_term_deconstruct scopes in cases where the invariants that now hold (mainly the absence of anything but deconstruct unifications) make such traversals unnecessary. compiler/live_vars.m: compiler/liveness.m: compiler/structure_reuse.lbu.m: Add comments about exploiting from_ground_term_deconstruct scopes. compiler/det_analysis.m: compiler/hlds_out_goal.m: compiler/polymorphism.m: compiler/saved_vars.m: compiler/unique_modes.m: Handle from_ground_term_initial scopes. compiler/handle_options.m: Add a dump verbosity option that is useful for comparing HLDS dumps created by two different compilers. compiler/type_util.m: Minor speedup. compiler/mode_info.m: compiler/modecheck_conj.m: compiler/prog_data.m: compiler/rbmm.region_transformation.m: compiler/typecheck.m: Improve documentation.
873 lines
36 KiB
Mathematica
873 lines
36 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2001, 2003-2011 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: mode_info.m.
|
|
% Main author: fjh.
|
|
%
|
|
% This file defines the mode_info data structure, which is used to hold
|
|
% the information we need during mode analysis.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.mode_info.
|
|
:- interface.
|
|
|
|
:- import_module check_hlds.delay_info.
|
|
:- import_module check_hlds.mode_errors.
|
|
:- 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.pred_table.
|
|
:- import_module hlds.instmap.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bag.
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The mode_info data structure and access predicates.
|
|
|
|
% XXX `side' is not used
|
|
:- type mode_context
|
|
---> mode_context_call(
|
|
call_id,
|
|
int % Argument number (offset so that the real
|
|
% arguments start at number 1 whereas the
|
|
% type_info arguments have numbers <= 0).
|
|
)
|
|
; mode_context_unify(
|
|
unify_context, % original source of the unification
|
|
side % LHS or RHS
|
|
)
|
|
; mode_context_uninitialized.
|
|
|
|
:- type side
|
|
---> left
|
|
; right.
|
|
|
|
:- type call_context
|
|
---> call_context_unify(unify_context)
|
|
; call_context_call(call_id).
|
|
|
|
:- type var_lock_reason
|
|
---> var_lock_negation
|
|
; var_lock_if_then_else
|
|
; var_lock_lambda(pred_or_func)
|
|
; var_lock_trace_goal
|
|
; var_lock_atomic_goal
|
|
; var_lock_par_conj.
|
|
|
|
% Specify how to process goals - using either modes.m or unique_modes.m.
|
|
:- type how_to_check_goal
|
|
---> check_modes
|
|
; check_unique_modes.
|
|
|
|
% Is mode analysis allowed to change which procedure of a predicate
|
|
% is called. It may not change the called procedure after deforestation
|
|
% has performed a generalisation step, since that could result
|
|
% in selecting a less efficient mode, or one which doesn't even
|
|
% have the same termination behaviour.
|
|
% Also, when rechecking a goal after adding extra goals, it is
|
|
% not necessary to choose again which procedure is to be called.
|
|
:- type may_change_called_proc
|
|
---> may_change_called_proc
|
|
; may_not_change_called_proc.
|
|
|
|
:- type locked_vars == assoc_list(var_lock_reason, set_of_progvar).
|
|
|
|
:- type mode_info.
|
|
|
|
:- type debug_flags
|
|
---> debug_flags(
|
|
% The value --debug-modes-verbose.
|
|
verbose :: bool,
|
|
|
|
% The value --debug-modes-minimal.
|
|
minimal :: bool,
|
|
|
|
% The value --debug-modes-statistics.
|
|
statistics :: bool
|
|
).
|
|
|
|
% Initialize the mode_info.
|
|
%
|
|
:- pred mode_info_init(module_info::in, pred_id::in,
|
|
proc_id::in, prog_context::in, set_of_progvar::in, instmap::in,
|
|
how_to_check_goal::in, may_change_called_proc::in, mode_info::out) is det.
|
|
|
|
% The mode_info contains a flag indicating whether initialisation calls,
|
|
% converting a solver variable from `free' to `any', may be inserted
|
|
% during mode analysis.
|
|
:- type may_init_solver_vars
|
|
---> may_init_solver_vars
|
|
; may_not_init_solver_vars.
|
|
|
|
:- type in_promise_purity_scope
|
|
---> in_promise_purity_scope
|
|
; not_in_promise_purity_scope.
|
|
|
|
:- type in_from_ground_term_scope
|
|
---> in_from_ground_term_scope
|
|
; not_in_from_ground_term_scope.
|
|
|
|
:- type had_from_ground_term_scope
|
|
---> had_from_ground_term_scope
|
|
; did_not_have_from_ground_term_scope.
|
|
|
|
:- type make_ground_terms_unique
|
|
---> make_ground_terms_unique
|
|
; do_not_make_ground_terms_unique.
|
|
|
|
:- type in_dupl_for_switch
|
|
---> in_dupl_for_switch
|
|
; not_in_dupl_for_switch.
|
|
|
|
% We use a stack of pairs of sets of variables used to mode-check
|
|
% parallel conjunctions. The first set is the nonlocals of the
|
|
% parallel conjunction. The second set is a subset of the
|
|
% first, and is the set of variables that have been [further]
|
|
% bound inside the current parallel conjunct. The stack
|
|
% is for the correct handling of nested parallel conjunctions.
|
|
:- type par_conj_mode_check_stack == list(par_conj_mode_check).
|
|
:- type par_conj_mode_check
|
|
---> par_conj_mode_check(
|
|
par_conj_nonlocals :: set_of_progvar,
|
|
par_conj_bound :: set_of_progvar
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred mode_info_get_module_info(mode_info::in, module_info::out) is det.
|
|
:- pred mode_info_get_preds(mode_info::in, pred_table::out) is det.
|
|
:- pred mode_info_get_modes(mode_info::in, mode_table::out) is det.
|
|
:- pred mode_info_get_insts(mode_info::in, inst_table::out) is det.
|
|
:- pred mode_info_get_pred_id(mode_info::in, pred_id::out) is det.
|
|
:- pred mode_info_get_proc_id(mode_info::in, proc_id::out) is det.
|
|
:- pred mode_info_get_debug_modes(mode_info::in, maybe(debug_flags)::out)
|
|
is det.
|
|
:- pred mode_info_get_context(mode_info::in, prog_context::out) is det.
|
|
:- pred mode_info_get_mode_context(mode_info::in, mode_context::out) is det.
|
|
:- pred mode_info_get_instmap(mode_info::in, instmap::out) is det.
|
|
:- pred mode_info_get_locked_vars(mode_info::in, locked_vars::out) is det.
|
|
:- pred mode_info_get_errors(mode_info::in, list(mode_error_info)::out) is det.
|
|
:- pred mode_info_get_warnings(mode_info::in, list(mode_warning_info)::out)
|
|
is det.
|
|
:- pred mode_info_get_need_to_requantify(mode_info::in,
|
|
need_to_requantify::out) is det.
|
|
:- pred mode_info_get_in_promise_purity_scope(mode_info::in,
|
|
in_promise_purity_scope::out) is det.
|
|
:- pred mode_info_get_num_errors(mode_info::in, int::out) is det.
|
|
:- pred mode_info_get_liveness(mode_info::in, set_of_progvar::out) is det.
|
|
:- pred mode_info_get_varset(mode_info::in, prog_varset::out) is det.
|
|
:- pred mode_info_get_instvarset(mode_info::in, inst_varset::out) is det.
|
|
:- pred mode_info_get_var_types(mode_info::in, vartypes::out) is det.
|
|
:- pred mode_info_get_delay_info(mode_info::in, delay_info::out) is det.
|
|
:- pred mode_info_get_live_vars(mode_info::in, bag(prog_var)::out) is det.
|
|
:- pred mode_info_get_nondet_live_vars(mode_info::in, bag(prog_var)::out)
|
|
is det.
|
|
:- pred mode_info_get_last_checkpoint_insts(mode_info::in, instmap::out)
|
|
is det.
|
|
:- pred mode_info_get_parallel_vars(mode_info::in,
|
|
par_conj_mode_check_stack::out) is det.
|
|
:- pred mode_info_get_changed_flag(mode_info::in, bool::out) is det.
|
|
:- pred mode_info_get_how_to_check(mode_info::in,
|
|
how_to_check_goal::out) is det.
|
|
:- pred mode_info_get_may_change_called_proc(mode_info::in,
|
|
may_change_called_proc::out) is det.
|
|
:- pred mode_info_get_initial_instmap(mode_info::in, instmap::out) is det.
|
|
:- pred mode_info_get_checking_extra_goals(mode_info::in, bool::out) is det.
|
|
:- pred mode_info_get_may_init_solver_vars(mode_info::in,
|
|
may_init_solver_vars::out) is det.
|
|
:- pred mode_info_get_in_from_ground_term(mode_info::in,
|
|
in_from_ground_term_scope::out) is det.
|
|
:- pred mode_info_get_had_from_ground_term(mode_info::in,
|
|
had_from_ground_term_scope::out) is det.
|
|
:- pred mode_info_get_make_ground_terms_unique(mode_info::in,
|
|
make_ground_terms_unique::out) is det.
|
|
:- pred mode_info_get_in_dupl_for_switch(mode_info::in,
|
|
in_dupl_for_switch::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred mode_info_set_module_info(module_info::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_pred_id(pred_id::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_proc_id(proc_id::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_context(prog_context::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_mode_context(mode_context::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_call_context(call_context::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_call_arg_context(int::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_unset_call_context(
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_instmap(instmap::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_locked_vars(locked_vars::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_errors(list(mode_error_info)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_warnings(list(mode_warning_info)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_need_to_requantify(need_to_requantify::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_in_promise_purity_scope(in_promise_purity_scope::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_add_live_vars(set_of_progvar::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_remove_live_vars(set_of_progvar::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_varset(prog_varset::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_var_types(vartypes::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_delay_info(delay_info::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_live_vars(bag(prog_var)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_nondet_live_vars(bag(prog_var)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_last_checkpoint_insts(instmap::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_parallel_vars(par_conj_mode_check_stack::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_changed_flag(bool::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_how_to_check(how_to_check_goal::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_may_change_called_proc(may_change_called_proc::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_checking_extra_goals(bool::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_may_init_solver_vars(may_init_solver_vars::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_in_from_ground_term(in_from_ground_term_scope::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_had_from_ground_term(had_from_ground_term_scope::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_make_ground_terms_unique(make_ground_terms_unique::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_in_dupl_for_switch(in_dupl_for_switch::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred mode_info_get_types_of_vars(mode_info::in,
|
|
list(prog_var)::in, list(mer_type)::out) is det.
|
|
|
|
:- pred mode_info_lock_vars(var_lock_reason::in, set_of_progvar::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_unlock_vars(var_lock_reason::in, set_of_progvar::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_var_is_locked(mode_info::in, prog_var::in,
|
|
var_lock_reason::out) is semidet.
|
|
|
|
% Find the simple_call_id to use in error messages
|
|
% for the given pred_id.
|
|
%
|
|
:- pred mode_info_get_call_id(mode_info::in, pred_id::in,
|
|
simple_call_id::out) is det.
|
|
|
|
% Check whether a variable or a list of variables are live or not.
|
|
%
|
|
:- pred mode_info_var_list_is_live(mode_info::in, list(prog_var)::in,
|
|
list(is_live)::out) is det.
|
|
:- pred mode_info_var_is_live(mode_info::in, prog_var::in,
|
|
is_live::out) is det.
|
|
|
|
% Check whether a variable is nondet_live or not.
|
|
%
|
|
:- pred mode_info_var_is_nondet_live(mode_info::in, prog_var::in,
|
|
is_live::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Record a mode error (and associated context info) in the mode_info.
|
|
%
|
|
:- pred mode_info_error(set_of_progvar::in, mode_error::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_add_error(mode_error_info::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_warning(mode_warning::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_add_warning(mode_warning_info::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_need_to_requantify(mode_info::in, mode_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred mode_info_may_init_solver_vars(mode_info::in) is semidet.
|
|
|
|
% Succeeds if automatic initialisation of solver variables is
|
|
% supported. This is only the case if the developer-only option
|
|
% `--solver-type-auto-init' is enabled.
|
|
%
|
|
:- pred mode_info_solver_init_is_supported(mode_info::in) is semidet.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.delay_info.
|
|
:- import_module check_hlds.mode_errors.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module term.
|
|
:- import_module varset.
|
|
|
|
:- type mode_sub_info
|
|
---> mode_sub_info(
|
|
% The pred we are checking.
|
|
msi_pred_id :: pred_id,
|
|
|
|
% The mode which we are checking.
|
|
msi_proc_id :: proc_id,
|
|
|
|
% The variables in the current proc.
|
|
msi_varset :: prog_varset,
|
|
|
|
% The types of the variables.
|
|
msi_vartypes :: vartypes,
|
|
|
|
% Is mode debugging of this procedure enabled? If yes,
|
|
% is verbose mode debugging enabled, is minimal mode debugging
|
|
% enabled, and is statistics printing enabled?
|
|
msi_debug :: maybe(debug_flags),
|
|
|
|
% The "locked" variables, i.e. variables which cannot be
|
|
% further instantiated inside a negated context.
|
|
msi_locked_vars :: locked_vars,
|
|
|
|
% The live variables, i.e. those variables which may be
|
|
% referenced again on forward execution or after shallow
|
|
% backtracking. (By shallow backtracking, I mean semidet
|
|
% backtracking in a negation, if-then-else, or semidet
|
|
% disjunction within the current predicate.)
|
|
msi_live_vars :: bag(prog_var),
|
|
|
|
msi_instvarset :: inst_varset,
|
|
|
|
% A stack of pairs of sets of variables used to mode-check
|
|
% parallel conjunctions. The first set is the nonlocals of the
|
|
% parallel conjunction. The second set is a subset of the
|
|
% first, and is the set of variables that have been [further]
|
|
% bound inside the current parallel conjunct - the stack
|
|
% is for the correct handling of nested parallel conjunctions.
|
|
msi_par_conj :: par_conj_mode_check_stack,
|
|
|
|
msi_how_to_check :: how_to_check_goal,
|
|
|
|
% Is mode analysis allowed to change which procedure is called?
|
|
msi_may_change_called_proc :: may_change_called_proc,
|
|
|
|
% `yes' if calls to the initialisation predicates for solver
|
|
% vars can be inserted during mode analysis in order to make
|
|
% goals schedulable.
|
|
msi_may_init_solver_vars :: may_init_solver_vars,
|
|
|
|
% This field is used by the checkpoint code when debug_modes
|
|
% is on. It has the instmap that was current at the last mode
|
|
% checkpoint, so that checkpoints do not print out the insts
|
|
% of variables whose insts have not changed since the last
|
|
% checkpoint. This field will always contain an unreachable
|
|
% instmap if debug_modes is off, since its information is not
|
|
% needed then.
|
|
msi_last_checkpoint_insts :: instmap,
|
|
|
|
% Changed flag: if `yes', then we may need to repeat
|
|
% mode inference.
|
|
msi_changed_flag :: bool,
|
|
|
|
% Are we rechecking a goal after introducing unifications
|
|
% for complicated sub-unifications or an implied mode? If so,
|
|
% redoing the mode check should not introduce more extra
|
|
% unifications.
|
|
msi_checking_extra_goals :: bool,
|
|
|
|
% The initial instmap of the procedure body. Used to decide
|
|
% whether a unification that cannot fail could be influenced
|
|
% by an argument mode that enforces a subtype.
|
|
msi_initial_instmap :: instmap,
|
|
|
|
% The mode warnings found.
|
|
msi_warnings :: list(mode_warning_info),
|
|
|
|
% Says whether we need to requantify the procedure body
|
|
% after mode analysis finishes.
|
|
msi_need_to_requantify :: need_to_requantify,
|
|
|
|
% Says whether we are in a promise_<purity> scope.
|
|
% This information is needed to check that potentially impure
|
|
% uses of inst any non-locals in negated contexts are properly
|
|
% acknowledged by the programmer.
|
|
msi_in_promise_purity_scope :: in_promise_purity_scope,
|
|
|
|
% Says whether we are in a from_ground_term scope.
|
|
% This information allows us to optimize some aspects of
|
|
% mode analysis.
|
|
msi_in_from_ground_term :: in_from_ground_term_scope,
|
|
|
|
% Says whether we have ever come across in a from_ground_term
|
|
% scope.
|
|
msi_had_from_ground_term :: had_from_ground_term_scope,
|
|
|
|
% Says whether we should copy the ground terms created by
|
|
% from_ground_term scopes, making them unique.
|
|
msi_make_ground_terms_unique :: make_ground_terms_unique,
|
|
|
|
% Set to `yes' if we are inside a goal with a
|
|
% duplicate_for_switch feature.
|
|
msi_in_dupl_for_switch :: in_dupl_for_switch
|
|
).
|
|
|
|
% Please try to keep the size of this structure down to eight fields.
|
|
% Even one more field will cause the Boehm allocator to round up the size
|
|
% of each memory cell to 16 words.
|
|
%
|
|
:- type mode_info
|
|
---> mode_info(
|
|
/* 1 */ mi_module_info :: module_info,
|
|
|
|
% The current instantiatedness of the variables.
|
|
/* 2 */ mi_instmap :: instmap,
|
|
|
|
% Info about delayed goals.
|
|
/* 3 */ mi_delay_info :: delay_info,
|
|
|
|
% The mode errors found.
|
|
/* 4 */ mi_errors :: list(mode_error_info),
|
|
|
|
% A description of where in the goal the error occurred.
|
|
/* 5 */ mi_mode_context :: mode_context,
|
|
|
|
% The line number of the subgoal we are currently checking.
|
|
/* 6 */ mi_context :: prog_context,
|
|
|
|
% The nondet-live variables, i.e. those variables which may be
|
|
% referenced again after deep backtracking TO THE CURRENT
|
|
% EXECUTION POINT. These are the variables which need to be
|
|
% made mostly_unique rather than unique when we get to a
|
|
% nondet disjunction or a nondet call. We do not include
|
|
% variables which may be referenced again after backtracking
|
|
% to a point EARLIER THAN the current execution point, since
|
|
% those variables will *already* have been marked as
|
|
% mostly_unique rather than unique.)
|
|
/* 7 */ mi_nondet_live_vars :: bag(prog_var),
|
|
|
|
/* 8 */ mi_sub_info :: mode_sub_info
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_init(ModuleInfo, PredId, ProcId, Context, LiveVars, InstMap0,
|
|
HowToCheck, MayChangeProc, ModeInfo) :-
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, debug_modes, DebugModes),
|
|
globals.lookup_int_option(Globals, debug_modes_pred_id,
|
|
DebugModesPredId),
|
|
pred_id_to_int(PredId, PredIdInt),
|
|
(
|
|
DebugModes = yes,
|
|
( DebugModesPredId >= 0 => DebugModesPredId = PredIdInt )
|
|
->
|
|
globals.lookup_bool_option(Globals, debug_modes_verbose, DebugVerbose),
|
|
globals.lookup_bool_option(Globals, debug_modes_minimal, DebugMinimal),
|
|
globals.lookup_bool_option(Globals, debug_modes_statistics,
|
|
Statistics),
|
|
Flags = debug_flags(DebugVerbose, DebugMinimal, Statistics),
|
|
Debug = yes(Flags)
|
|
;
|
|
Debug = no
|
|
),
|
|
|
|
module_info_proc_info(ModuleInfo, PredId, ProcId, ProcInfo),
|
|
proc_info_get_varset(ProcInfo, VarSet),
|
|
proc_info_get_vartypes(ProcInfo, VarTypes),
|
|
proc_info_get_inst_varset(ProcInfo, InstVarSet),
|
|
|
|
bag.from_sorted_list(set_of_var.to_sorted_list(LiveVars), LiveVarsBag),
|
|
instmap.init_unreachable(LastCheckpointInstMap),
|
|
LockedVars = [],
|
|
ParallelVars = [],
|
|
WarningList = [],
|
|
|
|
Changed = no,
|
|
CheckingExtraGoals = no,
|
|
MayInitSolverVars = may_init_solver_vars,
|
|
NeedToRequantify = do_not_need_to_requantify,
|
|
InPromisePurityScope = not_in_promise_purity_scope,
|
|
InFromGroundTerm = not_in_from_ground_term_scope,
|
|
HadFromGroundTerm = did_not_have_from_ground_term_scope,
|
|
MakeGroundTermsUnique = do_not_make_ground_terms_unique,
|
|
InDuplForSwitch = not_in_dupl_for_switch,
|
|
|
|
ModeSubInfo = mode_sub_info(PredId, ProcId, VarSet, VarTypes, Debug,
|
|
LockedVars, LiveVarsBag, InstVarSet, ParallelVars, HowToCheck,
|
|
MayChangeProc, MayInitSolverVars, LastCheckpointInstMap, Changed,
|
|
CheckingExtraGoals, InstMap0, WarningList, NeedToRequantify,
|
|
InPromisePurityScope, InFromGroundTerm, HadFromGroundTerm,
|
|
MakeGroundTermsUnique, InDuplForSwitch),
|
|
|
|
mode_context_init(ModeContext),
|
|
delay_info_init(DelayInfo),
|
|
ErrorList = [],
|
|
bag.from_sorted_list(set_of_var.to_sorted_list(LiveVars),
|
|
NondetLiveVarsBag),
|
|
|
|
ModeInfo = mode_info(ModuleInfo, InstMap0, DelayInfo, ErrorList,
|
|
ModeContext, Context, NondetLiveVarsBag, ModeSubInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_get_module_info(MI, MI ^ mi_module_info).
|
|
mode_info_get_pred_id(MI, MI ^ mi_sub_info ^ msi_pred_id).
|
|
mode_info_get_proc_id(MI, MI ^ mi_sub_info ^ msi_proc_id).
|
|
mode_info_get_debug_modes(MI, MI ^ mi_sub_info ^ msi_debug).
|
|
mode_info_get_varset(MI, MI ^ mi_sub_info ^ msi_varset).
|
|
mode_info_get_var_types(MI, MI ^ mi_sub_info ^ msi_vartypes).
|
|
mode_info_get_context(MI, MI ^ mi_context).
|
|
mode_info_get_mode_context(MI, MI ^ mi_mode_context).
|
|
mode_info_get_instmap(MI, MI ^ mi_instmap).
|
|
mode_info_get_instvarset(ModeInfo, ModeInfo ^ mi_sub_info ^ msi_instvarset).
|
|
mode_info_get_locked_vars(MI, MI ^ mi_sub_info ^ msi_locked_vars).
|
|
mode_info_get_errors(MI, MI ^ mi_errors).
|
|
mode_info_get_warnings(MI, MI ^ mi_sub_info ^ msi_warnings).
|
|
mode_info_get_need_to_requantify(MI,
|
|
MI ^ mi_sub_info ^ msi_need_to_requantify).
|
|
mode_info_get_in_promise_purity_scope(MI,
|
|
MI ^ mi_sub_info ^ msi_in_promise_purity_scope).
|
|
mode_info_get_delay_info(MI, MI ^ mi_delay_info).
|
|
mode_info_get_live_vars(MI, MI ^ mi_sub_info ^ msi_live_vars).
|
|
mode_info_get_nondet_live_vars(MI, MI ^ mi_nondet_live_vars).
|
|
mode_info_get_last_checkpoint_insts(MI,
|
|
MI ^ mi_sub_info ^ msi_last_checkpoint_insts).
|
|
mode_info_get_parallel_vars(MI, MI ^ mi_sub_info ^ msi_par_conj).
|
|
mode_info_get_changed_flag(MI, MI ^ mi_sub_info ^ msi_changed_flag).
|
|
mode_info_get_how_to_check(MI, MI ^ mi_sub_info ^ msi_how_to_check).
|
|
mode_info_get_may_change_called_proc(MI,
|
|
MI ^ mi_sub_info ^ msi_may_change_called_proc).
|
|
mode_info_get_may_init_solver_vars(MI,
|
|
MI ^ mi_sub_info ^ msi_may_init_solver_vars).
|
|
mode_info_get_initial_instmap(MI, MI ^ mi_sub_info ^ msi_initial_instmap).
|
|
mode_info_get_checking_extra_goals(MI,
|
|
MI ^ mi_sub_info ^ msi_checking_extra_goals).
|
|
mode_info_get_in_from_ground_term(MI,
|
|
MI ^ mi_sub_info ^ msi_in_from_ground_term).
|
|
mode_info_get_had_from_ground_term(MI,
|
|
MI ^ mi_sub_info ^ msi_had_from_ground_term).
|
|
mode_info_get_make_ground_terms_unique(MI,
|
|
MI ^ mi_sub_info ^ msi_make_ground_terms_unique).
|
|
mode_info_get_in_dupl_for_switch(MI,
|
|
MI ^ mi_sub_info ^ msi_in_dupl_for_switch).
|
|
|
|
mode_info_set_module_info(ModuleInfo, MI, MI ^ mi_module_info := ModuleInfo).
|
|
mode_info_set_pred_id(PredId, MI, MI ^ mi_sub_info ^ msi_pred_id := PredId).
|
|
mode_info_set_proc_id(ProcId, MI, MI ^ mi_sub_info ^ msi_proc_id := ProcId).
|
|
mode_info_set_varset(VarSet, MI, MI ^ mi_sub_info ^ msi_varset := VarSet).
|
|
mode_info_set_var_types(VarTypes, MI,
|
|
MI ^ mi_sub_info ^ msi_vartypes := VarTypes).
|
|
mode_info_set_context(Context, MI, MI ^ mi_context := Context).
|
|
mode_info_set_mode_context(ModeContext,
|
|
MI, MI ^ mi_mode_context := ModeContext).
|
|
mode_info_set_locked_vars(LockedVars, MI,
|
|
MI ^ mi_sub_info ^ msi_locked_vars := LockedVars).
|
|
mode_info_set_errors(Errors, MI, MI ^ mi_errors := Errors).
|
|
mode_info_set_warnings(Warnings, MI,
|
|
MI ^ mi_sub_info ^ msi_warnings := Warnings).
|
|
mode_info_set_need_to_requantify(NTRQ, MI,
|
|
MI ^ mi_sub_info ^ msi_need_to_requantify := NTRQ).
|
|
mode_info_set_in_promise_purity_scope(INC, MI,
|
|
MI ^ mi_sub_info ^ msi_in_promise_purity_scope := INC).
|
|
mode_info_set_delay_info(DelayInfo, MI, MI ^ mi_delay_info := DelayInfo).
|
|
mode_info_set_live_vars(LiveVarsList, MI,
|
|
MI ^ mi_sub_info ^ msi_live_vars := LiveVarsList).
|
|
mode_info_set_nondet_live_vars(NondetLiveVars, MI,
|
|
MI ^ mi_nondet_live_vars := NondetLiveVars).
|
|
mode_info_set_last_checkpoint_insts(LastCheckpointInsts, MI,
|
|
MI ^ mi_sub_info ^ msi_last_checkpoint_insts := LastCheckpointInsts).
|
|
mode_info_set_parallel_vars(PVars, MI,
|
|
MI ^ mi_sub_info ^ msi_par_conj := PVars).
|
|
mode_info_set_changed_flag(Changed, MI,
|
|
MI ^ mi_sub_info ^ msi_changed_flag := Changed).
|
|
mode_info_set_how_to_check(How, MI,
|
|
MI ^ mi_sub_info ^ msi_how_to_check := How).
|
|
mode_info_set_may_change_called_proc(MayChange, MI,
|
|
MI ^ mi_sub_info ^ msi_may_change_called_proc := MayChange).
|
|
mode_info_set_may_init_solver_vars(MayInit, MI,
|
|
MI ^ mi_sub_info ^ msi_may_init_solver_vars := MayInit).
|
|
mode_info_set_in_from_ground_term(IFGI, MI,
|
|
MI ^ mi_sub_info ^ msi_in_from_ground_term := IFGI).
|
|
mode_info_set_had_from_ground_term(HFGI, MI,
|
|
MI ^ mi_sub_info ^ msi_had_from_ground_term := HFGI).
|
|
mode_info_set_make_ground_terms_unique(MGTU, MI,
|
|
MI ^ mi_sub_info ^ msi_make_ground_terms_unique := MGTU).
|
|
mode_info_set_in_dupl_for_switch(INFS, MI,
|
|
MI ^ mi_sub_info ^ msi_in_dupl_for_switch := INFS).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_get_preds(ModeInfo, Preds) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_preds(ModuleInfo, Preds).
|
|
|
|
mode_info_get_modes(ModeInfo, Modes) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_mode_table(ModuleInfo, Modes).
|
|
|
|
mode_info_get_insts(ModeInfo, Insts) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_inst_table(ModuleInfo, Insts).
|
|
|
|
mode_info_set_call_context(call_context_unify(UnifyContext), !MI) :-
|
|
mode_info_set_mode_context(mode_context_unify(UnifyContext, left), !MI).
|
|
mode_info_set_call_context(call_context_call(CallId), !MI) :-
|
|
mode_info_set_mode_context(mode_context_call(CallId, 0), !MI).
|
|
|
|
mode_info_set_call_arg_context(ArgNum, !ModeInfo) :-
|
|
mode_info_get_mode_context(!.ModeInfo, ModeContext0),
|
|
(
|
|
ModeContext0 = mode_context_call(CallId, _),
|
|
mode_info_set_mode_context(mode_context_call(CallId, ArgNum),
|
|
!ModeInfo)
|
|
;
|
|
ModeContext0 = mode_context_unify(_UnifyContext, _Side),
|
|
% This only happens when checking that the typeinfo variables
|
|
% for polymorphic complicated unifications are ground.
|
|
% For that case, we don't care about the ArgNum.
|
|
true
|
|
;
|
|
ModeContext0 = mode_context_uninitialized,
|
|
unexpected($module, $pred, "uninitialized")
|
|
).
|
|
|
|
mode_info_unset_call_context(!MI) :-
|
|
mode_info_set_mode_context(mode_context_uninitialized, !MI).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_set_instmap(InstMap, !MI) :-
|
|
mode_info_get_instmap(!.MI, InstMap0),
|
|
!MI ^ mi_instmap := InstMap,
|
|
(
|
|
instmap_is_unreachable(InstMap),
|
|
instmap_is_reachable(InstMap0)
|
|
->
|
|
mode_info_get_delay_info(!.MI, DelayInfo0),
|
|
delay_info_bind_all_vars(DelayInfo0, DelayInfo),
|
|
mode_info_set_delay_info(DelayInfo, !MI)
|
|
;
|
|
true
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_get_num_errors(ModeInfo, NumErrors) :-
|
|
mode_info_get_errors(ModeInfo, Errors),
|
|
list.length(Errors, NumErrors).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% We keep track of the live variables and the nondet-live variables
|
|
% as two bags. This allows us to easily add and remove sets of variables.
|
|
% It is probably not maximally efficient.
|
|
|
|
% Add a set of vars to the bag of live vars and the bag of
|
|
% nondet-live vars.
|
|
|
|
mode_info_add_live_vars(NewLiveVars, !MI) :-
|
|
set_of_var.to_sorted_list(NewLiveVars, NewLiveVarsList),
|
|
mode_info_get_live_vars(!.MI, LiveVars0),
|
|
mode_info_get_nondet_live_vars(!.MI, NondetLiveVars0),
|
|
bag.insert_list(NewLiveVarsList, LiveVars0, LiveVars),
|
|
bag.insert_list(NewLiveVarsList, NondetLiveVars0, NondetLiveVars),
|
|
mode_info_set_live_vars(LiveVars, !MI),
|
|
mode_info_set_nondet_live_vars(NondetLiveVars, !MI).
|
|
|
|
% Remove a set of vars from the bag of live vars and
|
|
% the bag of nondet-live vars.
|
|
|
|
mode_info_remove_live_vars(OldLiveVars, !MI) :-
|
|
set_of_var.to_sorted_list(OldLiveVars, OldLiveVarsList),
|
|
mode_info_get_live_vars(!.MI, LiveVars0),
|
|
mode_info_get_nondet_live_vars(!.MI, NondetLiveVars0),
|
|
bag.det_remove_list(OldLiveVarsList, LiveVars0, LiveVars),
|
|
bag.det_remove_list(OldLiveVarsList, NondetLiveVars0, NondetLiveVars),
|
|
mode_info_set_live_vars(LiveVars, !MI),
|
|
mode_info_set_nondet_live_vars(NondetLiveVars, !MI),
|
|
|
|
% When a variable becomes dead, we may be able to wake up a goal
|
|
% which is waiting on that variable.
|
|
mode_info_get_delay_info(!.MI, DelayInfo0),
|
|
delay_info_bind_var_list(OldLiveVarsList, DelayInfo0, DelayInfo),
|
|
mode_info_set_delay_info(DelayInfo, !MI).
|
|
|
|
mode_info_var_list_is_live(_, [], []).
|
|
mode_info_var_list_is_live(ModeInfo, [Var | Vars], [Live | Lives]) :-
|
|
mode_info_var_is_live(ModeInfo, Var, Live),
|
|
mode_info_var_list_is_live(ModeInfo, Vars, Lives).
|
|
|
|
% Check whether a variable is live or not
|
|
|
|
mode_info_var_is_live(ModeInfo, Var, Result) :-
|
|
mode_info_get_live_vars(ModeInfo, LiveVars0),
|
|
( bag.contains(LiveVars0, Var) ->
|
|
Result = is_live
|
|
;
|
|
Result = is_dead
|
|
).
|
|
|
|
mode_info_var_is_nondet_live(ModeInfo, Var, Result) :-
|
|
mode_info_get_nondet_live_vars(ModeInfo, NondetLiveVars0),
|
|
( bag.contains(NondetLiveVars0, Var) ->
|
|
Result = is_live
|
|
;
|
|
Result = is_dead
|
|
).
|
|
|
|
mode_info_get_liveness(ModeInfo, LiveVars) :-
|
|
mode_info_get_live_vars(ModeInfo, LiveVarsBag),
|
|
bag.to_list_without_duplicates(LiveVarsBag, SortedList),
|
|
set_of_var.sorted_list_to_set(SortedList, LiveVars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_get_types_of_vars(ModeInfo, Vars, TypesOfVars) :-
|
|
mode_info_get_var_types(ModeInfo, VarTypes),
|
|
map.apply_to_list(Vars, VarTypes, TypesOfVars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The locked variables are stored as a stack of sets of variables.
|
|
% A variable is locked if it is a member of any of the sets.
|
|
% To lock a set of vars, we just push them on the stack,
|
|
% and to unlock a set of vars, we just pop them off the stack.
|
|
% The stack is implemented as a list.
|
|
|
|
mode_info_lock_vars(Reason, Vars, !ModeInfo) :-
|
|
mode_info_get_locked_vars(!.ModeInfo, LockedVars),
|
|
mode_info_set_locked_vars([Reason - Vars | LockedVars], !ModeInfo).
|
|
|
|
mode_info_unlock_vars(Reason, Vars, !ModeInfo) :-
|
|
mode_info_get_locked_vars(!.ModeInfo, LockedVars0),
|
|
(
|
|
LockedVars0 = [Reason - TheseVars | LockedVars1],
|
|
set_of_var.equal(TheseVars, Vars)
|
|
->
|
|
LockedVars = LockedVars1
|
|
;
|
|
unexpected($module, $pred, "some kind of nesting error")
|
|
),
|
|
mode_info_set_locked_vars(LockedVars, !ModeInfo).
|
|
|
|
mode_info_var_is_locked(ModeInfo, Var, Reason) :-
|
|
mode_info_get_locked_vars(ModeInfo, LockedVarsList),
|
|
mode_info_var_is_locked_2(LockedVarsList, Var, Reason).
|
|
|
|
:- pred mode_info_var_is_locked_2(locked_vars::in, prog_var::in,
|
|
var_lock_reason::out) is semidet.
|
|
|
|
mode_info_var_is_locked_2([ThisReason - Set | Sets], Var, Reason) :-
|
|
( set_of_var.member(Set, Var) ->
|
|
Reason = ThisReason
|
|
;
|
|
mode_info_var_is_locked_2(Sets, Var, Reason)
|
|
).
|
|
|
|
mode_info_set_checking_extra_goals(Checking, !MI) :-
|
|
mode_info_get_checking_extra_goals(!.MI, Checking0),
|
|
(
|
|
Checking0 = yes,
|
|
Checking = yes
|
|
->
|
|
% This should never happen - once the extra goals are introduced,
|
|
% rechecking the goal should not introduce more extra goals.
|
|
unexpected($module, $pred,
|
|
"rechecking extra goals adds more extra goals")
|
|
;
|
|
!MI ^ mi_sub_info ^ msi_checking_extra_goals := Checking
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_get_call_id(ModeInfo, PredId, CallId) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
pred_info_get_call_id(PredInfo, CallId).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_error(Vars, ModeError, !ModeInfo) :-
|
|
mode_info_get_context(!.ModeInfo, Context),
|
|
mode_info_get_mode_context(!.ModeInfo, ModeContext),
|
|
ModeErrorInfo = mode_error_info(Vars, ModeError, Context, ModeContext),
|
|
mode_info_add_error(ModeErrorInfo, !ModeInfo).
|
|
|
|
mode_info_add_error(ModeErrorInfo, !ModeInfo) :-
|
|
mode_info_get_errors(!.ModeInfo, Errors0),
|
|
list.append(Errors0, [ModeErrorInfo], Errors),
|
|
mode_info_set_errors(Errors, !ModeInfo).
|
|
|
|
mode_info_warning(ModeWarning, !ModeInfo) :-
|
|
mode_info_get_context(!.ModeInfo, Context),
|
|
mode_info_get_mode_context(!.ModeInfo, ModeContext),
|
|
ModeWarningInfo = mode_warning_info(ModeWarning, Context, ModeContext),
|
|
mode_info_add_warning(ModeWarningInfo, !ModeInfo).
|
|
|
|
mode_info_add_warning(ModeWarningInfo, !ModeInfo) :-
|
|
mode_info_get_warnings(!.ModeInfo, Warnings0),
|
|
list.append(Warnings0, [ModeWarningInfo], Warnings),
|
|
mode_info_set_warnings(Warnings, !ModeInfo).
|
|
|
|
mode_info_need_to_requantify(!ModeInfo) :-
|
|
mode_info_set_need_to_requantify(need_to_requantify, !ModeInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_may_init_solver_vars(ModeInfo) :-
|
|
mode_info_get_may_init_solver_vars(ModeInfo, MayInitSolverVars),
|
|
MayInitSolverVars = may_init_solver_vars.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_solver_init_is_supported(ModeInfo) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, solver_type_auto_init, yes).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module check_hlds.mode_info.
|
|
%-----------------------------------------------------------------------------%
|