X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=gmqcc.h;h=10b74f5ab2741e92ff9421fc7a3d8bc2ac888244;hp=51ca1887e85777056012a87dadc50fe841886c69;hb=620bd76e76804b1fc1de46daa03724e23339e8bf;hpb=0291726c092b42ec9c5aae076ee4eaba26ee5a48 diff --git a/gmqcc.h b/gmqcc.h index 51ca188..10b74f5 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -1,50 +1,22 @@ -/* - * 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_HDR #define GMQCC_HDR +#include +#include +#include +#include +using std::move; #include -#include /* TODO: remove this */ - -/* - * 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 */ +#include +#include +#include +#include #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 +#define GMQCC_VERSION_PATCH 6 +#define GMQCC_VERSION ((GMQCC_VERSION_MAJOR<<16)|(GMQCC_VERSION_MINOR<<8)|GMQCC_VERSION_PATCH) + +#ifdef GMQCC_VERSION_TYPE_RELEASE # ifdef GMQCC_GITINFO # define GMQCC_DEV_VERSION_STRING "git build: " GMQCC_GITINFO "\n" # elif defined(GMQCC_VERSION_TYPE_DEVEL) @@ -66,107 +38,52 @@ 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 (unsigned)(0) -# define true (unsigned)(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 */ +# define false (unsigned char)(0) +# define true (unsigned char)(1) + typedef unsigned char bool; +#endif -/* - * 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)) +# include +# if (__GNUC__ >= 2) || defined(__CLANG__) +# define GMQCC_NORETURN __attribute__((noreturn)) +# define GMQCC_FORCEINLINE __attribute__((always_inline)) +# define GMQCC_INLINE __inline +# endif +# define GMQCC_LIKELY(X) __builtin_expect((X), 1) +# define GMQCC_UNLIKELY(X) __builtin_expect((X), 0) +# define GMQCC_WARN __attribute__((warn_unused_result)) +# define GMQCC_USED __attribute__((used)) +# define GMQCC_RESTRICT __restrict__ #else +# ifdef _MSC_VER + /* conversion from 'int' to 'float', possible loss of data */ +# pragma warning(disable : 4244) + + 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; +# define GMQCC_NORETURN __declspec(noreturn) +# define GMQCC_FORCEINLINE __forceinline +# define GMQCC_INLINE __inline +# define GMQCC_RESTRICT __restrict +# else +# define GMQCC_NORETURN +# define GMQCC_FORCEINLINE +# define GMQCC_INLINE +# define GMQCC_RESTRICT +# endif +# define GMQCC_LIKELY(X) (X) +# define GMQCC_UNLIKELY(X) (X) # define GMQCC_WARN # define GMQCC_USED -#endif /*! defined(__GNUC__) || defined (__CLANG__) */ - -/* - * 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 */ +#endif -/* - * 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 @@ -240,78 +157,20 @@ GMQCC_IND_STRING(GMQCC_VERSION_PATCH) \ # else # define PLATFORM_BYTE_ORDER -1 # endif -#endif /*! !defined (PLATFORM_BYTE_ORDER) */ +#endif -/* - * 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__) */ - -/*===================================================================*/ -/*=========================== stat.c ================================*/ -/*===================================================================*/ -void stat_info (void); -char *stat_mem_strdup (const char *, size_t, const char *, bool); -void *stat_mem_reallocate(void *, size_t, size_t, const char *); -void stat_mem_deallocate(void *); -void *stat_mem_allocate (size_t, size_t, const char *); - -#define mem_a(SIZE) stat_mem_allocate ((SIZE), __LINE__, __FILE__) -#define mem_d(PTRN) stat_mem_deallocate((void*)(PTRN)) -#define mem_r(PTRN, SIZE) stat_mem_reallocate((void*)(PTRN), (SIZE), __LINE__, __FILE__) -#define mem_af(SIZE, FILE, LINE) stat_mem_allocate ((SIZE), (LINE), (FILE)) - -/* TODO: rename to mem variations */ -#define util_strdup(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, false) -#define util_strdupe(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, true) - -/*===================================================================*/ -/*=========================== util.c ================================*/ -/*===================================================================*/ +#define GMQCC_ARRAY_COUNT(X) (sizeof(X) / sizeof((X)[0])) + +/* stat.c */ +char *stat_mem_strdup(const char *, bool); + +#define mem_a(SIZE) malloc(SIZE) +#define mem_d(PTRN) free((void*)PTRN) +#define mem_r(PTRN, SIZE) realloc((void*)PTRN, SIZE) + +#define util_strdup(SRC) stat_mem_strdup((char*)(SRC), false) +#define util_strdupe(SRC) stat_mem_strdup((char*)(SRC), true) -/* - * Microsoft implements against the spec versions of ctype.h. Which - * means what ever the current set locale is will render the actual - * results of say isalpha('A') wrong for what ever retarded locale - * is used. Simalerly these are also implemented inefficently on - * some toolchains and end up becoming actual library calls. Perhaps - * this is why tools like yacc provide their own? Regardless implementing - * these as functions is equally as silly, the call overhead is not - * justified when this could happen on every character from an input - * stream. We provide our own as macros for absolute inlinability. - */ #define util_isalpha(a) ((((unsigned)(a)|32)-'a') < 26) #define util_isdigit(a) (((unsigned)(a)-'0') < 10) #define util_islower(a) (((unsigned)(a)-'a') < 26) @@ -319,32 +178,32 @@ void *stat_mem_allocate (size_t, size_t, const char *); #define util_isprint(a) (((unsigned)(a)-0x20) < 0x5F) #define util_isspace(a) (((a) >= 9 && (a) <= 13) || (a) == ' ') -bool util_filexists (const char *); -bool util_strupper (const char *); -bool util_strdigit (const char *); -void util_debug (const char *, const char *, ...); -void util_endianswap (void *, size_t, unsigned int); +bool util_strupper(const char *); +bool util_strdigit(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); +size_t util_strtocmd (const char *, char *, size_t); +size_t util_strtononcmd (const char *, char *, size_t); +size_t util_optimizationtostr(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); -/* - * 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); +int util_asprintf (char **ret, const char *fmt, ...); +int util_sscanf (const char *str, const char *format, ...); +char *util_strncpy (char *dest, const char *src, size_t n); +char *util_strncat (char *dest, const char *src, size_t n); +char *util_strcat (char *dest, const char *src); +const char *util_strerror(int err); + +const struct tm *util_localtime(const time_t *timer); +const char *util_ctime (const time_t *timer); + +bool util_isatty(FILE *); +size_t hash(const char *key); /* * A flexible vector implementation: all vector pointers contain some @@ -353,24 +212,26 @@ const char *util_strerror (int num); * us to use the array [] to access individual elements from the vector * opposed to using set/get methods. */ -typedef struct { +struct vector_t { 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) ( \ +void _util_vec_delete(void *vec); + +#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_meta(A) ((vector_t*)(((char *)(A)) - sizeof(vector_t))) +#define vec_free(A) ((void)((A) ? (_util_vec_delete((void *)(A)), (A) = nullptr) : 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)]) @@ -379,109 +240,30 @@ void _util_vec_grow(void **a, size_t i, size_t s); #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(void); - -typedef struct hash_table_t { +typedef struct hash_table_s { 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 ================================*/ -/*===================================================================*/ +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); +int util_snprintf(char *str, size_t, const char *fmt, ...); +int util_getline(char **, size_t *, FILE *); + +/* code.c */ -/* TODO: cleanup */ /* Note: if you change the order, fix type_sizeof in ir.c */ -enum { +enum qc_type { TYPE_VOID , TYPE_STRING , TYPE_FLOAT , @@ -523,24 +305,24 @@ 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 { +struct prog_section_t { 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; +}; + +struct prog_header_t { + uint32_t version; /* Program version (6) */ + uint16_t crc16; + uint16_t skip; + + prog_section_t statements; /* prog_section_statement */ + prog_section_t defs; /* prog_section_def */ + prog_section_t fields; /* prog_section_field */ + prog_section_t functions; /* prog_section_function */ + prog_section_t strings; + prog_section_t globals; + uint32_t entfield; /* Number of entity fields */ +}; /* * Each paramater incerements by 3 since vector types hold @@ -557,37 +339,19 @@ typedef struct { #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; +union operand_t { + int16_t s1; + uint16_t u1; +}; - /* - * 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; +struct prog_section_statement_t { + uint16_t opcode; + operand_t o1; + operand_t o2; + operand_t o3; +}; -typedef struct { +struct prog_section_both_t { /* * The types: * 0 = ev_void @@ -603,16 +367,16 @@ typedef struct { 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; +typedef prog_section_both_t prog_section_def_t; +typedef prog_section_both_t prog_section_field_t; /* this is ORed to the type */ #define DEF_SAVEGLOBAL (1<<15) #define DEF_TYPEMASK ((1<<15)-1) -typedef struct { +struct prog_section_function_t { int32_t entry; /* in statement table for instructions */ uint32_t firstlocal; /* First local in local table */ uint32_t locals; /* Total ints of params + locals */ @@ -621,7 +385,7 @@ typedef struct { 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 @@ -704,30 +468,67 @@ enum { 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 + VINSTR_NRCALL, + + /* Emulated instructions. */ + VINSTR_BITAND_V, /* BITAND_V must be the first emulated bitop */ + VINSTR_BITAND_VF, + VINSTR_BITOR_V, + VINSTR_BITOR_VF, + VINSTR_BITXOR, + VINSTR_BITXOR_V, + VINSTR_BITXOR_VF, + VINSTR_CROSS, + VINSTR_NEG_F, + VINSTR_NEG_V +}; + +/* TODO: elide */ +extern const char *util_instr_str[VINSTR_END]; + +void util_swap_header(prog_header_t &code_header); +void util_swap_statements(std::vector &statements); +void util_swap_defs_fields(std::vector §ion); +void util_swap_functions(std::vector &functions); +void util_swap_globals(std::vector &globals); + +typedef float qcfloat_t; +typedef int32_t qcint_t; +typedef uint32_t qcuint_t; + +struct code_t { + void* operator new(std::size_t); + void operator delete(void*); + code_t(); + ~code_t(); + std::vector statements; + std::vector linenums; + std::vector columnnums; + std::vector defs; + std::vector fields; + std::vector functions; + std::vector globals; + std::vector chars; + uint16_t crc = 0; + uint32_t entfields = 0; + ht string_cache; + qcint_t string_cached_empty = 0; }; -/* 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; +/* + * A shallow copy of a lex_file to remember where which ast node + * came from. + */ +struct lex_ctx_t { + const char *file; + size_t line; + size_t column; +}; /* * code_write -- writes out the compiled file @@ -742,23 +543,12 @@ GMQCC_WARN code_t *code_init (void); void code_cleanup (code_t *); 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); +qcint_t code_alloc_field (code_t *, size_t qcsize); +void code_push_statement(code_t *, prog_section_statement_t *stmt, lex_ctx_t ctx); 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; - size_t column; -} lex_ctx; -/*===================================================================*/ -/*============================ con.c ================================*/ -/*===================================================================*/ +/* conout.c */ enum { CON_BLACK = 30, CON_RED, @@ -782,14 +572,13 @@ FILE *con_default_err(void); void con_vprintmsg (int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap); void con_printmsg (int level, const char *name, size_t line, size_t column, 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_cvprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, va_list ap); +void con_cprintmsg (lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, ...); void con_close (void); void con_init (void); void con_reset (void); 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 *, ...); @@ -800,95 +589,31 @@ 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_error_ (lex_ctx_t ctx, /*LVL_ERROR*/ const char *msg, ...); +void /********/ vcompile_error (lex_ctx_t ctx, /*LVL_ERROR*/ const char *msg, va_list ap); +bool GMQCC_WARN compile_warning_(lex_ctx_t ctx, int warntype, const char *fmt, ...); +bool GMQCC_WARN vcompile_warning(lex_ctx_t ctx, int warntype, const char *fmt, va_list ap); void compile_show_werrors(void); -/*===================================================================*/ -/*========================= 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 ================================*/ -/*===================================================================*/ +template +inline constexpr const T formatNormalize(const T argument) { return argument; } + +inline const char *formatNormalize(const std::string &argument) { + return argument.c_str(); +} -enum store_types { +template +inline bool GMQCC_WARN compile_warning(lex_ctx_t ctx, int warntype, const char *fmt, const Ts&... ts) { + return compile_warning_(ctx, warntype, fmt, formatNormalize(ts)...); +} +template +inline void /********/ compile_error (lex_ctx_t ctx, /*LVL_ERROR*/ const char *msg, const Ts&... ts) { + return compile_error_(ctx, msg, formatNormalize(ts)...); +} + +/* ir.c */ +/* TODO: cleanup */ +enum store_type { store_global, store_local, /* local, assignable for now, should get promoted later */ store_param, /* parameters, they are locals with a fixed position */ @@ -896,18 +621,11 @@ enum store_types { 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); +struct vec3_t { + qcfloat_t x, y, z; +}; -/*===================================================================*/ -/*============================= exec.c ==============================*/ -/*===================================================================*/ +/* exec.c */ /* TODO: cleanup */ /* @@ -917,22 +635,21 @@ vector vec3_mulvf(vector, float); * 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]; + qcint_t _int; + qcint_t string; + qcint_t function; + qcint_t edict; + qcfloat_t _float; + qcfloat_t vector[3]; + qcint_t ivector[3]; +} qcany_t; + +typedef char qcfloat_t_size_is_correct [sizeof(qcfloat_t) == 4 ?1:-1]; +typedef char qcint_t_size_is_correct [sizeof(qcint_t) == 4 ?1:-1]; enum { VMERR_OK, VMERR_TEMPSTRING_ALLOC, - VMERR_END }; @@ -943,103 +660,109 @@ enum { #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 qc_program qc_program_t; +typedef int (*prog_builtin_t)(qc_program_t *prog); -typedef struct { - qcint stmt; - size_t localsp; - prog_section_function *function; -} qc_exec_stack; +struct qc_exec_stack_t { + qcint_t stmt; + size_t localsp; + prog_section_function_t *function; +}; -typedef struct qc_program_s { - char *filename; +struct qc_program { + qc_program() = delete; + qc_program(const char *name, uint16_t crc, size_t entfields); - prog_section_statement *code; - prog_section_def *defs; - prog_section_def *fields; - prog_section_function *functions; - char *strings; - qcint *globals; - qcint *entitydata; - bool *entitypool; + std::string filename; + std::vector code; + std::vector defs; + std::vector fields; + std::vector functions; + std::vector strings; + std::vector globals; + std::vector entitydata; + std::vector entitypool; - const char* *function_stack; + std::vector function_stack; uint16_t crc16; size_t tempstring_start; size_t tempstring_at; - qcint vmerror; + qcint_t vmerror; - size_t *profile; + std::vector profile; - prog_builtin *builtins; - size_t builtins_count; + prog_builtin_t *builtins; + size_t builtins_count; /* size_t ip; */ - qcint entities; + qcint_t entities; size_t entityfields; bool allowworldwrites; - qcint *localstack; - qc_exec_stack *stack; + std::vector localstack; + std::vector 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); - -const 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 (void); -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 (void); -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 ========================*/ -/*===================================================================*/ + + /* cached fields */ + struct { + qcint_t frame; + qcint_t nextthink; + qcint_t think; + } cached_fields; + + struct { + qcint_t self; + qcint_t time; + } cached_globals; + + bool supports_state; /* is INSTR_STATE supported? */ +}; + +qc_program_t* prog_load (const char *filename, bool ignoreversion); +void prog_delete (qc_program_t *prog); +bool prog_exec (qc_program_t *prog, prog_section_function_t *func, size_t flags, long maxjumps); +const char* prog_getstring (qc_program_t *prog, qcint_t str); +prog_section_def_t* prog_entfield (qc_program_t *prog, qcint_t off); +prog_section_def_t* prog_getdef (qc_program_t *prog, qcint_t off); +qcany_t* prog_getedict (qc_program_t *prog, qcint_t e); +qcint_t prog_tempstring(qc_program_t *prog, const char *_str); + + +/* parser.c */ +struct parser_t; +parser_t *parser_create(void); +bool parser_compile_file(parser_t *parser, const char *); +bool parser_compile_string(parser_t *parser, const char *, const char *, size_t); +bool parser_finish(parser_t *parser, const char *); + +/* ftepp.c */ +struct ftepp_t; +ftepp_t *ftepp_create (void); +bool ftepp_preprocess_file (ftepp_t *ftepp, const char *filename); +bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str); +void ftepp_finish(ftepp_t *ftepp); +const char *ftepp_get(ftepp_t *ftepp); +void ftepp_flush(ftepp_t *ftepp); +void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name); +void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value); + +/* main.c */ #if 1 /* Helpers to allow for a whole lot of flags. Otherwise we'd limit * to 32 or 64 -f options... */ -typedef struct { +struct longbit { 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 @@ -1048,25 +771,16 @@ typedef uint32_t longbit; #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 { +/* utf.8 */ +typedef long utf8ch_t; +int utf8_from(char *, utf8ch_t); +int utf8_to(utf8ch_t *, const unsigned char *, size_t); + +/* opts.c */ +struct opts_flag_def_t { const char *name; longbit bit; -} opts_flag_def; +}; bool opts_setflag (const char *, bool); bool opts_setwarn (const char *, bool); @@ -1084,18 +798,13 @@ void opts_restore_non_Wall(void); void opts_backup_non_Werror_all(void); void opts_restore_non_Werror_all(void); + 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 @@ -1103,12 +812,6 @@ enum { # 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 @@ -1116,18 +819,6 @@ enum { # 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 @@ -1136,25 +827,34 @@ enum { OPTION_COUNT }; -extern unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS]; +extern const opts_flag_def_t opts_flag_list[COUNT_FLAGS+1]; +extern const opts_flag_def_t opts_warn_list[COUNT_WARNINGS+1]; +extern const opts_flag_def_t opts_opt_list[COUNT_OPTIMIZATIONS+1]; +extern const unsigned int opts_opt_oflag[COUNT_OPTIMIZATIONS+1]; +extern unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS]; /* other options: */ -typedef enum { +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; +}; +struct opt_value_t { + union { + bool b; + uint16_t u16; + uint32_t u32; + union { + char *p; + const char *c; + } str; + } data; + bool allocated; +}; -typedef struct { +struct opts_cmd_t { opt_value_t options [OPTION_COUNT]; uint32_t flags [1 + (COUNT_FLAGS / 32)]; uint32_t warn [1 + (COUNT_WARNINGS / 32)]; @@ -1163,18 +863,22 @@ typedef struct { 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<< (unsigned)((i)%32)))) +#define OPTS_GENERIC(f,i) (!! (((f)[(i)/32]) & (1U << (unsigned)((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) + +#define OPTS_OPTION_DUPED(X) (opts.options[X].allocated) +#define OPTS_OPTION_BOOL(X) (opts.options[X].data.b) +#define OPTS_OPTION_U16(X) (opts.options[X].data.u16) +#define OPTS_OPTION_U32(X) (opts.options[X].data.u32) +#define OPTS_OPTION_STR(X) (opts.options[X].data.str.c) +#define OPTS_OPTION_DUP(X) *(OPTS_OPTION_DUPED(X)=true, &(opts.options[X].data.str.p)) #endif /*! GMQCC_HDR */