[z88dk-dev] cpp magic for generating prototypes

Bridge to the z88dk-developers mailing list
Post Reply
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

[z88dk-dev] cpp magic for generating prototypes

Post by alvin »

Am I right in thinking it's not possible to use cpp to generate prototypes as described below?


What I want to do is define macros "FASTCALL(...)" and "CALLEE(..)" whose definitions depend on which compiler is active. Then the header file declares prototypes in terms of these macros as in "FASTCALL(char *,strlen,char *s)"

Just as an example:

*******************
FILE "__prototypes__.h"
********************


#ifdef __PROTOTYPES_H__
#define __PROTOTYPES_H__

#undef FASTCALL
#undef CALLEE

#ifdef __SDCC

#define FASTCALL(ret0,ret1,func,arg) \
ret0 ret1##func(arg); \
ret0 ret1##func##_fastcall(arg) __z88dk_fastcall; \
\#define func(a) func##_fastcall(a)

#else

#define FASTCALL(ret0,ret1,func,arg) \
ret0 __LIB__ __FASTCALL__ ret1##func(arg);

#endif

#endif


***********
FILE "string.h"
***********

#ifndef _STRING_H_
#define _STRING_H_

#include <__prototypes__.h>

FASTCALL(char,*,strlen,char *s)

#endif




In "string.h" the FASTCALL macro expands to create the prototype. For the sdcc case the macro is more complicated for fastcall and with the definition above and strlen as test the generated output is:


char *strlen(char *s); char *strlen_fastcall(char *s) __z88dk_fastcall; \#define strlen(a) strlen_fastcall(a)


What is needed is:

char *strlen(char *s);
char *strlen_fastcall(char *s) __z88dk_fastcall;
#define strlen(a) strlen_fastcall(a)


Aside from the lack of formatiing in the result, I can't see of any way to generate another #define from inside a macro.


If this can't be done with cpp what do you think of passing the headers through m4 before sending to cpp?



------------------------------------------------------------------------------
Monitor 25 network devices or servers for free with OpManager!
OpManager is web-based network management software that monitors
network devices and physical & virtual servers, alerts via email & sms
for fault. Monitor 25 devices for free with no restriction. Download now
http://ad.doubleclick.net/ddm/clk/292181274;119417398;o
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I think cpp can't do this so I tried an m4 solution instead. Here's how it works.

FILE "__link__.m4" is stored with the rest of the C header files. This is an m4 file that defines FASTCALL and CALLEE macros whose definitions depend on the currently active compiler.

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

divert(-1)

###################################################################
# SET APPROPRIATE FUNCTION LINKAGE DEPENDING ON THE ACTIVE COMPILER
###################################################################

undefine(`FASTCALL')
undefine(`CALLEE')

# FASTCALL MACRO FOR FASTCALL FUNCTIONS

define(`FASTCALL', ifdef(`m4_SDCC',dnl
dnl
extern `$1' `$2'`$3'`'(`$4');
extern `$1' `$2'`$3'_fastcall`'(`$4') __z88dk_fastcall;
`#define' `$3'`'(a) `$3'_fastcall`'(a)

,ifdef(`m4_SCCZ80',dnl
dnl
extern `$1' __LIB__ __FASTCALL__ `$2'`$3'`'(`$4');

,dnl
dnl
extern `$1' `$2'`$3'`'(`$4');
)))

# CALLEE MACRO FOR CALLEE FUNCTIONS

