[m-dev.] for review: retry across I/O

Zoltan Somogyi zs at cs.mu.OZ.AU
Mon Dec 4 16:56:36 AEDT 2000


On 08-Nov-2000, Fergus Henderson <fjh at cs.mu.OZ.AU> wrote:
> On 06-Nov-2000, Zoltan Somogyi <zs at cs.mu.OZ.AU> wrote:
> >  eval_method_requires_ground_args(eval_normal) = no.
> >  eval_method_requires_ground_args(eval_loop_check) = yes.
> > +eval_method_requires_ground_args(eval_table_io) = yes.
> >  eval_method_requires_ground_args(eval_memo) = yes.
> >  eval_method_requires_ground_args(eval_minimal) = yes.
> 
> Hmm, the error message that you will get for violations of this will
> be from report_eval_method_requires_ground_args in modes.m, which will
> output the following message:
> 
> 	foo.m:123: Sorry, not implemented: `pragma table_io'
> 	foo.m:123:   declaration not allowed for procedure with
> 	foo.m:123:   partially instantiated modes.
> 
> This is misleading since there's no `pragma table_io' declaration.

Eval_method_requires_ground_args should only be called before the tabling
phase, so I made it call error if its argument is eval_table_io.

> > +	if (MR_DETISM_DET_STACK(level_layout->MR_sle_detism)) {
> > +		saved_io_counter = MR_based_stackvar(base_sp,
> > +			level_layout->MR_sle_maybe_io_seq);
> > +	} else {
> > +		saved_io_counter = MR_based_framevar(base_curfr,
> > +			level_layout->MR_sle_maybe_io_seq);
> > +	}
> 
> It would be cleaner if some of that was abstracted out into a macro
> (or function), i.e.
> 
> 	#define MR_based_slot(layout, base_sp, base_curfr, slot)	\
> 		( MR_DETISM_DET_STACK((layout)->MR_sle_detism) ?	\
> 			MR_based_stackvar((base_sp), (slot))		\
> 		:							\
> 			MR_based_framevar((base_curfr), (slot))		\
> 		)							\

I don't want to do this. With the original, verbose version, there is a good
chance that either the author or the reviewer will catch passing base_sp to
MR_based_framevar or vice versa. With the macro, the chance that someone will
catch passing base_sp and base_curfr in the wrong order is much smaller,
and the resulting bug would be tough to track down. Given that the macro
only saves a few lines of code, the tradeoff is not worth it.

I did make all the other suggestions. After the new log message, an interdiff
follows. Since this had some rejected hunks, I will post the whole new diff
in the next message.

Add tabling of I/O actions for the debugger.

compiler/options.m:
	Add a new option, --trace-table-io, that enables the tabling of I/O
	actions, and another, --trace-table-io-states, that governs whether the
	tabling includes the I/O state variables themselves. (You want to table
	these variables iff they contain meaningful information that is not
	stored in global variables.) These options are for developers only
	for now.

compiler/modules.m:
	Implicitly import table_builtin if --trace-table-io is specified.

compiler/prog_data.m:
	Add eval_table_io as a new eval method.

compiler/hlds_pred.m:
	Add a mechanism for checking whether a predicate has an input/output
	pair of io__state args.

	Extend the tables indexed by eval_method to handle eval_table_io.

compiler/hlds_out.m:
	Print the eval method in HLDS dumps.

compiler/table_gen.m:
	If a procedure has a pair of I/O state args and is defined using pragma
	C code that has the tabled_for_io marker, and --trace-table-io is
	specified, then perform I/O tabling on it and mark it as tabled.

compiler/notes/compiler_design.m:
	Document that table_gen.m can now change the evaluation methods of
	procedures (to eval_table_io).

compiler/stack_layout.m:
runtime/mercury_stack_layout.h:
	Add an extra field to proc layouts. If debugging is enabled and a
	procedure has I/O state arguments, this field gives the number of the
	stack slot which will be filled with the I/O action counter at the
	time of the call, so that on retry the debugger can reset the I/O
	action counter to this value.

compiler/trace.m:
	Add code to reserve and fill this stack slot.

	Make the order of fields in the trace_slots structure match the order
	in proc layouts.

compiler/code_info.m:
compiler/live_vars.m:
	Pass a module_info to trace__setup and trace__reserved_slots.

library/io.m:
	Mark the I/O primitives (i.e. procedures that are defined by pragma C
	code and do I/O) with the tabled_for_io feature. (See the discussion
	of I/O primitives in compiler/table_gen.m.)

	Standardize the formatting of predicates defined by pragma C codes.

library/table_builtin.m:
	Define the predicates that perform I/O tabling, to which calls are
	inserted in I/O tabled predicates. These depend on knowing what the
	maximum MR_Unsigned value is.

library/table_builtin.m:
runtime/mercury_tabling_macros.h:
	Table nodes implementing a simple kind of trie, which can also be
	viewed as a hash table with the hash function hash(n) = hash - start
	were already supported by mercury_tabling.c. They are used to
	implement I/O tabling, since I/O the tabled action numbers form a
	contiguous sequence. Now allow that functionality to be accessed
	from the library through macros.

runtime/mercury_trace_base.[ch]:
	Add the global variables required by I/O tabling.

trace/mercury_trace.c:
	Implement retry across I/O by resetting the I/O counter to the value
	it had on entry to the retried call.

trace/mercury_trace_internal.c:
	Add commands to start and stop I/O tabling. For now, they are for use
	by developers only and are undocumented; I expect they will change
	significantly before being let loose on users.

tests/debugger/tabled_read.{m,inp,exp,data}:
	A new test case to check retry across tabled I/O.

tests/debugger/Mmakefile:
	Enable the new test case.

--- configure.in	2000/10/30 10:53:19
+++ configure.in	2000/10/20 10:23:45	1.230
@@ -844,81 +844,10 @@
 
 if test "$mercury_cv_word_type" = int; then
 	AC_DEFINE_UNQUOTED(MR_INTEGER_LENGTH_MODIFIER, "")
-	AC_DEFINE_UNQUOTED(MR_INTEGER_MAX, INT_MAX)
-	AC_DEFINE_UNQUOTED(MR_UNSIGNED_MAX, UINT_MAX)
 elif test "$mercury_cv_word_type" = long; then
 	AC_DEFINE_UNQUOTED(MR_INTEGER_LENGTH_MODIFIER, "l")
