X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=ast.c;h=49446a111d7035085c90981018c91b02e48f5e06;hb=214c063b3f53430960c256372914f1b90d1457eb;hp=4b439d4eabb10b67db259c2300d0d1ee964adeab;hpb=da9a4fef295154759ca41bf7932b52bb26432502;p=xonotic%2Fgmqcc.git diff --git a/ast.c b/ast.c index 4b439d4..49446a1 100644 --- a/ast.c +++ b/ast.c @@ -36,7 +36,7 @@ ( (ast_node*)self )->node.destroy = (ast_node_delete*)destroyfn /* It must not be possible to get here. */ -static void _ast_node_destroy(ast_node *self) +static GMQCC_NORETURN void _ast_node_destroy(ast_node *self) { fprintf(stderr, "ast node missing destroy()\n"); abort(); @@ -238,8 +238,10 @@ ast_ifthen* ast_ifthen_new(lex_ctx ctx, ast_expression *cond, ast_expression *on void ast_ifthen_delete(ast_ifthen *self) { ast_unref(self->cond); - ast_unref(self->on_true); - ast_unref(self->on_false); + if (self->on_true) + ast_unref(self->on_true); + if (self->on_false) + ast_unref(self->on_false); ast_expression_delete((ast_expression*)self); mem_d(self); } @@ -271,6 +273,41 @@ void ast_ternary_delete(ast_ternary *self) mem_d(self); } +ast_loop* ast_loop_new(lex_ctx ctx, + ast_expression *initexpr, + ast_expression *precond, + ast_expression *postcond, + ast_expression *increment, + ast_expression *body) +{ + ast_instantiate(ast_loop, ctx, ast_loop_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_loop_codegen); + + self->initexpr = initexpr; + self->precond = precond; + self->postcond = postcond; + self->increment = increment; + self->body = body; + + return self; +} + +void ast_loop_delete(ast_loop *self) +{ + if (self->initexpr) + ast_unref(self->initexpr); + if (self->precond) + ast_unref(self->precond); + if (self->postcond) + ast_unref(self->postcond); + if (self->increment) + ast_unref(self->increment); + if (self->body) + ast_unref(self->body); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + ast_store* ast_store_new(lex_ctx ctx, int op, ast_value *dest, ast_expression *source) { @@ -340,6 +377,9 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype) self->ir_func = NULL; self->curblock = NULL; + self->breakblock = NULL; + self->continueblock = NULL; + vtype->isconst = true; vtype->constval.vfunc = self; @@ -368,10 +408,36 @@ void ast_function_delete(ast_function *self) mem_d(self); } -const char* ast_function_label(ast_function *self) +static void ast_util_hexitoa(char *buf, size_t size, unsigned int num) +{ + unsigned int base = 10; +#define checknul() do { if (size == 1) { *buf = 0; return; } } while (0) +#define addch(x) do { *buf++ = (x); --size; checknul(); } while (0) + if (size < 1) + return; + checknul(); + if (!num) + addch('0'); + else { + while (num) + { + int digit = num % base; + num /= base; + addch('0' + digit); + } + } + + *buf = 0; +#undef addch +#undef checknul +} + +const char* ast_function_label(ast_function *self, const char *prefix) { size_t id = (self->labelcount++); - sprintf(self->labelbuf, "label%8u", (unsigned int)id); + size_t len = strlen(prefix); + strncpy(self->labelbuf, prefix, sizeof(self->labelbuf)); + ast_util_hexitoa(self->labelbuf + len, sizeof(self->labelbuf)-len, id); return self->labelbuf; } @@ -519,6 +585,19 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir) if (!(*gen)((ast_expression*)self->blocks[i], self, false, &dummy)) return false; } + + /* TODO: check return types */ + if (!self->curblock->is_return) + { + if (!self->vtype->expression.next || + self->vtype->expression.next->expression.vtype == TYPE_VOID) + return ir_block_create_return(self->curblock, NULL); + else + { + /* error("missing return"); */ + return false; + } + } return true; } @@ -613,7 +692,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va if (!(*cgen)((ast_expression*)(self->right), func, false, &right)) return false; - *out = ir_block_create_binop(func->curblock, ast_function_label(func), + *out = ir_block_create_binop(func->curblock, ast_function_label(func, "bin"), self->op, left, right); if (!*out) return false; @@ -623,17 +702,133 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va bool ast_entfield_codegen(ast_entfield *self, ast_function *func, bool lvalue, ir_value **out) { - return false; + ast_expression_codegen *cgen; + ir_value *ent, *field; + + /* This function needs to take the 'lvalue' flag into account! + * As lvalue we provide a field-pointer, as rvalue we provide the + * value in a temp. + */ + + cgen = self->entity->expression.codegen; + if (!(*cgen)((ast_expression*)(self->entity), func, false, &ent)) + return false; + + cgen = self->field->expression.codegen; + if (!(*cgen)((ast_expression*)(self->field), func, false, &field)) + return false; + + if (lvalue) { + /* address! */ + *out = ir_block_create_fieldaddress(func->curblock, ast_function_label(func, "efa"), + ent, field); + } else { + *out = ir_block_create_load_from_ent(func->curblock, ast_function_label(func, "efv"), + ent, field, self->expression.vtype); + } + if (!*out) + return false; + + /* Hm that should be it... */ + return true; } bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_value **out) { - if (out) *out = NULL; - return false; + ast_expression_codegen *cgen; + + ir_value *condval; + ir_value *dummy; + + ir_block *cond = func->curblock; + ir_block *ontrue; + ir_block *onfalse; + ir_block *merge; + + /* We don't output any value, thus also don't care about r/lvalue */ + (void)out; + (void)lvalue; + + /* generate the condition */ + func->curblock = cond; + cgen = self->cond->expression.codegen; + if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval)) + return false; + + /* on-true path */ + + if (self->on_true) { + /* create on-true block */ + ontrue = ir_function_create_block(func->ir_func, ast_function_label(func, "ontrue")); + if (!ontrue) + return false; + + /* enter the block */ + func->curblock = ontrue; + + /* generate */ + cgen = self->on_true->expression.codegen; + if (!(*cgen)((ast_expression*)(self->on_true), func, false, &dummy)) + return false; + } else + ontrue = NULL; + + /* on-false path */ + if (self->on_false) { + /* create on-false block */ + onfalse = ir_function_create_block(func->ir_func, ast_function_label(func, "onfalse")); + if (!onfalse) + return false; + + /* enter the block */ + func->curblock = onfalse; + + /* generate */ + cgen = self->on_false->expression.codegen; + if (!(*cgen)((ast_expression*)(self->on_false), func, false, &dummy)) + return false; + } else + onfalse = NULL; + + /* Merge block were they all merge in to */ + merge = ir_function_create_block(func->ir_func, ast_function_label(func, "endif")); + if (!merge) + return false; + + /* add jumps ot the merge block */ + if (ontrue && !ir_block_create_jump(ontrue, merge)) + return false; + if (onfalse && !ir_block_create_jump(onfalse, merge)) + return false; + + /* we create the if here, that way all blocks are ordered :) + */ + if (!ir_block_create_if(cond, condval, + (ontrue ? ontrue : merge), + (onfalse ? onfalse : merge))) + { + return false; + } + + /* Now enter the merge block */ + func->curblock = merge; + + return true; } bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_value **out) { + ast_expression_codegen *cgen; + + ir_value *condval; + ir_value *trueval, *falseval; + ir_instr *phi; + + ir_block *cond = func->curblock; + ir_block *ontrue; + ir_block *onfalse; + ir_block *merge; + /* In theory it shouldn't be possible to pass through a node twice, but * in case we add any kind of optimization pass for the AST itself, it * may still happen, thus we remember a created ir_value and simply return one @@ -643,5 +838,313 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_ *out = self->phi_out; return true; } - return false; + + /* Ternary can never create an lvalue... */ + if (lvalue) + return false; + + /* In the following, contraty to ast_ifthen, we assume both paths exist. */ + + /* generate the condition */ + func->curblock = cond; + cgen = self->cond->expression.codegen; + if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval)) + return false; + + /* create on-true block */ + ontrue = ir_function_create_block(func->ir_func, ast_function_label(func, "tern_T")); + if (!ontrue) + return false; + else + { + /* enter the block */ + func->curblock = ontrue; + + /* generate */ + cgen = self->on_true->expression.codegen; + if (!(*cgen)((ast_expression*)(self->on_true), func, false, &trueval)) + return false; + } + + /* create on-false block */ + onfalse = ir_function_create_block(func->ir_func, ast_function_label(func, "tern_F")); + if (!onfalse) + return false; + else + { + /* enter the block */ + func->curblock = onfalse; + + /* generate */ + cgen = self->on_false->expression.codegen; + if (!(*cgen)((ast_expression*)(self->on_false), func, false, &falseval)) + return false; + } + + /* create merge block */ + merge = ir_function_create_block(func->ir_func, ast_function_label(func, "tern_out")); + if (!merge) + return false; + /* jump to merge block */ + if (!ir_block_create_jump(ontrue, merge)) + return false; + if (!ir_block_create_jump(onfalse, merge)) + return false; + + /* create if instruction */ + if (!ir_block_create_if(cond, condval, ontrue, onfalse)) + return false; + + /* Now enter the merge block */ + func->curblock = merge; + + /* Here, now, we need a PHI node + * but first some sanity checking... + */ + if (trueval->vtype != falseval->vtype) { + /* error("ternary with different types on the two sides"); */ + return false; + } + + /* create PHI */ + phi = ir_block_create_phi(merge, ast_function_label(func, "phi"), trueval->vtype); + if (!phi || + !ir_phi_add(phi, ontrue, trueval) || + !ir_phi_add(phi, onfalse, falseval)) + { + return false; + } + + self->phi_out = ir_phi_value(phi); + *out = self->phi_out; + + return true; +} + +bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + + ir_value *dummy = NULL; + ir_value *precond = NULL; + ir_value *postcond = NULL; + + /* Since we insert some jumps "late" so we have blocks + * ordered "nicely", we need to keep track of the actual end-blocks + * of expressions to add the jumps to. + */ + ir_block *bbody = NULL, *end_bbody = NULL; + ir_block *bprecond = NULL, *end_bprecond = NULL; + ir_block *bpostcond = NULL, *end_bpostcond = NULL; + ir_block *bincrement = NULL, *end_bincrement = NULL; + ir_block *bout = NULL, *bin = NULL; + + /* let's at least move the outgoing block to the end */ + size_t bout_id; + + /* 'break' and 'continue' need to be able to find the right blocks */ + ir_block *bcontinue = NULL; + ir_block *bbreak = NULL; + + ir_block *old_bcontinue = NULL; + ir_block *old_bbreak = NULL; + + ir_block *tmpblock = NULL; + + (void)lvalue; + (void)out; + + /* NOTE: + * Should we ever need some kind of block ordering, better make this function + * move blocks around than write a block ordering algorithm later... after all + * the ast and ir should work together, not against each other. + */ + + /* initexpr doesn't get its own block, it's pointless, it could create more blocks + * anyway if for example it contains a ternary. + */ + if (self->initexpr) + { + cgen = self->initexpr->expression.codegen; + if (!(*cgen)((ast_expression*)(self->initexpr), func, false, &dummy)) + return false; + } + + /* Store the block from which we enter this chaos */ + bin = func->curblock; + + /* The pre-loop condition needs its own block since we + * need to be able to jump to the start of that expression. + */ + if (self->precond) + { + bprecond = ir_function_create_block(func->ir_func, ast_function_label(func, "pre_loop_cond")); + if (!bprecond) + return false; + + /* the pre-loop-condition the least important place to 'continue' at */ + bcontinue = bprecond; + + /* enter */ + func->curblock = bprecond; + + /* generate */ + cgen = self->precond->expression.codegen; + if (!(*cgen)((ast_expression*)(self->precond), func, false, &precond)) + return false; + + end_bprecond = func->curblock; + } else { + bprecond = end_bprecond = NULL; + } + + /* Now the next blocks won't be ordered nicely, but we need to + * generate them this early for 'break' and 'continue'. + */ + if (self->increment) { + bincrement = ir_function_create_block(func->ir_func, ast_function_label(func, "loop_increment")); + if (!bincrement) + return false; + bcontinue = bincrement; /* increment comes before the pre-loop-condition */ + } else { + bincrement = end_bincrement = NULL; + } + + if (self->postcond) { + bpostcond = ir_function_create_block(func->ir_func, ast_function_label(func, "post_loop_cond")); + if (!bpostcond) + return false; + bcontinue = bpostcond; /* postcond comes before the increment */ + } else { + bpostcond = end_bpostcond = NULL; + } + + bout_id = func->ir_func->blocks_count; + bout = ir_function_create_block(func->ir_func, ast_function_label(func, "after_loop")); + if (!bout) + return false; + bbreak = bout; + + /* The loop body... */ + if (self->body) + { + bbody = ir_function_create_block(func->ir_func, ast_function_label(func, "loop_body")); + if (!bbody) + return false; + + /* enter */ + func->curblock = bbody; + + old_bbreak = func->breakblock; + old_bcontinue = func->continueblock; + func->breakblock = bbreak; + func->continueblock = bcontinue; + + /* generate */ + cgen = self->body->expression.codegen; + if (!(*cgen)((ast_expression*)(self->body), func, false, &dummy)) + return false; + + end_bbody = func->curblock; + func->breakblock = old_bbreak; + func->continueblock = old_bcontinue; + } + + /* post-loop-condition */ + if (self->postcond) + { + /* enter */ + func->curblock = bpostcond; + + /* generate */ + cgen = self->postcond->expression.codegen; + if (!(*cgen)((ast_expression*)(self->postcond), func, false, &postcond)) + return false; + + end_bpostcond = func->curblock; + } + + /* The incrementor */ + if (self->increment) + { + /* enter */ + func->curblock = bincrement; + + /* generate */ + cgen = self->increment->expression.codegen; + if (!(*cgen)((ast_expression*)(self->increment), func, false, &dummy)) + return false; + + end_bincrement = func->curblock; + } + + /* In any case now, we continue from the outgoing block */ + func->curblock = bout; + + /* Now all blocks are in place */ + /* From 'bin' we jump to whatever comes first */ + if (bprecond) tmpblock = bprecond; + else if (bbody) tmpblock = bbody; + else if (bpostcond) tmpblock = bpostcond; + else tmpblock = bout; + if (!ir_block_create_jump(bin, tmpblock)) + return false; + + /* From precond */ + if (bprecond) + { + ir_block *ontrue, *onfalse; + if (bbody) ontrue = bbody; + else if (bincrement) ontrue = bincrement; + else if (bpostcond) ontrue = bpostcond; + else ontrue = bprecond; + onfalse = bout; + if (!ir_block_create_if(end_bprecond, precond, ontrue, onfalse)) + return false; + } + + /* from body */ + if (bbody) + { + if (bincrement) tmpblock = bincrement; + else if (bpostcond) tmpblock = bpostcond; + else if (bprecond) tmpblock = bprecond; + else tmpblock = bout; + if (!ir_block_create_jump(end_bbody, tmpblock)) + return false; + } + + /* from increment */ + if (bincrement) + { + if (bpostcond) tmpblock = bpostcond; + else if (bprecond) tmpblock = bprecond; + else if (bbody) tmpblock = bbody; + else tmpblock = bout; + if (!ir_block_create_jump(end_bincrement, tmpblock)) + return false; + } + + /* from postcond */ + if (bpostcond) + { + ir_block *ontrue, *onfalse; + if (bprecond) ontrue = bprecond; + else if (bbody) ontrue = bbody; + else if (bincrement) ontrue = bincrement; + else ontrue = bpostcond; + onfalse = bout; + if (!ir_block_create_if(end_bpostcond, postcond, ontrue, onfalse)) + return false; + } + + /* Move 'bout' to the end */ + if (!ir_function_blocks_remove(func->ir_func, bout_id) || + !ir_function_blocks_add(func->ir_func, bout)) + { + ir_block_delete(bout); + return false; + } + + return true; }