]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
array accessor function genaration
authorWolfgang (Blub) Bumiller <blub@speed.at>
Sun, 11 Nov 2012 15:06:27 +0000 (16:06 +0100)
committerWolfgang (Blub) Bumiller <blub@speed.at>
Sun, 11 Nov 2012 15:06:27 +0000 (16:06 +0100)
ast.c
ast.h
parser.c

diff --git a/ast.c b/ast.c
index 7e4e75e0413df91114a1f8cfabc72b3036fe2a4c..cd6662fcfc5740ec6d7bd5cea55eb02bb93bff23 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -563,6 +563,39 @@ void ast_member_delete(ast_member *self)
     mem_d(self);
 }
 
+ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_expression *index)
+{
+    const ast_expression *outtype;
+    ast_instantiate(ast_array_index, ctx, ast_array_index_delete);
+
+    outtype = array->expression.next;
+    if (!outtype) {
+        mem_d(self);
+        /* Error: field has no type... */
+        return NULL;
+    }
+
+    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_array_index_codegen);
+
+    self->array = array;
+    self->index = index;
+
+    if (!ast_type_adopt(self, outtype)) {
+        ast_array_index_delete(self);
+        return NULL;
+    }
+
+    return self;
+}
+
+void ast_array_index_delete(ast_array_index *self)
+{
+    ast_unref(self->array);
+    ast_unref(self->index);
+    ast_expression_delete((ast_expression*)self);
+    mem_d(self);
+}
+
 ast_ifthen* ast_ifthen_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse)
 {
     ast_instantiate(ast_ifthen, ctx, ast_ifthen_delete);
@@ -929,7 +962,6 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 
         ast_expression_common *elemtype = &self->expression.next->expression;
         int vtype = elemtype->vtype;
-printf("Generating `%s`\n", self->name);
         /* 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);
@@ -1477,6 +1509,48 @@ bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_va
     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 (!ast_istype(self->array, ast_value)) {
+        asterror(ast_ctx(self), "array indexing this way is not supported");
+        return false;
+    }
+
+    if (!ast_istype(self->index, ast_value)) {
+        if (lvalue) {
+            asterror(ast_ctx(self), "array indexing here needs a compile-time constant");
+            return false;
+        } else {
+            /* Time to use accessor functions */
+            /*
+            ast_expression_codegen *cgen;
+            ir_value               *iridx;
+            */
+        }
+    }
+
+    arr = (ast_value*)self->array;
+    idx = (ast_value*)self->index;
+
+    if (!idx->isconst) {
+        asterror(ast_ctx(self), "(.2) array indexing here needs a compile-time constant");
+        return false;
+    }
+
+    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;
diff --git a/ast.h b/ast.h
index 1d9efb33d4b9d4313caf34bc9c5900d9e988e083..6af3ad0648ab0fed4e7a4d5452e2e052a8563ebc 100644 (file)
--- a/ast.h
+++ b/ast.h
 typedef union ast_node_u ast_node;
 typedef union ast_expression_u ast_expression;
 
