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) {
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<char[]> name(new char[namelen+16]);
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
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;
}
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<char[]> name(new char[namelen+16]);
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;
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<char[]> name(new char[namelen+16]);
util_strncpy(name.get(), m_name.c_str(), namelen);
}
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
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;
/* 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;
{
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))
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;
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<ast_entfield*>(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))
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)
*/
std::vector<basic_value_t> m_initlist;
- /* usecount for the parser */
- size_t m_uses = 0;
-
ir_value *m_ir_v = nullptr;
std::vector<ir_value*> m_ir_values;
size_t m_ir_value_count = 0;
.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
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;
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) { \
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();
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 */
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;
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;
}
/***********************************************************************
}
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;
}
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;
printf("<illegal instruction %d>\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 &&
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
}
}
- vec_push(prog->stack, st);
+ prog->stack.emplace_back(st);
return func->entry;
}
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 */
}
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;
const char *value;
};
-static qcvm_parameter *main_params = nullptr;
+static std::vector<qcvm_parameter> main_params;
#define CheckArgs(num) do { \
if (prog->argc != (num)) { \
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;
&qc_normalize, /* 12 */
&qc_sqrt, /* 13 */
&qc_floor, /* 14 */
- &qc_pow /* 15 */
+ &qc_pow, /* 15 */
+ &qc_stov /* 16 */
};
static const char *arg0 = nullptr;
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;
bool opts_info = false;
bool noexec = false;
const char *progsfile = nullptr;
- const char **dis_list = nullptr;
int opts_v = 0;
+ std::vector<const char*> dis_list;
arg0 = argv[0];
usage();
exit(EXIT_FAILURE);
}
- vec_push(dis_list, argv[1]);
+ dis_list.emplace_back(argv[1]);
--argc;
++argv;
noexec = true;
}
p.value = argv[1];
- vec_push(main_params, p);
+ main_params.emplace_back(p);
--argc;
++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) {
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:
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;
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;
}
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;
}
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:
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];
{
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:
{
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_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;
}
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++ */
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]);
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:
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");
case INSTR_DIV_F:
if (swapped)
return nullptr;
+ [[fallthrough]];
case INSTR_MUL_F:
if (immvalue_float(load) == 1.0f) {
++opts_optimizationcount[OPTIM_PEEPHOLE];
case INSTR_SUB_F:
if (swapped)
return nullptr;
+ [[fallthrough]];
case INSTR_ADD_F:
if (immvalue_float(load) == 0.0f) {
++opts_optimizationcount[OPTIM_PEEPHOLE];
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];
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));
#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 {
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<prog_section_statement_t> code;
std::vector<prog_section_def_t> defs;
std::vector<prog_section_def_t> fields;
std::vector<prog_section_function_t> functions;
std::vector<char> strings;
std::vector<qcint_t> globals;
- qcint_t *entitydata;
- bool *entitypool;
+ std::vector<qcint_t> entitydata;
+ std::vector<bool> entitypool;
- const char* *function_stack;
+ std::vector<const char*> function_stack;
uint16_t crc16;
qcint_t vmerror;
- size_t *profile;
+ std::vector<size_t> profile;
prog_builtin_t *builtins;
size_t builtins_count;
size_t entityfields;
bool allowworldwrites;
- qcint_t *localstack;
- qc_exec_stack_t *stack;
+ std::vector<qcint_t> localstack;
+ std::vector<qc_exec_stack_t> stack;
size_t statement;
size_t xflags;
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;
[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’.
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‐
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<ir_block *> &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;
}
{
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];
++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)
(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];
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
* 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)
{
++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
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 */
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());
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;
}
delete in;
return false;
}
- vec_push(self->m_instr, in);
+ self->m_instr.push_back(in);
return true;
}
delete in;
return false;
}
- vec_push(self->m_instr, in);
+ self->m_instr.push_back(in);
return true;
}
return false;
}
- vec_push(self->m_instr, in);
+ self->m_instr.push_back(in);
return true;
}
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;
}
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;
}
delete in;
return nullptr;
}
- vec_push(self->m_instr, in);
+ self->m_instr.push_back(in);
return in;
}
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)) {
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;
goto on_error;
}
- vec_push(self->m_instr, instr);
+ self->m_instr.push_back(instr);
return out;
on_error:
* 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) {
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;
}
}
*/
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;
}
* 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<std::unique_ptr<ir_value>> locals;
+ std::vector<size_t> sizes;
+ std::vector<size_t> positions;
+ std::vector<bool> unique;
};
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)
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;
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;
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;
}
bool ir_function_allocate_locals(ir_function *self)
{
- bool retval = true;
size_t pos;
bool opt_gt = OPTS_OPTIMIZATION(OPTIM_GLOBAL_TEMPS);
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)
{
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)
{
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 */
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;
}
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 */
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
{
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];
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");
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];
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;
{
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;
ir_value *ep;
prog_section_statement_t stmt;
- numparams = vec_size(self->m_params);
+ numparams = self->m_params.size();
if (!numparams)
return true;
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;
if (!name || name[0] == '#' || OPTS_FLAG(SINGLE_VECTOR_DEFS))
return;
- def.type = TYPE_FLOAT;
+ def.type = type;
len = strlen(name);
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;
}
}
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:
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;
}
}
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());
}
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;
}
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),
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<ir_instr *> m_instr;
+ std::vector<ir_block *> m_entries;
+ std::vector<ir_block *> m_exits;
std::vector<ir_value *> m_living;
/* For the temp-allocation */
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<int> m_params;
+ ir_flag_t m_flags = 0;
+ int m_builtin = 0;
std::vector<std::unique_ptr<ir_block>> m_blocks;
#include "gmqcc.h"
#include "lexer.h"
+#include "parser.h"
/* TODO: cleanup this whole file .. it's a fuckign mess */
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++)
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) {
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)
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;
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;
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)) {
case opid2('|','|'):
generated_op += 1; /* INSTR_OR */
+ [[fallthrough]];
case opid2('&','&'):
generated_op += INSTR_AND;
if (!(out = parser->m_fold.op(op, exprs))) {
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)) {
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));
}
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));
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,
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,
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),
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
* 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<ast_expression*> 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;
}
*/
sy->out[fid] = syexp(foldval->m_context, foldval);
sy->out.erase(sy->out.end() - paramcount, sy->out.end());
- vec_free(exprs);
return true;
}
}
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));
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)
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);
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)) {
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");
bool flag = false;
bool reverse = false;
qcfloat_t num = 0;
- ast_value **values = nullptr;
ast_value *var = nullptr;
ast_value *asvalue;
+ std::vector<ast_value*> values;
ast_expression *old;
break;
}
parseerror(parser, "expected identifier or `}`");
- goto onerror;
+ return false;
}
old = parser_find_field(parser, parser_tokval(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;
if (!parser_next(parser)) {
parseerror(parser, "expected `=`, `}` or comma after identifier");
- goto onerror;
+ return false;
}
if (parser->tok == ',')
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! */
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;
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)
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;
}
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");
/* 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;
}
}
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);
}
}
- /* 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.
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);
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]);
}
}
/* 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
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;
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 = "<internal>";
+ 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, "<vector.x>", TYPE_NOEXPR);
+ const_vec[1] = new ast_value(empty_ctx, "<vector.y>", TYPE_NOEXPR);
+ const_vec[2] = new ast_value(empty_ctx, "<vector.z>", 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;
}
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 = "<internal>";
- 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, "<vector.x>", TYPE_NOEXPR);
- parser->const_vec[1] = new ast_value(empty_ctx, "<vector.y>", TYPE_NOEXPR);
- parser->const_vec[2] = new ast_value(empty_ctx, "<vector.z>", 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;
}
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) {
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);
}
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))
delete ir;
return false;
}
+
+ // ir->generate can generate compiler warnings
+ fnCheckWErrors();
}
delete ir;
return retval;
#define GMQCC_PARSER_HDR
#include "gmqcc.h"
#include "lexer.h"
-//#include "ast.h"
+#include "ast.h"
#include "intrin.h"
#include "fold.h"
#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;
std::vector<const char *> continues;
/* A list of hashtables for each scope */
- ht *variables;
+ std::vector<ht> variables;
ht htfields;
ht htglobals;
- ht *typedefs;
+ std::vector<ht> 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<ast_expression*> _locals;
+ std::vector<size_t> _blocklocals;
+ std::vector<std::unique_ptr<ast_value>> _typedefs;
+ std::vector<size_t> _blocktypedefs;
+ std::vector<lex_ctx_t> _block_ctx;
/* we store the '=' operator info */
const oper_info *assign_op;
float (float val) sqrt = #13;
float (float val) floor = #14;
float (float val1, float val2) pow = #15;
+vector (string str) stov = #16;
I: fieldfuncs.qc
D: test fields with functions
-T: -compile
+T: -execute
C: -std=fte
M: 42
M: 42
--- /dev/null
+.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));
+}
--- /dev/null
+I: memberbinop.qc
+D: test member bin ops
+T: -execute
+C: -std=gmqcc
+E: $null
+M: '2 4 6'
--- /dev/null
+.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");
+}
--- /dev/null
+I: vecfields.qc
+D: vector field member accesses
+T: -execute
+C: -std=gmqcc -fftepp
+M: 42 => '1 42 3'
--- /dev/null
+void main(vector vin) {
+ stov("'15 43 0'"); // set OFS_RETURN
+ vector v2 = -vin;
+ print(vtos(v2), "\n");
+}
--- /dev/null
+I: vecmath.qc
+D: previously problematic vector math
+T: -execute
+C: -std=gmqcc -fftepp
+E: -vector '5 5 5'
+M: '-5 -5 -5'