From: Erik Naggum
Subject: function call conventions
Date: 
Message-ID: <3039432101496639@arcana.naggum.no>
I'm looking for papers on function call conventions and implementations for
Common Lisp, including such issues as

    * &optional and &key arguments
    * &optional and &key arguments with init-forms
    * &optional and &key arguments with supplied-p indicators
    * &rest arguments
    * sequencing of &key arguments

it appears from my reading of CLtL2 and dpANS CL that the supplied-p
indicators are computed locally to the function, implying that there is a
model for function call conventions that include a variable number of
arguments, callee's responsibility for init-forms (which also have the
preceding arguments in scope, no less), and this again implies that &key
arguments may be very expensive because the callee may have to scan the
argument list for the keywords in the order specified in the lambda list
(otherwise, it would not be able to compute the init-forms correctly should
they reference preceding keyword values), and &rest may have to cons up a
list from values possibly passed on the stack.

the real question: is it possible to assume in the caller that what is
known at its compile-time about the function's lambda list will not change,
provided that the function to be called is not declared `notinline'?

-- 
reinvention is its own reward

From: Sean Foderaro
Subject: Re: function call conventions
Date: 
Message-ID: <JKF.96Apr25120950@tiger.Franz.com>
>> the real question: is it possible to assume in the caller that what is
>> known at its compile-time about the function's lambda list will not change,
>> provided that the function to be called is not declared `notinline'?

 No, you can't make that assumption.    The function's lambda list
may be different by the time you get around to calling it.
 
From: Erik Naggum
Subject: Re: function call conventions
Date: 
Message-ID: <3039470963102521@arcana.naggum.no>
[Sean Foderaro]

|   >> the real question: is it possible to assume in the caller that what
|   >> is known at its compile-time about the function's lambda list will
|   >> not change, provided that the function to be called is not declared
|   >> `notinline'?
|   
|   No, you can't make that assumption.  The function's lambda list may be
|   different by the time you get around to calling it.

(thanks also to David Gadbois for his reply.)

I was unclear.  I was thinking about complete type declarations, not just
function definitions, but since I have been working with type inference
systems to produce such declarations automatically, I didn't mention them
explicitly.

suppose you have a function definition

    (defun fun (foo &optional (bar 0) &key (zot 0) (quux #C(0d0 0d0)))
      ...
      (1+ bar))

and the compiler is able to infer the types so that the declaration
(here largely meaningless -- a real example would be too large)

      (declare (string foo)
	       (fixnum bar)
	       (integer zot)
	       ((complex double-float) quux))

would apply to the lambda list, and the declaration that would apply to
function calls would go

    (declaim (ftype (function (string &optional fixnum
			       &key (zot integer)
				    (quux (complex double-float)))
			      integer)
		    fun))

given this, and the lack of a `notinline' declaration, I would like to
believe that the compiler can, indeed, utilize this information in making
the most out of the function call.

dpANS CL (I believe the last public draft is practically the same as the
published standard), says that a declamation of a function's type makes a
function call

    (fun "foo" 9 :zot 42 :quux #C(23d0 1d0))

will be treated as if it were

    (the integer (fun (the string "foo") (the fixnum 9)
		      :zot (the integer 42)
		      :quux (the (complex double-float) #C(23d0 1d0)))

I'm a little disappointed by the sheer amount of manual work involved in
declaring these types, but, hey, that's what programmable programming
languages are for, and I would never have worried about this if I weren't
working with type inference systems.

now I can clarify the question: provided that the function is not declared
`notinline', can I do a heavily optimized function call utilizing this
knowledge to make short-cuts, or would I still have to do the general case?

that is, I infer from my readings of the standard and CLtL2 that the amount
of optimization available to the compiler in function calls is limited with
the `notinline' declaration, and expanded with the `inline' declaration.

or, put another way, I think I understand the standard to mean that if I
want to be able to redefine functions, I should declare them `notinline'.
if I don't declare them `notinline', the compiler is free to make any
number of assumptions from its visible definition of those functions, where
"definition" _includes_ declarations of their calling patterns (ftype
declarations).  (the standard does not say this outright, I wonder if I can
make that assumption.)


now, IF these assumptions hold, I can reduce the function call overhead in
Common Lisp to that of the SPARC function call convention, for instance,
just like compilers for other languages do (I think these are called
"ABI").  in other words, I should be able to compile a Common Lisp function
and calls to it into "normal" function call conventions without very
peculiar calling conventions (as far as other languages see them).

the purpose of this elaborated question is to find out whether I can write
a system for calling functions in any number of languages from Common Lisp
through predefined calling conventions, and vice versa.  this is not only a
question of foreign function interfaces, but also of being able to write in
Common Lisp and generate, e.g., C code that conforms to the C function
calling conventions.

using the extremely flexible lambda lists in Common Lisp, I think I should
be able to map over the set of calling conventions in other languages and
thus I can compute the complexity of the lambda lists that can communicate
with individual languages.  instead of using an approach like ILU, I would
like to "compile" (translate, really), restricted sets of Common Lisp code
into languages whose calling conventions I know.  by extension, I expect to
be able to write arbitrarily complex "calling conventions" that include
protecting variables from GC and calling functions with variant calling
conventions, such as in GNU Emacs, without enforcing these conventions on
the caller.  rather, the callee should specify its relationship to the
external world through an interface specification, and then callers would
comply.

however, all this hinges on being able to write code in Common Lisp that
retains its ability to compile and run in a Common Lisp system with all the
bells and whistles present.  I'm currently at the stage where I try to find
out whether this is outright lunacy or just a hell of a lot of work.

-- 
reinvention is its own reward
From: David Gadbois
Subject: Re: function call conventions
Date: 
Message-ID: <4loo90$9ok@bobby.cs.utexas.edu>
Erik Naggum  <ยทยทยทยท@naggum.no> wrote:
>I'm looking for papers on function call conventions and implementations for
>Common Lisp, including such issues as
>
>    * &optional and &key arguments
>    * &optional and &key arguments with init-forms
>    * &optional and &key arguments with supplied-p indicators
>    * &rest arguments
>    * sequencing of &key arguments

Don't forget about &ALLOW-OTHER-KEYS and :ALLOW-OTHER-KEYS!

Naggum goes on to describe accurately what a big hairy mess Common
Lisp general function calling is.  Except for general-case keyword
matching, it is really not as bad as it looks: You just have to be
careful about order-of-evaluation and evaluation environment issues.

>the real question: is it possible to assume in the caller that what is
>known at its compile-time about the function's lambda list will not change,
>provided that the function to be called is not declared `notinline'?

The cases where you can assume inlineness are covered in the
"Semantics Constraints" section of the ANS CL spec (section 3.2.2.3 of
X3J13/92-102.  Has anyone succeeded in a good HTMLization of the last
public spec?).  Basically, it allows for compile-time assumptions in
the case of recursive calls, FTYPE and INLINE declarations, and, in
the case of COMPILE-FILE, calls to functions within the same file.

With such assumptions, you can really go to town on keyword arguments.
The approach in the CMUCL's Python compiler is to treat it as a
constant-propogation problem.  A programmer-level approach that avoids
the issue is to define a macro wrapper that takes a bunch of keywords
and expands into a call to a function with positional arguments.

--David Gadbois