From: Dale Weiler Date: Sun, 26 Nov 2017 22:30:30 +0000 (-0500) Subject: Merge pull request #180 from xonotic/mem_leak_fix_on_failure_paths X-Git-Tag: xonotic-v0.8.5~29^2~1 X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=commitdiff_plain;h=d9127bf28ad5c1f7626487bbc5f29e72e9912d67;hp=8538658e830e2860799ef25f81983ef60a422cc6 Merge pull request #180 from xonotic/mem_leak_fix_on_failure_paths two small memory leak fixes on failure paths --- diff --git a/ast.cpp b/ast.cpp index cf8ffc7..cbc5db9 100644 --- a/ast.cpp +++ b/ast.cpp @@ -1132,6 +1132,8 @@ bool ast_value::generateGlobal(ir_builder *ir, bool isfield) m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF; if (m_flags & AST_FLAG_ERASEABLE) m_ir_v->m_flags |= IR_FLAG_ERASABLE; + if (m_flags & AST_FLAG_NOREF) + m_ir_v->m_flags |= IR_FLAG_NOREF; /* initialize */ if (m_hasvalue) { @@ -1236,6 +1238,8 @@ bool ast_value::generateGlobalField(ir_builder *ir) m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF; if (m_flags & AST_FLAG_ERASEABLE) m_ir_v->m_flags |= IR_FLAG_ERASABLE; + if (m_flags & AST_FLAG_NOREF) + m_ir_v->m_flags |= IR_FLAG_NOREF; const size_t namelen = m_name.length(); std::unique_ptr name(new char[namelen+16]); @@ -1254,7 +1258,9 @@ bool ast_value::generateGlobalField(ir_builder *ir) array->m_ir_values[ai]->m_unique_life = true; array->m_ir_values[ai]->m_locked = true; if (m_flags & AST_FLAG_INCLUDE_DEF) - m_ir_values[ai]->m_flags |= IR_FLAG_INCLUDE_DEF; + array->m_ir_values[ai]->m_flags |= IR_FLAG_INCLUDE_DEF; + if (m_flags & AST_FLAG_NOREF) + array->m_ir_values[ai]->m_flags |= IR_FLAG_NOREF; } } else @@ -1266,9 +1272,10 @@ bool ast_value::generateGlobalField(ir_builder *ir) m_ir_v = v; if (m_flags & AST_FLAG_INCLUDE_DEF) m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF; - if (m_flags & AST_FLAG_ERASEABLE) m_ir_v->m_flags |= IR_FLAG_ERASABLE; + if (m_flags & AST_FLAG_NOREF) + m_ir_v->m_flags |= IR_FLAG_NOREF; } return true; } @@ -1299,7 +1306,9 @@ ir_value *ast_value::prepareGlobalArray(ir_builder *ir) if (m_flags & AST_FLAG_INCLUDE_DEF) v->m_flags |= IR_FLAG_INCLUDE_DEF; if (m_flags & AST_FLAG_ERASEABLE) - m_ir_v->m_flags |= IR_FLAG_ERASABLE; + v->m_flags |= IR_FLAG_ERASABLE; + if (m_flags & AST_FLAG_NOREF) + v->m_flags |= IR_FLAG_NOREF; const size_t namelen = m_name.length(); std::unique_ptr name(new char[namelen+16]); @@ -1319,6 +1328,8 @@ ir_value *ast_value::prepareGlobalArray(ir_builder *ir) m_ir_values[ai]->m_locked = true; if (m_flags & AST_FLAG_INCLUDE_DEF) m_ir_values[ai]->m_flags |= IR_FLAG_INCLUDE_DEF; + if (m_flags & AST_FLAG_NOREF) + m_ir_values[ai]->m_flags |= IR_FLAG_NOREF; } return v; @@ -1365,6 +1376,9 @@ bool ast_value::generateLocal(ir_function *func, bool param) v->m_unique_life = true; v->m_locked = true; + if (m_flags & AST_FLAG_NOREF) + v->m_flags |= IR_FLAG_NOREF; + const size_t namelen = m_name.length(); std::unique_ptr name(new char[namelen+16]); util_strncpy(name.get(), m_name.c_str(), namelen); @@ -1379,7 +1393,10 @@ bool ast_value::generateLocal(ir_function *func, bool param) } m_ir_values[ai]->m_context = m_context; m_ir_values[ai]->m_unique_life = true; - m_ir_values[ai]->m_locked = true; + m_ir_values[ai]->m_locked = true; + + if (m_flags & AST_FLAG_NOREF) + m_ir_values[ai]->m_flags |= IR_FLAG_NOREF; } } else @@ -1418,6 +1435,9 @@ bool ast_value::generateLocal(ir_function *func, bool param) v->m_cvq = m_cvq; m_ir_v = v; + if (m_flags & AST_FLAG_NOREF) + m_ir_v->m_flags |= IR_FLAG_NOREF; + if (!generateAccessors(func->m_owner)) return false; return true; @@ -1490,9 +1510,9 @@ bool ast_function::generateFunction(ir_builder *ir) /* fill the parameter list */ for (auto &it : m_function_type->m_type_params) { if (it->m_vtype == TYPE_FIELD) - vec_push(irf->m_params, it->m_next->m_vtype); + irf->m_params.push_back(it->m_next->m_vtype); else - vec_push(irf->m_params, it->m_vtype); + irf->m_params.push_back(it->m_vtype); if (!m_builtin) { if (!it->generateLocal(m_ir_func, true)) return false; @@ -1562,7 +1582,7 @@ bool ast_function::generateFunction(ir_builder *ir) { return ir_block_create_return(m_curblock, m_context, nullptr); } - else if (vec_size(m_curblock->m_entries) || m_curblock == irf->m_first) + else if (m_curblock->m_entries.size() || m_curblock == irf->m_first) { if (m_return_value) { if (!m_return_value->codegen(this, false, &dummy)) @@ -1784,7 +1804,7 @@ bool ast_binary::codegen(ast_function *func, bool lvalue, ir_value **out) return false; } /* use the likely flag */ - vec_last(func->m_curblock->m_instr)->m_likely = true; + func->m_curblock->m_instr.back()->m_likely = true; /* enter the right-expression's block */ func->m_curblock = other; @@ -2098,13 +2118,45 @@ bool ast_member::codegen(ast_function *func, bool lvalue, ir_value **out) compile_error(m_context, "not an l-value (member access)"); return false; } - if (m_outl) { + if (lvalue && m_outl) { *out = m_outl; return true; } + if (!lvalue && m_outr) { + *out = m_outr; + return true; + } - if (!m_owner->codegen(func, false, &vec)) - return false; + if (ast_istype(m_owner, ast_entfield)) { + ir_value *ent, *field; + auto entfield = reinterpret_cast(m_owner); + if (!entfield->m_entity->codegen(func, false, &ent)) + return false; + if (!entfield->m_field->codegen(func, false, &vec)) + return false; + field = vec->vectorMember(m_field); + if (lvalue) { + *out = ir_block_create_fieldaddress(func->m_curblock, m_context, func->makeLabel("mefa"), + ent, field); + } else { + *out = ir_block_create_load_from_ent(func->m_curblock, m_context, func->makeLabel("mefv"), + ent, field, m_vtype); + } + if (!*out) { + compile_error(m_context, "failed to create %s instruction (output type %s)", + (lvalue ? "ADDRESS" : "FIELD"), + type_name[m_vtype]); + return false; + } + if (lvalue) + m_outl = *out; + else + m_outr = *out; + return (*out != nullptr); + } else { + if (!m_owner->codegen(func, false, &vec)) + return false; + } if (vec->m_vtype != TYPE_VECTOR && !(vec->m_vtype == TYPE_FIELD && m_owner->m_next->m_vtype == TYPE_VECTOR)) diff --git a/ast.h b/ast.h index b305dac..04fa4d8 100644 --- a/ast.h +++ b/ast.h @@ -62,6 +62,12 @@ enum { AST_FLAG_COVERAGE = 1 << 12, AST_FLAG_BLOCK_COVERAGE = 1 << 13, + /* + * Propagates norefness to the IR so the unused (read/write) check can be + * more intelligently done. + */ + AST_FLAG_NOREF = 1 << 14, + AST_FLAG_LAST, AST_FLAG_TYPE_MASK = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN), AST_FLAG_COVERAGE_MASK = (AST_FLAG_BLOCK_COVERAGE) @@ -223,9 +229,6 @@ struct ast_value : ast_expression */ std::vector m_initlist; - /* usecount for the parser */ - size_t m_uses = 0; - ir_value *m_ir_v = nullptr; std::vector m_ir_values; size_t m_ir_value_count = 0; diff --git a/doc/gmqcc.1 b/doc/gmqcc.1 index 78d767e..510e754 100644 --- a/doc/gmqcc.1 +++ b/doc/gmqcc.1 @@ -186,6 +186,9 @@ variables can be opened using .Ql #pragma noref 1 and closed via .Ql #pragma noref 0 Ns . +.It Fl W Ns Cm unused-component +Generate a warning about vector variables which are declared but not all their +components are used. .It Fl W Ns Cm used-uninitialized Generate a warning if it is possible that a variable can be used without prior initialization. Note that this warning is not diff --git a/exec.cpp b/exec.cpp index e76004e..387923f 100644 --- a/exec.cpp +++ b/exec.cpp @@ -28,6 +28,12 @@ static void qcvmerror(qc_program_t *prog, const char *fmt, ...) putchar('\n'); } +qc_program::qc_program(const char *name, uint16_t crc, size_t entfields) + : filename(name) + , crc16(crc) + , entityfields(entfields) +{} + qc_program_t* prog_load(const char *filename, bool skipversion) { prog_header_t header; @@ -58,22 +64,7 @@ qc_program_t* prog_load(const char *filename, bool skipversion) return nullptr; } - prog = (qc_program_t*)mem_a(sizeof(qc_program_t)); - if (!prog) { - fclose(file); - fprintf(stderr, "failed to allocate program data\n"); - return nullptr; - } - memset(prog, 0, sizeof(*prog)); - - prog->entityfields = header.entfield; - prog->crc16 = header.crc16; - - prog->filename = util_strdup(filename); - if (!prog->filename) { - loaderror("failed to store program name"); - goto error; - } + prog = new qc_program(filename, header.crc16, header.entfield); #define read_data(hdrvar, progvar, reserved) \ if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) { \ @@ -110,7 +101,8 @@ qc_program_t* prog_load(const char *filename, bool skipversion) fclose(file); /* profile counters */ - memset(vec_add(prog->profile, prog->code.size()), 0, sizeof(prog->profile[0]) * prog->code.size()); + prog->profile.resize(prog->code.size()); + memset(&prog->profile[0], 0, sizeof(prog->profile[0]) * prog->profile.size()); /* Add tempstring area */ prog->tempstring_start = prog->strings.size(); @@ -119,8 +111,10 @@ qc_program_t* prog_load(const char *filename, bool skipversion) prog->strings.resize(prog->strings.size() + 16*1024, '\0'); /* spawn the world entity */ - vec_push(prog->entitypool, true); - memset(vec_add(prog->entitydata, prog->entityfields), 0, prog->entityfields * sizeof(prog->entitydata[0])); + prog->entitypool.emplace_back(true); + prog->entitydata.resize(prog->entityfields); + if (prog->entitydata.size()) + memset(prog->entitydata.data(), 0, sizeof(prog->entitydata[0]) * prog->entityfields); prog->entities = 1; /* cache some globals and fields from names */ @@ -156,11 +150,7 @@ qc_program_t* prog_load(const char *filename, bool skipversion) return prog; error: - if (prog->filename) - mem_d(prog->filename); - vec_free(prog->entitydata); - vec_free(prog->entitypool); - mem_d(prog); + delete prog; fclose(file); return nullptr; @@ -168,13 +158,7 @@ error: void prog_delete(qc_program_t *prog) { - if (prog->filename) mem_d(prog->filename); - vec_free(prog->entitydata); - vec_free(prog->entitypool); - vec_free(prog->localstack); - vec_free(prog->stack); - vec_free(prog->profile); - mem_d(prog); + delete prog; } /*********************************************************************** @@ -205,28 +189,31 @@ prog_section_def_t* prog_getdef(qc_program_t *prog, qcint_t off) } qcany_t* prog_getedict(qc_program_t *prog, qcint_t e) { - if (e >= (qcint_t)vec_size(prog->entitypool)) { + if (e >= (qcint_t)prog->entitypool.size()) { prog->vmerror++; fprintf(stderr, "Accessing out of bounds edict %i\n", (int)e); e = 0; } - return (qcany_t*)(prog->entitydata + (prog->entityfields * e)); + return (qcany_t*)&prog->entitydata[prog->entityfields * e]; } static qcint_t prog_spawn_entity(qc_program_t *prog) { char *data; qcint_t e; - for (e = 0; e < (qcint_t)vec_size(prog->entitypool); ++e) { + for (e = 0; e < (qcint_t)prog->entitypool.size(); ++e) { if (!prog->entitypool[e]) { - data = (char*)(prog->entitydata + (prog->entityfields * e)); + data = (char*)&prog->entitydata[prog->entityfields * e]; memset(data, 0, prog->entityfields * sizeof(qcint_t)); return e; } } - vec_push(prog->entitypool, true); + prog->entitypool.emplace_back(true); prog->entities++; - data = (char*)vec_add(prog->entitydata, prog->entityfields); - memset(data, 0, prog->entityfields * sizeof(qcint_t)); + size_t sz = prog->entitydata.size(); + prog->entitydata.resize(sz + prog->entityfields); + data = (char*)&prog->entitydata[sz]; + memset(data, 0, sz * sizeof(qcint_t)); + return e; } @@ -236,7 +223,7 @@ static void prog_free_entity(qc_program_t *prog, qcint_t e) { fprintf(stderr, "Trying to free world entity\n"); return; } - if (e >= (qcint_t)vec_size(prog->entitypool)) { + if (e >= (qcint_t)prog->entitypool.size()) { prog->vmerror++; fprintf(stderr, "Trying to free out of bounds entity\n"); return; @@ -369,11 +356,11 @@ static void prog_print_statement(qc_program_t *prog, prog_section_statement_t *s printf("\n", st->opcode); return; } - if ((prog->xflags & VMXF_TRACE) && vec_size(prog->function_stack)) { + if ((prog->xflags & VMXF_TRACE) && !prog->function_stack.empty()) { size_t i; - for (i = 0; i < vec_size(prog->function_stack); ++i) + for (i = 0; i < prog->function_stack.size(); ++i) printf("->"); - printf("%s:", vec_last(prog->function_stack)); + printf("%s:", prog->function_stack.back()); } printf(" <> %-12s", util_instr_str[st->opcode]); if (st->opcode >= INSTR_IF && @@ -467,30 +454,30 @@ static qcint_t prog_enterfunction(qc_program_t *prog, prog_section_function_t *f int32_t p; /* back up locals */ - st.localsp = vec_size(prog->localstack); + st.localsp = prog->localstack.size(); st.stmt = prog->statement; st.function = func; if (prog->xflags & VMXF_TRACE) { const char *str = prog_getstring(prog, func->name); - vec_push(prog->function_stack, str); + prog->function_stack.emplace_back(str); } #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS - if (vec_size(prog->stack)) + if (prog->stack.size()) { prog_section_function_t *cur; - cur = prog->stack[vec_size(prog->stack)-1].function; + cur = prog->stack.back().function; if (cur) { qcint_t *globals = &prog->globals[0] + cur->firstlocal; - vec_append(prog->localstack, cur->locals, globals); + prog->localstack.insert(prog->localstack.end(), globals, globals + cur->locals); } } #else { qcint_t *globals = &prog->globals[0] + func->firstlocal; - vec_append(prog->localstack, func->locals, globals); + prog->localstack.insert(prog->localstack.end(), globals, globals + func->locals); } #endif @@ -505,7 +492,7 @@ static qcint_t prog_enterfunction(qc_program_t *prog, prog_section_function_t *f } } - vec_push(prog->stack, st); + prog->stack.emplace_back(st); return func->entry; } @@ -514,30 +501,31 @@ static qcint_t prog_leavefunction(qc_program_t *prog) { prog_section_function_t *prev = nullptr; size_t oldsp; - qc_exec_stack_t st = vec_last(prog->stack); + qc_exec_stack_t st = prog->stack.back(); if (prog->xflags & VMXF_TRACE) { - if (vec_size(prog->function_stack)) - vec_pop(prog->function_stack); + if (!prog->function_stack.empty()) + prog->function_stack.pop_back(); } #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS - if (vec_size(prog->stack) > 1) { - prev = prog->stack[vec_size(prog->stack)-2].function; - oldsp = prog->stack[vec_size(prog->stack)-2].localsp; + if (prog->stack.size() > 1) { + prev = prog->stack[prog->stack.size()-2].function; + oldsp = prog->stack[prog->stack.size()-2].localsp; } #else - prev = prog->stack[vec_size(prog->stack)-1].function; - oldsp = prog->stack[vec_size(prog->stack)-1].localsp; + prev = prog->stack[prog->stack.size()-1].function; + oldsp = prog->stack[prog->stack.size()-1].localsp; #endif if (prev) { - qcint_t *globals = &prog->globals[0] + prev->firstlocal; - memcpy(globals, prog->localstack + oldsp, prev->locals * sizeof(prog->localstack[0])); - /* vec_remove(prog->localstack, oldsp, vec_size(prog->localstack)-oldsp); */ - vec_shrinkto(prog->localstack, oldsp); + if (prev->locals) { + qcint_t *globals = &prog->globals[0] + prev->firstlocal; + memcpy(globals, &prog->localstack[oldsp], prev->locals * sizeof(prog->localstack[0])); + } + prog->localstack.resize(oldsp); } - vec_pop(prog->stack); + prog->stack.pop_back(); return st.stmt - 1; /* offset the ++st */ } @@ -584,8 +572,8 @@ bool prog_exec(qc_program_t *prog, prog_section_function_t *func, size_t flags, cleanup: prog->xflags = oldxflags; - vec_free(prog->localstack); - vec_free(prog->stack); + prog->localstack.clear(); + prog->stack.clear(); if (prog->vmerror) return false; return true; @@ -623,7 +611,7 @@ struct qcvm_parameter { const char *value; }; -static qcvm_parameter *main_params = nullptr; +static std::vector main_params; #define CheckArgs(num) do { \ if (prog->argc != (num)) { \ @@ -683,6 +671,19 @@ static int qc_stof(qc_program_t *prog) { return 0; } +static int qc_stov(qc_program_t *prog) { + qcany_t *str; + qcany_t num; + CheckArgs(1); + str = GetArg(0); + (void)util_sscanf(prog_getstring(prog, str->string), " ' %f %f %f ' ", + &num.vector[0], + &num.vector[1], + &num.vector[2]); + Return(num); + return 0; +} + static int qc_vtos(qc_program_t *prog) { char buffer[512]; qcany_t *num; @@ -848,7 +849,8 @@ static prog_builtin_t qc_builtins[] = { &qc_normalize, /* 12 */ &qc_sqrt, /* 13 */ &qc_floor, /* 14 */ - &qc_pow /* 15 */ + &qc_pow, /* 15 */ + &qc_stov /* 16 */ }; static const char *arg0 = nullptr; @@ -887,7 +889,7 @@ static void prog_main_setparams(qc_program_t *prog) { size_t i; qcany_t *arg; - for (i = 0; i < vec_size(main_params); ++i) { + for (i = 0; i < main_params.size(); ++i) { arg = GetGlobal(OFS_PARM0 + 3*i); arg->vector[0] = 0; arg->vector[1] = 0; @@ -926,8 +928,8 @@ int main(int argc, char **argv) { bool opts_info = false; bool noexec = false; const char *progsfile = nullptr; - const char **dis_list = nullptr; int opts_v = 0; + std::vector dis_list; arg0 = argv[0]; @@ -997,7 +999,7 @@ int main(int argc, char **argv) { usage(); exit(EXIT_FAILURE); } - vec_push(dis_list, argv[1]); + dis_list.emplace_back(argv[1]); --argc; ++argv; noexec = true; @@ -1042,7 +1044,7 @@ int main(int argc, char **argv) { } p.value = argv[1]; - vec_push(main_params, p); + main_params.emplace_back(p); --argc; ++argv; } @@ -1111,7 +1113,7 @@ int main(int argc, char **argv) { prog_delete(prog); return 0; } - for (i = 0; i < vec_size(dis_list); ++i) { + for (i = 0; i < dis_list.size(); ++i) { size_t k; printf("Looking for `%s`\n", dis_list[i]); for (k = 1; k < prog->functions.size(); ++k) { @@ -1282,7 +1284,7 @@ while (prog->vmerror == 0) { switch (st->opcode) { default: - qcvmerror(prog, "Illegal instruction in %s\n", prog->filename); + qcvmerror(prog, "Illegal instruction in %s\n", prog->filename.c_str()); goto cleanup; case INSTR_DONE: @@ -1293,7 +1295,7 @@ while (prog->vmerror == 0) { GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2]; st = &prog->code[0] + prog_leavefunction(prog); - if (!vec_size(prog->stack)) + if (prog->stack.empty()) goto cleanup; break; @@ -1402,12 +1404,12 @@ while (prog->vmerror == 0) { case INSTR_LOAD_ENT: case INSTR_LOAD_FNC: if (OPA->edict < 0 || OPA->edict >= prog->entities) { - qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename); + qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename.c_str()); goto cleanup; } if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) { qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)", - prog->filename, + prog->filename.c_str(), OPB->_int); goto cleanup; } @@ -1416,13 +1418,13 @@ while (prog->vmerror == 0) { break; case INSTR_LOAD_V: if (OPA->edict < 0 || OPA->edict >= prog->entities) { - qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename); + qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename.c_str()); goto cleanup; } if (OPB->_int < 0 || OPB->_int + 3 > (qcint_t)prog->entityfields) { qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)", - prog->filename, + prog->filename.c_str(), OPB->_int + 2); goto cleanup; } @@ -1435,19 +1437,19 @@ while (prog->vmerror == 0) { case INSTR_ADDRESS: if (OPA->edict < 0 || OPA->edict >= prog->entities) { - qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict); + qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename.c_str(), OPA->edict); goto cleanup; } if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) { qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)", - prog->filename, + prog->filename.c_str(), OPB->_int); goto cleanup; } ed = prog_getedict(prog, OPA->edict); - OPC->_int = ((qcint_t*)ed) - prog->entitydata + OPB->_int; + OPC->_int = ((qcint_t*)ed) - prog->entitydata.data() + OPB->_int; break; case INSTR_STORE_F: @@ -1468,29 +1470,29 @@ while (prog->vmerror == 0) { case INSTR_STOREP_ENT: case INSTR_STOREP_FLD: case INSTR_STOREP_FNC: - if (OPB->_int < 0 || OPB->_int >= (qcint_t)vec_size(prog->entitydata)) { - qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int); + if (OPB->_int < 0 || OPB->_int >= (qcint_t)prog->entitydata.size()) { + qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename.c_str(), OPB->_int); goto cleanup; } if (OPB->_int < (qcint_t)prog->entityfields && !prog->allowworldwrites) qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n", - prog->filename, + prog->filename.c_str(), prog_getstring(prog, prog_entfield(prog, OPB->_int)->name), OPB->_int); - ptr = (qcany_t*)(prog->entitydata + OPB->_int); + ptr = (qcany_t*)&prog->entitydata[OPB->_int]; ptr->_int = OPA->_int; break; case INSTR_STOREP_V: - if (OPB->_int < 0 || OPB->_int + 2 >= (qcint_t)vec_size(prog->entitydata)) { - qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int); + if (OPB->_int < 0 || OPB->_int + 2 >= (qcint_t)prog->entitydata.size()) { + qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename.c_str(), OPB->_int); goto cleanup; } if (OPB->_int < (qcint_t)prog->entityfields && !prog->allowworldwrites) qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n", - prog->filename, + prog->filename.c_str(), prog_getstring(prog, prog_entfield(prog, OPB->_int)->name), OPB->_int); - ptr = (qcany_t*)(prog->entitydata + OPB->_int); + ptr = (qcany_t*)&prog->entitydata[OPB->_int]; ptr->ivector[0] = OPA->ivector[0]; ptr->ivector[1] = OPA->ivector[1]; ptr->ivector[2] = OPA->ivector[2]; @@ -1521,7 +1523,7 @@ while (prog->vmerror == 0) { { st += st->o2.s1 - 1; /* offset the s++ */ if (++jumpcount >= maxjumps) - qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount); + qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount); } break; case INSTR_IFNOT: @@ -1529,7 +1531,7 @@ while (prog->vmerror == 0) { { st += st->o2.s1 - 1; /* offset the s++ */ if (++jumpcount >= maxjumps) - qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount); + qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount); } break; @@ -1544,11 +1546,11 @@ while (prog->vmerror == 0) { case INSTR_CALL8: prog->argc = st->opcode - INSTR_CALL0; if (!OPA->function) - qcvmerror(prog, "nullptr function in `%s`", prog->filename); + qcvmerror(prog, "nullptr function in `%s`", prog->filename.c_str()); if(!OPA->function || OPA->function >= (qcint_t)prog->functions.size()) { - qcvmerror(prog, "CALL outside the program in `%s`", prog->filename); + qcvmerror(prog, "CALL outside the program in `%s`", prog->filename.c_str()); goto cleanup; } @@ -1565,7 +1567,7 @@ while (prog->vmerror == 0) { prog->builtins[builtinnumber](prog); else qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources", - builtinnumber, prog->filename); + builtinnumber, prog->filename.c_str()); } else st = &prog->code[0] + prog_enterfunction(prog, newf) - 1; /* offset st++ */ @@ -1579,7 +1581,7 @@ while (prog->vmerror == 0) { qcfloat_t *time; qcfloat_t *frame; if (!prog->supports_state) { - qcvmerror(prog, "`%s` tried to execute a STATE operation but misses its defs!", prog->filename); + qcvmerror(prog, "`%s` tried to execute a STATE operation but misses its defs!", prog->filename.c_str()); goto cleanup; } ed = prog_getedict(prog, prog->globals[prog->cached_globals.self]); @@ -1596,7 +1598,7 @@ while (prog->vmerror == 0) { case INSTR_GOTO: st += st->o1.s1 - 1; /* offset the s++ */ if (++jumpcount == 10000000) - qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount); + qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount); break; case INSTR_AND: diff --git a/fold.cpp b/fold.cpp index 7a0df0e..7b13db6 100644 --- a/fold.cpp +++ b/fold.cpp @@ -1392,8 +1392,8 @@ ast_expression *fold::op(const oper_info *info, ast_expression **opexprs) { return nullptr; switch(info->operands) { - case 3: if(!c) return nullptr; - case 2: if(!b) return nullptr; + case 3: if(!c) return nullptr; [[fallthrough]]; + case 2: if(!b) return nullptr; [[fallthrough]]; case 1: if(!a) { compile_error(ctx(), "internal error: fold_op no operands to fold\n"); @@ -1560,6 +1560,7 @@ ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, i case INSTR_DIV_F: if (swapped) return nullptr; + [[fallthrough]]; case INSTR_MUL_F: if (immvalue_float(load) == 1.0f) { ++opts_optimizationcount[OPTIM_PEEPHOLE]; @@ -1572,6 +1573,7 @@ ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, i case INSTR_SUB_F: if (swapped) return nullptr; + [[fallthrough]]; case INSTR_ADD_F: if (immvalue_float(load) == 0.0f) { ++opts_optimizationcount[OPTIM_PEEPHOLE]; @@ -1591,6 +1593,7 @@ ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, i case INSTR_SUB_V: if (swapped) return nullptr; + [[fallthrough]]; case INSTR_ADD_V: if (vec3_cmp(immvalue_vector(load), vec3_create(0, 0, 0))) { ++opts_optimizationcount[OPTIM_PEEPHOLE]; diff --git a/ftepp.cpp b/ftepp.cpp index f1d5f36..190978f 100644 --- a/ftepp.cpp +++ b/ftepp.cpp @@ -920,7 +920,7 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) if ( vec_size(params) < vec_size(macro->params) || (vec_size(params) > vec_size(macro->params) && !macro->variadic) ) { - ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name, + ftepp_error(ftepp, "macro %s expects%s %u parameters, %u provided", macro->name, (macro->variadic ? " at least" : ""), (unsigned int)vec_size(macro->params), (unsigned int)vec_size(params)); diff --git a/gmqcc.h b/gmqcc.h index 4e3fb39..10b74f5 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -660,7 +660,7 @@ enum { #define VMXF_TRACE 0x0001 /* trace: print statements before executing */ #define VMXF_PROFILE 0x0002 /* profile: increment the profile counters */ -struct qc_program_t; +typedef struct qc_program qc_program_t; typedef int (*prog_builtin_t)(qc_program_t *prog); struct qc_exec_stack_t { @@ -669,18 +669,21 @@ struct qc_exec_stack_t { prog_section_function_t *function; }; -struct qc_program_t { - char *filename; +struct qc_program { + qc_program() = delete; + qc_program(const char *name, uint16_t crc, size_t entfields); + + std::string filename; std::vector code; std::vector defs; std::vector fields; std::vector functions; std::vector strings; std::vector globals; - qcint_t *entitydata; - bool *entitypool; + std::vector entitydata; + std::vector entitypool; - const char* *function_stack; + std::vector function_stack; uint16_t crc16; @@ -689,7 +692,7 @@ struct qc_program_t { qcint_t vmerror; - size_t *profile; + std::vector profile; prog_builtin_t *builtins; size_t builtins_count; @@ -699,8 +702,8 @@ struct qc_program_t { size_t entityfields; bool allowworldwrites; - qcint_t *localstack; - qc_exec_stack_t *stack; + std::vector localstack; + std::vector stack; size_t statement; size_t xflags; @@ -738,7 +741,6 @@ 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 *); -void parser_cleanup(parser_t *parser); /* ftepp.c */ struct ftepp_t; diff --git a/gmqcc.ini.example b/gmqcc.ini.example index 59a27f9..5c1ad37 100644 --- a/gmqcc.ini.example +++ b/gmqcc.ini.example @@ -348,7 +348,7 @@ [warnings] #Generate a warning about variables which are declared but never - #used. This can be avoided by adding the ‘noref’ keyword in front + #used. This can be avoided by adding the ‘noref’ keyword in front #of the variable declaration. Additionally a complete section of #unreferenced variables can be opened using ‘#pragma noref 1’ and #closed via ‘#pragma noref 0’. @@ -356,6 +356,11 @@ UNUSED_VARIABLE = false + #Generate a warning about vector variables which are declared but + #components of it are never used. + + UNUSED_COMPONENT = false + #Generate a warning if it is possible that a variable can be used #without prior initialization. Note that this warning is not nec‐ #essarily reliable if the initialization happens only under cer‐ diff --git a/ir.cpp b/ir.cpp index 3a166c7..facbc33 100644 --- a/ir.cpp +++ b/ir.cpp @@ -235,15 +235,14 @@ static bool GMQCC_WARN vec_ir_value_find(std::vector &vec, const ir_ return false; } -static bool GMQCC_WARN vec_ir_block_find(ir_block **vec, ir_block *what, size_t *idx) +static bool GMQCC_WARN vec_ir_block_find(std::vector &vec, ir_block *what, size_t *idx) { - size_t i; - size_t len = vec_size(vec); - for (i = 0; i < len; ++i) { - if (vec[i] == what) { - if (idx) *idx = i; - return true; - } + for (auto &it : vec) { + if (it != what) + continue; + if (idx) + *idx = &it - &vec[0]; + return true; } return false; } @@ -433,7 +432,7 @@ static bool ir_function_pass_peephole(ir_function *self) { for (auto& bp : self->m_blocks) { ir_block *block = bp.get(); - for (size_t i = 0; i < vec_size(block->m_instr); ++i) { + for (size_t i = 0; i < block->m_instr.size(); ++i) { ir_instr *inst; inst = block->m_instr[i]; @@ -481,7 +480,7 @@ static bool ir_function_pass_peephole(ir_function *self) ++opts_optimizationcount[OPTIM_PEEPHOLE]; (void)!ir_instr_op(oper, 0, store->_m_ops[0], true); - vec_remove(block->m_instr, i, 1); + block->m_instr.erase(block->m_instr.begin() + i); delete store; } else if (inst->m_opcode == VINSTR_COND) @@ -515,15 +514,15 @@ static bool ir_function_pass_peephole(ir_function *self) (void)!ir_instr_op(inst, 0, inot->_m_ops[1], false); /* remove NOT */ tmp = inot->m_owner; - for (inotid = 0; inotid < vec_size(tmp->m_instr); ++inotid) { + for (inotid = 0; inotid < tmp->m_instr.size(); ++inotid) { if (tmp->m_instr[inotid] == inot) break; } - if (inotid >= vec_size(tmp->m_instr)) { + if (inotid >= tmp->m_instr.size()) { compile_error(inst->m_context, "sanity-check failed: failed to find instruction to optimize out"); return false; } - vec_remove(tmp->m_instr, inotid, 1); + tmp->m_instr.erase(tmp->m_instr.begin() + inotid); delete inot; /* swap ontrue/onfalse */ tmp = inst->m_bops[0]; @@ -548,14 +547,14 @@ static bool ir_function_pass_tailrecursion(ir_function *self) ir_value *funcval; ir_instr *ret, *call, *store = nullptr; - if (!block->m_final || vec_size(block->m_instr) < 2) + if (!block->m_final || block->m_instr.size() < 2) continue; - ret = block->m_instr[vec_size(block->m_instr)-1]; + ret = block->m_instr.back(); if (ret->m_opcode != INSTR_DONE && ret->m_opcode != INSTR_RETURN) continue; - call = block->m_instr[vec_size(block->m_instr)-2]; + call = block->m_instr[block->m_instr.size()-2]; if (call->m_opcode >= INSTR_STORE_F && call->m_opcode <= INSTR_STORE_FNC) { /* account for the unoptimized * CALL @@ -563,11 +562,11 @@ static bool ir_function_pass_tailrecursion(ir_function *self) * RETURN %tmp * version */ - if (vec_size(block->m_instr) < 3) + if (block->m_instr.size() < 3) continue; store = call; - call = block->m_instr[vec_size(block->m_instr)-3]; + call = block->m_instr[block->m_instr.size()-3]; } if (call->m_opcode < INSTR_CALL0 || call->m_opcode > INSTR_CALL8) @@ -581,7 +580,7 @@ static bool ir_function_pass_tailrecursion(ir_function *self) { ++opts_optimizationcount[OPTIM_PEEPHOLE]; call->_m_ops[0] = store->_m_ops[0]; - vec_remove(block->m_instr, vec_size(block->m_instr) - 2, 1); + block->m_instr.erase(block->m_instr.end()-2); delete store; } else @@ -602,7 +601,7 @@ static bool ir_function_pass_tailrecursion(ir_function *self) continue; ++opts_optimizationcount[OPTIM_TAIL_RECURSION]; - vec_shrinkby(block->m_instr, 2); + block->m_instr.erase(block->m_instr.end()-2, block->m_instr.end()); block->m_final = false; /* open it back up */ @@ -631,6 +630,36 @@ bool ir_function_finalize(ir_function *self) if (self->m_builtin) return true; + for (auto& lp : self->m_locals) { + ir_value *v = lp.get(); + if (v->m_reads.empty() && v->m_writes.size() && !(v->m_flags & IR_FLAG_NOREF)) { + // if it's a vector check to ensure all it's members are unused before + // claiming it's unused, otherwise skip the vector entierly + if (v->m_vtype == TYPE_VECTOR) + { + size_t mask = (1 << 3) - 1, bits = 0; + for (size_t i = 0; i < 3; i++) + if (!v->m_members[i] || (v->m_members[i]->m_reads.empty() + && v->m_members[i]->m_writes.size())) + bits |= (1 << i); + // all components are unused so just report the vector + if (bits == mask && irwarning(v->m_context, WARN_UNUSED_VARIABLE, + "unused variable: `%s`", v->m_name.c_str())) + return false; + else if (bits != mask) + // individual components are unused so mention them + for (size_t i = 0; i < 3; i++) + if ((bits & (1 << i)) + && irwarning(v->m_context, WARN_UNUSED_COMPONENT, + "unused vector component: `%s.%c`", v->m_name.c_str(), "xyz"[i])) + return false; + } + // just a standard variable + else if (irwarning(v->m_context, WARN_UNUSED_VARIABLE, + "unused variable: `%s`", v->m_name.c_str())) return false; + } + } + if (OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) { if (!ir_function_pass_peephole(self)) { irerror(self->m_context, "generic optimization pass broke something in `%s`", self->m_name.c_str()); @@ -713,19 +742,15 @@ ir_block::ir_block(ir_function* owner, const std::string& name) ir_block::~ir_block() { - for (size_t i = 0; i != vec_size(m_instr); ++i) - delete m_instr[i]; - vec_free(m_instr); - vec_free(m_entries); - vec_free(m_exits); + for (auto &i : m_instr) + delete i; } static void ir_block_delete_quick(ir_block* self) { - size_t i; - for (i = 0; i != vec_size(self->m_instr); ++i) - ir_instr_delete_quick(self->m_instr[i]); - vec_free(self->m_instr); + for (auto &i : self->m_instr) + ir_instr_delete_quick(i); + self->m_instr.clear(); delete self; } @@ -1236,7 +1261,7 @@ bool ir_block_create_store_op(ir_block *self, lex_ctx_t ctx, int op, ir_value *t delete in; return false; } - vec_push(self->m_instr, in); + self->m_instr.push_back(in); return true; } @@ -1256,7 +1281,7 @@ bool ir_block_create_state_op(ir_block *self, lex_ctx_t ctx, ir_value *frame, ir delete in; return false; } - vec_push(self->m_instr, in); + self->m_instr.push_back(in); return true; } @@ -1325,7 +1350,7 @@ bool ir_block_create_return(ir_block *self, lex_ctx_t ctx, ir_value *v) return false; } - vec_push(self->m_instr, in); + self->m_instr.push_back(in); return true; } @@ -1349,12 +1374,12 @@ bool ir_block_create_if(ir_block *self, lex_ctx_t ctx, ir_value *v, in->m_bops[0] = ontrue; in->m_bops[1] = onfalse; - vec_push(self->m_instr, in); + self->m_instr.push_back(in); - vec_push(self->m_exits, ontrue); - vec_push(self->m_exits, onfalse); - vec_push(ontrue->m_entries, self); - vec_push(onfalse->m_entries, self); + self->m_exits.push_back(ontrue); + self->m_exits.push_back(onfalse); + ontrue->m_entries.push_back(self); + onfalse->m_entries.push_back(self); return true; } @@ -1369,10 +1394,10 @@ bool ir_block_create_jump(ir_block *self, lex_ctx_t ctx, ir_block *to) return false; in->m_bops[0] = to; - vec_push(self->m_instr, in); + self->m_instr.push_back(in); - vec_push(self->m_exits, to); - vec_push(to->m_entries, self); + self->m_exits.push_back(to); + to->m_entries.push_back(self); return true; } @@ -1400,7 +1425,7 @@ ir_instr* ir_block_create_phi(ir_block *self, lex_ctx_t ctx, const char *label, delete in; return nullptr; } - vec_push(self->m_instr, in); + self->m_instr.push_back(in); return in; } @@ -1452,7 +1477,7 @@ ir_instr* ir_block_create_call(ir_block *self, lex_ctx_t ctx, const char *label, delete out; return nullptr; } - vec_push(self->m_instr, in); + self->m_instr.push_back(in); /* if (noreturn) { if (!ir_block_create_return(self, ctx, nullptr)) { @@ -1619,7 +1644,7 @@ ir_value* ir_block_create_unary(ir_block *self, lex_ctx_t ctx, case VINSTR_NEG_F: return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_F, nullptr, operand, ot); case VINSTR_NEG_V: - return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, nullptr, operand, TYPE_VECTOR); + return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, self->m_owner->m_owner->m_nil, operand, TYPE_VECTOR); default: ot = operand->m_vtype; @@ -1656,7 +1681,7 @@ static ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx_t ctx, co goto on_error; } - vec_push(self->m_instr, instr); + self->m_instr.push_back(instr); return out; on_error: @@ -1730,13 +1755,13 @@ static bool ir_block_naive_phi(ir_block *self) * to a list so we don't need to loop through blocks * - anyway: "don't optimize YET" */ - for (i = 0; i < vec_size(self->m_instr); ++i) + for (i = 0; i < self->m_instr.size(); ++i) { ir_instr *instr = self->m_instr[i]; if (instr->m_opcode != VINSTR_PHI) continue; - vec_remove(self->m_instr, i, 1); + self->m_instr.erase(self->m_instr.begin()+i); --i; /* NOTE: i+1 below */ for (auto &it : instr->m_phi) { @@ -1748,14 +1773,14 @@ static bool ir_block_naive_phi(ir_block *self) return false; } else { /* force a move instruction */ - ir_instr *prevjump = vec_last(b->m_instr); - vec_pop(b->m_instr); + ir_instr *prevjump = b->m_instr.back(); + b->m_instr.pop_back(); b->m_final = false; instr->_m_ops[0]->m_store = store_global; if (!ir_block_create_store(b, instr->m_context, instr->_m_ops[0], v)) return false; instr->_m_ops[0]->m_store = store_value; - vec_push(b->m_instr, prevjump); + b->m_instr.push_back(prevjump); b->m_final = true; } } @@ -1777,12 +1802,9 @@ static bool ir_block_naive_phi(ir_block *self) */ static void ir_block_enumerate(ir_block *self, size_t *_eid) { - size_t i; size_t eid = *_eid; - for (i = 0; i < vec_size(self->m_instr); ++i) - { - self->m_instr[i]->m_eid = eid++; - } + for (auto &i : self->m_instr) + i->m_eid = eid++; *_eid = eid; } @@ -1815,10 +1837,10 @@ void ir_function_enumerate(ir_function *self) * This is the counterpart to register-allocation in register machines. */ struct function_allocator { - ir_value **locals; - size_t *sizes; - size_t *positions; - bool *unique; + std::vector> locals; + std::vector sizes; + std::vector positions; + std::vector unique; }; static bool function_allocator_alloc(function_allocator *alloc, ir_value *var) @@ -1826,7 +1848,7 @@ static bool function_allocator_alloc(function_allocator *alloc, ir_value *var) ir_value *slot; size_t vsize = var->size(); - var->m_code.local = vec_size(alloc->locals); + var->m_code.local = alloc->locals.size(); slot = new ir_value("reg", store_global, var->m_vtype); if (!slot) @@ -1835,9 +1857,9 @@ static bool function_allocator_alloc(function_allocator *alloc, ir_value *var) if (!slot->mergeLife(var)) goto localerror; - vec_push(alloc->locals, slot); - vec_push(alloc->sizes, vsize); - vec_push(alloc->unique, var->m_unique_life); + alloc->locals.emplace_back(slot); + alloc->sizes.push_back(vsize); + alloc->unique.push_back(var->m_unique_life); return true; @@ -1849,23 +1871,22 @@ localerror: static bool ir_function_allocator_assign(ir_function *self, function_allocator *alloc, ir_value *v) { size_t a; - ir_value *slot; if (v->m_unique_life) return function_allocator_alloc(alloc, v); - for (a = 0; a < vec_size(alloc->locals); ++a) + for (a = 0; a < alloc->locals.size(); ++a) { /* if it's reserved for a unique liferange: skip */ if (alloc->unique[a]) continue; - slot = alloc->locals[a]; + ir_value *slot = alloc->locals[a].get(); /* never resize parameters * will be required later when overlapping temps + locals */ - if (a < vec_size(self->m_params) && + if (a < self->m_params.size() && alloc->sizes[a] < v->size()) { continue; @@ -1884,7 +1905,7 @@ static bool ir_function_allocator_assign(ir_function *self, function_allocator * v->m_code.local = a; return true; } - if (a >= vec_size(alloc->locals)) { + if (a >= alloc->locals.size()) { if (!function_allocator_alloc(alloc, v)) return false; } @@ -1893,7 +1914,6 @@ static bool ir_function_allocator_assign(ir_function *self, function_allocator * bool ir_function_allocate_locals(ir_function *self) { - bool retval = true; size_t pos; bool opt_gt = OPTS_OPTIMIZATION(OPTIM_GLOBAL_TEMPS); @@ -1902,15 +1922,6 @@ bool ir_function_allocate_locals(ir_function *self) if (self->m_locals.empty() && self->m_values.empty()) return true; - globalloc.locals = nullptr; - globalloc.sizes = nullptr; - globalloc.positions = nullptr; - globalloc.unique = nullptr; - lockalloc.locals = nullptr; - lockalloc.sizes = nullptr; - lockalloc.positions = nullptr; - lockalloc.unique = nullptr; - size_t i; for (i = 0; i < self->m_locals.size(); ++i) { @@ -1919,12 +1930,12 @@ bool ir_function_allocate_locals(ir_function *self) v->m_locked = true; v->m_unique_life = true; } - else if (i >= vec_size(self->m_params)) + else if (i >= self->m_params.size()) break; else v->m_locked = true; /* lock parameters locals */ if (!function_allocator_alloc((v->m_locked || !opt_gt ? &lockalloc : &globalloc), v)) - goto error; + return false; } for (; i < self->m_locals.size(); ++i) { @@ -1932,7 +1943,7 @@ bool ir_function_allocate_locals(ir_function *self) if (v->m_life.empty()) continue; if (!ir_function_allocator_assign(self, (v->m_locked || !opt_gt ? &lockalloc : &globalloc), v)) - goto error; + return false; } /* Allocate a slot for any value that still exists */ @@ -1958,7 +1969,7 @@ bool ir_function_allocate_locals(ir_function *self) ir_instr *call = v->m_reads[0]; if (!vec_ir_value_find(call->m_params, v, ¶m)) { irerror(call->m_context, "internal error: unlocked parameter %s not found", v->m_name.c_str()); - goto error; + return false; } ++opts_optimizationcount[OPTIM_CALL_STORES]; v->m_callparam = true; @@ -1992,33 +2003,33 @@ bool ir_function_allocate_locals(ir_function *self) } if (!ir_function_allocator_assign(self, (v->m_locked || !opt_gt ? &lockalloc : &globalloc), v)) - goto error; + return false; } - if (!lockalloc.sizes && !globalloc.sizes) { - goto cleanup; - } - vec_push(lockalloc.positions, 0); - vec_push(globalloc.positions, 0); + if (lockalloc.sizes.empty() && globalloc.sizes.empty()) + return true; + + lockalloc.positions.push_back(0); + globalloc.positions.push_back(0); /* Adjust slot positions based on sizes */ - if (lockalloc.sizes) { - pos = (vec_size(lockalloc.sizes) ? lockalloc.positions[0] : 0); - for (i = 1; i < vec_size(lockalloc.sizes); ++i) + if (!lockalloc.sizes.empty()) { + pos = (lockalloc.sizes.size() ? lockalloc.positions[0] : 0); + for (i = 1; i < lockalloc.sizes.size(); ++i) { pos = lockalloc.positions[i-1] + lockalloc.sizes[i-1]; - vec_push(lockalloc.positions, pos); + lockalloc.positions.push_back(pos); } - self->m_allocated_locals = pos + vec_last(lockalloc.sizes); + self->m_allocated_locals = pos + lockalloc.sizes.back(); } - if (globalloc.sizes) { - pos = (vec_size(globalloc.sizes) ? globalloc.positions[0] : 0); - for (i = 1; i < vec_size(globalloc.sizes); ++i) + if (!globalloc.sizes.empty()) { + pos = (globalloc.sizes.size() ? globalloc.positions[0] : 0); + for (i = 1; i < globalloc.sizes.size(); ++i) { pos = globalloc.positions[i-1] + globalloc.sizes[i-1]; - vec_push(globalloc.positions, pos); + globalloc.positions.push_back(pos); } - self->m_globaltemps = pos + vec_last(globalloc.sizes); + self->m_globaltemps = pos + globalloc.sizes.back(); } /* Locals need to know their new position */ @@ -2036,24 +2047,7 @@ bool ir_function_allocate_locals(ir_function *self) value->m_code.local = globalloc.positions[value->m_code.local]; } - goto cleanup; - -error: - retval = false; -cleanup: - for (i = 0; i < vec_size(lockalloc.locals); ++i) - delete lockalloc.locals[i]; - for (i = 0; i < vec_size(globalloc.locals); ++i) - delete globalloc.locals[i]; - vec_free(globalloc.unique); - vec_free(globalloc.locals); - vec_free(globalloc.sizes); - vec_free(globalloc.positions); - vec_free(lockalloc.unique); - vec_free(lockalloc.locals); - vec_free(lockalloc.sizes); - vec_free(lockalloc.positions); - return retval; + return true; } /* Get information about which operand @@ -2118,21 +2112,19 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed) { ir_instr *instr; ir_value *value; - size_t i, o, p, mem; + size_t i, o, mem; // bitmasks which operands are read from or written to size_t read, write; self->m_living.clear(); - p = vec_size(self->m_exits); - for (i = 0; i < p; ++i) { - ir_block *prev = self->m_exits[i]; + for (auto &prev : self->m_exits) { for (auto &it : prev->m_living) if (!vec_ir_value_find(self->m_living, it, nullptr)) self->m_living.push_back(it); } - i = vec_size(self->m_instr); + i = self->m_instr.size(); while (i) { --i; instr = self->m_instr[i]; @@ -2323,7 +2315,7 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed) bool ir_function_calculate_liferanges(ir_function *self) { /* parameters live at 0 */ - for (size_t i = 0; i < vec_size(self->m_params); ++i) + for (size_t i = 0; i < self->m_params.size(); ++i) if (!self->m_locals[i].get()->setAlive(0)) compile_error(self->m_context, "internal error: failed value-life merging"); @@ -2490,7 +2482,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc block->m_generated = true; block->m_code_start = code->statements.size(); - for (i = 0; i < vec_size(block->m_instr); ++i) + for (i = 0; i < block->m_instr.size(); ++i) { instr = block->m_instr[i]; @@ -3011,7 +3003,7 @@ bool ir_builder::generateGlobalFunction(ir_value *global) fun.name = global->m_code.name; fun.file = filestring(global->m_context.file); fun.profile = 0; /* always 0 */ - fun.nargs = vec_size(irfun->m_params); + fun.nargs = irfun->m_params.size(); if (fun.nargs > 8) fun.nargs = 8; @@ -3076,7 +3068,7 @@ static bool gen_function_extparam_copy(code_t *code, ir_function *self) { ir_builder *ir = self->m_owner; - size_t numparams = vec_size(self->m_params); + size_t numparams = self->m_params.size(); if (!numparams) return true; @@ -3112,7 +3104,7 @@ static bool gen_function_varargs_copy(code_t *code, ir_function *self) ir_value *ep; prog_section_statement_t stmt; - numparams = vec_size(self->m_params); + numparams = self->m_params.size(); if (!numparams) return true; @@ -3248,7 +3240,7 @@ bool ir_builder::generateGlobalFunctionCode(ir_value *global) return true; } -static void gen_vector_defs(code_t *code, prog_section_def_t def, const char *name) +static void gen_vector_defs(code_t *code, prog_section_def_t def, const char *name, int type) { char *component; size_t len, i; @@ -3256,7 +3248,7 @@ static void gen_vector_defs(code_t *code, prog_section_def_t def, const char *na if (!name || name[0] == '#' || OPTS_FLAG(SINGLE_VECTOR_DEFS)) return; - def.type = TYPE_FLOAT; + def.type = type; len = strlen(name); @@ -3356,9 +3348,9 @@ bool ir_builder::generateGlobal(ir_value *global, bool islocal) def.offset = global->codeAddress(); m_code->defs.push_back(def); if (global->m_vtype == TYPE_VECTOR) - gen_vector_defs(m_code.get(), def, global->m_name.c_str()); + gen_vector_defs(m_code.get(), def, global->m_name.c_str(), TYPE_FLOAT); else if (global->m_vtype == TYPE_FIELD && global->m_fieldtype == TYPE_VECTOR) - gen_vector_defs(m_code.get(), def, global->m_name.c_str()); + gen_vector_defs(m_code.get(), def, global->m_name.c_str(), TYPE_FIELD); return true; } } @@ -3400,7 +3392,7 @@ bool ir_builder::generateGlobal(ir_value *global, bool islocal) if (pushdef) { m_code->defs.push_back(def); if (global->m_fieldtype == TYPE_VECTOR) - gen_vector_defs(m_code.get(), def, global->m_name.c_str()); + gen_vector_defs(m_code.get(), def, global->m_name.c_str(), TYPE_FIELD); } return gen_global_field(m_code.get(), global); case TYPE_ENTITY: @@ -3466,7 +3458,7 @@ bool ir_builder::generateGlobal(ir_value *global, bool islocal) if (pushdef) { m_code->defs.push_back(def); def.type &= ~DEF_SAVEGLOBAL; - gen_vector_defs(m_code.get(), def, global->m_name.c_str()); + gen_vector_defs(m_code.get(), def, global->m_name.c_str(), TYPE_FLOAT); } return global->m_code.globaladdr >= 0; } @@ -3569,7 +3561,7 @@ static bool ir_builder_gen_field(ir_builder *self, ir_value *field) } if (field->m_fieldtype == TYPE_VECTOR) { - gen_vector_defs (self->m_code.get(), def, field->m_name.c_str()); + gen_vector_defs (self->m_code.get(), def, field->m_name.c_str(), TYPE_FIELD); gen_vector_fields(self->m_code.get(), fld, field->m_name.c_str()); } @@ -3953,14 +3945,13 @@ void ir_function_dump(ir_function *f, char *ind, void ir_block_dump(ir_block* b, char *ind, int (*oprintf)(const char*, ...)) { - size_t i; oprintf("%s:%s\n", ind, b->m_label.c_str()); util_strncat(ind, "\t", IND_BUFSZ-1); - if (b->m_instr && b->m_instr[0]) + if (!b->m_instr.empty() && b->m_instr[0]) oprintf("%s (%i) [entry]\n", ind, (int)(b->m_instr[0]->m_eid-1)); - for (i = 0; i < vec_size(b->m_instr); ++i) - ir_instr_dump(b->m_instr[i], ind, oprintf); + for (auto &i : b->m_instr) + ir_instr_dump(i, ind, oprintf); ind[strlen(ind)-1] = 0; } diff --git a/ir.h b/ir.h index 9fa8ab5..42f6ce5 100644 --- a/ir.h +++ b/ir.h @@ -27,8 +27,8 @@ enum { IR_FLAG_INCLUDE_DEF = 1 << 3, IR_FLAG_ERASABLE = 1 << 4, IR_FLAG_BLOCK_COVERAGE = 1 << 5, - - IR_FLAG_SPLIT_VECTOR = 1 << 6, + IR_FLAG_NOREF = 1 << 6, + IR_FLAG_SPLIT_VECTOR = 1 << 7, IR_FLAG_LAST, IR_FLAG_MASK_NO_OVERLAP = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED), @@ -148,9 +148,9 @@ struct ir_block { lex_ctx_t m_context; bool m_final = false; /* once a jump is added we're done */ - ir_instr **m_instr = nullptr; - ir_block **m_entries = nullptr; - ir_block **m_exits = nullptr; + std::vector m_instr; + std::vector m_entries; + std::vector m_exits; std::vector m_living; /* For the temp-allocation */ @@ -201,11 +201,11 @@ struct ir_function { ir_builder *m_owner; - std::string m_name; - qc_type m_outtype; - int *m_params = nullptr; - ir_flag_t m_flags = 0; - int m_builtin = 0; + std::string m_name; + qc_type m_outtype; + std::vector m_params; + ir_flag_t m_flags = 0; + int m_builtin = 0; std::vector> m_blocks; diff --git a/main.cpp b/main.cpp index 449067d..76c1d31 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include "gmqcc.h" #include "lexer.h" +#include "parser.h" /* TODO: cleanup this whole file .. it's a fuckign mess */ @@ -736,7 +737,7 @@ cleanup: vec_free(ppems); if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) - if(parser) parser_cleanup(parser); + delete parser; /* free allocated option strings */ for (itr = 0; itr < OPTION_COUNT; itr++) diff --git a/opts.cpp b/opts.cpp index b2517a0..c35fd73 100644 --- a/opts.cpp +++ b/opts.cpp @@ -149,9 +149,9 @@ void opts_set(uint32_t *flags, size_t idx, bool on) { LONGBIT_SET(lb, idx); if (on) - flags[lb.idx] |= (1<<(lb.bit)); + flags[lb.idx] |= (1u<<(lb.bit)); else - flags[lb.idx] &= ~(1<<(lb.bit)); + flags[lb.idx] &= ~(1u<<(lb.bit)); } void opts_setoptimlevel(unsigned int level) { diff --git a/opts.def b/opts.def index f5ce0af..2dc4fb7 100644 --- a/opts.def +++ b/opts.def @@ -44,6 +44,7 @@ GMQCC_DEFINE_FLAG(UNINITIALIZED_GLOBAL) GMQCC_DEFINE_FLAG(DEBUG) GMQCC_DEFINE_FLAG(UNUSED_VARIABLE) + GMQCC_DEFINE_FLAG(UNUSED_COMPONENT) GMQCC_DEFINE_FLAG(USED_UNINITIALIZED) GMQCC_DEFINE_FLAG(UNKNOWN_CONTROL_SEQUENCE) GMQCC_DEFINE_FLAG(EXTENSIONS) diff --git a/parser.cpp b/parser.cpp index 7bb2a10..ddde654 100644 --- a/parser.cpp +++ b/parser.cpp @@ -139,7 +139,7 @@ static ast_expression* parser_find_local(parser_t *parser, const char *name, siz hash = util_hthash(parser->htglobals, name); *isparam = false; - for (i = vec_size(parser->variables); i > upto;) { + for (i = parser->variables.size(); i > upto;) { --i; if ( (e = (ast_expression*)util_htgeth(parser->variables[i], name, hash)) ) return e; @@ -171,7 +171,7 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t ast_value *e; hash = util_hthash(parser->typedefs[0], name); - for (i = vec_size(parser->typedefs); i > upto;) { + for (i = parser->typedefs.size(); i > upto;) { --i; if ( (e = (ast_value*)util_htgeth(parser->typedefs[i], name, hash)) ) return e; @@ -293,6 +293,23 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out) return true; } +static int store_op_for(ast_expression* expr) +{ + if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) && expr->m_vtype == TYPE_FIELD && expr->m_next->m_vtype == TYPE_VECTOR) { + return type_storep_instr[TYPE_VECTOR]; + } + + if (ast_istype(expr, ast_member) && ast_istype(((ast_member*)expr)->m_owner, ast_entfield)) { + return type_storep_instr[expr->m_vtype]; + } + + if (ast_istype(expr, ast_entfield)) { + return type_storep_instr[expr->m_vtype]; + } + + return type_store_instr[expr->m_vtype]; +} + static bool check_write_to(lex_ctx_t ctx, ast_expression *expr) { if (ast_istype(expr, ast_value)) { @@ -728,6 +745,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) case opid2('|','|'): generated_op += 1; /* INSTR_OR */ + [[fallthrough]]; case opid2('&','&'): generated_op += INSTR_AND; if (!(out = parser->m_fold.op(op, exprs))) { @@ -857,10 +875,13 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) case opid1('>'): generated_op += 1; /* INSTR_GT */ + [[fallthrough]]; case opid1('<'): generated_op += 1; /* INSTR_LT */ + [[fallthrough]]; case opid2('>', '='): generated_op += 1; /* INSTR_GE */ + [[fallthrough]]; case opid2('<', '='): generated_op += INSTR_LE; if (NotSameType(TYPE_FLOAT)) { @@ -896,14 +917,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) case opid1('='): if (ast_istype(exprs[0], ast_entfield)) { ast_expression *field = ((ast_entfield*)exprs[0])->m_field; - if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) && - exprs[0]->m_vtype == TYPE_FIELD && - exprs[0]->m_next->m_vtype == TYPE_VECTOR) - { - assignop = type_storep_instr[TYPE_VECTOR]; - } - else - assignop = type_storep_instr[exprs[0]->m_vtype]; + assignop = store_op_for(exprs[0]); if (assignop == VINSTR_END || !field->m_next->compareType(*exprs[1])) { ast_type_to_string(field->m_next, ty1, sizeof(ty1)); @@ -921,15 +935,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } else { - if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) && - exprs[0]->m_vtype == TYPE_FIELD && - exprs[0]->m_next->m_vtype == TYPE_VECTOR) - { - assignop = type_store_instr[TYPE_VECTOR]; - } - else { - assignop = type_store_instr[exprs[0]->m_vtype]; - } + assignop = store_op_for(exprs[0]); if (assignop == VINSTR_END) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); @@ -1024,10 +1030,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } (void)check_write_to(ctx, exprs[0]); - if (ast_istype(exprs[0], ast_entfield)) - assignop = type_storep_instr[exprs[0]->m_vtype]; - else - assignop = type_store_instr[exprs[0]->m_vtype]; + assignop = store_op_for(exprs[0]); switch (exprs[0]->m_vtype) { case TYPE_FLOAT: out = new ast_binstore(ctx, assignop, @@ -1059,10 +1062,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } (void)check_write_to(ctx, exprs[0]); - if (ast_istype(exprs[0], ast_entfield)) - assignop = type_storep_instr[exprs[0]->m_vtype]; - else - assignop = type_store_instr[exprs[0]->m_vtype]; + assignop = store_op_for(exprs[0]); switch (exprs[0]->m_vtype) { case TYPE_FLOAT: out = new ast_binstore(ctx, assignop, @@ -1103,10 +1103,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } (void)check_write_to(ctx, exprs[0]); - if (ast_istype(exprs[0], ast_entfield)) - assignop = type_storep_instr[exprs[0]->m_vtype]; - else - assignop = type_store_instr[exprs[0]->m_vtype]; + assignop = store_op_for(exprs[0]); if (exprs[0]->m_vtype == TYPE_FLOAT) out = new ast_binstore(ctx, assignop, (op->id == opid2('^','=') ? VINSTR_BITXOR : op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR), @@ -1128,10 +1125,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ty1, ty2); return false; } - if (ast_istype(exprs[0], ast_entfield)) - assignop = type_storep_instr[exprs[0]->m_vtype]; - else - assignop = type_store_instr[exprs[0]->m_vtype]; + assignop = store_op_for(exprs[0]); if (exprs[0]->m_vtype == TYPE_FLOAT) out = fold::binary(ctx, INSTR_BITAND, exprs[0], exprs[1]); else @@ -1263,14 +1257,14 @@ static bool parser_close_call(parser_t *parser, shunt *sy) * intrinsic call and just evaluate it i.e constant fold it. */ if (fold && ast_istype(fun, ast_value) && ((ast_value*)fun)->m_intrinsic) { - ast_expression **exprs = nullptr; + std::vector exprs; ast_expression *foldval = nullptr; + exprs.reserve(paramcount); for (i = 0; i < paramcount; i++) - vec_push(exprs, sy->out[fid+1 + i].out); + exprs.push_back(sy->out[fid+1 + i].out); - if (!(foldval = parser->m_intrin.do_fold((ast_value*)fun, exprs))) { - vec_free(exprs); + if (!(foldval = parser->m_intrin.do_fold((ast_value*)fun, exprs.data()))) { goto fold_leave; } @@ -1280,7 +1274,6 @@ static bool parser_close_call(parser_t *parser, shunt *sy) */ sy->out[fid] = syexp(foldval->m_context, foldval); sy->out.erase(sy->out.end() - paramcount, sy->out.end()); - vec_free(exprs); return true; } @@ -1668,13 +1661,16 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) } else { - if (ast_istype(var, ast_value)) { - ((ast_value*)var)->m_uses++; + // promote these to norefs + if (ast_istype(var, ast_value)) + { + ((ast_value *)var)->m_flags |= AST_FLAG_NOREF; } - else if (ast_istype(var, ast_member)) { - ast_member *mem = (ast_member*)var; + else if (ast_istype(var, ast_member)) + { + ast_member *mem = (ast_member *)var; if (ast_istype(mem->m_owner, ast_value)) - ((ast_value*)(mem->m_owner))->m_uses++; + ((ast_value *)mem->m_owner)->m_flags |= AST_FLAG_NOREF; } } sy->out.push_back(syexp(parser_ctx(parser), var)); @@ -1993,11 +1989,11 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool static void parser_enterblock(parser_t *parser) { - vec_push(parser->variables, util_htnew(PARSER_HT_SIZE)); - vec_push(parser->_blocklocals, vec_size(parser->_locals)); - vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE)); - vec_push(parser->_blocktypedefs, vec_size(parser->_typedefs)); - vec_push(parser->_block_ctx, parser_ctx(parser)); + parser->variables.push_back(util_htnew(PARSER_HT_SIZE)); + parser->_blocklocals.push_back(parser->_locals.size()); + parser->typedefs.push_back(util_htnew(TYPEDEF_HT_SIZE)); + parser->_blocktypedefs.push_back(parser->_typedefs.size()); + parser->_block_ctx.push_back(parser_ctx(parser)); } static bool parser_leaveblock(parser_t *parser) @@ -2005,48 +2001,37 @@ static bool parser_leaveblock(parser_t *parser) bool rv = true; size_t locals, typedefs; - if (vec_size(parser->variables) <= PARSER_HT_LOCALS) { + if (parser->variables.size() <= PARSER_HT_LOCALS) { parseerror(parser, "internal error: parser_leaveblock with no block"); return false; } - util_htdel(vec_last(parser->variables)); + util_htdel(parser->variables.back()); - vec_pop(parser->variables); - if (!vec_size(parser->_blocklocals)) { + parser->variables.pop_back(); + if (!parser->_blocklocals.size()) { parseerror(parser, "internal error: parser_leaveblock with no block (2)"); return false; } - locals = vec_last(parser->_blocklocals); - vec_pop(parser->_blocklocals); - while (vec_size(parser->_locals) != locals) { - ast_expression *e = vec_last(parser->_locals); - ast_value *v = (ast_value*)e; - vec_pop(parser->_locals); - if (ast_istype(e, ast_value) && !v->m_uses) { - if (compile_warning(v->m_context, WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->m_name)) - rv = false; - } - } + locals = parser->_blocklocals.back(); + parser->_blocklocals.pop_back(); + parser->_locals.resize(locals); - typedefs = vec_last(parser->_blocktypedefs); - while (vec_size(parser->_typedefs) != typedefs) { - delete vec_last(parser->_typedefs); - vec_pop(parser->_typedefs); - } - util_htdel(vec_last(parser->typedefs)); - vec_pop(parser->typedefs); + typedefs = parser->_blocktypedefs.back(); + parser->_typedefs.resize(typedefs); + util_htdel(parser->typedefs.back()); + parser->typedefs.pop_back(); - vec_pop(parser->_block_ctx); + parser->_block_ctx.pop_back(); return rv; } static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e) { - vec_push(parser->_locals, e); - util_htset(vec_last(parser->variables), name, (void*)e); + parser->_locals.push_back(e); + util_htset(parser->variables.back(), name, (void*)e); } static void parser_addlocal(parser_t *parser, const std::string &name, ast_expression *e) { return parser_addlocal(parser, name.c_str(), e); @@ -2634,6 +2619,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou retval = new ast_value(ctx, "#LOCAL_RETURN", TYPE_VOID); retval->adoptType(*expected->m_next); parser->function->m_return_value = retval; + parser->function->m_return_value->m_flags |= AST_FLAG_NOREF; } if (!exp->compareType(*retval)) { @@ -3120,6 +3106,52 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * return false; } swcase.m_value = parse_expression_leave(parser, false, false, false); + + if (!operand->compareType(*swcase.m_value)) { + char ty1[1024]; + char ty2[1024]; + + ast_type_to_string(swcase.m_value, ty1, sizeof ty1); + ast_type_to_string(operand, ty2, sizeof ty2); + + auto fnLiteral = [](ast_expression *expression) -> char* { + if (!ast_istype(expression, ast_value)) + return nullptr; + ast_value *value = (ast_value *)expression; + if (!value->m_hasvalue) + return nullptr; + char *string = nullptr; + basic_value_t *constval = &value->m_constval; + switch (value->m_vtype) + { + case TYPE_FLOAT: + util_asprintf(&string, "%.2f", constval->vfloat); + return string; + case TYPE_VECTOR: + util_asprintf(&string, "'%.2f %.2f %.2f'", + constval->vvec.x, + constval->vvec.y, + constval->vvec.z); + return string; + case TYPE_STRING: + util_asprintf(&string, "\"%s\"", constval->vstring); + return string; + default: + break; + } + return nullptr; + }; + + char *literal = fnLiteral(swcase.m_value); + if (literal) + compile_error(parser_ctx(parser), "incompatible type `%s` for switch case `%s` expected `%s`", ty1, literal, ty2); + else + compile_error(parser_ctx(parser), "incompatible type `%s` for switch case expected `%s`", ty1, ty2); + mem_d(literal); + delete switchnode; + return false; + } + if (!swcase.m_value) { delete switchnode; parseerror(parser, "expected expression for case"); @@ -3581,9 +3613,9 @@ static bool parse_enum(parser_t *parser) bool flag = false; bool reverse = false; qcfloat_t num = 0; - ast_value **values = nullptr; ast_value *var = nullptr; ast_value *asvalue; + std::vector values; ast_expression *old; @@ -3625,7 +3657,7 @@ static bool parse_enum(parser_t *parser) break; } parseerror(parser, "expected identifier or `}`"); - goto onerror; + return false; } old = parser_find_field(parser, parser_tokval(parser)); @@ -3634,11 +3666,11 @@ static bool parse_enum(parser_t *parser) if (old) { parseerror(parser, "value `%s` has already been declared here: %s:%i", parser_tokval(parser), old->m_context.file, old->m_context.line); - goto onerror; + return false; } var = new ast_value(parser_ctx(parser), parser_tokval(parser), TYPE_FLOAT); - vec_push(values, var); + values.push_back(var); var->m_cvq = CV_CONST; var->m_hasvalue = true; @@ -3648,7 +3680,7 @@ static bool parse_enum(parser_t *parser) if (!parser_next(parser)) { parseerror(parser, "expected `=`, `}` or comma after identifier"); - goto onerror; + return false; } if (parser->tok == ',') @@ -3657,12 +3689,12 @@ static bool parse_enum(parser_t *parser) break; if (parser->tok != '=') { parseerror(parser, "expected `=`, `}` or comma after identifier"); - goto onerror; + return false; } if (!parser_next(parser)) { parseerror(parser, "expected expression after `=`"); - goto onerror; + return false; } /* We got a value! */ @@ -3670,7 +3702,7 @@ static bool parse_enum(parser_t *parser) asvalue = (ast_value*)old; if (!ast_istype(old, ast_value) || asvalue->m_cvq != CV_CONST || !asvalue->m_hasvalue) { compile_error(var->m_context, "constant value or expression expected"); - goto onerror; + return false; } num = (var->m_constval.vfloat = asvalue->m_constval.vfloat) + 1; @@ -3678,38 +3710,33 @@ static bool parse_enum(parser_t *parser) break; if (parser->tok != ',') { parseerror(parser, "expected `}` or comma after expression"); - goto onerror; + return false; } } /* patch them all (for reversed attribute) */ if (reverse) { size_t i; - for (i = 0; i < vec_size(values); i++) - values[i]->m_constval.vfloat = vec_size(values) - i - 1; + for (i = 0; i < values.size(); i++) + values[i]->m_constval.vfloat = values.size() - i - 1; } if (parser->tok != '}') { parseerror(parser, "internal error: breaking without `}`"); - goto onerror; + return false; } if (!parser_next(parser) || parser->tok != ';') { parseerror(parser, "expected semicolon after enumeration"); - goto onerror; + return false; } if (!parser_next(parser)) { parseerror(parser, "parse error after enumeration"); - goto onerror; + return false; } - vec_free(values); return true; - -onerror: - vec_free(values); - return false; } static bool parse_block_into(parser_t *parser, ast_block *block) @@ -4128,7 +4155,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) parser->function = old; if (!parser_leaveblock(parser)) retval = false; - if (vec_size(parser->variables) != PARSER_HT_LOCALS) { + if (parser->variables.size() != PARSER_HT_LOCALS) { parseerror(parser, "internal error: local scopes left"); retval = false; } @@ -4947,15 +4974,15 @@ static bool parse_typedef(parser_t *parser) return false; } - if ( (oldtype = parser_find_typedef(parser, typevar->m_name, vec_last(parser->_blocktypedefs))) ) { + if ( (oldtype = parser_find_typedef(parser, typevar->m_name, parser->_blocktypedefs.back())) ) { parseerror(parser, "type `%s` has already been declared here: %s:%i", typevar->m_name, oldtype->m_context.file, oldtype->m_context.line); delete typevar; return false; } - vec_push(parser->_typedefs, typevar); - util_htset(vec_last(parser->typedefs), typevar->m_name.c_str(), typevar); + parser->_typedefs.emplace_back(typevar); + util_htset(parser->typedefs.back(), typevar->m_name.c_str(), typevar); if (parser->tok != ';') { parseerror(parser, "expected semicolon after typedef"); @@ -5187,12 +5214,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* Deal with end_sys_ vars */ was_end = false; if (var->m_name == "end_sys_globals") { - var->m_uses++; + var->m_flags |= AST_FLAG_NOREF; parser->crc_globals = parser->globals.size(); was_end = true; } else if (var->m_name == "end_sys_fields") { - var->m_uses++; + var->m_flags |= AST_FLAG_NOREF; parser->crc_fields = parser->fields.size(); was_end = true; } @@ -5322,7 +5349,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } else /* it's not a global */ { - old = parser_find_local(parser, var->m_name, vec_size(parser->variables)-1, &isparam); + old = parser_find_local(parser, var->m_name, parser->variables.size()-1, &isparam); if (old && !isparam) { parseerror(parser, "local `%s` already declared here: %s:%i", var->m_name, old->m_context.file, (int)old->m_context.line); @@ -5352,9 +5379,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } } - /* in a noref section we simply bump the usecount */ if (noref || parser->noref) - var->m_uses++; + var->m_flags |= AST_FLAG_NOREF; /* Part 2: * Create the global/local, and deal with vector types. @@ -5450,7 +5476,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield prefix_len = defname.length(); // Add it to the local scope - util_htset(vec_last(parser->variables), var->m_name.c_str(), (void*)var); + util_htset(parser->variables.back(), var->m_name.c_str(), (void*)var); // now rename the global defname.append(var->m_name); @@ -5480,8 +5506,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield if (isvector) { defname.erase(prefix_len); for (i = 0; i < 3; ++i) { - util_htset(vec_last(parser->variables), me[i]->m_name.c_str(), (void*)(me[i])); - me[i]->m_name = move(defname + me[i]->m_name); + util_htset(parser->variables.back(), me[i]->m_name.c_str(), (void*)(me[i])); + me[i]->m_name = defname + me[i]->m_name; parser->globals.push_back(me[i]); } } @@ -5718,15 +5744,15 @@ skipvar: /* remove it from the current locals */ if (isvector) { for (i = 0; i < 3; ++i) { - vec_pop(parser->_locals); + parser->_locals.pop_back(); localblock->m_collect.pop_back(); } } /* do sanity checking, this function really needs refactoring */ - if (vec_last(parser->_locals) != var) + if (parser->_locals.back() != var) parseerror(parser, "internal error: unexpected change in local variable handling"); else - vec_pop(parser->_locals); + parser->_locals.pop_back(); if (localblock->m_locals.back() != var) parseerror(parser, "internal error: unexpected change in local variable handling (2)"); else @@ -5755,7 +5781,10 @@ skipvar: var->m_cvq = CV_CONST; } if (cval == parser->nil) + { var->m_flags |= AST_FLAG_INITIALIZED; + var->m_flags |= AST_FLAG_NOREF; + } else { var->m_hasvalue = true; @@ -5977,21 +6006,72 @@ static void generate_checksum(parser_t *parser, ir_builder *ir) ir->m_code->crc = crc; } +parser_t::parser_t() + : lex(nullptr) + , tok(0) + , ast_cleaned(false) + , translated(0) + , crc_globals(0) + , crc_fields(0) + , function(nullptr) + , aliases(util_htnew(PARSER_HT_SIZE)) + , htfields(util_htnew(PARSER_HT_SIZE)) + , htglobals(util_htnew(PARSER_HT_SIZE)) + , assign_op(nullptr) + , noref(false) + , max_param_count(1) + // finish initializing the rest of the parser before initializing + // m_fold and m_intrin with the parser passed along + , m_fold() + , m_intrin() +{ + variables.push_back(htfields); + variables.push_back(htglobals); + typedefs.push_back(util_htnew(TYPEDEF_HT_SIZE)); + _blocktypedefs.push_back(0); + + lex_ctx_t empty_ctx; + empty_ctx.file = ""; + empty_ctx.line = 0; + empty_ctx.column = 0; + nil = new ast_value(empty_ctx, "nil", TYPE_NIL); + nil->m_cvq = CV_CONST; + if (OPTS_FLAG(UNTYPED_NIL)) + util_htset(htglobals, "nil", (void*)nil); + + const_vec[0] = new ast_value(empty_ctx, "", TYPE_NOEXPR); + const_vec[1] = new ast_value(empty_ctx, "", TYPE_NOEXPR); + const_vec[2] = new ast_value(empty_ctx, "", TYPE_NOEXPR); + + if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) { + reserved_version = new ast_value(empty_ctx, "reserved:version", TYPE_STRING); + reserved_version->m_cvq = CV_CONST; + reserved_version->m_hasvalue = true; + reserved_version->m_flags |= AST_FLAG_INCLUDE_DEF; + reserved_version->m_flags |= AST_FLAG_NOREF; + reserved_version->m_constval.vstring = util_strdup(GMQCC_FULL_VERSION_STRING); + } else { + reserved_version = nullptr; + } + + m_fold = fold(this); + m_intrin = intrin(this); +} + +parser_t::~parser_t() +{ + remove_ast(); +} + parser_t *parser_create() { parser_t *parser; - lex_ctx_t empty_ctx; size_t i; - parser = (parser_t*)mem_a(sizeof(parser_t)); + parser = new parser_t; if (!parser) return nullptr; - memset(parser, 0, sizeof(*parser)); - - // TODO: remove - new (parser) parser_t(); - for (i = 0; i < operator_count; ++i) { if (operators[i].id == opid1('=')) { parser->assign_op = operators+i; @@ -6000,43 +6080,10 @@ parser_t *parser_create() } if (!parser->assign_op) { con_err("internal error: initializing parser: failed to find assign operator\n"); - mem_d(parser); + delete parser; return nullptr; } - vec_push(parser->variables, parser->htfields = util_htnew(PARSER_HT_SIZE)); - vec_push(parser->variables, parser->htglobals = util_htnew(PARSER_HT_SIZE)); - vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE)); - vec_push(parser->_blocktypedefs, 0); - - parser->aliases = util_htnew(PARSER_HT_SIZE); - - empty_ctx.file = ""; - empty_ctx.line = 0; - empty_ctx.column = 0; - parser->nil = new ast_value(empty_ctx, "nil", TYPE_NIL); - parser->nil->m_cvq = CV_CONST; - if (OPTS_FLAG(UNTYPED_NIL)) - util_htset(parser->htglobals, "nil", (void*)parser->nil); - - parser->max_param_count = 1; - - parser->const_vec[0] = new ast_value(empty_ctx, "", TYPE_NOEXPR); - parser->const_vec[1] = new ast_value(empty_ctx, "", TYPE_NOEXPR); - parser->const_vec[2] = new ast_value(empty_ctx, "", TYPE_NOEXPR); - - if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) { - parser->reserved_version = new ast_value(empty_ctx, "reserved:version", TYPE_STRING); - parser->reserved_version->m_cvq = CV_CONST; - parser->reserved_version->m_hasvalue = true; - parser->reserved_version->m_flags |= AST_FLAG_INCLUDE_DEF; - parser->reserved_version->m_constval.vstring = util_strdup(GMQCC_FULL_VERSION_STRING); - } else { - parser->reserved_version = nullptr; - } - - parser->m_fold = fold(parser); - parser->m_intrin = intrin(parser); return parser; } @@ -6092,54 +6139,42 @@ bool parser_compile_string(parser_t *parser, const char *name, const char *str, return parser_compile(parser); } -static void parser_remove_ast(parser_t *parser) +void parser_t::remove_ast() { - size_t i; - if (parser->ast_cleaned) + if (ast_cleaned) return; - parser->ast_cleaned = true; - for (auto &it : parser->accessors) { + ast_cleaned = true; + for (auto &it : accessors) { delete it->m_constval.vfunc; it->m_constval.vfunc = nullptr; delete it; } - for (auto &it : parser->functions) delete it; - for (auto &it : parser->globals) delete it; - for (auto &it : parser->fields) delete it; - - for (i = 0; i < vec_size(parser->variables); ++i) - util_htdel(parser->variables[i]); - vec_free(parser->variables); - vec_free(parser->_blocklocals); - vec_free(parser->_locals); + for (auto &it : functions) delete it; + for (auto &it : globals) delete it; + for (auto &it : fields) delete it; - for (i = 0; i < vec_size(parser->_typedefs); ++i) - delete parser->_typedefs[i]; - vec_free(parser->_typedefs); - for (i = 0; i < vec_size(parser->typedefs); ++i) - util_htdel(parser->typedefs[i]); - vec_free(parser->typedefs); - vec_free(parser->_blocktypedefs); + for (auto &it : variables) util_htdel(it); + variables.clear(); + _blocklocals.clear(); + _locals.clear(); - vec_free(parser->_block_ctx); + _typedefs.clear(); + for (auto &it : typedefs) util_htdel(it); + typedefs.clear(); + _blocktypedefs.clear(); - delete parser->nil; + _block_ctx.clear(); - delete parser->const_vec[0]; - delete parser->const_vec[1]; - delete parser->const_vec[2]; + delete nil; - if (parser->reserved_version) - delete parser->reserved_version; + delete const_vec[0]; + delete const_vec[1]; + delete const_vec[2]; - util_htdel(parser->aliases); -} + if (reserved_version) + delete reserved_version; -void parser_cleanup(parser_t *parser) -{ - parser_remove_ast(parser); - parser->~parser_t(); - mem_d(parser); + util_htdel(aliases); } static bool parser_set_coverage_func(parser_t *parser, ir_builder *ir) { @@ -6227,7 +6262,7 @@ bool parser_finish(parser_t *parser, const char *output) if (!ast_istype(it, ast_value)) continue; asvalue = (ast_value*)it; - if (!asvalue->m_uses && !asvalue->m_hasvalue && asvalue->m_vtype != TYPE_FUNCTION) { + if (!(asvalue->m_flags & AST_FLAG_NOREF) && asvalue->m_cvq != CV_CONST && asvalue->m_vtype != TYPE_FUNCTION) { retval = retval && !compile_warning(asvalue->m_context, WARN_UNUSED_VARIABLE, "unused global: `%s`", asvalue->m_name); } @@ -6323,13 +6358,17 @@ bool parser_finish(parser_t *parser, const char *output) return false; } } - parser_remove_ast(parser); + parser->remove_ast(); - if (compile_Werrors) { - con_out("*** there were warnings treated as errors\n"); - compile_show_werrors(); - retval = false; - } + auto fnCheckWErrors = [&retval]() { + if (compile_Werrors) { + con_out("*** there were warnings treated as errors\n"); + compile_show_werrors(); + retval = false; + } + }; + + fnCheckWErrors(); if (retval) { if (OPTS_OPTION_BOOL(OPTION_DUMPFIN)) @@ -6340,6 +6379,9 @@ bool parser_finish(parser_t *parser, const char *output) delete ir; return false; } + + // ir->generate can generate compiler warnings + fnCheckWErrors(); } delete ir; return retval; diff --git a/parser.h b/parser.h index a09238d..e5361a8 100644 --- a/parser.h +++ b/parser.h @@ -2,7 +2,7 @@ #define GMQCC_PARSER_HDR #include "gmqcc.h" #include "lexer.h" -//#include "ast.h" +#include "ast.h" #include "intrin.h" #include "fold.h" @@ -12,7 +12,10 @@ struct parser_t; #define parser_ctx(p) ((p)->lex->tok.ctx) struct parser_t { - parser_t() { } + parser_t(); + ~parser_t(); + + void remove_ast(); lex_file *lex; int tok; @@ -45,17 +48,17 @@ struct parser_t { std::vector continues; /* A list of hashtables for each scope */ - ht *variables; + std::vector variables; ht htfields; ht htglobals; - ht *typedefs; + std::vector typedefs; /* not to be used directly, we use the hash table */ - ast_expression **_locals; - size_t *_blocklocals; - ast_value **_typedefs; - size_t *_blocktypedefs; - lex_ctx_t *_block_ctx; + std::vector _locals; + std::vector _blocklocals; + std::vector> _typedefs; + std::vector _blocktypedefs; + std::vector _block_ctx; /* we store the '=' operator info */ const oper_info *assign_op; diff --git a/tests/defs.qh b/tests/defs.qh index f6ea408..cbe8c98 100644 --- a/tests/defs.qh +++ b/tests/defs.qh @@ -18,3 +18,4 @@ vector (vector vec) normalize = #12; float (float val) sqrt = #13; float (float val) floor = #14; float (float val1, float val2) pow = #15; +vector (string str) stov = #16; diff --git a/tests/fieldfuncs.tmpl b/tests/fieldfuncs.tmpl index 1199b9b..04e0b8d 100644 --- a/tests/fieldfuncs.tmpl +++ b/tests/fieldfuncs.tmpl @@ -1,6 +1,6 @@ I: fieldfuncs.qc D: test fields with functions -T: -compile +T: -execute C: -std=fte M: 42 M: 42 diff --git a/tests/memberbinop.qc b/tests/memberbinop.qc new file mode 100644 index 0000000..55f5aea --- /dev/null +++ b/tests/memberbinop.qc @@ -0,0 +1,15 @@ +.vector k; + +void t(entity e) +{ + e.k = '0 0 0'; + e.k.x += 2; + e.k.y += 4; + e.k.z += 6; +} + +void main() { + entity e = spawn(); + t(e); + print(vtos(e.k)); +} diff --git a/tests/memberbinop.tmpl b/tests/memberbinop.tmpl new file mode 100644 index 0000000..21abd20 --- /dev/null +++ b/tests/memberbinop.tmpl @@ -0,0 +1,6 @@ +I: memberbinop.qc +D: test member bin ops +T: -execute +C: -std=gmqcc +E: $null +M: '2 4 6' diff --git a/tests/vecfields.qc b/tests/vecfields.qc new file mode 100644 index 0000000..6cc053a --- /dev/null +++ b/tests/vecfields.qc @@ -0,0 +1,13 @@ +.vector v1; + +float set(entity e, float v) { + e.v1.y = v; + return e.v1.y; +} + +void main() { + entity e = spawn(); + e.v1 = '1 2 3'; + print(ftos(set(e, 42)), " => "); + print(vtos(e.v1), "\n"); +} diff --git a/tests/vecfields.tmpl b/tests/vecfields.tmpl new file mode 100644 index 0000000..d54ba70 --- /dev/null +++ b/tests/vecfields.tmpl @@ -0,0 +1,5 @@ +I: vecfields.qc +D: vector field member accesses +T: -execute +C: -std=gmqcc -fftepp +M: 42 => '1 42 3' diff --git a/tests/vecmath.qc b/tests/vecmath.qc new file mode 100644 index 0000000..5a656bf --- /dev/null +++ b/tests/vecmath.qc @@ -0,0 +1,5 @@ +void main(vector vin) { + stov("'15 43 0'"); // set OFS_RETURN + vector v2 = -vin; + print(vtos(v2), "\n"); +} diff --git a/tests/vecmath.tmpl b/tests/vecmath.tmpl new file mode 100644 index 0000000..8bcea3a --- /dev/null +++ b/tests/vecmath.tmpl @@ -0,0 +1,6 @@ +I: vecmath.qc +D: previously problematic vector math +T: -execute +C: -std=gmqcc -fftepp +E: -vector '5 5 5' +M: '-5 -5 -5'