]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ast.c
'likely' hint for IFs
[xonotic/gmqcc.git] / ast.c
diff --git a/ast.c b/ast.c
index 2f218e37a4a776bc33dbeec92bf166a7be515110..f5ed565e93644b202798d2ff88c33ada85d57656 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -40,14 +40,14 @@ static void asterror(lex_ctx ctx, const char *msg, ...)
 {
     va_list ap;
     va_start(ap, msg);
-    cvprintmsg(ctx, LVL_ERROR, "error", msg, ap);
+    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)
 {
-    fprintf(stderr, "ast node missing destroy()\n");
+    con_err("ast node missing destroy()\n");
     abort();
 }
 
@@ -70,7 +70,7 @@ static void ast_expression_init(ast_expression *self,
     self->expression.outl     = NULL;
     self->expression.outr     = NULL;
     self->expression.variadic = false;
-    MEM_VECTOR_INIT(&self->expression, params);
+    self->expression.params   = NULL;
 }
 
 static void ast_expression_delete(ast_expression *self)
@@ -78,10 +78,10 @@ static void ast_expression_delete(ast_expression *self)
     size_t i;
     if (self->expression.next)
         ast_delete(self->expression.next);
-    for (i = 0; i < self->expression.params_count; ++i) {
+    for (i = 0; i < vec_size(self->expression.params); ++i) {
         ast_delete(self->expression.params[i]);
     }
-    MEM_VECTOR_CLEAR(&self->expression, params);
+    vec_free(self->expression.params);
 }
 
 static void ast_expression_delete_full(ast_expression *self)
@@ -90,8 +90,6 @@ static void ast_expression_delete_full(ast_expression *self)
     mem_d(self);
 }
 
