Files
mercury/compiler/lookup_util.m
Zoltan Somogyi 168f531867 Add new fields to the goal_info structure for region based memory management.
Estimated hours taken: 4
Branches: main

Add new fields to the goal_info structure for region based memory management.
The fields are currently unused, but (a) Quan will add the code to fill them
in, and then (b) I will modify the code generator to use the filled in fields.

compiler/hlds_goal.m:
	Make the change described above.

	Group all the procedures that access goal_info components together.
	Some of the getters were predicates while some were functions, so
	this diff changes them all to be functions. (The setters remain
	predicates.)

compiler/*.m:
	Trivial changes to conform to the change in hlds_goal.m.

	In simplify.m, break up a huge (800+ line) predicate into smaller
	pieces.
2007-08-07 07:10:09 +00:00

241 lines
9.6 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1996-2007 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: lookup_util.m.
% Author: zs.
%
% Utility predicates used by both lookup_switch.m and disj.m. These utility
% predicates help in the implementation of switches and disjunctions in which
% the code of each arm consists only of looking up the values of the output
% variables in a table.
%
%-----------------------------------------------------------------------------%
:- module ll_backend.lookup_util.
:- interface.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_llds.
:- import_module ll_backend.code_info.
:- import_module ll_backend.llds.
:- import_module parse_tree.prog_data.
:- import_module list.
:- import_module maybe.
:- import_module set.
% Figure out which variables are bound in the goal.
% We do this by using the current instmap and the instmap delta in the
% goal info to work out which variables are [further] bound by the goal.
%
:- pred figure_out_output_vars(code_info::in, hlds_goal_info::in,
list(prog_var)::out) is det.
% Is the input goal a conjunction of unifications?
%
:- pred goal_is_conj_of_unify(hlds_goal::in) is semidet.
% Run goal_is_conj_of_unify on each goal in the list.
%
:- pred all_disjuncts_are_conj_of_unify(list(hlds_goal)::in) is semidet.
% To figure out if the outputs are constants, we
%
% - check whether the determinism and structure of the goal are right,
% - generate code for the case,
% - check to see if each of the output vars is a constant,
% - check to see that no actual code was generated.
%
% For large goals, step 2 can be expensive. Step 1 is a cheap way of
% finding out whether steps 3 and 4 would fail without first running
% step 2. Therefore the caller should call goal_is_conj_of_unify (which
% does step 1) on Goal before calling generate_constants_for_arm (which
% does steps 2, 3 and 4).
%
:- pred generate_constants_for_arm(hlds_goal::in, list(prog_var)::in,
abs_store_map::in, list(rval)::out, branch_end::in, branch_end::out,
set(prog_var)::out, code_info::in, code_info::out) is semidet.
:- pred generate_constants_for_disjuncts(list(hlds_goal)::in,
list(prog_var)::in, abs_store_map::in, list(list(rval))::out,
branch_end::in, branch_end::out, maybe(set(prog_var))::out,
code_info::in, code_info::out) is semidet.
:- pred set_liveness_and_end_branch(abs_store_map::in, branch_end::in,
set(prog_var)::in, code_tree::out, code_info::in, code_info::out) is det.
:- pred generate_offset_assigns(list(prog_var)::in, int::in, lval::in,
code_info::in, code_info::out) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds.mode_util.
:- import_module hlds.code_model.
:- import_module hlds.instmap.
:- import_module libs.compiler_util.
:- import_module libs.globals.
:- import_module libs.tree.
:- import_module ll_backend.code_gen.
:- import_module ll_backend.exprn_aux.
:- import_module bool.
:- import_module int.
:- import_module pair.
:- import_module solutions.
figure_out_output_vars(CI, GoalInfo, OutVars) :-
InstMapDelta = goal_info_get_instmap_delta(GoalInfo),
( instmap_delta_is_unreachable(InstMapDelta) ->
OutVars = []
;
code_info.get_instmap(CI, CurrentInstMap),
code_info.get_module_info(CI, ModuleInfo),
instmap_delta_changed_vars(InstMapDelta, ChangedVars),
instmap.apply_instmap_delta(CurrentInstMap, InstMapDelta,
InstMapAfter),
Lambda = (pred(Var::out) is nondet :-
% If a variable has a final inst, then it changed
% instantiatedness during the switch.
set.member(Var, ChangedVars),
instmap.lookup_var(CurrentInstMap, Var, Initial),
instmap.lookup_var(InstMapAfter, Var, Final),
mode_is_output(ModuleInfo, (Initial -> Final))
),
solutions.solutions(Lambda, OutVars)
).
goal_is_conj_of_unify(Goal) :-
Goal = hlds_goal(_GoalExpr, GoalInfo),
CodeModel = goal_info_get_code_model(GoalInfo),
CodeModel = model_det,
goal_to_conj_list(Goal, Conj),
only_constant_goals(Conj).
all_disjuncts_are_conj_of_unify([]).
all_disjuncts_are_conj_of_unify([Disjunct | Disjuncts]) :-
goal_is_conj_of_unify(Disjunct),
all_disjuncts_are_conj_of_unify(Disjuncts).
:- pred only_constant_goals(list(hlds_goal)::in) is semidet.
only_constant_goals([]).
only_constant_goals([Goal | Goals]) :-
Goal = hlds_goal(GoalExpr, _),
% We could allow calls as well. Some procedures have an output inst
% that fixes the value of the output variable, which is thus a constant.
% However, calls to such procedures should have been inlined by now.
GoalExpr = unify(_, _, _, _, _),
only_constant_goals(Goals).
generate_constants_for_arm(Goal, Vars, StoreMap, !MaybeEnd, CaseRvals,
Liveness, !CI) :-
do_generate_constants_for_arm(Goal, Vars, StoreMap, no, !MaybeEnd,
CaseRvals, Liveness, !CI).
:- pred do_generate_constants_for_arm(hlds_goal::in, list(prog_var)::in,
abs_store_map::in, bool::in, list(rval)::out,
branch_end::in, branch_end::out, set(prog_var)::out,
code_info::in, code_info::out) is semidet.
do_generate_constants_for_arm(Goal, Vars, StoreMap, SetToUnknown, CaseRvals,
!MaybeEnd, Liveness, !CI) :-
code_info.remember_position(!.CI, BranchStart),
Goal = hlds_goal(_GoalExpr, GoalInfo),
CodeModel = goal_info_get_code_model(GoalInfo),
code_gen.generate_goal(CodeModel, Goal, Code, !CI),
tree.tree_of_lists_is_empty(Code),
code_info.get_forward_live_vars(!.CI, Liveness),
code_info.get_globals(!.CI, Globals),
globals.get_options(Globals, Options),
exprn_aux.init_exprn_opts(Options, ExprnOpts),
get_arm_rvals(Vars, CaseRvals, !CI, ExprnOpts),
(
SetToUnknown = no
;
SetToUnknown = yes,
code_info.set_resume_point_to_unknown(!CI)
),
% EndCode code may contain instructions that place Vars in the locations
% dictated by StoreMap, and thus does not have to be empty. (The array
% lookup code will put those variables in those locations directly.)
code_info.generate_branch_end(StoreMap, !MaybeEnd, _EndCode, !CI),
code_info.reset_to_position(BranchStart, !CI).
generate_constants_for_disjuncts([], _Vars, _StoreMap, [], !MaybeEnd,
no, !CI).
generate_constants_for_disjuncts([Disjunct0 | Disjuncts], Vars, StoreMap,
[Soln | Solns], !MaybeEnd, yes(Liveness), !CI) :-
% The pre_goal_update sanity check insists on no_resume_point, to ensure
% that all resume points have been handled by surrounding code.
Disjunct0 = hlds_goal(DisjunctGoalExpr, DisjunctGoalInfo0),
goal_info_set_resume_point(no_resume_point,
DisjunctGoalInfo0, DisjunctGoalInfo),
Disjunct = hlds_goal(DisjunctGoalExpr, DisjunctGoalInfo),
do_generate_constants_for_arm(Disjunct, Vars, StoreMap, yes, Soln,
!MaybeEnd, Liveness, !CI),
generate_constants_for_disjuncts(Disjuncts, Vars, StoreMap, Solns,
!MaybeEnd, _, !CI).
%---------------------------------------------------------------------------%
:- pred get_arm_rvals(list(prog_var)::in, list(rval)::out,
code_info::in, code_info::out, exprn_opts::in) is semidet.
get_arm_rvals([], [], !CI, _ExprnOpts).
get_arm_rvals([Var | Vars], [Rval | Rvals], !CI, ExprnOpts) :-
code_info.produce_variable(Var, Code, Rval, !CI),
tree.tree_of_lists_is_empty(Code),
rval_is_constant(Rval, ExprnOpts),
get_arm_rvals(Vars, Rvals, !CI, ExprnOpts).
% rval_is_constant(Rval, ExprnOpts) is true iff Rval is a constant.
% This depends on the options governing nonlocal gotos, asm labels enabled
% and static ground terms, etc.
%
:- pred rval_is_constant(rval::in, exprn_opts::in) is semidet.
rval_is_constant(const(Const), ExprnOpts) :-
exprn_aux.const_is_constant(Const, ExprnOpts, yes).
rval_is_constant(unop(_, Exprn), ExprnOpts) :-
rval_is_constant(Exprn, ExprnOpts).
rval_is_constant(binop(_, Exprn0, Exprn1), ExprnOpts) :-
rval_is_constant(Exprn0, ExprnOpts),
rval_is_constant(Exprn1, ExprnOpts).
rval_is_constant(mkword(_, Exprn0), ExprnOpts) :-
rval_is_constant(Exprn0, ExprnOpts).
%---------------------------------------------------------------------------%
set_liveness_and_end_branch(StoreMap, MaybeEnd0, Liveness, BranchEndCode,
!CI) :-
% We keep track of what variables are supposed to be live at the end
% of cases. We have to do this explicitly because generating a `fail' slot
% last would yield the wrong liveness.
code_info.set_forward_live_vars(Liveness, !CI),
code_info.generate_branch_end(StoreMap, MaybeEnd0, _MaybeEnd,
BranchEndCode, !CI).
generate_offset_assigns([], _, _, !CI).
generate_offset_assigns([Var | Vars], Offset, BaseReg, !CI) :-
LookupLval = field(yes(0), lval(BaseReg), const(llconst_int(Offset))),
code_info.assign_lval_to_var(Var, LookupLval, Code, !CI),
expect(tree.is_empty(Code), this_file,
"generate_offset_assigns: nonempty code"),
generate_offset_assigns(Vars, Offset + 1, BaseReg, !CI).
%---------------------------------------------------------------------------%
:- func this_file = string.
this_file = "lookup_util.m".
%---------------------------------------------------------------------------%