/* 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();
}
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;
}
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;
}
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;
}
size_t len;
char *from;
- if (!opts_dump)
+ if (!opts_dump && !opts_dumpfin)
return NULL;
id = (self->labelcount++);
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
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");
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) {
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;
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;
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;
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
ast_expression_codegen *cgen;
ir_value *operand;
+ *out = NULL;
+
/* In the context of a return operation, we don't actually return
* anything...
*/
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 */
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;
* 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;
}
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;
}
{
ir_block *target;
+ *out = NULL;
+
if (lvalue) {
asterror(ast_ctx(self), "break/continue expression is not an l-value");
return false;
}
}
+ /* Jump from the last bnot to bout */
+ if (bfall && !bfall->final && !ir_block_create_jump(bfall, 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;
/* Jump from the last bnot to bout */
if (!func->curblock->final && !ir_block_create_jump(func->curblock, bout))
return false;
+ /* enter the outgoing block */
+ func->curblock = bout;
/* restore the break block */
func->breakblock = old_break;