X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=ir.c;h=30cdda9095cafcd7850bf89b3b9ac41a42a291e6;hb=915c002be42451f13c3f665cffcd4bf0f8b3e085;hp=11b34458e60deaecf5f1150ba63afc4e98ff113f;hpb=8858f265dd048bef0f3e4bd4cb7ee2beaff6f81f;p=xonotic%2Fgmqcc.git diff --git a/ir.c b/ir.c index 11b3445..30cdda9 100644 --- a/ir.c +++ b/ir.c @@ -208,20 +208,12 @@ static void irerror(lex_ctx ctx, const char *msg, ...) static bool irwarning(lex_ctx ctx, int warntype, const char *fmt, ...) { - va_list ap; - int lvl = LVL_WARNING; - - if (warntype && !OPTS_WARN(warntype)) - return false; - - if (opts.werror) - lvl = LVL_ERROR; - - va_start(ap, fmt); - con_vprintmsg(lvl, ctx.file, ctx.line, (opts.werror ? "error" : "warning"), fmt, ap); - va_end(ap); - - return opts.werror; + bool r; + va_list ap; + va_start(ap, fmt); + r = vcompile_warning(ctx, warntype, fmt, ap); + va_end(ap); + return r; } /*********************************************************************** @@ -546,49 +538,100 @@ bool ir_function_pass_peephole(ir_function *self) size_t i; ir_block *block = self->blocks[b]; - if (vec_size(block->instr) < 2) - continue; + for (i = 0; i < vec_size(block->instr); ++i) { + ir_instr *inst; + inst = block->instr[i]; - for (i = 1; i < vec_size(block->instr); ++i) { - ir_instr *store; - ir_instr *oper; - ir_value *value; - - store = block->instr[i]; - if (store->opcode < INSTR_STORE_F || - store->opcode > INSTR_STORE_FNC) + if (i >= 1 && + (inst->opcode >= INSTR_STORE_F && + inst->opcode <= INSTR_STORE_FNC)) { - continue; - } + ir_instr *store; + ir_instr *oper; + ir_value *value; - oper = block->instr[i-1]; - if (!instr_is_operation(oper->opcode)) - continue; + store = inst; - value = oper->_ops[0]; + oper = block->instr[i-1]; + if (!instr_is_operation(oper->opcode)) + continue; - /* only do it for SSA values */ - if (value->store != store_value) - continue; + value = oper->_ops[0]; - /* don't optimize out the temp if it's used later again */ - if (vec_size(value->reads) != 1) - continue; + /* only do it for SSA values */ + if (value->store != store_value) + continue; - /* The very next store must use this value */ - if (value->reads[0] != store) - continue; + /* don't optimize out the temp if it's used later again */ + if (vec_size(value->reads) != 1) + continue; - /* And of course the store must _read_ from it, so it's in - * OP 1 */ - if (store->_ops[1] != value) - continue; + /* The very next store must use this value */ + if (value->reads[0] != store) + continue; + + /* And of course the store must _read_ from it, so it's in + * OP 1 */ + if (store->_ops[1] != value) + continue; - ++optimization_count[OPTIM_PEEPHOLE]; - oper->_ops[0] = store->_ops[0]; + ++opts_optimizationcount[OPTIM_PEEPHOLE]; + (void)!ir_instr_op(oper, 0, store->_ops[0], true); - vec_remove(block->instr, i, 1); - ir_instr_delete(store); + vec_remove(block->instr, i, 1); + ir_instr_delete(store); + } + else if (inst->opcode == VINSTR_COND) + { + /* COND on a value resulting from a NOT could + * remove the NOT and swap its operands + */ + while (true) { + ir_block *tmp; + size_t inotid; + ir_instr *inot; + ir_value *value; + value = inst->_ops[0]; + + if (value->store != store_value || + vec_size(value->reads) != 1 || + value->reads[0] != inst) + { + break; + } + + inot = value->writes[0]; + if (inot->_ops[0] != value || + inot->opcode < INSTR_NOT_F || + inot->opcode > INSTR_NOT_FNC || + inot->opcode == INSTR_NOT_V) /* can't do this one */ + { + break; + } + + /* count */ + ++opts_optimizationcount[OPTIM_PEEPHOLE]; + /* change operand */ + (void)!ir_instr_op(inst, 0, inot->_ops[1], false); + /* remove NOT */ + tmp = inot->owner; + for (inotid = 0; inotid < vec_size(tmp->instr); ++inotid) { + if (tmp->instr[inotid] == inot) + break; + } + if (inotid >= vec_size(tmp->instr)) { + compile_error(inst->context, "sanity-check failed: failed to find instruction to optimize out"); + return false; + } + vec_remove(tmp->instr, inotid, 1); + ir_instr_delete(inot); + /* swap ontrue/onfalse */ + tmp = inst->bops[0]; + inst->bops[0] = inst->bops[1]; + inst->bops[1] = tmp; + } + continue; + } } } @@ -635,7 +678,7 @@ bool ir_function_pass_tailcall(ir_function *self) ret->_ops[0] == store->_ops[0] && store->_ops[1] == call->_ops[0]) { - ++optimization_count[OPTIM_PEEPHOLE]; + ++opts_optimizationcount[OPTIM_PEEPHOLE]; call->_ops[0] = store->_ops[0]; vec_remove(block->instr, vec_size(block->instr) - 2, 1); ir_instr_delete(store); @@ -657,7 +700,7 @@ bool ir_function_pass_tailcall(ir_function *self) if (ret->_ops[0] && call->_ops[0] != ret->_ops[0]) continue; - ++optimization_count[OPTIM_TAIL_RECURSION]; + ++opts_optimizationcount[OPTIM_TAIL_RECURSION]; vec_shrinkby(block->instr, 2); block->final = false; /* open it back up */ @@ -945,6 +988,8 @@ ir_value* ir_value_var(const char *name, int storetype, int vtype) ir_value* ir_value_vector_member(ir_value *self, unsigned int member) { + char *name; + size_t len; ir_value *m; if (member >= 3) return NULL; @@ -952,9 +997,16 @@ ir_value* ir_value_vector_member(ir_value *self, unsigned int member) if (self->members[member]) return self->members[member]; + len = strlen(self->name); + name = (char*)mem_a(len + 3); + memcpy(name, self->name, len); + name[len+0] = '_'; + name[len+1] = 'x' + member; + name[len+2] = '\0'; if (self->vtype == TYPE_VECTOR) { - m = ir_value_var(self->name, self->store, TYPE_FLOAT); + m = ir_value_var(name, self->store, TYPE_FLOAT); + mem_d(name); if (!m) return NULL; m->context = self->context; @@ -966,7 +1018,8 @@ ir_value* ir_value_vector_member(ir_value *self, unsigned int member) { if (self->fieldtype != TYPE_VECTOR) return NULL; - m = ir_value_var(self->name, self->store, TYPE_FIELD); + m = ir_value_var(name, self->store, TYPE_FIELD); + mem_d(name); if (!m) return NULL; m->fieldtype = TYPE_FLOAT; @@ -1307,15 +1360,19 @@ bool ir_values_overlap(const ir_value *a, const ir_value *b) *IR main operations */ +static bool ir_check_unreachable(ir_block *self) +{ + /* The IR should never have to deal with unreachable code */ + if (!self->final/* || OPTS_FLAG(ALLOW_UNREACHABLE_CODE)*/) + return true; + irerror(self->context, "unreachable statement (%s)", self->label); + return false; +} + bool ir_block_create_store_op(ir_block *self, lex_ctx ctx, int op, ir_value *target, ir_value *what) { ir_instr *in; - if (self->final) { - irerror(self->context, "unreachable statement (%s)", self->label); - return false; - } - in = ir_instr_new(ctx, self, op); - if (!in) + if (!ir_check_unreachable(self)) return false; if (target->store == store_value && @@ -1327,9 +1384,14 @@ bool ir_block_create_store_op(ir_block *self, lex_ctx ctx, int op, ir_value *tar return false; } + in = ir_instr_new(ctx, self, op); + if (!in) + return false; + if (!ir_instr_op(in, 0, target, true) || !ir_instr_op(in, 1, what, false)) { + ir_instr_delete(in); return false; } vec_push(self->instr, in); @@ -1386,18 +1448,18 @@ bool ir_block_create_storep(ir_block *self, lex_ctx ctx, ir_value *target, ir_va bool ir_block_create_return(ir_block *self, lex_ctx ctx, ir_value *v) { ir_instr *in; - if (self->final) { - irerror(self->context, "unreachable statement (%s)", self->label); + if (!ir_check_unreachable(self)) return false; - } self->final = true; self->is_return = true; in = ir_instr_new(ctx, self, INSTR_RETURN); if (!in) return false; - if (v && !ir_instr_op(in, 0, v, false)) + if (v && !ir_instr_op(in, 0, v, false)) { + ir_instr_delete(in); return false; + } vec_push(self->instr, in); return true; @@ -1407,10 +1469,8 @@ bool ir_block_create_if(ir_block *self, lex_ctx ctx, ir_value *v, ir_block *ontrue, ir_block *onfalse) { ir_instr *in; - if (self->final) { - irerror(self->context, "unreachable statement (%s)", self->label); + if (!ir_check_unreachable(self)) return false; - } self->final = true; /*in = ir_instr_new(ctx, self, (v->vtype == TYPE_STRING ? INSTR_IF_S : INSTR_IF_F));*/ in = ir_instr_new(ctx, self, VINSTR_COND); @@ -1437,10 +1497,8 @@ bool ir_block_create_if(ir_block *self, lex_ctx ctx, ir_value *v, bool ir_block_create_jump(ir_block *self, lex_ctx ctx, ir_block *to) { ir_instr *in; - if (self->final) { - irerror(self->context, "unreachable statement (%s)", self->label); + if (!ir_check_unreachable(self)) return false; - } self->final = true; in = ir_instr_new(ctx, self, VINSTR_JUMP); if (!in) @@ -1457,10 +1515,8 @@ bool ir_block_create_jump(ir_block *self, lex_ctx ctx, ir_block *to) bool ir_block_create_goto(ir_block *self, lex_ctx ctx, ir_block *to) { ir_instr *in; - if (self->final) { - irerror(self->context, "unreachable statement (%s)", self->label); + if (!ir_check_unreachable(self)) return false; - } self->final = true; in = ir_instr_new(ctx, self, INSTR_GOTO); if (!in) @@ -1478,6 +1534,8 @@ ir_instr* ir_block_create_phi(ir_block *self, lex_ctx ctx, const char *label, in { ir_value *out; ir_instr *in; + if (!ir_check_unreachable(self)) + return false; in = ir_instr_new(ctx, self, VINSTR_PHI); if (!in) return NULL; @@ -1519,13 +1577,21 @@ void ir_phi_add(ir_instr* self, ir_block *b, ir_value *v) } /* call related code */ -ir_instr* ir_block_create_call(ir_block *self, lex_ctx ctx, const char *label, ir_value *func) +ir_instr* ir_block_create_call(ir_block *self, lex_ctx ctx, const char *label, ir_value *func, bool noreturn) { ir_value *out; ir_instr *in; - in = ir_instr_new(ctx, self, INSTR_CALL0); + if (!ir_check_unreachable(self)) + return false; + in = ir_instr_new(ctx, self, (noreturn ? VINSTR_NRCALL : INSTR_CALL0)); if (!in) return NULL; + /* + if (noreturn) { + self->final = true; + self->is_return = true; + } + */ out = ir_value_out(self->owner, label, (func->outtype == TYPE_VOID) ? store_return : store_value, func->outtype); if (!out) { ir_instr_delete(in); @@ -1539,6 +1605,13 @@ ir_instr* ir_block_create_call(ir_block *self, lex_ctx ctx, const char *label, i return NULL; } vec_push(self->instr, in); + if (noreturn) { + if (!ir_block_create_return(self, ctx, NULL)) { + compile_error(ctx, "internal error: failed to generate dummy-return instruction"); + ir_instr_delete(in); + return NULL; + } + } return in; } @@ -2126,8 +2199,24 @@ bool ir_function_calculate_liferanges(ir_function *self) ir_block *block = self->blocks[0]; for (i = 0; i < vec_size(block->living); ++i) { ir_value *v = block->living[i]; - if (v->memberof || v->store != store_local) + if (v->store != store_local) continue; + if ((v->members[0] && v->members[1] && v->members[2])) { + /* all vector members have been accessed - only treat this as uninitialized + * if any of them is also uninitialized. + */ + if (!vec_ir_value_find(block->living, v->members[0], NULL) && + !vec_ir_value_find(block->living, v->members[1], NULL) && + !vec_ir_value_find(block->living, v->members[2], NULL)) + { + continue; + } + } + if (v->memberof) { + /* A member is only uninitialized if the whole vector is also uninitialized */ + if (!vec_ir_value_find(block->living, v->memberof, NULL)) + continue; + } if (irwarning(v->context, WARN_USED_UNINITIALIZED, "variable `%s` may be used uninitialized in this function", v->name)) { @@ -2403,8 +2492,6 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change for (p = 0; p < vec_size(instr->phi); ++p) { value = instr->phi[p].value; - if (value->memberof) - value = value->memberof; if (!vec_ir_value_find(self->living, value, NULL)) vec_push(self->living, value); } @@ -2413,8 +2500,6 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change for (p = 0; p < vec_size(instr->params); ++p) { value = instr->params[p]; - if (value->memberof) - value = value->memberof; if (!vec_ir_value_find(self->living, value, NULL)) vec_push(self->living, value); } @@ -2442,8 +2527,6 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change continue; value = instr->_ops[o]; - if (value->memberof) - value = value->memberof; /* We only care about locals */ /* we also calculate parameter liferanges so that locals @@ -2648,7 +2731,8 @@ tailcall: stmt.o1.s1 = (target->code_start) - vec_size(code_statements); stmt.o2.s1 = 0; stmt.o3.s1 = 0; - code_push_statement(&stmt, instr->context.line); + if (stmt.o1.s1 != 1) + code_push_statement(&stmt, instr->context.line); /* no further instructions can be in this block */ return true; @@ -2668,12 +2752,14 @@ tailcall: if (ontrue->generated) { stmt.opcode = INSTR_IF; stmt.o2.s1 = (ontrue->code_start) - vec_size(code_statements); - code_push_statement(&stmt, instr->context.line); + if (stmt.o2.s1 != 1) + code_push_statement(&stmt, instr->context.line); } if (onfalse->generated) { stmt.opcode = INSTR_IFNOT; stmt.o2.s1 = (onfalse->code_start) - vec_size(code_statements); - code_push_statement(&stmt, instr->context.line); + if (stmt.o2.s1 != 1) + code_push_statement(&stmt, instr->context.line); } if (!ontrue->generated) { if (onfalse->generated) { @@ -2707,6 +2793,12 @@ tailcall: if (onfalse->generated) { /* fixup the jump address */ code_statements[stidx].o2.s1 = (onfalse->code_start) - (stidx); + if (code_statements[stidx].o2.s1 == 1) { + code_statements[stidx] = code_statements[stidx+1]; + if (code_statements[stidx].o1.s1 < 0) + code_statements[stidx].o1.s1++; + code_pop_statement(); + } stmt.opcode = vec_last(code_statements).opcode; if (stmt.opcode == INSTR_GOTO || stmt.opcode == INSTR_IF || @@ -2722,15 +2814,24 @@ tailcall: stmt.o1.s1 = (onfalse->code_start) - vec_size(code_statements); stmt.o2.s1 = 0; stmt.o3.s1 = 0; - code_push_statement(&stmt, instr->context.line); + if (stmt.o1.s1 != 1) + code_push_statement(&stmt, instr->context.line); return true; } + else if (code_statements[stidx].o2.s1 == 1) { + code_statements[stidx] = code_statements[stidx+1]; + if (code_statements[stidx].o1.s1 < 0) + code_statements[stidx].o1.s1++; + code_pop_statement(); + } /* if not, generate now */ block = onfalse; goto tailcall; } - if (instr->opcode >= INSTR_CALL0 && instr->opcode <= INSTR_CALL8) { + if ( (instr->opcode >= INSTR_CALL0 && instr->opcode <= INSTR_CALL8) + || instr->opcode == VINSTR_NRCALL) + { /* Trivial call translation: * copy all params to OFS_PARM* * if the output's storetype is not store_return, @@ -2854,7 +2955,7 @@ tailcall: if (stmt.o2.u1 == stmt.o1.u1 && OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) { - ++optimization_count[OPTIM_PEEPHOLE]; + ++opts_optimizationcount[OPTIM_PEEPHOLE]; continue; } } @@ -2886,8 +2987,8 @@ static bool gen_function_code(ir_function *self) return false; } - /* otherwise code_write crashes since it debug-prints functions until AINSTR_END */ - stmt.opcode = AINSTR_END; + /* code_write and qcvm -disasm need to know that the function ends here */ + stmt.opcode = INSTR_DONE; stmt.o1.u1 = 0; stmt.o2.u1 = 0; stmt.o3.u1 = 0; @@ -3102,6 +3203,62 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global) return true; } +static void gen_vector_defs(prog_section_def def, const char *name) +{ + char *component; + size_t len, i; + + if (!name || OPTS_FLAG(SINGLE_VECTOR_DEFS)) + return; + + def.type = TYPE_FLOAT; + + len = strlen(name); + + component = (char*)mem_a(len+3); + memcpy(component, name, len); + len += 2; + component[len-0] = 0; + component[len-2] = '_'; + + component[len-1] = 'x'; + + for (i = 0; i < 3; ++i) { + def.name = code_genstring(component); + vec_push(code_defs, def); + def.offset++; + component[len-1]++; + } +} + +static void gen_vector_fields(prog_section_field fld, const char *name) +{ + char *component; + size_t len, i; + + if (!name || OPTS_FLAG(SINGLE_VECTOR_DEFS)) + return; + + fld.type = TYPE_FLOAT; + + len = strlen(name); + + component = (char*)mem_a(len+3); + memcpy(component, name, len); + len += 2; + component[len-0] = 0; + component[len-2] = '_'; + + component[len-1] = 'x'; + + for (i = 0; i < 3; ++i) { + fld.name = code_genstring(component); + vec_push(code_fields, fld); + fld.offset++; + component[len-1]++; + } +} + static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool islocal) { size_t i; @@ -3153,6 +3310,7 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc return gen_global_pointer(global); case TYPE_FIELD: vec_push(code_defs, def); + gen_vector_defs(def, global->name); return gen_global_field(global); case TYPE_ENTITY: /* fall through */ @@ -3164,9 +3322,9 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc vec_push(code_globals, *iptr); } else { vec_push(code_globals, 0); - if (!islocal) - def.type |= DEF_SAVEGLOBAL; } + if (!islocal && global->cvq != CV_CONST) + def.type |= DEF_SAVEGLOBAL; vec_push(code_defs, def); return global->code.globaladdr >= 0; @@ -3178,9 +3336,9 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc vec_push(code_globals, code_genstring(global->constval.vstring)); } else { vec_push(code_globals, 0); - if (!islocal) - def.type |= DEF_SAVEGLOBAL; } + if (!islocal && global->cvq != CV_CONST) + def.type |= DEF_SAVEGLOBAL; vec_push(code_defs, def); return global->code.globaladdr >= 0; } @@ -3193,23 +3351,23 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc vec_push(code_globals, iptr[0]); if (global->code.globaladdr < 0) return false; - for (d = 1; d < type_sizeof[global->vtype]; ++d) - { + for (d = 1; d < type_sizeof[global->vtype]; ++d) { vec_push(code_globals, iptr[d]); } } else { vec_push(code_globals, 0); if (global->code.globaladdr < 0) return false; - for (d = 1; d < type_sizeof[global->vtype]; ++d) - { + for (d = 1; d < type_sizeof[global->vtype]; ++d) { vec_push(code_globals, 0); } - if (!islocal) - def.type |= DEF_SAVEGLOBAL; } + if (!islocal && global->cvq != CV_CONST) + def.type |= DEF_SAVEGLOBAL; vec_push(code_defs, def); + def.type &= ~DEF_SAVEGLOBAL; + gen_vector_defs(def, global->name); return global->code.globaladdr >= 0; } case TYPE_FUNCTION: @@ -3222,9 +3380,9 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc vec_push(code_globals, vec_size(code_functions)); if (!gen_global_function(self, global)) return false; - if (!islocal) - def.type |= DEF_SAVEGLOBAL; } + if (!islocal && global->cvq != CV_CONST) + def.type |= DEF_SAVEGLOBAL; vec_push(code_defs, def); return true; case TYPE_VARIANT: @@ -3309,6 +3467,11 @@ static bool ir_builder_gen_field(ir_builder *self, ir_value *field) vec_push(code_globals, fld.offset+2); } + if (field->fieldtype == TYPE_VECTOR) { + gen_vector_defs(def, field->name); + gen_vector_fields(fld, field->name); + } + return field->code.globaladdr >= 0; } @@ -3354,15 +3517,15 @@ bool ir_builder_generate(ir_builder *self, const char *filename) return false; } - /* DP errors if the last instruction is not an INSTR_DONE - * and for debugging purposes we add an additional AINSTR_END - * to the end of functions, so here it goes: - */ - stmt.opcode = INSTR_DONE; - stmt.o1.u1 = 0; - stmt.o2.u1 = 0; - stmt.o3.u1 = 0; - code_push_statement(&stmt, vec_last(code_linenums)); + /* DP errors if the last instruction is not an INSTR_DONE. */ + if (vec_last(code_statements).opcode != INSTR_DONE) + { + stmt.opcode = INSTR_DONE; + stmt.o1.u1 = 0; + stmt.o2.u1 = 0; + stmt.o3.u1 = 0; + code_push_statement(&stmt, vec_last(code_linenums)); + } if (opts.pp_only) return true; @@ -3403,8 +3566,8 @@ bool ir_builder_generate(ir_builder *self, const char *filename) #define IND_BUFSZ 1024 -#ifdef WIN32 -# define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE) +#ifdef _MSC_VER +# define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE) #endif const char *qc_opname(int op) @@ -3538,7 +3701,7 @@ void ir_instr_dump(ir_instr *in, char *ind, if (in->_ops[1] || in->_ops[2]) oprintf(" <- "); } - if (in->opcode == INSTR_CALL0) { + if (in->opcode == INSTR_CALL0 || in->opcode == VINSTR_NRCALL) { oprintf("CALL%i\t", vec_size(in->params)); } else oprintf("%s\t", qc_opname(in->opcode));