-	AC_DEFINE_UNQUOTED(MR_INTEGER_MAX, LONG_MAX)
-	AC_DEFINE_UNQUOTED(MR_UNSIGNED_MAX, ULONG_MAX)
 elif test "$mercury_cv_word_type" = "long long"; then
 	AC_DEFINE_UNQUOTED(MR_INTEGER_LENGTH_MODIFIER, "ll")
-	AC_TRY_RUN([
-	#include <stdio.h>
-
-	extern	void	signed_size(FILE *fp);
-	extern	void	unsigned_size(FILE *fp);
-
-	int main(void)
-	{
-		FILE *fp;
-
-		fp = fopen("conftest.intmax", "w");
-		if (fp == NULL)
-			return 1;
-		signed_size(fp);
-
-		fp = fopen("conftest.uintmax", "w");
-		if (fp == NULL)
-			return 1;
-		unsigned_size(fp);
-
-		return 0;
-	}
-
-	void signed_size(FILE *fp)
-	{
-		long long cur, prev;
-
-		cur = 1;
-		prev = 0;
-
-		while (cur > prev) {
-			prev = cur;
-			cur = (cur << 1) + 1;
-		}
-
-		fprintf(fp, "%lld\n", prev);
-	}
-
-	void unsigned_size(FILE *fp)
-	{
-		long long unsigned cur, prev;
-
-		cur = 1;
-		prev = 0;
-
-		while (cur > prev) {
-			prev = cur;
-			cur = (cur << 1) + 1;
-		}
-
-		fprintf(fp, "%llu\n", prev);
-	}
-	}],
-	[mercury_cv_int_max=`cat conftest.intmax` ;
-		mercury_cv_uint_max=`cat conftest.uintmax`],
-	[mercury_cv_int_max=unknown ; mercury_cv_uint_max=unknown],
-	[mercury_cv_int_max=unknown ; mercury_cv_uint_max=unknown])
-	if test "$mercury_cv_int_max" = "unknown"; then
-		AC_MSG_ERROR("Cannot determine the maximum MR_Integer value.")
-	else
-		AC_DEFINE_UNQUOTED(MR_INTEGER_MAX, LONG_MAX)
-	fi
-	if test "$mercury_cv_uint_max" = "unknown"; then
-		AC_MSG_ERROR("Cannot determine the maximum MR_Unsigned value.")
-	else
-		AC_DEFINE_UNQUOTED(MR_UNSIGNED_MAX, ULONG_MAX)
-	fi
 else
 	AC_MSG_ERROR("Cannot determine the length modifier for the MR_Integer type.")
 fi
