Files
mercury/compiler/implicit_parallelism.m
Julien Fischer 9958d3883c Fix some formatting.
Estimated hours taken: 0
Branches: main

Fix some formatting.

compiler/distance_granularity.m:
compiler/exception_analysis.m:
compiler/implicit_parallelism.m:
compiler/inst_graph.m:
compiler/interval.m:
compiler/layout_out.m:
compiler/lp_rational.m:
compiler/make.program_target.m:
compiler/modules.m:
compiler/prog_data.m:
compiler/purity.m:
compiler/recompilation.check.m:
compiler/term_constr_data.m:
compiler/term_util.m:
compiler/xml_documentation.m:
deep_profiler/mdprof_cgi.m:
library/pqueue.m:
profiler/output.m:
	Fix the positioning of commas.

	s/[_|_]/[_ | _]/ in a spot.
2007-05-23 10:09:24 +00:00

965 lines
38 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2006-2007 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File : implicit_parallelism.m.
% Author: tannier.
%
% This module uses deep profiling feedback information generated by
% mdprof_feedback to introduce parallel conjunctions where it could be
% worthwhile (implicit parallelism). It deals with both independent and
% dependent parallelism.
%
% TODO
% - Once a call which is a candidate for implicit parallelism is found,
% search forward AND backward for the closest goal which is also a
% candidate for implicit parallelism/parallel conjunction and determine
% which side is the best (on the basis of the number of shared variables).
%
%-----------------------------------------------------------------------------%
:- module transform_hlds.implicit_parallelism.
:- interface.
:- import_module hlds.hlds_module.
:- import_module io.
%-----------------------------------------------------------------------------%
% apply_implicit_parallelism_transformation(!ModuleInfo, FeedbackFile, !IO)
%
% Apply the implicit parallelism transformation using the specified
% feedback file.
%
:- pred apply_implicit_parallelism_transformation(
module_info::in, module_info::out, string::in, io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds.inst_match.
:- import_module hlds.hlds_goal.
:- import_module check_hlds.mode_util.
:- import_module hlds.goal_util.
:- import_module hlds.hlds_pred.
:- import_module hlds.instmap.
:- import_module hlds.quantification.
:- import_module libs.compiler_util.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.error_util.
:- import_module parse_tree.prog_data.
:- import_module transform_hlds.dep_par_conj.
:- import_module bool.
:- import_module char.
:- import_module counter.
:- import_module int.
:- import_module list.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module string.
%-----------------------------------------------------------------------------%
% Represent a call site static which is a candidate for introducing
% implicit parallelism.
%
:- type candidate_call_site
---> candidate_call_site(
caller :: string, % The caller of the call.
slot_number :: int, % The slot number of the call.
kind :: call_site_kind, % The kind of the call.
callee :: string % The callee of the call.
).
% Represent the kind of a call site.
%
:- type call_site_kind
---> csk_normal
; csk_special
; csk_higher_order
; csk_method
; csk_callback.
% Construct a call_site_kind from its string representation.
%
:- pred construct_call_site_kind(string::in, call_site_kind::out) is semidet.
construct_call_site_kind("normal_call", csk_normal).
construct_call_site_kind("special_call", csk_special).
construct_call_site_kind("higher_order_call", csk_higher_order).
construct_call_site_kind("method_call", csk_method).
construct_call_site_kind("callback", csk_callback).
%-----------------------------------------------------------------------------%
apply_implicit_parallelism_transformation(!ModuleInfo, FeedbackFile, !IO) :-
parse_feedback_file(FeedbackFile, MaybeCandidateCallSites, !IO),
(
MaybeCandidateCallSites = error(Error),
io.stderr_stream(Stderr, !IO),
io.write_string(Stderr, Error ++ "\n", !IO)
;
MaybeCandidateCallSites = ok(CandidateCallSites),
module_info_predids(PredIds, !ModuleInfo),
process_preds_for_implicit_parallelism(PredIds, CandidateCallSites,
!ModuleInfo)
).
% Process predicates for implicit parallelism.
%
:- pred process_preds_for_implicit_parallelism(list(pred_id)::in,
list(candidate_call_site)::in, module_info::in, module_info::out)
is det.
process_preds_for_implicit_parallelism([], _ListCandidateCallSite,
!ModuleInfo).
process_preds_for_implicit_parallelism([ PredId | PredIdList ],
ListCandidateCallSite, !ModuleInfo) :-
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
ProcIds = pred_info_non_imported_procids(PredInfo),
process_procs_for_implicit_parallelism(PredId, ProcIds,
ListCandidateCallSite, !ModuleInfo),
process_preds_for_implicit_parallelism(PredIdList,
ListCandidateCallSite, !ModuleInfo).
% Process procedures for implicit parallelism.
%
:- pred process_procs_for_implicit_parallelism(pred_id::in,
list(proc_id)::in, list(candidate_call_site)::in,
module_info::in, module_info::out) is det.
process_procs_for_implicit_parallelism(_PredId, [],
_ListCandidateCallSite, !ModuleInfo).
process_procs_for_implicit_parallelism(PredId, [ ProcId | ProcIds ],
ListCandidateCallSite, !ModuleInfo) :-
module_info_pred_proc_info(!.ModuleInfo, PredId, ProcId,
PredInfo0, ProcInfo0),
% Initialize the counter for the slot number.
SiteNumCounter = counter.init(0),
pred_proc_id_to_raw_id(PredInfo0, ProcId, CallerRawId),
get_callees_feedback(CallerRawId, ListCandidateCallSite, [],
CallSites),
list.length(CallSites, NumCallSites),
( NumCallSites = 0 ->
% No candidate calls for implicit parallelism in this procedure.
process_procs_for_implicit_parallelism(PredId, ProcIds,
ListCandidateCallSite, !ModuleInfo)
;
proc_info_get_goal(ProcInfo0, Body0),
process_goal_for_implicit_parallelism(Body0, Body, ProcInfo0,
!ModuleInfo, no, _, 0, _, CallSites, _, SiteNumCounter, _),
proc_info_set_goal(Body, ProcInfo0, ProcInfo1),
proc_info_set_has_parallel_conj(yes, ProcInfo1, ProcInfo2),
requantify_proc(ProcInfo2, ProcInfo3),
RecomputeAtomic = no,
recompute_instmap_delta_proc(RecomputeAtomic, ProcInfo3, ProcInfo,
!ModuleInfo),
pred_info_set_proc_info(ProcId, ProcInfo, PredInfo0, PredInfo),
module_info_set_pred_info(PredId, PredInfo, !ModuleInfo),
process_procs_for_implicit_parallelism(PredId, ProcIds,
ListCandidateCallSite, !ModuleInfo)
).
% Filter the list of call site information from the feedback file so that
% the resulting list only contains those call sites that belong to the first
% argument, e.g. the caller.
%
:- pred get_callees_feedback(string::in, list(candidate_call_site)::in,
list(candidate_call_site)::in, list(candidate_call_site)::out) is det.
get_callees_feedback(_Caller, [], !ResultAcc).
get_callees_feedback(Caller, [ CandidateCallSite | ListCandidateCallSite ],
!ResultAcc) :-
CandidateCallSite = candidate_call_site(CSSCaller, _, _, _),
( Caller = CSSCaller ->
!:ResultAcc = [ CandidateCallSite | !.ResultAcc ],
get_callees_feedback(Caller, ListCandidateCallSite, !ResultAcc)
;
get_callees_feedback(Caller, ListCandidateCallSite, !ResultAcc)
).
% Process a goal for implicit parallelism.
% MaybeConj is the conjunction which contains Goal.
%
:- pred process_goal_for_implicit_parallelism(hlds_goal::in, hlds_goal::out,
proc_info::in, module_info::in, module_info::out,
maybe(hlds_goal_expr)::in, maybe(hlds_goal_expr)::out, int ::in, int::out,
list(candidate_call_site)::in, list(candidate_call_site)::out,
counter::in, counter::out) is det.
process_goal_for_implicit_parallelism(!Goal, ProcInfo, !ModuleInfo,
!MaybeConj, !IndexInConj, !CalleeListToBeParallelized, !SiteNumCounter) :-
!.Goal = hlds_goal(GoalExpr0, GoalInfo),
(
GoalExpr0 = unify(_, _, _, _, _),
increment_index_if_in_conj(!.MaybeConj, !IndexInConj)
;
GoalExpr0 = plain_call(_, _, _, _, _, _),
process_call_for_implicit_parallelism(!.Goal, ProcInfo, !ModuleInfo,
!IndexInConj, !MaybeConj, !CalleeListToBeParallelized,
!SiteNumCounter)
% We deal with the index in the conjunction in
% process_call_for_implicit_parallelism.
;
GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _),
process_call_for_implicit_parallelism(!.Goal, ProcInfo, !ModuleInfo,
!IndexInConj, !MaybeConj, !CalleeListToBeParallelized,
!SiteNumCounter)
;
GoalExpr0 = generic_call(Details, _, _, _),
(
Details = higher_order(_, _, _, _),
process_call_for_implicit_parallelism(!.Goal, ProcInfo,
!ModuleInfo, !IndexInConj, !MaybeConj,
!CalleeListToBeParallelized, !SiteNumCounter)
;
Details = class_method(_, _, _, _),
process_call_for_implicit_parallelism(!.Goal, ProcInfo,
!ModuleInfo, !IndexInConj, !MaybeConj,
!CalleeListToBeParallelized, !SiteNumCounter)
;
Details = event_call(_),
increment_index_if_in_conj(!.MaybeConj, !IndexInConj)
;
Details = cast(_),
increment_index_if_in_conj(!.MaybeConj, !IndexInConj)
)
;
% No distinction is made between plain conjunctions and parallel
% conjunctions. We have to process the parallel conjunction for the
% slot number.
GoalExpr0 = conj(_, _),
process_conj_for_implicit_parallelism(GoalExpr0, GoalExpr, 1,
ProcInfo, !ModuleInfo, !CalleeListToBeParallelized,
!SiteNumCounter),
% A plain conjunction will never be contained in an other plain
% conjunction. As for parallel conjunctions, they will not
% be modified. Therefore, incrementing the index suffices (no need to
% call update_conj_and_index).
!:Goal = hlds_goal(GoalExpr, GoalInfo),
increment_index_if_in_conj(!.MaybeConj, !IndexInConj)
;
GoalExpr0 = disj(Goals0),
process_disj_for_implicit_parallelism(Goals0, [], Goals,
ProcInfo, !ModuleInfo, !CalleeListToBeParallelized,
!SiteNumCounter),
GoalProcessed = hlds_goal(disj(Goals), GoalInfo),
update_conj_and_index(!MaybeConj, GoalProcessed, !IndexInConj),
% If we are not in a conjunction, then we need to return the modified
% value of Goal. In we are in a conjunction, that information is not
% read (see process_conj_for_implicit_parallelism).
!:Goal = GoalProcessed
;
GoalExpr0 = switch(Var, CanFail, Cases0),
process_switch_cases_for_implicit_parallelism(Cases0, [], Cases,
ProcInfo, !ModuleInfo, !CalleeListToBeParallelized,
!SiteNumCounter),
GoalProcessed = hlds_goal(switch(Var, CanFail, Cases), GoalInfo),
update_conj_and_index(!MaybeConj, GoalProcessed, !IndexInConj),
!:Goal = GoalProcessed
;
GoalExpr0 = negation(Goal0),
process_goal_for_implicit_parallelism(Goal0, Goal, ProcInfo,
!ModuleInfo, !MaybeConj, !IndexInConj, !CalleeListToBeParallelized,
!SiteNumCounter),
GoalProcessed = hlds_goal(negation(Goal), GoalInfo),
update_conj_and_index(!MaybeConj, GoalProcessed, !IndexInConj),
!:Goal = GoalProcessed
;
GoalExpr0 = scope(Reason, Goal0),
% 0 is the default value when we are not in a conjunction (in this case
% a scope).
process_goal_for_implicit_parallelism(Goal0, Goal, ProcInfo,
!ModuleInfo, no, _, 0, _, !CalleeListToBeParallelized,
!SiteNumCounter),
GoalProcessed = hlds_goal(scope(Reason, Goal), GoalInfo),
update_conj_and_index(!MaybeConj, GoalProcessed, !IndexInConj),
!:Goal = GoalProcessed
;
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
process_goal_for_implicit_parallelism(Cond0, Cond, ProcInfo,
!ModuleInfo, no, _, 0, _, !CalleeListToBeParallelized,
!SiteNumCounter),
process_goal_for_implicit_parallelism(Then0, Then, ProcInfo,
!ModuleInfo, no, _, 0, _, !CalleeListToBeParallelized,
!SiteNumCounter),
process_goal_for_implicit_parallelism(Else0, Else, ProcInfo,
!ModuleInfo, no, _, 0, _, !CalleeListToBeParallelized,
!SiteNumCounter),
GoalProcessed = hlds_goal(if_then_else(Vars, Cond, Then, Else),
GoalInfo),
update_conj_and_index(!MaybeConj, GoalProcessed, !IndexInConj),
!:Goal = GoalProcessed
;
GoalExpr0 = shorthand(_),
increment_index_if_in_conj(!.MaybeConj, !IndexInConj)
).
% Increment the index if we are in a conjunction.
%
:- pred increment_index_if_in_conj(maybe(hlds_goal_expr)::in, int::in, int::out)
is det.
increment_index_if_in_conj(MaybeConj, !IndexInConj) :-
(
MaybeConj = yes(_),
!:IndexInConj = !.IndexInConj + 1
;
MaybeConj = no
).
% Process a call for implicit parallelism.
%
:- pred process_call_for_implicit_parallelism(hlds_goal::in, proc_info::in,
module_info::in, module_info::out, int::in, int::out,
maybe(hlds_goal_expr)::in, maybe(hlds_goal_expr)::out,
list(candidate_call_site)::in, list(candidate_call_site)::out,
counter::in, counter::out) is det.
process_call_for_implicit_parallelism(Call, ProcInfo, !ModuleInfo,
!IndexInConj, !MaybeConj, !CalleeListToBeParallelized, !SiteNumCounter) :-
counter.allocate(SlotNumber, !SiteNumCounter),
get_call_kind_and_callee(!.ModuleInfo, Call, Kind, CalleeRawId),
(
!.MaybeConj = yes(Conj0), Conj0 = conj(plain_conj, ConjGoals0)
->
(
is_in_css_list_to_be_parallelized(Kind, SlotNumber, CalleeRawId,
!.CalleeListToBeParallelized, [], !:CalleeListToBeParallelized)
->
(
build_goals_surrounded_by_calls_to_be_parallelized(ConjGoals0,
!.ModuleInfo, [ Call ], Goals, !.IndexInConj + 1, End,
!SiteNumCounter, !CalleeListToBeParallelized)
->
parallelize_calls(Goals, !.IndexInConj, End, Conj0, Conj,
ProcInfo, !ModuleInfo),
!:IndexInConj = End,
!:MaybeConj = yes(Conj)
;
% The next call is not in the feedback file or we've hit a
% plain conjunction/disjunction/switch/if then else.
!:IndexInConj = !.IndexInConj + 1
)
;
% Not to be parallelized.
!:IndexInConj = !.IndexInConj + 1
)
;
% Call is not in a conjunction or the call is already in a parallel
% conjunction.
true
).
% Give the raw id (the same as in the deep profiler) of a callee contained
% in a call.
%
:- pred get_call_kind_and_callee(module_info::in, hlds_goal::in,
call_site_kind::out, string::out) is det.
get_call_kind_and_callee(ModuleInfo, Call, Kind, CalleeRawId) :-
GoalExpr = Call ^ hlds_goal_expr,
(
GoalExpr = plain_call(PredId, ProcId, _, _, _, _)
->
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
PredInfo, _),
pred_proc_id_to_raw_id(PredInfo, ProcId, CalleeRawId),
Kind = csk_normal
;
(
GoalExpr = call_foreign_proc(_, PredId, ProcId, _, _, _, _)
->
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
PredInfo, _),
pred_proc_id_to_raw_id(PredInfo, ProcId, CalleeRawId),
Kind = csk_special
;
(
GoalExpr = generic_call(Details, _, _, _)
->
(
Details = higher_order(_, _, _, _),
CalleeRawId = "",
Kind = csk_higher_order
;
Details = class_method(_, _, _, _),
CalleeRawId = "",
Kind = csk_method
;
Details = event_call(_),
unexpected(this_file, "get_call_kind_and_callee")
;
Details = cast(_),
unexpected(this_file, "get_call_kind_and_callee")
)
;
unexpected(this_file, "get_call_kind_and_callee")
)
)
).
% Convert a pred_info and a proc_id to the raw procedure id (the same used
% in the deep profiler).
%
:- pred pred_proc_id_to_raw_id(pred_info::in, proc_id::in, string::out) is det.
pred_proc_id_to_raw_id(PredInfo, ProcId, RawId) :-
ModuleName = pred_info_module(PredInfo),
Name = pred_info_name(PredInfo),
OrigArity = pred_info_orig_arity(PredInfo),
IsPredOrFunc = pred_info_is_pred_or_func(PredInfo),
ModuleString = sym_name_to_string(ModuleName),
ProcIdInt = proc_id_to_int(ProcId),
RawId = string.append_list([ ModuleString, ".", Name, "/",
string.int_to_string(OrigArity),
( IsPredOrFunc = pf_function -> "+1" ; ""), "-",
string.from_int(ProcIdInt) ]).
% Succeeds if the caller, slot number and callee correspond to a
% candidate_call_site in the list given as a parameter.
% Fail otherwise.
%
:- pred is_in_css_list_to_be_parallelized(call_site_kind::in, int::in,
string::in, list(candidate_call_site)::in,
list(candidate_call_site)::in, list(candidate_call_site)::out)
is semidet.
is_in_css_list_to_be_parallelized(Kind, SlotNumber, CalleeRawId,
ListCandidateCallSite, !ResultAcc) :-
(
ListCandidateCallSite = [],
fail
;
ListCandidateCallSite = [ CandidateCallSite |
ListCandidateCallSite0 ],
CandidateCallSite = candidate_call_site(_, CSSSlotNumber, CSSKind,
CSSCallee),
% =< because there is not a one to one correspondance with the source
% code. New calls might have been added by the previous passes of the
% compiler.
( CSSSlotNumber =< SlotNumber, CSSKind = Kind, CSSCallee = CalleeRawId
->
list.append(!.ResultAcc, ListCandidateCallSite0, !:ResultAcc)
;
list.append(!.ResultAcc, [ CandidateCallSite ], !:ResultAcc),
is_in_css_list_to_be_parallelized(Kind, SlotNumber, CalleeRawId,
ListCandidateCallSite0, !ResultAcc)
)
).
% Build a list of goals surrounded by two calls which are in the feedback
% file or by a call which is in the feedback file and a parallel
% conjunction.
%
% Succeed if we can build that list of goals.
% Fail otherwise.
%
:- pred build_goals_surrounded_by_calls_to_be_parallelized(list(hlds_goal)::in,
module_info::in, list(hlds_goal)::in, list(hlds_goal)::out,
int::in, int::out, counter::in, counter::out,
list(candidate_call_site)::in, list(candidate_call_site)::out)
is semidet.
build_goals_surrounded_by_calls_to_be_parallelized(ConjGoals, ModuleInfo,
!ResultAcc, !Index, !SiteNumCounter, !CalleeListToBeParallelized) :-
list.length(ConjGoals, Length),
( !.Index > Length ->
fail
;
list.index1_det(ConjGoals, !.Index, Goal),
GoalExpr = Goal ^ hlds_goal_expr,
(
( GoalExpr = disj(_)
; GoalExpr = switch(_, _, _)
; GoalExpr = if_then_else(_, _, _, _)
; GoalExpr = conj(plain_conj, _)
)
->
fail
;
( goal_is_conjunction(Goal, parallel_conj) ->
list.append(!.ResultAcc, [ Goal ], !:ResultAcc)
;
( goal_is_call_or_negated_call(Goal) ->
counter.allocate(SlotNumber, !SiteNumCounter),
get_call_kind_and_callee(ModuleInfo, Goal, Kind,
CalleeRawId),
( is_in_css_list_to_be_parallelized(Kind, SlotNumber,
CalleeRawId, !.CalleeListToBeParallelized, [],
!:CalleeListToBeParallelized)
->
list.append(!.ResultAcc, [ Goal ], !:ResultAcc)
;
list.append(!.ResultAcc, [ Goal ], !:ResultAcc),
!:Index = !.Index + 1,
build_goals_surrounded_by_calls_to_be_parallelized(
ConjGoals, ModuleInfo, !ResultAcc, !Index,
!SiteNumCounter, !CalleeListToBeParallelized)
)
;
list.append(!.ResultAcc, [ Goal ], !:ResultAcc),
!:Index = !.Index + 1,
build_goals_surrounded_by_calls_to_be_parallelized(
ConjGoals, ModuleInfo, !ResultAcc, !Index,
!SiteNumCounter, !CalleeListToBeParallelized)
)
)
)
).
% Succeeds if Goal is a conjunction and return the type of the
% conjunction. Fail otherwise.
%
:- pred goal_is_conjunction(hlds_goal::in, conj_type::out) is semidet.
goal_is_conjunction(Goal, Type) :-
GoalExpr = Goal ^ hlds_goal_expr,
GoalExpr = conj(Type, _).
% Succeed if Goal is a call or a negated call.
% Call here includes higher-order and class method calls.
% Fail otherwise.
%
:- pred goal_is_call_or_negated_call(hlds_goal::in) is semidet.
goal_is_call_or_negated_call(Goal) :-
GoalExpr = Goal ^ hlds_goal_expr,
(
GoalExpr = plain_call(_, _, _, _, _, _)
;
GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
;
GoalExpr = generic_call(Details, _, _, _),
(
Details = class_method(_, _, _, _)
;
Details = higher_order(_, _, _, _)
)
;
GoalExpr = negation(GoalNeg),
GoalNegExpr = GoalNeg ^ hlds_goal_expr,
(
GoalNegExpr = plain_call(_, _, _, _, _, _)
;
GoalNegExpr = call_foreign_proc(_, _, _, _, _, _, _)
;
GoalNegExpr = generic_call(Details, _, _, _),
(
Details = class_method(_, _, _, _)
;
Details = higher_order(_, _, _, _)
)
)
).
% Parallelize two calls/a call and a parallel conjunction which might have
% goals between them. If these have no dependencies with the first call then
% we move them before the first call and parallelize the two calls/call and
% parallel conjunction.
%
% Goals is contained in Conj.
%
:- pred parallelize_calls(list(hlds_goal)::in, int::in, int::in,
hlds_goal_expr::in, hlds_goal_expr::out, proc_info::in,
module_info::in, module_info::out) is det.
parallelize_calls(Goals, Start, End, !Conj, ProcInfo, !ModuleInfo) :-
( !.Conj = conj(plain_conj, ConjGoals0) ->
( ConjGoals0 = [ FirstGoal, LastGoal ] ->
( is_worth_parallelizing(FirstGoal, LastGoal, ProcInfo,
!.ModuleInfo)
->
( goal_is_conjunction(LastGoal, parallel_conj) ->
% The parallel conjunction has to remain flatened.
add_call_to_parallel_conjunction(FirstGoal, LastGoal,
ParallelGoal),
!:Conj = ParallelGoal ^ hlds_goal_expr
;
!:Conj = conj(parallel_conj, ConjGoals0)
)
;
% Not worth parallelizing.
true
)
;
% There are more than two goals in the conjunction.
list.length(Goals, Length),
list.index1_det(Goals, 1, FirstGoal),
list.index1_det(Goals, Length, LastGoal),
( is_worth_parallelizing(FirstGoal, LastGoal, ProcInfo,
!.ModuleInfo)
->
GoalsInBetweenAndLast = list.det_tail(Goals),
list.delete_all(GoalsInBetweenAndLast, LastGoal,
GoalsInBetween),
% Check the dependencies of GoalsInBetween with FirstGoal.
list.filter(goal_depends_on_goal(FirstGoal),
GoalsInBetween, GoalsFiltered),
( list.is_empty(GoalsFiltered) ->
( goal_is_conjunction(LastGoal, parallel_conj) ->
add_call_to_parallel_conjunction(FirstGoal, LastGoal,
ParallelGoal)
;
create_conj(FirstGoal, LastGoal, parallel_conj,
ParallelGoal)
),
( Start = 1 ->
GoalsFront = []
;
list.det_split_list(Start - 1, ConjGoals0,
GoalsFront, _)
),
list.length(ConjGoals0, ConjLength),
( End = ConjLength ->
GoalsBack = []
;
list.det_split_list(End, ConjGoals0, _,
GoalsBack)
),
list.append(GoalsFront, GoalsInBetween,
GoalsFrontWithBetween),
list.append(GoalsFrontWithBetween, [ ParallelGoal ],
GoalsWithoutBack),
list.append(GoalsWithoutBack, GoalsBack, ConjGoals),
!:Conj = conj(plain_conj, ConjGoals)
;
% The goals between the two calls/call and parallel
% conjunction can't be moved before the first call.
true
)
;
% Not worth parallelizing.
true
)
)
;
% Conj is not a conjunction.
unexpected(this_file, "parallelize_calls")
).
% Two calls are worth parallelizing if the number of shared variables is
% smaller than the number of argument variables of at least one of the two
% calls.
%
% A call and a parallel conjunction are worth parallelizing if the number of
% shared variables is smaller than the number of argument variables of the
% call.
%
% Succeed if it is worth parallelizing the two goals.
% Fail otherwise.
%
:- pred is_worth_parallelizing(hlds_goal::in, hlds_goal::in, proc_info::in,
module_info::in) is semidet.
is_worth_parallelizing(GoalA, GoalB, ProcInfo, ModuleInfo) :-
proc_info_get_initial_instmap(ProcInfo, ModuleInfo, InstMap),
SharedVars = find_shared_variables(ModuleInfo, InstMap, [ GoalA, GoalB ]),
set.to_sorted_list(SharedVars, SharedVarsList),
list.length(SharedVarsList, NbSharedVars),
( NbSharedVars = 0 ->
% No shared vars between the goals.
true
;
( goal_is_conjunction(GoalB, parallel_conj) ->
get_number_args(GoalA, NbArgsA),
NbSharedVars < NbArgsA
;
(
get_number_args(GoalA, NbArgsA),
get_number_args(GoalB, NbArgsB)
->
( NbSharedVars < NbArgsA, NbSharedVars < NbArgsB
; NbSharedVars = NbArgsA, NbSharedVars < NbArgsB
; NbSharedVars < NbArgsA, NbSharedVars = NbArgsB
)
;
unexpected(this_file, "is_worth_parallelizing")
)
)
).
% Give the number of argument variables of a call.
%
:- pred get_number_args(hlds_goal::in, int::out) is semidet.
get_number_args(Call, NbArgs) :-
CallExpr = Call ^ hlds_goal_expr,
(
CallExpr = plain_call(_, _, Args, _, _, _),
list.length(Args, NbArgs)
;
CallExpr = generic_call(Details, Args, _, _),
(
Details = higher_order(_, _, _, _),
list.length(Args, NbArgs)
;
Details = class_method(_, _, _, _),
list.length(Args, NbArgs)
)
;
CallExpr = call_foreign_proc(_, _, _, Args, _, _, _),
list.length(Args, NbArgs)
).
% Add a call to an existing parallel conjunction.
%
:- pred add_call_to_parallel_conjunction(hlds_goal::in, hlds_goal::in,
hlds_goal::out) is det.
add_call_to_parallel_conjunction(Call, ParallelGoal0, ParallelGoal) :-
ParallelGoalExpr0 = ParallelGoal0 ^ hlds_goal_expr,
ParallelGoalInfo0 = ParallelGoal0 ^ hlds_goal_info,
( ParallelGoalExpr0 = conj(parallel_conj, GoalList0) ->
GoalList = [ Call | GoalList0 ],
goal_list_nonlocals(GoalList, NonLocals),
goal_list_instmap_delta(GoalList, InstMapDelta),
goal_list_determinism(GoalList, Detism),
goal_list_purity(GoalList, Purity),
goal_info_set_nonlocals(NonLocals, ParallelGoalInfo0,
ParallelGoalInfo1),
goal_info_set_instmap_delta(InstMapDelta, ParallelGoalInfo1,
ParallelGoalInfo2),
goal_info_set_determinism(Detism, ParallelGoalInfo2, ParallelGoalInfo3),
goal_info_set_purity(Purity, ParallelGoalInfo3, ParallelGoalInfo),
ParallelGoalExpr = conj(parallel_conj, GoalList),
ParallelGoal = hlds_goal(ParallelGoalExpr, ParallelGoalInfo)
;
unexpected(this_file, "add_call_to_parallel_conjunction")
).
% Succeed if the first goal depends on the second one.
% Fail otherwise.
%
:- pred goal_depends_on_goal(hlds_goal::in, hlds_goal::in) is semidet.
goal_depends_on_goal(hlds_goal(_, GoalInfo1), hlds_goal(_, GoalInfo2)) :-
goal_info_get_instmap_delta(GoalInfo1, InstmapDelta1),
instmap_delta_changed_vars(InstmapDelta1, ChangedVars1),
goal_info_get_nonlocals(GoalInfo2, NonLocals2),
set.intersect(ChangedVars1, NonLocals2, Intersection),
\+ set.empty(Intersection).
% Process a conjunction for implicit parallelism.
%
:- pred process_conj_for_implicit_parallelism(
hlds_goal_expr::in, hlds_goal_expr::out, int::in,
proc_info::in, module_info::in, module_info::out,
list(candidate_call_site)::in, list(candidate_call_site)::out,
counter::in, counter::out) is det.
process_conj_for_implicit_parallelism(!GoalExpr, IndexInConj, ProcInfo,
!ModuleInfo, !CalleeListToBeParallelized, !SiteNumCounter) :-
( !.GoalExpr = conj(_, GoalsConj) ->
list.length(GoalsConj, Length),
( IndexInConj > Length ->
true
;
MaybeConj0 = yes(!.GoalExpr),
list.index1_det(GoalsConj, IndexInConj, GoalInConj),
% We are not interested in the return value of GoalInConj, only
% MaybeConj matters.
process_goal_for_implicit_parallelism(GoalInConj, _, ProcInfo,
!ModuleInfo, MaybeConj0, MaybeConj, IndexInConj, IndexInConj0,
!CalleeListToBeParallelized, !SiteNumCounter),
( MaybeConj = yes(GoalExprProcessed) ->
!:GoalExpr = GoalExprProcessed
;
unexpected(this_file, "process_conj_for_implicit_parallelism")
),
process_conj_for_implicit_parallelism(!GoalExpr, IndexInConj0,
ProcInfo, !ModuleInfo, !CalleeListToBeParallelized,
!SiteNumCounter)
)
;
unexpected(this_file, "process_conj_for_implicit_parallelism")
).
% Process a disjunction for implicit parallelism.
%
:- pred process_disj_for_implicit_parallelism(
list(hlds_goal)::in, list(hlds_goal)::in, list(hlds_goal)::out,
proc_info::in, module_info::in, module_info::out,
list(candidate_call_site)::in, list(candidate_call_site)::out,
counter::in, counter::out) is det.
process_disj_for_implicit_parallelism([], !GoalsAcc, _ProcInfo,
!ModuleInfo, !CalleeListToBeParallelized, !SiteNumCounter).
process_disj_for_implicit_parallelism([ Goal0 | Goals ], !GoalsAcc,
ProcInfo, !ModuleInfo, !CalleeListToBeParallelized, !SiteNumCounter) :-
process_goal_for_implicit_parallelism(Goal0, Goal, ProcInfo,
!ModuleInfo, no, _, 0, _, !CalleeListToBeParallelized, !SiteNumCounter),
list.append(!.GoalsAcc, [ Goal ], !:GoalsAcc),
process_disj_for_implicit_parallelism(Goals, !GoalsAcc, ProcInfo,
!ModuleInfo, !CalleeListToBeParallelized, !SiteNumCounter).
% If we are in a conjunction, update it by replacing the goal at index by
% Goal and increment the index.
%
:- pred update_conj_and_index(
maybe(hlds_goal_expr)::in, maybe(hlds_goal_expr)::out,
hlds_goal::in, int::in, int::out) is det.
update_conj_and_index(!MaybeConj, Goal, !IndexInConj) :-
( !.MaybeConj = yes(conj(Type, Goals0)) ->
list.replace_nth_det(Goals0, !.IndexInConj, Goal, Goals),
!:IndexInConj = !.IndexInConj + 1,
!:MaybeConj = yes(conj(Type, Goals))
;
true
).
% Process a switch for implicit parallelism.
%
:- pred process_switch_cases_for_implicit_parallelism(
list(case)::in, list(case)::in, list(case)::out, proc_info::in,
module_info::in, module_info::out,
list(candidate_call_site)::in, list(candidate_call_site)::out,
counter::in, counter::out) is det.
process_switch_cases_for_implicit_parallelism([], !CasesAcc, _ProcInfo,
!ModuleInfo, !CalleeListToBeParallelized, !SiteNumCounter).
process_switch_cases_for_implicit_parallelism([ Case0 | Cases ], !CasesAcc,
ProcInfo, !ModuleInfo, !CalleeListToBeParallelized, !SiteNumCounter) :-
Case0 = case(Functor, Goal0),
process_goal_for_implicit_parallelism(Goal0, Goal, ProcInfo,
!ModuleInfo, no, _, 0, _, !CalleeListToBeParallelized, !SiteNumCounter),
list.append(!.CasesAcc, [ case(Functor, Goal) ], !:CasesAcc),
process_switch_cases_for_implicit_parallelism(Cases, !CasesAcc,
ProcInfo, !ModuleInfo, !CalleeListToBeParallelized, !SiteNumCounter).
%-----------------------------------------------------------------------------%
% Parse the feedback file (header and body).
%
:- pred parse_feedback_file(string::in,
maybe_error(list(candidate_call_site))::out, io::di, io::uo) is det.
parse_feedback_file(InputFile, MaybeListCandidateCallSite, !IO) :-
io.open_input(InputFile, Result, !IO),
(
Result = io.error(ErrInput),
MaybeListCandidateCallSite = error(io.error_message(ErrInput))
;
Result = ok(Stream),
io.read_file_as_string(Stream, MaybeFileAsString, !IO),
(
MaybeFileAsString = ok(FileAsString),
LineList = string.words_separator(is_carriage_return,
FileAsString),
process_header(LineList, MaybeBodyFileAsListString, !IO),
(
MaybeBodyFileAsListString = error(ErrProcessHeader),
MaybeListCandidateCallSite = error(ErrProcessHeader)
;
MaybeBodyFileAsListString = ok(BodyFileAsListString),
process_body(BodyFileAsListString, MaybeListCandidateCallSite)
)
;
MaybeFileAsString = error(_, ErrReadFileAsString),
MaybeListCandidateCallSite =
error(io.error_message(ErrReadFileAsString))
),
io.close_input(Stream, !IO)
).
:- pred is_carriage_return(char::in) is semidet.
is_carriage_return(Char) :-
Char = '\n'.
% Process the header of the feedback file.
%
:- pred process_header(list(string)::in, maybe_error(list(string))::out,
io::di, io::uo) is det.
process_header(FileAsListString, MaybeFileAsListStringWithoutHeader, !IO) :-
( list.index0(FileAsListString, 0, Type) ->
( Type = "Profiling feedback file" ->
(list.index0(FileAsListString, 1, Version) ->
( Version = "Version = 1.0" ->
list.det_split_list(4, FileAsListString, _,
FileAsListStringWithoutHeader),
MaybeFileAsListStringWithoutHeader =
ok(FileAsListStringWithoutHeader)
;
MaybeFileAsListStringWithoutHeader = error("Profiling" ++
" feedback file version incorrect")
)
;
MaybeFileAsListStringWithoutHeader = error("Not a profiling"
++ " feedback file")
)
;
MaybeFileAsListStringWithoutHeader = error("Not a profiling" ++
" feedback file")
)
;
MaybeFileAsListStringWithoutHeader = error("Not a profiling feedback"
++ " file")
).
% Process the body of the feedback file.
%
:- pred process_body(list(string)::in,
maybe_error(list(candidate_call_site))::out) is det.
process_body(CoreFileAsListString, MaybeListCandidateCallSite) :-
( process_body2(CoreFileAsListString, [], ListCandidateCallSite) ->
MaybeListCandidateCallSite = ok(ListCandidateCallSite)
;
MaybeListCandidateCallSite = error("Profiling feedback file is not"
++ " well-formed")
).
:- pred process_body2(list(string)::in, list(candidate_call_site)::in,
list(candidate_call_site)::out) is semidet.
process_body2([], !ListCandidateCallSiteAcc).
process_body2([ Line | Lines ], !ListCandidateCallSiteAcc) :-
Words = string.words_separator(is_whitespace, Line),
list.index0_det(Words, 0, Caller),
( Caller = "Mercury" ->
process_body2(Lines, !ListCandidateCallSiteAcc)
;
list.index0_det(Words, 1, SlotNumber),
string.to_int(SlotNumber, IntSlotNumber),
list.index0_det(Words, 2, KindAsString),
( construct_call_site_kind(KindAsString, Kind) ->
( Kind = csk_normal ->
list.index0_det(Words, 3, Callee),
CandidateCallSite = candidate_call_site(Caller, IntSlotNumber,
Kind, Callee)
;
CandidateCallSite = candidate_call_site(Caller, IntSlotNumber,
Kind, "")
)
;
% Unexpected call site kind.
unexpected(this_file, "process_body2")
),
!:ListCandidateCallSiteAcc = [ CandidateCallSite |
!.ListCandidateCallSiteAcc ],
process_body2(Lines, !ListCandidateCallSiteAcc)
).
%-----------------------------------------------------------------------------%
:- func this_file = string.
this_file = "implicit_parallelism.m".
%-----------------------------------------------------------------------------%
:- end_module transform_hlds.implicit_parallelism.
%-----------------------------------------------------------------------------%