+ * 2) The cast itself depends on fresh storage (newly allocated in which
+ * ever function is using the cast macros), the contents of which are
+ * transferred in a way that the obligation to release storage is not
+ * propagated.
+ */
+typedef union {
+ void *enter;
+ qcint_t leave;
+} code_hash_entry_t;
+
+/* Some sanity macros */
+#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter)
+#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave)
+
+void code_push_statement(code_t *code, prog_section_statement_t *stmt_in, lex_ctx_t ctx)
+{
+ prog_section_statement_t stmt = *stmt_in;
+
+ if (OPTS_FLAG(TYPELESS_STORES)) {
+ switch (stmt.opcode) {
+ case INSTR_LOAD_S:
+ case INSTR_LOAD_ENT:
+ case INSTR_LOAD_FLD:
+ case INSTR_LOAD_FNC:
+ stmt.opcode = INSTR_LOAD_F;
+ break;
+ case INSTR_STORE_S:
+ case INSTR_STORE_ENT:
+ case INSTR_STORE_FLD:
+ case INSTR_STORE_FNC:
+ stmt.opcode = INSTR_STORE_F;
+ break;
+ case INSTR_STOREP_S:
+ case INSTR_STOREP_ENT:
+ case INSTR_STOREP_FLD:
+ case INSTR_STOREP_FNC:
+ stmt.opcode = INSTR_STOREP_F;
+ break;
+ }
+ }
+
+
+ if (OPTS_FLAG(SORT_OPERANDS)) {
+ uint16_t pair;
+
+ switch (stmt.opcode) {
+ case INSTR_MUL_F:
+ case INSTR_MUL_V:
+ case INSTR_ADD_F:
+ case INSTR_EQ_F:
+ case INSTR_EQ_S:
+ case INSTR_EQ_E:
+ case INSTR_EQ_FNC:
+ case INSTR_NE_F:
+ case INSTR_NE_V:
+ case INSTR_NE_S:
+ case INSTR_NE_E:
+ case INSTR_NE_FNC:
+ case INSTR_AND:
+ case INSTR_OR:
+ case INSTR_BITAND:
+ case INSTR_BITOR:
+ if (stmt.o1.u1 < stmt.o2.u1) {
+ uint16_t a = stmt.o2.u1;
+ stmt.o1.u1 = stmt.o2.u1;
+ stmt.o2.u1 = a;
+ }
+ break;
+
+ case INSTR_MUL_VF: pair = INSTR_MUL_FV; goto case_pair_gen;
+ case INSTR_MUL_FV: pair = INSTR_MUL_VF; goto case_pair_gen;
+ case INSTR_LT: pair = INSTR_GT; goto case_pair_gen;
+ case INSTR_GT: pair = INSTR_LT; goto case_pair_gen;
+ case INSTR_LE: pair = INSTR_GT; goto case_pair_gen;
+ case INSTR_GE: pair = INSTR_LE;
+
+ case_pair_gen:
+ if (stmt.o1.u1 < stmt.o2.u1) {
+ uint16_t x = stmt.o1.u1;
+ stmt.o1.u1 = stmt.o2.u1;
+ stmt.o2.u1 = x;
+ stmt.opcode = pair;
+ }
+ break;
+ }
+ }
+
+ vec_push(code->statements, stmt);
+ vec_push(code->linenums, (int)ctx.line);
+ vec_push(code->columnnums, (int)ctx.column);
+}
+
+void code_pop_statement(code_t *code)
+{
+ vec_pop(code->statements);
+ vec_pop(code->linenums);
+ vec_pop(code->columnnums);
+}
+
+code_t *code_init() {
+ static lex_ctx_t empty_ctx = {0, 0, 0};
+ static prog_section_function_t empty_function = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
+ static prog_section_statement_t empty_statement = {0,{0},{0},{0}};
+ static prog_section_def_t empty_def = {0, 0, 0};
+
+ code_t *code = (code_t*)mem_a(sizeof(code_t));
+ int i = 0;
+
+ memset(code, 0, sizeof(code_t));
+ code->entfields = 0;
+ code->string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
+
+ /*
+ * The way progs.dat is suppose to work is odd, there needs to be
+ * some null (empty) statements, functions, and 28 globals
+ */
+ for(; i < 28; i++)
+ vec_push(code->globals, 0);
+
+ vec_push(code->chars, '\0');
+ vec_push(code->functions, empty_function);
+
+ code_push_statement(code, &empty_statement, empty_ctx);
+
+ vec_push(code->defs, empty_def);
+ vec_push(code->fields, empty_def);
+
+ return code;
+}
+
+void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
+
+uint32_t code_genstring(code_t *code, const char *str) {
+ size_t hash;
+ code_hash_entry_t existing;
+
+ if (!str)
+ return 0;
+
+ if (!*str) {
+ if (!code->string_cached_empty) {
+ code->string_cached_empty = vec_size(code->chars);
+ vec_push(code->chars, 0);
+ }
+ return code->string_cached_empty;
+ }
+
+ if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
+ hash = ((unsigned char*)str)[strlen(str)-1];
+ CODE_HASH_ENTER(existing) = code_util_str_htgeth(code->string_cache, str, hash);
+ } else {
+ hash = util_hthash(code->string_cache, str);
+ CODE_HASH_ENTER(existing) = util_htgeth(code->string_cache, str, hash);
+ }
+
+ if (CODE_HASH_ENTER(existing))
+ return CODE_HASH_LEAVE(existing);
+
+ CODE_HASH_LEAVE(existing) = vec_size(code->chars);
+ vec_append(code->chars, strlen(str)+1, str);
+
+ util_htseth(code->string_cache, str, hash, CODE_HASH_ENTER(existing));
+ return CODE_HASH_LEAVE(existing);
+}
+
+qcint_t code_alloc_field (code_t *code, size_t qcsize)
+{
+ qcint_t pos = (qcint_t)code->entfields;
+ code->entfields += qcsize;
+ return pos;
+}
+
+static size_t code_size_generic(code_t *code, prog_header_t *code_header, bool lno) {
+ size_t size = 0;
+ if (lno) {
+ size += 4; /* LNOF */
+ size += sizeof(uint32_t); /* version */
+ size += sizeof(code_header->defs.length);
+ size += sizeof(code_header->globals.length);
+ size += sizeof(code_header->fields.length);
+ size += sizeof(code_header->statements.length);
+ size += sizeof(code->linenums[0]) * vec_size(code->linenums);
+ size += sizeof(code->columnnums[0]) * vec_size(code->columnnums);
+ } else {
+ size += sizeof(prog_header_t);
+ size += sizeof(prog_section_statement_t) * vec_size(code->statements);
+ size += sizeof(prog_section_def_t) * vec_size(code->defs);
+ size += sizeof(prog_section_field_t) * vec_size(code->fields);
+ size += sizeof(prog_section_function_t) * vec_size(code->functions);
+ size += sizeof(int32_t) * vec_size(code->globals);
+ size += 1 * vec_size(code->chars);
+ }
+ return size;
+}
+
+#define code_size_binary(C, H) code_size_generic((C), (H), false)
+#define code_size_debug(C, H) code_size_generic((C), (H), true)
+
+static void code_create_header(code_t *code, prog_header_t *code_header, const char *filename, const char *lnofile) {
+ size_t i;
+
+ code_header->statements.offset = sizeof(prog_header_t);
+ code_header->statements.length = vec_size(code->statements);
+ code_header->defs.offset = code_header->statements.offset + (sizeof(prog_section_statement_t) * vec_size(code->statements));
+ code_header->defs.length = vec_size(code->defs);
+ code_header->fields.offset = code_header->defs.offset + (sizeof(prog_section_def_t) * vec_size(code->defs));
+ code_header->fields.length = vec_size(code->fields);
+ code_header->functions.offset = code_header->fields.offset + (sizeof(prog_section_field_t) * vec_size(code->fields));
+ code_header->functions.length = vec_size(code->functions);
+ code_header->globals.offset = code_header->functions.offset + (sizeof(prog_section_function_t) * vec_size(code->functions));
+ code_header->globals.length = vec_size(code->globals);
+ code_header->strings.offset = code_header->globals.offset + (sizeof(int32_t) * vec_size(code->globals));
+ code_header->strings.length = vec_size(code->chars);
+ code_header->version = 6;
+ code_header->skip = 0;
+
+ if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
+ code_header->crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
+ else
+ code_header->crc16 = code->crc;
+ code_header->entfield = code->entfields;
+
+ if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
+ /* >= + P */
+ vec_push(code->chars, '\0'); /* > */
+ vec_push(code->chars, '\0'); /* = */
+ vec_push(code->chars, '\0'); /* P */
+ }
+
+ /* ensure all data is in LE format */
+ util_endianswap(&code_header->version, 1, sizeof(code_header->version));
+ util_endianswap(&code_header->crc16, 1, sizeof(code_header->crc16));
+ util_endianswap(&code_header->statements, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->defs, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->fields, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->functions, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->strings, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->globals, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->entfield, 1, sizeof(code_header->entfield));
+
+ /*
+ * These are not part of the header but we ensure LE format here to save on duplicated
+ * code.
+ */
+ util_endianswap(code->statements, vec_size(code->statements), sizeof(prog_section_statement_t));
+ util_endianswap(code->defs, vec_size(code->defs), sizeof(prog_section_def_t));
+ util_endianswap(code->fields, vec_size(code->fields), sizeof(prog_section_field_t));
+ util_endianswap(code->functions, vec_size(code->functions), sizeof(prog_section_function_t));
+ util_endianswap(code->globals, vec_size(code->globals), sizeof(int32_t));
+
+
+ if (!OPTS_OPTION_BOOL(OPTION_QUIET)) {
+ if (lnofile)
+ con_out("writing '%s' and '%s'...\n", filename, lnofile);
+ else
+ con_out("writing '%s'\n", filename);
+ }
+
+ if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
+ !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
+ {
+ char buffer[1024];
+ con_out("\nOptimizations:\n");
+ for (i = 0; i < COUNT_OPTIMIZATIONS; ++i) {
+ if (opts_optimizationcount[i]) {
+ util_optimizationtostr(opts_opt_list[i].name, buffer, sizeof(buffer));
+ con_out(
+ " %s: %u\n",
+ buffer,
+ (unsigned int)opts_optimizationcount[i]
+ );
+ }
+ }
+ }
+}
+
+static void code_stats(const char *filename, const char *lnofile, code_t *code, prog_header_t *code_header) {
+ if (OPTS_OPTION_BOOL(OPTION_QUIET) ||
+ OPTS_OPTION_BOOL(OPTION_PP_ONLY))
+ return;
+
+ con_out("\nFile statistics:\n");
+ con_out(" dat:\n");
+ con_out(" name: %s\n", filename);
+ con_out(" size: %u (bytes)\n", code_size_binary(code, code_header));
+ con_out(" crc: 0x%04X\n", code->crc);
+
+ if (lnofile) {
+ con_out(" lno:\n");
+ con_out(" name: %s\n", lnofile);
+ con_out(" size: %u (bytes)\n", code_size_debug(code, code_header));
+ }
+
+ con_out("\n");
+}
+
+/*
+ * Same principle except this one allocates memory and writes the lno(optional) and the dat file
+ * directly out to allocated memory. Which is actually very useful for the future library support
+ * we're going to add.