[m-dev.] for review: document pragma import & nondet pragma c_code
Fergus Henderson
fjh at cs.mu.OZ.AU
Thu Nov 5 17:38:43 AEDT 1998
On 05-Nov-1998, Tyson Dowd <trd at cs.mu.OZ.AU> wrote:
> Are typeclass_infos handled at all? If not is it noted as a
> bug/limitation somewhere?
That is already a problem with the existing documentation
for `pragma export' and `pragma c_code', rather than a new
problem added by this change. So I propose to fix that one in a
separate change.
> > + at c XXX document the default values
>
> Yes please.
Likewise, this is another existing problem which this change
just makes more visible. I will fix it in a separate change.
I found a formatting problem with my changes, which I fixed
as follows:
@c
@c We can't use @samp in node names -- if we do, makeinfo doesn't mind,
@c but texi2dvi barfs on the cross-references. So the node names are
@c e.g. "`pragma import'" rather than "@samp{pragma import}".
@c
Thanks for the reviews everyone -- here's the final diff.
I plan to commit this one.
--------------------
Estimated hours taken: 4
doc/reference_manual.texi:
Document `pragma import' and nondet `pragma c_code'.
Also make various other minor improvements to the documentation
of the C interface, and fix a few layout errors
(e.g. the use of "..." instead of "@dots{}").
Index: doc/reference_manual.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/reference_manual.texi,v
retrieving revision 1.109
diff -u -r1.109 reference_manual.texi
--- reference_manual.texi 1998/11/02 02:35:28 1.109
+++ reference_manual.texi 1998/11/05 06:37:02
@@ -747,7 +747,7 @@
@noindent
where @var{N} >= 0, @var{Func} is a term of type
- at samp{func(T1, T2, ..., Tn) = T}, @var{FuncVar} is a variable
+ at samp{func(T1, T2, @dots{}, Tn) = T}, @var{FuncVar} is a variable
of that type, and
@var{Arg1}, @var{Arg2}, @dots{}, @var{ArgN} are terms of types
@samp{T1}, @samp{T2}, @dots{}, @samp{Tn}.
@@ -2035,7 +2035,7 @@
@example
:- type set(T) ---> set(list(T))
- where equality is set_equals.
+ where equality is set_equals.
@end example
@noindent
@@ -2046,8 +2046,8 @@
@example
:- pred set_equals(set(T)::in, set(T)::in) is semidet.
set_equals(S1, S2) :-
- subset(S1, S2),
- subset(S2, S1).
+ subset(S1, S2),
+ subset(S2, S1).
@end example
A type declaration for a type @var{T} may contain a
@@ -3222,10 +3222,103 @@
@node Calling C code from Mercury
@subsection Calling C code from Mercury
+There are two slightly different mechanisms for calling C code from Mercury:
+ at samp{pragma import} and @samp{pragma c_code}. @samp{pragma import}
+allows you to call C functions from Mercury. @samp{pragma c_code}
+allows you to implement Mercury procedures using arbitrary fragments
+of C code. @samp{pragma import} is usually simpler, but
+ at samp{pragma c_code} is a bit more flexible.
+
+ at c
+ at c We can't use "@samp" or even "`...'" in node names -- if we use
+ at c either, then texi2dvi barfs. So the node names are
+ at c e.g. "pragma import" rather than "@samp{pragma import}".
+ at c
+ at menu
+* pragma import:: Importing C functions
+* pragma c_code:: Defining Mercury procedures using C code
+* Nondet pragma c_code:: Using @samp{pragma c_code} for Mercury procedures
+ that can have more than one solution.
+* C code attributes:: Describing properties of C functions or C code.
+* Purity and side effects:: Explains when side effects are allowed.
+ at end menu
+
+ at node pragma import
+ at subsubsection pragma import
+
A declaration of the form
@example
-:- pragma c_code(@var{Pred}(@var{Var1}::@var{Mode1}, @var{Var2}::@var{Mode2}, ...),
+:- pragma import(@var{Pred}(@var{Mode1}, @var{Mode2}, @dots{}),
+ @var{Attributes}, "@var{C_Name}").
+ at end example
+
+ at noindent
+or
+
+ at example
+:- pragma import(@var{Func}(@var{Mode1}, @var{Mode2}, @dots{}) = @var{Mode},
+ @var{Attributes}, "@var{C_Name}").
+ at end example
+
+ at noindent
+imports a C function for use by Mercury.
+ at var{Pred} or @var{Func} must specify the name of a previously declared
+Mercury predicate or function, and @var{Mode1}, @var{Mode2}, @dots{},
+and (for functions) @var{Mode} must specify one of the
+modes of that predicate or function. There must be no clauses
+for the specified Mercury procedure; instead, any calls to that
+procedure will be executed by calling the C function named
+ at var{C_Name}. The @var{Attributes} argument is optional; if present,
+it specifies properties of the given C function (see @pxref{C code attributes}).
+
+For example, the following code imports the C function @samp{cos()}
+as the Mercury function @samp{cos/1}:
+
+ at example
+:- func cos(float) = float.
+:- pragma import(cos(in) = out, [will_not_call_mercury], "cos").
+ at end example
+
+The interface to the C function for a given Mercury procedure is
+determined as follows. Mercury types are converted to C types
+according to the rules in @xref{Passing data to and from C}.
+Mercury arguments declared with input modes are passed by value to the
+C function. For output arguments, the Mercury implementation will pass
+to the C function an address in which to store the result.
+If the Mercury procedure can fail, then its C function should return a
+truth value of type @samp{Integer} indicating success or failure:
+non-zero indicates success, and zero indicates failure.
+If the Mercury procedure is a Mercury function that cannot fail, and
+the function result has an output mode, then the C function should
+return the Mercury function result value.
+Otherwise the function result is appended as an extra argument.
+Arguments of type @samp{io__state} or @samp{store__store(_)} are not
+passed at all; that's because these types represent mutable state, and
+in C modifications to mutable state are done via side effects, rather
+than argument passing.
+
+If you use @samp{pragma import} for a polymorphically typed Mercury procedure,
+the compiler will prepend one @samp{type_info} argument to the parameters
+passed to the C function for each polymorphic type variable in the
+Mercury procedure's type signature. The values passed in these arguments
+will be the same as the values that would be obtained using the Mercury
+ at samp{type_of} function in the Mercury standard library module @samp{std_util}.
+These values may be useful in case the C function wishes to in turn call
+another polymorphic Mercury procedure (@pxref{Calling Mercury code from C}).
+
+You may not give a @samp{pragma import} declaration for a procedure
+with determinism @samp{nondet} or @samp{multi}.
+(It is however possible to define a @samp{nondet} or @samp{multi} procedure
+using @samp{pragma c_code} -- see @pxref{Nondet pragma c_code}).
+
+ at node pragma c_code
+ at subsubsection pragma c_code
+
+A declaration of the form
+
+ at example
+:- pragma c_code(@var{Pred}(@var{Var1}::@var{Mode1}, @var{Var2}::@var{Mode2}, @dots{}),
@var{Attributes}, @var{C_Code}).
@end example
@@ -3233,8 +3326,8 @@
or
@example
-:- pragma c_code(@var{Func}(@var{Var1}::@var{Mode1}, @var{Var2}::@var{Mode2}, ...) = (@var{Var}::@var{Mode}),
- may_call_mercury, @var{C_Code}).
+:- pragma c_code(@var{Func}(@var{Var1}::@var{Mode1}, @var{Var2}::@var{Mode2}, @dots{}) = (@var{Var}::@var{Mode}),
+ @var{Attributes}, @var{C_Code}).
@end example
@noindent
@@ -3242,7 +3335,9 @@
will result in execution of the C code given in @var{C_Code}.
The C code fragment may refer to the specified variables
(@var{Var1}, @var{Var2}, @dots{}, and @var{Var})
-directly by name.
+directly by name. These variables will have C types corresponding
+to their Mercury types, as determined by the rules specified in
+ at xref{Passing data to and from C}.
The C code fragment may declare local variables, but it should not
declare any static variables unless there is also a Mercury
@@ -3252,11 +3347,10 @@
which would result in the program having multiple instances of the
static variable, rather than a single shared instance.
-If there is a @code{pragma c_code} declaration for a mode of a predicate
-or function, then that mode of the predicate may not have determinism
- at samp{multi} or @samp{nondet}, there must not be any clauses for that
-predicate or function, and there must be a @code{pragma c_code} declaration
-for every mode of the predicate or function.
+If there is a @code{pragma import} or @code{pragma c_code} declaration for a
+mode of a predicate or function, then there must not be any clauses for that
+predicate or function, and there must be a @code{pragma c_code}
+or @code{pragma import} declaration for every mode of the predicate or function.
For example, the following piece of code defines a Mercury function
@samp{sin/1} which calls the C function @samp{sin()} of the same name.
@@ -3264,8 +3358,8 @@
@example
:- func sin(float) = float.
:- pragma c_code(sin(X::in) = (Sin::out),
- may_call_mercury,
- "Sin = sin(X);").
+ [may_call_mercury],
+ "Sin = sin(X);").
@end example
If the C code does not recursively invoke Mercury code,
@@ -3275,25 +3369,6 @@
(If you use this form, and the C code @emph{does} invoke Mercury code,
then the behaviour is undefined --- your program may misbehave or crash.)
-All Mercury implementations must support the attributes described below.
-They may also support additional attributes.
-
-The attributes which must be supported by all implementations
-are as follows:
-
- at table @asis
-
- at item @samp{may_call_mercury}/@samp{will_not_call_mercury} declares
-whether or not execution inside this C code may call back into Mercury
-or not.
-
- at item @samp{thread_safe}/@samp{not_thread_safe} declares whether or not
-it is safe for multiple threads to execute this C code concurrently.
-C code that is not thread_safe has code inserted around it to obtain
-and release a mutex. All non-thread-safe C code shares a single mutex.
-
- at end table
-
The C code in a @code{pragma c_code} declaration
for any procedure whose determinism indicates that it could fail
must assign a truth value to the macro @samp{SUCCESS_INDICATOR}.
@@ -3304,7 +3379,7 @@
:- mode string__contains_char(in, in) is semidet.
:- pragma c_code(string__contains_char(Str::in, Ch::in),
- will_not_call_mercury,
+ [will_not_call_mercury],
"SUCCESS_INDICATOR = (strchr(Str, Ch) != NULL);").
@end example
@@ -3321,7 +3396,180 @@
before returning. If the procedure fails, the C code need only
set @code{SUCCESS_INDICATOR} to false (zero).
-Note that procedures implemented in C must still be ``pure'',
+ at node Nondet pragma c_code
+ at subsubsection Nondet pragma c_code
+
+For procedures that can return more than one result on backtracking,
+i.e. those with determinism @samp{nondet} or @samp{multi},
+the form of @samp{pragma c_code} declaration described previously
+does not suffice. Instead, you should use a declaration of the form
+shown below:
+
+ at example
+:- pragma c_code(@var{Pred}(@var{Var1}::@var{Mode1}, @var{Var2}::@var{Mode2}, @dots{}),
+ @var{Attributes}, local_vars(@var{LocalVars}), first_code(@var{FirstCode}),
+ retry_code(@var{RetryCode}), common_code(@var{CommonCode})).
+ at end example
+
+ at noindent
+or
+
+ at example
+:- pragma c_code(@var{Func}(@var{Var1}::@var{Mode1}, @var{Var2}::@var{Mode2}, @dots{}) = (@var{Var}::@var{Mode}),
+ @var{Attributes}, local_vars(@var{LocalVars}), first_code(@var{FirstCode}),
+ retry_code(@var{RetryCode}), common_code(@var{CommonCode})).
+ at end example
+
+ at noindent
+Here @var{FirstCode}, @var{RetryCode}, and @var{CommonCode} are all
+Mercury strings containing C code.
+ at var{FirstCode} will be executed whenever the Mercury procedure is called.
+ at var{RetryCode} will be executed whenever a given call to the procedure
+is re-entered on backtracking to find subsequent solutions.
+The @samp{common_code(@var{CommonCode})} argument is optional; if present,
+ at var{CommonCode} will be executed after each execution of
+ at var{FirstCode} or @var{RetryCode}.
+
+The code that is executed on each call or retry should finish by
+executing one of the three macros @samp{FAIL}, @samp{SUCCEED}, or
+ at samp{SUCCEED_LAST}. The @samp{FAIL} macro indicates that the call has
+failed; the call will not be retried. The @samp{SUCCEED} macro
+indicates that the call has succeeded, and that there may be more
+solutions; the call may be retried on backtracking. The
+ at samp{SUCCEED_LAST} macro indicates that the call has succeeded, but
+that there are no more solutions after this one; the call will not be
+retried.
+
+ at var{LocalVars} is a sequence of struct member declarations which are
+used to hold any state which needs to be preserved in case of backtracking
+or passed between the different C code fragments.
+The code fragments @var{FirstCode}, @var{RetryCode}, and @var{CommonCode}
+may use the macro @samp{LOCALS}, which is defined to be a pointer to a struct
+containing the fields specified by @var{LocalVars}, to access this saved state.
+
+Note @var{RetryCode} and @var{CommonCode} may not access the input
+variables -- only @var{FirstCode} should access the input variables.
+If @var{RetryCode} or @var{CommonCode} need to access any of the input
+variables, then @var{FirstCode} should copy the values needed to the
+ at var{LocalVars}.
+
+The following example shows how you can use a state variable to
+keep track of the next alternative to return.
+
+ at example
+%
+% This example implements the equivalent of
+% foo(X) :- X = 20 ; X = 10 ; X = 42 ; X = 99 ; fail.
+%
+:- pred foo(int).
+:- mode foo(out) is multi.
+:- pragma c_code(foo(X::out), [will_not_call_mercury, thread_safe],
+ local_vars("
+ int state;
+ "),
+ first_code("
+ LOCALS->state = 0;
+ "),
+ retry_code("
+ LOCALS->state++;
+ "),
+ common_code("
+ switch (LOCALS->state) @{
+ case 1: X = 20; SUCCEED; break;
+ case 2: X = 10; SUCCEED; break;
+ case 3: X = 42; SUCCEED; break;
+ case 4: X = 99; SUCCEED; break;
+ case 5: FAIL; break;
+ @}
+ ")
+).
+ at end example
+
+ at noindent
+The next example is a more realistic example;
+it shows how you could implement the reverse
+mode of @samp{string__append}, which returns
+all possible ways of splitting a string into
+two pieces, using @samp{pragma c_code}.
+
+ at example
+:- pred string__append(string, string, string).
+:- mode string__append(out, out, in) is multi.
+:- pragma c_code(string__append(S1::out, S2::out, S3::in),
+ [will_not_call_mercury, thread_safe],
+ local_vars("
+ String s;
+ size_t len;
+ size_t count;
+ "),
+ first_code("
+ LOCALS->s = S3;
+ LOCALS->len = strlen(S3);
+ LOCALS->count = 0;
+ "),
+ retry_code("
+ LOCALS->count++;
+ "),
+ common_code("
+ S1 = copy_substring(LOCALS->s, 0, LOCALS->count);
+ S2 = copy_substring(LOCALS->s, LOCALS->count + 1,
+ LOCALS->len);
+ if (LOCALS->count < LOCALS->len) @{
+ SUCCEED;
+ @} else @{
+ SUCCEED_LAST;
+ @}
+ ")
+).
+ at end example
+
+ at node C code attributes
+ at subsubsection C code attributes
+
+As described above, @samp{pragma import} and @samp{pragma c_code}
+declarations may include a list of attributes describing properties
+of the given C function or C code.
+All Mercury implementations must support the attributes listed below.
+They may also support additional attributes.
+
+The attributes which must be supported by all implementations
+are as follows:
+
+ at table @asis
+
+ at item @samp{may_call_mercury}/@samp{will_not_call_mercury}
+This attribute declares whether or not execution inside this C code may
+call back into Mercury or not. If you specify @samp{will_not_call_mercury},
+but the C code @emph{does} invoke Mercury code, then the behaviour is
+undefined.
+
+ at item @samp{thread_safe}/@samp{not_thread_safe}
+This attribute declares whether or not it is safe for multiple threads
+to execute this C code concurrently.
+If the C code is declared @samp{thread_safe}, then the Mercury implementation
+is permitted to execute the code concurrently from multiple threads without
+taking any special precautions. If the C code is declared
+ at samp{not_thread_safe},
+then the Mercury implementation must not invoke the code concurrently from
+multiple threads. If the Mercury implementation does use multithreading,
+then it must take appropriate steps to prevent this.
+(The experimental multithreaded version of the current
+University of Melbourne Mercury implementation protects
+ at samp{not_thread_safe} code using a mutex:
+C code that is not thread-safe has code inserted around it to obtain
+and release a mutex. All non-thread-safe C code shares a single mutex.]
+ at c XXX this can cause deadlocks if not_thread_safe C code calls
+ at c Mercury which calls C
+
+ at c XXX document the default values
+
+ at end table
+
+ at node Purity and side effects
+ at subsubsection Purity and side effects
+
+Note that procedures implemented in C using either
+ at samp{pragma import} or @samp{pragma c_code} must still be ``pure'',
unless declared otherwise (@pxref{Impurity}), and they must
be type-correct and mode-correct. (Determinism-correctness
is also required, but it follows from the rules already stated
@@ -3347,7 +3595,7 @@
:- mode c_write_string(in, di, uo) is det.
:- pragma c_code(c_write_string(S::in, IO0::di, IO::uo),
- may_call_mercury,
+ [may_call_mercury],
"puts(S); IO = IO0;").
@end example
@@ -3437,14 +3685,14 @@
A declaration of the form
@example
-:- pragma export(@var{Pred}(@var{Mode1}, @var{Mode2}, ...), "@var{C_Name_1}").
+:- pragma export(@var{Pred}(@var{Mode1}, @var{Mode2}, @dots{}), "@var{C_Name_1}").
@end example
@noindent
or
@example
-:- pragma export(@var{Func}(@var{Mode1}, @var{Mode2}, ...) = @var{Mode}, "@var{C_Name_2}").
+:- pragma export(@var{Func}(@var{Mode1}, @var{Mode2}, @dots{}) = @var{Mode}, "@var{C_Name_2}").
@end example
@noindent
@@ -3458,6 +3706,9 @@
the specified Mercury predicate or function.
The interface to a Mercury procedure is determined as follows.
+(The rules here are just the converse of the rules for @samp{pragma import}).
+Mercury types are converted to C types according to the rules in
+ at xref{Passing data to and from C}.
Input arguments are passed by value. For output arguments, the
caller must pass the address in which to store the result.
If the Mercury procedure can fail, then its C interface function
@@ -3537,6 +3788,13 @@
given by the corresponding typedef. Mercury variables of any other
type are passed as a @samp{Word}, which in the current implementation
is a typedef for an unsigned type whose size is the same size as a pointer.
+(Note: it would in fact be better for each Mercury type to map to a distinct
+abstract type in C, since that would be more type-safe, and thus we may
+change this in a future release. We advise programmers who are manipulating
+Mercury types in C code to use typedefs for each user-defined Mercury type,
+and to treat each such type as an abstract data type. This is good style
+and it will also minimize any compatibility problems if and when we do change
+this.)
Mercury lists can be manipulated by C code using the following macros,
which are defined by the Mercury implementation.
@@ -3583,11 +3841,11 @@
");
:- pragma c_code(initialise_complicated_structure(Structure::uo),
- may_call_mercury,
+ [may_call_mercury],
"Structure = init_struct();").
:- pragma c_code(do_calculation(Value::in, Structure0::di, Structure::uo,
- may_call_mercury,
+ [may_call_mercury],
"Structure = perform_calculation(Value, Structure0);").
@end example
@@ -3806,10 +4064,11 @@
entry whenever you create a delayed goal, and putting the appropriate
check for floundering in the @samp{MR_commit} and @samp{MR_solve} cases
of your function.
-(For examples of this, see the @samp{ML_cfloat_untrail_func()}
+The Mercury distribution includes some examples of this:
+see the @samp{ML_cfloat_untrail_func()}
function in the file @samp{extras/clpr/cfloat.m} and the
- at samp{ML_var_untrail_func} function in the file
- at samp{extras/trailed_update/var.m} in the Mercury distribution.)
+ at samp{ML_var_untrail_func()} function in the file
+ at samp{extras/trailed_update/var.m}.)
If your function does detect floundering, then it should print
an error message and then abort execution.
@@ -3834,7 +4093,7 @@
@itemx @bullet{} @code{MR_null_choicepoint_id()}
Prototypes:
@example
-typedef ... MR_ChoicepointId;
+typedef @dots{} MR_ChoicepointId;
MR_ChoicepointId MR_current_choicepoint_id(void);
MR_ChoicepointId MR_null_choicepoint_id(void);
@end example
@@ -4020,14 +4279,14 @@
or @code{semipure}, respectively. That is, a declaration of the form:
@example
-:- impure pred @var{Pred}(@var{Arguments...}).
+:- impure pred @var{Pred}(@var{Arguments}@dots{}).
@end example
@noindent
or
@example
-:- semipure pred @var{Pred}(@var{Arguments...}).
+:- semipure pred @var{Pred}(@var{Arguments}@dots{}).
@end example
@noindent
@@ -4084,17 +4343,17 @@
:- impure pred init_max is det.
:- pragma c_code(init_max,
- will_not_call_mercury,
+ [will_not_call_mercury],
"max = INT_MIN;").
:- impure pred set_max(int::in) is det.
:- pragma c_code(set_max(X::in),
- will_not_call_mercury,
+ [will_not_call_mercury],
"if (X > max) max = X;").
:- semipure pred get_max(int::out) is det.
:- pragma c_code(get_max(X::out),
- will_not_call_mercury,
+ [will_not_call_mercury],
"X = max;").
:- pragma promise_pure(max_solution/2).
@@ -4223,7 +4482,7 @@
additional pragmas:
@menu
-* Fact tables:: Support for very large tables of facts.
+* Fact tables:: Support for very large tables of facts.
* Tabled evaluation:: Support for automatically recording previously
calculated results and detecting or avoiding
certain kinds of infinite loops.
--
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