ast_node_init((ast_node*)self, ctx, TYPE_##T); \
( (ast_node*)self )->node.destroy = (ast_node_delete*)destroyfn
-/* error handling */
-static void asterror(lex_ctx ctx, const char *msg, ...)
-{
- va_list ap;
- va_start(ap, msg);
- con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", msg, ap);
- va_end(ap);
-}
/* 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->node.destroy = &_ast_node_destroy;
self->node.keep = false;
self->node.nodetype = nodetype;
+ self->node.side_effects = false;
+}
+
+/* weight and side effects */
+static void _ast_propagate_effects(ast_node *self, ast_node *other)
+{
+ if (ast_side_effects(other))
+ ast_side_effects(self) = true;
}
+#define ast_propagate_effects(s,o) _ast_propagate_effects(((ast_node*)(s)), ((ast_node*)(o)))
/* General expression initialization */
static void ast_expression_init(ast_expression *self,
self->expression.next = NULL;
self->expression.outl = NULL;
self->expression.outr = NULL;
- self->expression.variadic = false;
self->expression.params = NULL;
+ self->expression.count = 0;
+ self->expression.flags = 0;
}
static void ast_expression_delete(ast_expression *self)
}
fromex = &self->expression;
selfex = &cp->expression;
- selfex->variadic = fromex->variadic;
+ selfex->count = fromex->count;
+ selfex->flags = fromex->flags;
for (i = 0; i < vec_size(fromex->params); ++i) {
ast_value *v = ast_value_copy(fromex->params[i]);
if (!v) {
}
fromex = &other->expression;
selfex = &self->expression;
- selfex->variadic = fromex->variadic;
+ selfex->count = fromex->count;
+ selfex->flags = fromex->flags;
for (i = 0; i < vec_size(fromex->params); ++i) {
ast_value *v = ast_value_copy(fromex->params[i]);
if (!v)
else
selfex->next = NULL;
- selfex->variadic = fromex->variadic;
+ selfex->count = fromex->count;
+ selfex->flags = fromex->flags;
for (i = 0; i < vec_size(fromex->params); ++i) {
ast_value *v = ast_value_copy(fromex->params[i]);
if (!v) {
return false;
if (vec_size(a->expression.params) != vec_size(b->expression.params))
return false;
- if (a->expression.variadic != b->expression.variadic)
+ if (a->expression.flags != b->expression.flags)
return false;
if (vec_size(a->expression.params)) {
size_t i;
self->name = name ? util_strdup(name) : NULL;
self->expression.vtype = t;
self->expression.next = NULL;
- self->isconst = false;
+ self->isfield = false;
+ self->cvq = CV_NONE;
+ self->hasvalue = false;
self->uses = 0;
memset(&self->constval, 0, sizeof(self->constval));
{
if (self->name)
mem_d((void*)self->name);
- if (self->isconst) {
+ if (self->hasvalue) {
switch (self->expression.vtype)
{
case TYPE_STRING:
self->left = left;
self->right = right;
+ ast_propagate_effects(self, left);
+ ast_propagate_effects(self, right);
+
if (op >= INSTR_EQ_F && op <= INSTR_GT)
self->expression.vtype = TYPE_FLOAT;
- else if (op == INSTR_AND || op == INSTR_OR ||
- op == INSTR_BITAND || op == INSTR_BITOR)
+ else if (op == INSTR_AND || op == INSTR_OR) {
+ if (OPTS_FLAG(PERL_LOGIC))
+ ast_type_adopt(self, right);
+ else
+ self->expression.vtype = TYPE_FLOAT;
+ }
+ else if (op == INSTR_BITAND || op == INSTR_BITOR)
self->expression.vtype = TYPE_FLOAT;
else if (op == INSTR_MUL_VF || op == INSTR_MUL_FV)
self->expression.vtype = TYPE_VECTOR;
ast_instantiate(ast_binstore, ctx, ast_binstore_delete);
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_binstore_codegen);
+ ast_side_effects(self) = true;
+
self->opstore = storop;
self->opbin = op;
self->dest = left;
self->source = right;
- self->expression.vtype = left->expression.vtype;
- if (left->expression.next) {
- self->expression.next = ast_type_copy(ctx, left);
- if (!self->expression.next) {
- ast_delete(self);
- return NULL;
- }
+ self->keep_dest = false;
+
+ if (!ast_type_adopt(self, left)) {
+ ast_delete(self);
+ return NULL;
}
- else
- self->expression.next = NULL;
return self;
}
void ast_binstore_delete(ast_binstore *self)
{
- ast_unref(self->dest);
+ if (!self->keep_dest)
+ ast_unref(self->dest);
ast_unref(self->source);
ast_expression_delete((ast_expression*)self);
mem_d(self);
self->op = op;
self->operand = expr;
+ ast_propagate_effects(self, expr);
+
if (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) {
self->expression.vtype = TYPE_FLOAT;
} else
- asterror(ctx, "cannot determine type of unary operation %s", asm_instr[op].m);
+ compile_error(ctx, "cannot determine type of unary operation %s", asm_instr[op].m);
return self;
}
self->operand = expr;
+ if (expr)
+ ast_propagate_effects(self, expr);
+
return self;
}
ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field)
{
if (field->expression.vtype != TYPE_FIELD) {
- asterror(ctx, "ast_entfield_new with expression not of type field");
+ compile_error(ctx, "ast_entfield_new with expression not of type field");
return NULL;
}
return ast_entfield_new_force(ctx, entity, field, field->expression.next);
self->entity = entity;
self->field = field;
+ ast_propagate_effects(self, entity);
+ ast_propagate_effects(self, field);
if (!ast_type_adopt(self, outtype)) {
ast_entfield_delete(self);
mem_d(self);
}
-ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int field)
+ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int field, const char *name)
{
ast_instantiate(ast_member, ctx, ast_member_delete);
if (field >= 3) {
if (owner->expression.vtype != TYPE_VECTOR &&
owner->expression.vtype != TYPE_FIELD) {
- asterror(ctx, "member-access on an invalid owner of type %s", type_name[owner->expression.vtype]);
+ compile_error(ctx, "member-access on an invalid owner of type %s", type_name[owner->expression.vtype]);
mem_d(self);
return NULL;
}
}
self->owner = owner;
+ ast_propagate_effects(self, owner);
+
self->field = field;
+ if (name)
+ self->name = util_strdup(name);
+ else
+ self->name = NULL;
return self;
}
self->array = array;
self->index = index;
+ ast_propagate_effects(self, array);
+ ast_propagate_effects(self, index);
if (!ast_type_adopt(self, outtype)) {
ast_array_index_delete(self);
}
if (array->expression.vtype == TYPE_FIELD && outtype->expression.vtype == TYPE_ARRAY) {
if (self->expression.vtype != TYPE_ARRAY) {
- asterror(ast_ctx(self), "array_index node on type");
+ compile_error(ast_ctx(self), "array_index node on type");
ast_array_index_delete(self);
return NULL;
}
self->cond = cond;
self->on_true = ontrue;
self->on_false = onfalse;
+ ast_propagate_effects(self, cond);
+ if (ontrue)
+ ast_propagate_effects(self, ontrue);
+ if (onfalse)
+ ast_propagate_effects(self, onfalse);
return self;
}
self->cond = cond;
self->on_true = ontrue;
self->on_false = onfalse;
- self->phi_out = NULL;
+ ast_propagate_effects(self, cond);
+ ast_propagate_effects(self, ontrue);
+ ast_propagate_effects(self, onfalse);
+
+ if (!ast_type_adopt(self, ontrue)) {
+ ast_ternary_delete(self);
+ return NULL;
+ }
return self;
}
self->increment = increment;
self->body = body;
+ if (initexpr)
+ ast_propagate_effects(self, initexpr);
+ if (precond)
+ ast_propagate_effects(self, precond);
+ if (postcond)
+ ast_propagate_effects(self, postcond);
+ if (increment)
+ ast_propagate_effects(self, increment);
+ if (body)
+ ast_propagate_effects(self, body);
+
return self;
}
self->operand = op;
self->cases = NULL;
+ ast_propagate_effects(self, op);
+
return self;
}
mem_d(self);
}
+ast_label* ast_label_new(lex_ctx ctx, const char *name)
+{
+ ast_instantiate(ast_label, ctx, ast_label_delete);
+ ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_label_codegen);
+
+ self->name = util_strdup(name);
+ self->irblock = NULL;
+ self->gotos = NULL;
+
+ return self;
+}
+
+void ast_label_delete(ast_label *self)
+{
+ mem_d((void*)self->name);
+ vec_free(self->gotos);
+ ast_expression_delete((ast_expression*)self);
+ mem_d(self);
+}
+
+void ast_label_register_goto(ast_label *self, ast_goto *g)
+{
+ vec_push(self->gotos, g);
+}
+
+ast_goto* ast_goto_new(lex_ctx ctx, const char *name)
+{
+ ast_instantiate(ast_goto, ctx, ast_goto_delete);
+ ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_goto_codegen);
+
+ self->name = util_strdup(name);
+ self->target = NULL;
+ self->irblock_from = NULL;
+
+ return self;
+}
+
+void ast_goto_delete(ast_goto *self)
+{
+ mem_d((void*)self->name);
+ ast_expression_delete((ast_expression*)self);
+ mem_d(self);
+}
+
+void ast_goto_set_label(ast_goto *self, ast_label *label)
+{
+ self->target = label;
+}
+
ast_call* ast_call_new(lex_ctx ctx,
ast_expression *funcexpr)
{
ast_instantiate(ast_call, ctx, ast_call_delete);
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_call_codegen);
+ ast_side_effects(self) = true;
+
self->params = NULL;
self->func = funcexpr;
- self->expression.vtype = funcexpr->expression.next->expression.vtype;
- if (funcexpr->expression.next->expression.next)
- self->expression.next = ast_type_copy(ctx, funcexpr->expression.next->expression.next);
+ ast_type_adopt(self, funcexpr->expression.next);
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));
+ compile_error(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;
}
ast_instantiate(ast_store, ctx, ast_store_delete);
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_store_codegen);
+ ast_side_effects(self) = true;
+
self->op = op;
self->dest = dest;
self->source = source;
- self->expression.vtype = dest->expression.vtype;
- if (dest->expression.next) {
- self->expression.next = ast_type_copy(ctx, dest);
- if (!self->expression.next) {
- ast_delete(self);
- return NULL;
- }
+ if (!ast_type_adopt(self, dest)) {
+ ast_delete(self);
+ return NULL;
}
- else
- self->expression.next = NULL;
return self;
}
return self;
}
+bool ast_block_add_expr(ast_block *self, ast_expression *e)
+{
+ ast_propagate_effects(self, e);
+ vec_push(self->exprs, e);
+ if (self->expression.next) {
+ ast_delete(self->expression.next);
+ self->expression.next = NULL;
+ }
+ if (!ast_type_adopt(self, e)) {
+ compile_error(ast_ctx(self), "internal error: failed to adopt type");
+ return false;
+ }
+ return true;
+}
+
void ast_block_collect(ast_block *self, ast_expression *expr)
{
vec_push(self->collect, expr);
{
if (self->expression.next)
ast_delete(self->expression.next);
- self->expression.vtype = from->expression.vtype;
- if (from->expression.next) {
- self->expression.next = ast_type_copy(self->expression.node.context, from->expression.next);
- if (!self->expression.next)
- return false;
- }
- else
- self->expression.next = NULL;
+ if (!ast_type_adopt(self, from))
+ return false;
return true;
}
ast_instantiate(ast_function, ctx, ast_function_delete);
if (!vtype ||
- vtype->isconst ||
+ vtype->hasvalue ||
vtype->expression.vtype != TYPE_FUNCTION)
{
+ compile_error(ast_ctx(self), "internal error: ast_function_new condition %i %i type=%i (probably 2 bodies?)",
+ (int)!vtype,
+ (int)vtype->hasvalue,
+ vtype->expression.vtype);
mem_d(self);
return NULL;
}
self->breakblock = NULL;
self->continueblock = NULL;
- vtype->isconst = true;
+ vtype->hasvalue = true;
vtype->constval.vfunc = self;
return self;
mem_d((void*)self->name);
if (self->vtype) {
/* ast_value_delete(self->vtype); */
- self->vtype->isconst = false;
+ self->vtype->hasvalue = false;
self->vtype->constval.vfunc = NULL;
/* We use unref - if it was stored in a global table it is supposed
* to be deleted from *there*
size_t len;
char *from;
- if (!opts_dump)
+ if (!opts.dump && !opts.dumpfin && !opts.debug)
return NULL;
id = (self->labelcount++);
* But I can't imagine a pituation where the output is truly unnecessary.
*/
+void _ast_codegen_output_type(ast_expression_common *self, ir_value *out)
+{
+ if (out->vtype == TYPE_FIELD)
+ out->fieldtype = self->next->expression.vtype;
+ if (out->vtype == TYPE_FUNCTION)
+ out->outtype = self->next->expression.vtype;
+}
+
+#define codegen_output_type(a,o) (_ast_codegen_output_type(&((a)->expression),(o)))
+#define codegen_output_type_expr(a,o) (_ast_codegen_output_type(a,(o)))
+
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
if (!self->ir_v) {
char typename[1024];
ast_type_to_string((ast_expression*)self, typename, sizeof(typename));
- asterror(ast_ctx(self), "ast_value used before generated %s %s", typename, self->name);
+ compile_error(ast_ctx(self), "ast_value used before generated %s %s", typename, self->name);
return false;
}
*out = self->ir_v;
{
ir_value *v = NULL;
- if (self->isconst && self->expression.vtype == TYPE_FUNCTION)
+ if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
{
ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->expression.vtype);
if (!func)
if (isfield && self->expression.vtype == TYPE_FIELD) {
ast_expression *fieldtype = self->expression.next;
- if (self->isconst) {
- asterror(ast_ctx(self), "TODO: constant field pointers with value");
+ if (self->hasvalue) {
+ compile_error(ast_ctx(self), "TODO: constant field pointers with value");
goto error;
}
ast_value *array = (ast_value*)fieldtype;
if (!ast_istype(fieldtype, ast_value)) {
- asterror(ast_ctx(self), "internal error: ast_value required");
+ compile_error(ast_ctx(self), "internal error: ast_value required");
return false;
}
/* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
- if (!array->expression.count || array->expression.count > opts_max_array_size)
- asterror(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count);
+ if (!array->expression.count || array->expression.count > opts.max_array_size)
+ compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count);
elemtype = &array->expression.next->expression;
vtype = elemtype->vtype;
v = ir_builder_create_field(ir, self->name, vtype);
if (!v) {
- asterror(ast_ctx(self), "ir_builder_create_global failed");
+ compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name);
return false;
}
- if (vtype == TYPE_FIELD)
- v->fieldtype = elemtype->next->expression.vtype;
v->context = ast_ctx(self);
+ v->unique_life = true;
array->ir_v = self->ir_v = v;
namelen = strlen(self->name);
array->ir_values[ai] = ir_builder_create_field(ir, name, vtype);
if (!array->ir_values[ai]) {
mem_d(name);
- asterror(ast_ctx(self), "ir_builder_create_global failed");
+ compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", name);
return false;
}
- if (vtype == TYPE_FIELD)
- array->ir_values[ai]->fieldtype = elemtype->next->expression.vtype;
array->ir_values[ai]->context = ast_ctx(self);
+ array->ir_values[ai]->unique_life = true;
}
mem_d(name);
}
int vtype = elemtype->vtype;
/* same as with field arrays */
- if (!self->expression.count || self->expression.count > opts_max_array_size)
- asterror(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
+ if (!self->expression.count || self->expression.count > opts.max_array_size)
+ compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
v = ir_builder_create_global(ir, self->name, vtype);
if (!v) {
- asterror(ast_ctx(self), "ir_builder_create_global failed");
+ compile_error(ast_ctx(self), "ir_builder_create_global failed `%s`", self->name);
return false;
}
- if (vtype == TYPE_FIELD)
- v->fieldtype = elemtype->next->expression.vtype;
v->context = ast_ctx(self);
+ v->unique_life = true;
namelen = strlen(self->name);
name = (char*)mem_a(namelen + 16);
self->ir_values[ai] = ir_builder_create_global(ir, name, vtype);
if (!self->ir_values[ai]) {
mem_d(name);
- asterror(ast_ctx(self), "ir_builder_create_global failed");
+ compile_error(ast_ctx(self), "ir_builder_create_global failed `%s`", name);
return false;
}
- if (vtype == TYPE_FIELD)
- self->ir_values[ai]->fieldtype = elemtype->next->expression.vtype;
self->ir_values[ai]->context = ast_ctx(self);
+ self->ir_values[ai]->unique_life = true;
}
mem_d(name);
}
*/
v = ir_builder_create_global(ir, self->name, self->expression.vtype);
if (!v) {
- asterror(ast_ctx(self), "ir_builder_create_global failed");
+ compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name);
return false;
}
- if (self->expression.vtype == TYPE_FIELD)
- v->fieldtype = self->expression.next->expression.vtype;
+ codegen_output_type(self, v);
v->context = ast_ctx(self);
}
- if (self->isconst) {
+ if (self->hasvalue) {
switch (self->expression.vtype)
{
case TYPE_FLOAT:
goto error;
break;
case TYPE_ARRAY:
- asterror(ast_ctx(self), "TODO: global constant array");
+ compile_error(ast_ctx(self), "TODO: global constant array");
break;
case TYPE_FUNCTION:
- asterror(ast_ctx(self), "global of type function not properly generated");
+ compile_error(ast_ctx(self), "global of type function not properly generated");
goto error;
/* Cannot generate an IR value for a function,
* need a pointer pointing to a function rather.
*/
+ case TYPE_FIELD:
+ if (!self->constval.vfield) {
+ compile_error(ast_ctx(self), "field constant without vfield set");
+ goto error;
+ }
+ if (!self->constval.vfield->ir_v) {
+ compile_error(ast_ctx(self), "field constant generated before its field");
+ goto error;
+ }
+ if (!ir_value_set_field(v, self->constval.vfield->ir_v))
+ goto error;
+ break;
default:
- asterror(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
+ compile_error(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
break;
}
}
/* link us to the ir_value */
+ v->cvq = self->cvq;
self->ir_v = v;
return true;
bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
{
ir_value *v = NULL;
- if (self->isconst && self->expression.vtype == TYPE_FUNCTION)
+ if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
{
/* Do we allow local functions? I think not...
* this is NOT a function pointer atm.
int vtype = elemtype->vtype;
if (param) {
- asterror(ast_ctx(self), "array-parameters are not supported");
+ compile_error(ast_ctx(self), "array-parameters are not supported");
return false;
}
/* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
- if (!self->expression.count || self->expression.count > opts_max_array_size) {
- asterror(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
+ if (!self->expression.count || self->expression.count > opts.max_array_size) {
+ compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
}
self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count);
if (!self->ir_values) {
- asterror(ast_ctx(self), "failed to allocate array values");
+ compile_error(ast_ctx(self), "failed to allocate array values");
return false;
}
v = ir_function_create_local(func, self->name, vtype, param);
if (!v) {
- asterror(ast_ctx(self), "ir_function_create_local failed");
+ compile_error(ast_ctx(self), "ir_function_create_local failed");
return false;
}
- if (vtype == TYPE_FIELD)
- v->fieldtype = elemtype->next->expression.vtype;
v->context = ast_ctx(self);
+ v->unique_life = true;
namelen = strlen(self->name);
name = (char*)mem_a(namelen + 16);
snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
self->ir_values[ai] = ir_function_create_local(func, name, vtype, param);
if (!self->ir_values[ai]) {
- asterror(ast_ctx(self), "ir_builder_create_global failed");
+ compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", name);
return false;
}
- if (vtype == TYPE_FIELD)
- self->ir_values[ai]->fieldtype = elemtype->next->expression.vtype;
self->ir_values[ai]->context = ast_ctx(self);
+ self->ir_values[ai]->unique_life = true;
}
}
else
v = ir_function_create_local(func, self->name, self->expression.vtype, param);
if (!v)
return false;
- if (self->expression.vtype == TYPE_FIELD)
- v->fieldtype = self->expression.next->expression.vtype;
+ codegen_output_type(self, v);
v->context = ast_ctx(self);
}
/* A constant local... hmmm...
* I suppose the IR will have to deal with this
*/
- if (self->isconst) {
+ if (self->hasvalue) {
switch (self->expression.vtype)
{
case TYPE_FLOAT:
goto error;
break;
default:
- asterror(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
+ compile_error(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
break;
}
}
/* link us to the ir_value */
+ v->cvq = self->cvq;
self->ir_v = v;
+ if (!ast_generate_accessors(self, func->owner))
+ return false;
+ return true;
+
+error: /* clean up */
+ ir_value_delete(v);
+ return false;
+}
+
+bool ast_generate_accessors(ast_value *self, ir_builder *ir)
+{
+ size_t i;
+ bool warn = OPTS_WARN(WARN_USED_UNINITIALIZED);
+ if (!self->setter || !self->getter)
+ return true;
+ for (i = 0; i < self->expression.count; ++i) {
+ if (!self->ir_values) {
+ compile_error(ast_ctx(self), "internal error: no array values generated for `%s`", self->name);
+ return false;
+ }
+ if (!self->ir_values[i]) {
+ compile_error(ast_ctx(self), "internal error: not all array values have been generated for `%s`", self->name);
+ return false;
+ }
+ if (self->ir_values[i]->life) {
+ compile_error(ast_ctx(self), "internal error: function containing `%s` already generated", self->name);
+ return false;
+ }
+ }
+
+ opts_set(opts.warn, WARN_USED_UNINITIALIZED, false);
if (self->setter) {
- if (!ast_global_codegen(self->setter, func->owner, false) ||
- !ast_function_codegen(self->setter->constval.vfunc, func->owner) ||
+ if (!ast_global_codegen (self->setter, ir, false) ||
+ !ast_function_codegen(self->setter->constval.vfunc, ir) ||
!ir_function_finalize(self->setter->constval.vfunc->ir_func))
+ {
+ compile_error(ast_ctx(self), "internal error: failed to generate setter for `%s`", self->name);
+ opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
return false;
+ }
}
if (self->getter) {
- if (!ast_global_codegen(self->getter, func->owner, false) ||
- !ast_function_codegen(self->getter->constval.vfunc, func->owner) ||
+ if (!ast_global_codegen (self->getter, ir, false) ||
+ !ast_function_codegen(self->getter->constval.vfunc, ir) ||
!ir_function_finalize(self->getter->constval.vfunc->ir_func))
+ {
+ compile_error(ast_ctx(self), "internal error: failed to generate getter for `%s`", self->name);
+ opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
return false;
+ }
}
+ for (i = 0; i < self->expression.count; ++i) {
+ vec_free(self->ir_values[i]->life);
+ }
+ opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
return true;
-
-error: /* clean up */
- ir_value_delete(v);
- return false;
}
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");
+ compile_error(ast_ctx(self), "ast_function's related ast_value was not generated yet");
return false;
}
ec = &self->vtype->expression;
for (i = 0; i < vec_size(ec->params); ++i)
{
- vec_push(irf->params, ec->params[i]->expression.vtype);
+ if (ec->params[i]->expression.vtype == TYPE_FIELD)
+ vec_push(irf->params, ec->params[i]->expression.next->expression.vtype);
+ else
+ vec_push(irf->params, ec->params[i]->expression.vtype);
if (!self->builtin) {
if (!ast_local_codegen(ec->params[i], self->ir_func, true))
return false;
}
if (!vec_size(self->blocks)) {
- asterror(ast_ctx(self), "function `%s` has no body", self->name);
+ compile_error(ast_ctx(self), "function `%s` has no body", self->name);
return false;
}
- self->curblock = ir_function_create_block(irf, "entry");
+ self->curblock = ir_function_create_block(ast_ctx(self), irf, "entry");
if (!self->curblock) {
- asterror(ast_ctx(self), "failed to allocate entry block for `%s`", self->name);
+ compile_error(ast_ctx(self), "failed to allocate entry block for `%s`", self->name);
return false;
}
/* TODO: check return types */
if (!self->curblock->is_return)
{
- return ir_block_create_return(self->curblock, NULL);
- /* From now on the parser has to handle this situation */
-#if 0
if (!self->vtype->expression.next ||
self->vtype->expression.next->expression.vtype == TYPE_VOID)
{
- return ir_block_create_return(self->curblock, NULL);
+ return ir_block_create_return(self->curblock, ast_ctx(self), NULL);
}
- else
+ else if (vec_size(self->curblock->entries))
{
/* error("missing return"); */
- asterror(ast_ctx(self), "function `%s` missing return value", self->name);
- return false;
+ if (compile_warning(ast_ctx(self), WARN_MISSING_RETURN_VALUES,
+ "control reaches end of non-void function (`%s`) via %s",
+ self->name, self->curblock->label))
+ {
+ return false;
+ }
+ return ir_block_create_return(self->curblock, ast_ctx(self), NULL);
}
-#endif
}
return true;
}
* of the form: (a, b, c) = x should not assign to c...
*/
if (lvalue) {
- asterror(ast_ctx(self), "not an l-value (code-block)");
+ compile_error(ast_ctx(self), "not an l-value (code-block)");
return false;
}
for (i = 0; i < vec_size(self->locals); ++i)
{
if (!ast_local_codegen(self->locals[i], func->ir_func, false)) {
- if (opts_debug)
- asterror(ast_ctx(self), "failed to generate local `%s`", self->locals[i]->name);
+ if (opts.debug)
+ compile_error(ast_ctx(self), "failed to generate local `%s`", self->locals[i]->name);
return false;
}
}
for (i = 0; i < vec_size(self->exprs); ++i)
{
- ast_expression_codegen *gen = self->exprs[i]->expression.codegen;
- if (func->curblock->final) {
- asterror(ast_ctx(self->exprs[i]), "unreachable statement");
- return false;
+ ast_expression_codegen *gen;
+ if (func->curblock->final && !ast_istype(self->exprs[i], ast_label)) {
+ if (compile_warning(ast_ctx(self->exprs[i]), WARN_UNREACHABLE_CODE, "unreachable statement"))
+ return false;
+ continue;
}
+ gen = self->exprs[i]->expression.codegen;
if (!(*gen)(self->exprs[i], func, false, out))
return false;
}
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) {
ai = (ast_array_index*)self->dest;
idx = (ast_value*)ai->index;
- if (ast_istype(ai->index, ast_value) && idx->isconst)
+ if (ast_istype(ai->index, ast_value) && idx->hasvalue && idx->cvq == CV_CONST)
ai = NULL;
}
ir_instr *call;
if (lvalue) {
- asterror(ast_ctx(self), "array-subscript assignment cannot produce lvalues");
+ compile_error(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);
+ compile_error(ast_ctx(self), "value has no setter (%s)", arr->name);
return false;
}
if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
return false;
- call = ir_block_create_call(func->curblock, ast_function_label(func, "store"), funval);
+ call = ir_block_create_call(func->curblock, ast_ctx(self), ast_function_label(func, "store"), funval, false);
if (!call)
return false;
ir_call_param(call, iridx);
if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
return false;
- if (!ir_block_create_store_op(func->curblock, self->op, left, right))
+ if (!ir_block_create_store_op(func->curblock, ast_ctx(self), self->op, left, right))
return false;
self->expression.outr = right;
}
/* A binary operation cannot yield an l-value */
if (lvalue) {
- asterror(ast_ctx(self), "not an l-value (binop)");
+ compile_error(ast_ctx(self), "not an l-value (binop)");
return false;
}
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(ast_ctx(self), 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) {
+ compile_error(ast_ctx(self), "don't know how to cast to bool...");
+ return false;
+ }
+ left = ir_block_create_unary(func->curblock, ast_ctx(self),
+ ast_function_label(func, "sce_not"),
+ notop,
+ left);
+ }
+ from_left = func->curblock;
+
+ other = ir_function_create_block(ast_ctx(self), 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, ast_ctx(self), left, other, merge))
+ return false;
+ } else {
+ if (!ir_block_create_if(func->curblock, ast_ctx(self), 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) {
+ compile_error(ast_ctx(self), "don't know how to cast to bool...");
+ return false;
+ }
+ right = ir_block_create_unary(func->curblock, ast_ctx(self),
+ ast_function_label(func, "sce_not"),
+ notop,
+ right);
+ }
+ from_right = func->curblock;
+
+ if (!ir_block_create_jump(func->curblock, ast_ctx(self), 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_ctx(self), 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) {
+ compile_error(ast_ctx(self), "don't know how to cast to bool...");
+ return false;
+ }
+ *out = ir_block_create_unary(func->curblock, ast_ctx(self),
+ 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;
- *out = ir_block_create_binop(func->curblock, ast_function_label(func, "bin"),
+ *out = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "bin"),
self->op, left, right);
if (!*out)
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->hasvalue && idx->cvq == CV_CONST)
+ 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;
return false;
/* now the binary */
- bin = ir_block_create_binop(func->curblock, ast_function_label(func, "binst"),
+ bin = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "binst"),
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) {
+ compile_error(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) {
+ compile_error(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_ctx(self), ast_function_label(func, "store"), funval, false);
+ 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, ast_ctx(self), 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
/* An unary operation cannot yield an l-value */
if (lvalue) {
- asterror(ast_ctx(self), "not an l-value (binop)");
+ compile_error(ast_ctx(self), "not an l-value (binop)");
return false;
}
if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand))
return false;
- *out = ir_block_create_unary(func->curblock, ast_function_label(func, "unary"),
+ *out = ir_block_create_unary(func->curblock, ast_ctx(self), ast_function_label(func, "unary"),
self->op, operand);
if (!*out)
return false;
ast_expression_codegen *cgen;
ir_value *operand;
+ *out = NULL;
+
/* In the context of a return operation, we don't actually return
* anything...
*/
if (lvalue) {
- asterror(ast_ctx(self), "return-expression is not an l-value");
+ compile_error(ast_ctx(self), "return-expression is not an l-value");
return false;
}
if (self->expression.outr) {
- asterror(ast_ctx(self), "internal error: ast_return cannot be reused, it bears no result!");
+ compile_error(ast_ctx(self), "internal error: ast_return cannot be reused, it bears no result!");
return false;
}
self->expression.outr = (ir_value*)1;
if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand))
return false;
- if (!ir_block_create_return(func->curblock, operand))
+ if (!ir_block_create_return(func->curblock, ast_ctx(self), operand))
return false;
} else {
- if (!ir_block_create_return(func->curblock, NULL))
+ if (!ir_block_create_return(func->curblock, ast_ctx(self), NULL))
return false;
}
if (lvalue) {
/* address! */
- *out = ir_block_create_fieldaddress(func->curblock, ast_function_label(func, "efa"),
+ *out = ir_block_create_fieldaddress(func->curblock, ast_ctx(self), ast_function_label(func, "efa"),
ent, field);
} else {
- *out = ir_block_create_load_from_ent(func->curblock, ast_function_label(func, "efv"),
+ *out = ir_block_create_load_from_ent(func->curblock, ast_ctx(self), ast_function_label(func, "efv"),
ent, field, self->expression.vtype);
+ /* Done AFTER error checking:
+ codegen_output_type(self, *out);
+ */
}
if (!*out) {
- asterror(ast_ctx(self), "failed to create %s instruction (output type %s)",
+ compile_error(ast_ctx(self), "failed to create %s instruction (output type %s)",
(lvalue ? "ADDRESS" : "FIELD"),
type_name[self->expression.vtype]);
return false;
}
+ if (!lvalue)
+ codegen_output_type(self, *out);
if (lvalue)
self->expression.outl = *out;
}
if (!ast_istype(self->array, ast_value)) {
- asterror(ast_ctx(self), "array indexing this way is not supported");
+ compile_error(ast_ctx(self), "array indexing this way is not supported");
/* note this would actually be pointer indexing because the left side is
* not an actual array but (hopefully) an indexable expression.
* Once we get integer arithmetic, and GADDRESS/GSTORE/GLOAD instruction
arr = (ast_value*)self->array;
idx = (ast_value*)self->index;
- if (!ast_istype(self->index, ast_value) || !idx->isconst) {
+ if (!ast_istype(self->index, ast_value) || !idx->hasvalue || idx->cvq != CV_CONST) {
/* Time to use accessor functions */
ast_expression_codegen *cgen;
ir_value *iridx, *funval;
ir_instr *call;
if (lvalue) {
- asterror(ast_ctx(self), "(.2) array indexing here needs a compile-time constant");
+ compile_error(ast_ctx(self), "(.2) array indexing here needs a compile-time constant");
return false;
}
if (!arr->getter) {
- asterror(ast_ctx(self), "value has no getter, don't know how to index it");
+ compile_error(ast_ctx(self), "value has no getter, don't know how to index it");
return false;
}
cgen = self->index->expression.codegen;
- if (!(*cgen)((ast_expression*)(self->index), func, true, &iridx))
+ if (!(*cgen)((ast_expression*)(self->index), func, false, &iridx))
return false;
cgen = arr->getter->expression.codegen;
if (!(*cgen)((ast_expression*)(arr->getter), func, true, &funval))
return false;
- call = ir_block_create_call(func->curblock, ast_function_label(func, "fetch"), funval);
+ call = ir_block_create_call(func->curblock, ast_ctx(self), ast_function_label(func, "fetch"), funval, false);
if (!call)
return false;
ir_call_param(call, iridx);
return true;
}
- if (idx->expression.vtype == TYPE_FLOAT)
- *out = arr->ir_values[(int)idx->constval.vfloat];
- else if (idx->expression.vtype == TYPE_INTEGER)
- *out = arr->ir_values[idx->constval.vint];
+ if (idx->expression.vtype == TYPE_FLOAT) {
+ unsigned int arridx = idx->constval.vfloat;
+ if (arridx >= self->array->expression.count)
+ {
+ compile_error(ast_ctx(self), "array index out of bounds: %i", arridx);
+ return false;
+ }
+ *out = arr->ir_values[arridx];
+ }
+ else if (idx->expression.vtype == TYPE_INTEGER) {
+ unsigned int arridx = idx->constval.vint;
+ if (arridx >= self->array->expression.count)
+ {
+ compile_error(ast_ctx(self), "array index out of bounds: %i", arridx);
+ return false;
+ }
+ *out = arr->ir_values[arridx];
+ }
else {
- asterror(ast_ctx(self), "array indexing here needs an integer constant");
+ compile_error(ast_ctx(self), "array indexing here needs an integer constant");
return false;
}
return true;
ir_value *condval;
ir_value *dummy;
- ir_block *cond = func->curblock;
+ ir_block *cond;
ir_block *ontrue;
ir_block *onfalse;
ir_block *ontrue_endblock = NULL;
ir_block *onfalse_endblock = NULL;
- ir_block *merge;
+ ir_block *merge = NULL;
/* We don't output any value, thus also don't care about r/lvalue */
(void)out;
(void)lvalue;
if (self->expression.outr) {
- asterror(ast_ctx(self), "internal error: ast_ifthen cannot be reused, it bears no result!");
+ compile_error(ast_ctx(self), "internal error: ast_ifthen cannot be reused, it bears no result!");
return false;
}
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 */
if (self->on_true) {
/* create on-true block */
- ontrue = ir_function_create_block(func->ir_func, ast_function_label(func, "ontrue"));
+ ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "ontrue"));
if (!ontrue)
return false;
/* on-false path */
if (self->on_false) {
/* create on-false block */
- onfalse = ir_function_create_block(func->ir_func, ast_function_label(func, "onfalse"));
+ onfalse = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "onfalse"));
if (!onfalse)
return false;
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;
+ if (!ontrue || !onfalse || !ontrue_endblock->final || !onfalse_endblock->final)
+ {
+ merge = ir_function_create_block(ast_ctx(self), 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, ast_ctx(self), merge))
+ return false;
+ if (onfalse && !onfalse_endblock->final && !ir_block_create_jump(onfalse_endblock, ast_ctx(self), merge))
+ return false;
- /* add jumps ot the merge block */
- if (ontrue && !ontrue_endblock->final && !ir_block_create_jump(ontrue_endblock, merge))
- return false;
- if (onfalse && !onfalse_endblock->final && !ir_block_create_jump(onfalse_endblock, merge))
- return false;
+ /* Now enter the merge block */
+ func->curblock = merge;
+ }
/* we create the if here, that way all blocks are ordered :)
*/
- if (!ir_block_create_if(cond, condval,
+ if (!ir_block_create_if(cond, ast_ctx(self), condval,
(ontrue ? ontrue : merge),
(onfalse ? onfalse : merge)))
{
return false;
}
- /* Now enter the merge block */
- func->curblock = merge;
-
return true;
}
ir_instr *phi;
ir_block *cond = func->curblock;
- ir_block *ontrue;
- ir_block *onfalse;
+ ir_block *cond_out = NULL;
+ ir_block *ontrue, *ontrue_out = NULL;
+ ir_block *onfalse, *onfalse_out = NULL;
ir_block *merge;
/* Ternary can never create an lvalue... */
* 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;
}
cgen = self->cond->expression.codegen;
if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval))
return false;
+ cond_out = func->curblock;
/* create on-true block */
- ontrue = ir_function_create_block(func->ir_func, ast_function_label(func, "tern_T"));
+ ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_T"));
if (!ontrue)
return false;
else
cgen = self->on_true->expression.codegen;
if (!(*cgen)((ast_expression*)(self->on_true), func, false, &trueval))
return false;
+
+ ontrue_out = func->curblock;
}
/* create on-false block */
- onfalse = ir_function_create_block(func->ir_func, ast_function_label(func, "tern_F"));
+ onfalse = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_F"));
if (!onfalse)
return false;
else
cgen = self->on_false->expression.codegen;
if (!(*cgen)((ast_expression*)(self->on_false), func, false, &falseval))
return false;
+
+ onfalse_out = func->curblock;
}
/* create merge block */
- merge = ir_function_create_block(func->ir_func, ast_function_label(func, "tern_out"));
+ merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_out"));
if (!merge)
return false;
/* jump to merge block */
- if (!ir_block_create_jump(ontrue, merge))
+ if (!ir_block_create_jump(ontrue_out, ast_ctx(self), merge))
return false;
- if (!ir_block_create_jump(onfalse, merge))
+ if (!ir_block_create_jump(onfalse_out, ast_ctx(self), merge))
return false;
/* create if instruction */
- if (!ir_block_create_if(cond, condval, ontrue, onfalse))
+ if (!ir_block_create_if(cond_out, ast_ctx(self), condval, ontrue, onfalse))
return false;
/* Now enter the merge block */
}
/* create PHI */
- phi = ir_block_create_phi(merge, ast_function_label(func, "phi"), trueval->vtype);
+ phi = ir_block_create_phi(merge, ast_ctx(self), ast_function_label(func, "phi"), trueval->vtype);
if (!phi)
return false;
- ir_phi_add(phi, ontrue, trueval);
- ir_phi_add(phi, onfalse, falseval);
+ ir_phi_add(phi, ontrue_out, trueval);
+ ir_phi_add(phi, onfalse_out, falseval);
+
+ self->expression.outr = ir_phi_value(phi);
+ *out = self->expression.outr;
- self->phi_out = ir_phi_value(phi);
- *out = self->phi_out;
+ codegen_output_type(self, *out);
return true;
}
(void)out;
if (self->expression.outr) {
- asterror(ast_ctx(self), "internal error: ast_loop cannot be reused, it bears no result!");
+ compile_error(ast_ctx(self), "internal error: ast_loop cannot be reused, it bears no result!");
return false;
}
self->expression.outr = (ir_value*)1;
*/
if (self->precond)
{
- bprecond = ir_function_create_block(func->ir_func, ast_function_label(func, "pre_loop_cond"));
+ bprecond = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "pre_loop_cond"));
if (!bprecond)
return false;
* 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"));
+ bincrement = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "loop_increment"));
if (!bincrement)
return false;
bcontinue = bincrement; /* increment comes before the pre-loop-condition */
}
if (self->postcond) {
- bpostcond = ir_function_create_block(func->ir_func, ast_function_label(func, "post_loop_cond"));
+ bpostcond = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "post_loop_cond"));
if (!bpostcond)
return false;
bcontinue = bpostcond; /* postcond comes before the increment */
}
bout_id = vec_size(func->ir_func->blocks);
- bout = ir_function_create_block(func->ir_func, ast_function_label(func, "after_loop"));
+ bout = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "after_loop"));
if (!bout)
return false;
bbreak = bout;
/* The loop body... */
- if (self->body)
+ /* if (self->body) */
{
- bbody = ir_function_create_block(func->ir_func, ast_function_label(func, "loop_body"));
+ bbody = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "loop_body"));
if (!bbody)
return false;
old_bcontinue = func->continueblock;
func->breakblock = bbreak;
func->continueblock = bcontinue;
+ if (!func->continueblock)
+ func->continueblock = bbody;
/* generate */
- cgen = self->body->expression.codegen;
- if (!(*cgen)((ast_expression*)(self->body), func, false, &dummy))
- return false;
+ if (self->body) {
+ cgen = self->body->expression.codegen;
+ if (!(*cgen)((ast_expression*)(self->body), func, false, &dummy))
+ return false;
+ }
end_bbody = func->curblock;
func->breakblock = old_bbreak;
else if (bbody) tmpblock = bbody;
else if (bpostcond) tmpblock = bpostcond;
else tmpblock = bout;
- if (!ir_block_create_jump(bin, tmpblock))
+ if (!ir_block_create_jump(bin, ast_ctx(self), tmpblock))
return false;
/* From precond */
else if (bpostcond) ontrue = bpostcond;
else ontrue = bprecond;
onfalse = bout;
- if (!ir_block_create_if(end_bprecond, precond, ontrue, onfalse))
+ if (!ir_block_create_if(end_bprecond, ast_ctx(self), precond, ontrue, onfalse))
return false;
}
if (bincrement) tmpblock = bincrement;
else if (bpostcond) tmpblock = bpostcond;
else if (bprecond) tmpblock = bprecond;
- else tmpblock = bout;
- if (!end_bbody->final && !ir_block_create_jump(end_bbody, tmpblock))
+ else tmpblock = bbody;
+ if (!end_bbody->final && !ir_block_create_jump(end_bbody, ast_ctx(self), tmpblock))
return false;
}
else if (bprecond) tmpblock = bprecond;
else if (bbody) tmpblock = bbody;
else tmpblock = bout;
- if (!ir_block_create_jump(end_bincrement, tmpblock))
+ if (!ir_block_create_jump(end_bincrement, ast_ctx(self), tmpblock))
return false;
}
else if (bincrement) ontrue = bincrement;
else ontrue = bpostcond;
onfalse = bout;
- if (!ir_block_create_if(end_bpostcond, postcond, ontrue, onfalse))
+ if (!ir_block_create_if(end_bpostcond, ast_ctx(self), postcond, ontrue, onfalse))
return false;
}
{
ir_block *target;
+ *out = NULL;
+
if (lvalue) {
- asterror(ast_ctx(self), "break/continue expression is not an l-value");
+ compile_error(ast_ctx(self), "break/continue expression is not an l-value");
return false;
}
if (self->expression.outr) {
- asterror(ast_ctx(self), "internal error: ast_breakcont cannot be reused!");
+ compile_error(ast_ctx(self), "internal error: ast_breakcont cannot be reused!");
return false;
}
self->expression.outr = (ir_value*)1;
else
target = func->breakblock;
- if (!ir_block_create_jump(func->curblock, target))
+ 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;
}
{
ast_expression_codegen *cgen;
- ast_switch_case *def_case = NULL;
- ir_block *def_bfall = NULL;
+ 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;
uint16_t cmpinstr;
if (lvalue) {
- asterror(ast_ctx(self), "switch expression is not an l-value");
+ compile_error(ast_ctx(self), "switch expression is not an l-value");
return false;
}
if (self->expression.outr) {
- asterror(ast_ctx(self), "internal error: ast_switch cannot be reused!");
+ compile_error(ast_ctx(self), "internal error: ast_switch cannot be reused!");
return false;
}
self->expression.outr = (ir_value*)1;
cmpinstr = type_eq_instr[irop->vtype];
if (cmpinstr >= AINSTR_END) {
ast_type_to_string(self->operand, typestr, sizeof(typestr));
- asterror(ast_ctx(self), "invalid type to perform a switch on: %s", 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(func->ir_func, ast_function_label(func, "after_switch"));
+ bout = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "after_switch"));
if (!bout)
return false;
if (!(*cgen)((ast_expression*)(swcase->value), func, false, &val))
return false;
/* generate the condition */
- cond = ir_block_create_binop(func->curblock, ast_function_label(func, "switch_eq"), cmpinstr, irop, val);
+ 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(func->ir_func, ast_function_label(func, "case"));
+ 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(func->ir_func, ast_function_label(func, "not_case"));
+ bnot = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "not_case"));
if (!bcase || !bnot)
return false;
- if (!ir_block_create_if(func->curblock, cond, bcase, bnot))
+ 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, bcase))
+ if (!ir_block_create_jump(bfall, ast_ctx(self), bcase))
return false;
}
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, bout)) {
+ if (bfall && !bfall->final && !ir_block_create_jump(bfall, ast_ctx(self), bout)) {
/*
astwarning(ast_ctx(bfall), WARN_???, "missing break after last case");
*/
/* Insert the fallthrough jump */
if (def_bfall && !def_bfall->final) {
- if (!ir_block_create_jump(def_bfall, bcase))
+ if (!ir_block_create_jump(def_bfall, ast_ctx(self), bcase))
return false;
}
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, bout))
+ if (!func->curblock->final && !ir_block_create_jump(func->curblock, ast_ctx(self), bout))
return false;
/* enter the outgoing block */
func->curblock = bout;
return true;
}
+bool ast_label_codegen(ast_label *self, ast_function *func, bool lvalue, ir_value **out)
+{
+ size_t i;
+ ir_value *dummy;
+
+ *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_jump(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_jump(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);
+ }
+
+ return true;
+}
+
bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out)
{
ast_expression_codegen *cgen;
/* return values are never lvalues */
if (lvalue) {
- asterror(ast_ctx(self), "not an l-value (function call)");
+ compile_error(ast_ctx(self), "not an l-value (function call)");
return false;
}
vec_push(params, param);
}
- callinstr = ir_block_create_call(func->curblock, ast_function_label(func, "call"), funval);
+ callinstr = ir_block_create_call(func->curblock, ast_ctx(self),
+ ast_function_label(func, "call"),
+ funval, !!(self->func->expression.flags & AST_FLAG_NORETURN));
if (!callinstr)
goto error;
*out = ir_call_value(callinstr);
self->expression.outr = *out;
+ codegen_output_type(self, *out);
+
vec_free(params);
return true;
error: