Files
mercury/compiler/add_heap_ops.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

375 lines
15 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2000-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: add_heap_ops.m.
% Author: fjh.
%
% This module is an HLDS-to-HLDS transformation that inserts code to
% handle heap reclamation on backtracking, by saving and restoring
% the values of the heap pointer.
% The transformation involves adding calls to impure predicates defined in
% library/private_builtin.m, which in turn call the MR_mark_hp() and
% MR_restore_hp() macros defined in runtime/mercury_heap.h.
%
% This pass is currently only used for the MLDS back-end. For historical
% reasons, the LLDS back-end inserts the heap operations as it is generating
% LLDS code, rather than via an HLDS to HLDS transformation.
%
% This module is very similar to add_trail_ops.m.
%
%-----------------------------------------------------------------------------%
%
% XXX check goal_infos for correctness
%
%-----------------------------------------------------------------------------%
:- module ml_backend.add_heap_ops.
:- interface.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- pred add_heap_ops(module_info::in, proc_info::in, proc_info::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.code_model.
:- import_module hlds.goal_form.
:- import_module hlds.goal_util.
:- import_module hlds.hlds_goal.
:- import_module hlds.instmap.
:- import_module hlds.pred_table.
:- import_module hlds.quantification.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.builtin_lib_types.
:- import_module parse_tree.prog_data.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module require.
:- import_module term.
:- import_module varset.
%-----------------------------------------------------------------------------%
% As we traverse the goal, we add new variables to hold the saved values
% of the heap pointer. So we need the varset and the vartypes map to record
% the names and types of the new variables.
%
% We also keep the module_info around, so that we can use the predicate
% table that it contains to lookup the pred_ids for the builtin procedures
% that we insert calls to. We do not update the module_info as we are
% traversing the goal.
%
:- type heap_ops_info
---> heap_ops_info(
heap_varset :: prog_varset,
heap_var_types :: vartypes,
heap_module_info :: module_info
).
add_heap_ops(ModuleInfo0, !Proc) :-
proc_info_get_goal(!.Proc, Goal0),
proc_info_get_varset(!.Proc, VarSet0),
proc_info_get_vartypes(!.Proc, VarTypes0),
TrailOpsInfo0 = heap_ops_info(VarSet0, VarTypes0, ModuleInfo0),
goal_add_heap_ops(Goal0, Goal, TrailOpsInfo0, TrailOpsInfo),
TrailOpsInfo = heap_ops_info(VarSet, VarTypes, _),
proc_info_set_goal(Goal, !Proc),
proc_info_set_varset(VarSet, !Proc),
proc_info_set_vartypes(VarTypes, !Proc),
% The code below does not maintain the non-local variables,
% so we need to requantify.
% XXX it would be more efficient to maintain them rather than recomputing
% them every time.
requantify_proc_general(ordinary_nonlocals_no_lambda, !Proc).
:- pred goal_add_heap_ops(hlds_goal::in, hlds_goal::out,
heap_ops_info::in, heap_ops_info::out) is det.
goal_add_heap_ops(Goal0, Goal, !Info) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo),
goal_expr_add_heap_ops(GoalExpr0, GoalInfo, Goal, !Info).
:- pred goal_expr_add_heap_ops(hlds_goal_expr::in, hlds_goal_info::in,
hlds_goal::out, heap_ops_info::in, heap_ops_info::out) is det.
goal_expr_add_heap_ops(GoalExpr0, GoalInfo0, Goal, !Info) :-
(
GoalExpr0 = conj(ConjType, Goals0),
conj_add_heap_ops(Goals0, Goals, !Info),
GoalExpr = conj(ConjType, Goals),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = disj(Disjuncts0),
(
Disjuncts0 = [],
GoalExpr = GoalExpr0
;
Disjuncts0 = [FirstDisjunct0 | _],
Context = goal_info_get_context(GoalInfo0),
CodeModel = goal_info_get_code_model(GoalInfo0),
% If necessary, save the heap pointer so that we can restore it
% on back-tracking. We don't need to do this here if it is a
% model_det or model_semi disjunction and the first disjunct
% won't allocate any heap -- in that case, we delay saving the heap
% pointer until just before the first disjunct that might allocate
% heap.
(
( CodeModel = model_non
; goal_may_allocate_heap(FirstDisjunct0)
)
->
new_saved_hp_var(SavedHeapPointerVar, !Info),
gen_mark_hp(SavedHeapPointerVar, Context, MarkHeapPointerGoal,
!Info),
disj_add_heap_ops(Disjuncts0, Disjuncts, is_first_disjunct,
yes(SavedHeapPointerVar), GoalInfo0, !Info),
DisjGoalExpr = disj(Disjuncts),
DisjGoal = hlds_goal(DisjGoalExpr, GoalInfo0),
ConjGoalExpr = conj(plain_conj,
[MarkHeapPointerGoal, DisjGoal]),
ConjGoal = hlds_goal(ConjGoalExpr, GoalInfo0),
Purity0 = goal_info_get_purity(GoalInfo0),
GoalExpr = scope(promise_purity(Purity0), ConjGoal)
;
disj_add_heap_ops(Disjuncts0, Disjuncts, is_first_disjunct,
no, GoalInfo0, !Info),
GoalExpr = disj(Disjuncts)
)
),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
cases_add_heap_ops(Cases0, Cases, !Info),
GoalExpr = switch(Var, CanFail, Cases),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = negation(InnerGoal),
OuterGoalInfo = GoalInfo0,
% We handle negations by converting them into if-then-elses:
% not(G) ===> (if G then fail else true)
Context = goal_info_get_context(OuterGoalInfo),
InnerGoal = hlds_goal(_, InnerGoalInfo),
Determinism = goal_info_get_determinism(InnerGoalInfo),
determinism_components(Determinism, _CanFail, NumSolns),
True = true_goal_with_context(Context),
Fail = fail_goal_with_context(Context),
ModuleInfo = !.Info ^ heap_module_info,
(
NumSolns = at_most_zero,
% The "then" part of the if-then-else will be unreachable, but to
% preserve the invariants that the MLDS back-end relies on, we
% need to make sure that it can't fail. So we use a call to
% `private_builtin.unused' (which will call error/1) rather than
% `fail' for the "then" part.
heap_generate_call("unused", detism_det, purity_pure, [],
instmap_delta_bind_no_var, ModuleInfo, Context, ThenGoal)
;
( NumSolns = at_most_one
; NumSolns = at_most_many
; NumSolns = at_most_many_cc
),
ThenGoal = Fail
),
NewOuterGoal = if_then_else([], InnerGoal, ThenGoal, True),
goal_expr_add_heap_ops(NewOuterGoal, OuterGoalInfo, Goal, !Info)
;
GoalExpr0 = scope(Reason, SubGoal0),
( Reason = from_ground_term(_, from_ground_term_construct) ->
SubGoal = SubGoal0
;
goal_add_heap_ops(SubGoal0, SubGoal, !Info)
),
GoalExpr = scope(Reason, SubGoal),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = if_then_else(Vars, CondGoal0, ThenGoal0, ElseGoal0),
goal_add_heap_ops(CondGoal0, CondGoal, !Info),
goal_add_heap_ops(ThenGoal0, ThenGoal, !Info),
goal_add_heap_ops(ElseGoal0, ElseGoal1, !Info),
% If the condition can allocate heap space, save the heap pointer
% so that we can restore it if the condition fails.
( goal_may_allocate_heap(CondGoal0) ->
new_saved_hp_var(SavedHeapPointerVar, !Info),
Context = goal_info_get_context(GoalInfo0),
gen_mark_hp(SavedHeapPointerVar, Context, MarkHeapPointerGoal,
!Info),
% Generate code to restore the heap pointer, and insert that code
% at the start of the Else branch.
gen_restore_hp(SavedHeapPointerVar, Context,
RestoreHeapPointerGoal, !Info),
ElseGoal1 = hlds_goal(_, ElseGoal1Info),
ElseGoalExpr = conj(plain_conj,
[RestoreHeapPointerGoal, ElseGoal1]),
ElseGoal = hlds_goal(ElseGoalExpr, ElseGoal1Info),
ITEGoalExpr = if_then_else(Vars, CondGoal, ThenGoal, ElseGoal),
ITEGoal = hlds_goal(ITEGoalExpr, GoalInfo0),
ConjGoalExpr = conj(plain_conj, [MarkHeapPointerGoal, ITEGoal]),
ConjGoal = hlds_goal(ConjGoalExpr, GoalInfo0),
Purity0 = goal_info_get_purity(GoalInfo0),
GoalExpr = scope(promise_purity(Purity0), ConjGoal)
;
GoalExpr = if_then_else(Vars, CondGoal, ThenGoal, ElseGoal1)
),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
( GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = unify(_, _, _, _, _)
),
Goal = hlds_goal(GoalExpr0, GoalInfo0)
;
GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _),
Goal = hlds_goal(GoalExpr0, GoalInfo0)
;
GoalExpr0 = shorthand(_),
% These should have been expanded out by now.
unexpected($module, $pred, "shorthand")
).
:- pred conj_add_heap_ops(hlds_goals::in, hlds_goals::out,
heap_ops_info::in, heap_ops_info::out) is det.
conj_add_heap_ops(Goals0, Goals, !Info) :-
list.map_foldl(goal_add_heap_ops, Goals0, Goals, !Info).
:- pred disj_add_heap_ops(list(hlds_goal)::in, list(hlds_goal)::out,
is_first_disjunct::in, maybe(prog_var)::in, hlds_goal_info::in,
heap_ops_info::in, heap_ops_info::out) is det.
disj_add_heap_ops([], [], _, _, _, !Info).
disj_add_heap_ops([Goal0 | Goals0], DisjGoals, IsFirstBranch,
MaybeSavedHeapPointerVar, DisjGoalInfo, !Info) :-
goal_add_heap_ops(Goal0, Goal1, !Info),
Goal1 = hlds_goal(_, GoalInfo),
Context = goal_info_get_context(GoalInfo),
% If needed, reset the heap pointer before executing the goal,
% to reclaim heap space allocated in earlier branches.
(
IsFirstBranch = is_not_first_disjunct,
MaybeSavedHeapPointerVar = yes(SavedHeapPointerVar0)
->
gen_restore_hp(SavedHeapPointerVar0, Context, RestoreHeapPointerGoal,
!Info),
conj_list_to_goal([RestoreHeapPointerGoal, Goal1], GoalInfo, Goal)
;
Goal = Goal1
),
% Save the heap pointer,
% - if we haven't already done so,
% - if this disjunct might allocate heap space, and
% - if a next disjunct exists to give us a chance to recover
% that heap space.
(
MaybeSavedHeapPointerVar = no,
goal_may_allocate_heap(Goal),
Goals0 = [_ | _]
->
% Generate code to save the heap pointer.
new_saved_hp_var(SavedHeapPointerVar, !Info),
gen_mark_hp(SavedHeapPointerVar, Context, MarkHeapPointerGoal, !Info),
% Recursively handle the remaining disjuncts.
disj_add_heap_ops(Goals0, Goals1, is_not_first_disjunct,
yes(SavedHeapPointerVar), DisjGoalInfo, !Info),
% Put this disjunct and the remaining disjuncts in a nested
% disjunction, so that the heap pointer variable can scope over
% these disjuncts. (This wouldn't work if Goals0 were [].)
InnerDisjGoalExpr = disj([Goal | Goals1]),
InnerDisjGoal = hlds_goal(InnerDisjGoalExpr, DisjGoalInfo),
ConjGoalExpr = conj(plain_conj, [MarkHeapPointerGoal, InnerDisjGoal]),
ConjGoal = hlds_goal(ConjGoalExpr, DisjGoalInfo),
Purity = goal_info_get_purity(DisjGoalInfo),
ScopeGoalExpr = scope(promise_purity(Purity), ConjGoal),
ScopeGoal = hlds_goal(ScopeGoalExpr, DisjGoalInfo),
DisjGoals = [ScopeGoal]
;
% Just recursively handle the remaining disjuncts.
disj_add_heap_ops(Goals0, Goals, is_not_first_disjunct,
MaybeSavedHeapPointerVar, DisjGoalInfo, !Info),
DisjGoals = [Goal | Goals]
).
:- pred cases_add_heap_ops(list(case)::in, list(case)::out,
heap_ops_info::in, heap_ops_info::out) is det.
cases_add_heap_ops([], [], !Info).
cases_add_heap_ops([Case0 | Cases0], [Case | Cases], !Info) :-
Case0 = case(MainConsId, OtherConsIds, Goal0),
goal_add_heap_ops(Goal0, Goal, !Info),
Case = case(MainConsId, OtherConsIds, Goal),
cases_add_heap_ops(Cases0, Cases, !Info).
%-----------------------------------------------------------------------------%
:- pred gen_mark_hp(prog_var::in, prog_context::in, hlds_goal::out,
heap_ops_info::in, heap_ops_info::out) is det.
gen_mark_hp(SavedHeapPointerVar, Context, MarkHeapPointerGoal, !Info) :-
heap_generate_call("mark_hp", detism_det, purity_impure,
[SavedHeapPointerVar], instmap_delta_bind_var(SavedHeapPointerVar),
!.Info ^ heap_module_info, Context, MarkHeapPointerGoal).
:- pred gen_restore_hp(prog_var::in, prog_context::in, hlds_goal::out,
heap_ops_info::in, heap_ops_info::out) is det.
gen_restore_hp(SavedHeapPointerVar, Context, RestoreHeapPointerGoal, !Info) :-
heap_generate_call("restore_hp", detism_det, purity_impure,
[SavedHeapPointerVar], instmap_delta_bind_no_var,
!.Info ^ heap_module_info, Context, RestoreHeapPointerGoal).
:- func ground_inst = mer_inst.
ground_inst = ground(unique, none).
%-----------------------------------------------------------------------------%
:- pred new_saved_hp_var(prog_var::out,
heap_ops_info::in, heap_ops_info::out) is det.
new_saved_hp_var(Var, !Info) :-
new_var("HeapPointer", heap_pointer_type, Var, !Info).
:- pred new_var(string::in, mer_type::in, prog_var::out,
heap_ops_info::in, heap_ops_info::out) is det.
new_var(Name, Type, Var, !Info) :-
VarSet0 = !.Info ^ heap_varset,
VarTypes0 = !.Info ^ heap_var_types,
varset.new_named_var(Name, Var, VarSet0, VarSet),
add_var_type(Var, Type, VarTypes0, VarTypes),
!Info ^ heap_varset := VarSet,
!Info ^ heap_var_types := VarTypes.
%-----------------------------------------------------------------------------%
:- pred heap_generate_call(string::in, determinism::in, purity::in,
list(prog_var)::in, instmap_delta::in, module_info::in,
term.context::in, hlds_goal::out) is det.
heap_generate_call(PredName, Detism, Purity, Args, InstMapDelta, ModuleInfo,
Context, CallGoal) :-
goal_util.generate_simple_call(mercury_private_builtin_module, PredName,
pf_predicate, only_mode, Detism, Purity, Args, [], InstMapDelta,
ModuleInfo, Context, CallGoal).
%-----------------------------------------------------------------------------%
:- end_module ml_backend.add_heap_ops.
%-----------------------------------------------------------------------------%