+ if (self->expression.outr) {
+ compile_error(ast_ctx(self), "internal error: ast_breakcont cannot be reused!");
+ return false;
+ }
+ self->expression.outr = (ir_value*)1;
+
+ if (self->is_continue)
+ target = func->continueblocks[vec_size(func->continueblocks)-1-self->levels];
+ else
+ target = func->breakblocks[vec_size(func->breakblocks)-1-self->levels];
+
+ if (!target) {
+ compile_error(ast_ctx(self), "%s is lacking a target block", (self->is_continue ? "continue" : "break"));
+ return false;
+ }
+
+ if (!ir_block_create_jump(func->curblock, ast_ctx(self), target))
+ return false;
+ return true;
+}
+
+bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_value **out)
+{
+ ast_expression_codegen *cgen;
+
+ ast_switch_case *def_case = NULL;
+ ir_block *def_bfall = NULL;
+ ir_block *def_bfall_to = NULL;
+ bool set_def_bfall_to = false;
+
+ ir_value *dummy = NULL;
+ ir_value *irop = NULL;
+ ir_block *bout = NULL;
+ ir_block *bfall = NULL;
+ size_t bout_id;
+ size_t c;
+
+ char typestr[1024];
+ uint16_t cmpinstr;
+
+ if (lvalue) {
+ compile_error(ast_ctx(self), "switch expression is not an l-value");
+ return false;
+ }
+
+ if (self->expression.outr) {
+ compile_error(ast_ctx(self), "internal error: ast_switch cannot be reused!");
+ return false;
+ }
+ self->expression.outr = (ir_value*)1;
+
+ (void)lvalue;
+ (void)out;
+
+ cgen = self->operand->expression.codegen;
+ if (!(*cgen)((ast_expression*)(self->operand), func, false, &irop))
+ return false;
+
+ if (!vec_size(self->cases))
+ return true;
+
+ cmpinstr = type_eq_instr[irop->vtype];
+ if (cmpinstr >= AINSTR_END) {
+ ast_type_to_string(self->operand, typestr, sizeof(typestr));
+ compile_error(ast_ctx(self), "invalid type to perform a switch on: %s", typestr);
+ return false;
+ }
+
+ bout_id = vec_size(func->ir_func->blocks);
+ bout = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "after_switch"));
+ if (!bout)
+ return false;
+
+ /* setup the break block */
+ vec_push(func->breakblocks, bout);
+
+ /* Now create all cases */
+ for (c = 0; c < vec_size(self->cases); ++c) {
+ ir_value *cond, *val;
+ ir_block *bcase, *bnot;
+ size_t bnot_id;
+
+ ast_switch_case *swcase = &self->cases[c];
+
+ if (swcase->value) {
+ /* A regular case */
+ /* generate the condition operand */
+ cgen = swcase->value->expression.codegen;
+ if (!(*cgen)((ast_expression*)(swcase->value), func, false, &val))
+ return false;
+ /* generate the condition */
+ cond = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "switch_eq"), cmpinstr, irop, val);
+ if (!cond)
+ return false;
+
+ bcase = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "case"));
+ bnot_id = vec_size(func->ir_func->blocks);
+ bnot = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "not_case"));
+ if (!bcase || !bnot)
+ return false;
+ if (set_def_bfall_to) {
+ set_def_bfall_to = false;
+ def_bfall_to = bcase;
+ }
+ if (!ir_block_create_if(func->curblock, ast_ctx(self), cond, bcase, bnot))
+ return false;
+
+ /* Make the previous case-end fall through */
+ if (bfall && !bfall->final) {
+ if (!ir_block_create_jump(bfall, ast_ctx(self), bcase))
+ return false;
+ }
+
+ /* enter the case */
+ func->curblock = bcase;
+ cgen = swcase->code->expression.codegen;
+ if (!(*cgen)((ast_expression*)swcase->code, func, false, &dummy))
+ return false;
+
+ /* remember this block to fall through from */
+ bfall = func->curblock;
+
+ /* enter the else and move it down */
+ func->curblock = bnot;
+ vec_remove(func->ir_func->blocks, bnot_id, 1);
+ vec_push(func->ir_func->blocks, bnot);
+ } else {
+ /* The default case */
+ /* Remember where to fall through from: */
+ def_bfall = bfall;
+ bfall = NULL;
+ /* remember which case it was */
+ def_case = swcase;
+ /* And the next case will be remembered */
+ set_def_bfall_to = true;
+ }
+ }
+
+ /* Jump from the last bnot to bout */
+ if (bfall && !bfall->final && !ir_block_create_jump(bfall, ast_ctx(self), bout)) {
+ /*
+ astwarning(ast_ctx(bfall), WARN_???, "missing break after last case");
+ */
+ return false;
+ }
+
+ /* If there was a default case, put it down here */
+ if (def_case) {
+ ir_block *bcase;
+
+ /* No need to create an extra block */
+ bcase = func->curblock;
+
+ /* Insert the fallthrough jump */
+ if (def_bfall && !def_bfall->final) {
+ if (!ir_block_create_jump(def_bfall, ast_ctx(self), bcase))
+ return false;
+ }
+
+ /* Now generate the default code */
+ cgen = def_case->code->expression.codegen;
+ if (!(*cgen)((ast_expression*)def_case->code, func, false, &dummy))
+ return false;
+
+ /* see if we need to fall through */
+ if (def_bfall_to && !func->curblock->final)
+ {
+ if (!ir_block_create_jump(func->curblock, ast_ctx(self), def_bfall_to))
+ return false;
+ }
+ }
+
+ /* Jump from the last bnot to bout */
+ if (!func->curblock->final && !ir_block_create_jump(func->curblock, ast_ctx(self), bout))
+ return false;
+ /* enter the outgoing block */
+ func->curblock = bout;
+
+ /* restore the break block */
+ vec_pop(func->breakblocks);
+
+ /* Move 'bout' to the end, it's nicer */
+ vec_remove(func->ir_func->blocks, bout_id, 1);
+ vec_push(func->ir_func->blocks, bout);
+
+ return true;
+}
+
+bool ast_label_codegen(ast_label *self, ast_function *func, bool lvalue, ir_value **out)
+{
+ size_t i;
+ ir_value *dummy;
+
+ if (self->undefined) {
+ compile_error(ast_ctx(self), "internal error: ast_label never defined");
+ return false;
+ }
+
+ *out = NULL;
+ if (lvalue) {
+ compile_error(ast_ctx(self), "internal error: ast_label cannot be an lvalue");
+ return false;
+ }
+
+ /* simply create a new block and jump to it */
+ self->irblock = ir_function_create_block(ast_ctx(self), func->ir_func, self->name);
+ if (!self->irblock) {
+ compile_error(ast_ctx(self), "failed to allocate label block `%s`", self->name);
+ return false;
+ }
+ if (!func->curblock->final) {
+ if (!ir_block_create_jump(func->curblock, ast_ctx(self), self->irblock))
+ return false;
+ }
+
+ /* enter the new block */
+ func->curblock = self->irblock;
+
+ /* Generate all the leftover gotos */
+ for (i = 0; i < vec_size(self->gotos); ++i) {
+ if (!ast_goto_codegen(self->gotos[i], func, false, &dummy))
+ return false;
+ }
+
+ return true;
+}
+
+bool ast_goto_codegen(ast_goto *self, ast_function *func, bool lvalue, ir_value **out)
+{
+ *out = NULL;
+ if (lvalue) {
+ compile_error(ast_ctx(self), "internal error: ast_goto cannot be an lvalue");
+ return false;
+ }
+
+ if (self->target->irblock) {
+ if (self->irblock_from) {
+ /* we already tried once, this is the callback */
+ self->irblock_from->final = false;
+ if (!ir_block_create_goto(self->irblock_from, ast_ctx(self), self->target->irblock)) {
+ compile_error(ast_ctx(self), "failed to generate goto to `%s`", self->name);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ir_block_create_goto(func->curblock, ast_ctx(self), self->target->irblock)) {
+ compile_error(ast_ctx(self), "failed to generate goto to `%s`", self->name);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ /* the target has not yet been created...
+ * close this block in a sneaky way:
+ */
+ func->curblock->final = true;
+ self->irblock_from = func->curblock;
+ ast_label_register_goto(self->target, self);
+ }
+