NAME¶
avcall - build a C argument list incrementally and call a C function on it.
SYNOPSIS¶
#include <avcall.h>
av_alist alist;
av_start_type(alist, &func
[[
, return_type]
, &return_value
]
);
av_type(alist,
[
arg_type,]
value);
av_call(alist);
DESCRIPTION¶
This set of macros builds an argument list for a C function and calls the
function on it. It significantly reduces the amount of `glue' code required
for parsers, debuggers, imbedded interpreters, C extensions to application
programs and other situations where collections of functions need to be called
on lists of externally-supplied arguments.
Function calling conventions differ considerably on different machines and
avcall attempts to provide some degree of isolation from such
architecture dependencies.
The interface is like
stdarg(3) in reverse. All of the macros return 0
for success, < 0 for failure (e.g., argument list overflow or
type-not-supported).
- (1)
- #include <avcall.h>
and declare the argument list structure
av_alist alist;
- (2)
- Set any special flags. This is architecture and compiler dependent.
Compiler options that affect passing conventions may need to be flagged by
#defines before the #include <avcall.h> statement.
However, the configure script should have determined which
#defines are needed and put them at the top of
avcall.h.
- (3)
- Initialize the alist with the function address and return value pointer
(if any). There is a separate macro for each simple return type ([u]char,
[u]short, [u]int, [u]long, [u]longlong, float, double, where `u' indicates
`unsigned'). The macros for functions returning structures or pointers
require an explicit type argument.
E.g.,
av_start_int (alist, &func,
&int_return);
av_start_double (alist, &func,
&double_return);
av_start_void (alist, &func);
av_start_struct (alist, &func, struct_type, splittable,
&struct_return);
av_start_ptr (alist, &func, pointer_type,
&pointer_return);
The
splittable flag specifies whether the
struct_type can be
returned in registers such that every struct field fits entirely in a single
register. This needs to be specified for structs of size 2*sizeof(long). For
structs of size <= sizeof(long),
splittable is ignored and assumed
to be 1. For structs of size > 2*sizeof(long),
splittable is ignored
and assumed to be 0. There are some handy macros for this:
av_word_splittable_1 (type1)
av_word_splittable_2 (type1, type2)
av_word_splittable_3 (type1, type2, type3)
av_word_splittable_4 (type1, type2, type3, type4)
For a struct with three slots
struct { type1 id1; type2 id2; type3 id3; }
you can specify
splittable as
av_word_splittable_3
(type1, type2, type3) .
- (4)
- Push the arguments on to the list in order. Again there is a macro for
each simple built-in type, and the macros for structure and pointer
arguments require an extra type argument:
av_int (alist, int_value);
av_double (alist, double_value);
av_struct (alist, struct_or_union_type,
struct_value);
av_ptr (alist, pointer_type,
pointer_value);
- (5)
- Call the function, set the return value, and tidy up:
av_call (alist);
NOTES¶
(1) Functions whose first declaration is in Kernighan & Ritchie style (i.e.,
without a typed argument list) MUST use default K&R C expression
promotions (char and short to int, float to double) whether they are compiled
by a K&R or an ANSI compiler, because the true argument types may not be
known at the call point. Such functions typically back-convert their arguments
to the declared types on function entry. (In fact, the only way to pass a true
char, short or float in K&R C is by an explicit cast:
func((char)c,(float)f) ). Similarly, some K&R compilers (such as
Sun cc on the sparc) actually return a float as a double.
Hence, for arguments of functions declared in K&R style you should use
av_int() and
av_double() rather than
av_char(),
av_short() or
av_float(). If you use a K&R compiler, the
avcall header files may be able to detect this and define
av_float(),
etc, appropriately, but with an ANSI compiler there is no way
avcall
can know how a function was declared, so you have to correct the argument
types yourself.
(2) The explicit type arguments of the
av_struct() and
av_ptr()
macros are typically used to calculate size, alignment, and passing
conventions. This may not be sufficient for some machines with unusual
structure and pointer handling: in this case additional
av_start_type() and
av_type() macros
may be defined.
(3) The macros
av_start_longlong(),
av_start_ulonglong(),
av_longlong() and
av_ulonglong() work only if the C compiler has
a working
long long 64-bit integer type.
(4) The struct types used in
av_start_struct() and
av_struct()
must only contain (signed or unsigned) int, long, long long or pointer fields.
Struct types containing (signed or unsigned) char, short, float, double or
other structs are not supported.
SEE ALSO¶
stdarg(3),
varargs(3).
BUGS¶
The current implementations have been tested on a selection of common cases but
there are probably still many bugs.
There are typically built-in limits on the size of the argument-list, which may
also include the size of any structure arguments.
The decision whether a struct is to be returned in registers or in memory
considers only the struct's size and alignment. This is inaccurate: for
example, gcc on m68k-next returns
struct { char a,b,c; } in registers
and
struct { char a[3]; } in memory, although both types have the same
size and the same alignment.
NON-BUGS¶
All information is passed in CPU registers and the stack. The
avcall
package is therefore multithread-safe.
PORTING AVCALL¶
Ports, bug-fixes, and suggestions are most welcome. The macros required for
argument pushing are pretty grungy, but it does seem to be possible to port
avcall to a range of machines. Ports to non-standard or non-32-bit machines
are especially welcome so we can sort the interface out before it's too late.
Knowledge about argument passing conventions can be found in the gcc source,
file gcc-2.6.3/config/
cpu/
cpu.h, section "Stack layout;
function entry, exit and calling."
Some of the grunge is usually handled by a C or assembly level glue routine that
actually pushes the arguments, calls the function and unpacks any return
value. This is called __builtin_avcall(). A precompiled assembler version for
people without gcc is also made available. The routine should ideally have
flags for the passing conventions of other compilers.
Many of the current routines waste a lot of stack space and generally do hairy
things to stack frames - a bit more assembly code would probably help things
along quite a bit here.
AUTHOR¶
Bill Triggs <Bill.Triggs@inrialpes.fr>.
ACKNOWLEDGEMENTS¶
Some initial ideas were stolen from the C interface to the Zelk extensions to
Oliver Laumann's Elk scheme interpreter by J.P.Lewis, NEC C&C Research,
<zilla@ccrl.nj.nec.com> (for Sun4 & SGI), and Roy Featherstone's
<roy@robots.oxford.ac.uk> personal C interface library for Sun[34] &
SGI. I also looked at the machine-dependent parts of the GCC and GDB
distributions, and put the gcc asm() extensions to good use. Thanks guys!
This work was partly supported by EC-ESPRIT Basic Research Action SECOND.