Files
mercury/compiler/disj_gen.m
Zoltan Somogyi 6d1bc24d0b Make vartypes an abstract data type, in preparation for exploring
Estimated hours taken: 4
Branches: main

compiler/prog_data.m:
	Make vartypes an abstract data type, in preparation for exploring
	better representations for it.

compiler/mode_util.m:
	Provide two different versions of a predicate. The generic version
	continues to use map lookups. The other version knows it works on
	prog_vars, so it can use the abstract operations on them provided
	by prog_data.m.

compiler/accumulator.m:
compiler/add_class.m:
compiler/add_heap_ops.m:
compiler/add_pragma.m:
compiler/add_pred.m:
compiler/add_trail_ops.m:
compiler/arg_info.m:
compiler/builtin_lib_types.m:
compiler/bytecode_gen.m:
compiler/call_gen.m:
compiler/clause_to_proc.m:
compiler/closure_analysis.m:
compiler/code_info.m:
compiler/common.m:
compiler/complexity.m:
compiler/const_prop.m:
compiler/constraint.m:
compiler/continuation_info.m:
compiler/cse_detection.m:
compiler/ctgc.datastruct.m:
compiler/ctgc.util.m:
compiler/deep_profiling.m:
compiler/deforest.m:
compiler/dep_par_conj.m:
compiler/det_analysis.m:
compiler/det_report.m:
compiler/det_util.m:
compiler/disj_gen.m:
compiler/equiv_type_hlds.m:
compiler/erl_call_gen.m:
compiler/erl_code_gen.m:
compiler/erl_code_util.m:
compiler/exception_analysis.m:
compiler/float_regs.m:
compiler/follow_vars.m:
compiler/format_call.m:
compiler/goal_path.m:
compiler/goal_util.m:
compiler/hhf.m:
compiler/higher_order.m:
compiler/hlds_clauses.m:
compiler/hlds_goal.m:
compiler/hlds_out_goal.m:
compiler/hlds_out_pred.m:
compiler/hlds_pred.m:
compiler/hlds_rtti.m:
compiler/inlining.m:
compiler/instmap.m:
compiler/intermod.m:
compiler/interval.m:
compiler/lambda.m:
compiler/lco.m:
compiler/live_vars.m:
compiler/liveness.m:
compiler/lookup_switch.m:
compiler/mercury_to_mercury.m:
compiler/ml_accurate_gc.m:
compiler/ml_closure_gen.m:
compiler/ml_code_gen.m:
compiler/ml_code_util.m:
compiler/ml_disj_gen.m:
compiler/ml_lookup_switch.m:
compiler/ml_proc_gen.m:
compiler/ml_unify_gen.m:
compiler/mode_info.m:
compiler/modecheck_call.m:
compiler/modecheck_conj.m:
compiler/modecheck_goal.m:
compiler/modecheck_unify.m:
compiler/modecheck_util.m:
compiler/modes.m:
compiler/par_loop_control.m:
compiler/pd_info.m:
compiler/pd_util.m:
compiler/polymorphism.m:
compiler/post_typecheck.m:
compiler/prog_type_subst.m:
compiler/prop_mode_constraints.m:
compiler/purity.m:
compiler/qual_info.m:
compiler/rbmm.points_to_info.m:
compiler/rbmm.region_liveness_info.m:
compiler/rbmm.region_transformation.m:
compiler/saved_vars.m:
compiler/simplify.m:
compiler/size_prof.m:
compiler/ssdebug.m:
compiler/stack_alloc.m:
compiler/stack_opt.m:
compiler/store_alloc.m:
compiler/structure_reuse.analysis.m:
compiler/structure_reuse.direct.choose_reuse.m:
compiler/structure_reuse.direct.detect_garbage.m:
compiler/structure_reuse.indirect.m:
compiler/structure_sharing.analysis.m:
compiler/structure_sharing.domain.m:
compiler/switch_detection.m:
compiler/table_gen.m:
compiler/term_constr_build.m:
compiler/term_constr_util.m:
compiler/term_traversal.m:
compiler/term_util.m:
compiler/trace_gen.m:
compiler/trailing_analysis.m:
compiler/try_expand.m:
compiler/tupling.m:
compiler/type_constraints.m:
compiler/type_util.m:
compiler/typecheck.m:
compiler/typecheck_errors.m:
compiler/typecheck_info.m:
compiler/unify_gen.m:
compiler/unify_proc.m:
compiler/unique_modes.m:
compiler/untupling.m:
compiler/unused_args.m:
compiler/var_locn.m:
	Conform to the above.

compiler/prog_type.m:
compiler/rbmm.points_to_graph.m:
	Conform to the above.

	Move some comments where they belong.

compiler/stm_expand.m:
	Conform to the above.

	Do not export a predicate that is not used outside this module.

	Disable some debugging output unless it is asked for.

	Remove unnecessary prefixes on variable names.

