mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 09:53:36 +00:00
Estimated hours taken: 20
Branches: main
Implement some of Mark's wish list for making user events more useful.
1. When executing "print *" in mdb, we used to print both the values of all
attributes and the values of all live variables. Since some attributes'
values were given directly by live variables, this lead to some things being
printed twice. This diff eliminates this duplication.
2. At user events, we now print the name of the event. Whether we print the
other stuff we also print at events (the predicate containing the event,
and its source location) is now controlled by a new mdb command,
"user_event_context".
3. We would like different solvers to be compilable independently of one
another. This means that neither solver's event set should depend on the
existence of the events needed by the other solvers. This diff therefore
eliminates the requirement that all modules of the program be compiled with
the same event set specification. Instead, a program may contain modules
that were compiled with different event sets. Each event set is named;
the new requirement is that different named event sets may coexist in the
program (each being used to compile some modules), but two event sets with
the same name must be identical in all other respects as well (we need this
requirement to prevent inconsistencies arising between different versions of
the same event set).
4. We now generate user events even from modules compiled with --trace shallow.
The problem here is that user events can occur in procedures that do not
get caller events and whose ancestors may not get caller events either.
Yet these procedures must still pass on debugger information such as call
sequence numbers and the call depth to the predicate with the user event.
This diff therefore decouples the generation of code for this basic debugger
infrastructure information from the generation of call events by inventing
two new trace levels, settable by the compiler only (i.e. not from the
command line). The trace level "basic_user" is for procedures containing a
user event whose trace level (in a shallow traced module) would otherwise be
"none". The trace level "basic" is for procedures not containing a user
event but which nevertheless may need to transmit information (e.g. depth)
to a user event. For the foreseeable future, this means that shallow traced
modules containing user events will have some debugging overhead compiled
into *all* their procedures.
runtime/mercury_stack_layout.h:
Add a new field to MR_UserEvent structures, giving the HLDS number of
the variable representing each attribute.
Add a new field to module layout structures, giving the name of the
event set (if any) the module was compiled with.
Add the new trace levels to the MR_TraceLevel type.
Update the current layout structure version number.
runtime/mercury_stack_trace.[ch]:
Allow the printing of the containing predicate's name and/or the
filename/linenumber context to be turned off when printing contexts.
Factor out some of the code involved in this printing.
Give a bunch of variables better names.
Rename a type to get rid of unnecessary underscores.
compiler/prog_data.m:
compiler/prog_event.m:
Include the event set name in the information we have about the event
set.
compiler/simplify.m:
Mark each procedure and each module that contains user events
as containing user events.
Use the same technique to mark each procedure that contains parallel
conjunctions as containing parallel conjunctions, instead of marking
the predicate containing the procedure. (Switch detection may eliminate
arbitrary goals, including parallel conjunctions, from switch arms
that are unreachable due to initial insts, and in any case we want to
handle the procedures of a predicate independently from each other
after mode analysis.)
Also, change the code handling generic calls to switch on the generic
call kind, and factor out some common code.
compiler/hlds_module.m:
compiler/hlds_pred.m:
Provide slots in the proc_info and the module_info for the information
gathered by simplify.
compiler/trace_params.m:
Implement the new trace levels described above. This required changing
the signature of some of the predicates of this module.
compiler/code_info.m:
Record whether the compiler generated any trace events. We need to know
this, because if it did, then we must generate a proc layout structure
for it.
compiler/proc_gen.m:
Act on the information recorded by code_info.m.
Factor out the code for generating the call event and its layout
structure, since the conditions for generating this event have changed.
compiler/continuation_info.m:
compiler/call_gen.m:
For each user event, record the id of the variables corresponding to
each argument of a user event.
compiler/layout.m:
compiler/layout_out.m:
compiler/stack_layout.m:
Generate the new field (giving the HLDS variable number of each
attribute) in user event structures, and the new field (event set name)
in module layout structures.
Allow the call event's layout structure to be missing. This is needed
for user events in shallow traced modules.
compiler/options.m:
compiler/handle_options.m:
compiler/mercury_compiler.m:
Rename the option for specifying event sets from --event-spec-file-name
to --event-set-file-name, since it specifies only one event set, not
all events.
compiler/jumpopt.m:
Give some predicates better names.
compiler/dep_par_conj.m:
compiler/deforest.m:
compiler/granularity.m:
compiler/hlds_out.m:
compiler/inlining.m:
compiler/intermod.m:
compiler/lambda.m:
compiler/liveness.m:
compiler/modes.m:
compiler/opt_debug.m:
compiler/optimize.m:
compiler/size_proc.m:
compiler/stack_alloc.m:
compiler/store_alloc.m:
compiler/table_gen.m:
compiler/trace_gen.m:
compiler/typecheck.m:
Conform to the changes above.
doc/mdb_categories:
Mention the new mdb command.
doc/user_guide.texi:
Update the documentation of user events to account for the changes
above.
trace/mercury_event_parser.y:
trace/mercury_event_scanner.l:
Modify the grammar for event set specifications to a name for the
event set.
trace/mercury_event_spec.[ch]:
Instead of recording information about event sets internally
in this module, return a representation of each event set read in
to the callers, for them to do with as they please.
Include the event set name when we print the Mercury term for
compiler/prog_event.m.
trace/mercury_trace.c:
Do not assume that every procedure that contains an event contains a
call event (and hence a call event layout structure), since that
is not true anymore.
trace/mercury_trace_cmd_parameter.[ch]:
Implement the new mdb command "user_event_context".
trace/mercury_trace_cmd_internal.[ch]:
Include "user_event_context" in the list of mdb commands.
Print the user event name at user events. Let the current setting
of the user_event_context mdb command determine what else to print
at such events.
Instead of reading in one event set on initialization, read in
all event sets that occur in the program.
trace/mercury_trace_tables.[ch]:
Allow the gathering of information for more than one event set
from the modules of the program.
trace/mercury_trace_vars.[ch]:
For each attribute value of a user event, record what the HLDS variable
number of the attribute is. When printing all variables at an event,
mark the variable numbers of printed attributes as being printed
already, causing the variable with the same number not to be printed.
Include the name of the variable (if it has one) in the description
of an attribute. Without this, users may wonder why the value of the
variable wasn't printed.
trace/mercury_trace_cmd_browsing.[ch]:
Pass the current setting of the user_event_context mdb command to
runtime/mercury_stack_trace.c when printing the context of an event.
tests/debugger/user_event_shallow.{m,inp,exp}:
New test case to test the new functionality. This test case is the same
as the user_event test case, but it is compiled with shallow tracing,
and its mdb input exercises the user_event_context mdb command.
tests/debugger/user_event_spec:
tests/invalid/invalid_event_spec:
Update these event set spec files by adding an event set name.
tests/debugger/Mmakefile:
tests/debugger/Mercury.options:
Enable the new test case.
tests/debugger/user_event.exp:
Update the expected output of the old user event test case, which now
prints event names, but doesn't print attribute values twice.
tests/debugger/completion.exp:
Expect the new "user_event_context" mdb command in the command list.
tests/debugger/mdb_command_test.inp:
Test the existence of the documentation for the new mdb command.
tests/invalid/Mercury.options:
Conform to the name change of the --event-spec-file-name option.
269 lines
11 KiB
Mathematica
269 lines
11 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2002-2006 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 stack_alloc.m.
|
|
% Authors: zs, conway.
|
|
%
|
|
% This module allocates stack slots to the variables that need to be saved
|
|
% across a call, across a goal that may fail, or in a parallel conjunction.
|
|
%
|
|
% The jobs is done in two steps. First we traverse the predicate definition
|
|
% looking for sets of variables that must be saved on the stack at the same
|
|
% time. If --optimize-stack-slots is set, then this phase is done by
|
|
% stack_opt.m; if --optimize-stack-slots is not set, then it is done by this
|
|
% module. Then we use a graph colouring algorithm to find an allocation of
|
|
% stack slots (colours) to variables such that in each set of variables that
|
|
% must be saved at the same time, each variable has a different colour.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.stack_alloc.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
|
|
:- import_module io.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred allocate_stack_slots_in_proc(pred_id::in, proc_id::in, module_info::in,
|
|
proc_info::in, proc_info::out, io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.graph_colour.
|
|
:- import_module libs.options.
|
|
:- import_module libs.trace_params.
|
|
:- import_module ll_backend.live_vars.
|
|
:- import_module ll_backend.liveness.
|
|
:- import_module ll_backend.trace_gen.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module set.
|
|
:- import_module svmap.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
allocate_stack_slots_in_proc(PredId, _ProcId, ModuleInfo, !ProcInfo, !IO) :-
|
|
initial_liveness(!.ProcInfo, PredId, ModuleInfo, Liveness0),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_trace_level(Globals, TraceLevel),
|
|
(
|
|
eff_trace_level_needs_fail_vars(ModuleInfo, PredInfo, !.ProcInfo,
|
|
TraceLevel) = yes
|
|
->
|
|
trace_fail_vars(ModuleInfo, !.ProcInfo, FailVars)
|
|
;
|
|
set.init(FailVars)
|
|
),
|
|
body_should_use_typeinfo_liveness(PredInfo, Globals, TypeInfoLiveness),
|
|
globals.lookup_bool_option(Globals, opt_no_return_calls,
|
|
OptNoReturnCalls),
|
|
AllocData = alloc_data(ModuleInfo, !.ProcInfo, TypeInfoLiveness,
|
|
OptNoReturnCalls),
|
|
set.init(NondetLiveness0),
|
|
SimpleStackAlloc0 = stack_alloc(set.make_singleton_set(FailVars)),
|
|
proc_info_get_goal(!.ProcInfo, Goal0),
|
|
build_live_sets_in_goal_no_par_stack(Goal0, Goal, FailVars, AllocData,
|
|
SimpleStackAlloc0, SimpleStackAlloc, Liveness0, _Liveness,
|
|
NondetLiveness0, _NondetLiveness),
|
|
proc_info_set_goal(Goal, !ProcInfo),
|
|
SimpleStackAlloc = stack_alloc(LiveSets0),
|
|
|
|
do_we_need_maxfr_slot(Globals, ModuleInfo, PredInfo, !ProcInfo),
|
|
trace_reserved_slots(ModuleInfo, PredInfo, !.ProcInfo, Globals,
|
|
NumReservedSlots, MaybeReservedVarInfo),
|
|
(
|
|
MaybeReservedVarInfo = yes(ResVar - _),
|
|
set.singleton_set(ResVarSet, ResVar),
|
|
set.insert(LiveSets0, ResVarSet, LiveSets1)
|
|
;
|
|
MaybeReservedVarInfo = no,
|
|
LiveSets1 = LiveSets0
|
|
),
|
|
proc_info_get_vartypes(!.ProcInfo, VarTypes),
|
|
filter_out_dummy_values(ModuleInfo, VarTypes, LiveSets1, LiveSets,
|
|
DummyVars),
|
|
graph_colour.group_elements(LiveSets, ColourSets),
|
|
set.to_sorted_list(ColourSets, ColourList),
|
|
proc_info_interface_code_model(!.ProcInfo, CodeModel),
|
|
allocate_stack_slots(ColourList, CodeModel, NumReservedSlots,
|
|
MaybeReservedVarInfo, StackSlots1),
|
|
allocate_dummy_stack_slots(DummyVars, CodeModel, -1,
|
|
StackSlots1, StackSlots),
|
|
proc_info_set_stack_slots(StackSlots, !ProcInfo).
|
|
|
|
:- pred filter_out_dummy_values(module_info::in, vartypes::in,
|
|
set(set(prog_var))::in, set(set(prog_var))::out,
|
|
list(prog_var)::out) is det.
|
|
|
|
filter_out_dummy_values(ModuleInfo, VarTypes, LiveSet0, LiveSet, DummyVars) :-
|
|
set.to_sorted_list(LiveSet0, LiveList0),
|
|
filter_out_dummy_values_2(ModuleInfo, VarTypes, LiveList0, LiveList,
|
|
set.init, Dummies),
|
|
set.list_to_set(LiveList, LiveSet),
|
|
set.to_sorted_list(Dummies, DummyVars).
|
|
|
|
:- pred filter_out_dummy_values_2(module_info::in, vartypes::in,
|
|
list(set(prog_var))::in, list(set(prog_var))::out,
|
|
set(prog_var)::in, set(prog_var)::out) is det.
|
|
|
|
filter_out_dummy_values_2(_, _VarTypes, [], [], !Dummies).
|
|
filter_out_dummy_values_2(ModuleInfo, VarTypes,
|
|
[LiveSet0 | LiveSets0], LiveSets, !Dummies) :-
|
|
filter_out_dummy_values_2(ModuleInfo, VarTypes, LiveSets0, LiveSets1,
|
|
!Dummies),
|
|
set.to_sorted_list(LiveSet0, LiveList0),
|
|
list.filter(var_is_of_dummy_type(ModuleInfo, VarTypes), LiveList0,
|
|
DummyVars, NonDummyVars),
|
|
set.insert_list(!.Dummies, DummyVars, !:Dummies),
|
|
(
|
|
NonDummyVars = [],
|
|
LiveSets = LiveSets1
|
|
;
|
|
NonDummyVars = [_ | _],
|
|
LiveSets = [list_to_set(NonDummyVars) | LiveSets1]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type stack_alloc
|
|
---> stack_alloc(
|
|
set(set(prog_var)) % The sets of vars that need to be
|
|
% on the stack at the same time.
|
|
).
|
|
|
|
:- instance stack_alloc_info(stack_alloc) where [
|
|
pred(at_call_site/4) is alloc_at_call_site,
|
|
pred(at_resume_site/4) is alloc_at_resume_site,
|
|
pred(at_par_conj/4) is alloc_at_par_conj
|
|
].
|
|
|
|
:- pred alloc_at_call_site(need_across_call::in, hlds_goal_info::in,
|
|
stack_alloc::in, stack_alloc::out) is det.
|
|
|
|
alloc_at_call_site(NeedAtCall, _GoalInfo, StackAlloc0, StackAlloc) :-
|
|
NeedAtCall = need_across_call(ForwardVars, ResumeVars, NondetLiveVars),
|
|
LiveSet = set.union_list([ForwardVars, ResumeVars, NondetLiveVars]),
|
|
StackAlloc0 = stack_alloc(LiveSets0),
|
|
LiveSets = set.insert(LiveSets0, LiveSet),
|
|
StackAlloc = stack_alloc(LiveSets).
|
|
|
|
:- pred alloc_at_resume_site(need_in_resume::in, hlds_goal_info::in,
|
|
stack_alloc::in, stack_alloc::out) is det.
|
|
|
|
alloc_at_resume_site(NeedAtResume, _GoalInfo, StackAlloc0, StackAlloc) :-
|
|
NeedAtResume = need_in_resume(ResumeOnStack, ResumeVars, NondetLiveVars),
|
|
(
|
|
ResumeOnStack = no,
|
|
StackAlloc = StackAlloc0
|
|
;
|
|
ResumeOnStack = yes,
|
|
LiveSet = set.union(ResumeVars, NondetLiveVars),
|
|
StackAlloc0 = stack_alloc(LiveSets0),
|
|
LiveSets = set.insert(LiveSets0, LiveSet),
|
|
StackAlloc = stack_alloc(LiveSets)
|
|
).
|
|
|
|
:- pred alloc_at_par_conj(need_in_par_conj::in, hlds_goal_info::in,
|
|
stack_alloc::in, stack_alloc::out) is det.
|
|
|
|
alloc_at_par_conj(NeedParConj, _GoalInfo, StackAlloc0, StackAlloc) :-
|
|
NeedParConj = need_in_par_conj(StackVars),
|
|
StackAlloc0 = stack_alloc(LiveSets0),
|
|
LiveSets = set.insert(LiveSets0, StackVars),
|
|
StackAlloc = stack_alloc(LiveSets).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred allocate_stack_slots(list(set(prog_var))::in, code_model::in, int::in,
|
|
maybe(pair(prog_var, int))::in, stack_slots::out) is det.
|
|
|
|
allocate_stack_slots(ColourList, CodeModel, NumReservedSlots,
|
|
MaybeReservedVarInfo, StackSlots) :-
|
|
% The reserved slots are referred to by fixed number
|
|
% (e.g. framevar(1)) in trace.setup.
|
|
FirstVarSlot = NumReservedSlots + 1,
|
|
allocate_stack_slots_2(ColourList, CodeModel, FirstVarSlot,
|
|
MaybeReservedVarInfo, map.init, StackSlots).
|
|
|
|
:- pred allocate_stack_slots_2(list(set(prog_var))::in, code_model::in,
|
|
int::in, maybe(pair(prog_var, int))::in,
|
|
stack_slots::in, stack_slots::out) is det.
|
|
|
|
allocate_stack_slots_2([], _, _, _, !StackSlots).
|
|
allocate_stack_slots_2([Vars | VarSets], CodeModel, N0, MaybeReservedVarInfo,
|
|
!StackSlots) :-
|
|
(
|
|
MaybeReservedVarInfo = yes(ResVar - ResSlotNum),
|
|
set.member(ResVar, Vars)
|
|
->
|
|
SlotNum = ResSlotNum,
|
|
N1 = N0
|
|
;
|
|
SlotNum = N0,
|
|
N1 = N0 + 1
|
|
),
|
|
set.to_sorted_list(Vars, VarList),
|
|
allocate_same_stack_slot(VarList, CodeModel, SlotNum, !StackSlots),
|
|
allocate_stack_slots_2(VarSets, CodeModel, N1, MaybeReservedVarInfo,
|
|
!StackSlots).
|
|
|
|
:- pred allocate_same_stack_slot(list(prog_var)::in, code_model::in, int::in,
|
|
stack_slots::in, stack_slots::out) is det.
|
|
|
|
allocate_same_stack_slot([], _CodeModel, _Slot, !StackSlots).
|
|
allocate_same_stack_slot([Var | Vars], CodeModel, Slot, !StackSlots) :-
|
|
( CodeModel = model_non ->
|
|
Locn = nondet_slot(Slot)
|
|
;
|
|
Locn = det_slot(Slot)
|
|
),
|
|
svmap.det_insert(Var, Locn, !StackSlots),
|
|
allocate_same_stack_slot(Vars, CodeModel, Slot, !StackSlots).
|
|
|
|
% We must not allocate the same stack slot to dummy variables. If we do,
|
|
% then the code that saves variables on the stack at calls will get
|
|
% confused. After saving one dummy variable on the stack, it will try
|
|
% to save the next in the same stack slot; believing the first variable
|
|
% to still be live, it will move it away.
|
|
%
|
|
% In ordinary grades, it is possible to have one value of type io.state
|
|
% and another of type store.store live at the same time; in debugging
|
|
% grades, due to our policy of extending variable lifetimes, more than
|
|
% one io.state may be live at the same time.
|
|
%
|
|
:- pred allocate_dummy_stack_slots(list(prog_var)::in, code_model::in,
|
|
int::in, stack_slots::in, stack_slots::out) is det.
|
|
|
|
allocate_dummy_stack_slots([], _, _, !StackSlots).
|
|
allocate_dummy_stack_slots([Var | Vars], CodeModel, N0, !StackSlots) :-
|
|
allocate_same_stack_slot([Var], CodeModel, N0, !StackSlots),
|
|
allocate_dummy_stack_slots(Vars, CodeModel, N0 - 1, !StackSlots).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module stack_alloc.
|
|
%-----------------------------------------------------------------------------%
|