-MEM_VEC_FUNCTIONS(ast_expression_common, ast_value*, params)
-
 ast_value* ast_value_copy(const ast_value *self)
 {
     size_t i;
@@ -108,12 +106,13 @@ ast_value* ast_value_copy(const ast_value *self)
     fromex   = &self->expression;
     selfex = &cp->expression;
     selfex->variadic = fromex->variadic;
-    for (i = 0; i < fromex->params_count; ++i) {
+    for (i = 0; i < vec_size(fromex->params); ++i) {
         ast_value *v = ast_value_copy(fromex->params[i]);
-        if (!v || !ast_expression_common_params_add(selfex, v)) {
+        if (!v) {
             ast_value_delete(cp);
             return NULL;
         }
+        vec_push(selfex->params, v);
     }
     return cp;
 }
@@ -132,10 +131,11 @@ bool ast_type_adopt_impl(ast_expression *self, const ast_expression *other)
     fromex   = &other->expression;
     selfex = &self->expression;
     selfex->variadic = fromex->variadic;
-    for (i = 0; i < fromex->params_count; ++i) {
+    for (i = 0; i < vec_size(fromex->params); ++i) {
         ast_value *v = ast_value_copy(fromex->params[i]);
-        if (!v || !ast_expression_common_params_add(selfex, v))
+        if (!v)
             return false;
+        vec_push(selfex->params, v);
     }
     return true;
 }
@@ -182,12 +182,13 @@ ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex)
             selfex->next = NULL;
 
         selfex->variadic = fromex->variadic;
-        for (i = 0; i < fromex->params_count; ++i) {
+        for (i = 0; i < vec_size(fromex->params); ++i) {
             ast_value *v = ast_value_copy(fromex->params[i]);
-            if (!v || !ast_expression_common_params_add(selfex, v)) {
+            if (!v) {
                 ast_expression_delete_full(self);
                 return NULL;
             }
+            vec_push(selfex->params, v);
         }
 
         return self;
@@ -200,13 +201,13 @@ bool ast_compare_type(ast_expression *a, ast_expression *b)
         return false;
     if (!a->expression.next != !b->expression.next)
         return false;
-    if (a->expression.params_count != b->expression.params_count)
+    if (vec_size(a->expression.params) != vec_size(b->expression.params))
         return false;
     if (a->expression.variadic != b->expression.variadic)
         return false;
-    if (a->expression.params_count) {
+    if (vec_size(a->expression.params)) {
         size_t i;
-        for (i = 0; i < a->expression.params_count; ++i) {
+        for (i = 0; i < vec_size(a->expression.params); ++i) {
             if (!ast_compare_type((ast_expression*)a->expression.params[i],
                                   (ast_expression*)b->expression.params[i]))
                 return false;
@@ -257,14 +258,14 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
             pos = ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
             if (pos + 2 >= bufsize)
                 goto full;
-            if (e->expression.params_count == 0) {
+            if (!vec_size(e->expression.params)) {
                 buf[pos++] = '(';
                 buf[pos++] = ')';
                 return pos;
             }
             buf[pos++] = '(';
             pos = ast_type_to_string_impl((ast_expression*)(e->expression.params[0]), buf, bufsize, pos);
-            for (i = 1; i < e->expression.params_count; ++i) {
+            for (i = 1; i < vec_size(e->expression.params); ++i) {
                 if (pos + 2 >= bufsize)
                     goto full;
                 buf[pos++] = ',';
@@ -276,6 +277,17 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
             buf[pos++] = ')';
             return pos;
 
+        case TYPE_ARRAY:
+            pos = ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
+            if (pos + 1 >= bufsize)
+                goto full;
+            buf[pos++] = '[';
+            pos += snprintf(buf + pos, bufsize - pos - 1, "%i", (int)e->expression.count);
+            if (pos + 1 >= bufsize)
+                goto full;
+            buf[pos++] = ']';
+            return pos;
+
         default:
             typestr = type_name[e->expression.vtype];
             typelen = strlen(typestr);
@@ -312,7 +324,12 @@ ast_value* ast_value_new(lex_ctx ctx, const char *name, int t)
     self->uses    = 0;
     memset(&self->constval, 0, sizeof(self->constval));
 
-    self->ir_v    = NULL;
+    self->ir_v           = NULL;
+    self->ir_values      = NULL;
+    self->ir_value_count = 0;
+
+    self->setter = NULL;
+    self->getter = NULL;
 
     return self;
 }
@@ -338,13 +355,15 @@ void ast_value_delete(ast_value* self)
             break;
         }
     }
+    if (self->ir_values)
+        mem_d(self->ir_values);
     ast_expression_delete((ast_expression*)self);
     mem_d(self);
 }
 
-bool GMQCC_WARN ast_value_params_add(ast_value *self, ast_value *p)
+void ast_value_params_add(ast_value *self, ast_value *p)
 {
-    return ast_expression_common_params_add(&self->expression, p);
+    vec_push(self->expression.params, p);
 }
 
 bool ast_value_set_name(ast_value *self, const char *name)
@@ -465,16 +484,17 @@ void ast_return_delete(ast_return *self)
 
 ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field)
 {
-    const ast_expression *outtype;
-
-    ast_instantiate(ast_entfield, ctx, ast_entfield_delete);
-
     if (field->expression.vtype != TYPE_FIELD) {
-        mem_d(self);
+        asterror(ctx, "ast_entfield_new with expression not of type field");
         return NULL;
     }
+    return ast_entfield_new_force(ctx, entity, field, field->expression.next);
+}
+
+ast_entfield* ast_entfield_new_force(lex_ctx ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype)
+{
+    ast_instantiate(ast_entfield, ctx, ast_entfield_delete);
 
-    outtype = field->expression.next;
     if (!outtype) {
         mem_d(self);
         /* Error: field has no type... */
@@ -548,6 +568,48 @@ 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)
+{
+    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;
+    }
+    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");
+            ast_array_index_delete(self);
+            return NULL;
+        }
+        self->array = outtype;
+        self->expression.vtype = TYPE_FIELD;
+    }
+
+    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);
@@ -638,15 +700,57 @@ void ast_loop_delete(ast_loop *self)
     mem_d(self);
 }
 
+ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont)
+{
+    ast_instantiate(ast_breakcont, ctx, ast_breakcont_delete);
+    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_breakcont_codegen);
+
+    self->is_continue = iscont;
+
+    return self;
+}
+
+void ast_breakcont_delete(ast_breakcont *self)
+{
+    ast_expression_delete((ast_expression*)self);
+    mem_d(self);
+}
+
+ast_switch* ast_switch_new(lex_ctx ctx, ast_expression *op)
+{
+    ast_instantiate(ast_switch, ctx, ast_switch_delete);
+    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_switch_codegen);
+
+    self->operand = op;
+    self->cases   = NULL;
+
+    return self;
+}
+
+void ast_switch_delete(ast_switch *self)
+{
+    size_t i;
+    ast_unref(self->operand);
+
+    for (i = 0; i < vec_size(self->cases); ++i) {
+        if (self->cases[i].value)
+            ast_unref(self->cases[i].value);
+        ast_unref(self->cases[i].code);
+    }
+    vec_free(self->cases);
+
+    ast_expression_delete((ast_expression*)self);
+    mem_d(self);
+}
+
 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);
 
