[m-dev.] diff: more bug fixes for liveness.
Tyson Richard DOWD
trd at cs.mu.oz.au
Thu Jul 3 15:03:31 AEST 1997
I've added some documentation to this change, describing the invariant
that accurate GC needs, and how it is achieved.
I'll commit this change very soon.
===================================================================
Estimated hours taken: 5
Fix several bugs in liveness for accurate GC.
compiler/liveness.m:
- Add `liveness__get_nonlocals_and_typeinfos'.
- Call `liveness__get_nonlocals_and_typeinfos' instead of just
getting nonlocals. This removes a lot of repeated code (and
improves correctness).
- Intersect the initially live variables with the non-locals and
their typeinfo vars, not just the non-locals.
- Don't add corresponding typeinfos to the liveness after the
intersection (previously we did this, which "fixed" many
problems that doing the intersection wrong introduced, but
introduced a few bugs of its own).
tests/valid/Mmake:
tests/valid/agc_graph.m:
tests/valid/agc_ho_pred.m:
Test cases for these bugs.
Index: compiler/liveness.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/liveness.m,v
retrieving revision 1.75
diff -u -r1.75 liveness.m
--- liveness.m 1997/06/23 05:32:20 1.75
+++ liveness.m 1997/07/03 04:56:05
@@ -6,7 +6,7 @@
%
% File: liveness.m
%
-% Main authors: conway, zs.
+% 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.
@@ -40,15 +40,53 @@
% 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. Note that when using accurate gc,
-% a variable holding a typeinfo is live while any variable described
-% (in whole or in part) by that typeinfo is live.
+% 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.
+%
+% Accurate Garbage Collection Notes:
+%
+% Note that when using accurate gc, liveness is computed slightly
+% differently. The garbage collector needs access to the typeinfo
+% variables of any variable that could be garbage collected.
+%
+% Hence, the invariant needed for accurate GC is:
+% 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 accurate GC, 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.
+%
%-----------------------------------------------------------------------------%
@@ -112,7 +150,7 @@
Liveness, Goal - GoalInfo) :-
% work out which variables get born in this goal
- goal_info_get_nonlocals(GoalInfo0, NonLocals),
+ 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),
@@ -298,24 +336,11 @@
set__union(Deadness0, PostDeaths0, Deadness1),
set__difference(Deadness1, PostBirths0, Deadness2),
- goal_info_get_nonlocals(GoalInfo0, NonLocals0),
- live_info_get_module_info(LiveInfo, ModuleInfo),
- module_info_globals(ModuleInfo, Globals),
- globals__get_gc_method(Globals, GCmethod),
+ liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo0, NonLocals),
set__init(Empty),
(
goal_is_atomic(Goal0)
->
- (
- GCmethod = accurate
- ->
- live_info_get_proc_info(LiveInfo, ProcInfo),
- proc_info_get_typeinfo_vars_setwise(ProcInfo,
- NonLocals0, TypeInfoVars),
- set__union(NonLocals0, TypeInfoVars, NonLocals)
- ;
- NonLocals = NonLocals0
- ),
NewPreDeaths = Empty,
set__difference(NonLocals, Deadness2, NewPostDeaths),
set__union(Deadness2, NewPostDeaths, Deadness3),
@@ -348,14 +373,14 @@
detect_deadness_in_goal_2(disj(Goals0, SM), GoalInfo, Deadness0,
LiveInfo, Deadness, disj(Goals, SM)) :-
set__init(Union0),
- goal_info_get_nonlocals(GoalInfo, NonLocals),
+ 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),
- goal_info_get_nonlocals(GoalInfo, NonLocals),
+ liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo, NonLocals),
detect_deadness_in_cases(Var, Cases0, Deadness0, NonLocals,
LiveInfo, Union0, Deadness, Cases).
@@ -373,20 +398,7 @@
detect_deadness_in_goal(Cond0, DeadnessThen, LiveInfo,
DeadnessCond, Cond1),
- goal_info_get_nonlocals(GoalInfo, NonLocals0),
- live_info_get_module_info(LiveInfo, ModuleInfo),
- module_info_globals(ModuleInfo, Globals),
- globals__get_gc_method(Globals, GCmethod),
- (
- GCmethod = accurate
- ->
- live_info_get_proc_info(LiveInfo, ProcInfo),
- proc_info_get_typeinfo_vars_setwise(ProcInfo, NonLocals0,
- TypeInfoVars),
- set__union(NonLocals0, TypeInfoVars, NonLocals)
- ;
- NonLocals = NonLocals0
- ),
+ liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo, NonLocals),
set__union(DeadnessCond, DeadnessElse, Deadness),
set__intersect(Deadness, NonLocals, NonLocalDeadness),
@@ -799,27 +811,28 @@
;
error("initial_liveness: list length mismatch")
),
- % If doing accurate garbage collection, the corresponding
- % typeinfos need to be added to these.
module_info_globals(ModuleInfo, Globals),
globals__get_gc_method(Globals, GCmethod),
- (
- GCmethod = accurate
- ->
- proc_info_get_typeinfo_vars_setwise(ProcInfo, Liveness2,
- TypeInfoVars),
- set__union(Liveness2, TypeInfoVars, Liveness3)
- ;
- Liveness3 = Liveness2
- ),
+
% 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.
+ % So we intersect the headvars with the non-locals and
+ % their typeinfo vars.
proc_info_goal(ProcInfo, _Goal - GoalInfo),
- goal_info_get_nonlocals(GoalInfo, NonLocals),
- set__intersect(Liveness3, NonLocals, Liveness).
+ goal_info_get_nonlocals(GoalInfo, NonLocals0),
+ (
+ GCmethod = accurate
+ ->
+ 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(var), list(mode), list(type), module_info,
set(var), set(var)).
@@ -1005,5 +1018,30 @@
:- mode live_info_get_varset(in, out) is det.
live_info_get_varset(live_info(_, _, _, Varset), Varset).
+
+%-----------------------------------------------------------------------------%
+
+ % Get the nonlocals, and, if doing accurate GC, add the
+ % typeinfo vars for the nonlocals.
+
+:- pred liveness__get_nonlocals_and_typeinfos(live_info, hlds_goal_info,
+ set(var)).
+:- mode liveness__get_nonlocals_and_typeinfos(in, in, out) is det.
+liveness__get_nonlocals_and_typeinfos(LiveInfo, GoalInfo,
+ NonLocals) :-
+ live_info_get_module_info(LiveInfo, ModuleInfo),
+ module_info_globals(ModuleInfo, Globals),
+ globals__get_gc_method(Globals, GCmethod),
+ goal_info_get_nonlocals(GoalInfo, NonLocals0),
+ (
+ GCmethod = accurate
+ ->
+ live_info_get_proc_info(LiveInfo, ProcInfo),
+ proc_info_get_typeinfo_vars_setwise(ProcInfo, NonLocals0,
+ TypeInfoVarsNonLocals),
+ set__union(NonLocals0, TypeInfoVarsNonLocals, NonLocals)
+ ;
+ NonLocals = NonLocals0
+ ).
%-----------------------------------------------------------------------------%
Index: tests/valid/Mmake
===================================================================
RCS file: /home/staff/zs/imp/tests/valid/Mmake,v
retrieving revision 1.43
diff -u -r1.43 Mmake
--- Mmake 1997/06/29 23:23:30 1.43
+++ Mmake 1997/07/02 05:22:26
@@ -8,6 +8,8 @@
# please keep this list sorted
SOURCES= \
+ agc_graph.m \
+ agc_ho_pred.m \
agc_ite.m \
agc_unbound_typevars.m \
agc_unbound_typevars2.m \
@@ -81,6 +83,8 @@
# some regression tests only failed with particular options enabled
# (please keep this list sorted)
+GRADE-agc_graph = asm_fast.agc
+GRADE-agc_ho_pred = asm_fast.agc
GRADE-agc_ite = asm_fast.agc
GRADE-agc_unbound_typevars = asm_fast.agc
GRADE-agc_unbound_typevars2 = asm_fast.agc
New File: tests/valid/agc_graph.m
===================================================================
%
% Regression test.
%
% Name: agc_graph.m
%
% Description of bug:
% The liveness of polymorphic type variables for accurate gc
% wasn't being computed correctly.
% It was being removed from the initial_liveness for not being
% part of the non-locals (like an unused variable).
%
% Symptom(s) of bug:
% % Allocating storage locations for live vars in predicate
% `agc_graph:insert_node/4' mode 0
% Software error: variable TypeInfo_for_A (18) not found
%
% Date bug existed: 1-July-1997
%
% Author: trd
%
% Note: This code was taken from library/graph.m and simplified.
%
:- module agc_graph.
:- interface.
:- import_module std_util.
:- type graph(N, A).
:- type node(N).
:- type arc(A).
:- type graph(N) == graph(N, unit).
:- type arc == arc(unit).
:- pred agc_graph__insert_node(graph(N, A), N, node(N), graph(N, A)).
:- mode agc_graph__insert_node(in, in, out, out) is semidet.
%------------------------------------------------------------------------------%
:- implementation.
:- import_module map, int, std_util, list.
:- import_module require.
:- type graph(N, A) --->
graph(
agc_graph__node_supply,
agc_graph__arc_supply,
map(node(N), N),
map(arc(A), arc_info(N, A)),
map(node(N), map(arc(A), node(N)))
).
:- type agc_graph__node_supply == int.
:- type agc_graph__arc_supply == int.
:- type node(N) == int.
:- type arc(A) == int.
:- type arc_info(N, A) ---> arc_info(node(N), node(N), A).
%------------------------------------------------------------------------------%
agc_graph__insert_node(G, NInfo, N, G) :-
G = graph(_, _, Nodes0, _, _),
agc_graph__get_node_supply(G, N),
agc_graph__get_edges(G, Edges0),
\+ map__member(Nodes0, _, NInfo),
map__init(Edges0).
%------------------------------------------------------------------------------%
:- pred agc_graph__get_node_supply(graph(N, A), agc_graph__node_supply).
:- mode agc_graph__get_node_supply(in, out) is det.
agc_graph__get_node_supply(G, NS) :-
G = graph(NS, _AS, _N, _A, _E).
:- pred agc_graph__get_edges(graph(N, A), map(node(N), map(arc(A), node(N)))).
:- mode agc_graph__get_edges(in, out) is det.
agc_graph__get_edges(G, E) :-
G = graph(_NS, _AS, _N, _A, E).
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
New File: tests/valid/agc_ho_pred.m
===================================================================
%
% Regression test.
%
% Name: agc_ho_pred.m
%
% Description of bug:
% The liveness of a typeinfo variable was being computed
% incorrectly.
% This was caused because the variable TypeInfo_for_OptionType was
% incorrectly added to the initial_liveness. It was assumed that
% all type variables for the types of the input arguments would
% have corresponding typeinfos as input. This is incorrect, higher
% order preds do not have typeinfo variables as input.
%
% Symptom(s) of bug:
% % Generating code for predicate
% `agc_ho_pred:agc_ho_pred__LambdaGoal__1/2'
% Software error: variable TypeInfo_for_OptionType (16) not found
%
% This code was taken from library/getopt.m, and simplified.
:- module agc_ho_pred.
:- interface.
:- import_module bool, char, list, map, std_util.
:- type option_ops(OptionType)
---> option_ops(
pred(char, OptionType), % short_option
pred(string, OptionType), % long_option
pred(OptionType, option_data) % option_default
)
; option_ops(
pred(char, OptionType), % short_option
pred(string, OptionType), % long_option
pred(OptionType, option_data), % option_default
pred(OptionType, special_data, % special option handler
option_table(OptionType),
maybe_option_table(OptionType))
).
:- inst option_ops =
bound((
option_ops(
pred(in, out) is semidet, % short_option
pred(in, out) is semidet, % long_option
pred(out, out) is nondet % option_default
)
; option_ops(
pred(in, out) is semidet, % short_option
pred(in, out) is semidet, % long_option
pred(out, out) is nondet, % option_default
pred(in, in, in, out) is semidet% special handler
)
)).
:- type option_data
---> bool(bool)
; int(int)
; string(string)
; maybe_string(maybe(string))
; accumulating(list(string))
; special
; bool_special
; int_special
; string_special.
:- type special_data
---> none
; bool(bool)
; int(int)
; string(string).
:- type option_table(OptionType)
== map(OptionType, option_data).
:- type maybe_option_table(OptionType)
---> ok(option_table(OptionType))
; error(string).
:- pred agc_ho_pred__process_options(
option_ops(OptionType)::in(option_ops),
list(pair(OptionType, option_data))::out
) is det.
:- implementation.
agc_ho_pred__process_options(OptionOps, Args) :-
(
OptionOps = option_ops(_, _, OptionDefaultsPred)
;
OptionOps = option_ops(_, _, OptionDefaultsPred, _)
),
solutions(lambda([OptionDataPair::out] is nondet, (
OptionDataPair = Option - OptionData,
call(OptionDefaultsPred, Option, OptionData)
)), OptionDefaultsList),
Args = OptionDefaultsList.
--
Tyson Dowd # Assimilation doesn't kill people --
# resistance kills people.
trd at cs.mu.oz.au #
http://www.cs.mu.oz.au/~trd # (Seen on back of Borg cube.)
More information about the developers
mailing list