Files
mercury/compiler/higher_order.m
Zoltan Somogyi bc97676d98 Change the definition of is_builtin to use less space.
Estimated hours taken: 2

hlds_goal:
	Change the definition of is_builtin to use less space.

bytecode_gen, code_aux, code_gen, code_util, dependency_graph, follow_code,
higher_order, inlining, live_vars, make_hlds, modecheck_unify, modes,
polymorphism, stratify, unused_args:
	Fixes to accommodate the change to is_builtin.
1996-12-24 02:42:21 +00:00

1116 lines
42 KiB
Mathematica

%------------------------------------------------------------------------------
% Copyright (C) 1995 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.
%-----------------------------------------------------------------------------%
%
:- module higher_order.
% Main author: stayl
%
% Specializes calls to higher order predicates where the value of one or more
% higher order arguments are known. Since this creates a new copy of the
% called procedure I have limited the specialization to cases where the called
% procedure's goal contains less than 20 calls and unifications. For predicates
% above this size the overhead of the higher order call becomes less
% significant while the increase in code size becomes significant.
% If a specialization creates new opportunities for specialization, the
% specialization process will be iterated until no further opportunities arise.
% The specialized version for predicate 'foo' is named 'foo__ho<n>', where n
% is a number that uniquely identifies this specialized version.
%-------------------------------------------------------------------------------
:- interface.
:- import_module hlds_module, io.
:- pred specialize_higher_order(module_info::in, module_info::out,
io__state::di, io__state::uo) is det.
%-------------------------------------------------------------------------------
:- implementation.
:- import_module hlds_pred, hlds_goal, hlds_data, instmap.
:- import_module code_util, globals, make_hlds, mode_util, goal_util.
:- import_module type_util, options, prog_data, quantification.
:- import_module mercury_to_mercury.
:- import_module assoc_list, bool, int, list, map, require, set.
:- import_module std_util, string, varset, term.
% Iterate collecting requests and processing them until there
% are no more requests remaining.
specialize_higher_order(ModuleInfo0, ModuleInfo) -->
{ get_specialization_requests(Requests, GoalSizes,
ModuleInfo0, ModuleInfo1) },
{ map__init(NewPreds0) },
process_requests(Requests, GoalSizes, 1, _NextHOid,
NewPreds0, _NewPreds, ModuleInfo1, ModuleInfo).
:- pred process_requests(set(request)::in, goal_sizes::in, int::in,
int::out, new_preds::in, new_preds::out, module_info::in,
module_info::out, io__state::di, io__state::uo) is det.
process_requests(Requests0, GoalSizes0, NextHOid0, NextHOid,
NewPreds0, NewPreds, ModuleInfo1, ModuleInfo) -->
{ filter_requests(Requests0, GoalSizes0, Requests) },
(
{ Requests = [] }
->
{ ModuleInfo = ModuleInfo1 },
{ NextHOid = NextHOid0 },
{ NewPreds = NewPreds0 }
;
{ set__init(PredProcsToFix0) },
{ map__init(NewPredsForThisPass0) },
create_new_preds(Requests, NewPredsForThisPass0,
NewPredsForThisPass, PredProcsToFix0, PredProcsToFix,
NextHOid0, NextHOid1, ModuleInfo1, ModuleInfo2),
{ map__keys(NewPredsForThisPass, SpecializedPreds) },
{ map__merge(NewPreds0, NewPredsForThisPass, NewPreds1) },
{ set__to_sorted_list(PredProcsToFix, PredProcs) },
{ fixup_preds(PredProcs, NewPreds1, ModuleInfo2, ModuleInfo3) },
{ set__init(NewRequests0) },
{ create_specialized_versions(SpecializedPreds, NewPreds1,
NewRequests0, NewRequests, GoalSizes0,
GoalSizes, ModuleInfo3, ModuleInfo4) },
process_requests(NewRequests, GoalSizes, NextHOid1,
NextHOid, NewPreds1, NewPreds, ModuleInfo4, ModuleInfo)
).
%-------------------------------------------------------------------------------
% The largest goal that will be specialized. Goal size is measured
% by the number of calls and unifications the goal contains. This is
% used to stop specialization of large predicates, for which the
% call overhead will be less noticeable and the increase in code size
% will be significant.
:- pred max_specialized_goal_size(int::out) is det.
max_specialized_goal_size(20).
:- type request --->
request(
pred_proc_id, % calling pred
pred_proc_id, % called pred
list(higher_order_arg)
).
% Stores pred_id, proc_id, index in argument vector, number of
% curried arguments of a higher order argument, higher-order
% curried arguments with known values.
:- type higher_order_arg --->
higher_order_arg(
pred_id,
proc_id,
int, % index in argument vector
int, % number of curried args
list(higher_order_arg) % higher-order curried arguments
% with known values
).
:- type goal_sizes == map(pred_id, int). %stores the size of each
% predicate's goal used in the heuristic
% to decide which preds are specialized
% Used to hold the value of known higher order variables.
% If a variable is not in the map, it does not have a value yet
% If it is in the map as a yes, it has been seen previously, and has
% a unique possible value, and calls involving it can be specialized.
% If it is in the map as a no, it has more than one possible value,
% and higher order calls involving it cannot be specialized.
:- type pred_vars == map(var, maybe_pred_and_args).
% The list of vars is a list of the curried arguments, which must
% be explicitly passed to the specialized predicate. The list of
% uni_modes is their modes in the unification.
:- type maybe_pred_and_args --->
yes(pred_id, proc_id, list(var))
; no.
% used while traversing goals
:- type higher_order_info --->
info(pred_vars, set(request), new_preds, module_info).
:- type new_preds == map(pred_proc_id, set(new_pred)).
:- type new_pred --->
new_pred(
pred_id,
proc_id,
sym_name, % name
list(higher_order_arg) % specialized args
).
% Returned by traverse_goal.
:- type changed --->
changed % Need to requantify goal + check other procs
; request % Need to check other procs
; unchanged. % Do nothing more for this predicate
%-------------------------------------------------------------------------------
:- pred get_specialization_requests(set(request)::out, goal_sizes::out,
module_info::in, module_info::out) is det.
get_specialization_requests(Requests, GoalSizes, ModuleInfo0, ModuleInfo) :-
module_info_predids(ModuleInfo0, PredIds),
map__init(GoalSizes0),
set__init(Requests0),
get_specialization_requests_2(PredIds, Requests0, Requests,
GoalSizes0, GoalSizes, ModuleInfo0, ModuleInfo).
:- pred get_specialization_requests_2(list(pred_id)::in, set(request)::in,
set(request)::out, goal_sizes::in, goal_sizes::out,
module_info::in, module_info::out) is det.
get_specialization_requests_2([], Requests, Requests, Sizes, Sizes,
ModuleInfo, ModuleInfo).
get_specialization_requests_2([PredId | PredIds], Requests0, Requests,
GoalSizes0, GoalSizes, ModuleInfo0, ModuleInfo) :-
module_info_preds(ModuleInfo0, Preds0),
map__lookup(Preds0, PredId, PredInfo0),
pred_info_non_imported_procids(PredInfo0, NonImportedProcs),
(
NonImportedProcs = [],
Requests2 = Requests0,
GoalSizes1 = GoalSizes0,
ModuleInfo1 = ModuleInfo0
;
NonImportedProcs = [ProcId | ProcIds],
pred_info_procedures(PredInfo0, Procs0),
map__lookup(Procs0, ProcId, ProcInfo0),
proc_info_goal(ProcInfo0, Goal0),
map__init(PredVars0),
% first time through we can only specialize call/N
map__init(NewPreds0),
PredProcId = proc(PredId, ProcId),
Info0 = info(PredVars0, Requests0, NewPreds0, ModuleInfo0),
traverse_goal(Goal0, Goal1, PredProcId, Changed,
GoalSize, Info0, info(_, Requests1,_,_)),
map__set(GoalSizes0, PredId, GoalSize, GoalSizes1),
(
Changed = changed
->
proc_info_vartypes(ProcInfo0, VarTypes0),
proc_info_headvars(ProcInfo0, HeadVars),
proc_info_variables(ProcInfo0, Varset0),
implicitly_quantify_clause_body(HeadVars, Goal1,
Varset0, VarTypes0, Goal, Varset, VarTypes, _),
proc_info_set_goal(ProcInfo0, Goal, ProcInfo1),
proc_info_set_variables(ProcInfo1, Varset, ProcInfo2),
proc_info_set_vartypes(ProcInfo2, VarTypes, ProcInfo),
map__det_update(Procs0, ProcId, ProcInfo, Procs1)
;
Procs1 = Procs0
),
(
(Changed = request ; Changed = changed)
->
traverse_other_procs(PredId, ProcIds, ModuleInfo0,
Requests1, Requests2, Procs1, Procs),
pred_info_set_procedures(PredInfo0, Procs, PredInfo),
map__det_update(Preds0, PredId, PredInfo, Preds),
module_info_set_preds(ModuleInfo0, Preds, ModuleInfo1)
;
ModuleInfo1 = ModuleInfo0,
Requests2 = Requests1
)
),
get_specialization_requests_2(PredIds, Requests2, Requests,
GoalSizes1, GoalSizes, ModuleInfo1, ModuleInfo).
% This is called when the first procedure of a pred was
% changed. It fixes up all the other procs, ignoring the
% goal_size and requests that come out, since that information
% has already been collected.
:- pred traverse_other_procs(pred_id::in, list(proc_id)::in, module_info::in,
set(request)::in, set(request)::out, proc_table::in,
proc_table::out) is det.
traverse_other_procs(_PredId, [], _Module, Requests, Requests, Procs, Procs).
traverse_other_procs(PredId, [ProcId | ProcIds], ModuleInfo, Requests0,
Requests, Procs0, Procs) :-
map__init(PredVars0),
map__init(NewPreds0),
map__lookup(Procs0, ProcId, ProcInfo0),
proc_info_goal(ProcInfo0, Goal0),
proc_info_vartypes(ProcInfo0, VarTypes0),
Info0 = info(PredVars0, Requests0, NewPreds0, ModuleInfo),
traverse_goal(Goal0, Goal1, proc(PredId, ProcId), _, _,
Info0, info(_, Requests1,_,_)),
proc_info_headvars(ProcInfo0, HeadVars),
proc_info_variables(ProcInfo0, Varset0),
implicitly_quantify_clause_body(HeadVars, Goal1, Varset0, VarTypes0,
Goal, Varset, VarTypes, _),
proc_info_set_goal(ProcInfo0, Goal, ProcInfo1),
proc_info_set_variables(ProcInfo1, Varset, ProcInfo2),
proc_info_set_vartypes(ProcInfo2, VarTypes, ProcInfo),
map__det_update(Procs0, ProcId, ProcInfo, Procs1),
traverse_other_procs(PredId, ProcIds, ModuleInfo, Requests1,
Requests, Procs1, Procs).
%-------------------------------------------------------------------------------
% Goal traversal
% Traverses the goal collecting higher order variables for which
% the value is known, and specializing calls and adding
% specialization requests to the request_info structure.
% The first time through the only predicate we can specialize
% is call/N. The pred_proc_id is that of the current procedure,
% used to find out which procedures need fixing up later.
:- pred traverse_goal(hlds__goal::in, hlds__goal::out, pred_proc_id::in,
changed::out, int::out, higher_order_info::in,
higher_order_info::out) is det.
traverse_goal(conj(Goals0) - Info, conj(Goals) - Info,
PredProcId, Changed, GoalSize) -->
traverse_conj(Goals0, Goals, PredProcId, unchanged, Changed,
0, GoalSize).
traverse_goal(disj(Goals0, SM) - Info, disj(Goals, SM) - Info,
PredProcId, Changed, GoalSize) -->
traverse_disj(Goals0, Goals, PredProcId, Changed, GoalSize).
% a switch is treated as a disjunction
traverse_goal(switch(Var, CanFail, Cases0, SM) - Info,
switch(Var, CanFail, Cases, SM) - Info,
PredProcId, Changed, GoalSize) -->
traverse_cases(Cases0, Cases, PredProcId, Changed, GoalSize).
% check whether this call could be specialized
traverse_goal(Goal0, Goal, PredProcId, Changed, 1) -->
{ Goal0 = higher_order_call(_,_,_,_,_) - _ },
maybe_specialize_higher_order_call(Goal0, Goal, PredProcId, Changed).
% check whether this call could be specialized
traverse_goal(Goal0, Goal, PredProcId, Changed, 1) -->
{ Goal0 = call(_,_,_,_,_,_) - _ },
maybe_specialize_call(Goal0, Goal, PredProcId, Changed).
% if-then-elses are handled as disjunctions
traverse_goal(Goal0, Goal, PredProcId, Changed, GoalSize, Info0, Info) :-
Goal0 = if_then_else(Vars, Cond0, Then0, Else0, SM) - GoalInfo,
traverse_goal(Cond0, Cond, PredProcId, Changed1,
GoalSize1, Info0, Info1),
traverse_goal(Then0, Then, PredProcId, Changed2,
GoalSize2, Info1, Info2),
traverse_goal(Else0, Else, PredProcId, Changed3,
GoalSize3, Info0, Info3),
Goal = if_then_else(Vars, Cond, Then, Else, SM) - GoalInfo,
GoalSize is GoalSize1 + GoalSize2 + GoalSize3,
update_changed_status(Changed1, Changed2, Changed4),
update_changed_status(Changed4, Changed3, Changed),
merge_higher_order_infos(Info2, Info3, Info).
traverse_goal(not(NegGoal0) - Info, not(NegGoal) - Info,
PredProcId, Changed, GoalSize) -->
traverse_goal(NegGoal0, NegGoal, PredProcId, Changed, GoalSize).
traverse_goal(some(Vars, Goal0) - Info, some(Vars, Goal) - Info,
PredProcId, Changed, GoalSize) -->
traverse_goal(Goal0, Goal, PredProcId, Changed, GoalSize).
traverse_goal(Goal, Goal, _, unchanged, 1) -->
{ Goal = pragma_c_code(_, _, _, _, _, _) - _ }.
traverse_goal(Goal, Goal, _, unchanged, 1) -->
{ Goal = unify(_, _, _, Unify, _) - _ },
check_unify(Unify).
:- pred traverse_conj(hlds__goals::in, hlds__goals::out, pred_proc_id::in,
changed::in, changed::out, int::in, int::out, higher_order_info::in,
higher_order_info::out) is det.
traverse_conj([], [], _, Changed, Changed, Size, Size) --> [].
traverse_conj([Goal0 | Goals0], [Goal | Goals],
PredProcId, Changed0, Changed, GoalSize0, GoalSize) -->
traverse_goal(Goal0, Goal, PredProcId, LocalChanged, ThisGoalSize),
{ GoalSize1 is GoalSize0 + ThisGoalSize },
{ update_changed_status(Changed0, LocalChanged, Changed1) },
traverse_conj(Goals0, Goals, PredProcId, Changed1, Changed,
GoalSize1, GoalSize).
% to process a disjunction, we process each disjunct with the
% specialization information before the goal, then merge the
% results to give the specialization information after the
% disjunction.
:- pred traverse_disj(hlds__goals::in, hlds__goals::out, pred_proc_id::in,
changed::out, int::out, higher_order_info::in,
higher_order_info::out) is det.
traverse_disj([], [], _, unchanged, 0) --> [].
traverse_disj([Goal0 | Goals0], [Goal | Goals], PredProcId,
Changed, GoalSize) -->
=(Info0),
traverse_goal(Goal0, Goal, PredProcId, Changed0, GoalSize0),
traverse_disj_2(Goals0, Goals, PredProcId,
Changed0, Changed, GoalSize0, GoalSize, Info0).
:- pred traverse_disj_2(hlds__goals::in, hlds__goals::out, pred_proc_id::in,
changed::in, changed::out, int::in, int::out, higher_order_info::in,
higher_order_info::in, higher_order_info::out) is det.
traverse_disj_2([], [], _, Changed, Changed, Size, Size, _, Info, Info).
traverse_disj_2([Goal0 | Goals0], [Goal | Goals], PredProcId, Changed0, Changed,
GoalSize0, GoalSize, InitialInfo, Info0, Info) :-
traverse_goal(Goal0, Goal, PredProcId, LocalChanged, ThisGoalSize,
InitialInfo, ThisGoalInfo),
update_changed_status(Changed0, LocalChanged, Changed1),
GoalSize1 is GoalSize0 + ThisGoalSize,
merge_higher_order_infos(Info0, ThisGoalInfo, Info1),
traverse_disj_2(Goals0, Goals, PredProcId, Changed1, Changed,
GoalSize1, GoalSize, InitialInfo, Info1, Info).
% Switches are treated in exactly the same way as disjunctions.
:- pred traverse_cases(list(case)::in, list(case)::out, pred_proc_id::in,
changed::out, int::out, higher_order_info::in,
higher_order_info::out) is det.
traverse_cases([], [], _, unchanged, 0) --> [].
traverse_cases([case(ConsId, Goal0) | Cases0], [case(ConsId, Goal) | Cases],
PredProcId, Changed, GoalSize) -->
=(Info0),
traverse_goal(Goal0, Goal, PredProcId, Changed0, ThisGoalSize),
traverse_cases_2(Cases0, Cases, PredProcId, Changed0,
Changed, ThisGoalSize, GoalSize, Info0).
:- pred traverse_cases_2(list(case)::in, list(case)::out, pred_proc_id::in,
changed::in, changed::out, int::in, int::out, higher_order_info::in,
higher_order_info::in, higher_order_info::out) is det.
traverse_cases_2([], [], _, Changed, Changed, Size, Size, _, Info, Info).
traverse_cases_2([Case0 | Cases0], [Case | Cases], PredProcId, Changed0,
Changed, GoalSize0, GoalSize, InitialInfo, Info0, Info) :-
Case0 = case(ConsId, Goal0),
traverse_goal(Goal0, Goal, PredProcId, LocalChanged,
ThisGoalSize, InitialInfo, ThisGoalInfo),
Case = case(ConsId, Goal),
update_changed_status(Changed0, LocalChanged, Changed1),
GoalSize1 is GoalSize0 + ThisGoalSize,
merge_higher_order_infos(Info0, ThisGoalInfo, Info1),
traverse_cases_2(Cases0, Cases, PredProcId, Changed1, Changed,
GoalSize1, GoalSize, InitialInfo, Info1, Info).
% This is used in traversing disjunctions. We save the initial
% accumulator, then traverse each disjunct starting with the initial
% info. We then merge the resulting infos.
:- pred merge_higher_order_infos(higher_order_info::in, higher_order_info::in,
higher_order_info::out) is det.
merge_higher_order_infos(Info1, Info2, Info) :-
Info1 = info(PredVars1, Requests1, NewPreds, ModuleInfo),
Info2 = info(PredVars2, Requests2,_,_),
merge_pred_vars(PredVars1, PredVars2, PredVars),
set__union(Requests1, Requests2, Requests12),
set__to_sorted_list(Requests12, List12),
set__sorted_list_to_set(List12, Requests),
Info = info(PredVars, Requests, NewPreds, ModuleInfo).
:- pred merge_pred_vars(pred_vars::in, pred_vars::in, pred_vars::out) is det.
merge_pred_vars(PredVars1, PredVars2, PredVars) :-
map__to_assoc_list(PredVars1, PredVarList1),
map__to_assoc_list(PredVars2, PredVarList2),
merge_pred_var_lists(PredVarList1, PredVarList2, PredVarList),
map__from_assoc_list(PredVarList, PredVars).
% find out which variables after a disjunction cannot
% be specialized
:- pred merge_pred_var_lists(assoc_list(var, maybe_pred_and_args)::in,
assoc_list(var, maybe_pred_and_args)::in,
assoc_list(var, maybe_pred_and_args)::out) is det.
merge_pred_var_lists([], List, List).
merge_pred_var_lists([PredVar | PredVars], List2, MergedList) :-
merge_pred_var_with_list(PredVar, List2, MergedList1),
merge_pred_var_lists(PredVars, MergedList1, MergedList).
:- pred merge_pred_var_with_list(pair(var, maybe_pred_and_args)::in,
assoc_list(var, maybe_pred_and_args)::in,
assoc_list(var, maybe_pred_and_args)::out) is det.
merge_pred_var_with_list(VarValue, [], [VarValue]).
merge_pred_var_with_list(Var1 - Value1, [Var2 - Value2 | Vars], MergedList) :-
(
Var1 = Var2
->
( (
Value1 \= Value2
; Value1 = no
; Value2 = no
)
->
MergedList = [Var1 - no | Vars]
;
MergedList = [Var2 - Value2 | Vars]
)
% each var occurs at most once most in each list
% so if we have seen it we don't need to go on
;
MergedList = [Var2 - Value2 | MergedList1],
merge_pred_var_with_list(Var1 - Value1, Vars, MergedList1)
).
:- pred check_unify(unification::in, higher_order_info::in,
higher_order_info::out) is det.
% testing two higher order terms for equality is not allowed
check_unify(simple_test(_, _)) --> [].
check_unify(assign(Var1, Var2)) -->
maybe_add_alias(Var1, Var2).
% deconstructing a higher order term is not allowed
check_unify(deconstruct(_, _, _, _, _)) --> [].
check_unify(construct(LVar, ConsId, Args, _Modes), Info0, Info) :-
Info0 = info(PredVars0, Requests, NewPreds, ModuleInfo),
(
ConsId = pred_const(PredId, ProcId)
->
(
map__search(PredVars0, LVar, Specializable)
->
(
% we can't specialize calls involving
% a variable with more than one
% possible value
Specializable = yes(_, _, _),
map__det_update(PredVars0, LVar, no, PredVars)
;
% if a variable is already
% non-specializable, it can't become
% specializable
Specializable = no,
PredVars = PredVars0
)
;
map__set(PredVars0, LVar, yes(PredId, ProcId, Args),
PredVars)
)
;
PredVars = PredVars0
),
Info = info(PredVars, Requests, NewPreds, ModuleInfo).
check_unify(complicated_unify(_, _)) -->
{ error("higher_order:check_unify - complicated unification") }.
% Process a higher-order call to see if it could possibly
% be specialized.
:- pred maybe_specialize_higher_order_call( hlds__goal::in, hlds__goal::out,
pred_proc_id::in, changed::out, higher_order_info::in,
higher_order_info::out) is det.
maybe_specialize_higher_order_call(Goal0 - GoalInfo, Goal - GoalInfo,
PredProcId, Changed, Info0, Info) :-
Info0 = info(PredVars, Requests0, NewPreds, Module),
( Goal0 = higher_order_call(PredVar0, Args0, _Types, _Modes, _Det) ->
PredVar = PredVar0,
Args = Args0
;
error("higher_order.m: higher_order_call expected")
),
% We can trivially specialize calls to call/N.
(
map__search(PredVars, PredVar,
yes(PredId, ProcId, CurriedArgs))
->
module_info_pred_info(Module, PredId, PredInfo),
pred_info_module(PredInfo, ModuleName),
pred_info_name(PredInfo, PredName),
code_util__builtin_state(Module, PredId, ProcId, Builtin),
list__append(CurriedArgs, Args, AllArgs),
MaybeContext = no,
Goal1 = call(PredId, ProcId, AllArgs,
Builtin, MaybeContext,
qualified(ModuleName, PredName)),
maybe_specialize_call(Goal1 - GoalInfo,
Goal - _, PredProcId, _, Info0,
info(_, Requests, _, _)),
Changed = changed
;
% non-specializable call to call/N
Goal = Goal0,
Changed = unchanged,
Requests = Requests0
),
Info = info(PredVars, Requests, NewPreds, Module).
% Process a call to see if it could possibly be specialized.
:- pred maybe_specialize_call( hlds__goal::in, hlds__goal::out,
pred_proc_id::in, changed::out, higher_order_info::in,
higher_order_info::out) is det.
maybe_specialize_call(Goal0 - GoalInfo, Goal - GoalInfo, PredProcId,
Changed, Info0, Info) :-
Info0 = info(PredVars, Requests0, NewPreds, Module),
(
Goal0 = call(_, _, _, _, _, _)
->
Goal0 = call(CalledPred, CalledProc, Args0, IsBuiltin,
MaybeContext, _SymName0)
;
error("higher_order.m: call expected")
),
find_higher_order_args(Args0, PredVars, 1,
[], HigherOrderArgs, Args0, Args1),
(
HigherOrderArgs = []
->
Requests = Requests0,
Changed = unchanged,
Goal = Goal0
;
% Check to see if any of the specialized
% versions of the called pred apply here.
map__search(NewPreds,
proc(CalledPred, CalledProc),
NewPredSet),
set__to_sorted_list(NewPredSet, NewPredList), % NOP
list__filter(lambda([X::in] is semidet, (
X = new_pred(_,_,_, HigherOrderArgs)
)), NewPredList, Matches),
(
Matches = [Match],
Match = new_pred(NewCalledPred,
NewCalledProc, NewName,
_HOArgs)
;
Matches = [_,_|_],
error("multiple specializations")
)
->
remove_listof_higher_order_args(Args1, 1,
HigherOrderArgs, Args2),
Goal = call(NewCalledPred, NewCalledProc,
Args2, IsBuiltin, MaybeContext, NewName),
Changed = changed,
Requests = Requests0
;
% There is a known higher order variable in the
% call, so we put in a request for a specialized
% version of the pred.
Goal = Goal0,
Request = request(PredProcId,
proc(CalledPred, CalledProc),
HigherOrderArgs),
set__insert(Requests0, Request, Requests),
Changed = request
),
Info = info(PredVars, Requests, NewPreds, Module).
% Returns a list of the higher-order arguments in a call that have
% a known value. Also update the argument list to now include
% curried arguments that need to be explicitly passed.
:- pred find_higher_order_args(list(var)::in, pred_vars::in, int::in,
list(higher_order_arg)::in, list(higher_order_arg)::out,
list(var)::in, list(var)::out) is det.
find_higher_order_args([], _, _, HOArgs, HOArgs, NewArgs, NewArgs).
find_higher_order_args([Arg | Args], PredVars, ArgNo,
HOArgs0, HOArgs, NewArgs0, NewArgs) :-
NextArg is ArgNo + 1,
(
map__search(PredVars, Arg, yes(PredId, ProcId, CurriedArgs))
->
find_higher_order_args(CurriedArgs, PredVars, 1,
[], HOCurriedArgs, CurriedArgs, NewExtraArgs0),
list__length(CurriedArgs, NumArgs),
remove_listof_higher_order_args(NewExtraArgs0, 1, HOCurriedArgs,
NewExtraArgs),
HOArgs1 = [higher_order_arg(PredId, ProcId, ArgNo,
NumArgs, HOCurriedArgs) | HOArgs0],
list__append(NewArgs0, NewExtraArgs, NewArgs1)
;
HOArgs1 = HOArgs0,
NewArgs1 = NewArgs0
),
find_higher_order_args(Args, PredVars, NextArg,
HOArgs1, HOArgs, NewArgs1, NewArgs).
% if the right argument of an assignment is a higher order
% term with a known value, we need to add an entry for
% the left argument
:- pred maybe_add_alias(var::in, var::in, higher_order_info::in,
higher_order_info::out) is det.
maybe_add_alias(LVar, RVar,
info(PredVars0, Requests, NewPreds, ModuleInfo),
info(PredVars, Requests, NewPreds, ModuleInfo)) :-
(
map__search(PredVars0, RVar, yes(A, B, C))
->
map__set(PredVars0, LVar, yes(A, B, C), PredVars)
;
PredVars = PredVars0
).
:- pred update_changed_status(changed::in, changed::in, changed::out) is det.
update_changed_status(changed, _, changed).
update_changed_status(request, changed, changed).
update_changed_status(request, request, request).
update_changed_status(request, unchanged, request).
update_changed_status(unchanged, Changed, Changed).
%-------------------------------------------------------------------------------
% Predicates to process requests for specialization, and create any
% new predicates that are required.
% Filter out requests for higher-order specialization
% for preds which are too large. Maybe we could allow
% programmers to declare which predicates they want
% specialized, as with inlining?
% Nonlocal predicates are filtered out here, since they
% will not have an entry in the goal_sizes.
:- pred filter_requests(set(request)::in, goal_sizes::in,
list(request)::out) is det.
filter_requests(Requests0, GoalSizes, Requests) :-
set__to_sorted_list(Requests0, Requests1),
list__filter(lambda([X::in] is semidet, (
X = request(_, CalledPredProcId, _),
CalledPredProcId = proc(CalledPredId, _),
map__search(GoalSizes, CalledPredId, GoalSize),
max_specialized_goal_size(MaxSize),
GoalSize =< MaxSize)),
Requests1, Requests).
:- pred create_new_preds(list(request)::in, new_preds::in, new_preds::out,
set(pred_proc_id)::in, set(pred_proc_id)::out, int::in,
int::out, module_info::in, module_info::out,
io__state::di, io__state::uo) is det.
create_new_preds([], NewPreds, NewPreds, ToFix, ToFix, NextId, NextId,
Mod, Mod, IO, IO).
create_new_preds([Request | Requests], NewPreds0, NewPreds, PredsToFix0,
PredsToFix, NextHOid0, NextHOid, Module0, Module, IO0, IO) :-
Request = request(CallingPredProcId, CalledPredProcId, HOArgs),
set__insert(PredsToFix0, CallingPredProcId, PredsToFix1),
(
map__search(NewPreds0, CalledPredProcId, SpecVersions0)
->
(
% check that we aren't redoing the same pred
% SpecVersions are pred_proc_ids of the specialized
% versions of the current pred.
\+ (
set__member(X, SpecVersions0),
X = new_pred(_,_,_, DoneHOArgs),
DoneHOArgs = HOArgs
)
->
create_new_pred(Request, NewPred, NextHOid0,
NextHOid1, Module0, Module1, IO0, IO1),
set__insert(SpecVersions0, NewPred, SpecVersions),
map__det_update(NewPreds0, CalledPredProcId,
SpecVersions, NewPreds1)
;
Module1 = Module0,
NewPreds1 = NewPreds0,
IO1 = IO0,
NextHOid1 = NextHOid0
)
;
create_new_pred(Request, NewPred, NextHOid0, NextHOid1,
Module0, Module1, IO0, IO1),
set__singleton_set(SpecVersions0, NewPred),
map__set(NewPreds0, CalledPredProcId, SpecVersions0, NewPreds1)
),
create_new_preds(Requests, NewPreds1, NewPreds, PredsToFix1, PredsToFix,
NextHOid1, NextHOid, Module1, Module, IO1, IO).
% Here we create the pred_info for the new predicate.
:- pred create_new_pred(request::in, new_pred::out, int::in, int::out,
module_info::in, module_info::out, io__state::di, io__state::uo) is det.
create_new_pred(request(_CallingPredProc, CalledPredProc, HOArgs),
new_pred(NewPredId, NewProcId, Name, HOArgs), NextHOid0,
NextHOid, ModuleInfo0, ModuleInfo, IOState0, IOState) :-
CalledPredProc = proc(CalledPred, _),
module_info_get_predicate_table(ModuleInfo0,
PredTable0),
predicate_table_get_preds(PredTable0, Preds0),
map__lookup(Preds0, CalledPred, PredInfo0),
pred_info_name(PredInfo0, Name0),
pred_info_arity(PredInfo0, Arity),
pred_info_get_is_pred_or_func(PredInfo0, PredOrFunc),
module_info_name(ModuleInfo0, ModuleName),
pred_info_module(PredInfo0, PredModule),
globals__io_lookup_bool_option(very_verbose, VeryVerbose,
IOState0, IOState1),
pred_info_arg_types(PredInfo0, Tvars, Types0),
string__int_to_string(Arity, ArStr),
(
VeryVerbose = yes
->
io__write_strings(["% Specializing calls to `", PredModule, ":",
Name0, "'/", ArStr, " with higher-order arguments:\n"],
IOState1, IOState2),
list__length(Types0, ActualArity),
NumToDrop is ActualArity - Arity,
output_higher_order_args(ModuleInfo0, NumToDrop,
HOArgs, IOState2, IOState)
;
IOState = IOState1
),
string__int_to_string(NextHOid0, IdStr),
NextHOid is NextHOid0 + 1,
( ModuleName = PredModule ->
NamePrefix = ""
;
string__append(PredModule, "__", NamePrefix)
),
string__append_list([NamePrefix, Name0, "__ho", IdStr], PredName),
pred_info_typevarset(PredInfo0, TypeVars),
remove_listof_higher_order_args(Types0, 1, HOArgs, Types),
pred_info_context(PredInfo0, Context),
pred_info_clauses_info(PredInfo0, ClausesInfo),
(
pred_info_is_inlined(PredInfo0)
->
Inline = yes
;
Inline = no
),
pred_info_get_goal_type(PredInfo0, GoalType),
% *** This will need to be fixed when the condition
% field of the pred_info becomes used
Name = qualified(ModuleName, PredName),
pred_info_init(ModuleName, Name, Arity, Tvars,
Types, true, Context, ClausesInfo, local, Inline, GoalType,
PredOrFunc, PredInfo1),
pred_info_set_typevarset(PredInfo1, TypeVars, PredInfo2),
pred_info_procedures(PredInfo2, Procs0),
next_mode_id(Procs0, no, NewProcId),
predicate_table_insert(PredTable0, PredInfo2, NewPredId, PredTable),
module_info_set_predicate_table(ModuleInfo0, PredTable, ModuleInfo).
:- pred output_higher_order_args(module_info::in, int::in,
list(higher_order_arg)::in, io__state::di, io__state::uo) is det.
output_higher_order_args(_, _, []) --> [].
output_higher_order_args(ModuleInfo, NumToDrop, [HOArg | HOArgs]) -->
{ HOArg = higher_order_arg(PredId, _ProcId, ArgNo, NumArgs, _) },
{ module_info_pred_info(ModuleInfo, PredId, PredInfo) },
{ pred_info_name(PredInfo, Name) },
{ pred_info_arity(PredInfo, Arity) },
% adjust message for type_infos
{ DeclaredArgNo is ArgNo - NumToDrop },
io__write_string("\tHeadVar__"),
io__write_int(DeclaredArgNo),
io__write_string(" = `"),
io__write_string(Name),
io__write_string("'/"),
io__write_int(Arity),
io__write_string(" with "),
io__write_int(NumArgs),
io__write_string(" curried arguments\n"),
output_higher_order_args(ModuleInfo, NumToDrop, HOArgs).
:- pred remove_listof_higher_order_args(list(T)::in, int::in,
list(higher_order_arg)::in, list(T)::out) is det.
remove_listof_higher_order_args(List0, ArgNo, ArgsToRemove, List) :-
(
ArgsToRemove = []
->
List = List0
;
(
List0 = [Head | Tail],
NextArg is ArgNo + 1,
(
list__member(HOArg, ArgsToRemove),
HOArg = higher_order_arg(_, _, ArgNo, _, _)
->
List = List1
;
List = [Head | List1]
),
remove_listof_higher_order_args(Tail, NextArg,
ArgsToRemove, List1)
;
List0 = [],
List = List0
)
).
% Fixup calls to specialized predicates.
:- pred fixup_preds(list(pred_proc_id)::in, new_preds::in,
module_info::in, module_info::out) is det.
fixup_preds([], _, ModuleInfo, ModuleInfo).
fixup_preds([PredProcId | PredProcIds], NewPreds, ModuleInfo0, ModuleInfo) :-
PredProcId = proc(PredId, ProcId),
module_info_preds(ModuleInfo0, Preds0),
map__lookup(Preds0, PredId, PredInfo0),
pred_info_procedures(PredInfo0, Procs0),
map__lookup(Procs0, ProcId, ProcInfo0),
proc_info_goal(ProcInfo0, Goal0),
map__init(PredVars0),
set__init(Requests0),
traverse_goal(Goal0, Goal1, PredProcId, _, _,
info(PredVars0, Requests0, NewPreds, ModuleInfo0), _),
proc_info_variables(ProcInfo0, Varset0),
proc_info_headvars(ProcInfo0, HeadVars),
proc_info_vartypes(ProcInfo0, VarTypes0),
implicitly_quantify_clause_body(HeadVars, Goal1, Varset0, VarTypes0,
Goal, Varset, VarTypes, _),
proc_info_set_goal(ProcInfo0, Goal, ProcInfo1),
proc_info_set_variables(ProcInfo1, Varset, ProcInfo2),
proc_info_set_vartypes(ProcInfo2, VarTypes, ProcInfo),
map__det_update(Procs0, ProcId, ProcInfo, Procs),
pred_info_set_procedures(PredInfo0, Procs, PredInfo),
map__det_update(Preds0, PredId, PredInfo, Preds),
module_info_set_preds(ModuleInfo0, Preds, ModuleInfo1),
fixup_preds(PredProcIds, NewPreds, ModuleInfo1, ModuleInfo).
:- pred create_specialized_versions(list(pred_proc_id)::in, new_preds::in,
set(request)::in, set(request)::out, goal_sizes::in,
goal_sizes::out, module_info::in, module_info::out) is det.
create_specialized_versions([], _, Requests, Requests, Sizes, Sizes, Mod, Mod).
create_specialized_versions([PredProc | PredProcs], NewPreds, Requests0,
Requests, GoalSizes0, GoalSizes, ModuleInfo0, ModuleInfo) :-
map__lookup(NewPreds, PredProc, SpecVersions0),
set__to_sorted_list(SpecVersions0, SpecVersions),
PredProc = proc(PredId, ProcId),
module_info_pred_proc_info(ModuleInfo0, PredId, ProcId, _, ProcInfo),
create_specialized_versions_2(SpecVersions, NewPreds, ProcInfo,
Requests0, Requests1, GoalSizes0, GoalSizes1,
ModuleInfo0, ModuleInfo1),
create_specialized_versions(PredProcs, NewPreds, Requests1, Requests,
GoalSizes1, GoalSizes, ModuleInfo1, ModuleInfo).
% Create specialized versions of a single procedure.
:- pred create_specialized_versions_2(list(new_pred)::in, new_preds::in,
proc_info::in, set(request)::in, set(request)::out,
goal_sizes::in, goal_sizes::out, module_info::in,
module_info::out) is det.
create_specialized_versions_2([], _, _, Requests, Requests, Sizes, Sizes,
ModuleInfo, ModuleInfo).
create_specialized_versions_2([NewPred | NewPreds], NewPredMap, NewProcInfo0,
Requests0, Requests, GoalSizes0, GoalSizes, ModuleInfo0, ModuleInfo)
:-
NewPred = new_pred(NewPredId, NewProcId, _Name, HOArgs),
module_info_get_predicate_table(ModuleInfo0, PredTable0),
predicate_table_get_preds(PredTable0, Preds0),
map__lookup(Preds0, NewPredId, NewPredInfo0),
pred_info_procedures(NewPredInfo0, NewProcs0),
map__init(Substitution0),
proc_info_headvars(NewProcInfo0, HeadVars0),
proc_info_argmodes(NewProcInfo0, ArgModes0),
construct_higher_order_terms(ModuleInfo0, HeadVars0, HeadVars1,
ArgModes0, ArgModes1, HOArgs, NewProcInfo0, NewProcInfo1,
NewPredInfo0, NewPredInfo1, Substitution0,
Substitution, Constructions),
proc_info_goal(NewProcInfo1, Goal0),
Goal0 = GoalExpr0 - Info0,
% put in constructions to bind the headvars to
% their specialized values
(
GoalExpr0 = conj(Goals0)
->
list__append(Constructions, Goals0, Goals)
;
list__append(Constructions, [Goal0], Goals)
),
Goal1 = conj(Goals) - Info0,
remove_listof_higher_order_args(HeadVars1, 1, HOArgs, HeadVars),
remove_listof_higher_order_args(ArgModes1, 1, HOArgs, ArgModes),
% specialize types
proc_info_vartypes(NewProcInfo1, VarTypes0),
apply_substitution_to_type_map(VarTypes0, Substitution, VarTypes1),
map__apply_to_list(HeadVars, VarTypes1, ArgTypes0),
term__vars_list(ArgTypes0, TypeVars),
varset__init(ArgTVarset0),
map__init(DummyVarTypes), % type vars don't have a type
map__init(Renaming0),
varset__init(OldVarNames),
goal_util__create_variables(TypeVars, ArgTVarset0, DummyVarTypes,
Renaming0, DummyVarTypes, OldVarNames, ArgTVarset, _, Renaming),
term__apply_variable_renaming_to_list(ArgTypes0, Renaming, ArgTypes),
pred_info_set_arg_types(NewPredInfo1, ArgTVarset, ArgTypes,
NewPredInfo2),
map__init(PredVars0),
traverse_goal(Goal1, Goal2, proc(NewPredId, NewProcId), _, GoalSize,
info(PredVars0, Requests0, NewPredMap, ModuleInfo0),
info(_, Requests1,_,_)),
map__set(GoalSizes0, NewPredId, GoalSize, GoalSizes1),
proc_info_variables(NewProcInfo1, Varset0),
implicitly_quantify_clause_body(HeadVars, Goal2, Varset0, VarTypes1,
Goal3, Varset, VarTypes, _),
recompute_instmap_delta(Goal3, Goal4, ModuleInfo0, ModuleInfo1),
proc_info_set_goal(NewProcInfo1, Goal4, NewProcInfo1a),
proc_info_set_variables(NewProcInfo1a, Varset, NewProcInfo2),
proc_info_set_vartypes(NewProcInfo2, VarTypes, NewProcInfo3),
proc_info_set_argmodes(NewProcInfo3, ArgModes, NewProcInfo4),
proc_info_set_headvars(NewProcInfo4, HeadVars, NewProcInfo),
map__det_insert(NewProcs0, NewProcId, NewProcInfo, NewProcs),
pred_info_set_procedures(NewPredInfo2, NewProcs, NewPredInfo),
map__det_update(Preds0, NewPredId, NewPredInfo, Preds),
predicate_table_set_preds(PredTable0, Preds, PredTable),
module_info_set_predicate_table(ModuleInfo1, PredTable, ModuleInfo2),
create_specialized_versions_2(NewPreds, NewPredMap, NewProcInfo0,
Requests1, Requests, GoalSizes1, GoalSizes,
ModuleInfo2, ModuleInfo).
% Returns a list of hlds__goals which construct the list of
% higher order arguments which have been specialized. Traverse
% goal will then recognize these as having a unique possible
% value and will specialize any calls involving them.
% Substitution* is a substitution for all variables in the
% specialized predicates. The caller should apply this
% to the type map for this procedure.
% This predicate fixes the tvarset in the pred_info
% containing the type vars in the goal.
% The caller should fix the argument types and the
% corresponding tvarset.
% Takes an original list of headvars and arg_modes and
% returns these with curried arguments added. The
% caller should remove the higher-order arguments from
% the argument list.
% The predicate is recursively applied to all curried
% higher order arguments of higher order arguments.
:- pred construct_higher_order_terms(module_info::in, list(var)::in,
list(var)::out, list(mode)::in, list(mode)::out,
list(higher_order_arg)::in, proc_info::in, proc_info::out,
pred_info::in, pred_info::out, tsubst::in,
tsubst::out, list(hlds__goal)::out) is det.
construct_higher_order_terms(_, HeadVars, HeadVars, ArgModes, ArgModes,
[], ProcInfo, ProcInfo, PredInfo, PredInfo, Subst, Subst, []).
construct_higher_order_terms(ModuleInfo, HeadVars0, HeadVars, ArgModes0,
ArgModes, [HOArg | HOArgs], ProcInfo0, ProcInfo, PredInfo0,
PredInfo, Substitution0, Substitution, Goals) :-
HOArg = higher_order_arg(PredId, ProcId, Index, NumArgs, CurriedHOArgs),
list__index1_det(HeadVars0, Index, LVar),
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
CalledPredInfo, CalledProcInfo),
pred_info_arg_types(CalledPredInfo, CalledTVarset, CalledArgTypes0),
% Add the curried arguments to the procedure's argument list.
proc_info_argmodes(CalledProcInfo, CalledArgModes),
(
list__split_list(NumArgs, CalledArgModes,
CurriedArgModes0a, UnCurriedArgModes0)
->
CurriedArgModes0 = CurriedArgModes0a,
UnCurriedArgModes = UnCurriedArgModes0
;
error("list__split_list_failed")
),
proc_info_variables(ProcInfo0, Varset0),
varset__new_vars(Varset0, NumArgs, NewHeadVars0, Varset1),
proc_info_set_variables(ProcInfo0, Varset1, ProcInfo1),
% Find the type substitution and work out the types
% of the new variables.
proc_info_vartypes(ProcInfo1, VarTypes0),
map__lookup(VarTypes0, LVar, LVarType),
pred_info_typevarset(PredInfo0, TypeVarset0),
varset__merge(TypeVarset0, CalledTVarset, CalledArgTypes0,
TypeVarset1, CalledArgTypes),
pred_info_set_typevarset(PredInfo0, TypeVarset1, PredInfo1),
(
list__split_list(NumArgs, CalledArgTypes,
CurriedArgTypes0, UnCurriedArgTypes0)
->
CurriedArgTypes = CurriedArgTypes0,
UnCurriedArgTypes = UnCurriedArgTypes0
;
error("list__split failed")
),
(
type_is_higher_order(LVarType, _PredOrFunc, LVarArgTypes)
->
(
type_list_subsumes(LVarArgTypes, UnCurriedArgTypes,
NewSubstitution)
->
% Add the substitution found for this higher-order
% term to the substitution to be applied to the type
% map.
map__overlay(Substitution0, NewSubstitution,
Substitution1)
;
Substitution1 = Substitution0
)
;
error("specialized argument not of higher-order type")
),
map__det_insert_from_corresponding_lists(VarTypes0, NewHeadVars0,
CurriedArgTypes, VarTypes1),
proc_info_set_vartypes(ProcInfo1, VarTypes1, ProcInfo2),
% Recursively construct the curried higher-order arguments.
construct_higher_order_terms(ModuleInfo, NewHeadVars0, NewHeadVars1,
CurriedArgModes0, CurriedArgModes1, CurriedHOArgs,
ProcInfo2, ProcInfo3, PredInfo1, PredInfo2, Substitution1,
Substitution2, CurriedGoals),
% Fix up the argument lists.
remove_listof_higher_order_args(CurriedArgModes1, 1,
CurriedHOArgs, CurriedArgModes),
remove_listof_higher_order_args(NewHeadVars1, 1,
CurriedHOArgs, NewHeadVars),
list__append(ArgModes0, CurriedArgModes, ArgModes1),
list__append(HeadVars0, NewHeadVars, HeadVars1),
% Build the higher-order constant.
pred_info_module(CalledPredInfo, Module),
pred_info_name(CalledPredInfo, Name),
Rhs = functor(cons(qualified(Module, Name), NumArgs), NewHeadVars0),
Context = unify_context(head(Index), []),
mode_util__modes_to_uni_modes(CurriedArgModes1, CurriedArgModes1,
ModuleInfo, UniModes),
Unify = construct(LVar, pred_const(PredId, ProcId),
NewHeadVars0, UniModes),
proc_info_inferred_determinism(ProcInfo3, Detism),
pred_info_get_is_pred_or_func(PredInfo2, PredOrFunc),
PredInstInfo = pred_inst_info(PredOrFunc, UnCurriedArgModes, Detism),
Inst = ground(shared, yes(PredInstInfo)),
Unimode = (free -> Inst) - (Inst -> Inst),
Goal = unify(LVar, Rhs, Unimode, Unify, Context),
goal_info_init(Info0),
goal_info_set_determinism(Info0, Detism, Info1),
instmap_delta_init_reachable(InstmapDelta0),
instmap_delta_insert(InstmapDelta0, LVar, Inst, InstmapDelta),
goal_info_set_instmap_delta(Info1, InstmapDelta, Info),
construct_higher_order_terms(ModuleInfo, HeadVars1, HeadVars, ArgModes1,
ArgModes, HOArgs, ProcInfo3, ProcInfo, PredInfo2, PredInfo,
Substitution2, Substitution, Goals1),
list__condense([CurriedGoals, [Goal - Info], Goals1], Goals).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%