-    MEM_VECTOR_INIT(self, params);
-
-    self->func = funcexpr;
+    self->params = NULL;
+    self->func   = funcexpr;
 
     self->expression.vtype = funcexpr->expression.next->expression.vtype;
     if (funcexpr->expression.next->expression.next)
@@ -654,14 +758,13 @@ ast_call* ast_call_new(lex_ctx ctx,
 
     return self;
 }
-MEM_VEC_FUNCTIONS(ast_call, ast_expression*, params)
 
 void ast_call_delete(ast_call *self)
 {
     size_t i;
-    for (i = 0; i < self->params_count; ++i)
+    for (i = 0; i < vec_size(self->params); ++i)
         ast_unref(self->params[i]);
-    MEM_VECTOR_CLEAR(self, params);
+    vec_free(self->params);
 
     if (self->func)
         ast_unref(self->func);
@@ -675,9 +778,9 @@ bool ast_call_check_types(ast_call *self)
     size_t i;
     bool   retval = true;
     const  ast_expression *func = self->func;
-    size_t count = self->params_count;
-    if (count > func->expression.params_count)
-        count = func->expression.params_count;
+    size_t count = vec_size(self->params);
+    if (count > vec_size(func->expression.params))
+        count = vec_size(func->expression.params);
 
     for (i = 0; i < count; ++i) {
         if (!ast_compare_type(self->params[i], (ast_expression*)(func->expression.params[i]))) {
@@ -728,36 +831,31 @@ ast_block* ast_block_new(lex_ctx ctx)
     ast_expression_init((ast_expression*)self,
                         (ast_expression_codegen*)&ast_block_codegen);
 
-    MEM_VECTOR_INIT(self, locals);
-    MEM_VECTOR_INIT(self, exprs);
-    MEM_VECTOR_INIT(self, collect);
+    self->locals  = NULL;
+    self->exprs   = NULL;
+    self->collect = NULL;
 
     return self;
 }
-MEM_VEC_FUNCTIONS(ast_block, ast_value*, locals)
-MEM_VEC_FUNCTIONS(ast_block, ast_expression*, exprs)
-MEM_VEC_FUNCTIONS(ast_block, ast_expression*, collect)
 
-bool ast_block_collect(ast_block *self, ast_expression *expr)
+void ast_block_collect(ast_block *self, ast_expression *expr)
 {
-    if (!ast_block_collect_add(self, expr))
-        return false;
+    vec_push(self->collect, expr);
     expr->expression.node.keep = true;
-    return true;
 }
 
 void ast_block_delete(ast_block *self)
 {
     size_t i;
-    for (i = 0; i < self->exprs_count; ++i)
+    for (i = 0; i < vec_size(self->exprs); ++i)
         ast_unref(self->exprs[i]);
-    MEM_VECTOR_CLEAR(self, exprs);
-    for (i = 0; i < self->locals_count; ++i)
+    vec_free(self->exprs);
+    for (i = 0; i < vec_size(self->locals); ++i)
         ast_delete(self->locals[i]);
-    MEM_VECTOR_CLEAR(self, locals);
-    for (i = 0; i < self->collect_count; ++i)
+    vec_free(self->locals);
+    for (i = 0; i < vec_size(self->collect); ++i)
         ast_delete(self->collect[i]);
-    MEM_VECTOR_CLEAR(self, collect);
+    vec_free(self->collect);
     ast_expression_delete((ast_expression*)self);
     mem_d(self);
 }
@@ -789,9 +887,9 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype)
         return NULL;
     }
 
