Files
mercury/compiler/simplify.m
Simon Taylor 2725b1a331 Aditi update syntax, type and mode checking.
Estimated hours taken: 220

Aditi update syntax, type and mode checking.

Change the hlds_goal for constructions in preparation for
structure reuse to avoid making multiple conflicting changes.

compiler/hlds_goal.m:
	Merge `higher_order_call' and `class_method_call' into a single
	`generic_call' goal type. This also has alternatives for the
	various Aditi builtins for which type declarations can't
	be written.

	Remove the argument types field from higher-order/class method calls.
	It wasn't used often, and wasn't updated by optimizations
	such as inlining. The types can be obtained from the vartypes
	field of the proc_info.

	Add a `lambda_eval_method' field to lambda_goals.

	Add a field to constructions to identify which RL code fragment should
	be used for an top-down Aditi closure.

	Add fields to constructions to hold structure reuse information.
	This is currently ignored -- the changes to implement structure
	reuse will be committed to the alias branch.
	This is included here to avoid lots of CVS conflicts caused by
	changing the definition of `hlds_goal' twice.

	Add a field to `some' goals to specify whether the quantification
	can be removed. This is used to make it easier to ensure that
	indexes are used for updates.

	Add a field to lambda_goals to describe whether the modes were
	guessed by the compiler and may need fixing up after typechecking
	works out the argument types.

	Add predicate `hlds_goal__generic_call_id' to work out a call_id
	for a generic call for use in error messages.

compiler/purity.m:
compiler/post_typecheck.m:
	Fill in the modes of Aditi builtin calls and closure constructions.
	This needs to know which are the `aditi__state' arguments, so
	it must be done after typechecking.

compiler/prog_data.m:
	Added `:- type sym_name_and_arity ---> sym_name/arity'.

	Add a type `lambda_eval_method', which describes how a closure
	is to be executed. The alternatives are normal Mercury execution,
	bottom-up execution by Aditi and top-down execution by Aditi.

compiler/prog_out.m:
	Add predicate `prog_out__write_sym_name_and_arity', which
	replaces duplicated inline code in a few places.

compiler/hlds_data.m:
	Add a `lambda_eval_method' field to `pred_const' cons_ids and
	`pred_closure_tag' cons_tags.

compiler/hlds_pred.m:
	Remove type `pred_call_id', replace it with type `simple_call_id',
	which combines a `pred_or_func' and a `sym_name_and_arity'.

	Add a type `call_id' which describes all the different types of call,
	including normal calls, higher-order and class-method calls
	and Aditi builtins.

	Add `aditi_top_down' to the type `marker'.

	Remove `aditi_interface' from type `marker'. Interfacing to
	Aditi predicates is now handled by `generic_call' hlds_goals.

	Add a type `rl_exprn_id' which identifies a predicate to
	be executed top-down by Aditi.
	Add a `maybe(rl_exprn_id)'  field to type `proc_info'.

	Add predicate `adjust_func_arity' to convert between the arity
	of a function to its arity as a predicate.

	Add predicates `get_state_args' and `get_state_args_det' to
	extract the DCG state arguments from an argument list.

	Add predicate `pred_info_get_call_id' to get a `simple_call_id'
	for a predicate for use in error messages.

compiler/hlds_out.m:
	Write the new representation for call_ids.

	Add a predicate `hlds_out__write_call_arg_id' which
	replaces similar code in mode_errors.m and typecheck.m.

compiler/prog_io_goal.m:
	Add support for `aditi_bottom_up' and `aditi_top_down' annotations
	on pred expressions.

compiler/prog_io_util.m:
compiler/prog_io_pragma.m:
	Add predicates
	- `prog_io_util:parse_name_and_arity' to parse `SymName/Arity'
		(moved from prog_io_pragma.m).
	- `prog_io_util:parse_pred_or_func_name_and_arity to parse
		`pred SymName/Arity' or `func SymName/Arity'.
	- `prog_io_util:parse_pred_or_func_and_args' to parse terms resembling
		a clause head (moved from prog_io_pragma.m).

compiler/type_util.m:
	Add support for `aditi_bottom_up' and `aditi_top_down' annotations
	on higher-order types.

	Add predicates `construct_higher_order_type',
	`construct_higher_order_pred_type' and
	`construct_higher_order_func_type' to avoid some code duplication.

compiler/mode_util.m:
	Add predicate `unused_mode/1', which returns `builtin:unused'.
	Add functions `aditi_di_mode/0', `aditi_ui_mode/0' and
	`aditi_uo_mode/0' which return `in', `in', and `out', but will
	be changed to return `di', `ui' and `uo' when alias tracking
	is implemented.

compiler/goal_util.m:
	Add predicate `goal_util__generic_call_vars' which returns
	any arguments to a generic_call which are not in the argument list,
	for example the closure passed to a higher-order call or
	the typeclass_info for a class method call.

compiler/llds.m:
compiler/exprn_aux.m:
compiler/dupelim.m:
compiler/llds_out.m:
compiler/opt_debug.m:
	Add builtin labels for the Aditi update operations.

compiler/hlds_module.m:
	Add predicate predicate_table_search_pf_sym, used for finding
	possible matches for a call with the wrong number of arguments.

compiler/intermod.m:
	Don't write predicates which build `aditi_top_down' goals,
	because there is currently no way to tell importing modules
	which RL code fragment to use.

compiler/simplify.m:
	Obey the `cannot_remove' field of explicit quantification goals.

compiler/make_hlds.m:
	Parse Aditi updates.

	Don't typecheck clauses for which syntax errors in Aditi updates
	are found - this avoids spurious "undefined predicate `aditi_insert/3'"
	errors.

	Factor out some common code to handle terms of the form `Head :- Body'.
	Factor out common code in the handling of pred and func expressions.

compiler/typecheck.m:
	Typecheck Aditi builtins.

	Allow the argument types of matching predicates to be adjusted
	when typechecking the higher-order arguments of Aditi builtins.

	Change `typecheck__resolve_pred_overloading' to take a list of
	argument types rather than a `map(var, type)' and a list of
	arguments to allow a transformation to be performed on the
	argument types before passing them.

compiler/error_util.m:
	Move the part of `report_error_num_args' which writes
	"wrong number of arguments (<x>; expected <y>)" from
	typecheck.m for use by make_hlds.m when reporting errors
	for Aditi builtins.

compiler/modes.m:
compiler/unique_modes.m:
compiler/modecheck_call.m:
	Modecheck Aditi builtins.

compiler/lambda.m:
	Handle the markers for predicates introduced for
	`aditi_top_down' and `aditi_bottom_up' lambda expressions.

compiler/polymorphism.m:
	Add extra type_infos to `aditi_insert' calls
	describing the tuple to insert.

compiler/call_gen.m:
	Generate code for Aditi builtins.

compiler/unify_gen.m:
compiler/bytecode_gen.m:
	Abort on `aditi_top_down' and `aditi_bottom_up' lambda
	expressions - code generation for them is not yet implemented.

compiler/magic.m:
	Use the `aditi_call' generic_call rather than create
	a new procedure for each Aditi predicate called from C.

compiler/rl_out.pp:
compiler/rl_gen.m:
compiler/rl.m:
	Move some utility code used by magic.m and call_gen.m into rl.m.

	Remove an XXX comment about reference counting being not yet
	implemented - Evan has fixed that.

library/ops.m:
compiler/mercury_to_mercury.m:
doc/transition_guide.texi:
	Add unary prefix operators `aditi_bottom_up' and `aditi_top_down',
	used as qualifiers on lambda expressions.
	Add infix operator `==>' to separate the tuples in an
	`aditi_modify' call.

compiler/follow_vars.m:
	Thread a `map(prog_var, type)' through, needed because
	type information is no longer held in higher-order call goals.

compiler/table_gen.m:
	Use the `make_*_construction' predicates in hlds_goal.m
	to construct constants.

