[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