%-----------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %-----------------------------------------------------------------------------% % Copyright (C) 1997-2010 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: 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.globals. :- import_module ll_backend.code_info. :- import_module ll_backend.continuation_info. :- import_module ll_backend.llds. :- import_module mdbcomp.program_representation. :- import_module parse_tree.prog_data. :- 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 nondet_foreign_proc_trace_port ---> nondet_foreign_proc_first ; nondet_foreign_proc_later. :- 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(prog_var)::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) 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) is det. % If we are doing execution tracing, generate code for a nondet % foreign_proc trace event. % :- pred maybe_generate_foreign_proc_event_code( nondet_foreign_proc_trace_port::in, prog_context::in, llds_code::out, code_info::in, code_info::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) 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) 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) 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.builtin_ops. :- import_module check_hlds.inst_match. :- import_module check_hlds.mode_util. :- import_module check_hlds.type_util. :- import_module hlds.goal_path. :- import_module hlds.code_model. :- import_module hlds.hlds_llds. :- import_module hlds.instmap. :- 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_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( forward_goal_path, % The id of the tail recursive call. assoc_list(prog_var, arg_info) % The list of arguments of this call. ) ; port_info_internal( forward_goal_path, % The id of the goal whose start this port represents. set(prog_var) % The pre-death set of this goal. ) ; port_info_negation_end( forward_goal_path % The id of the goal whose end (one way or another) % this port represents. ) ; port_info_user( forward_goal_path % The id of the goal. ) ; port_info_nondet_foreign_proc. 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), ( build_fail_vars(HeadVars, Insts, ArgInfos, ModuleInfo, VarTypes, FailVarsList) -> set.list_to_set(FailVarsList, FailVars) ; unexpected(this_file, "length mismatch in trace.fail_vars") ). do_we_need_maxfr_slot(Globals, ModuleInfo, PredInfo0, !ProcInfo) :- globals.get_trace_level(Globals, TraceLevel), CodeModel = proc_info_interface_code_model(!.ProcInfo), ( 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) -> MaxfrFlag = yes ; MaxfrFlag = no ), proc_info_set_need_maxfr_slot(MaxfrFlag, !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. ( proc_info_interface_code_model(ProcInfo) = model_non, eff_trace_needs_port(ModuleInfo, PredInfo, ProcInfo, TraceLevel, TraceSuppress, port_redo) = yes -> RedoLayout = 1 ; RedoLayout = 0 ), % Stage 3. ( eff_trace_level_needs_from_full_slot(ModuleInfo, PredInfo, ProcInfo, TraceLevel) = yes -> FromFull = 1 ; 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_need_maxfr_slot(ProcInfo, NeedMaxfr), ( NeedMaxfr = yes, Maxfr = 1 ; NeedMaxfr = no, Maxfr = 0 ), % Stage 7. proc_info_get_has_tail_call_events(ProcInfo, TailCallEvents), ( TailCallEvents = tail_call_events, TailRec = 1 ; TailCallEvents = no_tail_call_events, 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. ( TraceRedo = yes, CodeModel = model_non -> 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 ; 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_need_maxfr_slot(ProcInfo, NeedMaxfr), ( NeedMaxfr = yes, MaxfrSlot = !.NextSlot, MaybeMaxfrSlot = yes(MaxfrSlot), MaxfrLval = stack_slot_num_to_lval(StackId, MaxfrSlot), MaybeMaxfrLval = yes(MaxfrLval), !:NextSlot = !.NextSlot + 1 ; NeedMaxfr = no, 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, yes(_)) -> CallTableSlot = !.NextSlot, MaybeCallTableSlot = yes(CallTableSlot), CallTableLval = stack_slot_num_to_lval(StackId, CallTableSlot), MaybeCallTableLval = yes(CallTableLval) ; 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) :- get_maybe_trace_info(!.CI, MaybeTraceInfo), ( MaybeTraceInfo = yes(TraceInfo), Goal = hlds_goal(_, GoalInfo), GoalId = goal_info_get_goal_id(GoalInfo), get_containing_goal_map_det(!.CI, ContainingGoalMap), map.lookup(ContainingGoalMap, GoalId, ContainingGoal), ( ContainingGoal = containing_goal(_, LastStep), ( LastStep = step_switch(_, _), PortPrime = port_switch ; LastStep = step_disj(DisjunctNum), ( DisjunctNum = 1 -> PortPrime = port_disj_first ; 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 ) -> Port = PortPrime ; unexpected(this_file, "generate_internal_event_code: bad path") ), ( 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 -> goal_info_get_pre_deaths(GoalInfo, PreDeaths), Context = goal_info_get_context(GoalInfo), ( goal_info_has_feature(OutsideGoalInfo, feature_hide_debug_event) -> HideEvent = yes ; 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) ; Code = empty ) ; MaybeTraceInfo = no, Code = empty ). maybe_generate_negated_event_code(Goal, OutsideGoalInfo, NegPort, Code, !CI) :- get_maybe_trace_info(!.CI, MaybeTraceInfo), ( 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 -> Goal = hlds_goal(_, GoalInfo), GoalId = goal_info_get_goal_id(GoalInfo), Context = goal_info_get_context(GoalInfo), ( goal_info_has_feature(OutsideGoalInfo, feature_hide_debug_event) -> HideEvent = yes ; HideEvent = no ), get_containing_goal_map_det(!.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) ; Code = empty ). maybe_generate_foreign_proc_event_code(PragmaPort, Context, Code, !CI) :- get_maybe_trace_info(!.CI, MaybeTraceInfo), ( MaybeTraceInfo = yes(TraceInfo), Port = convert_nondet_foreign_proc_port_type(PragmaPort), 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 -> generate_event_code(Port, port_info_nondet_foreign_proc, yes(TraceInfo), Context, no, no, _, _, Code, !CI) ; Code = empty ). generate_user_event_code(UserInfo, GoalInfo, Code, !CI) :- GoalId = goal_info_get_goal_id(GoalInfo), get_containing_goal_map_det(!.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). generate_external_event_code(ExternalPort, TraceInfo, Context, MaybeExternalInfo, !CI) :- 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), MaybeExternalInfo = yes(external_event_info(Label, TvarDataMap, Code)) ; NeedPort = no, MaybeExternalInfo = no ). generate_tailrec_event_code(TraceInfo, ArgsInfos, GoalId, Context, Code, TailRecLabel, !CI) :- Port = port_tailrec_call, get_containing_goal_map_det(!.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), MaybeTailRecInfo = TraceInfo ^ ti_tail_rec_info, ( MaybeTailRecInfo = yes(_ - TailRecLabel) ; MaybeTailRecInfo = no, unexpected(this_file, "generate_tailrec_event_code: 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), this_file, "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), this_file, "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, lval(TailRecLval), const(llconst_int(1)))), "increment tail recursion counter") ) ; TailRecInfo = no, unexpected(this_file, "generate_tailrec_reset_slots_code: 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) is det. generate_event_code(Port, PortInfo, MaybeTraceInfo, Context, HideEvent, MaybeUserInfo, Label, TvarDataMap, Code, !CI) :- get_next_label(Label, !CI), get_known_variables(!.CI, LiveVars0), ( PortInfo = port_info_external, LiveVars = LiveVars0, Path = fgp([]), 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(this_file, "generate_event_code: tailrec call without TraceInfo") ) ; PortInfo = port_info_internal(Path, PreDeaths), ResumeVars = current_resume_point_vars(!.CI), set.difference(PreDeaths, ResumeVars, RealPreDeaths), set.to_sorted_list(RealPreDeaths, RealPreDeathList), 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 ; PortInfo = port_info_nondet_foreign_proc, LiveVars = [], ( Port = port_nondet_foreign_proc_first -> Path = fgp([step_first]) ; Port = port_nondet_foreign_proc_later -> Path = fgp([step_later]) ; unexpected(this_file, "generate_event_code: bad nondet foreign_proc port") ), TailRecResetCode = empty ), VarTypes = get_var_types(!.CI), get_varset(!.CI, VarSet), get_instmap(!.CI, InstMap), trace_produce_vars(LiveVars, VarSet, VarTypes, InstMap, Port, set.init, TvarSet, [], VarInfoList, ProduceCode, !CI), max_reg_in_use(!.CI, MaxReg), get_max_reg_in_use_at_trace(!.CI, MaxTraceReg0), ( MaxTraceReg0 < MaxReg -> set_max_reg_in_use_at_trace(MaxReg, !CI) ; true ), variable_locations(!.CI, 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), ( Port = port_fail, MaybeTraceInfo = yes(TraceInfo), TraceInfo ^ ti_redo_label = yes(RedoLabel) -> % 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) ; 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)), this_file, "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_info::out) is det. trace_produce_vars([], _, _, _, _, !TVars, !VarInfos, empty, !CI). trace_produce_vars([Var | Vars], VarSet, VarTypes, InstMap, Port, !TVars, !VarInfos, VarCode ++ VarsCode, !CI) :- map.lookup(VarTypes, Var, Type), get_module_info(!.CI, ModuleInfo), IsDummy = check_dummy_type(ModuleInfo, Type), ( IsDummy = is_dummy_type, VarCode = empty ; IsDummy = is_not_dummy_type, trace_produce_var(Var, VarSet, InstMap, !TVars, VarInfo, VarCode, !CI), !:VarInfos = [VarInfo | !.VarInfos] ), trace_produce_vars(Vars, VarSet, VarTypes, InstMap, Port, !TVars, !VarInfos, VarsCode, !CI). :- 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_info::out) is det. trace_produce_var(Var, VarSet, InstMap, !Tvars, VarInfo, VarCode, !CI) :- produce_variable_in_reg_or_stack(Var, VarCode, Lval, !CI), Type = variable_type(!.CI, Var), get_module_info(!.CI, ModuleInfo), ( varset.search_name(VarSet, Var, SearchName) -> Name = SearchName ; Name = "" ), instmap_lookup_var(InstMap, Var, Inst), ( inst_match.inst_is_ground(ModuleInfo, Inst) -> LldsInst = llds_inst_ground ; LldsInst = llds_inst_partial(Inst) ), LiveType = live_value_var(Var, Name, Type, LldsInst), VarInfo = layout_var_info(locn_direct(Lval), LiveType, "trace"), type_vars(Type, TypeVars), set.insert_list(!.Tvars, 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), ( ArgMode = top_in, \+ inst_is_clobbered(ModuleInfo, Inst), map.lookup(VarTypes, Var, Type), check_dummy_type(ModuleInfo, Type) = is_not_dummy_type -> FailVars = [Var | FailVars0] ; FailVars = FailVars0 ). %-----------------------------------------------------------------------------% :- func trace_code_model_to_string(code_model) = string. trace_code_model_to_string(model_det) = "MR_MODEL_DET". trace_code_model_to_string(model_semi) = "MR_MODEL_SEMI". trace_code_model_to_string(model_non) = "MR_MODEL_NON". :- pred stackref_to_string(lval::in, string::out) is det. stackref_to_string(Lval, LvalStr) :- ( Lval = stackvar(Slot) -> string.int_to_string(Slot, SlotString), LvalStr = "MR_sv(" ++ SlotString ++ ")" ; Lval = framevar(Slot) -> string.int_to_string(Slot, SlotString), LvalStr = "MR_fv(" ++ SlotString ++ ")" ; unexpected(this_file, "non-stack lval in stackref_to_string") ). %-----------------------------------------------------------------------------% :- 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. :- func convert_nondet_foreign_proc_port_type(nondet_foreign_proc_trace_port) = trace_port. convert_nondet_foreign_proc_port_type(nondet_foreign_proc_first) = port_nondet_foreign_proc_first. convert_nondet_foreign_proc_port_type(nondet_foreign_proc_later) = port_nondet_foreign_proc_later. %-----------------------------------------------------------------------------% :- 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(this_file, "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). %-----------------------------------------------------------------------------% :- func this_file = string. this_file = "trace_gen.m". %-----------------------------------------------------------------------------%