AVCALL(3) AVCALL(3)
NNAAMMEE
avcall - build a C argument list incrementally and call a
C function on it.
SSYYNNOOPPSSIISS
##iinncclluuddee <>
aavv__aalliisstt _a_l_i_s_t;;
aavv__ssttaarrtt___t_y_p_e((_a_l_i_s_t,, _&_f_u_n_c [[,, _r_e_t_u_r_n___t_y_p_e],, _&_r_e_t_u_r_n___v_a_l_u_e
]));;
aavv___t_y_p_e((_a_l_i_s_t,, [_a_r_g___t_y_p_e,,] _v_a_l_u_e));;
aavv__ccaallll((_a_l_i_s_t));;
DDEESSCCRRIIPPTTIIOONN
This set of macros builds an argument list for a C func-
tion and calls the function on it. It significantly
reduces the amount of `glue' code required for parsers,
debuggers, imbedded interpreters, C extensions to applica-
tion programs and other situations where collections of
functions need to be called on lists of externally-sup-
plied arguments.
Function calling conventions differ considerably on dif-
ferent machines and _a_v_c_a_l_l attempts to provide some degree
of isolation from such architecture dependencies.
The interface is like ssttddaarrgg(3) in reverse. All of the
macros return 0 for success, < 0 for failure (e.g., argu-
ment list overflow or type-not-supported).
(1) ##iinncclluuddee <>
and declare the argument list structure
aavv__aalliisstt _a_l_i_s_t;;
(2) Set any special flags. This is architecture and
compiler dependent. Compiler options that affect
passing conventions may need to be flagged by
##ddeeffiinnees before the ##iinncclluuddee <> statement.
However, the _c_o_n_f_i_g_u_r_e script should have deter-
mined which ##ddeeffiinnees are needed and put them at the
top of aavvccaallll..hh.
(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, dou-
ble, where `u' indicates `unsigned'). The macros
for functions returning structures or pointers
require an explicit type argument.
E.g.,
14 January 2001 1
AVCALL(3) AVCALL(3)
aavv__ssttaarrtt__iinntt ((_a_l_i_s_t,, _&_f_u_n_c,, _&_i_n_t___r_e_t_u_r_n));;
aavv__ssttaarrtt__ddoouubbllee ((_a_l_i_s_t,, _&_f_u_n_c,, _&_d_o_u_b_l_e___r_e_t_u_r_n));;
aavv__ssttaarrtt__vvooiidd ((_a_l_i_s_t,, _&_f_u_n_c));;
aavv__ssttaarrtt__ssttrruucctt ((_a_l_i_s_t,, _&_f_u_n_c,, _s_t_r_u_c_t___t_y_p_e,, _s_p_l_i_t_t_a_b_l_e,,
_&_s_t_r_u_c_t___r_e_t_u_r_n));;
aavv__ssttaarrtt__ppttrr ((_a_l_i_s_t,, _&_f_u_n_c,, _p_o_i_n_t_e_r___t_y_p_e,,
_&_p_o_i_n_t_e_r___r_e_t_u_r_n));;
The _s_p_l_i_t_t_a_b_l_e flag specifies whether the _s_t_r_u_c_t___t_y_p_e 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), _s_p_l_i_t_t_a_b_l_e is ignored and assumed to be 1.
For structs of size > 2*sizeof(long), _s_p_l_i_t_t_a_b_l_e is
ignored and assumed to be 0. There are some handy macros
for this:
aavv__wwoorrdd__sspplliittttaabbllee__11 ((_t_y_p_e_1))
aavv__wwoorrdd__sspplliittttaabbllee__22 ((_t_y_p_e_1,, _t_y_p_e_2))
aavv__wwoorrdd__sspplliittttaabbllee__33 ((_t_y_p_e_1,, _t_y_p_e_2,, _t_y_p_e_3))
aavv__wwoorrdd__sspplliittttaabbllee__44 ((_t_y_p_e_1,, _t_y_p_e_2,, _t_y_p_e_3,, _t_y_p_e_4))
For a struct with three slots
ssttrruucctt {{ _t_y_p_e_1 _i_d_1;; _t_y_p_e_2 _i_d_2;; _t_y_p_e_3 _i_d_3;; }}
you can specify _s_p_l_i_t_t_a_b_l_e as aavv__wwoorrdd__sspplliittttaabbllee__33 ((_t_y_p_e_1,,
_t_y_p_e_2,, _t_y_p_e_3)) .
(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:
aavv__iinntt ((_a_l_i_s_t,, _i_n_t___v_a_l_u_e));;
aavv__ddoouubbllee ((_a_l_i_s_t,, _d_o_u_b_l_e___v_a_l_u_e));;
aavv__ssttrruucctt ((_a_l_i_s_t,, _s_t_r_u_c_t___o_r___u_n_i_o_n___t_y_p_e,, _s_t_r_u_c_t___v_a_l_u_e));;
aavv__ppttrr ((_a_l_i_s_t,, _p_o_i_n_t_e_r___t_y_p_e,, _p_o_i_n_t_e_r___v_a_l_u_e));;
(5) Call the function, set the return value, and tidy
up:
aavv__ccaallll ((_a_l_i_s_t));;
NNOOTTEESS
(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
14 January 2001 2
AVCALL(3) AVCALL(3)
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:
ffuunncc((((cchhaarr))cc,,((ffllooaatt))ff)) ). 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 aavv__iinntt(()) and aavv__ddoouubbllee(()) rather than
aavv__cchhaarr(()),, aavv__sshhoorrtt(()) or aavv__ffllooaatt(()).. If you use a K&R
compiler, the avcall header files may be able to detect
this and define aavv__ffllooaatt(()),, etc, appropriately, but with
an ANSI compiler there is no way _a_v_c_a_l_l can know how a
function was declared, so you have to correct the argument
types yourself.
(2) The explicit type arguments of the aavv__ssttrruucctt(()) and
aavv__ppttrr(()) macros are typically used to calculate size,
alignment, and passing conventions. This may not be suf-
ficient for some machines with unusual structure and
pointer handling: in this case additional aavv__ssttaarrtt___t_y_p_e(())
and aavv___t_y_p_e(()) macros may be defined.
(3) The macros aavv__ssttaarrtt__lloonngglloonngg(()), aavv__ssttaarrtt__uulloonngglloonngg(()),
aavv__lloonngglloonngg(()) and aavv__uulloonngglloonngg(()) work only if the C com-
piler has a working lloonngg lloonngg 64-bit integer type.
(4) The struct types used in aavv__ssttaarrtt__ssttrruucctt(()) and
aavv__ssttrruucctt(()) must only contain (signed or unsigned) int,
long, long long or pointer fields. Struct types contain-
ing (signed or unsigned) char, short, float, double or
other structs are not supported.
SSEEEE AALLSSOO
ssttddaarrgg(3), vvaarraarrggss(3).
BBUUGGSS
The current implementations have been tested on a selec-
tion 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 regis-
ters or in memory considers only the struct's size and
alignment. This is inaccurate: for example, gcc on m68k-
next returns ssttrruucctt {{ cchhaarr aa,,bb,,cc;; }} in registers and
ssttrruucctt {{ cchhaarr aa[[33]];; }} in memory, although both types have
the same size and the same alignment.
14 January 2001 3
AVCALL(3) AVCALL(3)
NNOONN--BBUUGGSS
All information is passed in CPU registers and the stack.
The aavvccaallll package is therefore multithread-safe.
PPOORRTTIINNGG AAVVCCAALLLL
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/_c_p_u/_c_p_u.h, sec-
tion "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.
AAUUTTHHOORR
Bill Triggs .
AACCKKNNOOWWLLEEDDGGEEMMEENNTTSS
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,
(for Sun4 & SGI), and Roy Featherstone's
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.
14 January 2001 4