+ INSTR_DONE,
+ INSTR_MUL_F,
+ INSTR_MUL_V,
+ INSTR_MUL_FV,
+ INSTR_MUL_VF,
+ 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 assembler
+ * keep at the end but before virtual instructions
+ * for the IR below.
+ */
+ AINSTR_END,
+
+ /*
+ * Virtual instructions used by the IR
+ * Keep at the end!
+ */
+ VINSTR_PHI,
+ VINSTR_JUMP,
+ VINSTR_COND
+};
+
+/*
+ * The symbols below are created by the following
+ * expanded macros:
+ *
+ * VECTOR_MAKE(prog_section_statement, code_statements);
+ * VECTOR_MAKE(prog_section_def, code_defs );
+ * VECTOR_MAKE(prog_section_field, code_fields );
+ * VECTOR_MAKE(prog_section_function, code_functions );
+ * VECTOR_MAKE(int, code_globals );
+ * VECTOR_MAKE(char, code_chars );
+ */
+VECTOR_PROT(prog_section_statement, code_statements);
+VECTOR_PROT(prog_section_statement, code_statements);
+VECTOR_PROT(prog_section_def, code_defs );
+VECTOR_PROT(prog_section_field, code_fields );
+VECTOR_PROT(prog_section_function, code_functions );
+VECTOR_PROT(int, code_globals );
+VECTOR_PROT(char, code_chars );
+
+/*
+ * code_write -- writes out the compiled file
+ * code_init -- prepares the code file
+ */
+bool code_write (const char *filename);
+void code_init ();
+uint32_t code_genstring (const char *string);
+uint32_t code_cachedstring(const char *string);
+
+/*===================================================================*/
+/*========================= assembler.c =============================*/
+/*===================================================================*/
+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 },
+ { "DUB_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 */
+};
+
+void asm_init (const char *, FILE **);
+void asm_close(FILE *);
+void asm_parse(FILE *);
+/*===================================================================*/
+/*============================= ast.c ===============================*/
+/*===================================================================*/
+#define MEM_VECTOR_PROTO(Towner, Tmem, mem) \
+ bool GMQCC_WARN Towner##_##mem##_add(Towner*, Tmem); \
+ bool GMQCC_WARN Towner##_##mem##_remove(Towner*, size_t)
+
+#define MEM_VECTOR_PROTO_ALL(Towner, Tmem, mem) \
+ MEM_VECTOR_PROTO(Towner, Tmem, mem); \
+ bool GMQCC_WARN Towner##_##mem##_find(Towner*, Tmem, size_t*); \
+ void Towner##_##mem##_clear(Towner*)
+
+#define MEM_VECTOR_MAKE(Twhat, name) \
+ Twhat *name; \
+ size_t name##_count; \
+ size_t name##_alloc
+
+#define MEM_VEC_FUN_ADD(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_add(Tself *self, Twhat f) \
+{ \
+ Twhat *reall; \
+ if (self->mem##_count == self->mem##_alloc) { \
+ if (!self->mem##_alloc) { \
+ self->mem##_alloc = 16; \
+ } else { \
+ self->mem##_alloc *= 2; \
+ } \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * self->mem##_alloc); \
+ if (!reall) { \
+ return false; \
+ } \
+ memcpy(reall, self->mem, sizeof(Twhat) * self->mem##_count); \
+ mem_d(self->mem); \
+ self->mem = reall; \
+ } \
+ self->mem[self->mem##_count++] = f; \
+ return true; \
+}
+
+#define MEM_VEC_FUN_REMOVE(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_remove(Tself *self, size_t idx) \
+{ \
+ size_t i; \
+ Twhat *reall; \
+ if (idx >= self->mem##_count) { \
+ return true; /* huh... */ \
+ } \
+ for (i = idx; i < self->mem##_count-1; ++i) { \
+ self->mem[i] = self->mem[i+1]; \
+ } \
+ self->mem##_count--; \
+ if (self->mem##_count < self->mem##_count/2) { \
+ self->mem##_alloc /= 2; \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * self->mem##_count); \
+ if (!reall) { \
+ return false; \
+ } \
+ memcpy(reall, self->mem, sizeof(Twhat) * self->mem##_count); \
+ mem_d(self->mem); \
+ self->mem = reall; \
+ } \
+ return true; \
+}
+
+#define MEM_VEC_FUN_FIND(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_find(Tself *self, Twhat obj, size_t *idx) \
+{ \
+ size_t i; \
+ for (i = 0; i < self->mem##_count; ++i) { \
+ if (self->mem[i] == obj) { \
+ if (idx) { \
+ *idx = i; \
+ } \
+ return true; \
+ } \
+ } \
+ return false; \
+}
+
+#define MEM_VEC_FUN_APPEND(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_append(Tself *s, Twhat *p, size_t c) \
+{ \
+ Twhat *reall; \
+ if (s->mem##_count+c >= s->mem##_alloc) { \
+ if (!s->mem##_alloc) { \
+ s->mem##_alloc = c < 16 ? 16 : c; \
+ } else { \
+ s->mem##_alloc *= 2; \
+ if (s->mem##_count+c >= s->mem##_alloc) { \
+ s->mem##_alloc = s->mem##_count+c; \
+ } \
+ } \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * s->mem##_alloc); \
+ if (!reall) { \
+ return false; \
+ } \
+ memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count); \
+ mem_d(s->mem); \
+ s->mem = reall; \
+ } \
+ memcpy(&s->mem[s->mem##_count], p, c*sizeof(*p)); \
+ s->mem##_count += c; \
+ return true; \
+}
+
+#define MEM_VEC_FUN_RESIZE(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_resize(Tself *s, size_t c) \
+{ \
+ Twhat *reall; \
+ if (c > s->mem##_alloc) { \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * c); \
+ if (!reall) { return false; } \
+ memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count); \
+ s->mem##_alloc = c; \
+ mem_d(s->mem); \
+ s->mem = reall; \
+ return true; \
+ } \
+ s->mem##_count = c; \
+ if (c < (s->mem##_alloc / 2)) { \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * c); \
+ if (!reall) { return false; } \
+ memcpy(reall, s->mem, sizeof(Twhat) * c); \
+ mem_d(s->mem); \
+ s->mem = reall; \
+ } \
+ return true; \
+}
+
+#define MEM_VEC_FUN_CLEAR(Tself, mem) \
+void Tself##_##mem##_clear(Tself *self) \
+{ \
+ if (!self->mem) \
+ return; \
+ mem_d((void*) self->mem); \
+ self->mem = NULL; \
+ self->mem##_count = 0; \
+ self->mem##_alloc = 0; \
+}
+
+#define MEM_VECTOR_CLEAR(owner, mem) \
+ if ((owner)->mem) \
+ mem_d((void*)((owner)->mem)); \
+ (owner)->mem = NULL; \
+ (owner)->mem##_count = 0; \
+ (owner)->mem##_alloc = 0
+
+#define MEM_VECTOR_INIT(owner, mem) \
+{ \
+ (owner)->mem = NULL; \
+ (owner)->mem##_count = 0; \
+ (owner)->mem##_alloc = 0; \
+}
+
+#define MEM_VEC_FUNCTIONS(Tself, Twhat, mem) \
+MEM_VEC_FUN_REMOVE(Tself, Twhat, mem) \
+MEM_VEC_FUN_ADD(Tself, Twhat, mem)
+
+#define MEM_VEC_FUNCTIONS_ALL(Tself, Twhat, mem) \
+MEM_VEC_FUNCTIONS(Tself, Twhat, mem) \
+MEM_VEC_FUN_CLEAR(Tself, mem) \
+MEM_VEC_FUN_FIND(Tself, Twhat, mem)
+
+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 {
+ float x, y, z;
+} vector;
+
+/*
+ * 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;
+
+/*===================================================================*/
+/*============================= exec.c ==============================*/
+/*===================================================================*/
+
+/* 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 float qcfloat;
+typedef int32_t qcint;
+
+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;
+
+ MEM_VECTOR_MAKE(prog_section_statement, code);
+ MEM_VECTOR_MAKE(prog_section_def, defs);
+ MEM_VECTOR_MAKE(prog_section_def, fields);
+ MEM_VECTOR_MAKE(prog_section_function, functions);
+ MEM_VECTOR_MAKE(char, strings);
+ MEM_VECTOR_MAKE(qcint, globals);
+ MEM_VECTOR_MAKE(qcint, entitydata);
+
+ size_t tempstring_start;
+ size_t tempstring_at;
+
+ qcint vmerror;
+
+ MEM_VECTOR_MAKE(size_t, profile);
+
+ MEM_VECTOR_MAKE(prog_builtin, builtins);
+
+ /* size_t ip; */
+ qcint entities;
+ size_t entityfields;
+ bool allowworldwrites;
+
+ MEM_VECTOR_MAKE(qcint, localstack);
+ MEM_VECTOR_MAKE(qc_exec_stack, stack);
+ size_t statement;
+
+ int argc; /* current arg count for debugging */
+} qc_program;
+
+qc_program* prog_load(const char *filename);
+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);
+
+/*===================================================================*/
+/*======================= main.c commandline ========================*/
+/*===================================================================*/
+
+#if 0
+/* 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; /* index _into_ the 32 bit word, thus just uint8 */
+} longbit;
+#define LONGBIT(bit) { ((bit)/32), ((bit)%32) }
+#else
+typedef uint32_t longbit;
+#define LONGBIT(bit) (bit)
+#endif
+
+/* Used to store the list of flags with names */
+typedef struct {
+ const char *name;
+ longbit bit;
+} opts_flag_def;
+
+/*===================================================================*/
+/* list of -f flags, like -fdarkplaces-string-table-bug */
+enum {
+# define GMQCC_DEFINE_FLAG(X) X,
+# include "flags.def"
+# undef GMQCC_DEFINE_FLAG
+ COUNT_FLAGS
+};
+static const opts_flag_def opts_flag_list[] = {
+# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) },
+# include "flags.def"
+# undef GMQCC_DEFINE_FLAG
+ { NULL, LONGBIT(0) }
+};
+
+enum {
+# define GMQCC_DEFINE_FLAG(X) X,
+# include "warns.def"
+# undef GMQCC_DEFINE_FLAG
+ COUNT_WARNINGS
+};
+static const opts_flag_def opts_warn_list[] = {
+# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) },
+# include "warns.def"
+# undef GMQCC_DEFINE_FLAG
+ { NULL, LONGBIT(0) }
+};
+
+/* other options: */
+enum {
+ COMPILER_QCC, /* circa QuakeC */
+ COMPILER_FTEQCC, /* fteqcc QuakeC */
+ COMPILER_QCCX, /* qccx QuakeC */
+ COMPILER_GMQCC /* this QuakeC */