Files
mercury/compiler/code_info.m
Zoltan Somogyi 5013dd9c76 Implement nondet pragma C codes.
Estimated hours taken: 40

Implement nondet pragma C codes.

runtime/mercury_stacks.h:
	Define a new macro, mkpragmaframe, for use in the implementation
	of nondet pragma C codes. This new macro includes space for a
	struct with a given sruct tag in the nondet stack frame being created.

compiler/{prog_data.m,hlds_goal.m}:
	Revise the representation of pragma C codes, both as the item and
	in the HLDS.

compiler/prog_io_pragma.m:
	Parse nondet pragma C declarations.

	Fix the indentation in some places.

compiler/llds.m:
	Include an extra argument in mkframe instructions. This extra argument
	gives the details of the C structure (if any) to be included in the
	nondet stack frame to be created.

	Generalize the LLDS representation of pragma C codes. Instead of a
	fixed sequence of <assign from inputs, user c code, assign to outputs>,
	let the sequence contain these elements, as well as arbitrary
	compiler-generated C code, in any order and possibly with repetitions.
	This flexibility is needed for nondet pragma C codes.

	Add a field to pragma C codes to say whether they can call Mercury.
	Some optimizations can do a better job if they know that a pragma C
	code cannot call Mercury.

	Add another field to pragma C codes to give the name of the label
	they refer to (if any). This is needed to prevent labelopt from
	incorrectly optimizing away the label definition.

	Add a new alternative to the type pragma_c_decl, to describe the
	declaration of the local variable that points to the save struct.

compiler/llds_out.m:
	Output mkframe instructions that specify a struct as invoking the new
	mkpragmaframe macro, and make sure that the struct is declared just
	before the procedure that uses it.

	Other minor changes to keep up with the changes to the representation
	of pragma C code in the LLDS, and to make the output look a bit nicer.

compiler/pragma_c_gen.m:
	Add code to generate code for nondet pragma C codes. Revise the utility
	predicates and their data structures a bit to make this possible.

compiler/code_gen.m:
	Add code for the necessary special handling of prologs and epilogs
	of procedures defined by nondet pragma C codes. The prologs need
	to be modified to include a programmer-defined C structure in the
	nondet stack frame and to communicate the location of this structure
	to the pragma C code, whereas the functionality of the epilog is
	taken care of by the pragma C code itself.

compiler/make_hlds.m:
	When creating a proc_info for a procedure defined by a pragma C code,
	we used to insert unifications between the headvars and the vars of
	the pragma C code into the body goal. We now perform substitutions
	instead. This removes a factor that would complicate the generation
	of code for nondet pragma C codes.

	Pass a moduleinfo down the procedures that warn about singletons
	(and other basic scope errors). When checking whether to warn about
	an argument of a pragma C code not being mentioned in the C code
	fragment, we need to know whether the argument is input or output,
	since input variables should appear in some code fragments in a
	nondet pragma C code and must not appear in others. The
	mode_is_{in,out}put checks need the moduleinfo.

	(We do not need to check for any variables being mentioned where
	they shouldn't be. The C compiler will fail in the presence of any
	errors of that type, and since those variables could be referred
	to via macros whose definitions we do not see, we couldn't implement
	a reliable test anyway.)

compiler/opt_util.m:
	Recognize that some sorts of pragma_c codes cannot affect the data
	structures that control backtracking. This allows peepholing to
	do a better job on code sequences produced for nondet pragma C codes.

	Recognize that the C code strings inside some pragma_c codes refer to
	other labels in the procedure. This prevents labelopt from incorrectly
	optimizing away these labels.

compiler/dupelim.m:
	If a label is referred to from within a C code string, then do not
	attempt to optimize it away.

compiler/det_analysis.m:
	Remove a now incorrect part of an error message.

