From: Wolfgang (Blub) Bumiller Date: Sun, 11 Nov 2012 15:06:27 +0000 (+0100) Subject: array accessor function genaration X-Git-Tag: 0.1~19^2~34 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=7d9568f806d62ccdb3656cd95178684ac1c44dd6;p=xonotic%2Fgmqcc.git array accessor function genaration --- diff --git a/ast.c b/ast.c index 7e4e75e..cd6662f 100644 --- 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 1d9efb3..6af3ad0 100644 --- a/ast.h +++ b/ast.h @@ -31,20 +31,21 @@ 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. diff --git a/parser.c b/parser.c index ad5ec47..534f810 100644 --- 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), "", 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 == ';') {