for review: document pragma import & nondet pragma c_code

Fergus Henderson fjh at cs.mu.OZ.AU
Thu Nov 5 04:33:39 AEDT 1998


Hi,

Tom and Zoltan, could you both please review this?

--------------------

Estimated hours taken: 3

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/04 17:30:08
@@ -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}. 
@@ -3222,10 +3222,98 @@
 @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 menu
+* @samp{pragma import}::           Importing C functions
+* @samp{pragma c_code}::           Defining Mercury procedures using C code
+* Nondet @samp{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 @samp{pragma import}
+ at subsubsection @samp{pragma import}
+
+A declaration of the form
+
+ at example
+:- 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
+ at 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 @samp{pragma c_code}}).
+
+ at node @samp{pragma c_code}
+ at subsubsection @samp{pragma c_code}
+
 A declaration of the form
 
 @example
-:- pragma c_code(@var{Pred}(@var{Var1}::@var{Mode1}, @var{Var2}::@var{Mode2}, ...),
+:- pragma c_code(@var{Pred}(@var{Var1}::@var{Mode1}, @var{Var2}::@var{Mode2}, @dots{}),
         @var{Attributes}, @var{C_Code}).
 @end example
 
@@ -3233,8 +3321,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 +3330,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 +3342,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 +3353,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 +3364,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 +3374,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 +3391,172 @@
 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 @samp{pragma c_code}
+ at subsubsection Nondet @samp{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}, @var{LocalVars}, @var{FirstCode}, @var{RetryCode},
+        @var{SharedCode}).
+ 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}), shared_code(@var{SharedCode})).
+ at end example
+
+ at noindent
+Here @var{FirstCode}, @var{RetryCode}, and @var{SharedCode} 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 shared_code(@var{SharedCode}) argument is optional; if present,
+ at var{SharedCode} willl 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 @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 @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{SharedCode}
+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{SharedCode} may not access the input variables --
+only @var{FirstCode} should access the input variables.
+if @var{RetryCode} or @var{SharedCode} need to access any of the input variables,
+then @var{FirstCode} should copy the values needed to the @var{LocalVars}.
+
+The following example shows how you can use a state variable to
+keep track of 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;
+        "),
+        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 @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 @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 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 +3582,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 +3672,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 +3693,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 export}).
+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 +3775,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 +3828,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
 
@@ -3834,7 +4079,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 +4265,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 +4329,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 +4468,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