-    self->vtype = vtype;
-    self->name = name ? util_strdup(name) : NULL;
-    MEM_VECTOR_INIT(self, blocks);
+    self->vtype  = vtype;
+    self->name   = name ? util_strdup(name) : NULL;
+    self->blocks = NULL;
 
     self->labelcount = 0;
     self->builtin = 0;
@@ -808,8 +906,6 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype)
     return self;
 }
 
-MEM_VEC_FUNCTIONS(ast_function, ast_block*, blocks)
-
 void ast_function_delete(ast_function *self)
 {
     size_t i;
@@ -824,9 +920,9 @@ void ast_function_delete(ast_function *self)
          */
         ast_unref(self->vtype);
     }
-    for (i = 0; i < self->blocks_count; ++i)
+    for (i = 0; i < vec_size(self->blocks); ++i)
         ast_delete(self->blocks[i]);
-    MEM_VECTOR_CLEAR(self, blocks);
+    vec_free(self->blocks);
     mem_d(self);
 }
 
@@ -870,7 +966,9 @@ bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_valu
      * on all the globals.
      */
     if (!self->ir_v) {
-        asterror(ast_ctx(self), "ast_value used before generated (%s)", self->name);
+        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);
         return false;
     }
     *out = self->ir_v;
@@ -880,6 +978,7 @@ bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_valu
 bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 {
     ir_value *v = NULL;
+
     if (self->isconst && self->expression.vtype == TYPE_FUNCTION)
     {
         ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->expression.vtype);
@@ -895,26 +994,130 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
     }
 
     if (isfield && self->expression.vtype == TYPE_FIELD) {
-        v = ir_builder_create_field(ir, self->name, self->expression.next->expression.vtype);
-        if (!v)
-            return false;
-        v->context = ast_ctx(self);
+        ast_expression *fieldtype = self->expression.next;
+
         if (self->isconst) {
             asterror(ast_ctx(self), "TODO: constant field pointers with value");
             goto error;
         }
-        self->ir_v = v;
+
+        if (fieldtype->expression.vtype == TYPE_ARRAY) {
+            size_t ai;
+            char   *name;
+            size_t  namelen;
+
+            ast_expression_common *elemtype;
+            int                    vtype;
+            ast_value             *array = (ast_value*)fieldtype;
+
+            if (!ast_istype(fieldtype, ast_value)) {
+                asterror(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);
+
+            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");
+                return false;
+            }
+            if (vtype == TYPE_FIELD)
+                v->fieldtype = elemtype->next->expression.vtype;
+            v->context = ast_ctx(self);
+            array->ir_v = self->ir_v = v;
+
+            namelen = strlen(self->name);
+            name    = (char*)mem_a(namelen + 16);
+            strcpy(name, self->name);
+
+            array->ir_values = (ir_value**)mem_a(sizeof(array->ir_values[0]) * array->expression.count);
+            array->ir_values[0] = v;
+            for (ai = 1; ai < array->expression.count; ++ai) {
+                snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
+                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");
+                    return false;
+                }
+                if (vtype == TYPE_FIELD)
+                    array->ir_values[ai]->fieldtype = elemtype->next->expression.vtype;
+                array->ir_values[ai]->context = ast_ctx(self);
+            }
+            mem_d(name);
+        }
+        else
+        {
+            v = ir_builder_create_field(ir, self->name, self->expression.next->expression.vtype);
+            if (!v)
+                return false;
+            v->context = ast_ctx(self);
+            self->ir_v = v;
+        }
         return true;
     }
 
