%-----------------------------------------------------------------------------% % 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. :- import_module hlds.hlds_goal. :- import_module hlds.hlds_module. :- import_module hlds.hlds_pred. :- import_module libs. :- import_module libs.globals. :- import_module libs.trace_params. :- 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 pair. :- import_module set. %-----------------------------------------------------------------------------% % 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(eff_trace_level::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(globals::in, proc_info::in, eff_trace_level::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(globals::in, proc_info::in, eff_trace_level::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. :- 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. :- 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. % 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 hlds.code_model. :- import_module hlds.hlds_llds. :- import_module hlds.instmap. :- import_module libs.options. :- 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 parse_tree.var_table. :- import_module bool. :- import_module cord. :- import_module int. :- import_module list. :- import_module require. :- import_module string. :- import_module term. %-----------------------------------------------------------------------------% % Information for tracing that is valid throughout the execution % of a procedure. :- type trace_info ---> trace_info( ti_eff_trace_level :: eff_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). %-----------------------------------------------------------------------------% % 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_var_table(ProcInfo, VarTable), proc_info_get_headvars(ProcInfo, HeadVars), proc_info_get_argmodes(ProcInfo, Modes), mode_list_get_final_insts(ModuleInfo, Modes, FinalInsts), proc_info_arg_info(ProcInfo, ArgInfos), ( if build_fail_vars(ModuleInfo, VarTable, HeadVars, FinalInsts, ArgInfos, FailVarsList) then set_of_var.list_to_set(FailVarsList, FailVars) else unexpected($pred, "length mismatch") ). :- pred build_fail_vars(module_info::in, var_table::in, list(prog_var)::in, list(mer_inst)::in, list(arg_info)::in, list(prog_var)::out) is semidet. build_fail_vars(_, _, [], [], [], []). build_fail_vars(ModuleInfo, VarTable, [Var | Vars], [Inst | Insts], [Info | Infos], FailVars) :- build_fail_vars(ModuleInfo, VarTable, Vars, Insts, Infos, FailVars0), Info = arg_info(_Loc, ArgMode), ( if ArgMode = top_in, not inst_is_clobbered(ModuleInfo, Inst), lookup_var_entry(VarTable, Var, Entry), Entry ^ vte_is_dummy = is_not_dummy_type then FailVars = [Var | FailVars0] else FailVars = FailVars0 ). %-----------------------------------------------------------------------------% do_we_need_maxfr_slot(EffTraceLevel, !ProcInfo) :- CodeModel = proc_info_interface_code_model(!.ProcInfo), ( if is_exec_trace_enabled_at_eff_trace_level(EffTraceLevel) = exec_trace_is_enabled, 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(Globals, ProcInfo, EffTraceLevel, ReservedSlots, MaybeTableVarInfo) :- globals.get_trace_suppress(Globals, TraceSuppress), globals.lookup_bool_option(Globals, trace_table_io, TraceTableIo), FixedSlots = eff_trace_level_needs_fixed_slots(EffTraceLevel), ( 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_level_needs_port(EffTraceLevel, TraceSuppress, port_redo) = yes then RedoLayout = 1 else RedoLayout = 0 ), % Stage 3. NeedFromFullSlot = eff_trace_level_needs_from_full_slot(EffTraceLevel), ( NeedFromFullSlot = yes, FromFull = 1 ; NeedFromFullSlot = no, 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(Globals, ProcInfo, EffTraceLevel, MaybeTailRecLabel, TraceSlotInfo, TraceInfo, !CI) :- CodeModel = get_proc_model(!.CI), globals.get_trace_suppress(Globals, TraceSuppress), globals.lookup_bool_option(Globals, trace_table_io, TraceTableIo), TraceRedo = eff_trace_level_needs_port(EffTraceLevel, 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(EffTraceLevel), 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(EffTraceLevel, 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") ), EffTraceLevel = TraceInfo ^ ti_eff_trace_level, TraceSuppress = TraceInfo ^ ti_trace_suppress_items, NeedsPort = eff_trace_level_needs_port(EffTraceLevel, TraceSuppress, Port), ( NeedsPort = yes, 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) ; NeedsPort = no, 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 ), EffTraceLevel = TraceInfo ^ ti_eff_trace_level, TraceSuppress = TraceInfo ^ ti_trace_suppress_items, eff_trace_level_needs_port(EffTraceLevel, TraceSuppress, 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), EffTraceLevel = TraceInfo ^ ti_eff_trace_level, TraceSuppress = TraceInfo ^ ti_trace_suppress_items, NeedPort = eff_trace_level_needs_port(EffTraceLevel, TraceSuppress, 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_var_table(!.CI, VarTable), get_instmap(!.CLD, InstMap), trace_produce_vars(LiveVars, VarTable, InstMap, Port, set.init, TVarSet, [], VarInfoList, ProduceCode, !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), proc_info_get_rtti_varmaps(ProcInfo, RttiVarMaps), set.to_sorted_list(TVarSet, TVarList), continuation_info.find_typeinfos_for_tvars(VarTable, RttiVarMaps, TVarList, VarLocs, 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, var_table::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_loc_dep::in, code_loc_dep::out) is det. trace_produce_vars([], _, _, _, !TVars, !VarInfos, empty, !CLD). trace_produce_vars([Var | Vars], VarTable, InstMap, Port, !TVars, !VarInfos, VarCode ++ VarsCode, !CLD) :- lookup_var_entry(VarTable, Var, Entry), IsDummy = Entry ^ vte_is_dummy, ( IsDummy = is_dummy_type, VarCode = empty ; IsDummy = is_not_dummy_type, trace_produce_var(Var, Entry, InstMap, !TVars, VarInfo, VarCode, !CLD), !:VarInfos = [VarInfo | !.VarInfos] ), trace_produce_vars(Vars, VarTable, InstMap, Port, !TVars, !VarInfos, VarsCode, !CLD). :- pred trace_produce_var(prog_var::in, var_table_entry::in, instmap::in, set(tvar)::in, set(tvar)::out, layout_var_info::out, llds_code::out, code_loc_dep::in, code_loc_dep::out) is det. trace_produce_var(Var, Entry, _InstMap, !TVars, VarInfo, VarCode, !CLD) :- produce_variable_in_reg_or_stack(Var, VarCode, Lval, !CLD), Entry = vte(Name, Type, _), % 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_in_type(Type, TypeVars), set.insert_list(TypeVars, !TVars). %-----------------------------------------------------------------------------% :- 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") ). %-----------------------------------------------------------------------------% :- end_module ll_backend.trace_gen. %-----------------------------------------------------------------------------%