mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 21:35:49 +00:00
Estimated hours taken: 4 Branches: main This diff contains no changes in algorithms whatsoever. browser/*.m: compiler/*.m: library/*.m: Replace old-style lambdas with new-style lambdas or with named procedures.
1735 lines
58 KiB
Mathematica
1735 lines
58 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1996-2003 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.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% unused_args.m
|
|
%
|
|
% Main author - stayl, Jan 1996
|
|
%
|
|
% Detects and removes unused input arguments in procedures, especially
|
|
% type_infos. Currently only does analysis within a module.
|
|
%
|
|
% To enable the warnings use --warn-unused-args
|
|
% To enable the optimisation use --optimize-unused-args
|
|
%
|
|
% An argument is considered used if it (or any of its aliases) are
|
|
% - used in a call to a predicate external to the current module
|
|
% - used in a higher-order call
|
|
% - used to instantiate an output variable
|
|
% - involved in a simple test, switch or a semidet deconstruction
|
|
% - used as an argument to another predicate in this module which is used.
|
|
% When using alternate liveness calculation, the following variables are
|
|
% also considered used
|
|
% - a type-info (or part of a type-info) of a type parameter of the
|
|
% type of a variable that is used (for example, if a variable
|
|
% of type list(T) is used, then TypeInfo_for_T is used)
|
|
%
|
|
% The first step is to determine which arguments of which predicates are
|
|
% used locally to their predicate. For each unused argument, a set of
|
|
% other arguments that it depends on is built up.
|
|
% The next step is to iterate over the this map, checking for each unused
|
|
% argument whether any of the arguments it depends on has become used
|
|
% in the last iteration. Iterations are repeated until a fixpoint is
|
|
% reached.
|
|
% Warnings are then output. The warning message indicates which arguments
|
|
% are used in none of the modes of a predicate.
|
|
% The predicates are then fixed up. Unused variables and unifications are
|
|
% removed.
|
|
|
|
:- module transform_hlds__unused_args.
|
|
|
|
%-------------------------------------------------------------------------------
|
|
:- interface.
|
|
|
|
:- import_module analysis.
|
|
:- import_module hlds__hlds_module.
|
|
|
|
:- import_module io.
|
|
|
|
:- pred unused_args__process_module(module_info::in, module_info::out,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
% Instances used by mmc_analysis.m.
|
|
:- type unused_args_answer.
|
|
:- type unused_args_func_info.
|
|
:- instance analysis(unused_args_func_info, any_call, unused_args_answer).
|
|
:- instance partial_order(unused_args_func_info, any_call).
|
|
:- instance call_pattern(unused_args_func_info, any_call).
|
|
:- instance partial_order(unused_args_func_info, unused_args_answer).
|
|
:- instance answer_pattern(unused_args_func_info, unused_args_answer).
|
|
:- instance to_string(unused_args_answer).
|
|
|
|
%-------------------------------------------------------------------------------
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds__inst_match.
|
|
:- import_module check_hlds__mode_util.
|
|
:- import_module check_hlds__polymorphism.
|
|
:- import_module check_hlds__type_util.
|
|
:- import_module hlds__goal_util.
|
|
:- import_module hlds__hlds_data.
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module hlds__hlds_out.
|
|
:- import_module hlds__hlds_pred.
|
|
:- import_module hlds__instmap.
|
|
:- import_module hlds__make_hlds.
|
|
:- import_module hlds__passes_aux.
|
|
:- import_module hlds__quantification.
|
|
:- import_module hlds__special_pred.
|
|
:- import_module libs__globals.
|
|
:- import_module libs__options.
|
|
:- import_module parse_tree__mercury_to_mercury.
|
|
:- import_module parse_tree__modules.
|
|
:- import_module parse_tree__prog_data.
|
|
:- import_module parse_tree__prog_out.
|
|
:- import_module parse_tree__prog_util.
|
|
:- import_module transform_hlds__mmc_analysis.
|
|
|
|
:- import_module bool, int, char, string, list, assoc_list, set, map.
|
|
:- import_module std_util, require.
|
|
|
|
% Information about the dependencies of a variable
|
|
% that is not known to be used.
|
|
:- type usage_info --->
|
|
unused(set(prog_var), set(arg)).
|
|
|
|
% A collection of variable usages for each procedure.
|
|
:- type var_usage == map(pred_proc_id, var_dep).
|
|
|
|
% arguments are stored as their variable id, not their index
|
|
% in the argument vector
|
|
:- type arg == pair(pred_proc_id, prog_var).
|
|
|
|
% Contains dependency information for the variables
|
|
% in a procedure that are not yet known to be used.
|
|
:- type var_dep == map(prog_var, usage_info).
|
|
|
|
:- type warning_info --->
|
|
warning_info(prog_context, string, int, list(int)).
|
|
% context, pred name, arity, list of args to warn
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
% Types and instances used by mmc_analysis.m.
|
|
|
|
% The list of unused arguments is in sorted order.
|
|
:- type unused_args_answer ---> unused_args(list(int)).
|
|
|
|
:- instance analysis(unused_args_func_info, any_call,
|
|
unused_args_answer) where [
|
|
analysis_name(_, _, _) = "unused_args",
|
|
analysis_version_number(_, _, _) = 1,
|
|
preferred_fixpoint_type(_, _, _) = least_fixpoint
|
|
].
|
|
|
|
:- type unused_args_func_info ---> unused_args_func_info(arity).
|
|
|
|
:- instance call_pattern(unused_args_func_info, any_call) where [].
|
|
:- instance partial_order(unused_args_func_info, any_call) where [
|
|
(more_precise_than(_, _, _) :- semidet_fail),
|
|
(equivalent(_, _, _) :- semidet_succeed)
|
|
].
|
|
|
|
:- instance answer_pattern(unused_args_func_info, unused_args_answer) where [
|
|
bottom(unused_args_func_info(Arity)) = unused_args(1 `..` Arity),
|
|
top(_) = unused_args([])
|
|
].
|
|
:- instance partial_order(unused_args_func_info, unused_args_answer) where [
|
|
(more_precise_than(_, unused_args(Args1), unused_args(Args2)) :-
|
|
set__subset(sorted_list_to_set(Args2),
|
|
sorted_list_to_set(Args1))
|
|
),
|
|
equivalent(_, Args, Args)
|
|
].
|
|
|
|
:- instance to_string(unused_args_answer) where [
|
|
to_string(unused_args(Args)) =
|
|
string__join_list(" ", list__map(int_to_string, Args)),
|
|
(from_string(String) = unused_args(Args) :-
|
|
{Digits, Args0} = string__foldl(
|
|
(func(Char, {Digits0, Ints0}) =
|
|
( char__is_digit(Char) ->
|
|
{[Char | Digits0], Ints0}
|
|
;
|
|
{[], [string__det_to_int(
|
|
string__from_rev_char_list(Digits0)) | Ints0]}
|
|
)
|
|
), String, {[], []}),
|
|
Args = list__reverse([string__det_to_int(
|
|
string__from_rev_char_list(Digits)) | Args0])
|
|
)
|
|
].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
unused_args__process_module(!ModuleInfo) -->
|
|
globals__io_lookup_bool_option(very_verbose, VeryVerbose),
|
|
init_var_usage(VarUsage0, PredProcs, ProcCallInfo0, !ModuleInfo),
|
|
%maybe_write_string(VeryVerbose, "% Finished initialisation.\n"),
|
|
|
|
{ unused_args_pass(PredProcs, VarUsage0, VarUsage) },
|
|
%maybe_write_string(VeryVerbose, "% Finished analysis.\n"),
|
|
|
|
{ map__init(UnusedArgInfo0) },
|
|
{ get_unused_arg_info(!.ModuleInfo, PredProcs, VarUsage,
|
|
UnusedArgInfo0, UnusedArgInfo) },
|
|
|
|
{ map__keys(UnusedArgInfo, PredProcsToFix) },
|
|
|
|
globals__io_lookup_bool_option(make_optimization_interface, MakeOpt),
|
|
( { MakeOpt = yes } ->
|
|
{ module_info_name(!.ModuleInfo, ModuleName) },
|
|
module_name_to_file_name(ModuleName, ".opt.tmp", no,
|
|
OptFileName),
|
|
io__open_append(OptFileName, OptFileRes),
|
|
( { OptFileRes = ok(OptFile) },
|
|
{ MaybeOptFile = yes(OptFile) }
|
|
; { OptFileRes = error(IOError) },
|
|
{ io__error_message(IOError, IOErrorMessage) },
|
|
io__write_strings(["Cannot open `", OptFileName,
|
|
"' for output: ", IOErrorMessage]),
|
|
io__set_exit_status(1),
|
|
{ MaybeOptFile = no }
|
|
)
|
|
;
|
|
{ MaybeOptFile = no }
|
|
),
|
|
globals__io_lookup_bool_option(warn_unused_args, DoWarn),
|
|
( { DoWarn = yes ; MakeOpt = yes } ->
|
|
{ set__init(WarnedPredIds0) },
|
|
output_warnings_and_pragmas(!.ModuleInfo, UnusedArgInfo,
|
|
MaybeOptFile, DoWarn, PredProcsToFix, WarnedPredIds0)
|
|
;
|
|
[]
|
|
),
|
|
( { MaybeOptFile = yes(OptFile2) } ->
|
|
io__close_output(OptFile2)
|
|
;
|
|
[]
|
|
),
|
|
globals__io_lookup_bool_option(optimize_unused_args, DoFixup),
|
|
(
|
|
{ DoFixup = yes }
|
|
->
|
|
list__foldl3(create_new_pred(UnusedArgInfo), PredProcsToFix,
|
|
ProcCallInfo0, ProcCallInfo, !ModuleInfo),
|
|
% maybe_write_string(VeryVerbose, "% Finished new preds.\n"),
|
|
fixup_unused_args(VarUsage, PredProcs,
|
|
ProcCallInfo, !ModuleInfo, VeryVerbose),
|
|
% maybe_write_string(VeryVerbose, "% Fixed up goals.\n"),
|
|
{ map__is_empty(ProcCallInfo) ->
|
|
true
|
|
;
|
|
% The dependencies have changed, so the dependency
|
|
% graph needs rebuilding.
|
|
module_info_clobber_dependency_info(!ModuleInfo)
|
|
}
|
|
;
|
|
[]
|
|
).
|
|
|
|
%-------------------------------------------------------------------------------
|
|
% Initialisation section
|
|
|
|
% init_var_usage/4 - set initial status of all args of local
|
|
% procs by examining the module_info.
|
|
% PredProcList is the list of procedures to do the fixpoint
|
|
% iteration over.
|
|
% OptPredProcList is a list of procedures for which we got
|
|
% unused argument information from .opt files.
|
|
:- pred init_var_usage(var_usage::out, pred_proc_list::out,
|
|
proc_call_info::out, module_info::in, module_info::out,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
init_var_usage(VarUsage, PredProcList, ProcCallInfo, !ModuleInfo, !IO) :-
|
|
map__init(ProcCallInfo0),
|
|
map__init(VarUsage0),
|
|
module_info_predids(!.ModuleInfo, PredIds),
|
|
module_info_unused_arg_info(!.ModuleInfo, UnusedArgInfo),
|
|
setup_local_var_usage(PredIds, UnusedArgInfo,
|
|
VarUsage0, VarUsage, [], PredProcList,
|
|
ProcCallInfo0, ProcCallInfo, !ModuleInfo, !IO).
|
|
|
|
% setup args for the whole module.
|
|
:- pred setup_local_var_usage(list(pred_id)::in, unused_arg_info::in,
|
|
var_usage::in, var_usage::out, pred_proc_list::in, pred_proc_list::out,
|
|
proc_call_info::in, proc_call_info::out, module_info::in,
|
|
module_info::out, io__state::di, io__state::uo) is det.
|
|
|
|
setup_local_var_usage([], _, !VarUsage, !PredProcs,
|
|
!OptProcs, !ModuleInfo, !IO).
|
|
setup_local_var_usage([PredId | PredIds], UnusedArgInfo,
|
|
!VarUsage, !PredProcList, !OptProcs, !ModuleInfo, !IO) :-
|
|
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
|
|
(
|
|
% The builtins use all their arguments.
|
|
% We also want to treat stub procedures
|
|
% (those which originally had no clauses)
|
|
% as if they use all of their arguments,
|
|
% to avoid spurious warnings in their callers.
|
|
(
|
|
pred_info_is_builtin(PredInfo)
|
|
;
|
|
pred_info_get_markers(PredInfo, Markers),
|
|
check_marker(Markers, stub)
|
|
)
|
|
->
|
|
setup_local_var_usage(PredIds, UnusedArgInfo, !VarUsage,
|
|
!PredProcList, !OptProcs, !ModuleInfo, !IO)
|
|
;
|
|
ProcIds = pred_info_procids(PredInfo),
|
|
setup_pred_args(PredId, ProcIds, UnusedArgInfo, !VarUsage,
|
|
!PredProcList, !OptProcs, !ModuleInfo, !IO),
|
|
setup_local_var_usage(PredIds, UnusedArgInfo, !VarUsage,
|
|
!PredProcList, !OptProcs, !ModuleInfo, !IO)
|
|
).
|
|
|
|
% setup args for a predicate
|
|
:- pred setup_pred_args(pred_id::in, list(proc_id)::in, unused_arg_info::in,
|
|
var_usage::in, var_usage::out, pred_proc_list::in, pred_proc_list::out,
|
|
proc_call_info::in, proc_call_info::out, module_info::in,
|
|
module_info::out, io__state::di, io__state::uo) is det.
|
|
|
|
setup_pred_args(_, [], _, !VarUsage, !PredProcs, !OptProcs, !ModuleInfo, !IO).
|
|
setup_pred_args(PredId, [ProcId | Rest], UnusedArgInfo, !VarUsage,
|
|
!PredProcs, !OptProcs, !ModuleInfo, !IO) :-
|
|
module_info_pred_proc_info(!.ModuleInfo, PredId, ProcId,
|
|
PredInfo, ProcInfo),
|
|
map__init(VarDep0),
|
|
globals__io_lookup_bool_option(intermodule_analysis, Intermod, !IO),
|
|
(
|
|
% Don't use the intermodule analysis info when we have the
|
|
% clauses (opt_imported preds) since we may be able to do
|
|
% better with the information in this module.
|
|
Intermod = yes,
|
|
pred_info_is_imported(PredInfo)
|
|
->
|
|
PredModule = pred_info_module(PredInfo),
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
PredName = pred_info_name(PredInfo),
|
|
PredArity = pred_info_arity(PredInfo),
|
|
FuncId = pred_or_func_name_arity_to_func_id(PredOrFunc,
|
|
PredName, PredArity, ProcId),
|
|
module_info_analysis_info(!.ModuleInfo, AnalysisInfo0),
|
|
lookup_best_result(module_name_to_module_id(PredModule),
|
|
FuncId, unused_args_func_info(PredArity),
|
|
any_call, MaybeBestResult,
|
|
AnalysisInfo0, AnalysisInfo, !IO),
|
|
module_info_set_analysis_info(AnalysisInfo, !ModuleInfo),
|
|
( MaybeBestResult = yes(_ - unused_args(UnusedArgs)) ->
|
|
proc_info_headvars(ProcInfo, HeadVars),
|
|
list__map(list__index1_det(HeadVars),
|
|
UnusedArgs, UnusedVars),
|
|
initialise_vardep(VarDep0, UnusedVars, VarDep),
|
|
!:VarUsage = map__set(!.VarUsage,
|
|
proc(PredId, ProcId), VarDep),
|
|
globals__io_lookup_bool_option(optimize_unused_args,
|
|
Optimize, !IO),
|
|
( Optimize = yes ->
|
|
make_imported_unused_args_pred_info(
|
|
proc(PredId, ProcId), UnusedArgs,
|
|
!OptProcs, !ModuleInfo)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
(
|
|
pred_info_is_imported(PredInfo)
|
|
;
|
|
pred_info_is_pseudo_imported(PredInfo),
|
|
hlds_pred__in_in_unification_proc_id(ProcId)
|
|
)
|
|
->
|
|
true
|
|
;
|
|
proc_info_vartypes(ProcInfo, VarTypes),
|
|
map__keys(VarTypes, Vars),
|
|
initialise_vardep(VarDep0, Vars, VarDep1),
|
|
setup_output_args(!.ModuleInfo, ProcInfo, VarDep1, VarDep2),
|
|
|
|
module_info_globals(!.ModuleInfo, Globals),
|
|
proc_interface_should_use_typeinfo_liveness(PredInfo, ProcId,
|
|
Globals, TypeInfoLiveness),
|
|
( TypeInfoLiveness = yes ->
|
|
proc_info_typeinfo_varmap(ProcInfo, TVarMap),
|
|
setup_typeinfo_deps(Vars, VarTypes,
|
|
proc(PredId, ProcId), TVarMap, VarDep2,
|
|
VarDep3)
|
|
;
|
|
VarDep2 = VarDep3
|
|
),
|
|
|
|
proc_info_goal(ProcInfo, Goal - _),
|
|
Info = traverse_info(!.ModuleInfo, VarTypes),
|
|
traverse_goal(Info, Goal, VarDep3, VarDep),
|
|
!:VarUsage = map__set(!.VarUsage,
|
|
proc(PredId, ProcId), VarDep),
|
|
|
|
!:PredProcs = [proc(PredId, ProcId) | !.PredProcs]
|
|
),
|
|
setup_pred_args(PredId, Rest, UnusedArgInfo,
|
|
!VarUsage, !PredProcs, !OptProcs, !ModuleInfo, !IO).
|
|
|
|
|
|
:- pred initialise_vardep(var_dep::in, list(prog_var)::in, var_dep::out) is det.
|
|
|
|
initialise_vardep(VarDep, [], VarDep).
|
|
initialise_vardep(VarDep0, [Var | Vars], VarDep) :-
|
|
set__init(VDep),
|
|
set__init(Args),
|
|
map__set(VarDep0, Var, unused(VDep, Args), VarDep1),
|
|
initialise_vardep(VarDep1, Vars, VarDep).
|
|
|
|
%-------------------------------------------------------------------------------
|
|
% Predicates for manipulating the var_usage and var_dep structures.
|
|
|
|
% For each variable ensure the typeinfos describing the
|
|
% type parameters of the type of the variable depend on the
|
|
% head variable.
|
|
% For example, if HeadVar1 has type list(T), then TypeInfo_for_T
|
|
% is used if HeadVar1 is used.
|
|
:- pred setup_typeinfo_deps(list(prog_var)::in, map(prog_var, type)::in,
|
|
pred_proc_id::in, map(tvar, type_info_locn)::in,
|
|
var_dep::in, var_dep::out) is det.
|
|
|
|
setup_typeinfo_deps([], _, _, _, VarDep, VarDep).
|
|
setup_typeinfo_deps([Var | Vars], VarTypeMap, PredProcId, TVarMap, VarDep0,
|
|
VarDep) :-
|
|
map__lookup(VarTypeMap, Var, Type),
|
|
type_util__vars(Type, TVars),
|
|
list__map((pred(TVar::in, TypeInfoVar::out) is det :-
|
|
map__lookup(TVarMap, TVar, Locn),
|
|
type_info_locn_var(Locn, TypeInfoVar)
|
|
), TVars, TypeInfoVars),
|
|
AddArgDependency =
|
|
(pred(TVar::in, VarDepA::in, VarDepB::out) is det :-
|
|
add_arg_dep(VarDepA, TVar, PredProcId, Var, VarDepB)
|
|
),
|
|
list__foldl(AddArgDependency, TypeInfoVars, VarDep0, VarDep1),
|
|
setup_typeinfo_deps(Vars, VarTypeMap, PredProcId, TVarMap,
|
|
VarDep1, VarDep).
|
|
|
|
|
|
% Get output arguments for a procedure given the headvars and the
|
|
% argument modes, and set them as used.
|
|
:- pred setup_output_args(module_info::in, proc_info::in,
|
|
var_dep::in, var_dep::out) is det.
|
|
|
|
setup_output_args(ModuleInfo, ProcInfo, VarDep0, VarDep) :-
|
|
proc_info_instantiated_head_vars(ModuleInfo, ProcInfo,
|
|
ChangedInstHeadVars),
|
|
list__foldl(set_var_used, ChangedInstHeadVars, VarDep0, VarDep).
|
|
|
|
% searches for the dependencies of a variable, succeeds if the variable
|
|
% is definitely used
|
|
:- pred var_is_used(pred_proc_id::in, prog_var::in, var_usage::in) is semidet.
|
|
|
|
var_is_used(PredProc, Var, VarUsage) :-
|
|
\+ (
|
|
map__search(VarUsage, PredProc, UsageInfos),
|
|
map__contains(UsageInfos, Var)
|
|
).
|
|
|
|
:- pred local_var_is_used(var_dep::in, prog_var::in) is semidet.
|
|
|
|
local_var_is_used(VarDep, Var) :-
|
|
\+ map__contains(VarDep, Var).
|
|
|
|
% add a list of aliases for a variable
|
|
:- pred add_aliases(var_dep::in, prog_var::in, list(prog_var)::in,
|
|
var_dep::out) is det.
|
|
|
|
add_aliases(UseInf0, Var, Aliases, UseInf) :-
|
|
(
|
|
map__search(UseInf0, Var, VarInf0)
|
|
->
|
|
VarInf0 = unused(VarDep0, ArgDep),
|
|
set__insert_list(VarDep0, Aliases, VarDep),
|
|
VarInf = unused(VarDep, ArgDep),
|
|
map__det_update(UseInf0, Var, VarInf, UseInf)
|
|
;
|
|
UseInf = UseInf0
|
|
).
|
|
|
|
:- pred set_list_vars_used(var_dep::in, list(prog_var)::in,
|
|
var_dep::out) is det.
|
|
|
|
set_list_vars_used(UseInfo0, Vars, UseInfo) :-
|
|
map__delete_list(UseInfo0, Vars, UseInfo).
|
|
|
|
:- pred set_var_used(prog_var::in, var_dep::in, var_dep::out) is det.
|
|
|
|
set_var_used(Var, UseInfo0, UseInfo) :-
|
|
map__delete(UseInfo0, Var, UseInfo).
|
|
|
|
|
|
:- pred lookup_local_var(var_dep::in, prog_var::in, usage_info::out) is semidet.
|
|
|
|
lookup_local_var(VarDep, Var, UsageInfo) :-
|
|
map__search(VarDep, Var, UsageInfo).
|
|
|
|
|
|
%-------------------------------------------------------------------------------
|
|
% Traversal of goal structure, building up dependencies for all
|
|
% variables.
|
|
|
|
:- type traverse_info
|
|
---> traverse_info(
|
|
module_info :: module_info,
|
|
vartypes :: vartypes
|
|
).
|
|
|
|
:- pred traverse_goal(traverse_info::in, hlds_goal_expr::in,
|
|
var_dep::in, var_dep::out) is det.
|
|
|
|
% handle conjunction
|
|
traverse_goal(Info, conj(Goals), UseInf0, UseInf) :-
|
|
traverse_list_of_goals(Info, Goals, UseInf0, UseInf).
|
|
|
|
% handle parallel conjunction
|
|
traverse_goal(Info, par_conj(Goals), UseInf0, UseInf) :-
|
|
traverse_list_of_goals(Info, Goals, UseInf0, UseInf).
|
|
|
|
% handle disjunction
|
|
traverse_goal(Info, disj(Goals), UseInf0, UseInf) :-
|
|
traverse_list_of_goals(Info, Goals, UseInf0, UseInf).
|
|
|
|
% handle switch
|
|
traverse_goal(Info, switch(Var, _, Cases), UseInf0, UseInf) :-
|
|
set_var_used(Var, UseInf0, UseInf1),
|
|
list_case_to_list_goal(Cases, Goals),
|
|
traverse_list_of_goals(Info, Goals, UseInf1, UseInf).
|
|
|
|
% handle predicate call
|
|
traverse_goal(Info, call(PredId, ProcId, Args, _, _, _),
|
|
UseInf0, UseInf) :-
|
|
module_info_pred_proc_info(Info^module_info, PredId, ProcId, _Pred,
|
|
Proc),
|
|
proc_info_headvars(Proc, HeadVars),
|
|
add_pred_call_arg_dep(proc(PredId, ProcId), Args, HeadVars,
|
|
UseInf0, UseInf).
|
|
|
|
% handle if then else
|
|
traverse_goal(Info, if_then_else(_, Cond - _, Then - _, Else - _),
|
|
UseInf0, UseInf) :-
|
|
traverse_goal(Info, Cond, UseInf0, UseInf1),
|
|
traverse_goal(Info, Then, UseInf1, UseInf2),
|
|
traverse_goal(Info, Else, UseInf2, UseInf).
|
|
|
|
% handle negation
|
|
traverse_goal(Info, not(Goal - _), UseInf0, UseInf) :-
|
|
traverse_goal(Info, Goal, UseInf0, UseInf).
|
|
|
|
% handle quantification
|
|
traverse_goal(Info, some(_, _, Goal - _), UseInf0, UseInf) :-
|
|
traverse_goal(Info, Goal, UseInf0, UseInf).
|
|
|
|
% we assume that higher-order predicate calls use all variables involved
|
|
traverse_goal(_, generic_call(GenericCall, Args, _, _), UseInf0, UseInf) :-
|
|
goal_util__generic_call_vars(GenericCall, CallArgs),
|
|
set_list_vars_used(UseInf0, CallArgs, UseInf1),
|
|
set_list_vars_used(UseInf1, Args, UseInf).
|
|
|
|
% handle pragma foreign_proc(...) -
|
|
% only those arguments which have names can be used in the foreign code.
|
|
traverse_goal(_, foreign_proc(_, _, _, Args, Names, _, _),
|
|
UseInf0, UseInf) :-
|
|
assoc_list__from_corresponding_lists(Args, Names, ArgsAndNames),
|
|
ArgIsUsed = (pred(ArgAndName::in, Arg::out) is semidet :-
|
|
ArgAndName = Arg - MaybeName,
|
|
MaybeName = yes(_)
|
|
),
|
|
list__filter_map(ArgIsUsed, ArgsAndNames, UsedArgs),
|
|
set_list_vars_used(UseInf0, UsedArgs, UseInf).
|
|
|
|
% cases to handle all the different types of unification
|
|
traverse_goal(_, unify(_, _, _, simple_test(Var1, Var2),_), UseInf0, UseInf)
|
|
:-
|
|
set_var_used(Var1, UseInf0, UseInf1),
|
|
set_var_used(Var2, UseInf1, UseInf).
|
|
|
|
traverse_goal(_, unify(_, _, _, assign(Var1, Var2), _), UseInf0, UseInf) :-
|
|
( local_var_is_used(UseInf0, Var1) ->
|
|
% if Var1 used to instantiate an output argument, Var2 used
|
|
set_var_used(Var2, UseInf0, UseInf)
|
|
;
|
|
add_aliases(UseInf0, Var2, [Var1], UseInf)
|
|
).
|
|
|
|
traverse_goal(Info,
|
|
unify(Var1, _, _,
|
|
deconstruct(_, _, Args, Modes, CanFail, _), _),
|
|
UseInf0, UseInf) :-
|
|
partition_deconstruct_args(Info, Args,
|
|
Modes, InputVars, OutputVars),
|
|
% The deconstructed variable is used if any of the
|
|
% variables, that the deconstruction binds are used.
|
|
add_aliases(UseInf0, Var1, OutputVars, UseInf1),
|
|
% Treat a deconstruction that further instantiates its
|
|
% left arg as a partial construction.
|
|
add_construction_aliases(UseInf1, Var1, InputVars, UseInf2),
|
|
(
|
|
CanFail = can_fail
|
|
->
|
|
% a deconstruction that can_fail uses its left arg
|
|
set_var_used(Var1, UseInf2, UseInf)
|
|
;
|
|
UseInf = UseInf2
|
|
).
|
|
|
|
traverse_goal(_, unify(Var1, _, _, construct(_, _, Args, _, _, _, _), _),
|
|
UseInf0, UseInf) :-
|
|
( local_var_is_used(UseInf0, Var1) ->
|
|
set_list_vars_used(UseInf0, Args, UseInf)
|
|
;
|
|
add_construction_aliases(UseInf0, Var1, Args, UseInf)
|
|
).
|
|
|
|
% These should be transformed into calls by polymorphism.m.
|
|
traverse_goal(_, unify(Var, Rhs, _, complicated_unify(_, _, _), _),
|
|
UseInf0, UseInf) :-
|
|
% This is here to cover the case where unused arguments is called
|
|
% with --error-check-only and polymorphism has not been run.
|
|
% Complicated unifications should only be var-var.
|
|
( Rhs = var(RhsVar) ->
|
|
set_var_used(RhsVar, UseInf0, UseInf1),
|
|
set_var_used(Var, UseInf1, UseInf)
|
|
;
|
|
error("complicated unifications should only be var-var")
|
|
).
|
|
|
|
traverse_goal(_, shorthand(_), _, _) :-
|
|
% these should have been expanded out by now
|
|
error("traverse_goal: unexpected shorthand").
|
|
|
|
% add PredProc - HeadVar as an alias for the same element of Args.
|
|
:- pred add_pred_call_arg_dep(pred_proc_id::in, list(prog_var)::in,
|
|
list(prog_var)::in, var_dep::in, var_dep::out) is det.
|
|
|
|
add_pred_call_arg_dep(PredProc, LocalArguments, HeadVarIds,
|
|
UseInf0, UseInf) :-
|
|
(
|
|
LocalArguments = [Arg | Args], HeadVarIds = [HeadVar | HeadVars]
|
|
->
|
|
add_arg_dep(UseInf0, Arg, PredProc, HeadVar, UseInf1),
|
|
add_pred_call_arg_dep(PredProc, Args, HeadVars,
|
|
UseInf1, UseInf)
|
|
;
|
|
LocalArguments = [], HeadVarIds = []
|
|
->
|
|
UseInf = UseInf0
|
|
;
|
|
error("add_pred_call_arg_dep: invalid call")
|
|
).
|
|
|
|
|
|
:- pred add_arg_dep(var_dep::in, prog_var::in, pred_proc_id::in,
|
|
prog_var::in, var_dep::out) is det.
|
|
|
|
add_arg_dep(UseInf0, Var, PredProc, Arg, UseInf) :-
|
|
(
|
|
lookup_local_var(UseInf0, Var, VarUsage0)
|
|
->
|
|
VarUsage0 = unused(VarDep, ArgDep0),
|
|
set__insert(ArgDep0, PredProc - Arg, ArgDep),
|
|
map__det_update(UseInf0, Var, unused(VarDep, ArgDep), UseInf)
|
|
;
|
|
UseInf = UseInf0
|
|
).
|
|
|
|
% Partition the arguments to a deconstruction into inputs
|
|
% and outputs.
|
|
:- pred partition_deconstruct_args(traverse_info::in, list(prog_var)::in,
|
|
list(uni_mode)::in, list(prog_var)::out,
|
|
list(prog_var)::out) is det.
|
|
|
|
partition_deconstruct_args(Info, ArgVars, ArgModes, InputVars, OutputVars) :-
|
|
(
|
|
ArgVars = [Var | Vars], ArgModes = [Mode | Modes]
|
|
->
|
|
partition_deconstruct_args(Info, Vars, Modes, InputVars1,
|
|
OutputVars1),
|
|
Mode = ((InitialInst1 - InitialInst2) ->
|
|
(FinalInst1 - FinalInst2)),
|
|
|
|
map__lookup(Info^vartypes, Var, Type),
|
|
|
|
% If the inst of the argument of the LHS is changed,
|
|
% the argument is input.
|
|
(
|
|
inst_matches_binding(InitialInst1, FinalInst1,
|
|
Type, Info^module_info)
|
|
->
|
|
InputVars = InputVars1
|
|
;
|
|
InputVars = [Var | InputVars1]
|
|
),
|
|
|
|
% If the inst of the argument of the RHS is changed,
|
|
% the argument is output.
|
|
(
|
|
inst_matches_binding(InitialInst2, FinalInst2,
|
|
Type, Info^module_info)
|
|
->
|
|
OutputVars = OutputVars1
|
|
;
|
|
OutputVars = [Var | OutputVars1]
|
|
)
|
|
;
|
|
ArgVars = [], ArgModes = []
|
|
->
|
|
InputVars = [],
|
|
OutputVars = []
|
|
;
|
|
error("get_instantiating_variables - invalid call")
|
|
).
|
|
|
|
% add Alias as an alias for all of Vars
|
|
:- pred add_construction_aliases(var_dep::in, prog_var::in, list(prog_var)::in,
|
|
var_dep::out) is det.
|
|
|
|
add_construction_aliases(UseInf, _, [], UseInf).
|
|
add_construction_aliases(UseInf0, Alias, [Var | Vars], UseInf) :-
|
|
(
|
|
lookup_local_var(UseInf0, Var, VarInf)
|
|
->
|
|
VarInf = unused(VarDep0, ArgDep),
|
|
set__insert(VarDep0, Alias, VarDep),
|
|
map__set(UseInf0, Var, unused(VarDep, ArgDep), UseInf1)
|
|
;
|
|
UseInf1 = UseInf0
|
|
),
|
|
add_construction_aliases(UseInf1, Alias, Vars, UseInf).
|
|
|
|
|
|
:- pred list_case_to_list_goal(list(case)::in, list(hlds_goal)::out) is det.
|
|
|
|
list_case_to_list_goal([], []).
|
|
list_case_to_list_goal([case(_, Goal) | Cases], [Goal | Goals]) :-
|
|
list_case_to_list_goal(Cases, Goals).
|
|
|
|
|
|
:- pred traverse_list_of_goals(traverse_info::in, list(hlds_goal)::in,
|
|
var_dep::in, var_dep::out) is det.
|
|
|
|
traverse_list_of_goals(_, [], UseInf, UseInf).
|
|
traverse_list_of_goals(Info, [Goal - _ | Goals], UseInf0, UseInf) :-
|
|
traverse_goal(Info, Goal, UseInf0, UseInf1),
|
|
traverse_list_of_goals(Info, Goals, UseInf1, UseInf).
|
|
|
|
|
|
%-------------------------------------------------------------------------------
|
|
% Analysis section - do the fixpoint iteration.
|
|
|
|
% Do a full iteration, check if anything changed, if so, repeat.
|
|
:- pred unused_args_pass(pred_proc_list::in, var_usage::in,var_usage::out)
|
|
is det.
|
|
|
|
unused_args_pass(LocalPredProcs, VarUsage0, VarUsage) :-
|
|
unused_args_single_pass(LocalPredProcs, no, Changed,
|
|
VarUsage0, VarUsage1),
|
|
(
|
|
Changed = yes
|
|
->
|
|
unused_args_pass(LocalPredProcs, VarUsage1, VarUsage)
|
|
;
|
|
VarUsage = VarUsage1
|
|
).
|
|
|
|
|
|
% check over all the procedures in a module
|
|
:- pred unused_args_single_pass(pred_proc_list::in, bool::in, bool::out,
|
|
var_usage::in, var_usage::out) is det.
|
|
|
|
unused_args_single_pass([], Changed, Changed, VarUsage, VarUsage).
|
|
unused_args_single_pass([PredProc | Rest], Changed0, Changed,
|
|
VarUsage0, VarUsage) :-
|
|
unused_args_check_proc(PredProc, Changed0, Changed1,
|
|
VarUsage0, VarUsage1),
|
|
unused_args_single_pass(Rest, Changed1, Changed, VarUsage1, VarUsage).
|
|
|
|
|
|
% check a single procedure
|
|
:- pred unused_args_check_proc(pred_proc_id::in, bool::in, bool::out,
|
|
var_usage::in, var_usage::out) is det.
|
|
|
|
unused_args_check_proc(PredProcId, Changed0, Changed, VarUsage0, VarUsage) :-
|
|
map__lookup(VarUsage0, PredProcId, LocalUsages0),
|
|
map__keys(LocalUsages0, Vars),
|
|
unused_args_check_all_vars(VarUsage0, no, LocalChanged, Vars,
|
|
LocalUsages0, LocalUsages),
|
|
(
|
|
LocalChanged = yes
|
|
->
|
|
map__det_update(VarUsage0, PredProcId, LocalUsages, VarUsage),
|
|
Changed = yes
|
|
;
|
|
VarUsage = VarUsage0,
|
|
Changed = Changed0
|
|
).
|
|
|
|
|
|
|
|
% check each var of a procedure in turn
|
|
:- pred unused_args_check_all_vars(var_usage::in, bool::in, bool::out,
|
|
list(prog_var)::in, var_dep::in, var_dep::out) is det.
|
|
|
|
unused_args_check_all_vars(_, Changed, Changed, [], LocalVars, LocalVars).
|
|
unused_args_check_all_vars(VarUsage, Changed0, Changed, [Var| Vars],
|
|
LocalVars0, LocalVars) :-
|
|
(
|
|
lookup_local_var(LocalVars0, Var, Usage)
|
|
->
|
|
Usage = unused(VarDep0, ArgDep0),
|
|
(
|
|
(
|
|
% Check whether any arguments that the
|
|
% current variable depends on are used.
|
|
set__member(Argument, ArgDep0),
|
|
Argument = PredProc - ArgVar,
|
|
var_is_used(PredProc, ArgVar, VarUsage)
|
|
;
|
|
% Check whether any variables that the
|
|
% current variable depends on are used.
|
|
set__member(Var2, VarDep0),
|
|
local_var_is_used(LocalVars0, Var2)
|
|
)
|
|
->
|
|
% set the current variable to used
|
|
set_var_used(Var, LocalVars0, LocalVars1),
|
|
Changed1 = yes
|
|
;
|
|
Changed1 = Changed0,
|
|
LocalVars1 = LocalVars0
|
|
)
|
|
;
|
|
LocalVars1 = LocalVars0,
|
|
Changed1 = Changed0
|
|
),
|
|
unused_args_check_all_vars(VarUsage, Changed1, Changed,
|
|
Vars, LocalVars1, LocalVars).
|
|
|
|
|
|
|
|
:- pred get_unused_arg_info(module_info::in, pred_proc_list::in, var_usage::in,
|
|
unused_arg_info::in, unused_arg_info::out) is det.
|
|
|
|
get_unused_arg_info(_, [], _, UnusedArgInfo, UnusedArgInfo).
|
|
get_unused_arg_info(ModuleInfo, [PredProc | PredProcs], VarUsage,
|
|
UnusedArgInfo0, UnusedArgInfo) :-
|
|
PredProc = proc(PredId, ProcId),
|
|
map__lookup(VarUsage, PredProc, LocalVarUsage),
|
|
module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo),
|
|
proc_info_headvars(ProcInfo, HeadVars),
|
|
get_unused_arg_nos(LocalVarUsage, HeadVars, 1, UnusedArgs),
|
|
map__det_insert(UnusedArgInfo0, PredProc, UnusedArgs, UnusedArgInfo1),
|
|
get_unused_arg_info(ModuleInfo, PredProcs, VarUsage,
|
|
UnusedArgInfo1, UnusedArgInfo).
|
|
|
|
|
|
%-------------------------------------------------------------------------------
|
|
% Fix up the module
|
|
|
|
% information about predicates which have new predicates
|
|
% created for the optimized version
|
|
:- type proc_call_info == map(pred_proc_id, new_proc_info).
|
|
|
|
% new pred_id, proc_id, name, and the indices in the argument
|
|
% vector of the arguments that have been removed.
|
|
:- type new_proc_info --->
|
|
call_info(pred_id, proc_id, sym_name, list(int)).
|
|
|
|
|
|
% Create a new predicate for each procedure which has unused
|
|
% arguments. There are two reasons why we can't throw away
|
|
% the old procedure for non-exported predicates. One is
|
|
% higher-order terms - we can't remove arguments from them
|
|
% without changing their type, so they need the old calling
|
|
% interface.
|
|
% The other is that the next proc_id for a predicate is
|
|
% chosen based on the length of the list of proc_ids.
|
|
:- pred create_new_pred(unused_arg_info::in, pred_proc_id::in,
|
|
proc_call_info::in, proc_call_info::out,
|
|
module_info::in, module_info::out,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
create_new_pred(UnusedArgInfo, proc(PredId, ProcId),
|
|
!ProcCallInfo, !ModuleInfo, !IO) :-
|
|
map__lookup(UnusedArgInfo, proc(PredId, ProcId), UnusedArgs),
|
|
module_info_pred_proc_info(!.ModuleInfo,
|
|
PredId, ProcId, OrigPredInfo, OrigProcInfo),
|
|
(
|
|
UnusedArgs = []
|
|
->
|
|
true
|
|
;
|
|
PredModule = pred_info_module(OrigPredInfo),
|
|
PredName = pred_info_name(OrigPredInfo),
|
|
|
|
globals__io_lookup_bool_option(intermodule_analysis,
|
|
Intermod, !IO),
|
|
( Intermod = yes ->
|
|
module_info_analysis_info(!.ModuleInfo, AnalysisInfo0),
|
|
PredOrFunc = pred_info_is_pred_or_func(OrigPredInfo),
|
|
PredArity = pred_info_arity(OrigPredInfo),
|
|
ModuleId = module_name_to_module_id(PredModule),
|
|
FuncId = pred_or_func_name_arity_to_func_id(PredOrFunc,
|
|
PredName, PredArity, ProcId),
|
|
FuncInfo = unused_args_func_info(PredArity),
|
|
|
|
lookup_results(ModuleId, FuncId, FuncInfo,
|
|
IntermodResults0, AnalysisInfo0, AnalysisInfo1,
|
|
!IO),
|
|
IntermodResults1 = list__map(
|
|
(func(any_call - unused_args(ResArgs)) = ResArgs),
|
|
IntermodResults0),
|
|
( list__member(UnusedArgs, IntermodResults1) ->
|
|
AnalysisInfo = AnalysisInfo1
|
|
;
|
|
analysis__record_result(ModuleId,
|
|
FuncId, FuncInfo, any_call,
|
|
unused_args(UnusedArgs),
|
|
AnalysisInfo1, AnalysisInfo)
|
|
),
|
|
module_info_set_analysis_info(AnalysisInfo,
|
|
!ModuleInfo),
|
|
|
|
%
|
|
% XXX Mark versions which have more unused arguments
|
|
% than what we have computed here as invalid
|
|
% in the AnalysisInfo, so that modules which use
|
|
% those versions can be recompiled.
|
|
%
|
|
IntermodResults = list__filter(
|
|
(pred(VersionUnusedArgs::in) is semidet :-
|
|
VersionUnusedArgs \= UnusedArgs,
|
|
set__subset(
|
|
sorted_list_to_set(VersionUnusedArgs),
|
|
sorted_list_to_set(UnusedArgs))
|
|
), IntermodResults1)
|
|
;
|
|
IntermodResults0 = [],
|
|
IntermodResults = []
|
|
),
|
|
pred_info_import_status(OrigPredInfo, Status0),
|
|
(
|
|
Status0 = opt_imported,
|
|
IntermodResults0 = [_|_],
|
|
IntermodResults = []
|
|
->
|
|
% If this predicate is from a .opt file, and
|
|
% no more arguments have been removed than in the
|
|
% original module, then leave the import status
|
|
% as opt_imported so that dead_proc_elim will remove
|
|
% it if no other optimization is performed on it.
|
|
Status = opt_imported
|
|
;
|
|
status_is_exported(Status0, yes)
|
|
->
|
|
% This specialized version of the predicate will be
|
|
% declared in the analysis file for this module so
|
|
% it must be exported.
|
|
Status = Status0
|
|
;
|
|
Status = local
|
|
),
|
|
make_new_pred_info(!.ModuleInfo, UnusedArgs, Status,
|
|
proc(PredId, ProcId), OrigPredInfo, NewPredInfo0),
|
|
NewPredName = pred_info_name(NewPredInfo0),
|
|
pred_info_procedures(NewPredInfo0, NewProcs0),
|
|
|
|
% Assign the old procedure to a new predicate, which
|
|
% will be fixed up in fixup_unused_args.
|
|
map__set(NewProcs0, ProcId, OrigProcInfo, NewProcs),
|
|
pred_info_set_procedures(NewProcs, NewPredInfo0, NewPredInfo),
|
|
|
|
% add the new proc to the pred table
|
|
module_info_get_predicate_table(!.ModuleInfo, PredTable0),
|
|
predicate_table_insert(PredTable0,
|
|
NewPredInfo, NewPredId, PredTable),
|
|
module_info_set_predicate_table(PredTable, !ModuleInfo),
|
|
|
|
% add the new proc to the proc_call_info map
|
|
PredSymName = qualified(PredModule, NewPredName),
|
|
map__det_insert(!.ProcCallInfo, proc(PredId, ProcId),
|
|
call_info(NewPredId, ProcId, PredSymName, UnusedArgs),
|
|
!:ProcCallInfo),
|
|
|
|
% add a forwarding predicate with the
|
|
% original interface.
|
|
create_call_goal(UnusedArgs, NewPredId, ProcId, PredModule,
|
|
NewPredName, OrigProcInfo, ForwardingProcInfo),
|
|
module_info_set_pred_proc_info(PredId, ProcId, OrigPredInfo,
|
|
ForwardingProcInfo, !ModuleInfo),
|
|
|
|
% add forwarding predicates for results
|
|
% produced in previous compilations.
|
|
list__foldl(
|
|
make_intermod_proc(PredId, NewPredId, ProcId,
|
|
NewPredName, OrigPredInfo, OrigProcInfo,
|
|
UnusedArgs),
|
|
IntermodResults, !ModuleInfo)
|
|
).
|
|
|
|
:- pred make_intermod_proc(pred_id::in, pred_id::in, proc_id::in, string::in,
|
|
pred_info::in, proc_info::in, list(int)::in, list(int)::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
make_intermod_proc(PredId, NewPredId, ProcId, NewPredName,
|
|
OrigPredInfo, OrigProcInfo, UnusedArgs,
|
|
UnusedArgs2, !ModuleInfo) :-
|
|
% Add an exported predicate with the number of removed
|
|
% arguments promised in the analysis file which just calls
|
|
% the new predicate.
|
|
make_new_pred_info(!.ModuleInfo, UnusedArgs2, exported,
|
|
proc(PredId, ProcId), OrigPredInfo, ExtraPredInfo0),
|
|
PredModule = pred_info_module(OrigPredInfo),
|
|
create_call_goal(UnusedArgs, NewPredId, ProcId,
|
|
PredModule, NewPredName, OrigProcInfo, ExtraProc0),
|
|
proc_info_headvars(OrigProcInfo, HeadVars0),
|
|
remove_listof_elements(HeadVars0, 1, UnusedArgs2, IntermodHeadVars),
|
|
proc_info_set_headvars(IntermodHeadVars, ExtraProc0, ExtraProc1),
|
|
proc_info_argmodes(OrigProcInfo, ArgModes0),
|
|
remove_listof_elements(ArgModes0, 1, UnusedArgs2, IntermodArgModes),
|
|
proc_info_set_argmodes(IntermodArgModes, ExtraProc1, ExtraProc),
|
|
pred_info_procedures(ExtraPredInfo0, ExtraProcs0),
|
|
map__set(ExtraProcs0, ProcId, ExtraProc, ExtraProcs),
|
|
pred_info_set_procedures(ExtraProcs, ExtraPredInfo0, ExtraPredInfo),
|
|
module_info_get_predicate_table(!.ModuleInfo, PredTable0),
|
|
predicate_table_insert(PredTable0, ExtraPredInfo, _, PredTable),
|
|
module_info_set_predicate_table(PredTable, !ModuleInfo).
|
|
|
|
:- pred make_new_pred_info(module_info::in, list(int)::in, import_status::in,
|
|
pred_proc_id::in, pred_info::in, pred_info::out) is det.
|
|
|
|
make_new_pred_info(ModuleInfo, UnusedArgs, Status, proc(_PredId, ProcId),
|
|
!PredInfo) :-
|
|
PredModule = pred_info_module(!.PredInfo),
|
|
Name0 = pred_info_name(!.PredInfo),
|
|
PredOrFunc = pred_info_is_pred_or_func(!.PredInfo),
|
|
pred_info_arg_types(!.PredInfo, Tvars, ExistQVars, ArgTypes0),
|
|
pred_info_get_maybe_special_pred(!.PredInfo, MaybeSpecial),
|
|
% create a unique new pred name using the old proc_id
|
|
(
|
|
string__prefix(Name0, "__"),
|
|
\+ string__prefix(Name0, "__LambdaGoal__")
|
|
->
|
|
(
|
|
% fix up special pred names
|
|
MaybeSpecial = yes(_SpecialId - TypeCtor)
|
|
->
|
|
type_util__type_ctor_module(ModuleInfo,
|
|
TypeCtor, TypeModule),
|
|
type_util__type_ctor_name(ModuleInfo,
|
|
TypeCtor, TypeName),
|
|
type_util__type_ctor_arity(ModuleInfo,
|
|
TypeCtor, TypeArity),
|
|
string__int_to_string(TypeArity, TypeAr),
|
|
prog_out__sym_name_to_string(TypeModule,
|
|
TypeModuleString0),
|
|
string__replace_all(TypeModuleString0, ".", "__",
|
|
TypeModuleString1),
|
|
string__replace_all(TypeModuleString1, ":", "__",
|
|
TypeModuleString),
|
|
string__append_list([Name0, "_", TypeModuleString,
|
|
"__", TypeName, "_", TypeAr], Name1)
|
|
;
|
|
% The special predicate has already been specialised.
|
|
Name1 = Name0
|
|
)
|
|
;
|
|
Name1 = Name0
|
|
),
|
|
make_pred_name(PredModule, "UnusedArgs", yes(PredOrFunc),
|
|
Name1, unused_args(UnusedArgs), Name2),
|
|
% The mode number is included because we want to
|
|
% avoid the creation of more than one predicate with the same
|
|
% name if more than one mode of a predicate is specialized.
|
|
% Since the names of e.g. deep profiling proc_static structures
|
|
% are derived from the names of predicates, duplicate predicate
|
|
% names lead to duplicate global variable names and hence to
|
|
% link errors.
|
|
proc_id_to_int(ProcId, ProcInt),
|
|
add_sym_name_suffix(Name2, "_" ++ int_to_string(ProcInt), Name),
|
|
Arity = pred_info_arity(!.PredInfo),
|
|
pred_info_typevarset(!.PredInfo, TypeVars),
|
|
remove_listof_elements(ArgTypes0, 1, UnusedArgs, ArgTypes),
|
|
pred_info_context(!.PredInfo, Context),
|
|
pred_info_clauses_info(!.PredInfo, ClausesInfo),
|
|
pred_info_get_markers(!.PredInfo, Markers),
|
|
pred_info_get_goal_type(!.PredInfo, GoalType),
|
|
pred_info_get_class_context(!.PredInfo, ClassContext),
|
|
pred_info_get_aditi_owner(!.PredInfo, Owner),
|
|
map__init(EmptyProofs),
|
|
pred_info_init(PredModule, Name, Arity, Tvars, ExistQVars, ArgTypes,
|
|
true, Context, ClausesInfo, Status, Markers, GoalType,
|
|
PredOrFunc, ClassContext, EmptyProofs, Owner, !:PredInfo),
|
|
pred_info_set_typevarset(TypeVars, !PredInfo).
|
|
|
|
% Replace the goal in the procedure with one to call the given
|
|
% pred_id and proc_id.
|
|
:- pred create_call_goal(list(int)::in, pred_id::in, proc_id::in,
|
|
module_name::in, string::in, proc_info::in, proc_info::out) is det.
|
|
|
|
create_call_goal(UnusedArgs, NewPredId, NewProcId, PredModule,
|
|
PredName, !OldProc) :-
|
|
proc_info_headvars(!.OldProc, HeadVars),
|
|
proc_info_goal(!.OldProc, Goal0),
|
|
Goal0 = _GoalExpr - GoalInfo0,
|
|
|
|
% We must use the interface determinism for determining
|
|
% the determinism of the version of the goal with its
|
|
% arguments removed, not the actual determinism of the
|
|
% body is it may be more lax, which will lead to code
|
|
% gen problems.
|
|
proc_info_interface_determinism(!.OldProc, Determinism),
|
|
goal_info_set_determinism(GoalInfo0, Determinism, GoalInfo1),
|
|
|
|
proc_info_vartypes(!.OldProc, VarTypes0),
|
|
set__list_to_set(HeadVars, NonLocals),
|
|
map__apply_to_list(HeadVars, VarTypes0, VarTypeList),
|
|
map__from_corresponding_lists(HeadVars, VarTypeList, VarTypes1),
|
|
% the varset should probably be fixed up, but it
|
|
% shouldn't make too much difference
|
|
proc_info_varset(!.OldProc, Varset0),
|
|
remove_listof_elements(HeadVars, 1, UnusedArgs, NewHeadVars),
|
|
GoalExpr = call(NewPredId, NewProcId, NewHeadVars,
|
|
not_builtin, no, qualified(PredModule, PredName)),
|
|
Goal1 = GoalExpr - GoalInfo1,
|
|
implicitly_quantify_goal(NonLocals, _, Goal1, Goal, Varset0, Varset,
|
|
VarTypes1, VarTypes),
|
|
proc_info_set_goal(Goal, !OldProc),
|
|
proc_info_set_varset(Varset, !OldProc),
|
|
proc_info_set_vartypes(VarTypes, !OldProc).
|
|
|
|
% Create a pred_info for an imported pred with a pragma unused_args
|
|
% in the .opt file.
|
|
:- pred make_imported_unused_args_pred_info(pred_proc_id::in, list(int)::in,
|
|
proc_call_info::in, proc_call_info::out,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
make_imported_unused_args_pred_info(OptProc, UnusedArgs,
|
|
ProcCallInfo0, ProcCallInfo, !ModuleInfo) :-
|
|
OptProc = proc(PredId, ProcId),
|
|
module_info_pred_proc_info(!.ModuleInfo,
|
|
PredId, ProcId, PredInfo0, ProcInfo0),
|
|
make_new_pred_info(!.ModuleInfo, UnusedArgs, imported(interface),
|
|
OptProc, PredInfo0, NewPredInfo0),
|
|
pred_info_procedures(NewPredInfo0, NewProcs0),
|
|
|
|
% Assign the old procedure to a new predicate.
|
|
proc_info_headvars(ProcInfo0, HeadVars0),
|
|
remove_listof_elements(HeadVars0, 1, UnusedArgs, HeadVars),
|
|
proc_info_set_headvars(HeadVars, ProcInfo0, HeadVarsProcInfo),
|
|
proc_info_argmodes(HeadVarsProcInfo, ArgModes0),
|
|
remove_listof_elements(ArgModes0, 1, UnusedArgs, ArgModes),
|
|
proc_info_set_argmodes(ArgModes, ProcInfo0, ProcInfo),
|
|
map__set(NewProcs0, ProcId, ProcInfo, NewProcs),
|
|
pred_info_set_procedures(NewProcs, NewPredInfo0, NewPredInfo),
|
|
|
|
% Add the new proc to the pred table.
|
|
module_info_get_predicate_table(!.ModuleInfo, PredTable0),
|
|
predicate_table_insert(PredTable0, NewPredInfo, NewPredId, PredTable1),
|
|
module_info_set_predicate_table(PredTable1, !ModuleInfo),
|
|
PredModule = pred_info_module(NewPredInfo),
|
|
PredName = pred_info_name(NewPredInfo),
|
|
PredSymName = qualified(PredModule, PredName),
|
|
% Add the new proc to the proc_call_info map.
|
|
map__det_insert(ProcCallInfo0, proc(PredId, ProcId),
|
|
call_info(NewPredId, ProcId, PredSymName, UnusedArgs),
|
|
ProcCallInfo).
|
|
|
|
:- pred remove_listof_elements(list(T)::in, int::in, list(int)::in,
|
|
list(T)::out) is det.
|
|
|
|
remove_listof_elements(List0, ArgNo, ElemsToRemove, List) :-
|
|
(
|
|
ElemsToRemove = []
|
|
->
|
|
List = List0
|
|
;
|
|
(
|
|
List0 = [Head | Tail],
|
|
NextArg = ArgNo + 1,
|
|
(
|
|
list__member(ArgNo, ElemsToRemove)
|
|
->
|
|
List = List1
|
|
;
|
|
List = [Head | List1]
|
|
),
|
|
remove_listof_elements(Tail, NextArg,
|
|
ElemsToRemove, List1)
|
|
;
|
|
List0 = [],
|
|
List = List0
|
|
)
|
|
).
|
|
|
|
|
|
:- pred get_unused_arg_nos(var_dep::in, list(prog_var)::in, int::in,
|
|
list(int)::out) is det.
|
|
|
|
get_unused_arg_nos(_, [], _, []).
|
|
get_unused_arg_nos(LocalVars, [HeadVar | HeadVars], ArgNo, UnusedArgs) :-
|
|
NextArg = ArgNo + 1,
|
|
(
|
|
map__contains(LocalVars, HeadVar)
|
|
->
|
|
UnusedArgs = [ArgNo | UnusedArgs1]
|
|
;
|
|
UnusedArgs = UnusedArgs1
|
|
),
|
|
get_unused_arg_nos(LocalVars, HeadVars, NextArg, UnusedArgs1).
|
|
|
|
|
|
% note - we should probably remove unused variables from
|
|
% the type map
|
|
:- pred fixup_unused_args(var_usage::in, pred_proc_list::in, proc_call_info::in,
|
|
module_info::in, module_info::out, bool::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
fixup_unused_args(_, [], _, Mod, Mod, _) --> [].
|
|
fixup_unused_args(VarUsage, [PredProc | PredProcs], ProcCallInfo,
|
|
ModuleInfo0, ModuleInfo, VeryVerbose) -->
|
|
(
|
|
{ VeryVerbose = yes }
|
|
->
|
|
{ PredProc = proc(PredId, ProcId) },
|
|
io__write_string("% Fixing up `"),
|
|
{ predicate_name(ModuleInfo0, PredId, Name) },
|
|
{ predicate_arity(ModuleInfo0, PredId, Arity) },
|
|
{ proc_id_to_int(ProcId, ProcInt) },
|
|
io__write_string(Name),
|
|
io__write_string("/"),
|
|
io__write_int(Arity),
|
|
io__write_string("' in mode "),
|
|
io__write_int(ProcInt),
|
|
io__write_char('\n')
|
|
;
|
|
[]
|
|
),
|
|
{ do_fixup_unused_args(VarUsage, PredProc, ProcCallInfo, ModuleInfo0,
|
|
ModuleInfo1) },
|
|
fixup_unused_args(VarUsage, PredProcs, ProcCallInfo, ModuleInfo1,
|
|
ModuleInfo, VeryVerbose).
|
|
|
|
:- pred do_fixup_unused_args(var_usage::in, pred_proc_id::in,
|
|
proc_call_info::in, module_info::in, module_info::out) is det.
|
|
|
|
do_fixup_unused_args(VarUsage, proc(OldPredId, OldProcId), ProcCallInfo,
|
|
Mod0, Mod) :-
|
|
(
|
|
% work out which proc we should be fixing up
|
|
map__search(ProcCallInfo, proc(OldPredId, OldProcId),
|
|
call_info(NewPredId, NewProcId, _, UnusedArgs0))
|
|
->
|
|
UnusedArgs = UnusedArgs0,
|
|
PredId = NewPredId,
|
|
ProcId = NewProcId
|
|
;
|
|
UnusedArgs = [],
|
|
PredId = OldPredId,
|
|
ProcId = OldProcId
|
|
),
|
|
map__lookup(VarUsage, proc(OldPredId, OldProcId), UsageInfos),
|
|
map__keys(UsageInfos, UnusedVars),
|
|
module_info_pred_proc_info(Mod0, PredId, ProcId, PredInfo0, ProcInfo0),
|
|
proc_info_vartypes(ProcInfo0, VarTypes0),
|
|
module_info_preds(Mod0, Preds0),
|
|
pred_info_procedures(PredInfo0, Procs0),
|
|
|
|
proc_info_headvars(ProcInfo0, HeadVars0),
|
|
proc_info_argmodes(ProcInfo0, ArgModes0),
|
|
proc_info_varset(ProcInfo0, Varset0),
|
|
proc_info_goal(ProcInfo0, Goal0),
|
|
remove_listof_elements(HeadVars0, 1, UnusedArgs, HeadVars),
|
|
remove_listof_elements(ArgModes0, 1, UnusedArgs, ArgModes),
|
|
proc_info_set_headvars(HeadVars, ProcInfo0, FixedProc1),
|
|
proc_info_set_argmodes(ArgModes, FixedProc1, FixedProc2),
|
|
|
|
% remove unused vars from goal
|
|
fixup_goal(Mod0, UnusedVars, ProcCallInfo, Changed, Goal0, Goal1),
|
|
(
|
|
Changed = yes,
|
|
% if anything has changed, rerun quantification
|
|
set__list_to_set(HeadVars, NonLocals),
|
|
implicitly_quantify_goal(NonLocals, _, Goal1, Goal,
|
|
Varset0, Varset, VarTypes0, VarTypes),
|
|
proc_info_set_goal(Goal, FixedProc2, FixedProc3),
|
|
proc_info_set_varset(Varset, FixedProc3, FixedProc4),
|
|
proc_info_set_vartypes(VarTypes, FixedProc4, FixedProc5)
|
|
;
|
|
Changed = no,
|
|
proc_info_set_vartypes(VarTypes0, FixedProc2, FixedProc5)
|
|
),
|
|
map__set(Procs0, ProcId, FixedProc5, Procs),
|
|
pred_info_set_procedures(Procs, PredInfo0, PredInfo),
|
|
map__set(Preds0, PredId, PredInfo, Preds),
|
|
module_info_set_preds(Preds, Mod0, Mod).
|
|
|
|
% this is the important bit of the transformation
|
|
:- pred fixup_goal(module_info::in, list(prog_var)::in, proc_call_info::in,
|
|
bool::out, hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
fixup_goal(ModuleInfo, UnusedVars, ProcCallInfo, Changed, Goal0, Goal) :-
|
|
fixup_goal_expr(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
Changed, Goal0, Goal1),
|
|
Goal1 = GoalExpr - GoalInfo0,
|
|
(
|
|
Changed = yes
|
|
->
|
|
fixup_goal_info(UnusedVars, GoalInfo0, GoalInfo)
|
|
;
|
|
GoalInfo = GoalInfo0
|
|
),
|
|
Goal = GoalExpr - GoalInfo.
|
|
|
|
|
|
:- pred fixup_goal_expr(module_info::in, list(prog_var)::in, proc_call_info::in,
|
|
bool::out, hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
fixup_goal_expr(ModuleInfo, UnusedVars, ProcCallInfo, Changed,
|
|
conj(Goals0) - GoalInfo, conj(Goals) - GoalInfo) :-
|
|
fixup_conjuncts(ModuleInfo, UnusedVars, ProcCallInfo, no,
|
|
Changed, Goals0, Goals).
|
|
|
|
fixup_goal_expr(ModuleInfo, UnusedVars, ProcCallInfo, Changed,
|
|
par_conj(Goals0) - GoalInfo,
|
|
par_conj(Goals) - GoalInfo) :-
|
|
fixup_conjuncts(ModuleInfo, UnusedVars, ProcCallInfo, no,
|
|
Changed, Goals0, Goals).
|
|
|
|
fixup_goal_expr(ModuleInfo, UnusedVars, ProcCallInfo, Changed,
|
|
disj(Goals0) - GoalInfo, disj(Goals) - GoalInfo) :-
|
|
fixup_disjuncts(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
no, Changed, Goals0, Goals).
|
|
|
|
fixup_goal_expr(ModuleInfo, UnusedVars, ProcCallInfo, Changed,
|
|
not(NegGoal0) - GoalInfo, not(NegGoal) - GoalInfo) :-
|
|
fixup_goal(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
Changed, NegGoal0, NegGoal).
|
|
|
|
fixup_goal_expr(ModuleInfo, UnusedVars, ProcCallInfo, Changed,
|
|
switch(Var, CanFail, Cases0) - GoalInfo,
|
|
switch(Var, CanFail, Cases) - GoalInfo) :-
|
|
fixup_cases(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
no, Changed, Cases0, Cases).
|
|
|
|
fixup_goal_expr(ModuleInfo, UnusedVars, ProcCallInfo, Changed,
|
|
if_then_else(Vars, Cond0, Then0, Else0) - GoalInfo,
|
|
if_then_else(Vars, Cond, Then, Else) - GoalInfo) :-
|
|
fixup_goal(ModuleInfo, UnusedVars, ProcCallInfo, Changed1, Cond0, Cond),
|
|
fixup_goal(ModuleInfo, UnusedVars, ProcCallInfo, Changed2, Then0, Then),
|
|
fixup_goal(ModuleInfo, UnusedVars, ProcCallInfo, Changed3, Else0, Else),
|
|
bool__or_list([Changed1, Changed2, Changed3], Changed).
|
|
|
|
fixup_goal_expr(ModuleInfo, UnusedVars, ProcCallInfo, Changed,
|
|
some(Vars, CanRemove, SubGoal0) - GoalInfo,
|
|
some(Vars, CanRemove, SubGoal) - GoalInfo) :-
|
|
fixup_goal(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
Changed, SubGoal0, SubGoal).
|
|
|
|
fixup_goal_expr(_ModuleInfo, _UnusedVars, ProcCallInfo, Changed,
|
|
call(PredId0, ProcId0, ArgVars0, B, C, Name0) - GoalInfo,
|
|
call(PredId, ProcId, ArgVars, B, C, Name) - GoalInfo) :-
|
|
(
|
|
map__search(ProcCallInfo, proc(PredId0, ProcId0),
|
|
call_info(NewPredId, NewProcId, NewName, UnusedArgs))
|
|
->
|
|
Changed = yes,
|
|
remove_listof_elements(ArgVars0, 1, UnusedArgs, ArgVars),
|
|
PredId = NewPredId,
|
|
ProcId = NewProcId,
|
|
Name = NewName
|
|
;
|
|
Changed = no,
|
|
PredId = PredId0,
|
|
ProcId = ProcId0,
|
|
ArgVars = ArgVars0,
|
|
Name = Name0
|
|
).
|
|
|
|
fixup_goal_expr(ModuleInfo, UnusedVars, _ProcCallInfo,
|
|
Changed, GoalExpr0 - GoalInfo, GoalExpr - GoalInfo) :-
|
|
GoalExpr0 = unify(Var, Rhs, Mode, Unify0, Context),
|
|
(
|
|
fixup_unify(ModuleInfo, UnusedVars, Changed0, Unify0, Unify)
|
|
->
|
|
GoalExpr = unify(Var, Rhs, Mode, Unify, Context),
|
|
Changed = Changed0
|
|
;
|
|
GoalExpr = conj([]),
|
|
Changed = yes
|
|
).
|
|
|
|
fixup_goal_expr(_ModuleInfo, _UnusedVars, _ProcCallInfo, no,
|
|
GoalExpr - GoalInfo, GoalExpr - GoalInfo) :-
|
|
GoalExpr = generic_call(_, _, _, _).
|
|
|
|
fixup_goal_expr(_ModuleInfo, _UnusedVars, _ProcCallInfo, no,
|
|
GoalExpr - GoalInfo, GoalExpr - GoalInfo) :-
|
|
GoalExpr = foreign_proc(_, _, _, _, _, _, _).
|
|
|
|
fixup_goal_expr(_, _, _, _, shorthand(_) - _, _) :-
|
|
% these should have been expanded out by now
|
|
error("fixup_goal_expr: unexpected shorthand").
|
|
|
|
% Remove useless unifications from a list of conjuncts.
|
|
:- pred fixup_conjuncts(module_info::in, list(prog_var)::in, proc_call_info::in,
|
|
bool::in, bool::out, hlds_goals::in, hlds_goals::out) is det.
|
|
|
|
fixup_conjuncts(_, _, _, Changed, Changed, [], []).
|
|
fixup_conjuncts(ModuleInfo, UnusedVars, ProcCallInfo, Changed0, Changed,
|
|
[Goal0 | Goals0], Goals) :-
|
|
fixup_goal(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
LocalChanged, Goal0, Goal),
|
|
(
|
|
LocalChanged = yes
|
|
->
|
|
Changed1 = yes
|
|
;
|
|
Changed1 = Changed0
|
|
),
|
|
(
|
|
% replacing a goal with conj([]) signals that it is
|
|
% no longer needed
|
|
Goal = conj([]) - _
|
|
->
|
|
Goals = Goals1
|
|
;
|
|
Goals = [Goal | Goals1]
|
|
),
|
|
fixup_conjuncts(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
Changed1, Changed, Goals0, Goals1).
|
|
|
|
|
|
% We can't remove unused goals from the list of disjuncts as we do
|
|
% for conjuncts, since that would change the determinism of
|
|
% the goal.
|
|
:- pred fixup_disjuncts(module_info::in, list(prog_var)::in, proc_call_info::in,
|
|
bool::in, bool::out, hlds_goals::in, hlds_goals::out) is det.
|
|
|
|
fixup_disjuncts(_, _, _, Changed, Changed, [], []).
|
|
fixup_disjuncts(ModuleInfo, UnusedVars, ProcCallInfo, Changed0, Changed,
|
|
[Goal0 | Goals0], [Goal | Goals]) :-
|
|
fixup_goal(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
LocalChanged, Goal0, Goal),
|
|
(
|
|
LocalChanged = yes
|
|
->
|
|
Changed1 = yes
|
|
;
|
|
Changed1 = Changed0
|
|
),
|
|
fixup_disjuncts(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
Changed1, Changed, Goals0, Goals).
|
|
|
|
:- pred fixup_cases(module_info::in, list(prog_var)::in, proc_call_info::in,
|
|
bool::in, bool::out, list(case)::in, list(case)::out) is det.
|
|
|
|
fixup_cases(_, _, _, Changed, Changed, [], []).
|
|
fixup_cases(ModuleInfo, UnusedVars, ProcCallInfo, Changed0, Changed,
|
|
[case(ConsId, Goal0) | Cases0], [case(ConsId, Goal) | Cases]) :-
|
|
fixup_goal(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
LocalChanged, Goal0, Goal),
|
|
(
|
|
LocalChanged = yes
|
|
->
|
|
Changed1 = yes
|
|
;
|
|
Changed1 = Changed0
|
|
),
|
|
fixup_cases(ModuleInfo, UnusedVars, ProcCallInfo,
|
|
Changed1, Changed, Cases0, Cases).
|
|
|
|
|
|
% fix up a unification, fail if the unification is no
|
|
% longer needed
|
|
:- pred fixup_unify(module_info::in, list(prog_var)::in, bool::out,
|
|
unification::in, unification::out) is semidet.
|
|
|
|
% a simple test doesn't have any unused vars to fixup
|
|
fixup_unify(_, _UnusedVars, no, simple_test(A, B), simple_test(A, B)).
|
|
|
|
% Var1 unused => we don't need the assignment
|
|
% Var2 unused => Var1 unused
|
|
fixup_unify(_, UnusedVars, no, assign(Var1, Var2), assign(Var1, Var2)) :-
|
|
\+ list__member(Var1, UnusedVars).
|
|
|
|
% LVar unused => we don't need the unification
|
|
fixup_unify(_, UnusedVars, no, Unify, Unify) :-
|
|
Unify = construct(LVar, _, _, _, _, _, _),
|
|
\+ list__member(LVar, UnusedVars).
|
|
|
|
fixup_unify(ModuleInfo, UnusedVars, Changed, Unify, Unify) :-
|
|
Unify = deconstruct(LVar, _, ArgVars, ArgModes, CanFail, _CanCGC),
|
|
\+ list__member(LVar, UnusedVars),
|
|
(
|
|
% are any of the args unused, if so we need to
|
|
% to fix up the goal_info
|
|
CanFail = cannot_fail,
|
|
check_deconstruct_args(ModuleInfo, UnusedVars, ArgVars,
|
|
ArgModes, Changed, no)
|
|
;
|
|
CanFail = can_fail,
|
|
Changed = no
|
|
).
|
|
|
|
% These should be transformed into calls by polymorphism.m.
|
|
fixup_unify(_, _, _, complicated_unify(_, _, _), _) :-
|
|
error("unused_args:fixup_goal : complicated unify").
|
|
|
|
% Check if any of the arguments of a deconstruction are unused, if
|
|
% so Changed will be yes and quantification will be rerun. Fails if
|
|
% none of the arguments are used. Arguments which further instantiate
|
|
% the deconstructed variable are ignored in this.
|
|
:- pred check_deconstruct_args(module_info::in, list(prog_var)::in,
|
|
list(prog_var)::in, list(uni_mode)::in,
|
|
bool::out, bool::in) is semidet.
|
|
|
|
check_deconstruct_args(ModuleInfo, UnusedVars, Args, Modes, Changed, Used) :-
|
|
(
|
|
Args = [ArgVar | ArgVars], Modes = [ArgMode | ArgModes]
|
|
->
|
|
(
|
|
ArgMode = ((Inst1 - Inst2) -> _),
|
|
mode_is_output(ModuleInfo, (Inst1 -> Inst2)),
|
|
list__member(ArgVar, UnusedVars)
|
|
->
|
|
check_deconstruct_args(ModuleInfo, UnusedVars,
|
|
ArgVars, ArgModes, _, Used),
|
|
Changed = yes
|
|
;
|
|
check_deconstruct_args(ModuleInfo, UnusedVars,
|
|
ArgVars, ArgModes, Changed, yes)
|
|
)
|
|
;
|
|
Args = [], Modes = []
|
|
->
|
|
Changed = no,
|
|
Used = yes
|
|
;
|
|
error("check_deconstruct_args - invalid call")
|
|
).
|
|
|
|
% Remove unused vars from the instmap_delta, quantification fixes
|
|
% up the rest.
|
|
:- pred fixup_goal_info(list(prog_var)::in, hlds_goal_info::in,
|
|
hlds_goal_info::out) is det.
|
|
|
|
fixup_goal_info(UnusedVars, GoalInfo0, GoalInfo) :-
|
|
goal_info_get_instmap_delta(GoalInfo0, InstMap0),
|
|
instmap_delta_delete_vars(InstMap0, UnusedVars, InstMap),
|
|
goal_info_set_instmap_delta(GoalInfo0, InstMap, GoalInfo).
|
|
|
|
%-------------------------------------------------------------------------------
|
|
|
|
% Except for type_infos, all args that are unused
|
|
% in one mode of a predicate should be unused in all of the
|
|
% modes of a predicate, so we only need to put out one warning
|
|
% for each predicate.
|
|
:- pred output_warnings_and_pragmas(module_info::in, unused_arg_info::in,
|
|
maybe(io__output_stream)::in, bool::in, pred_proc_list::in,
|
|
set(pred_id)::in, io__state::di, io__state::uo) is det.
|
|
|
|
output_warnings_and_pragmas(_, _, _, _, [], _) --> [].
|
|
output_warnings_and_pragmas(ModuleInfo, UnusedArgInfo, WriteOptPragmas,
|
|
DoWarn, [proc(PredId, ProcId) | Rest], WarnedPredIds0) -->
|
|
(
|
|
{ map__search(UnusedArgInfo, proc(PredId, ProcId),
|
|
UnusedArgs) }
|
|
->
|
|
{ module_info_pred_info(ModuleInfo, PredId, PredInfo) },
|
|
(
|
|
{
|
|
Name = pred_info_name(PredInfo),
|
|
\+ pred_info_is_imported(PredInfo),
|
|
\+ pred_info_import_status(PredInfo, opt_imported),
|
|
% Don't warn about builtins
|
|
% that have unused arguments.
|
|
\+ pred_info_is_builtin(PredInfo),
|
|
\+ is_unify_or_compare_pred(PredInfo),
|
|
% Don't warn about stubs for procedures
|
|
% with no clauses -- in that case,
|
|
% we *expect* that none of the arguments
|
|
% will be used,
|
|
pred_info_get_markers(PredInfo, Markers),
|
|
\+ check_marker(Markers, stub),
|
|
% Don't warn about lambda expressions
|
|
% not using arguments. (The warning
|
|
% message for these doesn't contain
|
|
% context, so it's useless)
|
|
\+ string__sub_string_search(Name,
|
|
"__LambdaGoal__", _),
|
|
\+ (
|
|
% don't warn for a specialized version
|
|
string__sub_string_search(Name, "__ho",
|
|
Position),
|
|
string__length(Name, Length),
|
|
IdLen = Length - Position - 4,
|
|
string__right(Name, IdLen, Id),
|
|
string__to_int(Id, _)
|
|
)
|
|
}
|
|
->
|
|
write_unused_args_to_opt_file(WriteOptPragmas,
|
|
PredInfo, ProcId, UnusedArgs),
|
|
maybe_warn_unused_args(DoWarn, ModuleInfo, PredInfo,
|
|
PredId, ProcId, UnusedArgs,
|
|
WarnedPredIds0, WarnedPredIds1)
|
|
;
|
|
{ WarnedPredIds1 = WarnedPredIds0 }
|
|
)
|
|
;
|
|
{ WarnedPredIds1 = WarnedPredIds0 }
|
|
),
|
|
output_warnings_and_pragmas(ModuleInfo, UnusedArgInfo,
|
|
WriteOptPragmas, DoWarn, Rest, WarnedPredIds1).
|
|
|
|
|
|
:- pred write_unused_args_to_opt_file(maybe(io__output_stream)::in,
|
|
pred_info::in, proc_id::in, list(int)::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
write_unused_args_to_opt_file(no, _, _, _) --> [].
|
|
write_unused_args_to_opt_file(yes(OptStream), PredInfo, ProcId, UnusedArgs) -->
|
|
(
|
|
( { pred_info_is_exported(PredInfo) }
|
|
; { pred_info_is_opt_exported(PredInfo) }
|
|
; { pred_info_is_exported_to_submodules(PredInfo) }
|
|
),
|
|
{ UnusedArgs \= [] }
|
|
->
|
|
{ Module = pred_info_module(PredInfo) },
|
|
{ Name = pred_info_name(PredInfo) },
|
|
{ Arity = pred_info_arity(PredInfo) },
|
|
{ PredOrFunc = pred_info_is_pred_or_func(PredInfo) },
|
|
io__set_output_stream(OptStream, OldOutput),
|
|
{ proc_id_to_int(ProcId, ModeNum) },
|
|
mercury_output_pragma_unused_args(PredOrFunc,
|
|
qualified(Module, Name), Arity, ModeNum, UnusedArgs),
|
|
io__set_output_stream(OldOutput, _)
|
|
;
|
|
[]
|
|
).
|
|
|
|
:- pred maybe_warn_unused_args(bool::in, module_info::in, pred_info::in,
|
|
pred_id::in, proc_id::in, list(int)::in, set(pred_id)::in,
|
|
set(pred_id)::out, io__state::di, io__state::uo) is det.
|
|
|
|
|
|
maybe_warn_unused_args(no, _, _, _, _, _, WarnedPredIds, WarnedPredIds) --> [].
|
|
maybe_warn_unused_args(yes, _ModuleInfo, PredInfo, PredId, ProcId,
|
|
UnusedArgs0, WarnedPredIds0, WarnedPredIds) -->
|
|
( { set__member(PredId, WarnedPredIds0) } ->
|
|
{ WarnedPredIds = WarnedPredIds0 }
|
|
;
|
|
{
|
|
set__insert(WarnedPredIds0, PredId, WarnedPredIds),
|
|
pred_info_procedures(PredInfo, Procs),
|
|
map__lookup(Procs, ProcId, Proc),
|
|
proc_info_headvars(Proc, HeadVars),
|
|
list__length(HeadVars, NumHeadVars),
|
|
|
|
% Strip off the extra type_info arguments
|
|
% inserted at the front by polymorphism.m
|
|
NumToDrop = NumHeadVars - pred_info_arity(PredInfo),
|
|
adjust_unused_args(NumToDrop, UnusedArgs0, UnusedArgs)
|
|
},
|
|
( { UnusedArgs \= [] } ->
|
|
report_unused_args(PredInfo, UnusedArgs)
|
|
;
|
|
[]
|
|
)
|
|
).
|
|
|
|
|
|
% Warn about unused arguments in a predicate. Only arguments unused
|
|
% in every mode of a predicate are warned about. The warning is
|
|
% suppressed for type_infos.
|
|
:- pred report_unused_args(pred_info::in, list(int)::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
report_unused_args(PredInfo, UnusedArgs) -->
|
|
{ list__length(UnusedArgs, NumArgs) },
|
|
{ pred_info_context(PredInfo, Context) },
|
|
{ PredOrFunc = pred_info_is_pred_or_func(PredInfo) },
|
|
{ Module = pred_info_module(PredInfo) },
|
|
{ Name = pred_info_name(PredInfo) },
|
|
{ Arity = pred_info_arity(PredInfo) },
|
|
prog_out__write_context(Context),
|
|
io__write_string("In "),
|
|
hlds_out__write_pred_or_func(PredOrFunc),
|
|
io__write_string(" `"),
|
|
prog_out__write_sym_name(Module),
|
|
io__write_string("."),
|
|
io__write_string(Name),
|
|
io__write_string("/"),
|
|
io__write_int(Arity),
|
|
io__write_string("':\n"),
|
|
prog_out__write_context(Context),
|
|
io__write_string(" warning: "),
|
|
(
|
|
{ NumArgs = 1 }
|
|
->
|
|
io__write_string("argument "),
|
|
output_arg_list(UnusedArgs),
|
|
io__write_string(" is unused.\n")
|
|
;
|
|
io__write_string("arguments "),
|
|
output_arg_list(UnusedArgs),
|
|
io__write_string(" are unused.\n")
|
|
).
|
|
|
|
% adjust warning message for the presence of type_infos.
|
|
:- pred adjust_unused_args(int::in, list(int)::in, list(int)::out) is det.
|
|
|
|
adjust_unused_args(_, [], []).
|
|
adjust_unused_args(NumToDrop, [UnusedArgNo | UnusedArgNos0], AdjUnusedArgs) :-
|
|
NewArg = UnusedArgNo - NumToDrop,
|
|
( NewArg < 1 ->
|
|
AdjUnusedArgs = AdjUnusedArgs1
|
|
;
|
|
AdjUnusedArgs = [NewArg | AdjUnusedArgs1]
|
|
),
|
|
adjust_unused_args(NumToDrop, UnusedArgNos0, AdjUnusedArgs1).
|
|
|
|
|
|
:- pred output_arg_list(list(int)::in, io__state::di, io__state::uo) is det.
|
|
|
|
output_arg_list([]) --> { error("output_list_int called with empty list") }.
|
|
output_arg_list([Arg | Rest]) -->
|
|
io__write_int(Arg),
|
|
(
|
|
{ Rest = [] }
|
|
;
|
|
{ Rest = [_ | _] },
|
|
output_arg_list_2(Rest)
|
|
).
|
|
|
|
|
|
:- pred output_arg_list_2(list(int)::in, io__state::di,
|
|
io__state::uo) is det.
|
|
|
|
output_arg_list_2(Args) -->
|
|
(
|
|
{ Args = [First, Second | Rest] }
|
|
->
|
|
io__write_string(", "),
|
|
io__write_int(First),
|
|
output_arg_list_2([Second | Rest])
|
|
;
|
|
{ Args = [Last] }
|
|
->
|
|
io__write_string(" and "),
|
|
io__write_int(Last)
|
|
;
|
|
{ error("output_arg_list_2 called with empty list") }
|
|
).
|
|
|