library/version_array.m:
	Instead writing code for field access lookalike functions and defining
	lookup, set etc in terms of them, write code for lookup, set etc,
	and define the field access lookalike functions in terms of them.

	Change argument orders of some internal predicates to be
	more state variable friendly.

	Fix typos in comments.

tests/hard_coded/version_array_test.exp:
	Conform to the change to version_array.m.
2012-07-02 01:16:39 +00:00

932 lines
38 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1994-2000,2002-2012 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.
%
% The outlines of the implementation of lookup disjunctions here is shared
% with similar code in ml_disj_gen.m that does the same thing for the MLDS
% backend. Any changes here may need to be reflected there as well.
%
%-----------------------------------------------------------------------------%
:- module ll_backend.disj_gen.
:- interface.
:- import_module hlds.code_model.
:- import_module hlds.hlds_goal.
:- import_module ll_backend.code_info.
:- import_module ll_backend.llds.
:- import_module list.
%-----------------------------------------------------------------------------%
:- pred generate_disj(code_model::in, list(hlds_goal)::in, hlds_goal_info::in,
llds_code::out, code_info::in, code_info::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.builtin_ops.
:- import_module hlds.goal_form.
:- import_module hlds.hlds_llds.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module ll_backend.code_gen.
:- import_module ll_backend.continuation_info.
:- import_module ll_backend.global_data.
:- import_module ll_backend.lookup_util.
:- import_module ll_backend.trace_gen.
:- import_module parse_tree.prog_data.
:- 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 term.
%-----------------------------------------------------------------------------%
generate_disj(CodeModel, Goals, DisjGoalInfo, Code, !CI) :-
(
Goals = [],
(
CodeModel = model_semi,
generate_failure(Code, !CI)
;
( CodeModel = model_det
; CodeModel = model_non
),
unexpected($module, $pred, "empty disjunction")
)
;
Goals = [Goal | _],
Goal = hlds_goal(_, GoalInfo),
goal_info_get_resume_point(GoalInfo, Resume),
(
Resume = resume_point(ResumeVarsPrime, _),
ResumeVars = ResumeVarsPrime
;
Resume = no_resume_point,
ResumeVars = set_of_var.init
),
AddTrailOps = should_add_trail_ops(!.CI, GoalInfo),
AddRegionOps = should_add_region_ops(!.CI, GoalInfo),
(
CodeModel = model_non,
is_lookup_disj(AddTrailOps, AddRegionOps, ResumeVars, Goals,
DisjGoalInfo, LookupDisjInfo, !CI)
->
generate_lookup_disj(ResumeVars, LookupDisjInfo, Code, !CI)
;
generate_real_disj(AddTrailOps, AddRegionOps, CodeModel,
ResumeVars, Goals, DisjGoalInfo, Code, !CI)
)
).
:- type lookup_disj_info
---> lookup_disj_info(
ldi_variables :: list(prog_var),
% The output variables.
ldi_store_map :: abs_store_map,
ldi_branch_end :: branch_end,
ldi_liveness :: set_of_progvar,
lds_cur_slot :: lval,
lds_resume_map :: resume_map,
lds_flush_code :: llds_code,
lds_save_ticket_code :: llds_code,
lds_maybe_ticket_slot :: maybe(lval),
lds_save_hp_code :: llds_code,
lds_maybe_hp_slot :: maybe(lval),
lds_hijack_info :: disj_hijack_info,
lds_prepare_hijack_code :: llds_code,
ldi_solns :: list(list(rval)),
ldi_field_types :: list(llds_type)
).
:- pred is_lookup_disj(add_trail_ops::in, add_region_ops::in,
set_of_progvar::in, list(hlds_goal)::in, hlds_goal_info::in,
lookup_disj_info::out, code_info::in, code_info::out) is semidet.
is_lookup_disj(AddTrailOps, AddRegionOps, ResumeVars, Disjuncts, DisjGoalInfo,
LookupDisjInfo, !CI) :-
% Since we generate two code sequences, one for the first solution and one
% for all the later solutions, we don't get any benefit over the code
% sequence generated by generate_real_disj unless there are at least three
% solutions.
Disjuncts = [FirstDisjunct | LaterDisjuncts],
LaterDisjuncts = [_, _ | _],
DisjNonLocals = goal_info_get_nonlocals(DisjGoalInfo),
goal_is_conj_of_unify(DisjNonLocals, FirstDisjunct),
all_disjuncts_are_conj_of_unify(DisjNonLocals, LaterDisjuncts),
get_maybe_trace_info(!.CI, MaybeTraceInfo),
MaybeTraceInfo = no,
% Lookup disjunctions rely on static ground terms to work.
get_globals(!.CI, Globals),
globals.lookup_bool_option(Globals, static_ground_cells, yes),
% XXX The code to generate lookup disjunctions hasn't yet been updated
% to handle region operations.
AddRegionOps = do_not_add_region_ops,
figure_out_output_vars(!.CI, DisjGoalInfo, OutVars),
VarTypes = get_var_types(!.CI),
lookup_var_types(VarTypes, OutVars, OutTypes),
produce_vars(set_of_var.to_sorted_list(ResumeVars), ResumeMap,
FlushCode, !CI),
% We cannot release this stack slot anywhere within the disjunction,
% since it will be needed after backtracking to later disjuncts.
% However, if we are inside an outer branched control structure
% (disjunction, switch, if-then-else), it may be released (implicitly)
% when we get into the next branch of that outer control structure.
acquire_temp_slot(slot_lookup_disj_cur, non_persistent_temp_slot, CurSlot,
!CI),
maybe_save_ticket(AddTrailOps, SaveTicketCode, MaybeTicketSlot, !CI),
get_globals(!.CI, Globals),
globals.lookup_bool_option(Globals, reclaim_heap_on_nondet_failure,
ReclaimHeap),
maybe_save_hp(ReclaimHeap, SaveHpCode, MaybeHpSlot, !CI),
prepare_for_disj_hijack(model_non, HijackInfo, PrepareHijackCode, !CI),
% Every update to the code_info that needs to persist beyond the
% disjunction as a whole (e.g. the reservations of the stack slots required
% to implement the hijack) must be done before we remember this position.
remember_position(!.CI, CurPos),
goal_info_get_store_map(DisjGoalInfo, StoreMap),
generate_constants_for_disjunct(FirstDisjunct, OutVars, StoreMap,
FirstSoln, no, MaybeEnd1, Liveness, !CI),
generate_constants_for_disjuncts(LaterDisjuncts, OutVars, StoreMap,
LaterSolns, MaybeEnd1, MaybeEnd, !CI),
Solns = [FirstSoln | LaterSolns],
reset_to_position(CurPos, !CI),
get_exprn_opts(!.CI, ExprnOpts),
UnboxFloats = get_unboxed_floats(ExprnOpts),
find_general_llds_types(UnboxFloats, OutTypes, Solns, LLDSTypes),
LookupDisjInfo = lookup_disj_info(OutVars, StoreMap, MaybeEnd, Liveness,
CurSlot, ResumeMap, FlushCode, SaveTicketCode, MaybeTicketSlot,
SaveHpCode, MaybeHpSlot, HijackInfo, PrepareHijackCode,
Solns, LLDSTypes).
:- pred generate_lookup_disj(set_of_progvar::in, lookup_disj_info::in,
llds_code::out, code_info::in, code_info::out) is det.
generate_lookup_disj(ResumeVars, LookupDisjInfo, Code, !CI) :-
LookupDisjInfo = lookup_disj_info(OutVars, StoreMap, MaybeEnd0, Liveness,
CurSlot, ResumeMap, FlushCode, SaveTicketCode, MaybeTicketSlot,
SaveHpCode, MaybeHpSlot, HijackInfo, PrepareHijackCode,
Solns, LLDSTypes),
list.length(Solns, NumSolns),
list.length(OutVars, NumOutVars),
add_vector_static_cell(LLDSTypes, Solns, SolnVectorAddr, !CI),
SolnVectorAddrRval = const(llconst_data_addr(SolnVectorAddr, no)),
get_next_label(EndLabel, !CI),
% Since we release BaseReg only after the calls to generate_branch_end
% (invoked through set_liveness_and_end_branch) we must ensure that
% generate_branch_end won't want to overwrite BaseReg.
acquire_reg_not_in_storemap(StoreMap, reg_r, BaseReg, !CI),
BaseRegInitCode = cord.singleton(
llds_instr(assign(BaseReg,
mem_addr(heap_ref(SolnVectorAddrRval, yes(0),
const(llconst_int(0))))),
"Compute base address for this case")
),
SaveSlotCode = cord.singleton(
llds_instr(assign(CurSlot, const(llconst_int(NumOutVars))),
"Setup current slot in the solution array")
),
remember_position(!.CI, DisjEntry),
make_resume_point(set_of_var.to_sorted_list(ResumeVars),
resume_locs_stack_only, ResumeMap, ResumePoint, !CI),
effect_resume_point(ResumePoint, model_non, UpdateRedoipCode, !CI),
generate_offset_assigns(OutVars, 0, BaseReg, !CI),
flush_resume_vars_to_stack(FirstFlushResumeVarsCode, !CI),
% 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.
pop_resume_point(!CI),
pickup_zombies(FirstZombies, !CI),
make_vars_forward_dead(FirstZombies, !CI),
set_liveness_and_end_branch(StoreMap, Liveness, MaybeEnd0, MaybeEnd1,
FirstBranchEndCode, !CI),
release_reg(BaseReg, !CI),
GotoEndCode = singleton(
llds_instr(goto(code_label(EndLabel)), "goto end of lookup disj")
),
reset_to_position(DisjEntry, !CI),
generate_resume_point(ResumePoint, ResumePointCode, !CI),
maybe_reset_ticket(MaybeTicketSlot, reset_reason_undo, RestoreTicketCode),
maybe_restore_hp(MaybeHpSlot, RestoreHpCode),
acquire_reg_not_in_storemap(StoreMap, reg_r, LaterBaseReg, !CI),
get_next_label(UndoLabel, !CI),
get_next_label(AfterUndoLabel, !CI),
MaxSlot = (NumSolns - 1) * NumOutVars,
TestMoreSolnsCode = from_list([
llds_instr(assign(LaterBaseReg, lval(CurSlot)),
"Init later base register"),
llds_instr(if_val(binop(int_ge, lval(LaterBaseReg),
const(llconst_int(MaxSlot))),
code_label(UndoLabel)),
"Jump to undo hijack code if there are no more solutions"),
llds_instr(assign(CurSlot,
binop(int_add, lval(CurSlot), const(llconst_int(NumOutVars)))),
"Update current slot"),
llds_instr(goto(code_label(AfterUndoLabel)),
"Jump around undo hijack code"),
llds_instr(label(UndoLabel),
"Undo hijack code")
]),
undo_disj_hijack(HijackInfo, UndoHijackCode, !CI),
AfterUndoLabelCode = from_list([
llds_instr(label(AfterUndoLabel),
"Return later answer code"),
llds_instr(assign(LaterBaseReg,
mem_addr(heap_ref(SolnVectorAddrRval, yes(0),
lval(LaterBaseReg)))),
"Compute base address in later array for this solution")
]),
% We need to call effect_resume_point in order to push ResumePoint
% onto the failure continuation stack, so pop_resume_point can pop
% it off. However, since the redoip already points there, we don't need
% to execute _LaterUpdateRedoipCode.
effect_resume_point(ResumePoint, model_non, _LaterUpdateRedoipCode, !CI),
generate_offset_assigns(OutVars, 0, LaterBaseReg, !CI),
flush_resume_vars_to_stack(LaterFlushResumeVarsCode, !CI),
% 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.
pop_resume_point(!CI),
pickup_zombies(LaterZombies, !CI),
make_vars_forward_dead(LaterZombies, !CI),
set_liveness_and_end_branch(StoreMap, Liveness, MaybeEnd1, MaybeEnd,
LaterBranchEndCode, !CI),
after_all_branches(StoreMap, MaybeEnd, !CI),
EndLabelCode = singleton(
llds_instr(label(EndLabel), "end of lookup disj")
),
Comment = singleton(
llds_instr(comment("lookup disj"), "")
),
Code =
Comment ++
FlushCode ++
BaseRegInitCode ++
SaveSlotCode ++
SaveTicketCode ++
SaveHpCode ++
PrepareHijackCode ++
UpdateRedoipCode ++
FirstFlushResumeVarsCode ++
FirstBranchEndCode ++
GotoEndCode ++
ResumePointCode ++
RestoreTicketCode ++
RestoreHpCode ++
TestMoreSolnsCode ++
UndoHijackCode ++
AfterUndoLabelCode ++
LaterFlushResumeVarsCode ++
LaterBranchEndCode ++
EndLabelCode.
%---------------------------------------------------------------------------%
:- pred generate_real_disj(add_trail_ops::in, add_region_ops::in,
code_model::in, set_of_progvar::in, list(hlds_goal)::in,
hlds_goal_info::in, llds_code::out, code_info::in, code_info::out) is det.
generate_real_disj(AddTrailOps, AddRegionOps, CodeModel, ResumeVars, Goals,
DisjGoalInfo, Code, !CI) :-
% 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.
produce_vars(set_of_var.to_sorted_list(ResumeVars), ResumeMap,
FlushCode, !CI),
% 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.
get_globals(!.CI, Globals),
maybe_save_ticket(AddTrailOps, SaveTicketCode, MaybeTicketSlot, !CI),
% 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),
maybe_save_hp(ReclaimHeap, SaveHpCode, MaybeHpSlot, !CI),
maybe_create_disj_region_frame_nondet(AddRegionOps, DisjGoalInfo,
BeforeEnterRegionCode, LaterRegionCode, LastRegionCode, !CI),
% We can't release any of the stack slots holding the embedded stack
% frame, since we can't let code to the right of the disjunction reuse
% any of those slots.
RegionStackVarsToRelease = [],
RegionCommitDisjCleanup = no_commit_disj_region_cleanup
;
( CodeModel = model_det
; CodeModel = model_semi
),
% 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,
MaybeRbmmInfo = goal_info_get_maybe_rbmm(DisjGoalInfo),
(
MaybeRbmmInfo = no,
BeforeEnterRegionCode = empty,
LaterRegionCode = empty,
LastRegionCode = empty,
RegionStackVarsToRelease = [],
RegionCommitDisjCleanup = no_commit_disj_region_cleanup
;
MaybeRbmmInfo = yes(RbmmInfo),
RbmmInfo = rbmm_goal_info(DisjCreatedRegionVars,
DisjRemovedRegionVars, _DisjCarriedRegionVars,
DisjAllocRegionVars, _DisjUsedRegionVars),
(
set.empty(DisjCreatedRegionVars),
set.empty(DisjRemovedRegionVars),
set.empty(DisjAllocRegionVars)
->
BeforeEnterRegionCode = empty,
LaterRegionCode = empty,
LastRegionCode = empty,
RegionStackVarsToRelease = [],
RegionCommitDisjCleanup = no_commit_disj_region_cleanup
;
% We only need region support for backtracking if some disjunct
% performs some region operations (allocation or removal).
maybe_create_disj_region_frame_semi(AddRegionOps,
set_of_var.set_to_bitset(DisjRemovedRegionVars),
set_of_var.set_to_bitset(DisjAllocRegionVars),
BeforeEnterRegionCode, LaterRegionCode, LastRegionCode,
RegionStackVars, RegionCommitDisjCleanup, !CI),
RegionStackVarsToRelease = RegionStackVars
)
)
),
% 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.
prepare_for_disj_hijack(CodeModel, HijackInfo, PrepareHijackCode, !CI),
get_next_label(EndLabel, !CI),
remember_position(!.CI, BranchStart),
generate_disjuncts(Goals, CodeModel, ResumeMap, no, HijackInfo,
DisjGoalInfo, RegionCommitDisjCleanup, EndLabel, ReclaimHeap,
MaybeHpSlot, MaybeTicketSlot, LaterRegionCode, LastRegionCode,
BranchStart, no, MaybeEnd, GoalsCode, !CI),
goal_info_get_store_map(DisjGoalInfo, StoreMap),
after_all_branches(StoreMap, MaybeEnd, !CI),
release_several_temp_slots(RegionStackVarsToRelease,
non_persistent_temp_slot, !CI),
(
CodeModel = model_non,
set_resume_point_to_unknown(!CI)
;
( CodeModel = model_det
; CodeModel = model_semi
)
% The resume point is unchanged.
),
Code = FlushCode ++ SaveTicketCode ++ SaveHpCode ++
BeforeEnterRegionCode ++ PrepareHijackCode ++ GoalsCode.
:- pred 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, commit_disj_region_cleanup::in, label::in, bool::in,
maybe(lval)::in, maybe(lval)::in, llds_code::in, llds_code::in,
position_info::in, maybe(branch_end_info)::in, maybe(branch_end_info)::out,
llds_code::out, code_info::in, code_info::out) is det.
generate_disjuncts([], _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, !CI) :-
unexpected($module, $pred, "empty disjunction").
generate_disjuncts([Goal0 | Goals], CodeModel, FullResumeMap,
MaybeEntryResumePoint, HijackInfo, DisjGoalInfo,
RegionCommitDisjCleanup, EndLabel, ReclaimHeap,
MaybeHpSlot0, MaybeTicketSlot, LaterRegionCode, LastRegionCode,
BranchStart0, MaybeEnd0, MaybeEnd, Code, !CI) :-
reset_to_position(BranchStart0, !CI),
% If this is not the first disjunct, generate the resume point by which
% we arrive at this disjunct.
(
MaybeEntryResumePoint = yes(EntryResumePoint),
generate_resume_point(EntryResumePoint, EntryResumePointCode, !CI)
;
MaybeEntryResumePoint = no,
EntryResumePointCode = empty
),
Goal0 = hlds_goal(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.
maybe_restore_hp(MaybeHpSlot0, RestoreHpCode),
% Reset the solver state if necessary.
maybe_reset_ticket(MaybeTicketSlot, reset_reason_undo,
RestoreTicketCode),
ThisDisjunctRegionCode = LaterRegionCode
;
MaybeEntryResumePoint = no,
RestoreHpCode = empty,
RestoreTicketCode = empty,
ThisDisjunctRegionCode = 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(no_resume_point, GoalInfo0, GoalInfo),
Goal = hlds_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
->
save_hp(SaveHpCode, HpSlot, !CI),
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.
save_hp_in_branch(BranchSaveHpCode, BranchHpSlot,
BranchStart0, BranchStart, !CI),
HpCodeInstrs = cord.list(SaveHpCode),
BranchHpCodeInstrs = cord.list(BranchSaveHpCode),
expect(unify(HpCodeInstrs, BranchHpCodeInstrs), $module, $pred,
"cannot use same code for saving hp"),
expect(unify(HpSlot, BranchHpSlot), $module, $pred,
"cannot allocate same slot for saved hp")
;
SaveHpCode = empty,
MaybeHpSlot = MaybeHpSlot0,
BranchStart = BranchStart0
),
make_resume_point(set_of_var.to_sorted_list(ResumeVars),
ResumeLocs, FullResumeMap, NextResumePoint, !CI),
effect_resume_point(NextResumePoint, CodeModel, ModContCode, !CI),
maybe_generate_internal_event_code(Goal, DisjGoalInfo, TraceCode, !CI),
GoalCodeModel = goal_info_get_code_model(GoalInfo),
code_gen.generate_goal(GoalCodeModel, Goal, GoalCode, !CI),
(
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.
flush_resume_vars_to_stack(ResumeVarsCode, !CI),
% 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
;
( CodeModel = model_det
; CodeModel = model_semi
),
ResumeVarsCode = empty,
maybe_release_hp(MaybeHpSlot, !CI),
% We're committing to this disjunct if it succeeds.
maybe_reset_prune_and_release_ticket(MaybeTicketSlot,
reset_reason_commit, PruneTicketCode, !CI),
reset_resume_known(BranchStart, !CI)
),
% 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.
pop_resume_point(!CI),
pickup_zombies(Zombies, !CI),
make_vars_forward_dead(Zombies, !CI),
% 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),
generate_branch_end(StoreMap, MaybeEnd0, MaybeEnd1, SaveCode, !CI),
(
RegionCommitDisjCleanup = no_commit_disj_region_cleanup,
BranchCode = singleton(
llds_instr(goto(code_label(EndLabel)),
"skip to end of disjunction")
)
;
RegionCommitDisjCleanup = commit_disj_region_cleanup(CleanupLabel,
_CleanupCode),
BranchCode = singleton(
llds_instr(goto(code_label(CleanupLabel)),
"skip to end of disjunction after nonlast region disjunct")
)
),
generate_disjuncts(Goals, CodeModel, FullResumeMap,
yes(NextResumePoint), HijackInfo, DisjGoalInfo,
RegionCommitDisjCleanup, EndLabel,
ReclaimHeap, MaybeHpSlot, MaybeTicketSlot,
LaterRegionCode, LastRegionCode, BranchStart,
MaybeEnd1, MaybeEnd, RestCode, !CI),
Code =
EntryResumePointCode ++
RestoreHpCode ++
RestoreTicketCode ++
SaveHpCode ++
ThisDisjunctRegionCode ++
ModContCode ++
TraceCode ++
GoalCode ++
ResumeVarsCode ++
PruneTicketCode ++
SaveCode ++
BranchCode ++
RestCode
;
Resume = no_resume_point,
% Emit code for the last disjunct.
% Restore the heap pointer and solver state if necessary.
maybe_restore_and_release_hp(MaybeHpSlot0, RestoreHpCode, !CI),
maybe_reset_discard_and_release_ticket(MaybeTicketSlot,
reset_reason_undo, RestoreTicketCode, !CI),
undo_disj_hijack(HijackInfo, UndoCode, !CI),
maybe_generate_internal_event_code(Goal0, DisjGoalInfo, TraceCode,
!CI),
code_gen.generate_goal(CodeModel, Goal0, GoalCode, !CI),
goal_info_get_store_map(DisjGoalInfo, StoreMap),
generate_branch_end(StoreMap, MaybeEnd0, MaybeEnd, SaveCode, !CI),
(
RegionCommitDisjCleanup = no_commit_disj_region_cleanup,
RegionCleanupCode = empty
;
RegionCommitDisjCleanup = commit_disj_region_cleanup(CleanupLabel,
CleanupCode),
RegionCleanupStartCode = from_list([
llds_instr(goto(code_label(EndLabel)),
"Skip over cleanup code at end of disjunction"),
llds_instr(label(CleanupLabel),
"Cleanup at end of disjunction")
]),
RegionCleanupCode = RegionCleanupStartCode ++ CleanupCode
),
EndLabelCode = singleton(
llds_instr(label(EndLabel),
"End of disjunction")
),
Code =
EntryResumePointCode ++
TraceCode ++ % XXX Should this be after LastRegionCode?
RestoreHpCode ++
RestoreTicketCode ++
LastRegionCode ++
UndoCode ++
GoalCode ++
SaveCode ++
RegionCleanupCode ++
EndLabelCode
).
%-----------------------------------------------------------------------------%
:- type commit_disj_region_cleanup
---> no_commit_disj_region_cleanup
; commit_disj_region_cleanup(
cleanup_label :: label,
cleanup_code :: llds_code
).
:- pred maybe_create_disj_region_frame_nondet(add_region_ops::in,
hlds_goal_info::in, llds_code::out, llds_code::out, llds_code::out,
code_info::in, code_info::out) is det.
maybe_create_disj_region_frame_nondet(DisjRegionOps, _DisjGoalInfo,
BeforeEnterCode, LaterCode, LastCode, !CI) :-
(
DisjRegionOps = do_not_add_region_ops,
BeforeEnterCode = empty,
LaterCode = empty,
LastCode = empty
;
DisjRegionOps = add_region_ops,
get_forward_live_vars(!.CI, ForwardLiveVars),
LiveRegionVars = filter_region_vars(!.CI, ForwardLiveVars),
% Protection of backward live regions for nondet disjunction is by
% saving the sequence number to the disj frame, therefore we do not
% need to save any protected regions.
% XXX In computing SnapshotRegionVars,
% we should intersect LiveRegionVars with the set of region variables
% whose regions (the regions themselves, not their variables) are live
% at the starts of some later disjuncts (i.e. aren't used only in the
% first disjunct). We don't yet gather this information.
SnapshotRegionVars = LiveRegionVars,
SnapshotRegionVarList = set_of_var.to_sorted_list(SnapshotRegionVars),
list.length(SnapshotRegionVarList, NumSnapshotRegionVars),
get_globals(!.CI, Globals),
globals.lookup_int_option(Globals, size_region_disj_fixed,
FixedSize),
globals.lookup_int_option(Globals, size_region_disj_snapshot,
SnapshotSize),
FrameSize = FixedSize + SnapshotSize * NumSnapshotRegionVars,
Items = list.duplicate(FrameSize, slot_region_disj),
acquire_several_temp_slots(Items, non_persistent_temp_slot,
_StackVars, MainStackId, FirstSlotNum, LastSlotNum, !CI),
EmbeddedStackFrame = embedded_stack_frame_id(MainStackId,
FirstSlotNum, LastSlotNum),
FirstNonFixedAddr =
first_nonfixed_embedded_slot_addr(EmbeddedStackFrame, FixedSize),
acquire_reg(reg_r, SnapshotNumRegLval, !CI),
acquire_reg(reg_r, AddrRegLval, !CI),
PushInitCode = from_list([
llds_instr(
push_region_frame(region_stack_disj, EmbeddedStackFrame),
"Save stack pointer of embedded region nondet stack"),
llds_instr(
assign(SnapshotNumRegLval, const(llconst_int(0))),
"Initialize number of snapshot_infos"),
llds_instr(
assign(AddrRegLval, FirstNonFixedAddr),
"Initialize pointer to nonfixed part of embedded frame")
]),
disj_alloc_snapshot_regions(SnapshotNumRegLval, AddrRegLval,
EmbeddedStackFrame, SnapshotRegionVarList, SnapshotRegionCode,
!CI),
SetCode = singleton(
llds_instr(
region_set_fixed_slot(region_set_disj_num_snapshots,
EmbeddedStackFrame, lval(SnapshotNumRegLval)),
"Store the number of snapshot_infos")
),
release_reg(SnapshotNumRegLval, !CI),
release_reg(AddrRegLval, !CI),
BeforeEnterCode = PushInitCode ++ SnapshotRegionCode ++ SetCode,
LaterCode = singleton(
llds_instr(
use_and_maybe_pop_region_frame(region_disj_later,
EmbeddedStackFrame),
"region enter later disjunct")
),
LastCode = singleton(
llds_instr(
use_and_maybe_pop_region_frame(region_disj_last,
EmbeddedStackFrame),
"region enter last disjunct")
)
).
:- pred maybe_create_disj_region_frame_semi(add_region_ops::in,
set_of_progvar::in, set_of_progvar::in, llds_code::out, llds_code::out,
llds_code::out, list(lval)::out, commit_disj_region_cleanup::out,
code_info::in, code_info::out) is det.
maybe_create_disj_region_frame_semi(DisjRegionOps, DisjRemovedRegionVars,
DisjAllocRegionVars, BeforeEnterCode, LaterCode, LastCode, StackVars,
RegionCommitDisjCleanup, !CI) :-
(
DisjRegionOps = do_not_add_region_ops,
BeforeEnterCode = empty,
LaterCode = empty,
LastCode = empty,
StackVars = [],
RegionCommitDisjCleanup = no_commit_disj_region_cleanup
;
DisjRegionOps = add_region_ops,
% For a semidet disjunction, we need to save the protected regions,
% i.e., those removed in the scope of the semidet disjunction,
% into the disj frame so that if a non-last disjunct succeeds,
% we can reclaim such regions. We will only save ones which
% are currently not protected (this is checked at runtime).
ProtectRegionVars = DisjRemovedRegionVars,
% XXX In computing SnapshotRegionVars,
% we should intersect DisjAllocRegionVars with the set of region
% variables whose regions (the regions themselves, not their variables)
% are live at the starts of some later disjuncts (i.e. aren't used only
% in the first disjunct). We don't yet gather this information.
%
SnapshotRegionVars = DisjAllocRegionVars,
ProtectRegionVarList = set_of_var.to_sorted_list(ProtectRegionVars),
SnapshotRegionVarList = set_of_var.to_sorted_list(SnapshotRegionVars),
list.length(ProtectRegionVarList, NumProtectRegionVars),
list.length(SnapshotRegionVarList, NumSnapshotRegionVars),
get_globals(!.CI, Globals),
globals.lookup_int_option(Globals, size_region_disj_fixed,
FixedSize),
globals.lookup_int_option(Globals, size_region_semi_disj_protect,
ProtectSize),
globals.lookup_int_option(Globals, size_region_disj_snapshot,
SnapshotSize),
FrameSize = FixedSize
+ ProtectSize * NumProtectRegionVars
+ SnapshotSize * NumSnapshotRegionVars,
Items = list.duplicate(FrameSize, slot_region_disj),
acquire_several_temp_slots(Items, non_persistent_temp_slot,
StackVars, MainStackId, FirstSlotNum, LastSlotNum, !CI),
EmbeddedStackFrame = embedded_stack_frame_id(MainStackId,
FirstSlotNum, LastSlotNum),
FirstNonFixedAddr =
first_nonfixed_embedded_slot_addr(EmbeddedStackFrame, FixedSize),
acquire_reg(reg_r, ProtectNumRegLval, !CI),
acquire_reg(reg_r, SnapshotNumRegLval, !CI),
acquire_reg(reg_r, AddrRegLval, !CI),
PushInitCode = from_list([
llds_instr(
push_region_frame(region_stack_disj, EmbeddedStackFrame),
"Save stack pointer of embedded region nondet stack"),
llds_instr(
assign(ProtectNumRegLval, const(llconst_int(0))),
"Initialize number of protect_infos"),
llds_instr(
assign(SnapshotNumRegLval, const(llconst_int(0))),
"Initialize number of snapshot_infos"),
llds_instr(
assign(AddrRegLval, FirstNonFixedAddr),
"Initialize pointer to nonfixed part of embedded frame")
]),
disj_protect_regions(ProtectNumRegLval, AddrRegLval,
EmbeddedStackFrame, ProtectRegionVarList, ProtectRegionCode,
!CI),
disj_alloc_snapshot_regions(SnapshotNumRegLval, AddrRegLval,
EmbeddedStackFrame, SnapshotRegionVarList, SnapshotRegionCode,
!CI),
SetCode = from_list([
llds_instr(
region_set_fixed_slot(region_set_disj_num_protects,
EmbeddedStackFrame, lval(ProtectNumRegLval)),
"Store the number of protect_infos"),
llds_instr(
region_set_fixed_slot(region_set_disj_num_snapshots,
EmbeddedStackFrame, lval(SnapshotNumRegLval)),
"Store the number of snapshot_infos")
]),
release_reg(ProtectNumRegLval, !CI),
release_reg(SnapshotNumRegLval, !CI),
release_reg(AddrRegLval, !CI),
BeforeEnterCode = PushInitCode ++ ProtectRegionCode ++
SnapshotRegionCode ++ SetCode,
LaterCode = singleton(
llds_instr(
use_and_maybe_pop_region_frame(region_disj_later,
EmbeddedStackFrame),
"region enter later disjunct")
),
LastCode = singleton(
llds_instr(
use_and_maybe_pop_region_frame(region_disj_last,
EmbeddedStackFrame),
"region enter last disjunct")
),
get_next_label(CleanupLabel, !CI),
CleanupCode = singleton(
llds_instr(
use_and_maybe_pop_region_frame(
region_disj_nonlast_semi_commit, EmbeddedStackFrame),
"region cleanup commit for nonlast disjunct")
),
RegionCommitDisjCleanup = commit_disj_region_cleanup(CleanupLabel,
CleanupCode)
).
:- pred disj_protect_regions(lval::in, lval::in, embedded_stack_frame_id::in,
list(prog_var)::in, llds_code::out, code_info::in, code_info::out) is det.
disj_protect_regions(_, _, _, [], empty, !CI).
disj_protect_regions(NumLval, AddrLval, EmbeddedStackFrame,
[RegionVar | RegionVars], Code ++ Codes, !CI) :-
produce_variable(RegionVar, ProduceVarCode, RegionVarRval, !CI),
SaveCode = singleton(
llds_instr(
region_fill_frame(region_fill_semi_disj_protect,
EmbeddedStackFrame, RegionVarRval, NumLval, AddrLval),
"disj protect the region if needed")
),
Code = ProduceVarCode ++ SaveCode,
disj_protect_regions(NumLval, AddrLval, EmbeddedStackFrame,
RegionVars, Codes, !CI).
:- pred disj_alloc_snapshot_regions(lval::in, lval::in,
embedded_stack_frame_id::in, list(prog_var)::in, llds_code::out,
code_info::in, code_info::out) is det.
disj_alloc_snapshot_regions(_, _, _, [], empty, !CI).
disj_alloc_snapshot_regions(NumLval, AddrLval, EmbeddedStackFrame,
[RegionVar | RegionVars], Code ++ Codes, !CI) :-
produce_variable(RegionVar, ProduceVarCode, RegionVarRval, !CI),
SaveCode = singleton(
llds_instr(
region_fill_frame(region_fill_disj_snapshot,
EmbeddedStackFrame, RegionVarRval, NumLval, AddrLval),
"take alloc snapshot of the region")
),
Code = ProduceVarCode ++ SaveCode,
disj_alloc_snapshot_regions(NumLval, AddrLval, EmbeddedStackFrame,
RegionVars, Codes, !CI).
%-----------------------------------------------------------------------------%
:- end_module ll_backend.disj_gen.
%-----------------------------------------------------------------------------%