mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-22 04:43:53 +00:00
compiler/hlds_pred.m:
This module used to maintain a distinction between valid and invalid
procedures in a pred_info. The distinction was based on whether the
proc_info field containing a list of mode_error_infos was empty
(such procedures were valid) or nonempty (such procedures were invalid).
This field was used only during the early phases of the compiler
from mode analysis to unique mode analysis, but all later passes
had to check whether the field was empty before processing the procedure.
This diff deletes this field from proc_infos. The information that this
field used to contain is now stored in *temporary* data structures
maintained and used only by the mode and unique mode analysis phases.
These phases use the code they share in modes.m to delete all invalid
procedures from the HLDS before they hand over that HLDS to other phases.
This means that outside these two compiler phases, *all* procedures in the
HLDS will be valid.
Delete all service predicates and functions that tested procedures
for validity, since this has now become a meaningless test. In one case,
where there was no non-validity-testing equivalent, make one.
compiler/mode_info.m:
Define the proc_mode_error_map, which effectively replaces the fields
deleted from proc_infos. Define the operations on it that we need.
compiler/modes.m:
Initialize proc_mode_error_maps to empty, and then pass them through
mode analysis as part of the mode_info, allowing mode analysis code
to use it to check procedure validity.
When a mode analysis phase (either ordinary or unique mode analysis)
is done, delete the procedures that we have now detected are invalid.
However, before we do, print any inference messages about them.
compiler/unique_modes.m:
Use the new field in mode_info to check procedures' validity.
Delete the unique_modes_check_proc predicate, because it had
only one caller in modes.m, which called another predicate in modes.m
through it.
compiler/modecheck_call.m:
compiler/modecheck_unify.m:
Use the new field in mode_info to check procedures' validity.
compiler/try_expand.m:
Conform to the changes above.
When a mode check of a procedure repeated after try expansion finds
an error, delete the now-detected-to-be-invalid procedure.
compiler/cse_detection.m:
Conform to the changes above.
When a mode check of a procedure repeated after common subexpression
eliminate finds an error, don't bother to delete the now-detected-to-be-
invalid procedure, because the code on that path throws an exception
anyway.
Fix an old incongruity: one trace goal created a new ProgressStream
in a predicate that would have been given existing one if needed.
compiler/direct_arg_in_out.m:
Conform to the changes above by deleting a validity test.
Delete a predicate that had no job *except* that validity test.
compiler/style_checks.m:
Use missing procedure ids to detect invalid procedures. Add an XXX
about a limitation of this approach.
compiler/bytecode_gen.m:
compiler/dead_proc_elim.m:
compiler/deep_profiling.m:
compiler/delay_partial_inst.m:
compiler/dep_par_conj.m:
compiler/det_analysis.m:
compiler/distance_granularity.m:
compiler/exception_analysis.m:
compiler/float_regs.m:
compiler/goal_mode.m:
compiler/higher_order.m:
compiler/hlds_call_tree.m:
compiler/hlds_dependency_graph.m:
compiler/hlds_out_pred.m:
compiler/intermod.m:
compiler/intermod_analysis.m:
compiler/introduce_parallelism.m:
compiler/lambda.m:
compiler/liveness.m:
compiler/mark_tail_calls.m:
compiler/mercury_compile_front_end.m:
compiler/mercury_compile_llds_back_end.m:
compiler/ml_proc_gen.m:
compiler/passes_aux.m:
compiler/pd_util.m:
compiler/polymorphism.m:
compiler/polymorphism_goal.m:
compiler/proc_gen.m:
compiler/purity.m:
compiler/rbmm.condition_renaming.m:
compiler/rbmm.execution_path.m:
compiler/rbmm.live_region_analysis.m:
compiler/rbmm.live_variable_analysis.m:
compiler/rbmm.points_to_analysis.m:
compiler/rbmm.region_arguments.m:
compiler/rbmm.region_instruction.m:
compiler/rbmm.region_transformation.m:
compiler/ssdebug.m:
compiler/stm_expand.m:
compiler/stratify.m:
compiler/structure_reuse.analysis.m:
compiler/structure_reuse.direct.m:
compiler/structure_reuse.domain.m:
compiler/structure_sharing.analysis.m:
compiler/structure_sharing.domain.m:
compiler/switch_detection.m:
compiler/table_gen.m:
compiler/tabling_analysis.m:
compiler/term_constr_initial.m:
compiler/termination.m:
compiler/trailing_analysis.m:
compiler/untupling.m:
compiler/unused_args.m:
Conform to the changes above, mostly by deleting validity tests.
1254 lines
50 KiB
Mathematica
1254 lines
50 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2001, 2003-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: 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_goal.
|
|
:- import_module hlds.hlds_inst_mode.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.instmap.
|
|
:- import_module hlds.pred_table.
|
|
:- 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 parse_tree.var_table.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bag.
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The mode_info data structure and access predicates.
|
|
|
|
% XXX `side' is not used
|
|
:- type mode_context
|
|
---> mode_context_call(
|
|
mode_call_id,
|
|
|
|
% Argument number (offset so that the real arguments
|
|
% start at number 1 whereas the type_info arguments
|
|
% have numbers <= 0).
|
|
int
|
|
)
|
|
; mode_context_unify(
|
|
% original source of the unification
|
|
unify_context,
|
|
|
|
% LHS or RHS
|
|
side
|
|
)
|
|
; mode_context_not_call_or_unify.
|
|
|
|
% Initialize a mode_context.
|
|
%
|
|
:- pred mode_context_init(mode_context::out) is det.
|
|
|
|
:- type side
|
|
---> left
|
|
; right.
|
|
|
|
:- type call_context
|
|
---> call_context_unify(unify_context)
|
|
; call_context_call(mode_call_id).
|
|
|
|
:- type mode_call_id
|
|
---> mode_call_plain(pred_id)
|
|
; mode_call_generic(generic_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 empty string when mode checking, and a prefix to put
|
|
% in front of goal kind names when unique mode checking.
|
|
unique_prefix :: string,
|
|
|
|
% 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, proc_mode_error_map::in,
|
|
pred_id::in, proc_id::in, prog_context::in, set_of_progvar::in,
|
|
head_inst_vars::in, instmap::in, how_to_check_goal::in,
|
|
may_change_called_proc::in, mode_info::out) is det.
|
|
|
|
:- 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
|
|
).
|
|
|
|
:- type pred_var_multimode_error_map ==
|
|
map(prog_var, pred_id_var_multimode_error).
|
|
|
|
:- type proc_mode_error_map ==
|
|
map(pred_id, map(proc_id, list(mode_error_info))).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Please keep these getter and setter predicates in the same order
|
|
% as the fields in mode_info/mode_sub_info.
|
|
%
|
|
|
|
% Getters of the fields of mode_info.
|
|
:- pred mode_info_get_module_info(mode_info::in, module_info::out) is det.
|
|
:- pred mode_info_get_instmap(mode_info::in, instmap::out) is det.
|
|
:- pred mode_info_get_delay_info(mode_info::in, delay_info::out) is det.
|
|
:- pred mode_info_get_errors(mode_info::in, list(mode_error_info)::out) is det.
|
|
:- pred mode_info_get_mode_context(mode_info::in, mode_context::out) is det.
|
|
:- pred mode_info_get_context(mode_info::in, prog_context::out) is det.
|
|
:- pred mode_info_get_nondet_live_vars(mode_info::in, bag(prog_var)::out)
|
|
is det.
|
|
|
|
% Getters of the fields of mode_sub_info.
|
|
:- 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_var_table(mode_info::in, var_table::out) is det.
|
|
:- pred mode_info_get_debug_modes(mode_info::in, maybe(debug_flags)::out)
|
|
is det.
|
|
:- pred mode_info_get_locked_vars(mode_info::in, locked_vars::out) is det.
|
|
:- pred mode_info_get_live_vars(mode_info::in, bag(prog_var)::out) is det.
|
|
:- pred mode_info_get_instvarset(mode_info::in, inst_varset::out) is det.
|
|
:- pred mode_info_get_parallel_vars(mode_info::in,
|
|
par_conj_mode_check_stack::out) is det.
|
|
:- pred mode_info_get_last_checkpoint_insts(mode_info::in, instmap::out)
|
|
is det.
|
|
:- pred mode_info_get_initial_instmap(mode_info::in, instmap::out) is det.
|
|
:- pred mode_info_get_head_inst_vars(mode_info::in, head_inst_vars::out)
|
|
is det.
|
|
:- pred mode_info_get_warnings(mode_info::in, list(mode_warning_info)::out)
|
|
is det.
|
|
:- pred mode_info_get_pred_var_multimode_error_map(mode_info::in,
|
|
pred_var_multimode_error_map::out) is det.
|
|
:- pred mode_info_get_proc_mode_error_map(mode_info::in,
|
|
proc_mode_error_map::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_changed_flag(mode_info::in, bool::out) is det.
|
|
:- pred mode_info_get_checking_extra_goals(mode_info::in, bool::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_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.
|
|
|
|
%---------------------%
|
|
|
|
% Setters of the fields of mode_info.
|
|
:- pred mode_info_set_module_info(module_info::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is a mode_info_set_instmap, but it is not JUST a setter.
|
|
:- pred mode_info_set_delay_info(delay_info::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_mode_context(mode_context::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_nondet_live_vars(bag(prog_var)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
% Setters of the fields of mode_sub_info.
|
|
:- 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_var_table(var_table::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is no mode_info_set_debug_modes; this field is read-only.
|
|
:- pred mode_info_set_locked_vars(locked_vars::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_instvarset(inst_varset::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_last_checkpoint_insts(instmap::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is no mode_info_set_initial_instmap; this field is read-only.
|
|
% There is no mode_info_set_head_var_insts; this field is read-only.
|
|
:- pred mode_info_set_warnings(list(mode_warning_info)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_pred_var_multimode_error_map(
|
|
pred_var_multimode_error_map::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_proc_mode_error_map(proc_mode_error_map::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is no mode_info_set_how_to_check; this field is read-only.
|
|
:- 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_changed_flag(bool::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is a mode_info_set_checking_extra_goals, but it is not JUST a setter.
|
|
:- 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_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.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Nontrivial getter predicates.
|
|
%
|
|
|
|
:- 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_pred_id_table(mode_info::in, pred_id_table::out) is det.
|
|
:- pred mode_info_get_insts(mode_info::in, inst_table::out) is det.
|
|
:- pred mode_info_get_modes(mode_info::in, mode_table::out) is det.
|
|
|
|
:- pred mode_info_get_types_of_vars(mode_info::in,
|
|
list(prog_var)::in, list(mer_type)::out) is det.
|
|
|
|
:- pred look_up_proc_mode_errors(mode_info::in, pred_id::in, proc_id::in,
|
|
list(mode_error_info)::out) is det.
|
|
:- pred look_up_proc_mode_errors_raw(proc_mode_error_map::in,
|
|
pred_id::in, proc_id::in, list(mode_error_info)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Nontrivial setter predicates.
|
|
%
|
|
|
|
:- pred mode_info_set_instmap(instmap::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_call_context(call_context::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_call_arg_context(int::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_need_to_requantify(mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred set_proc_mode_errors(pred_id::in, proc_id::in,
|
|
list(mode_error_info)::in,
|
|
proc_mode_error_map::in, proc_mode_error_map::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Predicates related to liveness.
|
|
%
|
|
|
|
% Check whether a variable or a list of variables are live or not.
|
|
%
|
|
:- pred mode_info_var_is_live(mode_info::in, prog_var::in,
|
|
is_live::out) is det.
|
|
:- pred mode_info_var_list_is_live(mode_info::in, list(prog_var)::in,
|
|
list(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.
|
|
|
|
% 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.
|
|
%
|
|
% mode_info_add_live_vars adds the given set of vars
|
|
% to the bag of live vars and the bag of nondet-live vars.
|
|
%
|
|
% mode_info_remove_live_vars removes the given set of vars
|
|
% from the bag of live vars and the bag of nondet-live vars.
|
|
%
|
|
:- 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.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Predicates related to locked variables.
|
|
%
|
|
|
|
:- pred mode_info_var_is_locked(mode_info::in, prog_var::in,
|
|
var_lock_reason::out) is semidet.
|
|
|
|
:- 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.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Predicates related to errors and warnings.
|
|
%
|
|
|
|
% 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.
|
|
|
|
% Find the pf_sym_name_arity to use in error messages
|
|
% for the given pred_id.
|
|
%
|
|
:- pred mode_info_get_pf_sym_name_arity(mode_info::in, pred_id::in,
|
|
pf_sym_name_arity::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module parse_tree.write_error_spec.
|
|
|
|
:- import_module getopt.
|
|
:- import_module int.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module varset.
|
|
|
|
% The information we need while we modecheck a procedure.
|
|
%
|
|
% The mode_info and mode_sub_info types constitute a single logical
|
|
% data structure split into two parts for efficiency purposes.
|
|
%
|
|
% The most frequently used fields are in the mode_info type,
|
|
% while all the other fields are in the mode_sub_info type.
|
|
%
|
|
% The sub-word-sized arguments of the mode_sub_info type
|
|
% are all at the end of the structure, to allow them to be packed together.
|
|
:- type mode_info
|
|
---> mode_info(
|
|
% The Boehm collector allocates blocks whose sizes are
|
|
% multiples of 2, so we should keep the number of fields
|
|
% in a mode_info to be a multiple of 2 as well.
|
|
|
|
/* 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),
|
|
|
|
% Almost all mode errors are diagnosed when mode analysis
|
|
% processes a unification or a call. To prepape for
|
|
% the possibility of an error, before mode analysis
|
|
% starts processing one side of a unification, it records
|
|
% the identity of the side and of the unification in the
|
|
% mode context, and likewise before it starts processing
|
|
% the Nth argument of a call, we record N and the identity
|
|
% of the call. If an error occurs, the code in mode_errors.m
|
|
% that constructs the error message will pick up this context
|
|
% from here.
|
|
%
|
|
% A few kinds of mode errors occur outside both unifications
|
|
% and calls, and when processing HLDS constructs in which
|
|
% such errors can occur, this field should contain
|
|
% "mode_context_not_call_or_unify".
|
|
%
|
|
% When not processing a HLDS construct that is guaranteed
|
|
% not to generate a mode error, the contents of this field
|
|
% does not matter; it may be anything.
|
|
%
|
|
% When an error is discovered by code that is specific
|
|
% to e.g. unifications, this field should not be needed;
|
|
% the code should know in which part of which construct
|
|
% it found the error. On the other hand, some errors
|
|
% are caught by predicates that help process many kinds
|
|
% of HLDS goals, and they need their caller to tell them
|
|
% e.g. where the insts they handle come from.
|
|
%
|
|
% XXX The reason this field exists is probably because
|
|
% Fergus thought that it is easier to stuff this information
|
|
% into the mode_info, which those general-purpose predicates
|
|
% have already got, than to pass them in separate arguments,
|
|
% even though the latter would be semantically cleaner.
|
|
/* 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
|
|
).
|
|
|
|
:- 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 names and types of the variables.
|
|
msi_var_table :: var_table,
|
|
|
|
% 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),
|
|
|
|
% This starts out as the inst_varset taken from the proc_info
|
|
% of the procedure we are doing mode analysis on. As we make
|
|
% calls, we add to it the renamed-apart inst variables from
|
|
% the proc_infos of the procedures we call.
|
|
%
|
|
% I (zs) don't know the reason why these additions are needed.
|
|
%
|
|
% XXX Actually, we add to it the renamed-apart inst variables
|
|
% from the proc_infos of every procedure we *have considered*
|
|
% calling, because if a called predicate has two or more modes,
|
|
% we add the insts from *all* the inst_varsets of those
|
|
% procedures. Interestingly, if we throw away the additions
|
|
% to the inst_varset in the mode_info after we have finished
|
|
% considering making the call to a particular procedure,
|
|
% a bootcheck completes with just one test case failure,
|
|
% and that failure consists of the compiler generating
|
|
% an error message for invalid/constrained_poly_insts2
|
|
% that replaces a constrained inst variable with the
|
|
% constraining inst.
|
|
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,
|
|
|
|
% 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,
|
|
|
|
% 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 inst vars that appear in the procedure head and their
|
|
% constraints.
|
|
msi_head_inst_vars :: head_inst_vars,
|
|
|
|
% The mode warnings found.
|
|
msi_warnings :: list(mode_warning_info),
|
|
|
|
% This field maps variables for which we have generated
|
|
% a mode_error_unify_var_multimode_pred to the rest of the
|
|
% fields in that mode error. If this variable is later found
|
|
% to be insufficiently instantiated, we will want to print
|
|
% the mode_error_unify_var_multimode_pred as a possible
|
|
% explanation of that error.
|
|
%
|
|
% Any code that creates a mode_error_unify_var_multimode_pred
|
|
% should include the information in that error in this map.
|
|
%
|
|
% Since this information is valid only in straight-line
|
|
% conjunctions, entries added to this map must be thrown away
|
|
% when the code that added them is backtracked over.
|
|
% The simplest way to make this happen is to take a snapshot
|
|
% of the value of this map at the start of each branched
|
|
% control structure, and to reset it to that snapshot
|
|
% at the start of each branch.
|
|
msi_pred_var_multimode_error_map
|
|
:: pred_var_multimode_error_map,
|
|
|
|
% This field records which procedures have mode errors
|
|
% generated for them. A procedure which has a nonempty list
|
|
% of mode errors is considered invalid by mode analysis,
|
|
% and will be deleted from the HLDS at end of the current
|
|
% mode analysis pass.
|
|
msi_proc_mode_error_map :: proc_mode_error_map,
|
|
|
|
% All the arguments from here on are sub-word-sized,
|
|
% which should allow the compiler to pack them together.
|
|
|
|
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,
|
|
|
|
% 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,
|
|
|
|
% 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
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_context_init(mode_context_not_call_or_unify).
|
|
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_info_init(ModuleInfo, ProcModeErrorMap, PredId, ProcId, Context, LiveVars,
|
|
HeadInstVars, 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),
|
|
( if
|
|
DebugModes = yes,
|
|
( DebugModesPredId >= 0 => DebugModesPredId = PredIdInt )
|
|
then
|
|
(
|
|
HowToCheck = check_modes,
|
|
UniquePrefix = ""
|
|
;
|
|
HowToCheck = check_unique_modes,
|
|
UniquePrefix = "unique "
|
|
),
|
|
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(UniquePrefix, DebugVerbose, DebugMinimal,
|
|
Statistics),
|
|
MaybeDebug = yes(Flags)
|
|
else
|
|
MaybeDebug = no
|
|
),
|
|
|
|
module_info_proc_info(ModuleInfo, PredId, ProcId, ProcInfo),
|
|
proc_info_get_var_table(ProcInfo, VarTable),
|
|
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 = [],
|
|
Warnings = [],
|
|
|
|
Changed = no,
|
|
CheckingExtraGoals = no,
|
|
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,
|
|
map.init(PredVarMultiModeMap),
|
|
|
|
ModeSubInfo = mode_sub_info(PredId, ProcId, VarTable,
|
|
MaybeDebug, LockedVars, LiveVarsBag, InstVarSet, ParallelVars,
|
|
LastCheckpointInstMap, InstMap0, HeadInstVars, Warnings,
|
|
PredVarMultiModeMap, ProcModeErrorMap,
|
|
HowToCheck, MayChangeProc, Changed, CheckingExtraGoals,
|
|
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).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Getter and setter predicates for mode_info.
|
|
% The setter predicates where the new value of the field is often
|
|
% the same as its old value check whether the new value may differ
|
|
% from the old one, and allocate a new mode_info structure (and maybe
|
|
% a new mode_sub_info structure) only if the old and new values are
|
|
% *not* guaranteed to be the same.
|
|
%
|
|
% I (zs) used profiling data derived from a bootcheck to establish
|
|
% which mode_info fields fall into this category. See below for further info.
|
|
%
|
|
|
|
% Getters of the fields of mode_info.
|
|
mode_info_get_module_info(MI, X) :-
|
|
X = MI ^ mi_module_info.
|
|
mode_info_get_instmap(MI, X) :-
|
|
X = MI ^ mi_instmap.
|
|
mode_info_get_delay_info(MI, X) :-
|
|
X = MI ^ mi_delay_info.
|
|
mode_info_get_errors(MI, X) :-
|
|
X = MI ^ mi_errors.
|
|
mode_info_get_mode_context(MI, X) :-
|
|
X = MI ^ mi_mode_context.
|
|
mode_info_get_context(MI, X) :-
|
|
X = MI ^ mi_context.
|
|
mode_info_get_nondet_live_vars(MI, X) :-
|
|
X = MI ^ mi_nondet_live_vars.
|
|
|
|
% Getters of the fields of mode_sub_info.
|
|
mode_info_get_pred_id(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_pred_id.
|
|
mode_info_get_proc_id(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_proc_id.
|
|
mode_info_get_var_table(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_var_table.
|
|
mode_info_get_debug_modes(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_debug.
|
|
mode_info_get_locked_vars(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_locked_vars.
|
|
mode_info_get_live_vars(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_live_vars.
|
|
mode_info_get_instvarset(ModeInfo, X) :-
|
|
X = ModeInfo ^ mi_sub_info ^ msi_instvarset.
|
|
mode_info_get_parallel_vars(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_par_conj.
|
|
mode_info_get_last_checkpoint_insts(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_last_checkpoint_insts.
|
|
mode_info_get_initial_instmap(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_initial_instmap.
|
|
mode_info_get_head_inst_vars(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_head_inst_vars.
|
|
mode_info_get_warnings(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_warnings.
|
|
mode_info_get_pred_var_multimode_error_map(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_pred_var_multimode_error_map.
|
|
mode_info_get_proc_mode_error_map(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_proc_mode_error_map.
|
|
mode_info_get_how_to_check(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_how_to_check.
|
|
mode_info_get_may_change_called_proc(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_may_change_called_proc.
|
|
mode_info_get_changed_flag(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_changed_flag.
|
|
mode_info_get_checking_extra_goals(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_checking_extra_goals.
|
|
mode_info_get_need_to_requantify(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_need_to_requantify.
|
|
mode_info_get_in_promise_purity_scope(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_in_promise_purity_scope.
|
|
mode_info_get_in_from_ground_term(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_in_from_ground_term.
|
|
mode_info_get_had_from_ground_term(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_had_from_ground_term.
|
|
mode_info_get_make_ground_terms_unique(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_make_ground_terms_unique.
|
|
mode_info_get_in_dupl_for_switch(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_in_dupl_for_switch.
|
|
|
|
%---------------------%
|
|
|
|
% Setters of the fields of mode_info.
|
|
mode_info_set_module_info(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_module_info) then
|
|
true
|
|
else
|
|
!MI ^ mi_module_info := X
|
|
).
|
|
mode_info_set_delay_info(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_delay_info) then
|
|
true
|
|
else
|
|
!MI ^ mi_delay_info := X
|
|
).
|
|
mode_info_set_errors(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_errors) then
|
|
true
|
|
else
|
|
!MI ^ mi_errors := X
|
|
).
|
|
mode_info_set_mode_context(X, !MI) :-
|
|
!MI ^ mi_mode_context := X.
|
|
mode_info_set_context(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_context) then
|
|
true
|
|
else
|
|
!MI ^ mi_context := X
|
|
).
|
|
mode_info_set_nondet_live_vars(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_nondet_live_vars) then
|
|
true
|
|
else
|
|
!MI ^ mi_nondet_live_vars := X
|
|
).
|
|
|
|
% Setters of the fields of mode_sub_info.
|
|
mode_info_set_pred_id(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_pred_id := X.
|
|
mode_info_set_proc_id(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_proc_id := X.
|
|
mode_info_set_var_table(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_var_table := X.
|
|
mode_info_set_locked_vars(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_locked_vars := X.
|
|
mode_info_set_live_vars(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_live_vars := X.
|
|
mode_info_set_instvarset(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_instvarset := X.
|
|
mode_info_set_parallel_vars(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_par_conj := X.
|
|
mode_info_set_last_checkpoint_insts(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_last_checkpoint_insts := X.
|
|
mode_info_set_warnings(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_warnings := X.
|
|
mode_info_set_pred_var_multimode_error_map(X, !MI) :-
|
|
( if
|
|
private_builtin.pointer_equal(X,
|
|
!.MI ^ mi_sub_info ^ msi_pred_var_multimode_error_map)
|
|
then
|
|
true
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_pred_var_multimode_error_map := X
|
|
).
|
|
mode_info_set_proc_mode_error_map(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_proc_mode_error_map := X.
|
|
mode_info_set_may_change_called_proc(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_may_change_called_proc := X.
|
|
mode_info_set_changed_flag(X, !MI) :-
|
|
( if
|
|
private_builtin.pointer_equal(X, !.MI ^ mi_sub_info ^ msi_changed_flag)
|
|
then
|
|
true
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_changed_flag := X
|
|
).
|
|
mode_info_set_need_to_requantify(X, !MI) :-
|
|
( if
|
|
private_builtin.pointer_equal(X,
|
|
!.MI ^ mi_sub_info ^ msi_need_to_requantify)
|
|
then
|
|
true
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_need_to_requantify := X
|
|
).
|
|
mode_info_set_in_promise_purity_scope(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_in_promise_purity_scope := X.
|
|
mode_info_set_in_from_ground_term(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_in_from_ground_term := X.
|
|
mode_info_set_had_from_ground_term(X, !MI) :-
|
|
( if
|
|
private_builtin.pointer_equal(X,
|
|
!.MI ^ mi_sub_info ^ msi_had_from_ground_term)
|
|
then
|
|
true
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_had_from_ground_term := X
|
|
).
|
|
mode_info_set_make_ground_terms_unique(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_make_ground_terms_unique := X.
|
|
mode_info_set_in_dupl_for_switch(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_in_dupl_for_switch := X.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% I (zs) decided which mode_info fields to call pointer_equal for
|
|
% based on this profiling information derived from a bootcheck. During this
|
|
% bootcheck, each of the setter predicates was given a number from 0 to 25,
|
|
% and called gather_mode_info_stats below to record whether the new value
|
|
% was the same bit pattern as the old one.
|
|
%
|
|
% The profiling code follows the data derived from it.
|
|
|
|
% same diff same%
|
|
% 0: 22869923 651583 97.230% module_info
|
|
% 1: 0 1 0.000% pred_id
|
|
% 2: 1 0 100.000% proc_id
|
|
% 3: 4 8815 0.045% varset
|
|
% 4: 4 8815 0.045% vartypes
|
|
% 5: 1824389 3523646 34.113% context
|
|
% 6: 1314611 32769008 3.857% mode_context
|
|
% 7: 0 438852 0.000% locked_vars
|
|
% 8: 0 1283911 0.000% instvarset
|
|
% 9: 3234680 1901125 62.983% errors
|
|
% 10: 0 892 0.000% warnings
|
|
% 11: 57326 19964 74.170% need_to_requantify
|
|
% 12: 0 868 0.000% promise_purity_scope
|
|
% 13: 12223802 2022001 85.806% delay_info
|
|
% 14: 58338 9161895 0.633% live_vars
|
|
% 15: 1707132 12480878 12.032% nondet_live_vars
|
|
% 16: 0 0 last_checkpoint
|
|
% 17: 0 0 parallel vars
|
|
% 18: 604507 562 99.907% changed_flag
|
|
% 19: 0 0 how_to_check
|
|
% 20: 0 16620 0.000% may_change_called_proc
|
|
% 21: 1156755 684924 62.810% may_init_solver_vars
|
|
% 22: 0 0 in_from_ground_term
|
|
% 23: 2355 1775 57.022% had_from_ground_term
|
|
% 24: 0 1 0.000% make_ground_term_unique
|
|
% 25: 212 16930 1.237% in_dupl_for_switch
|
|
|
|
% :- import_module io.
|
|
% :- pred write_mode_info_stats(io::di, io::uo) is det.
|
|
%
|
|
% :- pragma foreign_decl("C", local,
|
|
% "
|
|
% #define MR_NUM_MODE_INFO_STATS 26
|
|
% unsigned long MR_stats_same[MR_NUM_MODE_INFO_STATS];
|
|
% unsigned long MR_stats_diff[MR_NUM_MODE_INFO_STATS];
|
|
% ").
|
|
%
|
|
% :- pred gather_mode_info_stats(int::in, T::in, T::in,
|
|
% mode_info::in, mode_info::out) is det.
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% gather_mode_info_stats(N::in, Old::in, New::in, MI0::in, MI::out),
|
|
% [will_not_call_mercury, promise_pure],
|
|
% "
|
|
% if (((MR_Unsigned) Old) == ((MR_Unsigned) New)) {
|
|
% ++MR_stats_same[N];
|
|
% } else {
|
|
% ++MR_stats_diff[N];
|
|
% }
|
|
%
|
|
% MI = MI0;
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% write_mode_info_stats(IO0::di, IO::uo),
|
|
% [will_not_call_mercury, promise_pure],
|
|
% "
|
|
% FILE *fp;
|
|
%
|
|
% fp = fopen(""/tmp/MODE_INFO_STATS"", ""a"");
|
|
% if (fp != NULL) {
|
|
% int i;
|
|
% for (i = 0; i < MR_NUM_MODE_INFO_STATS; i++) {
|
|
% fprintf(fp, ""%d: %lu %lu\\n"",
|
|
% i, MR_stats_same[i], MR_stats_diff[i]);
|
|
% }
|
|
% }
|
|
%
|
|
% IO = IO0;
|
|
% ").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_info_get_num_errors(ModeInfo, NumErrors) :-
|
|
mode_info_get_errors(ModeInfo, Errors),
|
|
list.length(Errors, NumErrors).
|
|
|
|
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_pred_id_table(ModeInfo, PredIdTable) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_pred_id_table(ModuleInfo, PredIdTable).
|
|
|
|
mode_info_get_insts(ModeInfo, Insts) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_inst_table(ModuleInfo, Insts).
|
|
|
|
mode_info_get_modes(ModeInfo, Modes) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_mode_table(ModuleInfo, Modes).
|
|
|
|
mode_info_get_types_of_vars(ModeInfo, Vars, TypesOfVars) :-
|
|
mode_info_get_var_table(ModeInfo, VarTable),
|
|
lookup_var_types(VarTable, Vars, TypesOfVars).
|
|
|
|
look_up_proc_mode_errors(ModeInfo, PredId, ProcId, ModeErrors) :-
|
|
mode_info_get_proc_mode_error_map(ModeInfo, ProcModeErrorMap),
|
|
look_up_proc_mode_errors_raw(ProcModeErrorMap, PredId, ProcId, ModeErrors).
|
|
|
|
look_up_proc_mode_errors_raw(ProcModeErrorMap, PredId, ProcId, ModeErrors) :-
|
|
( if map.search(ProcModeErrorMap, PredId, ProcMap) then
|
|
( if map.search(ProcMap, ProcId, ModeErrorsPrime) then
|
|
ModeErrors = ModeErrorsPrime
|
|
else
|
|
ModeErrors = []
|
|
)
|
|
else
|
|
ModeErrors = []
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_info_set_instmap(InstMap, !MI) :-
|
|
mode_info_get_instmap(!.MI, InstMap0),
|
|
!MI ^ mi_instmap := InstMap,
|
|
( if
|
|
instmap_is_unreachable(InstMap),
|
|
instmap_is_reachable(InstMap0)
|
|
then
|
|
mode_info_get_delay_info(!.MI, DelayInfo0),
|
|
delay_info_bind_all_vars(DelayInfo0, DelayInfo),
|
|
mode_info_set_delay_info(DelayInfo, !MI)
|
|
else
|
|
true
|
|
).
|
|
|
|
mode_info_set_checking_extra_goals(Checking, !MI) :-
|
|
mode_info_get_checking_extra_goals(!.MI, Checking0),
|
|
( if
|
|
Checking0 = yes,
|
|
Checking = yes
|
|
then
|
|
% This should never happen - once the extra goals are introduced,
|
|
% rechecking the goal should not introduce more extra goals.
|
|
unexpected($pred, "rechecking extra goals adds more extra goals")
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_checking_extra_goals := Checking
|
|
).
|
|
|
|
mode_info_set_call_context(CallContext, !MI) :-
|
|
(
|
|
CallContext = call_context_unify(UnifyContext),
|
|
mode_info_set_mode_context(mode_context_unify(UnifyContext, left), !MI)
|
|
;
|
|
CallContext = call_context_call(CallId),
|
|
mode_info_set_mode_context(mode_context_call(CallId, 0), !MI)
|
|
).
|
|
|
|
mode_info_unset_call_context(!MI) :-
|
|
mode_info_set_mode_context(mode_context_not_call_or_unify, !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.
|
|
;
|
|
ModeContext0 = mode_context_not_call_or_unify,
|
|
unexpected($pred, "not_call_or_unify")
|
|
).
|
|
|
|
mode_info_need_to_requantify(!ModeInfo) :-
|
|
mode_info_set_need_to_requantify(need_to_requantify, !ModeInfo).
|
|
|
|
set_proc_mode_errors(PredId, ProcId, ModeErrors, !ProcModeErrorMap) :-
|
|
(
|
|
ModeErrors = [],
|
|
% We delete the entry for the PredId/ProcId combo in !ProcModeErrorMap,
|
|
% together with the entry for PredId if it has become empty.
|
|
( if map.search(!.ProcModeErrorMap, PredId, ProcMap0) then
|
|
map.delete(ProcId, ProcMap0, ProcMap),
|
|
( if map.is_empty(ProcMap) then
|
|
map.delete(PredId, !ProcModeErrorMap)
|
|
else
|
|
map.det_update(PredId, ProcMap, !ProcModeErrorMap)
|
|
)
|
|
else
|
|
true
|
|
)
|
|
;
|
|
ModeErrors = [_ | _],
|
|
( if map.search(!.ProcModeErrorMap, PredId, ProcMap0) then
|
|
map.set(ProcId, ModeErrors, ProcMap0, ProcMap),
|
|
map.det_update(PredId, ProcMap, !ProcModeErrorMap)
|
|
else
|
|
ProcMap = map.singleton(ProcId, ModeErrors),
|
|
map.det_insert(PredId, ProcMap, !ProcModeErrorMap)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_info_var_is_live(ModeInfo, Var, Result) :-
|
|
mode_info_get_live_vars(ModeInfo, LiveVars0),
|
|
( if bag.contains(LiveVars0, Var) then
|
|
Result = is_live
|
|
else
|
|
Result = is_dead
|
|
).
|
|
|
|
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).
|
|
|
|
mode_info_var_is_nondet_live(ModeInfo, Var, Result) :-
|
|
mode_info_get_nondet_live_vars(ModeInfo, NondetLiveVars0),
|
|
( if bag.contains(NondetLiveVars0, Var) then
|
|
Result = is_live
|
|
else
|
|
Result = is_dead
|
|
).
|
|
|
|
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).
|
|
|
|
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).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% 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_var_is_locked(ModeInfo, Var, Reason) :-
|
|
mode_info_get_locked_vars(ModeInfo, LockedVarsList),
|
|
mode_info_var_is_locked_loop(LockedVarsList, Var, Reason).
|
|
|
|
:- pred mode_info_var_is_locked_loop(locked_vars::in, prog_var::in,
|
|
var_lock_reason::out) is semidet.
|
|
|
|
mode_info_var_is_locked_loop([ThisReason - Set | Sets], Var, Reason) :-
|
|
( if set_of_var.member(Set, Var) then
|
|
Reason = ThisReason
|
|
else
|
|
mode_info_var_is_locked_loop(Sets, Var, Reason)
|
|
).
|
|
|
|
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),
|
|
( if
|
|
LockedVars0 = [Reason - TheseVars | LockedVars1],
|
|
set_of_var.equal(TheseVars, Vars)
|
|
then
|
|
LockedVars = LockedVars1
|
|
else
|
|
unexpected($pred, "some kind of nesting error")
|
|
),
|
|
mode_info_set_locked_vars(LockedVars, !ModeInfo).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
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_get_debug_modes(!.ModeInfo, DebugModes),
|
|
(
|
|
DebugModes = no
|
|
;
|
|
DebugModes = yes(_DebugFlags),
|
|
trace [io(!IO)] (
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals0),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
get_debug_output_stream(Globals0, ModuleName, DebugStream, !IO),
|
|
list.length(Errors, ErrorNum),
|
|
io.format(DebugStream, "Adding error_spec %d\n",
|
|
[i(ErrorNum)], !IO),
|
|
Spec = mode_error_info_to_spec(!.ModeInfo, ModeErrorInfo),
|
|
globals.set_option(print_error_spec_id, bool(yes),
|
|
Globals0, Globals),
|
|
write_error_spec(DebugStream, Globals, Spec, !IO),
|
|
io.flush_output(DebugStream, !IO)
|
|
)
|
|
).
|
|
|
|
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_get_pf_sym_name_arity(ModeInfo, PredId, CallId) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
pred_info_get_pf_sym_name_arity(PredInfo, CallId).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module check_hlds.mode_info.
|
|
%---------------------------------------------------------------------------%
|