[m-rev.] for review: tail recursion and debugging

Julien Fischer juliensf at csse.unimelb.edu.au
Tue Nov 25 17:57:01 AEDT 2008


On Fri, 21 Nov 2008, Zoltan Somogyi wrote:

> Implement a new compiler option, --exec-trace-tail-rec, that preserves direct
> tail recursion in det and semidet procedures even when debugging is enabled.
> This should allow the debugging of programs that previously ran out of stack.
>
> The problem arose because even a directly tail-recursive call had some code
> after it: the code for the EXIT event, like this:
>
> 	p:
> 		incr_sp
> 		fill in the usual debug slots
> 		CALL EVENT
> 		...
> 		/* tail call */
> 		move arguments to registers as usual
> 		call p, return to p_ret
> 	p_ret:
> 		/* code to move output arguments to right registers is empty */
> 		EXIT EVENT
> 		decr_sp
> 		return
>
> If the new option is enabled, the compiler will now generate code like this:
>
> 	p:
> 		incr_sp
> 		fill in the usual debug slots
> 		fill in new "stack frame reuse count" slot with 0
> 		CALL EVENT
> 	p_1:
> 		...
> 		/* tail call */
> 		move arguments to registers as usual
> 		update the usual debug slots
> 		increment the "stack frame reuse count" slot
> 		TAILCALL EVENT
> 		goto p_1
>
> The new TAIL event takes place in the caller's stack frame, so that the local
> variables of the caller are available. This includes the arguments of the
> recursive call (though if they are unnamed variables, the debugger will not
> show them). The TAIL event serves as a replacement for the CALL event
> of the recursive invocation.
>
> compiler/options.m:
> 	Add the new option.
>
> compiler/handle_options.m:
> 	Handle an implications of the new option: the declarative debugger

s/implications/implication/

> 	does not (yet) understand TAIL events.
>
> compiler/mark_tail_calls.m:
> 	New module to mark directly tail recursive calls and the procedures
> 	containing them as such.
>
> compiler/hlds.m:
> compiler/notes/compiler_design.html:
> 	Mention the new module.
>
> compiler/mercury_compile.m:
> 	Invoke the new module when the new option asks us to.
>
> compiler/hlds_goal.m:
> 	Add the feature used to mark tail recursive calls for the debugger.
> 	Rename an existing feature with a similar but not identical purpose
> 	to avoid possible confusion.
>
> compiler/hlds_pred.m:
> 	Add a field to proc_infos that says whether the procedure contains
> 	tail recursive calls.
>
> 	Minor style improvements.
>
> compiler/passes_aux.m:
> 	Minor change to accommodate the needs of the new module.
>
> compiler/code_info.m:
> 	Transmit the information from mark_tail_calls to the code generator.
>
> compiler/call_gen.m:
> 	Implement the new option.
>
> compiler/trace_gen.m:
> 	Reserve the extra slot needed for the new option.
>
> 	Switch to state variable notation in the code that does the slot
> 	allocation, since this is less error-prone than the previous approach.
>
> compiler/layout.m:
> compiler/layout_out.m:
> compiler/stack_layout.m:
> 	Remember what stack slot holds the stack frame reuse counter,
> 	for transmission to the runtime system.
>
> compiler/proc_gen.m:
> 	Add the new label needed for tail recursion.
>
> 	Put the arguments of some procedures into a more logical order.
>
> compiler/deep_profiling.m:
> compiler/deforest.m:
> compiler/saved_vars.m:
> compiler/table_gen.m:
> 	Conform to the changes above.
>
> compiler/trace_params.m:
> mdbcomp/prim_data.m:
> runtime/mercury_trace_base.[ch]:
> 	Add the new event type.
>
> 	Convert mercury_trace_base.h to four-space indentation.
>
> runtime/mercury_stack_layout.h:
> 	Add a field to the execution trace information we have for each
> 	procedure that gives the number of the stack slot (if any) that holds
> 	the stack frame reuse counter. Add a macro to get the value in the
> 	counter.
>
> 	Convert this header file to four-space indentation.
>
> runtime/mercury_stack_trace.[ch]:
> 	When walking the stack, we now have to be prepared to encounter stack
> 	frames that have been reused. Modify the algorithms in this module
> 	accordingly, and modify the interfaces of the exported functions
> 	to allow the functions' callers to behave accordingly as well.
>
> 	Group the information we gather about stack frame for printing into
> 	one structure, and document it.
>
> 	Convert the header to four-space indentation.
>
> library/exception.m:
> mdbcomp/trace_counts.m:
> 	Conform to the changes above.
>
> 	In trace_counts.m, fix an apparent cut-and-paste error (that hasn't
> 	caused any test case failures yet).
>
> trace/mercury_trace.c:
> 	Modify the implementation of the "next" and "finish" commands
> 	to accommodate the possibility that the procedure at the selected
> 	depth may have had its stack frame reused. In such cases
>
> tests/debugger/tailrec1.{m,inp,exp,data}:
> 	A new test case to check the handling of tail recursive procedures.

