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