Files
mercury/runtime/mercury_minimal_model.c
2025-08-25 15:48:28 +02:00

2493 lines
86 KiB
C

// vim: ts=4 sw=4 expandtab ft=c
// Copyright (C) 2003-2006 The University of Melbourne.
// Copyright (C) 2014, 2016, 2018, 2025 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.
// This module contains the functions related specifically to the stack copy
// style of minimal model tabling.
#include "mercury_imp.h"
#include "mercury_array_macros.h"
#include "mercury_tabling.h"
#include "mercury_minimal_model.h"
#include <stdio.h>
#ifdef MR_MINIMAL_MODEL_DEBUG
// MR_MINIMAL_MODEL_DEBUG implies MR_TABLE_DEBUG in this file, since
// if we want to debug minimal model tabling, we need to enable all the
// debugging facilities of this file. However, since MR_TABLE_DEBUG
// increases object file sizes and link times significantly (by implying
// MR_DEBUG_LABEL_NAMES), we don't necessarily want this implication
// to hold globally.
#define MR_TABLE_DEBUG
#endif
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
#ifdef MR_TABLE_DEBUG
static MR_Word *saved_to_real_nondet_stack(MR_SavedState *saved_state,
MR_Word *saved_ptr);
static MR_Word *real_to_saved_nondet_stack(MR_SavedState *saved_state,
MR_Word *real_ptr);
#endif
static MR_Word *nearest_common_ancestor(MR_Word *fr1, MR_Word *fr2);
static void save_state(MR_SavedState *saved_state, MR_Word *generator_fr,
const char *who, const char *what,
const MR_LabelLayout *top_layout);
static void restore_state(MR_SavedState *saved_state, const char *who,
const char *what);
static void prune_right_branches(MR_SavedState *saved_state,
MR_Integer already_pruned, MR_Subgoal *subgoal);
static void extend_consumer_stacks(MR_Subgoal *leader,
MR_Consumer *consumer);
static void make_subgoal_follow_leader(MR_Subgoal *this_follower,
MR_Subgoal *leader);
static void print_saved_state(FILE *fp, MR_SavedState *saved_state);
static void print_stack_segment(FILE *fp, MR_Word *segment,
MR_Integer size);
#ifdef MR_TABLE_STATISTICS
static int MR_minmodel_stats_cnt_save_state = 0;
static int MR_minmodel_stats_save_state_non_words = 0;
static int MR_minmodel_stats_save_state_det_words = 0;
static int MR_minmodel_stats_save_state_gen_frames = 0;
static int MR_minmodel_stats_save_state_cut_frames = 0;
static int MR_minmodel_stats_save_state_pneg_frames = 0;
static int MR_minmodel_stats_save_state_max_non_words = 0;
static int MR_minmodel_stats_save_state_max_det_words = 0;
static int MR_minmodel_stats_save_state_max_gen_frames = 0;
static int MR_minmodel_stats_save_state_max_cut_frames = 0;
static int MR_minmodel_stats_save_state_max_pneg_frames = 0;
static int MR_minmodel_stats_cnt_restore_state = 0;
static int MR_minmodel_stats_restore_state_non_words = 0;
static int MR_minmodel_stats_restore_state_det_words = 0;
static int MR_minmodel_stats_restore_state_gen_frames = 0;
static int MR_minmodel_stats_restore_state_cut_frames = 0;
static int MR_minmodel_stats_restore_state_pneg_frames = 0;
static int MR_minmodel_stats_restore_state_max_non_words = 0;
static int MR_minmodel_stats_restore_state_max_det_words = 0;
static int MR_minmodel_stats_restore_state_max_gen_frames = 0;
static int MR_minmodel_stats_restore_state_max_cut_frames = 0;
static int MR_minmodel_stats_restore_state_max_pneg_frames = 0;
static int MR_minmodel_stats_cnt_extend_state = 0;
static int MR_minmodel_stats_extend_state_non_words = 0;
static int MR_minmodel_stats_extend_state_det_words = 0;
static int MR_minmodel_stats_extend_state_gen_frames = 0;
static int MR_minmodel_stats_extend_state_cut_frames = 0;
static int MR_minmodel_stats_extend_state_pneg_frames = 0;
static int MR_minmodel_stats_extend_state_max_non_words = 0;
static int MR_minmodel_stats_extend_state_max_det_words = 0;
static int MR_minmodel_stats_extend_state_max_gen_frames = 0;
static int MR_minmodel_stats_extend_state_max_cut_frames = 0;
static int MR_minmodel_stats_extend_state_max_pneg_frames = 0;
static int MR_minmodel_stats_cnt_prune = 0;
static int MR_minmodel_stats_prune_loop = 0;
static int MR_minmodel_stats_max_prune_loop = 0;
static int MR_minmodel_stats_cnt_make_subgoal_follow_leader = 0;
static int MR_minmodel_stats_cnt_suspend = 0;
static int MR_minmodel_stats_cnt_completion = 0;
static int MR_minmodel_stats_cnt_completion_start_completion_op = 0;
static int MR_minmodel_stats_cnt_completion_loop_over_subgoals = 0;
static int MR_minmodel_stats_cnt_completion_loop_over_suspensions = 0;
static int MR_minmodel_stats_cnt_completion_return_answer = 0;
static int MR_minmodel_stats_cnt_completion_redo_point = 0;
static int MR_minmodel_stats_cnt_completion_restart_point = 0;
static int MR_minmodel_stats_cnt_completion_fixpoint_check = 0;
static int MR_minmodel_stats_cnt_completion_reached_fixpoint = 0;
static int MR_minmodel_stats_cnt_setup = 0;
static int MR_minmodel_stats_cnt_setup_new = 0;
int MR_minmodel_stats_cnt_dupl_check;
int MR_minmodel_stats_cnt_dupl_check_not_dupl;
#define update_max(max_counter, value) \
( ((value) > (max_counter)) ? (max_counter) = (value) : (void) 0 )
#endif
////////////////////////////////////////////////////////////////////////////
// This part of the file maintains data structures that can be used
// to debug minimal model tabling. It does so by allowing the debugger
// to refer to tabling data structures such as subgoals and consumers
// by small, easily remembered numbers, not memory addresses.
// Set by MR_trace_event, used by table_mm_setup.
const MR_ProcLayout *MR_subgoal_debug_cur_proc = NULL;
struct MR_ConsumerDebug_Struct
{
MR_Consumer *MR_cod_consumer;
int MR_cod_sequence_num;
int MR_cod_version_num;
int MR_cod_valid;
};
struct MR_SubgoalDebug_Struct
{
MR_Subgoal *MR_sgd_subgoal;
int MR_sgd_sequence_num;
int MR_sgd_version_num;
int MR_sgd_valid;
};
#define MR_CONSUMER_DEBUG_INIT 10
#define MR_SUBGOAL_DEBUG_INIT 10
#define MR_NAME_BUF 1024
static MR_ConsumerDebug *MR_consumer_debug_infos = NULL;
static int MR_consumer_debug_info_next = 0;
static int MR_consumer_debug_info_max = 0;
static MR_SubgoalDebug *MR_subgoal_debug_infos = NULL;
static int MR_subgoal_debug_info_next = 0;
static int MR_subgoal_debug_info_max = 0;
void
MR_enter_consumer_debug(MR_Consumer *consumer)
{
int i;
for (i = 0; i < MR_consumer_debug_info_next; i++) {
if (MR_consumer_debug_infos[i].MR_cod_consumer == consumer) {
MR_consumer_debug_infos[i].MR_cod_version_num++;
MR_consumer_debug_infos[i].MR_cod_valid = MR_TRUE;
return;
}
}
MR_ensure_room_for_next(MR_consumer_debug_info, MR_ConsumerDebug,
MR_CONSUMER_DEBUG_INIT);
i = MR_consumer_debug_info_next;
MR_consumer_debug_infos[i].MR_cod_consumer = consumer;
MR_consumer_debug_infos[i].MR_cod_sequence_num = i;
MR_consumer_debug_infos[i].MR_cod_version_num = 0;
MR_consumer_debug_infos[i].MR_cod_valid = MR_TRUE;
MR_consumer_debug_info_next++;
}
MR_ConsumerDebug *
MR_lookup_consumer_debug_addr(MR_Consumer *consumer)
{
int i;
for (i = 0; i < MR_consumer_debug_info_next; i++) {
if (MR_consumer_debug_infos[i].MR_cod_consumer == consumer) {
return &MR_consumer_debug_infos[i];
}
}
return NULL;
}
MR_ConsumerDebug *
MR_lookup_consumer_debug_num(MR_Unsigned consumer_index)
{
MR_Unsigned i;
for (i = 0; i < MR_consumer_debug_info_next; i++) {
if (MR_consumer_debug_infos[i].MR_cod_sequence_num == consumer_index) {
return &MR_consumer_debug_infos[i];
}
}
return NULL;
}
const char *
MR_consumer_debug_name(MR_ConsumerDebug *consumer_debug)
{
const char *warning;
char buf[MR_NAME_BUF];
if (consumer_debug == NULL) {
return "unknown";
}
if (consumer_debug->MR_cod_valid) {
warning = "";
} else {
warning = " INVALID";
}
if (consumer_debug->MR_cod_version_num > 0) {
sprintf(buf, "con %d/%d (%p)%s", consumer_debug->MR_cod_sequence_num,
consumer_debug->MR_cod_version_num,
consumer_debug->MR_cod_consumer, warning);
} else {
sprintf(buf, "con %d (%p)%s", consumer_debug->MR_cod_sequence_num,
consumer_debug->MR_cod_consumer, warning);
}
return strdup(buf);
}
const char *
MR_consumer_addr_name(MR_Consumer *consumer)
{
MR_ConsumerDebug *consumer_debug;
if (consumer == NULL) {
return "NULL";
}
consumer_debug = MR_lookup_consumer_debug_addr(consumer);
return MR_consumer_debug_name(consumer_debug);
}
const char *
MR_consumer_num_name(MR_Unsigned consumer_index)
{
MR_ConsumerDebug *consumer_debug;
consumer_debug = MR_lookup_consumer_debug_num(consumer_index);
return MR_consumer_debug_name(consumer_debug);
}
void
MR_enter_subgoal_debug(MR_Subgoal *subgoal)
{
int i;
for (i = 0; i < MR_subgoal_debug_info_next; i++) {
if (MR_subgoal_debug_infos[i].MR_sgd_subgoal == subgoal) {
MR_subgoal_debug_infos[i].MR_sgd_version_num++;
MR_subgoal_debug_infos[i].MR_sgd_valid = MR_TRUE;
return;
}
}
MR_ensure_room_for_next(MR_subgoal_debug_info, MR_SubgoalDebug,
MR_SUBGOAL_DEBUG_INIT);
i = MR_subgoal_debug_info_next;
MR_subgoal_debug_infos[i].MR_sgd_subgoal = subgoal;
MR_subgoal_debug_infos[i].MR_sgd_sequence_num = i;
MR_subgoal_debug_infos[i].MR_sgd_version_num = 0;
MR_subgoal_debug_infos[i].MR_sgd_valid = MR_TRUE;
MR_subgoal_debug_info_next++;
}
MR_SubgoalDebug *
MR_lookup_subgoal_debug_addr(MR_Subgoal *subgoal)
{
int i;
for (i = 0; i < MR_subgoal_debug_info_next; i++) {
if (MR_subgoal_debug_infos[i].MR_sgd_subgoal == subgoal) {
return &MR_subgoal_debug_infos[i];
}
}
return NULL;
}
MR_SubgoalDebug *
MR_lookup_subgoal_debug_num(MR_Unsigned subgoal_index)
{
MR_Unsigned i;
for (i = 0; i < MR_subgoal_debug_info_next; i++) {
if (MR_subgoal_debug_infos[i].MR_sgd_sequence_num == subgoal_index) {
return &MR_subgoal_debug_infos[i];
}
}
return NULL;
}
const char *
MR_subgoal_debug_name(MR_SubgoalDebug *subgoal_debug)
{
const char *warning;
char buf[MR_NAME_BUF];
if (subgoal_debug == NULL) {
return "unknown";
}
if (subgoal_debug->MR_sgd_valid) {
warning = "";
} else {
warning = " INVALID";
}
if (subgoal_debug->MR_sgd_version_num > 0) {
sprintf(buf, "sub %d/%d (%p)%s", subgoal_debug->MR_sgd_sequence_num,
subgoal_debug->MR_sgd_version_num,
subgoal_debug->MR_sgd_subgoal, warning);
} else {
sprintf(buf, "sub %d (%p)%s", subgoal_debug->MR_sgd_sequence_num,
subgoal_debug->MR_sgd_subgoal, warning);
}
return strdup(buf);
}
const char *
MR_subgoal_addr_name(MR_Subgoal *subgoal)
{
MR_SubgoalDebug *subgoal_debug;
if (subgoal == NULL) {
return "NULL";
}
subgoal_debug = MR_lookup_subgoal_debug_addr(subgoal);
return MR_subgoal_debug_name(subgoal_debug);
}
const char *
MR_subgoal_num_name(int subgoal_index)
{
MR_SubgoalDebug *subgoal_debug;
subgoal_debug = MR_lookup_subgoal_debug_num(subgoal_index);
return MR_subgoal_debug_name(subgoal_debug);
}
const char *
MR_subgoal_status(MR_SubgoalStatus status)
{
switch (status) {
case MR_SUBGOAL_INACTIVE:
return "INACTIVE";
case MR_SUBGOAL_ACTIVE:
return "ACTIVE";
case MR_SUBGOAL_COMPLETE:
return "COMPLETE";
}
return "INVALID";
}
void
MR_print_subgoal_debug(FILE *fp, const MR_ProcLayout *proc,
MR_SubgoalDebug *subgoal_debug)
{
if (subgoal_debug == NULL) {
fprintf(fp, "NULL subgoal_debug\n");
} else {
MR_print_subgoal(fp, proc, subgoal_debug->MR_sgd_subgoal);
}
}
void
MR_print_subgoal(FILE *fp, const MR_ProcLayout *proc, MR_Subgoal *subgoal)
{
MR_SubgoalList follower;
MR_ConsumerList consumer;
MR_AnswerList answer_list;
MR_Word *answer;
int answer_num;
if (subgoal == NULL) {
fprintf(fp, "NULL subgoal\n");
return;
}
#ifdef MR_MINIMAL_MODEL_DEBUG
if (proc == NULL && subgoal->MR_sg_proc_layout != NULL) {
proc = subgoal->MR_sg_proc_layout;
}
#endif
fprintf(fp, "subgoal %s: status %s, generator frame ",
MR_subgoal_addr_name(subgoal),
MR_subgoal_status(subgoal->MR_sg_status));
MR_print_nondetstackptr(fp, subgoal->MR_sg_generator_fr);
if (subgoal->MR_sg_back_ptr == NULL) {
fprintf(fp, ", DELETED");
}
fprintf(fp, "\n");
if (proc != NULL) {
fprintf(fp, "proc: ");
MR_print_proc_id(fp, proc);
fprintf(fp, "\n");
}
fprintf(fp, "leader: %s, ",
MR_subgoal_addr_name(subgoal->MR_sg_leader));
fprintf(fp, "followers:");
for (follower = subgoal->MR_sg_followers;
follower != NULL; follower = follower->MR_sl_next)
{
fprintf(fp, " %s", MR_subgoal_addr_name(follower->MR_sl_item));
}
fprintf(fp, "\nconsumers:");
for (consumer = subgoal->MR_sg_consumer_list;
consumer != NULL; consumer = consumer->MR_cl_next)
{
fprintf(fp, " %s", MR_consumer_addr_name(consumer->MR_cl_item));
}
fprintf(fp, "\n");
fprintf(fp, "answers: %" MR_INTEGER_LENGTH_MODIFIER "d\n",
subgoal->MR_sg_num_ans);
if (proc != NULL) {
answer_list = subgoal->MR_sg_answer_list;
answer_num = 1;
while (answer_list != NULL) {
fprintf(fp, "answer #%d: <", answer_num);
MR_print_answerblock(fp, proc, answer_list->MR_aln_answer_block);
fprintf(fp, ">\n");
answer_list = answer_list->MR_aln_next_answer;
answer_num++;
}
}
}
void
MR_print_consumer_debug(FILE *fp, const MR_ProcLayout *proc,
MR_ConsumerDebug *consumer_debug)
{
if (consumer_debug == NULL) {
fprintf(fp, "NULL consumer_debug\n");
} else {
MR_print_consumer(fp, proc, consumer_debug->MR_cod_consumer);
}
}
void
MR_print_consumer(FILE *fp, const MR_ProcLayout *proc, MR_Consumer *consumer)
{
if (consumer == NULL) {
fprintf(fp, "NULL consumer\n");
return;
}
fprintf(fp, "consumer %s", MR_consumer_addr_name(consumer));
if (consumer->MR_cns_subgoal != NULL) {
fprintf(fp, ", of subgoal %s",
MR_subgoal_addr_name(consumer->MR_cns_subgoal));
fprintf(fp, "\nreturned answers %" MR_INTEGER_LENGTH_MODIFIER "d,",
consumer->MR_cns_num_returned_answers);
fprintf(fp, " remaining answers ptr %p\n",
consumer->MR_cns_remaining_answer_list_ptr);
print_saved_state(fp, &consumer->MR_cns_saved_state);
} else {
fprintf(fp, ", DELETED\n");
}
}
////////////////////////////////////////////////////////////////////////////
MR_Subgoal *
MR_setup_subgoal(MR_TrieNode trie_node)
{
// Initialize the subgoal if this is the first time we see it.
// If the subgoal structure already exists but is marked inactive,
// then it was left by a previous generator that couldn't
// complete the evaluation of the subgoal due to a commit.
// In that case, we want to forget all about the old generator.
MR_restore_transient_registers();
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_setup++;
#endif
if (trie_node->MR_subgoal == NULL) {
MR_Subgoal *subgoal;
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_setup_new++;
#endif
subgoal = MR_TABLE_NEW(MR_Subgoal);
subgoal->MR_sg_back_ptr = trie_node;
subgoal->MR_sg_status = MR_SUBGOAL_INACTIVE;
subgoal->MR_sg_leader = NULL;
subgoal->MR_sg_followers = MR_TABLE_NEW(MR_SubgoalListNode);
subgoal->MR_sg_followers->MR_sl_item = subgoal;
subgoal->MR_sg_followers->MR_sl_next = NULL;
subgoal->MR_sg_followers_tail =
&(subgoal->MR_sg_followers->MR_sl_next);
subgoal->MR_sg_answer_table.MR_integer = 0;
subgoal->MR_sg_num_ans = 0;
subgoal->MR_sg_answer_list = NULL;
subgoal->MR_sg_answer_list_tail = &subgoal->MR_sg_answer_list;
subgoal->MR_sg_consumer_list = NULL;
subgoal->MR_sg_consumer_list_tail = &subgoal->MR_sg_consumer_list;
#ifdef MR_MINIMAL_MODEL_DEBUG
// MR_subgoal_debug_cur_proc refers to the last procedure
// that executed a call event, if any. If the procedure that is
// executing table_mm_setup is traced, this will be that
// procedure, and recording the layout structure of the
// processor in the subgoal allows us to interpret the contents
// of the subgoal's answer tables. If the procedure executing
// table_mm_setup is not traced, then the layout structure
// belongs to another procedure and the any use of the
// MR_sg_proc_layout field will probably cause a core dump.
// For implementors debugging minimal model tabling, this is
// the right tradeoff.
subgoal->MR_sg_proc_layout = MR_subgoal_debug_cur_proc;
#endif
#ifdef MR_TABLE_DEBUG
MR_enter_subgoal_debug(subgoal);
if (MR_tabledebug) {
printf("setting up subgoal %p -> %s, ",
trie_node, MR_subgoal_addr_name(subgoal));
printf("answer slot %p\n", subgoal->MR_sg_answer_list_tail);
#ifdef MR_MINIMAL_MODEL_DEBUG
if (subgoal->MR_sg_proc_layout != NULL) {
printf("proc: ");
MR_print_proc_id(stdout, subgoal->MR_sg_proc_layout);
printf("\n");
}
#endif
}
if (MR_maxfr != MR_curfr) {
MR_fatal_error("MR_maxfr != MR_curfr at table setup\n");
}
#endif
subgoal->MR_sg_generator_fr = MR_curfr;
subgoal->MR_sg_deepest_nca_fr = MR_curfr;
trie_node->MR_subgoal = subgoal;
}
MR_save_transient_registers();
return trie_node->MR_subgoal;
}
////////////////////////////////////////////////////////////////////////////
#ifdef MR_TABLE_STATISTICS
void
MR_minimal_model_report_stats(FILE *fp)
{
fprintf(fp, "number of setup operations: %d\n",
MR_minmodel_stats_cnt_setup);
fprintf(fp, "number of nontrivial setup operations: %d\n",
MR_minmodel_stats_cnt_setup_new);
fprintf(fp, "number of duplicate check operations: %d\n",
MR_minmodel_stats_cnt_dupl_check);
fprintf(fp, "number of duplicate check operations new answer: %d\n",
MR_minmodel_stats_cnt_dupl_check_not_dupl);
fprintf(fp, "number of suspend operations: %d\n",
MR_minmodel_stats_cnt_suspend);
fprintf(fp, "number of coup operations: %d\n",
MR_minmodel_stats_cnt_make_subgoal_follow_leader);
fprintf(fp, "number of completion operations: %d\n",
MR_minmodel_stats_cnt_completion);
fprintf(fp, "number of completion start completion: %d\n",
MR_minmodel_stats_cnt_completion_start_completion_op);
fprintf(fp, "number of completion start loop over subgoals: %d\n",
MR_minmodel_stats_cnt_completion_loop_over_subgoals);
fprintf(fp, "number of completion start loop over suspension: %d\n",
MR_minmodel_stats_cnt_completion_loop_over_suspensions);
fprintf(fp, "number of completion redo point: %d\n",
MR_minmodel_stats_cnt_completion_redo_point);
fprintf(fp, "number of completion return answer: %d\n",
MR_minmodel_stats_cnt_completion_return_answer);
fprintf(fp, "number of completion restart point: %d\n",
MR_minmodel_stats_cnt_completion_restart_point);
fprintf(fp, "number of completion fixpoint check: %d\n",
MR_minmodel_stats_cnt_completion_fixpoint_check);
fprintf(fp, "number of completion reached fixpoint: %d\n",
MR_minmodel_stats_cnt_completion_reached_fixpoint);
fprintf(fp, "number of save_state operations: %d\n",
MR_minmodel_stats_cnt_save_state);
if (MR_minmodel_stats_cnt_save_state > 0) {
fprintf(fp, "non stack words copied in save_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_save_state_non_words,
MR_minmodel_stats_save_state_max_non_words,
(float) MR_minmodel_stats_save_state_non_words /
MR_minmodel_stats_cnt_save_state);
fprintf(fp, "det stack words copied in save_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_save_state_det_words,
MR_minmodel_stats_save_state_max_det_words,
(float) MR_minmodel_stats_save_state_det_words /
MR_minmodel_stats_cnt_save_state);
fprintf(fp, "gen stack frames copied in save_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_save_state_gen_frames,
MR_minmodel_stats_save_state_max_gen_frames,
(float) MR_minmodel_stats_save_state_gen_frames /
MR_minmodel_stats_cnt_save_state);
fprintf(fp, "cut stack frames copied in save_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_save_state_cut_frames,
MR_minmodel_stats_save_state_max_cut_frames,
(float) MR_minmodel_stats_save_state_cut_frames /
MR_minmodel_stats_cnt_save_state);
fprintf(fp, "pneg stack frames copied in save_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_save_state_pneg_frames,
MR_minmodel_stats_save_state_max_pneg_frames,
(float) MR_minmodel_stats_save_state_pneg_frames /
MR_minmodel_stats_cnt_save_state);
}
fprintf(fp, "number of restore_state operations: %d\n",
MR_minmodel_stats_cnt_restore_state);
if (MR_minmodel_stats_cnt_restore_state > 0) {
fprintf(fp, "non stack words copied in restore_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_restore_state_non_words,
MR_minmodel_stats_restore_state_max_non_words,
(float) MR_minmodel_stats_restore_state_non_words /
MR_minmodel_stats_cnt_restore_state);
fprintf(fp, "det stack words copied in restore_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_restore_state_det_words,
MR_minmodel_stats_restore_state_max_det_words,
(float) MR_minmodel_stats_restore_state_det_words /
MR_minmodel_stats_cnt_restore_state);
fprintf(fp, "gen stack frames copied in restore_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_restore_state_gen_frames,
MR_minmodel_stats_restore_state_max_gen_frames,
(float) MR_minmodel_stats_restore_state_gen_frames /
MR_minmodel_stats_cnt_restore_state);
fprintf(fp, "cut stack frames copied in restore_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_restore_state_cut_frames,
MR_minmodel_stats_restore_state_max_cut_frames,
(float) MR_minmodel_stats_restore_state_cut_frames /
MR_minmodel_stats_cnt_restore_state);
fprintf(fp, "pneg stack frames copied in restore_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_restore_state_pneg_frames,
MR_minmodel_stats_restore_state_max_pneg_frames,
(float) MR_minmodel_stats_restore_state_pneg_frames /
MR_minmodel_stats_cnt_restore_state);
}
fprintf(fp, "number of extend_state operations: %d\n",
MR_minmodel_stats_cnt_extend_state);
if (MR_minmodel_stats_cnt_extend_state > 0) {
fprintf(fp, "non stack words copied in extend_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_extend_state_non_words,
MR_minmodel_stats_extend_state_max_non_words,
(float) MR_minmodel_stats_extend_state_non_words /
MR_minmodel_stats_cnt_extend_state);
fprintf(fp, "det stack words copied in extend_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_extend_state_det_words,
MR_minmodel_stats_extend_state_max_det_words,
(float) MR_minmodel_stats_extend_state_det_words /
MR_minmodel_stats_cnt_extend_state);
fprintf(fp, "gen stack frames copied in extend_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_extend_state_gen_frames,
MR_minmodel_stats_extend_state_max_gen_frames,
(float) MR_minmodel_stats_extend_state_gen_frames /
MR_minmodel_stats_cnt_extend_state);
fprintf(fp, "cut stack frames copied in extend_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_extend_state_cut_frames,
MR_minmodel_stats_extend_state_max_cut_frames,
(float) MR_minmodel_stats_extend_state_cut_frames /
MR_minmodel_stats_cnt_extend_state);
fprintf(fp, "pneg stack frames copied in extend_state: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_extend_state_pneg_frames,
MR_minmodel_stats_extend_state_max_pneg_frames,
(float) MR_minmodel_stats_extend_state_pneg_frames /
MR_minmodel_stats_cnt_extend_state);
}
fprintf(fp, "number of prune operations: %d\n",
MR_minmodel_stats_cnt_prune);
if (MR_minmodel_stats_cnt_prune > 0) {
fprintf(fp, "prune loop iterations: ");
fprintf(fp, "%8d, %8d max, %6.2f avg\n",
MR_minmodel_stats_prune_loop,
MR_minmodel_stats_max_prune_loop,
(float) MR_minmodel_stats_prune_loop /
MR_minmodel_stats_cnt_prune);
}
}
#endif
////////////////////////////////////////////////////////////////////////////
// This part of the file provides the utility functions needed for
// suspensions and resumptions of derivations.
#define SUSPEND_LABEL(name) \
MR_label_name(MR_MMSC_SUSPEND_ENTRY, name)
#define COMPLETION_LABEL(name) \
MR_label_name(MR_MMSC_COMPLETION_ENTRY, name)
#define RET_ALL_MULTI_LABEL(name) \
MR_label_name(MR_MMSC_RET_ALL_MULTI_ENTRY, name)
#define RET_ALL_NONDET_LABEL(name) \
MR_label_name(MR_MMSC_RET_ALL_NONDET_ENTRY, name)
// With debugging of tabling code enabled, define function versions
// of saved_to_real_nondet_stack and real_to_saved_nondet_stack, to allow
// programmers to put breakpoints on them. These can execute sanity tests.
// With debugging of tabling code disabled, define macro versions.
#ifdef MR_TABLE_DEBUG
static MR_Word *
saved_to_real_nondet_stack(MR_SavedState *saved_state, MR_Word *saved_ptr)
{
MR_Word *real_ptr;
if (saved_state->MR_ss_non_stack_saved_block <= saved_ptr
&& saved_ptr < saved_state->MR_ss_non_stack_saved_block +
saved_state->MR_ss_non_stack_block_size)
{
MR_Integer offset;
offset = saved_ptr - saved_state->MR_ss_non_stack_saved_block;
real_ptr = saved_state->MR_ss_non_stack_real_start + offset;
#if 0
printf("real start %p, saved block %p, real ptr %p, saved ptr %p\n",
saved_state->MR_ss_non_stack_real_start,
saved_state->MR_ss_non_stack_saved_block,
real_ptr,
saved_ptr);
#endif
return real_ptr;
} else {
MR_fatal_error("saved_to_real_nondet_stack: out of bounds");
}
}
static MR_Word *
real_to_saved_nondet_stack(MR_SavedState *saved_state, MR_Word *real_ptr)
{
MR_Word *saved_ptr;
if (saved_state->MR_ss_non_stack_real_start <= real_ptr
&& real_ptr < saved_state->MR_ss_non_stack_real_start +
saved_state->MR_ss_non_stack_block_size)
{
MR_Integer offset;
offset = real_ptr - saved_state->MR_ss_non_stack_real_start;
saved_ptr = saved_state->MR_ss_non_stack_saved_block + offset;
#if 0
printf("real start %p, saved block %p, real ptr %p, saved ptr %p\n",
saved_state->MR_ss_non_stack_real_start,
saved_state->MR_ss_non_stack_saved_block,
real_ptr,
saved_ptr);
#endif
return saved_ptr;
} else {
MR_fatal_error("real_to_saved_nondet_stack: out of bounds");
}
}
#else
#define saved_to_real_nondet_stack(saved_state, saved_ptr) \
((saved_state)->MR_ss_non_stack_real_start + \
((saved_ptr) - (saved_state)->MR_ss_non_stack_saved_block))
#define real_to_saved_nondet_stack(saved_state, real_ptr) \
((saved_state)->MR_ss_non_stack_saved_block + \
((real_ptr) - (saved_state)->MR_ss_non_stack_real_start))
#endif
// Given pointers to two ordinary frames on the nondet stack, return the
// address of the stack frame of their nearest common ancestor on that stack.
static MR_Word *
nearest_common_ancestor(MR_Word *fr1, MR_Word *fr2)
{
while (fr1 != fr2) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("common ancestor search: ");
MR_print_nondetstackptr(stdout, fr1);
printf(" vs ");
MR_print_nondetstackptr(stdout, fr2);
printf("\n");
}
#endif
if (fr1 > fr2) {
fr1 = MR_succfr_slot(fr1);
} else {
fr2 = MR_succfr_slot(fr2);
}
}
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("the common ancestor is ");
MR_print_nondetstackptr(stdout, fr1);
printf("\n");
}
#endif
return fr1;
}
// Save the current state of the Mercury abstract machine, so that the
// current computation may be suspended for a while, and restored later.
// The generator_fr argument gives the point from which we need to copy the
// nondet and (indirectly) the det stacks. The parts of those stacks below
// the given points will not change between the suspension and the resumption
// of this state, or if they do, the stack segments in the saved state
// will be extended (via extend_consumer_stacks).
static void
save_state(MR_SavedState *saved_state, MR_Word *generator_fr,
const char *who, const char *what, const MR_LabelLayout *top_layout)
{
MR_Word *common_ancestor_fr;
MR_Word *start_non;
MR_Word *start_det;
MR_Integer start_gen;
MR_restore_transient_registers();
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_save_state++;
#endif
if (MR_not_nearest_flag) {
// This can yield incorrect results, as documented in mday_sld.tex
// in papers/tabling2. It is included here only to allow demonstrations
// of *why* this is incorrect.
common_ancestor_fr = generator_fr;
} else {
common_ancestor_fr = nearest_common_ancestor(MR_curfr, generator_fr);
}
start_non = MR_prevfr_slot(common_ancestor_fr) + 1;
start_det = MR_table_detfr_slot(common_ancestor_fr) + 1;
saved_state->MR_ss_succ_ip = MR_succip;
saved_state->MR_ss_s_p = MR_sp;
saved_state->MR_ss_cur_fr = MR_curfr;
saved_state->MR_ss_max_fr = MR_maxfr;
saved_state->MR_ss_common_ancestor_fr = common_ancestor_fr;
saved_state->MR_ss_gen_sp = MR_gen_next;
#ifdef MR_MINIMAL_MODEL_DEBUG
saved_state->MR_ss_top_layout = top_layout;
#endif
// We copy from start_det to MR_sp, both inclusive.
saved_state->MR_ss_det_stack_real_start = start_det;
if (MR_sp >= start_det) {
saved_state->MR_ss_det_stack_block_size = MR_sp + 1 - start_det;
saved_state->MR_ss_det_stack_saved_block = MR_table_allocate_words(
saved_state->MR_ss_det_stack_block_size);
MR_table_copy_words(saved_state->MR_ss_det_stack_saved_block,
saved_state->MR_ss_det_stack_real_start,
saved_state->MR_ss_det_stack_block_size);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_save_state_det_words +=
saved_state->MR_ss_det_stack_block_size;
update_max(MR_minmodel_stats_save_state_max_det_words,
saved_state->MR_ss_det_stack_block_size);
#endif
} else {
saved_state->MR_ss_det_stack_block_size = 0;
saved_state->MR_ss_det_stack_saved_block = NULL;
}
// We copy from start_non to MR_maxfr, both inclusive.
saved_state->MR_ss_non_stack_real_start = start_non;
if (MR_maxfr >= start_non) {
saved_state->MR_ss_non_stack_block_size = MR_maxfr + 1 - start_non;
saved_state->MR_ss_non_stack_saved_block = MR_table_allocate_words(
saved_state->MR_ss_non_stack_block_size);
MR_table_copy_words(saved_state->MR_ss_non_stack_saved_block,
saved_state->MR_ss_non_stack_real_start,
saved_state->MR_ss_non_stack_block_size);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_save_state_non_words +=
saved_state->MR_ss_non_stack_block_size;
update_max(MR_minmodel_stats_save_state_max_non_words,
saved_state->MR_ss_non_stack_block_size);
#endif
start_gen = MR_gen_next;
while (start_gen > 0 &&
MR_gen_stack[start_gen - 1].MR_gen_frame >= start_non)
{
start_gen--;
}
saved_state->MR_ss_gen_stack_real_start = start_gen;
if (MR_gen_next > start_gen) {
saved_state->MR_ss_gen_stack_block_size = MR_gen_next - start_gen;
saved_state->MR_ss_gen_stack_saved_block =
MR_table_allocate_structs(
saved_state->MR_ss_gen_stack_block_size, MR_GenStackFrame);
MR_table_copy_structs(saved_state->MR_ss_gen_stack_saved_block,
&MR_gen_stack[start_gen],
saved_state->MR_ss_gen_stack_block_size, MR_GenStackFrame);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_save_state_gen_frames +=
saved_state->MR_ss_gen_stack_block_size;
update_max(MR_minmodel_stats_save_state_max_gen_frames,
saved_state->MR_ss_gen_stack_block_size);
#endif
} else {
saved_state->MR_ss_gen_stack_block_size = 0;
saved_state->MR_ss_gen_stack_saved_block = NULL;
}
} else {
saved_state->MR_ss_non_stack_block_size = 0;
saved_state->MR_ss_non_stack_saved_block = NULL;
saved_state->MR_ss_gen_stack_block_size = 0;
saved_state->MR_ss_gen_stack_saved_block = NULL;
}
saved_state->MR_ss_cut_next = MR_cut_next;
saved_state->MR_ss_cut_stack_saved_block = MR_table_allocate_structs(
MR_cut_next, MR_CutStackFrame);
MR_table_copy_structs(saved_state->MR_ss_cut_stack_saved_block,
MR_cut_stack, saved_state->MR_ss_cut_next, MR_CutStackFrame);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_save_state_cut_frames += saved_state->MR_ss_cut_next;
update_max(MR_minmodel_stats_save_state_max_cut_frames,
saved_state->MR_ss_cut_next);
#endif
saved_state->MR_ss_pneg_next = MR_pneg_next;
saved_state->MR_ss_pneg_stack_saved_block = MR_table_allocate_structs(
MR_pneg_next, MR_PNegStackFrame);
MR_table_copy_structs(saved_state->MR_ss_pneg_stack_saved_block,
MR_pneg_stack, saved_state->MR_ss_pneg_next, MR_PNegStackFrame);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_save_state_pneg_frames += saved_state->MR_ss_pneg_next;
update_max(MR_minmodel_stats_save_state_max_pneg_frames,
saved_state->MR_ss_pneg_next);
#endif
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("\n%s saves %s stacks\n", who, what);
print_saved_state(stdout, saved_state);
}
if (MR_tablestackdebug) {
MR_dump_nondet_stack_from_layout(stdout, start_non, 0, 0, MR_maxfr,
top_layout, MR_sp, MR_curfr);
}
#endif // MR_TABLE_DEBUG
MR_save_transient_registers();
}
// Restore the state of the Mercury abstract machine from saved_state.
static void
restore_state(MR_SavedState *saved_state, const char *who, const char *what)
{
MR_Integer start_gen;
MR_restore_transient_registers();
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_restore_state++;
#endif
MR_succip_word = (MR_Word) saved_state->MR_ss_succ_ip;
MR_sp_word = (MR_Word) saved_state->MR_ss_s_p;
MR_curfr_word = (MR_Word) saved_state->MR_ss_cur_fr;
MR_maxfr_word = (MR_Word) saved_state->MR_ss_max_fr;
MR_gen_next = saved_state->MR_ss_gen_sp;
MR_table_copy_words(saved_state->MR_ss_non_stack_real_start,
saved_state->MR_ss_non_stack_saved_block,
saved_state->MR_ss_non_stack_block_size);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_restore_state_non_words +=
saved_state->MR_ss_non_stack_block_size;
update_max(MR_minmodel_stats_restore_state_max_non_words,
saved_state->MR_ss_non_stack_block_size);
#endif
MR_table_copy_words(saved_state->MR_ss_det_stack_real_start,
saved_state->MR_ss_det_stack_saved_block,
saved_state->MR_ss_det_stack_block_size);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_restore_state_det_words +=
saved_state->MR_ss_det_stack_block_size;
update_max(MR_minmodel_stats_restore_state_max_det_words,
saved_state->MR_ss_det_stack_block_size);
#endif
start_gen = saved_state->MR_ss_gen_stack_real_start;
MR_table_copy_structs(&MR_gen_stack[start_gen],
saved_state->MR_ss_gen_stack_saved_block,
saved_state->MR_ss_gen_stack_block_size, MR_GenStackFrame);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_restore_state_gen_frames +=
saved_state->MR_ss_gen_stack_block_size;
update_max(MR_minmodel_stats_restore_state_max_gen_frames,
saved_state->MR_ss_gen_stack_block_size);
#endif
MR_cut_next = saved_state->MR_ss_cut_next;
MR_table_copy_structs(MR_cut_stack,
saved_state->MR_ss_cut_stack_saved_block,
saved_state->MR_ss_cut_next, MR_CutStackFrame);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_restore_state_cut_frames += saved_state->MR_ss_cut_next;
update_max(MR_minmodel_stats_restore_state_max_cut_frames,
saved_state->MR_ss_cut_next);
#endif
MR_pneg_next = saved_state->MR_ss_pneg_next;
MR_table_copy_structs(MR_pneg_stack,
saved_state->MR_ss_pneg_stack_saved_block,
saved_state->MR_ss_pneg_next, MR_PNegStackFrame);
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_restore_state_pneg_frames += saved_state->MR_ss_pneg_next;
update_max(MR_minmodel_stats_restore_state_max_pneg_frames,
saved_state->MR_ss_pneg_next);
#endif
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("\n%s restores %s stacks\n", who, what);
print_saved_state(stdout, saved_state);
}
#ifdef MR_MINIMAL_MODEL_DEBUG
if (MR_tablestackdebug) {
MR_dump_nondet_stack_from_layout(stdout,
saved_state->MR_ss_non_stack_real_start, 0, 0, MR_maxfr,
saved_state->MR_ss_top_layout, MR_sp, MR_curfr);
}
#endif // MR_MINIMAL_MODEL_DEBUG
#endif // MR_TABLE_DEBUG
MR_save_transient_registers();
}
// The saved state of a consumer for a subgoal (say subgoal A) includes
// the stack segments between the tops of the stack at the time that
// A's generator was entered and the time that A's consumer was entered.
// When A becomes a follower of another subgoal B, the responsibility for
// scheduling A's consumers passes to B's generator. Since by definition
// B's nondet stack frame is lower in the stack than A's generator's,
// nca(consumer, B) will in general be lower than nca(consumer, A)
// (where nca = nearest common ancestor). The consumer's saved state
// goes down to nca(consumer, A); we need to extend it to nca(consumer, B).
static void
extend_consumer_stacks(MR_Subgoal *leader, MR_Consumer *consumer)
{
MR_Word *arena_block;
MR_Word *arena_start;
MR_Word arena_size;
MR_Word extension_size;
MR_Word *old_common_ancestor_fr;
MR_Word *new_common_ancestor_fr;
MR_SavedState *cons_saved_state;
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_extend_state++;
#endif
cons_saved_state = &consumer->MR_cns_saved_state;
old_common_ancestor_fr = cons_saved_state->MR_ss_common_ancestor_fr;
new_common_ancestor_fr = nearest_common_ancestor(old_common_ancestor_fr,
leader->MR_sg_generator_fr);
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("extending saved state of consumer %s for %s\n",
MR_consumer_addr_name(consumer), MR_subgoal_addr_name(leader));
printf("common ancestors: old ");
MR_print_nondetstackptr(stdout, old_common_ancestor_fr);
printf(", new ");
MR_print_nondetstackptr(stdout, new_common_ancestor_fr);
printf("\nold saved state:\n");
print_saved_state(stdout, cons_saved_state);
}
#endif // MR_TABLE_DEBUG
cons_saved_state->MR_ss_common_ancestor_fr = new_common_ancestor_fr;
arena_start = MR_table_detfr_slot(new_common_ancestor_fr) + 1;
extension_size = cons_saved_state->MR_ss_det_stack_real_start
- arena_start;
if (extension_size > 0) {
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_extend_state_det_words += extension_size;
#endif
arena_size = extension_size
+ cons_saved_state->MR_ss_det_stack_block_size;
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("assert: det arena_start ");
MR_print_detstackptr(stdout, arena_start);
printf(" + %d = ", arena_size);
MR_print_detstackptr(stdout, cons_saved_state->MR_ss_s_p);
printf(" + 1: diff %d\n",
(arena_start + arena_size)
- (cons_saved_state->MR_ss_s_p + 1));
}
#endif
if (arena_size != 0) {
assert(arena_start + arena_size
== cons_saved_state->MR_ss_s_p + 1);
}
arena_block = MR_table_allocate_words(arena_size);
MR_table_copy_words(arena_block, arena_start, extension_size);
MR_table_copy_words(arena_block + extension_size,
cons_saved_state->MR_ss_det_stack_saved_block,
cons_saved_state->MR_ss_det_stack_block_size);
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("extending det stack of consumer %s for %s\n",
MR_consumer_addr_name(consumer), MR_subgoal_addr_name(leader));
printf("start: old %p, new %p\n",
cons_saved_state->MR_ss_det_stack_real_start, arena_start);
printf("size: old %d, new %d\n",
cons_saved_state->MR_ss_det_stack_block_size, arena_size);
printf("block: old %p, new %p\n",
cons_saved_state->MR_ss_det_stack_saved_block, arena_block);
}
#endif // MR_TABLE_DEBUG
cons_saved_state->MR_ss_det_stack_saved_block = arena_block;
cons_saved_state->MR_ss_det_stack_block_size = arena_size;
cons_saved_state->MR_ss_det_stack_real_start = arena_start;
}
arena_start = MR_prevfr_slot(new_common_ancestor_fr) + 1;
extension_size = cons_saved_state->MR_ss_non_stack_real_start
- arena_start;
if (extension_size > 0) {
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_extend_state_non_words += extension_size;
#endif
arena_size = extension_size
+ cons_saved_state->MR_ss_non_stack_block_size;
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("assert: non arena_start ");
MR_print_nondetstackptr(stdout, arena_start);
printf(" + %d = ", arena_size);
MR_print_nondetstackptr(stdout, cons_saved_state->MR_ss_max_fr);
printf(" + 1: diff %d\n",
(arena_start + arena_size)
- (cons_saved_state->MR_ss_max_fr + 1));
}
#endif
if (arena_size != 0) {
assert(arena_start + arena_size
== cons_saved_state->MR_ss_max_fr + 1);
}
arena_block = MR_table_allocate_words(arena_size);
MR_table_copy_words(arena_block, arena_start, extension_size);
MR_table_copy_words(arena_block + extension_size,
cons_saved_state->MR_ss_non_stack_saved_block,
cons_saved_state->MR_ss_non_stack_block_size);
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("extending non stack of suspension %s for %s\n",
MR_consumer_addr_name(consumer), MR_subgoal_addr_name(leader));
printf("start: old %p, new %p\n",
cons_saved_state->MR_ss_non_stack_real_start, arena_start);
printf("size: old %d, new %d\n",
cons_saved_state->MR_ss_non_stack_block_size, arena_size);
printf("block: old %p, new %p\n",
cons_saved_state->MR_ss_non_stack_saved_block, arena_block);
}
#endif // MR_TABLE_DEBUG
cons_saved_state->MR_ss_non_stack_saved_block = arena_block;
cons_saved_state->MR_ss_non_stack_block_size = arena_size;
cons_saved_state->MR_ss_non_stack_real_start = arena_start;
prune_right_branches(cons_saved_state, arena_size - extension_size,
NULL);
}
#ifdef MR_TABLE_DEBUG
if (MR_tablestackdebug) {
printf("\nfinished extending saved consumer stacks\n");
print_saved_state(stdout, cons_saved_state);
}
#endif // MR_TABLE_DEBUG
}
MR_declare_entry(MR_table_mm_commit);
static void
prune_right_branches(MR_SavedState *saved_state, MR_Integer already_pruned,
MR_Subgoal *subgoal)
{
MR_Word *saved_fr;
MR_Word *saved_stop_fr;
MR_Word *saved_top_fr;
MR_Word *saved_next_fr;
MR_Word *saved_redoip_addr;
MR_Word *real_fr;
MR_Word *real_main_branch_fr;
MR_Word frame_size;
MR_Integer cur_gen;
MR_Integer cur_cut;
MR_Integer cur_pneg;
MR_bool ordinary;
MR_bool generator_is_at_bottom;
#ifdef MR_TABLE_STATISTICS
int prune_loop_cnt = 0;
#endif
if (already_pruned > 0) {
generator_is_at_bottom = MR_TRUE;
} else {
generator_is_at_bottom = MR_FALSE;
}
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_prune++;
#endif
#ifdef MR_TABLE_DEBUG
if (MR_tablestackdebug) {
printf("\nbefore pruning nondet stack, already pruned %d\n",
already_pruned);
print_saved_state(stdout, saved_state);
}
#endif // MR_TABLE_DEBUG
saved_stop_fr = saved_state->MR_ss_non_stack_saved_block - 1;
saved_top_fr = saved_state->MR_ss_non_stack_saved_block +
saved_state->MR_ss_non_stack_block_size - 1;
saved_fr = saved_top_fr;
real_main_branch_fr =
saved_to_real_nondet_stack(saved_state, saved_top_fr);
cur_gen = MR_gen_next - 1;
cur_cut = MR_cut_next - 1;
cur_pneg = MR_pneg_next - 1;
while (saved_fr > saved_stop_fr) {
#ifdef MR_TABLE_STATISTICS
prune_loop_cnt++;
#endif
real_fr = saved_to_real_nondet_stack(saved_state, saved_fr);
frame_size = real_fr - MR_prevfr_slot(saved_fr);
saved_next_fr = saved_fr - frame_size;
if (frame_size >= MR_NONDET_FIXED_SIZE) {
ordinary = MR_TRUE;
} else {
ordinary = MR_FALSE;
}
#ifdef MR_TABLE_DEBUG
if (MR_tablestackdebug) {
printf("considering %s frame ",
(ordinary? "ordinary" : "temp"));
MR_print_nondetstackptr(stdout,
saved_to_real_nondet_stack(saved_state, saved_fr));
printf(" with redoip slot at ");
MR_print_nondetstackptr(stdout,
saved_to_real_nondet_stack(saved_state,
MR_redoip_addr(saved_fr)));
printf("\n");
}
#endif
if (already_pruned > 0) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("already pruned %d -> %d\n",
already_pruned, already_pruned - frame_size);
}
#endif // MR_TABLE_DEBUG
already_pruned -= frame_size;
if (real_fr == real_main_branch_fr && ordinary) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("next main sequence frame ");
MR_print_nondetstackptr(stdout, MR_succfr_slot(saved_fr));
}
#endif // MR_TABLE_DEBUG
real_main_branch_fr = MR_succfr_slot(saved_fr);
}
} else if (MR_redofr_slot(saved_fr) != real_main_branch_fr) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
int num_frame_vars;
int i;
printf("thrashing non-main-branch frame\n");
// The saved copies of the stack frames that aren't on
// the main branch shouldn't be used after the state is
// restored. The vandalism below is intended to test this.
num_frame_vars = frame_size - MR_NONDET_FIXED_SIZE;
for (i = 1; i <= num_frame_vars; i++) {
*MR_based_framevar_addr(saved_fr, i) = -1;
}
}
#endif // MR_TABLE_DEBUG
// do nothing ;
} else if (generator_is_at_bottom && saved_next_fr == saved_stop_fr) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("setting redoip to schedule completion in bottom frame ");
MR_print_nondetstackptr(stdout,
saved_to_real_nondet_stack(saved_state, saved_fr));
printf(" (in saved copy)\n");
}
#endif // MR_TABLE_DEBUG
*MR_redoip_addr(saved_fr) =
(MR_Word) MR_ENTRY(MR_MMSC_COMPLETION_ENTRY);
} else if (!generator_is_at_bottom &&
real_fr == MR_gen_stack[cur_gen].MR_gen_frame)
{
assert(subgoal != NULL);
assert(ordinary);
if (MR_gen_stack[cur_gen].MR_gen_subgoal == subgoal) {
// This is the nondet stack frame of the generator
// corresponding to the consumer whose saved state
// we are pickling.
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("setting redoip to schedule completion in frame ");
MR_print_nondetstackptr(stdout,
saved_to_real_nondet_stack(saved_state, saved_fr));
printf(" (in saved copy)\n");
}
#endif // MR_TABLE_DEBUG
*MR_redoip_addr(saved_fr) =
(MR_Word) MR_ENTRY(MR_MMSC_COMPLETION_ENTRY);
} else {
// This is the nondet stack frame of some other generator.
// reenable XXX
assert(MR_prevfr_slot(saved_fr) !=
saved_to_real_nondet_stack(saved_state, saved_stop_fr));
#ifdef MR_TABLE_DEBUG
if (MR_tablestackdebug) {
printf("clobbering redoip of follower frame at ");
MR_print_nondetstackptr(stdout, real_fr);
printf(" (in saved copy)\n");
}
#endif // MR_TABLE_DEBUG
*MR_redoip_addr(saved_fr) = (MR_Word) MR_ENTRY(MR_do_fail);
MR_save_transient_registers();
make_subgoal_follow_leader(
MR_gen_stack[cur_gen].MR_gen_subgoal, subgoal);
MR_restore_transient_registers();
}
cur_gen--;
} else if (generator_is_at_bottom && cur_cut > 0
&& real_fr == MR_cut_stack[cur_cut].MR_cut_frame)
{
assert(! ordinary);
#ifdef MR_TABLE_DEBUG
if (MR_tablestackdebug) {
printf("committing redoip of frame at ");
MR_print_nondetstackptr(stdout, real_fr);
printf(" (in saved copy)\n");
}
#endif // MR_TABLE_DEBUG
*MR_redoip_addr(saved_fr) = (MR_Word)
MR_ENTRY(MR_table_mm_commit);
cur_cut--;
} else {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("clobbering redoip of frame at ");
MR_print_nondetstackptr(stdout, real_fr);
printf(" (in saved copy)\n");
}
*MR_redoip_addr(saved_fr) = (MR_Word) MR_ENTRY(MR_do_fail);
#endif // MR_TABLE_DEBUG
}
saved_fr -= frame_size;
real_fr -= frame_size;
}
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_prune_loop += prune_loop_cnt;
update_max(MR_minmodel_stats_max_prune_loop, prune_loop_cnt);
#endif
}
// When we discover that two subgoals depend on each other, neither can be
// completed alone. We therefore pass responsibility for completing all
// the subgoals in an SCC to the subgoal whose nondet stack frame is
// lowest in the nondet stack.
static void
make_subgoal_follow_leader(MR_Subgoal *this_follower, MR_Subgoal *leader)
{
MR_SubgoalList sub_follower;
MR_ConsumerList suspend_list;
MR_restore_transient_registers();
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_make_subgoal_follow_leader++;
#endif
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("\nmaking %s follow %s\n",
MR_subgoal_addr_name(this_follower), MR_subgoal_addr_name(leader));
}
#endif // MR_TABLE_DEBUG
for (sub_follower = this_follower->MR_sg_followers;
sub_follower != NULL; sub_follower = sub_follower->MR_sl_next)
{
for (suspend_list = sub_follower->MR_sl_item->MR_sg_consumer_list;
suspend_list != NULL;
suspend_list = suspend_list->MR_cl_next)
{
MR_save_transient_registers();
extend_consumer_stacks(leader, suspend_list->MR_cl_item);
MR_restore_transient_registers();
}
sub_follower->MR_sl_item->MR_sg_leader = leader;
}
// XXX Extend saved state of this_follower.
this_follower->MR_sg_leader = leader;
*(leader->MR_sg_followers_tail) = this_follower->MR_sg_followers;
this_follower->MR_sg_followers = NULL;
MR_save_transient_registers();
}
static void
print_saved_state(FILE *fp, MR_SavedState *saved_state)
{
fprintf(fp, "saved state parameters:\n");
fprintf(fp, "succip:\t");
MR_printlabel(fp, saved_state->MR_ss_succ_ip);
fprintf(fp, "sp:\t");
MR_print_detstackptr(fp, saved_state->MR_ss_s_p);
fprintf(fp, "\ncurfr:\t");
MR_print_nondetstackptr(fp, saved_state->MR_ss_cur_fr);
fprintf(fp, "\nmaxfr:\t");
MR_print_nondetstackptr(fp, saved_state->MR_ss_max_fr);
fprintf(fp, "\n");
fprintf(fp, "slots saved: %" MR_INTEGER_LENGTH_MODIFIER "d non,",
saved_state->MR_ss_non_stack_block_size);
fprintf(fp, " %" MR_INTEGER_LENGTH_MODIFIER "d det,",
saved_state->MR_ss_det_stack_block_size);
fprintf(fp, " %" MR_INTEGER_LENGTH_MODIFIER "d generator,",
saved_state->MR_ss_gen_stack_block_size);
fprintf(fp, " %" MR_INTEGER_LENGTH_MODIFIER "d cut,",
saved_state->MR_ss_cut_next);
fprintf(fp, " %" MR_INTEGER_LENGTH_MODIFIER "d pneg\n",
saved_state->MR_ss_pneg_next);
if (saved_state->MR_ss_non_stack_block_size > 0) {
fprintf(fp, "non region from ");
MR_print_nondetstackptr(fp, saved_state->MR_ss_non_stack_real_start);
fprintf(fp, " to ");
MR_print_nondetstackptr(fp, saved_state->MR_ss_non_stack_real_start +
saved_state->MR_ss_non_stack_block_size - 1);
fprintf(fp, " (both inclusive)\n");
}
#ifdef MR_TABLE_SEGMENT_DEBUG
if (saved_state->MR_ss_non_stack_block_size > 0) {
fprintf(fp, "stored at %p to %p (both inclusive)\n",
saved_state->MR_ss_non_stack_saved_block,
saved_state->MR_ss_non_stack_saved_block +
saved_state->MR_ss_non_stack_block_size - 1);
fprint_stack_segment(fp, saved_state->MR_ss_non_stack_saved_block,
saved_state->MR_ss_non_stack_block_size);
}
#endif // MR_TABLE_SEGMENT_DEBUG
if (saved_state->MR_ss_det_stack_block_size > 0) {
fprintf(fp, "det region from ");
MR_print_detstackptr(fp, saved_state->MR_ss_det_stack_real_start);
fprintf(fp, " to ");
MR_print_detstackptr(fp, saved_state->MR_ss_det_stack_real_start +
saved_state->MR_ss_det_stack_block_size - 1);
fprintf(fp, " (both inclusive)\n");
}
#ifdef MR_TABLE_SEGMENT_DEBUG
if (saved_state->MR_ss_det_stack_block_size > 0) {
fprintf(fp, "stored at %p to %p (both inclusive)\n",
saved_state->MR_ss_det_stack_saved_block,
saved_state->MR_ss_det_stack_saved_block +
saved_state->MR_ss_det_stack_block_size - 1);
print_stack_segment(fp, saved_state->MR_ss_det_stack_saved_block,
saved_state->MR_ss_det_stack_block_size);
}
#endif // MR_TABLE_SEGMENT_DEBUG
if (saved_state->MR_ss_gen_stack_block_size > 0) {
fprintf(fp, "gen region from %" MR_INTEGER_LENGTH_MODIFIER "d",
saved_state->MR_ss_gen_stack_real_start);
fprintf(fp, " to %" MR_INTEGER_LENGTH_MODIFIER "d",
saved_state->MR_ss_gen_stack_real_start +
saved_state->MR_ss_gen_stack_block_size - 1);
fprintf(fp, " (both inclusive)\n");
}
#ifdef MR_TABLE_SEGMENT_DEBUG
if (saved_state->MR_ss_gen_stack_block_size > 0) {
fprintf(fp, "stored at %p to %p (both inclusive)\n",
saved_state->MR_ss_gen_stack_saved_block,
saved_state->MR_ss_gen_stack_saved_block +
saved_state->MR_ss_gen_stack_block_size - 1);
MR_print_any_gen_stack(fp, saved_state->MR_ss_gen_stack_block_size,
saved_state->MR_ss_gen_stack_saved_block);
}
#endif // MR_TABLE_SEGMENT_DEBUG
MR_print_any_cut_stack(fp, saved_state->MR_ss_cut_next,
saved_state->MR_ss_cut_stack_saved_block);
MR_print_any_pneg_stack(fp, saved_state->MR_ss_pneg_next,
saved_state->MR_ss_pneg_stack_saved_block);
fprintf(fp, "\n");
}
static void
print_stack_segment(FILE *fp, MR_Word *segment, MR_Integer size)
{
int i;
for (i = 0; i < size; i++) {
fprintf(fp, "%2d %p: %ld (%lx)\n", i, &segment[i],
(long) segment[i], (long) segment[i]);
}
}
#endif // MR_USE_MINIMAL_MODEL_STACK_COPY
////////////////////////////////////////////////////////////////////////////
// This part of the file implements the suspension and resumption of
// derivations.
//
// We need to define stubs for the predicates which are marked as `:- external'
// in table_builtin.m, even if MR_USE_MINIMAL_MODEL_STACK_COPY is not enabled,
// because in profiling grades the code generated for table_builtin.m will
// take their address to store in the label table.
//
// We provide three definitions for these procedures: one for high level
// code (which is incompatible with minimal model tabling), and two for low
// level code. The first of the latter two is for grades without minimal model
// tabling, the second is for grades with minimal model tabling.
#ifdef MR_HIGHLEVEL_CODE
void MR_CALL
mercury__table_builtin__table_mm_completion_1_p_0(
MR_C_Pointer subgoal_table_node, MR_C_Pointer *answer_block,
MR_Cont cont, void *cont_env_ptr)
{
MR_fatal_error("sorry, not implemented: "
"minimal model tabling with --high-level-code");
}
void MR_CALL
mercury__table_builtin__table_mm_suspend_consumer_2_p_0(
MR_C_Pointer subgoal_table_node)
{
MR_fatal_error("sorry, not implemented: "
"minimal model tabling with --high-level-code");
}
void MR_CALL
mercury__table_builtin__table_mm_return_all_nondet_2_p_0(
MR_C_Pointer answer_list, MR_C_Pointer answer_block)
{
MR_fatal_error("sorry, not implemented: "
"minimal model tabling with --high-level-code");
}
void MR_CALL
mercury__table_builtin__table_mm_return_all_multi_2_p_0(
MR_C_Pointer answer_list, MR_C_Pointer answer_block)
{
MR_fatal_error("sorry, not implemented: "
"minimal model tabling with --high-level-code");
}
#else // ! MR_HIGHLEVEL_CODE
MR_define_extern_entry(MR_MMSC_SUSPEND_ENTRY);
MR_define_extern_entry(MR_MMSC_COMPLETION_ENTRY);
MR_define_extern_entry(MR_MMSC_RET_ALL_NONDET_ENTRY);
MR_define_extern_entry(MR_MMSC_RET_ALL_MULTI_ENTRY);
MR_EXTERN_USER_PROC_ID_PROC_LAYOUT(MR_DETISM_NON, 0, -1,
MR_PREDICATE, table_builtin, table_mm_suspend_consumer, 2, 0);
MR_EXTERN_USER_PROC_ID_PROC_LAYOUT(MR_DETISM_NON, 0, -1,
MR_PREDICATE, table_builtin, table_mm_completion, 1, 0);
MR_EXTERN_USER_PROC_ID_PROC_LAYOUT(MR_DETISM_NON, 0, -1,
MR_PREDICATE, table_builtin, table_mm_return_all_nondet, 2, 0);
MR_EXTERN_USER_PROC_ID_PROC_LAYOUT(MR_DETISM_NON, 0, -1,
MR_PREDICATE, table_builtin, table_mm_return_all_multi, 2, 0);
#ifndef MR_USE_MINIMAL_MODEL_STACK_COPY
MR_BEGIN_MODULE(mmsc_module)
MR_init_entry_sl(MR_MMSC_SUSPEND_ENTRY);
MR_init_entry_sl(MR_MMSC_COMPLETION_ENTRY);
MR_init_entry_sl(MR_MMSC_RET_ALL_NONDET_ENTRY);
MR_init_entry_sl(MR_MMSC_RET_ALL_MULTI_ENTRY);
MR_INIT_PROC_LAYOUT_ADDR(MR_MMSC_SUSPEND_ENTRY);
MR_INIT_PROC_LAYOUT_ADDR(MR_MMSC_COMPLETION_ENTRY);
MR_INIT_PROC_LAYOUT_ADDR(MR_MMSC_RET_ALL_NONDET_ENTRY);
MR_INIT_PROC_LAYOUT_ADDR(MR_MMSC_RET_ALL_MULTI_ENTRY);
MR_BEGIN_CODE
MR_define_entry(MR_MMSC_SUSPEND_ENTRY);
MR_fatal_error("call to table_mm_suspend_consumer/2 in a grade "
"without stack copy minimal model tabling");
MR_define_entry(MR_MMSC_COMPLETION_ENTRY);
MR_fatal_error("call to table_mm_completion/1 in a grade "
"without stack copy minimal model tabling");
MR_define_entry(MR_MMSC_RET_ALL_NONDET_ENTRY);
MR_fatal_error("call to table_mm_return_all_nondet/2 in a grade "
"without stack copy minimal model tabling");
MR_define_entry(MR_MMSC_RET_ALL_MULTI_ENTRY);
MR_fatal_error("call to table_mm_return_all_multi/2 in a grade "
"without stack copy minimal model tabling");
MR_END_MODULE
#else // MR_USE_MINIMAL_MODEL_STACK_COPY
MR_Subgoal *MR_cur_leader;
MR_define_extern_entry(MR_table_mm_commit);
MR_declare_entry(MR_do_trace_redo_fail);
MR_declare_entry(MR_table_mm_commit);
MR_declare_label(SUSPEND_LABEL(Call));
MR_declare_label(COMPLETION_LABEL(StartCompletionOp));
MR_declare_label(COMPLETION_LABEL(LoopOverSubgoals));
MR_declare_label(COMPLETION_LABEL(LoopOverSuspensions));
MR_declare_label(COMPLETION_LABEL(ReturnAnswer));
MR_declare_label(COMPLETION_LABEL(RedoPoint));
MR_declare_label(COMPLETION_LABEL(RestartPoint));
MR_declare_label(COMPLETION_LABEL(FixPointCheck));
MR_declare_label(COMPLETION_LABEL(ReachedFixpoint));
MR_declare_label(RET_ALL_NONDET_LABEL(Next));
MR_declare_label(RET_ALL_MULTI_LABEL(Next));
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_suspend_consumer, 2, 0,
Call);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_completion, 1, 0,
LoopOverSubgoals);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_completion, 1, 0,
StartCompletionOp);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_completion, 1, 0,
LoopOverSuspensions);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_completion, 1, 0,
ReturnAnswer);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_completion, 1, 0,
RedoPoint);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_completion, 1, 0,
RestartPoint);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_completion, 1, 0,
FixPointCheck);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_completion, 1, 0,
ReachedFixpoint);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_return_all_nondet, 2, 0,
Next);
MR_MAKE_USER_INTERNAL_LAYOUT(table_builtin, table_mm_return_all_multi, 2, 0,
Next);
MR_BEGIN_MODULE(mmsc_module)
MR_init_entry_sl(MR_MMSC_SUSPEND_ENTRY);
MR_INIT_PROC_LAYOUT_ADDR(MR_MMSC_SUSPEND_ENTRY);
MR_init_label_sl(SUSPEND_LABEL(Call));
MR_init_entry_sl(MR_MMSC_COMPLETION_ENTRY);
MR_INIT_PROC_LAYOUT_ADDR(MR_MMSC_COMPLETION_ENTRY);
MR_init_label_sl(COMPLETION_LABEL(StartCompletionOp));
MR_init_label_sl(COMPLETION_LABEL(LoopOverSubgoals));
MR_init_label_sl(COMPLETION_LABEL(LoopOverSuspensions));
MR_init_label_sl(COMPLETION_LABEL(ReturnAnswer));
MR_init_label_sl(COMPLETION_LABEL(RedoPoint));
MR_init_label_sl(COMPLETION_LABEL(RestartPoint));
MR_init_label_sl(COMPLETION_LABEL(FixPointCheck));
MR_init_label_sl(COMPLETION_LABEL(ReachedFixpoint));
MR_init_entry_an(MR_table_mm_commit);
MR_init_entry_sl(MR_MMSC_RET_ALL_NONDET_ENTRY);
MR_INIT_PROC_LAYOUT_ADDR(MR_MMSC_RET_ALL_NONDET_ENTRY);
MR_init_label_sl(RET_ALL_NONDET_LABEL(Next));
MR_init_entry_sl(MR_MMSC_RET_ALL_MULTI_ENTRY);
MR_INIT_PROC_LAYOUT_ADDR(MR_MMSC_RET_ALL_MULTI_ENTRY);
MR_init_label_sl(RET_ALL_MULTI_LABEL(Next));
MR_BEGIN_CODE
MR_define_entry(MR_MMSC_SUSPEND_ENTRY);
// The suspend procedure saves the state of the Mercury runtime so that
// it may be used in the table_mm_completion procedure below to return
// answers through this saved state. table_mm_suspend_consumer is
// declared as nondet but the code below is obviously of detism failure;
// the reason for this is quite simple. Normally when a nondet proc is
// called it will first return all of its answers and then fail. In the
// case of calls to this procedure this is reversed: first the call will
// fail then later on, when the answers are found, answers will be
// returned. It is also important to note that the answers are returned
// not from the procedure that was originally called
// (table_mm_suspend_consumer) but from the procedure table_mm_completion.
// So essentially what is below is the code to do the initial fail;
// the code to return the answers is in table_mm_completion.
// This frame is not used in table_mm_suspend_consumer, but it is copied
// to the suspend list as part of the saved nondet stack fragment,
// and it *will* be used when table_mm_completion copies back the
// nondet stack fragment. The framevar slot is for use by
// table_mm_completion.
MR_mkframe(MR_STRINGIFY(MR_MMSC_SUSPEND_ENTRY), 1, MR_ENTRY(MR_do_fail));
MR_define_label(SUSPEND_LABEL(Call));
{
MR_SubgoalPtr subgoal;
MR_Consumer *consumer;
MR_ConsumerList listnode;
MR_Integer cur_gen;
MR_Integer cur_cut;
MR_Integer cur_pneg;
MR_Word *fr;
MR_Word *prev_fr;
MR_Word *stop_addr;
MR_Word offset;
MR_Word *clobber_addr;
MR_Word *common_ancestor;
subgoal = (MR_SubgoalPtr) MR_r1;
consumer = MR_table_allocate_struct(MR_Consumer);
consumer->MR_cns_remaining_answer_list_ptr = &subgoal->MR_sg_answer_list;
consumer->MR_cns_subgoal = subgoal;
consumer->MR_cns_num_returned_answers = 0;
#ifdef MR_TABLE_DEBUG
MR_enter_consumer_debug(consumer);
if (MR_tabledebug) {
printf("setting up consumer %s\n", MR_consumer_addr_name(consumer));
}
#endif
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_suspend++;
#endif
MR_save_transient_registers();
save_state(&(consumer->MR_cns_saved_state), subgoal->MR_sg_generator_fr,
"suspension", "consumer",
(const MR_LabelLayout *) &MR_LAYOUT_FROM_LABEL(SUSPEND_LABEL(Call)));
MR_restore_transient_registers();
MR_register_suspension(consumer);
common_ancestor = consumer->MR_cns_saved_state.MR_ss_common_ancestor_fr;
if (common_ancestor < subgoal->MR_sg_deepest_nca_fr) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("resetting deepest nca for subgoal %s from ",
MR_subgoal_addr_name(subgoal));
MR_print_nondetstackptr(stdout, subgoal->MR_sg_deepest_nca_fr);
printf(" to ");
MR_print_nondetstackptr(stdout, common_ancestor);
printf("\n");
}
#endif
subgoal->MR_sg_deepest_nca_fr = common_ancestor;
}
prune_right_branches(&consumer->MR_cns_saved_state, 0, subgoal);
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("adding consumer %s to table %s",
MR_consumer_addr_name(consumer),
MR_subgoal_addr_name(subgoal));
printf("\n\tat slot %p\n", subgoal->MR_sg_consumer_list_tail);
}
#endif // MR_TABLE_DEBUG
assert(*(subgoal->MR_sg_consumer_list_tail) == NULL);
listnode = MR_table_allocate_struct(MR_ConsumerListNode);
*(subgoal->MR_sg_consumer_list_tail) = listnode;
subgoal->MR_sg_consumer_list_tail = &(listnode->MR_cl_next);
listnode->MR_cl_item = consumer;
listnode->MR_cl_next = NULL;
}
MR_fail();
MR_define_entry(MR_MMSC_COMPLETION_ENTRY);
// The completion procedure restores answers to suspended consumers.
// It works by restoring the consumer state saved by the consumer's call
// to table_mm_suspend_consumer. By restoring such states and then
// returning answers, table_mm_completion is essentially returning answers
// out of the call to table_mm_suspend_consumer, not out of the call to
// table_mm_completion.
//
// The code is arranged as a three level iteration to a fixpoint. The
// three levels are: iterating over all subgoals in a connected component,
// iterating over all consumers of each of those subgoals, and iterating
// over all the answers to be returned to each of those consumers.
// Note that returning an answer could lead to further answers for
// any of the subgoals in the connected component; it can even lead
// to the expansion of the component (i.e. the addition of more subgoals
// to it).
MR_cur_leader = MR_top_generator_table();
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_completion++;
#endif
MR_define_label(COMPLETION_LABEL(StartCompletionOp));
{
MR_CompletionInfo *completion_info;
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_completion_start_completion_op++;
#endif
if (MR_cur_leader->MR_sg_leader != NULL) {
// The predicate that called table_mm_completion is not the leader
// of its component. We will leave all answers to be returned
// by the leader.
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("non-leader table_mm_completion fails\n");
}
#endif // MR_TABLE_DEBUG
(void) MR_pop_generator();
MR_redo();
}
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("table_mm_completion enter: current leader is %s\n",
MR_subgoal_addr_name(MR_cur_leader));
}
#endif // MR_TABLE_DEBUG
if (MR_cur_leader->MR_sg_completion_info != NULL) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("using existing completion info %p\n",
MR_cur_leader->MR_sg_completion_info);
}
completion_info = MR_cur_leader->MR_sg_completion_info;
#endif // MR_TABLE_DEBUG
} else {
completion_info = MR_TABLE_NEW(MR_CompletionInfo);
MR_cur_leader->MR_sg_completion_info = completion_info;
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("completion info succip ");
MR_printlabel(stdout, MR_succip);
}
#endif // MR_TABLE_DEBUG
// XXX
//
// We should compute, for all followers, the common ancestor
// of the follower and this generator, and save to the deepest
// common ancestor.
//
// We should special case the situation where there are no answers
// we have not yet returned to consumers.
MR_save_transient_registers();
save_state(&(completion_info->MR_ri_leader_state),
MR_cur_leader->MR_sg_generator_fr, "resumption", "generator",
NULL);
MR_restore_transient_registers();
completion_info->MR_ri_subgoal_list = MR_cur_leader->MR_sg_followers;
completion_info->MR_ri_cur_subgoal = NULL;
completion_info->MR_ri_consumer_list = NULL;
completion_info->MR_ri_cur_consumer = NULL;
completion_info->MR_ri_saved_succip = MR_succip; /*NEW*/
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("creating new completion info %p\n", completion_info);
}
#endif // MR_TABLE_DEBUG
}
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
MR_SubgoalList subgoal_list;
printf("the list of subgoals to iterate over:");
for (subgoal_list = completion_info->MR_ri_subgoal_list;
subgoal_list != NULL;
subgoal_list = subgoal_list->MR_sl_next)
{
printf(" %s", MR_subgoal_addr_name(subgoal_list->MR_sl_item));
}
printf("\n");
}
#endif // MR_TABLE_DEBUG
// Fall through to LoopOverSubgoals.
}
// For each of the subgoals on our list of followers.
MR_define_label(COMPLETION_LABEL(LoopOverSubgoals));
{
MR_CompletionInfo *completion_info;
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_completion_loop_over_subgoals++;
#endif
completion_info = MR_cur_leader->MR_sg_completion_info;
if (completion_info->MR_ri_subgoal_list == NULL) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("no more subgoals in the followers list\n");
}
#endif // MR_TABLE_DEBUG
MR_GOTO_LABEL(COMPLETION_LABEL(FixPointCheck));
}
completion_info->MR_ri_cur_subgoal =
completion_info->MR_ri_subgoal_list->MR_sl_item;
completion_info->MR_ri_subgoal_list =
completion_info->MR_ri_subgoal_list->MR_sl_next;
completion_info->MR_ri_consumer_list =
completion_info->MR_ri_cur_subgoal->MR_sg_consumer_list;
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
MR_ConsumerList consumer_list;
printf("returning answers to the consumers of subgoal %s\n",
MR_subgoal_addr_name(completion_info->MR_ri_cur_subgoal));
printf("the list of consumers to iterate over:");
for (consumer_list = completion_info->MR_ri_consumer_list;
consumer_list != NULL;
consumer_list = consumer_list->MR_cl_next)
{
printf(" %s", MR_consumer_addr_name(consumer_list->MR_cl_item));
}
printf("\n");
}
#endif // MR_TABLE_DEBUG
// Fall through to LoopOverSuspensions.
}
// For each of the suspended nodes for cur_subgoal.
MR_define_label(COMPLETION_LABEL(LoopOverSuspensions));
{
MR_CompletionInfo *completion_info;
MR_AnswerList cur_consumer_answer_list;
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_completion_loop_over_suspensions++;
#endif
completion_info = MR_cur_leader->MR_sg_completion_info;
if (completion_info->MR_ri_consumer_list == NULL) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("no more consumers for current subgoal\n");
}
#endif // MR_TABLE_DEBUG
MR_GOTO_LABEL(COMPLETION_LABEL(LoopOverSubgoals));
}
completion_info->MR_ri_cur_consumer =
completion_info->MR_ri_consumer_list->MR_cl_item;
completion_info->MR_ri_consumer_list =
completion_info->MR_ri_consumer_list->MR_cl_next;
cur_consumer_answer_list =
*(completion_info->MR_ri_cur_consumer->
MR_cns_remaining_answer_list_ptr);
if (cur_consumer_answer_list == NULL) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("no first answer for this consumers\n");
}
#endif // MR_TABLE_DEBUG
MR_GOTO_LABEL(COMPLETION_LABEL(LoopOverSuspensions));
}
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("resuming consumer %s from table %s\n",
MR_consumer_addr_name(completion_info->MR_ri_cur_consumer),
MR_subgoal_addr_name(completion_info->MR_ri_cur_subgoal));
}
#endif // MR_TABLE_DEBUG
MR_save_transient_registers();
restore_state(&(completion_info->MR_ri_cur_consumer->MR_cns_saved_state),
"resumption", "consumer");
MR_restore_transient_registers();
// Check that there is room for exactly one framevar.
assert((MR_maxfr - MR_prevfr_slot(MR_maxfr)) ==
(MR_NONDET_FIXED_SIZE + 1));
// We set up the stack frame of the suspend predicate so that a redo into
// the call to suspend from the consumer will return the next answer
// MR_gen_next = completion_info->MR_ri_leader_state.MR_ss_gen_next; BUG?
MR_redoip_slot_word(MR_maxfr) = (MR_Word)
MR_LABEL(COMPLETION_LABEL(RedoPoint));
MR_redofr_slot_word(MR_maxfr) = MR_maxfr_word;
MR_based_framevar(MR_maxfr, 1) = (MR_Word) MR_cur_leader;
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
MR_AnswerList answer_list;
MR_Consumer *consumer;
consumer = completion_info->MR_ri_cur_consumer;
printf("returning answers to consumer %s\n",
MR_consumer_addr_name(consumer));
#ifdef MR_MINIMAL_MODEL_DEBUG
printf("the list of answers to return:");
for (answer_list = cur_consumer_answer_list;
answer_list != NULL;
answer_list = answer_list->MR_aln_next_answer)
{
MR_print_answerblock(stdout,
consumer->MR_cns_subgoal->MR_sg_proc_layout,
answer_list->MR_aln_answer_block);
}
#endif // MR_MINIMAL_MODEL_DEBUG
printf("\n");
}
#endif // MR_TABLE_DEBUG
// Fall through to ReturnAnswer.
}
MR_define_label(COMPLETION_LABEL(ReturnAnswer));
{
MR_CompletionInfo *completion_info;
MR_Consumer *consumer;
MR_AnswerList answer_list;
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_completion_return_answer++;
#endif
completion_info = MR_cur_leader->MR_sg_completion_info;
consumer = completion_info->MR_ri_cur_consumer;
answer_list = *consumer->MR_cns_remaining_answer_list_ptr;
// Return the next answer in the answer_list of the current consumer
// to the current consumer. Since we have already restored the context
// of the suspended consumer before we returned the first answer,
// we don't need to restore it again, since will not have changed
// in the meantime.
//
// XXX We need to prove that assertion.
MR_r1 = (MR_Word) answer_list->MR_aln_answer_block;
consumer->MR_cns_remaining_answer_list_ptr =
&(answer_list->MR_aln_next_answer);
consumer->MR_cns_num_returned_answers++;
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("returning answer to consumer %s\n",
MR_consumer_addr_name(completion_info->MR_ri_cur_consumer));
#ifdef MR_MINIMAL_MODEL_DEBUG
MR_print_answerblock(stdout, completion_info->MR_ri_cur_consumer->
MR_cns_subgoal->MR_sg_proc_layout,
answer_list->MR_aln_answer_block);
#endif
}
#endif // MR_TABLE_DEBUG
// Return the answer. Since we just restored the state of the
// computation that existed when suspend was called, the code
// that we return to is the code following the call to suspend.
MR_succeed();
}
MR_define_label(COMPLETION_LABEL(RedoPoint));
MR_update_prof_current_proc(MR_LABEL(MR_MMSC_COMPLETION_ENTRY));
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_completion_redo_point++;
#endif
// This is where the current consumer suspension will go on
// backtracking when it wants the next solution. If there is a solution
// we haven't returned to this consumer yet, we do so, otherwise we
// remember how many answers we have returned to this consumer so far
// and move on to the next suspended consumer of the current subgoal.
MR_cur_leader = (MR_Subgoal *) MR_based_framevar(MR_maxfr, 1);
// Fall through to RestartPoint.
MR_define_label(COMPLETION_LABEL(RestartPoint));
{
MR_CompletionInfo *completion_info;
MR_AnswerList cur_consumer_answer_list;
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_completion_restart_point++;
#endif
completion_info = MR_cur_leader->MR_sg_completion_info;
cur_consumer_answer_list =
*(completion_info->MR_ri_cur_consumer->
MR_cns_remaining_answer_list_ptr);
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("*cur_consumer->remaining_answer_list_ptr: %p\n",
cur_consumer_answer_list);
}
#endif
if (cur_consumer_answer_list != NULL) {
MR_GOTO_LABEL(COMPLETION_LABEL(ReturnAnswer));
}
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("no more unreturned answers for this consumer\n");
}
#endif // MR_TABLE_DEBUG
MR_GOTO_LABEL(COMPLETION_LABEL(LoopOverSuspensions));
}
MR_define_label(COMPLETION_LABEL(FixPointCheck));
{
MR_CompletionInfo *completion_info;
MR_SubgoalList subgoal_list;
MR_Subgoal *subgoal;
MR_ConsumerList consumer_list;
MR_Consumer *consumer;
MR_bool found_changes;
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_completion_fixpoint_check++;
#endif
completion_info = MR_cur_leader->MR_sg_completion_info;
found_changes = MR_FALSE;
for (subgoal_list = MR_cur_leader->MR_sg_followers;
subgoal_list != NULL;
subgoal_list = subgoal_list->MR_sl_next)
{
subgoal = subgoal_list->MR_sl_item;
for (consumer_list = subgoal->MR_sg_consumer_list;
consumer_list != NULL;
consumer_list = consumer_list->MR_cl_next)
{
consumer = consumer_list->MR_cl_item;
if (*(consumer->MR_cns_remaining_answer_list_ptr) != NULL) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("consumer %s of subgoal %s has unconsumed answers\n",
MR_consumer_addr_name(consumer),
MR_subgoal_addr_name(subgoal));
}
#endif // MR_TABLE_DEBUG
found_changes = MR_TRUE;
}
}
}
if (found_changes) {
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("no fixpoint; start completion op all over again\n");
}
#endif // MR_TABLE_DEBUG
completion_info->MR_ri_subgoal_list = MR_cur_leader->MR_sg_followers;
MR_GOTO_LABEL(COMPLETION_LABEL(StartCompletionOp));
}
// Fall through to ReachedFixpoint.
}
MR_define_label(COMPLETION_LABEL(ReachedFixpoint));
{
MR_SubgoalList subgoal_list;
MR_CompletionInfo *completion_info;
#ifdef MR_TABLE_STATISTICS
MR_minmodel_stats_cnt_completion_reached_fixpoint++;
#endif
for (subgoal_list = MR_cur_leader->MR_sg_followers;
subgoal_list != NULL;
subgoal_list = subgoal_list->MR_sl_next)
{
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("marking table %s complete\n",
MR_subgoal_addr_name(subgoal_list->MR_sl_item));
}
#endif
subgoal_list->MR_sl_item->MR_sg_status = MR_SUBGOAL_COMPLETE;
}
completion_info = MR_cur_leader->MR_sg_completion_info;
// Restore the state we had when table_mm_completion was called.
MR_save_transient_registers();
restore_state(&(completion_info->MR_ri_leader_state),
"resumption", "generator");
MR_restore_transient_registers();
// XXX This will go to code that does fail().
MR_succip_word = (MR_Word) completion_info->MR_ri_saved_succip;
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("using completion info succip ");
MR_printlabel(stdout, MR_succip);
}
#endif // MR_TABLE_DEBUG
// We should free the old completion_info structure.
MR_cur_leader->MR_sg_completion_info = NULL;
// We are done with this generator.
(void) MR_pop_generator();
MR_proceed();
}
MR_define_entry(MR_table_mm_commit);
MR_commit_cut();
MR_fail();
MR_define_entry(MR_MMSC_RET_ALL_NONDET_ENTRY);
{
MR_SubgoalPtr Subgoal;
MR_AnswerList CurNode0;
MR_AnswerBlock AnswerBlock;
MR_AnswerList CurNode;
Subgoal = (MR_SubgoalPtr) MR_r1;
CurNode0 = Subgoal->MR_sg_answer_list;
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("picking up all answers in %p -> %s\n",
Subgoal->MR_sg_back_ptr, MR_subgoal_addr_name(Subgoal));
}
#endif
if (CurNode0 == NULL) {
MR_redo();
}
AnswerBlock = CurNode0->MR_aln_answer_block;
CurNode = CurNode0->MR_aln_next_answer;
// Consider not creating the stack frame if CurNode is NULL.
MR_mkframe("pred table_builtin.table_mm_return_all_nondet/2-0", 1,
MR_LABEL(RET_ALL_NONDET_LABEL(Next)));
MR_framevar(1) = (MR_Word) CurNode;
MR_r1 = (MR_Word) AnswerBlock;
}
MR_succeed();
MR_define_label(RET_ALL_NONDET_LABEL(Next));
{
MR_AnswerList CurNode0;
MR_AnswerBlock AnswerBlock;
MR_AnswerList CurNode;
CurNode0 = (MR_AnswerList) MR_framevar(1);
if (CurNode0 == NULL) {
MR_fail();
}
AnswerBlock = CurNode0->MR_aln_answer_block;
CurNode = CurNode0->MR_aln_next_answer;
MR_framevar(1) = (MR_Word) CurNode;
MR_r1 = (MR_Word) AnswerBlock;
}
MR_succeed();
MR_define_entry(MR_MMSC_RET_ALL_MULTI_ENTRY);
{
MR_SubgoalPtr Subgoal;
MR_AnswerList CurNode0;
MR_AnswerBlock AnswerBlock;
MR_AnswerList CurNode;
Subgoal = (MR_SubgoalPtr) MR_r1;
CurNode0 = Subgoal->MR_sg_answer_list;
#ifdef MR_TABLE_DEBUG
if (MR_tabledebug) {
printf("picking up all answers in %p -> %s\n",
Subgoal->MR_sg_back_ptr, MR_subgoal_addr_name(Subgoal));
}
#endif
if (CurNode0 == NULL) {
MR_fatal_error("table_mm_return_all_multi: no answers");
}
AnswerBlock = CurNode0->MR_aln_answer_block;
CurNode = CurNode0->MR_aln_next_answer;
// Consider not creating the stack frame if CurNode is NULL.
MR_mkframe("pred table_builtin.table_mm_return_all_multi/2-0", 1,
MR_LABEL(RET_ALL_MULTI_LABEL(Next)));
MR_framevar(1) = (MR_Word) CurNode;
MR_r1 = (MR_Word) AnswerBlock;
}
MR_succeed();
MR_define_label(RET_ALL_MULTI_LABEL(Next));
{
MR_AnswerList CurNode0;
MR_AnswerBlock AnswerBlock;
MR_AnswerList CurNode;
CurNode0 = (MR_AnswerList) MR_framevar(1);
if (CurNode0 == NULL) {
MR_fail();
}
AnswerBlock = CurNode0->MR_aln_answer_block;
CurNode = CurNode0->MR_aln_next_answer;
MR_framevar(1) = (MR_Word) CurNode;
MR_r1 = (MR_Word) AnswerBlock;
}
MR_succeed();
MR_END_MODULE
#endif // MR_USE_MINIMAL_MODEL_STACK_COPY
#endif // MR_HIGHLEVEL_CODE
// Ensure that the initialization code for the above modules gets to run.
/*
INIT mercury_sys_init_mmsc_modules
*/
#ifndef MR_HIGHLEVEL_CODE
MR_MODULE_STATIC_OR_EXTERN MR_ModuleFunc mmsc_module;
#endif
// Forward declarations to suppress gcc -Wmissing-decl warnings.
void mercury_sys_init_mmsc_modules_init(void);
void mercury_sys_init_mmsc_modules_init_type_tables(void);
#ifdef MR_DEEP_PROFILING
void mercury_sys_init_mmsc_modules_write_out_proc_statics(FILE *fp);
#endif
void mercury_sys_init_mmsc_modules_init(void)
{
#ifndef MR_HIGHLEVEL_CODE
mmsc_module();
#endif // MR_HIGHLEVEL_CODE
}
void mercury_sys_init_mmsc_modules_init_type_tables(void)
{
// No types to register.
}
#ifdef MR_DEEP_PROFILING
void mercury_sys_init_mmsc_modules_write_out_proc_statics(FILE *fp)
{
// No proc_statics to write out.
// XXX We need to fix the deep profiling
// of minimal model tabled predicates.
}
#endif