X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=ir.c;h=8d52898bd5a957dece321cd5fc33b8d3e44f9257;hp=f92d5524eb12281e4d13fe1044f906e150718e41;hb=refs%2Ftags%2Fxonotic-v0.8.1;hpb=81df8fa139791a4c96af6ff261c84364b918a1b2 diff --git a/ir.c b/ir.c index f92d552..8d52898 100644 --- a/ir.c +++ b/ir.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 + * Copyright (C) 2012, 2013, 2014, 2015 * Wolfgang Bumiller * Dale Weiler * @@ -355,6 +355,8 @@ ir_builder* ir_builder_new(const char *modulename) } self->reserved_va_count = NULL; + self->coverage_func = NULL; + self->code = code_init(); return self; @@ -602,6 +604,10 @@ ir_block* ir_function_create_block(lex_ctx_t ctx, ir_function *self, const char ir_block* bn = ir_block_new(self, label); bn->context = ctx; vec_push(self->blocks, bn); + + if ((self->flags & IR_FLAG_BLOCK_COVERAGE) && self->owner->coverage_func) + (void)ir_block_create_call(bn, ctx, NULL, self->owner->coverage_func, false); + return bn; } @@ -613,7 +619,7 @@ static bool instr_is_operation(uint16_t op) (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) || (op >= INSTR_AND && op <= INSTR_BITOR) || (op >= INSTR_CALL0 && op <= INSTR_CALL8) || - (op >= VINSTR_BITAND_V && op <= VINSTR_CROSS) ); + (op >= VINSTR_BITAND_V && op <= VINSTR_NEG_V) ); } static bool ir_function_pass_peephole(ir_function *self) @@ -1115,6 +1121,20 @@ ir_value* ir_value_var(const char *name, int storetype, int vtype) return self; } +/* helper function */ +static ir_value* ir_builder_imm_float(ir_builder *self, float value, bool add_to_list) { + ir_value *v = ir_value_var("#IMMEDIATE", store_global, TYPE_FLOAT); + v->flags |= IR_FLAG_ERASABLE; + v->hasvalue = true; + v->cvq = CV_CONST; + v->constval.vfloat = value; + + vec_push(self->globals, v); + if (add_to_list) + vec_push(self->const_floats, v); + return v; +} + ir_value* ir_value_vector_member(ir_value *self, unsigned int member) { char *name; @@ -1200,9 +1220,11 @@ void ir_value_delete(ir_value* self) if (self->vtype == TYPE_STRING) mem_d((void*)self->constval.vstring); } - for (i = 0; i < 3; ++i) { - if (self->members[i]) - ir_value_delete(self->members[i]); + if (!(self->flags & IR_FLAG_SPLIT_VECTOR)) { + for (i = 0; i < 3; ++i) { + if (self->members[i]) + ir_value_delete(self->members[i]); + } } vec_free(self->reads); vec_free(self->writes); @@ -1531,6 +1553,26 @@ bool ir_block_create_store_op(ir_block *self, lex_ctx_t ctx, int op, ir_value *t return true; } +bool ir_block_create_state_op(ir_block *self, lex_ctx_t ctx, ir_value *frame, ir_value *think) +{ + ir_instr *in; + if (!ir_check_unreachable(self)) + return false; + + in = ir_instr_new(ctx, self, INSTR_STATE); + if (!in) + return false; + + if (!ir_instr_op(in, 0, frame, false) || + !ir_instr_op(in, 1, think, false)) + { + ir_instr_delete(in); + return false; + } + vec_push(self->instr, in); + return true; +} + static bool ir_block_create_store(ir_block *self, lex_ctx_t ctx, ir_value *target, ir_value *what) { int op = 0; @@ -1583,7 +1625,9 @@ bool ir_block_create_return(ir_block *self, lex_ctx_t ctx, ir_value *v) ir_instr *in; if (!ir_check_unreachable(self)) return false; + self->final = true; + self->is_return = true; in = ir_instr_new(ctx, self, INSTR_RETURN); if (!in) @@ -1878,16 +1922,20 @@ ir_value* ir_block_create_unary(ir_block *self, lex_ctx_t ctx, case INSTR_NOT_V: case INSTR_NOT_S: case INSTR_NOT_ENT: - case INSTR_NOT_FNC: -#if 0 - case INSTR_NOT_I: -#endif + case INSTR_NOT_FNC: /* + case INSTR_NOT_I: */ ot = TYPE_FLOAT; break; - /* QC doesn't have other unary operations. We expect extensions to fill - * the above list, otherwise we assume out-type = in-type, eg for an - * unary minus + + /* + * Negation for virtual instructions is emulated with 0-value. Thankfully + * the operand for 0 already exists so we just source it from here. */ + case VINSTR_NEG_F: + return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_F, NULL, operand, ot); + case VINSTR_NEG_V: + return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, NULL, operand, TYPE_VECTOR); + default: ot = operand->vtype; break; @@ -3097,7 +3145,21 @@ 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); + + if (param->vtype == TYPE_VECTOR && (param->flags & IR_FLAG_SPLIT_VECTOR)) { + /* fetch 3 separate floats */ + stmt.opcode = INSTR_STORE_F; + stmt.o1.u1 = ir_value_code_addr(param->members[0]); + code_push_statement(code, &stmt, instr->context); + stmt.o2.u1++; + stmt.o1.u1 = ir_value_code_addr(param->members[1]); + code_push_statement(code, &stmt, instr->context); + stmt.o2.u1++; + stmt.o1.u1 = ir_value_code_addr(param->members[2]); + code_push_statement(code, &stmt, instr->context); + } + else + code_push_statement(code, &stmt, instr->context); } /* Now handle extparams */ first = vec_size(instr->params); @@ -3126,7 +3188,20 @@ 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); + if (param->vtype == TYPE_VECTOR && (param->flags & IR_FLAG_SPLIT_VECTOR)) { + /* fetch 3 separate floats */ + stmt.opcode = INSTR_STORE_F; + stmt.o1.u1 = ir_value_code_addr(param->members[0]); + code_push_statement(code, &stmt, instr->context); + stmt.o2.u1++; + stmt.o1.u1 = ir_value_code_addr(param->members[1]); + code_push_statement(code, &stmt, instr->context); + stmt.o2.u1++; + stmt.o1.u1 = ir_value_code_addr(param->members[2]); + code_push_statement(code, &stmt, instr->context); + } + else + code_push_statement(code, &stmt, instr->context); } stmt.opcode = INSTR_CALL0 + vec_size(instr->params); @@ -3155,8 +3230,14 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc } if (instr->opcode == INSTR_STATE) { - irerror(block->context, "TODO: state instruction"); - return false; + stmt.opcode = instr->opcode; + if (instr->_ops[0]) + stmt.o1.u1 = ir_value_code_addr(instr->_ops[0]); + if (instr->_ops[1]) + stmt.o2.u1 = ir_value_code_addr(instr->_ops[1]); + stmt.o3.u1 = 0; + code_push_statement(code, &stmt, instr->context); + continue; } stmt.opcode = instr->opcode; @@ -3500,7 +3581,7 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global) * 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) { + if (global->flags & IR_FLAG_ERASABLE && irfun->code_function_def < 0) { return true; } @@ -3597,6 +3678,10 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc prog_section_def_t def; bool pushdef = opts.optimizeoff; + /* we don't generate split-vectors */ + if (global->vtype == TYPE_VECTOR && (global->flags & IR_FLAG_SPLIT_VECTOR)) + return true; + def.type = global->vtype; def.offset = vec_size(self->code->globals); def.name = 0; @@ -3608,7 +3693,7 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc * 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) { + if (global->flags & IR_FLAG_ERASABLE && vec_size(global->reads) == 0) { return true; } @@ -3687,6 +3772,8 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc { ir_value_code_setaddr(global, vec_size(self->code->globals)); if (global->hasvalue) { + if (global->cvq == CV_CONST && !vec_size(global->reads)) + return true; iptr = (int32_t*)&global->constval.ivec[0]; vec_push(self->code->globals, *iptr); } else { @@ -3702,7 +3789,10 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc { ir_value_code_setaddr(global, vec_size(self->code->globals)); if (global->hasvalue) { - uint32_t load = code_genstring(self->code, global->constval.vstring); + uint32_t load; + if (global->cvq == CV_CONST && !vec_size(global->reads)) + return true; + load = code_genstring(self->code, global->constval.vstring); vec_push(self->code->globals, load); } else { vec_push(self->code->globals, 0); @@ -3847,12 +3937,113 @@ static bool ir_builder_gen_field(ir_builder *self, ir_value *field) return field->code.globaladdr >= 0; } +static void ir_builder_collect_reusables(ir_builder *builder) { + size_t i; + ir_value **reusables = NULL; + for (i = 0; i < vec_size(builder->globals); ++i) { + ir_value *value = builder->globals[i]; + if (value->vtype != TYPE_FLOAT || !value->hasvalue) + continue; + if (value->cvq == CV_CONST || (value->name && value->name[0] == '#')) { + vec_push(reusables, value); + } + } + builder->const_floats = reusables; +} + +static void ir_builder_split_vector(ir_builder *self, ir_value *vec) { + size_t i, count; + ir_value* found[3] = { NULL, NULL, NULL }; + + /* must not be written to */ + if (vec_size(vec->writes)) + return; + /* must not be trying to access individual members */ + if (vec->members[0] || vec->members[1] || vec->members[2]) + return; + /* should be actually used otherwise it won't be generated anyway */ + count = vec_size(vec->reads); + if (!count) + return; + + /* may only be used directly as function parameters, so if we find some other instruction cancel */ + for (i = 0; i != count; ++i) { + /* we only split vectors if they're used directly as parameter to a call only! */ + ir_instr *user = vec->reads[i]; + if ((user->opcode < INSTR_CALL0 || user->opcode > INSTR_CALL8) && user->opcode != VINSTR_NRCALL) + return; + } + + vec->flags |= IR_FLAG_SPLIT_VECTOR; + + /* find existing floats making up the split */ + count = vec_size(self->const_floats); + for (i = 0; i != count; ++i) { + ir_value *c = self->const_floats[i]; + if (!found[0] && c->constval.vfloat == vec->constval.vvec.x) + found[0] = c; + if (!found[1] && c->constval.vfloat == vec->constval.vvec.y) + found[1] = c; + if (!found[2] && c->constval.vfloat == vec->constval.vvec.z) + found[2] = c; + if (found[0] && found[1] && found[2]) + break; + } + + /* generate floats for not yet found components */ + if (!found[0]) + found[0] = ir_builder_imm_float(self, vec->constval.vvec.x, true); + if (!found[1]) { + if (vec->constval.vvec.y == vec->constval.vvec.x) + found[1] = found[0]; + else + found[1] = ir_builder_imm_float(self, vec->constval.vvec.y, true); + } + if (!found[2]) { + if (vec->constval.vvec.z == vec->constval.vvec.x) + found[2] = found[0]; + else if (vec->constval.vvec.z == vec->constval.vvec.y) + found[2] = found[1]; + else + found[2] = ir_builder_imm_float(self, vec->constval.vvec.z, true); + } + + /* the .members array should be safe to use here. */ + vec->members[0] = found[0]; + vec->members[1] = found[1]; + vec->members[2] = found[2]; + + /* register the readers for these floats */ + count = vec_size(vec->reads); + for (i = 0; i != count; ++i) { + vec_push(found[0]->reads, vec->reads[i]); + vec_push(found[1]->reads, vec->reads[i]); + vec_push(found[2]->reads, vec->reads[i]); + } +} + +static void ir_builder_split_vectors(ir_builder *self) { + size_t i, count = vec_size(self->globals); + for (i = 0; i != count; ++i) { + ir_value *v = self->globals[i]; + if (v->vtype != TYPE_VECTOR || !v->name || v->name[0] != '#') + continue; + ir_builder_split_vector(self, self->globals[i]); + } +} + bool ir_builder_generate(ir_builder *self, const char *filename) { prog_section_statement_t stmt; size_t i; char *lnofile = NULL; + if (OPTS_FLAG(SPLIT_VECTOR_PARAMETERS)) { + ir_builder_collect_reusables(self); + if (vec_size(self->const_floats) > 0) + ir_builder_split_vectors(self); + } + for (i = 0; i < vec_size(self->fields); ++i) { ir_builder_prepare_field(self->code, self->fields[i]); @@ -3918,7 +4109,7 @@ bool ir_builder_generate(ir_builder *self, const char *filename) } if (vec_size(self->code->globals) >= 65536) { - irerror(vec_last(self->globals)->context, "This progs file would require more globals than the metadata can handle. Bailing out."); + irerror(vec_last(self->globals)->context, "This progs file would require more globals than the metadata can handle (%u). Bailing out.", (unsigned int)vec_size(self->code->globals)); return false; } @@ -3973,10 +4164,6 @@ bool ir_builder_generate(ir_builder *self, const char *filename) #define IND_BUFSZ 1024 -#ifdef _MSC_VER -# define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE) -#endif - static const char *qc_opname(int op) { if (op < 0) return ""; @@ -3995,6 +4182,8 @@ static const char *qc_opname(int op) case VINSTR_BITOR_VF: return "BITOR_VF"; case VINSTR_BITXOR_VF: return "BITXOR_VF"; case VINSTR_CROSS: return "CROSS"; + case VINSTR_NEG_F: return "NEG_F"; + case VINSTR_NEG_V: return "NEG_V"; default: return ""; } } @@ -4033,7 +4222,7 @@ void ir_function_dump(ir_function *f, char *ind, return; } oprintf("%sfunction %s\n", ind, f->name); - strncat(ind, "\t", IND_BUFSZ-1); + util_strncat(ind, "\t", IND_BUFSZ-1); if (vec_size(f->locals)) { oprintf("%s%i locals:\n", ind, (int)vec_size(f->locals)); @@ -4129,7 +4318,7 @@ void ir_block_dump(ir_block* b, char *ind, { size_t i; oprintf("%s:%s\n", ind, b->label); - strncat(ind, "\t", IND_BUFSZ-1); + util_strncat(ind, "\t", IND_BUFSZ-1); if (b->instr && b->instr[0]) oprintf("%s (%i) [entry]\n", ind, (int)(b->instr[0]->eid-1)); @@ -4163,7 +4352,7 @@ void ir_instr_dump(ir_instr *in, char *ind, return; } - strncat(ind, "\t", IND_BUFSZ-1); + util_strncat(ind, "\t", IND_BUFSZ-1); if (in->_ops[0] && (in->_ops[1] || in->_ops[2])) { ir_value_dump(in->_ops[0], oprintf);