compiler/*.m:
	Minor changes to conform to changes to the HLDS and LLDS data
	structures.
1998-01-13 10:14:23 +00:00

3116 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_pred, hlds_goal, llds, instmap, trace.
:- import_module globals.
:- import_module bool, set, std_util, assoc_list.
:- implementation.
:- import_module hlds_module, hlds_data, code_util.
:- import_module code_exprn, set, varset, term, stack, prog_data.
:- import_module continuation_info, type_util, mode_util, options.
:- import_module string, require, char, list, map, 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 */, continuation_info, code_info).
:- mode code_info__init(in, 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_continuation_info(continuation_info,
code_info, code_info).
:- mode code_info__get_continuation_info(out, in, out) is det.
:- pred code_info__set_continuation_info(continuation_info,
code_info, code_info).
:- mode code_info__set_continuation_info(in, 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__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).
continuation_info,
% Information on which values
% are live at continuation
% points, for 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, Shapes, C) :-
proc_info_headvars(ProcInfo, HeadVars),
proc_info_arg_info(ProcInfo, ArgInfos),
assoc_list__from_corresponding_lists(HeadVars, ArgInfos, Args),
code_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),
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
).
% XXX This should be in arg_info.m.
:- pred code_info__build_input_arg_list(assoc_list(var, arg_info),
assoc_list(var, rval)).
:- mode code_info__build_input_arg_list(in, out) is det.
code_info__build_input_arg_list([], []).
code_info__build_input_arg_list([V - Arg | Rest0], VarArgs) :-
Arg = arg_info(Loc, Mode),
(
Mode = top_in
->
code_util__arg_loc_to_register(Loc, Reg),
VarArgs = [V - lval(Reg) | VarArgs0]
;
VarArgs = VarArgs0
),
code_info__build_input_arg_list(Rest0, VarArgs0).
%---------------------------------------------------------------------------%
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_continuation_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 continuation_info,
% % Information on which values
% % are live at continuation
% % points, for 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_continuation_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.
%---------------------------------------------------------------------------%
:- 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_continuation_info(ContInfo, C1, _),
code_info__set_continuation_info(ContInfo, 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).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
% 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__top_failure_cont(FailureCont0),
{ FailureCont0 = failure_cont(_, FailureMap0) },
code_info__clone_resume_maps(FailureMap0, FailureMap),
{ FailureCont = failure_cont(nondet(known, no), FailureMap) },
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.
{ map__init(Empty) },
{ FailureCont = failure_cont(nondet(known, no),
stack_only(Empty, 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) },
{ string__append_list(["commit in ", ModuleName, ":", 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)) }.
:- pred code_info__clone_resume_maps(resume_maps, resume_maps,
code_info, code_info).
:- mode code_info__clone_resume_maps(in, out, in, out) is det.
code_info__clone_resume_maps(ResumeMaps0, ResumeMaps) -->
(
{ ResumeMaps0 = orig_only(Map1, _) },
code_info__get_next_label(Label1),
{ Addr1 = label(Label1) },
{ ResumeMaps = orig_only(Map1, Addr1) }
;
{ ResumeMaps0 = stack_only(Map1, _) },
code_info__get_next_label(Label1),
{ Addr1 = label(Label1) },
{ ResumeMaps = stack_only(Map1, Addr1) }
;
{ ResumeMaps0 = stack_and_orig(Map1, _, Map2, _) },
code_info__get_next_label(Label1),
{ Addr1 = label(Label1) },
code_info__get_next_label(Label2),
{ Addr2 = label(Label2) },
{ ResumeMaps = stack_and_orig(Map1, Addr1, Map2, Addr2) }
;
{ ResumeMaps0 = orig_and_stack(Map1, _, Map2, _) },
code_info__get_next_label(Label1),
{ Addr1 = label(Label1) },
code_info__get_next_label(Label2),
{ Addr2 = label(Label2) },
{ ResumeMaps = orig_and_stack(Map1, Addr1, Map2, Addr2) }
).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
% 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__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.
%---------------------------------------------------------------------------%
:- 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.
:- 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.
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) }.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
% 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]) -->
(
{ 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), TypeParams) }
;
{ LiveLval = live_lvalue(Lval, unwanted, []) }
),
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
).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%