X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=ir.c;h=b31c0629b25f25e8cb3756a6b969cee69cd69f08;hb=db5484bdefc9ac572e61101c25308e36888cad7c;hp=789e67ba1ac35716f5af2f4e937af90dd86fd178;hpb=fe150aee72b013f2aa584aa3a50149328ab54bcd;p=xonotic%2Fgmqcc.git diff --git a/ir.c b/ir.c index 789e67b..b31c062 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; } /*********************************************************************** @@ -538,7 +530,7 @@ static bool instr_is_operation(uint16_t op) (op >= INSTR_AND && op <= INSTR_BITOR) ); } -bool ir_function_pass_minor(ir_function *self) +bool ir_function_pass_peephole(ir_function *self) { size_t b; @@ -546,49 +538,100 @@ bool ir_function_pass_minor(ir_function *self) size_t i; ir_block *block = self->blocks[b]; - if (vec_size(block->instr) < 2) - continue; - - for (i = 1; i < vec_size(block->instr); ++i) { - ir_instr *store; - ir_instr *oper; - ir_value *value; + for (i = 0; i < vec_size(block->instr); ++i) { + ir_instr *inst; + inst = block->instr[i]; - 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_MINOR]; - 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_MINOR]; + ++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 */ @@ -687,8 +730,8 @@ bool ir_function_finalize(ir_function *self) if (self->builtin) return true; - if (OPTS_OPTIMIZATION(OPTIM_MINOR)) { - if (!ir_function_pass_minor(self)) { + if (OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) { + if (!ir_function_pass_peephole(self)) { irerror(self->context, "generic optimization pass broke something in `%s`", self->name); return false; } @@ -937,6 +980,8 @@ ir_value* ir_value_var(const char *name, int storetype, int vtype) self->members[2] = NULL; self->memberof = NULL; + self->unique_life = false; + self->life = NULL; return self; } @@ -2145,6 +2190,7 @@ typedef struct { ir_value **locals; size_t *sizes; size_t *positions; + bool *unique; } function_allocator; static bool function_allocator_alloc(function_allocator *alloc, const ir_value *var) @@ -2161,6 +2207,7 @@ static bool function_allocator_alloc(function_allocator *alloc, const ir_value * vec_push(alloc->locals, slot); vec_push(alloc->sizes, vsize); + vec_push(alloc->unique, var->unique_life); return true; @@ -2186,9 +2233,12 @@ bool ir_function_allocate_locals(ir_function *self) alloc.locals = NULL; alloc.sizes = NULL; alloc.positions = NULL; + alloc.unique = NULL; for (i = 0; i < vec_size(self->locals); ++i) { + if (!OPTS_OPTIMIZATION(OPTIM_LOCALTEMPS)) + self->locals[i]->unique_life = true; if (!function_allocator_alloc(&alloc, self->locals[i])) goto error; } @@ -2203,8 +2253,21 @@ bool ir_function_allocate_locals(ir_function *self) for (a = 0; a < vec_size(alloc.locals); ++a) { + /* if it's reserved for a unique liferange: skip */ + if (alloc.unique[a]) + continue; + slot = alloc.locals[a]; + /* never resize parameters + * will be required later when overlapping temps + locals + */ + if (a < vec_size(self->params) && + alloc.sizes[a] < type_sizeof[v->vtype]) + { + continue; + } + if (ir_values_overlap(v, slot)) continue; @@ -2244,7 +2307,11 @@ bool ir_function_allocate_locals(ir_function *self) self->allocated_locals = pos + vec_last(alloc.sizes); - /* Take over the actual slot positions */ + /* Locals need to know their new position */ + for (i = 0; i < vec_size(self->locals); ++i) { + self->locals[i]->code.local = alloc.positions[i]; + } + /* Take over the actual slot positions on values */ for (i = 0; i < vec_size(self->values); ++i) { self->values[i]->code.local = alloc.positions[self->values[i]->code.local]; } @@ -2329,7 +2396,6 @@ static bool ir_block_life_prop_previous(ir_block* self, ir_block *prev, bool *ch * So we have to remove whatever does not exist in the previous block. * They will be re-added on-read, but the liferange merge won't cause * a change. - */ for (i = 0; i < vec_size(self->living); ++i) { if (!vec_ir_value_find(prev->living, self->living[i], NULL)) { @@ -2337,6 +2403,7 @@ static bool ir_block_life_prop_previous(ir_block* self, ir_block *prev, bool *ch --i; } } + */ /* Whatever the previous block still has in its living set * must now be added to ours as well. @@ -2624,7 +2691,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; @@ -2698,7 +2766,8 @@ 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; } /* if not, generate now */ @@ -2823,6 +2892,16 @@ tailcall: /* 2-operand instructions with A -> B */ stmt.o2.u1 = stmt.o3.u1; stmt.o3.u1 = 0; + + /* tiny optimization, don't output + * STORE a, a + */ + if (stmt.o2.u1 == stmt.o1.u1 && + OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) + { + ++opts_optimizationcount[OPTIM_PEEPHOLE]; + continue; + } } code_push_statement(&stmt, instr->context.line); @@ -2852,8 +2931,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; @@ -2886,7 +2965,9 @@ static bool gen_global_function(ir_builder *ir, ir_value *global) ir_function *irfun; size_t i; +#ifndef NEW_ALLOC_STRAT size_t local_var_end; +#endif if (!global->hasvalue || (!global->constval.vfunc)) { @@ -2912,6 +2993,7 @@ static bool gen_global_function(ir_builder *ir, ir_value *global) fun.firstlocal = vec_size(code_globals); +#ifndef NEW_ALLOC_STRAT local_var_end = fun.firstlocal; for (i = 0; i < vec_size(irfun->locals); ++i) { if (!ir_builder_gen_global(ir, irfun->locals[i], true)) { @@ -2939,6 +3021,25 @@ static bool gen_global_function(ir_builder *ir, ir_value *global) } fun.locals = vec_size(code_globals) - fun.firstlocal; +#else + fun.locals = irfun->allocated_locals; + for (i = 0; i < vec_size(irfun->locals); ++i) { + if (!ir_builder_gen_global(ir, irfun->locals[i], true)) { + irerror(irfun->locals[i]->context, "Failed to generate local %s", irfun->locals[i]->name); + return false; + } + ir_value_code_setaddr(irfun->locals[i], fun.firstlocal + irfun->locals[i]->code.local); + } + for (i = vec_size(code_globals) - fun.firstlocal; i < fun.locals; ++i) { + vec_push(code_globals, 0); + } + for (i = 0; i < vec_size(irfun->values); ++i) + { + /* generate code.globaladdr for ssa values */ + ir_value *v = irfun->values[i]; + ir_value_code_setaddr(v, fun.firstlocal + v->code.local); + } +#endif if (irfun->builtin) fun.entry = irfun->builtin+1; @@ -3108,9 +3209,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; @@ -3122,9 +3223,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; } @@ -3137,21 +3238,19 @@ 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); return global->code.globaladdr >= 0; @@ -3166,9 +3265,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: @@ -3202,7 +3301,7 @@ static bool ir_builder_gen_field(ir_builder *self, ir_value *field) def.offset = (uint16_t)vec_size(code_globals); /* create a global named the same as the field */ - if (opts_standard == COMPILER_GMQCC) { + if (opts.standard == COMPILER_GMQCC) { /* in our standard, the global gets a dot prefix */ size_t len = strlen(field->name); char name[1024]; @@ -3298,17 +3397,17 @@ 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) + if (opts.pp_only) return true; if (vec_size(code_statements) != vec_size(code_linenums)) { @@ -3408,7 +3507,7 @@ void ir_function_dump(ir_function *f, char *ind, for (i = 0; i < vec_size(f->locals); ++i) { size_t l; ir_value *v = f->locals[i]; - oprintf("%s\t%s: unique ", ind, v->name); + oprintf("%s\t%s: %s@%i ", ind, v->name, (v->unique_life ? "unique " : ""), (int)v->code.local); for (l = 0; l < vec_size(v->life); ++l) { oprintf("[%i,%i] ", v->life[l].start, v->life[l].end); }