compiler/*.m:
	Trivial changes to add extra fields to hlds_goal structures.

doc/reference_manual.texi:
	Document Aditi updates.

	Use @samp{pragma base_relation} instead of
	@samp{:- pragma base_relation} throughout the Aditi documentation
	to be consistent with other parts of the reference manual.

tests/valid/Mmakefile:
tests/valid/aditi_update.m:
tests/valid/aditi.m:
	Test case.

tests/valid/Mmakefile:
	Remove some hard-coded --intermodule-optimization rules which are
	no longer needed because `mmake depend' is now run in this directory.

tests/invalid/*.err_exp:
	Fix expected output for changes in reporting of call_ids
	in typecheck.m.

tests/invalid/Mmakefile
tests/invalid/aditi_update_errors.{m,err_exp}:
tests/invalid/aditi_update_mode_errors.{m,err_exp}:
	Test error messages for Aditi updates.

tests/valid/aditi.m:
tests/invalid/aditi.m:
	Cut down version of extras/aditi/aditi.m to provide basic declarations
	for Aditi compilation such as `aditi__state' and the modes
	`aditi_di', `aditi_uo' and `aditi_ui'. Installing extras/aditi/aditi.m
	somewhere would remove the need for these.
1999-07-13 08:55:28 +00:00

2135 lines
73 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1996-1999 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.
%-----------------------------------------------------------------------------%
%
% Main authors: zs, stayl.
%
% The two jobs of the simplification module are
%
% to find and exploit opportunities for simplifying the internal form
% of the program, both to optimize the code and to massage the code
% into a form the code generator will accept, and
%
% to warn the programmer about any constructs that are so simple that
% they should not have been included in the program in the first place.
%
% Simplification is done in two passes. The first pass performs common
% structure and duplicate call elimination. The second pass performs
% excess assignment elimination and cleans up the code after the first pass.
% Two passes are required because the goal must be requantified after the
% optimizations in common.m are run so that excess assignment elimination
% works properly.
%
%-----------------------------------------------------------------------------%
:- module simplify.
:- interface.
:- import_module hlds_goal, hlds_module, hlds_pred, det_report, det_util.
:- import_module common, instmap, globals.
:- import_module io, bool, list, map.
:- pred simplify__pred(list(simplification), pred_id, module_info, module_info,
pred_info, pred_info, int, int, io__state, io__state).
:- mode simplify__pred(in, in, in, out, in, out, out, out, di, uo) is det.
:- pred simplify__proc(list(simplification), pred_id, proc_id,
module_info, module_info, proc_info, proc_info, io__state, io__state).
:- mode simplify__proc(in, in, in, in, out, in, out, di, uo) is det.
:- pred simplify__proc_2(list(simplification), pred_id, proc_id, module_info,
module_info, proc_info, proc_info, set(det_msg)).
:- mode simplify__proc_2(in, in, in, in, out, in, out, out) is det.
:- pred simplify__process_goal(hlds_goal, hlds_goal,
simplify_info, simplify_info).
:- mode simplify__process_goal(in, out, in, out) is det.
% Find out which simplifications should be run from the options table
% stored in the globals. The first argument states whether warnings
% should be issued during this pass of simplification.
:- pred simplify__find_simplifications(bool, globals, list(simplification)).
:- mode simplify__find_simplifications(in, in, out) is det.
:- type simplification
---> warn_simple_code % --warn-simple-code
; warn_duplicate_calls % --warn-duplicate-calls
; do_once % run things that should be done once
; excess_assigns % remove excess assignment unifications
; duplicate_calls % optimize duplicate calls
; constant_prop % partially evaluate calls
; common_struct % common structure elimination
; extra_common_struct % do common structure elimination
% even when it might increase stack
% usage (used by deforestation).
.
:- type simplify_info.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module code_aux, det_analysis, follow_code, goal_util, const_prop.
:- import_module hlds_module, hlds_data, (inst), inst_match, varset.
:- import_module options, passes_aux, prog_data, mode_util, type_util.
:- import_module code_util, quantification, modes, purity, pd_cost.
:- import_module prog_util, unify_proc, special_pred, polymorphism.
:- import_module set, require, std_util, int, term.
%-----------------------------------------------------------------------------%
simplify__pred(Simplifications0, PredId, ModuleInfo0, ModuleInfo,
PredInfo0, PredInfo, WarnCnt, ErrCnt) -->
write_pred_progress_message("% Simplifying ", PredId, ModuleInfo0),
{ pred_info_non_imported_procids(PredInfo0, ProcIds) },
{ MaybeMsgs0 = no },
{
% Don't warn for compiler-generated procedures.
list__member(warn_simple_code, Simplifications0),
module_info_pred_info(ModuleInfo0, PredId, PredInfo0),
code_util__compiler_generated(PredInfo0)
->
list__delete_all(Simplifications0, warn_simple_code,
Simplifications)
;
Simplifications = Simplifications0
},
{ simplify__procs(Simplifications, PredId, ProcIds, ModuleInfo0,
ModuleInfo, PredInfo0, PredInfo, MaybeMsgs0, MaybeMsgs) },
( { MaybeMsgs = yes(Msgs0 - Msgs1) } ->
{ set__union(Msgs0, Msgs1, Msgs2) },
{ set__to_sorted_list(Msgs2, Msgs) },
det_report_msgs(Msgs, ModuleInfo, WarnCnt, ErrCnt)
;
{ WarnCnt = 0 },
{ ErrCnt = 0 }
).
:- pred simplify__procs(list(simplification), pred_id, list(proc_id),
module_info, module_info, pred_info, pred_info,
maybe(pair(set(det_msg))), maybe(pair(set(det_msg)))).
:- mode simplify__procs(in, in, in, in, out, in, out,
in, out) is det.
simplify__procs(_, _, [], ModuleInfo, ModuleInfo, PredInfo, PredInfo,
Msgs, Msgs).
simplify__procs(Simplifications, PredId, [ProcId | ProcIds], ModuleInfo0,
ModuleInfo, PredInfo0, PredInfo, MaybeMsgs0, MaybeMsgs) :-
pred_info_procedures(PredInfo0, Procs0),
map__lookup(Procs0, ProcId, Proc0),
simplify__proc_2(Simplifications, PredId, ProcId, ModuleInfo0,
ModuleInfo1, Proc0, Proc, Msgs1),
map__det_update(Procs0, ProcId, Proc, Procs),
pred_info_set_procedures(PredInfo0, Procs, PredInfo1),
set__to_sorted_list(Msgs1, Msgs2),
list__filter(lambda([Msg::in] is semidet,
det_msg_is_any_mode_msg(Msg, any_mode)),
Msgs2, AnyModeMsgs1, AllModeMsgs1),
set__sorted_list_to_set(AnyModeMsgs1, AnyModeMsgs2),
set__sorted_list_to_set(AllModeMsgs1, AllModeMsgs2),
( MaybeMsgs0 = yes(AnyModeMsgs0 - AllModeMsgs0) ->
set__union(AnyModeMsgs0, AnyModeMsgs2, AnyModeMsgs),
set__intersect(AllModeMsgs0, AllModeMsgs2, AllModeMsgs),
MaybeMsgs1 = yes(AllModeMsgs - AnyModeMsgs)
;
MaybeMsgs1 = yes(AnyModeMsgs2 - AllModeMsgs2)
),
simplify__procs(Simplifications, PredId, ProcIds, ModuleInfo1,
ModuleInfo, PredInfo1, PredInfo, MaybeMsgs1, MaybeMsgs).
simplify__proc(Simplifications, PredId, ProcId, ModuleInfo0, ModuleInfo,
Proc0, Proc) -->
write_pred_progress_message("% Simplifying ", PredId, ModuleInfo0),
{ simplify__proc_2(Simplifications, PredId, ProcId, ModuleInfo0,
ModuleInfo, Proc0, Proc, _) }.
simplify__proc_2(Simplifications, PredId, ProcId, ModuleInfo0, ModuleInfo,
ProcInfo0, ProcInfo, Msgs) :-
module_info_globals(ModuleInfo0, Globals),
det_info_init(ModuleInfo0, PredId, ProcId, Globals, DetInfo0),
proc_info_get_initial_instmap(ProcInfo0, ModuleInfo0, InstMap0),
proc_info_varset(ProcInfo0, VarSet0),
proc_info_vartypes(ProcInfo0, VarTypes0),
proc_info_goal(ProcInfo0, Goal0),
simplify_info_init(DetInfo0, Simplifications, InstMap0,
VarSet0, VarTypes0, Info0),
simplify__process_goal(Goal0, Goal, Info0, Info),
simplify_info_get_varset(Info, VarSet),
simplify_info_get_var_types(Info, VarTypes),
proc_info_set_varset(ProcInfo0, VarSet, ProcInfo1),
proc_info_set_vartypes(ProcInfo1, VarTypes, ProcInfo2),
proc_info_set_goal(ProcInfo2, Goal, ProcInfo),
simplify_info_get_module_info(Info, ModuleInfo),
simplify_info_get_msgs(Info, Msgs).
simplify__process_goal(Goal0, Goal, Info0, Info) :-
simplify_info_get_simplifications(Info0, Simplifications0),
simplify_info_get_instmap(Info0, InstMap0),
( (simplify_do_common(Info0); simplify_do_calls(Info0)) ->
% On the first pass do common structure and call elimination.
NotOnFirstPass = [do_once, excess_assigns],
set__delete_list(Simplifications0, NotOnFirstPass,
Simplifications1),
simplify_info_set_simplifications(Info0, Simplifications1,
Info1),
simplify__do_process_goal(Goal0, Goal1, Info1, Info2),
NotOnSecondPass = [warn_simple_code, warn_duplicate_calls,
common_struct, duplicate_calls],
set__delete_list(Simplifications0, NotOnSecondPass,
Simplifications2),
simplify_info_reinit(Simplifications2, InstMap0, Info2, Info3)
;
Info3 = Info0,
Goal1 = Goal0
),
% On the second pass do excess assignment elimination and
% some cleaning up after the common structure pass.
simplify__do_process_goal(Goal1, Goal, Info3, Info).
:- pred simplify__do_process_goal(hlds_goal::in, hlds_goal::out,
simplify_info::in, simplify_info::out) is det.
simplify__do_process_goal(Goal0, Goal, Info0, Info) :-
simplify_info_get_instmap(Info0, InstMap0),
simplify__goal(Goal0, Goal1, Info0, Info1),
simplify_info_get_varset(Info1, VarSet0),
simplify_info_get_var_types(Info1, VarTypes0),
( simplify_info_requantify(Info1) ->
Goal1 = _ - GoalInfo1,
goal_info_get_nonlocals(GoalInfo1, NonLocals),
implicitly_quantify_goal(Goal1, VarSet0, VarTypes0, NonLocals,
Goal2, VarSet, VarTypes, _),
simplify_info_set_varset(Info1, VarSet, Info2),
simplify_info_set_var_types(Info2, VarTypes, Info3),
% Always recompute instmap_deltas for atomic goals - this
% is safer in the case where unused variables should no
% longer be included in the instmap_delta for a goal.
% In the alias branch this is necessary anyway.
RecomputeAtomic = yes,
simplify_info_get_module_info(Info3, ModuleInfo3),
recompute_instmap_delta(RecomputeAtomic, Goal2, Goal3,
InstMap0, ModuleInfo3, ModuleInfo4),
simplify_info_set_module_info(Info3, ModuleInfo4, Info4)
;
Goal3 = Goal1,
Info4 = Info1
),
( simplify_info_rerun_det(Info4) ->
Goal0 = _ - GoalInfo0,
goal_info_get_determinism(GoalInfo0, Det),
det_get_soln_context(Det, SolnContext),
% det_infer_goal looks up the proc_info in the module_info
% for the vartypes, so we'd better stick them back in the
% module_info.
simplify_info_get_module_info(Info4, ModuleInfo5),
simplify_info_get_varset(Info4, VarSet4),
simplify_info_get_var_types(Info4, VarTypes4),
simplify_info_get_det_info(Info4, DetInfo4),
det_info_get_pred_id(DetInfo4, PredId),
det_info_get_proc_id(DetInfo4, ProcId),
module_info_pred_proc_info(ModuleInfo5, PredId, ProcId,
PredInfo, ProcInfo0),
proc_info_set_vartypes(ProcInfo0, VarTypes4, ProcInfo1),
proc_info_set_varset(ProcInfo1, VarSet4, ProcInfo),
module_info_set_pred_proc_info(ModuleInfo5, PredId, ProcId,
PredInfo, ProcInfo, ModuleInfo6),
simplify_info_set_module_info(Info4, ModuleInfo6, Info),
simplify_info_get_det_info(Info, DetInfo),
det_infer_goal(Goal3, InstMap0, SolnContext,
DetInfo, Goal, _, _)
;
Info = Info4,
Goal = Goal3
).
%-----------------------------------------------------------------------------%
simplify__find_simplifications(WarnThisPass, Globals, S) :-
simplify__find_simplifications_2(WarnThisPass, Globals, [], S).
:- pred simplify__find_simplifications_2(bool, globals,
list(simplification), list(simplification)).
:- mode simplify__find_simplifications_2(in, in, in, out) is det.
simplify__find_simplifications_2(WarnThisPass, Globals) -->
( { WarnThisPass = yes } ->
simplify__lookup_option(Globals, warn_duplicate_calls,
warn_duplicate_calls),
simplify__lookup_option(Globals, warn_simple_code,
warn_simple_code)
;
[]
),
simplify__lookup_option(Globals, excess_assign, excess_assigns),
simplify__lookup_option(Globals, common_struct, common_struct),
simplify__lookup_option(Globals, optimize_duplicate_calls,
duplicate_calls),
simplify__lookup_option(Globals, constant_propagation,
constant_prop).
:- pred simplify__lookup_option(globals::in, option::in, simplification::in,
list(simplification)::in, list(simplification)::out) is det.
simplify__lookup_option(Globals, Option, Simplification,
Simplifications0, Simplifications) :-
globals__lookup_bool_option(Globals, Option, Result),
( Result = yes ->
Simplifications = [Simplification | Simplifications0]
;
Simplifications = Simplifications0
).
%-----------------------------------------------------------------------------%
:- pred simplify__goal(hlds_goal, hlds_goal, simplify_info, simplify_info).
:- mode simplify__goal(in, out, in, out) is det.
simplify__goal(Goal0, Goal - GoalInfo, Info0, Info) :-
Goal0 = _ - GoalInfo0,
goal_info_get_determinism(GoalInfo0, Detism),
simplify_info_get_det_info(Info0, DetInfo),
simplify_info_get_module_info(Info0, ModuleInfo),
(
%
% if --no-fully-strict,
% replace goals with determinism failure with `fail'.
%
Detism = failure,
% ensure goal is pure or semipure
\+ goal_info_is_impure(GoalInfo0),
( det_info_get_fully_strict(DetInfo, no)
; code_aux__goal_cannot_loop(ModuleInfo, Goal0)
)
->
% warn about this, unless the goal was an explicit
% `fail', or some goal containing `fail'.
goal_info_get_context(GoalInfo0, Context),
(
simplify_do_warn(Info0),
\+ (goal_contains_goal(Goal0, SubGoal),
SubGoal = disj([], _) - _)
->
simplify_info_add_msg(Info0,
goal_cannot_succeed(Context), Info1)
;
Info1 = Info0
),
% If the goal had any non-locals we should requantify.
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
( set__empty(NonLocals0) ->
Info2 = Info1
;
simplify_info_set_requantify(Info1, Info2)
),
pd_cost__goal(Goal0, CostDelta),
simplify_info_incr_cost_delta(Info2, CostDelta, Info3),
fail_goal(Context, Goal1)
;
%
% if --no-fully-strict,
% replace goals which cannot fail and have no
% output variables with `true'.
% However, we don't do this for erroneous goals,
% since these may occur in conjunctions where there
% are no producers for some variables, and the
% code generator would fail for these.
%
determinism_components(Detism, cannot_fail, MaxSoln),
MaxSoln \= at_most_zero,
goal_info_get_instmap_delta(GoalInfo0, InstMapDelta),
goal_info_get_nonlocals(GoalInfo0, NonLocalVars),
simplify_info_get_instmap(Info0, InstMap0),
det_no_output_vars(NonLocalVars, InstMap0, InstMapDelta,
DetInfo),
% ensure goal is pure or semipure
\+ goal_info_is_impure(GoalInfo0),
( det_info_get_fully_strict(DetInfo, no)
; code_aux__goal_cannot_loop(ModuleInfo, Goal0)
)
->
/******************
The following warning is disabled, because it often results in spurious
warnings. Sometimes predicate calls are used just to constrain the types,
to avoid type ambiguities or unbound type variables, and in such cases,
it is perfectly legitimate for a call to be det and to have no outputs.
There's no simple way of telling those cases from cases for which we
really ought to warn.
% warn about this, if the goal wasn't `true', wasn't `!',
% and wasn't a deconstruction unification.
% We don't warn about deconstruction unifications
% with no outputs that always succeed, because that
% would result in bogus warnings, since switch detection
% converts deconstruction unifications that can fail
% into ones that always succeed by moving the test into
% the switch.
% We also don't warn about conjunctions or existential
% quantifications, because it seems that warnings in those
% cases are usually spurious.
(
simplify_do_warn(Info0),
% Goal0 \= conj([]) - _,
\+ (Goal0 = call(_, _, _, _, _, SymName) - _,
unqualify_name(SymName, "!")),
Goal0 \= conj(_) - _,
Goal0 \= some(_, _) - _,
\+ (Goal0 = unify(_, _, _, Unification, _) - _,
Unification = deconstruct(_, _, _, _, _))
->
simplify_info_add_msg(Info0,
det_goal_has_no_outputs(Context), Info1)
;
Info1 = Info0
),
******************/
Info0 = Info1,
% If the goal had any non-locals we should requantify.
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
( set__empty(NonLocals0) ->
Info2 = Info1
;
simplify_info_set_requantify(Info1, Info2)
),
pd_cost__goal(Goal0, CostDelta),
simplify_info_incr_cost_delta(Info2, CostDelta, Info3),
goal_info_get_context(GoalInfo0, Context),
true_goal(Context, Goal1)
;
Goal1 = Goal0,
Info3 = Info0
),
simplify_info_maybe_clear_structs(before, Goal1, Info3, Info4),
Goal1 = GoalExpr1 - GoalInfo1,
simplify__goal_2(GoalExpr1, GoalInfo1, Goal, GoalInfo2, Info4, Info5),
simplify_info_maybe_clear_structs(after, Goal - GoalInfo2,
Info5, Info6),
simplify__enforce_invariant(GoalInfo2, GoalInfo, Info6, Info).
:- pred simplify__enforce_invariant(hlds_goal_info, hlds_goal_info,
simplify_info, simplify_info).
:- mode simplify__enforce_invariant(in, out, in, out) is det.
%
% Ensure that the mode information and the determinism
% information say consistent things about unreachability.
%
simplify__enforce_invariant(GoalInfo0, GoalInfo, Info0, Info) :-
goal_info_get_determinism(GoalInfo0, Determinism0),
goal_info_get_instmap_delta(GoalInfo0, DeltaInstmap0),
determinism_components(Determinism0, CanFail0, NumSolns0),
(
NumSolns0 = at_most_zero,
instmap_delta_is_reachable(DeltaInstmap0)
->
instmap_delta_init_unreachable(UnreachableInstMapDelta),
goal_info_set_instmap_delta(GoalInfo0, UnreachableInstMapDelta,
GoalInfo),
simplify_info_set_rerun_det(Info0, Info)
;
instmap_delta_is_unreachable(DeltaInstmap0),
NumSolns0 \= at_most_zero
->
determinism_components(Determinism, CanFail0, at_most_zero),
goal_info_set_determinism(GoalInfo0, Determinism, GoalInfo),
simplify_info_set_rerun_det(Info0, Info)
;
GoalInfo = GoalInfo0,
Info = Info0
).
%-----------------------------------------------------------------------------%
:- pred simplify__goal_2(hlds_goal_expr, hlds_goal_info, hlds_goal_expr,
hlds_goal_info, simplify_info, simplify_info).
:- mode simplify__goal_2(in, in, out, out, in, out) is det.
simplify__goal_2(conj(Goals0), GoalInfo0, Goal, GoalInfo, Info0, Info) :-
simplify_info_get_instmap(Info0, InstMap0),
simplify__conj(Goals0, [], Goals, GoalInfo0, Info0, Info1),
simplify_info_set_instmap(Info1, InstMap0, Info2),
( Goals = [] ->
goal_info_get_context(GoalInfo0, Context),
true_goal(Context, Goal - GoalInfo),
Info = Info2
; Goals = [SingleGoal - SingleGoalInfo] ->
% a singleton conjunction is equivalent to the goal itself
simplify__maybe_wrap_goal(GoalInfo0, SingleGoalInfo,
SingleGoal, Goal, GoalInfo, Info2, Info)
;
%
% Conjunctions that cannot produce solutions may nevertheless
% contain nondet and multidet goals. If this happens, the
% conjunction is put inside a `some' to appease the code
% generator.
%
Info = Info2,
goal_info_get_determinism(GoalInfo0, Detism),
(
simplify_do_once(Info),
determinism_components(Detism, CanFail, at_most_zero),
simplify__contains_multisoln_goal(Goals)
->
determinism_components(InnerDetism,
CanFail, at_most_many),
goal_info_set_determinism(GoalInfo0,
InnerDetism, InnerInfo),
InnerGoal = conj(Goals) - InnerInfo,
Goal = some([], can_remove, InnerGoal)
;
Goal = conj(Goals)
),
GoalInfo = GoalInfo0
).
simplify__goal_2(par_conj(Goals0, SM), GoalInfo0, Goal,
GoalInfo, Info0, Info) :-
(
Goals0 = []
->
goal_info_get_context(GoalInfo0, Context),
true_goal(Context, Goal - GoalInfo),
Info = Info0
;
Goals0 = [SingleGoal0]
->
simplify__goal(SingleGoal0, SingleGoal - SingleGoalInfo,
Info0, Info1),
simplify__maybe_wrap_goal(GoalInfo0, SingleGoalInfo,
SingleGoal, Goal, GoalInfo, Info1, Info)
;
GoalInfo = GoalInfo0,
simplify__par_conj(Goals0, Goals, Info0, Info0, Info),
Goal = par_conj(Goals, SM)
).
simplify__goal_2(disj(Disjuncts0, SM), GoalInfo0,
Goal, GoalInfo, Info0, Info) :-
simplify_info_get_instmap(Info0, InstMap0),
simplify__disj(Disjuncts0, [], Disjuncts, [], InstMaps,
Info0, Info0, Info1),
( Disjuncts = [] ->
goal_info_get_context(GoalInfo0, Context),
fail_goal(Context, Goal - GoalInfo),
Info = Info1
; Disjuncts = [SingleGoal] ->
% a singleton disjunction is equivalent to the goal itself
SingleGoal = Goal1 - GoalInfo1,
simplify__maybe_wrap_goal(GoalInfo0, GoalInfo1,
Goal1, Goal, GoalInfo, Info1, Info)
;
Goal = disj(Disjuncts, SM),
simplify_info_get_module_info(Info1, ModuleInfo1),
goal_info_get_nonlocals(GoalInfo0, NonLocals),
merge_instmap_deltas(InstMap0, NonLocals, InstMaps,
NewDelta, ModuleInfo1, ModuleInfo2),
simplify_info_set_module_info(Info1, ModuleInfo2, Info),
goal_info_set_instmap_delta(GoalInfo0, NewDelta, GoalInfo)
).
simplify__goal_2(switch(Var, SwitchCanFail0, Cases0, SM),
GoalInfo0, Goal, GoalInfo, Info0, Info) :-
simplify_info_get_instmap(Info0, InstMap0),
simplify_info_get_module_info(Info0, ModuleInfo0),
instmap__lookup_var(InstMap0, Var, VarInst),
( inst_is_bound_to_functors(ModuleInfo0, VarInst, Functors) ->
functors_to_cons_ids(Functors, ConsIds0),
list__sort(ConsIds0, ConsIds),
delete_unreachable_cases(Cases0, ConsIds, Cases1),
MaybeConsIds = yes(ConsIds)
;
Cases1 = Cases0,
MaybeConsIds = no
),
simplify__switch(Var, Cases1, [], Cases, [], InstMaps,
SwitchCanFail0, SwitchCanFail, Info0, Info0, Info1),
( Cases = [] ->
% An empty switch always fails.
pd_cost__eliminate_switch(CostDelta),
simplify_info_incr_cost_delta(Info1, CostDelta, Info),
goal_info_get_context(GoalInfo0, Context),
fail_goal(Context, Goal - GoalInfo)
; Cases = [case(ConsId, SingleGoal)] ->
% a singleton switch is equivalent to the goal itself with
% a possibly can_fail unification with the functor on the front.
cons_id_arity(ConsId, Arity),
(
SwitchCanFail = can_fail,
MaybeConsIds \= yes([ConsId])
->
simplify__create_test_unification(Var, ConsId, Arity,
UnifyGoal, Info1, Info2),
% Conjoin the test and the rest of the case.
goal_to_conj_list(SingleGoal, SingleGoalConj),
GoalList = [UnifyGoal | SingleGoalConj],
% Work out the nonlocals, instmap_delta
% and determinism of the entire conjunction.
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
set__insert(NonLocals0, Var, NonLocals),
goal_info_get_instmap_delta(GoalInfo0, InstMapDelta0),
simplify_info_get_instmap(Info2, InstMap),
instmap_delta_bind_var_to_functor(Var, ConsId,
InstMap, InstMapDelta0, InstMapDelta,
ModuleInfo0, ModuleInfo),
simplify_info_set_module_info(Info2,
ModuleInfo, Info3),
goal_info_get_determinism(GoalInfo0, CaseDetism),
det_conjunction_detism(semidet, CaseDetism, Detism),
goal_info_init(NonLocals, InstMapDelta, Detism,
CombinedGoalInfo),
simplify_info_set_requantify(Info3, Info4),
Goal = conj(GoalList),
GoalInfo = CombinedGoalInfo
;
% The var can only be bound to this cons_id, so
% a test is unnecessary.
SingleGoal = Goal - GoalInfo,
Info4 = Info1
),
pd_cost__eliminate_switch(CostDelta),
simplify_info_incr_cost_delta(Info4, CostDelta, Info)
;
Goal = switch(Var, SwitchCanFail, Cases, SM),
simplify_info_get_module_info(Info1, ModuleInfo1),
goal_info_get_nonlocals(GoalInfo0, NonLocals),
merge_instmap_deltas(InstMap0, NonLocals, InstMaps,
NewDelta, ModuleInfo1, ModuleInfo2),
simplify_info_set_module_info(Info1, ModuleInfo2, Info),
goal_info_set_instmap_delta(GoalInfo0, NewDelta, GoalInfo)
).
simplify__goal_2(Goal0, GoalInfo, Goal, GoalInfo, Info0, Info) :-
Goal0 = generic_call(GenericCall, Args, Modes, Det),
(
simplify_do_calls(Info0),
% XXX We should do duplicate call elimination for
% class method calls here.
GenericCall = higher_order(Closure, _, _)
->
common__optimise_higher_order_call(Closure, Args, Modes, Det,
Goal0, GoalInfo, Goal, Info0, Info)
;
simplify_do_warn_calls(Info0),
GenericCall = higher_order(Closure, _, _)
->
% We need to do the pass, for the warnings, but we ignore
% the optimized goal and instead use the original one.
common__optimise_higher_order_call(Closure, Args, Modes, Det,
Goal0, GoalInfo, _Goal1, Info0, Info),
Goal = Goal0
;
Goal = Goal0,
Info = Info0
).
simplify__goal_2(Goal0, GoalInfo0, Goal, GoalInfo, Info0, Info) :-
Goal0 = call(PredId, ProcId, Args, IsBuiltin, _, _),
simplify_info_get_module_info(Info0, ModuleInfo),
module_info_pred_proc_info(ModuleInfo, PredId, ProcId, PredInfo,
ProcInfo),
%
% check for calls to predicates with `pragma obsolete' declarations
%
(
simplify_do_warn(Info0),
pred_info_get_markers(PredInfo, Markers),
check_marker(Markers, obsolete),
%
% Don't warn about directly recursive calls.
% (That would cause spurious warnings, particularly
% with builtin predicates, or preds defined using
% pragma c_code.)
%
simplify_info_get_det_info(Info0, DetInfo0),
det_info_get_pred_id(DetInfo0, ThisPredId),
PredId \= ThisPredId
->
goal_info_get_context(GoalInfo0, Context1),
simplify_info_add_msg(Info0, warn_obsolete(PredId, Context1),
Info1)
;
Info1 = Info0
),
%
% Check for recursive calls with the same input arguments,
% and warn about them (since they will lead to infinite loops).
%
(
simplify_do_warn(Info1),
%
% Is this a (directly) recursive call,
% i.e. is the procedure being called the same as the
% procedure we're analyzing?
%
simplify_info_get_det_info(Info1, DetInfo),
det_info_get_pred_id(DetInfo, PredId),
det_info_get_proc_id(DetInfo, ProcId),
%
% Don't count inline builtins.
% (The compiler generates code for builtins that looks
% recursive, so that you can take their address, but since
% the recursive call actually expands into inline
% instructions, so it's not infinite recursion.)
%
IsBuiltin \= inline_builtin,
%
% Don't warn if we're inside a lambda goal, because the
% recursive call may not be executed.
%
\+ simplify_info_inside_lambda(Info1),
%
% Are the input arguments the same (or equivalent)?
%
simplify_info_get_module_info(Info1, ModuleInfo1),
module_info_pred_proc_info(ModuleInfo1, PredId, ProcId,
PredInfo1, ProcInfo1),
proc_info_headvars(ProcInfo1, HeadVars),
proc_info_argmodes(ProcInfo1, ArgModes),
simplify_info_get_common_info(Info1, CommonInfo1),
simplify__input_args_are_equiv(Args, HeadVars, ArgModes,
CommonInfo1, ModuleInfo1),
%
% Don't count procs using minimal evaluation as they
% should always terminate if they have a finite number
% of answers.
%
\+ proc_info_eval_method(ProcInfo, eval_minimal),
% Don't warn about Aditi relations.
\+ hlds_pred__pred_info_is_aditi_relation(PredInfo1)
->
goal_info_get_context(GoalInfo0, Context2),
simplify_info_add_msg(Info1, warn_infinite_recursion(Context2),
Info2)
;
Info2 = Info1
),
%
% check for duplicate calls to the same procedure
%
( simplify_do_calls(Info2),
goal_info_is_pure(GoalInfo0)
->
common__optimise_call(PredId, ProcId, Args, Goal0, GoalInfo0,
Goal1, Info2, Info3)
; simplify_do_warn_calls(Info0),
goal_info_is_pure(GoalInfo0)
->
% we need to do the pass, for the warnings, but we ignore
% the optimized goal and instead use the original one
common__optimise_call(PredId, ProcId, Args, Goal0, GoalInfo0,
_Goal1, Info2, Info3),
Goal1 = Goal0
;
Goal1 = Goal0,
Info3 = Info2
),
%
% Try to evaluate the call at compile-time.
%
( simplify_do_const_prop(Info3) ->
simplify_info_get_instmap(Info3, Instmap0),
simplify_info_get_module_info(Info3, ModuleInfo2),
(
Goal1 = call(_, _, _, _, _, _),
evaluate_builtin(PredId, ProcId, Args, GoalInfo0,
Goal2, GoalInfo2, Instmap0,
ModuleInfo2, ModuleInfo3)
->
Goal = Goal2,
GoalInfo = GoalInfo2,
simplify_info_set_module_info(Info3, ModuleInfo3, Info4),
simplify_info_set_requantify(Info4, Info)
;
Goal = Goal1,
GoalInfo = GoalInfo0,
Info = Info3
)
;
Goal = Goal1,
GoalInfo = GoalInfo0,
Info = Info3
).
simplify__goal_2(Goal0, GoalInfo0, Goal, GoalInfo, Info0, Info) :-
Goal0 = unify(LT0, RT0, M, U0, C),
(
% A unification of the form X = X can safely be
% optimised away.
RT0 = var(LT0)
->
goal_info_get_context(GoalInfo0, Context),
true_goal(Context, Goal - GoalInfo),
Info = Info0
;
RT0 = lambda_goal(PredOrFunc, EvalMethod, FixModes,
NonLocals, Vars, Modes, LambdaDeclaredDet, LambdaGoal0)
->
simplify_info_enter_lambda(Info0, Info1),
simplify_info_get_common_info(Info1, Common1),
simplify_info_get_module_info(Info1, ModuleInfo),
simplify_info_get_instmap(Info1, InstMap1),
instmap__pre_lambda_update(ModuleInfo, Vars, Modes,
InstMap1, InstMap2),
simplify_info_set_instmap(Info1, InstMap2, Info2),
% Don't attempt to pass structs into lambda_goals,
% since that could change the curried non-locals of the
% lambda_goal, and that would be difficult to fix up.
common_info_init(Common2),
simplify_info_set_common_info(Info2, Common2, Info3),
% Don't attempt to pass structs out of lambda_goals.
simplify__goal(LambdaGoal0, LambdaGoal, Info3, Info4),
simplify_info_set_common_info(Info4, Common1, Info5),
simplify_info_set_instmap(Info5, InstMap1, Info6),
RT = lambda_goal(PredOrFunc, EvalMethod, FixModes, NonLocals,
Vars, Modes, LambdaDeclaredDet, LambdaGoal),
simplify_info_leave_lambda(Info6, Info),
Goal = unify(LT0, RT, M, U0, C),
GoalInfo = GoalInfo0
;
U0 = complicated_unify(UniMode, CanFail, TypeInfoVars)
->
( RT0 = var(V) ->
simplify__process_compl_unify(LT0, V,
UniMode, CanFail, TypeInfoVars,
C, GoalInfo0, Goal1,
Info0, Info),
Goal1 = Goal - GoalInfo
;
error("simplify.m: invalid RHS for complicated unify")
)
;
simplify_do_common(Info0)
->
common__optimise_unification(U0, LT0, RT0, M, C,
Goal0, GoalInfo0, Goal, GoalInfo, Info0, Info)
;
( simplify_do_calls(Info0)
; simplify_do_warn_calls(Info0)
)
->
% We need to do the pass, to record the variable
% equivalences used for optimizing or warning about
% duplicate calls. But we don't want to perform
% the optimization, so we disregard the optimized goal
% and instead use the original one.
common__optimise_unification(U0, LT0, RT0, M, C,
Goal0, GoalInfo0, _Goal1, _GoalInfo1, Info0, Info),
Goal = Goal0,
GoalInfo = GoalInfo0
;
Goal = Goal0,
GoalInfo = GoalInfo0,
Info = Info0
).
% (A -> B ; C) is logically equivalent to (A, B ; ~A, C).
% If the determinism of A means that one of these disjuncts
% cannot succeed, then we replace the if-then-else with the
% other disjunct. (We could also eliminate A, but we leave
% that to the recursive invocations.)
%
% The conjunction operator in the remaining disjunct ought to be
% a sequential conjunction, because Mercury's if-then-else always
% guarantees sequentiality, whereas conjunction only guarantees
% sequentiality if the --no-reorder-conj option is enabled.
%
% However, currently reordering is only done in mode analysis,
% not in the code generator, so we don't yet need a sequential
% conjunction construct. This will change when constraint pushing
% is finished, or when we start doing coroutining.
simplify__goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM),
GoalInfo0, Goal, GoalInfo, Info0, Info) :-
Cond0 = _ - CondInfo0,
goal_info_get_determinism(CondInfo0, CondDetism0),
determinism_components(CondDetism0, CondCanFail0, CondSolns0),
( CondCanFail0 = cannot_fail ->
goal_to_conj_list(Cond0, CondList),
goal_to_conj_list(Then0, ThenList),
list__append(CondList, ThenList, List),
simplify__goal(conj(List) - GoalInfo0, Goal - GoalInfo,
Info0, Info1),
goal_info_get_context(GoalInfo0, Context),
simplify_info_add_msg(Info1, ite_cond_cannot_fail(Context),
Info)
; CondSolns0 = at_most_zero ->
% Optimize away the condition and the `then' part.
det_negation_det(CondDetism0, MaybeNegDetism),
( Cond0 = not(NegCond) - _ ->
Cond = NegCond
;
(
MaybeNegDetism = yes(NegDetism1),
(
NegDetism1 = erroneous,
instmap_delta_init_unreachable(
NegInstMapDelta1)
;
NegDetism1 = det,
instmap_delta_init_reachable(
NegInstMapDelta1)
)
->
NegDetism = NegDetism1,
NegInstMapDelta = NegInstMapDelta1
;
error("simplify__goal_2: cannot get negated determinism")
),
goal_info_set_determinism(CondInfo0,
NegDetism, NegCondInfo0),
goal_info_set_instmap_delta(NegCondInfo0,
NegInstMapDelta, NegCondInfo),
Cond = not(Cond0) - NegCondInfo
),
goal_to_conj_list(Else0, ElseList),
List = [Cond | ElseList],
simplify__goal(conj(List) - GoalInfo0, Goal - GoalInfo,
Info0, Info1),
goal_info_get_context(GoalInfo0, Context),
simplify_info_add_msg(Info1, ite_cond_cannot_succeed(Context),
Info)
; Else0 = disj([], _) - _ ->
% (A -> C ; fail) is equivalent to (A, C)
goal_to_conj_list(Cond0, CondList),
goal_to_conj_list(Then0, ThenList),
list__append(CondList, ThenList, List),
simplify__goal(conj(List) - GoalInfo0, Goal - GoalInfo,
Info0, Info)
;
simplify_info_get_instmap(Info0, InstMap0),
simplify__goal(Cond0, Cond, Info0, Info1),
simplify_info_update_instmap(Info1, Cond, Info2),
simplify__goal(Then0, Then, Info2, Info3),
simplify_info_post_branch_update(Info0, Info3, Info4),
simplify__goal(Else0, Else, Info4, Info5),
simplify_info_post_branch_update(Info0, Info5, Info6),
Cond = _ - CondInfo,
goal_info_get_instmap_delta(CondInfo, CondDelta),
Then = _ - ThenInfo,
goal_info_get_instmap_delta(ThenInfo, ThenDelta),
instmap_delta_apply_instmap_delta(CondDelta, ThenDelta,
CondThenDelta),
Else = _ - ElseInfo,
goal_info_get_instmap_delta(ElseInfo, ElseDelta),
goal_info_get_nonlocals(GoalInfo0, NonLocals),
simplify_info_get_module_info(Info6, ModuleInfo0),
merge_instmap_deltas(InstMap0, NonLocals,
[CondThenDelta, ElseDelta], NewDelta,
ModuleInfo0, ModuleInfo1),
simplify_info_set_module_info(Info6, ModuleInfo1, Info),
goal_info_set_instmap_delta(GoalInfo0, NewDelta, GoalInfo1),
IfThenElse = if_then_else(Vars, Cond, Then, Else, SM),
%
% If-then-elses that are det or semidet may nevertheless
% contain nondet or multidet conditions. If this happens, the
% if-then-else must be put inside a `some' to appease the code
% generator.
%
goal_info_get_determinism(GoalInfo0, IfThenElseDetism0),
determinism_components(IfThenElseDetism0, IfThenElseCanFail,
IfThenElseNumSolns),
(
simplify_do_once(Info),
goal_info_get_determinism(CondInfo, CondDetism),
determinism_components(CondDetism, _, at_most_many),
IfThenElseNumSolns \= at_most_many
->
determinism_components(InnerDetism, IfThenElseCanFail,
at_most_many),
goal_info_set_determinism(GoalInfo1, InnerDetism,
InnerInfo),
Goal = some([], can_remove, IfThenElse - InnerInfo)
;
Goal = IfThenElse
),
GoalInfo = GoalInfo1
).
simplify__goal_2(not(Goal0), GoalInfo0, Goal, GoalInfo, Info0, Info) :-
% Can't use calls or unifications seen within a negation,
% since non-local variables may not be bound within the negation.
simplify_info_get_common_info(Info0, Common),
simplify__goal(Goal0, Goal1, Info0, Info1),
simplify_info_set_common_info(Info1, Common, Info2),
Goal1 = _ - GoalInfo1,
goal_info_get_determinism(GoalInfo1, Detism),
determinism_components(Detism, CanFail, MaxSoln),
goal_info_get_context(GoalInfo0, Context),
( CanFail = cannot_fail ->
simplify_info_add_msg(Info2,
negated_goal_cannot_fail(Context), Info3)
; MaxSoln = at_most_zero ->
simplify_info_add_msg(Info2,
negated_goal_cannot_succeed(Context), Info3)
;
Info3 = Info2
),
(
% replace `not true' with `fail'
Goal1 = conj([]) - _GoalInfo
->
fail_goal(Context, Goal - GoalInfo),
Info = Info3
;
% replace `not fail' with `true'
Goal1 = disj([], _) - _GoalInfo2
->
true_goal(Context, Goal - GoalInfo),
Info = Info3
;
% remove double negation
Goal1 = not(SubGoal - SubGoalInfo) - _
->
simplify__maybe_wrap_goal(GoalInfo0, SubGoalInfo, SubGoal,
Goal, GoalInfo, Info3, Info)
;
Goal = not(Goal1),
GoalInfo = GoalInfo0,
Info = Info3
).
simplify__goal_2(some(Vars1, CanRemove0, Goal1), SomeInfo,
Goal, GoalInfo, Info0, Info) :-
simplify__goal(Goal1, Goal2, Info0, Info),
simplify__nested_somes(CanRemove0, Vars1, Goal2,
CanRemove, Vars, Goal3),
Goal3 = GoalExpr3 - GoalInfo3,
(
goal_info_get_determinism(GoalInfo3, Detism),
goal_info_get_determinism(SomeInfo, Detism),
CanRemove = can_remove
->
% If the inner and outer detisms match the `some'
% is unnecessary.
Goal = GoalExpr3,
GoalInfo = GoalInfo3
;
Goal = some(Vars, CanRemove, Goal3),
GoalInfo = SomeInfo
).
simplify__goal_2(Goal0, GoalInfo, Goal, GoalInfo, Info0, Info) :-
Goal0 = pragma_c_code(_, PredId, ProcId, Args, _, _, _),
(
simplify_do_calls(Info0),
goal_info_is_pure(GoalInfo)
->
common__optimise_call(PredId, ProcId, Args, Goal0,
GoalInfo, Goal, Info0, Info)
;
Info = Info0,
Goal = Goal0
).
%-----------------------------------------------------------------------------%
:- pred simplify__process_compl_unify(prog_var, prog_var,
uni_mode, can_fail, list(prog_var), unify_context,
hlds_goal_info, hlds_goal, simplify_info, simplify_info).
:- mode simplify__process_compl_unify(in, in, in, in, in, in, in, out,
in, out) is det.
simplify__process_compl_unify(XVar, YVar, UniMode, CanFail, OldTypeInfoVars,
Context, GoalInfo0, Goal) -->
=(Info0),
{ simplify_info_get_module_info(Info0, ModuleInfo) },
{ simplify_info_get_var_types(Info0, VarTypes) },
{ map__lookup(VarTypes, XVar, Type) },
( { Type = term__variable(TypeVar) } ->
%
% Convert polymorphic unifications into calls to
% `unify/2', the general unification predicate, passing
% the appropriate type_info
% unify(TypeInfoVar, X, Y)
% where TypeInfoVar is the type_info variable
% associated with the type of the variables that
% are being unified.
%
simplify__type_info_locn(TypeVar, TypeInfoVar, ExtraGoals),
{ ArgVars = [TypeInfoVar, XVar, YVar] },
% sanity check: the TypeInfoVars we computed here should
% match with what was stored in the complicated_unify struct
{ require(unify(OldTypeInfoVars, [TypeInfoVar]),
"simplify__process_compl_unify: mismatched type_info vars") },
{ module_info_get_predicate_table(ModuleInfo,
PredicateTable) },
{ mercury_public_builtin_module(MercuryBuiltin) },
{ predicate_table_search_pred_m_n_a(PredicateTable,
MercuryBuiltin, "unify", 2, [CallPredId])
->
PredId = CallPredId
;
error("simplify.m: can't find `builtin:unify/2'")
},
% Note: the mode for polymorphic unifications
% should be `in, in'.
% (This should have been checked by mode analysis.)
{ hlds_pred__in_in_unification_proc_id(ProcId) },
{ SymName = unqualified("unify") },
{ code_util__builtin_state(ModuleInfo, PredId, ProcId,
BuiltinState) },
{ CallContext = call_unify_context(XVar, var(YVar), Context) },
{ Call = call(PredId, ProcId, ArgVars,
BuiltinState, yes(CallContext), SymName)
- GoalInfo0 }
; { type_is_higher_order(Type, _, _, _) } ->
%
% convert higher-order unifications into calls to
% builtin_unify_pred (which calls error/1)
%
{ SymName = unqualified("builtin_unify_pred") },
{ ArgVars = [XVar, YVar] },
{ module_info_get_predicate_table(ModuleInfo,
PredicateTable) },
{
mercury_private_builtin_module(PrivateBuiltin),
predicate_table_search_pred_m_n_a(
PredicateTable,
PrivateBuiltin, "builtin_unify_pred", 2,
[PredId0])
->
PredId = PredId0
;
error("can't locate private_builtin:builtin_unify_pred/2")
},
{ hlds_pred__in_in_unification_proc_id(ProcId) },
{ CallContext = call_unify_context(XVar, var(YVar), Context) },
{ Call0 = call(PredId, ProcId, ArgVars, not_builtin,
yes(CallContext), SymName) },
simplify__goal_2(Call0, GoalInfo0, Call1, GoalInfo),
{ Call = Call1 - GoalInfo },
{ ExtraGoals = [] }
; { type_to_type_id(Type, TypeId, TypeArgs) } ->
%
% Convert other complicated unifications into
% calls to specific unification predicates,
% inserting extra typeinfo arguments if necessary.
%
% generate code to construct the new type_info arguments
simplify__make_type_info_vars(TypeArgs, TypeInfoVars,
ExtraGoals),
% create the new call goal
{ list__append(TypeInfoVars, [XVar, YVar], ArgVars) },
{ module_info_get_special_pred_map(ModuleInfo,
SpecialPredMap) },
{ map__lookup(SpecialPredMap, unify - TypeId, PredId) },
{ determinism_components(Det, CanFail, at_most_one) },
{ unify_proc__lookup_mode_num(ModuleInfo, TypeId,
UniMode, Det, ProcId) },
{ SymName = unqualified("__Unify__") },
{ CallContext = call_unify_context(XVar, var(YVar), Context) },
{ Call0 = call(PredId, ProcId, ArgVars, not_builtin,
yes(CallContext), SymName) },
% add the extra type_info vars to the nonlocals for the call
{ goal_info_get_nonlocals(GoalInfo0, NonLocals0) },
{ set__insert_list(NonLocals0, TypeInfoVars, NonLocals) },
{ goal_info_set_nonlocals(GoalInfo0, NonLocals,
CallGoalInfo0) },
% recursively simplify the call goal
simplify__goal_2(Call0, CallGoalInfo0, Call1, CallGoalInfo1),
{ Call = Call1 - CallGoalInfo1 }
;
{ error("simplify: type_to_type_id failed") }
),
{ list__append(ExtraGoals, [Call], ConjList) },
{ conj_list_to_goal(ConjList, GoalInfo0, Goal) }.
:- pred simplify__make_type_info_vars(list(type)::in, list(prog_var)::out,
list(hlds_goal)::out, simplify_info::in, simplify_info::out) is det.
simplify__make_type_info_vars(Types, TypeInfoVars, TypeInfoGoals,
Info0, Info) :-
%
% Extract the information from simplify_info
%
simplify_info_get_det_info(Info0, DetInfo0),
simplify_info_get_varset(Info0, VarSet0),
simplify_info_get_var_types(Info0, VarTypes0),
det_info_get_module_info(DetInfo0, ModuleInfo0),
det_info_get_pred_id(DetInfo0, PredId),
det_info_get_proc_id(DetInfo0, ProcId),
%
% Put the varset and vartypes from the simplify_info
% back in the proc_info
%
module_info_pred_proc_info(ModuleInfo0, PredId, ProcId,
PredInfo0, ProcInfo0),
proc_info_set_vartypes(ProcInfo0, VarTypes0, ProcInfo1),
proc_info_set_varset(ProcInfo1, VarSet0, ProcInfo2),
%
% Call polymorphism.m to create the type_infos
%
create_poly_info(ModuleInfo0, PredInfo0, ProcInfo2, PolyInfo0),
ExistQVars = [],
term__context_init(Context),
polymorphism__make_type_info_vars(Types, ExistQVars, Context,
TypeInfoVars, TypeInfoGoals, PolyInfo0, PolyInfo),
poly_info_extract(PolyInfo, PredInfo0, PredInfo,
ProcInfo0, ProcInfo, ModuleInfo1),
%
% Get the new varset and vartypes from the proc_info
% and put them back in the simplify_info.
%
proc_info_vartypes(ProcInfo, VarTypes),
proc_info_varset(ProcInfo, VarSet),
simplify_info_set_var_types(Info0, VarTypes, Info1),
simplify_info_set_varset(Info1, VarSet, Info2),
%
% Put the new proc_info and pred_info back
% in the module_info and put the new module_info
% back in the simplify_info.
%
module_info_set_pred_proc_info(ModuleInfo1, PredId, ProcId,
PredInfo, ProcInfo, ModuleInfo),
simplify_info_set_module_info(Info2, ModuleInfo, Info).
:- pred simplify__type_info_locn(tvar, prog_var, list(hlds_goal),
simplify_info, simplify_info).
:- mode simplify__type_info_locn(in, out, out, in, out) is det.
simplify__type_info_locn(TypeVar, TypeInfoVar, Goals) -->
=(Info0),
{ simplify_info_get_typeinfo_map(Info0, TypeInfoMap) },
{ map__lookup(TypeInfoMap, TypeVar, TypeInfoLocn) },
(
% If the typeinfo is available in a variable,
% just use it
{ TypeInfoLocn = type_info(TypeInfoVar) },
{ Goals = [] }
;
% If the typeinfo is in a typeclass_info
% then we need to extract it
{ TypeInfoLocn =
typeclass_info(TypeClassInfoVar, Index) },
simplify__extract_type_info(TypeVar, TypeClassInfoVar, Index,
Goals, TypeInfoVar)
).
:- pred simplify__extract_type_info(tvar, prog_var, int,
list(hlds_goal), prog_var, simplify_info, simplify_info).
:- mode simplify__extract_type_info(in, in, in, out, out, in, out) is det.
simplify__extract_type_info(TypeVar, TypeClassInfoVar, Index,
Goals, TypeInfoVar, Info0, Info) :-
simplify_info_get_module_info(Info0, ModuleInfo),
simplify_info_get_varset(Info0, VarSet0),
simplify_info_get_var_types(Info0, VarTypes0),
simplify_info_get_typeinfo_map(Info0, TypeInfoLocns0),
polymorphism__gen_extract_type_info(TypeVar, TypeClassInfoVar, Index,
ModuleInfo, Goals, TypeInfoVar,
VarSet0, VarTypes0, TypeInfoLocns0,
VarSet, VarTypes, _TypeInfoLocns),
simplify_info_set_var_types(Info0, VarTypes, Info1),
simplify_info_set_varset(Info1, VarSet, Info).
%-----------------------------------------------------------------------------%
% simplify__input_args_are_equiv(Args, HeadVars, Modes,
% CommonInfo, ModuleInfo1):
% Succeeds if all the input arguments (determined by looking at
% `Modes') in `Args' are equivalent (according to the equivalence
% class specified by `CommonInfo') to the corresponding variables
% in HeadVars. HeadVars, Modes, and Args should all be lists of
% the same length.
:- pred simplify__input_args_are_equiv(list(prog_var), list(prog_var),
list(mode), common_info, module_info).
:- mode simplify__input_args_are_equiv(in, in, in, in, in) is semidet.
simplify__input_args_are_equiv([], [], _, _, _).
simplify__input_args_are_equiv([Arg|Args], [HeadVar|HeadVars], [Mode|Modes],
CommonInfo, ModuleInfo1) :-
( mode_is_input(ModuleInfo1, Mode) ->
common__vars_are_equivalent(Arg, HeadVar, CommonInfo)
;
true
),
simplify__input_args_are_equiv(Args, HeadVars, Modes,
CommonInfo, ModuleInfo1).
%-----------------------------------------------------------------------------%
% replace nested `some's with a single `some',
:- pred simplify__nested_somes(can_remove::in, list(prog_var)::in,
hlds_goal::in, can_remove::out, list(prog_var)::out,
hlds_goal::out) is det.
simplify__nested_somes(CanRemove0, Vars0, Goal0, CanRemove, Vars, Goal) :-
( Goal0 = some(Vars1, CanRemove1, Goal1) - _ ->
(
( CanRemove0 = cannot_remove
; CanRemove1 = cannot_remove
)
->
CanRemove2 = cannot_remove
;
CanRemove2 = can_remove
),
list__append(Vars0, Vars1, Vars2),
simplify__nested_somes(CanRemove2, Vars2, Goal1,
CanRemove, Vars, Goal)
;
CanRemove = CanRemove0,
Vars = Vars0,
Goal = Goal0
).
%-----------------------------------------------------------------------------%
% When removing a level of wrapping around a goal,
% if the determinisms are not the same, we really
% need to rerun determinism analysis on the
% procedure. I think this is a similar situation
% to inlining of erroneous goals. The safe thing
% to do is to wrap a `some' around the inner goal if
% the inner and outer determinisms are not the same.
% It probably won't happen that often.
:- pred simplify__maybe_wrap_goal(hlds_goal_info::in, hlds_goal_info::in,
hlds_goal_expr::in, hlds_goal_expr::out, hlds_goal_info::out,
simplify_info::in, simplify_info::out) is det.
simplify__maybe_wrap_goal(OuterGoalInfo, InnerGoalInfo,
Goal1, Goal, GoalInfo, Info0, Info) :-
(
goal_info_get_determinism(InnerGoalInfo, Det),
goal_info_get_determinism(OuterGoalInfo, Det)
->
Goal = Goal1,
GoalInfo = InnerGoalInfo,
Info = Info0
;
Goal = some([], can_remove, Goal1 - InnerGoalInfo),
GoalInfo = OuterGoalInfo,
simplify_info_set_rerun_det(Info0, Info)
).
%-----------------------------------------------------------------------------%
:- pred simplify__conj(list(hlds_goal), list(hlds_goal),
list(hlds_goal), hlds_goal_info,
simplify_info, simplify_info).
:- mode simplify__conj(in, in, out, in, in, out) is det.
simplify__conj([], RevGoals, Goals, _, Info, Info) :-
list__reverse(RevGoals, Goals).
simplify__conj([Goal0 | Goals0], RevGoals0, Goals, ConjInfo, Info0, Info) :-
% Flatten conjunctions.
( Goal0 = conj(SubGoals) - _ ->
list__append(SubGoals, Goals0, Goals1),
simplify__conj(Goals1, RevGoals0, Goals, ConjInfo, Info0, Info)
;
simplify__goal(Goal0, Goal1, Info0, Info1),
(
% Flatten conjunctions.
Goal1 = conj(SubGoals1) - _
->
simplify_info_undo_goal_updates(Info0, Info1, Info2),
list__append(SubGoals1, Goals0, Goals1),
simplify__conj(Goals1, RevGoals0, Goals, ConjInfo, Info2, Info)
;
% Delete unreachable goals.
(
simplify_info_get_instmap(Info1, InstMap1),
instmap__is_unreachable(InstMap1)
;
Goal1 = _ - GoalInfo1,
goal_info_get_determinism(GoalInfo1, Detism1),
determinism_components(Detism1, _, at_most_zero)
)
->
Info = Info1,
simplify__conjoin_goal_and_rev_goal_list(Goal1,
RevGoals0, RevGoals1),
( (Goal1 = disj([], _) - _ ; Goals0 = []) ->
RevGoals = RevGoals1
;
% We insert an explicit failure at the end
% of the non-succeeding conjunction. This
% is necessary, since the unreachability of
% the instmap could have been derived using
% inferred determinism information. Without the
% explicit fail goal, mode errors could result if mode
% analysis is rerun, since according to the language
% specification, mode analysis does not use inferred
% determinism information when deciding what can
% never succeed.
Goal0 = _ - GoalInfo0,
goal_info_get_context(GoalInfo0, Context),
fail_goal(Context, Fail),
simplify__conjoin_goal_and_rev_goal_list(Fail,
RevGoals1, RevGoals)
),
list__reverse(RevGoals, Goals)
;
simplify__excess_assigns(Goal1, ConjInfo,
Goals0, Goals1, RevGoals0, RevGoals1,
GoalNeeded, Info1, Info2),
( GoalNeeded = yes ->
simplify__conjoin_goal_and_rev_goal_list(Goal1,
RevGoals1, RevGoals2)
;
RevGoals2 = RevGoals1
),
simplify_info_update_instmap(Info2, Goal1, Info3),
simplify__conj(Goals1, RevGoals2, Goals,
ConjInfo, Info3, Info)
)
).
:- pred simplify__conjoin_goal_and_rev_goal_list(hlds_goal::in,
hlds_goals::in, hlds_goals::out) is det.
simplify__conjoin_goal_and_rev_goal_list(Goal, RevGoals0, RevGoals) :-
( Goal = conj(Goals) - _ ->
list__reverse(Goals, Goals1),
list__append(Goals1, RevGoals0, RevGoals)
;
RevGoals = [Goal | RevGoals0]
).
%-----------------------------------------------------------------------------%
:- pred simplify__par_conj(list(hlds_goal), list(hlds_goal),
simplify_info, simplify_info, simplify_info).
:- mode simplify__par_conj(in, out, in, in, out) is det.
simplify__par_conj([], [], _, Info, Info).
simplify__par_conj([Goal0 |Goals0], [Goal | Goals], Info0, Info1, Info) :-
simplify__goal(Goal0, Goal, Info1, Info2),
simplify_info_post_branch_update(Info0, Info2, Info3),
simplify__par_conj(Goals0, Goals, Info0, Info3, Info).
%-----------------------------------------------------------------------------%
:- pred simplify__excess_assigns(hlds_goal::in, hlds_goal_info::in,
hlds_goals::in, hlds_goals::out,
hlds_goals::in, hlds_goals::out, bool::out,
simplify_info::in, simplify_info::out) is det.
simplify__excess_assigns(Goal0, ConjInfo, Goals0, Goals,
RevGoals0, RevGoals, GoalNeeded, Info0, Info) :-
(
simplify_do_excess_assigns(Info0),
Goal0 = unify(_, _, _, Unif, _) - _,
goal_info_get_nonlocals(ConjInfo, NonLocals),
Unif = assign(LeftVar, RightVar),
( \+ set__member(LeftVar, NonLocals) ->
LocalVar = LeftVar, ReplacementVar = RightVar
; \+ set__member(RightVar, NonLocals) ->
LocalVar = RightVar, ReplacementVar = LeftVar
;
fail
)
->
GoalNeeded = no,
map__init(Subn0),
map__det_insert(Subn0, LocalVar, ReplacementVar, Subn),
goal_util__rename_vars_in_goals(Goals0, no,
Subn, Goals),
goal_util__rename_vars_in_goals(RevGoals0, no,
Subn, RevGoals),
simplify_info_get_varset(Info0, VarSet0),
varset__delete_var(VarSet0, LocalVar, VarSet),
simplify_info_set_varset(Info0, VarSet, Info)
;
GoalNeeded = yes,
Goals = Goals0,
RevGoals = RevGoals0,
Info = Info0
).
%-----------------------------------------------------------------------------%
:- pred simplify__switch(prog_var, list(case), list(case), list(case),
list(instmap_delta), list(instmap_delta), can_fail, can_fail,
simplify_info, simplify_info, simplify_info).
:- mode simplify__switch(in, in, in, out, in, out, in, out,
in, in, out) is det.
simplify__switch(_, [], RevCases, Cases, InstMaps, InstMaps,
CanFail, CanFail, _, Info, Info) :-
list__reverse(RevCases, Cases).
simplify__switch(Var, [Case0 | Cases0], RevCases0, Cases, InstMaps0, InstMaps,
CanFail0, CanFail, Info0, Info1, Info) :-
simplify_info_get_instmap(Info0, InstMap0),
Case0 = case(ConsId, Goal0),
simplify_info_get_module_info(Info1, ModuleInfo0),
instmap__bind_var_to_functor(Var, ConsId,
InstMap0, InstMap1, ModuleInfo0, ModuleInfo1),
simplify_info_set_module_info(Info1, ModuleInfo1, Info2),
simplify_info_set_instmap(Info2, InstMap1, Info3),
simplify__goal(Goal0, Goal, Info3, Info4),
% Remove failing branches.
( Goal = disj([], _) - _ ->
RevCases = RevCases0,
InstMaps1 = InstMaps0,
CanFail1 = can_fail,
Info5 = Info4
;
Case = case(ConsId, Goal),
Goal = _ - GoalInfo,
%
% Make sure the switched on variable appears in the
% instmap delta. This avoids an abort in merge_instmap_delta
% if another branch further instantiates the switched-on
% variable. If the switched on variable does not appear in
% this branch's instmap_delta, the inst before the goal
% would be used, resulting in a mode error.
%
goal_info_get_instmap_delta(GoalInfo, InstMapDelta0),
simplify_info_get_module_info(Info4, ModuleInfo5),
instmap_delta_bind_var_to_functor(Var, ConsId,
InstMap0, InstMapDelta0, InstMapDelta,
ModuleInfo5, ModuleInfo),
simplify_info_set_module_info(Info4, ModuleInfo, Info5),
InstMaps1 = [InstMapDelta | InstMaps0],
RevCases = [Case | RevCases0],
CanFail1 = CanFail0
),
simplify_info_post_branch_update(Info0, Info5, Info6),
simplify__switch(Var, Cases0, RevCases, Cases, InstMaps1, InstMaps,
CanFail1, CanFail, Info0, Info6, Info).
% Create a semidet unification at the start of a singleton case
% in a can_fail switch.
:- pred simplify__create_test_unification(prog_var::in, cons_id::in, int::in,
hlds_goal::out, simplify_info::in, simplify_info::out) is det.
simplify__create_test_unification(Var, ConsId, ConsArity,
ExtraGoal - ExtraGoalInfo, Info0, Info) :-
simplify_info_get_varset(Info0, VarSet0),
simplify_info_get_var_types(Info0, VarTypes0),
varset__new_vars(VarSet0, ConsArity, ArgVars, VarSet),
map__lookup(VarTypes0, Var, VarType),
simplify_info_get_module_info(Info0, ModuleInfo),
type_util__get_cons_id_arg_types(ModuleInfo,
VarType, ConsId, ArgTypes),
map__det_insert_from_corresponding_lists(VarTypes0, ArgVars,
ArgTypes, VarTypes),
simplify_info_set_varset(Info0, VarSet, Info1),
simplify_info_set_var_types(Info1, VarTypes, Info),
simplify_info_get_instmap(Info, InstMap),
instmap__lookup_var(InstMap, Var, Inst0),
(
inst_expand(ModuleInfo, Inst0, Inst1),
get_arg_insts(Inst1, ConsId, ConsArity, ArgInsts1)
->
ArgInsts = ArgInsts1
;
error("simplify__goal_2 - get_arg_insts failed")
),
InstToUniMode =
lambda([ArgInst::in, ArgUniMode::out] is det, (
ArgUniMode = ((ArgInst - free) -> (ArgInst - ArgInst))
)),
list__map(InstToUniMode, ArgInsts, UniModes),
UniMode = (Inst0 -> Inst0) - (Inst0 -> Inst0),
UnifyContext = unify_context(explicit, []),
Unification = deconstruct(Var, ConsId,
ArgVars, UniModes, can_fail),
ExtraGoal = unify(Var, functor(ConsId, ArgVars),
UniMode, Unification, UnifyContext),
set__singleton_set(NonLocals, Var),
% The test can't bind any variables, so the
% InstMapDelta should be empty.
instmap_delta_init_reachable(InstMapDelta),
goal_info_init(NonLocals, InstMapDelta, semidet, ExtraGoalInfo).
%-----------------------------------------------------------------------------%
:- pred simplify__disj(list(hlds_goal), list(hlds_goal), list(hlds_goal),
list(instmap_delta), list(instmap_delta),
simplify_info, simplify_info, simplify_info).
:- mode simplify__disj(in, in, out, in, out, in, in, out) is det.
simplify__disj([], RevGoals, Goals, InstMaps, InstMaps, _, Info, Info) :-
list__reverse(RevGoals, Goals).
simplify__disj([Goal0 | Goals0], RevGoals0, Goals, PostBranchInstMaps0,
PostBranchInstMaps, Info0, Info1, Info) :-
simplify__goal(Goal0, Goal, Info1, Info2),
Goal = _ - GoalInfo,
(
% Don't prune or warn about impure disjuncts
% that can't succeed.
\+ goal_info_is_impure(GoalInfo),
goal_info_get_determinism(GoalInfo, Detism),
determinism_components(Detism, _CanFail, MaxSolns),
MaxSolns = at_most_zero
->
(
simplify_do_warn(Info2),
% Don't warn where the initial goal was fail,
% since that can result from mode analysis
% pruning away cases in a switch which cannot
% succeed due to sub-typing in the modes.
Goal0 \= disj([], _) - _
->
goal_info_get_context(GoalInfo, Context),
simplify_info_add_msg(Info2,
zero_soln_disjunct(Context), Info3)
;
Info3 = Info2
),
%
% Prune away non-succeeding disjuncts where possible.
%
(
(
Goal0 = disj([], _) - _
;
% Only remove disjuncts that might loop
% or call error/1 if --no-fully-strict.
simplify_info_get_det_info(Info3, DetInfo),
det_info_get_fully_strict(DetInfo, no)
)
->
RevGoals1 = RevGoals0,
PostBranchInstMaps1 = PostBranchInstMaps0
;
RevGoals1 = [Goal | RevGoals0],
goal_info_get_instmap_delta(GoalInfo, InstMapDelta),
PostBranchInstMaps1 =
[InstMapDelta | PostBranchInstMaps0]
)
;
Info3 = Info2,
RevGoals1 = [Goal | RevGoals0],
goal_info_get_instmap_delta(GoalInfo, InstMapDelta),
PostBranchInstMaps1 = [InstMapDelta | PostBranchInstMaps0]
),
simplify_info_post_branch_update(Info0, Info3, Info4),
simplify__disj(Goals0, RevGoals1, Goals, PostBranchInstMaps1,
PostBranchInstMaps, Info0, Info4, Info).
% Disjunctions that cannot succeed more than once when viewed from the
% outside generally need some fixing up, and/or some warnings to be
% issued.
% We previously converted them all to if-then-elses using the code
% below, however converting disjs that have output variables but
% that nevertheless cannot succeed more than one
% (e.g. cc_nondet or cc_multi disjs) into if-then-elses
% may cause problems with other parts of the compiler that
% assume that an if-then-else is mode-correct, i.e. that
% the condition doesn't bind variables.
/****
goal_info_get_determinism(GoalInfo, Detism),
determinism_components(Detism, _CanFail, MaxSoln),
MaxSoln \= at_most_many
->
goal_info_get_instmap_delta(GoalInfo, DeltaInstMap),
goal_info_get_nonlocals(GoalInfo, NonLocalVars),
(
det_no_output_vars(NonLocalVars, InstMap0,
DeltaInstMap, DetInfo)
->
OutputVars = no
;
OutputVars = yes
),
simplify__fixup_disj(Disjuncts, Detism, OutputVars,
GoalInfo, SM, InstMap0, DetInfo, Goal,
MsgsA, Msgs)
;
****/
:- pred simplify__fixup_disj(list(hlds_goal), determinism, bool,
hlds_goal_info, follow_vars, hlds_goal_expr,
simplify_info, simplify_info).
:- mode simplify__fixup_disj(in, in, in, in, in, out, in, out) is det.
simplify__fixup_disj(Disjuncts, _, _OutputVars, GoalInfo, SM,
Goal, Info0, Info) :-
det_disj_to_ite(Disjuncts, GoalInfo, SM, IfThenElse),
simplify__goal(IfThenElse, Simplified, Info0, Info),
Simplified = Goal - _.
% det_disj_to_ite is used to transform disjunctions that occur
% in prunable contexts into if-then-elses.
% For example, it would transform
%
% ( Disjunct1
% ; Disjunct2
% ; Disjunct3
% )
% into
% ( Disjunct1 ->
% true
% ; Disjunct2 ->
% true
% ;
% Disjunct3
% ).
:- pred det_disj_to_ite(list(hlds_goal), hlds_goal_info, follow_vars,
hlds_goal).
:- mode det_disj_to_ite(in, in, in, out) is det.
det_disj_to_ite([], _GoalInfo, _SM, _) :-
error("reached base case of det_disj_to_ite").
det_disj_to_ite([Disjunct | Disjuncts], GoalInfo, SM, Goal) :-
( Disjuncts = [] ->
Goal = Disjunct
;
Cond = Disjunct,
Cond = _CondGoal - CondGoalInfo,
true_goal(Then),
det_disj_to_ite(Disjuncts, GoalInfo, SM, Rest),
Rest = _RestGoal - RestGoalInfo,
goal_info_get_nonlocals(CondGoalInfo, CondNonLocals),
goal_info_get_nonlocals(RestGoalInfo, RestNonLocals),
set__union(CondNonLocals, RestNonLocals, NonLocals),
goal_info_set_nonlocals(GoalInfo, NonLocals, NewGoalInfo0),
goal_info_get_instmap_delta(GoalInfo, InstMapDelta0),
instmap_delta_restrict(InstMapDelta0, NonLocals, InstMapDelta),
goal_info_set_instmap_delta(NewGoalInfo0, InstMapDelta,
NewGoalInfo1),
goal_info_get_determinism(CondGoalInfo, CondDetism),
goal_info_get_determinism(RestGoalInfo, RestDetism),
determinism_components(CondDetism, CondCanFail, CondMaxSoln),
determinism_components(RestDetism, RestCanFail, RestMaxSoln),
det_disjunction_canfail(CondCanFail, RestCanFail, CanFail),
det_disjunction_maxsoln(CondMaxSoln, RestMaxSoln, MaxSoln0),
( MaxSoln0 = at_most_many ->
MaxSoln = at_most_one
;
MaxSoln = MaxSoln0
),
determinism_components(Detism, CanFail, MaxSoln),
goal_info_set_determinism(NewGoalInfo1, Detism, NewGoalInfo),
Goal = if_then_else([], Cond, Then, Rest, SM) - NewGoalInfo
).
%-----------------------------------------------------------------------------%
:- pred simplify__contains_multisoln_goal(list(hlds_goal)::in) is semidet.
simplify__contains_multisoln_goal(Goals) :-
list__member(_Goal - GoalInfo, Goals),
goal_info_get_determinism(GoalInfo, Detism),
determinism_components(Detism, _, at_most_many).
%-----------------------------------------------------------------------------%
:- type simplify_info
---> simplify_info(
det_info,
set(det_msg),
set(simplification),
common_info, % Info about common subexpressions.
instmap,
prog_varset,
map(prog_var, type),
bool, % Does the goal need requantification.
bool, % Do we need to recompute
% instmap_deltas for atomic goals
bool, % Does determinism analysis need to
% be rerun.
int, % Measure of the improvement in
% the goal from simplification.
int % Count of the number of lambdas
% which enclose the current goal.
).
simplify_info_init(DetInfo, Simplifications0, InstMap,
VarSet, VarTypes, Info) :-
common_info_init(CommonInfo),
set__init(Msgs),
set__list_to_set(Simplifications0, Simplifications),
Info = simplify_info(DetInfo, Msgs, Simplifications, CommonInfo,
InstMap, VarSet, VarTypes, no, no, no, 0, 0).
% Reinitialise the simplify_info before reprocessing a goal.
:- pred simplify_info_reinit(set(simplification)::in, instmap::in,
simplify_info::in, simplify_info::out) is det.
simplify_info_reinit(Simplifications, InstMap0, Info0, Info) :-
Info0 = simplify_info(DetInfo, Msgs, _, _, _,
VarSet, VarTypes, _, _, _, CostDelta, _),
common_info_init(Common),
Info = simplify_info(DetInfo, Msgs, Simplifications, Common, InstMap0,
VarSet, VarTypes, no, no, no, CostDelta, 0).
% exported for common.m
:- interface.
:- import_module prog_data.
:- import_module set.
:- pred simplify_info_init(det_info, list(simplification), instmap,
prog_varset, map(prog_var, type), simplify_info).
:- mode simplify_info_init(in, in, in, in, in, out) is det.
:- pred simplify_info_get_det_info(simplify_info::in, det_info::out) is det.
:- pred simplify_info_get_msgs(simplify_info::in, set(det_msg)::out) is det.
:- pred simplify_info_get_instmap(simplify_info::in, instmap::out) is det.
:- pred simplify_info_get_simplifications(simplify_info::in,
set(simplification)::out) is det.
:- pred simplify_info_get_common_info(simplify_info::in,
common_info::out) is det.
:- pred simplify_info_get_varset(simplify_info::in, prog_varset::out) is det.
:- pred simplify_info_get_var_types(simplify_info::in,
map(prog_var, type)::out) is det.
:- pred simplify_info_requantify(simplify_info::in) is semidet.
:- pred simplify_info_recompute_atomic(simplify_info::in) is semidet.
:- pred simplify_info_rerun_det(simplify_info::in) is semidet.
:- pred simplify_info_get_cost_delta(simplify_info::in, int::out) is det.
:- pred simplify_info_get_module_info(simplify_info::in,
module_info::out) is det.
:- implementation.
simplify_info_get_det_info(simplify_info(Det, _,_,_,_,_,_,_,_,_,_,_), Det).
simplify_info_get_msgs(simplify_info(_, Msgs, _,_,_,_,_,_,_,_,_,_), Msgs).
simplify_info_get_simplifications(simplify_info(_,_,Simplify,_,_,_,_,_,_,_,_,_),
Simplify).
simplify_info_get_common_info(simplify_info(_,_,_,Common, _,_,_,_,_,_,_,_),
Common).
simplify_info_get_instmap(simplify_info(_,_,_,_, InstMap,_,_,_,_,_,_,_),
InstMap).
simplify_info_get_varset(simplify_info(_,_,_,_,_, VarSet, _,_,_,_,_,_), VarSet).
simplify_info_get_var_types(simplify_info(_,_,_,_,_,_, VarTypes, _,_,_,_,_),
VarTypes).
simplify_info_requantify(simplify_info(_,_,_,_,_,_,_, yes, _,_,_,_)).
simplify_info_recompute_atomic(simplify_info(_,_,_,_,_,_,_,_, yes,_,_,_)).
simplify_info_rerun_det(simplify_info(_,_,_,_,_,_,_,_,_, yes,_,_)).
simplify_info_get_cost_delta(simplify_info(_,_,_,_,_,_,_,_,_,_,CostDelta, _),
CostDelta).
simplify_info_get_module_info(Info, ModuleInfo) :-
simplify_info_get_det_info(Info, DetInfo),
det_info_get_module_info(DetInfo, ModuleInfo).
:- interface.
:- pred simplify_info_set_det_info(simplify_info::in,
det_info::in, simplify_info::out) is det.
:- pred simplify_info_set_msgs(simplify_info::in,
set(det_msg)::in, simplify_info::out) is det.
:- pred simplify_info_set_simplifications(simplify_info::in,
set(simplification)::in, simplify_info::out) is det.
:- pred simplify_info_set_instmap(simplify_info::in,
instmap::in, simplify_info::out) is det.
:- pred simplify_info_set_common_info(simplify_info::in, common_info::in,
simplify_info::out) is det.
:- pred simplify_info_set_varset(simplify_info::in, prog_varset::in,
simplify_info::out) is det.
:- pred simplify_info_set_var_types(simplify_info::in, map(prog_var, type)::in,
simplify_info::out) is det.
:- pred simplify_info_set_requantify(simplify_info::in,
simplify_info::out) is det.
:- pred simplify_info_set_recompute_atomic(simplify_info::in,
simplify_info::out) is det.
:- pred simplify_info_set_rerun_det(simplify_info::in,
simplify_info::out) is det.
:- pred simplify_info_add_msg(simplify_info::in, det_msg::in,
simplify_info::out) is det.
:- pred simplify_info_do_add_msg(simplify_info::in, det_msg::in,
simplify_info::out) is det.
:- pred simplify_info_set_cost_delta(simplify_info::in, int::in,
simplify_info::out) is det.
:- pred simplify_info_incr_cost_delta(simplify_info::in,
int::in, simplify_info::out) is det.
:- pred simplify_info_enter_lambda(simplify_info::in, simplify_info::out)
is det.
:- pred simplify_info_leave_lambda(simplify_info::in, simplify_info::out)
is det.
:- pred simplify_info_inside_lambda(simplify_info::in) is semidet.
:- pred simplify_info_set_module_info(simplify_info::in,
module_info::in, simplify_info::out) is det.
:- implementation.
simplify_info_set_det_info(simplify_info(_, B, C, D, E, F, G, H, I, J, K, L),
Det, simplify_info(Det, B, C, D, E, F, G, H, I, J, K, L)).
simplify_info_set_msgs(simplify_info(A, _, C, D, E, F, G, H, I, J, K, L), Msgs,
simplify_info(A, Msgs, C, D, E, F, G, H, I, J, K, L)).
simplify_info_set_simplifications(
simplify_info(A, B, _, D, E, F, G, H, I, J, K, L),
Simp, simplify_info(A, B, Simp, D, E, F, G, H, I, J, K, L)).
simplify_info_set_instmap(simplify_info(A, B, C, D, _, F, G, H, I, J, K, L),
InstMap,
simplify_info(A, B, C, D, InstMap, F, G, H, I, J, K, L)).
simplify_info_set_common_info(simplify_info(A, B, C, _, E, F, G, H, I, J, K, L),
Common,
simplify_info(A, B, C, Common, E, F, G, H, I, J, K, L)).
simplify_info_set_varset(simplify_info(A, B, C, D, E, _, G, H, I, J, K, L),
VarSet,
simplify_info(A, B, C, D, E, VarSet, G, H, I, J, K, L)).
simplify_info_set_var_types(simplify_info(A, B, C, D, E, F, _, H, I, J, K, L),
VarTypes, simplify_info(A, B, C, D, E, F, VarTypes, H,I,J,K,L)).
simplify_info_set_requantify(simplify_info(A, B, C, D, E, F, G, _, I, J, K, L),
simplify_info(A, B, C, D, E, F, G, yes, I, J, K, L)).
simplify_info_set_recompute_atomic(simplify_info(A, B, C, D, E, F, G,H,_,J,K,L),
simplify_info(A, B, C, D, E, F, G, H, yes, J, K, L)).
simplify_info_set_rerun_det(simplify_info(A, B, C, D, E, F, G,H,I,_,K,L),
simplify_info(A, B, C, D, E, F, G, H, I, yes, K, L)).
simplify_info_set_cost_delta(simplify_info(A, B, C, D, E, F, G, H, I, J, _, L),
Delta, simplify_info(A, B, C, D, E, F, G, H, I, J, Delta, L)).
simplify_info_incr_cost_delta(
simplify_info(A, B, C, D, E, F,G,H,I,J, Delta0, L),
Incr, simplify_info(A, B, C, D, E, F, G, H, I, J, Delta, L)) :-
Delta is Delta0 + Incr.
simplify_info_add_msg(Info0, Msg, Info) :-
( simplify_do_warn(Info0) ->
simplify_info_do_add_msg(Info0, Msg, Info)
;
Info = Info0
).
simplify_info_do_add_msg(Info0, Msg, Info) :-
simplify_info_get_msgs(Info0, Msgs0),
set__insert(Msgs0, Msg, Msgs),
simplify_info_set_msgs(Info0, Msgs, Info).
simplify_info_enter_lambda(
simplify_info(A, B, C, D, E, F, G, H, I, J, K, LambdaCount0),
simplify_info(A, B, C, D, E, F, G, H, I, J, K, LambdaCount)) :-
LambdaCount is LambdaCount0 + 1.
simplify_info_leave_lambda(
simplify_info(A, B, C, D, E, F, G, H, I, J, K, LambdaCount0),
simplify_info(A, B, C, D, E, F, G, H, I, J, K, LambdaCount)) :-
LambdaCount1 is LambdaCount0 - 1,
(
LambdaCount1 >= 0
->
LambdaCount = LambdaCount1
;
error("simplify_info_leave_lambda: Left too many lambdas")
).
simplify_info_inside_lambda(
simplify_info(_,_,_,_,_,_,_,_,_,_,_,LambdaCount)) :-
LambdaCount > 0.
simplify_info_set_module_info(Info0, ModuleInfo, Info) :-
simplify_info_get_det_info(Info0, DetInfo0),
det_info_set_module_info(DetInfo0, ModuleInfo, DetInfo),
simplify_info_set_det_info(Info0, DetInfo, Info).
:- interface.
:- pred simplify_do_warn(simplify_info::in) is semidet.
:- pred simplify_do_warn_calls(simplify_info::in) is semidet.
:- pred simplify_do_once(simplify_info::in) is semidet.
:- pred simplify_do_common(simplify_info::in) is semidet.
:- pred simplify_do_excess_assigns(simplify_info::in) is semidet.
:- pred simplify_do_calls(simplify_info::in) is semidet.
:- pred simplify_do_const_prop(simplify_info::in) is semidet.
:- pred simplify_do_more_common(simplify_info::in) is semidet.
:- implementation.
simplify_do_warn(Info) :-
simplify_info_get_simplifications(Info, Simplifications),
set__member(warn_simple_code, Simplifications).
simplify_do_warn_calls(Info) :-
simplify_info_get_simplifications(Info, Simplifications),
set__member(warn_duplicate_calls, Simplifications).
simplify_do_once(Info) :-
simplify_info_get_simplifications(Info, Simplifications),
set__member(do_once, Simplifications).
simplify_do_excess_assigns(Info) :-
simplify_info_get_simplifications(Info, Simplifications),
set__member(excess_assigns, Simplifications).
simplify_do_calls(Info) :-
simplify_info_get_simplifications(Info, Simplifications),
set__member(duplicate_calls, Simplifications).
simplify_do_const_prop(Info) :-
simplify_info_get_simplifications(Info, Simplifications),
set__member(constant_prop, Simplifications).
simplify_do_common(Info) :-
simplify_info_get_simplifications(Info, Simplifications),
set__member(common_struct, Simplifications).
simplify_do_more_common(Info) :-
simplify_info_get_simplifications(Info, Simplifications),
set__member(extra_common_struct, Simplifications).
:- pred simplify_info_get_typeinfo_map(simplify_info::in,
map(tvar, type_info_locn)::out) is det.
simplify_info_get_typeinfo_map(Info0, TypeInfoMap) :-
simplify_info_get_det_info(Info0, DetInfo0),
det_info_get_module_info(DetInfo0, ModuleInfo),
det_info_get_pred_id(DetInfo0, ThisPredId),
det_info_get_proc_id(DetInfo0, ThisProcId),
module_info_pred_proc_info(ModuleInfo, ThisPredId, ThisProcId,
_PredInfo, ProcInfo),
proc_info_typeinfo_varmap(ProcInfo, TypeInfoMap).
:- pred simplify_info_update_instmap(simplify_info::in, hlds_goal::in,
simplify_info::out) is det.
simplify_info_update_instmap(
simplify_info(A, B, C, D, InstMap0, F, G, H, I, J, K, L), Goal,
simplify_info(A, B, C, D, InstMap, F, G, H, I, J, K, L)) :-
update_instmap(Goal, InstMap0, InstMap).
:- type before_after
---> before
; after.
% Clear the common_info structs accumulated since the last goal that
% could cause a stack flush. This is done to avoid replacing a
% deconstruction with assignments to the arguments where this
% would cause more variables to be live across the stack flush.
% Calls and construction unifications are not treated in this
% way since it is nearly always better to optimize them away.
% When doing deforestation, it may be better to remove
% as many common structures as possible.
:- pred simplify_info_maybe_clear_structs(before_after::in, hlds_goal::in,
simplify_info::in, simplify_info::out) is det.
simplify_info_maybe_clear_structs(BeforeAfter, Goal, Info0, Info) :-
(
( code_util__cannot_stack_flush(Goal)
; simplify_do_more_common(Info0)
)
->
Info = Info0
;
% First check to see if a call is common and can be replaced
% by a series of unifications.
simplify_do_common(Info0),
(
BeforeAfter = after
;
BeforeAfter = before,
Goal = GoalExpr - _,
GoalExpr \= call(_, _, _, _, _, _),
GoalExpr \= generic_call(_, _, _, _),
GoalExpr \= pragma_c_code(_, _, _, _, _, _, _)
)
->
simplify_info_get_common_info(Info0, CommonInfo0),
common_info_clear_structs(CommonInfo0, CommonInfo),
simplify_info_set_common_info(Info0, CommonInfo, Info)
;
Info = Info0
).
% Reset the instmap and seen calls for the next branch.
:- pred simplify_info_post_branch_update(simplify_info::in, simplify_info::in,
simplify_info::out) is det.
simplify_info_post_branch_update(PreBranchInfo, PostBranchInfo0, Info) :-
simplify_info_get_instmap(PreBranchInfo, InstMap),
simplify_info_set_instmap(PostBranchInfo0, InstMap, PostBranchInfo1),
simplify_info_get_common_info(PreBranchInfo, Common),
simplify_info_set_common_info(PostBranchInfo1, Common, Info).
% Undo updates to the simplify_info before redoing
% simplification on a goal.
:- pred simplify_info_undo_goal_updates(simplify_info::in, simplify_info::in,
simplify_info::out) is det.
simplify_info_undo_goal_updates(Info1, Info2, Info) :-
simplify_info_get_common_info(Info1, CommonInfo0),
simplify_info_set_common_info(Info2, CommonInfo0, Info3),
simplify_info_get_instmap(Info1, InstMap),
simplify_info_set_instmap(Info3, InstMap, Info).
%-----------------------------------------------------------------------------%
:- pred goal_contains_goal(hlds_goal, hlds_goal).
:- mode goal_contains_goal(in, out) is multi.
goal_contains_goal(Goal, Goal).
goal_contains_goal(Goal - _, SubGoal) :-
direct_subgoal(Goal, DirectSubGoal),
goal_contains_goal(DirectSubGoal, SubGoal).
:- pred direct_subgoal(hlds_goal_expr, hlds_goal).
:- mode direct_subgoal(in, out) is nondet.
direct_subgoal(some(_, _, Goal), Goal).
direct_subgoal(not(Goal), Goal).
direct_subgoal(if_then_else(_, If, Then, Else, _), Goal) :-
( Goal = If
; Goal = Then
; Goal = Else
).
direct_subgoal(conj(ConjList), Goal) :-
list__member(Goal, ConjList).
direct_subgoal(par_conj(ConjList, _), Goal) :-
list__member(Goal, ConjList).
direct_subgoal(disj(DisjList, _), Goal) :-
list__member(Goal, DisjList).
direct_subgoal(switch(_, _, CaseList, _), Goal) :-
list__member(Case, CaseList),
Case = case(_, Goal).
%-----------------------------------------------------------------------------%