mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-17 02:13:54 +00:00
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.
336 lines
7.0 KiB
C
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;
|
|
}
|