-    v = ir_builder_create_global(ir, self->name, self->expression.vtype);
-    if (!v) {
-        asterror(ast_ctx(self), "ir_builder_create_global failed");
-        return false;
+    if (self->expression.vtype == TYPE_ARRAY) {
+        size_t ai;
+        char   *name;
+        size_t  namelen;
+
+        ast_expression_common *elemtype = &self->expression.next->expression;
+        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);
+
+        v = ir_builder_create_global(ir, self->name, vtype);
+        if (!v) {
+            asterror(ast_ctx(self), "ir_builder_create_global failed");
+            return false;
+        }
+        if (vtype == TYPE_FIELD)
+            v->fieldtype = elemtype->next->expression.vtype;
+        v->context = ast_ctx(self);
+
+        namelen = strlen(self->name);
+        name    = (char*)mem_a(namelen + 16);
+        strcpy(name, self->name);
+
+        self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count);
+        self->ir_values[0] = v;
+        for (ai = 1; ai < self->expression.count; ++ai) {
+            snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
+            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");
+                return false;
+            }
+            if (vtype == TYPE_FIELD)
+                self->ir_values[ai]->fieldtype = elemtype->next->expression.vtype;
+            self->ir_values[ai]->context = ast_ctx(self);
+        }
+        mem_d(name);
+    }
+    else
+    {
+        /* Arrays don't do this since there's no "array" value which spans across the
+         * whole thing.
+         */
+        v = ir_builder_create_global(ir, self->name, self->expression.vtype);
+        if (!v) {
+            asterror(ast_ctx(self), "ir_builder_create_global failed");
+            return false;
+        }
+        if (self->expression.vtype == TYPE_FIELD)
+            v->fieldtype = self->expression.next->expression.vtype;
+        v->context = ast_ctx(self);
     }
-    if (self->expression.vtype == TYPE_FIELD)
-        v->fieldtype = self->expression.next->expression.vtype;
-    v->context = ast_ctx(self);
 
     if (self->isconst) {
         switch (self->expression.vtype)
@@ -931,6 +1134,9 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
                 if (!ir_value_set_string(v, self->constval.vstring))
                     goto error;
                 break;
+            case TYPE_ARRAY:
+                asterror(ast_ctx(self), "TODO: global constant array");
+                break;
             case TYPE_FUNCTION:
                 asterror(ast_ctx(self), "global of type function not properly generated");
                 goto error;
@@ -963,12 +1169,65 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
         return false;
     }
 
-    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;
-    v->context = ast_ctx(self);
+    if (self->expression.vtype == TYPE_ARRAY) {
+        size_t ai;
+        char   *name;
+        size_t  namelen;
+
+        ast_expression_common *elemtype = &self->expression.next->expression;
+        int vtype = elemtype->vtype;
+
+        if (param) {
+            asterror(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);
+        }
+
+        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");
+            return false;
+        }
+
+        v = ir_function_create_local(func, self->name, vtype, param);
+        if (!v) {
+            asterror(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);
+
+        namelen = strlen(self->name);
+        name    = (char*)mem_a(namelen + 16);
+        strcpy(name, self->name);
+
+        self->ir_values[0] = v;
+        for (ai = 1; ai < self->expression.count; ++ai) {
+            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");
+                return false;
+            }
+            if (vtype == TYPE_FIELD)
+                self->ir_values[ai]->fieldtype = elemtype->next->expression.vtype;
+            self->ir_values[ai]->context = ast_ctx(self);
+        }
+    }
+    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;
+        v->context = ast_ctx(self);
+    }
 
     /* A constant local... hmmm...
      * I suppose the IR will have to deal with this
@@ -996,6 +1255,19 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
 
     /* link us to the ir_value */
     self->ir_v = v;
+
+    if (self->setter) {
+        if (!ast_global_codegen(self->setter, func->owner, false) ||
+            !ast_function_codegen(self->setter->constval.vfunc, func->owner) ||
+            !ir_function_finalize(self->setter->constval.vfunc->ir_func))
+            return false;
+    }
+    if (self->getter) {
+        if (!ast_global_codegen(self->getter, func->owner, false) ||
+            !ast_function_codegen(self->getter->constval.vfunc, func->owner) ||
+            !ir_function_finalize(self->getter->constval.vfunc->ir_func))
+            return false;
+    }
     return true;
 
 error: /* clean up */
@@ -1018,10 +1290,9 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
 
     /* fill the parameter list */
     ec = &self->vtype->expression;
-    for (i = 0; i < ec->params_count; ++i)
+    for (i = 0; i < vec_size(ec->params); ++i)
     {
-        if (!ir_function_params_add(irf, ec->params[i]->expression.vtype))
-            return false;
+        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;
@@ -1033,7 +1304,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
         return true;
     }
 
-    if (!self->blocks_count) {
+    if (!vec_size(self->blocks)) {
         asterror(ast_ctx(self), "function `%s` has no body", self->name);
         return false;
     }
@@ -1044,7 +1315,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
         return false;
     }
 