...

> Index: runtime/mercury_stack_layout.h
> ===================================================================
> RCS file: /home/mercury/mercury1/repository/mercury/runtime/mercury_stack_layout.h,v
> retrieving revision 1.114
> diff -u -b -r1.114 mercury_stack_layout.h
> --- runtime/mercury_stack_layout.h	18 Mar 2008 01:31:33 -0000	1.114
> +++ runtime/mercury_stack_layout.h	21 Nov 2008 02:41:18 -0000
> @@ -1,4 +1,7 @@
> /*
> +** vim: ts=4 sw=4 expandtab
> +*/
> +/*
> ** Copyright (C) 1998-2008 The University of Melbourne.
> ** This file may only be copied under the terms of the GNU Library General
> ** Public License - see the file COPYING.LIB in the Mercury distribution.
> @@ -913,6 +916,14 @@
> ** The flags field encodes boolean properties of the procedure. For now,
> ** the only property is whether the procedure has a pair of I/O state
> ** arguments.
> +**
> +XXX
> +** If the procedure contains on the nondet stack, or if it cannot create any

That first bit doesn't make sense.

> +** temporary nondet stack frames, the maybe_maxfr field will contain a negative
> +** number. If it lives on the det stack, and can create temporary nondet stack
> +** frames, it will contain the number number of the stack slot that contains the
> +** value of maxfr on entry, for use in executing the retry debugger command
> +** from the middle of the procedure.
> */
>
> #define	MR_EVAL_METHOD_MEMO_STRICT	MR_EVAL_METHOD_MEMO
> @@ -964,6 +975,7 @@
> 	MR_int_least8_t		MR_exec_maybe_call_table;
> 	MR_TraceLevelInt	MR_exec_trace_level_CAST_ME;
> 	MR_uint_least8_t	MR_exec_flags;
> +    MR_int_least8_t             MR_exec_maybe_tail_rec;
> } MR_ExecTrace;
>
> #define MR_compute_max_mr_num(max_mr_num, layout)			\

...

> Index: runtime/mercury_stack_trace.c
> ===================================================================
> RCS file: /home/mercury/mercury1/repository/mercury/runtime/mercury_stack_trace.c,v
> retrieving revision 1.81
> diff -u -b -r1.81 mercury_stack_trace.c
> --- runtime/mercury_stack_trace.c	7 Aug 2008 01:16:19 -0000	1.81
> +++ runtime/mercury_stack_trace.c	21 Nov 2008 03:04:04 -0000

...