-typedef struct ast_value_s      ast_value;
-typedef struct ast_function_s   ast_function;
-typedef struct ast_block_s      ast_block;
-typedef struct ast_binary_s     ast_binary;
-typedef struct ast_store_s      ast_store;
-typedef struct ast_binstore_s   ast_binstore;
-typedef struct ast_entfield_s   ast_entfield;
-typedef struct ast_ifthen_s     ast_ifthen;
-typedef struct ast_ternary_s    ast_ternary;
-typedef struct ast_loop_s       ast_loop;
-typedef struct ast_call_s       ast_call;
-typedef struct ast_unary_s      ast_unary;
-typedef struct ast_return_s     ast_return;
-typedef struct ast_member_s     ast_member;
+typedef struct ast_value_s       ast_value;
+typedef struct ast_function_s    ast_function;
+typedef struct ast_block_s       ast_block;
+typedef struct ast_binary_s      ast_binary;
+typedef struct ast_store_s       ast_store;
+typedef struct ast_binstore_s    ast_binstore;
+typedef struct ast_entfield_s    ast_entfield;
+typedef struct ast_ifthen_s      ast_ifthen;
+typedef struct ast_ternary_s     ast_ternary;
+typedef struct ast_loop_s        ast_loop;
+typedef struct ast_call_s        ast_call;
+typedef struct ast_unary_s       ast_unary;
+typedef struct ast_return_s      ast_return;
+typedef struct ast_member_s      ast_member;
+typedef struct ast_array_index_s ast_array_index;
 
 enum {
     TYPE_ast_node,
@@ -62,7 +63,8 @@ enum {
     TYPE_ast_call,
     TYPE_ast_unary,
     TYPE_ast_return,
-    TYPE_ast_member
+    TYPE_ast_member,
+    TYPE_ast_array_index
 };
 
 #define ast_istype(x, t) ( ((ast_node_common*)x)->nodetype == (TYPE_##t) )
@@ -305,6 +307,27 @@ void ast_member_delete(ast_member*);
 
 bool ast_member_codegen(ast_member*, ast_function*, bool lvalue, ir_value**);
 
+/* Array index access:
+ *
+ * QC forces us to take special action on arrays:
+ * an ast_store on an ast_array_index must not codegen the index,
+ * but call its setter - unless we have an instruction set which supports
+ * what we need.
+ * Any other array index access will be codegened to a call to the getter.
+ * In any case, accessing an element via a compiletime-constant index will
+ * result in quick access to that variable.
+ */
+struct ast_array_index_s
+{
+    ast_expression_common expression;
+    ast_expression *array;
+    ast_expression *index;
+};
+ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_expression *index);
+void ast_array_index_delete(ast_array_index*);
+
+bool ast_array_index_codegen(ast_array_index*, ast_function*, bool lvalue, ir_value**);
+
 /* Store
  *
  * Stores left<-right and returns left.
index ad5ec47cf36da8616a7e168c35c3a17bf501b3c2..534f810fd2b9aa9d7167bc16e312425febcfd9a4 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -2180,6 +2180,317 @@ enderr:
     return false;
 }
 
+static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast_value *index, ast_value *value, size_t from, size_t afterend)
+{
+    lex_ctx ctx = ast_ctx(array);
+
+    if (from+1 == afterend) {
+        // set this value
+        ast_block       *block;
+        ast_return      *ret;
+        ast_array_index *subscript;
+        int assignop = type_store_instr[value->expression.vtype];
+
+        if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+            assignop = INSTR_STORE_V;
+
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+        if (!subscript)
+            return NULL;
+
+        ast_store *st = ast_store_new(ctx, assignop, (ast_expression*)subscript, (ast_expression*)value);
+        if (!st) {
+            ast_delete(subscript);
+            return NULL;
+        }
+
+        block = ast_block_new(ctx);
+        if (!block) {
+            ast_delete(st);
+            return NULL;
+        }
+
+        if (!ast_block_exprs_add(block, (ast_expression*)st)) {
+            ast_delete(block);
+            return NULL;
+        }
+
+        ret = ast_return_new(ctx, NULL);
+        if (!ret) {
+            ast_delete(block);
+            return NULL;
+        }
+
+        if (!ast_block_exprs_add(block, (ast_expression*)ret)) {
+            ast_delete(block);
+            return NULL;
+        }
+
+        return (ast_expression*)block;
+    } else {
+        ast_ifthen *ifthen;
+        ast_expression *left, *right;
+        ast_binary *cmp;
+
+        size_t diff = afterend - from;
+        size_t middle = from + diff/2;
+
+        left  = array_setter_node(parser, array, index, value, from, middle);
+        right = array_setter_node(parser, array, index, value, middle, afterend);
+        if (!left || !right) {
+            if (left)  ast_delete(left);
+            if (right) ast_delete(right);
+            return NULL;
+        }
+
+        cmp = ast_binary_new(ctx, INSTR_LT,
+                             (ast_expression*)index,
+                             (ast_expression*)parser_const_float(parser, from + diff/2));
+        if (!cmp) {
+            ast_delete(left);
+            ast_delete(right);
+            parseerror(parser, "internal error: failed to create comparison for array setter");
+            return NULL;
+        }
+
+        ifthen = ast_ifthen_new(ctx, (ast_expression*)cmp, left, right);
+        if (!ifthen) {
+            ast_delete(cmp); /* will delete left and right */
+            parseerror(parser, "internal error: failed to create conditional jump for array setter");
+            return NULL;
+        }
+
+        return (ast_expression*)ifthen;
+    }
+}
+
+static ast_expression *array_getter_node(parser_t *parser, ast_value *array, ast_value *index, size_t from, size_t afterend)
+{
+    lex_ctx ctx = ast_ctx(array);
+
+    if (from+1 == afterend) {
+        ast_return      *ret;
+        ast_array_index *subscript;
+
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+        if (!subscript)
+            return NULL;
+
+        ret = ast_return_new(ctx, (ast_expression*)subscript);
+        if (!ret) {
+            ast_delete(subscript);
+            return NULL;
+        }
+
+        return (ast_expression*)ret;
+    } else {
+        ast_ifthen *ifthen;
+        ast_expression *left, *right;
+        ast_binary *cmp;
+
+        size_t diff = afterend - from;
+        size_t middle = from + diff/2;
+
+        left  = array_getter_node(parser, array, index, from, middle);
+        right = array_getter_node(parser, array, index, middle, afterend);
+        if (!left || !right) {
+            if (left)  ast_delete(left);
+            if (right) ast_delete(right);
+            return NULL;
+        }
+
+        cmp = ast_binary_new(ctx, INSTR_LT,
+                             (ast_expression*)index,
+                             (ast_expression*)parser_const_float(parser, from + diff/2));
+        if (!cmp) {
+            ast_delete(left);
+            ast_delete(right);
+            parseerror(parser, "internal error: failed to create comparison for array setter");
+            return NULL;
+        }
+
+        ifthen = ast_ifthen_new(ctx, (ast_expression*)cmp, left, right);
+        if (!ifthen) {
+            ast_delete(cmp); /* will delete left and right */
+            parseerror(parser, "internal error: failed to create conditional jump for array setter");
+            return NULL;
+        }
+
+        return (ast_expression*)ifthen;
+    }
+}
+
+static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname)
+{
+    ast_expression *root = NULL;
+    ast_function   *func = NULL;
+    ast_value      *fval = NULL;
+    ast_block      *body;
+    ast_value      *index, *value;
+    varentry_t     entry;
+
+    if (!ast_istype(array->expression.next, ast_value)) {
+        parseerror(parser, "internal error: array accessor needs to build an ast_value with a copy of the element type");
+        return false;
+    }
+
+    body = ast_block_new(ast_ctx(array));
+    if (!body) {
+        parseerror(parser, "failed to create block for array accessor");
+        return false;
+    }
+
+    index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
+    value = ast_value_copy((ast_value*)array->expression.next);
+
+    if (!index || !value) {
+        ast_delete(body);
+        if (index) ast_delete(index);
+        if (value) ast_delete(value);
+        parseerror(parser, "failed to create locals for array accessor");
+        return false;
+    }
+
+    root = array_setter_node(parser, array, index, value, 0, array->expression.count);
+    if (!root) {
+        parseerror(parser, "failed to build accessor search tree");
+        goto cleanup;
+    }
+
+    if (!ast_block_exprs_add(body, root)) {
+        parseerror(parser, "failed to build accessor search block");
+        goto cleanup;
+    }
+
+    fval = ast_value_new(ast_ctx(array), funcname, TYPE_FUNCTION);
+    if (!fval) {
+        parseerror(parser, "failed to create accessor function value");
+        goto cleanup;
+    }
+    fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
+
+    (void)!ast_value_set_name(value, "value"); /* not important */
+    if (!ast_expression_common_params_add(&fval->expression, index)) {
+        parseerror(parser, "failed to build array setter");
+        goto cleanup;
+    }
+    if (!ast_expression_common_params_add(&fval->expression, value)) {
+        ast_delete(index);
+        parseerror(parser, "failed to build array setter");
+        goto cleanup2;
+    }
+
+    func = ast_function_new(ast_ctx(array), funcname, fval);
+    if (!func) {
+        parseerror(parser, "failed to create accessor function node");
+        goto cleanup2;
+    }
+
+    if (!ast_function_blocks_add(func, body))
+        goto cleanup2;
+
+    entry.name = util_strdup(funcname);
+    entry.var  = (ast_expression*)fval;
+    if (!parser_t_globals_add(parser, entry)) {
+        mem_d(entry.name);
+        goto cleanup2;
+    }
+    if (!parser_t_functions_add(parser, func))
+        goto cleanup2;
+
+    return true;
+cleanup:
+    ast_delete(index);
+    ast_delete(value);
+cleanup2:
+    ast_delete(body);
+    if (root) ast_delete(root);
+    if (func) ast_delete(func);
+    if (fval) ast_delete(fval);
+    return false;
+}
+
+static bool parser_create_array_getter(parser_t *parser, ast_value *array, const char *funcname)
+{
+    ast_expression *root = NULL;
+    ast_function   *func = NULL;
+    ast_value      *fval = NULL;
+    ast_block      *body;
+    ast_value      *index;
+    varentry_t     entry;
+
+    if (!ast_istype(array->expression.next, ast_value)) {
+        parseerror(parser, "internal error: array accessor needs to build an ast_value with a copy of the element type");
+        return false;
+    }
+
+    body = ast_block_new(ast_ctx(array));
+    if (!body) {
+        parseerror(parser, "failed to create block for array accessor");
+        return false;
+    }
+
+    index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
+
+    if (!index) {
+        ast_delete(body);
+        if (index) ast_delete(index);
+        parseerror(parser, "failed to create locals for array accessor");
+        return false;
+    }
+
+    root = array_getter_node(parser, array, index, 0, array->expression.count);
+    if (!root) {
+        parseerror(parser, "failed to build accessor search tree");
+        goto cleanup;
+    }
+
+    if (!ast_block_exprs_add(body, root)) {
+        parseerror(parser, "failed to build accessor search block");
+        goto cleanup;
+    }
+
+    fval = ast_value_new(ast_ctx(array), funcname, TYPE_FUNCTION);
+    if (!fval) {
+        parseerror(parser, "failed to create accessor function value");
+        goto cleanup;
+    }
+    fval->expression.next = ast_type_copy(ast_ctx(array), array->expression.next);
+
+    if (!ast_expression_common_params_add(&fval->expression, index)) {
+        parseerror(parser, "failed to build array setter");
+        goto cleanup;
+    }
+
+    func = ast_function_new(ast_ctx(array), funcname, fval);
+    if (!func) {
+        parseerror(parser, "failed to create accessor function node");
+        goto cleanup2;
+    }
+
+    if (!ast_function_blocks_add(func, body))
+        goto cleanup2;
+
+    entry.name = util_strdup(funcname);
+    entry.var  = (ast_expression*)fval;
+    if (!parser_t_globals_add(parser, entry)) {
+        mem_d(entry.name);
+        goto cleanup2;
+    }
+    if (!parser_t_functions_add(parser, func))
+        goto cleanup2;
+
+    return true;
+cleanup:
+    ast_delete(index);
+cleanup2:
+    ast_delete(body);
+    if (root) ast_delete(root);
+    if (func) ast_delete(func);
+    if (fval) ast_delete(fval);
+    return false;
+}
+
 typedef struct {
     MEM_VECTOR_MAKE(ast_value*, p);
 } paramlist_t;
@@ -2739,6 +3050,18 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             ve[0].var  = ve[1].var  = ve[2].var  = NULL;
             cleanvar = false;
         }
+        /* Part 2.2
+         * deal with arrays
+         */
+        if (var->expression.vtype == TYPE_ARRAY) {
+            char          name[1024];
+            snprintf(name, sizeof(name), "%s::SET", var->name);
+            if (!parser_create_array_setter(parser, var, name))
+                goto cleanup;
+            snprintf(name, sizeof(name), "%s::GET", var->name);
+            if (!parser_create_array_getter(parser, var, name))
+                goto cleanup;
+        }
 
 skipvar:
         if (parser->tok == ';') {