mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-11 20:03:28 +00:00
Estimated hours taken: 0.2 Branches: main compiler/common.m: Add an XXX about a missed opportunity.
731 lines
26 KiB
Mathematica
731 lines
26 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1995-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.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Original author: squirrel (Jane Anna Langley).
|
|
% Some bugs fixed by fjh.
|
|
% Extensive revision by zs.
|
|
% More revision by stayl.
|
|
%
|
|
% This module attempts to optimise out instances where a variable is
|
|
% decomposed and then soon after reconstructed from the parts. If possible
|
|
% we would like to "short-circuit" this process.
|
|
% It also optimizes deconstructions of known cells, replacing them with
|
|
% assignments to the arguments where this is guaranteed to not increase
|
|
% the number of stack slots required by the goal.
|
|
% Repeated calls to predicates with the same input arguments are replaced by
|
|
% assigments and warnings are returned.
|
|
%
|
|
% IMPORTANT: This module does a small subset of the job of compile-time
|
|
% garbage collection, but it does so without paying attention to uniqueness
|
|
% information, since the compiler does not yet have such information.
|
|
% Once we implement ctgc, the assumptions made by this module will have
|
|
% to be revisited.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds__common.
|
|
:- interface.
|
|
|
|
:- import_module check_hlds__simplify.
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module hlds__hlds_pred.
|
|
:- import_module parse_tree__prog_data.
|
|
|
|
:- import_module list.
|
|
|
|
% If we find a deconstruction or a construction we cannot optimize,
|
|
% record the details of the memory cell in CommonInfo.
|
|
|
|
% If we find a construction that constructs a cell identical to one
|
|
% we have seen before, replace the construction with an assignment
|
|
% from the variable unified with that cell.
|
|
|
|
:- pred common__optimise_unification(unification, prog_var, unify_rhs,
|
|
unify_mode, unify_context, hlds_goal_expr, hlds_goal_info,
|
|
hlds_goal_expr, hlds_goal_info, simplify_info, simplify_info).
|
|
:- mode common__optimise_unification(in, in, in, in, in, in, in,
|
|
out, out, in, out) is det.
|
|
|
|
% Check whether this call has been seen before and is replaceable, if
|
|
% so produce assignment unification for the non-local output variables,
|
|
% and give a warning.
|
|
% A call is considered replaceable if it has no uniquely moded outputs
|
|
% and no destructive inputs.
|
|
% It is the caller's responsibility to check that the call is pure.
|
|
|
|
:- pred common__optimise_call(pred_id, proc_id, list(prog_var), hlds_goal_expr,
|
|
hlds_goal_info, hlds_goal_expr, simplify_info, simplify_info).
|
|
:- mode common__optimise_call(in, in, in, in, in, out, in, out) is det.
|
|
|
|
:- pred common__optimise_higher_order_call(prog_var, list(prog_var), list(mode),
|
|
determinism, hlds_goal_expr, hlds_goal_info, hlds_goal_expr,
|
|
simplify_info, simplify_info).
|
|
:- mode common__optimise_higher_order_call(in, in, in, in, in, in, out,
|
|
in, out) is det.
|
|
|
|
% succeeds if the two variables are equivalent
|
|
% according to the specified equivalence class.
|
|
:- pred common__vars_are_equivalent(prog_var, prog_var, common_info).
|
|
:- mode common__vars_are_equivalent(in, in, in) is semidet.
|
|
|
|
% Assorted stuff used here that simplify.m doesn't need to know about.
|
|
:- type common_info.
|
|
|
|
:- pred common_info_init(common_info).
|
|
:- mode common_info_init(out) is det.
|
|
|
|
:- pred common_info_clear_structs(common_info, common_info).
|
|
:- mode common_info_clear_structs(in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds__det_report.
|
|
:- import_module check_hlds__det_util.
|
|
:- import_module check_hlds__inst_match.
|
|
:- import_module check_hlds__mode_util.
|
|
:- import_module check_hlds__type_util.
|
|
:- import_module hlds__goal_util.
|
|
:- import_module hlds__hlds_data.
|
|
:- import_module hlds__hlds_module.
|
|
:- import_module hlds__instmap.
|
|
:- import_module hlds__quantification.
|
|
:- import_module libs__globals.
|
|
:- import_module libs__options.
|
|
:- import_module parse_tree__inst.
|
|
:- import_module parse_tree__prog_util.
|
|
:- import_module transform_hlds__pd_cost.
|
|
|
|
:- import_module bool, map, set, eqvclass, require, std_util, string, term.
|
|
|
|
:- type structure
|
|
---> structure(prog_var, type, cons_id, list(prog_var)).
|
|
|
|
:- type call_args
|
|
---> call_args(prog_context, list(prog_var), list(prog_var)).
|
|
% input, output args. For higher-order calls,
|
|
% the closure is the first input argument.
|
|
|
|
:- type struct_map == map(cons_id, list(structure)).
|
|
:- type seen_calls == map(seen_call_id, list(call_args)).
|
|
|
|
:- type common_info
|
|
---> common(
|
|
eqvclass(prog_var),
|
|
struct_map, % all structs seen.
|
|
struct_map, % structs seen since the last call.
|
|
seen_calls
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
common_info_init(CommonInfo) :-
|
|
eqvclass__init(VarEqv0),
|
|
map__init(StructMap0),
|
|
map__init(SeenCalls0),
|
|
CommonInfo = common(VarEqv0, StructMap0, StructMap0, SeenCalls0).
|
|
|
|
% Clear structs seen since the last call. Replacing deconstructions
|
|
% of these structs with assignments after the call would cause an
|
|
% increase in the number of stack slots required.
|
|
common_info_clear_structs(common(VarEqv, StructMap, _, SeenCalls),
|
|
common(VarEqv, StructMap, Empty, SeenCalls)) :-
|
|
map__init(Empty).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
common__optimise_unification(Unification0, _Left0, _Right0, Mode, _Context,
|
|
Goal0, GoalInfo0, Goal, GoalInfo, Info0, Info) :-
|
|
(
|
|
Unification0 = construct(Var, ConsId, ArgVars, _, _, _, _),
|
|
Mode = LVarMode - _,
|
|
simplify_info_get_module_info(Info0, ModuleInfo),
|
|
mode_get_insts(ModuleInfo, LVarMode, _, Inst),
|
|
(
|
|
% Don't optimise partially instantiated
|
|
% deconstruction unifications, because it's
|
|
% tricky to work out how to mode the
|
|
% replacement asssignment unifications.
|
|
% In the vast majority of cases, the
|
|
% variable is ground.
|
|
\+ inst_is_ground(ModuleInfo, Inst)
|
|
->
|
|
Goal = Goal0,
|
|
GoalInfo = GoalInfo0,
|
|
Info = Info0
|
|
;
|
|
% common__generate_assign assumes that the
|
|
% output variable is in the instmap_delta, which
|
|
% will not be true if the variable is a local.
|
|
% The optimization is pointless in that case.
|
|
goal_info_get_instmap_delta(GoalInfo0, InstMapDelta),
|
|
instmap_delta_search_var(InstMapDelta, Var, _),
|
|
common__find_matching_cell(Var, ConsId, ArgVars,
|
|
construction, Info0, OldStruct)
|
|
->
|
|
OldStruct = structure(OldVar, _, _, _),
|
|
( ArgVars = [] ->
|
|
% Constants don't use memory, so there's
|
|
% no point optimizing away their
|
|
% construction -- in fact, doing so
|
|
% could cause more stack usage.
|
|
common__record_equivalence(Var, OldVar,
|
|
Info0, Info),
|
|
Goal = Goal0,
|
|
GoalInfo = GoalInfo0
|
|
;
|
|
UniMode = ((free - Inst) -> (Inst - Inst)),
|
|
common__generate_assign(Var, OldVar, UniMode,
|
|
GoalInfo0, Goal - GoalInfo,
|
|
Info0, Info1),
|
|
simplify_info_set_requantify(Info1, Info2),
|
|
pd_cost__goal(Goal0 - GoalInfo0, Cost),
|
|
simplify_info_incr_cost_delta(Info2,
|
|
Cost, Info)
|
|
)
|
|
;
|
|
Goal = Goal0,
|
|
GoalInfo = GoalInfo0,
|
|
common__record_cell(Var, ConsId, ArgVars, Info0, Info)
|
|
)
|
|
;
|
|
Unification0 = deconstruct(Var, ConsId,
|
|
ArgVars, UniModes, CanFail, _),
|
|
simplify_info_get_module_info(Info0, ModuleInfo),
|
|
(
|
|
% Don't optimise partially instantiated
|
|
% deconstruction unifications, because it's
|
|
% tricky to work out how to mode the
|
|
% replacement asssignment unifications.
|
|
% In the vast majority of cases, the
|
|
% variable is ground.
|
|
Mode = LVarMode - _,
|
|
mode_get_insts(ModuleInfo, LVarMode, Inst0, _),
|
|
\+ inst_is_ground(ModuleInfo, Inst0)
|
|
->
|
|
Goal = Goal0,
|
|
Info = Info0
|
|
;
|
|
% Do not delete deconstruction unifications inserted by
|
|
% stack_opt.m, which has done a more comprehensive cost
|
|
% analysis than common.m can do.
|
|
\+ goal_info_has_feature(GoalInfo, stack_opt),
|
|
common__find_matching_cell(Var, ConsId, ArgVars,
|
|
deconstruction, Info0, OldStruct)
|
|
->
|
|
OldStruct = structure(_, _, _, OldArgVars),
|
|
common__create_output_unifications(GoalInfo0, ArgVars,
|
|
OldArgVars, UniModes, Goals, Info0, Info1),
|
|
Goal = conj(Goals),
|
|
pd_cost__goal(Goal0 - GoalInfo0, Cost),
|
|
simplify_info_incr_cost_delta(Info1, Cost, Info2),
|
|
simplify_info_set_requantify(Info2, Info3),
|
|
( CanFail = can_fail ->
|
|
simplify_info_set_rerun_det(Info3, Info)
|
|
;
|
|
Info = Info3
|
|
)
|
|
;
|
|
Goal = Goal0,
|
|
common__record_cell(Var, ConsId, ArgVars, Info0, Info)
|
|
),
|
|
GoalInfo = GoalInfo0
|
|
;
|
|
Unification0 = assign(Var1, Var2),
|
|
Goal = Goal0,
|
|
common__record_equivalence(Var1, Var2, Info0, Info),
|
|
GoalInfo = GoalInfo0
|
|
;
|
|
Unification0 = simple_test(Var1, Var2),
|
|
Goal = Goal0,
|
|
common__record_equivalence(Var1, Var2, Info0, Info),
|
|
GoalInfo = GoalInfo0
|
|
;
|
|
Unification0 = complicated_unify(_, _, _),
|
|
Goal = Goal0,
|
|
Info = Info0,
|
|
GoalInfo = GoalInfo0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type unification_type
|
|
---> deconstruction
|
|
; construction.
|
|
|
|
:- pred common__find_matching_cell(prog_var, cons_id,
|
|
list(prog_var), unification_type, simplify_info, structure).
|
|
:- mode common__find_matching_cell(in, in, in, in, in, out) is semidet.
|
|
|
|
common__find_matching_cell(Var, ConsId, ArgVars, UniType, Info, OldStruct) :-
|
|
simplify_info_get_common_info(Info, CommonInfo),
|
|
simplify_info_get_var_types(Info, VarTypes),
|
|
CommonInfo = common(VarEqv, StructMapAll, StructMapSinceLastFlush, _),
|
|
(
|
|
UniType = construction,
|
|
StructMapToUse = StructMapAll
|
|
;
|
|
% For deconstructions, using the arguments of a cell
|
|
% created before the last stack flush would cause more
|
|
% variables to be saved on the stack.
|
|
UniType = deconstruction,
|
|
StructMapToUse = StructMapSinceLastFlush
|
|
),
|
|
map__search(StructMapToUse, ConsId, Structs),
|
|
common__find_matching_cell_2(Structs, Var, ConsId, ArgVars, UniType,
|
|
VarEqv, VarTypes, OldStruct).
|
|
|
|
:- pred common__find_matching_cell_2(list(structure), prog_var, cons_id,
|
|
list(prog_var),
|
|
unification_type, eqvclass(prog_var), map(prog_var, type), structure).
|
|
:- mode common__find_matching_cell_2(in, in, in, in, in,
|
|
in, in, out) is semidet.
|
|
|
|
common__find_matching_cell_2([Struct | Structs], Var, ConsId, ArgVars,
|
|
UniType, VarEqv, VarTypes, OldStruct) :-
|
|
Struct = structure(OldVar, StructType, StructConsId, StructArgVars),
|
|
(
|
|
% Are the arguments the same (or equivalent) variables?
|
|
ConsId = StructConsId,
|
|
(
|
|
UniType = construction,
|
|
common__var_lists_are_equiv(ArgVars,
|
|
StructArgVars, VarEqv),
|
|
|
|
% Two structures of the same shape may have different
|
|
% types and therefore different representations.
|
|
map__lookup(VarTypes, Var, VarType),
|
|
common__compatible_types(VarType, StructType)
|
|
;
|
|
UniType = deconstruction,
|
|
common__vars_are_equiv(Var, OldVar, VarEqv)
|
|
)
|
|
->
|
|
OldStruct = Struct
|
|
;
|
|
common__find_matching_cell_2(Structs, Var, ConsId, ArgVars,
|
|
UniType, VarEqv, VarTypes, OldStruct)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Two structures have compatible representations if the top
|
|
% level of their types are unifiable. % For example, if we have
|
|
%
|
|
% :- type maybe_err(T) --> ok(T) ; err(string).
|
|
%
|
|
% :- pred p(maybe_err(foo)::in, maybe_err(bar)::out) is semidet.
|
|
% p(err(X), err(X)).
|
|
%
|
|
% then we want to reuse the `err(X)' in the first arg rather than
|
|
% constructing a new copy of it for the second arg.
|
|
% The two occurrences of `err(X)' have types `maybe_err(int)'
|
|
% and `maybe(float)', but we know that they have the same
|
|
% representation.
|
|
|
|
:- pred common__compatible_types(type, type).
|
|
:- mode common__compatible_types(in, in) is semidet.
|
|
|
|
common__compatible_types(Type1, Type2) :-
|
|
type_to_ctor_and_args(Type1, TypeCtor1, _),
|
|
type_to_ctor_and_args(Type2, TypeCtor2, _),
|
|
TypeCtor1 = TypeCtor2.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% succeeds if the two lists of variables are equivalent
|
|
% according to the specified equivalence class.
|
|
:- pred common__var_lists_are_equiv(list(prog_var), list(prog_var),
|
|
eqvclass(prog_var)).
|
|
:- mode common__var_lists_are_equiv(in, in, in) is semidet.
|
|
|
|
common__var_lists_are_equiv([], [], _VarEqv).
|
|
common__var_lists_are_equiv([X | Xs], [Y | Ys], VarEqv) :-
|
|
common__vars_are_equiv(X, Y, VarEqv),
|
|
common__var_lists_are_equiv(Xs, Ys, VarEqv).
|
|
|
|
common__vars_are_equivalent(X, Y, CommonInfo) :-
|
|
CommonInfo = common(EqvVars, _, _, _),
|
|
common__vars_are_equiv(X, Y, EqvVars).
|
|
|
|
% succeeds if the two variables are equivalent
|
|
% according to the specified equivalence class.
|
|
:- pred common__vars_are_equiv(prog_var, prog_var, eqvclass(prog_var)).
|
|
:- mode common__vars_are_equiv(in, in, in) is semidet.
|
|
|
|
common__vars_are_equiv(X, Y, VarEqv) :-
|
|
% write('looking for equivalence of '),
|
|
% write(X),
|
|
% write(' and '),
|
|
% write(Y),
|
|
% nl,
|
|
(
|
|
X = Y
|
|
;
|
|
eqvclass__is_member(VarEqv, X),
|
|
eqvclass__is_member(VarEqv, Y),
|
|
eqvclass__same_eqvclass(VarEqv, X, Y)
|
|
).
|
|
% write('they are equivalent'),
|
|
% nl.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred common__record_cell(prog_var, cons_id, list(prog_var),
|
|
simplify_info, simplify_info).
|
|
:- mode common__record_cell(in, in, in, in, out) is det.
|
|
|
|
common__record_cell(Var, ConsId, ArgVars, Info0, Info) :-
|
|
simplify_info_get_common_info(Info0, CommonInfo0),
|
|
simplify_info_get_var_types(Info0, VarTypes),
|
|
CommonInfo0 = common(VarEqv, StructMapAll0,
|
|
StructMapLastCall0, SeenCalls),
|
|
map__lookup(VarTypes, Var, VarType),
|
|
Struct = structure(Var, VarType, ConsId, ArgVars),
|
|
common__do_record_cell(StructMapAll0, ConsId, Struct, StructMapAll),
|
|
common__do_record_cell(StructMapLastCall0, ConsId, Struct,
|
|
StructMapLastCall),
|
|
CommonInfo = common(VarEqv, StructMapAll,
|
|
StructMapLastCall, SeenCalls),
|
|
simplify_info_set_common_info(Info0, CommonInfo, Info).
|
|
|
|
:- pred common__do_record_cell(struct_map, cons_id, structure, struct_map).
|
|
:- mode common__do_record_cell(in, in, in, out) is det.
|
|
|
|
common__do_record_cell(StructMap0, ConsId, Struct, StructMap) :-
|
|
( map__search(StructMap0, ConsId, StructList0Prime) ->
|
|
StructList0 = StructList0Prime
|
|
;
|
|
StructList0 = []
|
|
),
|
|
|
|
% Insert the new cell at the front of the list. If it hides
|
|
% an equivalent cell, at least the reuse of this cell will
|
|
% require saving its address over fewer calls.
|
|
|
|
StructList = [Struct | StructList0],
|
|
map__set(StructMap0, ConsId, StructList, StructMap).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred common__record_equivalence(prog_var, prog_var,
|
|
simplify_info, simplify_info).
|
|
:- mode common__record_equivalence(in, in, in, out) is det.
|
|
|
|
common__record_equivalence(Var1, Var2, Info0, Info) :-
|
|
simplify_info_get_common_info(Info0, CommonInfo0),
|
|
CommonInfo0 = common(VarEqv0, StructMap0, StructMap1, SeenCalls),
|
|
% write('ensuring equivalence of '),
|
|
% write(Var1),
|
|
% write(' and '),
|
|
% write(Var2),
|
|
% nl,
|
|
eqvclass__ensure_equivalence(VarEqv0, Var1, Var2, VarEqv),
|
|
CommonInfo = common(VarEqv, StructMap0, StructMap1, SeenCalls),
|
|
simplify_info_set_common_info(Info0, CommonInfo, Info).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
common__optimise_call(PredId, ProcId, Args, Goal0,
|
|
GoalInfo, Goal, Info0, Info) :-
|
|
(
|
|
goal_info_get_determinism(GoalInfo, Det),
|
|
common__check_call_detism(Det),
|
|
simplify_info_get_var_types(Info0, VarTypes),
|
|
simplify_info_get_module_info(Info0, ModuleInfo),
|
|
module_info_pred_proc_info(ModuleInfo, PredId,
|
|
ProcId, _, ProcInfo),
|
|
proc_info_argmodes(ProcInfo, ArgModes),
|
|
common__partition_call_args(VarTypes, ModuleInfo, ArgModes,
|
|
Args, InputArgs, OutputArgs, OutputModes)
|
|
->
|
|
common__optimise_call_2(seen_call(PredId, ProcId), InputArgs,
|
|
OutputArgs, OutputModes, Goal0, GoalInfo, Goal,
|
|
Info0, Info)
|
|
;
|
|
Goal = Goal0,
|
|
Info = Info0
|
|
).
|
|
|
|
common__optimise_higher_order_call(Closure, Args, Modes, Det, Goal0,
|
|
GoalInfo, Goal, Info0, Info) :-
|
|
(
|
|
common__check_call_detism(Det),
|
|
simplify_info_get_var_types(Info0, VarTypes),
|
|
simplify_info_get_module_info(Info0, ModuleInfo),
|
|
common__partition_call_args(VarTypes, ModuleInfo, Modes, Args,
|
|
InputArgs, OutputArgs, OutputModes)
|
|
->
|
|
common__optimise_call_2(higher_order_call,
|
|
[Closure | InputArgs], OutputArgs, OutputModes, Goal0,
|
|
GoalInfo, Goal, Info0, Info)
|
|
;
|
|
Goal = Goal0,
|
|
Info = Info0
|
|
).
|
|
|
|
:- pred common__check_call_detism(determinism::in) is semidet.
|
|
|
|
common__check_call_detism(Det) :-
|
|
determinism_components(Det, _, SolnCount),
|
|
% Replacing nondet or mulidet calls would cause
|
|
% loss of solutions.
|
|
( SolnCount = at_most_one
|
|
; SolnCount = at_most_many_cc
|
|
).
|
|
|
|
:- pred common__optimise_call_2(seen_call_id, list(prog_var), list(prog_var),
|
|
list(mode), hlds_goal_expr, hlds_goal_info, hlds_goal_expr,
|
|
simplify_info, simplify_info).
|
|
:- mode common__optimise_call_2(in, in, in, in, in, in, out, in, out) is det.
|
|
|
|
common__optimise_call_2(SeenCall, InputArgs, OutputArgs, Modes, Goal0,
|
|
GoalInfo, Goal, Info0, Info) :-
|
|
simplify_info_get_common_info(Info0, CommonInfo0),
|
|
CommonInfo0 = common(Eqv0, Structs0, Structs1, SeenCalls0),
|
|
(
|
|
map__search(SeenCalls0, SeenCall, SeenCallsList0)
|
|
->
|
|
( common__find_previous_call(SeenCallsList0, InputArgs,
|
|
Eqv0, OutputArgs2, PrevContext)
|
|
->
|
|
simplify_info_get_module_info(Info0, ModuleInfo),
|
|
mode_util__modes_to_uni_modes(Modes, Modes, ModuleInfo,
|
|
UniModes),
|
|
common__create_output_unifications(GoalInfo,
|
|
OutputArgs, OutputArgs2, UniModes,
|
|
Goals, Info0, Info1),
|
|
Goal = conj(Goals),
|
|
simplify_info_get_var_types(Info0, VarTypes),
|
|
(
|
|
simplify_do_warn_calls(Info1),
|
|
% Don't warn for cases such as:
|
|
% set__init(Set1 : set(int)),
|
|
% set__init(Set2 : set(float)).
|
|
map__apply_to_list(OutputArgs, VarTypes,
|
|
OutputArgTypes1),
|
|
map__apply_to_list(OutputArgs2, VarTypes,
|
|
OutputArgTypes2),
|
|
common__types_match_exactly_list(OutputArgTypes1,
|
|
OutputArgTypes2)
|
|
->
|
|
goal_info_get_context(GoalInfo, Context),
|
|
simplify_info_do_add_msg(Info1,
|
|
duplicate_call(SeenCall, PrevContext,
|
|
Context),
|
|
Info2)
|
|
;
|
|
Info2 = Info1
|
|
),
|
|
CommonInfo = common(Eqv0, Structs0,
|
|
Structs1, SeenCalls0),
|
|
pd_cost__goal(Goal0 - GoalInfo, Cost),
|
|
simplify_info_incr_cost_delta(Info2, Cost, Info3),
|
|
simplify_info_set_requantify(Info3, Info4),
|
|
goal_info_get_determinism(GoalInfo, Detism0),
|
|
( Detism0 \= det ->
|
|
simplify_info_set_rerun_det(Info4, Info5)
|
|
;
|
|
Info5 = Info4
|
|
)
|
|
;
|
|
goal_info_get_context(GoalInfo, Context),
|
|
ThisCall = call_args(Context, InputArgs, OutputArgs),
|
|
map__det_update(SeenCalls0, SeenCall,
|
|
[ThisCall | SeenCallsList0], SeenCalls),
|
|
CommonInfo = common(Eqv0, Structs0,
|
|
Structs1, SeenCalls),
|
|
Goal = Goal0,
|
|
Info5 = Info0
|
|
)
|
|
;
|
|
goal_info_get_context(GoalInfo, Context),
|
|
ThisCall = call_args(Context, InputArgs, OutputArgs),
|
|
map__det_insert(SeenCalls0, SeenCall, [ThisCall], SeenCalls),
|
|
CommonInfo = common(Eqv0, Structs0, Structs1, SeenCalls),
|
|
Goal = Goal0,
|
|
Info5 = Info0
|
|
),
|
|
simplify_info_set_common_info(Info5, CommonInfo, Info).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Partition the arguments of a call into inputs and outputs,
|
|
% failing if any of the outputs have a unique component
|
|
% or if any of the outputs contain any `any' insts.
|
|
:- pred common__partition_call_args(vartypes::in, module_info::in,
|
|
list(mode)::in, list(prog_var)::in, list(prog_var)::out,
|
|
list(prog_var)::out, list(mode)::out) is semidet.
|
|
|
|
common__partition_call_args(_, _, [], [_ | _], _, _, _) :-
|
|
error("common__partition_call_args").
|
|
common__partition_call_args(_, _, [_ | _], [], _, _, _) :-
|
|
error("common__partition_call_args").
|
|
common__partition_call_args(_, _, [], [], [], [], []).
|
|
common__partition_call_args(VarTypes, ModuleInfo, [ArgMode | ArgModes],
|
|
[Arg | Args], InputArgs, OutputArgs, OutputModes) :-
|
|
common__partition_call_args(VarTypes, ModuleInfo, ArgModes, Args,
|
|
InputArgs1, OutputArgs1, OutputModes1),
|
|
mode_get_insts(ModuleInfo, ArgMode, InitialInst, FinalInst),
|
|
map__lookup(VarTypes, Arg, Type),
|
|
( inst_matches_binding(InitialInst, FinalInst, Type, ModuleInfo) ->
|
|
InputArgs = [Arg | InputArgs1],
|
|
OutputArgs = OutputArgs1,
|
|
OutputModes = OutputModes1
|
|
;
|
|
% Calls with partly unique outputs cannot be replaced,
|
|
% since a unique copy of the outputs must be produced.
|
|
inst_is_not_partly_unique(ModuleInfo, FinalInst),
|
|
|
|
% Don't optimize calls whose outputs include any
|
|
% `any' insts, since that would create false aliasing
|
|
% between the different variables.
|
|
% (inst_matches_binding applied to identical insts
|
|
% fails only for `any' insts.)
|
|
inst_matches_binding(FinalInst, FinalInst, Type, ModuleInfo),
|
|
|
|
% Don't optimize calls where a partially instantiated
|
|
% variable is further instantiated. That case is difficult
|
|
% to test properly because mode analysis currently
|
|
% rejects most potential test cases.
|
|
inst_is_free(ModuleInfo, InitialInst),
|
|
|
|
InputArgs = InputArgs1,
|
|
OutputArgs = [Arg | OutputArgs1],
|
|
OutputModes = [ArgMode | OutputModes1]
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred common__find_previous_call(list(call_args)::in, list(prog_var)::in,
|
|
eqvclass(prog_var)::in, list(prog_var)::out,
|
|
prog_context::out) is semidet.
|
|
|
|
common__find_previous_call([SeenCall | SeenCalls], InputArgs,
|
|
Eqv, OutputArgs2, PrevContext) :-
|
|
SeenCall = call_args(PrevContext, InputArgs1, OutputArgs1),
|
|
( common__var_lists_are_equiv(InputArgs, InputArgs1, Eqv) ->
|
|
OutputArgs2 = OutputArgs1
|
|
;
|
|
common__find_previous_call(SeenCalls, InputArgs, Eqv,
|
|
OutputArgs2, PrevContext)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred common__create_output_unifications(hlds_goal_info::in,
|
|
list(prog_var)::in, list(prog_var)::in, list(uni_mode)::in,
|
|
list(hlds_goal)::out, simplify_info::in,
|
|
simplify_info::out) is det.
|
|
|
|
% Create unifications to assign the vars in OutputArgs from
|
|
% the corresponding var in OutputArgs2.
|
|
% This needs to be done even if OutputArg is not a nonlocal in
|
|
% the original goal because later goals in the conjunction may
|
|
% match against the cell and need all the output arguments.
|
|
% The unneeded assignments will be removed later.
|
|
|
|
common__create_output_unifications(GoalInfo, OutputArgs, OldOutputArgs,
|
|
UniModes, Goals, Info0, Info) :-
|
|
(
|
|
OutputArgs = [OutputArg | OutputArgs1],
|
|
OldOutputArgs = [OldOutputArg | OldOutputArgs1],
|
|
UniModes = [UniMode | UniModes1]
|
|
->
|
|
(
|
|
% This can happen if the first cell was created
|
|
% with a partially instantiated deconstruction.
|
|
OutputArg \= OldOutputArg
|
|
->
|
|
common__generate_assign(OutputArg, OldOutputArg,
|
|
UniMode, GoalInfo, Goal, Info0, Info1),
|
|
common__create_output_unifications(GoalInfo,
|
|
OutputArgs1, OldOutputArgs1, UniModes1,
|
|
Goals1, Info1, Info),
|
|
Goals = [Goal | Goals1]
|
|
;
|
|
common__create_output_unifications(GoalInfo,
|
|
OutputArgs1, OldOutputArgs1, UniModes1, Goals,
|
|
Info0, Info)
|
|
)
|
|
;
|
|
OutputArgs = [],
|
|
OldOutputArgs = [],
|
|
UniModes = []
|
|
->
|
|
Goals = [],
|
|
Info = Info0
|
|
;
|
|
error("common__create_output_unifications: mode mismatch")
|
|
).
|
|
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred common__generate_assign(prog_var, prog_var, uni_mode,
|
|
hlds_goal_info, hlds_goal, simplify_info, simplify_info).
|
|
:- mode common__generate_assign(in, in, in, in, out, in, out) is det.
|
|
|
|
common__generate_assign(ToVar, FromVar, UniMode, _, Goal, Info0, Info) :-
|
|
simplify_info_get_var_types(Info0, VarTypes),
|
|
map__lookup(VarTypes, ToVar, ToVarType),
|
|
map__lookup(VarTypes, FromVar, FromVarType),
|
|
|
|
set__list_to_set([ToVar, FromVar], NonLocals),
|
|
UniMode = ((_ - ToVarInst0) -> (_ - ToVarInst)),
|
|
( common__types_match_exactly(ToVarType, FromVarType) ->
|
|
UnifyMode = (ToVarInst0 -> ToVarInst) -
|
|
(ToVarInst -> ToVarInst),
|
|
UnifyContext = unify_context(explicit, []),
|
|
GoalExpr = unify(ToVar, var(FromVar), UnifyMode,
|
|
assign(ToVar, FromVar), UnifyContext)
|
|
;
|
|
% If the cells we are optimizing don't have exactly the same
|
|
% type, we insert explicit type casts to ensure type
|
|
% correctness. This avoids problems with HLDS optimizations
|
|
% such as inlining which expect the HLDS to be well-typed.
|
|
% Unfortunately this loses information for other optimizations,
|
|
% since the call to the type cast hides the equivalence of
|
|
% the input and output.
|
|
Modes = [(ToVarInst -> ToVarInst),
|
|
(free -> ToVarInst)],
|
|
GoalExpr = generic_call(unsafe_cast, [FromVar, ToVar],
|
|
Modes, det)
|
|
),
|
|
|
|
% `ToVar' may not appear in the original instmap_delta,
|
|
% so we can't just use instmap_delta_restrict on the
|
|
% original instmap_delta here.
|
|
instmap_delta_from_assoc_list([ToVar - ToVarInst], InstMapDelta),
|
|
|
|
goal_info_init(NonLocals, InstMapDelta, det, pure, GoalInfo),
|
|
Goal = GoalExpr - GoalInfo,
|
|
common__record_equivalence(ToVar, FromVar, Info0, Info).
|
|
|
|
:- pred common__types_match_exactly((type), (type)).
|
|
:- mode common__types_match_exactly(in, in) is semidet.
|
|
|
|
common__types_match_exactly(term__variable(Var), term__variable(Var)).
|
|
common__types_match_exactly(Type1, Type2) :-
|
|
% XXX should succeed for embedded constraints
|
|
type_to_ctor_and_args(Type1, TypeCtor1, Args1),
|
|
type_to_ctor_and_args(Type2, TypeCtor2, Args2),
|
|
TypeCtor1 = TypeCtor2,
|
|
common__types_match_exactly_list(Args1, Args2).
|
|
|
|
:- pred common__types_match_exactly_list(list(type), list(type)).
|
|
:- mode common__types_match_exactly_list(in, in) is semidet.
|
|
|
|
common__types_match_exactly_list([], []).
|
|
common__types_match_exactly_list([Type1 | Types1], [Type2 | Types2]) :-
|
|
common__types_match_exactly(Type1, Type2),
|
|
common__types_match_exactly_list(Types1, Types2).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|