X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=ir.c;h=3efad3da81164b9064c882e5c11179ace81e9463;hb=9908209f5825821eb8c73ff4926bc2b504848792;hp=4f53533887bdd0e73feef89352f7ece84bd39856;hpb=d6ca5673dc7f2760faca1200477808d919a290ca;p=xonotic%2Fgmqcc.git diff --git a/ir.c b/ir.c index 4f53533..3efad3d 100644 --- a/ir.c +++ b/ir.c @@ -312,6 +312,7 @@ static void ir_function_delete_quick(ir_function *self); ir_builder* ir_builder_new(const char *modulename) { ir_builder* self; + size_t i; self = (ir_builder*)mem_a(sizeof(*self)); if (!self) @@ -344,6 +345,15 @@ ir_builder* ir_builder_new(const char *modulename) self->nil = ir_value_var("nil", store_value, TYPE_NIL); self->nil->cvq = CV_CONST; + for (i = 0; i != IR_MAX_VINSTR_TEMPS; ++i) { + /* we write to them, but they're not supposed to be used outside the IR, so + * let's not allow the generation of ir_instrs which use these. + * So it's a constant noexpr. + */ + self->vinstr_temp[i] = ir_value_var("vinstr_temp", store_value, TYPE_NOEXPR); + self->vinstr_temp[i]->cvq = CV_CONST; + } + self->reserved_va_count = NULL; self->code = code_init(); @@ -374,6 +384,9 @@ void ir_builder_delete(ir_builder* self) ir_value_delete(self->fields[i]); } ir_value_delete(self->nil); + for (i = 0; i != IR_MAX_VINSTR_TEMPS; ++i) { + ir_value_delete(self->vinstr_temp[i]); + } vec_free(self->fields); vec_free(self->filenames); vec_free(self->filestrings); @@ -434,7 +447,7 @@ ir_value* ir_builder_create_global(ir_builder *self, const char *name, int vtype { ir_value *ve; - if (name && name[0] != '#') + if (name[0] != '#') { ve = ir_builder_get_global(self, name); if (ve) { @@ -599,7 +612,8 @@ static bool instr_is_operation(uint16_t op) (op == INSTR_ADDRESS) || (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) || (op >= INSTR_AND && op <= INSTR_BITOR) || - (op >= INSTR_CALL0 && op <= INSTR_CALL8) ); + (op >= INSTR_CALL0 && op <= INSTR_CALL8) || + (op >= VINSTR_BITAND_V && op <= VINSTR_BITXOR_VF) ); } static bool ir_function_pass_peephole(ir_function *self) @@ -628,6 +642,7 @@ static bool ir_function_pass_peephole(ir_function *self) if (!instr_is_operation(oper->opcode)) continue; + /* Don't change semantics of MUL_VF in engines where these may not alias. */ if (OPTS_FLAG(LEGACY_VECTOR_MATHS)) { if (oper->opcode == INSTR_MUL_VF && oper->_ops[2]->memberof == oper->_ops[1]) continue; @@ -1019,6 +1034,11 @@ static void ir_instr_delete(ir_instr *self) static bool ir_instr_op(ir_instr *self, int op, ir_value *v, bool writing) { + if (v && v->vtype == TYPE_NOEXPR) { + irerror(self->context, "tried to use a NOEXPR value"); + return false; + } + if (self->_ops[op]) { size_t idx; if (writing && vec_ir_instr_find(self->_ops[op]->writes, self, &idx)) @@ -1752,6 +1772,7 @@ ir_value* ir_block_create_binop(ir_block *self, lex_ctx_t ctx, #endif case INSTR_BITAND: case INSTR_BITOR: + case VINSTR_BITXOR: #if 0 case INSTR_SUB_S: /* -- offset of string as float */ case INSTR_MUL_IF: @@ -1788,6 +1809,12 @@ ir_value* ir_block_create_binop(ir_block *self, lex_ctx_t ctx, case INSTR_SUB_V: case INSTR_MUL_VF: case INSTR_MUL_FV: + case VINSTR_BITAND_V: + case VINSTR_BITOR_V: + case VINSTR_BITXOR_V: + case VINSTR_BITAND_VF: + case VINSTR_BITOR_VF: + case VINSTR_BITXOR_VF: #if 0 case INSTR_DIV_VF: case INSTR_MUL_IV: @@ -2483,7 +2510,15 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed) } } - if (instr->opcode == INSTR_MUL_VF) + /* These operations need a special case as they can break when using + * same source and destination operand otherwise, as the engine may + * read the source multiple times. */ + if (instr->opcode == INSTR_MUL_VF || + instr->opcode == VINSTR_BITAND_VF || + instr->opcode == VINSTR_BITOR_VF || + instr->opcode == VINSTR_BITXOR || + instr->opcode == VINSTR_BITXOR_VF || + instr->opcode == VINSTR_BITXOR_V) { value = instr->_ops[2]; /* the float source will get an additional lifetime */ @@ -2492,7 +2527,12 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed) if (value->memberof && ir_value_life_merge(value->memberof, instr->eid+1)) *changed = true; } - else if (instr->opcode == INSTR_MUL_FV || instr->opcode == INSTR_LOAD_V) + + if (instr->opcode == INSTR_MUL_FV || + instr->opcode == INSTR_LOAD_V || + instr->opcode == VINSTR_BITXOR || + instr->opcode == VINSTR_BITXOR_VF || + instr->opcode == VINSTR_BITXOR_V) { value = instr->_ops[1]; /* the float source will get an additional lifetime */ @@ -2748,6 +2788,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc ir_block *onfalse; size_t stidx; size_t i; + int j; block->generated = true; block->code_start = vec_size(code->statements); @@ -2774,12 +2815,151 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.o2.s1 = 0; stmt.o3.s1 = 0; if (stmt.o1.s1 != 1) - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); /* no further instructions can be in this block */ return true; } + if (instr->opcode == VINSTR_BITXOR) { + stmt.opcode = INSTR_BITOR; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context); + stmt.opcode = INSTR_BITAND; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]); + code_push_statement(code, &stmt, instr->context); + stmt.opcode = INSTR_SUB_F; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]); + stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context); + + /* instruction generated */ + continue; + } + + if (instr->opcode == VINSTR_BITAND_V) { + stmt.opcode = INSTR_BITAND; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context); + ++stmt.o1.s1; + ++stmt.o2.s1; + ++stmt.o3.s1; + code_push_statement(code, &stmt, instr->context); + ++stmt.o1.s1; + ++stmt.o2.s1; + ++stmt.o3.s1; + code_push_statement(code, &stmt, instr->context); + + /* instruction generated */ + continue; + } + + if (instr->opcode == VINSTR_BITOR_V) { + stmt.opcode = INSTR_BITOR; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context); + ++stmt.o1.s1; + ++stmt.o2.s1; + ++stmt.o3.s1; + code_push_statement(code, &stmt, instr->context); + ++stmt.o1.s1; + ++stmt.o2.s1; + ++stmt.o3.s1; + code_push_statement(code, &stmt, instr->context); + + /* instruction generated */ + continue; + } + + if (instr->opcode == VINSTR_BITXOR_V) { + for (j = 0; j < 3; ++j) { + stmt.opcode = INSTR_BITOR; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j; + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + j; + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]) + j; + code_push_statement(code, &stmt, instr->context); + stmt.opcode = INSTR_BITAND; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j; + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + j; + stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]) + j; + code_push_statement(code, &stmt, instr->context); + } + stmt.opcode = INSTR_SUB_V; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]); + stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context); + + /* instruction generated */ + continue; + } + + if (instr->opcode == VINSTR_BITAND_VF) { + stmt.opcode = INSTR_BITAND; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context); + ++stmt.o1.s1; + ++stmt.o3.s1; + code_push_statement(code, &stmt, instr->context); + ++stmt.o1.s1; + ++stmt.o3.s1; + code_push_statement(code, &stmt, instr->context); + + /* instruction generated */ + continue; + } + + if (instr->opcode == VINSTR_BITOR_VF) { + stmt.opcode = INSTR_BITOR; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context); + ++stmt.o1.s1; + ++stmt.o3.s1; + code_push_statement(code, &stmt, instr->context); + ++stmt.o1.s1; + ++stmt.o3.s1; + code_push_statement(code, &stmt, instr->context); + + /* instruction generated */ + continue; + } + + if (instr->opcode == VINSTR_BITXOR_VF) { + for (j = 0; j < 3; ++j) { + stmt.opcode = INSTR_BITOR; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j; + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]) + j; + code_push_statement(code, &stmt, instr->context); + stmt.opcode = INSTR_BITAND; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j; + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]) + j; + code_push_statement(code, &stmt, instr->context); + } + stmt.opcode = INSTR_SUB_V; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]); + stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context); + + /* instruction generated */ + continue; + } + if (instr->opcode == VINSTR_COND) { ontrue = instr->bops[0]; onfalse = instr->bops[1]; @@ -2795,13 +2975,13 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.opcode = INSTR_IF; stmt.o2.s1 = (ontrue->code_start) - vec_size(code->statements); if (stmt.o2.s1 != 1) - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); } if (onfalse->generated) { stmt.opcode = INSTR_IFNOT; stmt.o2.s1 = (onfalse->code_start) - vec_size(code->statements); if (stmt.o2.s1 != 1) - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); } if (!ontrue->generated) { if (onfalse->generated) @@ -2821,7 +3001,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc ontrue = tmp; } stidx = vec_size(code->statements); - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); /* on false we jump, so add ontrue-path */ if (!gen_blocks_recursive(code, func, ontrue)) return false; @@ -2853,7 +3033,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.o2.s1 = 0; stmt.o3.s1 = 0; if (stmt.o1.s1 != 1) - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); return true; } else if (stidx+2 == vec_size(code->statements) && code->statements[stidx].o2.s1 == 1) { @@ -2892,7 +3072,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.opcode = type_store_instr[param->vtype]; stmt.o1.u1 = ir_value_code_addr(param); stmt.o2.u1 = OFS_PARM0 + 3 * p; - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); } /* Now handle extparams */ first = vec_size(instr->params); @@ -2921,7 +3101,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.opcode = type_store_instr[param->vtype]; stmt.o1.u1 = ir_value_code_addr(param); stmt.o2.u1 = ir_value_code_addr(targetparam); - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); } stmt.opcode = INSTR_CALL0 + vec_size(instr->params); @@ -2930,7 +3110,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.o1.u1 = ir_value_code_addr(instr->_ops[1]); stmt.o2.u1 = 0; stmt.o3.u1 = 0; - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); retvalue = instr->_ops[0]; if (retvalue && retvalue->store != store_return && @@ -2944,7 +3124,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.o1.u1 = OFS_RETURN; stmt.o2.u1 = ir_value_code_addr(retvalue); stmt.o3.u1 = 0; - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); } continue; } @@ -2994,7 +3174,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc } } - code_push_statement(code, &stmt, instr->context.line); + code_push_statement(code, &stmt, instr->context); } return true; } @@ -3031,11 +3211,16 @@ static bool gen_function_code(code_t *code, ir_function *self) retst->opcode = INSTR_DONE; ++opts_optimizationcount[OPTIM_VOID_RETURN]; } else { + lex_ctx_t last; + stmt.opcode = INSTR_DONE; - stmt.o1.u1 = 0; - stmt.o2.u1 = 0; - stmt.o3.u1 = 0; - code_push_statement(code, &stmt, vec_last(code->linenums)); + stmt.o1.u1 = 0; + stmt.o2.u1 = 0; + stmt.o3.u1 = 0; + last.line = vec_last(code->linenums); + last.column = vec_last(code->columnnums); + + code_push_statement(code, &stmt, last); } return true; } @@ -3168,7 +3353,7 @@ static bool gen_function_extparam_copy(code_t *code, ir_function *self) } stmt.o1.u1 = ir_value_code_addr(ep); stmt.o2.u1 = ir_value_code_addr(self->locals[i]); - code_push_statement(code, &stmt, self->context.line); + code_push_statement(code, &stmt, self->context); } return true; @@ -3193,7 +3378,7 @@ static bool gen_function_varargs_copy(code_t *code, ir_function *self) if (i < 8) { stmt.o1.u1 = OFS_PARM0 + 3*i; stmt.o2.u1 = ir_value_code_addr(self->locals[i]); - code_push_statement(code, &stmt, self->context.line); + code_push_statement(code, &stmt, self->context); continue; } ext = i - 8; @@ -3204,7 +3389,7 @@ static bool gen_function_varargs_copy(code_t *code, ir_function *self) stmt.o1.u1 = ir_value_code_addr(ep); stmt.o2.u1 = ir_value_code_addr(self->locals[i]); - code_push_statement(code, &stmt, self->context.line); + code_push_statement(code, &stmt, self->context); } return true; @@ -3279,6 +3464,14 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global) if (irfun->builtin) return true; + /* + * If there is no definition and the thing is eraseable, we can ignore + * outputting the function to begin with. + */ + if (global->flags & IR_FLAG_ERASEABLE && irfun->code_function_def < 0) { + return true; + } + if (irfun->code_function_def < 0) { irerror(irfun->context, "`%s`: IR global wasn't generated, failed to access function-def", irfun->name); return false; @@ -3379,6 +3572,14 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc { pushdef = true; + /* + * if we're eraseable and the function isn't referenced ignore outputting + * the function. + */ + if (global->flags & IR_FLAG_ERASEABLE && vec_size(global->reads) == 0) { + return true; + } + if (OPTS_OPTIMIZATION(OPTIM_STRIP_CONSTANT_NAMES) && !(global->flags & IR_FLAG_INCLUDE_DEF) && (global->name[0] == '#' || global->cvq == CV_CONST)) @@ -3386,7 +3587,7 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc pushdef = false; } - if (pushdef && global->name) { + if (pushdef) { if (global->name[0] == '#') { if (!self->str_immediate) self->str_immediate = code_genstring(self->code, "IMMEDIATE"); @@ -3652,6 +3853,14 @@ bool ir_builder_generate(ir_builder *self, const char *filename) vec_push(self->code->globals, 0); vec_push(self->code->globals, 0); + /* generate virtual-instruction temps */ + for (i = 0; i < IR_MAX_VINSTR_TEMPS; ++i) { + ir_value_code_setaddr(self->vinstr_temp[i], vec_size(self->code->globals)); + vec_push(self->code->globals, 0); + vec_push(self->code->globals, 0); + vec_push(self->code->globals, 0); + } + /* generate global temps */ self->first_common_globaltemp = vec_size(self->code->globals); for (i = 0; i < self->max_globaltemps; ++i) { @@ -3681,11 +3890,16 @@ bool ir_builder_generate(ir_builder *self, const char *filename) /* DP errors if the last instruction is not an INSTR_DONE. */ if (vec_last(self->code->statements).opcode != INSTR_DONE) { + lex_ctx_t last; + stmt.opcode = INSTR_DONE; - stmt.o1.u1 = 0; - stmt.o2.u1 = 0; - stmt.o3.u1 = 0; - code_push_statement(self->code, &stmt, vec_last(self->code->linenums)); + stmt.o1.u1 = 0; + stmt.o2.u1 = 0; + stmt.o3.u1 = 0; + last.line = vec_last(self->code->linenums); + last.column = vec_last(self->code->columnnums); + + code_push_statement(self->code, &stmt, last); } if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) @@ -3709,16 +3923,11 @@ bool ir_builder_generate(ir_builder *self, const char *filename) memcpy(vec_add(lnofile, 5), ".lno", 5); } - if (!OPTS_OPTION_BOOL(OPTION_QUIET)) { - if (lnofile) - con_out("writing '%s' and '%s'...\n", filename, lnofile); - else - con_out("writing '%s'\n", filename); - } if (!code_write(self->code, filename, lnofile)) { vec_free(lnofile); return false; } + vec_free(lnofile); return true; } @@ -3739,11 +3948,18 @@ static const char *qc_opname(int op) if (op < VINSTR_END) return util_instr_str[op]; switch (op) { - case VINSTR_END: return "END"; - case VINSTR_PHI: return "PHI"; - case VINSTR_JUMP: return "JUMP"; - case VINSTR_COND: return "COND"; - default: return ""; + case VINSTR_END: return "END"; + case VINSTR_PHI: return "PHI"; + case VINSTR_JUMP: return "JUMP"; + case VINSTR_COND: return "COND"; + case VINSTR_BITXOR: return "BITXOR"; + case VINSTR_BITAND_V: return "BITAND_V"; + case VINSTR_BITOR_V: return "BITOR_V"; + case VINSTR_BITXOR_V: return "BITXOR_V"; + case VINSTR_BITAND_VF: return "BITAND_VF"; + case VINSTR_BITOR_VF: return "BITOR_VF"; + case VINSTR_BITXOR_VF: return "BITXOR_VF"; + default: return ""; } }