mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 23:05:21 +00:00
Estimated hours taken: 3 compiler/quantification.m: Add an alternative method of computing the non-locals for goals which do structure reuse, This is done to avoid unnecessary field extractions and variable saves for arguments of reused terms. The code-gen non-locals are the same as the ordinary non-locals except that arguments of a reconstruction which are taken from the reused cell are not included in the non-locals set. Mode analysis still uses the full non-locals set. Add a field to type quant_info to record which set of non-locals is being computed. Use record syntax for the access predicates. compiler/hlds_goal.m: Add access predicates to extract and update the code_gen_nonlocals in a goal_info. When structure reuse is not performed, the nonlocals and the code-gen nonlocals will always be the same. Use record syntax for hlds_goal_info access predicates. compiler/goal_util.m: Add a predicate `goal_contains_reconstruction', which succeeds if the goal does any structure reuse. This will always fail on the main branch. Add missing disjuncts for par_conj goals in `goal_calls' and `goal_calls_pred_id'. compiler/liveness.m: compiler/live_vars.m: compiler/ml_code_gen.m: compiler/par_conj_gen.m: Use the code_gen_nonlocals rather than the ordinary nonlocals. configure.in: Add a test for record syntax.
1189 lines
46 KiB
Mathematica
1189 lines
46 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2000 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: liveness.m
|
|
%
|
|
% Main authors: conway, zs, trd.
|
|
%
|
|
% This module traverses the goal for each procedure, and adds
|
|
% liveness annotations to the goal_info for each sub-goal.
|
|
% These annotations are the pre-birth set, the post-birth set,
|
|
% the pre-death set, the post-death set, and the resume_point field.
|
|
%
|
|
% Because it recomputes each of these annotations from scratch,
|
|
% it should be safe to call this module multiple times
|
|
% (although this has not been tested yet).
|
|
%
|
|
% Note - the concept of `liveness' here is different to that used in
|
|
% mode analysis. Mode analysis is concerned with the liveness of what
|
|
% is *pointed* to by a variable, for the purpose of avoiding and/or keeping
|
|
% track of aliasing and for structure re-use optimization, whereas here
|
|
% we are concerned with the liveness of the variable itself, for the
|
|
% purposes of optimizing stack slot and register usage.
|
|
% Variables have a lifetime: each variable is born, gets used, and then dies.
|
|
% To minimize stack slot and register usage, the birth should be
|
|
% as late as possible (but before the first possible use), and the
|
|
% death should be as early as possible (but after the last possible use).
|
|
%
|
|
% We compute liveness related information in three distinct passes.
|
|
%
|
|
% The first pass, detect_liveness_in_goal, finds the first value-giving
|
|
% occurrence of each variable on each computation path. Goals containing
|
|
% the first such occurrence of a variable include that variable in their
|
|
% pre-birth set. In branched structures, branches whose endpoint is not
|
|
% reachable include a post-birth set listing the variables that should
|
|
% have been born in that branch but haven't. Variables that shouldn't have
|
|
% been born but have been (in computation paths that cannot succeed)
|
|
% are included in the post-death set of the goal concerned.
|
|
%
|
|
% The second pass, detect_deadness_in_goal, finds the last occurrence
|
|
% of each variable on each computation path. Goals containing the last
|
|
% occurrence of a variable include that variable in their post-death
|
|
% set. In branched structures, branches in which a variable is not
|
|
% used at all include a pre-death set listing the variables that
|
|
% have died in parallel branches.
|
|
%
|
|
% The third pass, detect_resume_points_in_goal, finds goals that
|
|
% establish resume points and attaches to them a resume_point
|
|
% annotation listing the variables that may be referenced by the
|
|
% code at that resume point as well as the nature of the required
|
|
% entry labels.
|
|
%
|
|
% Typeinfo liveness calculation notes:
|
|
%
|
|
% When using accurate gc or execution tracing, liveness is computed
|
|
% slightly differently. The runtime system needs access to the
|
|
% typeinfo variables of any variable that is live at a continuation.
|
|
%
|
|
% Hence, the invariant needed for alternate liveness calculation:
|
|
% a variable holding a typeinfo must be live at any continuation
|
|
% where any variable whose type is described (in whole or in part)
|
|
% by that typeinfo is live.
|
|
%
|
|
% Typeinfos are introduced as either one of the head variables, or a new
|
|
% variable created by a goal in the procedure. If introduced as a head
|
|
% variable, initial_liveness will add it to the initial live set of
|
|
% variables -- no variable could be introduced earlier than the start of
|
|
% the goal. If introduced by a goal in the procedure, that goal must
|
|
% occur before any call that requires the typeinfo, so the variable will
|
|
% be born before the continuation after the call. So the typeinfo
|
|
% variables will always be born before any continuation where they are
|
|
% needed.
|
|
%
|
|
% A typeinfo variable becomes dead after both the following conditions
|
|
% are true:
|
|
%
|
|
% (1) The typeinfo variable is not used again (when it is no
|
|
% longer part of the nonlocals)
|
|
% (2) No other nonlocal variable's type is described by that typeinfo
|
|
% variable.
|
|
%
|
|
% (1) happens without any changes to the liveness computation (it is
|
|
% the normal condition for variables becoming dead). This more
|
|
% conservative than what is required for the invariant, but is
|
|
% required for code generation, so we should keep it ;-)
|
|
% (2) is implemented by adding the typeinfo variables for the types of the
|
|
% nonlocals to the nonlocals for the purposes of computing liveness.
|
|
%
|
|
% So typeinfo variables will always be born before they are needed, and
|
|
% die only when no other variable needing them will be live, so the
|
|
% invariant holds.
|
|
%
|
|
% Quantification notes:
|
|
%
|
|
% If a variable is not live on entry to a goal, but the goal gives it a value,
|
|
% the code of this module assumes that
|
|
%
|
|
% (a) any parallel goals also give it a value, or
|
|
% (b) the variable is local to this goal and hence does not occur in parallel
|
|
% goals.
|
|
%
|
|
% If a variable occurs in the nonlocal set of the goal, the code of this
|
|
% assumes that (b) is not true, and will therefore require (a) to be true.
|
|
% If some of the parallel goals cannot succeed, the first pass will include
|
|
% the variable in their post-birth sets.
|
|
%
|
|
% If a variable occurs in the nonlocal set of the goal, but is actually
|
|
% local to the goal, then any occurrence of that variable in the postbirth
|
|
% sets of parallel goals will lead to an inconsistency, because the variable
|
|
% will not die on those parallel paths, but will die on the path that
|
|
% actually gives a value to the variable.
|
|
%
|
|
% The nonlocal set of a goal is in general allowed to overapproximate the
|
|
% true set of nonlocal variables of the goal. Since this module requires
|
|
% *exact* information about nonlocals, it must recompute the nonlocal sets
|
|
% before starting.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module liveness.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds_module, hlds_pred, prog_data.
|
|
:- import_module set.
|
|
|
|
% Add liveness annotations to the goal of the procedure.
|
|
% This consists of the {pre,post}{birth,death} sets and
|
|
% resume point information.
|
|
|
|
:- pred detect_liveness_proc(proc_info, pred_id, module_info, proc_info).
|
|
:- mode detect_liveness_proc(in, in, in, out) is det.
|
|
|
|
% Return the set of variables live at the start of the procedure.
|
|
|
|
:- pred initial_liveness(proc_info, pred_id, module_info, set(prog_var)).
|
|
:- mode initial_liveness(in, in, in, out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds_goal, hlds_data, llds, quantification, (inst), instmap.
|
|
:- import_module hlds_out, mode_util, code_util, quantification, options.
|
|
:- import_module trace, globals, polymorphism, passes_aux.
|
|
:- import_module term, varset.
|
|
|
|
:- import_module bool, map, std_util, list, assoc_list, require.
|
|
:- import_module string.
|
|
|
|
detect_liveness_proc(ProcInfo0, PredId, ModuleInfo, ProcInfo) :-
|
|
requantify_proc(ProcInfo0, ProcInfo1),
|
|
proc_info_goal(ProcInfo1, Goal0),
|
|
proc_info_varset(ProcInfo1, Varset),
|
|
proc_info_vartypes(ProcInfo1, VarTypes),
|
|
module_info_globals(ModuleInfo, Globals),
|
|
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
pred_info_module(PredInfo, PredModule),
|
|
pred_info_name(PredInfo, PredName),
|
|
pred_info_arity(PredInfo, PredArity),
|
|
(
|
|
polymorphism__no_type_info_builtin(PredModule,
|
|
PredName, PredArity)
|
|
->
|
|
TypeInfoLiveness = no
|
|
;
|
|
body_should_use_typeinfo_liveness(Globals, TypeInfoLiveness)
|
|
),
|
|
live_info_init(ModuleInfo, ProcInfo1, TypeInfoLiveness,
|
|
VarTypes, Varset, LiveInfo),
|
|
|
|
initial_liveness(ProcInfo1, PredId, ModuleInfo, Liveness0),
|
|
detect_liveness_in_goal(Goal0, Liveness0, LiveInfo,
|
|
_, Goal1),
|
|
|
|
initial_deadness(ProcInfo1, LiveInfo, ModuleInfo, Deadness0),
|
|
detect_deadness_in_goal(Goal1, Deadness0, LiveInfo, _, Goal2),
|
|
|
|
globals__get_trace_level(Globals, TraceLevel),
|
|
( TraceLevel \= none ->
|
|
trace__fail_vars(ModuleInfo, ProcInfo0, ResumeVars0)
|
|
;
|
|
set__init(ResumeVars0)
|
|
),
|
|
detect_resume_points_in_goal(Goal2, Liveness0, LiveInfo,
|
|
ResumeVars0, Goal, _),
|
|
|
|
proc_info_set_goal(ProcInfo1, Goal, ProcInfo2),
|
|
proc_info_set_liveness_info(ProcInfo2, Liveness0, ProcInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_liveness_in_goal(hlds_goal, set(prog_var), live_info,
|
|
set(prog_var), hlds_goal).
|
|
:- mode detect_liveness_in_goal(in, in, in, out, out) is det.
|
|
|
|
detect_liveness_in_goal(Goal0 - GoalInfo0, Liveness0, LiveInfo,
|
|
Liveness, Goal - GoalInfo) :-
|
|
|
|
% work out which variables get born in this goal
|
|
liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo0, NonLocals),
|
|
set__difference(NonLocals, Liveness0, NewVarsSet),
|
|
set__to_sorted_list(NewVarsSet, NewVarsList),
|
|
goal_info_get_instmap_delta(GoalInfo0, InstMapDelta),
|
|
set__init(Empty),
|
|
( instmap_delta_is_unreachable(InstMapDelta) ->
|
|
Births = Empty
|
|
;
|
|
set__init(Births0),
|
|
find_value_giving_occurrences(NewVarsList, LiveInfo,
|
|
InstMapDelta, Births0, Births)
|
|
),
|
|
set__union(Liveness0, Births, Liveness),
|
|
(
|
|
goal_is_atomic(Goal0)
|
|
->
|
|
PreDeaths = Empty,
|
|
PreBirths = Births,
|
|
PostDeaths = Empty,
|
|
PostBirths = Empty,
|
|
Goal = Goal0
|
|
;
|
|
PreDeaths = Empty,
|
|
PreBirths = Empty,
|
|
detect_liveness_in_goal_2(Goal0, Liveness0, NonLocals,
|
|
LiveInfo, ActualLiveness, Goal),
|
|
set__intersect(NonLocals, ActualLiveness, NonLocalLiveness),
|
|
set__union(NonLocalLiveness, Liveness0, FinalLiveness),
|
|
set__difference(FinalLiveness, Liveness, PostDeaths),
|
|
set__difference(Liveness, FinalLiveness, PostBirths)
|
|
),
|
|
% We initialize all the fields in order to obliterate any
|
|
% annotations left by a previous invocation of this module.
|
|
goal_info_set_pre_deaths(GoalInfo0, PreDeaths, GoalInfo1),
|
|
goal_info_set_pre_births(GoalInfo1, PreBirths, GoalInfo2),
|
|
goal_info_set_post_deaths(GoalInfo2, PostDeaths, GoalInfo3),
|
|
goal_info_set_post_births(GoalInfo3, PostBirths, GoalInfo4),
|
|
goal_info_set_resume_point(GoalInfo4, no_resume_point, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Here we process each of the different sorts of goals.
|
|
|
|
:- pred detect_liveness_in_goal_2(hlds_goal_expr, set(prog_var), set(prog_var),
|
|
live_info, set(prog_var), hlds_goal_expr).
|
|
:- mode detect_liveness_in_goal_2(in, in, in, in, out, out) is det.
|
|
|
|
detect_liveness_in_goal_2(conj(Goals0), Liveness0, _, LiveInfo,
|
|
Liveness, conj(Goals)) :-
|
|
detect_liveness_in_conj(Goals0, Liveness0, LiveInfo, Liveness, Goals).
|
|
|
|
detect_liveness_in_goal_2(par_conj(Goals0, SM), Liveness0, NonLocals, LiveInfo,
|
|
Liveness, par_conj(Goals, SM)) :-
|
|
set__init(Union0),
|
|
detect_liveness_in_par_conj(Goals0, Liveness0, NonLocals, LiveInfo,
|
|
Union0, Union, Goals),
|
|
set__union(Liveness0, Union, Liveness).
|
|
|
|
detect_liveness_in_goal_2(disj(Goals0, SM), Liveness0, NonLocals, LiveInfo,
|
|
Liveness, disj(Goals, SM)) :-
|
|
set__init(Union0),
|
|
detect_liveness_in_disj(Goals0, Liveness0, NonLocals, LiveInfo,
|
|
Union0, Union, Goals),
|
|
set__union(Liveness0, Union, Liveness).
|
|
|
|
detect_liveness_in_goal_2(switch(Var, Det, Cases0, SM), Liveness0, NonLocals,
|
|
LiveInfo, Liveness, switch(Var, Det, Cases, SM)) :-
|
|
detect_liveness_in_cases(Cases0, Liveness0, NonLocals, LiveInfo,
|
|
Liveness0, Liveness, Cases).
|
|
|
|
detect_liveness_in_goal_2(not(Goal0), Liveness0, _, LiveInfo,
|
|
Liveness, not(Goal)) :-
|
|
detect_liveness_in_goal(Goal0, Liveness0, LiveInfo, Liveness, Goal).
|
|
|
|
detect_liveness_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM),
|
|
Liveness0, NonLocals, LiveInfo, Liveness,
|
|
if_then_else(Vars, Cond, Then, Else, SM)) :-
|
|
detect_liveness_in_goal(Cond0, Liveness0, LiveInfo, LivenessCond, Cond),
|
|
|
|
%
|
|
% If the condition cannot succeed, any variables which become live
|
|
% in the else part should be put in the post-birth set of the then part
|
|
% by add_liveness_after_goal, and the other sets should be empty.
|
|
%
|
|
Cond = _ - CondInfo,
|
|
goal_info_get_instmap_delta(CondInfo, CondDelta),
|
|
( instmap_delta_is_unreachable(CondDelta) ->
|
|
LivenessThen = LivenessCond,
|
|
Then1 = Then0
|
|
;
|
|
detect_liveness_in_goal(Then0, LivenessCond, LiveInfo,
|
|
LivenessThen, Then1)
|
|
),
|
|
|
|
detect_liveness_in_goal(Else0, Liveness0, LiveInfo, LivenessElse,
|
|
Else1),
|
|
|
|
set__union(LivenessThen, LivenessElse, Liveness),
|
|
set__intersect(Liveness, NonLocals, NonLocalLiveness),
|
|
|
|
set__difference(NonLocalLiveness, LivenessThen, ResidueThen),
|
|
set__difference(NonLocalLiveness, LivenessElse, ResidueElse),
|
|
|
|
add_liveness_after_goal(Then1, ResidueThen, Then),
|
|
add_liveness_after_goal(Else1, ResidueElse, Else).
|
|
|
|
detect_liveness_in_goal_2(some(Vars, CanRemove, Goal0), Liveness0, _, LiveInfo,
|
|
Liveness, some(Vars, CanRemove, Goal)) :-
|
|
detect_liveness_in_goal(Goal0, Liveness0, LiveInfo, Liveness, Goal).
|
|
|
|
detect_liveness_in_goal_2(generic_call(_,_,_,_), _, _, _, _, _) :-
|
|
error("higher-order-call in detect_liveness_in_goal_2").
|
|
|
|
detect_liveness_in_goal_2(call(_,_,_,_,_,_), _, _, _, _, _) :-
|
|
error("call in detect_liveness_in_goal_2").
|
|
|
|
detect_liveness_in_goal_2(unify(_,_,_,_,_), _, _, _, _, _) :-
|
|
error("unify in detect_liveness_in_goal_2").
|
|
|
|
detect_liveness_in_goal_2(pragma_c_code(_,_,_,_,_,_,_), _, _, _, _, _) :-
|
|
error("pragma_c_code in detect_liveness_in_goal_2").
|
|
|
|
detect_liveness_in_goal_2(bi_implication(_, _), _, _, _, _, _) :-
|
|
error("bi_implication in detect_liveness_in_goal_2").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_liveness_in_conj(list(hlds_goal), set(prog_var), live_info,
|
|
set(prog_var), list(hlds_goal)).
|
|
:- mode detect_liveness_in_conj(in, in, in, out, out) is det.
|
|
|
|
detect_liveness_in_conj([], Liveness, _LiveInfo, Liveness, []).
|
|
detect_liveness_in_conj([Goal0 | Goals0], Liveness0, LiveInfo, Liveness,
|
|
[Goal | Goals]) :-
|
|
detect_liveness_in_goal(Goal0, Liveness0, LiveInfo, Liveness1, Goal),
|
|
(
|
|
Goal0 = _ - GoalInfo,
|
|
goal_info_get_instmap_delta(GoalInfo, InstmapDelta),
|
|
instmap_delta_is_unreachable(InstmapDelta)
|
|
->
|
|
Goals = Goals0,
|
|
Liveness = Liveness1
|
|
;
|
|
detect_liveness_in_conj(Goals0, Liveness1, LiveInfo,
|
|
Liveness, Goals)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_liveness_in_disj(list(hlds_goal), set(prog_var), set(prog_var),
|
|
live_info, set(prog_var), set(prog_var), list(hlds_goal)).
|
|
:- mode detect_liveness_in_disj(in, in, in, in, in, out, out) is det.
|
|
|
|
detect_liveness_in_disj([], _Liveness, _NonLocals, _LiveInfo,
|
|
Union, Union, []).
|
|
detect_liveness_in_disj([Goal0 | Goals0], Liveness, NonLocals, LiveInfo,
|
|
Union0, Union, [Goal | Goals]) :-
|
|
detect_liveness_in_goal(Goal0, Liveness, LiveInfo, Liveness1, Goal1),
|
|
set__union(Union0, Liveness1, Union1),
|
|
detect_liveness_in_disj(Goals0, Liveness, NonLocals, LiveInfo,
|
|
Union1, Union, Goals),
|
|
set__intersect(Union, NonLocals, NonLocalUnion),
|
|
set__difference(NonLocalUnion, Liveness1, Residue),
|
|
add_liveness_after_goal(Goal1, Residue, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_liveness_in_cases(list(case), set(prog_var), set(prog_var),
|
|
live_info, set(prog_var), set(prog_var), list(case)).
|
|
:- mode detect_liveness_in_cases(in, in, in, in, in, out, out) is det.
|
|
|
|
detect_liveness_in_cases([], _Liveness, _NonLocals, _LiveInfo,
|
|
Union, Union, []).
|
|
detect_liveness_in_cases([case(Cons, Goal0) | Goals0], Liveness, NonLocals,
|
|
LiveInfo, Union0, Union, [case(Cons, Goal) | Goals]) :-
|
|
detect_liveness_in_goal(Goal0, Liveness, LiveInfo, Liveness1, Goal1),
|
|
set__union(Union0, Liveness1, Union1),
|
|
detect_liveness_in_cases(Goals0, Liveness, NonLocals, LiveInfo,
|
|
Union1, Union, Goals),
|
|
set__intersect(Union, NonLocals, NonLocalUnion),
|
|
set__difference(NonLocalUnion, Liveness1, Residue),
|
|
add_liveness_after_goal(Goal1, Residue, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_liveness_in_par_conj(list(hlds_goal), set(prog_var),
|
|
set(prog_var), live_info, set(prog_var), set(prog_var),
|
|
list(hlds_goal)).
|
|
:- mode detect_liveness_in_par_conj(in, in, in, in, in, out, out) is det.
|
|
|
|
detect_liveness_in_par_conj([], _Liveness, _NonLocals, _LiveInfo,
|
|
Union, Union, []).
|
|
detect_liveness_in_par_conj([Goal0 | Goals0], Liveness0, NonLocals, LiveInfo,
|
|
Union0, Union, [Goal | Goals]) :-
|
|
detect_liveness_in_goal(Goal0, Liveness0, LiveInfo, Liveness1, Goal1),
|
|
set__union(Union0, Liveness1, Union1),
|
|
detect_liveness_in_par_conj(Goals0, Liveness0, NonLocals, LiveInfo,
|
|
Union1, Union, Goals),
|
|
set__intersect(Union, NonLocals, NonLocalUnion),
|
|
set__difference(NonLocalUnion, Liveness1, Residue),
|
|
add_liveness_after_goal(Goal1, Residue, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_deadness_in_goal(hlds_goal, set(prog_var), live_info,
|
|
set(prog_var), hlds_goal).
|
|
:- mode detect_deadness_in_goal(in, in, in, out, out) is det.
|
|
|
|
detect_deadness_in_goal(Goal0 - GoalInfo0, Deadness0, LiveInfo, Deadness,
|
|
Goal - GoalInfo) :-
|
|
goal_info_get_pre_deaths(GoalInfo0, PreDeaths0),
|
|
goal_info_get_pre_births(GoalInfo0, PreBirths0),
|
|
goal_info_get_post_deaths(GoalInfo0, PostDeaths0),
|
|
goal_info_get_post_births(GoalInfo0, PostBirths0),
|
|
|
|
set__difference(Deadness0, PostBirths0, Deadness1),
|
|
set__union(Deadness1, PostDeaths0, Deadness2),
|
|
|
|
liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo0, NonLocals),
|
|
set__init(Empty),
|
|
(
|
|
goal_is_atomic(Goal0)
|
|
->
|
|
%
|
|
% The code below is slightly dodgy: the new postdeaths really
|
|
% ought to be computed as the difference between the liveness
|
|
% immediately before goal and the deadness immediately after
|
|
% goal. But we don't have liveness available here, and
|
|
% computing it would be complicated and perhaps costly. So
|
|
% instead we use the non-locals. The effect of this is that a
|
|
% variable will die after its last occurence, even if it has
|
|
% never been born. (This can occur in the case of variables
|
|
% with `free->free' modes, or for goals with determinism
|
|
% erroneous.) Thus the code generator must be willing to
|
|
% handle that -- it is not considered an error for a variable
|
|
% that is not yet live to die. [If we ever wanted to
|
|
% change this, the easiest thing to do would be to put
|
|
% some extra code in the third pass (detect_resume_points)
|
|
% to delete any such untimely deaths.]
|
|
%
|
|
set__difference(NonLocals, Deadness2, NewPostDeaths),
|
|
set__union(Deadness2, NewPostDeaths, Deadness3),
|
|
Goal = Goal0
|
|
;
|
|
NewPostDeaths = Empty,
|
|
detect_deadness_in_goal_2(Goal0, GoalInfo0, Deadness2,
|
|
LiveInfo, Deadness3, Goal)
|
|
),
|
|
set__union(PostDeaths0, NewPostDeaths, PostDeaths),
|
|
goal_info_set_post_deaths(GoalInfo0, PostDeaths, GoalInfo),
|
|
|
|
set__difference(Deadness3, PreBirths0, Deadness4),
|
|
set__union(Deadness4, PreDeaths0, Deadness).
|
|
|
|
% Here we process each of the different sorts of goals.
|
|
|
|
:- pred detect_deadness_in_goal_2(hlds_goal_expr, hlds_goal_info,
|
|
set(prog_var), live_info, set(prog_var), hlds_goal_expr).
|
|
:- mode detect_deadness_in_goal_2(in, in, in, in, out, out) is det.
|
|
|
|
detect_deadness_in_goal_2(conj(Goals0), _, Deadness0, LiveInfo,
|
|
Deadness, conj(Goals)) :-
|
|
detect_deadness_in_conj(Goals0, Deadness0, LiveInfo,
|
|
Goals, Deadness).
|
|
|
|
detect_deadness_in_goal_2(par_conj(Goals0, SM), GoalInfo, Deadness0, LiveInfo,
|
|
Deadness, par_conj(Goals, SM)) :-
|
|
set__init(Union0),
|
|
goal_info_get_code_gen_nonlocals(GoalInfo, NonLocals),
|
|
detect_deadness_in_par_conj(Goals0, Deadness0, NonLocals,
|
|
LiveInfo, Union0, Union, Goals),
|
|
set__union(Union, Deadness0, Deadness).
|
|
|
|
detect_deadness_in_goal_2(disj(Goals0, SM), GoalInfo, Deadness0,
|
|
LiveInfo, Deadness, disj(Goals, SM)) :-
|
|
set__init(Union0),
|
|
liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo, NonLocals),
|
|
detect_deadness_in_disj(Goals0, Deadness0, NonLocals,
|
|
LiveInfo, Union0, Deadness, Goals).
|
|
|
|
detect_deadness_in_goal_2(switch(Var, Det, Cases0, SM), GoalInfo, Deadness0,
|
|
LiveInfo, Deadness, switch(Var, Det, Cases, SM)) :-
|
|
set__init(Union0),
|
|
liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo, NonLocals),
|
|
detect_deadness_in_cases(Var, Cases0, Deadness0, NonLocals,
|
|
LiveInfo, Union0, Deadness, Cases).
|
|
|
|
detect_deadness_in_goal_2(not(Goal0), _, Deadness0, LiveInfo,
|
|
Deadness, not(Goal)) :-
|
|
detect_deadness_in_goal(Goal0, Deadness0, LiveInfo, Deadness, Goal).
|
|
|
|
detect_deadness_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM),
|
|
GoalInfo, Deadness0, LiveInfo, Deadness,
|
|
if_then_else(Vars, Cond, Then, Else, SM)) :-
|
|
detect_deadness_in_goal(Else0, Deadness0, LiveInfo,
|
|
DeadnessElse, Else1),
|
|
detect_deadness_in_goal(Then0, Deadness0, LiveInfo,
|
|
DeadnessThen, Then),
|
|
detect_deadness_in_goal(Cond0, DeadnessThen, LiveInfo,
|
|
DeadnessCond, Cond1),
|
|
|
|
liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo, NonLocals),
|
|
set__union(DeadnessCond, DeadnessElse, Deadness),
|
|
set__intersect(Deadness, NonLocals, NonLocalDeadness),
|
|
|
|
set__difference(NonLocalDeadness, DeadnessCond, ResidueCond),
|
|
set__difference(NonLocalDeadness, DeadnessElse, ResidueElse),
|
|
|
|
add_deadness_before_goal(Cond1, ResidueCond, Cond),
|
|
add_deadness_before_goal(Else1, ResidueElse, Else).
|
|
|
|
detect_deadness_in_goal_2(some(Vars, CanRemove, Goal0), _, Deadness0, LiveInfo,
|
|
Deadness, some(Vars, CanRemove, Goal)) :-
|
|
detect_deadness_in_goal(Goal0, Deadness0, LiveInfo, Deadness, Goal).
|
|
|
|
detect_deadness_in_goal_2(generic_call(_,_,_,_), _, _, _, _, _) :-
|
|
error("higher-order-call in detect_deadness_in_goal_2").
|
|
|
|
detect_deadness_in_goal_2(call(_,_,_,_,_,_), _, _, _, _, _) :-
|
|
error("call in detect_deadness_in_goal_2").
|
|
|
|
detect_deadness_in_goal_2(unify(_,_,_,_,_), _, _, _, _, _) :-
|
|
error("unify in detect_deadness_in_goal_2").
|
|
|
|
detect_deadness_in_goal_2(pragma_c_code(_,_,_,_,_,_,_), _, _, _, _, _) :-
|
|
error("pragma_c_code in detect_deadness_in_goal_2").
|
|
|
|
detect_deadness_in_goal_2(bi_implication(_, _), _, _, _, _, _) :-
|
|
error("bi_implication in detect_deadness_in_goal_2").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_deadness_in_conj(list(hlds_goal), set(prog_var), live_info,
|
|
list(hlds_goal), set(prog_var)).
|
|
:- mode detect_deadness_in_conj(in, in, in, out, out) is det.
|
|
|
|
detect_deadness_in_conj([], Deadness, _LiveInfo, [], Deadness).
|
|
detect_deadness_in_conj([Goal0 | Goals0], Deadness0, LiveInfo,
|
|
[Goal | Goals], Deadness) :-
|
|
(
|
|
Goal0 = _ - GoalInfo,
|
|
goal_info_get_instmap_delta(GoalInfo, InstmapDelta),
|
|
instmap_delta_is_unreachable(InstmapDelta)
|
|
->
|
|
Goals = Goals0,
|
|
detect_deadness_in_goal(Goal0, Deadness0, LiveInfo,
|
|
Deadness, Goal)
|
|
;
|
|
detect_deadness_in_conj(Goals0, Deadness0, LiveInfo,
|
|
Goals, Deadness1),
|
|
detect_deadness_in_goal(Goal0, Deadness1, LiveInfo,
|
|
Deadness, Goal)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_deadness_in_disj(list(hlds_goal), set(prog_var), set(prog_var),
|
|
live_info, set(prog_var), set(prog_var), list(hlds_goal)).
|
|
:- mode detect_deadness_in_disj(in, in, in, in, in, out, out) is det.
|
|
|
|
detect_deadness_in_disj([], _Deadness, _NonLocals, _LiveInfo,
|
|
Union, Union, []).
|
|
detect_deadness_in_disj([Goal0 | Goals0], Deadness, NonLocals, LiveInfo,
|
|
Union0, Union, [Goal | Goals]) :-
|
|
detect_deadness_in_goal(Goal0, Deadness, LiveInfo, Deadness1, Goal1),
|
|
set__union(Union0, Deadness1, Union1),
|
|
detect_deadness_in_disj(Goals0, Deadness, NonLocals, LiveInfo,
|
|
Union1, Union, Goals),
|
|
set__intersect(Union, NonLocals, NonLocalUnion),
|
|
set__difference(NonLocalUnion, Deadness1, Residue),
|
|
add_deadness_before_goal(Goal1, Residue, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_deadness_in_cases(prog_var, list(case), set(prog_var),
|
|
set(prog_var), live_info, set(prog_var), set(prog_var), list(case)).
|
|
:- mode detect_deadness_in_cases(in, in, in, in, in, in, out, out) is det.
|
|
|
|
detect_deadness_in_cases(_Var, [], _Deadness, _NonLocals, _LiveInfo,
|
|
Union, Union, []).
|
|
detect_deadness_in_cases(SwitchVar, [case(Cons, Goal0) | Goals0], Deadness0,
|
|
NonLocals, LiveInfo, Union0, Union,
|
|
[case(Cons, Goal) | Goals]) :-
|
|
detect_deadness_in_goal(Goal0, Deadness0, LiveInfo, Deadness1, Goal1),
|
|
set__union(Union0, Deadness1, Union1),
|
|
detect_deadness_in_cases(SwitchVar, Goals0, Deadness0, NonLocals,
|
|
LiveInfo, Union1, Union2, Goals),
|
|
% If the switch variable does not become dead in a case
|
|
% it must be put in the pre-death set of that case.
|
|
set__insert(Union2, SwitchVar, Union),
|
|
set__intersect(Union, NonLocals, NonLocalUnion),
|
|
set__difference(NonLocalUnion, Deadness1, Residue),
|
|
add_deadness_before_goal(Goal1, Residue, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_deadness_in_par_conj(list(hlds_goal), set(prog_var),
|
|
set(prog_var), live_info, set(prog_var), set(prog_var),
|
|
list(hlds_goal)).
|
|
:- mode detect_deadness_in_par_conj(in, in, in, in, in, out, out) is det.
|
|
|
|
detect_deadness_in_par_conj([], _Deadness, _NonLocals, _LiveInfo,
|
|
Union, Union, []).
|
|
detect_deadness_in_par_conj([Goal0 | Goals0], Deadness, NonLocals, LiveInfo,
|
|
Union0, Union, [Goal | Goals]) :-
|
|
detect_deadness_in_goal(Goal0, Deadness, LiveInfo, Deadness1, Goal1),
|
|
set__union(Union0, Deadness1, Union1),
|
|
detect_deadness_in_par_conj(Goals0, Deadness, NonLocals, LiveInfo,
|
|
Union1, Union, Goals),
|
|
set__intersect(Union, NonLocals, NonLocalUnion),
|
|
set__difference(NonLocalUnion, Deadness1, Residue),
|
|
add_deadness_before_goal(Goal1, Residue, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred detect_resume_points_in_goal(hlds_goal, set(prog_var), live_info,
|
|
set(prog_var), hlds_goal, set(prog_var)).
|
|
:- mode detect_resume_points_in_goal(in, in, in, in, out, out) is det.
|
|
|
|
detect_resume_points_in_goal(Goal0 - GoalInfo0, Liveness0, LiveInfo,
|
|
ResumeVars0, Goal - GoalInfo0, Liveness) :-
|
|
goal_info_get_pre_deaths(GoalInfo0, PreDeaths0),
|
|
goal_info_get_pre_births(GoalInfo0, PreBirths0),
|
|
goal_info_get_post_deaths(GoalInfo0, PostDeaths0),
|
|
goal_info_get_post_births(GoalInfo0, PostBirths0),
|
|
|
|
set__difference(Liveness0, PreDeaths0, Liveness1),
|
|
set__union(Liveness1, PreBirths0, Liveness2),
|
|
|
|
detect_resume_points_in_goal_2(Goal0, GoalInfo0, Liveness2, LiveInfo,
|
|
ResumeVars0, Goal, Liveness3),
|
|
|
|
set__difference(Liveness3, PostDeaths0, Liveness4),
|
|
set__union(Liveness4, PostBirths0, Liveness).
|
|
|
|
:- pred detect_resume_points_in_goal_2(hlds_goal_expr, hlds_goal_info,
|
|
set(prog_var), live_info, set(prog_var), hlds_goal_expr, set(prog_var)).
|
|
:- mode detect_resume_points_in_goal_2(in, in, in, in, in, out, out) is det.
|
|
|
|
detect_resume_points_in_goal_2(conj(Goals0), _, Liveness0, LiveInfo,
|
|
ResumeVars0, conj(Goals), Liveness) :-
|
|
detect_resume_points_in_conj(Goals0, Liveness0, LiveInfo, ResumeVars0,
|
|
Goals, Liveness).
|
|
|
|
detect_resume_points_in_goal_2(par_conj(Goals0, SM), _, Liveness0, LiveInfo,
|
|
ResumeVars0, par_conj(Goals, SM), Liveness) :-
|
|
detect_resume_points_in_par_conj(Goals0, Liveness0, LiveInfo,
|
|
ResumeVars0, Goals, Liveness).
|
|
|
|
detect_resume_points_in_goal_2(disj(Goals0, SM), GoalInfo, Liveness0, LiveInfo,
|
|
ResumeVars0, disj(Goals, SM), Liveness) :-
|
|
goal_info_get_code_model(GoalInfo, CodeModel),
|
|
( CodeModel = model_non ->
|
|
detect_resume_points_in_non_disj(Goals0, Liveness0, LiveInfo,
|
|
ResumeVars0, Goals, Liveness, _)
|
|
;
|
|
detect_resume_points_in_pruned_disj(Goals0, Liveness0, LiveInfo,
|
|
ResumeVars0, Goals, Liveness, _)
|
|
).
|
|
|
|
detect_resume_points_in_goal_2(switch(Var, CF, Cases0, SM), _, Liveness0,
|
|
LiveInfo, ResumeVars0, switch(Var, CF, Cases, SM), Liveness) :-
|
|
detect_resume_points_in_cases(Cases0, Liveness0, LiveInfo, ResumeVars0,
|
|
Cases, Liveness).
|
|
|
|
detect_resume_points_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM),
|
|
GoalInfo0, Liveness0, LiveInfo, ResumeVars0,
|
|
if_then_else(Vars, Cond, Then, Else, SM), LivenessThen) :-
|
|
|
|
% compute the set of variables that may be needed at the start
|
|
% of the else part and attach this set to the condition
|
|
Else0 = _ElseExpr0 - ElseInfo0,
|
|
goal_info_get_pre_deaths(ElseInfo0, ElsePreDeath0),
|
|
set__difference(Liveness0, ElsePreDeath0, CondResumeVars0),
|
|
set__union(CondResumeVars0, ResumeVars0, CondResumeVars),
|
|
|
|
detect_resume_points_in_goal(Cond0, Liveness0, LiveInfo,
|
|
CondResumeVars, Cond1, LivenessCond),
|
|
detect_resume_points_in_goal(Then0, LivenessCond, LiveInfo,
|
|
ResumeVars0, Then, LivenessThen),
|
|
detect_resume_points_in_goal(Else0, Liveness0, LiveInfo,
|
|
ResumeVars0, Else, LivenessElse),
|
|
|
|
% Figure out which entry labels we need at the resumption point.
|
|
% By minimizing the number of labels we use, we also minimize
|
|
% the amount of data movement code we emit between such labels.
|
|
(
|
|
code_util__cannot_stack_flush(Cond1),
|
|
goal_info_get_code_model(GoalInfo0, CodeModel),
|
|
CodeModel \= model_non
|
|
->
|
|
CondResumeLocs = orig_only
|
|
;
|
|
code_util__cannot_fail_before_stack_flush(Cond1)
|
|
->
|
|
CondResumeLocs = stack_only
|
|
;
|
|
CondResumeLocs = stack_and_orig
|
|
),
|
|
|
|
% Attach the set of variables needed after the condition
|
|
% as the resume point set of the condition.
|
|
CondResume = resume_point(CondResumeVars, CondResumeLocs),
|
|
goal_set_resume_point(Cond1, CondResume, Cond),
|
|
|
|
require_equal(LivenessThen, LivenessElse, "if-then-else", LiveInfo).
|
|
|
|
detect_resume_points_in_goal_2(some(Vars, CanRemove, Goal0), _, Liveness0,
|
|
LiveInfo, ResumeVars0, some(Vars, CanRemove, Goal),
|
|
Liveness) :-
|
|
detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars0,
|
|
Goal, Liveness).
|
|
|
|
detect_resume_points_in_goal_2(not(Goal0), _, Liveness0, LiveInfo, ResumeVars0,
|
|
not(Goal), Liveness) :-
|
|
detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars0,
|
|
_, Liveness),
|
|
set__union(Liveness, ResumeVars0, ResumeVars1),
|
|
detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars1,
|
|
Goal1, _Liveness),
|
|
|
|
% Figure out which entry labels we need at the resumption point.
|
|
% By minimizing the number of labels we use, we also minimize
|
|
% the amount of data movement code we emit between such labels.
|
|
( code_util__cannot_stack_flush(Goal1) ->
|
|
ResumeLocs = orig_only
|
|
; code_util__cannot_fail_before_stack_flush(Goal1) ->
|
|
ResumeLocs = stack_only
|
|
;
|
|
ResumeLocs = stack_and_orig
|
|
),
|
|
|
|
% Attach the set of variables alive after the negation
|
|
% as the resume point set of the negated goal.
|
|
Resume = resume_point(ResumeVars1, ResumeLocs),
|
|
goal_set_resume_point(Goal1, Resume, Goal).
|
|
|
|
detect_resume_points_in_goal_2(generic_call(A,B,C,D), _, Liveness,
|
|
_, _, generic_call(A,B,C,D), Liveness).
|
|
|
|
detect_resume_points_in_goal_2(call(A,B,C,D,E,F), _, Liveness, _, _,
|
|
call(A,B,C,D,E,F), Liveness).
|
|
|
|
detect_resume_points_in_goal_2(unify(A,B,C,D,E), _, Liveness, _, _,
|
|
unify(A,B,C,D,E), Liveness).
|
|
|
|
detect_resume_points_in_goal_2(pragma_c_code(A,B,C,D,E,F,G), _, Liveness,
|
|
_, _,
|
|
pragma_c_code(A,B,C,D,E,F,G), Liveness).
|
|
|
|
detect_resume_points_in_goal_2(bi_implication(_, _), _, _, _, _, _, _) :-
|
|
% these should have been expanded out by now
|
|
error("detect_resume_points_in_goal_2: unexpected bi_implication").
|
|
|
|
:- pred detect_resume_points_in_conj(list(hlds_goal), set(prog_var), live_info,
|
|
set(prog_var), list(hlds_goal), set(prog_var)).
|
|
:- mode detect_resume_points_in_conj(in, in, in, in, out, out) is det.
|
|
|
|
detect_resume_points_in_conj([], Liveness, _, _, [], Liveness).
|
|
detect_resume_points_in_conj([Goal0 | Goals0], Liveness0, LiveInfo,
|
|
ResumeVars0, [Goal | Goals], Liveness) :-
|
|
detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars0,
|
|
Goal, Liveness1),
|
|
detect_resume_points_in_conj(Goals0, Liveness1, LiveInfo, ResumeVars0,
|
|
Goals, Liveness).
|
|
|
|
% There are only two differences in the handling of pruned disjs
|
|
% versus nondet disjs. First, for nondet disjunctions we always
|
|
% generate code for all disjuncts, whereas for pruned disjunctions
|
|
% we stop generating code after the first cannot_fail disjunct.
|
|
% Second, an empty pruned disjunction is legal, while an empty
|
|
% nondet disjunction isn't.
|
|
|
|
:- pred detect_resume_points_in_non_disj(list(hlds_goal), set(prog_var),
|
|
live_info, set(prog_var), list(hlds_goal),
|
|
set(prog_var), set(prog_var)).
|
|
:- mode detect_resume_points_in_non_disj(in, in, in, in, out, out, out) is det.
|
|
|
|
detect_resume_points_in_non_disj([], _, _, _, _, _, _) :-
|
|
error("empty nondet disjunction").
|
|
detect_resume_points_in_non_disj([Goal0 | Goals0], Liveness0, LiveInfo,
|
|
ResumeVars0, [Goal | Goals], Liveness, Needed) :-
|
|
(
|
|
% If there are any more disjuncts, then this disjunct
|
|
% establishes a resumption point.
|
|
Goals0 = [_ | _]
|
|
->
|
|
detect_resume_points_in_non_disj(Goals0, Liveness0, LiveInfo,
|
|
ResumeVars0, Goals, LivenessRest, NeededRest),
|
|
detect_resume_points_in_non_last_disjunct(Goal0, no,
|
|
Liveness0, LivenessRest, LiveInfo, ResumeVars0, Goal,
|
|
Liveness, NeededRest, Needed)
|
|
;
|
|
detect_resume_points_in_last_disjunct(Goal0, Liveness0,
|
|
LiveInfo, ResumeVars0, Goal, Liveness, Needed),
|
|
Goals = Goals0
|
|
).
|
|
|
|
:- pred detect_resume_points_in_pruned_disj(list(hlds_goal), set(prog_var),
|
|
live_info, set(prog_var), list(hlds_goal), set(prog_var),
|
|
set(prog_var)).
|
|
:- mode detect_resume_points_in_pruned_disj(in, in, in, in, out, out, out)
|
|
is det.
|
|
|
|
detect_resume_points_in_pruned_disj([], Liveness, _, _, [], Liveness, Needed) :-
|
|
set__init(Needed).
|
|
detect_resume_points_in_pruned_disj([Goal0 | Goals0], Liveness0, LiveInfo,
|
|
ResumeVars0, [Goal | Goals], Liveness, Needed) :-
|
|
Goal0 = _ - GoalInfo0,
|
|
goal_info_get_determinism(GoalInfo0, Detism0),
|
|
determinism_components(Detism0, CanFail0, _),
|
|
(
|
|
% This disjunct establishes a resumption point only if
|
|
% there are more disjuncts *and* this one can fail.
|
|
% If there are more disjuncts but this one can't fail,
|
|
% then the code generator will ignore any later disjuncts,
|
|
% so this one will be effectively the last.
|
|
CanFail0 = can_fail,
|
|
Goals0 = [_ | _]
|
|
->
|
|
detect_resume_points_in_pruned_disj(Goals0, Liveness0, LiveInfo,
|
|
ResumeVars0, Goals, LivenessRest, NeededRest),
|
|
detect_resume_points_in_non_last_disjunct(Goal0, yes,
|
|
Liveness0, LivenessRest, LiveInfo, ResumeVars0, Goal,
|
|
Liveness, NeededRest, Needed)
|
|
;
|
|
detect_resume_points_in_last_disjunct(Goal0, Liveness0,
|
|
LiveInfo, ResumeVars0, Goal, Liveness, Needed),
|
|
Goals = Goals0
|
|
).
|
|
|
|
:- pred detect_resume_points_in_non_last_disjunct(hlds_goal, bool,
|
|
set(prog_var), set(prog_var), live_info, set(prog_var), hlds_goal,
|
|
set(prog_var), set(prog_var), set(prog_var)).
|
|
:- mode detect_resume_points_in_non_last_disjunct(in, in, in, in, in, in,
|
|
out, out, in, out) is det.
|
|
|
|
detect_resume_points_in_non_last_disjunct(Goal0, MayUseOrigOnly,
|
|
Liveness0, LivenessRest, LiveInfo, ResumeVars0, Goal,
|
|
Liveness, NeededRest, Needed) :-
|
|
% we must save a variable across this disjunct if it is
|
|
% needed in a later disjunct or in an enclosing resume point
|
|
|
|
set__union(NeededRest, ResumeVars0, ResumeVars1),
|
|
detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo,
|
|
ResumeVars1, Goal1, Liveness),
|
|
|
|
% Figure out which entry labels we need at the resumption point.
|
|
% By minimizing the number of labels we use, we also minimize
|
|
% the amount of data movement code we emit between such labels.
|
|
(
|
|
MayUseOrigOnly = yes,
|
|
code_util__cannot_stack_flush(Goal1)
|
|
->
|
|
ResumeLocs = orig_only
|
|
;
|
|
code_util__cannot_fail_before_stack_flush(Goal1)
|
|
->
|
|
ResumeLocs = stack_only
|
|
;
|
|
ResumeLocs = stack_and_orig
|
|
),
|
|
|
|
% Attach the set of variables needed in the following disjuncts
|
|
% as the resume point set of this disjunct.
|
|
Resume = resume_point(ResumeVars1, ResumeLocs),
|
|
goal_set_resume_point(Goal1, Resume, Goal),
|
|
|
|
Goal = _ - GoalInfo,
|
|
goal_info_get_pre_deaths(GoalInfo, PreDeaths),
|
|
set__difference(Liveness0, PreDeaths, NeededFirst),
|
|
set__union(NeededFirst, NeededRest, Needed),
|
|
|
|
require_equal(Liveness, LivenessRest, "disjunction", LiveInfo).
|
|
|
|
:- pred detect_resume_points_in_last_disjunct(hlds_goal, set(prog_var),
|
|
live_info, set(prog_var), hlds_goal, set(prog_var), set(prog_var)).
|
|
:- mode detect_resume_points_in_last_disjunct(in, in, in, in, out, out, out)
|
|
is det.
|
|
|
|
detect_resume_points_in_last_disjunct(Goal0, Liveness0, LiveInfo,
|
|
ResumeVars0, Goal, Liveness, Needed) :-
|
|
detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo,
|
|
ResumeVars0, Goal, Liveness),
|
|
Goal = _ - GoalInfo,
|
|
goal_info_get_pre_deaths(GoalInfo, PreDeaths),
|
|
set__difference(Liveness0, PreDeaths, Needed).
|
|
|
|
:- pred detect_resume_points_in_cases(list(case), set(prog_var), live_info,
|
|
set(prog_var), list(case), set(prog_var)).
|
|
:- mode detect_resume_points_in_cases(in, in, in, in, out, out) is det.
|
|
|
|
detect_resume_points_in_cases([], Liveness, _, _, [], Liveness).
|
|
detect_resume_points_in_cases([case(ConsId, Goal0) | Cases0], Liveness0,
|
|
LiveInfo, ResumeVars0,
|
|
[case(ConsId, Goal) | Cases], LivenessFirst) :-
|
|
detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars0,
|
|
Goal, LivenessFirst),
|
|
( Cases0 = [_ | _] ->
|
|
detect_resume_points_in_cases(Cases0, Liveness0, LiveInfo,
|
|
ResumeVars0, Cases, LivenessRest),
|
|
require_equal(LivenessFirst, LivenessRest, "switch",
|
|
LiveInfo)
|
|
;
|
|
Cases = Cases0
|
|
).
|
|
|
|
:- pred detect_resume_points_in_par_conj(list(hlds_goal), set(prog_var),
|
|
live_info, set(prog_var), list(hlds_goal), set(prog_var)).
|
|
:- mode detect_resume_points_in_par_conj(in, in, in, in, out, out) is det.
|
|
|
|
detect_resume_points_in_par_conj([], Liveness, _, _, [], Liveness).
|
|
detect_resume_points_in_par_conj([Goal0 | Goals0], Liveness0, LiveInfo,
|
|
ResumeVars0, [Goal | Goals], LivenessFirst) :-
|
|
detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars0,
|
|
Goal, LivenessFirst),
|
|
detect_resume_points_in_par_conj(Goals0, Liveness0, LiveInfo,
|
|
ResumeVars0, Goals, _LivenessRest).
|
|
|
|
:- pred require_equal(set(prog_var), set(prog_var), string, live_info).
|
|
:- mode require_equal(in, in, in, in) is det.
|
|
|
|
require_equal(LivenessFirst, LivenessRest, GoalType, LiveInfo) :-
|
|
(
|
|
set__equal(LivenessFirst, LivenessRest)
|
|
->
|
|
true
|
|
;
|
|
live_info_get_varset(LiveInfo, Varset),
|
|
set__to_sorted_list(LivenessFirst, FirstVarsList),
|
|
set__to_sorted_list(LivenessRest, RestVarsList),
|
|
list__map(varset__lookup_name(Varset),
|
|
FirstVarsList, FirstVarNames),
|
|
list__map(varset__lookup_name(Varset),
|
|
RestVarsList, RestVarNames),
|
|
Pad = lambda([S0::in, S::out] is det,
|
|
string__append(S0, " ", S)),
|
|
list__map(Pad, FirstVarNames, PaddedFirstNames),
|
|
list__map(Pad, RestVarNames, PaddedRestNames),
|
|
string__append_list(PaddedFirstNames, FirstNames),
|
|
string__append_list(PaddedRestNames, RestNames),
|
|
string__append_list(["branches of ", GoalType,
|
|
" disagree on liveness\nFirst: ",
|
|
FirstNames, "\nRest: ", RestNames], Msg),
|
|
error(Msg)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
initial_liveness(ProcInfo, PredId, ModuleInfo, Liveness) :-
|
|
proc_info_headvars(ProcInfo, Vars),
|
|
proc_info_argmodes(ProcInfo, Modes),
|
|
proc_info_vartypes(ProcInfo, VarTypes),
|
|
map__apply_to_list(Vars, VarTypes, Types),
|
|
set__init(Liveness0),
|
|
(
|
|
initial_liveness_2(Vars, Modes, Types, ModuleInfo,
|
|
Liveness0, Liveness1)
|
|
->
|
|
Liveness2 = Liveness1
|
|
;
|
|
error("initial_liveness: list length mismatch")
|
|
),
|
|
|
|
% If a variable is unused in the goal, it shouldn't be
|
|
% in the initial liveness. (If we allowed it to start
|
|
% live, it wouldn't ever become dead, because it would
|
|
% have to be used to be killed).
|
|
% So we intersect the headvars with the non-locals and
|
|
% (if doing typeinfo liveness calculation) their
|
|
% typeinfo vars.
|
|
module_info_globals(ModuleInfo, Globals),
|
|
proc_info_goal(ProcInfo, _Goal - GoalInfo),
|
|
goal_info_get_code_gen_nonlocals(GoalInfo, NonLocals0),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
body_should_use_typeinfo_liveness(Globals, TypeinfoLiveness),
|
|
pred_info_module(PredInfo, PredModule),
|
|
pred_info_name(PredInfo, PredName),
|
|
pred_info_arity(PredInfo, PredArity),
|
|
(
|
|
TypeinfoLiveness = yes,
|
|
\+ polymorphism__no_type_info_builtin(PredModule,
|
|
PredName, PredArity)
|
|
->
|
|
proc_info_get_typeinfo_vars_setwise(ProcInfo, NonLocals0,
|
|
TypeInfoNonLocals),
|
|
set__union(NonLocals0, TypeInfoNonLocals, NonLocals)
|
|
;
|
|
NonLocals = NonLocals0
|
|
),
|
|
set__intersect(Liveness2, NonLocals, Liveness).
|
|
|
|
:- pred initial_liveness_2(list(prog_var), list(mode), list(type), module_info,
|
|
set(prog_var), set(prog_var)).
|
|
:- mode initial_liveness_2(in, in, in, in, in, out) is semidet.
|
|
|
|
initial_liveness_2([], [], [], _ModuleInfo, Liveness, Liveness).
|
|
initial_liveness_2([V | Vs], [M | Ms], [T | Ts], ModuleInfo,
|
|
Liveness0, Liveness) :-
|
|
(
|
|
mode_to_arg_mode(ModuleInfo, M, T, top_in)
|
|
->
|
|
set__insert(Liveness0, V, Liveness1)
|
|
;
|
|
Liveness1 = Liveness0
|
|
),
|
|
initial_liveness_2(Vs, Ms, Ts, ModuleInfo, Liveness1, Liveness).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred initial_deadness(proc_info, live_info, module_info, set(prog_var)).
|
|
:- mode initial_deadness(in, in, in, out) is det.
|
|
|
|
initial_deadness(ProcInfo, LiveInfo, ModuleInfo, Deadness) :-
|
|
proc_info_headvars(ProcInfo, Vars),
|
|
proc_info_argmodes(ProcInfo, Modes),
|
|
proc_info_vartypes(ProcInfo, VarTypes),
|
|
map__apply_to_list(Vars, VarTypes, Types),
|
|
set__init(Deadness0),
|
|
% All output arguments are in the initial deadness.
|
|
(
|
|
initial_deadness_2(Vars, Modes, Types, ModuleInfo,
|
|
Deadness0, Deadness1)
|
|
->
|
|
Deadness2 = Deadness1
|
|
;
|
|
error("initial_deadness: list length mis-match")
|
|
),
|
|
|
|
% If doing alternate liveness, the corresponding
|
|
% typeinfos need to be added to these.
|
|
live_info_get_typeinfo_liveness(LiveInfo, TypeinfoLiveness),
|
|
(
|
|
TypeinfoLiveness = yes
|
|
->
|
|
proc_info_get_typeinfo_vars_setwise(ProcInfo, Deadness2,
|
|
TypeInfoVars),
|
|
set__union(Deadness2, TypeInfoVars, Deadness)
|
|
;
|
|
Deadness = Deadness2
|
|
).
|
|
|
|
:- pred initial_deadness_2(list(prog_var), list(mode), list(type),
|
|
module_info, set(prog_var), set(prog_var)).
|
|
:- mode initial_deadness_2(in, in, in, in, in, out) is semidet.
|
|
|
|
initial_deadness_2([], [], [], _ModuleInfo, Deadness, Deadness).
|
|
initial_deadness_2([V | Vs], [M | Ms], [T | Ts], ModuleInfo,
|
|
Deadness0, Deadness) :-
|
|
(
|
|
mode_to_arg_mode(ModuleInfo, M, T, top_out)
|
|
->
|
|
set__insert(Deadness0, V, Deadness1)
|
|
;
|
|
Deadness1 = Deadness0
|
|
),
|
|
initial_deadness_2(Vs, Ms, Ts, ModuleInfo, Deadness1, Deadness).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred add_liveness_after_goal(hlds_goal, set(prog_var), hlds_goal).
|
|
:- mode add_liveness_after_goal(in, in, out) is det.
|
|
|
|
add_liveness_after_goal(Goal - GoalInfo0, Residue, Goal - GoalInfo) :-
|
|
goal_info_get_post_births(GoalInfo0, PostBirths0),
|
|
set__union(PostBirths0, Residue, PostBirths),
|
|
goal_info_set_post_births(GoalInfo0, PostBirths, GoalInfo).
|
|
|
|
:- pred add_deadness_before_goal(hlds_goal, set(prog_var), hlds_goal).
|
|
:- mode add_deadness_before_goal(in, in, out) is det.
|
|
|
|
add_deadness_before_goal(Goal - GoalInfo0, Residue, Goal - GoalInfo) :-
|
|
goal_info_get_pre_deaths(GoalInfo0, PreDeaths0),
|
|
set__union(PreDeaths0, Residue, PreDeaths),
|
|
goal_info_set_pre_deaths(GoalInfo0, PreDeaths, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Given a list of variables and an instmap delta, determine which
|
|
% of those variables have a value given to them (i.e. they are bound
|
|
% or aliased; in the latter case the "value" is the location they
|
|
% should be stored in), and insert them into the accumulated set
|
|
% of value-given vars.
|
|
%
|
|
% We don't handle the aliasing part yet.
|
|
|
|
:- pred find_value_giving_occurrences(list(prog_var), live_info,
|
|
instmap_delta, set(prog_var), set(prog_var)).
|
|
:- mode find_value_giving_occurrences(in, in, in, in, out) is det.
|
|
|
|
find_value_giving_occurrences([], _, _, ValueVars, ValueVars).
|
|
find_value_giving_occurrences([Var | Vars], LiveInfo, InstMapDelta,
|
|
ValueVars0, ValueVars) :-
|
|
live_info_get_var_types(LiveInfo, VarTypes),
|
|
live_info_get_module_info(LiveInfo, ModuleInfo),
|
|
map__lookup(VarTypes, Var, Type),
|
|
(
|
|
instmap_delta_search_var(InstMapDelta, Var, Inst),
|
|
mode_to_arg_mode(ModuleInfo, (free -> Inst), Type, top_out)
|
|
->
|
|
set__insert(ValueVars0, Var, ValueVars1)
|
|
;
|
|
ValueVars1 = ValueVars0
|
|
),
|
|
find_value_giving_occurrences(Vars, LiveInfo, InstMapDelta,
|
|
ValueVars1, ValueVars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type live_info ---> live_info(
|
|
module_info,
|
|
proc_info,
|
|
bool, % Do we use typeinfo
|
|
% liveness for this
|
|
% proc?
|
|
map(prog_var, type),
|
|
prog_varset
|
|
).
|
|
|
|
:- pred live_info_init(module_info, proc_info, bool, map(prog_var, type),
|
|
prog_varset, live_info).
|
|
:- mode live_info_init(in, in, in, in, in, out) is det.
|
|
|
|
live_info_init(ModuleInfo, ProcInfo, TypeInfoLiveness, VarTypes, Varset,
|
|
live_info(ModuleInfo, ProcInfo, TypeInfoLiveness, VarTypes, Varset)).
|
|
|
|
:- pred live_info_get_module_info(live_info, module_info).
|
|
:- mode live_info_get_module_info(in, out) is det.
|
|
|
|
live_info_get_module_info(live_info(ModuleInfo, _, _, _, _), ModuleInfo).
|
|
|
|
:- pred live_info_get_proc_info(live_info, proc_info).
|
|
:- mode live_info_get_proc_info(in, out) is det.
|
|
|
|
live_info_get_proc_info(live_info(_, ProcInfo, _, _, _), ProcInfo).
|
|
|
|
:- pred live_info_get_typeinfo_liveness(live_info, bool).
|
|
:- mode live_info_get_typeinfo_liveness(in, out) is det.
|
|
|
|
live_info_get_typeinfo_liveness(live_info(_, _, TypeInfoLiveness, _, _),
|
|
TypeInfoLiveness).
|
|
|
|
:- pred live_info_get_var_types(live_info, map(prog_var, type)).
|
|
:- mode live_info_get_var_types(in, out) is det.
|
|
|
|
live_info_get_var_types(live_info(_, _, _, VarTypes, _), VarTypes).
|
|
|
|
:- pred live_info_get_varset(live_info, prog_varset).
|
|
:- mode live_info_get_varset(in, out) is det.
|
|
|
|
live_info_get_varset(live_info(_, _, _, _, Varset), Varset).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Get the nonlocals, and, if doing alternate liveness, add the
|
|
% typeinfo vars for the nonlocals.
|
|
|
|
:- pred liveness__get_nonlocals_and_typeinfos(live_info, hlds_goal_info,
|
|
set(prog_var)).
|
|
:- mode liveness__get_nonlocals_and_typeinfos(in, in, out) is det.
|
|
|
|
liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo,
|
|
NonLocals) :-
|
|
goal_info_get_code_gen_nonlocals(GoalInfo, NonLocals0),
|
|
live_info_get_typeinfo_liveness(LiveInfo, TypeinfoLiveness),
|
|
(
|
|
TypeinfoLiveness = yes
|
|
->
|
|
live_info_get_proc_info(LiveInfo, ProcInfo),
|
|
proc_info_get_typeinfo_vars_setwise(ProcInfo, NonLocals0,
|
|
TypeInfoVarsNonLocals),
|
|
set__union(NonLocals0, TypeInfoVarsNonLocals, NonLocals)
|
|
;
|
|
NonLocals = NonLocals0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|