Files
mercury/compiler/trace_gen.m
2018-04-07 18:25:43 +10:00

1412 lines
55 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1997-2012 The University of Melbourne.
% Copyright (C) 2015 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: trace_gen.m.
% Author: zs.
%
% This module handles the generation of traces for the trace analysis system.
%
% For the general basis of trace analysis systems, see the paper
% "Opium: An extendable trace analyser for Prolog" by Mireille Ducasse,
% available from http://www.irisa.fr/lande/ducasse.
%
% We reserve some slots in the stack frame of the traced procedure.
% One contains the call sequence number, which is set in the procedure prologue
% by incrementing a global counter. Another contains the call depth, which
% is also set by incrementing a global variable containing the depth of the
% caller. The caller sets this global variable from its own saved depth
% just before the call. We also save the event number, and sometimes also
% the redo layout and the from_full flag.
%
% Each event has a label associated with it. The stack layout for that label
% records what variables are live and where they are at the time of the event.
% These labels are generated by the same predicate that generates the code
% for the event, and are initially not used for anything else.
% However, some of these labels may be fallen into from other places,
% and thus optimization may redirect references from labels to one of these
% labels. This cannot happen in the opposite direction, due to the reference
% to each event's label from the event's foreign_proc_code instruction.
% (This prevents labelopt from removing the label.)
%
% We classify events into three kinds: external events (call, exit, fail),
% internal events (switch, disj, ite_then, ite_else), and nondet foreign_proc
% events (first, later). Code_gen.m, which calls this module to generate
% all external events, checks whether tracing is required before calling us;
% the predicates handing internal and nondet foreign_proc events must check
% this themselves. The predicates generating internal events need the goal
% following the event as a parameter. For the first and later arms of
% nondet foreign_proc, there is no such hlds_goal, which is why these events
% need a bit of special treatment.
%
%-----------------------------------------------------------------------------%
:- module ll_backend.trace_gen.
:- interface.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module libs.
:- import_module libs.globals.
:- import_module ll_backend.code_info.
:- import_module ll_backend.code_loc_dep.
:- import_module ll_backend.continuation_info.
:- import_module ll_backend.llds.
:- import_module mdbcomp.
:- import_module mdbcomp.goal_path.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.set_of_var.
:- import_module assoc_list.
:- import_module map.
:- import_module maybe.
:- import_module set.
:- import_module pair.
%-----------------------------------------------------------------------------%
% The kinds of external ports for which the code we generate will
% call MR_trace. The redo port is not on this list, because for that
% port the code that calls MR_trace is not in compiler-generated code,
% but in the runtime system. Likewise for the exception port.
% (The same comment applies to the type `trace_port' in llds.m.)
%
:- type external_trace_port
---> external_port_call
; external_port_exit
; external_port_fail
; external_port_tailrec_call.
% These ports are different from other internal ports (even neg_enter)
% because their goal path identifies not the goal we are about to enter
% but the goal we have just left.
%
:- type negation_end_port
---> neg_success
; neg_failure.
:- type trace_info.
:- pred get_trace_maybe_tail_rec_info(trace_info::in,
maybe(pair(lval, label))::out) is det.
:- type trace_slot_info
---> trace_slot_info(
% If the procedure is shallow traced, this will be yes(N),
% where stack slot N is the slot that holds the value of the
% from-full flag at call. Otherwise, it will be no.
slot_from_full :: maybe(int),
% If the procedure has io state arguments this will be yes(N),
% where stack slot N is the slot that holds the saved value
% of the io sequence number. Otherwise, it will be no.
slot_io :: maybe(int),
% If --use-trail is set, this will be yes(M), where stack slots
% M and M+1 are the slots that hold the saved values of the
% trail pointer and the ticket counter respectively at the time
% of the call. Otherwise, it will be no.
slot_trail :: maybe(int),
% If the procedure lives on the det stack but creates
% temporary frames on the nondet stack, this will be yes(M),
% where stack slot M is reserved to hold the value of maxfr
% at the time of the call. Otherwise, it will be no.
slot_maxfr :: maybe(int),
% If the procedure's evaluation method is memo, loopcheck
% or minimal model, this will be yes(M), where stack slot
% M holds the variable that represents the tip of the
% call table. Otherwise, it will be no.
slot_call_table :: maybe(int),
% If the procedure has tail recursive call events, this
% will be yes(M), where stack slot M holds the variable
% that represents number of times a tail recursive call
% has reused this stack frame. Otherwise, it will be no.
slot_tail_rec :: maybe(int)
).
% Return the set of input variables whose values should be preserved
% until the exit and fail ports. This will be all the input variables,
% except those that can be totally clobbered during the evaluation
% of the procedure (those partially clobbered may still be of interest,
% although to handle them properly we need to record insts in stack
% layouts), and those of dummy types.
%
:- pred trace_fail_vars(module_info::in, proc_info::in,
set_of_progvar::out) is det.
% Figure out whether we need a slot for storing the value of maxfr
% on entry, and record the result in the proc info.
%
:- pred do_we_need_maxfr_slot(globals::in, module_info::in, pred_info::in,
proc_info::in, proc_info::out) is det.
% Return the number of slots reserved for tracing information.
% If there are N slots, the reserved slots will be 1 through N.
%
% It is possible that one of these reserved slots contains a variable.
% If so, the variable and its slot number are returned in the last
% argument.
%
:- pred trace_reserved_slots(module_info::in, pred_info::in, proc_info::in,
globals::in, int::out, maybe(pair(prog_var, int))::out) is det.
% Construct and return an abstract struct that represents the
% tracing-specific part of the code generator state. Return also
% info about the non-fixed slots used by the tracing system,
% for eventual use in the constructing the procedure's layout structure.
%
:- pred trace_setup(module_info::in, pred_info::in, proc_info::in,
globals::in, maybe(label)::in, trace_slot_info::out, trace_info::out,
code_info::in, code_info::out) is det.
% Generate code to fill in the reserved stack slots.
%
:- pred generate_slot_fill_code(code_info::in, trace_info::in,
llds_code::out) is det.
% If we are doing execution tracing, generate code to prepare for a call.
%
:- pred trace_prepare_for_call(code_info::in, llds_code::out) is det.
% If we are doing execution tracing, generate code for an internal
% trace event. This predicate must be called just before generating code
% for the given goal.
%
:- pred maybe_generate_internal_event_code(hlds_goal::in,
hlds_goal_info::in, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
% If we are doing execution tracing, generate code for an trace event
% that represents leaving a negated goal (via success or failure).
%
:- pred maybe_generate_negated_event_code(hlds_goal::in,
hlds_goal_info::in, negation_end_port::in, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
% Generate code for a user-defined trace event.
%
:- pred generate_user_event_code(user_event_info::in, hlds_goal_info::in,
llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
:- pred generate_tailrec_event_code(trace_info::in,
assoc_list(prog_var, arg_info)::in, goal_id::in, prog_context::in,
llds_code::out, label::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
:- type external_event_info
---> external_event_info(
% The label associated with the external event.
label,
% The map giving the locations of the typeinfo variables
% needed to describe the types of the variables live at the
% event.
map(tvar, set(layout_locn)),
% The code generated for the event.
llds_code
).
% Generate code for an external trace event.
% Besides the trace code, we return the label on which we have hung
% the trace liveness information and data on the type variables in the
% liveness information, since some of our callers also need this
% information.
%
:- pred generate_external_event_code(external_trace_port::in,
trace_info::in, prog_context::in, maybe(external_event_info)::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
% If the trace level calls for redo events, generate code that pushes
% a temporary nondet stack frame whose redoip slot contains the
% address of one of the labels in the runtime that calls MR_trace
% for a redo event. Otherwise, generate empty code.
%
:- pred maybe_setup_redo_event(trace_info::in, llds_code::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module check_hlds.
:- import_module check_hlds.inst_test.
:- import_module check_hlds.mode_util.
:- import_module check_hlds.type_util.
:- import_module hlds.code_model.
:- import_module hlds.hlds_llds.
:- import_module hlds.instmap.
:- import_module hlds.vartypes.
:- import_module libs.options.
:- import_module libs.trace_params.
:- import_module ll_backend.code_util.
:- import_module ll_backend.layout_out.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.prog_data_foreign.
:- import_module parse_tree.prog_type.
:- import_module bool.
:- import_module cord.
:- import_module int.
:- import_module list.
:- import_module require.
:- import_module string.
:- import_module term.
:- import_module varset.
%-----------------------------------------------------------------------------%
% Information specific to a trace port.
%
:- type trace_port_info
---> port_info_external
; port_info_tailrec_call(
% The id of the tail recursive call.
forward_goal_path,
% The list of arguments of this call.
assoc_list(prog_var, arg_info)
)
; port_info_internal(
% The id of the goal whose start this port represents.
forward_goal_path,
% The pre-death set of this goal.
set_of_progvar
)
; port_info_negation_end(
% The id of the goal whose end (one way or another)
% this port represents.
forward_goal_path
)
; port_info_user(
% The id of the goal.
forward_goal_path
).
trace_fail_vars(ModuleInfo, ProcInfo, FailVars) :-
proc_info_get_headvars(ProcInfo, HeadVars),
proc_info_get_argmodes(ProcInfo, Modes),
proc_info_arg_info(ProcInfo, ArgInfos),
proc_info_get_vartypes(ProcInfo, VarTypes),
mode_list_get_final_insts(ModuleInfo, Modes, Insts),
( if
build_fail_vars(HeadVars, Insts, ArgInfos,
ModuleInfo, VarTypes, FailVarsList)
then
set_of_var.list_to_set(FailVarsList, FailVars)
else
unexpected($pred, "length mismatch")
).
do_we_need_maxfr_slot(Globals, ModuleInfo, PredInfo0, !ProcInfo) :-
globals.get_trace_level(Globals, TraceLevel),
CodeModel = proc_info_interface_code_model(!.ProcInfo),
( if
eff_trace_level_is_none(ModuleInfo, PredInfo0, !.ProcInfo, TraceLevel)
= no,
CodeModel \= model_non,
proc_info_get_goal(!.ProcInfo, Goal),
code_util.goal_may_alloc_temp_frame(Goal, yes)
then
NeedsMaxfrSlot = needs_maxfr_slot
else
NeedsMaxfrSlot = does_not_need_maxfr_slot
),
proc_info_set_needs_maxfr_slot(NeedsMaxfrSlot, !ProcInfo).
% trace_reserved_slots and trace_setup cooperate in the allocation of
% stack slots for tracing purposes. The allocation is done in the
% following stages.
%
% stage 1: Allocate the fixed slots, slots 1, 2 and 3, to hold
% the event number of call, the call sequence number
% and the call depth respectively.
%
% stage 2: If the procedure is model_non and --trace-redo is set,
% allocate the next available slot (which must be slot 4)
% to hold the address of the redo layout structure.
%
% stage 3: If the procedure is shallow traced, allocate the
% next available slot to the saved copy of the
% from-full flag. The number of this slot is recorded
% in the maybe_from_full field in the proc layout;
% if there is no such slot, that field will contain -1.
%
% stage 4: If --trace-table-io is given, allocate the next slot
% to hold the saved value of the io sequence number,
% for use in implementing retry. The number of this slot
% is recorded in the maybe_io_seq field in the proc
% layout; if there is no such slot, that field will
% contain -1.
%
% stage 5: If --use-trail is set (given or implied), allocate
% two slots to hold the saved value of the trail pointer
% and the ticket counter at the point of the call, for
% use in implementing retry. The number of the first of
% these two slots is recorded in the maybe_trail field
% in the proc layout; if there are no such slots, that
% field will contain -1.
%
% stage 6: If the procedure lives on the det stack but can put
% frames on the nondet stack, allocate a slot to hold
% the saved value of maxfr at the point of the call,
% for use in implementing retry. The number of this
% slot is recorded in the maybe_maxfr field in the proc
% layout; if there is no such slot, that field will
% contain -1.
%
% stage 7: If the procedure has tail call events, we allocate a slot
% to hold a variable that counts how many tail events we have
% executed in this stack frame so far. The number of this slot
% is recorded in the maybe_tail_rec field in the proc layout;
% if there is no such slot, that field will contain -1.
%
% stage 8: If the procedure's evaluation method is memo, loopcheck
% or minimal model, we allocate a slot to hold the
% variable that represents the tip of the call table.
% The debugger needs this, because when it executes a
% retry command, it must reset this tip to uninitialized.
% The number of this slot is recorded in the maybe_table
% field in the proc layout; if there is no such slot,
% that field will contain -1.
% (This should be the last stage; add new ones before this one.)
%
% If you add any more stages, please consider that any new slots
% may need to be reset or adjusted before TAIL events.
%
% The procedure's layout structure does not need to include
% information about the presence or absence of the slot holding
% the address of the redo layout structure. If we generate redo
% trace events, the runtime will know that this slot exists and
% what its number must be; if we do not, the runtime will never
% refer to such a slot.
%
% We need two redo labels in the runtime. Deep traced procedures
% do not have a from-full slot, but their slots 1 through 4 are always
% valid; the label handling their redos accesses those slots directly.
% Shallow traced procedures do have a from-full slot, and their slots
% 1-4 are valid only if the from-full slot is MR_TRUE; the label
% handling their redos thus checks this slot to see whether it can
% (or should) access the other slots. In shallow-traced model_non
% procedures that generate redo events, the from-full flag is always
% in slot 5.
%
% The slots allocated by stages 1 and 2 are only ever referred to
% by the runtime system if they are guaranteed to exist. The runtime
% system may of course also need to refer to slots allocated by later
% stages, but before it does so, it needs to know whether those slots
% exist or not. This is why setup returns TraceSlotInfo, which answers
% such questions, for later inclusion in the procedure's layout structure.
trace_reserved_slots(ModuleInfo, PredInfo, ProcInfo, Globals, ReservedSlots,
MaybeTableVarInfo) :-
globals.get_trace_level(Globals, TraceLevel),
globals.get_trace_suppress(Globals, TraceSuppress),
globals.lookup_bool_option(Globals, trace_table_io, TraceTableIo),
FixedSlots = eff_trace_level_needs_fixed_slots(ModuleInfo, PredInfo,
ProcInfo, TraceLevel),
(
FixedSlots = no,
ReservedSlots = 0,
MaybeTableVarInfo = no
;
FixedSlots = yes,
% Stage 1.
Fixed = 3, % event#, call#, call depth
% Stage 2.
( if
proc_info_interface_code_model(ProcInfo) = model_non,
eff_trace_needs_port(ModuleInfo, PredInfo, ProcInfo, TraceLevel,
TraceSuppress, port_redo) = yes
then
RedoLayout = 1
else
RedoLayout = 0
),
% Stage 3.
( if
eff_trace_level_needs_from_full_slot(ModuleInfo, PredInfo,
ProcInfo, TraceLevel) = yes
then
FromFull = 1
else
FromFull = 0
),
% Stage 4.
(
TraceTableIo = yes,
IoSeq = 1
;
TraceTableIo = no,
IoSeq = 0
),
% Stage 5.
globals.lookup_bool_option(Globals, use_trail, UseTrail),
(
UseTrail = yes,
Trail = 2
;
UseTrail = no,
Trail = 0
),
% Stage 6.
proc_info_get_needs_maxfr_slot(ProcInfo, NeedsMaxfrSlot),
(
NeedsMaxfrSlot = needs_maxfr_slot,
Maxfr = 1
;
NeedsMaxfrSlot = does_not_need_maxfr_slot,
Maxfr = 0
),
% Stage 7.
proc_info_get_has_tail_rec_call(ProcInfo, HasTailRecCall),
HasTailRecCall = has_tail_rec_call(HasSelfTailRecCall,
_HasMutualTailRecCall),
(
HasSelfTailRecCall = has_self_tail_rec_call,
TailRec = 1
;
HasSelfTailRecCall = has_no_self_tail_rec_call,
TailRec = 0
),
ReservedSlots0 = Fixed + RedoLayout + FromFull + IoSeq + Trail +
Maxfr + TailRec,
% Stage 8.
proc_info_get_call_table_tip(ProcInfo, MaybeCallTableVar),
(
MaybeCallTableVar = yes(CallTableVar),
ReservedSlots = ReservedSlots0 + 1,
MaybeTableVarInfo = yes(CallTableVar - ReservedSlots)
;
MaybeCallTableVar = no,
ReservedSlots = ReservedSlots0,
MaybeTableVarInfo = no
)
).
trace_setup(ModuleInfo, PredInfo, ProcInfo, Globals, MaybeTailRecLabel,
TraceSlotInfo, TraceInfo, !CI) :-
CodeModel = get_proc_model(!.CI),
globals.get_trace_level(Globals, TraceLevel),
globals.get_trace_suppress(Globals, TraceSuppress),
globals.lookup_bool_option(Globals, trace_table_io, TraceTableIo),
TraceRedo = eff_trace_needs_port(ModuleInfo, PredInfo, ProcInfo,
TraceLevel, TraceSuppress, port_redo),
some [!NextSlot] (
% Stages 1 and 2.
( if
TraceRedo = yes,
CodeModel = model_non
then
get_next_label(RedoLayoutLabel, !CI),
MaybeRedoLayoutLabel = yes(RedoLayoutLabel),
% We always reserve slots 1, 2 and 3, and we reserve slot 4
% for the redo layout label.
!:NextSlot = 5
else
MaybeRedoLayoutLabel = no,
% We always reserve slots 1, 2 and 3.
!:NextSlot = 4
),
% Stage 3.
HasFromFullSlot = eff_trace_level_needs_from_full_slot(ModuleInfo,
PredInfo, ProcInfo, TraceLevel),
StackId = code_model_to_main_stack(CodeModel),
(
HasFromFullSlot = yes,
FromFullSlot = !.NextSlot,
MaybeFromFullSlot = yes(FromFullSlot),
FromFullSlotLval = stack_slot_num_to_lval(StackId, FromFullSlot),
MaybeFromFullSlotLval = yes(FromFullSlotLval),
!:NextSlot = !.NextSlot + 1
;
HasFromFullSlot = no,
MaybeFromFullSlot = no,
MaybeFromFullSlotLval = no
),
% Stage 4.
(
TraceTableIo = yes,
IoSeqSlot = !.NextSlot,
MaybeIoSeqSlot = yes(IoSeqSlot),
IoSeqLval = stack_slot_num_to_lval(StackId, IoSeqSlot),
MaybeIoSeqLval = yes(IoSeqLval),
!:NextSlot = !.NextSlot + 1
;
TraceTableIo = no,
MaybeIoSeqSlot = no,
MaybeIoSeqLval = no
),
% Stage 5.
globals.lookup_bool_option(Globals, use_trail, UseTrail),
(
UseTrail = yes,
TrailSlot = !.NextSlot,
TicketSlot = !.NextSlot + 1,
MaybeTrailSlot = yes(TrailSlot),
TrailLval = stack_slot_num_to_lval(StackId, TrailSlot),
TicketLval = stack_slot_num_to_lval(StackId, TicketSlot),
MaybeTrailLvals = yes(TrailLval - TicketLval),
!:NextSlot = !.NextSlot + 2
;
UseTrail = no,
MaybeTrailSlot = no,
MaybeTrailLvals = no
),
% Stage 6.
proc_info_get_needs_maxfr_slot(ProcInfo, NeedsMaxfrSlot),
(
NeedsMaxfrSlot = needs_maxfr_slot,
MaxfrSlot = !.NextSlot,
MaybeMaxfrSlot = yes(MaxfrSlot),
MaxfrLval = stack_slot_num_to_lval(StackId, MaxfrSlot),
MaybeMaxfrLval = yes(MaxfrLval),
!:NextSlot = !.NextSlot + 1
;
NeedsMaxfrSlot = does_not_need_maxfr_slot,
MaybeMaxfrSlot = no,
MaybeMaxfrLval = no
),
% Stage 7.
(
MaybeTailRecLabel = yes(TailRecLabel),
TailRecSlot = !.NextSlot,
MaybeTailRecSlot = yes(TailRecSlot),
TailRecLval = stack_slot_num_to_lval(StackId, TailRecSlot),
MaybeTailRecInfo = yes(TailRecLval - TailRecLabel),
!:NextSlot = !.NextSlot + 1
;
MaybeTailRecLabel = no,
MaybeTailRecSlot = no,
MaybeTailRecInfo = no
),
% Stage 8.
proc_info_get_call_table_tip(ProcInfo, MaybeCallTableTip),
(
MaybeCallTableTip = yes(_),
CallTableSlot = !.NextSlot,
MaybeCallTableSlot = yes(CallTableSlot),
CallTableLval = stack_slot_num_to_lval(StackId, CallTableSlot),
MaybeCallTableLval = yes(CallTableLval)
;
MaybeCallTableTip = no,
MaybeCallTableSlot = no,
MaybeCallTableLval = no
)
),
TraceSlotInfo = trace_slot_info(MaybeFromFullSlot, MaybeIoSeqSlot,
MaybeTrailSlot, MaybeMaxfrSlot, MaybeCallTableSlot, MaybeTailRecSlot),
TraceInfo = trace_info(TraceLevel, TraceSuppress,
MaybeFromFullSlotLval, MaybeIoSeqLval, MaybeTrailLvals,
MaybeMaxfrLval, MaybeCallTableLval, MaybeTailRecInfo,
MaybeRedoLayoutLabel).
generate_slot_fill_code(CI, TraceInfo, TraceCode) :-
CodeModel = get_proc_model(CI),
MaybeFromFullSlot = TraceInfo ^ ti_from_full_lval,
MaybeIoSeqSlot = TraceInfo ^ ti_io_seq_lval,
MaybeTrailLvals = TraceInfo ^ ti_trail_lvals,
MaybeMaxfrLval = TraceInfo ^ ti_maxfr_lval,
MaybeCallTableLval = TraceInfo ^ ti_call_table_tip_lval,
MaybeTailRecInfo = TraceInfo ^ ti_tail_rec_info,
MaybeRedoLabel = TraceInfo ^ ti_redo_label,
event_num_slot(CodeModel, EventNumLval),
call_num_slot(CodeModel, CallNumLval),
call_depth_slot(CodeModel, CallDepthLval),
stackref_to_string(EventNumLval, EventNumStr),
stackref_to_string(CallNumLval, CallNumStr),
stackref_to_string(CallDepthLval, CallDepthStr),
some [!CodeStr] (
% Stage 1.
!:CodeStr = "\t\tMR_trace_fill_std_slots(" ++ EventNumStr ++ ", " ++
CallNumStr ++ ", " ++ CallDepthStr ++ ");\n",
% Stage 2.
(
MaybeRedoLabel = yes(RedoLayoutLabel),
redo_layout_slot(CodeModel, RedoLayoutLval),
stackref_to_string(RedoLayoutLval, RedoLayoutStr),
!:CodeStr = !.CodeStr ++ "\t\t" ++ RedoLayoutStr ++
" = (MR_Word) (const MR_Word *) MR_HASH_DEF_LABEL_LAYOUT;\n",
MaybeLayoutLabel = yes(RedoLayoutLabel),
MaybeHashDefLabel = yes(RedoLayoutLabel)
;
MaybeRedoLabel = no,
MaybeLayoutLabel = no,
MaybeHashDefLabel = no
),
% Stage 3 is handled later.
% Stage 4.
(
MaybeIoSeqSlot = yes(IoSeqLval),
stackref_to_string(IoSeqLval, IoSeqStr),
!:CodeStr = !.CodeStr ++
"\t\t" ++ IoSeqStr ++ " = MR_io_tabling_counter;\n"
;
MaybeIoSeqSlot = no
),
% Stage 5.
(
% This could be done by generating proper LLDS instead of C.
% However, in shallow traced code we want to execute this
% only when the caller is deep traced, and everything inside
% that test must be in C code.
MaybeTrailLvals = yes(TrailLval - TicketLval),
stackref_to_string(TrailLval, TrailLvalStr),
stackref_to_string(TicketLval, TicketLvalStr),
!:CodeStr = !.CodeStr ++
"\t\tMR_mark_ticket_stack(" ++ TicketLvalStr ++ ");\n" ++
"\t\tMR_store_ticket(" ++ TrailLvalStr ++ ");\n"
;
MaybeTrailLvals = no
),
% Stage 3 (delayed).
(
MaybeFromFullSlot = yes(CallFromFullSlot),
stackref_to_string(CallFromFullSlot, CallFromFullSlotStr),
!:CodeStr =
"\t\t" ++ CallFromFullSlotStr ++ " = MR_trace_from_full;\n" ++
"\t\tif (MR_trace_from_full) {\n" ++
!.CodeStr ++
"\t\t} else {\n" ++
"\t\t\t" ++ CallDepthStr ++ " = MR_trace_call_depth;\n" ++
"\t\t}\n"
;
MaybeFromFullSlot = no
),
TraceStmt1 = !.CodeStr
),
TraceComponents1 = [foreign_proc_raw_code(cannot_branch_away,
proc_does_not_affect_liveness, live_lvals_info(set.init), TraceStmt1)],
TraceCode1 = singleton(
llds_instr(foreign_proc_code([], TraceComponents1,
proc_will_not_call_mercury, no, no, MaybeLayoutLabel,
no, MaybeHashDefLabel, yes, proc_may_not_duplicate), "")
),
% Stage 6.
(
MaybeMaxfrLval = yes(MaxfrLval),
TraceCode2 = singleton(
llds_instr(assign(MaxfrLval, lval(maxfr)), "save initial maxfr")
)
;
MaybeMaxfrLval = no,
TraceCode2 = empty
),
% Stage 7.
(
MaybeTailRecInfo = yes(TailRecLval - _TailRecLabel),
stackref_to_string(TailRecLval, TailRecLvalStr),
TraceStmt3 =
"\t\tif (MR_trace_tailrec_have_reused_frames) {\n" ++
"\t\t\t" ++ TailRecLvalStr ++
" = MR_trace_tailrec_num_reused_frames;\n" ++
"\t\t\tMR_trace_tailrec_have_reused_frames = MR_FALSE;\n" ++
"\t\t} else {" ++
"\t\t\t" ++ TailRecLvalStr ++ " = 0;\n" ++
"\t\t}",
TraceComponents3 = [foreign_proc_raw_code(cannot_branch_away,
proc_does_not_affect_liveness, live_lvals_info(set.init),
TraceStmt3)],
TraceCode3 = singleton(
llds_instr(foreign_proc_code([], TraceComponents3,
proc_will_not_call_mercury, no, no, no, no, no,
yes, proc_may_not_duplicate),
"initialize tail recursion count")
)
;
MaybeTailRecInfo = no,
TraceCode3 = empty
),
% Stage 8.
(
MaybeCallTableLval = yes(CallTableLval),
stackref_to_string(CallTableLval, CallTableLvalStr),
TraceStmt4 = "\t\t" ++ CallTableLvalStr ++ " = 0;\n",
TraceComponents4 = [foreign_proc_raw_code(cannot_branch_away,
proc_does_not_affect_liveness, live_lvals_info(set.init),
TraceStmt4)],
TraceCode4 = singleton(
llds_instr(foreign_proc_code([], TraceComponents4,
proc_will_not_call_mercury, no, no, no, no, no,
yes, proc_may_not_duplicate), "")
)
;
MaybeCallTableLval = no,
TraceCode4 = empty
),
TraceCode = TraceCode1 ++ TraceCode2 ++ TraceCode3 ++ TraceCode4.
trace_prepare_for_call(CI, TraceCode) :-
get_maybe_trace_info(CI, MaybeTraceInfo),
CodeModel = get_proc_model(CI),
(
MaybeTraceInfo = yes(TraceInfo),
MaybeFromFullSlot = TraceInfo ^ ti_from_full_lval,
call_depth_slot(CodeModel, CallDepthLval),
stackref_to_string(CallDepthLval, CallDepthStr),
(
MaybeFromFullSlot = yes(_),
MacroStr = "MR_trace_reset_depth_from_shallow"
;
MaybeFromFullSlot = no,
MacroStr = "MR_trace_reset_depth_from_full"
),
ResetStmt = MacroStr ++ "(" ++ CallDepthStr ++ ");\n",
TraceCode = singleton(
llds_instr(arbitrary_c_code(proc_does_not_affect_liveness,
live_lvals_info(set.init), ResetStmt), "")
)
;
MaybeTraceInfo = no,
TraceCode = empty
).
maybe_generate_internal_event_code(Goal, OutsideGoalInfo, Code, !CI, !CLD) :-
get_maybe_trace_info(!.CI, MaybeTraceInfo),
(
MaybeTraceInfo = yes(TraceInfo),
Goal = hlds_goal(_, GoalInfo),
GoalId = goal_info_get_goal_id(GoalInfo),
get_containing_goal_map(!.CI, ContainingGoalMap),
map.lookup(ContainingGoalMap, GoalId, ContainingGoal),
( if
ContainingGoal = containing_goal(_, LastStep),
(
LastStep = step_switch(_, _),
PortPrime = port_switch
;
LastStep = step_disj(DisjunctNum),
( if DisjunctNum = 1 then
PortPrime = port_disj_first
else
PortPrime = port_disj_later
)
;
LastStep = step_ite_cond,
PortPrime = port_ite_cond
;
LastStep = step_ite_then,
PortPrime = port_ite_then
;
LastStep = step_ite_else,
PortPrime = port_ite_else
;
LastStep = step_neg,
PortPrime = port_neg_enter
)
then
Port = PortPrime
else
unexpected($pred, "bad path")
),
( if
get_module_info(!.CI, ModuleInfo),
get_pred_info(!.CI, PredInfo),
get_proc_info(!.CI, ProcInfo),
eff_trace_needs_port(ModuleInfo, PredInfo, ProcInfo,
TraceInfo ^ ti_trace_level,
TraceInfo ^ ti_trace_suppress_items, Port) = yes
then
goal_info_get_pre_deaths(GoalInfo, PreDeaths),
Context = goal_info_get_context(GoalInfo),
( if
goal_info_has_feature(OutsideGoalInfo,
feature_hide_debug_event)
then
HideEvent = yes
else
HideEvent = no
),
GoalPath = goal_id_to_forward_path(ContainingGoalMap, GoalId),
generate_event_code(Port, port_info_internal(GoalPath, PreDeaths),
yes(TraceInfo), Context, HideEvent, no, _, _, Code, !CI, !CLD)
else
Code = empty
)
;
MaybeTraceInfo = no,
Code = empty
).
maybe_generate_negated_event_code(Goal, OutsideGoalInfo, NegPort, Code,
!CI, !CLD) :-
get_maybe_trace_info(!.CI, MaybeTraceInfo),
( if
MaybeTraceInfo = yes(TraceInfo),
(
NegPort = neg_failure,
Port = port_neg_failure
;
NegPort = neg_success,
Port = port_neg_success
),
get_module_info(!.CI, ModuleInfo),
get_pred_info(!.CI, PredInfo),
get_proc_info(!.CI, ProcInfo),
eff_trace_needs_port(ModuleInfo, PredInfo, ProcInfo,
TraceInfo ^ ti_trace_level,
TraceInfo ^ ti_trace_suppress_items, Port) = yes
then
Goal = hlds_goal(_, GoalInfo),
GoalId = goal_info_get_goal_id(GoalInfo),
Context = goal_info_get_context(GoalInfo),
( if
goal_info_has_feature(OutsideGoalInfo, feature_hide_debug_event)
then
HideEvent = yes
else
HideEvent = no
),
get_containing_goal_map(!.CI, ContainingGoalMap),
GoalPath = goal_id_to_forward_path(ContainingGoalMap, GoalId),
generate_event_code(Port, port_info_negation_end(GoalPath),
yes(TraceInfo), Context, HideEvent, no, _, _, Code, !CI, !CLD)
else
Code = empty
).
generate_user_event_code(UserInfo, GoalInfo, Code, !CI, !CLD) :-
GoalId = goal_info_get_goal_id(GoalInfo),
get_containing_goal_map(!.CI, ContainingGoalMap),
GoalPath = goal_id_to_forward_path(ContainingGoalMap, GoalId),
Context = goal_info_get_context(GoalInfo),
Port = port_user,
PortInfo = port_info_user(GoalPath),
MaybeTraceInfo = no,
HideEvent = no,
generate_event_code(Port, PortInfo, MaybeTraceInfo, Context, HideEvent,
yes(UserInfo), _Label, _TvarDataMap, Code, !CI, !CLD).
generate_external_event_code(ExternalPort, TraceInfo, Context,
MaybeExternalInfo, !CI, !CLD) :-
Port = convert_external_port_type(ExternalPort),
get_module_info(!.CI, ModuleInfo),
get_pred_info(!.CI, PredInfo),
get_proc_info(!.CI, ProcInfo),
NeedPort = eff_trace_needs_port(ModuleInfo, PredInfo, ProcInfo,
TraceInfo ^ ti_trace_level, TraceInfo ^ ti_trace_suppress_items, Port),
(
NeedPort = yes,
generate_event_code(Port, port_info_external, yes(TraceInfo), Context,
no, no, Label, TvarDataMap, Code, !CI, !CLD),
MaybeExternalInfo = yes(external_event_info(Label, TvarDataMap, Code))
;
NeedPort = no,
MaybeExternalInfo = no
).
generate_tailrec_event_code(TraceInfo, ArgsInfos, GoalId, Context, Code,
TailRecLabel, !CI, !CLD) :-
Port = port_tailrec_call,
get_containing_goal_map(!.CI, ContainingGoalMap),
GoalPath = goal_id_to_forward_path(ContainingGoalMap, GoalId),
PortInfo = port_info_tailrec_call(GoalPath, ArgsInfos),
HideEvent = no,
MaybeUserInfo = no,
generate_event_code(Port, PortInfo, yes(TraceInfo), Context, HideEvent,
MaybeUserInfo, _Label, _TvarDataMap, Code, !CI, !CLD),
MaybeTailRecInfo = TraceInfo ^ ti_tail_rec_info,
(
MaybeTailRecInfo = yes(_ - TailRecLabel)
;
MaybeTailRecInfo = no,
unexpected($pred, "no tail rec label")
).
:- pred generate_tailrec_reset_slots_code(trace_info::in,
llds_code::out, code_info::in, code_info::out) is det.
generate_tailrec_reset_slots_code(TraceInfo, Code, !CI) :-
% We reset all the debugging slots that need to be reset. We handle them
% in the order of allocation.
% Stage 1.
CodeModel = get_proc_model(!.CI),
event_num_slot(CodeModel, EventNumLval),
call_num_slot(CodeModel, CallNumLval),
call_depth_slot(CodeModel, CallDepthLval),
stackref_to_string(EventNumLval, EventNumStr),
stackref_to_string(CallNumLval, CallNumStr),
stackref_to_string(CallDepthLval, CallDepthStr),
StdSlotCodeStr = "\t\tMR_trace_tailrec_std_slots(" ++
EventNumStr ++ ", " ++ CallNumStr ++ ", " ++ CallDepthStr ++ ");\n",
% Stage 2.
% Tail recursion events cannot happen in model_non procedures, so stage 2
% will not allocate any slots.
MaybeRedoLabelLval = TraceInfo ^ ti_redo_label,
expect(unify(MaybeRedoLabelLval, no), $pred,
"redo label in procedure with TAIL event"),
% Stage 3.
% Tail recursion events are disabled if the trace level is shallow tracing,
% so stage 3 will not allocate any slots.
MaybeFromFullLval = TraceInfo ^ ti_from_full_lval,
expect(unify(MaybeFromFullLval, no), $pred,
"from_full slot in procedure with TAIL event"),
% Stage 4.
MaybeIoSeqSlot = TraceInfo ^ ti_io_seq_lval,
(
MaybeIoSeqSlot = yes(IoSeqLval),
stackref_to_string(IoSeqLval, IoSeqStr),
IoSeqCodeStr = "\t\t" ++ IoSeqStr ++ " = MR_io_tabling_counter;\n"
;
MaybeIoSeqSlot = no,
IoSeqCodeStr = ""
),
% Stage 5.
MaybeTrailLvals = TraceInfo ^ ti_trail_lvals,
(
MaybeTrailLvals = yes(TrailLval - TicketLval),
stackref_to_string(TrailLval, TrailLvalStr),
stackref_to_string(TicketLval, TicketLvalStr),
TrailCodeStr =
"\t\tMR_mark_ticket_stack(" ++ TicketLvalStr ++ ");\n" ++
"\t\tMR_store_ticket(" ++ TrailLvalStr ++ ");\n"
;
MaybeTrailLvals = no,
TrailCodeStr = ""
),
% Stage 6.
MaybeMaxfrLval = TraceInfo ^ ti_maxfr_lval,
(
MaybeMaxfrLval = yes(MaxfrLval),
MaxfrCode = singleton(
llds_instr(assign(MaxfrLval, lval(maxfr)), "save initial maxfr")
)
;
MaybeMaxfrLval = no,
MaxfrCode = empty
),
% Stage 7.
TailRecInfo = TraceInfo ^ ti_tail_rec_info,
(
TailRecInfo = yes(TailRecLval - _),
TailRecLvalCode = singleton(
llds_instr(assign(TailRecLval,
binop(int_add(int_type_int), lval(TailRecLval),
const(llconst_int(1)))),
"increment tail recursion counter")
)
;
TailRecInfo = no,
unexpected($pred, "no tail rec lval")
),
% Stage 8.
MaybeCallTableLval = TraceInfo ^ ti_call_table_tip_lval,
(
MaybeCallTableLval = yes(CallTableLval),
stackref_to_string(CallTableLval, CallTableLvalStr),
CallTableCodeStr = "\t\t" ++ CallTableLvalStr ++ " = 0;\n"
;
MaybeCallTableLval = no,
CallTableCodeStr = ""
),
ForeignLangCodeStr = StdSlotCodeStr ++ IoSeqCodeStr ++ TrailCodeStr ++
CallTableCodeStr,
ForeignLangComponents = [foreign_proc_raw_code(cannot_branch_away,
proc_does_not_affect_liveness, live_lvals_info(set.init),
ForeignLangCodeStr)],
ForeignLangCode = singleton(
llds_instr(foreign_proc_code([], ForeignLangComponents,
proc_will_not_call_mercury, no, no, no, no, no,
yes, proc_may_duplicate), "")
),
Code = ForeignLangCode ++ MaxfrCode ++ TailRecLvalCode.
:- pred generate_event_code(trace_port::in, trace_port_info::in,
maybe(trace_info)::in, prog_context::in, bool::in,
maybe(user_event_info)::in, label::out,
map(tvar, set(layout_locn))::out, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
generate_event_code(Port, PortInfo, MaybeTraceInfo, Context, HideEvent,
MaybeUserInfo, Label, TvarDataMap, Code, !CI, !CLD) :-
get_next_label(Label, !CI),
get_known_variables(!.CLD, LiveVars0),
(
PortInfo = port_info_external,
LiveVars = LiveVars0,
Path = fgp_nil,
TailRecResetCode = empty
;
PortInfo = port_info_tailrec_call(Path, ArgsInfos),
% The pre_goal_update has added the output variables of the recursive
% call to the set of live variables; we must undo this.
find_output_vars(ArgsInfos, [], OutputVars),
list.delete_elems(LiveVars0, OutputVars, LiveVars),
(
MaybeTraceInfo = yes(TailRecTraceInfo),
generate_tailrec_reset_slots_code(TailRecTraceInfo,
TailRecResetCode, !CI)
;
MaybeTraceInfo = no,
unexpected($pred, "tailrec call without TraceInfo")
)
;
PortInfo = port_info_internal(Path, PreDeaths),
ResumeVars = current_resume_point_vars(!.CLD),
set_of_var.difference(PreDeaths, ResumeVars, RealPreDeaths),
RealPreDeathList = set_of_var.to_sorted_list(RealPreDeaths),
list.delete_elems(LiveVars0, RealPreDeathList, LiveVars),
TailRecResetCode = empty
;
PortInfo = port_info_negation_end(Path),
LiveVars = LiveVars0,
TailRecResetCode = empty
;
PortInfo = port_info_user(Path),
LiveVars = LiveVars0,
TailRecResetCode = empty
),
get_varset(!.CI, VarSet),
get_vartypes(!.CI, VarTypes),
get_instmap(!.CLD, InstMap),
trace_produce_vars(LiveVars, VarSet, VarTypes, InstMap, Port,
set.init, TvarSet, [], VarInfoList, ProduceCode, !.CI, !CLD),
max_reg_in_use(!.CLD, MaxRegR, MaxRegF),
get_max_regs_in_use_at_trace(!.CI, MaxTraceRegR0, MaxTraceRegF0),
int.max(MaxRegR, MaxTraceRegR0, MaxTraceRegR),
int.max(MaxRegF, MaxTraceRegF0, MaxTraceRegF),
( if
MaxTraceRegR0 = MaxTraceRegR,
MaxTraceRegF0 = MaxTraceRegF
then
true
else
set_max_regs_in_use_at_trace(MaxTraceRegR, MaxTraceRegF, !CI)
),
variable_locations(!.CLD, VarLocs),
get_proc_info(!.CI, ProcInfo),
set.to_sorted_list(TvarSet, TvarList),
continuation_info.find_typeinfos_for_tvars(TvarList, VarLocs, ProcInfo,
TvarDataMap),
% Compute the set of live lvals at the event.
VarLvals = list.map(find_lval_in_var_info, VarInfoList),
map.values(TvarDataMap, TvarLocnSets),
TvarLocnSet = set.union_list(TvarLocnSets),
set.to_sorted_list(TvarLocnSet, TvarLocns),
TvarLvals = list.map(find_lval_in_layout_locn, TvarLocns),
list.append(VarLvals, TvarLvals, LiveLvals),
LiveLvalSet = set.list_to_set(LiveLvals),
set.list_to_set(VarInfoList, VarInfoSet),
LayoutLabelInfo = layout_label_info(VarInfoSet, TvarDataMap),
(
MaybeUserInfo = no,
TraceStmt0 = "\t\tMR_EVENT_SYS\n"
;
MaybeUserInfo = yes(_),
TraceStmt0 = "\t\tMR_EVENT_USER\n"
),
TraceStmt = "\t\t/* port " ++ trace_port_to_string(Port) ++ ", " ++
"path <" ++ goal_path_to_string(Path) ++ "> */\n" ++
TraceStmt0,
add_trace_layout_for_label(Label, Context, Port, HideEvent,
Path, MaybeUserInfo, LayoutLabelInfo, !CI),
set_proc_trace_events(yes, !CI),
( if
Port = port_fail,
MaybeTraceInfo = yes(TraceInfo),
TraceInfo ^ ti_redo_label = yes(RedoLabel)
then
% The layout information for the redo event is the same as for the
% fail event; all the non-clobbered inputs in their stack slots.
% It is convenient to generate this common layout when the code
% generator state is set up for the fail event; generating it for
% the redo event would be much harder. On the other hand, the address
% of the layout structure for the redo event should be put into its
% fixed stack slot at procedure entry. Therefore setup reserves a label
% for the redo event, whose layout information is filled in when
% we get to the fail event.
add_trace_layout_for_label(RedoLabel, Context, port_redo,
HideEvent, Path, no, LayoutLabelInfo, !CI)
else
true
),
TraceComponents = [foreign_proc_raw_code(cannot_branch_away,
proc_does_not_affect_liveness, live_lvals_info(LiveLvalSet),
TraceStmt)],
TraceCode =
from_list([
llds_instr(label(Label),
"A label to hang trace liveness on"),
% Referring to the label from the foreign_proc_code
% prevents the label from being renamed or optimized away.
% The label is before the trace code because sometimes this
% pair is preceded by another label, and this way we can
% eliminate this other label.
llds_instr(foreign_proc_code([], TraceComponents,
proc_may_call_mercury, no, no, yes(Label), no, yes(Label),
yes, proc_may_not_duplicate), "")
]),
Code = ProduceCode ++ TailRecResetCode ++ TraceCode.
:- pred find_output_vars(assoc_list(prog_var, arg_info)::in,
list(prog_var)::in, list(prog_var)::out) is det.
find_output_vars([], !OutputVars).
find_output_vars([Arg - Info | ArgsInfos], !OutputVars) :-
Info = arg_info(_ArgLoc, Mode),
(
Mode = top_out,
!:OutputVars = [Arg | !.OutputVars]
;
Mode = top_in
;
Mode = top_unused
),
find_output_vars(ArgsInfos, !OutputVars).
:- func find_lval_in_var_info(layout_var_info) = lval.
find_lval_in_var_info(layout_var_info(LayoutLocn, _, _)) =
find_lval_in_layout_locn(LayoutLocn).
:- func find_lval_in_layout_locn(layout_locn) = lval.
find_lval_in_layout_locn(locn_direct(Lval)) = Lval.
find_lval_in_layout_locn(locn_indirect(Lval, _)) = Lval.
maybe_setup_redo_event(TraceInfo, Code) :-
TraceRedoLabel = TraceInfo ^ ti_redo_label,
(
TraceRedoLabel = yes(_),
MaybeFromFullSlot = TraceInfo ^ ti_from_full_lval,
(
MaybeFromFullSlot = yes(Lval),
% The code in the runtime looks for the from-full flag in
% framevar 5; see the comment before reserved_slots.
expect(unify(Lval, framevar(5)), $pred,
"from-full flag not stored in expected slot"),
Code = singleton(
llds_instr(mkframe(temp_frame(nondet_stack_proc),
yes(do_trace_redo_fail_shallow)),
"set up shallow redo event")
)
;
MaybeFromFullSlot = no,
Code = singleton(
llds_instr(mkframe(temp_frame(nondet_stack_proc),
yes(do_trace_redo_fail_deep)),
"set up deep redo event")
)
)
;
TraceRedoLabel = no,
Code = empty
).
:- pred trace_produce_vars(list(prog_var)::in, prog_varset::in, vartypes::in,
instmap::in, trace_port::in, set(tvar)::in, set(tvar)::out,
list(layout_var_info)::in, list(layout_var_info)::out,
llds_code::out, code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
trace_produce_vars([], _, _, _, _, !TVars, !VarInfos, empty, _CI, !CLD).
trace_produce_vars([Var | Vars], VarSet, VarTypes, InstMap, Port,
!TVars, !VarInfos, VarCode ++ VarsCode, CI, !CLD) :-
lookup_var_type(VarTypes, Var, Type),
get_module_info(CI, ModuleInfo),
IsDummy = is_type_a_dummy(ModuleInfo, Type),
(
IsDummy = is_dummy_type,
VarCode = empty
;
IsDummy = is_not_dummy_type,
trace_produce_var(Var, VarSet, InstMap, !TVars, VarInfo, VarCode,
CI, !CLD),
!:VarInfos = [VarInfo | !.VarInfos]
),
trace_produce_vars(Vars, VarSet, VarTypes, InstMap, Port, !TVars,
!VarInfos, VarsCode, CI, !CLD).
:- pred trace_produce_var(prog_var::in, prog_varset::in, instmap::in,
set(tvar)::in, set(tvar)::out, layout_var_info::out, llds_code::out,
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
trace_produce_var(Var, VarSet, _InstMap, !Tvars, VarInfo, VarCode, CI, !CLD) :-
produce_variable_in_reg_or_stack(Var, VarCode, Lval, CI, !CLD),
Type = variable_type(CI, Var),
( if varset.search_name(VarSet, Var, SearchName) then
Name = SearchName
else
Name = ""
),
% get_module_info(!.CI, ModuleInfo),
% instmap_lookup_var(InstMap, Var, Inst),
% ( if inst_match.inst_is_ground(ModuleInfo, Inst) then
% LldsInst = llds_inst_ground
% else
% LldsInst = llds_inst_partial(Inst)
% ),
LldsInst = llds_inst_better_be_ground,
LiveType = live_value_var(Var, Name, Type, LldsInst),
VarInfo = layout_var_info(locn_direct(Lval), LiveType, "trace"),
type_vars(Type, TypeVars),
set.insert_list(TypeVars, !Tvars).
%-----------------------------------------------------------------------------%
:- pred build_fail_vars(list(prog_var)::in, list(mer_inst)::in,
list(arg_info)::in, module_info::in, vartypes::in,
list(prog_var)::out) is semidet.
build_fail_vars([], [], [], _, _, []).
build_fail_vars([Var | Vars], [Inst | Insts], [Info | Infos], ModuleInfo,
VarTypes, FailVars) :-
build_fail_vars(Vars, Insts, Infos, ModuleInfo, VarTypes, FailVars0),
Info = arg_info(_Loc, ArgMode),
( if
ArgMode = top_in,
not inst_is_clobbered(ModuleInfo, Inst),
lookup_var_type(VarTypes, Var, Type),
is_type_a_dummy(ModuleInfo, Type) = is_not_dummy_type
then
FailVars = [Var | FailVars0]
else
FailVars = FailVars0
).
%-----------------------------------------------------------------------------%
:- pred stackref_to_string(lval::in, string::out) is det.
stackref_to_string(Lval, LvalStr) :-
( if Lval = stackvar(Slot) then
string.int_to_string(Slot, SlotString),
LvalStr = "MR_sv(" ++ SlotString ++ ")"
else if Lval = framevar(Slot) then
string.int_to_string(Slot, SlotString),
LvalStr = "MR_fv(" ++ SlotString ++ ")"
else if Lval = double_stackvar(_, _) then
% XXX how do we get here?
sorry($pred, "double-width stack slot")
else
unexpected($pred, "non-stack lval")
).
%-----------------------------------------------------------------------------%
:- func convert_external_port_type(external_trace_port) = trace_port.
convert_external_port_type(external_port_call) = port_call.
convert_external_port_type(external_port_exit) = port_exit.
convert_external_port_type(external_port_fail) = port_fail.
convert_external_port_type(external_port_tailrec_call) = port_tailrec_call.
%-----------------------------------------------------------------------------%
:- pred event_num_slot(code_model::in, lval::out) is det.
:- pred call_num_slot(code_model::in, lval::out) is det.
:- pred call_depth_slot(code_model::in, lval::out) is det.
:- pred redo_layout_slot(code_model::in, lval::out) is det.
event_num_slot(CodeModel, EventNumSlot) :-
(
CodeModel = model_non,
EventNumSlot = framevar(1)
;
( CodeModel = model_det
; CodeModel = model_semi
),
EventNumSlot = stackvar(1)
).
call_num_slot(CodeModel, CallNumSlot) :-
(
CodeModel = model_non,
CallNumSlot = framevar(2)
;
( CodeModel = model_det
; CodeModel = model_semi
),
CallNumSlot = stackvar(2)
).
call_depth_slot(CodeModel, CallDepthSlot) :-
(
CodeModel = model_non,
CallDepthSlot = framevar(3)
;
( CodeModel = model_det
; CodeModel = model_semi
),
CallDepthSlot = stackvar(3)
).
redo_layout_slot(CodeModel, RedoLayoutSlot) :-
(
CodeModel = model_non,
RedoLayoutSlot = framevar(4)
;
( CodeModel = model_det
; CodeModel = model_semi
),
unexpected($pred,
"attempt to access redo layout slot for det or semi procedure")
).
%-----------------------------------------------------------------------------%
% Information for tracing that is valid throughout the execution
% of a procedure.
:- type trace_info
---> trace_info(
ti_trace_level :: trace_level,
ti_trace_suppress_items :: trace_suppress_items,
% If the trace level is shallow, the lval of the slot
% that holds the from-full flag.
ti_from_full_lval :: maybe(lval),
% If the procedure has I/O state arguments, the lval
% of the slot that holds the initial value of the
% I/O action counter.
ti_io_seq_lval :: maybe(lval),
% If trailing is enabled, the lvals of the slots that hold
% the value of the trail pointer and the ticket counter
% at the time of the call.
ti_trail_lvals :: maybe(pair(lval)),
% If we reserve a slot for holding the value of maxfr
% at entry for use in implementing retry, the lval of the slot.
ti_maxfr_lval :: maybe(lval),
% If we reserve a slot for holding the value of the call table
% tip variable, the lval of this variable.
ti_call_table_tip_lval :: maybe(lval),
% If we reserve a slot for holding the number of times the
% stack frame was reused by tail recursive calls, the lval
% holding this counter, and the label that a tail recursive
% call should jump to.
ti_tail_rec_info :: maybe(pair(lval, label)),
% If we are generating redo events, this has the label
% associated with the fail event, which we then reserve
% in advance, so we can put the address of its layout struct
% into the slot which holds the layout for the redo event
% (the two events have identical layouts).
ti_redo_label :: maybe(label)
).
get_trace_maybe_tail_rec_info(TraceInfo, TraceInfo ^ ti_tail_rec_info).
%-----------------------------------------------------------------------------%
:- end_module ll_backend.trace_gen.
%-----------------------------------------------------------------------------%