> @@ -1081,83 +1097,98 @@
>     ** We cannot merge two calls even to the same procedure if we are printing
>     ** trace data, since this will differ between the calls.
>     **
> -    ** Note that it is not possible for two calls to the same procedure to differ
> -    ** on whether the procedure has trace layout data or not.
> +    ** Note that it is not possible for two calls to the same procedure
> +    ** to differ on whether the procedure has trace layout data or not.
>     */
> -    must_flush = (entry_layout != prev_entry_layout) || trace_data_enabled;
> +    must_flush = (proc_layout != prev_dump_info.MR_sdi_proc_layout)
> +        || trace_data_enabled;
>
>     if (must_flush) {
>         if (! at_line_limit) {
> -            MR_dump_stack_record_flush(fp, print_stack_record);
> +            MR_dump_stack_record_flush(fp, trace_data_enabled,
> +                print_stack_record);
>         }
>
> -        prev_entry_layout = entry_layout;
> -        prev_entry_layout_count = 1;
> -        prev_entry_start_level = current_level;
> -        prev_entry_base_sp = base_sp;
> -        prev_entry_base_curfr = base_curfr;
> -        prev_entry_filename = filename;
> -        prev_entry_linenumber = linenumber;
> -        prev_entry_goal_path = MR_label_goal_path(label_layout);
> -        prev_entry_context_mismatch = MR_FALSE;
> +        prev_dump_info.MR_sdi_proc_layout = proc_layout;
> +        prev_dump_info.MR_sdi_num_frames = 1;
> +        prev_dump_info.MR_sdi_min_level = current_level;
> +        prev_dump_info.MR_sdi_max_level = current_level + reused_frames;
> +        prev_dump_info.MR_sdi_filename = filename;
> +        prev_dump_info.MR_sdi_linenumber = linenumber;
> +        prev_dump_info.MR_sdi_context_mismatch = MR_FALSE;
> +
> +        prev_dump_info.MR_sdi_base_sp = base_sp;
> +        prev_dump_info.MR_sdi_base_curfr = base_curfr;
> +        prev_dump_info.MR_sdi_goal_path = MR_label_goal_path(label_layout);
> +
>         lines_printed = 1;
>     } else {
> -        prev_entry_layout_count++;
> -        if (prev_entry_filename != filename
> -            || prev_entry_linenumber != linenumber)
> +        prev_dump_info.MR_sdi_num_frames++;
> +        prev_dump_info.MR_sdi_max_level = current_level + reused_frames;
> +        if (prev_dump_info.MR_sdi_filename != filename
> +            || prev_dump_info.MR_sdi_linenumber != linenumber)
>         {
> -            prev_entry_context_mismatch = MR_TRUE;
> +            prev_dump_info.MR_sdi_context_mismatch = MR_TRUE;
>         }
> +
>         lines_printed = 0;
>     }
>
> -    current_level++;
> +    current_level += 1 + reused_frames;
>     return lines_printed;
> }
>
> static void
> -MR_dump_stack_record_flush(FILE *fp, MR_PrintStackRecord print_stack_record)
> +MR_dump_stack_record_flush(FILE *fp, MR_bool include_trace_data,
> +    MR_PrintStackRecord print_stack_record)
> {
> -    if (prev_entry_layout != NULL) {
> -        print_stack_record(fp, prev_entry_layout,
> -            prev_entry_layout_count, prev_entry_start_level,
> -            prev_entry_base_sp, prev_entry_base_curfr,
> -            prev_entry_filename, prev_entry_linenumber,
> -            prev_entry_goal_path, prev_entry_context_mismatch);
> +    if (prev_dump_info.MR_sdi_proc_layout != NULL) {
> +        print_stack_record(fp, include_trace_data, prev_dump_info);
>     }
> }
>
> void
> -MR_dump_stack_record_print(FILE *fp, const MR_ProcLayout *proc_layout,
> -    int count, MR_Level start_level, MR_Word *base_sp, MR_Word *base_curfr,
> -    const char *filename, int linenumber, const char *goal_path,
> -    MR_bool context_mismatch)
> -{
> -    fprintf(fp, "%4" MR_INTEGER_LENGTH_MODIFIER "d ", start_level);
> -
> -    if (count > 1) {
> -        fprintf(fp, " %3d* ", count);
> -    } else if (! trace_data_enabled) {
> -        fprintf(fp, "%5s ", "");
> -    } else {
> +MR_dump_stack_record_print(FILE *fp, MR_bool include_trace_data,
> +    const MR_StackDumpInfo dump_info)
> +{
> +    MR_Level    num_levels;
> +
> +    num_levels = dump_info.MR_sdi_max_level + 1 - dump_info.MR_sdi_min_level;
> +    fprintf(fp, "%4" MR_INTEGER_LENGTH_MODIFIER "d ",
> +        dump_info.MR_sdi_min_level);
> +
>         /*
> -        ** If we are printing trace data, we need all the horizonal
> -        ** room we can get, and there will not be any repeated lines,
> -        ** so we don't reserve space for the repeat counts.
> +    ** If we are printing trace data, we need all the horizonal room

s/horizonal/horizontal/

> +    ** we can get, and there will not be any repeated lines, so we do not
> +    ** reserve space for the repeat counts.

...


That looks okay otherwise.

Julien.
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list