mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
After this, I think all modules in the check_hlds package belong there.
compiler/inst_match.m:
compiler/mode_test.m:
Move these modules from the check_hlds package to the hlds package
because most of their uses are outside the semantic analysis passes
that the check_hlds package is intended to contain.
compiler/inst_merge.m:
Move this module from the check_hlds package to the hlds package
because it is imported by only two modules, instmap.m and inst_match.m,
and after this diff, both are in the hlds package.
compiler/implementation_defined_literals.m:
Move this module from the check_hlds package to the hlds package
because it does a straightforward program transformation that
does not have anything to do with semantic analysis (though its
invocation does happen between semantic analysis passes).
compiler/notes/compiler_design.html:
Update the documentation of the goal_path.m module. (I checked the
documentation of the moved modules, which did not need updates,
and found the need for this instead.)
compiler/*.m:
Conform to the changes above. (For many modules, this deletes
their import of the check_hlds package itself.)
688 lines
27 KiB
Mathematica
688 lines
27 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1998-2000, 2002-2011 The University of Melbourne.
|
|
% Copyright (C) 2013-2016, 2018-2026 The Mercury team.
|
|
% 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_gen.m.
|
|
% Main authors: conway, wangp.
|
|
%
|
|
% 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, sequential conjunction provides
|
|
% an implied ordering to the code: the conjuncts must not be reordered
|
|
% beyond the minimum that is necessary for mode correctness.
|
|
% The reason for this is to allow programmers to model the performance
|
|
% of the code more simply, which also includes (in the extreme)
|
|
% simpler reasoning about termination properties.
|
|
%
|
|
% Parallel conjunctions do not specify the order in which their conjuncts
|
|
% will be executed. However, any data dependencies between conjuncts
|
|
% will constrain the order of those conjuncts' execution at runtime.
|
|
% If the conjunction is executed with coroutining, a data dependency
|
|
% constrains the order of the start times of the conjuncts involved;
|
|
% if the conjunction is executed in parallel, the constraint does not
|
|
% extend that far, and imposes only a requirement for the standard
|
|
% reader-writer synchronization.
|
|
%
|
|
% Mode correctness:
|
|
% `,'/2 has a *sequential* behaviour: `A, B' proves *first* `A' and
|
|
% *then* proves `B'. Mode analysis only allows unidirectional data-
|
|
% dependencies for conjunction. Applying independent and-parallelism
|
|
% to `A & B', mode analysis would require 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.
|
|
% With dependant and-parallelism, mode analysis requires that each
|
|
% variable (or type-node) have a unique producer (as in independent
|
|
% and-parallelism), but an and-parallel goal may use bindings made
|
|
% in conjoined goals to its left which may lead to coroutining.
|
|
% (Allowing it to use conjoined goals to its right would, in general,
|
|
% allow circular data dependencies, which would lead to deadlock.)
|
|
%
|
|
% The current system implements mainly independent and-parallelism and
|
|
% a subset of dependent and-parallelism (see dep_par_conj.m).
|
|
%
|
|
% Type checking and mode analysis work exactly the same for parallel
|
|
% conjunction as for sequential conjunction.
|
|
%
|
|
% In principle, 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 model_det.
|
|
%
|
|
% The code we generate for a parallel conjunction consists of
|
|
%
|
|
% - a piece of initialization code, which creates a term on the heap
|
|
% that we use for controlling the synchronization of the conjuncts
|
|
% (we call them `sync_term's), and
|
|
% - the code for the conjuncts themselves.
|
|
%
|
|
% We execute conjuncts left to right. Before the code of the i'th conjunct,
|
|
% we put code to "spark" the i+1'th conjunct (if there is one). Sparking
|
|
% a conjunct means either assigning its execution to a spare thread
|
|
% (if there is one), or recording the information needed to begin executing it
|
|
% when a thread becomes available. A thread *should* become available
|
|
% when the ith conjunct ends.
|
|
%
|
|
% At the end of each conjunct we put a join_and_continue instruction.
|
|
% This executes the next parallel conjunct if there is one, or if there isn't,
|
|
% waits until all the conjuncts are finished, and then branches to the code
|
|
% *following* the parallel conjunction. We take care to ensure that the
|
|
% code following the parallel conjunction executes in the context
|
|
% that *originated* the parallel conjunction. If the originating context
|
|
% can't execute the next conjunct and the parallel conjunction isn't finished,
|
|
% it must suspend. When a non-originating context later finds that
|
|
% the parallel conjunction _is_ finished, it will then cause the originating
|
|
% context to resume execution at the join point. Please see the
|
|
% implementation of MR_join_and_continue() for the details.
|
|
%
|
|
% The runtime support for parallel conjunction is documented in the runtime
|
|
% directory in mercury_context.{c,h}.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.par_conj_gen.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module ll_backend.code_info.
|
|
:- import_module ll_backend.code_loc_dep.
|
|
:- import_module ll_backend.llds.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred generate_par_conj(list(hlds_goal)::in, hlds_goal_info::in,
|
|
code_model::in, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
:- pred generate_lc_spawn_off(hlds_goal::in, prog_var::in, prog_var::in,
|
|
lc_use_parent_stack::in, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_out.hlds_out_util.
|
|
:- import_module hlds.instmap.
|
|
:- import_module hlds.mode_test.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module ll_backend.code_gen.
|
|
:- import_module ll_backend.code_util.
|
|
:- import_module ll_backend.continuation_info.
|
|
:- import_module ll_backend.exprn_aux.
|
|
:- import_module ll_backend.llds_out.
|
|
:- import_module ll_backend.llds_out.llds_out_data.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module parse_tree.prog_data_foreign.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_par_conj(Conjuncts, GoalInfo, CodeModel, Code, !CI, !CLD) :-
|
|
% Some sanity checks.
|
|
(
|
|
CodeModel = model_det
|
|
;
|
|
CodeModel = model_semi,
|
|
sorry($pred, "semidet parallel conjunction not implemented")
|
|
;
|
|
CodeModel = model_non,
|
|
sorry($pred, "nondet parallel conjunction not implemented")
|
|
),
|
|
|
|
get_globals(!.CI, Globals),
|
|
globals.lookup_int_option(Globals, sync_term_size_in_words,
|
|
SyncTermSizeInWords),
|
|
|
|
% When entering a parallel conjunctions at the shallowest level in
|
|
% the procedure, we have to set the parent_sp register to the value
|
|
% of the sp register, and restore it when the parallel conjunction
|
|
% finishes.
|
|
get_par_conj_depth(!.CLD, Depth),
|
|
( if Depth = 0 then
|
|
acquire_temp_slot(slot_lval(parent_sp),
|
|
non_persistent_temp_slot, ParentSpSlot, !CI, !CLD),
|
|
MaybeSetParentSpCode = from_list([
|
|
llds_instr(assign(ParentSpSlot, lval(parent_sp)),
|
|
"save the old parent stack pointer"),
|
|
llds_instr(assign(parent_sp, lval(sp)),
|
|
"set the parent stack pointer")
|
|
]),
|
|
MaybeRestoreParentSpCode = singleton(
|
|
llds_instr(assign(parent_sp, lval(ParentSpSlot)),
|
|
"restore old parent stack pointer")
|
|
),
|
|
MaybeReleaseParentSpSlot = yes(ParentSpSlot)
|
|
else
|
|
MaybeSetParentSpCode = empty,
|
|
MaybeRestoreParentSpCode = empty,
|
|
MaybeReleaseParentSpSlot = no
|
|
),
|
|
|
|
get_known_variables(!.CLD, Vars),
|
|
save_variables_on_stack(Vars, SaveCode, !.CI, !CLD),
|
|
|
|
Nonlocals = goal_info_get_code_gen_nonlocals(GoalInfo),
|
|
set_of_var.to_sorted_list(Nonlocals, Variables),
|
|
get_instmap(!.CLD, InitialInstMap),
|
|
InstMapDelta = goal_info_get_instmap_delta(GoalInfo),
|
|
apply_instmap_delta(InstMapDelta, InitialInstMap, FinalInstMap),
|
|
get_module_info(!.CI, ModuleInfo),
|
|
find_outputs(Variables, InitialInstMap, FinalInstMap, ModuleInfo,
|
|
[], Outputs),
|
|
|
|
% Reserve a contiguous block on the stack to hold the synchronisation term.
|
|
Contents = list.duplicate(SyncTermSizeInWords, slot_sync_term),
|
|
acquire_several_temp_slots(Contents, persistent_temp_slot, SyncTermSlots,
|
|
StackId, _N, _M, !CI, !CLD),
|
|
( if
|
|
% The highest numbered slot has the lowest address.
|
|
list.last(SyncTermSlots, SyncTermBaseSlotPrime),
|
|
SyncTermBaseSlotPrime = stackvar(SlotNumPrime),
|
|
StackId = det_stack
|
|
then
|
|
SlotNum = SlotNumPrime,
|
|
SyncTermBaseSlot = SyncTermBaseSlotPrime,
|
|
ParentSyncTermBaseSlot = parent_stackvar(SlotNum)
|
|
else
|
|
unexpected($pred, "cannot find stack slot")
|
|
),
|
|
|
|
NumConjuncts = list.length(Conjuncts),
|
|
create_static_conj_id(GoalInfo, StaticConjId, !CI),
|
|
MakeSyncTermCode = singleton(
|
|
llds_instr(
|
|
init_sync_term(SyncTermBaseSlot, NumConjuncts, StaticConjId),
|
|
"initialize sync term")
|
|
),
|
|
|
|
get_next_label(EndLabel, !CI),
|
|
remember_position(!.CLD, BeforeConjunctionPos),
|
|
|
|
some [!InConjunctionCLD] (
|
|
!:InConjunctionCLD = !.CLD,
|
|
set_par_conj_depth(Depth+1, !InConjunctionCLD),
|
|
clear_all_registers(no, !InConjunctionCLD),
|
|
remember_position(!.InConjunctionCLD, ConjunctStartPos)
|
|
),
|
|
generate_det_par_conjuncts(ConjunctStartPos, InitialInstMap,
|
|
EndLabel, ParentSyncTermBaseSlot, Conjuncts, no, GoalCode, !CI),
|
|
|
|
EndLabelCode = from_list([
|
|
llds_instr(label(EndLabel), "end of parallel conjunction"),
|
|
llds_instr(ts_finish_par_conj_instr(SlotNum, SyncTermBaseSlot),
|
|
"finish parallel conjunction (ThreadScope instrumentation")
|
|
]),
|
|
Code =
|
|
MaybeSetParentSpCode ++
|
|
SaveCode ++
|
|
MakeSyncTermCode ++
|
|
GoalCode ++
|
|
EndLabelCode ++
|
|
MaybeRestoreParentSpCode,
|
|
|
|
% We can't release the sync slot right now, in case we are in a
|
|
% nested parallel conjunction. Consider:
|
|
%
|
|
% (
|
|
% (A & B) % inner1
|
|
% &
|
|
% (C & D) % inner2
|
|
% )
|
|
%
|
|
% If inner1 released its sync slot now then it might end up being reused
|
|
% by inner2. But inner1 and inner2 could be executing simultaneously.
|
|
% In general we can't release the sync slot of any parallel conjunction
|
|
% until we leave the shallowest parallel conjunction, i.e. at depth 0.
|
|
% For now we only release the sync slots of parallel conjunctions at the
|
|
% top level.
|
|
%
|
|
% XXX Release the sync slots of nested parallel conjunctions.
|
|
|
|
reset_to_position(BeforeConjunctionPos, !.CI, !:CLD),
|
|
( if Depth = 0 then
|
|
release_several_temp_slots(SyncTermSlots, persistent_temp_slot,
|
|
!CI, !CLD)
|
|
else
|
|
true
|
|
),
|
|
(
|
|
MaybeReleaseParentSpSlot = yes(ParentSpSlot1),
|
|
release_temp_slot(ParentSpSlot1, non_persistent_temp_slot, !CI, !CLD)
|
|
;
|
|
MaybeReleaseParentSpSlot = no
|
|
),
|
|
clear_all_registers(no, !CLD),
|
|
place_all_outputs(Outputs, !.CI, !CLD).
|
|
|
|
:- pred generate_det_par_conjuncts(position_info::in, instmap::in, label::in,
|
|
lval::in, list(hlds_goal)::in, branch_end::in, llds_code::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
generate_det_par_conjuncts(_ConjunctStartPos, _InitialInstMap, _EndLabel,
|
|
_ParentSyncTermBaseSlot, [], _, empty, !CI).
|
|
generate_det_par_conjuncts(ConjunctStartPos, InitialInstMap, EndLabel,
|
|
ParentSyncTermBaseSlot, [Conjunct | Conjuncts], MaybeEnd0, Code,
|
|
!CI) :-
|
|
some [!CLD] (
|
|
reset_to_position(ConjunctStartPos, !.CI, !:CLD),
|
|
code_gen.generate_goal(model_det, Conjunct, ThisConjunctCode0,
|
|
!CI, !CLD),
|
|
replace_stack_vars_by_parent_sv(ThisConjunctCode0, ThisConjunctCode),
|
|
|
|
get_stack_slots(!.CI, AllSlots),
|
|
get_known_variables(!.CLD, Variables),
|
|
set.list_to_set(Variables, LiveVars),
|
|
map.select(AllSlots, LiveVars, StoreMap0),
|
|
StoreMap = map.map_values_only(stack_slot_to_abs_locn, StoreMap0),
|
|
generate_branch_end(StoreMap, MaybeEnd0, MaybeEnd, SaveCode0, !.CLD),
|
|
replace_stack_vars_by_parent_sv(SaveCode0, SaveCode)
|
|
),
|
|
|
|
(
|
|
Conjuncts = [_ | _],
|
|
get_next_label(NextConjunct, !CI),
|
|
ForkCode = singleton(
|
|
llds_instr(fork_new_child(ParentSyncTermBaseSlot, NextConjunct),
|
|
"fork off a child")
|
|
),
|
|
JoinCode = from_list([
|
|
llds_instr(join_and_continue(ParentSyncTermBaseSlot, EndLabel),
|
|
"finish"),
|
|
llds_instr(label(NextConjunct),
|
|
"start of the next conjunct")
|
|
])
|
|
;
|
|
Conjuncts = [],
|
|
ForkCode = empty,
|
|
JoinCode = singleton(
|
|
llds_instr(join_and_continue(ParentSyncTermBaseSlot, EndLabel),
|
|
"finish")
|
|
)
|
|
),
|
|
ConjunctCode = ForkCode ++ ThisConjunctCode ++ SaveCode ++ JoinCode,
|
|
generate_det_par_conjuncts(ConjunctStartPos, InitialInstMap, EndLabel,
|
|
ParentSyncTermBaseSlot, Conjuncts, MaybeEnd, ConjunctsCode, !CI),
|
|
Code = ConjunctCode ++ ConjunctsCode.
|
|
|
|
:- func ts_finish_par_conj_instr(int, lval) = instr.
|
|
|
|
ts_finish_par_conj_instr(SyncTermBaseSlot, SyncTermBaseSlotLval) = Instr :-
|
|
CodeTemplate =
|
|
"#ifdef MR_THREADSCOPE
|
|
MR_threadscope_post_end_par_conj(&MR_sv(%d));
|
|
#endif
|
|
",
|
|
Components = [foreign_proc_raw_code(cannot_branch_away,
|
|
proc_does_not_affect_liveness,
|
|
live_lvals_info(set.make_singleton_set(SyncTermBaseSlotLval)),
|
|
string.format(CodeTemplate, [i(SyncTermBaseSlot)]))],
|
|
Instr = foreign_proc_code([], Components, proc_will_not_call_mercury,
|
|
no, no, no, no, no, refers_to_llds_stack, proc_may_duplicate).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
generate_lc_spawn_off(Goal, LCVar, LCSVar, UseParentStack, Code, !CI, !CLD) :-
|
|
% We don't need to save the parent stack pointer, we do not use it in the
|
|
% main context and all the worker contexts will never have some data that
|
|
% we shouldn't clobber there.
|
|
% We also expect the runtime code to setup the parent stack pointer for us.
|
|
|
|
get_known_variables(!.CLD, KnownVars),
|
|
KnownVarsSet = set_of_var.list_to_set(KnownVars),
|
|
NonLocalsSet = goal_info_get_nonlocals(Goal ^ hg_info),
|
|
InputVarsSet = set_of_var.intersect(NonLocalsSet, KnownVarsSet),
|
|
InputVars = set_of_var.to_sorted_list(InputVarsSet),
|
|
save_variables_on_stack(InputVars, SaveCode, !.CI, !CLD),
|
|
|
|
best_variable_location_det(!.CLD, LCVar, LCVarLocn),
|
|
best_variable_location_det(!.CLD, LCSVar, LCSVarLocn),
|
|
|
|
get_next_label(SpawnOffLabel, !CI),
|
|
SpawnUinstr = lc_spawn_off(lval(LCVarLocn), lval(LCSVarLocn),
|
|
SpawnOffLabel),
|
|
SpawnInstr = llds_instr(SpawnUinstr, ""),
|
|
SpawnOffCode = singleton(SpawnInstr),
|
|
remember_position(!.CLD, PositionAfterSpawnOff),
|
|
|
|
% Code to spawn off.
|
|
LabelUinstr = label(SpawnOffLabel),
|
|
LabelInstr = llds_instr(LabelUinstr, "Label for spawned off code"),
|
|
LabelCode = singleton(LabelInstr),
|
|
|
|
% We don't need to clear all the registers, all the variables except for
|
|
% LC and LCS are considered to be on the stack.
|
|
% Mark only the registers used by LC and LCS as clobbered.
|
|
clobber_regs([LCVarLocn, LCSVarLocn], !CLD),
|
|
|
|
generate_goal(model_det, Goal, GoalCode, !CI, !.CLD, _CLDAfterGoal),
|
|
% We expect that the join_and_terminate call is already in Goal.
|
|
SpawnedOffCode0 = LabelCode ++ GoalCode,
|
|
|
|
reset_to_position(PositionAfterSpawnOff, !.CI, !:CLD),
|
|
|
|
(
|
|
UseParentStack = lc_use_parent_stack_frame,
|
|
replace_stack_vars_by_parent_sv(SpawnedOffCode0, SpawnedOffCode),
|
|
CopyCode = cord.empty,
|
|
|
|
% Mark the output values as available in registers, code inserted after
|
|
% the recursive call expects to be able to read them. Because they are
|
|
% guaranteed to be placed in distinct stack slots it's okay to produce
|
|
% them a little early - really they could be produced from any point
|
|
% after spawn_off until the barrier in the base case.
|
|
|
|
% This module has a find_outputs predicate, but I can't see how set
|
|
% difference wouldn't work.
|
|
OutputVars = set_of_var.difference(NonLocalsSet, KnownVarsSet),
|
|
place_all_outputs(set_of_var.to_sorted_list(OutputVars), !.CI, !CLD)
|
|
;
|
|
UseParentStack = lc_create_frame_on_child_stack,
|
|
list.map(get_variable_slot(!.CI), InputVars, InputStackSlots),
|
|
|
|
% XXX: We could take this opportunity to remove gaps in the stack
|
|
% frame as discussed in our meetings.
|
|
instr_list_max_stack_ref(SpawnedOffCode0, MaxStackRef),
|
|
SpawnedOffCode = SpawnedOffCode0,
|
|
|
|
% We only know the size of the stack frame for certain after we have
|
|
% finished generating code for the procedure.
|
|
%
|
|
% There are several different ways we can set the size of the first
|
|
% stack frame on the stack of the child context.
|
|
%
|
|
% We have chosen to implement this by collecting all the stackvar
|
|
% references in SpawnedOffCode0, and base the size of the child stack
|
|
% frame on the highest numbered stackvar reference in there.
|
|
%
|
|
% We could also compress out any stack slots that are not used by
|
|
% GoalCode. This would require remapping all the stackvar references in
|
|
% GoalCode, as well as applying the compression map during the creation
|
|
% of CopyStr.
|
|
FrameSize = MaxStackRef,
|
|
copy_slots_to_child_stack(FrameSize, LCVarLocn, LCSVarLocn,
|
|
InputStackSlots, CopyStr),
|
|
AffectsLiveness = proc_does_not_affect_liveness,
|
|
LiveLvalsInfo = live_lvals_info(
|
|
set.list_to_set([LCVarLocn, LCSVarLocn | InputStackSlots])),
|
|
CopyUinstr = arbitrary_c_code(AffectsLiveness, LiveLvalsInfo,
|
|
CopyStr),
|
|
CopyInstr = llds_instr(CopyUinstr, ""),
|
|
CopyCode = singleton(CopyInstr)
|
|
),
|
|
|
|
% The spawned off code is written into the procedure separately.
|
|
add_out_of_line_code(SpawnedOffCode, !CI),
|
|
|
|
% Concatenate the inline code.
|
|
Code = SaveCode ++ CopyCode ++ SpawnOffCode.
|
|
|
|
:- pred copy_slots_to_child_stack(int::in, lval::in, lval::in, list(lval)::in,
|
|
string::out) is det.
|
|
|
|
copy_slots_to_child_stack(FrameSize, LCVarLocn, LCSVarLocn, StackSlots,
|
|
CodeStr) :-
|
|
( if
|
|
lval_to_string(LCVarLocn, LCVarNamePrime),
|
|
lval_to_string(LCSVarLocn, LCSVarNamePrime)
|
|
then
|
|
LCVarName = LCVarNamePrime,
|
|
LCSVarName = LCSVarNamePrime
|
|
else
|
|
unexpected($pred, "cannot convert to string")
|
|
),
|
|
|
|
FirstLine = "{\n",
|
|
IncrLine = string.format("\tMR_lc_inc_worker_sp(%s, %s, %d);\n",
|
|
[s(LCVarName), s(LCSVarName), i(FrameSize)]),
|
|
list.map(copy_one_slot_to_child_stack(LCVarName, LCSVarName),
|
|
StackSlots, CopyStrs),
|
|
string.append_list(CopyStrs, CopyLines),
|
|
LastLine = "\t}\n",
|
|
CodeStr = FirstLine ++ IncrLine ++ CopyLines ++ LastLine.
|
|
|
|
:- pred copy_one_slot_to_child_stack(string::in, string::in, lval::in,
|
|
string::out) is det.
|
|
|
|
copy_one_slot_to_child_stack(LCVarName, LCSVarName, StackSlot, CopyStr) :-
|
|
( if lval_to_string(StackSlot, StackSlotName) then
|
|
( if StackSlot = stackvar(N) then
|
|
CopyStr = string.format("\tMR_lc_worker_sv(%s, %s, %d) = %s;\n",
|
|
[s(LCVarName), s(LCSVarName), i(N), s(StackSlotName)])
|
|
else
|
|
unexpected($pred, "not stack slot")
|
|
)
|
|
else
|
|
unexpected($pred, "cannot convert to string")
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred best_variable_location_det(code_loc_dep::in, prog_var::in, lval::out)
|
|
is det.
|
|
|
|
best_variable_location_det(CLD, Var, Locn) :-
|
|
variable_locations(CLD, Map),
|
|
map.lookup(Map, Var, AllLocnsSet),
|
|
set.to_sorted_list(AllLocnsSet, AllLocns),
|
|
list.filter(lval_is_reg, AllLocns, RegLocns),
|
|
(
|
|
RegLocns = [Locn | _]
|
|
;
|
|
RegLocns = [],
|
|
(
|
|
AllLocns = [Locn | _]
|
|
;
|
|
AllLocns = [],
|
|
unexpected($pred, "Could not find location for variable")
|
|
)
|
|
).
|
|
|
|
:- pred lval_is_reg(lval::in) is semidet.
|
|
|
|
lval_is_reg(reg(_, _)).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% In the code of parallel conjuncts we have to refer to stack slots in
|
|
% the procedure's stack frame via the `parent_sp' register instead of the
|
|
% usual `sp' register, as the conjunct could be running in a different
|
|
% context.
|
|
%
|
|
:- pred replace_stack_vars_by_parent_sv(llds_code::in, llds_code::out) is det.
|
|
|
|
replace_stack_vars_by_parent_sv(!Code) :-
|
|
cord.map_pred(replace_stack_vars_by_parent_sv_instr, !Code).
|
|
|
|
:- pred replace_stack_vars_by_parent_sv_instr(instruction::in,
|
|
instruction::out) is det.
|
|
|
|
replace_stack_vars_by_parent_sv_instr(!Instr) :-
|
|
transform_lval_in_instr(replace_stack_vars_by_parent_sv_lval,
|
|
!Instr, unit, _).
|
|
|
|
:- pred replace_stack_vars_by_parent_sv_lval(lval::in, lval::out,
|
|
unit::in, unit::out) is det.
|
|
|
|
replace_stack_vars_by_parent_sv_lval(Lval0, Lval, !Acc) :-
|
|
TransformRval = replace_stack_vars_by_parent_sv_lval,
|
|
(
|
|
( Lval0 = stackvar(SlotNum)
|
|
; Lval0 = parent_stackvar(SlotNum)
|
|
),
|
|
Lval = parent_stackvar(SlotNum)
|
|
;
|
|
Lval0 = double_stackvar(Type, SlotNum),
|
|
(
|
|
Type = double_stackvar,
|
|
Lval = double_stackvar(double_parent_stackvar, SlotNum)
|
|
;
|
|
Type = double_parent_stackvar,
|
|
Lval = Lval0
|
|
)
|
|
;
|
|
( Lval0 = reg(_Type, _RegNum)
|
|
; Lval0 = succip
|
|
; Lval0 = maxfr
|
|
; Lval0 = curfr
|
|
; Lval0 = hp
|
|
; Lval0 = sp
|
|
; Lval0 = parent_sp
|
|
; Lval0 = temp(_Type, _TmpNum)
|
|
; Lval0 = framevar(_SlotNum)
|
|
; Lval0 = lvar(_Var)
|
|
; Lval0 = global_var_ref(_GlobalVarName)
|
|
),
|
|
Lval = Lval0
|
|
;
|
|
Lval0 = succip_slot(Rval0),
|
|
transform_lval_in_rval(TransformRval, Rval0, Rval, !Acc),
|
|
Lval = succip_slot(Rval)
|
|
;
|
|
Lval0 = redoip_slot(Rval0),
|
|
transform_lval_in_rval(TransformRval, Rval0, Rval, !Acc),
|
|
Lval = redoip_slot(Rval)
|
|
;
|
|
Lval0 = redofr_slot(Rval0),
|
|
transform_lval_in_rval(TransformRval, Rval0, Rval, !Acc),
|
|
Lval = redofr_slot(Rval)
|
|
;
|
|
Lval0 = succfr_slot(Rval0),
|
|
transform_lval_in_rval(TransformRval, Rval0, Rval, !Acc),
|
|
Lval = succfr_slot(Rval)
|
|
;
|
|
Lval0 = prevfr_slot(Rval0),
|
|
transform_lval_in_rval(TransformRval, Rval0, Rval, !Acc),
|
|
Lval = prevfr_slot(Rval)
|
|
;
|
|
Lval0 = field(Tag, Rval1, Rval2),
|
|
transform_lval_in_rval(TransformRval, Rval1, Rval3, !Acc),
|
|
transform_lval_in_rval(TransformRval, Rval2, Rval4, !Acc),
|
|
Lval = field(Tag, Rval3, Rval4)
|
|
;
|
|
Lval0 = mem_ref(Rval0),
|
|
transform_lval_in_rval(TransformRval, Rval0, Rval, !Acc),
|
|
Lval = mem_ref(Rval)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred instr_list_max_stack_ref(cord(instruction)::in, int::out) is det.
|
|
|
|
instr_list_max_stack_ref(Instrs, MaxRef) :-
|
|
instrs_rvals_and_lvals(cord.list(Instrs), RVals, LVals0),
|
|
LValsInRvalsLists = list.map(lvals_in_rval, set.to_sorted_list(RVals)),
|
|
LValsSets = list.map(set.list_to_set, LValsInRvalsLists),
|
|
LVals = set.union_list(LValsSets) `set.union` LVals0,
|
|
set.fold(max_stack_ref_acc, LVals, 0, MaxRef).
|
|
|
|
:- pred max_stack_ref_acc(lval::in, int::in, int::out) is det.
|
|
|
|
max_stack_ref_acc(LVal, Max0, Max) :-
|
|
( if
|
|
LVal = stackvar(N),
|
|
N > Max0
|
|
then
|
|
Max = N
|
|
else
|
|
Max = Max0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred find_outputs(list(prog_var)::in, instmap::in, instmap::in,
|
|
module_info::in, list(prog_var)::in, list(prog_var)::out) is det.
|
|
|
|
find_outputs([], _Initial, _Final, _ModuleInfo, !Outputs).
|
|
find_outputs([Var | Vars], Initial, Final, ModuleInfo, !Outputs) :-
|
|
instmap_lookup_var(Initial, Var, InitialInst),
|
|
instmap_lookup_var(Final, Var, FinalInst),
|
|
( if mode_is_output(ModuleInfo, from_to_mode(InitialInst, FinalInst)) then
|
|
!:Outputs = [Var | !.Outputs]
|
|
else
|
|
!:Outputs = !.Outputs
|
|
),
|
|
find_outputs(Vars, Initial, Final, ModuleInfo, !Outputs).
|
|
|
|
:- pred place_all_outputs(list(prog_var)::in,
|
|
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
place_all_outputs([], _CI, !CLD).
|
|
place_all_outputs([Var | Vars], CI, !CLD) :-
|
|
variable_locations(!.CLD, VarLocations),
|
|
get_variable_slot(CI, Var, Slot),
|
|
( if
|
|
map.search(VarLocations, Var, Locations),
|
|
set.member(Slot, Locations)
|
|
then
|
|
true
|
|
else
|
|
set_var_location(Var, Slot, !CLD)
|
|
),
|
|
place_all_outputs(Vars, CI, !CLD).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred create_static_conj_id(hlds_goal_info::in, int::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
create_static_conj_id(GoalInfo, SlotNum, !CI) :-
|
|
get_pred_id(!.CI, PredId),
|
|
get_proc_id(!.CI, ProcId),
|
|
get_module_info(!.CI, ModuleInfo),
|
|
ProcString = pred_proc_id_pair_to_dev_string(ModuleInfo, PredId, ProcId),
|
|
|
|
get_containing_goal_map(!.CI, ContainingGoalMap),
|
|
GoalId = goal_info_get_goal_id(GoalInfo),
|
|
GoalPath = goal_id_to_forward_path(ContainingGoalMap, GoalId),
|
|
GoalPathString = goal_path_to_string(GoalPath),
|
|
|
|
String = format("%s: %s", [s(ProcString), s(GoalPathString)]),
|
|
add_threadscope_string(String, SlotNum, !CI).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
:- end_module ll_backend.par_conj_gen.
|
|
%----------------------------------------------------------------------------%
|