return NULL;
}
- fn->value->isconst = true;
+ fn->value->hasvalue = true;
fn->value->outtype = outtype;
fn->value->constval.vfunc = fn;
fn->value->context = fn->context;
vec_push(self->values, v);
}
-ir_block* ir_function_create_block(ir_function *self, const char *label)
+ir_block* ir_function_create_block(lex_ctx ctx, ir_function *self, const char *label)
{
ir_block* bn = ir_block_new(self, label);
- memcpy(&bn->context, &self->context, sizeof(self->context));
+ bn->context = ctx;
vec_push(self->blocks, bn);
return bn;
}
+bool ir_function_pass_tailcall(ir_function *self)
+{
+ size_t b, p;
+
+ for (b = 0; b < vec_size(self->blocks); ++b) {
+ ir_value *funcval;
+ ir_instr *ret, *call, *store = NULL;
+ ir_block *block = self->blocks[b];
+
+ if (!block->final || vec_size(block->instr) < 2)
+ continue;
+
+ ret = block->instr[vec_size(block->instr)-1];
+ if (ret->opcode != INSTR_DONE && ret->opcode != INSTR_RETURN)
+ continue;
+
+ call = block->instr[vec_size(block->instr)-2];
+ if (call->opcode >= INSTR_STORE_F && call->opcode <= INSTR_STORE_FNC) {
+ /* account for the unoptimized
+ * CALL
+ * STORE %return, %tmp
+ * RETURN %tmp
+ * version
+ */
+ if (vec_size(block->instr) < 3)
+ continue;
+
+ store = call;
+ call = block->instr[vec_size(block->instr)-3];
+ }
+
+ if (call->opcode < INSTR_CALL0 || call->opcode > INSTR_CALL8)
+ continue;
+
+ if (store) {
+ /* optimize out the STORE */
+ if (ret->_ops[0] &&
+ ret->_ops[0] == store->_ops[0] &&
+ store->_ops[1] == call->_ops[0])
+ {
+ ++optimization_count[OPTIM_MINOR];
+ call->_ops[0] = store->_ops[0];
+ vec_remove(block->instr, vec_size(block->instr) - 2, 1);
+ ir_instr_delete(store);
+ }
+ else
+ continue;
+ }
+
+ if (!call->_ops[0])
+ continue;
+
+ funcval = call->_ops[1];
+ if (!funcval)
+ continue;
+ if (funcval->vtype != TYPE_FUNCTION || funcval->constval.vfunc != self)
+ continue;
+
+ /* now we have a CALL and a RET, check if it's a tailcall */
+ if (ret->_ops[0] && call->_ops[0] != ret->_ops[0])
+ continue;
+
+ ++optimization_count[OPTIM_TAIL_RECURSION];
+ vec_shrinkby(block->instr, 2);
+
+ block->final = false; /* open it back up */
+
+ /* emite parameter-stores */
+ for (p = 0; p < vec_size(call->params); ++p) {
+ /* assert(call->params_count <= self->locals_count); */
+ if (!ir_block_create_store(block, call->context, self->locals[p], call->params[p])) {
+ irerror(call->context, "failed to create tailcall store instruction for parameter %i", (int)p);
+ return false;
+ }
+ }
+ if (!ir_block_create_jump(block, call->context, self->blocks[0])) {
+ irerror(call->context, "failed to create tailcall jump");
+ return false;
+ }
+
+ ir_instr_delete(call);
+ ir_instr_delete(ret);
+ }
+
+ return true;
+}
+
bool ir_function_finalize(ir_function *self)
{
if (self->builtin)
return true;
+ if (OPTS_OPTIMIZATION(OPTIM_TAIL_RECURSION)) {
+ if (!ir_function_pass_tailcall(self)) {
+ irerror(self->context, "tailcall optimization pass broke something in `%s`", self->name);
+ return false;
+ }
+ }
+
if (!ir_function_naive_phi(self))
return false;
*IR Instructions
*/
-ir_instr* ir_instr_new(ir_block* owner, int op)
+ir_instr* ir_instr_new(lex_ctx ctx, ir_block* owner, int op)
{
ir_instr *self;
self = (ir_instr*)mem_a(sizeof(*self));
return NULL;
self->owner = owner;
- self->context.file = "<@no context>";
- self->context.line = 0;
+ self->context = ctx;
self->opcode = op;
self->_ops[0] = NULL;
self->_ops[1] = NULL;
self->reads = NULL;
self->writes = NULL;
- self->isconst = false;
+ self->cvq = CV_NONE;
+ self->hasvalue = false;
self->context.file = "<@no context>";
self->context.line = 0;
self->name = NULL;
size_t i;
if (self->name)
mem_d((void*)self->name);
- if (self->isconst)
+ if (self->hasvalue)
{
if (self->vtype == TYPE_STRING)
mem_d((void*)self->constval.vstring);
if (self->vtype != TYPE_FLOAT)
return false;
self->constval.vfloat = f;
- self->isconst = true;
+ self->hasvalue = true;
return true;
}
if (self->vtype != TYPE_FUNCTION)
return false;
self->constval.vint = f;
- self->isconst = true;
+ self->hasvalue = true;
return true;
}
if (self->vtype != TYPE_VECTOR)
return false;
self->constval.vvec = v;
- self->isconst = true;
+ self->hasvalue = true;
return true;
}
if (self->vtype != TYPE_FIELD)
return false;
self->constval.vpointer = fld;
- self->isconst = true;
+ self->hasvalue = true;
return true;
}
if (self->vtype != TYPE_STRING)
return false;
self->constval.vstring = ir_strdup(str);
- self->isconst = true;
+ self->hasvalue = true;
return true;
}
if (self->vtype != TYPE_INTEGER)
return false;
self->constval.vint = i;
- self->isconst = true;
+ self->hasvalue = true;
return true;
}
#endif
*IR main operations
*/
-bool ir_block_create_store_op(ir_block *self, int op, ir_value *target, ir_value *what)
+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(self, op);
+ in = ir_instr_new(ctx, self, op);
if (!in)
return false;
return true;
}
-bool ir_block_create_store(ir_block *self, ir_value *target, ir_value *what)
+bool ir_block_create_store(ir_block *self, lex_ctx ctx, ir_value *target, ir_value *what)
{
int op = 0;
int vtype;
op = INSTR_STORE_V;
}
- return ir_block_create_store_op(self, op, target, what);
+ return ir_block_create_store_op(self, ctx, op, target, what);
}
-bool ir_block_create_storep(ir_block *self, ir_value *target, ir_value *what)
+bool ir_block_create_storep(ir_block *self, lex_ctx ctx, ir_value *target, ir_value *what)
{
int op = 0;
int vtype;
op = INSTR_STOREP_V;
}
- return ir_block_create_store_op(self, op, target, what);
+ return ir_block_create_store_op(self, ctx, op, target, what);
}
-bool ir_block_create_return(ir_block *self, ir_value *v)
+bool ir_block_create_return(ir_block *self, lex_ctx ctx, ir_value *v)
{
ir_instr *in;
if (self->final) {
}
self->final = true;
self->is_return = true;
- in = ir_instr_new(self, INSTR_RETURN);
+ in = ir_instr_new(ctx, self, INSTR_RETURN);
if (!in)
return false;
return true;
}
-bool ir_block_create_if(ir_block *self, ir_value *v,
+bool ir_block_create_if(ir_block *self, lex_ctx ctx, ir_value *v,
ir_block *ontrue, ir_block *onfalse)
{
ir_instr *in;
return false;
}
self->final = true;
- /*in = ir_instr_new(self, (v->vtype == TYPE_STRING ? INSTR_IF_S : INSTR_IF_F));*/
- in = ir_instr_new(self, VINSTR_COND);
+ /*in = ir_instr_new(ctx, self, (v->vtype == TYPE_STRING ? INSTR_IF_S : INSTR_IF_F));*/
+ in = ir_instr_new(ctx, self, VINSTR_COND);
if (!in)
return false;
return true;
}
-bool ir_block_create_jump(ir_block *self, ir_block *to)
+bool ir_block_create_jump(ir_block *self, lex_ctx ctx, ir_block *to)
{
ir_instr *in;
if (self->final) {
return false;
}
self->final = true;
- in = ir_instr_new(self, VINSTR_JUMP);
+ in = ir_instr_new(ctx, self, VINSTR_JUMP);
if (!in)
return false;
return true;
}
-bool ir_block_create_goto(ir_block *self, ir_block *to)
+bool ir_block_create_goto(ir_block *self, lex_ctx ctx, ir_block *to)
{
ir_instr *in;
if (self->final) {
return false;
}
self->final = true;
- in = ir_instr_new(self, INSTR_GOTO);
+ in = ir_instr_new(ctx, self, INSTR_GOTO);
if (!in)
return false;
return true;
}
-ir_instr* ir_block_create_phi(ir_block *self, const char *label, int ot)
+ir_instr* ir_block_create_phi(ir_block *self, lex_ctx ctx, const char *label, int ot)
{
ir_value *out;
ir_instr *in;
- in = ir_instr_new(self, VINSTR_PHI);
+ in = ir_instr_new(ctx, self, VINSTR_PHI);
if (!in)
return NULL;
out = ir_value_out(self->owner, label, store_value, ot);
}
/* call related code */
-ir_instr* ir_block_create_call(ir_block *self, const char *label, ir_value *func)
+ir_instr* ir_block_create_call(ir_block *self, lex_ctx ctx, const char *label, ir_value *func)
{
ir_value *out;
ir_instr *in;
- in = ir_instr_new(self, INSTR_CALL0);
+ in = ir_instr_new(ctx, self, INSTR_CALL0);
if (!in)
return NULL;
out = ir_value_out(self->owner, label, (func->outtype == TYPE_VOID) ? store_return : store_value, func->outtype);
/* binary op related code */
-ir_value* ir_block_create_binop(ir_block *self,
+ir_value* ir_block_create_binop(ir_block *self, lex_ctx ctx,
const char *label, int opcode,
ir_value *left, ir_value *right)
{
return NULL;
}
- return ir_block_create_general_instr(self, label, opcode, left, right, ot);
+ return ir_block_create_general_instr(self, ctx, label, opcode, left, right, ot);
}
-ir_value* ir_block_create_unary(ir_block *self,
+ir_value* ir_block_create_unary(ir_block *self, lex_ctx ctx,
const char *label, int opcode,
ir_value *operand)
{
}
/* let's use the general instruction creator and pass NULL for OPB */
- return ir_block_create_general_instr(self, label, opcode, operand, NULL, ot);
+ return ir_block_create_general_instr(self, ctx, label, opcode, operand, NULL, ot);
}
-ir_value* ir_block_create_general_instr(ir_block *self, const char *label,
+ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx ctx, const char *label,
int op, ir_value *a, ir_value *b, int outype)
{
ir_instr *instr;
if (!out)
return NULL;
- instr = ir_instr_new(self, op);
+ instr = ir_instr_new(ctx, self, op);
if (!instr) {
ir_value_delete(out);
return NULL;
return NULL;
}
-ir_value* ir_block_create_fieldaddress(ir_block *self, const char *label, ir_value *ent, ir_value *field)
+ir_value* ir_block_create_fieldaddress(ir_block *self, lex_ctx ctx, const char *label, ir_value *ent, ir_value *field)
{
ir_value *v;
if (field->vtype != TYPE_FIELD)
return NULL;
- v = ir_block_create_general_instr(self, label, INSTR_ADDRESS, ent, field, TYPE_POINTER);
+ v = ir_block_create_general_instr(self, ctx, label, INSTR_ADDRESS, ent, field, TYPE_POINTER);
v->fieldtype = field->fieldtype;
return v;
}
-ir_value* ir_block_create_load_from_ent(ir_block *self, const char *label, ir_value *ent, ir_value *field, int outype)
+ir_value* ir_block_create_load_from_ent(ir_block *self, lex_ctx ctx, const char *label, ir_value *ent, ir_value *field, int outype)
{
int op;
if (ent->vtype != TYPE_ENTITY)
return NULL;
}
- return ir_block_create_general_instr(self, label, op, ent, field, outype);
+ return ir_block_create_general_instr(self, ctx, label, op, ent, field, outype);
}
-ir_value* ir_block_create_add(ir_block *self,
+ir_value* ir_block_create_add(ir_block *self, lex_ctx ctx,
const char *label,
ir_value *left, ir_value *right)
{
return NULL;
}
}
- return ir_block_create_binop(self, label, op, left, right);
+ return ir_block_create_binop(self, ctx, label, op, left, right);
}
-ir_value* ir_block_create_sub(ir_block *self,
+ir_value* ir_block_create_sub(ir_block *self, lex_ctx ctx,
const char *label,
ir_value *left, ir_value *right)
{
return NULL;
}
}
- return ir_block_create_binop(self, label, op, left, right);
+ return ir_block_create_binop(self, ctx, label, op, left, right);
}
-ir_value* ir_block_create_mul(ir_block *self,
+ir_value* ir_block_create_mul(ir_block *self, lex_ctx ctx,
const char *label,
ir_value *left, ir_value *right)
{
return NULL;
}
}
- return ir_block_create_binop(self, label, op, left, right);
+ return ir_block_create_binop(self, ctx, label, op, left, right);
}
-ir_value* ir_block_create_div(ir_block *self,
+ir_value* ir_block_create_div(ir_block *self, lex_ctx ctx,
const char *label,
ir_value *left, ir_value *right)
{
return NULL;
}
}
- return ir_block_create_binop(self, label, op, left, right);
+ return ir_block_create_binop(self, ctx, label, op, left, right);
}
/* PHI resolving breaks the SSA, and must thus be the last
vec_pop(b->instr);
b->final = false;
instr->_ops[0]->store = store_global;
- if (!ir_block_create_store(b, instr->_ops[0], v))
+ if (!ir_block_create_store(b, instr->context, instr->_ops[0], v))
return false;
instr->_ops[0]->store = store_value;
vec_push(b->instr, prevjump);
static bool gen_global_field(ir_value *global)
{
- if (global->isconst)
+ if (global->hasvalue)
{
ir_value *fld = global->constval.vpointer;
if (!fld) {
return false;
}
- /* Now, in this case, a relocation would be impossible to code
- * since it looks like this:
- * .vector v = origin; <- parse error, wtf is 'origin'?
- * .vector origin;
- *
- * But we will need a general relocation support later anyway
- * for functions... might as well support that here.
- */
- if (!fld->code.globaladdr) {
- irerror(global->context, "FIXME: Relocation support");
- return false;
- }
-
/* copy the field's value */
ir_value_code_setaddr(global, vec_size(code_globals));
- vec_push(code_globals, code_globals[fld->code.globaladdr]);
+ vec_push(code_globals, fld->code.fieldaddr);
if (global->fieldtype == TYPE_VECTOR) {
- vec_push(code_globals, code_globals[fld->code.globaladdr]+1);
- vec_push(code_globals, code_globals[fld->code.globaladdr]+2);
+ vec_push(code_globals, fld->code.fieldaddr+1);
+ vec_push(code_globals, fld->code.fieldaddr+2);
}
}
else
static bool gen_global_pointer(ir_value *global)
{
- if (global->isconst)
+ if (global->hasvalue)
{
ir_value *target = global->constval.vpointer;
if (!target) {
stmt.o1.s1 = (target->code_start) - vec_size(code_statements);
stmt.o2.s1 = 0;
stmt.o3.s1 = 0;
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
/* no further instructions can be in this block */
return true;
if (ontrue->generated) {
stmt.opcode = INSTR_IF;
stmt.o2.s1 = (ontrue->code_start) - vec_size(code_statements);
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
}
if (onfalse->generated) {
stmt.opcode = INSTR_IFNOT;
stmt.o2.s1 = (onfalse->code_start) - vec_size(code_statements);
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
}
if (!ontrue->generated) {
if (onfalse->generated) {
ontrue = tmp;
}
stidx = vec_size(code_statements);
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
/* on false we jump, so add ontrue-path */
if (!gen_blocks_recursive(func, ontrue))
return false;
stmt.o1.s1 = (onfalse->code_start) - vec_size(code_statements);
stmt.o2.s1 = 0;
stmt.o3.s1 = 0;
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
return true;
}
/* if not, generate now */
stmt.opcode = type_store_instr[param->vtype];
stmt.o1.u1 = ir_value_code_addr(param);
stmt.o2.u1 = OFS_PARM0 + 3 * p;
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
}
/* Now handle extparams */
first = vec_size(instr->params);
stmt.opcode = type_store_instr[param->vtype];
stmt.o1.u1 = ir_value_code_addr(param);
stmt.o2.u1 = ir_value_code_addr(targetparam);
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
}
stmt.opcode = INSTR_CALL0 + vec_size(instr->params);
stmt.o1.u1 = ir_value_code_addr(instr->_ops[1]);
stmt.o2.u1 = 0;
stmt.o3.u1 = 0;
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
retvalue = instr->_ops[0];
if (retvalue && retvalue->store != store_return && vec_size(retvalue->life))
stmt.o1.u1 = OFS_RETURN;
stmt.o2.u1 = ir_value_code_addr(retvalue);
stmt.o3.u1 = 0;
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
}
continue;
}
stmt.o3.u1 = 0;
}
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, instr->context.line);
}
return true;
}
stmt.o1.u1 = 0;
stmt.o2.u1 = 0;
stmt.o3.u1 = 0;
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, vec_last(code_linenums));
return true;
}
size_t i;
size_t local_var_end;
- if (!global->isconst || (!global->constval.vfunc))
+ if (!global->hasvalue || (!global->constval.vfunc))
{
irerror(global->context, "Invalid state of function-global: not constant: %s", global->name);
return false;
fun.nargs = 8;
for (i = 0;i < 8; ++i) {
- if (i >= fun.nargs)
+ if ((int32_t)i >= fun.nargs)
fun.argsize[i] = 0;
else
fun.argsize[i] = type_sizeof[irfun->params[i]];
}
stmt.o1.u1 = ir_value_code_addr(ep);
stmt.o2.u1 = ir_value_code_addr(self->locals[i]);
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, self->context.line);
}
return true;
irfun = global->constval.vfunc;
if (!irfun) {
- irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER,
- "function `%s` has no body and in QC implicitly becomes a function-pointer", global->name);
+ if (global->cvq == CV_NONE) {
+ irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER,
+ "function `%s` has no body and in QC implicitly becomes a function-pointer", global->name);
+ }
/* this was a function pointer, don't generate code for those */
return true;
}
case TYPE_FLOAT:
{
ir_value_code_setaddr(global, vec_size(code_globals));
- if (global->isconst) {
+ if (global->hasvalue) {
iptr = (int32_t*)&global->constval.ivec[0];
vec_push(code_globals, *iptr);
} else {
case TYPE_STRING:
{
ir_value_code_setaddr(global, vec_size(code_globals));
- if (global->isconst) {
+ if (global->hasvalue) {
vec_push(code_globals, code_genstring(global->constval.vstring));
} else {
vec_push(code_globals, 0);
{
size_t d;
ir_value_code_setaddr(global, vec_size(code_globals));
- if (global->isconst) {
+ if (global->hasvalue) {
iptr = (int32_t*)&global->constval.ivec[0];
vec_push(code_globals, iptr[0]);
if (global->code.globaladdr < 0)
}
case TYPE_FUNCTION:
ir_value_code_setaddr(global, vec_size(code_globals));
- if (!global->isconst) {
+ if (!global->hasvalue) {
vec_push(code_globals, 0);
if (global->code.globaladdr < 0)
return false;
}
}
+static void ir_builder_prepare_field(ir_value *field)
+{
+ field->code.fieldaddr = code_alloc_field(type_sizeof[field->fieldtype]);
+}
+
static bool ir_builder_gen_field(ir_builder *self, ir_value *field)
{
prog_section_def def;
return false;
}
- fld.offset = code_alloc_field(type_sizeof[field->fieldtype]);
+ fld.offset = field->code.fieldaddr;
vec_push(code_fields, fld);
{
prog_section_statement stmt;
size_t i;
+ char *lnofile = NULL;
code_init();
+ for (i = 0; i < vec_size(self->fields); ++i)
+ {
+ ir_builder_prepare_field(self->fields[i]);
+ }
+
for (i = 0; i < vec_size(self->globals); ++i)
{
if (!ir_builder_gen_global(self, self->globals[i], false)) {
stmt.o1.u1 = 0;
stmt.o2.u1 = 0;
stmt.o3.u1 = 0;
- vec_push(code_statements, stmt);
+ code_push_statement(&stmt, vec_last(code_linenums));
+
+ if (opts_pp_only)
+ return true;
- if (!opts_pp_only)
- con_out("writing '%s'...\n", filename);
- return code_write(filename);
+ if (vec_size(code_statements) != vec_size(code_linenums)) {
+ con_err("Linecounter wrong: %lu != %lu\n",
+ (unsigned long)vec_size(code_statements),
+ (unsigned long)vec_size(code_linenums));
+ } else if (OPTS_FLAG(LNO)) {
+ char *dot;
+ size_t filelen = strlen(filename);
+
+ memcpy(vec_add(lnofile, filelen+1), filename, filelen+1);
+ dot = strrchr(lnofile, '.');
+ if (!dot) {
+ vec_pop(lnofile);
+ } else {
+ vec_shrinkto(lnofile, dot - lnofile);
+ }
+ memcpy(vec_add(lnofile, 5), ".lno", 5);
+ }
+
+ if (lnofile)
+ con_out("writing '%s' and '%s'...\n", filename, lnofile);
+ else
+ con_out("writing '%s'\n", filename);
+ if (!code_write(filename, lnofile)) {
+ vec_free(lnofile);
+ return false;
+ }
+ vec_free(lnofile);
+ return true;
}
/***********************************************************************
for (i = 0; i < vec_size(b->globals); ++i)
{
oprintf("global ");
- if (b->globals[i]->isconst)
+ if (b->globals[i]->hasvalue)
oprintf("%s = ", b->globals[i]->name);
ir_value_dump(b->globals[i], oprintf);
oprintf("\n");
void ir_value_dump(ir_value* v, int (*oprintf)(const char*, ...))
{
- if (v->isconst) {
+ if (v->hasvalue) {
switch (v->vtype) {
default:
case TYPE_VOID: