X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=ast.c;h=49446a111d7035085c90981018c91b02e48f5e06;hb=214c063b3f53430960c256372914f1b90d1457eb;hp=0d0776876665f36b5dffae1a6650e19dee34f2a3;hpb=4c9c6ee5bd39618bcaa5b99177622369dfddf992;p=xonotic%2Fgmqcc.git diff --git a/ast.c b/ast.c index 0d07768..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); } +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, "%16s%8u", prefix, (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; } @@ -670,34 +749,60 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va (void)out; (void)lvalue; - /* create blocks first, it's nicer if they're ordered */ + /* 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 NULL; + return false; - /* generate the condition */ - func->curblock = cond; - cgen = self->cond->expression.codegen; - if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval)) + /* 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))) @@ -705,36 +810,6 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va return false; } - /* on-true path */ - if (ontrue) { - /* 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; - - /* jump to merge block */ - if (!ir_block_create_jump(ontrue, merge)) - return false; - } - - /* on-false path */ - if (onfalse) { - /* 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; - - /* jump to merge block */ - if (!ir_block_create_jump(ontrue, merge)) - return false; - } - /* Now enter the merge block */ func->curblock = merge; @@ -770,53 +845,54 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_ /* 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 NULL; - - /* generate the condition */ - func->curblock = cond; - cgen = self->cond->expression.codegen; - if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval)) - return false; - - if (!ir_block_create_if(cond, condval, ontrue, onfalse)) return false; - - /* on-true path */ - /* 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; - /* jump to merge block */ if (!ir_block_create_jump(ontrue, merge)) return false; - - /* on-false path */ - /* enter the block */ - func->curblock = onfalse; - - /* generate */ - cgen = self->on_false->expression.codegen; - if (!(*cgen)((ast_expression*)(self->on_false), func, false, &falseval)) + if (!ir_block_create_jump(onfalse, merge)) return false; - /* jump to merge block */ - if (!ir_block_create_jump(ontrue, merge)) + /* create if instruction */ + if (!ir_block_create_if(cond, condval, ontrue, onfalse)) return false; /* Now enter the merge block */ @@ -844,3 +920,231 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_ 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; +}