mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-28 07:44:43 +00:00
Estimated hours taken: 30 (including debugging) Add MR_TYPECTOR_REP_* to the type_ctor_info to describe the representation of this type. We want to do this because it allows us to check quickly to see what kind of data representation is being used. Previously this information was spread throughout the type_ctor_layout and type_ctor_functors data structures. It was complex to interpret and contained a lot of unnecessary duplication. We can now omit data structures such as the type_ctor_layout in many cases (it is only necessary for discriminated unions). Because we rule out some of the possible alternatives, the encodings used in the type_ctor_layout can be simplified. Also, the functors indicator in type_ctor_functors can be removed, as it subsumed by this data structure. Use this representation in code that uses RTTI. compiler/base_type_info.m: Add a missing alternative to the type_ctor_rep (this was a bug). library/array.m: library/builtin.m: library/private_builtin.m: runtime/mercury_bootstrap.c: Use MR_TYPECTOR_REP_* in the type_ctor_infos for builtin types. library/std_util.m: runtime/mercury_deep_copy_body.h: runtime/mercury_tabling.c: Use MR_TYPECTOR_REP_* and MR_DISCUNION_TAG_* to dispatch on data representations. Also, fix a bug in deep_copy when copying floating point values. I'm not sure when this stopped working, or if this is exactly the right fix, but it is more correct than the previous code. runtime/mercury_type_info.c: runtime/mercury_type_info.h: Update code to use MR_TYPECTOR_REP_*. Use a struct for type_ctor_info. tests/hard_coded/Mmakefile: tests/hard_coded/deep_copy.m: tests/hard_coded/deep_copy.exp: Add a test case for deep_copy.
1652 lines
46 KiB
C
1652 lines
46 KiB
C
/*
|
|
** Copyright (C) 1997-1999 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.
|
|
*/
|
|
|
|
#include "mercury_imp.h"
|
|
|
|
#include "mercury_type_info.h"
|
|
#include <stdio.h>
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
** this part defines the functions
|
|
** MR_int_hash_lookup_or_add(),
|
|
** MR_float_hash_lookup_or_add(), and
|
|
** MR_string_hash_lookup_or_add().
|
|
*/
|
|
|
|
/* Initial size of a new table */
|
|
#define TABLE_START_SIZE primes[0]
|
|
|
|
/*
|
|
** Maximum ratio of used to unused buckets in the table. Must be less than
|
|
** 0.9 if you want even poor lookup times.
|
|
*/
|
|
#define MAX_EL_SIZE_RATIO 0.65
|
|
|
|
/* Extract info from a table */
|
|
#define SIZE(table) (((TableRoot *) table)->size)
|
|
#define ELEMENTS(table) (((TableRoot *) table)->used_elements)
|
|
#define BUCKET(table, Bucket) ((TableNode **) &(((TableRoot *) table)-> \
|
|
elements))[(Bucket)]
|
|
typedef struct {
|
|
Word key;
|
|
Word * data;
|
|
} TableNode;
|
|
|
|
typedef struct {
|
|
Word size;
|
|
Word used_elements;
|
|
Word elements;
|
|
} TableRoot;
|
|
|
|
static Word next_prime(Word);
|
|
static Word * create_hash_table(Word);
|
|
static void re_hash(Word *, Word, TableNode * Node);
|
|
|
|
/*
|
|
** Prime numbers which are close to powers of 2. Used for choosing
|
|
** the next size for a hash table.
|
|
*/
|
|
|
|
#define NUM_OF_PRIMES 16
|
|
static Word primes[NUM_OF_PRIMES] =
|
|
{127, 257, 509, 1021, 2053, 4099, 8191, 16381, 32771, 65537, 131071,
|
|
262147, 524287, 1048573, 2097143, 4194301};
|
|
|
|
/*
|
|
** Return the next prime number greater than the number received.
|
|
** If no such prime number can be found, compute an approximate one.
|
|
*/
|
|
static Word
|
|
next_prime(Word old_size)
|
|
{
|
|
int i;
|
|
|
|
i = 0;
|
|
while ( (old_size >= primes[i]) && (i < NUM_OF_PRIMES) ) {
|
|
i++;
|
|
}
|
|
|
|
if (i < NUM_OF_PRIMES) {
|
|
return primes[i];
|
|
} else {
|
|
return 2 * old_size - 1;
|
|
}
|
|
}
|
|
|
|
/* Create a new empty hash table. */
|
|
static Word *
|
|
create_hash_table(Word table_size)
|
|
{
|
|
Word i;
|
|
TableRoot * table =
|
|
table_allocate_bytes(sizeof(Word) * 2 +
|
|
table_size * sizeof(TableNode *));
|
|
|
|
table->size = table_size;
|
|
table->used_elements = 0;
|
|
|
|
for (i = 0; i < table_size; i++) {
|
|
BUCKET(table, i) = NULL;
|
|
}
|
|
|
|
return (Word *) table;
|
|
}
|
|
|
|
/*
|
|
** Insert key and Data into a new hash table using the given hash.
|
|
** this function does not have to do compares as the given key
|
|
** is definitely not in the table.
|
|
*/
|
|
static void
|
|
re_hash(Word * table, Word hash, TableNode * node)
|
|
{
|
|
Word bucket = hash % SIZE(table);
|
|
|
|
while (BUCKET(table, bucket)) {
|
|
++bucket;
|
|
|
|
if (bucket == SIZE(table))
|
|
bucket = 0;
|
|
}
|
|
|
|
BUCKET(table, bucket) = node;
|
|
++ELEMENTS(table);
|
|
}
|
|
|
|
/*
|
|
** Look to see if the given integer key is in the given table. If it
|
|
** is return the address of the data pointer associated with the key.
|
|
** If it is not; create a new element for the key in the table and
|
|
** return the address of its data pointer.
|
|
*/
|
|
MR_TrieNode
|
|
MR_int_hash_lookup_or_add(MR_TrieNode t, Integer key)
|
|
{
|
|
TableNode * p, * q;
|
|
Word * table = *t; /* Deref the table pointer */
|
|
Word bucket;
|
|
|
|
/* Has the the table been built? */
|
|
if (table == NULL) {
|
|
table = create_hash_table(TABLE_START_SIZE);
|
|
*t = table;
|
|
}
|
|
|
|
bucket = key % SIZE(table);
|
|
p = BUCKET(table, bucket);
|
|
|
|
/* Find if the element is present. If not add it */
|
|
while (p) {
|
|
if (key == p->key) {
|
|
return &p->data;
|
|
}
|
|
|
|
if (bucket == SIZE(table))
|
|
bucket = 0;
|
|
|
|
p = BUCKET(table, bucket);
|
|
}
|
|
|
|
p = table_allocate_bytes(sizeof(TableNode));
|
|
p->key = key;
|
|
p->data = NULL;
|
|
|
|
/* Rehash the table if it has grown to full */
|
|
if ((float) ELEMENTS(table) / (float) SIZE(table) >
|
|
MAX_EL_SIZE_RATIO)
|
|
{
|
|
int old_size = SIZE(table);
|
|
int new_size = next_prime(old_size);
|
|
Word * new_table = create_hash_table(new_size);
|
|
int i;
|
|
|
|
for (i = 0; i < old_size; i++) {
|
|
q = BUCKET(table, i);
|
|
if (q) {
|
|
re_hash(new_table, q->key, q);
|
|
}
|
|
}
|
|
|
|
/* Free the old table */
|
|
table_free(table);
|
|
|
|
/* Point to the new table */
|
|
*t = new_table;
|
|
|
|
/* Add a new element */
|
|
re_hash(new_table, key, p);
|
|
} else {
|
|
BUCKET(table, bucket) = p;
|
|
++ELEMENTS(table);
|
|
}
|
|
|
|
return &p->data;
|
|
}
|
|
|
|
/*
|
|
** Look to see if the given float key is in the given table. If it
|
|
** is return the address of the data pointer associated with the key.
|
|
** If it is not create a new element for the key in the table and
|
|
** return the address of its data pointer.
|
|
*/
|
|
MR_TrieNode
|
|
MR_float_hash_lookup_or_add(MR_TrieNode t, Float key)
|
|
{
|
|
TableNode * p, * q;
|
|
Word * table = *t; /* Deref the table pointer */
|
|
Word bucket;
|
|
Word hash;
|
|
|
|
/* Has the the table been built? */
|
|
if (table == NULL) {
|
|
table = create_hash_table(TABLE_START_SIZE);
|
|
*t = table;
|
|
}
|
|
|
|
hash = hash_float(key);
|
|
bucket = hash % SIZE(table);
|
|
|
|
p = BUCKET(table, bucket);
|
|
|
|
/* Find if the element is present. If not add it */
|
|
while (p) {
|
|
if (key == word_to_float(p->key)) {
|
|
return &p->data;
|
|
}
|
|
++bucket;
|
|
|
|
if (bucket == SIZE(table))
|
|
bucket = 0;
|
|
|
|
p = BUCKET(table, bucket);
|
|
}
|
|
|
|
p = table_allocate_bytes(sizeof(TableNode));
|
|
p->key = float_to_word(key);
|
|
p->data = NULL;
|
|
|
|
/* Rehash the table if it has grown to full */
|
|
if ((float) ELEMENTS(table) / (float) SIZE(table) >
|
|
MAX_EL_SIZE_RATIO)
|
|
{
|
|
int old_size = SIZE(table);
|
|
int new_size = next_prime(old_size);
|
|
Word * new_table = create_hash_table(new_size);
|
|
int i;
|
|
|
|
for (i = 0; i < old_size; i++) {
|
|
q = BUCKET(table, i);
|
|
if (q) {
|
|
re_hash(new_table, hash_float(q->key), q);
|
|
}
|
|
}
|
|
|
|
/* Free the old table */
|
|
table_free(table);
|
|
|
|
/* Point to the new table */
|
|
*t = new_table;
|
|
|
|
/* Add a new element */
|
|
re_hash(new_table, hash, p);
|
|
} else {
|
|
++ELEMENTS(table);
|
|
BUCKET(table, bucket) = p;
|
|
}
|
|
|
|
return &p->data;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** Look to see if the given string key is in the given table. If it
|
|
** is return the address of the data pointer associated with the key.
|
|
** If it is not create a new element for the key in the table and
|
|
** return the address of its data pointer.
|
|
*/
|
|
MR_TrieNode
|
|
MR_string_hash_lookup_or_add(MR_TrieNode t, String key)
|
|
{
|
|
TableNode * p, * q;
|
|
Word * table = *t; /* Deref the table pointer */
|
|
Word bucket;
|
|
Word hash;
|
|
|
|
/* Has the the table been built? */
|
|
if (table == NULL) {
|
|
table = create_hash_table(TABLE_START_SIZE);
|
|
*t = table;
|
|
}
|
|
|
|
hash = hash_string((Word) key);
|
|
bucket = hash % SIZE(table);
|
|
|
|
p = BUCKET(table, bucket);
|
|
|
|
/* Find if the element is present. */
|
|
while (p) {
|
|
int res = strtest((String)p->key, key);
|
|
|
|
if (res == 0) {
|
|
return &p->data;
|
|
}
|
|
++bucket;
|
|
|
|
if (bucket == SIZE(table))
|
|
bucket = 0;
|
|
|
|
p = BUCKET(table, bucket);
|
|
}
|
|
|
|
p = table_allocate_bytes(sizeof(TableNode));
|
|
p->key = (Word) key;
|
|
p->data = NULL;
|
|
|
|
/* Rehash the table if it has grown to full */
|
|
if ((float) ELEMENTS(table) / (float) SIZE(table) >
|
|
MAX_EL_SIZE_RATIO)
|
|
{
|
|
int old_size = SIZE(table);
|
|
int new_size = next_prime(old_size);
|
|
Word * new_table = create_hash_table(new_size);
|
|
int i;
|
|
|
|
for (i = 0; i < old_size; i++) {
|
|
q = BUCKET(table, i);
|
|
if (q) {
|
|
re_hash(new_table,
|
|
hash_string((Word) q->key), q);
|
|
}
|
|
}
|
|
|
|
/* Free the old table */
|
|
table_free(t);
|
|
|
|
/* Point to the new table */
|
|
*t = new_table;
|
|
|
|
/* Add a new element to rehashed table */
|
|
re_hash(new_table, hash, p);
|
|
} else {
|
|
BUCKET(table, bucket) = p;
|
|
++ELEMENTS(table);
|
|
}
|
|
|
|
return &p->data;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
** This part defines the MR_int_index_lookup_or_add() function.
|
|
*/
|
|
|
|
#define ELEMENT(Table, Key) ((Word**)&((Table)[Key]))
|
|
|
|
/*
|
|
** MR_int_index_lookup_or_add() : This function maintains a simple indexed
|
|
** table of size Range.
|
|
*/
|
|
|
|
MR_TrieNode
|
|
MR_int_index_lookup_or_add(MR_TrieNode t, Integer range, Integer key)
|
|
{
|
|
Word *table = *t; /* Deref table */
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (key >= range) {
|
|
fatal_error("MR_int_index_lookup_or_add: key out of range");
|
|
}
|
|
#endif
|
|
|
|
if (table == NULL) {
|
|
*t = table = table_allocate_words(range);
|
|
memset(table, 0, sizeof(Word *) * range);
|
|
}
|
|
|
|
return ELEMENT(table, key);
|
|
}
|
|
|
|
#undef ELEMENT
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
** This part defines the type_info_lookup_or_add() function.
|
|
*/
|
|
|
|
typedef struct TreeNode_struct {
|
|
Word * key;
|
|
Word value;
|
|
struct TreeNode_struct * right;
|
|
struct TreeNode_struct * left;
|
|
} TreeNode;
|
|
|
|
MR_TrieNode
|
|
MR_type_info_lookup_or_add(MR_TrieNode table, Word * type_info)
|
|
{
|
|
TreeNode *p, *q;
|
|
int i;
|
|
|
|
if (*table == NULL) {
|
|
p = table_allocate_bytes(sizeof(TreeNode));
|
|
|
|
p->key = type_info;
|
|
p->value = (Word) NULL;
|
|
p->left = NULL;
|
|
p->right = NULL;
|
|
|
|
*table = (Word *) p;
|
|
|
|
return (Word**) &p->value;
|
|
}
|
|
|
|
p = (TreeNode *) *table;
|
|
|
|
while (p != NULL) {
|
|
i = MR_compare_type_info((Word) p->key, (Word) type_info);
|
|
|
|
if (i == COMPARE_EQUAL) {
|
|
return (Word **) &p->value;
|
|
}
|
|
|
|
q = p;
|
|
|
|
if (i == COMPARE_LESS) {
|
|
p = p->left;
|
|
} else {
|
|
p = p->right;
|
|
}
|
|
}
|
|
|
|
p = table_allocate_bytes(sizeof(TreeNode));
|
|
p->key = type_info;
|
|
p->value = (Word) NULL;
|
|
p->left = NULL;
|
|
p->right = NULL;
|
|
|
|
if (i == COMPARE_LESS) {
|
|
q->left = p;
|
|
} else {
|
|
q ->right = p;
|
|
}
|
|
|
|
return (Word **) &p->value;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*
|
|
** This part defines the MR_table_type() function.
|
|
*/
|
|
|
|
MR_DECLARE_STRUCT(mercury_data___type_ctor_info_pred_0);
|
|
MR_DECLARE_STRUCT(mercury_data___type_ctor_info_func_0);
|
|
|
|
/*
|
|
** Due to the depth of the control here, we'll use 4 space indentation.
|
|
**
|
|
** NOTE : changes to this function will probably also have to be reflected
|
|
** in mercury_deep_copy.c and std_util::ML_expand().
|
|
*/
|
|
|
|
MR_TrieNode
|
|
MR_table_type(MR_TrieNode table, Word *type_info, Word data)
|
|
{
|
|
MR_TypeCtorInfo type_ctor_info;
|
|
MR_TypeCtorLayout type_ctor_layout;
|
|
MR_TypeCtorFunctors type_ctor_functors;
|
|
|
|
Word layout_for_tag, *layout_vector_for_tag, *data_value;
|
|
int data_tag, entry_tag;
|
|
|
|
MR_DiscUnionTagRepresentation tag_rep;
|
|
|
|
MR_MemoryList allocated_memory_cells = NULL;
|
|
|
|
data_tag = tag(data);
|
|
data_value = (Word *) body(data, data_tag);
|
|
|
|
type_ctor_info = MR_TYPEINFO_GET_TYPE_CTOR_INFO(type_info);
|
|
type_ctor_layout = MR_TYPE_CTOR_INFO_GET_TYPE_CTOR_LAYOUT(type_ctor_info);
|
|
type_ctor_functors = MR_TYPE_CTOR_INFO_GET_TYPE_CTOR_FUNCTORS(type_ctor_info);
|
|
|
|
layout_for_tag = type_ctor_layout[data_tag];
|
|
layout_vector_for_tag = (Word *) strip_tag(layout_for_tag);
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("ENTRY %p %x, data rep: %d\n", table, data, data_rep);
|
|
}
|
|
#endif /* MR_TABLE_DEBUG */
|
|
|
|
switch (type_ctor_info->type_ctor_rep) {
|
|
case MR_TYPECTOR_REP_ENUM: {
|
|
int functors = MR_TYPE_CTOR_LAYOUT_ENUM_VECTOR_NUM_FUNCTORS(
|
|
layout_vector_for_tag);
|
|
MR_DEBUG_TABLE_ENUM(table, functors, data);
|
|
break;
|
|
}
|
|
case MR_TYPECTOR_REP_DU: {
|
|
tag_rep = MR_get_tag_representation((Word) layout_for_tag);
|
|
switch(tag_rep) {
|
|
case MR_DISCUNIONTAG_SHARED_LOCAL: {
|
|
int functors = MR_TYPE_CTOR_LAYOUT_ENUM_VECTOR_NUM_FUNCTORS(
|
|
layout_vector_for_tag);
|
|
MR_DEBUG_TABLE_TAG(table, data_tag);
|
|
MR_DEBUG_TABLE_ENUM(table, functors, unmkbody(data));
|
|
break;
|
|
}
|
|
case MR_DISCUNIONTAG_UNSHARED: {
|
|
int arity, i;
|
|
Word *argument_vector, *type_info_vector, *new_type_info;
|
|
|
|
argument_vector = data_value;
|
|
|
|
arity = layout_vector_for_tag[
|
|
TYPE_CTOR_LAYOUT_UNSHARED_ARITY_OFFSET];
|
|
type_info_vector = &layout_vector_for_tag[
|
|
TYPE_CTOR_LAYOUT_UNSHARED_ARGS_OFFSET];
|
|
|
|
MR_DEBUG_TABLE_TAG(table, data_tag);
|
|
|
|
/* copy arguments */
|
|
for (i = 0; i < arity; i++) {
|
|
new_type_info = MR_make_type_info(type_info,
|
|
(Word *) type_info_vector[i], &allocated_memory_cells);
|
|
|
|
MR_DEBUG_TABLE_ANY(table, new_type_info, argument_vector[i]);
|
|
}
|
|
break;
|
|
}
|
|
case MR_DISCUNIONTAG_SHARED_REMOTE: {
|
|
int arity, i;
|
|
Word *argument_vector, *type_info_vector, *new_type_info;
|
|
Word secondary_tag, num_sharers, *new_layout_vector;
|
|
|
|
secondary_tag = *data_value;
|
|
argument_vector = data_value + 1;
|
|
|
|
num_sharers = MR_TYPE_CTOR_LAYOUT_SHARED_REMOTE_VECTOR_NUM_SHARERS(
|
|
layout_vector_for_tag);
|
|
new_layout_vector =
|
|
MR_TYPE_CTOR_LAYOUT_SHARED_REMOTE_VECTOR_GET_FUNCTOR_DESCRIPTOR(
|
|
layout_vector_for_tag, secondary_tag);
|
|
arity = new_layout_vector[TYPE_CTOR_LAYOUT_UNSHARED_ARITY_OFFSET];
|
|
type_info_vector =
|
|
&new_layout_vector[TYPE_CTOR_LAYOUT_UNSHARED_ARGS_OFFSET];
|
|
|
|
MR_DEBUG_TABLE_TAG(table, data_tag);
|
|
MR_DEBUG_TABLE_ENUM(table, num_sharers, secondary_tag);
|
|
|
|
for (i = 0; i < arity; i++) {
|
|
new_type_info = MR_make_type_info(type_info,
|
|
(Word *) type_info_vector[i], &allocated_memory_cells);
|
|
|
|
MR_DEBUG_TABLE_ANY(table, new_type_info, argument_vector[i]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case MR_TYPECTOR_REP_NOTAG: {
|
|
Word *new_type_info;
|
|
new_type_info = MR_make_type_info(type_info,
|
|
(Word *) *MR_TYPE_CTOR_LAYOUT_NO_TAG_VECTOR_ARGS(
|
|
layout_vector_for_tag),
|
|
&allocated_memory_cells);
|
|
MR_DEBUG_TABLE_ANY(table, new_type_info, data);
|
|
break;
|
|
}
|
|
case MR_TYPECTOR_REP_EQUIV: {
|
|
Word *new_type_info;
|
|
new_type_info = MR_make_type_info(type_info,
|
|
(Word *) MR_TYPE_CTOR_LAYOUT_EQUIV_TYPE(layout_vector_for_tag),
|
|
&allocated_memory_cells);
|
|
MR_DEBUG_TABLE_ANY(table, new_type_info, data);
|
|
break;
|
|
}
|
|
case MR_TYPECTOR_REP_EQUIV_VAR:
|
|
MR_DEBUG_TABLE_ANY(table,
|
|
(Word *) type_info[(Word) layout_vector_for_tag], data);
|
|
break;
|
|
|
|
case MR_TYPECTOR_REP_INT:
|
|
MR_DEBUG_TABLE_INT(table, data);
|
|
break;
|
|
|
|
case MR_TYPECTOR_REP_CHAR:
|
|
MR_DEBUG_TABLE_CHAR(table, data);
|
|
break;
|
|
|
|
case MR_TYPECTOR_REP_FLOAT:
|
|
MR_DEBUG_TABLE_FLOAT(table, data);
|
|
break;
|
|
|
|
case MR_TYPECTOR_REP_STRING:
|
|
MR_DEBUG_TABLE_STRING(table, data);
|
|
break;
|
|
|
|
case MR_TYPECTOR_REP_PRED: {
|
|
int i;
|
|
Word args = data_value[0];
|
|
|
|
MR_DEBUG_TABLE_STRING(table, args);
|
|
MR_DEBUG_TABLE_STRING(table, data_value[1]);
|
|
|
|
for (i = 0; i < args; i++) {
|
|
MR_DEBUG_TABLE_ANY(table,
|
|
(Word *) type_info[i + TYPEINFO_OFFSET_FOR_PRED_ARGS],
|
|
data_value[i+2]);
|
|
}
|
|
break;
|
|
}
|
|
case MR_TYPECTOR_REP_UNIV:
|
|
MR_DEBUG_TABLE_TYPEINFO(table,
|
|
(Word *) data_value[UNIV_OFFSET_FOR_TYPEINFO]);
|
|
MR_DEBUG_TABLE_ANY(table,
|
|
(Word *) data_value[UNIV_OFFSET_FOR_TYPEINFO],
|
|
data_value[UNIV_OFFSET_FOR_DATA]);
|
|
break;
|
|
|
|
case MR_TYPECTOR_REP_VOID:
|
|
fatal_error("Cannot table a void type");
|
|
break;
|
|
|
|
case MR_TYPECTOR_REP_ARRAY: {
|
|
int i;
|
|
MR_ArrayType *array;
|
|
Word *new_type_info;
|
|
Integer array_size;
|
|
|
|
array = (MR_ArrayType *) data_value;
|
|
array_size = array->size;
|
|
|
|
new_type_info = MR_make_type_info(type_info, (Word *) 1,
|
|
&allocated_memory_cells);
|
|
|
|
for (i = 0; i < array_size; i++) {
|
|
MR_DEBUG_TABLE_ANY(table, new_type_info, array->elements[i]);
|
|
}
|
|
break;
|
|
}
|
|
case MR_TYPECTOR_REP_TYPEINFO:
|
|
MR_DEBUG_TABLE_TYPEINFO(table, (Word *) data_value);
|
|
break;
|
|
|
|
case MR_TYPECTOR_REP_C_POINTER:
|
|
fatal_error("Attempt to use a C_POINTER tag in table");
|
|
break;
|
|
|
|
case MR_TYPECTOR_REP_UNKNOWN: /* fallthru */
|
|
default:
|
|
fatal_error("Unknown layout tag in table_any");
|
|
break;
|
|
}
|
|
|
|
MR_deallocate(allocated_memory_cells);
|
|
|
|
return table;
|
|
} /* end table_any() */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL
|
|
|
|
/*
|
|
** 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_{maxfr,sp} arguments give the points from which we need
|
|
** to copy the nondet and 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,
|
|
Word *generator_maxfr, Word *generator_sp,
|
|
const char *who, const char *what)
|
|
{
|
|
restore_transient_registers();
|
|
|
|
saved_state->succ_ip = MR_succip;
|
|
saved_state->s_p = MR_sp;
|
|
saved_state->cur_fr = MR_curfr;
|
|
saved_state->max_fr = MR_maxfr;
|
|
|
|
saved_state->non_stack_block_start = generator_maxfr + 1;
|
|
if (MR_maxfr > generator_maxfr) {
|
|
saved_state->non_stack_block_size = MR_maxfr - generator_maxfr;
|
|
saved_state->non_stack_block =
|
|
table_allocate_words(saved_state->non_stack_block_size);
|
|
table_copy_words(saved_state->non_stack_block,
|
|
saved_state->non_stack_block_start,
|
|
saved_state->non_stack_block_size);
|
|
} else {
|
|
saved_state->non_stack_block_size = 0;
|
|
saved_state->non_stack_block = NULL;
|
|
}
|
|
|
|
saved_state->det_stack_block_start = generator_sp;
|
|
if (MR_sp > generator_sp) {
|
|
saved_state->det_stack_block_size = (MR_sp - 1) - generator_sp;
|
|
saved_state->det_stack_block =
|
|
table_allocate_words(saved_state->det_stack_block_size);
|
|
table_copy_words(saved_state->det_stack_block,
|
|
saved_state->det_stack_block_start,
|
|
saved_state->det_stack_block_size);
|
|
} else {
|
|
saved_state->det_stack_block_size = 0;
|
|
saved_state->det_stack_block = NULL;
|
|
}
|
|
|
|
saved_state->gen_next = MR_gen_next;
|
|
saved_state->generator_stack_block = table_allocate_bytes(
|
|
MR_gen_next * sizeof(MR_GeneratorStackFrame));
|
|
table_copy_bytes(saved_state->generator_stack_block,
|
|
MR_gen_stack, MR_gen_next * sizeof(MR_GeneratorStackFrame));
|
|
|
|
saved_state->cut_next = MR_cut_next;
|
|
saved_state->cut_stack_block = table_allocate_bytes(
|
|
MR_cut_next * sizeof(MR_CutStackFrame));
|
|
table_copy_bytes(saved_state->cut_stack_block,
|
|
MR_cut_stack, MR_cut_next * sizeof(MR_CutStackFrame));
|
|
|
|
#ifdef MR_USE_TRAIL
|
|
/*
|
|
** Saving the trail state here would not be sufficient to handle
|
|
** the combination of trailing and minimal model tabling.
|
|
** Consider the following sequence of events:
|
|
**
|
|
** execution enters a goal being committed across
|
|
** a new entry is pushed on the trail
|
|
** a tabled goal suspends,
|
|
** causing the saving of a trail segment
|
|
** and then a failure
|
|
** the goal being committed across fails,
|
|
** which invokes a failed commit on the trail entry
|
|
** ...
|
|
** the tabled goal is resumed,
|
|
** causing the restoring of the saved trail segment
|
|
** and then a success
|
|
** the goal being committed across now succeeds,
|
|
** which invokes a successful commit on the trail entry
|
|
**
|
|
** The trail handler will be thoroughly confused by such a sequence.
|
|
*/
|
|
|
|
fatal_error("Sorry, not implemented: "
|
|
"can't have both minimal model tabling and trailing");
|
|
#endif
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("\n%s saves %s stacks: ", who, what);
|
|
printf("%d non, %d det, %d generator, %d cut\n",
|
|
saved_state->non_stack_block_size,
|
|
saved_state->det_stack_block_size,
|
|
MR_gen_next, MR_cut_next);
|
|
|
|
printf("non region from ");
|
|
MR_printnondstackptr(saved_state->non_stack_block_start);
|
|
printf(" to ");
|
|
MR_printnondstackptr(MR_maxfr);
|
|
printf(" (both inclusive)\n");
|
|
printf("stored at %p to %p (both inclusive)\n",
|
|
saved_state->non_stack_block,
|
|
saved_state->non_stack_block +
|
|
saved_state->non_stack_block_size - 1);
|
|
|
|
printf("det region from ");
|
|
MR_printdetstackptr(saved_state->det_stack_block_start);
|
|
printf(" to ");
|
|
MR_printdetstackptr(MR_sp);
|
|
printf(" (both inclusive)\n");
|
|
printf("stored at %p to %p (both inclusive)\n",
|
|
saved_state->det_stack_block,
|
|
saved_state->det_stack_block +
|
|
saved_state->det_stack_block_size - 1);
|
|
|
|
printf("succip = %p, sp = ", (void *) MR_succip);
|
|
MR_printdetstackptr(MR_sp);
|
|
printf("\nmaxfr = ");
|
|
MR_printnondstackptr(MR_maxfr);
|
|
printf(", curfr = ");
|
|
MR_printnondstackptr(MR_curfr);
|
|
printf("\n\n");
|
|
|
|
MR_print_gen_stack(stdout);
|
|
|
|
if (MR_tablestackdebug) {
|
|
MR_dump_nondet_stack_from_layout(stdout, MR_maxfr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
restore_transient_registers();
|
|
|
|
MR_succip = saved_state->succ_ip;
|
|
MR_sp = saved_state->s_p;
|
|
MR_curfr = saved_state->cur_fr;
|
|
MR_maxfr = saved_state->max_fr;
|
|
|
|
table_copy_words(saved_state->non_stack_block_start,
|
|
saved_state->non_stack_block,
|
|
saved_state->non_stack_block_size);
|
|
|
|
table_copy_words(saved_state->det_stack_block_start,
|
|
saved_state->det_stack_block,
|
|
saved_state->det_stack_block_size);
|
|
|
|
MR_gen_next = saved_state->gen_next;
|
|
table_copy_bytes(MR_gen_stack, saved_state->generator_stack_block,
|
|
saved_state->gen_next * sizeof(MR_GeneratorStackFrame));
|
|
|
|
MR_cut_next = saved_state->cut_next;
|
|
table_copy_bytes(MR_cut_stack, saved_state->cut_stack_block,
|
|
saved_state->cut_next * sizeof(MR_CutStackFrame));
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("\n%s restores %s stacks: ", who, what);
|
|
printf("%d non, %d det, %d generator, %d cut\n",
|
|
saved_state->non_stack_block_size,
|
|
saved_state->det_stack_block_size,
|
|
saved_state->gen_next, saved_state->cut_next);
|
|
|
|
printf("non region from ");
|
|
MR_printnondstackptr(saved_state->non_stack_block_start);
|
|
printf(" to ");
|
|
MR_printnondstackptr(saved_state->non_stack_block_start +
|
|
saved_state->non_stack_block_size - 1);
|
|
printf(" (both inclusive)\n");
|
|
printf("stored at %p to %p (both inclusive)\n",
|
|
saved_state->non_stack_block,
|
|
saved_state->non_stack_block +
|
|
saved_state->non_stack_block_size - 1);
|
|
|
|
printf("det region from ");
|
|
MR_printdetstackptr(saved_state->det_stack_block_start);
|
|
printf(" to ");
|
|
MR_printdetstackptr(saved_state->det_stack_block_start +
|
|
saved_state->det_stack_block_size - 1);
|
|
printf(" (both inclusive)\n");
|
|
printf("stored at %p to %p (both inclusive)\n",
|
|
saved_state->det_stack_block,
|
|
saved_state->det_stack_block +
|
|
saved_state->det_stack_block_size - 1);
|
|
|
|
printf("succip = %p, sp = ", (void *) MR_succip);
|
|
MR_printdetstackptr(MR_sp);
|
|
printf("\nmaxfr = ");
|
|
MR_printnondstackptr(MR_maxfr);
|
|
printf(", curfr = ");
|
|
MR_printnondstackptr(MR_curfr);
|
|
printf("\n");
|
|
|
|
MR_print_gen_stack(stdout);
|
|
|
|
if (MR_tablestackdebug) {
|
|
MR_dump_nondet_stack_from_layout(stdout, MR_maxfr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
save_transient_registers();
|
|
}
|
|
|
|
static void
|
|
print_saved_state_stacks(MR_SavedState *saved_state)
|
|
{
|
|
int i;
|
|
|
|
printf("saved state parameters:\n");
|
|
printf("succip:\t");
|
|
printlabel(saved_state->succ_ip);
|
|
printf("sp:\t");
|
|
MR_printdetstackptr(saved_state->s_p);
|
|
printf("\ncurfr:\t");
|
|
MR_printnondstackptr(saved_state->cur_fr);
|
|
printf("\nmaxfr:\t");
|
|
MR_printnondstackptr(saved_state->max_fr);
|
|
|
|
printf("\n\nnondet stack block: %d words from %p\n",
|
|
saved_state->non_stack_block_size,
|
|
saved_state->non_stack_block_start);
|
|
for (i = 0; i < saved_state->non_stack_block_size; i++) {
|
|
printf("%2d: %x\n", i, saved_state->non_stack_block[i]);
|
|
}
|
|
|
|
printf("\ndet stack block: %d words from %p\n",
|
|
saved_state->det_stack_block_size,
|
|
saved_state->det_stack_block_start);
|
|
for (i = 0; i < saved_state->det_stack_block_size; i++) {
|
|
printf("%2d: %x\n", i, saved_state->det_stack_block[i]);
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
** 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,
|
|
** we need to extend the stack segments of A's consumers to also include
|
|
** the parts of the stacks between the generator of B and the generator of A.
|
|
*/
|
|
|
|
Declare_entry(mercury__table_nondet_resume_1_0);
|
|
|
|
static void
|
|
extend_consumer_stacks(MR_Subgoal *leader, MR_Consumer *suspension)
|
|
{
|
|
Word *arena_block;
|
|
Word *arena_start;
|
|
Word arena_size;
|
|
Word extension_size;
|
|
Word *saved_fr;
|
|
Word *real_fr;
|
|
Word frame_size;
|
|
Word offset;
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tablestackdebug) {
|
|
printf("\nextending saved consumer stacks\n");
|
|
print_saved_state_stacks(&suspension->saved_state);
|
|
}
|
|
#endif
|
|
|
|
arena_start = leader->generator_sp;
|
|
extension_size = suspension->saved_state.det_stack_block_start
|
|
- arena_start;
|
|
arena_size = extension_size
|
|
+ suspension->saved_state.det_stack_block_size;
|
|
if (arena_size != 0) {
|
|
assert(arena_start + arena_size
|
|
== suspension->saved_state.s_p - 1);
|
|
}
|
|
|
|
arena_block = table_allocate_words(arena_size);
|
|
|
|
table_copy_words(arena_block, arena_start, extension_size);
|
|
table_copy_words(arena_block + extension_size,
|
|
suspension->saved_state.det_stack_block,
|
|
suspension->saved_state.det_stack_block_size);
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("extending det stack of suspension %p for %p\n",
|
|
suspension, leader);
|
|
printf("start: old %p, new %p\n",
|
|
suspension->saved_state.det_stack_block_start,
|
|
arena_start);
|
|
printf("size: old %d, new %d\n",
|
|
suspension->saved_state.det_stack_block_size,
|
|
arena_size);
|
|
printf("block: old %p, new %p\n",
|
|
suspension->saved_state.det_stack_block,
|
|
arena_block);
|
|
}
|
|
#endif
|
|
|
|
suspension->saved_state.det_stack_block = arena_block;
|
|
suspension->saved_state.det_stack_block_size = arena_size;
|
|
suspension->saved_state.det_stack_block_start = arena_start;
|
|
|
|
arena_start = leader->generator_maxfr + 1;
|
|
extension_size = suspension->saved_state.non_stack_block_start
|
|
- arena_start;
|
|
arena_size = extension_size
|
|
+ suspension->saved_state.non_stack_block_size;
|
|
assert(leader->generator_maxfr + arena_size
|
|
== suspension->saved_state.max_fr);
|
|
|
|
arena_block = table_allocate_words(arena_size);
|
|
|
|
table_copy_words(arena_block, arena_start, extension_size);
|
|
table_copy_words(arena_block + extension_size,
|
|
suspension->saved_state.non_stack_block,
|
|
suspension->saved_state.non_stack_block_size);
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("extending non stack of suspension %p for %p\n",
|
|
suspension, leader);
|
|
printf("start: old %p, new %p\n",
|
|
suspension->saved_state.non_stack_block_start,
|
|
arena_start);
|
|
printf("size: old %d, new %d\n",
|
|
suspension->saved_state.non_stack_block_size,
|
|
arena_size);
|
|
printf("block: old %p, new %p\n",
|
|
suspension->saved_state.non_stack_block,
|
|
arena_block);
|
|
}
|
|
#endif
|
|
|
|
suspension->saved_state.non_stack_block = arena_block;
|
|
suspension->saved_state.non_stack_block_size = arena_size;
|
|
suspension->saved_state.non_stack_block_start = arena_start;
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tablestackdebug) {
|
|
printf("\nbefore pickling nondet stack\n");
|
|
print_saved_state_stacks(&suspension->saved_state);
|
|
}
|
|
#endif
|
|
|
|
saved_fr = suspension->saved_state.non_stack_block +
|
|
suspension->saved_state.non_stack_block_size - 1;
|
|
real_fr = suspension->saved_state.non_stack_block_start +
|
|
suspension->saved_state.non_stack_block_size - 1;
|
|
while (saved_fr > suspension->saved_state.non_stack_block) {
|
|
frame_size = real_fr - MR_prevfr_slot(saved_fr);
|
|
|
|
if (saved_fr - frame_size
|
|
> suspension->saved_state.non_stack_block)
|
|
{
|
|
*MR_redoip_addr(saved_fr) = (Word) ENTRY(do_fail);
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("do_fail to redoip at %p (%d)\n",
|
|
MR_redoip_addr(saved_fr),
|
|
MR_redoip_addr(saved_fr) -
|
|
suspension->
|
|
saved_state.non_stack_block);
|
|
}
|
|
#endif
|
|
} else {
|
|
*MR_redoip_addr(saved_fr) = (Word)
|
|
ENTRY(mercury__table_nondet_resume_1_0);
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("resume to redoip at %p (%d)\n",
|
|
MR_redoip_addr(saved_fr),
|
|
MR_redoip_addr(saved_fr) -
|
|
suspension->
|
|
saved_state.non_stack_block);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
saved_fr -= frame_size;
|
|
real_fr -= frame_size;
|
|
}
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tablestackdebug) {
|
|
printf("\nfinished extending saved consumer stacks\n");
|
|
print_saved_state_stacks(&suspension->saved_state);
|
|
}
|
|
#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_Consumer *suspension;
|
|
MR_SubgoalList sub_followers;
|
|
MR_ConsumerList suspend_list;
|
|
|
|
restore_transient_registers();
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("making %p follow %p\n", this_follower, leader);
|
|
}
|
|
#endif
|
|
|
|
for (sub_followers = this_follower->followers;
|
|
sub_followers != NULL; sub_followers = sub_followers->next)
|
|
{
|
|
for (suspend_list = sub_followers->item->consumer_list;
|
|
suspend_list != NULL;
|
|
suspend_list = suspend_list->next)
|
|
{
|
|
save_transient_registers();
|
|
extend_consumer_stacks(leader, suspend_list->item);
|
|
restore_transient_registers();
|
|
}
|
|
}
|
|
|
|
this_follower->leader = leader;
|
|
*(leader->followers_tail) = this_follower->followers;
|
|
this_follower->followers = NULL;
|
|
|
|
save_transient_registers();
|
|
}
|
|
|
|
/*
|
|
** The following procedure saves the state of the Mercury runtime
|
|
** so that it may be used in the table_nondet_resume procedure below to return
|
|
** answers through this saved state. The procedure table_nondet_suspend 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_nondet_suspend) but from the
|
|
** procedure table_nondet_resume. So essentially what is below is the code
|
|
** to do the initial fail; the code to return the answers is in
|
|
** table_nondet_resume.
|
|
*/
|
|
|
|
Declare_entry(mercury__table_nondet_resume_1_0);
|
|
Declare_entry(MR_do_trace_redo_fail);
|
|
Declare_entry(MR_table_nondet_commit);
|
|
Define_extern_entry(mercury__table_nondet_suspend_2_0);
|
|
MR_MAKE_PROC_LAYOUT(mercury__table_nondet_suspend_2_0,
|
|
MR_DETISM_NON, 0, MR_LONG_LVAL_TYPE_UNKNOWN,
|
|
MR_PREDICATE, "private_builtin", "table_nondet_suspend", 2, 0);
|
|
BEGIN_MODULE(table_nondet_suspend_module)
|
|
init_entry_sl(mercury__table_nondet_suspend_2_0);
|
|
MR_INIT_PROC_LAYOUT_ADDR(mercury__table_nondet_suspend_2_0);
|
|
BEGIN_CODE
|
|
|
|
Define_entry(mercury__table_nondet_suspend_2_0);
|
|
{
|
|
MR_Subgoal *table;
|
|
MR_Consumer *consumer;
|
|
MR_ConsumerList listnode;
|
|
Integer cur_gen;
|
|
Integer cur_cut;
|
|
Word *fr;
|
|
Word *prev_fr;
|
|
Word *stop_addr;
|
|
Word offset;
|
|
Word *clobber_addr;
|
|
|
|
/*
|
|
** This frame is not used in table_nondet_suspend, but it is copied
|
|
** to the suspend list as part of the saved nondet stack fragment,
|
|
** and it *will* be used when table_nondet_resume copies back the
|
|
** nondet stack fragment. The framevar slot is for use by
|
|
** table_nondet_resume.
|
|
*/
|
|
mkframe(mercury__table_nondet_suspend/2, 1, ENTRY(do_fail));
|
|
|
|
table = MR_SUBGOAL(r1);
|
|
consumer = table_allocate_bytes(sizeof(MR_Consumer));
|
|
consumer->remaining_answer_list_ptr = &table->answer_list;
|
|
|
|
save_transient_registers();
|
|
save_state(&(consumer->saved_state),
|
|
table->generator_maxfr, table->generator_sp,
|
|
"suspension", "consumer");
|
|
restore_transient_registers();
|
|
|
|
cur_gen = MR_gen_next - 1;
|
|
cur_cut = MR_cut_next - 1;
|
|
stop_addr = consumer->saved_state.non_stack_block_start;
|
|
for (fr = MR_maxfr; fr > stop_addr; fr = MR_prevfr_slot(fr))
|
|
{
|
|
offset = MR_redoip_addr(fr) -
|
|
consumer->saved_state.non_stack_block_start;
|
|
clobber_addr = consumer->saved_state.non_stack_block + offset;
|
|
#if 0
|
|
if (MR_tablestackdebug) {
|
|
printf("redoip addr ");
|
|
MR_printnondstackptr(MR_redoip_addr(fr));
|
|
printf(", offset %d from start, ", offset);
|
|
printf("saved copy at %p\n", clobber_addr);
|
|
}
|
|
#endif
|
|
|
|
if (fr == MR_gen_stack[cur_gen].generator_frame) {
|
|
if (MR_gen_stack[cur_gen].generator_table == table) {
|
|
/*
|
|
** This is the nondet stack frame of the
|
|
** generator corresponding to this consumer.
|
|
*/
|
|
|
|
assert(MR_prevfr_slot(fr) == (stop_addr - 1));
|
|
*clobber_addr = (Word)
|
|
ENTRY(mercury__table_nondet_resume_1_0);
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tablestackdebug) {
|
|
printf("completing redoip "
|
|
"of frame at ");
|
|
MR_printnondstackptr(fr);
|
|
printf(" (in saved copy)\n");
|
|
}
|
|
#endif
|
|
|
|
consumer->saved_state.gen_next = cur_gen + 1;
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("saved gen_next set to %d\n",
|
|
cur_gen + 1);
|
|
}
|
|
#endif
|
|
} else {
|
|
/*
|
|
** This is the nondet stack frame of some
|
|
** other generator.
|
|
*/
|
|
|
|
assert(MR_prevfr_slot(fr) != (stop_addr - 1));
|
|
|
|
*clobber_addr = (Word) ENTRY(do_fail);
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tablestackdebug) {
|
|
printf("clobbering redoip "
|
|
"of frame at ");
|
|
MR_printnondstackptr(fr);
|
|
printf(" (in saved copy)\n");
|
|
}
|
|
#endif
|
|
|
|
save_transient_registers();
|
|
make_subgoal_follow_leader(
|
|
MR_gen_stack[cur_gen].generator_table,
|
|
table);
|
|
restore_transient_registers();
|
|
}
|
|
|
|
cur_gen--;
|
|
} else if (cur_cut > 0 && fr == MR_cut_stack[cur_cut].frame) {
|
|
*clobber_addr = (Word) ENTRY(MR_table_nondet_commit);
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tablestackdebug) {
|
|
printf("committing redoip of frame at ");
|
|
MR_printnondstackptr(fr);
|
|
printf(" (in saved copy)\n");
|
|
}
|
|
#endif
|
|
|
|
cur_cut--;
|
|
} else {
|
|
*clobber_addr = (Word) ENTRY(do_fail);
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tablestackdebug) {
|
|
printf("clobbering redoip of frame at ");
|
|
MR_printnondstackptr(fr);
|
|
printf(" (in saved copy)\n");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("adding suspension node %p to table %p",
|
|
(void *) consumer, (void *) table);
|
|
printf(" at slot %p\n", table->consumer_list_tail);
|
|
}
|
|
#endif
|
|
|
|
assert(*(table->consumer_list_tail) == NULL);
|
|
listnode = table_allocate_bytes(sizeof(struct MR_ConsumerListNode));
|
|
*(table->consumer_list_tail) = listnode;
|
|
table->consumer_list_tail = &(listnode->next);
|
|
listnode->item = consumer;
|
|
listnode->next = NULL;
|
|
}
|
|
fail();
|
|
END_MODULE
|
|
|
|
MR_Subgoal *MR_cur_leader;
|
|
|
|
/*
|
|
** The procedure defined below restores answers to suspended consumers.
|
|
** It works by restoring the consumer state saved by the consumer's call
|
|
** to table_nondet_suspend. By restoring such states and then returning
|
|
** answers, table_nondet_resume is essentially returning answers out of
|
|
** the call to table_nondet_suspend, not out of the call to
|
|
** table_nondet_resume.
|
|
**
|
|
** 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).
|
|
*/
|
|
|
|
Define_extern_entry(mercury__table_nondet_resume_1_0);
|
|
Declare_label(mercury__table_nondet_resume_1_0_ChangeLoop);
|
|
Declare_label(mercury__table_nondet_resume_1_0_ReachedFixpoint);
|
|
Declare_label(mercury__table_nondet_resume_1_0_LoopOverSuspensions);
|
|
Declare_label(mercury__table_nondet_resume_1_0_ReturnAnswer);
|
|
Declare_label(mercury__table_nondet_resume_1_0_RedoPoint);
|
|
|
|
MR_MAKE_PROC_LAYOUT(mercury__table_nondet_resume_1_0,
|
|
MR_DETISM_NON, MR_ENTRY_NO_SLOT_COUNT, MR_LONG_LVAL_TYPE_UNKNOWN,
|
|
MR_PREDICATE, "private_builtin", "table_nondet_resume", 1, 0);
|
|
MR_MAKE_INTERNAL_LAYOUT_WITH_ENTRY(
|
|
mercury__table_nondet_resume_1_0_ChangeLoop,
|
|
mercury__table_nondet_resume_1_0);
|
|
MR_MAKE_INTERNAL_LAYOUT_WITH_ENTRY(
|
|
mercury__table_nondet_resume_1_0_ReachedFixpoint,
|
|
mercury__table_nondet_resume_1_0);
|
|
MR_MAKE_INTERNAL_LAYOUT_WITH_ENTRY(
|
|
mercury__table_nondet_resume_1_0_LoopOverSubgoals,
|
|
mercury__table_nondet_resume_1_0);
|
|
MR_MAKE_INTERNAL_LAYOUT_WITH_ENTRY(
|
|
mercury__table_nondet_resume_1_0_LoopOverSuspensions,
|
|
mercury__table_nondet_resume_1_0);
|
|
MR_MAKE_INTERNAL_LAYOUT_WITH_ENTRY(
|
|
mercury__table_nondet_resume_1_0_ReturnAnswer,
|
|
mercury__table_nondet_resume_1_0);
|
|
MR_MAKE_INTERNAL_LAYOUT_WITH_ENTRY(
|
|
mercury__table_nondet_resume_1_0_RedoPoint,
|
|
mercury__table_nondet_resume_1_0);
|
|
MR_MAKE_INTERNAL_LAYOUT_WITH_ENTRY(
|
|
mercury__table_nondet_resume_1_0_RestartPoint,
|
|
mercury__table_nondet_resume_1_0);
|
|
|
|
BEGIN_MODULE(table_nondet_resume_module)
|
|
init_entry_sl(mercury__table_nondet_resume_1_0);
|
|
MR_INIT_PROC_LAYOUT_ADDR(mercury__table_nondet_resume_1_0);
|
|
init_label_sl(mercury__table_nondet_resume_1_0_ChangeLoop);
|
|
init_label_sl(mercury__table_nondet_resume_1_0_ReachedFixpoint);
|
|
init_label_sl(mercury__table_nondet_resume_1_0_LoopOverSubgoals);
|
|
init_label_sl(mercury__table_nondet_resume_1_0_LoopOverSuspensions);
|
|
init_label_sl(mercury__table_nondet_resume_1_0_ReturnAnswer);
|
|
init_label_sl(mercury__table_nondet_resume_1_0_RedoPoint);
|
|
init_label_sl(mercury__table_nondet_resume_1_0_RestartPoint);
|
|
BEGIN_CODE
|
|
|
|
Define_entry(mercury__table_nondet_resume_1_0);
|
|
MR_cur_leader = MR_top_generator_table();
|
|
|
|
if (MR_cur_leader->leader != NULL) {
|
|
/*
|
|
** The predicate that called table_nondet_resume
|
|
** 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_nondet_resume fails\n");
|
|
}
|
|
#endif
|
|
|
|
(void) MR_pop_generator();
|
|
redo();
|
|
}
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("table_nondet_resume enter: current leader is %p\n",
|
|
MR_cur_leader);
|
|
}
|
|
#endif
|
|
|
|
if (MR_cur_leader->resume_info != NULL) {
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("using existing resume info %p\n",
|
|
MR_cur_leader->resume_info);
|
|
}
|
|
#endif
|
|
} else {
|
|
MR_cur_leader->resume_info = make(MR_ResumeInfo);
|
|
|
|
save_transient_registers();
|
|
save_state(&(MR_cur_leader->resume_info->leader_state),
|
|
MR_cur_leader->generator_maxfr,
|
|
MR_cur_leader->generator_sp,
|
|
"resumption", "generator");
|
|
restore_transient_registers();
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("creating new resume info %p\n",
|
|
MR_cur_leader->resume_info);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MR_cur_leader->resume_info->changed = TRUE;
|
|
|
|
Define_label(mercury__table_nondet_resume_1_0_ChangeLoop);
|
|
|
|
if (MR_cur_leader->resume_info->changed) {
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("changed flag set\n");
|
|
}
|
|
#endif
|
|
} else {
|
|
MR_SubgoalList table_list;
|
|
|
|
for (table_list = MR_cur_leader->resume_info->subgoal_list;
|
|
table_list != NULL; table_list = table_list->next)
|
|
{
|
|
if (table_list->item->num_committed_ans
|
|
!= table_list->item->num_ans)
|
|
{
|
|
MR_cur_leader->resume_info->changed = TRUE;
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("table %p has new answers\n",
|
|
table_list->item);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! MR_cur_leader->resume_info->changed) {
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("no more changes\n");
|
|
}
|
|
#endif
|
|
GOTO_LABEL(mercury__table_nondet_resume_1_0_ReachedFixpoint);
|
|
}
|
|
|
|
MR_cur_leader->resume_info->subgoal_list = MR_cur_leader->followers;
|
|
|
|
/* For each of the subgoals on our list of followers */
|
|
Define_label(mercury__table_nondet_resume_1_0_LoopOverSubgoals);
|
|
|
|
if (MR_cur_leader->resume_info->subgoal_list == NULL) {
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("no more subgoals in the followers list\n");
|
|
}
|
|
#endif
|
|
|
|
GOTO_LABEL(mercury__table_nondet_resume_1_0_ChangeLoop);
|
|
}
|
|
|
|
MR_cur_leader->resume_info->cur_subgoal =
|
|
MR_cur_leader->resume_info->subgoal_list->item;
|
|
MR_cur_leader->resume_info->subgoal_list =
|
|
MR_cur_leader->resume_info->subgoal_list->next;
|
|
|
|
MR_cur_leader->resume_info->consumer_list =
|
|
MR_cur_leader->resume_info->cur_subgoal->consumer_list;
|
|
|
|
MR_cur_leader->resume_info->changed = FALSE;
|
|
MR_cur_leader->resume_info->cur_subgoal->num_committed_ans =
|
|
MR_cur_leader->resume_info->cur_subgoal->num_ans;
|
|
|
|
/* For each of the suspended nodes for cur_subgoal */
|
|
Define_label(mercury__table_nondet_resume_1_0_LoopOverSuspensions);
|
|
|
|
if (MR_cur_leader->resume_info->consumer_list == NULL) {
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("no more suspensions for current subgoal\n");
|
|
}
|
|
#endif
|
|
GOTO_LABEL(mercury__table_nondet_resume_1_0_LoopOverSubgoals);
|
|
}
|
|
|
|
MR_cur_leader->resume_info->cur_consumer =
|
|
MR_cur_leader->resume_info->consumer_list->item;
|
|
MR_cur_leader->resume_info->consumer_list =
|
|
MR_cur_leader->resume_info->consumer_list->next;
|
|
|
|
MR_cur_leader->resume_info->cur_consumer_answer_list =
|
|
*(MR_cur_leader->resume_info->cur_consumer->
|
|
remaining_answer_list_ptr);
|
|
|
|
if (MR_cur_leader->resume_info->cur_consumer_answer_list == NULL) {
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("no first answer for this suspension\n");
|
|
}
|
|
#endif
|
|
GOTO_LABEL(mercury__table_nondet_resume_1_0_LoopOverSuspensions);
|
|
}
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("resuming consumer %p from table %p\n",
|
|
(void *) MR_cur_leader->resume_info->cur_consumer,
|
|
(void *) MR_cur_leader->resume_info->cur_subgoal);
|
|
}
|
|
#endif
|
|
|
|
save_transient_registers();
|
|
restore_state(
|
|
&(MR_cur_leader->resume_info->cur_consumer->saved_state),
|
|
"resumption", "consumer");
|
|
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));
|
|
|
|
MR_gen_next = MR_cur_leader->resume_info->leader_state.gen_next;
|
|
MR_redoip_slot(MR_maxfr) =
|
|
LABEL(mercury__table_nondet_resume_1_0_RedoPoint);
|
|
MR_redofr_slot(MR_maxfr) = MR_maxfr;
|
|
MR_based_framevar(MR_maxfr, 1) = (Word) MR_cur_leader;
|
|
|
|
Define_label(mercury__table_nondet_resume_1_0_ReturnAnswer);
|
|
|
|
/*
|
|
** Return the next answer in MR_cur_leader->resume_info->
|
|
** cur_consumer_answer_list 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.
|
|
*/
|
|
|
|
|
|
r1 = (Word) &MR_cur_leader->resume_info->cur_consumer_answer_list->
|
|
answer_data;
|
|
|
|
MR_cur_leader->resume_info->cur_consumer->remaining_answer_list_ptr =
|
|
&(MR_cur_leader->resume_info->cur_consumer_answer_list->
|
|
next_answer);
|
|
|
|
MR_cur_leader->resume_info->cur_consumer_answer_list =
|
|
MR_cur_leader->resume_info->cur_consumer_answer_list->
|
|
next_answer;
|
|
|
|
/*
|
|
** 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.
|
|
*/
|
|
succeed();
|
|
|
|
Define_label(mercury__table_nondet_resume_1_0_RedoPoint);
|
|
update_prof_current_proc(LABEL(mercury__table_nondet_resume_1_0));
|
|
|
|
/*
|
|
** 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);
|
|
|
|
Define_label(mercury__table_nondet_resume_1_0_RestartPoint);
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("cur_consumer_answer_list: %p\n",
|
|
MR_cur_leader->resume_info->cur_consumer_answer_list);
|
|
printf("*cur_consumer->remaining_answer_list_ptr: %p\n",
|
|
*(MR_cur_leader->resume_info->cur_consumer->
|
|
remaining_answer_list_ptr));
|
|
}
|
|
#endif
|
|
|
|
if (MR_cur_leader->resume_info->cur_consumer_answer_list != NULL) {
|
|
GOTO_LABEL(mercury__table_nondet_resume_1_0_ReturnAnswer);
|
|
}
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("no more unreturned answers for this suspension\n");
|
|
}
|
|
#endif
|
|
|
|
if (MR_cur_leader->resume_info->cur_subgoal->num_committed_ans
|
|
!= MR_cur_leader->resume_info->cur_subgoal->num_ans)
|
|
{
|
|
MR_cur_leader->resume_info->changed = TRUE;
|
|
}
|
|
|
|
GOTO_LABEL(mercury__table_nondet_resume_1_0_LoopOverSuspensions);
|
|
|
|
Define_label(mercury__table_nondet_resume_1_0_ReachedFixpoint);
|
|
{
|
|
MR_SubgoalList table_list;
|
|
|
|
for (table_list = MR_cur_leader->followers;
|
|
table_list != NULL; table_list = table_list->next)
|
|
{
|
|
#ifdef MR_TABLE_DEBUG
|
|
if (MR_tabledebug) {
|
|
printf("marking table %p complete\n",
|
|
table_list->item);
|
|
}
|
|
#endif
|
|
|
|
table_list->item->status = MR_SUBGOAL_COMPLETE;
|
|
table_list->item->num_committed_ans = -1;
|
|
}
|
|
}
|
|
|
|
/* Restore the state we had when table_nondet_resume was called */
|
|
save_transient_registers();
|
|
restore_state(&(MR_cur_leader->resume_info->leader_state),
|
|
"resumption", "generator");
|
|
restore_transient_registers();
|
|
|
|
/* XXX we should free this cell and its components */
|
|
MR_cur_leader->resume_info = NULL;
|
|
|
|
/* We are done with this generator */
|
|
(void) MR_pop_generator();
|
|
|
|
proceed();
|
|
END_MODULE
|
|
|
|
Define_extern_entry(MR_table_nondet_commit);
|
|
BEGIN_MODULE(table_nondet_commit_module)
|
|
init_entry_ai(MR_table_nondet_commit);
|
|
BEGIN_CODE
|
|
Define_entry(MR_table_nondet_commit);
|
|
MR_commit_cut();
|
|
fail();
|
|
END_MODULE
|
|
|
|
#endif
|
|
|
|
/* Ensure that the initialization code for the above modules gets to run. */
|
|
/*
|
|
INIT mercury_sys_init_table_modules
|
|
*/
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL
|
|
extern ModuleFunc table_nondet_suspend_module;
|
|
extern ModuleFunc table_nondet_resume_module;
|
|
extern ModuleFunc table_nondet_commit_module;
|
|
#endif
|
|
|
|
void mercury_sys_init_table_modules(void);
|
|
/* extra declaration to suppress gcc -Wmissing-decl warning */
|
|
void mercury_sys_init_table_modules(void) {
|
|
#ifdef MR_USE_MINIMAL_MODEL
|
|
table_nondet_suspend_module();
|
|
table_nondet_resume_module();
|
|
table_nondet_commit_module();
|
|
#endif
|
|
}
|