mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 13:55:07 +00:00
Estimated hours taken: 32
Branches: main
Major improvements to tabling, of two types. The first is the implementation
of the loopcheck and memo forms of tabling for model_non procedures, and the
second is a start on the implementation of a new method of implementing
minimal model tabling, one that has the potential for a proper fix of the
problem that we currently merely detect with the pneg stack (the detection
is followed by a runtime abort). Since this new method relies on giving each
own generator its own stack, the grade component denoting it is "mmos"
(minimal model own stack). The true name of the existing method is changed
from "mm" to "mmsc" (minimal model stack copy). The grade component "mm"
is now a shorthand for "mmsc"; when the new method works, "mm" will be changed
to be a shorthand for "mmos".
configure.in:
scripts/canonical_grade.sh-subr:
scripts/init_grade_options.sh-subr:
scripts/parse_grade_options.sh-subr:
scripts/final_grade_options.sh-subr:
compiler/options.m:
Handle the new way of handling minimal model grades.
scripts/mgnuc.in:
compiler/compile_target_code.m:
Conform to the changes in minimal model grade options.
compiler/table_gen.m:
Implement the transformations required by the loopcheck and memo
tabling of model_non procedures, and the minimal model own stack
transformation.
The new implementation transformations use foreign_procs with extra
args, since there is no point in implementing them both that way and
with separate calls to library predicates. This required making the
choice of which method to use at the top level of each transformation.
Fix an oversight that hasn't caused problems yet but may in the future:
mark goals wrapping the original goals as not impure for determinism
computations.
compiler/handle_options.m:
Handle the new arrangement of the options for minimal model tabling.
Detect simultaneous calls for both forms of minimal model tabling,
and generate an error message. Allow for more than one error message
generated at once; report them all once rather than separately.
compiler/globals.m:
Add a mechanism to allow a fix a problem detected by the changes
to handle_options: the fact that we currently may generate a usage
message more than once for invocations with more than one error.
compiler/mercury_compile.m:
compiler/make.program_target.m:
compiler/make.util.m:
Use the new mechanism in handle_options to avoid generating duplicate
usage messages.
compiler/error_util.m:
Add a utility predicate for use by handle_options.
compiler/hlds_pred.m:
Allow memo tabling for model_non predicates, and handle own stack
tabling.
compiler/hlds_out.m:
Print information about the modes of the arguments of foreign_procs,
since this is useful in debugging transformations such as tabling
that generate them.
compiler/prog_data.m:
compiler/layout_out.m:
compiler/prog_out.m:
runtime/mercury_stack_layout.h:
Mention the new evaluation method.
compiler/goal_util.m:
Change the predicates for creating calls and foreign_procs to allow
more than one goal feature to be attached to the new goal. table_gen.m
now uses this capability.
compiler/add_heap_ops.m:
compiler/add_trail_ops.m:
compiler/polymorphism.m:
compiler/simplify.m:
compiler/size_prof.m:
compiler/typecheck.m:
compiler/unify_proc.m:
Conform to the changes in goal_util.
compiler/code_info.m:
compiler/make_hlds.m:
compiler/modules.m:
compiler/prog_io_pragma.m:
Conform to the new the options controlling minimal model
tabling.
compiler/prog_util.m:
Add a utility predicate for use by table_gen.m.
library/std_util.m:
Conform to the changes in the macros for minimal model tabling grades.
library/table_builtin.m:
Add the types and predicates required by the new transformations.
Delete an obsolete comment.
runtime/mercury_grade.h:
Handle the new minimal model grade component.
runtime/mercury_conf_param.h:
List macros controlling minimal model grades.
runtime/mercury_tabling.[ch]:
Define the types needed by the new transformations,
Implement the performance-critical predicates that need to be
hand-written for memo tabling of model_non predicates.
Add utility predicates for debugging.
runtime/mercury_tabling_preds.h:
Add the implementations of the predicates required by the new
transformations.
runtime/mercury_mm_own_stacks.[ch]:
This new module contains the first draft of the implementation
of the own stack implementation of minimal model tabling.
runtime/mercury_imp.h:
Include the new file if the grade needs it.
runtime/Mmakefile:
Mention the new files, and sort the lists of filenames.
runtime/mercury_tabling_macros.h:
Add a macro for allocating answer blocks without requiring them to be
pointed to directly by trie nodes.
runtime/mercury_minimal_model.[ch]:
The structure type holding answer lists is now in mercury_tabling.h,
since it is now also needed by memo tabling of model_non predicates.
It no longer has a field for an answer num, because while it is ok
to require a separate grade for debugging minimal model tabling,
it is not ok to require a separate grade for debugging memo tabling
of model_non predicates. Instead of printing the answer numbers,
print the answers themselves when we need to identify solutions
for debugging.
Change function names, macro names, error messages etc where this is
useful to distinguish the two kinds of minimal model tabling.
Fix some oversights wrt transient registers.
runtime/mercury_context.[ch]:
runtime/mercury_engine.[ch]:
runtime/mercury_memory.[ch]:
runtime/mercury_wrapper.[ch]:
With own stack tabling, each subgoal has its own context, so record
the identity of the subgoal owning a context in the context itself.
The main computation's context is the exception: it has no owner.
Record not just the main context, but also the contexts of subgoals
in the engine.
Add variables for holding the sizes of the det and nondet stacks
of the contexts of subgoals (which should in general be smaller
than the sizes of the corresponding stacks of the main context),
and initialize them as needed.
Initialize the variables holding the sizes of the gen, cut and pneg
stacks, even in grades where the stacks are not used, for safety.
Fix some out-of-date documentation, and conform to our coding
guidelines.
runtime/mercury_memory_zones.[ch]:
Add a function to test whether a pointer is in a zone, to help
debugging.
runtime/mercury_debug.[ch]:
Add some functions to help debugging in the presence of multiple
contexts, and factor out some common code to help with this.
Delete the obsolete, unused function MR_printdetslot_as_label.
runtime/mercury_context.h:
runtime/mercury_bootstrap.h:
Move a bootstrapping #define from mercury_context.h to
mercury_bootstrap.h.
runtime/mercury_context.h:
runtime/mercury_bootstrap.h:
Move a bootstrapping #define from mercury_context.h to
mercury_bootstrap.h.
runtime/mercury_types.h:
Add some more forward declarations of type names.
runtime/mercury_dlist.[ch]:
Rename a field to avoid assignments that dereference NULL.
runtime/mercury_debug.c:
runtime/mercury_memory.c:
runtime/mercury_ml_expand_body.h:
runtime/mercury_stack_trace.c:
runtime/mercury_stacks.[ch]:
trace/mercury_trace_util.c
Update uses of the macros that control minimal model tabling.
runtime/mercury_stack_trace.c:
Provide a mechanism to allow stack traces to be suppressed entirely.
The intention is that by using this mechanism, by the testing system
won't have to provide separate .exp files for hlc grades, nondebug
LLDS grades and debug LLDS grades, as we do currently. The mechanism
is the environment variable MERCURY_SUPPRESS_STACK_TRACE.
tools/bootcheck:
tools/test_mercury:
Specify MERCURY_SUPPRESS_STACK_TRACE.
trace/mercury_trace.c:
When performing retries across tabled calls, handle memo tabled
model_non predicates, for which the call table tip variable holds
a record with a back pointer to a trie node, instead of the trie node
itself.
trace/mercury_trace_internal.c:
When printing tables, handle memo tabled model_non predicates. Delete
the code now moved to runtime/mercury_tabling.c.
Add functions for printing the data structures for own stack minimal
model tabling.
tests/debugger/print_table.{m,inp,exp}:
Update this test case to also test the printing of tables for
memo tabled model_non predicates.
tests/debugger/retry.{m,inp,exp}:
Update this test case to also test retries across memo tabled
model_non predicates.
tests/tabling/loopcheck_nondet.{m,exp}:
tests/tabling/loopcheck_nondet_non_loop.{m,exp}:
New test cases to test loopcheck tabled model_non predicates.
One test case has a loop to detect, one doesn't.
tests/tabling/memo_non.{m,exp}:
tests/tabling/tc_memo.{m,exp}:
tests/tabling/tc_memo2.{m,exp}:
New test cases to test memo tabled model_non predicates.
One test case has a loop to detect, one has a need for minimal model
tabling to detect, and the third doesn't have either.
tests/tabling/Mmakefile:
Add the new test cases, and reenable the existing tc_loop test case.
Rename some make variables and targets to make them better reflect
their meaning.
tests/tabling/test_mercury:
Conform to the change in the name of the make target.
613 lines
15 KiB
C
613 lines
15 KiB
C
/*
|
|
** Copyright (C) 1998-2001, 2003-2004 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.
|
|
*/
|
|
|
|
/*
|
|
** This file contains code for printing statistics about stack frame sizes,
|
|
** and for manipulating the generator stack, the cut stack and the pneg stack.
|
|
**
|
|
** The generator stack has one entry for each call to a minimal model tabled
|
|
** procedure that is (a) acting as the generator for its subgoal and (b) is
|
|
** in the active state. In systems such as XSB, each choice point has a flag
|
|
** saying whether it is an active generator or not, and if yes, where its
|
|
** subgoal's tabling information is stored. We achieve the same effect by
|
|
** checking whether a nondet stack frame at a given offset has an entry in
|
|
** the generator stack, an approach that minimizes the performance impact
|
|
** of tabling on non-tabled procedures.
|
|
**
|
|
** The cut stack has one entry for each commit goal that execution has entered
|
|
** but not yet exited. Each commit stack entry has a list of all the generators
|
|
** that have been started inside the corresponding commit goal. When the commit
|
|
** goal is exited, it is possible that some of these generators are left
|
|
** incomplete; due to the commit, they will in fact never be completed.
|
|
** The purpose of the cut stack is to enable us to reset the call table
|
|
** entries of such generators to inactive.
|
|
**
|
|
** All the functions in this file that take MR_TrieNode arguments use
|
|
** only the subgoal member of the union.
|
|
*/
|
|
|
|
#include "mercury_imp.h"
|
|
#include "mercury_runtime_util.h"
|
|
#include <stdio.h>
|
|
|
|
/***************************************************************************/
|
|
|
|
#ifdef MR_STACK_FRAME_STATS
|
|
|
|
#include "mercury_dword.h"
|
|
|
|
MR_Dword MR_det_frame_count;
|
|
MR_Dword MR_det_frame_total_size;
|
|
MR_Word *MR_det_frame_max;
|
|
MR_Dword MR_non_frame_count;
|
|
MR_Dword MR_non_frame_total_size;
|
|
MR_Word *MR_non_frame_max;
|
|
|
|
MR_uint_least32_t MR_old_low_tmp;
|
|
|
|
void
|
|
MR_init_stack_frame_stats(void)
|
|
{
|
|
MR_zero_dword(MR_det_frame_count);
|
|
MR_zero_dword(MR_det_frame_total_size);
|
|
MR_zero_dword(MR_non_frame_count);
|
|
MR_zero_dword(MR_non_frame_total_size);
|
|
|
|
/*
|
|
** We cannot initialize these to the starts of the their respective
|
|
** memory areas, since those areas may not have been initialized yet.
|
|
*/
|
|
|
|
MR_det_frame_max = NULL;
|
|
MR_non_frame_max = NULL;
|
|
}
|
|
|
|
void
|
|
MR_print_stack_frame_stats(void)
|
|
{
|
|
FILE *fp;
|
|
double det_frame_count;
|
|
double det_frame_total_size;
|
|
double non_frame_count;
|
|
double non_frame_total_size;
|
|
|
|
fp = MR_checked_fopen(MR_STACK_FRAME_STATS, "open", "a");
|
|
|
|
MR_convert_dword_to_double(MR_det_frame_count,
|
|
det_frame_count);
|
|
MR_convert_dword_to_double(MR_det_frame_total_size,
|
|
det_frame_total_size);
|
|
MR_convert_dword_to_double(MR_non_frame_count,
|
|
non_frame_count);
|
|
MR_convert_dword_to_double(MR_non_frame_total_size,
|
|
non_frame_total_size);
|
|
|
|
fprintf(fp, "number of det stack frames created: %.0f\n",
|
|
det_frame_count);
|
|
fprintf(fp, "number of words in det stack frames: %.0f\n",
|
|
det_frame_total_size);
|
|
fprintf(fp, "average size of a det stack frame: %.3f\n",
|
|
det_frame_total_size / det_frame_count);
|
|
fprintf(fp, "max size of det stack: %ld\n",
|
|
(long) (MR_det_frame_max
|
|
- MR_CONTEXT(MR_ctxt_detstack_zone)->min));
|
|
fprintf(fp, "\n");
|
|
|
|
fprintf(fp, "number of non stack frames created: %.0f\n",
|
|
non_frame_count);
|
|
fprintf(fp, "number of words in non stack frames: %.0f\n",
|
|
non_frame_total_size);
|
|
fprintf(fp, "average size of a non stack frame: %.3f\n",
|
|
non_frame_total_size / non_frame_count);
|
|
fprintf(fp, "max size of non stack: %ld\n",
|
|
(long) (MR_non_frame_max
|
|
- MR_CONTEXT(MR_ctxt_nondetstack_zone)->min));
|
|
fprintf(fp, "-------------------------------------------\n");
|
|
|
|
MR_checked_fclose(fp, MR_STACK_FRAME_STATS);
|
|
}
|
|
|
|
#endif /* MR_STACK_FRAME_STATS */
|
|
|
|
/***************************************************************************/
|
|
|
|
#undef MR_TABLE_DEBUG
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
|
|
|
|
MR_Integer MR_gen_next_var;
|
|
MR_GenStackFrame *MR_gen_stack_var;
|
|
|
|
MR_Integer MR_cut_next_var;
|
|
MR_CutStackFrame *MR_cut_stack_var;
|
|
|
|
MR_Integer MR_pneg_next_var;
|
|
MR_PNegStackFrame *MR_pneg_stack_var;
|
|
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
static int MR_pneg_cut_depth = 0;
|
|
#endif
|
|
|
|
static void MR_print_gen_stack_entry(FILE *fp, MR_Integer i,
|
|
MR_GenStackFrame *p);
|
|
|
|
static void MR_cleanup_generator_ptr(MR_SubgoalPtr generator_ptr);
|
|
static void MR_print_cut_stack_entry(FILE *fp, MR_Integer i,
|
|
MR_CutStackFrame *p);
|
|
|
|
static void MR_cleanup_consumer_ptr(MR_TrieNode consumer_ptr);
|
|
static void MR_print_pneg_stack_entry(FILE *fp, MR_Integer i,
|
|
MR_PNegStackFrame *p);
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
** Record that the nondet stack frame at address frame_addr is now the
|
|
** generator for subgoal.
|
|
*/
|
|
|
|
void
|
|
MR_push_generator(MR_Word *frame_addr, MR_SubgoalPtr subgoal)
|
|
{
|
|
MR_gen_stack[MR_gen_next].MR_gen_frame = frame_addr;
|
|
MR_gen_stack[MR_gen_next].MR_gen_subgoal = subgoal;
|
|
MR_gen_next++;
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("push ");
|
|
MR_print_gen_stack_entry(stdout, MR_gen_next - 1,
|
|
&MR_gen_stack[MR_gen_next - 1]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
** Return the subgoal of the topmost generator on the nondet stack.
|
|
*/
|
|
|
|
MR_Subgoal *
|
|
MR_top_generator_table(void)
|
|
{
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("top ");
|
|
MR_print_gen_stack_entry(stdout, MR_gen_next - 1,
|
|
&MR_gen_stack[MR_gen_next - 1]);
|
|
}
|
|
#endif
|
|
|
|
return MR_gen_stack[MR_gen_next - 1].MR_gen_subgoal;
|
|
}
|
|
|
|
/*
|
|
** Record the deletion of the topmost generator on the nondet stack.
|
|
*/
|
|
|
|
void
|
|
MR_pop_generator(void)
|
|
{
|
|
--MR_gen_next;
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("pop ");
|
|
MR_print_gen_stack_entry(stdout, MR_gen_next,
|
|
&MR_gen_stack[MR_gen_next]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
MR_print_gen_stack(FILE *fp)
|
|
{
|
|
MR_print_any_gen_stack(fp, MR_gen_next, MR_gen_stack);
|
|
}
|
|
|
|
void
|
|
MR_print_any_gen_stack(FILE *fp, MR_Integer gen_next,
|
|
MR_GenStackFrame *gen_block)
|
|
{
|
|
MR_Integer i;
|
|
|
|
fprintf(fp, "gen stack size: %d\n", (int) gen_next);
|
|
for (i = gen_next - 1; i >= 0; i--) {
|
|
MR_print_gen_stack_entry(fp, i, &MR_gen_stack[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
MR_print_gen_stack_entry(FILE *fp, MR_Integer i, MR_GenStackFrame *p)
|
|
{
|
|
MR_SubgoalDebug *subgoal_debug;
|
|
|
|
fprintf(fp, "gen %ld = <", (long) i);
|
|
MR_print_nondstackptr(fp, p->MR_gen_frame);
|
|
subgoal_debug = MR_lookup_subgoal_debug_addr(p->MR_gen_subgoal);
|
|
fprintf(fp, ", %s>\n", MR_subgoal_debug_name(subgoal_debug));
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
** Record the entering of a committed choice context.
|
|
*/
|
|
|
|
void
|
|
MR_commit_mark(void)
|
|
{
|
|
MR_restore_transient_registers();
|
|
|
|
MR_cut_stack[MR_cut_next].MR_cut_frame = MR_maxfr;
|
|
MR_cut_stack[MR_cut_next].MR_cut_gen_next = MR_gen_next;
|
|
MR_cut_stack[MR_cut_next].MR_cut_generators = NULL;
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
MR_cut_stack[MR_cut_next].MR_cut_depth = MR_pneg_cut_depth;
|
|
MR_pneg_cut_depth++;
|
|
#endif
|
|
MR_cut_next++;
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("commit stack next up to %ld\n", (long) MR_cut_next);
|
|
}
|
|
#endif
|
|
|
|
MR_save_transient_registers();
|
|
}
|
|
|
|
/*
|
|
** Record the leaving of a committed choice context, and clean up the
|
|
** generators that were created within the context that are still active.
|
|
** We need to clean them up because otherwise, consumers will be depend on this
|
|
** generator to find all the answers to the generator's subgoal, but the
|
|
** generation will never compute any more answers, since it will never be
|
|
** backtracked into.
|
|
*/
|
|
|
|
void
|
|
MR_commit_cut(void)
|
|
{
|
|
MR_CutGeneratorList g;
|
|
|
|
--MR_cut_next;
|
|
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
--MR_pneg_cut_depth;
|
|
#endif
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("commit stack next down to %ld\n",
|
|
(long) MR_cut_next);
|
|
printf("setting generator stack next back to %ld from %ld\n",
|
|
(long) MR_cut_stack[MR_cut_next].MR_cut_gen_next,
|
|
(long) MR_gen_next);
|
|
|
|
if (MR_gen_next < MR_cut_stack[MR_cut_next].MR_cut_gen_next) {
|
|
printf("MR_gen_next %ld, MR_cut_next %ld, "
|
|
"MR_cut_stack[MR_cut_next].gen_next %ld\n",
|
|
(long) MR_gen_next,
|
|
(long) MR_cut_next,
|
|
(long) MR_cut_stack[MR_cut_next].
|
|
MR_cut_gen_next);
|
|
MR_fatal_error("GEN_NEXT ASSERTION FAILURE");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (g = MR_cut_stack[MR_cut_next].MR_cut_generators; g != NULL;
|
|
g = g->MR_cut_next_generator)
|
|
{
|
|
MR_cleanup_generator_ptr(g->MR_cut_generator_ptr);
|
|
}
|
|
|
|
MR_cut_stack[MR_cut_next].MR_cut_generators = NULL;
|
|
MR_gen_next = MR_cut_stack[MR_cut_next].MR_cut_gen_next;
|
|
}
|
|
|
|
/*
|
|
** Record the creation of a generator, for possible cleanup later by
|
|
** MR_commit_cut.
|
|
*/
|
|
|
|
void
|
|
MR_register_generator_ptr(MR_SubgoalPtr subgoal)
|
|
{
|
|
struct MR_CutGeneratorListNode *node;
|
|
|
|
if (MR_cut_next <= 0) {
|
|
return;
|
|
}
|
|
|
|
node = MR_GC_NEW(struct MR_CutGeneratorListNode);
|
|
node->MR_cut_generator_ptr = subgoal;
|
|
node->MR_cut_next_generator =
|
|
MR_cut_stack[MR_cut_next - 1].MR_cut_generators;
|
|
MR_cut_stack[MR_cut_next - 1].MR_cut_generators = node;
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("registering generator %p -> %s "
|
|
"at commit stack level %d\n",
|
|
subgoal, MR_subgoal_addr_name(subgoal),
|
|
MR_cut_next - 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
MR_cleanup_generator_ptr(MR_SubgoalPtr subgoal)
|
|
{
|
|
if (subgoal->MR_sg_status == MR_SUBGOAL_COMPLETE) {
|
|
/* there is nothing to do, everything is OK */
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("no cleanup: generator %p -> %s is complete\n",
|
|
subgoal->MR_sg_back_ptr,
|
|
MR_subgoal_addr_name(subgoal));
|
|
}
|
|
#endif
|
|
} else {
|
|
/* this generator will never complete the subgoal */
|
|
MR_ConsumerList consumer_list;
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("cleanup: generator %p -> %s deleted\n",
|
|
subgoal->MR_sg_back_ptr,
|
|
MR_subgoal_addr_name(subgoal));
|
|
}
|
|
#endif
|
|
|
|
subgoal->MR_sg_back_ptr->MR_subgoal = NULL;
|
|
subgoal->MR_sg_back_ptr = NULL;
|
|
|
|
for (consumer_list = subgoal->MR_sg_consumer_list;
|
|
consumer_list != NULL;
|
|
consumer_list = consumer_list->MR_cl_next)
|
|
{
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("cleanup: consumer %s is deleted",
|
|
MR_consumer_addr_name(
|
|
consumer_list->MR_cl_item));
|
|
}
|
|
#endif
|
|
|
|
consumer_list->MR_cl_item->MR_cns_subgoal = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_print_cut_stack(FILE *fp)
|
|
{
|
|
MR_print_any_cut_stack(fp, MR_cut_next, MR_cut_stack);
|
|
}
|
|
|
|
void
|
|
MR_print_any_cut_stack(FILE *fp, MR_Integer cut_next,
|
|
MR_CutStackFrame *cut_block)
|
|
{
|
|
MR_Integer i;
|
|
|
|
fprintf(fp, "cut stack size: %d\n", (int) cut_next);
|
|
for (i = cut_next - 1; i >= 0; i--) {
|
|
MR_print_cut_stack_entry(fp, i, &cut_block[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
MR_print_cut_stack_entry(FILE *fp, MR_Integer i, MR_CutStackFrame *p)
|
|
{
|
|
MR_SubgoalDebug *subgoal_debug;
|
|
MR_CutGeneratorList gen_list;
|
|
|
|
fprintf(fp, "cut %ld = <", (long) i);
|
|
MR_print_nondstackptr(fp, p->MR_cut_frame);
|
|
fprintf(fp, ">");
|
|
fprintf(fp, ", cut_gen_next %d", (int) p->MR_cut_gen_next);
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
fprintf(fp, ", pneg+cut stack depth %d", (int) p->MR_cut_depth);
|
|
#endif
|
|
fprintf(fp, "\n");
|
|
|
|
fprintf(fp, "registered generators:");
|
|
gen_list = p->MR_cut_generators;
|
|
if (gen_list == NULL) {
|
|
fprintf(fp, " none");
|
|
} else {
|
|
while (gen_list != NULL) {
|
|
if (gen_list->MR_cut_generator_ptr == NULL) {
|
|
fprintf(fp, " <NULL>");
|
|
} else {
|
|
subgoal_debug = MR_lookup_subgoal_debug_addr(
|
|
gen_list->MR_cut_generator_ptr);
|
|
fprintf(fp, " <%s>",
|
|
MR_subgoal_debug_name(subgoal_debug));
|
|
}
|
|
|
|
gen_list = gen_list->MR_cut_next_generator;
|
|
}
|
|
}
|
|
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
void
|
|
MR_register_suspension(MR_Consumer *consumer)
|
|
{
|
|
MR_PNegConsumerList node_ptr;
|
|
|
|
if (MR_pneg_next <= 0) {
|
|
return;
|
|
}
|
|
|
|
node_ptr = MR_TABLE_NEW(MR_PNegConsumerListNode);
|
|
node_ptr->MR_pneg_consumer_ptr = consumer;
|
|
node_ptr->MR_pneg_next_consumer =
|
|
MR_pneg_stack[MR_pneg_next - 1].MR_pneg_consumers;
|
|
MR_pneg_stack[MR_pneg_next - 1].MR_pneg_consumers = node_ptr;
|
|
}
|
|
|
|
void
|
|
MR_pneg_enter_cond(void)
|
|
{
|
|
MR_restore_transient_registers();
|
|
|
|
MR_pneg_stack[MR_pneg_next].MR_pneg_frame = MR_maxfr;
|
|
MR_pneg_stack[MR_pneg_next].MR_pneg_consumers = NULL;
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
MR_pneg_stack[MR_pneg_next].MR_pneg_gen_next = MR_gen_next;
|
|
MR_pneg_stack[MR_pneg_next].MR_pneg_depth = MR_pneg_cut_depth;
|
|
MR_pneg_cut_depth++;
|
|
#endif
|
|
MR_pneg_next++;
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("pneg stack next up to %ld\n", (long) MR_pneg_next);
|
|
}
|
|
#endif
|
|
|
|
MR_save_transient_registers();
|
|
}
|
|
|
|
void
|
|
MR_pneg_enter_then(void)
|
|
{
|
|
MR_PNegConsumerList l;
|
|
MR_PNegConsumerList next;
|
|
|
|
MR_restore_transient_registers();
|
|
|
|
--MR_pneg_next;
|
|
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
--MR_pneg_cut_depth;
|
|
#endif
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("pneg stack down up to %ld (then)\n",
|
|
(long) MR_pneg_next);
|
|
}
|
|
#endif
|
|
|
|
for (l = MR_pneg_stack[MR_pneg_next].MR_pneg_consumers; l != NULL;
|
|
l = next)
|
|
{
|
|
next = l->MR_pneg_next_consumer;
|
|
MR_table_free(l);
|
|
}
|
|
|
|
MR_save_transient_registers();
|
|
}
|
|
|
|
void
|
|
MR_pneg_enter_else(void)
|
|
{
|
|
MR_PNegConsumerList l;
|
|
MR_PNegConsumerList next;
|
|
MR_PNegConsumerList consumer_list;
|
|
|
|
MR_restore_transient_registers();
|
|
|
|
--MR_pneg_next;
|
|
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
--MR_pneg_cut_depth;
|
|
#endif
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("pneg stack down up to %ld (else)\n",
|
|
(long) MR_pneg_next);
|
|
}
|
|
#endif
|
|
|
|
consumer_list = MR_pneg_stack[MR_pneg_next].MR_pneg_consumers;
|
|
for (l = consumer_list; l != NULL; l = next) {
|
|
MR_Subgoal *subgoal;
|
|
MR_Consumer *consumer;
|
|
|
|
next = l->MR_pneg_next_consumer;
|
|
consumer = l->MR_pneg_consumer_ptr;
|
|
if (consumer->MR_cns_subgoal == NULL) {
|
|
/* this consumer has logically been deleted */
|
|
continue;
|
|
}
|
|
|
|
subgoal = consumer->MR_cns_subgoal;
|
|
if (subgoal->MR_sg_status != MR_SUBGOAL_COMPLETE) {
|
|
MR_fatal_error("failing out of negated context "
|
|
"with incomplete consumer");
|
|
}
|
|
|
|
MR_table_free(l);
|
|
}
|
|
|
|
MR_save_transient_registers();
|
|
}
|
|
|
|
void
|
|
MR_print_pneg_stack(FILE *fp)
|
|
{
|
|
MR_print_any_pneg_stack(fp, MR_pneg_next, MR_pneg_stack);
|
|
}
|
|
|
|
void
|
|
MR_print_any_pneg_stack(FILE *fp, MR_Integer pneg_next,
|
|
MR_PNegStackFrame *pneg_block)
|
|
{
|
|
MR_Integer i;
|
|
|
|
fprintf(fp, "pneg stack size: %d\n", (int) pneg_next);
|
|
for (i = MR_pneg_next - 1; i >= 0; i--) {
|
|
MR_print_pneg_stack_entry(fp, i, &pneg_block[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
MR_print_pneg_stack_entry(FILE *fp, MR_Integer i, MR_PNegStackFrame *p)
|
|
{
|
|
MR_PNegConsumerList l;
|
|
|
|
fprintf(fp, "pneg %d = <", (int) i);
|
|
MR_print_nondstackptr(fp, p->MR_pneg_frame);
|
|
fprintf(fp, ">");
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
fprintf(fp, ", pneg_gen_next %d", (int) p->MR_pneg_gen_next);
|
|
fprintf(fp, ", pneg+cut stack depth %d\n", (int) p->MR_pneg_depth);
|
|
#endif
|
|
fprintf(fp, "\n");
|
|
|
|
fprintf(fp, "registered consumers: ");
|
|
if (p->MR_pneg_consumers == NULL) {
|
|
fprintf(fp, " none");
|
|
} else {
|
|
MR_Consumer *consumer;
|
|
int n;
|
|
|
|
for (n = 1, l = p->MR_pneg_consumers; l != NULL;
|
|
l = l->MR_pneg_next_consumer, n++)
|
|
{
|
|
consumer = l->MR_pneg_consumer_ptr;
|
|
fprintf(fp, " <%d: %s>",
|
|
n, MR_consumer_addr_name(consumer));
|
|
}
|
|
}
|
|
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
#endif /* MR_USE_MINIMAL_MODEL_STACK_COPY */
|