+ /* 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");
+ return false;
+ }
+
+ if (self->expression.outr) {
+ asterror(ast_ctx(self), "internal error: ast_return cannot be reused, it bears no result!");
+ return false;
+ }
+ self->expression.outr = (ir_value*)1;
+
+ if (self->operand) {
+ cgen = self->operand->expression.codegen;
+ /* lvalue! */
+ if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand))
+ return false;
+
+ if (!ir_block_create_return(func->curblock, operand))
+ return false;
+ } else {
+ if (!ir_block_create_return(func->curblock, NULL))
+ return false;
+ }
+
+ return true;
+}
+
+bool ast_entfield_codegen(ast_entfield *self, ast_function *func, bool lvalue, ir_value **out)
+{
+ ast_expression_codegen *cgen;
+ ir_value *ent, *field;
+
+ /* This function needs to take the 'lvalue' flag into account!
+ * As lvalue we provide a field-pointer, as rvalue we provide the
+ * value in a temp.
+ */
+
+ if (lvalue && self->expression.outl) {
+ *out = self->expression.outl;
+ return true;
+ }
+
+ if (!lvalue && self->expression.outr) {
+ *out = self->expression.outr;
+ return true;
+ }
+
+ cgen = self->entity->expression.codegen;
+ if (!(*cgen)((ast_expression*)(self->entity), func, false, &ent))
+ return false;
+
+ cgen = self->field->expression.codegen;
+ if (!(*cgen)((ast_expression*)(self->field), func, false, &field))
+ return false;
+
+ if (lvalue) {
+ /* address! */
+ *out = ir_block_create_fieldaddress(func->curblock, ast_function_label(func, "efa"),
+ ent, field);
+ } else {
+ *out = ir_block_create_load_from_ent(func->curblock, ast_function_label(func, "efv"),
+ ent, field, self->expression.vtype);
+ }
+ if (!*out) {
+ asterror(ast_ctx(self), "failed to create %s instruction (output type %s)",
+ (lvalue ? "ADDRESS" : "FIELD"),
+ type_name[self->expression.vtype]);
+ return false;
+ }
+
+ if (lvalue)
+ self->expression.outl = *out;
+ else
+ self->expression.outr = *out;
+
+ /* Hm that should be it... */
+ return true;
+}
+
+bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_value **out)
+{
+ ast_expression_codegen *cgen;
+ ir_value *vec;
+
+ /* in QC this is always an lvalue */
+ (void)lvalue;
+ if (self->expression.outl) {
+ *out = self->expression.outl;
+ return true;
+ }
+
+ cgen = self->owner->expression.codegen;
+ if (!(*cgen)((ast_expression*)(self->owner), func, true, &vec))
+ return false;
+
+ if (vec->vtype != TYPE_VECTOR &&
+ !(vec->vtype == TYPE_FIELD && self->owner->expression.next->expression.vtype == TYPE_VECTOR))
+ {
+ return false;
+ }
+
+ *out = ir_value_vector_member(vec, self->field);
+ self->expression.outl = *out;
+
+ return (*out != NULL);
+}
+
+bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lvalue, ir_value **out)
+{
+ ast_value *arr;
+ ast_value *idx;
+
+ if (!lvalue && self->expression.outr) {
+ *out = self->expression.outr;
+ }
+ if (lvalue && self->expression.outl) {
+ *out = self->expression.outl;
+ }
+
+ if (!ast_istype(self->array, ast_value)) {
+ asterror(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
+ * support this path will be filled.
+ */
+ return false;
+ }
+
+ arr = (ast_value*)self->array;
+ idx = (ast_value*)self->index;
+
+ if (!ast_istype(self->index, ast_value) || !idx->isconst) {
+ /* 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");
+ return false;
+ }
+
+ if (!arr->getter) {
+ asterror(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))
+ 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);
+ if (!call)
+ return false;
+ ir_call_param(call, iridx);
+
+ *out = ir_call_value(call);
+ self->expression.outr = *out;
+ 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];
+ else {
+ asterror(ast_ctx(self), "array indexing here needs an integer constant");
+ return false;
+ }
+ return true;
+}
+
+bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_value **out)
+{
+ ast_expression_codegen *cgen;
+
+ ir_value *condval;
+ ir_value *dummy;
+
+ ir_block *cond = func->curblock;
+ ir_block *ontrue;
+ ir_block *onfalse;
+ ir_block *ontrue_endblock = NULL;
+ ir_block *onfalse_endblock = NULL;
+ ir_block *merge;
+
+ /* 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!");
+ return false;
+ }
+ self->expression.outr = (ir_value*)1;
+
+ /* generate the condition */
+ 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"));
+ 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;
+
+ /* we now need to work from the current endpoint */
+ ontrue_endblock = func->curblock;