-    for (i = 0; i < self->blocks_count; ++i) {
+    for (i = 0; i < vec_size(self->blocks); ++i) {
         ast_expression_codegen *gen = self->blocks[i]->expression.codegen;
         if (!(*gen)((ast_expression*)self->blocks[i], self, false, &dummy))
             return false;
@@ -1105,7 +1376,7 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
     *out = NULL;
 
     /* generate locals */
-    for (i = 0; i < self->locals_count; ++i)
+    for (i = 0; i < vec_size(self->locals); ++i)
     {
         if (!ast_local_codegen(self->locals[i], func->ir_func, false)) {
             if (opts_debug)
@@ -1114,9 +1385,13 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
         }
     }
 
-    for (i = 0; i < self->exprs_count; ++i)
+    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;
+        }
         if (!(*gen)(self->exprs[i], func, false, out))
             return false;
     }
@@ -1131,6 +1406,10 @@ bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_valu
     ast_expression_codegen *cgen;
     ir_value *left, *right;
 
+    ast_value       *arr;
+    ast_value       *idx;
+    ast_array_index *ai = NULL;
+
     if (lvalue && self->expression.outl) {
         *out = self->expression.outl;
         return true;
@@ -1141,20 +1420,70 @@ bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_valu
         return true;
     }
 
-    cgen = self->dest->expression.codegen;
-    /* lvalue! */
-    if (!(*cgen)((ast_expression*)(self->dest), func, true, &left))
-        return false;
-    self->expression.outl = left;
+    if (ast_istype(self->dest, ast_array_index))
+    {
 
-    cgen = self->source->expression.codegen;
-    /* rvalue! */
-    if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
-        return false;
+        ai = (ast_array_index*)self->dest;
+        idx = (ast_value*)ai->index;
 
-    if (!ir_block_create_store_op(func->curblock, self->op, left, right))
-        return false;
-    self->expression.outr = right;
+        if (ast_istype(ai->index, ast_value) && idx->isconst)
+            ai = NULL;
+    }
+
+    if (ai) {
+        /* we need to call the setter */
+        ir_value  *iridx, *funval;
+        ir_instr  *call;
+
+        if (lvalue) {
+            asterror(ast_ctx(self), "array-subscript assignment cannot produce lvalues");
+            return false;
+        }
+
+        arr = (ast_value*)ai->array;
+        if (!ast_istype(ai->array, ast_value) || !arr->setter) {
+            asterror(ast_ctx(self), "value has no setter (%s)", arr->name);
+            return false;
+        }
+
+        cgen = idx->expression.codegen;
+        if (!(*cgen)((ast_expression*)(idx), func, false, &iridx))
+            return false;
+
+        cgen = arr->setter->expression.codegen;
+        if (!(*cgen)((ast_expression*)(arr->setter), func, true, &funval))
+            return false;
+
+        cgen = self->source->expression.codegen;
+        if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
+            return false;
+
+        call = ir_block_create_call(func->curblock, ast_function_label(func, "store"), funval);
+        if (!call)
+            return false;
+        ir_call_param(call, iridx);
+        ir_call_param(call, right);
+        self->expression.outr = right;
+    }
+    else
+    {
+        /* regular code */
+
+        cgen = self->dest->expression.codegen;
+        /* lvalue! */
+        if (!(*cgen)((ast_expression*)(self->dest), func, true, &left))
+            return false;
+        self->expression.outl = left;
+
+        cgen = self->source->expression.codegen;
+        /* rvalue! */
+        if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
+            return false;
+
+        if (!ir_block_create_store_op(func->curblock, self->op, left, right))
+            return false;
+        self->expression.outr = right;
+    }
 
     /* Theoretically, an assinment returns its left side as an
      * lvalue, if we don't need an lvalue though, we return
@@ -1184,13 +1513,60 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
         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;
+
+        merge_id = vec_size(func->blocks);
+        merge = ir_function_create_block(func->ir_func, ast_function_label(func, "sce_merge"));
+
+        cgen = self->left->expression.codegen;
+        if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
+            return false;
+
+        from_left = func->curblock;
+        other = ir_function_create_block(func->ir_func, ast_function_label(func, "sce_other"));
+        if (self->op == INSTR_AND) {
+            if (!ir_block_create_if(func->curblock, left, other, merge))
+                return false;
+        } else {
+            if (!ir_block_create_if(func->curblock, left, merge, other))
+                return false;
+        }
+        /* use the unlikely flag */
+        vec_last(func->curblock->instr)->likely = false;
+
+        func->curblock = other;
+        cgen = self->right->expression.codegen;
+        if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
+            return false;
+        from_right = func->curblock;
+
+        if (!ir_block_create_jump(func->curblock, merge))
+            return false;
+
+        vec_remove(func->ir_func->blocks, merge_id, 1);
+        vec_push(func->ir_func->blocks, merge);
+
+        func->curblock = merge;
+        phi = ir_block_create_phi(func->curblock, ast_function_label(func, "sce_value"), TYPE_FLOAT);
+        ir_phi_add(phi, from_left, left);
+        ir_phi_add(phi, from_right, right);
+        *out = ir_phi_value(phi);
+        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;
 
