1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
|
%-----------------------------------------------------------------------------%
% Copyright (C) 1998-1999 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: par_conj.m:
%
% Main authors: conway.
%
% The predicates of this module generate code for parallel conjunctions.
%
%---------------------------------------------------------------------------%
%
% Notes on parallel conjunction:
%
% A parallel conjunction (A & B) denotes that the goals `A' and `B' should
% be executed concurrently. Parallel conjunction has exactly the same
% declarative semantics as normal conjunction, but it has different (stricter)
% rules for mode-correctness and determinism-correctness, and it has different
% operational semantics.
% [Operational semantics]
% - `,'/2 gives some operational guarantees that `&'/2 does not:
% if `--no-reorder-conj' is set, there is an implied ordering
% in the code: conjunctions must not be reordered beyond the
% minimum necessary for mode correctness.
% This is justified for reasons performance modeling and ensuring
% predicatable termination properties.
% Parallel conjunction does not of itself suggest any information
% about which order two goals should be executed, however if
% coroutining (not currently implemented) is being used, then the
% data dependancies between the two goals will constrain the order
% of execution at runtime.
% [Mode correctness]
% - `,'/2 has a *sequential* behaviour `A, B' proves `A' *then*
% proves `B'. Mode analysis only allows unidirectional data-
% dependancies for conjunction. In independant and-parallelism,
% for the goal `A & B', mode analysis requires that `A' and `B'
% bind disjoint sets of free variables (or when mode analysis
% supports it properly, disjoint sets of type-nodes), and that
% `A' does not require any bindings made in `B' and vice versa.
% In dependant and-parallelism, mode analysis requires that each
% variable (or type-node) have a unique producer (as in independant
% and-parallelism), but an and-parallel goal may use bindings made
% in conjoined goals which may lead to coroutining.
%
% The current implementation only supports independant and-parallelism.
% The syntax for parallel conjunction is `&'/2 which behaves like `,'/2
% in that sequences get flattened (ie A & (B & C) <=> (A & B) & C).
%
% Type checking works exactly the same for parallel conjunction as it does
% for sequential conjunction.
%
% Mode analysis schedules a parallel conjunction if all the conjuncts can
% be scheduled independantly, and they bind disjoint sets of variables
% (type-nodes). This is done by mode checking each conjunct with the same
% initial instmap and `locking' (as is done for the nonlocal variables of a
% negation[1]) any variables that get bound in that conjunct before
% recursively processing the rest of the parallel conjunction. At the end of
% the conjunction the final instmaps from the conjuncts are merged by unifying
% them. Since the variable `locking' ensures that the variables bound by each
% conjunct are distinct from those bound by the other conjuncts, the
% unification of the instmaps is guarenteed to succeed.
%
% In principal, the determinism of a parallel conjunction is derived from
% its conjuncts in the same way as the determinism of a conjunction but
% because the current runtime implementation only allows model_det parallel
% conjunction, determinism analysis works by inferring the determinism of
% each conjunct and reporting an error if it is not a model_det determinism.
%
% We conservatively require that any variable that is nonlocal to more
% than one parallel conjunct become shared at the start of the parallel
% conjunction. This avoids problems where one conjunct has a use in a
% di mode and another in a ui mode. This would introduce an implicit
% dependency between the two conjuncts, which at present is illegal,
% since parallel conjunction is currently *independent* parallel
% conjunction only.
%
% The code generated for a parallel conjunction consists of a piece of
% initialization code which creates a term on the heap to be used for
% controlling the synchronization of the conjuncts and the code for the
% conjuncts each proceeded by a command to start the conjunct as a new
% thead of execution (except the last which executes in the "parent"
% thread), and each succeeded by a command that signals that the execution
% of the conjunct has completed and terminates the thread (except for
% the "parent" thread which suspends till all the other parallel conjuncts
% have terminated, when it will be woken up). The synchronization terms
% are refered to in the code as 'sync_term's.
%
% The runtime support for parallel conjunction is documented in the runtime
% directory in mercury_context.{c,h}.
%
%---------------------------------------------------------------------------%
:- module par_conj_gen.
:- interface.
:- import_module hlds_goal, llds, code_info.
:- import_module list.
:- pred par_conj_gen__generate_par_conj(list(hlds_goal), hlds_goal_info,
code_model, code_tree, code_info, code_info).
:- mode par_conj_gen__generate_par_conj(in, in, in, out, in, out) is det.
%---------------------------------------------------------------------------%
:- implementation.
:- import_module hlds_data, code_gen, code_util, options, globals, prog_data.
:- import_module hlds_module, (inst), instmap, mode_util, code_info.
:- import_module continuation_info.
:- import_module set, tree, list, map, std_util, require, int.
%---------------------------------------------------------------------------%
par_conj_gen__generate_par_conj(Goals, GoalInfo, CodeModel, Code) -->
{
CodeModel = model_det
;
CodeModel = model_semi,
error("sorry, semidet parallel conjunction not implemented")
;
CodeModel = model_non,
error("sorry, nondet parallel conjunction not implemented")
},
code_info__get_globals(Globals),
{ globals__lookup_int_option(Globals, sync_term_size, STSize) },
code_info__get_known_variables(Vars),
code_info__save_variables_on_stack(Vars, SaveCode),
{ goal_info_get_nonlocals(GoalInfo, Nonlocals) },
{ set__to_sorted_list(Nonlocals, Variables) },
code_info__get_instmap(Initial),
{ goal_info_get_instmap_delta(GoalInfo, Delta) },
{ instmap__apply_instmap_delta(Initial, Delta, Final) },
code_info__get_module_info(ModuleInfo),
{ par_conj_gen__find_outputs(Variables, Initial, Final, ModuleInfo,
[], Outputs) },
{ list__length(Goals, NumGoals) },
code_info__acquire_reg(r, RegLval),
code_info__acquire_temp_slot(sync_term, SyncSlot),
code_info__acquire_temp_slot(lval(sp), SpSlot),
{ MakeTerm = node([
assign(SpSlot, lval(sp))
- "save the parent stack pointer",
incr_hp(RegLval, no, const(int_const(STSize)),
"synchronization vector")
- "allocate a synchronization vector",
init_sync_term(RegLval, NumGoals)
- "initialize sync term",
assign(SyncSlot, lval(RegLval))
- "store the sync-term on the stack"
]) },
code_info__release_reg(RegLval),
code_info__clear_all_registers,
par_conj_gen__generate_det_par_conj_2(Goals, 0, SyncSlot, SpSlot,
Initial, no, GoalCode),
code_info__release_temp_slot(SyncSlot),
{ Code = tree(tree(SaveCode, MakeTerm), GoalCode) },
code_info__clear_all_registers,
par_conj_gen__place_all_outputs(Outputs).
:- pred par_conj_gen__generate_det_par_conj_2(list(hlds_goal), int, lval, lval,
instmap, branch_end, code_tree, code_info, code_info).
:- mode par_conj_gen__generate_det_par_conj_2(in, in, in, in,
in, in, out, in, out) is det.
par_conj_gen__generate_det_par_conj_2([], _N, _SyncTerm, _SpSlot, _Initial,
_, empty) --> [].
par_conj_gen__generate_det_par_conj_2([Goal|Goals], N, SyncTerm, SpSlot,
Initial, MaybeEnd0, Code) -->
code_info__remember_position(StartPos),
code_info__get_next_label(ThisConjunct),
code_info__get_next_label(NextConjunct),
code_gen__generate_goal(model_det, Goal, ThisGoalCode),
code_info__get_stack_slots(AllSlots),
code_info__get_known_variables(Variables),
{ set__list_to_set(Variables, LiveVars) },
{ map__select(AllSlots, LiveVars, StoreMap) },
code_info__generate_branch_end(StoreMap, MaybeEnd0, MaybeEnd,
SaveCode),
{ Goal = _GoalExpr - GoalInfo },
{ goal_info_get_instmap_delta(GoalInfo, Delta) },
{ instmap__apply_instmap_delta(Initial, Delta, Final) },
code_info__get_module_info(ModuleInfo),
{ par_conj_gen__find_outputs(Variables, Initial, Final, ModuleInfo,
[], TheseOutputs) },
par_conj_gen__copy_outputs(TheseOutputs, SpSlot, CopyCode),
(
{ Goals = [_|_] }
->
code_info__reset_to_position(StartPos),
code_info__get_total_stackslot_count(NumSlots),
{ ForkCode = node([
fork(ThisConjunct, NextConjunct, NumSlots)
- "fork off a child",
label(ThisConjunct)
- "child thread"
]) },
{ JoinCode = node([
join_and_terminate(SyncTerm)
- "finish",
label(NextConjunct)
- "start of the next conjunct"
]) }
;
code_info__get_next_label(ContLab),
{ ForkCode = empty },
{ JoinCode = node([
join_and_continue(SyncTerm, ContLab)
- "sync with children then continue",
label(ContLab)
- "end of parallel conjunction"
]) }
),
{ ThisCode = tree(
ForkCode,
tree(ThisGoalCode, tree(tree(SaveCode, CopyCode), JoinCode))
) },
{ N1 is N + 1 },
par_conj_gen__generate_det_par_conj_2(Goals, N1, SyncTerm, SpSlot,
Initial, MaybeEnd, RestCode),
{ Code = tree(ThisCode, RestCode) }.
:- pred par_conj_gen__find_outputs(list(prog_var), instmap, instmap,
module_info, list(prog_var), list(prog_var)).
:- mode par_conj_gen__find_outputs(in, in, in, in, in, out) is det.
par_conj_gen__find_outputs([], _Initial, _Final, _ModuleInfo,
Outputs, Outputs).
par_conj_gen__find_outputs([Var|Vars], Initial, Final, ModuleInfo,
Outputs0, Outputs) :-
instmap__lookup_var(Initial, Var, InitialInst),
instmap__lookup_var(Final, Var, FinalInst),
(
mode_is_output(ModuleInfo, (InitialInst -> FinalInst))
->
Outputs1 = [Var|Outputs0]
;
Outputs1 = Outputs0
),
par_conj_gen__find_outputs(Vars, Initial, Final, ModuleInfo,
Outputs1, Outputs).
:- pred par_conj_gen__copy_outputs(list(prog_var), lval, code_tree,
code_info, code_info).
:- mode par_conj_gen__copy_outputs(in, in, out, in, out) is det.
par_conj_gen__copy_outputs([], _, empty) --> [].
par_conj_gen__copy_outputs([Var|Vars], SpSlot, Code) -->
code_info__get_variable_slot(Var, SrcSlot),
(
{ SrcSlot = stackvar(SlotNum) }
->
{ NegSlotNum is (- SlotNum) },
{ DestSlot = field(yes(0), lval(SpSlot),
const(int_const(NegSlotNum))) }
;
{ error("par conj in model non procedure!") }
),
{ ThisCode = node([
assign(DestSlot, lval(SrcSlot))
- "copy result to parent stackframe"
]) },
{ Code = tree(ThisCode, RestCode) },
par_conj_gen__copy_outputs(Vars, SpSlot, RestCode).
:- pred par_conj_gen__place_all_outputs(list(prog_var), code_info, code_info).
:- mode par_conj_gen__place_all_outputs(in, in, out) is det.
par_conj_gen__place_all_outputs([]) --> [].
par_conj_gen__place_all_outputs([Var|Vars]) -->
code_info__variable_locations(VarLocations),
code_info__get_variable_slot(Var, Slot),
(
{ map__search(VarLocations, Var, Locations) },
{ set__member(lval(Slot), Locations) }
->
[]
;
code_info__set_var_location(Var, Slot)
),
par_conj_gen__place_all_outputs(Vars).
|