X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=ir.c;h=c8ac59ce32e41a24cf82b3a565f59663a847aa1f;hb=adb1ab6ce2b6a5d5d3da8be7c667bd3b4d0b39dd;hp=9c760d0f00313b2388a96c34e657d9b2dd8fe307;hpb=be620ff431f2b8a3e0ca18912ea6fd618b74b39f;p=xonotic%2Fgmqcc.git diff --git a/ir.c b/ir.c index 9c760d0..c8ac59c 100644 --- a/ir.c +++ b/ir.c @@ -292,3 +292,336 @@ void ir_instr_op(ir_instr *self, int op, ir_value *v, qbool writing) } self->_ops[op] = v; } + +/*********************************************************************** + *IR Value + */ + +ir_value* ir_value_var(const char *name, int storetype, int vtype) +{ + ir_value *self; + self = (ir_value*)mem_a(sizeof(*self)); + self->vtype = vtype; + self->store = storetype; + MEM_VECTOR_INIT(self, reads); + MEM_VECTOR_INIT(self, writes); + self->has_constval = ifalse; + self->context.file = "<@no context>"; + self->context.line = 0; + self->name = NULL; + ir_value_set_name(self, name); + + MEM_VECTOR_INIT(self, life); + return self; +} +MEM_VECTOR_FUNCTIONS(ir_value, ir_life_entry_t, life) +MEM_VECTOR_FUNCTIONS(ir_value, ir_instr*, reads) +MEM_VECTOR_FUNCTIONS(ir_value, ir_instr*, writes) + +ir_value* ir_value_out(ir_function *owner, const char *name, int storetype, int vtype) +{ + ir_value *v = ir_value_var(name, storetype, vtype); + ir_function_collect_value(owner, v); + return v; +} + +void ir_value_delete(ir_value* self) +{ + mem_d((void*)self->name); + if (self->has_constval) + { + if (self->vtype == qc_string) + mem_d((void*)self->cvalue.vstring); + } + MEM_VECTOR_CLEAR(self, reads); + MEM_VECTOR_CLEAR(self, writes); + MEM_VECTOR_CLEAR(self, life); + mem_d(self); +} + +void ir_value_set_name(ir_value *self, const char *name) +{ + if (self->name) + mem_d((void*)self->name); + self->name = util_strdup(name); +} + +qbool ir_value_set_float(ir_value *self, float f) +{ + if (self->vtype != qc_float) + return ifalse; + self->cvalue.vfloat = f; + self->has_constval = itrue; + return itrue; +} + +qbool ir_value_set_vector(ir_value *self, qc_vec_t v) +{ + if (self->vtype != qc_vector) + return ifalse; + self->cvalue.vvec = v; + self->has_constval = itrue; + return itrue; +} + +qbool ir_value_set_string(ir_value *self, const char *str) +{ + if (self->vtype != qc_string) + return ifalse; + self->cvalue.vstring = util_strdup(str); + self->has_constval = itrue; + return itrue; +} + +qbool ir_value_set_int(ir_value *self, int i) +{ + if (self->vtype != qc_int) + return ifalse; + self->cvalue.vint = i; + self->has_constval = itrue; + return itrue; +} + +qbool ir_value_lives(ir_value *self, size_t at) +{ + size_t i; + for (i = 0; i < self->life_count; ++i) + { + ir_life_entry_t *life = &self->life[i]; + if (life->start <= at && at <= life->end) + return itrue; + if (life->start > at) /* since it's ordered */ + return ifalse; + } + return ifalse; +} + +void ir_value_life_insert(ir_value *self, size_t idx, ir_life_entry_t e) +{ + size_t k; + ir_value_life_add(self, e); /* naive... */ + for (k = self->life_count-1; k > idx; --k) + self->life[k] = self->life[k-1]; + self->life[idx] = e; +} + +qbool ir_value_life_merge(ir_value *self, size_t s) +{ + size_t i; + ir_life_entry_t *life = NULL; + ir_life_entry_t *before = NULL; + ir_life_entry_t new_entry; + + /* Find the first range >= s */ + for (i = 0; i < self->life_count; ++i) + { + before = life; + life = &self->life[i]; + if (life->start > s) + break; + } + /* nothing found? append */ + if (i == self->life_count) { + if (life && life->end+1 == s) + { + /* previous life range can be merged in */ + life->end++; + return itrue; + } + if (life && life->end >= s) + return ifalse; + ir_life_entry_t e; + e.start = e.end = s; + ir_value_life_add(self, e); + return itrue; + } + /* found */ + if (before) + { + if (before->end + 1 == s && + life->start - 1 == s) + { + /* merge */ + before->end = life->end; + ir_value_life_remove(self, i); + return itrue; + } + if (before->end + 1 == s) + { + /* extend before */ + before->end++; + return itrue; + } + /* already contained */ + if (before->end >= s) + return ifalse; + } + /* extend */ + if (life->start - 1 == s) + { + life->start--; + return itrue; + } + /* insert a new entry */ + new_entry.start = new_entry.end = s; + ir_value_life_insert(self, i, new_entry); + return itrue; +} + +/*********************************************************************** + *IR main operations + */ + +qbool ir_block_create_store_op(ir_block *self, int op, ir_value *target, ir_value *what) +{ + if (target->store == qc_localval) { + fprintf(stderr, "cannot store to an SSA value\n"); + return ifalse; + } else { + ir_instr *in = ir_instr_new(self, op); + ir_instr_op(in, 0, target, itrue); + ir_instr_op(in, 1, what, ifalse); + ir_block_instr_add(self, in); + return itrue; + } +} + +qbool ir_block_create_store(ir_block *self, ir_value *target, ir_value *what) +{ + int op = 0; + int vtype; + if (target->vtype == qc_variant) + vtype = what->vtype; + else + vtype = target->vtype; + + switch (vtype) { + case qc_float: + if (what->vtype == qc_int) + op = INSTR_CONV_ITOF; + else + op = INSTR_STORE_F; + break; + case qc_vector: + op = INSTR_STORE_V; + break; + case qc_entity: + op = INSTR_STORE_ENT; + break; + case qc_string: + op = INSTR_STORE_S; + break; + case qc_int: + if (what->vtype == qc_int) + op = INSTR_CONV_FTOI; + else + op = INSTR_STORE_I; + break; + case qc_pointer: + op = INSTR_STORE_I; + break; + } + return ir_block_create_store_op(self, op, target, what); +} + +void ir_block_create_return(ir_block *self, ir_value *v) +{ + ir_instr *in; + if (self->final) { + fprintf(stderr, "block already ended (%s)\n", self->_label); + return; + } + self->final = itrue; + self->is_return = itrue; + in = ir_instr_new(self, INSTR_RETURN); + ir_instr_op(in, 0, v, ifalse); + ir_block_instr_add(self, in); +} + +void ir_block_create_if(ir_block *self, ir_value *v, + ir_block *ontrue, ir_block *onfalse) +{ + ir_instr *in; + if (self->final) { + fprintf(stderr, "block already ended (%s)\n", self->_label); + return; + } + self->final = itrue; + //in = ir_instr_new(self, (v->vtype == qc_string ? INSTR_IF_S : INSTR_IF_F)); + in = ir_instr_new(self, VINSTR_COND); + ir_instr_op(in, 0, v, ifalse); + in->bops[0] = ontrue; + in->bops[1] = onfalse; + ir_block_instr_add(self, in); + + ir_block_exits_add(self, ontrue); + ir_block_exits_add(self, onfalse); + ir_block_entries_add(ontrue, self); + ir_block_entries_add(onfalse, self); +} + +void ir_block_create_jump(ir_block *self, ir_block *to) +{ + ir_instr *in; + if (self->final) { + fprintf(stderr, "block already ended (%s)\n", self->_label); + return; + } + self->final = itrue; + in = ir_instr_new(self, VINSTR_JUMP); + in->bops[0] = to; + ir_block_instr_add(self, in); + + ir_block_exits_add(self, to); + ir_block_entries_add(to, self); +} + +void ir_block_create_goto(ir_block *self, ir_block *to) +{ + ir_instr *in; + if (self->final) { + fprintf(stderr, "block already ended (%s)\n", self->_label); + return; + } + self->final = itrue; + in = ir_instr_new(self, INSTR_GOTO); + in->bops[0] = to; + ir_block_instr_add(self, in); + + ir_block_exits_add(self, to); + ir_block_entries_add(to, self); +} + +ir_instr* ir_block_create_phi(ir_block *self, const char *label, int ot) +{ + ir_value *out; + ir_instr *in; + in = ir_instr_new(self, VINSTR_PHI); + out = ir_value_out(self->owner, label, qc_localval, ot); + ir_instr_op(in, 0, out, itrue); + ir_block_instr_add(self, in); + return in; +} + +ir_value* ir_phi_value(ir_instr *self) +{ + return self->_ops[0]; +} + +void ir_phi_add(ir_instr* self, ir_block *b, ir_value *v) +{ + ir_phi_entry_t pe; + + if (!ir_block_entries_find(self->owner, b, NULL)) { + /* Must not be possible to cause this, otherwise the AST + * is doing something wrong. + */ + fprintf(stderr, "Invalid entry block for PHI\n"); + abort(); + } + + pe.value = v; + pe.from = b; + ir_value_reads_add(v, self); + ir_instr_phi_add(self, pe); +}