Tabling round 2 [3/3]
Fergus Henderson
fjh at cs.mu.OZ.AU
Wed Mar 18 18:35:33 AEDT 1998
On 13-Mar-1998, Oliver Hutchison <ohutch at students.cs.mu.OZ.AU> wrote:
> New File: compiler/table_gen.m
...
> % This module transforms HLDS code to a form that allows tabled evaluation,
> % minimal model evaluation and loop detection.
> %
> % Main author: ohutch
> %
> % The tabling transformation adds calls to several tabling predicates as
> % well as restructuring the HLDS to implement answer clause resolution,
> % suspension and loop detection.
It would be good to cite a paper which describes this stuff in more detail,
e.g. Kostis' thesis.
> table_gen__process_module(Module0, Module) :-
> module_info_preds(Module0, Preds0),
> map__keys(Preds0, PredIds),
> table_gen__process_preds(PredIds, Module0, Module).
>
> :- pred table_gen__process_preds(list(pred_id), module_info, module_info).
> :- mode table_gen__process_preds(in, in, out) is det.
>
> table_gen__process_preds([], Module, Module).
> table_gen__process_preds([PredId | PredIds], Module0, Module) :-
> table_gen__process_pred(PredId, Module0, Module1),
> table_gen__process_preds(PredIds, Module1, Module).
>
> :- pred table_gen__process_pred(pred_id, module_info, module_info).
> :- mode table_gen__process_pred(in, in, out) is det.
>
> table_gen__process_pred(PredId, Module0, Module) :-
> module_info_pred_info(Module0, PredId, PredInfo),
> pred_info_procids(PredInfo, ProcIds),
> table_gen__process_procs(PredId, ProcIds, Module0, Module).
>
> :- pred table_gen__process_procs(pred_id, list(proc_id),
> module_info, module_info).
> :- mode table_gen__process_procs(in, in, in, out) is det.
> table_gen__process_procs(_PredId, [], Module, Module).
> table_gen__process_procs(PredId, [ProcId | ProcIds], Module0,
> Module) :-
> module_info_preds(Module0, PredTable),
> map__lookup(PredTable, PredId, PredInfo),
> pred_info_procedures(PredInfo, ProcTable),
> map__lookup(ProcTable, ProcId, ProcInfo),
Any particular reason why this code doesn't use the routines
in passes_aux.m?
> table_gen__create_new_det_goal(EvalMethod, OrigGoal, Module, HeadVars,
> HeadVarModes, VarTypes0, VarTypes, VarSet0, VarSet, Goal) :-
...
> (
> (
> EvalMethod = eval_memo
> ;
> EvalMethod = eval_loop_check
> )
> ->
> (
> EvalMethod = eval_loop_check
> ->
> SaveAnsGoal = DoneWorkingGoal
> ;
> SaveAnsGoal = SaveAnsGoal0
> ),
... lots of code ...
> ;
> error(
> "table_gen__create_new_det_goal: unsupported evaluation model")
> ),
It would be simpler to write this as
( EvalMethod = eval_loop_check ->
SaveAnsGoal = DoneWorkingGoal
; EvalMethod = eval_loop_check ->
SaveAnsGoal = SaveAnsGoal0
;
error(
"table_gen__create_new_det_goal: unsupported evaluation model")
),
... lots of code ...
> GoalEx = pragma_c_code(will_not_call_mercury, PredId, ProcId,
> [TableVar], [yes("TableVar" - TableVarMode)],
> [TableVarType], ordinary(
> " {
> static Word Table = 0;
> TableVar = (Word)&Table;
> }
> ",
I suggest s/Table/table/, to conform to our C coding guidelines.
Actually these names should have a prefix to avoid
name clashes. The prefix should probably be `MC_'.
(MR_ for names in the runtime, ML_ for names in the library,
and MC_ for names generated by the compiler.)
So I suggest
s/Table/MR_table/
s/TableVar/MC_TableVar/
> gen_lookup_call_for_type(TypeCat, Type, TableVar, ArgVar, Module, VarTypes0,
> VarTypes, VarSet0, VarSet, NextTableVar, Goal) :-
> (
> TypeCat = enum_type
> ->
> (
> type_to_type_id(Type, TypeId, _)
> ->
> module_info_types(Module, TypeDefnTable),
> map__lookup(TypeDefnTable, TypeId, TypeDefn),
> hlds_data__get_type_defn_body(TypeDefn, TypeBody),
> (
> TypeBody = du_type(Ctors, _, yes, no)
> ->
> list__length(Ctors, EnumRange)
> ;
> error(
> "gen_lookup_call_for_type: unsupported type in tabled predicate/function")
> ),
"unsupported" is misleading here; I think you mean "impossible".
Probably the message here should be
"gen_lookup_call_for_type: enum type is not du_type?")
> gen_restore_call_for_type(TypeCat, _Type, TableVar, Var, OffsetVar, Module,
> Goal) :-
> (
> (
> TypeCat = pred_type
> ;
> TypeCat = enum_type
> ;
> TypeCat = polymorphic_type
> ;
> TypeCat = user_type
> )
That condition is duplicate several times;
you should factor out the common code into a subroutine.
> generate_call(PredName, Args, Detism, Feature, InstMap, Module, Goal) :-
> list__length(Args, Arity),
> module_info_get_predicate_table(Module, PredTable),
> (
> predicate_table_search_pred_m_n_a(PredTable,
> unqualified("mercury_builtin"), PredName, Arity,
> [PredId0])
You should use `mercury_private_builtin_module/1' from modules.m
rather than hard-coding "mercury_builtin".
...
> Call = call(PredId, ProcId, Args, not_builtin, no, qualified(
> unqualified("mercury_builtin"), PredName)),
Likewise here.
> :- pred get_table_var_type(type).
> :- mode get_table_var_type(out) is det.
>
> get_table_var_type(Type) :-
> construct_type(qualified(unqualified("mercury_builtin"),
> "c_pointer") - 0, [], Type).
Ditto.
> case TYPELAYOUT_UNIV_VALUE:
> {
> Table = (Word**) MR_TABLE_TYPE_INFO(Table,
> data_value[UNIV_OFFSET_FOR_TYPEINFO]);
> Table = (Word**) MR_TABLE_ANY(Table,
> data_value[UNIV_OFFSET_FOR_TYPEINFO],
> data_value[UNIV_OFFSET_FOR_DATA]);
> break;
> }
> case TYPELAYOUT_PREDICATE_VALUE:
> {
> Word args = data_value[0];
>
> Table = (Word **) MR_TABLE_WORD(Table, args);
> Table = (Word **) MR_TABLE_WORD(Table, data_value[1]);
>
> for (i = 0; i < args; i++) {
> Table = (Word **) MR_TABLE_ANY(Table,
> (Word *) type_info[i +
> TYPEINFO_OFFSET_FOR_PRED_ARGS],
> data_value[i+2]);
> }
> }
The strange layout of the curly braces here is due to inconsistent
use of spaces and tables. Please fix this.
> Table = (Word**) MR_TABLE_TAG(Table, data_tag);
> Table = (Word**) MR_TABLE_ENUM(Table, functors,
> (Word)data_value);
s/(Word)/(Word) /
> static Word *
> make_type_info(Word *term_type_info, Word *arg_pseudo_type_info,
> bool *allocated)
As I said last time around:
| But actually, aren't those routines exactly the same as the ones
| of the same name somewhere else? If so, you should make them
| `extern' rather than `static' and just define them in only one place.
And indeed, it appears that make_type_info() here is exactly the
same as in runtime/mercury_deep_copy.c.
If so, you should export the version from mercury_deep_copy.c
and use that, rather than duplicating the code.
(It might make sense to move the function from mercury_deep_copy.c
to mercury_type_info.c.)
> New File: runtime/mercury_table_any.h
> ===================================================================
> #ifndef _MERCURY_TABLE_ANY_H
> #define _MERCURY_TABLE_ANY_H
>
> /*
> ** This function will lookup or insert any type of value into a
> ** table. It uses the provided type_info to extract the necessary
> ** info to do this.
> */
> Word ** MR_table_type(Word *type_info, Word data_value,
> Word ** Table);
What's the semantics of the return value?
> #define ELEMENT(Table, Key) ((Word**)&(Table[Key]))
You should use an extra set of parentheses:
#define ELEMENT(Table, Key) ((Word**)&((Table)[Key]))
^ ^
Otherwise something like
Element(ptr + offset, key);
will parse incorrectly.
> /*
> ** MR_int_index_lookup_or_add() : This function maintains a simple indexed
> ** table of size Range.
> */
> Word **
> MR_int_index_lookup_or_add(Word ** T, Integer Key,
> Integer Range)
s/T/t/
s/Key/key/
s/Range/range/
I guess the semantics of T(able), Key and Range are clear from the names,
but what's the semantics of the return value?
It would be good to use a typedef instead of `Word **'.
> /* Amount the table is allowed to fill. Must be less than 0.9 if you want
> even poor lookup times */
> #define MAX_EL_SIZE_RATIO 0.95
I don't quite understand the comment here.
If <0.9 is required for good performance, why is the value 0.95?
> /*
> * Prime numbers which are close to powers of 2. Used for choosing
> * the next size for a hash table.
> */
You should use
/*
** ... comments here ...
** ...
*/
instead of
/*
* ... comments here ...
* ...
*/
> /*
> * Return the next prime number greater than the number received.
> * If no such prime number can be found, compute an approximate one.
> */
Ditto.
> /* 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.
> */
Ditto.
> /* 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
> **/
Ditto.
> /* 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
> **/
Ditto.
> /* 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
> **/
Ditto.
> New File: runtime/mercury_table_int_float_string.h
...
> /* 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
> **/
Ditto.
Also please finish sentences with a full stop.
> /* 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
> **/
Ditto.
>
> /* 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
> **/
Ditto.
> New File: runtime/mercury_table_type_info.c
...
> typedef struct _tree_node {
> Word * key;
> Word value;
> struct _tree_node * right;
> struct _tree_node * left;
> } TreeNode;
s/_tree_node/TreeNode_struct/g
> New File: runtime/mercury_tabling.h
...
> #ifndef _MERCURY_TABLING_H
> #define _MERCURY_TABLING_H
s/_MERCURY_TABLING_H/MERCURY_TABLING_H/g
According to the ANSI/ISO C standard,
names starting with _[A-Z] are reserved for use
by the C implementation.
> #define MR_TABLE_ARRAY(Table, Array) \
> (Word**)0;fatal_error("Tabling of arrays not yet supported")
You shoudl write that as
(fatal_error("Tabling of arrays not yet supported"), (Word**)0)
This will ensure that fatal_error() gets called before the dummy
value (Word**)0 is used.
Can't use use the MR_TABLE_ANY macro in this case?
> #ifdef CONSERVATIVE_GC
>
> #define MR_TABLE_SAVE_ANSWER(Offset, AnswerBlock, Value, TypeInfo) \
> do { \
> (*((Word**)AnswerBlock))[Offset] = Value; \
> } while(0)
>
> #else /* not CONSERVATIVE_GC */
>
> #define MR_TABLE_SAVE_ANSWER(Offset, AnswerBlock, Value, TypeInfo) \
> do { \
> (*((Word**)AnswerBlock))[Offset] = \
> deep_copy(Value, &TypeInfo, NULL, NULL); \
> restore_transient_registers(); \
> } while(0)
Where's the corresponding call to save_transient_registers()?
> #define table_allocate(Size) \
> 0;fatal_error("tabling only supported in conservative gc grades")
> #define table_reallocate(Pointer, Size) \
> 0;fatal_error("tabling only supported in conservative gc grades")
These should be changed in a manner similar to that described for
the other call to fatal_error() above.
> static void table_printf(const char*, ...);
> static void table_printf(const char *A, ...)
> {
Why the duplicate declarations here?
Also, s/A/a/
Or better, s/A/format/
OK, that's the end of my review for this round. The number of issues
uncovered in this round means that we will need another round.
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3 | -- the last words of T. S. Garp.
More information about the developers
mailing list