X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=ast.c;h=d6f231c45d7d77935988da8b16dd337dc17171d5;hb=9c0231a58e19bffd5fc354049cc30e09619c14de;hp=e7d5f099a58592133b24044952e6f8a48011b029;hpb=5c52e817ea157341b5ed5d87609eec5acdfd7a3b;p=xonotic%2Fgmqcc.git diff --git a/ast.c b/ast.c index e7d5f09..d6f231c 100644 --- a/ast.c +++ b/ast.c @@ -47,6 +47,7 @@ static void asterror(lex_ctx ctx, const char *msg, ...) /* It must not be possible to get here. */ static GMQCC_NORETURN void _ast_node_destroy(ast_node *self) { + (void)self; con_err("ast node missing destroy()\n"); abort(); } @@ -651,7 +652,11 @@ ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression * self->cond = cond; self->on_true = ontrue; self->on_false = onfalse; - self->phi_out = NULL; + + if (!ast_type_adopt(self, ontrue)) { + ast_ternary_delete(self); + return NULL; + } return self; } @@ -784,8 +789,12 @@ bool ast_call_check_types(ast_call *self) for (i = 0; i < count; ++i) { if (!ast_compare_type(self->params[i], (ast_expression*)(func->expression.params[i]))) { - asterror(ast_ctx(self), "invalid type for parameter %u in function call", - (unsigned int)(i+1)); + char texp[1024]; + char tgot[1024]; + ast_type_to_string(self->params[i], tgot, sizeof(tgot)); + ast_type_to_string((ast_expression*)func->expression.params[i], texp, sizeof(texp)); + asterror(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s", + (unsigned int)(i+1), texp, tgot); /* we don't immediately return */ retval = false; } @@ -883,6 +892,10 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype) vtype->isconst || vtype->expression.vtype != TYPE_FUNCTION) { + asterror(ast_ctx(self), "internal error: ast_function_new condition %i %i type=%i", + (int)!vtype, + (int)vtype->isconst, + vtype->expression.vtype); mem_d(self); return NULL; } @@ -932,7 +945,7 @@ const char* ast_function_label(ast_function *self, const char *prefix) size_t len; char *from; - if (!opts_dump) + if (!opts_dump && !opts_dumpfin) return NULL; id = (self->labelcount++); @@ -958,6 +971,8 @@ const char* ast_function_label(ast_function *self, const char *prefix) bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_value **out) { + (void)func; + (void)lvalue; /* NOTE: This is the codegen for a variable used in an expression. * It is not the codegen to generate the value. For this purpose, * ast_local_codegen and ast_global_codegen are to be used before this @@ -1282,6 +1297,8 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir) ast_expression_common *ec; size_t i; + (void)ir; + irf = self->ir_func; if (!irf) { asterror(ast_ctx(self), "ast_function's related ast_value was not generated yet"); @@ -1404,10 +1421,11 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_value **out) { ast_expression_codegen *cgen; - ir_value *left, *right; + ir_value *left = NULL; + ir_value *right = NULL; ast_value *arr; - ast_value *idx; + ast_value *idx = 0; ast_array_index *ai = NULL; if (lvalue && self->expression.outl) { @@ -1513,13 +1531,105 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va return true; } + if (OPTS_FLAG(SHORT_LOGIC) && + (self->op == INSTR_AND || self->op == INSTR_OR)) + { + /* short circuit evaluation */ + ir_block *other, *merge; + ir_block *from_left, *from_right; + ir_instr *phi; + size_t merge_id; + uint16_t notop; + + /* Note about casting to true boolean values: + * We use a single NOT for sub expressions, and an + * overall NOT at the end, and for that purpose swap + * all the jump conditions in order for the NOT to get + * doubled. + * ie: (a && b) usually becomes (!!a ? !!b : !!a) + * but we translate this to (!(!a ? !a : !b)) + */ + + merge_id = vec_size(func->ir_func->blocks); + merge = ir_function_create_block(func->ir_func, ast_function_label(func, "sce_merge")); + + cgen = self->left->expression.codegen; + if (!(*cgen)((ast_expression*)(self->left), func, false, &left)) + return false; + if (!OPTS_FLAG(PERL_LOGIC)) { + notop = type_not_instr[left->vtype]; + if (notop == AINSTR_END) { + asterror(ast_ctx(self), "don't know how to cast to bool..."); + return false; + } + left = ir_block_create_unary(func->curblock, + ast_function_label(func, "sce_not"), + notop, + left); + } + from_left = func->curblock; + + other = ir_function_create_block(func->ir_func, ast_function_label(func, "sce_other")); + if ( !(self->op == INSTR_OR) != !OPTS_FLAG(PERL_LOGIC) ) { + if (!ir_block_create_if(func->curblock, left, other, merge)) + return false; + } else { + if (!ir_block_create_if(func->curblock, left, merge, other)) + return false; + } + /* use the likely flag */ + vec_last(func->curblock->instr)->likely = true; + + func->curblock = other; + cgen = self->right->expression.codegen; + if (!(*cgen)((ast_expression*)(self->right), func, false, &right)) + return false; + if (!OPTS_FLAG(PERL_LOGIC)) { + notop = type_not_instr[right->vtype]; + if (notop == AINSTR_END) { + asterror(ast_ctx(self), "don't know how to cast to bool..."); + return false; + } + right = ir_block_create_unary(func->curblock, + ast_function_label(func, "sce_not"), + notop, + right); + } + from_right = func->curblock; + + if (!ir_block_create_jump(func->curblock, merge)) + return false; + + vec_remove(func->ir_func->blocks, merge_id, 1); + vec_push(func->ir_func->blocks, merge); + + func->curblock = merge; + phi = ir_block_create_phi(func->curblock, ast_function_label(func, "sce_value"), TYPE_FLOAT); + ir_phi_add(phi, from_left, left); + ir_phi_add(phi, from_right, right); + *out = ir_phi_value(phi); + if (!OPTS_FLAG(PERL_LOGIC)) { + notop = type_not_instr[(*out)->vtype]; + if (notop == AINSTR_END) { + asterror(ast_ctx(self), "don't know how to cast to bool..."); + return false; + } + *out = ir_block_create_unary(func->curblock, + ast_function_label(func, "sce_final_not"), + notop, + *out); + } + if (!*out) + return false; + self->expression.outr = *out; + return true; + } + cgen = self->left->expression.codegen; - /* lvalue! */ if (!(*cgen)((ast_expression*)(self->left), func, false, &left)) return false; cgen = self->right->expression.codegen; - /* rvalue! */ if (!(*cgen)((ast_expression*)(self->right), func, false, &right)) return false; @@ -1535,7 +1645,12 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, ir_value **out) { ast_expression_codegen *cgen; - ir_value *leftl, *leftr, *right, *bin; + ir_value *leftl = NULL, *leftr, *right, *bin; + + ast_value *arr; + ast_value *idx = 0; + ast_array_index *ai = NULL; + ir_value *iridx = NULL; if (lvalue && self->expression.outl) { *out = self->expression.outl; @@ -1547,8 +1662,23 @@ bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, i return true; } + if (ast_istype(self->dest, ast_array_index)) + { + + ai = (ast_array_index*)self->dest; + idx = (ast_value*)ai->index; + + if (ast_istype(ai->index, ast_value) && idx->isconst) + ai = NULL; + } + /* for a binstore we need both an lvalue and an rvalue for the left side */ /* rvalue of destination! */ + if (ai) { + cgen = idx->expression.codegen; + if (!(*cgen)((ast_expression*)(idx), func, false, &iridx)) + return false; + } cgen = self->dest->expression.codegen; if (!(*cgen)((ast_expression*)(self->dest), func, false, &leftr)) return false; @@ -1563,16 +1693,45 @@ bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, i self->opbin, leftr, right); self->expression.outr = bin; - /* now store them */ - cgen = self->dest->expression.codegen; - /* lvalue of destination */ - if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftl)) - return false; - self->expression.outl = leftl; - if (!ir_block_create_store_op(func->curblock, self->opstore, leftl, bin)) - return false; - self->expression.outr = bin; + if (ai) { + /* we need to call the setter */ + ir_value *funval; + ir_instr *call; + + if (lvalue) { + asterror(ast_ctx(self), "array-subscript assignment cannot produce lvalues"); + return false; + } + + arr = (ast_value*)ai->array; + if (!ast_istype(ai->array, ast_value) || !arr->setter) { + asterror(ast_ctx(self), "value has no setter (%s)", arr->name); + return false; + } + + cgen = arr->setter->expression.codegen; + if (!(*cgen)((ast_expression*)(arr->setter), func, true, &funval)) + return false; + + call = ir_block_create_call(func->curblock, ast_function_label(func, "store"), funval); + if (!call) + return false; + ir_call_param(call, iridx); + ir_call_param(call, bin); + self->expression.outr = bin; + } else { + /* now store them */ + cgen = self->dest->expression.codegen; + /* lvalue of destination */ + if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftl)) + return false; + self->expression.outl = leftl; + + if (!ir_block_create_store_op(func->curblock, self->opstore, leftl, bin)) + return false; + self->expression.outr = bin; + } /* Theoretically, an assinment returns its left side as an * lvalue, if we don't need an lvalue though, we return @@ -1621,6 +1780,8 @@ bool ast_return_codegen(ast_return *self, ast_function *func, bool lvalue, ir_va ast_expression_codegen *cgen; ir_value *operand; + *out = NULL; + /* In the context of a return operation, we don't actually return * anything... */ @@ -1826,10 +1987,11 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va self->expression.outr = (ir_value*)1; /* generate the condition */ - func->curblock = cond; cgen = self->cond->expression.codegen; if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval)) return false; + /* update the block which will get the jump - because short-logic or ternaries may have changed this */ + cond = func->curblock; /* on-true path */ @@ -1876,7 +2038,6 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va 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 && !ontrue_endblock->final && !ir_block_create_jump(ontrue_endblock, merge)) return false; @@ -1920,8 +2081,8 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_ * may still happen, thus we remember a created ir_value and simply return one * if it already exists. */ - if (self->phi_out) { - *out = self->phi_out; + if (self->expression.outr) { + *out = self->expression.outr; return true; } @@ -1995,8 +2156,8 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_ ir_phi_add(phi, ontrue, trueval); ir_phi_add(phi, onfalse, falseval); - self->phi_out = ir_phi_value(phi); - *out = self->phi_out; + self->expression.outr = ir_phi_value(phi); + *out = self->expression.outr; return true; } @@ -2235,6 +2396,8 @@ bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue, { ir_block *target; + *out = NULL; + if (lvalue) { asterror(ast_ctx(self), "break/continue expression is not an l-value"); return false;