mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-16 14:25:56 +00:00
Estimated hours taken: 1 Branches: main compiler/code_util.m: compiler/goal_form.m: compiler/hlds_code_util.m: Move functionality that is not part of the LLDS backend from ll_backend__code_util to hlds__goal_form and to hlds__hlds_code_util. Turn some predicates into functions. compiler/*.m: Conform to the changes above.
344 lines
11 KiB
Mathematica
344 lines
11 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2000,2002-2003 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: disj_gen.m:
|
|
%
|
|
% Main authors: conway, zs.
|
|
%
|
|
% The predicates of this module generate code for disjunctions.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend__disj_gen.
|
|
|
|
:- interface.
|
|
|
|
:- import_module backend_libs__code_model.
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module ll_backend__code_info.
|
|
:- import_module ll_backend__llds.
|
|
|
|
:- import_module list.
|
|
|
|
:- pred disj_gen__generate_disj(code_model::in, list(hlds_goal)::in,
|
|
hlds_goal_info::in, code_tree::out, code_info::in, code_info::out)
|
|
is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds__goal_form.
|
|
:- import_module hlds__hlds_data.
|
|
:- import_module hlds__hlds_llds.
|
|
:- import_module libs__globals.
|
|
:- import_module libs__options.
|
|
:- import_module libs__tree.
|
|
:- import_module ll_backend__code_gen.
|
|
:- import_module ll_backend__trace.
|
|
:- import_module parse_tree__prog_data.
|
|
|
|
:- import_module bool, set, libs__tree, map, std_util, term, require.
|
|
|
|
disj_gen__generate_disj(CodeModel, Goals, DisjGoalInfo, Code) -->
|
|
(
|
|
{ Goals = [] },
|
|
( { CodeModel = model_semi } ->
|
|
code_info__generate_failure(Code)
|
|
;
|
|
{ error("empty disjunction") }
|
|
)
|
|
;
|
|
{ Goals = [Goal | _] },
|
|
{ Goal = _ - GoalInfo },
|
|
{ goal_info_get_resume_point(GoalInfo, Resume) },
|
|
{ Resume = resume_point(ResumeVarsPrime, _) ->
|
|
ResumeVars = ResumeVarsPrime
|
|
;
|
|
set__init(ResumeVars)
|
|
},
|
|
disj_gen__generate_real_disj(CodeModel, ResumeVars,
|
|
Goals, DisjGoalInfo, Code)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred disj_gen__generate_real_disj(code_model::in, set(prog_var)::in,
|
|
list(hlds_goal)::in, hlds_goal_info::in, code_tree::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
disj_gen__generate_real_disj(CodeModel, ResumeVars, Goals, DisjGoalInfo, Code)
|
|
-->
|
|
|
|
% Make sure that the variables whose values will be needed
|
|
% on backtracking to any disjunct are materialized into
|
|
% registers or stack slots. Their locations are recorded
|
|
% in ResumeMap.
|
|
code_info__produce_vars(ResumeVars, ResumeMap, FlushCode),
|
|
|
|
% If we are using a trail, save the current trail state
|
|
% before the first disjunct.
|
|
% XXX We should use a scheme such as the one we use for heap
|
|
% recovery for semi and det disjunctions, and delay saving
|
|
% the ticket until necessary.
|
|
code_info__get_globals(Globals),
|
|
{ globals__lookup_bool_option(Globals, use_trail, UseTrail) },
|
|
code_info__maybe_save_ticket(UseTrail, SaveTicketCode,
|
|
MaybeTicketSlot),
|
|
|
|
% If we are using a grade in which we can recover memory
|
|
% by saving and restoring the heap pointer, set up for
|
|
% doing so if necessary.
|
|
( { CodeModel = model_non } ->
|
|
% With nondet disjunctions, we must recover memory
|
|
% across all disjuncts, even disjuncts that cannot
|
|
% themselves allocate memory, since we can backtrack
|
|
% to disjunct N after control leaves disjunct N-1.
|
|
{ globals__lookup_bool_option(Globals,
|
|
reclaim_heap_on_nondet_failure, ReclaimHeap) },
|
|
code_info__maybe_save_hp(ReclaimHeap, SaveHpCode,
|
|
MaybeHpSlot)
|
|
;
|
|
% With other disjunctions, we can backtrack to
|
|
% disjunct N only from disjunct N-1, so if disjunct
|
|
% N-1 does not allocate memory, we need not recover
|
|
% memory across it. Since it is possible (and common)
|
|
% for no disjunct to allocate memory, we delay saving
|
|
% the heap pointer and allocating a stack slot for
|
|
% the saved hp as long as possible.
|
|
{ globals__lookup_bool_option(Globals,
|
|
reclaim_heap_on_semidet_failure, ReclaimHeap) },
|
|
{ SaveHpCode = empty },
|
|
{ MaybeHpSlot = no }
|
|
),
|
|
|
|
% Save the values of any stack slots we may hijack,
|
|
% and if necessary, set the redofr slot of the top frame
|
|
% to point to this frame.
|
|
code_info__prepare_for_disj_hijack(CodeModel,
|
|
HijackInfo, PrepareHijackCode),
|
|
|
|
code_info__get_next_label(EndLabel),
|
|
|
|
code_info__remember_position(BranchStart),
|
|
disj_gen__generate_disjuncts(Goals, CodeModel, ResumeMap, no,
|
|
HijackInfo, DisjGoalInfo, EndLabel,
|
|
ReclaimHeap, MaybeHpSlot, MaybeTicketSlot,
|
|
BranchStart, no, MaybeEnd, GoalsCode),
|
|
|
|
{ goal_info_get_store_map(DisjGoalInfo, StoreMap) },
|
|
code_info__after_all_branches(StoreMap, MaybeEnd),
|
|
( { CodeModel = model_non } ->
|
|
code_info__set_resume_point_to_unknown
|
|
;
|
|
[]
|
|
),
|
|
{ Code =
|
|
tree(FlushCode,
|
|
tree(SaveTicketCode,
|
|
tree(SaveHpCode,
|
|
tree(PrepareHijackCode,
|
|
GoalsCode))))
|
|
}.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred disj_gen__generate_disjuncts(list(hlds_goal)::in,
|
|
code_model::in, resume_map::in, maybe(resume_point_info)::in,
|
|
disj_hijack_info::in, hlds_goal_info::in, label::in,
|
|
bool::in, maybe(lval)::in, maybe(lval)::in, position_info::in,
|
|
maybe(branch_end_info)::in, maybe(branch_end_info)::out,
|
|
code_tree::out, code_info::in, code_info::out) is det.
|
|
|
|
disj_gen__generate_disjuncts([], _, _, _, _, _, _, _, _, _, _, _, _, _) -->
|
|
{ error("empty disjunction!") }.
|
|
disj_gen__generate_disjuncts([Goal0 | Goals], CodeModel, FullResumeMap,
|
|
MaybeEntryResumePoint, HijackInfo, DisjGoalInfo, EndLabel,
|
|
ReclaimHeap, MaybeHpSlot0, MaybeTicketSlot,
|
|
BranchStart0, MaybeEnd0, MaybeEnd, Code) -->
|
|
|
|
code_info__reset_to_position(BranchStart0),
|
|
|
|
% If this is not the first disjunct, generate the
|
|
% resume point by which arrive at this disjunct.
|
|
( { MaybeEntryResumePoint = yes(EntryResumePoint) } ->
|
|
code_info__generate_resume_point(EntryResumePoint,
|
|
EntryResumePointCode)
|
|
;
|
|
{ EntryResumePointCode = empty }
|
|
),
|
|
|
|
{ Goal0 = GoalExpr0 - GoalInfo0 },
|
|
{ goal_info_get_resume_point(GoalInfo0, Resume) },
|
|
(
|
|
{ Resume = resume_point(ResumeVars, ResumeLocs) }
|
|
->
|
|
% Emit code for a non-last disjunct, including setting things
|
|
% up for the execution of the next disjunct.
|
|
|
|
( { MaybeEntryResumePoint = yes(_) } ->
|
|
% Reset the heap pointer to recover memory
|
|
% allocated by the previous disjunct(s),
|
|
% if necessary.
|
|
code_info__maybe_restore_hp(MaybeHpSlot0,
|
|
RestoreHpCode),
|
|
|
|
% Reset the solver state if necessary.
|
|
code_info__maybe_reset_ticket(MaybeTicketSlot, undo,
|
|
RestoreTicketCode)
|
|
;
|
|
{ RestoreHpCode = empty },
|
|
{ RestoreTicketCode = empty }
|
|
),
|
|
|
|
% The pre_goal_update sanity check insists on
|
|
% no_resume_point, to make sure that all resume
|
|
% points have been handled by surrounding code.
|
|
{ goal_info_set_resume_point(GoalInfo0, no_resume_point,
|
|
GoalInfo) },
|
|
{ Goal = GoalExpr0 - GoalInfo },
|
|
|
|
% Save hp if it needs to be saved and hasn't been
|
|
% saved previously.
|
|
(
|
|
{ ReclaimHeap = yes },
|
|
{ goal_may_allocate_heap(Goal) },
|
|
{ MaybeHpSlot0 = no }
|
|
->
|
|
code_info__save_hp(SaveHpCode, HpSlot),
|
|
{ MaybeHpSlot = yes(HpSlot) },
|
|
% This method of updating BranchStart0 is
|
|
% ugly. The best alternative would be to
|
|
% arrange things so that a remember_position
|
|
% here could get BranchStart, but doing so
|
|
% is iffy because we have already created
|
|
% the resumption point for entry into this
|
|
% disjunction, which overwrites part of the
|
|
% location-dependent state originally in
|
|
% BranchStart0.
|
|
{ code_info__save_hp_in_branch(BranchSaveHpCode,
|
|
BranchHpSlot, BranchStart0, BranchStart) },
|
|
{ tree__flatten(SaveHpCode, HpCodeList) },
|
|
{ tree__flatten(BranchSaveHpCode, BranchHpCodeList) },
|
|
{ require(unify(HpCodeList, BranchHpCodeList),
|
|
"cannot use same code for saving hp") },
|
|
{ require(unify(HpSlot, BranchHpSlot),
|
|
"cannot allocate same slot for saved hp") }
|
|
;
|
|
{ SaveHpCode = empty },
|
|
{ MaybeHpSlot = MaybeHpSlot0 },
|
|
{ BranchStart = BranchStart0 }
|
|
),
|
|
|
|
code_info__make_resume_point(ResumeVars, ResumeLocs,
|
|
FullResumeMap, NextResumePoint),
|
|
code_info__effect_resume_point(NextResumePoint, CodeModel,
|
|
ModContCode),
|
|
|
|
trace__maybe_generate_internal_event_code(Goal, DisjGoalInfo,
|
|
TraceCode),
|
|
{ goal_info_get_code_model(GoalInfo, GoalCodeModel) },
|
|
code_gen__generate_goal(GoalCodeModel, Goal, GoalCode),
|
|
|
|
( { CodeModel = model_non } ->
|
|
% We can backtrack to the next disjunct from outside,
|
|
% so we make sure every variable in the resume set
|
|
% is in its stack slot.
|
|
code_info__flush_resume_vars_to_stack(ResumeVarsCode),
|
|
|
|
% We hang onto any temporary slots holding saved
|
|
% heap pointers and/or tickets, thus ensuring that
|
|
% they will still be reserved after the disjunction.
|
|
{ PruneTicketCode = empty }
|
|
;
|
|
{ ResumeVarsCode = empty },
|
|
|
|
code_info__maybe_release_hp(MaybeHpSlot),
|
|
% We're committing to this disjunct if it succeeds.
|
|
code_info__maybe_reset_prune_and_release_ticket(
|
|
MaybeTicketSlot, commit, PruneTicketCode),
|
|
|
|
code_info__reset_resume_known(BranchStart)
|
|
),
|
|
|
|
% Forget the variables that are needed only at the
|
|
% resumption point at the start of the next disjunct,
|
|
% so that we don't generate exceptions when their
|
|
% storage is clobbered by the movement of the live
|
|
% variables to the places indicated in the store map.
|
|
code_info__pop_resume_point,
|
|
code_info__pickup_zombies(Zombies),
|
|
code_info__make_vars_forward_dead(Zombies),
|
|
|
|
% Put every variable whose value is needed after
|
|
% the disjunction to the place indicated by StoreMap,
|
|
% and accumulate information about the code_info state
|
|
% at the ends of the branches so far.
|
|
{ goal_info_get_store_map(DisjGoalInfo, StoreMap) },
|
|
code_info__generate_branch_end(StoreMap, MaybeEnd0, MaybeEnd1,
|
|
SaveCode),
|
|
|
|
{ BranchCode = node([
|
|
goto(label(EndLabel)) -
|
|
"skip to end of nondet disj"
|
|
]) },
|
|
|
|
disj_gen__generate_disjuncts(Goals, CodeModel, FullResumeMap,
|
|
yes(NextResumePoint), HijackInfo, DisjGoalInfo,
|
|
EndLabel, ReclaimHeap, MaybeHpSlot, MaybeTicketSlot,
|
|
BranchStart, MaybeEnd1, MaybeEnd, RestCode),
|
|
|
|
{ Code =
|
|
tree(EntryResumePointCode,
|
|
tree(RestoreHpCode,
|
|
tree(RestoreTicketCode,
|
|
tree(SaveHpCode,
|
|
tree(ModContCode,
|
|
tree(TraceCode,
|
|
tree(GoalCode,
|
|
tree(ResumeVarsCode,
|
|
tree(PruneTicketCode,
|
|
tree(SaveCode,
|
|
tree(BranchCode,
|
|
RestCode)))))))))))
|
|
}
|
|
;
|
|
% Emit code for the last disjunct
|
|
|
|
% Restore the heap pointer and solver state
|
|
% if necessary.
|
|
code_info__maybe_restore_and_release_hp(MaybeHpSlot0,
|
|
RestoreHpCode),
|
|
code_info__maybe_reset_discard_and_release_ticket(
|
|
MaybeTicketSlot, undo, RestoreTicketCode),
|
|
|
|
code_info__undo_disj_hijack(HijackInfo, UndoCode),
|
|
|
|
trace__maybe_generate_internal_event_code(Goal0, DisjGoalInfo,
|
|
TraceCode),
|
|
code_gen__generate_goal(CodeModel, Goal0, GoalCode),
|
|
{ goal_info_get_store_map(DisjGoalInfo, StoreMap) },
|
|
code_info__generate_branch_end(StoreMap, MaybeEnd0, MaybeEnd,
|
|
SaveCode),
|
|
|
|
{ EndCode = node([
|
|
label(EndLabel) - "End of nondet disj"
|
|
]) },
|
|
{ Code =
|
|
tree(EntryResumePointCode,
|
|
tree(TraceCode,
|
|
tree(RestoreHpCode,
|
|
tree(RestoreTicketCode,
|
|
tree(UndoCode,
|
|
tree(GoalCode,
|
|
tree(SaveCode,
|
|
EndCode)))))))
|
|
}
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|