--- compiler/hlds_out.m
+++ compiler/hlds_out.m
@@ -294,2 +294 @@
-hlds_out__cons_id_to_string(tabling_pointer_const(_, _),
-	"<tabling_pointer>").
+hlds_out__cons_id_to_string(tabling_pointer_const(_, _), "<tabling_pointer>").
@@ -308 +307,2 @@
-hlds_out__cons_id_to_string(tabling_pointer_const(_, _), "<tabling_pointer>").
+hlds_out__cons_id_to_string(tabling_pointer_const(_, _),
+	"<tabling_pointer>").
@@ -2876 +2875,0 @@
-	{ proc_info_eval_method(Proc, EvalMethod) },
@@ -2891,0 +2891 @@
+	{ proc_info_eval_method(Proc, EvalMethod) },
@@ -2925,4 +2924,0 @@
-	io__write_string("% eval method: "),
-	hlds_out__write_eval_method(EvalMethod),
-	io__write_string("\n"),
-
@@ -2943,0 +2940,8 @@
+	( { EvalMethod = eval_normal } ->
+		[]
+	;
+		io__write_string("% eval method: "),
+		hlds_out__write_eval_method(EvalMethod),
+		io__write_string("\n")
+	),
+
@@ -3063,0 +3068,8 @@
+:- pred hlds_out__write_indent(int, io__state, io__state).
+:- mode hlds_out__write_indent(in, di, uo) is det.
+
+
+	io__write_string("can_fail").
+hlds_out__write_can_fail(cannot_fail) -->
+	io__write_string("cannot_fail").
+
@@ -3074,8 +3085,0 @@
-
-:- pred hlds_out__write_indent(int, io__state, io__state).
-:- mode hlds_out__write_indent(in, di, uo) is det.
-
-
-	io__write_string("can_fail").
-hlds_out__write_can_fail(cannot_fail) -->
-	io__write_string("cannot_fail").
--- compiler/hlds_pred.m
+++ compiler/hlds_pred.m
@@ -1508,12 +1507,0 @@
-	% If the procedure has a pair of io:state arguments, return the index
-	% of those arguments. The first output argument gives the index of the
-	% input state, the second the output state.
-	%
-	% Note that the automatically constructed unify, index and compare
-	% procedures for the io:state type are not counted as having io:state
-	% args, since they do not fall into the scheme of one input and one
-	% output arg. Since they should never be called, this should not
-	% matter.
-:- pred proc_info_has_io_state_args(module_info::in, proc_info::in,
-	int::out, int::out) is semidet.
-
@@ -1528,0 +1517,15 @@
+	% If the procedure has a input/output pair of io__state arguments,
+	% return the positions of those arguments in the argument list.
+	% The positions are given as argument numbers, with the first argument
+	% in proc_info_headvars being position 1, and so on. The first output
+	% argument gives the position of the input state, the second the
+	% position of the output state.
+	%
+	% Note that the automatically constructed unify, index and compare
+	% procedures for the io:state type are not counted as having io:state
+	% args, since they do not fall into the scheme of one input and one
+	% output arg. Since they should never be called, this should not
+	% matter.
+:- pred proc_info_has_io_state_pair(module_info::in, proc_info::in,
+	int::out, int::out) is semidet.
+
@@ -2008 +2011 @@
-proc_info_has_io_state_args(ModuleInfo, ProcInfo, InArgNum, OutArgNum) :-
+proc_info_has_io_state_pair(ModuleInfo, ProcInfo, InArgNum, OutArgNum) :-
@@ -2014 +2017 @@
-	proc_info_has_io_state_args_2(HeadVarsModes, ModuleInfo, VarTypes,
+	proc_info_has_io_state_pair_2(HeadVarsModes, ModuleInfo, VarTypes,
@@ -2019,2 +2021,0 @@
-	; MaybeIn = no, MaybeOut = no ->
-		fail
@@ -2022,4 +2022,0 @@
-		% We must be processing the automatically generated index
-		% procedure for the io_state type. Since this predicate
-		% should never be called, any transformation meant for
-		% procedures with io:states would be inappropriate.
@@ -2029 +2026 @@
-:- pred proc_info_has_io_state_args_2(assoc_list(prog_var, mode)::in,
+:- pred proc_info_has_io_state_pair_2(assoc_list(prog_var, mode)::in,
@@ -2032 +2029 @@
-	maybe(int)::out, maybe(int)::out) is det.
+	maybe(int)::out, maybe(int)::out) is semidet.
@@ -2034 +2031 @@
-proc_info_has_io_state_args_2([], _, _, _,
+proc_info_has_io_state_pair_2([], _, _, _,
@@ -2036 +2033 @@
-proc_info_has_io_state_args_2([Var - Mode | VarModes], ModuleInfo, VarTypes,
+proc_info_has_io_state_pair_2([Var - Mode | VarModes], ModuleInfo, VarTypes,
@@ -2040,5 +2037 @@
-		type_to_type_id(VarType, TypeCtor, []),
-		type_util__type_id_module(ModuleInfo, TypeCtor, 
-			unqualified("io")),
-		type_util__type_id_name(ModuleInfo, TypeCtor, "state"),
-		type_util__type_id_arity(ModuleInfo, TypeCtor, 0)
+		type_util__type_is_io_state(VarType)
@@ -2047 +2040,2 @@
-			( MaybeIn0 = no ->
+			(
+				MaybeIn0 = no,
@@ -2051 +2045,3 @@
-				% We must be processing the automatically
+				MaybeIn0 = yes(_),
+				% Procedures with two input arguments
+				% of type io__state (e.g. the automatically
@@ -2053,7 +2049,4 @@
-				% for the io_state type. Since these predicates
-				% should never be called, any transformation
-				% meant for procedures with io:states would
-				% be inappropriate. We therefore effectively
-				% forget both io:state arguments.
-				MaybeIn1 = no,
-				MaybeOut1 = MaybeOut0
+				% for the io__state type) do not fall into
+				% the one input/one output pattern we are
+				% looking for.
+				fail
@@ -2062,4 +2055,11 @@
-			require(unify(MaybeOut0, no),
-				"proc_info_has_io_state_args_2: two io:state outputs"),
-			MaybeOut1 = yes(ArgNum),
-			MaybeIn1 = MaybeIn0
+			(
+				MaybeOut0 = no,
+				MaybeOut1 = yes(ArgNum),
+				MaybeIn1 = MaybeIn0
+			;
+				MaybeOut0 = yes(_),
+				% Procedures with two output arguments of
+				% type io__state do not fall into the one
+				% input/one output pattern we are looking for.
+				fail
+			)
@@ -2067 +2067 @@
-			error("proc_info_has_io_state_args_2: bad io:state mode")
+			fail
@@ -2073 +2073 @@
-	proc_info_has_io_state_args_2(VarModes, ModuleInfo, VarTypes,
+	proc_info_has_io_state_pair_2(VarModes, ModuleInfo, VarTypes,
@@ -2359 +2359 @@
-:- mode valid_code_model_for_eval_method(in, out) is multi.
+:- mode valid_code_model_for_eval_method(in, out) is multidet.
@@ -2401 +2400,0 @@
-valid_determinism_for_eval_method(eval_memo, _).
@@ -2402,0 +2402,3 @@
+valid_determinism_for_eval_method(eval_table_io, _) :-
+	error("valid_determinism_for_eval_method called after tabling phase").
+valid_determinism_for_eval_method(eval_memo, _).
@@ -2407 +2408,0 @@
-eval_method_to_string(eval_memo,		"memo").
@@ -2408,0 +2410,2 @@
+eval_method_to_string(eval_table_io,		"table_io").
+eval_method_to_string(eval_memo,		"memo").
@@ -2412,0 +2416 @@
+eval_method_needs_stratification(eval_table_io) = no.
@@ -2417,0 +2422 @@
+eval_method_has_per_proc_tabling_pointer(eval_table_io) = no.
@@ -2422,0 +2428 @@
+eval_method_requires_tabling_transform(eval_table_io) = yes.
@@ -2427,0 +2434 @@
+eval_method_requires_ground_args(eval_table_io) = yes.
@@ -2432,0 +2440 @@
+eval_method_destroys_uniqueness(eval_table_io) = no.
@@ -2437,0 +2446 @@
+eval_method_change_determinism(eval_table_io, Detism, Detism).
--- compiler/live_vars.m
+++ compiler/live_vars.m
@@ -67 +67 @@
-	trace__reserved_slots(ModuleInfo, ProcInfo1, Globals, NumReservedSlots,
+	trace__reserved_slots(ProcInfo1, Globals, NumReservedSlots,
@@ -82 +82 @@
-	trace__reserved_slots(ProcInfo1, Globals, NumReservedSlots,
+	trace__reserved_slots(ModuleInfo, ProcInfo1, Globals, NumReservedSlots,
--- compiler/options.m
+++ compiler/options.m
@@ -491,2 +490,0 @@
-	trace_table_io		-	bool(no),
-	trace_table_io_states	-	bool(no),
@@ -504,0 +503,2 @@
+	trace_table_io		-	bool(no),
+	trace_table_io_states	-	bool(no),
@@ -882,2 +881,0 @@
-long_option("trace-table-io",		trace_table_io).
-long_option("trace-table-io-states",	trace_table_io_states).
@@ -902,0 +901,2 @@
+long_option("trace-table-io",		trace_table_io).
+long_option("trace-table-io-states",	trace_table_io_states).
@@ -1687,3 +1686,0 @@
-		"--trace-table-io",
-		"\tEnable the tabling of I/O actions, to allow the debugger",
-		"\tto execute retry commands across I/O actions.",
@@ -1713,0 +1711,7 @@
+%		"--trace-table-io",
+%		"\tEnable the tabling of I/O actions, to allow the debugger",
+%		"\tto execute retry commands across I/O actions.",
+%		"--trace-table-io-states",
+%		"\tWhen tabling I/O actions, table the io__state arguments",
+%		"\ttogether with the others. This should be required iff",
+%		"\tvalues of type io__state actually contain information.",
--- compiler/stack_layout.m
+++ compiler/stack_layout.m
@@ -430 +430,2 @@
-	incr_hp_atomic(tmp, (ArenaSize + sizeof(MR_Word)) / sizeof(MR_Word));
+	MR_incr_hp_atomic(tmp,
+		(ArenaSize + sizeof(MR_Word)) / sizeof(MR_Word));
--- compiler/table_gen.m
+++ compiler/table_gen.m
@@ -157,2 +157,3 @@
-% Example of transformation for tabling I/O, for I/O primitives that have
-% tabled_for_io:
+% Example of transformation for tabling I/O, for I/O primitives (i.e.
+% predicates defined by pragma c_code that take an input/output pair of
+% io_state arguments) that have the tabled_for_io feature:
@@ -181 +182 @@
-%				impure table_restore_any_ans(T, 1, S)
+%				table_io_copy_io_state(S0, S)
@@ -189,3 +190,2 @@
-%				impure table_io_create_ans_block(T, 2, AB),
-%				impure table_save_string_ans(AB, 0, B)
-%				impure table_save_any_ans(AB, 1, S)
+%				impure table_io_create_ans_block(T, 1, AnsBl),
+%				impure table_save_string_ans(AnsBl, 0, B)
@@ -199,5 +199,21 @@
-% For I/O primitives that have not_tabled_for_io, we should require that they
-% do not do any I/O in their own code, meaning that all their I/O is inside
-% the Mercury code they call. We can then leave such primitives untransformed;
-% the I/O primitives called from the inner Mercury engine will do the right
-% thing.
+% Note that copying the I/O state works only because variables of type
+% io__state don't actually contain any information; the information is actually
+% stored in global variables. However, if this ever changes, the transformation
+% can be fixed simply by changing the value of --trace-table-io-states to yes,
+% which will cause such values to be tabled along with the other output
+% arguments.
+%
+% For I/O primitives that do not have tabled_for_io, we should require that
+% they do not do any I/O in their own code, meaning that all their I/O is
+% inside any Mercury code they call. We can then leave such primitives
+% untransformed; the I/O primitives called from the inner Mercury engine
+% will do the right thing. For now, this requirement is not enforced,
+% which means that enabling I/O tabling (with --trace-table-io) does not
+% guarantee that *all* I/O actions are tabled. This can cause inconsistent
+% behavior after retry commands in mdb. This is the reason why retry across
+% I/O is experimental for now.
+%
+% The reason why we require I/O primitives to be marked manually by a
+% programmer with the tabled_for_io feature is to get the programmer to make
+% sure that the primitive meets the requirement. Unfortunately, this cannot be
+% automated, since automation would require analysis of arbitrary C code.
@@ -277 +293 @@
-		proc_info_has_io_state_args(Module0, ProcInfo0,
+		proc_info_has_io_state_pair(Module0, ProcInfo0,
@@ -278,0 +295 @@
+		proc_info_interface_code_model(ProcInfo0, model_det),
@@ -282 +299 @@
-			SubGoal = pragma_foreign_code(_, Attrs, _,_,_,_,_,_)
+			SubGoal = pragma_foreign_code(Attrs, _,_,_,_,_,_)
@@ -316,2 +332,0 @@
-		require(unify(CodeModel, model_det),
-			"tabled io proc not det"),
@@ -361,0 +377 @@
+
@@ -402 +418 @@
-		list__filter(table_gen__var_is_io_state(Module, VarTypes0),
+		list__filter(table_gen__var_is_io_state(VarTypes0),
@@ -431 +447 @@
-	list__filter(table_gen__var_is_io_state(Module, VarTypes), InputVars,
+	list__filter(table_gen__var_is_io_state(VarTypes), InputVars,
@@ -444,0 +461,3 @@
+			% The call to proc_info_has_io_state_pair in 
+			% table_gen__process_procs should ensure that we
+			% never get here.
@@ -899,2 +918,2 @@
-:- pred table_gen__var_is_io_state(module_info::in, map(prog_var, type)::in,
-	prog_var::in) is semidet.
+:- pred table_gen__var_is_io_state(map(prog_var, type)::in, prog_var::in)
+	is semidet.
@@ -902 +921 @@
-table_gen__var_is_io_state(ModuleInfo, VarTypes, Var) :-
+table_gen__var_is_io_state(VarTypes, Var) :-
@@ -904,9 +923 @@
-	table_gen__type_is_io_state(ModuleInfo, VarType).
-
-:- pred table_gen__type_is_io_state(module_info::in, (type)::in) is semidet.
-
-table_gen__type_is_io_state(ModuleInfo, Type) :-
-	type_to_type_id(Type, TypeCtor, []),
-	type_util__type_id_module(ModuleInfo, TypeCtor, unqualified("io")),
-	type_util__type_id_name(ModuleInfo, TypeCtor, "state"),
-	type_util__type_id_arity(ModuleInfo, TypeCtor, 0).
+	type_util__type_is_io_state(VarType).
@@ -1238 +1249 @@
-	( table_gen__type_is_io_state(Module, Type) ->
+	( type_util__type_is_io_state(Type) ->
@@ -1354 +1365 @@
-	( table_gen__type_is_io_state(Module, Type) ->
+	( type_util__type_is_io_state(Type) ->
--- compiler/trace.m
+++ compiler/trace.m
@@ -296,6 +296,6 @@
-	% stage 5:	If the procedure has io state arguments, allocate the
-	%		next slot to hold the saved value of the io sequence
-	%		number, for use in implementing retry. The number of
-	%		this slot is recorded in the maybe_io_seq field
-	%		in the proc layout; if there is no such slot, that
-	%		field will contain -1.
+	% stage 5:	If --trace-table-io is given, allocate the next slot
+	%		to hold the saved value of the io sequence number,
+	%		for use in implementing retry. The number of this slot
+	%		is recorded in the maybe_io_seq field in the proc
+	%		layout; if there is no such slot, that field will
+	%		contain -1.
@@ -311 +311 @@
-	% stage 6:	If the procedure lives on the det stack but can put
+	% stage 7:	If the procedure lives on the det stack but can put
@@ -319 +319 @@
-	% stage 7:	If the procedure's evaluation method is memo, loopcheck
+	% stage 8:	If the procedure's evaluation method is memo, loopcheck
@@ -352 +352 @@
-trace__reserved_slots(ModuleInfo, ProcInfo, Globals, ReservedSlots,
+trace__reserved_slots(_ModuleInfo, ProcInfo, Globals, ReservedSlots,
@@ -355,0 +356 @@
+	globals__lookup_bool_option(Globals, trace_table_io, TraceTableIo),
@@ -382 +383 @@
-		( proc_info_has_io_state_args(ModuleInfo, ProcInfo, _, _) ->
+		( TraceTableIo = yes ->
@@ -413 +414 @@
-trace__setup(ModuleInfo, ProcInfo, Globals, TraceSlotInfo, TraceInfo) -->
+trace__setup(_ModuleInfo, ProcInfo, Globals, TraceSlotInfo, TraceInfo) -->
@@ -416,0 +418 @@
+	{ globals__lookup_bool_option(Globals, trace_table_io, TraceTableIo) },
@@ -453 +455,2 @@
-	{ proc_info_has_io_state_args(ModuleInfo, ProcInfo, _, _) ->
+	{
+		TraceTableIo = yes,
@@ -459,0 +463 @@
+		TraceTableIo = no,
@@ -527 +531,13 @@
-	( MaybeRedoLabel = yes(RedoLayoutLabel) ->
+	(
+		MaybeIoSeqSlot = yes(IoSeqLval),
+		trace__stackref_to_string(IoSeqLval, IoSeqStr),
+		string__append_list([
+			FillThreeSlots, "\n",
+			"\t\t", IoSeqStr, " = MR_io_tabling_counter;"
+		], FillSlotsUptoIoSeq)
+	;
+		MaybeIoSeqSlot = no,
+		FillSlotsUptoIoSeq = FillThreeSlots
+	),
+	(
+		MaybeRedoLabel = yes(RedoLayoutLabel),
@@ -533 +549 @@
-			FillThreeSlots, "\n",
+			FillSlotsUptoIoSeq, "\n",
@@ -537 +553 @@
-		], FillFourSlots),
+		], FillSlotsUptoRedo),
@@ -540 +556,2 @@
-		FillFourSlots = FillThreeSlots,
+		MaybeRedoLabel = no,
+		FillSlotsUptoRedo = FillSlotsUptoIoSeq,
@@ -568 +585 @@
-			FillSixSlots,
+			FillSlotsUptoTrail,
@@ -570 +587 @@
-		], FillCondSlots)
+		], FillSlotsUptoMaxfr)
@@ -573 +590 @@
-		FillCondSlots = FillSixSlots
+		FillSlotsUptoMaxfr = FillSlotsUptoTrail
@@ -795 +812 @@
-	DeclStmt = "\t\tCode *MR_jumpaddr;\n",
+	DeclStmt = "\t\tMR_Code *MR_jumpaddr;\n",
--- library/io.m
+++ library/io.m
@@ -3543 +3543,2 @@
-		[may_call_mercury, thread_safe], "
+		[may_call_mercury, tabled_for_io, thread_safe],
+"
@@ -3549 +3550,2 @@
-		[may_call_mercury, thread_safe], "
+		[may_call_mercury, tabled_for_io, thread_safe],
+"
@@ -3555 +3557,2 @@
-		[may_call_mercury, thread_safe], "
+		[may_call_mercury, tabled_for_io, thread_safe],
+"
@@ -3561 +3564,2 @@
-		[may_call_mercury, thread_safe], "
+		[may_call_mercury, tabled_for_io, thread_safe],
+"
@@ -3570 +3574,2 @@
-		[will_not_call_mercury, thread_safe], "
+		[will_not_call_mercury, tabled_for_io, thread_safe],
+"
--- library/table_builtin.m
+++ library/table_builtin.m
@@ -320,0 +321,18 @@
+	% This procedure should be called exactly once for each I/O action.
+	% If I/O tabling is enabled, this predicate will increment the I/O 
+	% action counter, and will check if this action should be tabled.
+	% If not, it fails. If yes, it succeeds, and binds the output
+	% arguments, which are, in order:
+	%
+	% - The root trie node for all I/O actions. This is similar to
+	%   the per-procedure tabling pointers, but it is shared by all
+	%   I/O actions.
+	% - the I/O action number of this action.
+	% - The I/O action number of the first action in the tabled range.
+	%
+	% After the first tabled action, the root trie node will point to a
+	% (dynamically expandable) array of trie nodes. The trie node for
+	% I/O action number Counter is at offset Counter - Start in this array,
+	% where Start is the I/O action number of the first tabled action.
+	% The three output parameters together specify this location.
+
@@ -322,0 +341,6 @@
+	% This procedure should be called exactly once for each I/O action
+	% for which table_io_in_range returns true. Given the trie node
+	% for a given I/O action number, it returns true iff that action has
+	% been carried out before (i.e. the action is now being reexecuted
+	% after a retry command in the debugger).
+
@@ -324,0 +349,5 @@
+	% This predicate simply copies the input I/O state to become the output
+	% I/O state. It is used only because it is easier to get the insts
+	% right by calling this procedure than by hand-writing insts for a
+	% unification.
+
@@ -326,0 +356,2 @@
+	% N.B. interface continued below
+
@@ -334,3 +365,3 @@
-% Phase 0 consists of Mercury code executed prior to the first debugger event.
-% Even if main/2 is traced, this will include the initialization of the I/O
-% system itself. During this phase, MR_io_tabling_enabled will be FALSE.
+% Phase UNINIT consists of Mercury code executed prior to the first debugger
+% event. Even if main/2 is traced, this will include the initialization of the
+% I/O system itself. During this phase, MR_io_tabling_enabled will be FALSE.
@@ -338,5 +369,5 @@
-% Phase 1 consists of Mercury code during whose execution the user does not
-% need safe retry across I/O, probably because he/she does not require retry at
-% all. During this phase, MR_io_tabling_enabled will be TRUE while we ensure
-% that table_io_range returns FALSE by setting MR_io_tabling_start to the
-% highest possible value.
+% Phase BEFORE consists of Mercury code during whose execution the user does
+% not need safe retry across I/O, probably because he/she does not require
+% retry at all. During this phase, MR_io_tabling_enabled will be TRUE while
+% we ensure that table_io_range returns FALSE by setting MR_io_tabling_start
+% to the highest possible value.
@@ -344,5 +375,6 @@
-% Phase 2 consists of Mercury code during whose execution the user does need
-% safe retry across I/O. During this phase, MR_io_tabling_enabled will be TRUE,
-% and MR_io_tabling_start will be set to the value of MR_io_tabling_counter on
-% entry to phase 2. We will ensure that table_io_in_range returns TRUE by 
-% setting MR_io_tabling_end to the highest possible value.
+% Phase DURING consists of Mercury code during whose execution the user does
+% need safe retry across I/O. During this phase, MR_io_tabling_enabled will be
+% TRUE, and MR_io_tabling_start will be set to the value of
+% MR_io_tabling_counter on entry to phase DURING. We will ensure that
+% table_io_in_range returns TRUE by setting MR_io_tabling_end to the highest
+% possible value.
@@ -350,6 +382,6 @@
-% Phase 3 again consists of Mercury code during whose execution the user does
-% not need safe retry across I/O. During this phase, MR_io_tabling_enabled will
-% be TRUE, MR_io_tabling_start will contain the value of MR_io_tabling_counter
-% at the time of the entry to phase 2, while MR_io_tabling_end will contain the
-% value of MR_io_tabling_counter at the end of phase 2, thus ensuring that
-% table_io_in_range again returns FALSE.
+% Phase AFTER again consists of Mercury code during whose execution the user
+% does not need safe retry across I/O. During this phase, MR_io_tabling_enabled
+% will be TRUE, MR_io_tabling_start will contain the value of
+% MR_io_tabling_counter at the time of the entry to phase DURING, while
+% MR_io_tabling_end will contain the value of MR_io_tabling_counter at the end
+% of phase DURING, thus ensuring that table_io_in_range again returns FALSE.
@@ -357,2 +389,2 @@
-% The transition from phase 0 to phase 1 will occur during the initialization
-% of the debugger, at the first trace event.
+% The transition from phase UNINIT to phase BEFORE will occur during the
+% initialization of the debugger, at the first trace event.
@@ -360,6 +392,6 @@
-% The transition from phase 1 to phase 2 will occur when the user issues the
-% "table_io start" command, while the transition from phase 2 to phase 3 will
-% occur when the user issues the "table_io end" command. The user may automate
-% entry into phase 2 by putting "table_io start" into a .mdbrc file. Of course
-% the program will never enter phase 2 or phase 3 if the user never gives the
-% commands that start those phases.
+% The transition from phase BEFORE to phase DURING will occur when the user
+% issues the "table_io start" command, while the transition from phase DURING
+% to phase AFTER will occur when the user issues the "table_io end" command.
+% The user may automate entry into phase DURING by putting "table_io start"
+% into a .mdbrc file. Of course the program will never enter phase DURING or
+% phase AFTER if the user never gives the commands that start those phases.
@@ -381 +413 @@
-			T = (Word) &MR_io_tabling_pointer;
+			T = (MR_Word) &MR_io_tabling_pointer;
@@ -830,2 +862 @@
-	% Save any type of answer in the given answer block at the given
-	% offset.
+	% Save an I/O state in the given answer block at the given offset.
@@ -859,2 +890 @@
-	% Restore any type of answer from the given answer block at the
-	% given offset.
+	% Restore an I/O state from the given answer block at the given offset.
--- runtime/mercury_conf.h.in	2000/10/30 10:54:39
+++ runtime/mercury_conf.h.in	2000/08/17 09:58:06	1.32
@@ -50,13 +50,6 @@
 */
 #undef MR_INTEGER_LENGTH_MODIFIER
 
-/* 
-** MR_INTEGER_MAX: the maximum MR_Integer value.
-** MR_UNSIGNED_MAX: the maximum MR_Unsigned value.
-*/
-#undef MR_INTEGER_MAX
-#undef MR_UNSIGNED_MAX
-
 /*
 ** MR_INT_LEAST64_TYPE:
 ** This must be a C integral type (e.g. int, long, long long)
--- runtime/mercury_tabling.h	2000/10/22 22:51:00
+++ runtime/mercury_tabling.h	2000/10/11 03:00:26	1.23
@@ -65,7 +65,7 @@
 ** important that all members be the same size; this is why the simple table
 ** status field is an (unsigned) integer, not an enum.
 **
+** The integer field is by generic code that does not know what kind of node
-** The integer field is for generic code that does not know what kind of node
 ** the node will be; this means initialization. A value of zero means the node
 ** is uninitialized; this must be true for all members. (Also, see below on
 ** duplicate detection.)
@@ -86,7 +86,7 @@
 ** the chain of trie nodes given by the input arguments of the tabled subgoal,
 ** will be overwritten by a pointer to the answer block containing the output
 ** arguments when the goal succeeds; the MR_SIMPLETABLE_SUCCEEDED status code
+** is used only when the goal has no outputs, and this no answer block.
-** is used only when the goal has no outputs, and thus no answer block.
 ** This is why MR_SIMPLETABLE_SUCCEEDED must have the highest value, and
 ** why code looking at MR_simpletable_status must test for success with
 ** "table->MR_simpletable_status >= MR_SIMPLETABLE_SUCCEEDED".
--- runtime/mercury_trace_base.c
+++ runtime/mercury_trace_base.c
@@ -98,0 +99,12 @@
+/*
+** I/O tabling is documented in library/table_builtin.m
+*/
+
+MR_IoTablingPhase	MR_io_tabling_phase = MR_IO_TABLING_UNINIT;
+bool			MR_io_tabling_enabled = FALSE;
+MR_TableNode		MR_io_tabling_pointer = { 0 };
+MR_Unsigned		MR_io_tabling_counter = 0;
+MR_Unsigned		MR_io_tabling_counter_hwm = 0;
+MR_Unsigned		MR_io_tabling_start = 0;
+MR_Unsigned		MR_io_tabling_end = 0;
+
--- runtime/mercury_wrapper.c	2000/11/06 01:49:57
+++ runtime/mercury_wrapper.c	2000/11/05 12:04:18	1.76
@@ -207,13 +207,11 @@
 void	(*MR_io_print_to_cur_stream)(MR_Word, MR_Word);
 void	(*MR_io_print_to_stream)(MR_Word, MR_Word, MR_Word);
 
+void	(*MR_DI_output_current_ptr)(MR_Integer, MR_Integer, MR_Integer, MR_Word, MR_String,
+		MR_String, MR_Integer, MR_Integer, MR_Integer, MR_Word, MR_String, MR_Word, MR_Word);
-void	(*MR_DI_output_current_ptr)(MR_Integer, MR_Integer, MR_Integer,
-		MR_Word, MR_String, MR_String, MR_Integer, MR_Integer,
-		MR_Integer, MR_Word, MR_String, MR_Word, MR_Word);
 		/* normally ML_DI_output_current (output_current/13) */
+bool	(*MR_DI_found_match)(MR_Integer, MR_Integer, MR_Integer, MR_Word, MR_String, MR_String,
+		MR_Integer, MR_Integer, MR_Integer, MR_Word, MR_String, MR_Word);
-bool	(*MR_DI_found_match)(MR_Integer, MR_Integer, MR_Integer, MR_Word,
-		MR_String, MR_String, MR_Integer, MR_Integer, MR_Integer,
-		MR_Word, MR_String, MR_Word);
 		/* normally ML_DI_found_match (output_current/12) */
 void	(*MR_DI_read_request_from_socket)(MR_Word, MR_Word *, MR_Integer *);
 
@@ -231,18 +229,6 @@
 void	(*MR_address_of_trace_interrupt_handler)(void);
 
 void	(*MR_register_module_layout)(const MR_Module_Layout *);
-
-/*
-** These variables are documented in library/table_builtin.m.
-*/
-
-int		MR_io_tabling_phase = 0;
-bool		MR_io_tabling_enabled = FALSE;
-MR_TableNode	MR_io_tabling_pointer = { 0 };
-MR_Unsigned	MR_io_tabling_counter = 0;
-MR_Unsigned	MR_io_tabling_counter_hwm = 0;
-MR_Unsigned	MR_io_tabling_start = 0;
-MR_Unsigned	MR_io_tabling_end = 0;
 
 #ifdef USE_GCC_NONLOCAL_GOTOS
 
--- runtime/mercury_wrapper.h	2000/11/06 01:49:57
+++ runtime/mercury_wrapper.h	2000/11/05 12:04:19	1.37
@@ -153,14 +153,6 @@
 */
 extern	void		(*MR_register_module_layout)(const MR_Module_Layout *);
 
-extern	int		MR_io_tabling_phase;
-extern	bool		MR_io_tabling_enabled;
-extern	MR_TableNode	MR_io_tabling_pointer;
-extern	MR_Unsigned	MR_io_tabling_counter;
-extern	MR_Unsigned	MR_io_tabling_counter_hwm;
-extern	MR_Unsigned	MR_io_tabling_start;
-extern	MR_Unsigned	MR_io_tabling_end;
-
 extern	void		do_init_modules(void);
 extern	void		do_init_modules_type_tables(void);
 extern	void		do_init_modules_debugger(void);
--- tabled_read.data
+++ tabled_read.data
@@ -1,0 +2,2 @@
+456
+789
--- tabled_read.inp
+++ tabled_read.inp
@@ -2,0 +3,2 @@
+context none
+table_io
@@ -8 +9,0 @@
-table_io
@@ -10,0 +12,7 @@
+finish -n
+print *
+table_io end
+continue
+finish -n
+print *
+retry
--- tabled_read.m
+++ tabled_read.m
@@ -1,1 +1,2 @@
+% We define our own I/O primitives, in case the library was compiled without
+% IO tabling.
+
 % IO tabling.
@@ -15,2 +18,2 @@
-	io__open_input("tabled_read.data", Res),
-	( { Res = ok(Stream) } ->
+	tabled_read__open_input("tabled_read.data", Res, Stream),
+	( { Res = 0 } ->
@@ -18,2 +21,3 @@
-		io__write_int(N),
-		io__write_string("\n")
+		tabled_read__write_int(N),
+		tabled_read__test(Stream, 0, M),
+		tabled_read__write_int(M)
@@ -24 +28 @@
-:- pred tabled_read__test(io__input_stream::in, int::in, int::out,
+:- pred tabled_read__test(c_pointer::in, int::in, int::out,
@@ -39,2 +43,12 @@
-% We define our own version of io__read_char_code, in case the library
-% was compiled without IO tabling.
+:- pragma c_header_code("#include <stdio.h>").
+
+:- pred tabled_read__open_input(string::in, int::out, c_pointer::out,
+	io__state::di, io__state::uo) is det.
+
+:- pragma c_code(tabled_read__open_input(FileName::in, Res::out, Stream::out,
+		IO0::di, IO::uo), [will_not_call_mercury, tabled_for_io],
+"
+	Stream = (MR_Word) fopen((const char *) FileName, ""r"");
+	Res = Stream? 0 : -1;
+	IO = IO0;
+").
@@ -42 +56 @@
-:- pred tabled_read__read_char_code(io__input_stream::in, int::out,
+:- pred tabled_read__read_char_code(c_pointer::in, int::out,
@@ -45,4 +59,4 @@
-:- pragma c_code(tabled_read__read_char_code(File::in, CharCode::out,
-		IO0::di, IO::uo), [will_not_call_mercury], "
-	extern	int	mercury_getc(MercuryFile* mf);
-	CharCode = mercury_getc((MercuryFile *) File);
+:- pragma c_code(tabled_read__read_char_code(Stream::in, CharCode::out,
+		IO0::di, IO::uo), [will_not_call_mercury, tabled_for_io],
+"
+	CharCode = getc((FILE *) Stream);
@@ -52,10 +66,8 @@
-% % We define our own version of io__write_string, in case the library
-% % was compiled without IO tabling.
-% 
-% :- pragma c_code(tabled_read__write_string(Stream::in, Message::in,
-% 		IO0::di, IO::uo), [may_call_mercury, thread_safe],
-% "{
-% 	MercuryFile *stream = (MercuryFile *) Stream;
-% 	mercury_print_string(stream, Message);
-% 	update_io(IO0, IO);
-% }").
+:- pred tabled_read__write_int(int::in, io__state::di, io__state::uo) is det.
+
+:- pragma c_code(tabled_read__write_int(N::in,
+		IO0::di, IO::uo), [may_call_mercury, thread_safe],
+"{
+	printf(""%d\n"", (int) N);
+	IO = IO0;
+}").
--- trace/mercury_trace_internal.c
+++ trace/mercury_trace_internal.c
@@ -160 +159,0 @@
-static	void	MR_print_unsigned(FILE *fp, MR_Unsigned value);
@@ -345,3 +344,3 @@
-		MR_io_tabling_phase = 1;
-		MR_io_tabling_start = MR_UNSIGNED_MAX;
-		MR_io_tabling_end = MR_UNSIGNED_MAX;
+		MR_io_tabling_phase = MR_IO_TABLING_BEFORE;
+		MR_io_tabling_start = MR_IO_ACTION_MAX;
+		MR_io_tabling_end = MR_IO_ACTION_MAX;
@@ -1067 +1066 @@
-			do_init_modules();
+			MR_do_init_modules();
@@ -1837 +1836 @@
-			do_init_modules();
+			MR_do_init_modules();
@@ -1848 +1847 @@
-			do_init_modules();
+			MR_do_init_modules();
@@ -1875 +1874,2 @@
-			if (MR_io_tabling_phase == 1) {
+			if (MR_io_tabling_phase == MR_IO_TABLING_BEFORE)
+			{
@@ -1878 +1878,2 @@
-			} else if (MR_io_tabling_phase == 2) {
+			} else if (MR_io_tabling_phase == MR_IO_TABLING_DURING)
+			{
@@ -1881 +1882,2 @@
-			} else if (MR_io_tabling_phase == 3) {
+			} else if (MR_io_tabling_phase == MR_IO_TABLING_AFTER)
+			{
@@ -1889,2 +1891,2 @@
-			if (MR_io_tabling_phase == 1) {
-				MR_io_tabling_phase = 2;
+			if (MR_io_tabling_phase == MR_IO_TABLING_BEFORE) {
+				MR_io_tabling_phase = MR_IO_TABLING_DURING;
@@ -1892 +1894 @@
-				MR_io_tabling_end = MR_UNSIGNED_MAX;
+				MR_io_tabling_end = MR_IO_ACTION_MAX;
@@ -1894 +1896,2 @@
-			} else if (MR_io_tabling_phase == 2) {
+			} else if (MR_io_tabling_phase == MR_IO_TABLING_DURING)
+			{
@@ -1897 +1900,2 @@
-			} else if (MR_io_tabling_phase == 3) {
+			} else if (MR_io_tabling_phase == MR_IO_TABLING_AFTER)
+			{
@@ -1905 +1909,2 @@
-			if (MR_io_tabling_phase == 1) {
+			if (MR_io_tabling_phase == MR_IO_TABLING_BEFORE)
+			{
@@ -1908,2 +1913,3 @@
-			} else if (MR_io_tabling_phase == 2) {
-				MR_io_tabling_phase = 3;
+			} else if (MR_io_tabling_phase == MR_IO_TABLING_DURING)
+			{
+				MR_io_tabling_phase = MR_IO_TABLING_AFTER;
@@ -1912 +1918,2 @@
-			} else if (MR_io_tabling_phase == 3) {
+			} else if (MR_io_tabling_phase == MR_IO_TABLING_AFTER)
+			{
@@ -2082,12 +2089 @@
-	fprintf(fp, "%s = ", var);
-	MR_print_unsigned(fp, value);
-	fprintf(fp, "\n");
-}
-
-static void
-MR_print_unsigned(FILE *fp, MR_Unsigned value)
-{
-	char	buf[20];
-
-	sprintf(buf, "%%%su", MR_INTEGER_LENGTH_MODIFIER);
-	fprintf(fp, buf, value);
+	fprintf(fp, "%s = %" MR_INTEGER_LENGTH_MODIFIER "u\n", var, value);
--- runtime/mercury_trace_base.h	2000/08/03 06:19:00	1.14
+++ runtime/mercury_trace_base.h	2000/11/28 10:05:47
@@ -18,6 +18,7 @@
 #include <stdio.h>
 #include "mercury_stack_layout.h"
 #include "mercury_std.h"
+#include "mercury_tabling.h"	/* for MR_TableNode */
 
 /*
 ** This enum should EXACTLY match the definition of the `trace_port_type'
@@ -48,7 +49,8 @@
 
 #define MR_trace_incr_seq()		((MR_Word) ++MR_trace_call_seqno)
 #define MR_trace_incr_depth()		((MR_Word) ++MR_trace_call_depth)
-#define MR_trace_reset_depth(d)		(MR_trace_call_depth = (MR_Unsigned) (d))
+#define MR_trace_reset_depth(d)		(MR_trace_call_depth = \
+						(MR_Unsigned) (d))
 
 /*
 ** MR_trace is called from Mercury modules compiled with tracing.
@@ -110,6 +112,48 @@
 
 extern	MR_Unsigned	MR_trace_event_number;
 extern	MR_Bool		MR_trace_from_full;
+
+/*
+** The details of I/O tabling are documented in library/table_builtin.m.
+*/
+
+typedef enum {
+	/* from program start to first debugger event */
+	MR_IO_TABLING_UNINIT,	
+
+	/* from first debugger event to "table_io start" command */
+	MR_IO_TABLING_BEFORE,
+
+	/* from "table_io start" command to "table_io end" command */
+	MR_IO_TABLING_DURING,
+
+	/* from "table_io end" command to program exit */
+	MR_IO_TABLING_AFTER
+} MR_IoTablingPhase;
+
+typedef	MR_Unsigned	MR_IoActionNum;
+
+#define	MR_IO_ACTION_MAX	((MR_IoActionNum) -1)
+
+extern	MR_IoTablingPhase	MR_io_tabling_phase;
+
+/* True iff I/O tabling is enabled. */
+extern	bool		MR_io_tabling_enabled;
+
+/* The root of the trie that we use for tabling I/O. */
+extern	MR_TableNode	MR_io_tabling_pointer;
+
+/* The I/O action number of the last I/O action. */
+extern	MR_IoActionNum	MR_io_tabling_counter;
+
+/* The highest I/O action number ever reached ("hwm" = "high water mark"). */
+extern	MR_IoActionNum	MR_io_tabling_counter_hwm;
+
+/* The highest I/O action number which is too early to be tabled. */
+extern	MR_IoActionNum	MR_io_tabling_start;
+
+/* The highest I/O action number which is to be tabled. */
+extern	MR_IoActionNum	MR_io_tabling_end;
 
 /*
 ** These functions will report the number of the last event,
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list