mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 23:05:21 +00:00
Estimated hours taken: 50
This change introduces interface tracing, and makes it possible to successfully
bootstrap the compiler with tracing (either interface or full).
compiler/options.m:
Change the bool options --generate-trace into a string option --trace
with three valid values: minimal, interface and full. The last two mean
what they say; the intention is that eventually minimal will mean
no tracing in non-tracing grades and interface tracing in tracing
grades.
compiler/globals.m:
Add a new global for the trace level.
compiler/handle_options.m:
Convert the argument of --trace to a trace level.
Use only consistent 4-space indentation in the deeply nested
if-then-else.
compiler/trace.m:
Implement interface tracing.
Rename trace__generate_depth_reset_code as trace__prepare_for_call,
since it does more than reset the depth if this module is compiled
with interface tracing.
Do not check whether tracing is enabled before calling MR_trace;
let MR_trace make the check. This trades increased non-tracing
execution time for a substantial code size reduction (which may
in turn benefit execution time).
compiler/call_gen.m:
Call trace__generate_depth_reset_code by its new name.
compiler/code_info.m:
Fix a bug in the handling of non/semi commits. When entering a commit,
we used to push a clone of whatever the top failure continuation was.
However, the resume setup for this continuation could have started
with a label that assumed that the resume vars were in their original
locations (which are often registers), whereas the method of
backtracking to that point only guarantees the survival of stack slots,
not registers.
(This bug caused two lines of incorrect code to be generated among
the approx 30 million lines of code in the stage 2 compiler when
compiled with tracing.)
Fix another bug (previously untriggered as far as I know) in the
handling of multi/det commits. This one was breaking the invariant
that the resume vars set of each entry on the failure continuation
stack included the resume vars set of every other entry below it,
which meant that the values of these resume vars were not guaranteed
to be preserved.
compiler/stack_layout.m:
Make layout structures local to their module. They are not (yet)
referred to by name from other modules, and by declaring them
to be global we caused their names to be included even in stripped
executables, adding several megabytes to the size of the binary.
(The names are not stripped because a dynamically linked library
may want to refer to them.)
Change the mercury_data__stack_layout__ prefix on the names of
generated globals vars to just mercury_data__layout__. It is now
merely too long instead of far too long.
Include the label number in the label layout structure and the number
of typeinfo variables in a var_info structure only with native gc.
Their only use is in debugging native gc.
Fix some documentation rot.
compiler/llds.m:
Add a new field to the pragma_c instruction that says whether the
compiler-generated C code fragments access any stack variables.
compiler/frameopt.m:
Use the new field in pragma_c's to avoid a bug. Because frameopt was
assuming that the pragma_c instruction that filled in the stack slots
containing the call sequence number and depth did not access the stack,
it moved the pragma_c before the incr_sp that allocates the frame
(it was trying to get it out of the loop).
compiler/*.m:
Minor changes to set or ignore the extra field in pragma_c, to refer
to layout structures via the new prefix, or to handle the --trace
option.
doc/user_guide.texi:
Update the documentation for --trace.
runtime/mercury_types.h:
Add the type Unsigned.
runtime/mercury_goto.h:
Use the shorter layout prefix.
runtime/mercury_stack_layout.h:
Use the shorter layout prefix, and include the label number only with
native gc.
runtime/mercury_trace.[ch]:
runtime/mercury_trace_internal.[ch]:
runtime/mercury_trace_external.[ch]:
runtime/mercury_trace_util.[ch]:
Divide the old mercury_trace.[ch] into several components, with one
module for the internal debugger, one for the interface to the
external debugger, one for utilities needed by both. Mercury_trace.c
now has only the top-level stuff that steers between the two
debuggers.
runtime/mercury_trace.[ch]:
Add the new global variable MR_trace_from_full. Before each call,
the calling procedure assigns TRUE to this variable if the caller
is fully traced, and FALSE otherwise. Interface traced procedures
generate trace events only if this variable is TRUE when they are
called (fully traced callee procedures ignore the initial value of
the variable).
Make MR_trace return immediately without doing anything unless
tracing is enabled and a new extra argument to MR_trace is TRUE.
This extra argument is always TRUE for trace events in fully traced
procedures, while for trace events from interface traced procedures,
its value is set from the value of MR_trace_from_full at the time
that the procedure was called (i.e. the event is ignored unless the
interface traced procedure was called from a fully traced procedure).
runtime/mercury_trace.[ch]:
runtime/mercury_trace_internal.[ch]:
For global variables that are stored in stack slots, make their type
Word rather than int.
Use a new function MR_trace_event_report instead of calling
MR_trace_event with a NULL command structure pointer to indicate
that the event is to be reported but there is to be no user
interaction.
Use %ld formats in printfs and casts to long for better portability.
runtime/mercury_trace_internal.c:
Save trace-related globals across calls to Mercury library code
in the debugger, since otherwise any trace events in this code
could screw up e.g. the event number or the call number sequence.
Create separate functions for printing port names and determinisms.
runtime/mercury_wrapper.h:
Disable the tracing of the initialization and finalization code
written in Mercury.
runtime/Mmakefile:
Update for the new source and header files.
tests/debugger/{debugger_regs,interpreter,queens}_lib.{m,inp,exp}:
One new copy of each existing test case. These ones are intended
to be used when the stage 2 library is compiled with tracing, which
affects the tests by adding events for the library procedures called
from the test programs.
The .m files are the same as before; one of the .inp files is a bit
different; the .exp files reflect the correct output when the library
is compiled with full tracing.
tests/debugger/Mmakefile:
Provide separate targets for the new set of test cases.
Use --trace full instead of --generate-trace.
tests/debugger/runtests:
Try both the new set of test cases if the old set fails, and report
failure only if both sets fail. This is simpler than trying to figure
out which set should be really tested, and the probability of a false
positive is negligible.
3096 lines
106 KiB
Mathematica
3096 lines
106 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-1998 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: code_info.m
|
|
%
|
|
% Main authors: conway, zs.
|
|
%
|
|
% This file defines the code_info type and various operations on it.
|
|
% The code_info structure is the 'state' of the code generator.
|
|
%
|
|
% This file is organized into eight submodules:
|
|
%
|
|
% - the code_info structure and its access predicates
|
|
% - simple wrappers around access predicates
|
|
% - handling failure continuations
|
|
% - handling liveness issues
|
|
% - saving and restoring heap pointers, trail tickets etc
|
|
% - interfacing to code_exprn
|
|
% - managing the info required by garbage collection and value numbering
|
|
% - managing stack slots
|
|
%
|
|
% Note: Any new "state" arguments (eg counters) that should be strictly
|
|
% threaded (for example the counter used for allocating new labels) will
|
|
% require changes to code_info__slap_code_info/3.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module code_info.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds_module, hlds_pred, hlds_goal, llds, instmap, trace.
|
|
:- import_module continuation_info, prog_data, hlds_data, globals.
|
|
|
|
:- import_module bool, set, varset, list, map, term, std_util, assoc_list.
|
|
|
|
:- implementation.
|
|
|
|
:- import_module code_util, code_exprn, prog_out.
|
|
:- import_module arg_info, type_util, mode_util, options.
|
|
|
|
:- import_module set, varset, stack.
|
|
:- import_module string, require, char, bimap, tree, int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Submodule for the code_info type and its access predicates.
|
|
%
|
|
% This submodule has the following components:
|
|
%
|
|
% declarations for exported access predicates
|
|
% declarations for non-exported access predicates
|
|
% the definition of the type and the init predicate
|
|
% the definition of the get access predicates
|
|
% the definition of the set access predicates
|
|
%
|
|
% Please keep the order of mention of the various fields
|
|
% consistent in each of these five components.
|
|
|
|
:- interface.
|
|
|
|
:- type code_info.
|
|
|
|
% Create a new code_info structure.
|
|
:- pred code_info__init(varset, set(var), stack_slots, bool, globals,
|
|
pred_id, proc_id, proc_info, instmap, follow_vars, module_info,
|
|
int /* cell number */, code_info).
|
|
:- mode code_info__init(in, in, in, in, in, in, in, in, in, in, in, in,
|
|
out) is det.
|
|
|
|
% Get the variables for the current procedure.
|
|
:- pred code_info__get_varset(varset, code_info, code_info).
|
|
:- mode code_info__get_varset(out, in, out) is det.
|
|
|
|
% Get the id of the predicate we are generating code for.
|
|
:- pred code_info__get_pred_id(pred_id, code_info, code_info).
|
|
:- mode code_info__get_pred_id(out, in, out) is det.
|
|
|
|
% Get the id of the procedure we are generating code for.
|
|
:- pred code_info__get_proc_id(proc_id, code_info, code_info).
|
|
:- mode code_info__get_proc_id(out, in, out) is det.
|
|
|
|
% Get the HLDS of the procedure we are generating code for.
|
|
:- pred code_info__get_proc_info(proc_info, code_info, code_info).
|
|
:- mode code_info__get_proc_info(out, in, out) is det.
|
|
|
|
% Get the flag that indicates whether succip is used or not.
|
|
:- pred code_info__get_succip_used(bool, code_info, code_info).
|
|
:- mode code_info__get_succip_used(out, in, out) is det.
|
|
|
|
% Get the HLDS of the entire module.
|
|
:- pred code_info__get_module_info(module_info, code_info, code_info).
|
|
:- mode code_info__get_module_info(out, in, out) is det.
|
|
|
|
% Get the set of currently forward-live variables.
|
|
:- pred code_info__get_forward_live_vars(set(var), code_info, code_info).
|
|
:- mode code_info__get_forward_live_vars(out, in, out) is det.
|
|
|
|
% Set the set of currently forward-live variables.
|
|
:- pred code_info__set_forward_live_vars(set(var), code_info, code_info).
|
|
:- mode code_info__set_forward_live_vars(in, in, out) is det.
|
|
|
|
% Get the table mapping variables to the current
|
|
% instantiation states.
|
|
:- pred code_info__get_instmap(instmap, code_info, code_info).
|
|
:- mode code_info__get_instmap(out, in, out) is det.
|
|
|
|
% Set the table mapping variables to the current
|
|
% instantiation states.
|
|
:- pred code_info__set_instmap(instmap, code_info, code_info).
|
|
:- mode code_info__set_instmap(in, in, out) is det.
|
|
|
|
% Get the globals table.
|
|
:- pred code_info__get_globals(globals, code_info, code_info).
|
|
:- mode code_info__get_globals(out, in, out) is det.
|
|
|
|
:- pred code_info__get_layout_info(map(label, internal_layout_info),
|
|
code_info, code_info).
|
|
:- mode code_info__get_layout_info(out, in, out) is det.
|
|
|
|
:- pred code_info__get_maybe_trace_info(maybe(trace_info),
|
|
code_info, code_info).
|
|
:- mode code_info__get_maybe_trace_info(out, in, out) is det.
|
|
|
|
:- pred code_info__set_maybe_trace_info(maybe(trace_info),
|
|
code_info, code_info).
|
|
:- mode code_info__set_maybe_trace_info(in, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- pred code_info__get_var_slot_count(int, code_info, code_info).
|
|
:- mode code_info__get_var_slot_count(out, in, out) is det.
|
|
|
|
:- pred code_info__get_label_count(int, code_info, code_info).
|
|
:- mode code_info__get_label_count(out, in, out) is det.
|
|
|
|
:- pred code_info__set_label_count(int, code_info, code_info).
|
|
:- mode code_info__set_label_count(in, in, out) is det.
|
|
|
|
:- pred code_info__set_cell_count(int, code_info, code_info).
|
|
:- mode code_info__set_cell_count(in, in, out) is det.
|
|
|
|
:- pred code_info__get_exprn_info(exprn_info, code_info, code_info).
|
|
:- mode code_info__get_exprn_info(out, in, out) is det.
|
|
|
|
:- pred code_info__set_exprn_info(exprn_info, code_info, code_info).
|
|
:- mode code_info__set_exprn_info(in, in, out) is det.
|
|
|
|
:- pred code_info__set_succip_used(bool, code_info, code_info).
|
|
:- mode code_info__set_succip_used(in, in, out) is det.
|
|
|
|
:- pred code_info__get_fail_stack(fail_stack, code_info, code_info).
|
|
:- mode code_info__get_fail_stack(out, in, out) is det.
|
|
|
|
:- pred code_info__set_fail_stack(fail_stack, code_info, code_info).
|
|
:- mode code_info__set_fail_stack(in, in, out) is det.
|
|
|
|
:- pred code_info__get_avail_temp_slots(set(lval), code_info, code_info).
|
|
:- mode code_info__get_avail_temp_slots(out, in, out) is det.
|
|
|
|
:- pred code_info__set_avail_temp_slots(set(lval), code_info, code_info).
|
|
:- mode code_info__set_avail_temp_slots(in, in, out) is det.
|
|
|
|
:- pred code_info__get_max_temp_slot_count(int, code_info, code_info).
|
|
:- mode code_info__get_max_temp_slot_count(out, in, out) is det.
|
|
|
|
:- pred code_info__set_max_temp_slot_count(int, code_info, code_info).
|
|
:- mode code_info__set_max_temp_slot_count(in, in, out) is det.
|
|
|
|
:- pred code_info__get_temps_in_use(map(lval, slot_contents),
|
|
code_info, code_info).
|
|
:- mode code_info__get_temps_in_use(out, in, out) is det.
|
|
|
|
:- pred code_info__set_temps_in_use(map(lval, slot_contents),
|
|
code_info, code_info).
|
|
:- mode code_info__set_temps_in_use(in, in, out) is det.
|
|
|
|
:- pred code_info__set_layout_info(map(label, internal_layout_info),
|
|
code_info, code_info).
|
|
:- mode code_info__set_layout_info(in, in, out) is det.
|
|
|
|
:- pred code_info__get_zombies(set(var), code_info, code_info).
|
|
:- mode code_info__get_zombies(out, in, out) is det.
|
|
|
|
:- pred code_info__set_zombies(set(var), code_info, code_info).
|
|
:- mode code_info__set_zombies(in, in, out) is det.
|
|
|
|
:- pred code_info__get_resume_point_stack(stack(set(var)),
|
|
code_info, code_info).
|
|
:- mode code_info__get_resume_point_stack(out, in, out) is det.
|
|
|
|
:- pred code_info__set_resume_point_stack(stack(set(var)),
|
|
code_info, code_info).
|
|
:- mode code_info__set_resume_point_stack(in, in, out) is det.
|
|
|
|
:- pred code_info__get_commit_triple_count(int, code_info, code_info).
|
|
:- mode code_info__get_commit_triple_count(out, in, out) is det.
|
|
|
|
:- pred code_info__set_commit_triple_count(int, code_info, code_info).
|
|
:- mode code_info__set_commit_triple_count(in, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type code_info --->
|
|
code_info(
|
|
int, % The number of stack slots allocated.
|
|
% for storing variables.
|
|
% (Some extra stack slots are used
|
|
% for saving and restoring registers.)
|
|
int, % Counter for the local labels used
|
|
% by this procedure.
|
|
varset, % The variables in this procedure.
|
|
pred_id, % The label of the current predicate.
|
|
proc_id, % The label of the current procedure.
|
|
int, % Counter for cells in this proc.
|
|
exprn_info, % A map storing the information about
|
|
% the status of each variable.
|
|
proc_info, % The proc_info for the this procedure.
|
|
bool, % do we need to store succip?
|
|
fail_stack, % The failure continuation stack
|
|
module_info, % The module_info structure - you just
|
|
% never know when you might need it.
|
|
% It should be read-only.
|
|
set(var), % Variables that are forward live
|
|
% after this goal
|
|
instmap, % insts of variables
|
|
set(lval), % Stack variables that have been used
|
|
% for temporaries and are now again
|
|
% available for reuse.
|
|
int, % The maximum number of extra
|
|
% temporary stackslots that have been
|
|
% used during the procedure
|
|
globals, % code generation options
|
|
map(lval, slot_contents),
|
|
% The temp locations in use on the stack
|
|
% and what they contain (for gc).
|
|
map(label, internal_layout_info),
|
|
% Information on which values
|
|
% are live and where at which labels,
|
|
% for tracing and/or accurate gc.
|
|
set(var), % Zombie variables; variables that have
|
|
% been killed but are protected by a
|
|
% resume point.
|
|
stack(set(var)),
|
|
% Each resumption point has an
|
|
% associated set of variables
|
|
% whose values may be needed on
|
|
% resumption at that point.
|
|
% This field gives those variables
|
|
% for a nested set of resumption
|
|
% points. Each element must be
|
|
% a superset of the ones below.
|
|
% When a variable included in the top
|
|
% set becomes no longer forward live,
|
|
% we must save its value to the stack.
|
|
int,
|
|
% A count of how many triples of
|
|
% curfr, maxfr and redoip are live on
|
|
% the det stack. These triples are
|
|
% pushed onto the det stack when
|
|
% generating code for commits within
|
|
% model_non procedures. The value
|
|
% numbering pass and the accurate
|
|
% garbage collector need to know about
|
|
% these slots. (Triple is a misnomer:
|
|
% in grades with trailing, it is four.)
|
|
maybe(trace_info)
|
|
% Information about which stack slots
|
|
% the call sequence number and depth
|
|
% are stored, provided tracing is
|
|
% switched on.
|
|
).
|
|
|
|
:- type slot_contents
|
|
---> ticket % a ticket (trail pointer)
|
|
; ticket_counter % a copy of the ticket counter
|
|
; trace_data
|
|
; lval(lval).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__init(Varset, Liveness, StackSlots, SaveSuccip, Globals,
|
|
PredId, ProcId, ProcInfo, Requests, FollowVars,
|
|
ModuleInfo, CellCount, C) :-
|
|
proc_info_headvars(ProcInfo, HeadVars),
|
|
proc_info_arg_info(ProcInfo, ArgInfos),
|
|
assoc_list__from_corresponding_lists(HeadVars, ArgInfos, Args),
|
|
arg_info__build_input_arg_list(Args, ArgList),
|
|
globals__get_options(Globals, Options),
|
|
code_exprn__init_state(ArgList, Varset, StackSlots, FollowVars,
|
|
Options, ExprnInfo),
|
|
stack__init(Continue),
|
|
stack__init(ResumeSetStack0),
|
|
set__init(AvailSlots0),
|
|
map__init(TempsInUse0),
|
|
set__init(Zombies0),
|
|
map__init(Shapes),
|
|
code_info__max_var_slot(StackSlots, VarSlotCount0),
|
|
proc_info_interface_code_model(ProcInfo, CodeModel),
|
|
(
|
|
CodeModel = model_non
|
|
->
|
|
VarSlotCount is VarSlotCount0 + 1
|
|
;
|
|
VarSlotCount = VarSlotCount0
|
|
),
|
|
C = code_info(
|
|
VarSlotCount,
|
|
0,
|
|
Varset,
|
|
PredId,
|
|
ProcId,
|
|
CellCount,
|
|
ExprnInfo,
|
|
ProcInfo,
|
|
SaveSuccip,
|
|
Continue,
|
|
ModuleInfo,
|
|
Liveness,
|
|
Requests,
|
|
AvailSlots0,
|
|
0,
|
|
Globals,
|
|
TempsInUse0,
|
|
Shapes,
|
|
Zombies0,
|
|
ResumeSetStack0,
|
|
0,
|
|
no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__get_var_slot_count(A, CI, CI) :-
|
|
CI = code_info(A, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_label_count(B, CI, CI) :-
|
|
CI = code_info(_, B, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_varset(C, CI, CI) :-
|
|
CI = code_info(_, _, C, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_pred_id(D, CI, CI) :-
|
|
CI = code_info(_, _, _, D, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_proc_id(E, CI, CI) :-
|
|
CI = code_info(_, _, _, _, E, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_cell_count(F, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, F, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_exprn_info(G, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, G, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_proc_info(H, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, H, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_succip_used(I, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, I, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_fail_stack(J, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, J, _, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_module_info(K, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, K, _, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_forward_live_vars(L, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, L, _, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_instmap(M, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, M, _, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_avail_temp_slots(N, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, N, _, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_max_temp_slot_count(O, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, O, _, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_globals(P, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, P, _, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_temps_in_use(Q, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Q, _, _,
|
|
_, _, _).
|
|
|
|
code_info__get_layout_info(R, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, R, _,
|
|
_, _, _).
|
|
|
|
code_info__get_zombies(S, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, S,
|
|
_, _, _).
|
|
|
|
code_info__get_resume_point_stack(T, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
T, _, _).
|
|
|
|
code_info__get_commit_triple_count(U, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, U, _).
|
|
|
|
code_info__get_maybe_trace_info(V, CI, CI) :-
|
|
CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, V).
|
|
|
|
% :- type code_info --->
|
|
% code_info(
|
|
% A int, % The number of stack slots allocated.
|
|
% % for storing variables.
|
|
% % (Some extra stack slots are used
|
|
% % for saving and restoring registers.)
|
|
% B int, % Counter for the local labels used
|
|
% % by this procedure.
|
|
% C varset, % The variables in this procedure.
|
|
% D pred_id, % The label of the current predicate.
|
|
% E proc_id, % The label of the current procedure.
|
|
% F int, % Counter for cells in this proc.
|
|
% G exprn_info, % A map storing the information about
|
|
% % the status of each variable.
|
|
% H proc_info, % The proc_info for the this procedure.
|
|
% I bool, % do we need to store succip?
|
|
% J fail_stack, % The failure continuation stack
|
|
% K module_info, % The module_info structure - you just
|
|
% % never know when you might need it.
|
|
% % It should be read-only.
|
|
% L set(var), % Variables that are forward live
|
|
% % after this goal
|
|
% M instmap, % insts of variables
|
|
% N set(lval), % Stack variables that have been used
|
|
% % for temporaries and are now again
|
|
% % available for reuse.
|
|
% O int, % The maximum number of extra
|
|
% % temporary stackslots that have been
|
|
% % used during the procedure
|
|
% P globals, % code generation options
|
|
% Q map(lval, slot_contents),
|
|
% % The temp locations in use on the stack
|
|
% % and what they contain (for gc).
|
|
% R map(label, internal_layout_info),
|
|
% % Information on which values
|
|
% % are live and where at which labels,
|
|
% % for tracing and/or accurate gc.
|
|
% S set(var), % Zombie variables; variables that have
|
|
% % been killed but are protected by a
|
|
% % resume point.
|
|
% T stack(set(var)),
|
|
% % Each resumption point has an
|
|
% % associated set of variables
|
|
% % whose values may be needed on
|
|
% % resumption at that point.
|
|
% % This field gives those variables
|
|
% % for a nested set of resumption
|
|
% % points. Each element must be
|
|
% % a superset of the ones below.
|
|
% % When a variable included in the top
|
|
% % set becomes no longer forward live,
|
|
% % we must save its value to the stack.
|
|
% U int,
|
|
% % A count of how many triples of
|
|
% % curfr, maxfr and redoip are live on
|
|
% % the det stack. These triples are
|
|
% % pushed onto the det stack when
|
|
% % generating code for commits within
|
|
% % model_non procedures. The value
|
|
% % numbering pass and the accurate
|
|
% % garbage collector need to know about
|
|
% % these slots. (Triple is a misnomer:
|
|
% % in grades with trailing, it is four.)
|
|
% V maybe(trace_info)
|
|
% % Information about which stack slots
|
|
% % the call sequence number and depth
|
|
% % are stored, provided tracing is
|
|
% % switched on.
|
|
% ).
|
|
%
|
|
% we don't need
|
|
% code_info__set_var_slot_count
|
|
% code_info__set_varset
|
|
% code_info__set_pred_id
|
|
% code_info__set_proc_id
|
|
% code_info__set_module_info
|
|
% code_info__set_globals
|
|
% code_info__set_code_model
|
|
|
|
code_info__set_label_count(B, CI0, CI) :-
|
|
CI0 = code_info(A, _, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_cell_count(F, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, _, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_exprn_info(G, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, _, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_succip_used(I, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, _, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_fail_stack(J, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, _, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_forward_live_vars(L, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, _, M, N, O, P, Q,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_instmap(M, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, _, N, O, P, Q,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_avail_temp_slots(N, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, _, O, P, Q,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_max_temp_slot_count(O, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, _, P, Q,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_temps_in_use(Q, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, _,
|
|
R, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_layout_info(R, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
_, S, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_zombies(S, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, _, T, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_resume_point_stack(T, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, _, U, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_commit_triple_count(U, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, _, V),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
code_info__set_maybe_trace_info(V, CI0, CI) :-
|
|
CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, _),
|
|
CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
|
|
R, S, T, U, V).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Submodule for simple wrappers around access predicates.
|
|
|
|
:- interface.
|
|
|
|
% Get the hlds mapping from variables to stack slots
|
|
:- pred code_info__get_stack_slots(stack_slots, code_info, code_info).
|
|
:- mode code_info__get_stack_slots(out, in, out) is det.
|
|
|
|
% Get the table that contains advice about where
|
|
% variables should be put.
|
|
:- pred code_info__get_follow_vars(follow_vars, code_info, code_info).
|
|
:- mode code_info__get_follow_vars(out, in, out) is det.
|
|
|
|
% Set the table that contains advice about where
|
|
% variables should be put.
|
|
:- pred code_info__set_follow_vars(follow_vars, code_info, code_info).
|
|
:- mode code_info__set_follow_vars(in, in, out) is det.
|
|
|
|
% code_info__pre_goal_update(GoalInfo, Atomic, OldCodeInfo, NewCodeInfo)
|
|
% updates OldCodeInfo to produce NewCodeInfo with the changes
|
|
% specified by GoalInfo.
|
|
:- pred code_info__pre_goal_update(hlds_goal_info, bool, code_info, code_info).
|
|
:- mode code_info__pre_goal_update(in, in, in, out) is det.
|
|
|
|
% code_info__post_goal_update(GoalInfo, OldCodeInfo, NewCodeInfo)
|
|
% updates OldCodeInfo to produce NewCodeInfo with the changes described
|
|
% by GoalInfo.
|
|
:- pred code_info__post_goal_update(hlds_goal_info, code_info, code_info).
|
|
:- mode code_info__post_goal_update(in, in, out) is det.
|
|
|
|
% Find out the type of the given variable.
|
|
:- pred code_info__variable_type(var, type, code_info, code_info).
|
|
:- mode code_info__variable_type(in, out, in, out) is det.
|
|
|
|
:- pred code_info__lookup_type_defn(type, hlds_type_defn,
|
|
code_info, code_info).
|
|
:- mode code_info__lookup_type_defn(in, out, in, out) is det.
|
|
|
|
% Given a list of type variables, find the lvals where the
|
|
% corresponding type_infos and typeclass_infos are being stored.
|
|
:- pred code_info__find_type_infos(list(var), assoc_list(var, lval),
|
|
code_info, code_info).
|
|
:- mode code_info__find_type_infos(in, out, in, out) is det.
|
|
|
|
% Given a constructor id, and a variable (so that we can work out the
|
|
% type of the constructor), determine correct tag (representation)
|
|
% of that constructor.
|
|
:- pred code_info__cons_id_to_tag(var, cons_id, cons_tag, code_info, code_info).
|
|
:- mode code_info__cons_id_to_tag(in, in, out, in, out) is det.
|
|
|
|
% Get the code model of the current procedure.
|
|
:- pred code_info__get_proc_model(code_model, code_info, code_info).
|
|
:- mode code_info__get_proc_model(out, in, out) is det.
|
|
|
|
:- pred code_info__get_headvars(list(var), code_info, code_info).
|
|
:- mode code_info__get_headvars(out, in, out) is det.
|
|
|
|
% Get the call argument information for the current procedure
|
|
:- pred code_info__get_arginfo(list(arg_info), code_info, code_info).
|
|
:- mode code_info__get_arginfo(out, in, out) is det.
|
|
|
|
% Get the call argument info for a given mode of a given predicate
|
|
:- pred code_info__get_pred_proc_arginfo(pred_id, proc_id, list(arg_info),
|
|
code_info, code_info).
|
|
:- mode code_info__get_pred_proc_arginfo(in, in, out, in, out) is det.
|
|
|
|
% Pop the failure continuation stack.
|
|
:- pred code_info__pop_failure_cont(code_info, code_info).
|
|
:- mode code_info__pop_failure_cont(in, out) is det.
|
|
|
|
:- pred code_info__push_resume_point_vars(set(var), code_info, code_info).
|
|
:- mode code_info__push_resume_point_vars(in, in, out) is det.
|
|
|
|
:- pred code_info__pop_resume_point_vars(code_info, code_info).
|
|
:- mode code_info__pop_resume_point_vars(in, out) is det.
|
|
|
|
:- pred code_info__variable_to_string(var, string, code_info, code_info).
|
|
:- mode code_info__variable_to_string(in, out, in, out) is det.
|
|
|
|
:- pred code_info__grab_code_info(code_info, code_info, code_info).
|
|
:- mode code_info__grab_code_info(out, in, out) is det.
|
|
|
|
:- pred code_info__slap_code_info(code_info, code_info, code_info).
|
|
:- mode code_info__slap_code_info(in, in, out) is det.
|
|
|
|
:- pred code_info__apply_instmap_delta(instmap_delta, code_info, code_info).
|
|
:- mode code_info__apply_instmap_delta(in, in, out) is det.
|
|
|
|
% Create a code address which holds the address of the specified
|
|
% procedure.
|
|
% The fourth argument should be `no' if the the caller wants the
|
|
% returned address to be valid from everywhere in the program.
|
|
% If being valid from within the current procedure is enough,
|
|
% this argument should be `yes' wrapped around the value of the
|
|
% --procs-per-c-function option and the current procedure id.
|
|
% Using an address that is only valid from within the current
|
|
% procedure may make jumps more efficient.
|
|
% XXX
|
|
:- pred code_info__make_entry_label(module_info, pred_id, proc_id, bool,
|
|
code_addr, code_info, code_info).
|
|
:- mode code_info__make_entry_label(in, in, in, in, out, in, out) is det.
|
|
|
|
% Generate the next local label in sequence.
|
|
:- pred code_info__get_next_label(label, code_info, code_info).
|
|
:- mode code_info__get_next_label(out, in, out) is det.
|
|
|
|
% Generate the next cell number in sequence.
|
|
:- pred code_info__get_next_cell_number(int, code_info, code_info).
|
|
:- mode code_info__get_next_cell_number(out, in, out) is det.
|
|
|
|
% Get the current cell number.
|
|
:- pred code_info__get_cell_count(int, code_info, code_info).
|
|
:- mode code_info__get_cell_count(out, in, out) is det.
|
|
|
|
:- pred code_info__succip_is_used(code_info, code_info).
|
|
:- mode code_info__succip_is_used(in, out) is det.
|
|
|
|
:- pred code_info__add_layout_for_label(label, internal_layout_info,
|
|
code_info, code_info).
|
|
:- mode code_info__add_layout_for_label(in, in, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- pred code_info__push_failure_cont(failure_cont, code_info, code_info).
|
|
:- mode code_info__push_failure_cont(in, in, out) is det.
|
|
|
|
% Lookup the value on the top of the failure continuation stack
|
|
|
|
:- pred code_info__top_failure_cont(failure_cont, code_info, code_info).
|
|
:- mode code_info__top_failure_cont(out, in, out) is det.
|
|
|
|
:- pred code_info__current_resume_point_vars(set(var), code_info, code_info).
|
|
:- mode code_info__current_resume_point_vars(out, in, out) is det.
|
|
|
|
:- pred code_info__add_commit_triple(code_info, code_info).
|
|
:- mode code_info__add_commit_triple(in, out) is det.
|
|
|
|
:- pred code_info__rem_commit_triple(code_info, code_info).
|
|
:- mode code_info__rem_commit_triple(in, out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_info__get_stack_slots(StackSlots, CI, CI) :-
|
|
code_info__get_exprn_info(ExprnInfo, CI, _),
|
|
code_exprn__get_stack_slots(StackSlots, ExprnInfo, _).
|
|
|
|
code_info__get_follow_vars(FollowVars, CI, CI) :-
|
|
code_info__get_exprn_info(ExprnInfo, CI, _),
|
|
code_exprn__get_follow_vars(FollowVars, ExprnInfo, _).
|
|
|
|
code_info__set_follow_vars(FollowVars, CI0, CI) :-
|
|
code_info__get_exprn_info(ExprnInfo0, CI0, _),
|
|
code_exprn__set_follow_vars(FollowVars, ExprnInfo0, ExprnInfo),
|
|
code_info__set_exprn_info(ExprnInfo, CI0, CI).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Update the code info structure to be consistent
|
|
% immediately prior to generating a goal
|
|
code_info__pre_goal_update(GoalInfo, Atomic) -->
|
|
% The liveness pass puts resume_point annotations on some kinds
|
|
% of goals. The parts of the code generator that handle those kinds
|
|
% of goals should handle the resume point annotation as well;
|
|
% when they do, they remove the annotation. The following code
|
|
% is a sanity check to make sure that this has in fact been done.
|
|
{ goal_info_get_resume_point(GoalInfo, ResumePoint) },
|
|
(
|
|
{ ResumePoint = no_resume_point }
|
|
;
|
|
{ ResumePoint = resume_point(_, _) },
|
|
{ error("pre_goal_update with resume point") }
|
|
),
|
|
{ goal_info_get_follow_vars(GoalInfo, MaybeFollowVars) },
|
|
(
|
|
{ MaybeFollowVars = yes(FollowVars) },
|
|
code_info__set_follow_vars(FollowVars)
|
|
;
|
|
{ MaybeFollowVars = no }
|
|
),
|
|
% note: we must be careful to apply deaths before births
|
|
{ goal_info_get_pre_deaths(GoalInfo, PreDeaths) },
|
|
code_info__rem_forward_live_vars(PreDeaths),
|
|
code_info__make_vars_forward_dead(PreDeaths),
|
|
{ goal_info_get_pre_births(GoalInfo, PreBirths) },
|
|
code_info__add_forward_live_vars(PreBirths),
|
|
( { Atomic = yes } ->
|
|
{ goal_info_get_post_deaths(GoalInfo, PostDeaths) },
|
|
code_info__rem_forward_live_vars(PostDeaths)
|
|
;
|
|
[]
|
|
).
|
|
|
|
% Update the code info structure to be consistent
|
|
% immediately after generating a goal
|
|
code_info__post_goal_update(GoalInfo) -->
|
|
% note: we must be careful to apply deaths before births
|
|
{ goal_info_get_post_deaths(GoalInfo, PostDeaths) },
|
|
code_info__rem_forward_live_vars(PostDeaths),
|
|
code_info__make_vars_forward_dead(PostDeaths),
|
|
{ goal_info_get_post_births(GoalInfo, PostBirths) },
|
|
code_info__add_forward_live_vars(PostBirths),
|
|
code_info__make_vars_forward_live(PostBirths),
|
|
{ goal_info_get_instmap_delta(GoalInfo, InstMapDelta) },
|
|
code_info__apply_instmap_delta(InstMapDelta).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__variable_type(Var, Type) -->
|
|
code_info__get_proc_info(ProcInfo),
|
|
{ proc_info_vartypes(ProcInfo, VarTypes) },
|
|
{ map__lookup(VarTypes, Var, Type) }.
|
|
|
|
code_info__lookup_type_defn(Type, TypeDefn) -->
|
|
code_info__get_module_info(ModuleInfo),
|
|
{ type_to_type_id(Type, TypeIdPrime, _) ->
|
|
TypeId = TypeIdPrime
|
|
;
|
|
error("unknown type in code_aux__lookup_type_defn")
|
|
},
|
|
{ module_info_types(ModuleInfo, TypeTable) },
|
|
{ map__lookup(TypeTable, TypeId, TypeDefn) }.
|
|
|
|
code_info__find_type_infos([], []) --> [].
|
|
code_info__find_type_infos([TVar | TVars], [TVar - Lval | Lvals]) -->
|
|
code_info__get_proc_info(ProcInfo),
|
|
{ proc_info_typeinfo_varmap(ProcInfo, TypeInfoMap) },
|
|
{
|
|
map__search(TypeInfoMap, TVar, Locn)
|
|
->
|
|
type_info_locn_var(Locn, Var)
|
|
;
|
|
error("cannot find var for type variable")
|
|
},
|
|
{ proc_info_stack_slots(ProcInfo, StackSlots) },
|
|
(
|
|
{ map__search(StackSlots, Var, Lval0) }
|
|
->
|
|
{ Lval = Lval0 }
|
|
;
|
|
code_info__variable_to_string(Var, VarString),
|
|
{ string__format("code_info__find_type_infos: can't find lval for type_info var %s",
|
|
[s(VarString)], ErrStr) },
|
|
{ error(ErrStr) }
|
|
),
|
|
code_info__find_type_infos(TVars, Lvals).
|
|
|
|
code_info__cons_id_to_tag(Var, ConsId, ConsTag) -->
|
|
code_info__variable_type(Var, Type),
|
|
code_info__get_module_info(ModuleInfo),
|
|
{ code_util__cons_id_to_tag(ConsId, Type, ModuleInfo, ConsTag) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__get_proc_model(CodeModel) -->
|
|
code_info__get_proc_info(ProcInfo),
|
|
{ proc_info_interface_code_model(ProcInfo, CodeModel) }.
|
|
|
|
code_info__get_headvars(HeadVars) -->
|
|
code_info__get_module_info(ModuleInfo),
|
|
code_info__get_pred_id(PredId),
|
|
code_info__get_proc_id(ProcId),
|
|
{ module_info_preds(ModuleInfo, Preds) },
|
|
{ map__lookup(Preds, PredId, PredInfo) },
|
|
{ pred_info_procedures(PredInfo, Procs) },
|
|
{ map__lookup(Procs, ProcId, ProcInfo) },
|
|
{ proc_info_headvars(ProcInfo, HeadVars) }.
|
|
|
|
code_info__get_arginfo(ArgInfo) -->
|
|
code_info__get_pred_id(PredId),
|
|
code_info__get_proc_id(ProcId),
|
|
code_info__get_pred_proc_arginfo(PredId, ProcId, ArgInfo).
|
|
|
|
code_info__get_pred_proc_arginfo(PredId, ProcId, ArgInfo) -->
|
|
code_info__get_module_info(ModuleInfo),
|
|
{ module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo) },
|
|
{ proc_info_arg_info(ProcInfo, ArgInfo) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__push_failure_cont(Cont) -->
|
|
code_info__get_fail_stack(Fall0),
|
|
{ stack__push(Fall0, Cont, Fall) },
|
|
code_info__set_fail_stack(Fall).
|
|
|
|
code_info__pop_failure_cont -->
|
|
code_info__get_fail_stack(Fall0),
|
|
( { stack__pop(Fall0, _, Fall) } ->
|
|
code_info__set_fail_stack(Fall)
|
|
;
|
|
{ error("code_info__pop_failure_cont: empty stack") }
|
|
).
|
|
|
|
code_info__top_failure_cont(Cont) -->
|
|
code_info__get_fail_stack(Fall),
|
|
( { stack__top(Fall, Cont0) } ->
|
|
{ Cont = Cont0 }
|
|
;
|
|
{ error("code_info__failure_cont: no failure continuation") }
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__current_resume_point_vars(ResumeVars) -->
|
|
code_info__get_resume_point_stack(ResumeStack),
|
|
{ stack__top(ResumeStack, TopVars) ->
|
|
ResumeVars = TopVars
|
|
;
|
|
set__init(ResumeVars)
|
|
}.
|
|
|
|
code_info__push_resume_point_vars(ResumeVars) -->
|
|
code_info__get_resume_point_stack(ResumeStack0),
|
|
{ stack__top(ResumeStack0, OldTopVars) ->
|
|
require(set__subset(OldTopVars, ResumeVars),
|
|
"new resume point variable set does not include old one")
|
|
;
|
|
true
|
|
},
|
|
{ stack__push(ResumeStack0, ResumeVars, ResumeStack) },
|
|
code_info__set_resume_point_stack(ResumeStack).
|
|
|
|
code_info__pop_resume_point_vars -->
|
|
code_info__get_resume_point_stack(ResumeStack0),
|
|
{ stack__pop_det(ResumeStack0, _, ResumeStack) },
|
|
code_info__set_resume_point_stack(ResumeStack).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__add_commit_triple -->
|
|
code_info__get_commit_triple_count(Count0),
|
|
{ Count is Count0 + 1 },
|
|
code_info__set_commit_triple_count(Count).
|
|
|
|
code_info__rem_commit_triple -->
|
|
code_info__get_commit_triple_count(Count0),
|
|
{ Count is Count0 - 1 },
|
|
code_info__set_commit_triple_count(Count).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__variable_to_string(Var, Name) -->
|
|
code_info__get_varset(Varset),
|
|
{ varset__lookup_name(Varset, Var, Name) }.
|
|
|
|
code_info__grab_code_info(C, C, C).
|
|
|
|
code_info__slap_code_info(C0, C1, C) :-
|
|
code_info__get_label_count(L, C1, _),
|
|
code_info__set_label_count(L, C0, C2),
|
|
code_info__get_succip_used(S, C1, _),
|
|
code_info__set_succip_used(S, C2, C3),
|
|
code_info__get_fail_stack(J, C0, _),
|
|
code_info__set_fail_stack(J, C3, C4),
|
|
code_info__get_max_temp_slot_count(PC, C1, _),
|
|
code_info__set_max_temp_slot_count(PC, C4, C5),
|
|
code_info__get_layout_info(LayoutInfo, C1, _),
|
|
code_info__set_layout_info(LayoutInfo, C5, C6),
|
|
code_info__get_cell_count(CellCount, C1, _),
|
|
code_info__set_cell_count(CellCount, C6, C).
|
|
|
|
code_info__apply_instmap_delta(Delta) -->
|
|
code_info__get_instmap(InstMap0),
|
|
{ instmap__apply_instmap_delta(InstMap0, Delta, InstMap) },
|
|
code_info__set_instmap(InstMap).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__make_entry_label(ModuleInfo, PredId, ProcId, Immed0, PredAddress) -->
|
|
(
|
|
{ Immed0 = no },
|
|
{ Immed = no }
|
|
;
|
|
{ Immed0 = yes },
|
|
code_info__get_globals(Globals),
|
|
{ globals__lookup_int_option(Globals, procs_per_c_function,
|
|
ProcsPerFunc) },
|
|
code_info__get_pred_id(CurPredId),
|
|
code_info__get_proc_id(CurProcId),
|
|
{ Immed = yes(ProcsPerFunc - proc(CurPredId, CurProcId)) }
|
|
),
|
|
{ code_util__make_entry_label(ModuleInfo, PredId, ProcId, Immed,
|
|
PredAddress) }.
|
|
|
|
code_info__get_next_label(Label) -->
|
|
code_info__get_module_info(ModuleInfo),
|
|
code_info__get_pred_id(PredId),
|
|
code_info__get_proc_id(ProcId),
|
|
code_info__get_label_count(N0),
|
|
{ N is N0 + 1 },
|
|
code_info__set_label_count(N),
|
|
{ code_util__make_internal_label(ModuleInfo, PredId, ProcId, N,
|
|
Label) }.
|
|
|
|
code_info__get_next_cell_number(N) -->
|
|
code_info__get_cell_count(N0),
|
|
{ N is N0 + 1 },
|
|
code_info__set_cell_count(N).
|
|
|
|
code_info__succip_is_used -->
|
|
code_info__set_succip_used(yes).
|
|
|
|
code_info__add_layout_for_label(Label, LayoutInfo) -->
|
|
code_info__get_layout_info(Internals0),
|
|
( { map__contains(Internals0, Label) } ->
|
|
{ error("adding layout for already known label") }
|
|
;
|
|
{ map__det_insert(Internals0, Label, LayoutInfo, Internals) },
|
|
code_info__set_layout_info(Internals)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Submodule for the handling of failure continuations.
|
|
|
|
:- interface.
|
|
|
|
% We manufacture a failure cont when we start generating
|
|
% code for a proc because on the failure of a procedure
|
|
% we don't need to prepare for execution to resume in this
|
|
% procedure.
|
|
|
|
:- pred code_info__manufacture_failure_cont(bool, code_info, code_info).
|
|
:- mode code_info__manufacture_failure_cont(in, in, out) is det.
|
|
|
|
% make_known_failure_cont(ResumeVars, ResumeLocs, IsNondet, Code):
|
|
% Push a new failure continuation onto the stack.
|
|
|
|
:- pred code_info__make_known_failure_cont(set(var), resume_locs, bool,
|
|
code_tree, code_info, code_info).
|
|
:- mode code_info__make_known_failure_cont(in, in, in, out, in, out)
|
|
is det.
|
|
|
|
% Generate some code to restore the current redoip, by looking
|
|
% at the top of the failure continuation stack.
|
|
|
|
:- pred code_info__restore_failure_cont(code_tree, code_info, code_info).
|
|
:- mode code_info__restore_failure_cont(out, in, out) is det.
|
|
|
|
% XXX
|
|
|
|
:- pred code_info__failure_is_direct_branch(code_addr, code_info, code_info).
|
|
:- mode code_info__failure_is_direct_branch(out, in, out) is semidet.
|
|
|
|
% XXX
|
|
|
|
:- pred code_info__generate_failure(code_tree, code_info, code_info).
|
|
:- mode code_info__generate_failure(out, in, out) is det.
|
|
|
|
% XXX
|
|
|
|
:- pred code_info__fail_if_rval_is_false(rval, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__fail_if_rval_is_false(in, out, in, out) is det.
|
|
|
|
% Set the topmost failure cont to `unknown' (e.g. after
|
|
% a nondet call or after a disjunction).
|
|
|
|
:- pred code_info__unset_failure_cont(code_tree, code_info, code_info).
|
|
:- mode code_info__unset_failure_cont(out, in, out) is det.
|
|
|
|
% Flush the variables needed for any current resumption point
|
|
% to their stack slots.
|
|
|
|
:- pred code_info__flush_resume_vars_to_stack(code_tree, code_info, code_info).
|
|
:- mode code_info__flush_resume_vars_to_stack(out, in, out) is det.
|
|
|
|
% XXX
|
|
|
|
:- pred code_info__may_use_nondet_tailcall(bool, code_info, code_info).
|
|
:- mode code_info__may_use_nondet_tailcall(out, in, out) is det.
|
|
|
|
% do_soft_cut(NondetFrameLvalAddress, SoftCutCode, SoftCutContCode)
|
|
% takes the lval where the address of the
|
|
% failure frame is stored, and it returns code to
|
|
% prune away the topmost choice point from that frame,
|
|
% typically by setting the redoip of that frame to do_fail.
|
|
% Note that the frame need not be the topmost frame -- a soft cut
|
|
% can prune away a single choice point from the middle.
|
|
% do_soft_cut also returns some additional code; in the case of
|
|
% trailing, the redoip will be set to point to that additional code,
|
|
% not to do_fail; the additional code will do a `discard_ticket'
|
|
% before branching to the usual failure continuation (e.g. do_fail).
|
|
|
|
:- pred code_info__do_soft_cut(lval, code_tree, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__do_soft_cut(in, out, out, in, out) is det.
|
|
|
|
% `generate_semi_pre_commit' and `generate_semi_commit' should be
|
|
% called before and after generating the code for the nondet goal
|
|
% being cut across. If the goal succeeds, the `commit' will cut
|
|
% any choice points generated in the goal.
|
|
%
|
|
% `generate_semi_pre_commit' returns a label (a failure cont label)
|
|
% and a tuple of stack slots; both of these should be passed to
|
|
% `generate_semi_commit'.
|
|
|
|
:- pred code_info__generate_semi_pre_commit(label, commit_slots, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__generate_semi_pre_commit(out, out, out, in, out) is det.
|
|
|
|
:- pred code_info__generate_semi_commit(label, commit_slots, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__generate_semi_commit(in, in, out, in, out) is det.
|
|
|
|
% `generate_det_pre_commit' and `generate_det_commit' should be
|
|
% called before and after generating the code for the multi goal
|
|
% being cut across. If the goal succeeds, the `commit' will cut
|
|
% any choice points generated in the goal.
|
|
%
|
|
% `generate_det_pre_commit' returns a tuple of stack slots, which
|
|
% should be passed to `generate_det_commit'.
|
|
|
|
:- pred code_info__generate_det_pre_commit(commit_slots, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__generate_det_pre_commit(out, out, in, out) is det.
|
|
|
|
:- pred code_info__generate_det_commit(commit_slots, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__generate_det_commit(in, out, in, out) is det.
|
|
|
|
:- type commit_slots
|
|
---> commit_slots(
|
|
lval, % curfr
|
|
lval, % maxfr
|
|
lval, % redoip
|
|
maybe(pair(lval, lval)) % ticket counter, trail pointer
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- type fail_stack == stack(failure_cont).
|
|
|
|
:- type failure_cont
|
|
---> failure_cont(
|
|
failure_cont_info,
|
|
resume_maps
|
|
).
|
|
|
|
:- type failure_cont_info
|
|
---> semidet
|
|
; nondet(
|
|
is_known, % Says whether on failure we can branch
|
|
% directly to one of the labels in
|
|
% the resume_maps, or if we must
|
|
% instead execute a redo.
|
|
|
|
maybe(label) % The maybe(label) is yes if we have
|
|
% created a temporary frame and we
|
|
% must restore curfr after a redo().
|
|
).
|
|
|
|
:- type is_known ---> known ; unknown.
|
|
|
|
:- type resume_map == map(var, set(rval)).
|
|
|
|
:- type resume_maps
|
|
---> orig_only(resume_map, code_addr)
|
|
; stack_only(resume_map, code_addr)
|
|
; orig_and_stack(resume_map, code_addr, resume_map, code_addr)
|
|
; stack_and_orig(resume_map, code_addr, resume_map, code_addr).
|
|
|
|
:- pred code_info__fail_cont_is_known(failure_cont_info).
|
|
:- mode code_info__fail_cont_is_known(in) is semidet.
|
|
|
|
code_info__fail_cont_is_known(FailContInfo) :-
|
|
FailContInfo \= nondet(unknown, _).
|
|
|
|
:- pred code_info__fail_cont_is_unknown(failure_cont_info).
|
|
:- mode code_info__fail_cont_is_unknown(in) is semidet.
|
|
|
|
code_info__fail_cont_is_unknown(FailContInfo) :-
|
|
FailContInfo = nondet(unknown, _).
|
|
|
|
:- pred code_info__have_temp_frame(fail_stack).
|
|
:- mode code_info__have_temp_frame(in) is semidet.
|
|
|
|
% have_temp_frame should succeed iff we have created
|
|
% a temp frame on the nondet stack. It traverses
|
|
% the entire failure continuation stack, looking for
|
|
% any failure continuations with the temp frame maybe(label)
|
|
% set to yes(_).
|
|
|
|
code_info__have_temp_frame(FailStack0) :-
|
|
stack__pop(FailStack0, FailContInfo, FailStack1),
|
|
(
|
|
FailContInfo = failure_cont(nondet(_, yes(_)), _)
|
|
;
|
|
code_info__have_temp_frame(FailStack1)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__manufacture_failure_cont(IsNondet) -->
|
|
{ map__init(Empty) },
|
|
(
|
|
{ IsNondet = no },
|
|
code_info__get_next_label(ContLab1),
|
|
{ Address1 = label(ContLab1) },
|
|
{ ResumeMap = stack_only(Empty, Address1) },
|
|
{ ContInfo = semidet }
|
|
;
|
|
{ IsNondet = yes },
|
|
{ Address1 = do_fail },
|
|
{ ResumeMap = stack_only(Empty, Address1) },
|
|
{ ContInfo = nondet(known, no) }
|
|
),
|
|
code_info__push_failure_cont(failure_cont(ContInfo, ResumeMap)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__make_known_failure_cont(ResumeVars, ResumeLocs, IsNondet,
|
|
ModContCode) -->
|
|
code_info__get_next_label(OrigLabel),
|
|
code_info__get_next_label(StackLabel),
|
|
{ OrigAddr = label(OrigLabel) },
|
|
{ StackAddr = label(StackLabel) },
|
|
(
|
|
% In semidet continuations we don't use the redoip
|
|
% of the top stack frame.
|
|
|
|
{ IsNondet = no },
|
|
{ TempFrameCode = empty },
|
|
{ FailContInfo = semidet }
|
|
;
|
|
% In nondet continuations we may use the redoip
|
|
% of the top stack frame. Therefore we must ensure
|
|
% that this redoip is free for use, creating our own
|
|
% frame if necessary.
|
|
|
|
{ IsNondet = yes },
|
|
code_info__top_failure_cont(FailureCont),
|
|
{ FailureCont = failure_cont(OrigInfo, _) },
|
|
(
|
|
{ code_info__fail_cont_is_unknown(OrigInfo) }
|
|
->
|
|
%
|
|
% If the failure continuation is unknown,
|
|
% then we need to create a new temporary frame
|
|
% so that we can make it known
|
|
%
|
|
code_info__get_next_label(RedoLabel),
|
|
{ MaybeRedoLabel = yes(RedoLabel) },
|
|
{ RedoAddr = label(RedoLabel) },
|
|
% this code could be better
|
|
% (mkframe is a bit of a sledge hammer)
|
|
{ TempFrameCode = node([
|
|
mkframe("temp frame", 1, no, RedoAddr)
|
|
- "create a temporary frame",
|
|
assign(curfr, lval(succfr(lval(maxfr))))
|
|
- "restore curfr after mkframe"
|
|
]) }
|
|
;
|
|
%
|
|
% The failure continuation is known.
|
|
% But did we create a temp frame?
|
|
%
|
|
code_info__get_fail_stack(FailStack),
|
|
(
|
|
{ code_info__have_temp_frame(FailStack) }
|
|
->
|
|
%
|
|
% If we created a temp frame, then
|
|
% we will need to restore curfr on redo,
|
|
% so we set the failure continuation to
|
|
% the RedoAddr rather than the usual
|
|
% StackAddr. (generate_failure_cont will
|
|
% generate code for it that restores curfr.)
|
|
%
|
|
code_info__get_next_label(RedoLabel),
|
|
{ MaybeRedoLabel = yes(RedoLabel) },
|
|
{ RedoAddr = label(RedoLabel) },
|
|
{ TempFrameCode = node([
|
|
assign(redoip(lval(maxfr)),
|
|
const(code_addr_const(RedoAddr)))
|
|
- "Set failure continuation on temp frame"
|
|
]) }
|
|
;
|
|
{ MaybeRedoLabel = no },
|
|
{ TempFrameCode = node([
|
|
assign(redoip(lval(maxfr)),
|
|
const(code_addr_const(StackAddr)))
|
|
- "Set failure continuation"
|
|
]) }
|
|
)
|
|
),
|
|
{ FailContInfo = nondet(known, MaybeRedoLabel) }
|
|
),
|
|
{ set__to_sorted_list(ResumeVars, VarList) },
|
|
(
|
|
{ ResumeLocs = orig_only },
|
|
code_info__produce_resume_vars(VarList, OrigMap, OrigCode),
|
|
{ ResumeMaps = orig_only(OrigMap, OrigAddr) }
|
|
;
|
|
{ ResumeLocs = stack_only },
|
|
code_info__produce_resume_vars(VarList, _OrigMap, OrigCode),
|
|
code_info__get_stack_slots(StackSlots),
|
|
{ map__select(StackSlots, ResumeVars, StackMap0) },
|
|
{ map__to_assoc_list(StackMap0, StackList0) },
|
|
{ code_info__tweak_stacklist(StackList0, StackList) },
|
|
{ map__from_assoc_list(StackList, StackMap) },
|
|
{ ResumeMaps = stack_only(StackMap, StackAddr) }
|
|
;
|
|
{ ResumeLocs = orig_and_stack },
|
|
code_info__produce_resume_vars(VarList, OrigMap, OrigCode),
|
|
code_info__get_stack_slots(StackSlots),
|
|
{ map__select(StackSlots, ResumeVars, StackMap0) },
|
|
{ map__to_assoc_list(StackMap0, StackList0) },
|
|
{ code_info__tweak_stacklist(StackList0, StackList) },
|
|
{ map__from_assoc_list(StackList, StackMap) },
|
|
{ ResumeMaps = orig_and_stack(OrigMap, OrigAddr,
|
|
StackMap, StackAddr) }
|
|
;
|
|
{ ResumeLocs = stack_and_orig },
|
|
code_info__produce_resume_vars(VarList, OrigMap, OrigCode),
|
|
code_info__get_stack_slots(StackSlots),
|
|
{ map__select(StackSlots, ResumeVars, StackMap0) },
|
|
{ map__to_assoc_list(StackMap0, StackList0) },
|
|
{ code_info__tweak_stacklist(StackList0, StackList) },
|
|
{ map__from_assoc_list(StackList, StackMap) },
|
|
{ ResumeMaps = stack_and_orig(StackMap, StackAddr,
|
|
OrigMap, OrigAddr) }
|
|
),
|
|
code_info__push_failure_cont(failure_cont(FailContInfo, ResumeMaps)),
|
|
{ ModContCode = tree(OrigCode, TempFrameCode) }.
|
|
|
|
:- pred code_info__produce_resume_vars(list(var), map(var, set(rval)),
|
|
code_tree, code_info, code_info).
|
|
:- mode code_info__produce_resume_vars(in, out, out, in, out) is det.
|
|
|
|
code_info__produce_resume_vars([], Map, empty) -->
|
|
{ map__init(Map) }.
|
|
code_info__produce_resume_vars([V | Vs], Map, Code) -->
|
|
code_info__produce_resume_vars(Vs, Map0, Code0),
|
|
code_info__produce_variable_in_reg_or_stack(V, Code1, Rval),
|
|
{ set__singleton_set(Rvals, Rval) },
|
|
{ map__set(Map0, V, Rvals, Map) },
|
|
{ Code = tree(Code0, Code1) }.
|
|
|
|
:- pred code_info__tweak_stacklist(assoc_list(var, lval),
|
|
assoc_list(var, set(rval))).
|
|
:- mode code_info__tweak_stacklist(in, out) is det.
|
|
|
|
code_info__tweak_stacklist([], []).
|
|
code_info__tweak_stacklist([V - L | Rest0], [V - Rs | Rest]) :-
|
|
set__singleton_set(Rs, lval(L)),
|
|
code_info__tweak_stacklist(Rest0, Rest).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% :- pred code_info__modify_failure_cont(code_tree, code_info, code_info).
|
|
% :- mode code_info__modify_failure_cont(out, in, out) is det.
|
|
%
|
|
% code_info__modify_failure_cont(ModifyCode) -->
|
|
% code_info__top_failure_cont(FailureCont),
|
|
% { FailureCont = failure_cont(OldCont, MaybeRedo0, FailureMap) },
|
|
% code_info__generate_failure_cont(FailureCont, FailureCode),
|
|
% code_info__pop_failure_cont,
|
|
% code_info__get_next_label(NewRegCont),
|
|
% code_info__get_next_label(NewStackCont),
|
|
% (
|
|
% { OldCont = unknown ; OldCont = known(yes) }
|
|
% ->
|
|
% { NewCont = known(yes) },
|
|
% ( { MaybeRedo0 = yes(_OldRedo) } ->
|
|
% code_info__get_next_label(NewRedoCont),
|
|
% { MaybeRedo = yes(NewRedoCont) }
|
|
% ;
|
|
% { NewRedoCont = NewStackCont },
|
|
% { MaybeRedo = no }
|
|
% ),
|
|
% { ResetCode = node([
|
|
% assign(redoip(lval(maxfr)),
|
|
% const(code_addr_const(label(NewRedoCont)))) -
|
|
% "modify failure cont"
|
|
% ]) }
|
|
% ;
|
|
% { error("code_info__modify_failure_cont: semidet context") }
|
|
% % { NewCont = known(no) },
|
|
% % { ResetCode = empty },
|
|
% % { MaybeRedo = no }
|
|
% ),
|
|
% (
|
|
% { FailureMap = [RegMap - _RegCont, StackMap - _StackCont] }
|
|
% ->
|
|
% code_info__push_failure_cont(failure_cont(NewCont, MaybeRedo,
|
|
% [RegMap - label(NewRegCont),
|
|
% StackMap - label(NewStackCont)]))
|
|
% ;
|
|
% { error("code_info__modify_failure_cont: bad failure map.") }
|
|
% ),
|
|
% { ModifyCode = tree(FailureCode, ResetCode) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__restore_failure_cont(Code) -->
|
|
code_info__top_failure_cont(CurFailureCont),
|
|
{ CurFailureCont = failure_cont(CurContInfo, _FailureMap) },
|
|
code_info__generate_failure_cont(CurFailureCont, FailureCode),
|
|
code_info__pop_failure_cont,
|
|
% Fixup the redoip of the top frame if necessary
|
|
(
|
|
{ CurContInfo = semidet },
|
|
{ ResetCode = empty }
|
|
;
|
|
{ CurContInfo = nondet(_, _) },
|
|
code_info__top_failure_cont(EnclosingFailureCont),
|
|
{ EnclosingFailureCont = failure_cont(EnclosingContInfo, _) },
|
|
{
|
|
EnclosingContInfo = semidet,
|
|
ResetCode = empty
|
|
;
|
|
EnclosingContInfo = nondet(unknown, _),
|
|
ResetCode = node([
|
|
assign(redoip(lval(maxfr)),
|
|
const(code_addr_const(do_fail))) -
|
|
"restore failure cont"
|
|
])
|
|
;
|
|
EnclosingContInfo = nondet(known, _),
|
|
code_info__find_first_resume_label(
|
|
EnclosingFailureCont, RedoAddress),
|
|
ResetCode = node([
|
|
assign(redoip(lval(maxfr)),
|
|
const(code_addr_const(RedoAddress)))
|
|
- "restore known failure cont"
|
|
])
|
|
}
|
|
),
|
|
{ Code = tree(FailureCode, ResetCode) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% In establishing this resumption point, we may have pushed
|
|
% a temporary frame onto the nondet stack. If we have done so,
|
|
% we will have recorded this fact by setting the second argument
|
|
% of the top failure continuation to yes, with the argument of
|
|
% the yes giving the name of the label whose address was put
|
|
% into the redoip slot of that frame.
|
|
%
|
|
% When control arrives at that label, curfr will point to the
|
|
% temporary frame. However, the variables needed by the code
|
|
% at the resumption point are in the main nondet frame of the
|
|
% procedure. Since this was the current frame when we created
|
|
% the temporary frame, we can find it by following the succfr
|
|
% link in the temporary frame.
|
|
%
|
|
% The code we generate in general is
|
|
%
|
|
% label(RedoLabel)
|
|
% <reset curfr>
|
|
% label(StackLabel)
|
|
% <assume variables are where StackMap says they are>
|
|
% <copy variables to their locations according to OrigMap>
|
|
% label(OrigLabel)
|
|
% <assume variables are where OrigMap says they are>
|
|
%
|
|
% If, in establishing this resumption point, we did not create
|
|
% a temporary frame, then curfr will be OK when code to the right
|
|
% does a fail(), and hence the first label and the resetting of
|
|
% the curfr register can be omitted.
|
|
%
|
|
% Failures at different points may cause control to arrive at
|
|
% the resumption point via any one of these each labels.
|
|
% The last line above is necessary since it may arrive at OrigLabel
|
|
% without going through StackLabel first.
|
|
%
|
|
% The first two lines above are generated in this predicate;
|
|
% the others are generated in code_info__generate_resume_setup.
|
|
% It may not generate some of these other lines if it knows that
|
|
% they won't be needed.
|
|
|
|
:- pred code_info__generate_failure_cont(failure_cont, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__generate_failure_cont(in, out, in, out) is det.
|
|
|
|
code_info__generate_failure_cont(FailureCont, Code) -->
|
|
{ FailureCont = failure_cont(ContInfo, ResumeMap) },
|
|
|
|
% Did we create a temp nondet frame for this continuation?
|
|
% If not, then curfr will be right when we arrive here.
|
|
% If yes, it will be wrong, pointing to the temp frame,
|
|
% so we must restore curfr before continuing.
|
|
{
|
|
ContInfo = nondet(_, MaybeRedoLabel),
|
|
MaybeRedoLabel = yes(RedoLabel)
|
|
->
|
|
FixCurFrCode = node([
|
|
label(RedoLabel) -
|
|
"redo entry point",
|
|
assign(curfr, lval(succfr(lval(maxfr)))) -
|
|
"restore curfr"
|
|
]),
|
|
(
|
|
( ResumeMap = orig_only(_, _)
|
|
; ResumeMap = orig_and_stack(_, _, _, _)
|
|
)
|
|
->
|
|
error("redo entry before an orig resume point")
|
|
;
|
|
true
|
|
)
|
|
;
|
|
FixCurFrCode = empty
|
|
},
|
|
code_info__generate_resume_setup(ResumeMap, ResumeCode),
|
|
{ Code = tree(FixCurFrCode, ResumeCode) }.
|
|
|
|
:- pred code_info__generate_resume_setup(resume_maps, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__generate_resume_setup(in, out, in, out) is det.
|
|
|
|
code_info__generate_resume_setup(ResumeMaps, Code) -->
|
|
(
|
|
{ ResumeMaps = orig_only(Map1, Addr1) },
|
|
{ extract_label_from_code_addr(Addr1, Label1) },
|
|
{ Code = node([
|
|
label(Label1) -
|
|
"orig only failure continuation"
|
|
]) },
|
|
code_info__set_var_locations(Map1)
|
|
;
|
|
{ ResumeMaps = stack_only(Map1, Addr1) },
|
|
{ extract_label_from_code_addr(Addr1, Label1) },
|
|
{ Code = node([
|
|
label(Label1) -
|
|
"stack only failure continuation"
|
|
]) },
|
|
code_info__set_var_locations(Map1)
|
|
;
|
|
{ ResumeMaps = stack_and_orig(Map1, Addr1, Map2, Addr2) },
|
|
{ extract_label_from_code_addr(Addr1, Label1) },
|
|
{ extract_label_from_code_addr(Addr2, Label2) },
|
|
{ Label1Code = node([
|
|
label(Label1) -
|
|
"stack failure continuation before orig"
|
|
]) },
|
|
code_info__set_var_locations(Map1),
|
|
{ map__to_assoc_list(Map2, AssocList2) },
|
|
code_info__place_resume_vars(AssocList2, PlaceCode),
|
|
{ Label2Code = node([
|
|
label(Label2) -
|
|
"orig failure continuation after stack"
|
|
]) },
|
|
code_info__set_var_locations(Map2),
|
|
{ Code = tree(Label1Code, tree(PlaceCode, Label2Code)) }
|
|
;
|
|
{ ResumeMaps = orig_and_stack(Map1, Addr1, Map2, Addr2) },
|
|
{ extract_label_from_code_addr(Addr1, Label1) },
|
|
{ extract_label_from_code_addr(Addr2, Label2) },
|
|
{ Label1Code = node([
|
|
label(Label1) -
|
|
"orig failure continuation before stack"
|
|
]) },
|
|
code_info__set_var_locations(Map1),
|
|
{ map__to_assoc_list(Map2, AssocList2) },
|
|
code_info__place_resume_vars(AssocList2, PlaceCode),
|
|
{ Label2Code = node([
|
|
label(Label2) -
|
|
"stack failure continuation after orig"
|
|
]) },
|
|
code_info__set_var_locations(Map2),
|
|
{ Code = tree(Label1Code, tree(PlaceCode, Label2Code)) }
|
|
).
|
|
|
|
:- pred extract_label_from_code_addr(code_addr, label).
|
|
:- mode extract_label_from_code_addr(in, out) is det.
|
|
|
|
extract_label_from_code_addr(CodeAddr, Label) :-
|
|
( CodeAddr = label(Label0) ->
|
|
Label = Label0
|
|
;
|
|
error("code_info__generate_resume_setup: non-label!")
|
|
).
|
|
|
|
:- pred code_info__place_resume_vars(assoc_list(var, set(rval)), code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__place_resume_vars(in, out, in, out) is det.
|
|
|
|
code_info__place_resume_vars([], empty) --> [].
|
|
code_info__place_resume_vars([Var - TargetSet | Rest], Code) -->
|
|
{ set__to_sorted_list(TargetSet, Targets) },
|
|
code_info__place_resume_var(Var, Targets, FirstCode),
|
|
{ Code = tree(FirstCode, RestCode) },
|
|
code_info__place_resume_vars(Rest, RestCode).
|
|
|
|
:- pred code_info__place_resume_var(var, list(rval), code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__place_resume_var(in, in, out, in, out) is det.
|
|
|
|
code_info__place_resume_var(_Var, [], empty) --> [].
|
|
code_info__place_resume_var(Var, [Target | Targets], Code) -->
|
|
( { Target = lval(TargetLval) } ->
|
|
code_info__place_var(Var, TargetLval, FirstCode)
|
|
;
|
|
{ error("code_info__place_resume_var: not lval") }
|
|
),
|
|
{ Code = tree(FirstCode, RestCode) },
|
|
code_info__place_resume_var(Var, Targets, RestCode).
|
|
|
|
% Reset the code generator's database of what is where.
|
|
% Remember that the variables in the map are available in their
|
|
% associated rvals; forget about all other variables.
|
|
|
|
:- pred code_info__set_var_locations(map(var, set(rval)), code_info, code_info).
|
|
:- mode code_info__set_var_locations(in, in, out) is det.
|
|
|
|
code_info__set_var_locations(Map) -->
|
|
{ map__to_assoc_list(Map, List0) },
|
|
{ code_info__flatten_varlval_list(List0, List) },
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__reinit_state(List, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
:- pred code_info__flatten_varlval_list(assoc_list(var, set(rval)),
|
|
assoc_list(var, rval)).
|
|
:- mode code_info__flatten_varlval_list(in, out) is det.
|
|
|
|
code_info__flatten_varlval_list([], []).
|
|
code_info__flatten_varlval_list([V - Rvals | Rest0], All) :-
|
|
code_info__flatten_varlval_list(Rest0, Rest),
|
|
set__to_sorted_list(Rvals, RvalList),
|
|
code_info__flatten_varlval_list_2(RvalList, V, Rest1),
|
|
list__append(Rest1, Rest, All).
|
|
|
|
:- pred code_info__flatten_varlval_list_2(list(rval), var,
|
|
assoc_list(var, rval)).
|
|
:- mode code_info__flatten_varlval_list_2(in, in, out) is det.
|
|
|
|
code_info__flatten_varlval_list_2([], _V, []).
|
|
code_info__flatten_varlval_list_2([R | Rs], V, [V - R | Rest]) :-
|
|
code_info__flatten_varlval_list_2(Rs, V, Rest).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__failure_is_direct_branch(CodeAddr) -->
|
|
code_info__top_failure_cont(FailureCont),
|
|
{ FailureCont = failure_cont(ContInfo, FailureMap) },
|
|
{ code_info__fail_cont_is_known(ContInfo) },
|
|
code_info__pick_matching_resume_addr(FailureMap, CodeAddr).
|
|
|
|
code_info__generate_failure(Code) -->
|
|
code_info__top_failure_cont(FailureCont),
|
|
{ FailureCont = failure_cont(ContInfo, FailureMap) },
|
|
(
|
|
{ code_info__fail_cont_is_known(ContInfo) }
|
|
->
|
|
(
|
|
code_info__pick_matching_resume_addr(FailureMap,
|
|
FailureAddress0)
|
|
->
|
|
{ FailureAddress = FailureAddress0 },
|
|
{ PlaceCode = empty }
|
|
;
|
|
{ code_info__pick_first_resume_point(FailureMap,
|
|
Map, FailureAddress) },
|
|
{ map__to_assoc_list(Map, AssocList) },
|
|
code_info__grab_code_info(CodeInfo),
|
|
code_info__place_vars(AssocList, PlaceCode),
|
|
code_info__slap_code_info(CodeInfo)
|
|
),
|
|
{ BranchCode = node([goto(FailureAddress) - "fail"]) },
|
|
{ Code = tree(PlaceCode, BranchCode) }
|
|
;
|
|
{ Code = node([goto(do_redo) - "fail"]) }
|
|
).
|
|
|
|
code_info__fail_if_rval_is_false(Rval0, Code) -->
|
|
code_info__top_failure_cont(FailureCont),
|
|
{ FailureCont = failure_cont(ContInfo, FailureMap) },
|
|
(
|
|
{ code_info__fail_cont_is_known(ContInfo) }
|
|
->
|
|
(
|
|
code_info__pick_matching_resume_addr(FailureMap,
|
|
FailureAddress0)
|
|
->
|
|
% We branch away if the test *fails*
|
|
{ code_util__neg_rval(Rval0, Rval) },
|
|
{ Code = node([
|
|
if_val(Rval, FailureAddress0) -
|
|
"Test for failure"
|
|
]) }
|
|
;
|
|
{ code_info__pick_first_resume_point(FailureMap,
|
|
Map, FailureAddress) },
|
|
{ map__to_assoc_list(Map, AssocList) },
|
|
code_info__get_next_label(SuccessLabel),
|
|
code_info__grab_code_info(CodeInfo),
|
|
code_info__place_vars(AssocList, PlaceCode),
|
|
code_info__slap_code_info(CodeInfo),
|
|
{ SuccessAddress = label(SuccessLabel) },
|
|
% We branch away if the test *fails*,
|
|
% therefore we branch around the code
|
|
% that moves variables to their failure
|
|
% locations and branches away
|
|
% if the test succeeds
|
|
{ TestCode = node([
|
|
if_val(Rval0, SuccessAddress) -
|
|
"Test for failure"
|
|
]) },
|
|
{ TailCode = node([
|
|
goto(FailureAddress) -
|
|
"Goto failure",
|
|
label(SuccessLabel) -
|
|
"Success continuation"
|
|
]) },
|
|
{ Code = tree(TestCode, tree(PlaceCode, TailCode)) }
|
|
)
|
|
;
|
|
{ FailureAddress = do_redo },
|
|
% We branch away if the test *fails*
|
|
{ code_util__neg_rval(Rval0, Rval) },
|
|
{ Code = node([
|
|
if_val(Rval, FailureAddress) -
|
|
"Test for failure"
|
|
]) }
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% See whether the current locations of variables match the locations
|
|
% associated with any of the options in the given failure map.
|
|
% If yes, return the code_addr of that option.
|
|
|
|
:- pred code_info__pick_matching_resume_addr(resume_maps, code_addr,
|
|
code_info, code_info).
|
|
:- mode code_info__pick_matching_resume_addr(in, out, in, out) is semidet.
|
|
|
|
code_info__pick_matching_resume_addr(ResumeMaps, Addr) -->
|
|
code_info__variable_locations(Locations),
|
|
{
|
|
ResumeMaps = orig_only(Map1, Addr1),
|
|
( code_info__match_resume_loc(Map1, Locations) ->
|
|
Addr = Addr1
|
|
;
|
|
fail
|
|
)
|
|
;
|
|
ResumeMaps = stack_only(Map1, Addr1),
|
|
( code_info__match_resume_loc(Map1, Locations) ->
|
|
Addr = Addr1
|
|
;
|
|
fail
|
|
)
|
|
;
|
|
ResumeMaps = orig_and_stack(Map1, Addr1, Map2, Addr2),
|
|
( code_info__match_resume_loc(Map1, Locations) ->
|
|
Addr = Addr1
|
|
; code_info__match_resume_loc(Map2, Locations) ->
|
|
Addr = Addr2
|
|
;
|
|
fail
|
|
)
|
|
;
|
|
ResumeMaps = stack_and_orig(Map1, Addr1, Map2, Addr2),
|
|
( code_info__match_resume_loc(Map1, Locations) ->
|
|
Addr = Addr1
|
|
; code_info__match_resume_loc(Map2, Locations) ->
|
|
Addr = Addr2
|
|
;
|
|
fail
|
|
)
|
|
}.
|
|
|
|
:- pred code_info__match_resume_loc(resume_map, resume_map).
|
|
:- mode code_info__match_resume_loc(in, in) is semidet.
|
|
|
|
code_info__match_resume_loc(Map, Locations0) :-
|
|
map__keys(Map, KeyList),
|
|
set__list_to_set(KeyList, Keys),
|
|
map__select(Locations0, Keys, Locations),
|
|
map__to_assoc_list(Locations, List),
|
|
\+ (
|
|
list__member(Thingy, List),
|
|
\+ (
|
|
Thingy = Var - Actual,
|
|
map__search(Map, Var, Rvals),
|
|
set__subset(Rvals, Actual)
|
|
)
|
|
).
|
|
|
|
% Find the first label that will be generated for the
|
|
% given failure continuation based on the scheme used by
|
|
% code_info__generate_failure_cont.
|
|
|
|
:- pred code_info__find_first_resume_label(failure_cont, code_addr).
|
|
:- mode code_info__find_first_resume_label(in, out) is det.
|
|
|
|
code_info__find_first_resume_label(FailureCont, Address) :-
|
|
FailureCont = failure_cont(ContInfo, FailMap),
|
|
(
|
|
ContInfo = nondet(_, MaybeRedoLabel),
|
|
MaybeRedoLabel = yes(RedoLabel)
|
|
->
|
|
Address = label(RedoLabel)
|
|
;
|
|
code_info__pick_first_resume_point(FailMap, _, Address)
|
|
).
|
|
|
|
:- pred code_info__pick_first_resume_point(resume_maps, resume_map, code_addr).
|
|
:- mode code_info__pick_first_resume_point(in, out, out) is det.
|
|
|
|
code_info__pick_first_resume_point(orig_only(Map, Addr), Map, Addr).
|
|
code_info__pick_first_resume_point(stack_only(Map, Addr), Map, Addr).
|
|
code_info__pick_first_resume_point(orig_and_stack(Map, Addr, _, _), Map, Addr).
|
|
code_info__pick_first_resume_point(stack_and_orig(Map, Addr, _, _), Map, Addr).
|
|
|
|
:- pred code_info__pick_last_resume_point(resume_maps, resume_map, code_addr).
|
|
:- mode code_info__pick_last_resume_point(in, out, out) is det.
|
|
|
|
code_info__pick_last_resume_point(orig_only(Map, Addr), Map, Addr).
|
|
code_info__pick_last_resume_point(stack_only(Map, Addr), Map, Addr).
|
|
code_info__pick_last_resume_point(orig_and_stack( _, _, Map, Addr), Map, Addr).
|
|
code_info__pick_last_resume_point(stack_and_orig( _, _, Map, Addr), Map, Addr).
|
|
|
|
:- pred code_info__pick_stack_resume_point(resume_maps, resume_map, code_addr).
|
|
:- mode code_info__pick_stack_resume_point(in, out, out) is det.
|
|
|
|
code_info__pick_stack_resume_point(orig_only(_, _), _, _) :-
|
|
error("no stack resume point").
|
|
code_info__pick_stack_resume_point(stack_only(Map, Addr), Map, Addr).
|
|
code_info__pick_stack_resume_point(orig_and_stack(_, _, Map, Addr), Map, Addr).
|
|
code_info__pick_stack_resume_point(stack_and_orig(Map, Addr, _, _), Map, Addr).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__unset_failure_cont(Code) -->
|
|
code_info__flush_resume_vars_to_stack(Code),
|
|
code_info__top_failure_cont(FailureCont0),
|
|
{ FailureCont0 = failure_cont(ContInfo0, FailureMap) },
|
|
code_info__pop_failure_cont,
|
|
{
|
|
ContInfo0 = semidet,
|
|
error("unset of semidet failure cont")
|
|
;
|
|
ContInfo0 = nondet(_, MaybeRedoLabel),
|
|
ContInfo = nondet(unknown, MaybeRedoLabel)
|
|
},
|
|
{ FailureCont = failure_cont(ContInfo, FailureMap) },
|
|
code_info__push_failure_cont(FailureCont).
|
|
|
|
code_info__flush_resume_vars_to_stack(Code) -->
|
|
code_info__top_failure_cont(FailureCont0),
|
|
{ FailureCont0 = failure_cont(_, FailureMap) },
|
|
{ code_info__pick_stack_resume_point(FailureMap, StackMap, _) },
|
|
{ map__to_assoc_list(StackMap, StackLocs) },
|
|
code_info__place_resume_vars(StackLocs, Code).
|
|
|
|
code_info__may_use_nondet_tailcall(MayTailCall) -->
|
|
code_info__top_failure_cont(FailureCont),
|
|
{ FailureCont = failure_cont(ContInfo, FailureMap) },
|
|
(
|
|
{ code_info__fail_cont_is_known(ContInfo) },
|
|
{ FailureMap = stack_only(_, do_fail) }
|
|
->
|
|
{ MayTailCall = yes }
|
|
;
|
|
{ MayTailCall = no }
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__do_soft_cut(TheFrame, Code, ContCode) -->
|
|
code_info__top_failure_cont(FailureCont),
|
|
{ FailureCont = failure_cont(ContInfo, _) },
|
|
(
|
|
{ code_info__fail_cont_is_known(ContInfo) }
|
|
->
|
|
{ code_info__find_first_resume_label(FailureCont, Address) }
|
|
;
|
|
% If the newly uncovered cont is unknown then
|
|
% we must have created a new frame before the
|
|
% condition which we no longer need, so we
|
|
% set its redoip to do_fail.
|
|
{ Address = do_fail }
|
|
),
|
|
code_info__get_globals(Globals),
|
|
{ globals__lookup_bool_option(Globals, use_trail, UseTrail) },
|
|
( { UseTrail = yes } ->
|
|
code_info__get_next_label(ContLabel),
|
|
{ ContAddress = label(ContLabel) },
|
|
{ ContCode = node([
|
|
label(ContLabel) - "soft cut failure cont entry point",
|
|
discard_ticket - "pop ticket stack",
|
|
goto(Address) - "branch to prev failure continuation"
|
|
]) }
|
|
;
|
|
{ ContAddress = Address },
|
|
{ ContCode = empty }
|
|
),
|
|
{ Code = node([
|
|
assign(redoip(lval(TheFrame)),
|
|
const(code_addr_const(ContAddress)))
|
|
- "prune away the `else' case of the if-then-else"
|
|
]) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__generate_semi_pre_commit(RedoLabel, Slots, PreCommit) -->
|
|
code_info__generate_pre_commit_saves(Slots, SaveCode),
|
|
code_info__get_next_label(ResumeLabel),
|
|
code_info__top_failure_cont(FailureCont0),
|
|
{ FailureCont0 = failure_cont(_, FailureMap0) },
|
|
{ code_info__pick_stack_resume_point(FailureMap0, StackMap, _) },
|
|
{ FailureCont = failure_cont(nondet(known, no),
|
|
stack_only(StackMap, label(ResumeLabel))) },
|
|
code_info__push_failure_cont(FailureCont),
|
|
code_info__get_next_label(RedoLabel),
|
|
{ HijackCode = node([
|
|
assign(redoip(lval(maxfr)),
|
|
const(code_addr_const(label(RedoLabel)))) -
|
|
"Hijack the failure cont"
|
|
]) },
|
|
{ PreCommit = tree(SaveCode, HijackCode) }.
|
|
|
|
code_info__generate_semi_commit(RedoLabel, Slots, Commit) -->
|
|
code_info__get_next_label(SuccLabel),
|
|
{ GotoSuccLabel = node([
|
|
goto(label(SuccLabel)) - "Jump to success continuation"
|
|
]) },
|
|
{ SuccLabelCode = node([
|
|
label(SuccLabel) - "Success continuation"
|
|
]) },
|
|
{ RedoLabelCode = node([
|
|
label(RedoLabel) - "Failure (redo) continuation"
|
|
]) },
|
|
|
|
code_info__grab_code_info(CodeInfo0),
|
|
code_info__top_failure_cont(FailureCont),
|
|
{ FailureCont = failure_cont(_, FailureMaps) },
|
|
code_info__generate_resume_setup(FailureMaps, FailureContCode),
|
|
code_info__pop_failure_cont,
|
|
code_info__generate_failure(Fail),
|
|
code_info__slap_code_info(CodeInfo0),
|
|
code_info__pop_failure_cont,
|
|
|
|
code_info__undo_pre_commit_saves(Slots, RestoreMaxfr, RestoreRedoip,
|
|
RestoreCurfr, SuccPopCode, FailPopCode),
|
|
|
|
{ SuccessCode =
|
|
tree(RestoreMaxfr,
|
|
tree(RestoreRedoip,
|
|
tree(RestoreCurfr,
|
|
SuccPopCode)))
|
|
},
|
|
{ FailCode =
|
|
tree(RedoLabelCode,
|
|
tree(RestoreCurfr,
|
|
tree(FailureContCode,
|
|
tree(RestoreRedoip,
|
|
tree(FailPopCode,
|
|
Fail)))))
|
|
},
|
|
{ Commit =
|
|
tree(SuccessCode,
|
|
tree(GotoSuccLabel,
|
|
tree(FailCode,
|
|
SuccLabelCode)))
|
|
}.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__generate_det_pre_commit(Slots, PreCommit) -->
|
|
code_info__generate_pre_commit_saves(Slots, PreCommit),
|
|
% Since the code we are cutting is model_non, it will call
|
|
% unset_failure_cont. If the current top entry on the failure stack
|
|
% at the time is semidet, this would cause unset_failure_cont to abort.
|
|
% We therefore push a dummy nondet failure continuation onto the
|
|
% failure stack. Since the code we are cutting across is multi,
|
|
% the failure continuation will never actually be used.
|
|
code_info__top_failure_cont(FailureCont0),
|
|
{ FailureCont0 = failure_cont(_, FailureMap) },
|
|
{ code_info__pick_stack_resume_point(FailureMap, StackMap, _) },
|
|
{ FailureCont = failure_cont(nondet(known, no),
|
|
stack_only(StackMap, do_fail)) },
|
|
code_info__push_failure_cont(FailureCont).
|
|
|
|
code_info__generate_det_commit(Slots, Commit) -->
|
|
% Remove the dummy failure continuation pushed by det_pre_commit.
|
|
code_info__pop_failure_cont,
|
|
code_info__undo_pre_commit_saves(Slots, RestoreMaxfr, RestoreRedoip,
|
|
RestoreCurfr, SuccPopCode, _FailPopCode),
|
|
{ Commit = tree(RestoreMaxfr,
|
|
tree(RestoreRedoip,
|
|
tree(RestoreCurfr,
|
|
SuccPopCode)))
|
|
}.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_info__generate_pre_commit_saves(commit_slots, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__generate_pre_commit_saves(out, out, in, out) is det.
|
|
|
|
code_info__generate_pre_commit_saves(Slots, Code) -->
|
|
code_info__get_globals(Globals),
|
|
{ globals__lookup_bool_option(Globals, use_trail, UseTrail) },
|
|
( { UseTrail = yes } ->
|
|
{ NumSlots = 5 },
|
|
{ SaveMessage =
|
|
"push space for curfr, maxfr, redoip, ticket_counter and trail_ptr" }
|
|
;
|
|
{ NumSlots = 3 },
|
|
{ SaveMessage = "push space for curfr, maxfr, and redoip" }
|
|
),
|
|
code_info__get_proc_model(CodeModel),
|
|
( { CodeModel = model_non } ->
|
|
% the pushes and pops on the det stack below will cause
|
|
% problems for accurate garbage collection. Hence we
|
|
% make sure the commit vals are made live, so gc
|
|
% can figure out what is going on later.
|
|
code_info__get_module_info(ModuleInfo),
|
|
code_info__get_pred_id(PredId),
|
|
{ predicate_module(ModuleInfo, PredId, ModuleName) },
|
|
{ predicate_name(ModuleInfo, PredId, PredName) },
|
|
{ prog_out__sym_name_to_string(ModuleName, ModuleNameString) },
|
|
{ string__append_list(["commit in ",
|
|
ModuleNameString, ":", PredName], Message) },
|
|
{ PushCode = node([
|
|
incr_sp(NumSlots, Message) - SaveMessage
|
|
]) },
|
|
{ CurfrSlot = stackvar(1) },
|
|
{ MaxfrSlot = stackvar(2) },
|
|
{ RedoipSlot = stackvar(3) },
|
|
( { UseTrail = yes } ->
|
|
{ TicketCounterSlot = stackvar(4) },
|
|
{ TrailPtrSlot = stackvar(5) },
|
|
{ MaybeTrailSlots = yes(TicketCounterSlot -
|
|
TrailPtrSlot) }
|
|
;
|
|
{ MaybeTrailSlots = no }
|
|
),
|
|
{ Slots = commit_slots(CurfrSlot, MaxfrSlot, RedoipSlot,
|
|
MaybeTrailSlots) },
|
|
code_info__add_commit_triple
|
|
;
|
|
{ PushCode = empty },
|
|
code_info__acquire_temp_slot(lval(curfr), CurfrSlot),
|
|
code_info__acquire_temp_slot(lval(maxfr), MaxfrSlot),
|
|
code_info__acquire_temp_slot(lval(redoip(lval(maxfr))),
|
|
RedoipSlot),
|
|
( { UseTrail = yes } ->
|
|
code_info__acquire_temp_slot(ticket_counter,
|
|
TicketCounterSlot),
|
|
code_info__acquire_temp_slot(ticket, TrailPtrSlot),
|
|
{ MaybeTrailSlots = yes(TicketCounterSlot -
|
|
TrailPtrSlot) }
|
|
;
|
|
{ MaybeTrailSlots = no }
|
|
),
|
|
{ Slots = commit_slots(CurfrSlot, MaxfrSlot, RedoipSlot,
|
|
MaybeTrailSlots) }
|
|
),
|
|
{ SaveCode = node([
|
|
assign(CurfrSlot, lval(curfr)) -
|
|
"Save current nondet frame pointer",
|
|
assign(MaxfrSlot, lval(maxfr)) -
|
|
"Save top of nondet stack",
|
|
assign(RedoipSlot, lval(redoip(lval(maxfr)))) -
|
|
"Save the top redoip"
|
|
]) },
|
|
{ MaybeTrailSlots = yes(TheTicketCounterSlot - TheTrailPtrSlot) ->
|
|
MaybeSaveTrailCode = node([
|
|
mark_ticket_stack(TheTicketCounterSlot) -
|
|
"Save the ticket counter",
|
|
store_ticket(TheTrailPtrSlot) -
|
|
"Save the trail pointer"
|
|
])
|
|
;
|
|
MaybeSaveTrailCode = empty
|
|
},
|
|
{ Code = tree(PushCode, tree(SaveCode, MaybeSaveTrailCode)) }.
|
|
|
|
:- pred code_info__undo_pre_commit_saves(commit_slots, code_tree, code_tree,
|
|
code_tree, code_tree, code_tree, code_info, code_info).
|
|
:- mode code_info__undo_pre_commit_saves(in, out, out, out, out, out, in, out)
|
|
is det.
|
|
|
|
code_info__undo_pre_commit_saves(Slots, RestoreMaxfr, RestoreRedoip,
|
|
RestoreCurfr, SuccPopCode, FailPopCode) -->
|
|
{ Slots = commit_slots(CurfrSlot, MaxfrSlot, RedoipSlot,
|
|
MaybeTrailSlots) },
|
|
{ RestoreMaxfr = node([
|
|
assign(maxfr, lval(MaxfrSlot)) -
|
|
"Prune away unwanted choice-points"
|
|
]) },
|
|
{ RestoreRedoip = node([
|
|
assign(redoip(lval(maxfr)), lval(RedoipSlot)) -
|
|
"Restore the top redoip"
|
|
]) },
|
|
{ RestoreCurfr = node([
|
|
assign(curfr, lval(CurfrSlot)) -
|
|
"Restore nondet frame pointer"
|
|
]) },
|
|
code_info__get_proc_model(CodeModel),
|
|
( { CodeModel = model_non } ->
|
|
code_info__rem_commit_triple,
|
|
( { MaybeTrailSlots = yes(TicketCounterSlot - TrailPtrSlot) } ->
|
|
{ CommitTrail = node([
|
|
reset_ticket(lval(TrailPtrSlot), commit) -
|
|
"discard trail entries and restore trail ptr"
|
|
]) },
|
|
{ RestoreTrail = node([
|
|
reset_ticket(lval(TrailPtrSlot), undo) -
|
|
"apply trail entries and restore trail ptr"
|
|
]) },
|
|
{ RestoreTicketCounter = node([
|
|
discard_tickets_to(lval(TicketCounterSlot)) -
|
|
"restore the ticket counter"
|
|
]) },
|
|
{ NumSlots = 5 },
|
|
{ PopMessage =
|
|
"pop curfr, maxfr, redoip, ticket_counter and trail_ptr" }
|
|
;
|
|
{ CommitTrail = empty },
|
|
{ RestoreTrail = empty },
|
|
{ RestoreTicketCounter = empty },
|
|
{ NumSlots = 3 },
|
|
{ PopMessage = "pop curfr, maxfr, and redoip" }
|
|
),
|
|
{ MainPopCode = node([
|
|
decr_sp(NumSlots) - PopMessage
|
|
]) }
|
|
;
|
|
code_info__release_temp_slot(CurfrSlot),
|
|
code_info__release_temp_slot(MaxfrSlot),
|
|
code_info__release_temp_slot(RedoipSlot),
|
|
( { MaybeTrailSlots = yes(TicketCounterSlot - TrailPtrSlot) } ->
|
|
{ CommitTrail = node([
|
|
reset_ticket(lval(TrailPtrSlot), commit) -
|
|
"discard trail entries and restore trail ptr"
|
|
]) },
|
|
{ RestoreTrail = node([
|
|
reset_ticket(lval(TrailPtrSlot), undo) -
|
|
"apply trail entries and restore trail ptr"
|
|
]) },
|
|
{ RestoreTicketCounter = node([
|
|
discard_tickets_to(lval(TicketCounterSlot)) -
|
|
"restore the ticket counter"
|
|
]) },
|
|
code_info__release_temp_slot(TicketCounterSlot),
|
|
code_info__release_temp_slot(TrailPtrSlot)
|
|
;
|
|
{ CommitTrail = empty },
|
|
{ RestoreTrail = empty },
|
|
{ RestoreTicketCounter = empty }
|
|
),
|
|
{ MainPopCode = empty }
|
|
),
|
|
{ SuccPopCode =
|
|
tree(CommitTrail,
|
|
tree(RestoreTicketCounter,
|
|
MainPopCode)) },
|
|
{ FailPopCode =
|
|
tree(RestoreTrail,
|
|
tree(RestoreTicketCounter,
|
|
MainPopCode)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Submodule to deal with liveness issues.
|
|
|
|
:- interface.
|
|
|
|
:- pred code_info__get_known_variables(list(var), code_info, code_info).
|
|
:- mode code_info__get_known_variables(out, in, out) is det.
|
|
|
|
:- pred code_info__variable_is_forward_live(var, code_info, code_info).
|
|
:- mode code_info__variable_is_forward_live(in, in, out) is semidet.
|
|
|
|
:- pred code_info__make_vars_forward_dead(set(var), code_info, code_info).
|
|
:- mode code_info__make_vars_forward_dead(in, in, out) is det.
|
|
|
|
:- pred code_info__pickup_zombies(set(var), code_info, code_info).
|
|
:- mode code_info__pickup_zombies(out, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- pred code_info__add_forward_live_vars(set(var), code_info, code_info).
|
|
:- mode code_info__add_forward_live_vars(in, in, out) is det.
|
|
|
|
:- pred code_info__rem_forward_live_vars(set(var), code_info, code_info).
|
|
:- mode code_info__rem_forward_live_vars(in, in, out) is det.
|
|
|
|
% Make these variables appear magically live.
|
|
% We don't care where they are put.
|
|
|
|
:- pred code_info__make_vars_forward_live(set(var), code_info, code_info).
|
|
:- mode code_info__make_vars_forward_live(in, in, out) is det.
|
|
|
|
code_info__get_known_variables(VarList) -->
|
|
code_info__get_forward_live_vars(ForwardLiveVars),
|
|
code_info__current_resume_point_vars(ResumeVars),
|
|
{ set__union(ForwardLiveVars, ResumeVars, Vars) },
|
|
{ set__to_sorted_list(Vars, VarList) }.
|
|
|
|
code_info__variable_is_forward_live(Var) -->
|
|
code_info__get_forward_live_vars(Liveness),
|
|
{ set__member(Var, Liveness) }.
|
|
|
|
code_info__add_forward_live_vars(Births) -->
|
|
code_info__get_forward_live_vars(Liveness0),
|
|
{ set__union(Liveness0, Births, Liveness) },
|
|
code_info__set_forward_live_vars(Liveness).
|
|
|
|
code_info__rem_forward_live_vars(Deaths) -->
|
|
code_info__get_forward_live_vars(Liveness0),
|
|
{ set__difference(Liveness0, Deaths, Liveness) },
|
|
code_info__set_forward_live_vars(Liveness).
|
|
|
|
code_info__make_vars_forward_live(Vars) -->
|
|
code_info__get_stack_slots(StackSlots),
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ set__to_sorted_list(Vars, VarList) },
|
|
{ code_info__make_vars_forward_live_2(VarList, StackSlots, 1,
|
|
Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
:- pred code_info__make_vars_forward_live_2(list(var), stack_slots, int,
|
|
exprn_info, exprn_info).
|
|
:- mode code_info__make_vars_forward_live_2(in, in, in, in, out) is det.
|
|
|
|
code_info__make_vars_forward_live_2([], _, _, Exprn, Exprn).
|
|
code_info__make_vars_forward_live_2([V | Vs], StackSlots, N0, Exprn0, Exprn) :-
|
|
( map__search(StackSlots, V, Lval0) ->
|
|
Lval = Lval0,
|
|
N1 = N0
|
|
;
|
|
code_info__find_unused_reg(N0, Exprn0, N1),
|
|
Lval = reg(r, N1)
|
|
),
|
|
code_exprn__maybe_set_var_location(V, Lval, Exprn0, Exprn1),
|
|
code_info__make_vars_forward_live_2(Vs, StackSlots, N1, Exprn1, Exprn).
|
|
|
|
:- pred code_info__find_unused_reg(int, exprn_info, int).
|
|
:- mode code_info__find_unused_reg(in, in, out) is det.
|
|
|
|
code_info__find_unused_reg(N0, Exprn0, N) :-
|
|
( code_exprn__lval_in_use(reg(r, N0), Exprn0, _) ->
|
|
N1 is N0 + 1,
|
|
code_info__find_unused_reg(N1, Exprn0, N)
|
|
;
|
|
N = N0
|
|
).
|
|
|
|
code_info__make_vars_forward_dead(Vars0) -->
|
|
code_info__current_resume_point_vars(ResumeVars),
|
|
{ set__intersect(Vars0, ResumeVars, FlushVars) },
|
|
code_info__get_zombies(Zombies0),
|
|
{ set__union(Zombies0, FlushVars, Zombies) },
|
|
code_info__set_zombies(Zombies),
|
|
{ set__difference(Vars0, Zombies, Vars) },
|
|
{ set__to_sorted_list(Vars, VarList) },
|
|
code_info__make_vars_forward_dead_2(VarList).
|
|
|
|
:- pred code_info__make_vars_forward_dead_2(list(var), code_info, code_info).
|
|
:- mode code_info__make_vars_forward_dead_2(in, in, out) is det.
|
|
|
|
code_info__make_vars_forward_dead_2([]) --> [].
|
|
code_info__make_vars_forward_dead_2([V | Vs]) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__var_becomes_dead(V, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn),
|
|
code_info__make_vars_forward_dead_2(Vs).
|
|
|
|
code_info__pickup_zombies(Zombies) -->
|
|
code_info__get_zombies(Zombies),
|
|
{ set__init(Empty) },
|
|
code_info__set_zombies(Empty).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Submodule for handling the saving and restoration
|
|
% of trail tickets, heap pointers, stack pointers etc.
|
|
|
|
:- interface.
|
|
|
|
:- pred code_info__save_hp(code_tree, lval, code_info, code_info).
|
|
:- mode code_info__save_hp(out, out, in, out) is det.
|
|
|
|
:- pred code_info__restore_hp(lval, code_tree, code_info, code_info).
|
|
:- mode code_info__restore_hp(in, out, in, out) is det.
|
|
|
|
:- pred code_info__restore_and_discard_hp(lval, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__restore_and_discard_hp(in, out, in, out) is det.
|
|
|
|
:- pred code_info__discard_hp(lval, code_info, code_info).
|
|
:- mode code_info__discard_hp(in, in, out) is det.
|
|
|
|
:- pred code_info__maybe_save_hp(bool, code_tree, maybe(lval),
|
|
code_info, code_info).
|
|
:- mode code_info__maybe_save_hp(in, out, out, in, out) is det.
|
|
|
|
:- pred code_info__maybe_restore_hp(maybe(lval), code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__maybe_restore_hp(in, out, in, out) is det.
|
|
|
|
:- pred code_info__maybe_restore_and_discard_hp(maybe(lval), code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__maybe_restore_and_discard_hp(in, out, in, out) is det.
|
|
|
|
:- pred code_info__maybe_discard_hp(maybe(lval), code_info, code_info).
|
|
:- mode code_info__maybe_discard_hp(in, in, out) is det.
|
|
|
|
:- pred code_info__save_ticket(code_tree, lval, code_info, code_info).
|
|
:- mode code_info__save_ticket(out, out, in, out) is det.
|
|
|
|
:- pred code_info__reset_ticket(lval, reset_trail_reason, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__reset_ticket(in, in, out, in, out) is det.
|
|
|
|
:- pred code_info__reset_and_discard_ticket(lval, reset_trail_reason, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__reset_and_discard_ticket(in, in, out, in, out) is det.
|
|
|
|
:- pred code_info__discard_ticket(lval, code_tree, code_info, code_info).
|
|
:- mode code_info__discard_ticket(in, out, in, out) is det.
|
|
|
|
:- pred code_info__maybe_save_ticket(bool, code_tree, maybe(lval),
|
|
code_info, code_info).
|
|
:- mode code_info__maybe_save_ticket(in, out, out, in, out) is det.
|
|
|
|
:- pred code_info__maybe_reset_ticket(maybe(lval), reset_trail_reason,
|
|
code_tree, code_info, code_info).
|
|
:- mode code_info__maybe_reset_ticket(in, in, out, in, out) is det.
|
|
|
|
:- pred code_info__maybe_reset_and_discard_ticket(maybe(lval),
|
|
reset_trail_reason, code_tree, code_info, code_info).
|
|
:- mode code_info__maybe_reset_and_discard_ticket(in, in, out, in, out) is det.
|
|
|
|
:- pred code_info__maybe_discard_ticket(maybe(lval), code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__maybe_discard_ticket(in, out, in, out) is det.
|
|
|
|
:- pred code_info__save_maxfr(lval, code_tree, code_info, code_info).
|
|
:- mode code_info__save_maxfr(out, out, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
code_info__save_hp(Code, HpSlot) -->
|
|
code_info__acquire_temp_slot(lval(hp), HpSlot),
|
|
{ Code = node([mark_hp(HpSlot) - "Save heap pointer"]) }.
|
|
|
|
code_info__restore_hp(HpSlot, Code) -->
|
|
{ Code = node([restore_hp(lval(HpSlot)) - "Restore heap pointer"]) }.
|
|
|
|
code_info__discard_hp(HpSlot) -->
|
|
code_info__release_temp_slot(HpSlot).
|
|
|
|
code_info__restore_and_discard_hp(HpSlot, Code) -->
|
|
{ Code = node([restore_hp(lval(HpSlot)) - "Restore heap pointer"]) },
|
|
code_info__discard_hp(HpSlot).
|
|
|
|
code_info__maybe_save_hp(Maybe, Code, MaybeHpSlot) -->
|
|
( { Maybe = yes } ->
|
|
code_info__save_hp(Code, HpSlot),
|
|
{ MaybeHpSlot = yes(HpSlot) }
|
|
;
|
|
{ Code = empty },
|
|
{ MaybeHpSlot = no }
|
|
).
|
|
|
|
code_info__maybe_restore_hp(MaybeHpSlot, Code) -->
|
|
( { MaybeHpSlot = yes(HpSlot) } ->
|
|
code_info__restore_hp(HpSlot, Code)
|
|
;
|
|
{ Code = empty }
|
|
).
|
|
|
|
code_info__maybe_restore_and_discard_hp(MaybeHpSlot, Code) -->
|
|
( { MaybeHpSlot = yes(HpSlot) } ->
|
|
code_info__restore_and_discard_hp(HpSlot, Code)
|
|
;
|
|
{ Code = empty }
|
|
).
|
|
|
|
code_info__maybe_discard_hp(MaybeHpSlot) -->
|
|
( { MaybeHpSlot = yes(HpSlot) } ->
|
|
code_info__discard_hp(HpSlot)
|
|
;
|
|
[]
|
|
).
|
|
|
|
code_info__save_ticket(Code, TicketSlot) -->
|
|
code_info__acquire_temp_slot(ticket, TicketSlot),
|
|
{ Code = node([store_ticket(TicketSlot) - "Save trail state"]) }.
|
|
|
|
code_info__reset_ticket(TicketSlot, Reason, Code) -->
|
|
{ Code = node([reset_ticket(lval(TicketSlot), Reason) - "Reset trail"]) }.
|
|
|
|
code_info__reset_and_discard_ticket(TicketSlot, Reason, Code) -->
|
|
code_info__release_temp_slot(TicketSlot),
|
|
{ Code = node([
|
|
reset_ticket(lval(TicketSlot), Reason) - "Restore trail",
|
|
discard_ticket - "Pop ticket stack"
|
|
]) }.
|
|
|
|
code_info__discard_ticket(TicketSlot, Code) -->
|
|
code_info__release_temp_slot(TicketSlot),
|
|
{ Code = node([discard_ticket - "Pop ticket stack"]) }.
|
|
|
|
code_info__maybe_save_ticket(Maybe, Code, MaybeTicketSlot) -->
|
|
( { Maybe = yes } ->
|
|
code_info__save_ticket(Code, TicketSlot),
|
|
{ MaybeTicketSlot = yes(TicketSlot) }
|
|
;
|
|
{ Code = empty },
|
|
{ MaybeTicketSlot = no }
|
|
).
|
|
|
|
code_info__maybe_reset_ticket(MaybeTicketSlot, Reason, Code) -->
|
|
( { MaybeTicketSlot = yes(TicketSlot) } ->
|
|
code_info__reset_ticket(TicketSlot, Reason, Code)
|
|
;
|
|
{ Code = empty }
|
|
).
|
|
|
|
code_info__maybe_reset_and_discard_ticket(MaybeTicketSlot, Reason, Code) -->
|
|
( { MaybeTicketSlot = yes(TicketSlot) } ->
|
|
code_info__reset_and_discard_ticket(TicketSlot, Reason, Code)
|
|
;
|
|
{ Code = empty }
|
|
).
|
|
|
|
code_info__maybe_discard_ticket(MaybeTicketSlot, Code) -->
|
|
( { MaybeTicketSlot = yes(TicketSlot) } ->
|
|
code_info__discard_ticket(TicketSlot, Code)
|
|
;
|
|
{ Code = empty }
|
|
).
|
|
|
|
code_info__save_maxfr(MaxfrSlot, Code) -->
|
|
code_info__acquire_temp_slot(lval(maxfr), MaxfrSlot),
|
|
{ Code = node([assign(MaxfrSlot, lval(maxfr)) - "Save maxfr"]) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Submodule to deal with code_exprn.
|
|
|
|
:- interface.
|
|
|
|
:- pred code_info__variable_locations(map(var, set(rval)),
|
|
code_info, code_info).
|
|
:- mode code_info__variable_locations(out, in, out) is det.
|
|
|
|
:- pred code_info__set_var_location(var, lval, code_info, code_info).
|
|
:- mode code_info__set_var_location(in, in, in, out) is det.
|
|
|
|
:- pred code_info__cache_expression(var, rval, code_info, code_info).
|
|
:- mode code_info__cache_expression(in, in, in, out) is det.
|
|
|
|
:- pred code_info__place_var(var, lval, code_tree, code_info, code_info).
|
|
:- mode code_info__place_var(in, in, out, in, out) is det.
|
|
|
|
:- pred code_info__produce_variable(var, code_tree, rval, code_info, code_info).
|
|
:- mode code_info__produce_variable(in, out, out, in, out) is det.
|
|
|
|
:- pred code_info__produce_variable_in_reg(var, code_tree, rval,
|
|
code_info, code_info).
|
|
:- mode code_info__produce_variable_in_reg(in, out, out, in, out) is det.
|
|
|
|
:- pred code_info__produce_variable_in_reg_or_stack(var, code_tree, rval,
|
|
code_info, code_info).
|
|
:- mode code_info__produce_variable_in_reg_or_stack(in, out, out, in, out)
|
|
is det.
|
|
|
|
:- pred code_info__materialize_vars_in_rval(rval, rval, code_tree, code_info,
|
|
code_info).
|
|
:- mode code_info__materialize_vars_in_rval(in, out, out, in, out) is det.
|
|
|
|
:- pred code_info__lock_reg(lval, code_info, code_info).
|
|
:- mode code_info__lock_reg(in, in, out) is det.
|
|
|
|
:- pred code_info__unlock_reg(lval, code_info, code_info).
|
|
:- mode code_info__unlock_reg(in, in, out) is det.
|
|
|
|
:- pred code_info__acquire_reg_for_var(var, lval, code_info, code_info).
|
|
:- mode code_info__acquire_reg_for_var(in, out, in, out) is det.
|
|
|
|
:- pred code_info__acquire_reg(reg_type, lval, code_info, code_info).
|
|
:- mode code_info__acquire_reg(in, out, in, out) is det.
|
|
|
|
:- pred code_info__release_reg(lval, code_info, code_info).
|
|
:- mode code_info__release_reg(in, in, out) is det.
|
|
|
|
:- pred code_info__clear_r1(code_tree, code_info, code_info).
|
|
:- mode code_info__clear_r1(out, in, out) is det.
|
|
|
|
:- pred code_info__generate_branch_end(code_model, store_map, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__generate_branch_end(in, in, out, in, out) is det.
|
|
|
|
% code_info__remake_with_store_map throws away the exprn_info data
|
|
% structure, forgetting the current locations of all variables,
|
|
% and rebuilds it from scratch based on the given store map.
|
|
% The new exprn_info will know about only the variables present
|
|
% in the store map, and will believe they are where the store map
|
|
% says they are.
|
|
|
|
:- pred code_info__remake_with_store_map(store_map, code_info, code_info).
|
|
:- mode code_info__remake_with_store_map(in, in, out) is det.
|
|
|
|
:- type call_direction ---> caller ; callee.
|
|
|
|
% Generate code to either setup the input arguments for a call
|
|
% (i.e. in the caller), or to setup the output arguments in the
|
|
% predicate epilog (i.e. in the callee).
|
|
|
|
:- pred code_info__setup_call(assoc_list(var, arg_info),
|
|
call_direction, code_tree, code_info, code_info).
|
|
:- mode code_info__setup_call(in, in, out, in, out) is det.
|
|
|
|
:- pred code_info__clear_all_registers(code_info, code_info).
|
|
:- mode code_info__clear_all_registers(in, out) is det.
|
|
|
|
:- pred code_info__save_variable_on_stack(var, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__save_variable_on_stack(in, out, in, out) is det.
|
|
|
|
:- pred code_info__save_variables_on_stack(list(var), code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__save_variables_on_stack(in, out, in, out) is det.
|
|
|
|
:- pred code_info__max_reg_in_use(int, code_info, code_info).
|
|
:- mode code_info__max_reg_in_use(out, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- pred code_info__place_vars(assoc_list(var, set(rval)), code_tree,
|
|
code_info, code_info).
|
|
:- mode code_info__place_vars(in, out, in, out) is det.
|
|
|
|
code_info__variable_locations(Locations) -->
|
|
code_info__get_exprn_info(Exprn),
|
|
{ code_exprn__get_varlocs(Exprn, Locations) }.
|
|
|
|
code_info__set_var_location(Var, Lval) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__set_var_location(Var, Lval, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__cache_expression(Var, Rval) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__cache_exprn(Var, Rval, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__place_var(Var, Lval, Code) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__place_var(Var, Lval, Code, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__place_vars([], empty) --> [].
|
|
code_info__place_vars([V - Rs | RestList], Code) -->
|
|
(
|
|
{ set__to_sorted_list(Rs, RList) },
|
|
{ code_info__lval_in_rval_list(L, RList) }
|
|
->
|
|
code_info__place_var(V, L, ThisCode)
|
|
;
|
|
{ ThisCode = empty }
|
|
),
|
|
code_info__place_vars(RestList, RestCode),
|
|
{ Code = tree(ThisCode, RestCode) }.
|
|
|
|
:- pred code_info__lval_in_rval_list(lval, list(rval)).
|
|
:- mode code_info__lval_in_rval_list(out, in) is semidet.
|
|
|
|
code_info__lval_in_rval_list(Lval, [Rval | Rvals]) :-
|
|
( Rval = lval(Lval0) ->
|
|
Lval = Lval0
|
|
;
|
|
code_info__lval_in_rval_list(Lval, Rvals)
|
|
).
|
|
|
|
code_info__produce_variable(Var, Code, Rval) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__produce_var(Var, Rval, Code, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__produce_variable_in_reg(Var, Code, Rval) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__produce_var_in_reg(Var, Rval, Code, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__produce_variable_in_reg_or_stack(Var, Code, Rval) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__produce_var_in_reg_or_stack(Var, Rval, Code,
|
|
Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__materialize_vars_in_rval(Rval0, Rval, Code) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__materialize_vars_in_rval(Rval0, Rval, Code,
|
|
Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__lock_reg(Reg) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__lock_reg(Reg, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__unlock_reg(Reg) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__unlock_reg(Reg, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__acquire_reg_for_var(Var, Lval) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
code_info__get_follow_vars(Follow),
|
|
(
|
|
{ map__search(Follow, Var, PrefLval) },
|
|
{ PrefLval = reg(PrefRegType, PrefRegNum) }
|
|
->
|
|
{ code_exprn__acquire_reg_prefer_given(PrefRegType, PrefRegNum,
|
|
Lval, Exprn0, Exprn) }
|
|
;
|
|
{ code_exprn__acquire_reg(r, Lval, Exprn0, Exprn) }
|
|
),
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__acquire_reg(Type, Lval) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__acquire_reg(Type, Lval, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__release_reg(Lval) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__release_reg(Lval, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__clear_r1(Code) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__clear_r1(Code, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__generate_branch_end(CodeModel, StoreMap, Code) -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ map__to_assoc_list(StoreMap, VarLocs) },
|
|
{ code_exprn__place_vars(VarLocs, PlaceCode, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn),
|
|
( { CodeModel = model_non } ->
|
|
code_info__unset_failure_cont(FlushCode),
|
|
{ Code = tree(PlaceCode, FlushCode) }
|
|
;
|
|
{ Code = PlaceCode }
|
|
).
|
|
|
|
code_info__remake_with_store_map(StoreMap) -->
|
|
{ map__to_assoc_list(StoreMap, VarLvals) },
|
|
{ code_info__fixup_lvallist(VarLvals, VarRvals) },
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__reinit_state(VarRvals, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
:- pred code_info__fixup_lvallist(assoc_list(var, lval), assoc_list(var, rval)).
|
|
:- mode code_info__fixup_lvallist(in, out) is det.
|
|
|
|
code_info__fixup_lvallist([], []).
|
|
code_info__fixup_lvallist([V - L | Ls], [V - lval(L) | Rs]) :-
|
|
code_info__fixup_lvallist(Ls, Rs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__setup_call([], _Direction, empty) --> [].
|
|
code_info__setup_call([V - arg_info(Loc, Mode) | Rest], Direction, Code) -->
|
|
(
|
|
{
|
|
Mode = top_in,
|
|
Direction = caller
|
|
;
|
|
Mode = top_out,
|
|
Direction = callee
|
|
}
|
|
->
|
|
{ code_util__arg_loc_to_register(Loc, Reg) },
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__place_var(V, Reg, Code0, Exprn0, Exprn1) },
|
|
% We need to test that either the variable
|
|
% is live OR it occurs in the remaining arguments
|
|
% because of a bug in polymorphism.m which
|
|
% causes some compiler generated code to violate
|
|
% superhomogeneous form
|
|
(
|
|
code_info__variable_is_forward_live(V)
|
|
->
|
|
{ IsLive = yes }
|
|
;
|
|
{ IsLive = no }
|
|
),
|
|
{
|
|
list__member(Vtmp - _, Rest),
|
|
V = Vtmp
|
|
->
|
|
Occurs = yes
|
|
;
|
|
Occurs = no
|
|
},
|
|
(
|
|
% We can't simply use a disj here
|
|
% because of bugs in modes/det_analysis
|
|
{ bool__or(Occurs, IsLive, yes) }
|
|
->
|
|
{ code_exprn__lock_reg(Reg, Exprn1, Exprn2) },
|
|
code_info__set_exprn_info(Exprn2),
|
|
code_info__setup_call(Rest, Direction, Code1),
|
|
code_info__get_exprn_info(Exprn3),
|
|
{ code_exprn__unlock_reg(Reg, Exprn3, Exprn) },
|
|
code_info__set_exprn_info(Exprn),
|
|
{ Code = tree(Code0, Code1) }
|
|
;
|
|
{ code_exprn__lock_reg(Reg, Exprn1, Exprn2) },
|
|
code_info__set_exprn_info(Exprn2),
|
|
{ set__singleton_set(Vset, V) },
|
|
code_info__make_vars_forward_dead(Vset),
|
|
code_info__setup_call(Rest, Direction, Code1),
|
|
code_info__get_exprn_info(Exprn4),
|
|
{ code_exprn__unlock_reg(Reg, Exprn4, Exprn) },
|
|
code_info__set_exprn_info(Exprn),
|
|
{ Code = tree(Code0, Code1) }
|
|
)
|
|
;
|
|
code_info__setup_call(Rest, Direction, Code)
|
|
).
|
|
|
|
% XXX We could use the sanity checking mechanism...
|
|
code_info__clear_all_registers -->
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__clobber_regs([], Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__save_variable_on_stack(Var, Code) -->
|
|
code_info__get_variable_slot(Var, Slot),
|
|
code_info__get_exprn_info(Exprn0),
|
|
{ code_exprn__place_var(Var, Slot, Code, Exprn0, Exprn) },
|
|
code_info__set_exprn_info(Exprn).
|
|
|
|
code_info__save_variables_on_stack([], empty) --> [].
|
|
code_info__save_variables_on_stack([Var | Vars], Code) -->
|
|
code_info__save_variable_on_stack(Var, FirstCode),
|
|
code_info__save_variables_on_stack(Vars, RestCode),
|
|
{ Code = tree(FirstCode, RestCode) }.
|
|
|
|
code_info__max_reg_in_use(Max) -->
|
|
code_info__get_exprn_info(Exprn),
|
|
{ code_exprn__max_reg_in_use(Exprn, Max) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Submodule for dealing with information for garbage collection
|
|
% and value numbering.
|
|
|
|
:- interface.
|
|
|
|
:- pred code_info__generate_stack_livevals(set(var), set(lval),
|
|
code_info, code_info).
|
|
:- mode code_info__generate_stack_livevals(in, out, in, out) is det.
|
|
|
|
:- pred code_info__generate_stack_livelvals(set(var), instmap,
|
|
list(liveinfo), code_info, code_info).
|
|
:- mode code_info__generate_stack_livelvals(in, in, out, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
code_info__generate_stack_livevals(Args, LiveVals) -->
|
|
code_info__get_known_variables(LiveVars),
|
|
{ set__list_to_set(LiveVars, Vars0) },
|
|
{ set__difference(Vars0, Args, Vars) },
|
|
{ set__to_sorted_list(Vars, VarList) },
|
|
{ set__init(LiveVals0) },
|
|
code_info__generate_var_livevals(VarList, LiveVals0, LiveVals1),
|
|
code_info__get_temps_in_use(TempsSet),
|
|
{ map__to_assoc_list(TempsSet, Temps) },
|
|
{ code_info__generate_temp_livevals(Temps, LiveVals1, LiveVals2) },
|
|
code_info__get_commit_triple_count(Triples),
|
|
{ code_info__generate_commit_livevals(Triples, LiveVals2, LiveVals) }.
|
|
|
|
:- pred code_info__generate_var_livevals(list(var), set(lval), set(lval),
|
|
code_info, code_info).
|
|
:- mode code_info__generate_var_livevals(in, in, out, in, out) is det.
|
|
|
|
code_info__generate_var_livevals([], Vals, Vals) --> [].
|
|
code_info__generate_var_livevals([V | Vs], Vals0, Vals) -->
|
|
code_info__get_variable_slot(V, Slot),
|
|
{ set__insert(Vals0, Slot, Vals1) },
|
|
code_info__generate_var_livevals(Vs, Vals1, Vals).
|
|
|
|
:- pred code_info__generate_temp_livevals(assoc_list(lval, slot_contents),
|
|
set(lval), set(lval)).
|
|
:- mode code_info__generate_temp_livevals(in, in, out) is det.
|
|
|
|
code_info__generate_temp_livevals([], Vals, Vals).
|
|
code_info__generate_temp_livevals([Slot - _ | Slots], Vals0, Vals) :-
|
|
set__insert(Vals0, Slot, Vals1),
|
|
code_info__generate_temp_livevals(Slots, Vals1, Vals).
|
|
|
|
:- pred code_info__generate_commit_livevals(int, set(lval), set(lval)).
|
|
:- mode code_info__generate_commit_livevals(in, in, out) is det.
|
|
|
|
code_info__generate_commit_livevals(Triples0, Vals0, Vals) :-
|
|
( Triples0 = 0 ->
|
|
Vals = Vals0
|
|
;
|
|
CurfrSlot is (Triples0 - 1) * 3 + 1,
|
|
MaxfrSlot is (Triples0 - 1) * 3 + 2,
|
|
RedoipSlot is (Triples0 - 1) * 3 + 3,
|
|
CurfrVar = stackvar(CurfrSlot),
|
|
MaxfrVar = stackvar(MaxfrSlot),
|
|
RedoipVar = stackvar(RedoipSlot),
|
|
set__insert(Vals0, CurfrVar, Vals1),
|
|
set__insert(Vals1, MaxfrVar, Vals2),
|
|
set__insert(Vals2, RedoipVar, Vals3),
|
|
Triples1 is Triples0 - 1,
|
|
code_info__generate_commit_livevals(Triples1, Vals3, Vals)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__generate_stack_livelvals(Args, AfterCallInstMap, LiveVals) -->
|
|
code_info__get_known_variables(LiveVars),
|
|
{ set__list_to_set(LiveVars, Vars0) },
|
|
{ set__difference(Vars0, Args, Vars) },
|
|
{ set__to_sorted_list(Vars, VarList) },
|
|
{ set__init(LiveVals0) },
|
|
code_info__generate_var_livelvals(VarList, LiveVals0, LiveVals1),
|
|
{ set__to_sorted_list(LiveVals1, LiveVals2) },
|
|
code_info__get_globals(Globals),
|
|
{ globals__get_gc_method(Globals, GC_Method) },
|
|
code_info__livevals_to_livelvals(LiveVals2, GC_Method,
|
|
AfterCallInstMap, LiveVals3),
|
|
code_info__get_temps_in_use(TempsSet),
|
|
{ map__to_assoc_list(TempsSet, Temps) },
|
|
{ code_info__generate_temp_livelvals(Temps, LiveVals3, LiveVals4) },
|
|
code_info__get_commit_triple_count(Triples),
|
|
{ code_info__generate_commit_livelvals(Triples, LiveVals4, LiveVals) }.
|
|
|
|
:- pred code_info__generate_var_livelvals(list(var),
|
|
set(pair(lval, var)), set(pair(lval, var)), code_info, code_info).
|
|
:- mode code_info__generate_var_livelvals(in, in, out, in, out) is det.
|
|
|
|
code_info__generate_var_livelvals([], Vals, Vals) --> [].
|
|
code_info__generate_var_livelvals([V | Vs], Vals0, Vals) -->
|
|
code_info__get_variable_slot(V, Slot),
|
|
{ set__insert(Vals0, Slot - V, Vals1) },
|
|
code_info__generate_var_livelvals(Vs, Vals1, Vals).
|
|
|
|
:- pred code_info__generate_temp_livelvals(assoc_list(lval, slot_contents),
|
|
list(liveinfo), list(liveinfo)).
|
|
:- mode code_info__generate_temp_livelvals(in, in, out) is det.
|
|
|
|
code_info__generate_temp_livelvals([], LiveInfo, LiveInfo).
|
|
code_info__generate_temp_livelvals([Slot - StoredLval | Slots], LiveInfo0,
|
|
[live_lvalue(Slot, LiveValueType, "", []) | LiveInfo1]) :-
|
|
code_info__get_live_value_type(StoredLval, LiveValueType),
|
|
code_info__generate_temp_livelvals(Slots, LiveInfo0, LiveInfo1).
|
|
|
|
:- pred code_info__generate_commit_livelvals(int,
|
|
list(liveinfo), list(liveinfo)).
|
|
:- mode code_info__generate_commit_livelvals(in, in, out) is det.
|
|
|
|
code_info__generate_commit_livelvals(Triples0, LiveInfo0, LiveInfo) :-
|
|
( Triples0 = 0 ->
|
|
LiveInfo = LiveInfo0
|
|
;
|
|
Triples1 is Triples0 - 1,
|
|
code_info__generate_commit_livelvals(Triples1,
|
|
LiveInfo0, LiveInfo1),
|
|
CurfrSlot is (Triples0 - 1) * 3 + 1,
|
|
MaxfrSlot is (Triples0 - 1) * 3 + 2,
|
|
RedoipSlot is (Triples0 - 1) * 3 + 3,
|
|
CurfrVar = stackvar(CurfrSlot),
|
|
MaxfrVar = stackvar(MaxfrSlot),
|
|
RedoipVar = stackvar(RedoipSlot),
|
|
code_info__get_live_value_type(lval(curfr), CurfrValueType),
|
|
code_info__get_live_value_type(lval(maxfr), MaxfrValueType),
|
|
code_info__get_live_value_type(lval(redoip(lval(maxfr))),
|
|
RedoipValueType),
|
|
LiveInfo2 = [live_lvalue(CurfrVar, CurfrValueType, "", []) |
|
|
LiveInfo1],
|
|
LiveInfo3 = [live_lvalue(MaxfrVar, MaxfrValueType, "", []) |
|
|
LiveInfo2],
|
|
LiveInfo = [live_lvalue(RedoipVar, RedoipValueType, "", []) |
|
|
LiveInfo3]
|
|
).
|
|
|
|
:- pred code_info__livevals_to_livelvals(assoc_list(lval, var), gc_method,
|
|
instmap, list(liveinfo), code_info, code_info).
|
|
:- mode code_info__livevals_to_livelvals(in, in, in, out, in, out) is det.
|
|
|
|
code_info__livevals_to_livelvals([], _GC_Method, _, []) --> [].
|
|
code_info__livevals_to_livelvals([Lval - Var | Ls], GC_Method, AfterCallInstMap,
|
|
[LiveLval | Lives]) -->
|
|
code_info__get_varset(VarSet),
|
|
{ varset__lookup_name(VarSet, Var, Name) },
|
|
(
|
|
{ GC_Method = accurate }
|
|
->
|
|
{ instmap__lookup_var(AfterCallInstMap, Var, Inst) },
|
|
|
|
code_info__variable_type(Var, Type),
|
|
{ type_util__vars(Type, TypeVars) },
|
|
code_info__find_type_infos(TypeVars, TypeParams),
|
|
{ LiveLval = live_lvalue(Lval, var(Type, Inst), Name,
|
|
TypeParams) }
|
|
;
|
|
{ LiveLval = live_lvalue(Lval, unwanted, Name, []) }
|
|
),
|
|
code_info__livevals_to_livelvals(Ls, GC_Method, AfterCallInstMap,
|
|
Lives).
|
|
|
|
:- pred code_info__get_live_value_type(slot_contents, live_value_type).
|
|
:- mode code_info__get_live_value_type(in, out) is det.
|
|
|
|
code_info__get_live_value_type(lval(succip), succip).
|
|
code_info__get_live_value_type(lval(hp), hp).
|
|
code_info__get_live_value_type(lval(maxfr), maxfr).
|
|
code_info__get_live_value_type(lval(curfr), curfr).
|
|
code_info__get_live_value_type(lval(succfr(_)), unwanted).
|
|
code_info__get_live_value_type(lval(prevfr(_)), unwanted).
|
|
code_info__get_live_value_type(lval(redoip(_)), unwanted).
|
|
code_info__get_live_value_type(lval(succip(_)), unwanted).
|
|
code_info__get_live_value_type(lval(sp), unwanted).
|
|
code_info__get_live_value_type(lval(lvar(_)), unwanted).
|
|
code_info__get_live_value_type(lval(field(_, _, _)), unwanted).
|
|
code_info__get_live_value_type(lval(temp(_, _)), unwanted).
|
|
code_info__get_live_value_type(lval(reg(_, _)), unwanted).
|
|
code_info__get_live_value_type(lval(stackvar(_)), unwanted).
|
|
code_info__get_live_value_type(lval(framevar(_)), unwanted).
|
|
code_info__get_live_value_type(lval(mem_ref(_)), unwanted). % XXX
|
|
code_info__get_live_value_type(ticket, unwanted). % XXX we may need to
|
|
% modify this, if the GC is going
|
|
% to garbage-collect the trail.
|
|
code_info__get_live_value_type(ticket_counter, unwanted).
|
|
code_info__get_live_value_type(trace_data, unwanted).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Submodule for managing stack slots.
|
|
|
|
% The det stack frame is organized as follows.
|
|
%
|
|
% ... unused ...
|
|
% sp ---> <first unused slot>
|
|
% <space for local var 1>
|
|
% ... local vars ...
|
|
% <space for local var n>
|
|
% <space for temporary reg save 1>
|
|
% ... temporary reg saves ...
|
|
% <space for temporary reg save n>
|
|
% <space for succip>
|
|
%
|
|
% The stack pointer points to the first free location at the
|
|
% top of the stack.
|
|
%
|
|
% `code_info__num_stackslots' counts the number of slots reserved
|
|
% for saving local variables. XXX
|
|
%
|
|
% `code_info__max_push_count' counts the number of slots reserved
|
|
% for saving and restoring registers (hp, redoip, etc.)
|
|
%
|
|
% `code_info__succip_used' determines whether we need a slot to
|
|
% hold the succip.
|
|
%
|
|
% The variable part of the nondet stack is organized in the same way
|
|
% as the det stack (but the nondet stack also contains several other
|
|
% fixed fields.)
|
|
|
|
:- interface.
|
|
|
|
% Returns the total stackslot count, but not including space for
|
|
% succip.
|
|
:- pred code_info__get_total_stackslot_count(int, code_info, code_info).
|
|
:- mode code_info__get_total_stackslot_count(out, in, out) is det.
|
|
|
|
:- pred code_info__get_trace_slot(lval, code_info, code_info).
|
|
:- mode code_info__get_trace_slot(out, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- pred code_info__acquire_temp_slot(slot_contents, lval,
|
|
code_info, code_info).
|
|
:- mode code_info__acquire_temp_slot(in, out, in, out) is det.
|
|
|
|
:- pred code_info__release_temp_slot(lval, code_info, code_info).
|
|
:- mode code_info__release_temp_slot(in, in, out) is det.
|
|
|
|
:- pred code_info__get_variable_slot(var, lval, code_info, code_info).
|
|
:- mode code_info__get_variable_slot(in, out, in, out) is det.
|
|
|
|
:- pred code_info__max_var_slot(stack_slots, int).
|
|
:- mode code_info__max_var_slot(in, out) is det.
|
|
|
|
:- pred code_info__stack_variable(int, lval, code_info, code_info).
|
|
:- mode code_info__stack_variable(in, out, in, out) is det.
|
|
|
|
code_info__get_trace_slot(StackVar) -->
|
|
code_info__acquire_temp_slot(trace_data, StackVar).
|
|
|
|
code_info__acquire_temp_slot(Item, StackVar) -->
|
|
code_info__get_avail_temp_slots(AvailSlots0),
|
|
( { set__remove_least(AvailSlots0, StackVarPrime, AvailSlots) } ->
|
|
{ StackVar = StackVarPrime },
|
|
code_info__set_avail_temp_slots(AvailSlots)
|
|
;
|
|
code_info__get_var_slot_count(VarSlots),
|
|
code_info__get_max_temp_slot_count(TempSlots0),
|
|
{ TempSlots is TempSlots0 + 1 },
|
|
{ Slot is VarSlots + TempSlots },
|
|
code_info__stack_variable(Slot, StackVar),
|
|
code_info__set_max_temp_slot_count(TempSlots)
|
|
),
|
|
code_info__get_temps_in_use(TempsInUse0),
|
|
{ map__det_insert(TempsInUse0, StackVar, Item, TempsInUse) },
|
|
code_info__set_temps_in_use(TempsInUse).
|
|
|
|
code_info__release_temp_slot(StackVar) -->
|
|
code_info__get_avail_temp_slots(AvailSlots0),
|
|
{ set__insert(AvailSlots0, StackVar, AvailSlots) },
|
|
code_info__set_avail_temp_slots(AvailSlots),
|
|
code_info__get_temps_in_use(TempsInUse0),
|
|
{ map__delete(TempsInUse0, StackVar, TempsInUse) },
|
|
code_info__set_temps_in_use(TempsInUse).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_info__get_variable_slot(Var, Slot) -->
|
|
code_info__get_stack_slots(StackSlots),
|
|
( { map__search(StackSlots, Var, SlotPrime) } ->
|
|
{ Slot = SlotPrime }
|
|
;
|
|
code_info__variable_to_string(Var, Name),
|
|
{ term__var_to_int(Var, Num) },
|
|
{ string__int_to_string(Num, NumStr) },
|
|
{ string__append_list([
|
|
"code_info__get_variable_slot: variable `",
|
|
Name, "' (", NumStr, ") not found"], Str) },
|
|
{ error(Str) }
|
|
).
|
|
|
|
code_info__max_var_slot(StackSlots, SlotCount) :-
|
|
map__values(StackSlots, StackSlotList),
|
|
code_info__max_var_slot_2(StackSlotList, 0, SlotCount).
|
|
|
|
:- pred code_info__max_var_slot_2(list(lval), int, int).
|
|
:- mode code_info__max_var_slot_2(in, in, out) is det.
|
|
|
|
code_info__max_var_slot_2([], Max, Max).
|
|
code_info__max_var_slot_2([L | Ls], Max0, Max) :-
|
|
( L = stackvar(N) ->
|
|
int__max(N, Max0, Max1)
|
|
; L = framevar(N) ->
|
|
int__max(N, Max0, Max1)
|
|
;
|
|
Max1 = Max0
|
|
),
|
|
code_info__max_var_slot_2(Ls, Max1, Max).
|
|
|
|
code_info__get_total_stackslot_count(NumSlots) -->
|
|
code_info__get_var_slot_count(SlotsForVars),
|
|
code_info__get_max_temp_slot_count(SlotsForTemps),
|
|
{ NumSlots is SlotsForVars + SlotsForTemps }.
|
|
|
|
code_info__stack_variable(Num, Lval) -->
|
|
code_info__get_proc_model(CodeModel),
|
|
( { CodeModel = model_non } ->
|
|
{ Num1 is Num - 1 }, % framevars start at zero
|
|
{ Lval = framevar(Num1) }
|
|
;
|
|
{ Lval = stackvar(Num) } % stackvars start at one
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|