From: Dale Weiler Date: Sun, 28 Apr 2013 13:54:01 +0000 (+0000) Subject: Perliminary library support X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=2f129b3b2de88d380140746fe37a67d221eb6faa;p=xonotic%2Fgmqcc.git Perliminary library support --- diff --git a/Makefile b/Makefile index f713417..821867f 100644 --- a/Makefile +++ b/Makefile @@ -238,18 +238,18 @@ uninstall: # DO NOT DELETE -util.o: gmqcc.h opts.def -code.o: gmqcc.h opts.def -ast.o: gmqcc.h opts.def ast.h ir.h -ir.o: gmqcc.h opts.def ir.h -conout.o: gmqcc.h opts.def -ftepp.o: gmqcc.h opts.def lexer.h -opts.o: gmqcc.h opts.def -fs.o: gmqcc.h opts.def -utf8.o: gmqcc.h opts.def -correct.o: gmqcc.h opts.def -pak.o: gmqcc.h opts.def -test.o: gmqcc.h opts.def -main.o: gmqcc.h opts.def lexer.h -lexer.o: gmqcc.h opts.def lexer.h -parser.o: gmqcc.h opts.def lexer.h ast.h ir.h intrin.h +util.o: base.h opts.def +code.o: base.h opts.def +ast.o: base.h opts.def ast.h ir.h +ir.o: base.h opts.def ir.h +conout.o: base.h opts.def +ftepp.o: base.h opts.def lexer.h +opts.o: base.h opts.def +fs.o: base.h opts.def +utf8.o: base.h opts.def +correct.o: base.h opts.def +pak.o: base.h opts.def +test.o: base.h opts.def +main.o: base.h opts.def lexer.h +lexer.o: base.h opts.def lexer.h +parser.o: base.h opts.def lexer.h ast.h ir.h intrin.h diff --git a/ast.c b/ast.c index 1e2836f..ca0e200 100644 --- a/ast.c +++ b/ast.c @@ -25,7 +25,7 @@ #include #include -#include "gmqcc.h" +#include "base.h" #include "ast.h" #define ast_instantiate(T, ctx, destroyfn) \ diff --git a/base.h b/base.h new file mode 100644 index 0000000..34f994e --- /dev/null +++ b/base.h @@ -0,0 +1,1160 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * Wolfgang Bumiller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef GMQCC_BASE_HDR +#define GMQCC_BASE_HDR +#include +#include +#include +#include +#include +#include + +/* + * Disable some over protective warnings in visual studio because fixing them is a waste + * of my time. + */ +#ifdef _MSC_VER +# pragma warning(disable : 4244 ) /* conversion from 'int' to 'float', possible loss of data */ +#endif /*! _MSC_VER */ + +#define GMQCC_VERSION_MAJOR 0 +#define GMQCC_VERSION_MINOR 3 +#define GMQCC_VERSION_PATCH 0 +#define GMQCC_VERSION_BUILD(J,N,P) (((J)<<16)|((N)<<8)|(P)) +#define GMQCC_VERSION \ + GMQCC_VERSION_BUILD(GMQCC_VERSION_MAJOR, GMQCC_VERSION_MINOR, GMQCC_VERSION_PATCH) +/* Undefine the following on a release-tag: */ +#define GMQCC_VERSION_TYPE_DEVEL + +/* Full version string in case we need it */ +#ifdef GMQCC_VERSION_TYPE_DEVEL +# ifdef GMQCC_GITINFO +# define GMQCC_DEV_VERSION_STRING "git build: " GMQCC_GITINFO "\n" +# elif defined(GMQCC_VERSION_TYPE_DEVEL) +# define GMQCC_DEV_VERSION_STRING "development build\n" +# else +# define GMQCC_DEV_VERSION_STRING +# endif /*! GMQCC_GITINGO */ +#else +# define GMQCC_DEV_VERSION_STRING +#endif + +#define GMQCC_STRINGIFY(x) #x +#define GMQCC_IND_STRING(x) GMQCC_STRINGIFY(x) +#define GMQCC_FULL_VERSION_STRING \ +"GMQCC " \ +GMQCC_IND_STRING(GMQCC_VERSION_MAJOR) "." \ +GMQCC_IND_STRING(GMQCC_VERSION_MINOR) "." \ +GMQCC_IND_STRING(GMQCC_VERSION_PATCH) \ +" Built " __DATE__ " " __TIME__ \ +"\n" GMQCC_DEV_VERSION_STRING + +/* + * We cannot rely on C99 at all, since compilers like MSVC + * simply don't support it. We define our own boolean type + * as a result (since we cannot include ). For + * compilers that are in 1999 mode (C99 compliant) we can use + * the language keyword _Bool which can allow for better code + * on GCC and GCC-like compilers, opposed to `int`. + */ +#ifndef __cplusplus +# ifdef false +# undef false +# endif /*! false */ +# ifdef true +# undef true +# endif /*! true */ +# define false (0) +# define true (1) +# ifdef __STDC_VERSION__ +# if __STDC_VERSION__ < 199901L && __GNUC__ < 3 + typedef int bool; +# else + typedef _Bool bool; +# endif /*! __STDC_VERSION__ < 199901L && __GNUC__ < 3 */ +# else + typedef int bool; +# endif /*! __STDC_VERSION__ */ +#endif /*! __cplusplus */ + +/* + * Of some functions which are generated we want to make sure + * that the result isn't ignored. To find such function calls, + * we use this macro. + */ +#if defined(__GNUC__) || defined(__CLANG__) +# define GMQCC_WARN __attribute__((warn_unused_result)) +# define GMQCC_USED __attribute__((used)) +#else +# define GMQCC_WARN +# define GMQCC_USED +#endif /*! defined(__GNUC__) || defined (__CLANG__) */ +/* + * This is a hack to silent clang regarding empty + * body if statements. + */ +#define GMQCC_SUPPRESS_EMPTY_BODY do { } while (0) + +/* + * Inline is not supported in < C90, however some compilers + * like gcc and clang might have an inline attribute we can + * use if present. + */ +#ifdef __STDC_VERSION__ +# if __STDC_VERSION__ < 199901L +# if defined(__GNUC__) || defined (__CLANG__) +# if __GNUC__ < 2 +# define GMQCC_INLINE +# else +# define GMQCC_INLINE __attribute__ ((always_inline)) +# endif /*! __GNUC__ < 2 */ +# else +# define GMQCC_INLINE +# endif /*! defined(__GNUC__) || defined (__CLANG__) */ +# else +# define GMQCC_INLINE inline +# endif /*! __STDC_VERSION < 199901L */ +/* + * Visual studio has __forcinline we can use. So lets use that + * I suspect it also has just __inline of some sort, but our use + * of inline is correct (not guessed), WE WANT IT TO BE INLINE + */ +#elif defined(_MSC_VER) +# define GMQCC_INLINE __forceinline +#else +# define GMQCC_INLINE +#endif /*! __STDC_VERSION__ */ + +/* + * noreturn is present in GCC and clang + * it's required for _ast_node_destory otherwise -Wmissing-noreturn + * in clang complains about there being no return since abort() is + * called. + */ +#if (defined(__GNUC__) && __GNUC__ >= 2) || defined(__CLANG__) +# define GMQCC_NORETURN __attribute__ ((noreturn)) +#else +# define GMQCC_NORETURN +#endif /*! (defined(__GNUC__) && __GNUC__ >= 2) || defined (__CLANG__) */ + +#ifndef _MSC_VER +# include +#else + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; +#endif /*! _MSC_VER */ + +/* + * Very roboust way at determining endianess at compile time: this handles + * almost every possible situation. Otherwise a runtime check has to be + * performed. + */ +#define GMQCC_BYTE_ORDER_LITTLE 1234 +#define GMQCC_BYTE_ORDER_BIG 4321 + +#if defined (__GNUC__) || defined (__GNU_LIBRARY__) +# if defined (__FreeBSD__) || defined (__OpenBSD__) +# include +# elif defined (BSD) && (BSD >= 199103) || defined (__DJGPP__) || defined (__CYGWIN32__) +# include +# elif defined (__APPLE__) +# if defined (__BIG_ENDIAN__) && !defined(BIG_ENDIAN) +# define BIG_ENDIAN +# elif defined (__LITTLE_ENDIAN__) && !defined (LITTLE_ENDIAN) +# define LITTLE_ENDIAN +# endif /*! defined (__BIG_ENDIAN__) && !defined(BIG_ENDIAN) */ +# elif !defined (__MINGW32__) +# include +# if !defined (__BEOS__) +# include +# endif /*! !definde (__BEOS__) */ +# endif /*! defined (__FreeBSD__) || defined (__OpenBSD__) */ +#endif /*! defined (__GNUC__) || defined (__GNU_LIBRARY__) */ +#if !defined(PLATFORM_BYTE_ORDER) +# if defined (LITTLE_ENDIAN) || defined (BIG_ENDIAN) +# if defined (LITTLE_ENDIAN) && !defined(BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif !defined (LITTLE_ENDIAN) && defined (BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# elif defined (BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif defined (BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# endif /*! defined (LITTLE_ENDIAN) && !defined(BIG_ENDIAN) */ +# elif defined (_LITTLE_ENDIAN) || defined (_BIG_ENDIAN) +# if defined (_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif !defined (_LITTLE_ENDIAN) && defined (_BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# elif defined (_BYTE_ORDER) && (_BYTE_ORDER == _LITTLE_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif defined (_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# endif /*! defined (_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) */ +# elif defined (__LITTLE_ENDIAN__) || defined (__BIG_ENDIAN__) +# if defined (__LITTLE_ENDIAN__) && !defined (__BIG_ENDIAN__) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif !defined (__LITTLE_ENDIAN__) && defined (__BIG_ENDIAN__) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# elif defined (__BYTE_ORDER__) && (__BYTE_ORDER__ == __LITTLE_ENDIAN__) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif defined (__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# endif /*! defined (__LITTLE_ENDIAN__) && !defined (__BIG_ENDIAN__) */ +# endif /*! defined(LITTLE_ENDIAN) || defined (BIG_ENDIAN) */ +#endif /*! !defined(PLATFORM_BYTE_ORDER) */ +#if !defined (PLATFORM_BYTE_ORDER) +# if defined (__alpha__) || defined (__alpha) || defined (i386) || \ + defined (__i386__) || defined (_M_I86) || defined (_M_IX86) || \ + defined (__OS2__) || defined (sun386) || defined (__TURBOC__) || \ + defined (vax) || defined (vms) || defined (VMS) || \ + defined (__VMS) || defined (__x86_64__) || defined (_M_IA64) || \ + defined (_M_X64) || defined (__i386) || defined (__x86_64) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif defined (AMIGA) || defined (applec) || defined (__AS400__) || \ + defined (_CRAY) || defined (__hppa) || defined (__hp9000) || \ + defined (ibm370) || defined (mc68000) || defined (m68k) || \ + defined (__MRC__) || defined (__MVS__) || defined (__MWERKS__) || \ + defined (sparc) || defined (__sparc) || defined (SYMANTEC_C) || \ + defined (__TANDEM) || defined (THINK_C) || defined (__VMCMS__) || \ + defined (__PPC__) || defined (__PPC) || defined (PPC) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# else +# define PLATFORM_BYTE_ORDER -1 +# endif +#endif /*! !defined (PLATFORM_BYTE_ORDER) */ + +/* + * On windows systems where we're not compiling with MING32 we need a + * little extra help on dependinces for implementing our own dirent.h + * in fs.c. + */ +#if defined(_WIN32) && !defined(__MINGW32__) +# define _WIN32_LEAN_AND_MEAN +# include +# include +# include + + struct dirent { + long d_ino; + unsigned short d_reclen; + unsigned short d_namlen; + char d_name[FILENAME_MAX]; + }; + + typedef struct { + struct _finddata_t dd_dta; + struct dirent dd_dir; + long dd_handle; + int dd_stat; + char dd_name[1]; + } DIR; + /* + * Visual studio also lacks S_ISDIR for sys/stat.h, so we emulate this as well + * which is not hard at all. + */ +# ifdef S_ISDIR +# undef S_ISDIR +# endif /*! S_ISDIR */ +# define S_ISDIR(X) ((X)&_S_IFDIR) +#else +# include +#endif /*! _WIN32 && !defined(__MINGW32__) */ + + +/*===================================================================*/ +/*=========================== util.c ================================*/ +/*===================================================================*/ +bool util_filexists (const char *); +bool util_strupper (const char *); +bool util_strdigit (const char *); +char *util_strdup (const char *); +char *util_strdupe (const char *); +size_t util_strtocmd (const char *, char *, size_t); +size_t util_strtononcmd (const char *, char *, size_t); + +uint16_t util_crc16(uint16_t crc, const char *data, size_t len); +void util_seed (uint32_t); +uint32_t util_rand (); + +void util_endianswap(void *, size_t, unsigned int); + +void *util_mem_a(size_t bytes); +void *util_mem_r(void *ptr, size_t bytes); +void util_mem_f(void *ptr); + +#define mem_a(X) util_mem_a(X) +#define mem_d(X) util_mem_f((void*)(X)) +#define mem_r(X,Y) util_mem_r((void*)(X), (Y)) + +/* + * String functions (formatting, copying, concatenating, errors). These are wrapped + * to use the MSVC _safe_ versions when using MSVC, plus some implementations of + * these are non-conformant or don't exist such as asprintf and snprintf, which are + * not supported in C90, but do exist in C99. + */ +int util_vasprintf(char **ret, const char *fmt, va_list); +int util_asprintf (char **ret, const char *fmt, ...); +int util_snprintf (char *src, size_t bytes, const char *format, ...); +char *util_strcat (char *dest, const char *src); +char *util_strncpy (char *dest, const char *src, size_t num); +const char *util_strerror (int num); + + +/* + * A flexible vector implementation: all vector pointers contain some + * data about themselfs exactly - sizeof(vector_t) behind the pointer + * this data is represented in the structure below. Doing this allows + * us to use the array [] to access individual elements from the vector + * opposed to using set/get methods. + */ +typedef struct { + size_t allocated; + size_t used; + + /* can be extended now! whoot */ +} vector_t; + +/* hidden interface */ +void _util_vec_grow(void **a, size_t i, size_t s); +#define GMQCC_VEC_WILLGROW(X,Y) ( \ + ((!(X) || vec_meta(X)->used + Y >= vec_meta(X)->allocated)) ? \ + (void)_util_vec_grow(((void**)&(X)), (Y), sizeof(*(X))) : \ + (void)0 \ +) + +/* exposed interface */ +#define vec_meta(A) (((vector_t*)((void*)A)) - 1) +#define vec_free(A) ((void)((A) ? (mem_d((void*)vec_meta(A)), (A) = NULL) : 0)) +#define vec_push(A,V) (GMQCC_VEC_WILLGROW((A),1), (A)[vec_meta(A)->used++] = (V)) +#define vec_size(A) ((A) ? vec_meta(A)->used : 0) +#define vec_add(A,N) (GMQCC_VEC_WILLGROW((A),(N)), vec_meta(A)->used += (N), &(A)[vec_meta(A)->used-(N)]) +#define vec_last(A) ((A)[vec_meta(A)->used - 1]) +#define vec_pop(A) ((void)(vec_meta(A)->used -= 1)) +#define vec_shrinkto(A,N) ((void)(vec_meta(A)->used = (N))) +#define vec_shrinkby(A,N) ((void)(vec_meta(A)->used -= (N))) +#define vec_append(A,N,S) ((void)(memcpy(vec_add((A), (N)), (S), (N) * sizeof(*(S))))) +#define vec_upload(X,Y,S) ((void)(memcpy(vec_add((X), (S) * sizeof(*(Y))), (Y), (S) * sizeof(*(Y))))) +#define vec_remove(A,I,N) ((void)(memmove((A)+(I),(A)+((I)+(N)),sizeof(*(A))*(vec_meta(A)->used-(I)-(N))),vec_meta(A)->used-=(N))) + +typedef struct trie_s { + void *value; + struct trie_s *entries; +} correct_trie_t; + +correct_trie_t* correct_trie_new(); + +typedef struct hash_table_t { + size_t size; + struct hash_node_t **table; +} hash_table_t, *ht; + +/* + * hashtable implementation: + * + * Note: + * This was designed for pointers: you manage the life of the object yourself + * if you do use this for non-pointers please be warned that the object may not + * be valid if the duration of it exceeds (i.e on stack). So you need to allocate + * yourself, or put those in global scope to ensure duration is for the whole + * runtime. + * + * util_htnew(size) -- to make a new hashtable + * util_htset(table, key, value, sizeof(value)) -- to set something in the table + * util_htget(table, key) -- to get something from the table + * util_htdel(table) -- to delete the table + * + * example of use: + * + * ht foo = util_htnew(1024); + * int data = 100; + * char *test = "hello world\n"; + * util_htset(foo, "foo", (void*)&data); + * util_gtset(foo, "bar", (void*)test); + * + * printf("foo: %d, bar %s", + * *((int *)util_htget(foo, "foo")), + * ((char*)util_htget(foo, "bar")) + * ); + * + * util_htdel(foo); + */ +hash_table_t *util_htnew (size_t size); +void util_htrem (hash_table_t *ht, void (*callback)(void *data)); +void util_htset (hash_table_t *ht, const char *key, void *value); +void util_htdel (hash_table_t *ht); +size_t util_hthash(hash_table_t *ht, const char *key); +void util_htseth(hash_table_t *ht, const char *key, size_t hash, void *value); +void util_htrmh (hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)); +void util_htrm (hash_table_t *ht, const char *key, void (*cb)(void*)); + +void *util_htget (hash_table_t *ht, const char *key); +void *util_htgeth(hash_table_t *ht, const char *key, size_t hash); + +/*===================================================================*/ +/*============================ file.c ===============================*/ +/*===================================================================*/ +/* file handling */ +void fs_file_close (FILE *); +int fs_file_error (FILE *); +int fs_file_getc (FILE *); +int fs_file_printf (FILE *, const char *, ...); +int fs_file_puts (FILE *, const char *); +int fs_file_seek (FILE *, long int, int); +long int fs_file_tell (FILE *); + +size_t fs_file_read (void *, size_t, size_t, FILE *); +size_t fs_file_write (const void *, size_t, size_t, FILE *); + +FILE *fs_file_open (const char *, const char *); +int fs_file_getline(char **, size_t *, FILE *); + +/* directory handling */ +int fs_dir_make (const char *); +DIR *fs_dir_open (const char *); +int fs_dir_close (DIR *); +struct dirent *fs_dir_read (DIR *); + + +/*===================================================================*/ +/*=========================== correct.c =============================*/ +/*===================================================================*/ +typedef struct { + char ***edits; + size_t **lens; +} correction_t; + +void correct_del (correct_trie_t*, size_t **); +void correct_add (correct_trie_t*, size_t ***, const char *); +char *correct_str (correction_t *, correct_trie_t*, const char *); +void correct_init(correction_t *); +void correct_free(correction_t *); + +/*===================================================================*/ +/*=========================== code.c ================================*/ +/*===================================================================*/ + +/* TODO: cleanup */ +/* Note: if you change the order, fix type_sizeof in ir.c */ +enum { + TYPE_VOID , + TYPE_STRING , + TYPE_FLOAT , + TYPE_VECTOR , + TYPE_ENTITY , + TYPE_FIELD , + TYPE_FUNCTION , + TYPE_POINTER , + TYPE_INTEGER , + TYPE_VARIANT , + TYPE_STRUCT , + TYPE_UNION , + TYPE_ARRAY , + + TYPE_NIL , /* it's its own type / untyped */ + TYPE_NOEXPR , /* simply invalid in expressions */ + + TYPE_COUNT +}; + +/* const/var qualifiers */ +#define CV_NONE 0 +#define CV_CONST 1 +#define CV_VAR -1 +#define CV_WRONG 0x8000 /* magic number to help parsing */ + +extern const char *type_name [TYPE_COUNT]; +extern const uint16_t type_store_instr [TYPE_COUNT]; +extern const uint16_t field_store_instr[TYPE_COUNT]; + +/* + * could use type_store_instr + INSTR_STOREP_F - INSTR_STORE_F + * but this breaks when TYPE_INTEGER is added, since with the enhanced + * instruction set, the old ones are left untouched, thus the _I instructions + * are at a seperate place. + */ +extern const uint16_t type_storep_instr[TYPE_COUNT]; +extern const uint16_t type_eq_instr [TYPE_COUNT]; +extern const uint16_t type_ne_instr [TYPE_COUNT]; +extern const uint16_t type_not_instr [TYPE_COUNT]; + +typedef struct { + uint32_t offset; /* Offset in file of where data begins */ + uint32_t length; /* Length of section (how many of) */ +} prog_section; + +typedef struct { + uint32_t version; /* Program version (6) */ + uint16_t crc16; + uint16_t skip; + + prog_section statements; /* prog_section_statement */ + prog_section defs; /* prog_section_def */ + prog_section fields; /* prog_section_field */ + prog_section functions; /* prog_section_function */ + prog_section strings; + prog_section globals; + uint32_t entfield; /* Number of entity fields */ +} prog_header; + +/* + * Each paramater incerements by 3 since vector types hold + * 3 components (x,y,z). + */ +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 (OFS_RETURN+3) +#define OFS_PARM1 (OFS_PARM0 +3) +#define OFS_PARM2 (OFS_PARM1 +3) +#define OFS_PARM3 (OFS_PARM2 +3) +#define OFS_PARM4 (OFS_PARM3 +3) +#define OFS_PARM5 (OFS_PARM4 +3) +#define OFS_PARM6 (OFS_PARM5 +3) +#define OFS_PARM7 (OFS_PARM6 +3) + +typedef struct { + uint16_t opcode; + + /* operand 1 */ + union { + int16_t s1; /* signed */ + uint16_t u1; /* unsigned */ + } o1; + /* operand 2 */ + union { + int16_t s1; /* signed */ + uint16_t u1; /* unsigned */ + } o2; + /* operand 3 */ + union { + int16_t s1; /* signed */ + uint16_t u1; /* unsigned */ + } o3; + + /* + * This is the same as the structure in darkplaces + * { + * unsigned short op; + * short a,b,c; + * } + * But this one is more sane to work with, and the + * type sizes are guranteed. + */ +} prog_section_statement; + +typedef struct { + /* + * The types: + * 0 = ev_void + * 1 = ev_string + * 2 = ev_float + * 3 = ev_vector + * 4 = ev_entity + * 5 = ev_field + * 6 = ev_function + * 7 = ev_pointer -- engine only + * 8 = ev_bad -- engine only + */ + uint16_t type; + uint16_t offset; + uint32_t name; +} prog_section_both; + +typedef prog_section_both prog_section_def; +typedef prog_section_both prog_section_field; + +/* this is ORed to the type */ +#define DEF_SAVEGLOBAL (1<<15) +#define DEF_TYPEMASK ((1<<15)-1) + +typedef struct { + int32_t entry; /* in statement table for instructions */ + uint32_t firstlocal; /* First local in local table */ + uint32_t locals; /* Total ints of params + locals */ + uint32_t profile; /* Always zero (engine uses this) */ + uint32_t name; /* name of function in string table */ + uint32_t file; /* file of the source file */ + int32_t nargs; /* number of arguments */ + uint8_t argsize[8]; /* size of arguments (keep 8 always?) */ +} prog_section_function; + +/* + * Instructions + * These are the external instructions supported by the interperter + * this is what things compile to (from the C code). + */ +enum { + INSTR_DONE, + INSTR_MUL_F, + INSTR_MUL_V, + INSTR_MUL_FV, /* NOTE: the float operands must NOT be at the same locations: A != C */ + INSTR_MUL_VF, /* and here: B != C */ + INSTR_DIV_F, + INSTR_ADD_F, + INSTR_ADD_V, + INSTR_SUB_F, + INSTR_SUB_V, + INSTR_EQ_F, + INSTR_EQ_V, + INSTR_EQ_S, + INSTR_EQ_E, + INSTR_EQ_FNC, + INSTR_NE_F, + INSTR_NE_V, + INSTR_NE_S, + INSTR_NE_E, + INSTR_NE_FNC, + INSTR_LE, + INSTR_GE, + INSTR_LT, + INSTR_GT, + INSTR_LOAD_F, + INSTR_LOAD_V, + INSTR_LOAD_S, + INSTR_LOAD_ENT, + INSTR_LOAD_FLD, + INSTR_LOAD_FNC, + INSTR_ADDRESS, + INSTR_STORE_F, + INSTR_STORE_V, + INSTR_STORE_S, + INSTR_STORE_ENT, + INSTR_STORE_FLD, + INSTR_STORE_FNC, + INSTR_STOREP_F, + INSTR_STOREP_V, + INSTR_STOREP_S, + INSTR_STOREP_ENT, + INSTR_STOREP_FLD, + INSTR_STOREP_FNC, + INSTR_RETURN, + INSTR_NOT_F, + INSTR_NOT_V, + INSTR_NOT_S, + INSTR_NOT_ENT, + INSTR_NOT_FNC, + INSTR_IF, + INSTR_IFNOT, + INSTR_CALL0, + INSTR_CALL1, + INSTR_CALL2, + INSTR_CALL3, + INSTR_CALL4, + INSTR_CALL5, + INSTR_CALL6, + INSTR_CALL7, + INSTR_CALL8, + INSTR_STATE, + INSTR_GOTO, + INSTR_AND, + INSTR_OR, + INSTR_BITAND, + INSTR_BITOR, + + /* + * Virtual instructions used by the IR + * Keep at the end! + */ + VINSTR_END, + VINSTR_PHI, + VINSTR_JUMP, + VINSTR_COND, + /* A never returning CALL. + * Creating this causes IR blocks to be marked as 'final'. + * No-Return-Call + */ + VINSTR_NRCALL +}; + +/* uhh? */ +typedef float qcfloat; +typedef int32_t qcint; + +typedef struct { + prog_section_statement *statements; + int *linenums; + prog_section_def *defs; + prog_section_field *fields; + prog_section_function *functions; + int *globals; + char *chars; + uint16_t crc; + uint32_t entfields; + ht string_cache; + qcint string_cached_empty; +} code_t; + +/* + * code_write -- writes out the compiled file + * code_init -- prepares the code file + * code_genstrin -- generates string for code + * code_alloc_field -- allocated a field + * code_push_statement -- keeps statements and linenumbers together + * code_pop_statement -- keeps statements and linenumbers together + */ +bool code_write (code_t *, const char *filename, const char *lno); +code_t *code_init (void); +uint32_t code_genstring (code_t *, const char *string); +qcint code_alloc_field (code_t *, size_t qcsize); +void code_push_statement(code_t *, prog_section_statement *stmt, int linenum); +void code_pop_statement (code_t *); + +/* + * A shallow copy of a lex_file to remember where which ast node + * came from. + */ +typedef struct { + const char *file; + size_t line; +} lex_ctx; + +/*===================================================================*/ +/*============================ con.c ================================*/ +/*===================================================================*/ +enum { + CON_BLACK = 30, + CON_RED, + CON_GREEN, + CON_BROWN, + CON_BLUE, + CON_MAGENTA, + CON_CYAN , + CON_WHITE +}; + +/* message level */ +enum { + LVL_MSG, + LVL_WARNING, + LVL_ERROR +}; + +FILE *con_default_out(); +FILE *con_default_err(); + +void con_vprintmsg (int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap); +void con_printmsg (int level, const char *name, size_t line, const char *msgtype, const char *msg, ...); +void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap); +void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...); + +void con_close (); +void con_init (); +void con_reset (); +void con_color (int); +int con_change(const char *, const char *); +int con_verr (const char *, va_list); +int con_vout (const char *, va_list); +int con_err (const char *, ...); +int con_out (const char *, ...); + +/* error/warning interface */ +extern size_t compile_errors; +extern size_t compile_Werrors; +extern size_t compile_warnings; + +void /********/ compile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, ...); +void /********/ vcompile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, va_list ap); +bool GMQCC_WARN compile_warning (lex_ctx ctx, int warntype, const char *fmt, ...); +bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap); +void compile_show_werrors(); + +/*===================================================================*/ +/*========================= assembler.c =============================*/ +/*===================================================================*/ +/* TODO: remove this ... */ +static const struct { + const char *m; /* menomic */ + const size_t o; /* operands */ + const size_t l; /* menomic len */ +} asm_instr[] = { + { "DONE" , 1, 4 }, + { "MUL_F" , 3, 5 }, + { "MUL_V" , 3, 5 }, + { "MUL_FV" , 3, 6 }, + { "MUL_VF" , 3, 6 }, + { "DIV" , 0, 3 }, + { "ADD_F" , 3, 5 }, + { "ADD_V" , 3, 5 }, + { "SUB_F" , 3, 5 }, + { "SUB_V" , 3, 5 }, + { "EQ_F" , 0, 4 }, + { "EQ_V" , 0, 4 }, + { "EQ_S" , 0, 4 }, + { "EQ_E" , 0, 4 }, + { "EQ_FNC" , 0, 6 }, + { "NE_F" , 0, 4 }, + { "NE_V" , 0, 4 }, + { "NE_S" , 0, 4 }, + { "NE_E" , 0, 4 }, + { "NE_FNC" , 0, 6 }, + { "LE" , 0, 2 }, + { "GE" , 0, 2 }, + { "LT" , 0, 2 }, + { "GT" , 0, 2 }, + { "FIELD_F" , 0, 7 }, + { "FIELD_V" , 0, 7 }, + { "FIELD_S" , 0, 7 }, + { "FIELD_ENT" , 0, 9 }, + { "FIELD_FLD" , 0, 9 }, + { "FIELD_FNC" , 0, 9 }, + { "ADDRESS" , 0, 7 }, + { "STORE_F" , 0, 7 }, + { "STORE_V" , 0, 7 }, + { "STORE_S" , 0, 7 }, + { "STORE_ENT" , 0, 9 }, + { "STORE_FLD" , 0, 9 }, + { "STORE_FNC" , 0, 9 }, + { "STOREP_F" , 0, 8 }, + { "STOREP_V" , 0, 8 }, + { "STOREP_S" , 0, 8 }, + { "STOREP_ENT", 0, 10}, + { "STOREP_FLD", 0, 10}, + { "STOREP_FNC", 0, 10}, + { "RETURN" , 0, 6 }, + { "NOT_F" , 0, 5 }, + { "NOT_V" , 0, 5 }, + { "NOT_S" , 0, 5 }, + { "NOT_ENT" , 0, 7 }, + { "NOT_FNC" , 0, 7 }, + { "IF" , 0, 2 }, + { "IFNOT" , 0, 5 }, + { "CALL0" , 1, 5 }, + { "CALL1" , 2, 5 }, + { "CALL2" , 3, 5 }, + { "CALL3" , 4, 5 }, + { "CALL4" , 5, 5 }, + { "CALL5" , 6, 5 }, + { "CALL6" , 7, 5 }, + { "CALL7" , 8, 5 }, + { "CALL8" , 9, 5 }, + { "STATE" , 0, 5 }, + { "GOTO" , 0, 4 }, + { "AND" , 0, 3 }, + { "OR" , 0, 2 }, + { "BITAND" , 0, 6 }, + { "BITOR" , 0, 5 }, + + { "END" , 0, 3 } /* virtual assembler instruction */ +}; +/*===================================================================*/ +/*============================= ir.c ================================*/ +/*===================================================================*/ + +enum store_types { + store_global, + store_local, /* local, assignable for now, should get promoted later */ + store_param, /* parameters, they are locals with a fixed position */ + store_value, /* unassignable */ + store_return /* unassignable, at OFS_RETURN */ +}; + +typedef struct { + qcfloat x, y, z; +} vector; + +vector vec3_add (vector, vector); +vector vec3_sub (vector, vector); +qcfloat vec3_mulvv(vector, vector); +vector vec3_mulvf(vector, float); + +/*===================================================================*/ +/*============================= exec.c ==============================*/ +/*===================================================================*/ + +/* TODO: cleanup */ +/* + * Darkplaces has (or will have) a 64 bit prog loader + * where the 32 bit qc program is autoconverted on load. + * Since we may want to support that as well, let's redefine + * float and int here. + */ +typedef union { + qcint _int; + qcint string; + qcint function; + qcint edict; + qcfloat _float; + qcfloat vector[3]; + qcint ivector[3]; +} qcany; + +typedef char qcfloat_size_is_correct [sizeof(qcfloat) == 4 ?1:-1]; +typedef char qcint_size_is_correct [sizeof(qcint) == 4 ?1:-1]; + +enum { + VMERR_OK, + VMERR_TEMPSTRING_ALLOC, + + VMERR_END +}; + +#define VM_JUMPS_DEFAULT 1000000 + +/* execute-flags */ +#define VMXF_DEFAULT 0x0000 /* default flags - nothing */ +#define VMXF_TRACE 0x0001 /* trace: print statements before executing */ +#define VMXF_PROFILE 0x0002 /* profile: increment the profile counters */ + +struct qc_program_s; + +typedef int (*prog_builtin)(struct qc_program_s *prog); + +typedef struct { + qcint stmt; + size_t localsp; + prog_section_function *function; +} qc_exec_stack; + +typedef struct qc_program_s { + char *filename; + + prog_section_statement *code; + prog_section_def *defs; + prog_section_def *fields; + prog_section_function *functions; + char *strings; + qcint *globals; + qcint *entitydata; + bool *entitypool; + + const char* *function_stack; + + uint16_t crc16; + + size_t tempstring_start; + size_t tempstring_at; + + qcint vmerror; + + size_t *profile; + + prog_builtin *builtins; + size_t builtins_count; + + /* size_t ip; */ + qcint entities; + size_t entityfields; + bool allowworldwrites; + + qcint *localstack; + qc_exec_stack *stack; + size_t statement; + + size_t xflags; + + int argc; /* current arg count for debugging */ +} qc_program; + +qc_program* prog_load(const char *filename, bool ignoreversion); +void prog_delete(qc_program *prog); + +bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps); + +char* prog_getstring (qc_program *prog, qcint str); +prog_section_def* prog_entfield (qc_program *prog, qcint off); +prog_section_def* prog_getdef (qc_program *prog, qcint off); +qcany* prog_getedict (qc_program *prog, qcint e); +qcint prog_tempstring(qc_program *prog, const char *_str); + + +/*===================================================================*/ +/*===================== parser.c commandline ========================*/ +/*===================================================================*/ +struct parser_s; + +struct parser_s *parser_create (); +bool parser_compile_file (struct parser_s *parser, const char *); +bool parser_compile_string(struct parser_s *parser, const char *, const char *, size_t); +bool parser_finish (struct parser_s *parser, const char *); +void parser_cleanup (struct parser_s *parser); + +/*===================================================================*/ +/*====================== ftepp.c commandline ========================*/ +/*===================================================================*/ +struct ftepp_s; +struct ftepp_s *ftepp_create (); +bool ftepp_preprocess_file (struct ftepp_s *ftepp, const char *filename); +bool ftepp_preprocess_string(struct ftepp_s *ftepp, const char *name, const char *str); +void ftepp_finish (struct ftepp_s *ftepp); +const char *ftepp_get (struct ftepp_s *ftepp); +void ftepp_flush (struct ftepp_s *ftepp); +void ftepp_add_define (struct ftepp_s *ftepp, const char *source, const char *name); +void ftepp_add_macro (struct ftepp_s *ftepp, const char *name, const char *value); + +/*===================================================================*/ +/*======================= main.c commandline ========================*/ +/*===================================================================*/ + +#if 1 +/* Helpers to allow for a whole lot of flags. Otherwise we'd limit + * to 32 or 64 -f options... + */ +typedef struct { + size_t idx; /* index into an array of 32 bit words */ + uint8_t bit; /* bit index for the 8 bit group idx points to */ +} longbit; +#define LONGBIT(bit) { ((bit)/32), ((bit)%32) } +#define LONGBIT_SET(B, I) ((B).idx = (I)/32, (B).bit = ((I)%32)) +#else +typedef uint32_t longbit; +#define LONGBIT(bit) (bit) +#define LONGBIT_SET(B, I) ((B) = (I)) +#endif + +/*===================================================================*/ +/*=========================== utf8lib.c =============================*/ +/*===================================================================*/ +typedef uint32_t uchar_t; + +bool u8_analyze (const char *_s, size_t *_start, size_t *_len, uchar_t *_ch, size_t _maxlen); +size_t u8_strlen (const char*); +size_t u8_strnlen (const char*, size_t); +uchar_t u8_getchar (const char*, const char**); +uchar_t u8_getnchar(const char*, const char**, size_t); +int u8_fromchar(uchar_t w, char *to, size_t maxlen); + +/*===================================================================*/ +/*============================= opts.c ==============================*/ +/*===================================================================*/ +typedef struct { + const char *name; + longbit bit; +} opts_flag_def; + +bool opts_setflag (const char *, bool); +bool opts_setwarn (const char *, bool); +bool opts_setwerror(const char *, bool); +bool opts_setoptim (const char *, bool); + +void opts_init (const char *, int, size_t); +void opts_set (uint32_t *, size_t, bool); +void opts_setoptimlevel(unsigned int); +void opts_ini_init (const char *); + +/* Saner flag handling */ +void opts_backup_non_Wall(); +void opts_restore_non_Wall(); +void opts_backup_non_Werror_all(); +void opts_restore_non_Werror_all(); + +enum { +# define GMQCC_TYPE_FLAGS +# define GMQCC_DEFINE_FLAG(X) X, +# include "opts.def" + COUNT_FLAGS +}; +static const opts_flag_def opts_flag_list[] = { +# define GMQCC_TYPE_FLAGS +# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) }, +# include "opts.def" + { NULL, LONGBIT(0) } +}; + +enum { +# define GMQCC_TYPE_WARNS +# define GMQCC_DEFINE_FLAG(X) WARN_##X, +# include "opts.def" + COUNT_WARNINGS +}; +static const opts_flag_def opts_warn_list[] = { +# define GMQCC_TYPE_WARNS +# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(WARN_##X) }, +# include "opts.def" + { NULL, LONGBIT(0) } +}; + +enum { +# define GMQCC_TYPE_OPTIMIZATIONS +# define GMQCC_DEFINE_FLAG(NAME, MIN_O) OPTIM_##NAME, +# include "opts.def" + COUNT_OPTIMIZATIONS +}; +static const opts_flag_def opts_opt_list[] = { +# define GMQCC_TYPE_OPTIMIZATIONS +# define GMQCC_DEFINE_FLAG(NAME, MIN_O) { #NAME, LONGBIT(OPTIM_##NAME) }, +# include "opts.def" + { NULL, LONGBIT(0) } +}; +static const unsigned int opts_opt_oflag[] = { +# define GMQCC_TYPE_OPTIMIZATIONS +# define GMQCC_DEFINE_FLAG(NAME, MIN_O) MIN_O, +# include "opts.def" + 0 +}; + +enum { +# define GMQCC_TYPE_OPTIONS +# define GMQCC_DEFINE_FLAG(X) OPTION_##X, +# include "opts.def" + OPTION_COUNT +}; + +extern unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS]; + +/* other options: */ +typedef enum { + COMPILER_QCC, /* circa QuakeC */ + COMPILER_FTEQCC, /* fteqcc QuakeC */ + COMPILER_QCCX, /* qccx QuakeC */ + COMPILER_GMQCC /* this QuakeC */ +} opts_std_t; + +typedef union { + bool B; + uint16_t U16; + uint32_t U32; + char *STR; +} opt_value_t; + + +typedef struct { + opt_value_t options [OPTION_COUNT]; + uint32_t flags [1 + (COUNT_FLAGS / 32)]; + uint32_t warn [1 + (COUNT_WARNINGS / 32)]; + uint32_t werror [1 + (COUNT_WARNINGS / 32)]; + uint32_t warn_backup [1 + (COUNT_WARNINGS / 32)]; + uint32_t werror_backup[1 + (COUNT_WARNINGS / 32)]; + uint32_t optimization [1 + (COUNT_OPTIMIZATIONS / 32)]; + bool optimizeoff; /* True when -O0 */ +} opts_cmd_t; + +extern opts_cmd_t opts; + +#define OPTS_GENERIC(f,i) (!! (((f)[(i)/32]) & (1<< ((i)%32)))) +#define OPTS_FLAG(i) OPTS_GENERIC(opts.flags, (i)) +#define OPTS_WARN(i) OPTS_GENERIC(opts.warn, (i)) +#define OPTS_WERROR(i) OPTS_GENERIC(opts.werror, (i)) +#define OPTS_OPTIMIZATION(i) OPTS_GENERIC(opts.optimization, (i)) +#define OPTS_OPTION_BOOL(X) (opts.options[X].B) +#define OPTS_OPTION_U16(X) (opts.options[X].U16) +#define OPTS_OPTION_U32(X) (opts.options[X].U32) +#define OPTS_OPTION_STR(X) (opts.options[X].STR) + +#endif /*! GMQCC_BASE_HDR */ diff --git a/code.c b/code.c index 77bba3e..4e404e9 100644 --- a/code.c +++ b/code.c @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "gmqcc.h" +#include "base.h" /* This is outrageous! */ #define QCINT_ENTRY void* @@ -136,9 +136,6 @@ static void code_create_header(code_t *code, prog_header *code_header) { code_header->entfield = code->entfields; if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) { - util_debug("GEN", "Patching stringtable for -fdarkplaces-stringtablebug\n"); - - /* >= + P */ vec_push(code->chars, '\0'); /* > */ vec_push(code->chars, '\0'); /* = */ vec_push(code->chars, '\0'); /* P */ @@ -253,8 +250,7 @@ bool code_write_memory(code_t *code, uint8_t **datmem, size_t *sizedat, uint8_t bool code_write(code_t *code, const char *filename, const char *lnofile) { prog_header code_header; - FILE *fp = NULL; - size_t it = 2; + FILE *fp = NULL; code_create_header(code, &code_header); @@ -300,60 +296,6 @@ bool code_write(code_t *code, const char *filename, const char *lnofile) { return false; } - util_debug("GEN","HEADER:\n"); - util_debug("GEN"," version: = %d\n", code_header.version ); - util_debug("GEN"," crc16: = %d\n", code_header.crc16 ); - util_debug("GEN"," entfield: = %d\n", code_header.entfield); - util_debug("GEN"," statements = {.offset = % 8d, .length = % 8d}\n", code_header.statements.offset, code_header.statements.length); - util_debug("GEN"," defs = {.offset = % 8d, .length = % 8d}\n", code_header.defs .offset, code_header.defs .length); - util_debug("GEN"," fields = {.offset = % 8d, .length = % 8d}\n", code_header.fields .offset, code_header.fields .length); - util_debug("GEN"," functions = {.offset = % 8d, .length = % 8d}\n", code_header.functions .offset, code_header.functions .length); - util_debug("GEN"," globals = {.offset = % 8d, .length = % 8d}\n", code_header.globals .offset, code_header.globals .length); - util_debug("GEN"," strings = {.offset = % 8d, .length = % 8d}\n", code_header.strings .offset, code_header.strings .length); - - /* FUNCTIONS */ - util_debug("GEN", "FUNCTIONS:\n"); - for (; it < vec_size(code->functions); it++) { - size_t j = code->functions[it].entry; - util_debug("GEN", " {.entry =% 5d, .firstlocal =% 5d, .locals =% 5d, .profile =% 5d, .name =% 5d, .file =% 5d, .nargs =% 5d, .argsize ={%d,%d,%d,%d,%d,%d,%d,%d} }\n", - code->functions[it].entry, - code->functions[it].firstlocal, - code->functions[it].locals, - code->functions[it].profile, - code->functions[it].name, - code->functions[it].file, - code->functions[it].nargs, - code->functions[it].argsize[0], - code->functions[it].argsize[1], - code->functions[it].argsize[2], - code->functions[it].argsize[3], - code->functions[it].argsize[4], - code->functions[it].argsize[5], - code->functions[it].argsize[6], - code->functions[it].argsize[7] - - ); - util_debug("GEN", " NAME: %s\n", &code->chars[code->functions[it].name]); - /* Internal functions have no code */ - if (code->functions[it].entry >= 0) { - util_debug("GEN", " CODE:\n"); - for (;;) { - if (code->statements[j].opcode != INSTR_DONE) - util_debug("GEN", " %-12s {% 5i,% 5i,% 5i}\n", - asm_instr[code->statements[j].opcode].m, - code->statements[j].o1.s1, - code->statements[j].o2.s1, - code->statements[j].o3.s1 - ); - else { - util_debug("GEN", " DONE {0x00000,0x00000,0x00000}\n"); - break; - } - j++; - } - } - } - vec_free(code->statements); vec_free(code->linenums); vec_free(code->defs); diff --git a/conout.c b/conout.c index dd4dca3..1228774 100644 --- a/conout.c +++ b/conout.c @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "gmqcc.h" +#include "base.h" #include /* diff --git a/correct.c b/correct.c index 7e16608..1453087 100644 --- a/correct.c +++ b/correct.c @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "gmqcc.h" +#include "base.h" /* * This is a very clever method for correcting mistakes in QuakeC code diff --git a/exec.c b/exec.c index 0ef3211..52e177b 100644 --- a/exec.c +++ b/exec.c @@ -27,7 +27,7 @@ #include #include -#include "gmqcc.h" +#include "base.h" static void loaderror(const char *fmt, ...) { diff --git a/fs.c b/fs.c index 501041f..159c759 100644 --- a/fs.c +++ b/fs.c @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "gmqcc.h" +#include "base.h" /* * This is essentially a "wrapper" interface around standard C's IO diff --git a/ftepp.c b/ftepp.c index 595b520..0b51715 100644 --- a/ftepp.c +++ b/ftepp.c @@ -23,7 +23,7 @@ */ #include #include -#include "gmqcc.h" +#include "base.h" #include "lexer.h" #define HT_MACROS 1024 diff --git a/gmqcc.h b/gmqcc.h index 0031227..1ac06f0 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -23,1152 +23,35 @@ */ #ifndef GMQCC_HDR #define GMQCC_HDR -#include -#include -#include -#include -#include -#include - -/* - * Disable some over protective warnings in visual studio because fixing them is a waste - * of my time. - */ -#ifdef _MSC_VER -# pragma warning(disable : 4244 ) /* conversion from 'int' to 'float', possible loss of data */ -#endif /*! _MSC_VER */ - -#define GMQCC_VERSION_MAJOR 0 -#define GMQCC_VERSION_MINOR 3 -#define GMQCC_VERSION_PATCH 0 -#define GMQCC_VERSION_BUILD(J,N,P) (((J)<<16)|((N)<<8)|(P)) -#define GMQCC_VERSION \ - GMQCC_VERSION_BUILD(GMQCC_VERSION_MAJOR, GMQCC_VERSION_MINOR, GMQCC_VERSION_PATCH) -/* Undefine the following on a release-tag: */ -#define GMQCC_VERSION_TYPE_DEVEL - -/* Full version string in case we need it */ -#ifdef GMQCC_VERSION_TYPE_DEVEL -# ifdef GMQCC_GITINFO -# define GMQCC_DEV_VERSION_STRING "git build: " GMQCC_GITINFO "\n" -# elif defined(GMQCC_VERSION_TYPE_DEVEL) -# define GMQCC_DEV_VERSION_STRING "development build\n" -# else -# define GMQCC_DEV_VERSION_STRING -# endif /*! GMQCC_GITINGO */ -#else -# define GMQCC_DEV_VERSION_STRING -#endif - -#define GMQCC_STRINGIFY(x) #x -#define GMQCC_IND_STRING(x) GMQCC_STRINGIFY(x) -#define GMQCC_FULL_VERSION_STRING \ -"GMQCC " \ -GMQCC_IND_STRING(GMQCC_VERSION_MAJOR) "." \ -GMQCC_IND_STRING(GMQCC_VERSION_MINOR) "." \ -GMQCC_IND_STRING(GMQCC_VERSION_PATCH) \ -" Built " __DATE__ " " __TIME__ \ -"\n" GMQCC_DEV_VERSION_STRING - -/* - * We cannot rely on C99 at all, since compilers like MSVC - * simply don't support it. We define our own boolean type - * as a result (since we cannot include ). For - * compilers that are in 1999 mode (C99 compliant) we can use - * the language keyword _Bool which can allow for better code - * on GCC and GCC-like compilers, opposed to `int`. - */ -#ifndef __cplusplus -# ifdef false -# undef false -# endif /*! false */ -# ifdef true -# undef true -# endif /*! true */ -# define false (0) -# define true (1) -# ifdef __STDC_VERSION__ -# if __STDC_VERSION__ < 199901L && __GNUC__ < 3 - typedef int bool; -# else - typedef _Bool bool; -# endif /*! __STDC_VERSION__ < 199901L && __GNUC__ < 3 */ -# else - typedef int bool; -# endif /*! __STDC_VERSION__ */ -#endif /*! __cplusplus */ - -/* - * Of some functions which are generated we want to make sure - * that the result isn't ignored. To find such function calls, - * we use this macro. - */ -#if defined(__GNUC__) || defined(__CLANG__) -# define GMQCC_WARN __attribute__((warn_unused_result)) -# define GMQCC_USED __attribute__((used)) -#else -# define GMQCC_WARN -# define GMQCC_USED -#endif /*! defined(__GNUC__) || defined (__CLANG__) */ -/* - * This is a hack to silent clang regarding empty - * body if statements. - */ -#define GMQCC_SUPPRESS_EMPTY_BODY do { } while (0) - -/* - * Inline is not supported in < C90, however some compilers - * like gcc and clang might have an inline attribute we can - * use if present. - */ -#ifdef __STDC_VERSION__ -# if __STDC_VERSION__ < 199901L -# if defined(__GNUC__) || defined (__CLANG__) -# if __GNUC__ < 2 -# define GMQCC_INLINE -# else -# define GMQCC_INLINE __attribute__ ((always_inline)) -# endif /*! __GNUC__ < 2 */ -# else -# define GMQCC_INLINE -# endif /*! defined(__GNUC__) || defined (__CLANG__) */ -# else -# define GMQCC_INLINE inline -# endif /*! __STDC_VERSION < 199901L */ -/* - * Visual studio has __forcinline we can use. So lets use that - * I suspect it also has just __inline of some sort, but our use - * of inline is correct (not guessed), WE WANT IT TO BE INLINE - */ -#elif defined(_MSC_VER) -# define GMQCC_INLINE __forceinline -#else -# define GMQCC_INLINE -#endif /*! __STDC_VERSION__ */ - -/* - * noreturn is present in GCC and clang - * it's required for _ast_node_destory otherwise -Wmissing-noreturn - * in clang complains about there being no return since abort() is - * called. - */ -#if (defined(__GNUC__) && __GNUC__ >= 2) || defined(__CLANG__) -# define GMQCC_NORETURN __attribute__ ((noreturn)) -#else -# define GMQCC_NORETURN -#endif /*! (defined(__GNUC__) && __GNUC__ >= 2) || defined (__CLANG__) */ - -#ifndef _MSC_VER -# include -#else - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; -#endif /*! _MSC_VER */ - -/* - * Very roboust way at determining endianess at compile time: this handles - * almost every possible situation. Otherwise a runtime check has to be - * performed. - */ -#define GMQCC_BYTE_ORDER_LITTLE 1234 -#define GMQCC_BYTE_ORDER_BIG 4321 - -#if defined (__GNUC__) || defined (__GNU_LIBRARY__) -# if defined (__FreeBSD__) || defined (__OpenBSD__) -# include -# elif defined (BSD) && (BSD >= 199103) || defined (__DJGPP__) || defined (__CYGWIN32__) -# include -# elif defined (__APPLE__) -# if defined (__BIG_ENDIAN__) && !defined(BIG_ENDIAN) -# define BIG_ENDIAN -# elif defined (__LITTLE_ENDIAN__) && !defined (LITTLE_ENDIAN) -# define LITTLE_ENDIAN -# endif /*! defined (__BIG_ENDIAN__) && !defined(BIG_ENDIAN) */ -# elif !defined (__MINGW32__) -# include -# if !defined (__BEOS__) -# include -# endif /*! !definde (__BEOS__) */ -# endif /*! defined (__FreeBSD__) || defined (__OpenBSD__) */ -#endif /*! defined (__GNUC__) || defined (__GNU_LIBRARY__) */ -#if !defined(PLATFORM_BYTE_ORDER) -# if defined (LITTLE_ENDIAN) || defined (BIG_ENDIAN) -# if defined (LITTLE_ENDIAN) && !defined(BIG_ENDIAN) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE -# elif !defined (LITTLE_ENDIAN) && defined (BIG_ENDIAN) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG -# elif defined (BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE -# elif defined (BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG -# endif /*! defined (LITTLE_ENDIAN) && !defined(BIG_ENDIAN) */ -# elif defined (_LITTLE_ENDIAN) || defined (_BIG_ENDIAN) -# if defined (_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE -# elif !defined (_LITTLE_ENDIAN) && defined (_BIG_ENDIAN) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG -# elif defined (_BYTE_ORDER) && (_BYTE_ORDER == _LITTLE_ENDIAN) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE -# elif defined (_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG -# endif /*! defined (_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) */ -# elif defined (__LITTLE_ENDIAN__) || defined (__BIG_ENDIAN__) -# if defined (__LITTLE_ENDIAN__) && !defined (__BIG_ENDIAN__) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE -# elif !defined (__LITTLE_ENDIAN__) && defined (__BIG_ENDIAN__) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG -# elif defined (__BYTE_ORDER__) && (__BYTE_ORDER__ == __LITTLE_ENDIAN__) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE -# elif defined (__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG -# endif /*! defined (__LITTLE_ENDIAN__) && !defined (__BIG_ENDIAN__) */ -# endif /*! defined(LITTLE_ENDIAN) || defined (BIG_ENDIAN) */ -#endif /*! !defined(PLATFORM_BYTE_ORDER) */ -#if !defined (PLATFORM_BYTE_ORDER) -# if defined (__alpha__) || defined (__alpha) || defined (i386) || \ - defined (__i386__) || defined (_M_I86) || defined (_M_IX86) || \ - defined (__OS2__) || defined (sun386) || defined (__TURBOC__) || \ - defined (vax) || defined (vms) || defined (VMS) || \ - defined (__VMS) || defined (__x86_64__) || defined (_M_IA64) || \ - defined (_M_X64) || defined (__i386) || defined (__x86_64) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE -# elif defined (AMIGA) || defined (applec) || defined (__AS400__) || \ - defined (_CRAY) || defined (__hppa) || defined (__hp9000) || \ - defined (ibm370) || defined (mc68000) || defined (m68k) || \ - defined (__MRC__) || defined (__MVS__) || defined (__MWERKS__) || \ - defined (sparc) || defined (__sparc) || defined (SYMANTEC_C) || \ - defined (__TANDEM) || defined (THINK_C) || defined (__VMCMS__) || \ - defined (__PPC__) || defined (__PPC) || defined (PPC) -# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG -# else -# define PLATFORM_BYTE_ORDER -1 -# endif -#endif /*! !defined (PLATFORM_BYTE_ORDER) */ - -/* - * On windows systems where we're not compiling with MING32 we need a - * little extra help on dependinces for implementing our own dirent.h - * in fs.c. - */ -#if defined(_WIN32) && !defined(__MINGW32__) -# define _WIN32_LEAN_AND_MEAN -# include -# include -# include - - struct dirent { - long d_ino; - unsigned short d_reclen; - unsigned short d_namlen; - char d_name[FILENAME_MAX]; - }; - - typedef struct { - struct _finddata_t dd_dta; - struct dirent dd_dir; - long dd_handle; - int dd_stat; - char dd_name[1]; - } DIR; - /* - * Visual studio also lacks S_ISDIR for sys/stat.h, so we emulate this as well - * which is not hard at all. - */ -# ifdef S_ISDIR -# undef S_ISDIR -# endif /*! S_ISDIR */ -# define S_ISDIR(X) ((X)&_S_IFDIR) -#else -# include -#endif /*! _WIN32 && !defined(__MINGW32__) */ - - -/*===================================================================*/ -/*=========================== util.c ================================*/ -/*===================================================================*/ -void *util_memory_a (size_t, /*****/ unsigned int, const char *); -void *util_memory_r (void *, size_t, unsigned int, const char *); -void util_memory_d (void *); -void util_meminfo (); - -bool util_filexists (const char *); -bool util_strupper (const char *); -bool util_strdigit (const char *); -char *_util_Estrdup (const char *, const char *, size_t); -char *_util_Estrdup_empty(const char *, const char *, size_t); -void util_debug (const char *, const char *, ...); -void util_endianswap (void *, size_t, unsigned int); - -size_t util_strtocmd (const char *, char *, size_t); -size_t util_strtononcmd (const char *, char *, size_t); - -uint16_t util_crc16(uint16_t crc, const char *data, size_t len); - -void util_seed(uint32_t); -uint32_t util_rand(); - -/* - * String functions (formatting, copying, concatenating, errors). These are wrapped - * to use the MSVC _safe_ versions when using MSVC, plus some implementations of - * these are non-conformant or don't exist such as asprintf and snprintf, which are - * not supported in C90, but do exist in C99. - */ -int util_vasprintf(char **ret, const char *fmt, va_list); -int util_asprintf (char **ret, const char *fmt, ...); -int util_snprintf (char *src, size_t bytes, const char *format, ...); -char *util_strcat (char *dest, const char *src); -char *util_strncpy (char *dest, const char *src, size_t num); -const char *util_strerror (int num); - - -#ifdef NOTRACK -# define mem_a(x) malloc (x) -# define mem_d(x) free ((void*)x) -# define mem_r(x, n) realloc((void*)x, n) -# define mem_af(x,f,l) malloc (x) -#else -# define mem_a(x) util_memory_a((x), __LINE__, __FILE__) -# define mem_d(x) util_memory_d((void*)(x)) -# define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__) -# define mem_af(x,f,l) util_memory_a((x), __LINE__, __FILE__) -#endif /*! NOTRACK */ - -#define util_strdup(X) _util_Estrdup((X), __FILE__, __LINE__) -#define util_strdupe(X) _util_Estrdup_empty((X), __FILE__, __LINE__) - -/* - * A flexible vector implementation: all vector pointers contain some - * data about themselfs exactly - sizeof(vector_t) behind the pointer - * this data is represented in the structure below. Doing this allows - * us to use the array [] to access individual elements from the vector - * opposed to using set/get methods. - */ -typedef struct { - size_t allocated; - size_t used; - - /* can be extended now! whoot */ -} vector_t; - -/* hidden interface */ -void _util_vec_grow(void **a, size_t i, size_t s); -#define GMQCC_VEC_WILLGROW(X,Y) ( \ - ((!(X) || vec_meta(X)->used + Y >= vec_meta(X)->allocated)) ? \ - (void)_util_vec_grow(((void**)&(X)), (Y), sizeof(*(X))) : \ - (void)0 \ -) - -/* exposed interface */ -#define vec_meta(A) (((vector_t*)((void*)A)) - 1) -#define vec_free(A) ((void)((A) ? (mem_d((void*)vec_meta(A)), (A) = NULL) : 0)) -#define vec_push(A,V) (GMQCC_VEC_WILLGROW((A),1), (A)[vec_meta(A)->used++] = (V)) -#define vec_size(A) ((A) ? vec_meta(A)->used : 0) -#define vec_add(A,N) (GMQCC_VEC_WILLGROW((A),(N)), vec_meta(A)->used += (N), &(A)[vec_meta(A)->used-(N)]) -#define vec_last(A) ((A)[vec_meta(A)->used - 1]) -#define vec_pop(A) ((void)(vec_meta(A)->used -= 1)) -#define vec_shrinkto(A,N) ((void)(vec_meta(A)->used = (N))) -#define vec_shrinkby(A,N) ((void)(vec_meta(A)->used -= (N))) -#define vec_append(A,N,S) ((void)(memcpy(vec_add((A), (N)), (S), (N) * sizeof(*(S))))) -#define vec_upload(X,Y,S) ((void)(memcpy(vec_add((X), (S) * sizeof(*(Y))), (Y), (S) * sizeof(*(Y))))) -#define vec_remove(A,I,N) ((void)(memmove((A)+(I),(A)+((I)+(N)),sizeof(*(A))*(vec_meta(A)->used-(I)-(N))),vec_meta(A)->used-=(N))) - -typedef struct trie_s { - void *value; - struct trie_s *entries; -} correct_trie_t; - -correct_trie_t* correct_trie_new(); - -typedef struct hash_table_t { - size_t size; - struct hash_node_t **table; -} hash_table_t, *ht; - -/* - * hashtable implementation: - * - * Note: - * This was designed for pointers: you manage the life of the object yourself - * if you do use this for non-pointers please be warned that the object may not - * be valid if the duration of it exceeds (i.e on stack). So you need to allocate - * yourself, or put those in global scope to ensure duration is for the whole - * runtime. - * - * util_htnew(size) -- to make a new hashtable - * util_htset(table, key, value, sizeof(value)) -- to set something in the table - * util_htget(table, key) -- to get something from the table - * util_htdel(table) -- to delete the table - * - * example of use: - * - * ht foo = util_htnew(1024); - * int data = 100; - * char *test = "hello world\n"; - * util_htset(foo, "foo", (void*)&data); - * util_gtset(foo, "bar", (void*)test); - * - * printf("foo: %d, bar %s", - * *((int *)util_htget(foo, "foo")), - * ((char*)util_htget(foo, "bar")) - * ); - * - * util_htdel(foo); - */ -hash_table_t *util_htnew (size_t size); -void util_htrem (hash_table_t *ht, void (*callback)(void *data)); -void util_htset (hash_table_t *ht, const char *key, void *value); -void util_htdel (hash_table_t *ht); -size_t util_hthash(hash_table_t *ht, const char *key); -void util_htseth(hash_table_t *ht, const char *key, size_t hash, void *value); -void util_htrmh (hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)); -void util_htrm (hash_table_t *ht, const char *key, void (*cb)(void*)); - -void *util_htget (hash_table_t *ht, const char *key); -void *util_htgeth(hash_table_t *ht, const char *key, size_t hash); - -/*===================================================================*/ -/*============================ file.c ===============================*/ -/*===================================================================*/ -/* file handling */ -void fs_file_close (FILE *); -int fs_file_error (FILE *); -int fs_file_getc (FILE *); -int fs_file_printf (FILE *, const char *, ...); -int fs_file_puts (FILE *, const char *); -int fs_file_seek (FILE *, long int, int); -long int fs_file_tell (FILE *); - -size_t fs_file_read (void *, size_t, size_t, FILE *); -size_t fs_file_write (const void *, size_t, size_t, FILE *); - -FILE *fs_file_open (const char *, const char *); -int fs_file_getline(char **, size_t *, FILE *); - -/* directory handling */ -int fs_dir_make (const char *); -DIR *fs_dir_open (const char *); -int fs_dir_close (DIR *); -struct dirent *fs_dir_read (DIR *); - - -/*===================================================================*/ -/*=========================== correct.c =============================*/ -/*===================================================================*/ -typedef struct { - char ***edits; - size_t **lens; -} correction_t; - -void correct_del (correct_trie_t*, size_t **); -void correct_add (correct_trie_t*, size_t ***, const char *); -char *correct_str (correction_t *, correct_trie_t*, const char *); -void correct_init(correction_t *); -void correct_free(correction_t *); - -/*===================================================================*/ -/*=========================== code.c ================================*/ -/*===================================================================*/ - -/* TODO: cleanup */ -/* Note: if you change the order, fix type_sizeof in ir.c */ -enum { - TYPE_VOID , - TYPE_STRING , - TYPE_FLOAT , - TYPE_VECTOR , - TYPE_ENTITY , - TYPE_FIELD , - TYPE_FUNCTION , - TYPE_POINTER , - TYPE_INTEGER , - TYPE_VARIANT , - TYPE_STRUCT , - TYPE_UNION , - TYPE_ARRAY , - - TYPE_NIL , /* it's its own type / untyped */ - TYPE_NOEXPR , /* simply invalid in expressions */ - - TYPE_COUNT -}; - -/* const/var qualifiers */ -#define CV_NONE 0 -#define CV_CONST 1 -#define CV_VAR -1 -#define CV_WRONG 0x8000 /* magic number to help parsing */ - -extern const char *type_name [TYPE_COUNT]; -extern const uint16_t type_store_instr [TYPE_COUNT]; -extern const uint16_t field_store_instr[TYPE_COUNT]; - -/* - * could use type_store_instr + INSTR_STOREP_F - INSTR_STORE_F - * but this breaks when TYPE_INTEGER is added, since with the enhanced - * instruction set, the old ones are left untouched, thus the _I instructions - * are at a seperate place. - */ -extern const uint16_t type_storep_instr[TYPE_COUNT]; -extern const uint16_t type_eq_instr [TYPE_COUNT]; -extern const uint16_t type_ne_instr [TYPE_COUNT]; -extern const uint16_t type_not_instr [TYPE_COUNT]; - -typedef struct { - uint32_t offset; /* Offset in file of where data begins */ - uint32_t length; /* Length of section (how many of) */ -} prog_section; - -typedef struct { - uint32_t version; /* Program version (6) */ - uint16_t crc16; - uint16_t skip; - - prog_section statements; /* prog_section_statement */ - prog_section defs; /* prog_section_def */ - prog_section fields; /* prog_section_field */ - prog_section functions; /* prog_section_function */ - prog_section strings; - prog_section globals; - uint32_t entfield; /* Number of entity fields */ -} prog_header; - -/* - * Each paramater incerements by 3 since vector types hold - * 3 components (x,y,z). - */ -#define OFS_NULL 0 -#define OFS_RETURN 1 -#define OFS_PARM0 (OFS_RETURN+3) -#define OFS_PARM1 (OFS_PARM0 +3) -#define OFS_PARM2 (OFS_PARM1 +3) -#define OFS_PARM3 (OFS_PARM2 +3) -#define OFS_PARM4 (OFS_PARM3 +3) -#define OFS_PARM5 (OFS_PARM4 +3) -#define OFS_PARM6 (OFS_PARM5 +3) -#define OFS_PARM7 (OFS_PARM6 +3) - -typedef struct { - uint16_t opcode; - - /* operand 1 */ - union { - int16_t s1; /* signed */ - uint16_t u1; /* unsigned */ - } o1; - /* operand 2 */ - union { - int16_t s1; /* signed */ - uint16_t u1; /* unsigned */ - } o2; - /* operand 3 */ - union { - int16_t s1; /* signed */ - uint16_t u1; /* unsigned */ - } o3; - - /* - * This is the same as the structure in darkplaces - * { - * unsigned short op; - * short a,b,c; - * } - * But this one is more sane to work with, and the - * type sizes are guranteed. - */ -} prog_section_statement; - -typedef struct { - /* - * The types: - * 0 = ev_void - * 1 = ev_string - * 2 = ev_float - * 3 = ev_vector - * 4 = ev_entity - * 5 = ev_field - * 6 = ev_function - * 7 = ev_pointer -- engine only - * 8 = ev_bad -- engine only - */ - uint16_t type; - uint16_t offset; - uint32_t name; -} prog_section_both; - -typedef prog_section_both prog_section_def; -typedef prog_section_both prog_section_field; - -/* this is ORed to the type */ -#define DEF_SAVEGLOBAL (1<<15) -#define DEF_TYPEMASK ((1<<15)-1) - -typedef struct { - int32_t entry; /* in statement table for instructions */ - uint32_t firstlocal; /* First local in local table */ - uint32_t locals; /* Total ints of params + locals */ - uint32_t profile; /* Always zero (engine uses this) */ - uint32_t name; /* name of function in string table */ - uint32_t file; /* file of the source file */ - int32_t nargs; /* number of arguments */ - uint8_t argsize[8]; /* size of arguments (keep 8 always?) */ -} prog_section_function; - -/* - * Instructions - * These are the external instructions supported by the interperter - * this is what things compile to (from the C code). - */ -enum { - INSTR_DONE, - INSTR_MUL_F, - INSTR_MUL_V, - INSTR_MUL_FV, /* NOTE: the float operands must NOT be at the same locations: A != C */ - INSTR_MUL_VF, /* and here: B != C */ - INSTR_DIV_F, - INSTR_ADD_F, - INSTR_ADD_V, - INSTR_SUB_F, - INSTR_SUB_V, - INSTR_EQ_F, - INSTR_EQ_V, - INSTR_EQ_S, - INSTR_EQ_E, - INSTR_EQ_FNC, - INSTR_NE_F, - INSTR_NE_V, - INSTR_NE_S, - INSTR_NE_E, - INSTR_NE_FNC, - INSTR_LE, - INSTR_GE, - INSTR_LT, - INSTR_GT, - INSTR_LOAD_F, - INSTR_LOAD_V, - INSTR_LOAD_S, - INSTR_LOAD_ENT, - INSTR_LOAD_FLD, - INSTR_LOAD_FNC, - INSTR_ADDRESS, - INSTR_STORE_F, - INSTR_STORE_V, - INSTR_STORE_S, - INSTR_STORE_ENT, - INSTR_STORE_FLD, - INSTR_STORE_FNC, - INSTR_STOREP_F, - INSTR_STOREP_V, - INSTR_STOREP_S, - INSTR_STOREP_ENT, - INSTR_STOREP_FLD, - INSTR_STOREP_FNC, - INSTR_RETURN, - INSTR_NOT_F, - INSTR_NOT_V, - INSTR_NOT_S, - INSTR_NOT_ENT, - INSTR_NOT_FNC, - INSTR_IF, - INSTR_IFNOT, - INSTR_CALL0, - INSTR_CALL1, - INSTR_CALL2, - INSTR_CALL3, - INSTR_CALL4, - INSTR_CALL5, - INSTR_CALL6, - INSTR_CALL7, - INSTR_CALL8, - INSTR_STATE, - INSTR_GOTO, - INSTR_AND, - INSTR_OR, - INSTR_BITAND, - INSTR_BITOR, - - /* - * Virtual instructions used by the IR - * Keep at the end! - */ - VINSTR_END, - VINSTR_PHI, - VINSTR_JUMP, - VINSTR_COND, - /* A never returning CALL. - * Creating this causes IR blocks to be marked as 'final'. - * No-Return-Call - */ - VINSTR_NRCALL -}; - -/* uhh? */ -typedef float qcfloat; -typedef int32_t qcint; - -typedef struct { - prog_section_statement *statements; - int *linenums; - prog_section_def *defs; - prog_section_field *fields; - prog_section_function *functions; - int *globals; - char *chars; - uint16_t crc; - uint32_t entfields; - ht string_cache; - qcint string_cached_empty; -} code_t; - -/* - * code_write -- writes out the compiled file - * code_init -- prepares the code file - * code_genstrin -- generates string for code - * code_alloc_field -- allocated a field - * code_push_statement -- keeps statements and linenumbers together - * code_pop_statement -- keeps statements and linenumbers together - */ -bool code_write (code_t *, const char *filename, const char *lno); -code_t *code_init (void); -uint32_t code_genstring (code_t *, const char *string); -qcint code_alloc_field (code_t *, size_t qcsize); -void code_push_statement(code_t *, prog_section_statement *stmt, int linenum); -void code_pop_statement (code_t *); - -/* - * A shallow copy of a lex_file to remember where which ast node - * came from. - */ -typedef struct { - const char *file; - size_t line; -} lex_ctx; - -/*===================================================================*/ -/*============================ con.c ================================*/ -/*===================================================================*/ -enum { - CON_BLACK = 30, - CON_RED, - CON_GREEN, - CON_BROWN, - CON_BLUE, - CON_MAGENTA, - CON_CYAN , - CON_WHITE -}; - -/* message level */ -enum { - LVL_MSG, - LVL_WARNING, - LVL_ERROR -}; - -FILE *con_default_out(); -FILE *con_default_err(); - -void con_vprintmsg (int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap); -void con_printmsg (int level, const char *name, size_t line, const char *msgtype, const char *msg, ...); -void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap); -void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...); - -void con_close (); -void con_init (); -void con_reset (); -void con_color (int); -int con_change(const char *, const char *); -int con_verr (const char *, va_list); -int con_vout (const char *, va_list); -int con_err (const char *, ...); -int con_out (const char *, ...); - -/* error/warning interface */ -extern size_t compile_errors; -extern size_t compile_Werrors; -extern size_t compile_warnings; - -void /********/ compile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, ...); -void /********/ vcompile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, va_list ap); -bool GMQCC_WARN compile_warning (lex_ctx ctx, int warntype, const char *fmt, ...); -bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap); -void compile_show_werrors(); - -/*===================================================================*/ -/*========================= assembler.c =============================*/ -/*===================================================================*/ -/* TODO: remove this ... */ -static const struct { - const char *m; /* menomic */ - const size_t o; /* operands */ - const size_t l; /* menomic len */ -} asm_instr[] = { - { "DONE" , 1, 4 }, - { "MUL_F" , 3, 5 }, - { "MUL_V" , 3, 5 }, - { "MUL_FV" , 3, 6 }, - { "MUL_VF" , 3, 6 }, - { "DIV" , 0, 3 }, - { "ADD_F" , 3, 5 }, - { "ADD_V" , 3, 5 }, - { "SUB_F" , 3, 5 }, - { "SUB_V" , 3, 5 }, - { "EQ_F" , 0, 4 }, - { "EQ_V" , 0, 4 }, - { "EQ_S" , 0, 4 }, - { "EQ_E" , 0, 4 }, - { "EQ_FNC" , 0, 6 }, - { "NE_F" , 0, 4 }, - { "NE_V" , 0, 4 }, - { "NE_S" , 0, 4 }, - { "NE_E" , 0, 4 }, - { "NE_FNC" , 0, 6 }, - { "LE" , 0, 2 }, - { "GE" , 0, 2 }, - { "LT" , 0, 2 }, - { "GT" , 0, 2 }, - { "FIELD_F" , 0, 7 }, - { "FIELD_V" , 0, 7 }, - { "FIELD_S" , 0, 7 }, - { "FIELD_ENT" , 0, 9 }, - { "FIELD_FLD" , 0, 9 }, - { "FIELD_FNC" , 0, 9 }, - { "ADDRESS" , 0, 7 }, - { "STORE_F" , 0, 7 }, - { "STORE_V" , 0, 7 }, - { "STORE_S" , 0, 7 }, - { "STORE_ENT" , 0, 9 }, - { "STORE_FLD" , 0, 9 }, - { "STORE_FNC" , 0, 9 }, - { "STOREP_F" , 0, 8 }, - { "STOREP_V" , 0, 8 }, - { "STOREP_S" , 0, 8 }, - { "STOREP_ENT", 0, 10}, - { "STOREP_FLD", 0, 10}, - { "STOREP_FNC", 0, 10}, - { "RETURN" , 0, 6 }, - { "NOT_F" , 0, 5 }, - { "NOT_V" , 0, 5 }, - { "NOT_S" , 0, 5 }, - { "NOT_ENT" , 0, 7 }, - { "NOT_FNC" , 0, 7 }, - { "IF" , 0, 2 }, - { "IFNOT" , 0, 5 }, - { "CALL0" , 1, 5 }, - { "CALL1" , 2, 5 }, - { "CALL2" , 3, 5 }, - { "CALL3" , 4, 5 }, - { "CALL4" , 5, 5 }, - { "CALL5" , 6, 5 }, - { "CALL6" , 7, 5 }, - { "CALL7" , 8, 5 }, - { "CALL8" , 9, 5 }, - { "STATE" , 0, 5 }, - { "GOTO" , 0, 4 }, - { "AND" , 0, 3 }, - { "OR" , 0, 2 }, - { "BITAND" , 0, 6 }, - { "BITOR" , 0, 5 }, - - { "END" , 0, 3 } /* virtual assembler instruction */ -}; -/*===================================================================*/ -/*============================= ir.c ================================*/ -/*===================================================================*/ - -enum store_types { - store_global, - store_local, /* local, assignable for now, should get promoted later */ - store_param, /* parameters, they are locals with a fixed position */ - store_value, /* unassignable */ - store_return /* unassignable, at OFS_RETURN */ -}; - -typedef struct { - qcfloat x, y, z; -} vector; - -vector vec3_add (vector, vector); -vector vec3_sub (vector, vector); -qcfloat vec3_mulvv(vector, vector); -vector vec3_mulvf(vector, float); - -/*===================================================================*/ -/*============================= exec.c ==============================*/ -/*===================================================================*/ - -/* TODO: cleanup */ -/* - * Darkplaces has (or will have) a 64 bit prog loader - * where the 32 bit qc program is autoconverted on load. - * Since we may want to support that as well, let's redefine - * float and int here. - */ -typedef union { - qcint _int; - qcint string; - qcint function; - qcint edict; - qcfloat _float; - qcfloat vector[3]; - qcint ivector[3]; -} qcany; - -typedef char qcfloat_size_is_correct [sizeof(qcfloat) == 4 ?1:-1]; -typedef char qcint_size_is_correct [sizeof(qcint) == 4 ?1:-1]; - -enum { - VMERR_OK, - VMERR_TEMPSTRING_ALLOC, - - VMERR_END -}; - -#define VM_JUMPS_DEFAULT 1000000 - -/* execute-flags */ -#define VMXF_DEFAULT 0x0000 /* default flags - nothing */ -#define VMXF_TRACE 0x0001 /* trace: print statements before executing */ -#define VMXF_PROFILE 0x0002 /* profile: increment the profile counters */ - -struct qc_program_s; - -typedef int (*prog_builtin)(struct qc_program_s *prog); - -typedef struct { - qcint stmt; - size_t localsp; - prog_section_function *function; -} qc_exec_stack; - -typedef struct qc_program_s { - char *filename; - - prog_section_statement *code; - prog_section_def *defs; - prog_section_def *fields; - prog_section_function *functions; - char *strings; - qcint *globals; - qcint *entitydata; - bool *entitypool; - - const char* *function_stack; - - uint16_t crc16; - - size_t tempstring_start; - size_t tempstring_at; - - qcint vmerror; - - size_t *profile; - - prog_builtin *builtins; - size_t builtins_count; - - /* size_t ip; */ - qcint entities; - size_t entityfields; - bool allowworldwrites; - - qcint *localstack; - qc_exec_stack *stack; - size_t statement; - - size_t xflags; - - int argc; /* current arg count for debugging */ -} qc_program; - -qc_program* prog_load(const char *filename, bool ignoreversion); -void prog_delete(qc_program *prog); - -bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps); - -char* prog_getstring (qc_program *prog, qcint str); -prog_section_def* prog_entfield (qc_program *prog, qcint off); -prog_section_def* prog_getdef (qc_program *prog, qcint off); -qcany* prog_getedict (qc_program *prog, qcint e); -qcint prog_tempstring(qc_program *prog, const char *_str); - - -/*===================================================================*/ -/*===================== parser.c commandline ========================*/ -/*===================================================================*/ -struct parser_s; - -struct parser_s *parser_create (); -bool parser_compile_file (struct parser_s *parser, const char *); -bool parser_compile_string(struct parser_s *parser, const char *, const char *, size_t); -bool parser_finish (struct parser_s *parser, const char *); -void parser_cleanup (struct parser_s *parser); - -/*===================================================================*/ -/*====================== ftepp.c commandline ========================*/ -/*===================================================================*/ -struct ftepp_s; -struct ftepp_s *ftepp_create (); -bool ftepp_preprocess_file (struct ftepp_s *ftepp, const char *filename); -bool ftepp_preprocess_string(struct ftepp_s *ftepp, const char *name, const char *str); -void ftepp_finish (struct ftepp_s *ftepp); -const char *ftepp_get (struct ftepp_s *ftepp); -void ftepp_flush (struct ftepp_s *ftepp); -void ftepp_add_define (struct ftepp_s *ftepp, const char *source, const char *name); -void ftepp_add_macro (struct ftepp_s *ftepp, const char *name, const char *value); - -/*===================================================================*/ -/*======================= main.c commandline ========================*/ -/*===================================================================*/ - -#if 1 -/* Helpers to allow for a whole lot of flags. Otherwise we'd limit - * to 32 or 64 -f options... - */ -typedef struct { - size_t idx; /* index into an array of 32 bit words */ - uint8_t bit; /* bit index for the 8 bit group idx points to */ -} longbit; -#define LONGBIT(bit) { ((bit)/32), ((bit)%32) } -#define LONGBIT_SET(B, I) ((B).idx = (I)/32, (B).bit = ((I)%32)) -#else -typedef uint32_t longbit; -#define LONGBIT(bit) (bit) -#define LONGBIT_SET(B, I) ((B) = (I)) -#endif - -/*===================================================================*/ -/*=========================== utf8lib.c =============================*/ -/*===================================================================*/ -typedef uint32_t uchar_t; - -bool u8_analyze (const char *_s, size_t *_start, size_t *_len, uchar_t *_ch, size_t _maxlen); -size_t u8_strlen (const char*); -size_t u8_strnlen (const char*, size_t); -uchar_t u8_getchar (const char*, const char**); -uchar_t u8_getnchar(const char*, const char**, size_t); -int u8_fromchar(uchar_t w, char *to, size_t maxlen); - -/*===================================================================*/ -/*============================= opts.c ==============================*/ -/*===================================================================*/ -typedef struct { - const char *name; - longbit bit; -} opts_flag_def; - -bool opts_setflag (const char *, bool); -bool opts_setwarn (const char *, bool); -bool opts_setwerror(const char *, bool); -bool opts_setoptim (const char *, bool); - -void opts_init (const char *, int, size_t); -void opts_set (uint32_t *, size_t, bool); -void opts_setoptimlevel(unsigned int); -void opts_ini_init (const char *); - -/* Saner flag handling */ -void opts_backup_non_Wall(); -void opts_restore_non_Wall(); -void opts_backup_non_Werror_all(); -void opts_restore_non_Werror_all(); - -enum { -# define GMQCC_TYPE_FLAGS -# define GMQCC_DEFINE_FLAG(X) X, -# include "opts.def" - COUNT_FLAGS -}; -static const opts_flag_def opts_flag_list[] = { -# define GMQCC_TYPE_FLAGS -# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) }, -# include "opts.def" - { NULL, LONGBIT(0) } -}; - -enum { -# define GMQCC_TYPE_WARNS -# define GMQCC_DEFINE_FLAG(X) WARN_##X, -# include "opts.def" - COUNT_WARNINGS -}; -static const opts_flag_def opts_warn_list[] = { -# define GMQCC_TYPE_WARNS -# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(WARN_##X) }, -# include "opts.def" - { NULL, LONGBIT(0) } -}; - -enum { -# define GMQCC_TYPE_OPTIMIZATIONS -# define GMQCC_DEFINE_FLAG(NAME, MIN_O) OPTIM_##NAME, -# include "opts.def" - COUNT_OPTIMIZATIONS -}; -static const opts_flag_def opts_opt_list[] = { -# define GMQCC_TYPE_OPTIMIZATIONS -# define GMQCC_DEFINE_FLAG(NAME, MIN_O) { #NAME, LONGBIT(OPTIM_##NAME) }, -# include "opts.def" - { NULL, LONGBIT(0) } -}; -static const unsigned int opts_opt_oflag[] = { -# define GMQCC_TYPE_OPTIMIZATIONS -# define GMQCC_DEFINE_FLAG(NAME, MIN_O) MIN_O, -# include "opts.def" - 0 -}; - -enum { -# define GMQCC_TYPE_OPTIONS -# define GMQCC_DEFINE_FLAG(X) OPTION_##X, -# include "opts.def" - OPTION_COUNT -}; - -extern unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS]; - -/* other options: */ -typedef enum { - COMPILER_QCC, /* circa QuakeC */ - COMPILER_FTEQCC, /* fteqcc QuakeC */ - COMPILER_QCCX, /* qccx QuakeC */ - COMPILER_GMQCC /* this QuakeC */ -} opts_std_t; - -typedef union { - bool B; - uint16_t U16; - uint32_t U32; - char *STR; -} opt_value_t; - - -typedef struct { - opt_value_t options [OPTION_COUNT]; - uint32_t flags [1 + (COUNT_FLAGS / 32)]; - uint32_t warn [1 + (COUNT_WARNINGS / 32)]; - uint32_t werror [1 + (COUNT_WARNINGS / 32)]; - uint32_t warn_backup [1 + (COUNT_WARNINGS / 32)]; - uint32_t werror_backup[1 + (COUNT_WARNINGS / 32)]; - uint32_t optimization [1 + (COUNT_OPTIMIZATIONS / 32)]; - bool optimizeoff; /* True when -O0 */ -} opts_cmd_t; - -extern opts_cmd_t opts; - -#define OPTS_GENERIC(f,i) (!! (((f)[(i)/32]) & (1<< ((i)%32)))) -#define OPTS_FLAG(i) OPTS_GENERIC(opts.flags, (i)) -#define OPTS_WARN(i) OPTS_GENERIC(opts.warn, (i)) -#define OPTS_WERROR(i) OPTS_GENERIC(opts.werror, (i)) -#define OPTS_OPTIMIZATION(i) OPTS_GENERIC(opts.optimization, (i)) -#define OPTS_OPTION_BOOL(X) (opts.options[X].B) -#define OPTS_OPTION_U16(X) (opts.options[X].U16) -#define OPTS_OPTION_U32(X) (opts.options[X].U32) -#define OPTS_OPTION_STR(X) (opts.options[X].STR) - +#ifdef __cplusplus +extern "C" { +#endif /*! __cplusplus */ + +/* + * Function: gmqcc_global_setmemory + * Set implementations for dynamic memory manipulation. + * + * Parameters: + * malloc_impl - Pointer to malloc function + * realloc_impl - Pointer to realloc function + * free_impl - Pointer to free function + * + * Returns: + * true on success, false otherwise. + * + * Remarks: + * Do not call this function unless you want to provide + * your own functions, by default the standard C library + * functions are used. + */ +bool gmqcc_global_setmemory ( + void *(*malloc_impl) (size_t), + void *(*realloc_impl)(void *, size_t), + void (*free_impl) (void *) +); + + +#ifdef __cplusplus +} +#endif /*! __cplusplus */ #endif /*! GMQCC_HDR */ diff --git a/ir.c b/ir.c index a124bd1..07eb679 100644 --- a/ir.c +++ b/ir.c @@ -22,7 +22,7 @@ */ #include #include -#include "gmqcc.h" +#include "base.h" #include "ir.h" /*********************************************************************** diff --git a/ir.h b/ir.h index dac16a3..85d759f 100644 --- a/ir.h +++ b/ir.h @@ -22,7 +22,7 @@ */ #ifndef GMQCC_IR_HDR #define GMQCC_IR_HDR -#include "gmqcc.h" +#include "base.h" /* ir_value */ typedef struct diff --git a/lexer.c b/lexer.c index 7722f4f..fdcf19f 100644 --- a/lexer.c +++ b/lexer.c @@ -25,7 +25,7 @@ #include #include -#include "gmqcc.h" +#include "base.h" #include "lexer.h" /* diff --git a/main.c b/main.c index bbc685a..93caf93 100644 --- a/main.c +++ b/main.c @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "gmqcc.h" +#include "base.h" #include "lexer.h" #include @@ -639,8 +639,6 @@ int main(int argc, char **argv) { } } - util_debug("COM", "starting ...\n"); - /* add macros */ if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) { for (itr = 0; itr < vec_size(ppems); itr++) { @@ -779,7 +777,6 @@ srcdone: } cleanup: - util_debug("COM", "cleaning ...\n"); if (ftepp) ftepp_finish(ftepp); con_close(); @@ -794,6 +791,5 @@ cleanup: mem_d((void*)operators); lex_cleanup(); - util_meminfo(); return retval; } diff --git a/opts.c b/opts.c index 77aa9c0..9c1ad4f 100644 --- a/opts.c +++ b/opts.c @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "gmqcc.h" +#include "base.h" unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS]; opts_cmd_t opts; /* command lien options */ diff --git a/pak.c b/pak.c index 653a463..ac20ac8 100644 --- a/pak.c +++ b/pak.c @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "gmqcc.h" +#include "base.h" /* * The PAK format uses a FOURCC concept for storing the magic ident within @@ -552,7 +552,6 @@ int main(int argc, char **argv) { /* not possible */ pak_close(pak); vec_free(files); - util_meminfo(); return EXIT_SUCCESS; } @@ -575,7 +574,5 @@ int main(int argc, char **argv) { pak_close(pak); vec_free(files); - - util_meminfo(); return EXIT_SUCCESS; } diff --git a/parser.c b/parser.c index a09a54a..3bbe68b 100644 --- a/parser.c +++ b/parser.c @@ -25,7 +25,7 @@ #include #include -#include "gmqcc.h" +#include "base.h" #include "lexer.h" #include "ast.h" diff --git a/test.c b/test.c index 7722af2..ae1bd04 100644 --- a/test.c +++ b/test.c @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "gmqcc.h" +#include "base.h" #include #include @@ -692,7 +692,6 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) { char *qcflags = NULL; task_t task; - util_debug("TEST", "compiling task template: %s/%s\n", curdir, files->d_name); found ++; if (!tmpl) { con_err("error compiling task template: %s\n", files->d_name); @@ -803,8 +802,6 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) { continue; } - util_debug("TEST", "executing test: `%s` [%s]\n", tmpl->description, buf); - /* * Open up some file desciptors for logging the stdout/stderr * to our own. @@ -827,11 +824,6 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) { } } - util_debug("TEST", "compiled %d task template files out of %d\n", - vec_size(task_tasks), - found - ); - fs_dir_close(dir); return success; } @@ -855,8 +847,6 @@ void task_precleanup(const char *curdir) { util_snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name); if (remove(buffer)) con_err("error removing temporary file: %s\n", buffer); - else - util_debug("TEST", "removed temporary file: %s\n", buffer); } } @@ -887,12 +877,8 @@ void task_destroy(void) { if (task_tasks[i].compiled || !strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) { if (remove(task_tasks[i].stdoutlogfile)) con_err("error removing stdout log file: %s\n", task_tasks[i].stdoutlogfile); - else - util_debug("TEST", "removed stdout log file: %s\n", task_tasks[i].stdoutlogfile); if (remove(task_tasks[i].stderrlogfile)) con_err("error removing stderr log file: %s\n", task_tasks[i].stderrlogfile); - else - util_debug("TEST", "removed stderr log file: %s\n", task_tasks[i].stderrlogfile); remove(task_tasks[i].tmpl->tempfilename); } @@ -937,11 +923,6 @@ bool task_trymatch(task_template_t *tmpl, char ***line) { ); } - util_debug("TEST", "executing qcvm: `%s` [%s]\n", - tmpl->description, - buffer - ); - execute = popen(buffer, "r"); if (!execute) return false; @@ -1054,7 +1035,6 @@ void task_schedualize(size_t *pad) { con_out("test #%u %*s", i + 1, strlen(space[0]) - strlen(space[1]), ""); - util_debug("TEST", "executing task: %d: %s\n", i, task_tasks[i].tmpl->description); /* * Generate a task from thin air if it requires execution in * the QCVM. @@ -1321,8 +1301,6 @@ int main(int argc, char **argv) { } con_change(redirout, redirerr); succeed = test_perform("tests", defs); - util_meminfo(); - return (succeed) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/utf8.c b/utf8.c index a10a11a..8d69c85 100644 --- a/utf8.c +++ b/utf8.c @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "gmqcc.h" +#include "base.h" static unsigned char utf8_lengths[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ascii characters */ diff --git a/util.c b/util.c index c8d50f5..f74688c 100644 --- a/util.c +++ b/util.c @@ -23,242 +23,80 @@ */ #include #include -#include "gmqcc.h" - -/* TODO: remove globals ... */ -static uint64_t mem_ab = 0; -static uint64_t mem_db = 0; -static uint64_t mem_at = 0; -static uint64_t mem_dt = 0; -static uint64_t mem_pk = 0; -static uint64_t mem_hw = 0; - -struct memblock_t { - const char *file; - unsigned int line; - size_t byte; - struct memblock_t *next; - struct memblock_t *prev; -}; - -#define PEAK_MEM \ - do { \ - if (mem_hw > mem_pk) \ - mem_pk = mem_hw; \ - } while (0) - -static struct memblock_t *mem_start = NULL; - -void *util_memory_a(size_t byte, unsigned int line, const char *file) { - struct memblock_t *info = (struct memblock_t*)malloc(sizeof(struct memblock_t) + byte); - void *data = (void*)(info+1); - if (!info) return NULL; - info->line = line; - info->byte = byte; - info->file = file; - info->prev = NULL; - info->next = mem_start; - if (mem_start) - mem_start->prev = info; - mem_start = info; - - mem_at++; - mem_ab += info->byte; - mem_hw += info->byte; - - PEAK_MEM; - - return data; +#include "base.h" + +static bool mem_provided = false; +static void *(*mem_malloc)(size_t) = NULL; +static void *(*mem_realloc)(void *, size_t) = NULL; +static void (*mem_free)(void *) = NULL; + +bool gmqcc_global_setmemory ( + void *(*malloc_impl) (size_t), + void *(*realloc_impl)(void *, size_t), + void (*free_impl) (void *) +) { + if (!malloc_impl || !realloc_impl || !free_impl) + return false; + + mem_malloc = malloc_impl; + mem_realloc = realloc_impl; + mem_free = free_impl; + mem_provided = true; + + return true; } -void util_memory_d(void *ptrn) { - struct memblock_t *info = NULL; - - if (!ptrn) return; - info = ((struct memblock_t*)ptrn - 1); - - mem_db += info->byte; - mem_hw -= info->byte; - mem_dt++; - - if (info->prev) - info->prev->next = info->next; - if (info->next) - info->next->prev = info->prev; - if (info == mem_start) - mem_start = info->next; - - free(info); +void *util_mem_a(size_t bytes) { + return (mem_provided) + ? mem_malloc(bytes) + : malloc (bytes); } -void *util_memory_r(void *ptrn, size_t byte, unsigned int line, const char *file) { - struct memblock_t *oldinfo = NULL; - - struct memblock_t *newinfo; - - if (!ptrn) - return util_memory_a(byte, line, file); - if (!byte) { - util_memory_d(ptrn); - return NULL; - } - - oldinfo = ((struct memblock_t*)ptrn - 1); - newinfo = ((struct memblock_t*)malloc(sizeof(struct memblock_t) + byte)); - - /* new data */ - if (!newinfo) { - util_memory_d(oldinfo+1); - return NULL; - } - - /* copy old */ - memcpy(newinfo+1, oldinfo+1, oldinfo->byte); - - /* free old */ - if (oldinfo->prev) - oldinfo->prev->next = oldinfo->next; - if (oldinfo->next) - oldinfo->next->prev = oldinfo->prev; - if (oldinfo == mem_start) - mem_start = oldinfo->next; - - /* fill info */ - newinfo->line = line; - newinfo->byte = byte; - newinfo->file = file; - newinfo->prev = NULL; - newinfo->next = mem_start; - if (mem_start) - mem_start->prev = newinfo; - mem_start = newinfo; - - mem_ab -= oldinfo->byte; - mem_hw -= oldinfo->byte; - mem_ab += newinfo->byte; - mem_hw += newinfo->byte; - - PEAK_MEM; - - free(oldinfo); - - return newinfo+1; +void *util_mem_r(void *ptr, size_t bytes) { + return (mem_provided) + ? mem_realloc(ptr, bytes) + : realloc (ptr, bytes); } -static void util_dumpmem(struct memblock_t *memory, uint16_t cols) { - uint32_t i, j; - for (i = 0; i < memory->byte + ((memory->byte % cols) ? (cols - memory->byte % cols) : 0); i++) { - if (i % cols == 0) con_out(" 0x%06X: ", i); - if (i < memory->byte) con_out("%02X " , 0xFF & ((char*)(memory + 1))[i]); - else con_out(" "); - - if ((uint16_t)(i % cols) == (cols - 1)) { - for (j = i - (cols - 1); j <= i; j++) { - con_out("%c", - (j >= memory->byte) - ? ' ' - : (isprint(((char*)(memory + 1))[j])) - ? 0xFF & ((char*)(memory + 1)) [j] - : '.' - ); - } - con_out("\n"); - } - } -} - -void util_meminfo() { - struct memblock_t *info; - - - if (OPTS_OPTION_BOOL(OPTION_DEBUG)) { - for (info = mem_start; info; info = info->next) { - con_out("lost: %u (bytes) at %s:%u\n", - info->byte, - info->file, - info->line); - - util_dumpmem(info, OPTS_OPTION_U16(OPTION_MEMDUMPCOLS)); - } - } - - if (OPTS_OPTION_BOOL(OPTION_DEBUG) || - OPTS_OPTION_BOOL(OPTION_MEMCHK)) { - con_out("Memory information:\n\ - Total allocations: %llu\n\ - Total deallocations: %llu\n\ - Total allocated: %f (MB)\n\ - Total deallocated: %f (MB)\n\ - Total peak memory: %f (MB)\n\ - Total leaked memory: %f (MB) in %llu allocations\n", - mem_at, - mem_dt, - (float)(mem_ab) / 1048576.0f, - (float)(mem_db) / 1048576.0f, - (float)(mem_pk) / 1048576.0f, - (float)(mem_ab - mem_db) / 1048576.0f, - - /* could be more clever */ - (mem_at - mem_dt) - ); - } +void util_mem_f(void *ptr) { + if (mem_provided) mem_free(ptr); + else free (ptr); } + /* * Some string utility functions, because strdup uses malloc, and we want * to track all memory (without replacing malloc). */ -char *_util_Estrdup(const char *s, const char *file, size_t line) { +char *util_strdup(const char *s) { size_t len = 0; char *ptr = NULL; - /* in case of -DNOTRACK */ - (void)file; - (void)line; - if (!s) return NULL; - if ((len = strlen(s)) && (ptr = (char*)mem_af(len+1, line, file))) { + if ((len = strlen(s)) && (ptr = (char*)util_mem_a(len+1))) { memcpy(ptr, s, len); ptr[len] = '\0'; } return ptr; } -char *_util_Estrdup_empty(const char *s, const char *file, size_t line) { +char *util_strdupe(const char *s) { size_t len = 0; char *ptr = NULL; - /* in case of -DNOTRACK */ - (void)file; - (void)line; - if (!s) return NULL; len = strlen(s); - if ((ptr = (char*)mem_af(len+1, line, file))) { + if ((ptr = (char*)util_mem_a(len+1))) { memcpy(ptr, s, len); ptr[len] = '\0'; } return ptr; } -void util_debug(const char *area, const char *ms, ...) { - va_list va; - if (!OPTS_OPTION_BOOL(OPTION_DEBUG)) - return; - - if (!strcmp(area, "MEM") && !OPTS_OPTION_BOOL(OPTION_MEMCHK)) - return; - - va_start(va, ms); - con_out ("[%s] ", area); - con_vout(ms, va); - va_end (va); -} - /* * only required if big endian .. otherwise no need to swap * data.