Files
mercury/runtime/mercury_table_int_float_string.c
Oliver Hutchison bcf7dbf9f8 Add support for tabling.
Estimated hours taken: 250

Add support for tabling.

This change allows for model_det, model_semidet and model_non memoing,
minimal model and loop detection tabling.

compiler/base_type_layout.m:
	Update comments to reflect new runtime naming standard.

compiler/det_analysis.m:
	Allow tabling to change the result of det analysis. This is
	necessary in the case of minimal model tabling which can
	turn a det procedure into a semidet one.

compiler/det_report.m:
compiler/hlds_data.m:
	Add code to report error messages for various non compatible
	tabling methods and determinism.

compiler/hlds_out.m:
compiler/modules.m:
	Remove reference to the old memo marker.

compiler/hlds_pred.m:
	Create new type (eval_method) to define which of the available
	evaluation methods should be used each procedure.
	Add new field to the proc_info structure.
	Add several new predicates relating to the new eval_method type.

compiler/inlining.m:
compiler/intermod.m:
	Make sure only procedures with normal evaluation are inlined.

compiler/make_hlds.m:
	Add code to process new tabling pragmas.

compiler/mercury_compile.m:
	Call the tabling transformation code.

compiler/modes.m:
	Make sure that all procedures with non normal evaluation have
	no unique/partially instantiated modes. Produce error messages
	if they do. Support for partially instantiated modes is currently
	missing as it represents a large amount of work for a case that
	is currently not used.

compiler/module_qual.m:
compile/prog_data.m:
compiler/prog_io_pragma.m:
	Add three new pragma types:
		`memo'
		`loop_check'
		`minimal_model'
	and code to support them.

compiler/simplify.m:
	Don't report infinite recursion warning if a procedure has
	minimal model evaluation.

compiler/stratify.m:
	Change the stratification analyser so that it reports cases of
	definite non-stratification. Rather than reporting warnings for
	any code that is not definitely stratified.
	Remove reference to the old memo marker.

compiler/switch_detection.m:
	Fix a small bug where goal were being placed in reverse order.
	Call list__reverse on the list of goals.

compiler/table_gen.m:
	New module to do the actual tabling transformation.

compiler/notes/compiler_design.html:
	Document addition of new tabling pass to the compiler.

doc/reference_manual.texi:
	Fix mistake in example.

library/mercury_builtin.m:
	Add many new predicates for support of tabling.

library/std_util.m:
library/store.m:
	Move the functions :
		ML_compare_type_info
		ML_collapse_equivalences
		ML_create_type_info
	to the runtime.

runtime/mercury_deep_copy.c:
runtime/mercury_type_info.h:
runtime/mercury_type_info.c:
	Move the make_type_info function into the mercury_type_info module
	and make it public.

runtime/Mmakefile:
runtime/mercury_imp.h:
	Add references to new files added for tabling support.

runtime/mercury_string.h:
	Change hash macro so it does not cause a name clash with any
	variable called "hash".

runtime/mercury_type_info.c:
runtime/mercury_type_info.h:
	Add three new functions taken from the library :
		MR_compare_type_info
		MR_collapse_equivalences
		MR_create_type_info.

runtime/mercury_table_any.c:
runtime/mercury_table_any.h:
runtime/mercury_table_enum.c:
runtime/mercury_table_enum.h:
runtime/mercury_table_int_float_string.c:
runtime/mercury_table_int_float_string.h:
runtime/mercury_table_type_info.c:
runtime/mercury_table_type_info.h:
runtime/mercury_tabling.h:
	New modules for the support of tabling.
1998-05-15 07:09:29 +00:00

336 lines
7.0 KiB
C

/*
** Copyright (C) 1998 the University of Melbourne.
** this file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/
/*
** this module defines the int_hash_lookup_or_add(), float_hash_lookup_or_add()
** and string_hash_lookup_or_add() function.
*/
#include "mercury_imp.h"
/* 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(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.
*/
TrieNode
MR_int_hash_lookup_or_add(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(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.
*/
TrieNode
MR_float_hash_lookup_or_add(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(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.
*/
TrieNode
MR_string_hash_lookup_or_add(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(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(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(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;
}