define(`CALLEE', `ifdef(`m4_SDCC',dnl
dnl
extern `$1' `$2'`$3'`'(`shift(shift(shift($@)))');
extern `$1' `$2'`$3'_callee`'(`shift(shift(shift($@)))') __z88dk_callee;
`#define' `$3'`'(substr(`a,b,c,d,e,f,g,h,i,j',0,eval($#+$#-7))) `$3'_callee`'(substr(`a,b,c,d,e,f,g,h,i,j',0,eval($#+$#-7)))

,`ifdef(`m4_SCCZ80',dnl
dnl
extern `$1' __LIB__ `$2'`$3'`'(`shift(shift(shift($@)))');
extern `$1' __LIB__ __CALLEE__ `$2'`$3'_callee`'(`shift(shift(shift($@)))');
`#define' `$3'`'(substr(`a,b,c,d,e,f,g,h,i,j',0,eval($#+$#-7))) `$3'_callee`'(substr(`a,b,c,d,e,f,g,h,i,j',0,eval($#+$#-7)))

,dnl
dnl
extern `$1' `$2'`$3'`'(`shift(shift(shift($@)))');
)')')

###################################################################

divert(0)

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


The C header files would then define prototypes using those macros. Here's is a short "string.h" to illustrate.

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

include(__link__.m4)

#ifndef _STRING_H
#define _STRING_H

#include <stddef.h>

#ifndef _SIZE_T_DEFINED
#define _SIZE_T_DEFINED
typedef unsigned int size_t;
#endif

#ifndef NULL
#define NULL ((void*)(0))
#endif

CALLEE(char,*,_memlwr_,void *p, size_t n)
CALLEE(char,*,_memstrcpy_,void *p, char *s, size_t n)
CALLEE(char,*,_memupr_,void *p,size_t n)
FASTCALL(char,*,_strrstrip_,char *s)
FASTCALL(int,,ffs,int i)
FASTCALL(int,,ffsl,long i)
CALLEE(size_t,,strlcpy,char *dst,char *src,size_t n)
FASTCALL(char,*,strrev,char *s)

#endif

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


In can run m4 on this header file in three different ways.


(1) "m4 string.h"
This generates a header file with prototypes lacking any decorations.

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

#ifndef _STRING_H
#define _STRING_H

#include <stddef.h>

#ifndef _SIZE_T_DEFINED
#define _SIZE_T_DEFINED
typedef unsigned int size_t;
#endif

#ifndef NULL
#define NULL ((void*)(0))
#endif

extern char *_memlwr_(void *p,size_t n);

extern char *_memstrcpy_(void *p,char *s,size_t n);

extern char *_memupr_(void *p,size_t n);

extern char *_strrstrip_(char *s);

extern int ffs(int i);

extern int ffsl(long i);

extern size_t strlcpy(char *dst,char *src,size_t n);

extern char *strrev(char *s);


#endif

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


(2) "m4 -Dm4_SCCZ80 string.h"
This generates a header with prototypes for sccz80.

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


#ifndef _STRING_H
#define _STRING_H

#include <stddef.h>

#ifndef _SIZE_T_DEFINED
#define _SIZE_T_DEFINED
typedef unsigned int size_t;
#endif

#ifndef NULL
#define NULL ((void*)(0))
#endif

extern char __LIB__ *_memlwr_(void *p,size_t n);
extern char __LIB__ __CALLEE__ *_memlwr__callee(void *p,size_t n);
#define _memlwr_(a,b) _memlwr__callee(a,b)


extern char __LIB__ *_memstrcpy_(void *p,char *s,size_t n);
extern char __LIB__ __CALLEE__ *_memstrcpy__callee(void *p,char *s,size_t n);
#define _memstrcpy_(a,b,c) _memstrcpy__callee(a,b,c)


extern char __LIB__ *_memupr_(void *p,size_t n);
extern char __LIB__ __CALLEE__ *_memupr__callee(void *p,size_t n);
#define _memupr_(a,b) _memupr__callee(a,b)


extern char __LIB__ __FASTCALL__ *_strrstrip_(char *s);


extern int __LIB__ __FASTCALL__ ffs(int i);


extern int __LIB__ __FASTCALL__ ffsl(long i);


extern size_t __LIB__ strlcpy(char *dst,char *src,size_t n);
extern size_t __LIB__ __CALLEE__ strlcpy_callee(char *dst,char *src,size_t n);
#define strlcpy(a,b,c) strlcpy_callee(a,b,c)


extern char __LIB__ __FASTCALL__ *strrev(char *s);



#endif

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


(3) "m4 -Dm4_SDCC string.h"
This generates a header with prototypes for sdcc.

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




#ifndef _STRING_H
#define _STRING_H

#include <stddef.h>

#ifndef _SIZE_T_DEFINED
#define _SIZE_T_DEFINED
typedef unsigned int size_t;
#endif

#ifndef NULL
#define NULL ((void*)(0))
#endif

extern char *_memlwr_(void *p,size_t n);
extern char *_memlwr__callee(void *p,size_t n) __z88dk_callee;
#define _memlwr_(a,b) _memlwr__callee(a,b)


extern char *_memstrcpy_(void *p,char *s,size_t n);
extern char *_memstrcpy__callee(void *p,char *s,size_t n) __z88dk_callee;
#define _memstrcpy_(a,b,c) _memstrcpy__callee(a,b,c)


extern char *_memupr_(void *p,size_t n);
extern char *_memupr__callee(void *p,size_t n) __z88dk_callee;
#define _memupr_(a,b) _memupr__callee(a,b)


extern char *_strrstrip_(char *s);
extern char *_strrstrip__fastcall(char *s) __z88dk_fastcall;
#define _strrstrip_(a) _strrstrip__fastcall(a)


extern int ffs(int i);
extern int ffs_fastcall(int i) __z88dk_fastcall;
#define ffs(a) ffs_fastcall(a)


extern int ffsl(long i);
extern int ffsl_fastcall(long i) __z88dk_fastcall;
#define ffsl(a) ffsl_fastcall(a)


extern size_t strlcpy(char *dst,char *src,size_t n);
extern size_t strlcpy_callee(char *dst,char *src,size_t n) __z88dk_callee;
#define strlcpy(a,b,c) strlcpy_callee(a,b,c)


extern char *strrev(char *s);
extern char *strrev_fastcall(char *s) __z88dk_fastcall;
#define strrev(a) strrev_fastcall(a)



#endif

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



I realize now the macro can probably check if there is only one param and automatically choose FASTCALL for that case and CALEE for others. Another macro would be necessary to generate ordinary prototypes for vararg functions.


There's a problem with when zcc should invoke m4. If it does things in this order: m4, zcpp, c compiler then the #includes have not been processed yet and the library function prototypes are not yet in the file. However if zcc does this: zcpp, m4, c compiler then zcpp cannot do the text substutions defined by the #defines generated by m4. I don't think zcc can get away with this: zcpp, m4, zcpp, c compiler because running some other macros twice could cause trouble. So I think we would have to add an option to zcpp to only process #includes then zcc could do this: zcpp -includes, m4, zcpp, c compiler.


Is this making any sense?



------------------------------------------------------------------------------
Monitor 25 network devices or servers for free with OpManager!
OpManager is web-based network management software that monitors
network devices and physical & virtual servers, alerts via email & sms
for fault. Monitor 25 devices for free with no restriction. Download now
http://ad.doubleclick.net/ddm/clk/292181274;119417398;o
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

There's a problem with when zcc should invoke m4. If it does things in this order: m4, zcpp, c compiler then the #includes have not been processed yet and the library function prototypes are not yet in the file. However if zcc does this: zcpp, m4, c compiler then zcpp cannot do the text substutions defined by the #defines generated by m4. I don't think zcc can get away with this: zcpp, m4, zcpp, c compiler because running some other macros twice could cause trouble. So I think we would have to add an option to zcpp to only process #includes then zcc could do this: zcpp -includes, m4, zcpp, c compiler.
We don't have to get m4 to process c files at compile time if these headers are generated ahead of time.

So I think the best way to do this might be to have all the header files created using the m4 method and then to generate the sdcc and sccz80 files in a subdir tree. Then when sdcc is used to compile its include path is the sdcc subdir and when sccz80 is used its include path is the sccz80 subdir. So no invoking m4 at compile time.

For the macros, there can be one macro to generate 'decorated prototypes', call it "D_PROTO" that will create both fastcall and callee prototypes where fastcall is chosen when the number of params = 1. Another called "O_PROTO" can generate ordinary prototypes as in prototypes without any fastcall/callee prototypes. One last type coul dbe "V_PROTO" that would generate prototypes for vararg functions. O_PROTO and V_PROTO probably won't be different but using V_PROTO for all vararg functions might make it possible to do something with this in future with little effort.

One future thing is maybe getting V_PROTO and perhaps the other m4 macros to reverse param order for sccz80 so that it can do R->L order. If this is done, sdcc and sccz80 will be able to completely share all asm code and most C preamble code and that will make it possible to mix object files for sccz80 and sdcc with only a few exceptions. But I think this will be an investigation for later -- I'll just do the first steps which is to clear up these headers and change all C preamble code to jump to the asm implementation rather than include in the callee preamble.



------------------------------------------------------------------------------
Monitor 25 network devices or servers for free with OpManager!
OpManager is web-based network management software that monitors
network devices and physical & virtual servers, alerts via email & sms
for fault. Monitor 25 devices for free with no restriction. Download now
http://ad.doubleclick.net/ddm/clk/292181274;119417398;o
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I've committed the new header file method now. math.h will be broken until I've done fastcall and callee properly, hopefully before the next build.

The top of the development include directory is here:

http://z88dk.cvs.sourceforge.net/viewvc ... VELOPMENT/

Most of those directories are dead. Only proto, sdcc, sccz80 are live along with the files Winmake.bat and __link__.m4

__link__.m4 defines the m4 macros "__DPROTO", "__OPROTO" and "__VPROTO". "__DPROTO" stands for decorated prototype and this will expand prototypes for fastcall or callee declaration appropriate for the current compiler. "__VPROTO" is for vararg functions and "__OPROTO" is for ordinary functions. There is no difference between the two currently and both declare prototypes without callee/fastcall decorations but in future there may be a reason to change VPROTO.

The actual prototypes are stored are in the proto subdir tree. These are written in terms of the m4 macros. An excerpt from stdio.h that shows usage of all three macros:

83 __OPROTO(int,,getchar,void)
84 __DPROTO(int,,getdelim,char **lineptr,size_t *n,int delim,FILE *stream)
85 __DPROTO(int,,getline,char **lineptr,size_t *n,FILE *stream)
86 __DPROTO(char,,gets,char *s)
87 __VPROTO(int,,obstack_printf,struct obstack *ob,char *format,...)

The first param is the return type without asterisks, the second parameter is the asterisk part of the return type if present, the third is the function name and the rest is the list of parameters.


The Winmake.bat file (http://z88dk.cvs.sourceforge.net/viewvc ... iew=markup) runs m4 on these prototypes to generate headers in the sdcc subdir tree and the sccz80 subdir tree tailored for each compiler. The size of the header files has decreased considerably.

The config files are changed to point the compile's include path to the sdcc subtree or the sccz80 subtree depending on the clib chosen.



------------------------------------------------------------------------------
Monitor 25 network devices or servers for free with OpManager!
OpManager is web-based network management software that monitors
network devices and physical & virtual servers, alerts via email & sms
for fault. Monitor 25 devices for free with no restriction. Download now
http://ad.doubleclick.net/ddm/clk/292181274;119417398;o
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

math.h will be broken until I've done fastcall and callee properly, hopefully before the next build.
Everything should be working. fastcall/callee is complete for math.h so you can call math functions via function pointers.

sccz80 is crashing while trying to generate code for function pointer calls with double parameters. I'll put a note in the bugs forum as well.


// zcc +zx -vn -a -clib=new crash.c

#include <math.h>

void *f;

main()
{
f = cos;
(f)(0.0);

// f = copysign;
// (f)(1.0,-1.0);
}



------------------------------------------------------------------------------
Monitor 25 network devices or servers for free with OpManager!
OpManager is web-based network management software that monitors
network devices and physical & virtual servers, alerts via email & sms
for fault. Monitor 25 devices for free with no restriction. Download now
http://ad.doubleclick.net/ddm/clk/292181274;119417398;o
Post Reply