@@ -1402,6 +1778,76 @@ 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 (!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;
@@ -1591,12 +2037,10 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
 
     /* create PHI */
     phi = ir_block_create_phi(merge, ast_function_label(func, "phi"), trueval->vtype);
-    if (!phi ||
-        !ir_phi_add(phi, ontrue,  trueval) ||
-        !ir_phi_add(phi, onfalse, falseval))
-    {
+    if (!phi)
         return false;
-    }
+    ir_phi_add(phi, ontrue,  trueval);
+    ir_phi_add(phi, onfalse, falseval);
 
     self->phi_out = ir_phi_value(phi);
     *out = self->phi_out;
@@ -1708,7 +2152,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
         bpostcond = end_bpostcond = NULL;
     }
 
-    bout_id = func->ir_func->blocks_count;
+    bout_id = vec_size(func->ir_func->blocks);
     bout = ir_function_create_block(func->ir_func, ast_function_label(func, "after_loop"));
     if (!bout)
         return false;
@@ -1828,20 +2272,195 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
     }
 
     /* Move 'bout' to the end */
-    if (!ir_function_blocks_remove(func->ir_func, bout_id) ||
-        !ir_function_blocks_add(func->ir_func, bout))
-    {
-        ir_block_delete(bout);
+    vec_remove(func->ir_func->blocks, bout_id, 1);
+    vec_push(func->ir_func->blocks, bout);
+
+    return true;
+}
+
+bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_block *target;
+
+    if (lvalue) {
+        asterror(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!");
+        return false;
+    }
+    self->expression.outr = (ir_value*)1;
+
+    if (self->is_continue)
+        target = func->continueblock;
+    else
+        target = func->breakblock;
+
+    if (!ir_block_create_jump(func->curblock, target))
+        return false;
+    return true;
+}
+
+bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_value **out)
+{
+    ast_expression_codegen *cgen;
+
+    ast_switch_case *def_case  = NULL;
+    ir_block        *def_bfall = NULL;
+
+    ir_value *dummy     = NULL;
+    ir_value *irop      = NULL;
+    ir_block *old_break = NULL;
+    ir_block *bout      = NULL;
+    ir_block *bfall     = NULL;
+    size_t    bout_id;
+    size_t    c;
+
+    char      typestr[1024];
+    uint16_t  cmpinstr;
+
+    if (lvalue) {
+        asterror(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!");
+        return false;
+    }
+    self->expression.outr = (ir_value*)1;
+
+    (void)lvalue;
+    (void)out;
+
+    cgen = self->operand->expression.codegen;
+    if (!(*cgen)((ast_expression*)(self->operand), func, false, &irop))
+        return false;
+
+    if (!vec_size(self->cases))
+        return true;
+
+    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);
+        return false;
+    }
+
+    bout_id = vec_size(func->ir_func->blocks);
+    bout = ir_function_create_block(func->ir_func, ast_function_label(func, "after_switch"));
+    if (!bout)
+        return false;
+
+    /* setup the break block */
+    old_break        = func->breakblock;
+    func->breakblock = bout;
+
+    /* Now create all cases */
+    for (c = 0; c < vec_size(self->cases); ++c) {
+        ir_value *cond, *val;
+        ir_block *bcase, *bnot;
+        size_t bnot_id;
+
+        ast_switch_case *swcase = &self->cases[c];
+
+        if (swcase->value) {
+            /* A regular case */
+            /* generate the condition operand */
+            cgen = swcase->value->expression.codegen;
+            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);
+            if (!cond)
+                return false;
+
+            bcase = ir_function_create_block(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"));
+            if (!bcase || !bnot)
+                return false;
+            if (!ir_block_create_if(func->curblock, cond, bcase, bnot))
+                return false;
+
+            /* Make the previous case-end fall through */
+            if (bfall && !bfall->final) {
+                if (!ir_block_create_jump(bfall, bcase))
+                    return false;
+            }
+
+            /* enter the case */
+            func->curblock = bcase;
+            cgen = swcase->code->expression.codegen;
+            if (!(*cgen)((ast_expression*)swcase->code, func, false, &dummy))
+                return false;
+
+            /* remember this block to fall through from */
+            bfall = func->curblock;
+
+            /* enter the else and move it down */
+            func->curblock = bnot;
+            vec_remove(func->ir_func->blocks, bnot_id, 1);
+            vec_push(func->ir_func->blocks, bnot);
+        } else {
+            /* The default case */
+            /* Remember where to fall through from: */
+            def_bfall = bfall;
+            bfall     = NULL;
+            /* remember which case it was */
+            def_case  = swcase;
+        }
+    }
+
+    /* Jump from the last bnot to bout */
+    if (bfall && !bfall->final && !ir_block_create_jump(bfall, bout)) {
+        /*
+        astwarning(ast_ctx(bfall), WARN_???, "missing break after last case");
+        */
+        return false;
+    }
+
+    /* If there was a default case, put it down here */
+    if (def_case) {
+        ir_block *bcase;
+
+        /* No need to create an extra block */
+        bcase = func->curblock;
+
+        /* Insert the fallthrough jump */
+        if (def_bfall && !def_bfall->final) {
+            if (!ir_block_create_jump(def_bfall, bcase))
+                return false;
+        }
+
+        /* Now generate the default code */
+        cgen = def_case->code->expression.codegen;
+        if (!(*cgen)((ast_expression*)def_case->code, func, false, &dummy))
+            return false;
+    }
+
+    /* Jump from the last bnot to bout */
+    if (!func->curblock->final && !ir_block_create_jump(func->curblock, bout))
+        return false;
+    /* enter the outgoing block */
+    func->curblock = bout;
+
+    /* restore the break block */
+    func->breakblock = old_break;
+
+    /* Move 'bout' to the end, it's nicer */
+    vec_remove(func->ir_func->blocks, bout_id, 1);
+    vec_push(func->ir_func->blocks, bout);
+
     return true;
 }
 
 bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out)
 {
     ast_expression_codegen *cgen;
-    ir_value_vector         params;
+    ir_value              **params;
     ir_instr               *callinstr;
     size_t i;
 
@@ -1864,10 +2483,10 @@ bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value
     if (!funval)
         return false;
 
-    MEM_VECTOR_INIT(&params, v);
+    params = NULL;
 
     /* parameters */
-    for (i = 0; i < self->params_count; ++i)
+    for (i = 0; i < vec_size(self->params); ++i)
     {
         ir_value *param;
         ast_expression *expr = self->params[i];
@@ -1877,25 +2496,23 @@ bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value
             goto error;
         if (!param)
             goto error;
-        if (!ir_value_vector_v_add(&params, param))
-            goto error;
+        vec_push(params, param);
     }
 
     callinstr = ir_block_create_call(func->curblock, ast_function_label(func, "call"), funval);
     if (!callinstr)
         goto error;
 
-    for (i = 0; i < params.v_count; ++i) {
-        if (!ir_call_param(callinstr, params.v[i]))
-            goto error;
+    for (i = 0; i < vec_size(params); ++i) {
+        ir_call_param(callinstr, params[i]);
     }
 
     *out = ir_call_value(callinstr);
     self->expression.outr = *out;
 
-    MEM_VECTOR_CLEAR(&params, v);
+    vec_free(params);
     return true;
 error:
-    MEM_VECTOR_CLEAR(&params, v);
+    vec_free(params);
     return false;
 }