]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ast.c
Make it a function
[xonotic/gmqcc.git] / ast.c
diff --git a/ast.c b/ast.c
index 87d34cedc84d83badf820f32ac492916138e8e56..24d0479a71f0b6ae956cce8aa3e264ebdff5cc1c 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
@@ -26,6 +26,7 @@
 
 #include "gmqcc.h"
 #include "ast.h"
+#include "parser.h"
 
 #define ast_instantiate(T, ctx, destroyfn)                          \
     T* self = (T*)mem_a(sizeof(T));                                 \
@@ -74,6 +75,7 @@ static void ast_binstore_delete(ast_binstore*);
 static bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**);
 static void ast_binary_delete(ast_binary*);
 static bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**);
+static bool ast_state_codegen(ast_state*, ast_function*, bool lvalue, ir_value**);
 
 /* It must not be possible to get here. */
 static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
@@ -84,7 +86,7 @@ static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
 }
 
 /* Initialize main ast node aprts */
-static void ast_node_init(ast_node *self, lex_ctx ctx, int nodetype)
+static void ast_node_init(ast_node *self, lex_ctx_t ctx, int nodetype)
 {
     self->context = ctx;
     self->destroy = &_ast_node_destroy;
@@ -112,8 +114,10 @@ static void ast_expression_init(ast_expression *self,
     self->outr     = NULL;
     self->params   = NULL;
     self->count    = 0;
-    self->flags    = 0;
     self->varparam = NULL;
+    self->flags    = 0;
+    if (OPTS_OPTION_BOOL(OPTION_COVERAGE))
+        self->flags |= AST_FLAG_BLOCK_COVERAGE;
 }
 
 static void ast_expression_delete(ast_expression *self)
@@ -174,7 +178,7 @@ void ast_type_adopt_impl(ast_expression *self, const ast_expression *other)
     }
 }
 
-static ast_expression* ast_shallow_type(lex_ctx ctx, int vtype)
+static ast_expression* ast_shallow_type(lex_ctx_t ctx, int vtype)
 {
     ast_instantiate(ast_expression, ctx, ast_expression_delete_full);
     ast_expression_init(self, NULL);
@@ -184,7 +188,7 @@ static ast_expression* ast_shallow_type(lex_ctx ctx, int vtype)
     return self;
 }
 
-ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex)
+ast_expression* ast_type_copy(lex_ctx_t ctx, const ast_expression *ex)
 {
     size_t i;
     const ast_expression *fromex;
@@ -342,7 +346,7 @@ void ast_type_to_string(ast_expression *e, char *buf, size_t bufsize)
 }
 
 static bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_value **out);
-ast_value* ast_value_new(lex_ctx ctx, const char *name, int t)
+ast_value* ast_value_new(lex_ctx_t ctx, const char *name, int t)
 {
     ast_instantiate(ast_value, ctx, ast_value_delete);
     ast_expression_init((ast_expression*)self,
@@ -356,6 +360,7 @@ ast_value* ast_value_new(lex_ctx ctx, const char *name, int t)
     self->cvq      = CV_NONE;
     self->hasvalue = false;
     self->isimm    = false;
+    self->inexact  = false;
     self->uses     = 0;
     memset(&self->constval, 0, sizeof(self->constval));
     self->initlist = NULL;
@@ -368,7 +373,8 @@ ast_value* ast_value_new(lex_ctx ctx, const char *name, int t)
     self->getter = NULL;
     self->desc   = NULL;
 
-    self->argcounter  = NULL;
+    self->argcounter = NULL;
+    self->intrinsic = false;
 
     return self;
 }
@@ -433,15 +439,34 @@ bool ast_value_set_name(ast_value *self, const char *name)
     return !!self->name;
 }
 
-ast_binary* ast_binary_new(lex_ctx ctx, int op,
+ast_binary* ast_binary_new(lex_ctx_t ctx, int op,
                            ast_expression* left, ast_expression* right)
 {
     ast_instantiate(ast_binary, ctx, ast_binary_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_binary_codegen);
 
+    if (ast_istype(right, ast_unary) && OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) {
+        ast_unary      *unary  = ((ast_unary*)right);
+        ast_expression *normal = unary->operand;
+
+        /* make a-(-b) => a + b */
+        if (unary->op == VINSTR_NEG_F || unary->op == VINSTR_NEG_V) {
+            if (op == INSTR_SUB_F) {
+                op = INSTR_ADD_F;
+                right = normal;
+                ++opts_optimizationcount[OPTIM_PEEPHOLE];
+            } else if (op == INSTR_SUB_V) {
+                op = INSTR_ADD_V;
+                right = normal;
+                ++opts_optimizationcount[OPTIM_PEEPHOLE];
+            }
+        }
+    }
+
     self->op = op;
     self->left = left;
     self->right = right;
+    self->right_first = false;
 
     ast_propagate_effects(self, left);
     ast_propagate_effects(self, right);
@@ -478,7 +503,7 @@ void ast_binary_delete(ast_binary *self)
     mem_d(self);
 }
 
-ast_binstore* ast_binstore_new(lex_ctx ctx, int storop, int op,
+ast_binstore* ast_binstore_new(lex_ctx_t ctx, int storop, int op,
                                ast_expression* left, ast_expression* right)
 {
     ast_instantiate(ast_binstore, ctx, ast_binstore_delete);
@@ -506,21 +531,40 @@ void ast_binstore_delete(ast_binstore *self)
     mem_d(self);
 }
 
-ast_unary* ast_unary_new(lex_ctx ctx, int op,
+ast_unary* ast_unary_new(lex_ctx_t ctx, int op,
                          ast_expression *expr)
 {
     ast_instantiate(ast_unary, ctx, ast_unary_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_unary_codegen);
 
-    self->op = op;
+    self->op      = op;
     self->operand = expr;
 
+
+    if (ast_istype(expr, ast_unary) && OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) {
+        ast_unary *prev = (ast_unary*)((ast_unary*)expr)->operand;
+
+        /* Handle for double negation */
+        if (((ast_unary*)expr)->op == op)
+            prev = (ast_unary*)((ast_unary*)expr)->operand;
+
+        if (ast_istype(prev, ast_unary)) {
+            ast_expression_delete((ast_expression*)self);
+            mem_d(self);
+            ++opts_optimizationcount[OPTIM_PEEPHOLE];
+            return prev;
+        }
+    }
+
     ast_propagate_effects(self, expr);
 
-    if (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) {
+    if ((op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) || op == VINSTR_NEG_F) {
         self->expression.vtype = TYPE_FLOAT;
-    } else
-        compile_error(ctx, "cannot determine type of unary operation %s", asm_instr[op].m);
+    } else if (op == VINSTR_NEG_V) {
+        self->expression.vtype = TYPE_VECTOR;
+    } else {
+        compile_error(ctx, "cannot determine type of unary operation %s", util_instr_str[op]);
+    }
 
     return self;
 }
@@ -532,7 +576,7 @@ void ast_unary_delete(ast_unary *self)
     mem_d(self);
 }
 
-ast_return* ast_return_new(lex_ctx ctx, ast_expression *expr)
+ast_return* ast_return_new(lex_ctx_t ctx, ast_expression *expr)
 {
     ast_instantiate(ast_return, ctx, ast_return_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_return_codegen);
@@ -553,7 +597,7 @@ void ast_return_delete(ast_return *self)
     mem_d(self);
 }
 
-ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field)
+ast_entfield* ast_entfield_new(lex_ctx_t ctx, ast_expression *entity, ast_expression *field)
 {
     if (field->vtype != TYPE_FIELD) {
         compile_error(ctx, "ast_entfield_new with expression not of type field");
@@ -562,7 +606,7 @@ ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expressi
     return ast_entfield_new_force(ctx, entity, field, field->next);
 }
 
-ast_entfield* ast_entfield_new_force(lex_ctx ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype)
+ast_entfield* ast_entfield_new_force(lex_ctx_t ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype)
 {
     ast_instantiate(ast_entfield, ctx, ast_entfield_delete);
 
@@ -591,7 +635,7 @@ void ast_entfield_delete(ast_entfield *self)
     mem_d(self);
 }
 
-ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int field, const char *name)
+ast_member* ast_member_new(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const char *name)
 {
     ast_instantiate(ast_member, ctx, ast_member_delete);
     if (field >= 3) {
@@ -653,7 +697,7 @@ bool ast_member_set_name(ast_member *self, const char *name)
     return !!self->name;
 }
 
-ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_expression *index)
+ast_array_index* ast_array_index_new(lex_ctx_t ctx, ast_expression *array, ast_expression *index)
 {
     ast_expression *outtype;
     ast_instantiate(ast_array_index, ctx, ast_array_index_delete);
@@ -696,7 +740,7 @@ void ast_array_index_delete(ast_array_index *self)
     mem_d(self);
 }
 
-ast_argpipe* ast_argpipe_new(lex_ctx ctx, ast_expression *index)
+ast_argpipe* ast_argpipe_new(lex_ctx_t ctx, ast_expression *index)
 {
     ast_instantiate(ast_argpipe, ctx, ast_argpipe_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_argpipe_codegen);
@@ -713,7 +757,7 @@ void ast_argpipe_delete(ast_argpipe *self)
     mem_d(self);
 }
 
-ast_ifthen* ast_ifthen_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse)
+ast_ifthen* ast_ifthen_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse)
 {
     ast_instantiate(ast_ifthen, ctx, ast_ifthen_delete);
     if (!ontrue && !onfalse) {
@@ -746,7 +790,7 @@ void ast_ifthen_delete(ast_ifthen *self)
     mem_d(self);
 }
 
-ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse)
+ast_ternary* ast_ternary_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse)
 {
     ast_expression *exprtype = ontrue;
     ast_instantiate(ast_ternary, ctx, ast_ternary_delete);
@@ -783,7 +827,7 @@ void ast_ternary_delete(ast_ternary *self)
     mem_d(self);
 }
 
-ast_loop* ast_loop_new(lex_ctx ctx,
+ast_loop* ast_loop_new(lex_ctx_t ctx,
                        ast_expression *initexpr,
                        ast_expression *precond, bool pre_not,
                        ast_expression *postcond, bool post_not,
@@ -832,7 +876,7 @@ void ast_loop_delete(ast_loop *self)
     mem_d(self);
 }
 
-ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels)
+ast_breakcont* ast_breakcont_new(lex_ctx_t ctx, bool iscont, unsigned int levels)
 {
     ast_instantiate(ast_breakcont, ctx, ast_breakcont_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_breakcont_codegen);
@@ -849,7 +893,7 @@ void ast_breakcont_delete(ast_breakcont *self)
     mem_d(self);
 }
 
-ast_switch* ast_switch_new(lex_ctx ctx, ast_expression *op)
+ast_switch* ast_switch_new(lex_ctx_t ctx, ast_expression *op)
 {
     ast_instantiate(ast_switch, ctx, ast_switch_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_switch_codegen);
@@ -878,7 +922,7 @@ void ast_switch_delete(ast_switch *self)
     mem_d(self);
 }
 
-ast_label* ast_label_new(lex_ctx ctx, const char *name, bool undefined)
+ast_label* ast_label_new(lex_ctx_t ctx, const char *name, bool undefined)
 {
     ast_instantiate(ast_label, ctx, ast_label_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_label_codegen);
@@ -906,7 +950,7 @@ static 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_goto* ast_goto_new(lex_ctx_t ctx, const char *name)
 {
     ast_instantiate(ast_goto, ctx, ast_goto_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_goto_codegen);
@@ -930,7 +974,27 @@ void ast_goto_set_label(ast_goto *self, ast_label *label)
     self->target = label;
 }
 
-ast_call* ast_call_new(lex_ctx ctx,
+ast_state* ast_state_new(lex_ctx_t ctx, ast_expression *frame, ast_expression *think)
+{
+    ast_instantiate(ast_state, ctx, ast_state_delete);
+    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_state_codegen);
+    self->framenum  = frame;
+    self->nextthink = think;
+    return self;
+}
+
+void ast_state_delete(ast_state *self)
+{
+    if (self->framenum)
+        ast_unref(self->framenum);
+    if (self->nextthink)
+        ast_unref(self->nextthink);
+
+    ast_expression_delete((ast_expression*)self);
+    mem_d(self);
+}
+
+ast_call* ast_call_new(lex_ctx_t ctx,
                        ast_expression *funcexpr)
 {
     ast_instantiate(ast_call, ctx, ast_call_delete);
@@ -1069,7 +1133,7 @@ bool ast_call_check_types(ast_call *self, ast_expression *va_type)
     return retval;
 }
 
-ast_store* ast_store_new(lex_ctx ctx, int op,
+ast_store* ast_store_new(lex_ctx_t ctx, int op,
                          ast_expression *dest, ast_expression *source)
 {
     ast_instantiate(ast_store, ctx, ast_store_delete);
@@ -1094,7 +1158,7 @@ void ast_store_delete(ast_store *self)
     mem_d(self);
 }
 
-ast_block* ast_block_new(lex_ctx ctx)
+ast_block* ast_block_new(lex_ctx_t ctx)
 {
     ast_instantiate(ast_block, ctx, ast_block_delete);
     ast_expression_init((ast_expression*)self,
@@ -1148,7 +1212,7 @@ void ast_block_set_type(ast_block *self, ast_expression *from)
     ast_type_adopt(self, from);
 }
 
-ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype)
+ast_function* ast_function_new(lex_ctx_t ctx, const char *name, ast_value *vtype)
 {
     ast_instantiate(ast_function, ctx, ast_function_delete);
 
@@ -1184,8 +1248,11 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype)
     self->fixedparams      = NULL;
     self->return_value     = NULL;
 
+    self->static_names     = NULL;
+    self->static_count     = 0;
+
     return self;
-    
+
 cleanup:
     mem_d(self);
     return NULL;
@@ -1205,6 +1272,9 @@ void ast_function_delete(ast_function *self)
          */
         ast_unref(self->vtype);
     }
+    for (i = 0; i < vec_size(self->static_names); ++i)
+        mem_d(self->static_names[i]);
+    vec_free(self->static_names);
     for (i = 0; i < vec_size(self->blocks); ++i)
         ast_delete(self->blocks[i]);
     vec_free(self->blocks);
@@ -1221,7 +1291,7 @@ void ast_function_delete(ast_function *self)
     mem_d(self);
 }
 
-static const char* ast_function_label(ast_function *self, const char *prefix)
+const char* ast_function_label(ast_function *self, const char *prefix)
 {
     size_t id;
     size_t len;
@@ -1347,6 +1417,20 @@ static bool ast_global_array_set(ast_value *self)
     return true;
 }
 
+static bool check_array(ast_value *self, ast_value *array)
+{
+    if (array->expression.flags & AST_FLAG_ARRAY_INIT && !array->initlist) {
+        compile_error(ast_ctx(self), "array without size: %s", self->name);
+        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_OPTION_U32(OPTION_MAX_ARRAY_SIZE)) {
+        compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count);
+        return false;
+    }
+    return true;
+}
+
 bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 {
     ir_value *v = NULL;
@@ -1368,6 +1452,10 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
         self->ir_v = func->value;
         if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
             self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
+        if (self->expression.flags & AST_FLAG_ERASEABLE)
+            self->ir_v->flags |= IR_FLAG_ERASEABLE;
+        if (self->expression.flags & AST_FLAG_BLOCK_COVERAGE)
+            func->flags |= IR_FLAG_BLOCK_COVERAGE;
         /* The function is filled later on ast_function_codegen... */
         return true;
     }
@@ -1394,9 +1482,8 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
                 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_OPTION_U32(OPTION_MAX_ARRAY_SIZE))
-                compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count);
+            if (!check_array(self, array))
+                return false;
 
             elemtype = array->expression.next;
             vtype = elemtype->vtype;
@@ -1410,8 +1497,11 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
             v->unique_life = true;
             v->locked      = true;
             array->ir_v = self->ir_v = v;
+
             if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
                 self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
+            if (self->expression.flags & AST_FLAG_ERASEABLE)
+                self->ir_v->flags |= IR_FLAG_ERASEABLE;
 
             namelen = strlen(self->name);
             name    = (char*)mem_a(namelen + 16);
@@ -1444,6 +1534,9 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
             self->ir_v = v;
             if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
                 self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
+
+            if (self->expression.flags & AST_FLAG_ERASEABLE)
+                self->ir_v->flags |= IR_FLAG_ERASEABLE;
         }
         return true;
     }
@@ -1462,8 +1555,8 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
         }
 
         /* same as with field arrays */
-        if (!self->expression.count || self->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE))
-            compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
+        if (!check_array(self, self))
+            return false;
 
         v = ir_builder_create_global(ir, self->name, vtype);
         if (!v) {
@@ -1473,8 +1566,11 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
         v->context = ast_ctx(self);
         v->unique_life = true;
         v->locked      = true;
+
         if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
             v->flags |= IR_FLAG_INCLUDE_DEF;
+        if (self->expression.flags & AST_FLAG_ERASEABLE)
+            self->ir_v->flags |= IR_FLAG_ERASEABLE;
 
         namelen = strlen(self->name);
         name    = (char*)mem_a(namelen + 16);
@@ -1515,8 +1611,11 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
     /* link us to the ir_value */
     v->cvq = self->cvq;
     self->ir_v = v;
+
     if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
         self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
+    if (self->expression.flags & AST_FLAG_ERASEABLE)
+        self->ir_v->flags |= IR_FLAG_ERASEABLE;
 
     /* initialize */
     if (self->hasvalue) {
@@ -1600,9 +1699,8 @@ static bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
         }
 
         /* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
-        if (!self->expression.count || self->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE)) {
-            compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
-        }
+        if (!check_array(self, self))
+            return false;
 
         self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count);
         if (!self->ir_values) {
@@ -1738,6 +1836,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
     ir_value    *dummy;
     ast_expression         *ec;
     ast_expression_codegen *cgen;
+
     size_t    i;
 
     (void)ir;
@@ -1848,6 +1947,17 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
     return true;
 }
 
+static bool starts_a_label(ast_expression *ex)
+{
+    while (ex && ast_istype(ex, ast_block)) {
+        ast_block *b = (ast_block*)ex;
+        ex = b->exprs[0];
+    }
+    if (!ex)
+        return false;
+    return ast_istype(ex, ast_label);
+}
+
 /* Note, you will not see ast_block_codegen generate ir_blocks.
  * To the AST and the IR, blocks are 2 different things.
  * In the AST it represents a block of code, usually enclosed in
@@ -1893,7 +2003,7 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
     for (i = 0; i < vec_size(self->exprs); ++i)
     {
         ast_expression_codegen *gen;
-        if (func->curblock->final && !ast_istype(self->exprs[i], ast_label)) {
+        if (func->curblock->final && !starts_a_label(self->exprs[i])) {
             if (compile_warning(ast_ctx(self->exprs[i]), WARN_UNREACHABLE_CODE, "unreachable statement"))
                 return false;
             continue;
@@ -2024,6 +2134,8 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
     if ((OPTS_FLAG(SHORT_LOGIC) || OPTS_FLAG(PERL_LOGIC)) &&
         (self->op == INSTR_AND || self->op == INSTR_OR))
     {
+        /* NOTE: The short-logic path will ignore right_first */
+
         /* short circuit evaluation */
         ir_block *other, *merge;
         ir_block *from_left, *from_right;
@@ -2121,13 +2233,21 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
         return true;
     }
 
-    cgen = self->left->codegen;
-    if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
-        return false;
-
-    cgen = self->right->codegen;
-    if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
-        return false;
+    if (self->right_first) {
+        cgen = self->right->codegen;
+        if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
+            return false;
+        cgen = self->left->codegen;
+        if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
+            return false;
+    } else {
+        cgen = self->left->codegen;
+        if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
+            return false;
+        cgen = self->right->codegen;
+        if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
+            return false;
+    }
 
     *out = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "bin"),
                                  self->op, left, right);
@@ -2513,6 +2633,7 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
     ir_block *ontrue_endblock = NULL;
     ir_block *onfalse_endblock = NULL;
     ir_block *merge = NULL;
+    int       fold  = 0;
 
     /* We don't output any value, thus also don't care about r/lvalue */
     (void)out;
@@ -2531,7 +2652,9 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
     /* update the block which will get the jump - because short-logic or ternaries may have changed this */
     cond = func->curblock;
 
-    /* on-true path */
+    /* try constant folding away the condition */
+    if ((fold = fold_cond_ifthen(condval, func, self)) != -1)
+        return fold;
 
     if (self->on_true) {
         /* create on-true block */
@@ -2613,6 +2736,7 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
     ir_block *ontrue, *ontrue_out = NULL;
     ir_block *onfalse, *onfalse_out = NULL;
     ir_block *merge;
+    int       fold  = 0;
 
     /* Ternary can never create an lvalue... */
     if (lvalue)
@@ -2637,6 +2761,10 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
         return false;
     cond_out = func->curblock;
 
+    /* try constant folding away the condition */
+    if ((fold = fold_cond_ternary(condval, func, self)) != -1)
+        return fold;
+
     /* create on-true block */
     ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_T"));
     if (!ontrue)
@@ -2884,12 +3012,12 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
     /* From 'bin' we jump to whatever comes first */
     if      (bprecond)   tmpblock = bprecond;
     else                 tmpblock = bbody;    /* can never be null */
-    
+
     /* DEAD CODE
     else if (bpostcond)  tmpblock = bpostcond;
     else                 tmpblock = bout;
     */
-    
+
     if (!ir_block_create_jump(bin, ast_ctx(self), tmpblock))
         return false;
 
@@ -2899,7 +3027,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
         ir_block *ontrue, *onfalse;
         ontrue = bbody; /* can never be null */
 
-        /* all of this is dead code 
+        /* all of this is dead code
         else if (bincrement) ontrue = bincrement;
         else                 ontrue = bpostcond;
         */
@@ -2943,7 +3071,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
         if      (bprecond)   ontrue = bprecond;
         else                 ontrue = bbody; /* can never be null */
 
-        /* all of this is dead code 
+        /* all of this is dead code
         else if (bincrement) ontrue = bincrement;
         else                 ontrue = bpostcond;
         */
@@ -3241,6 +3369,44 @@ bool ast_goto_codegen(ast_goto *self, ast_function *func, bool lvalue, ir_value
     return true;
 }
 
+#include <stdio.h>
+bool ast_state_codegen(ast_state *self, ast_function *func, bool lvalue, ir_value **out)
+{
+    ast_expression_codegen *cgen;
+
+    ir_value *frameval, *thinkval;
+
+    if (lvalue) {
+        compile_error(ast_ctx(self), "not an l-value (state operation)");
+        return false;
+    }
+    if (self->expression.outr) {
+        compile_error(ast_ctx(self), "internal error: ast_state cannot be reused!");
+        return false;
+    }
+    *out = NULL;
+
+    cgen = self->framenum->codegen;
+    if (!(*cgen)((ast_expression*)(self->framenum), func, false, &frameval))
+        return false;
+    if (!frameval)
+        return false;
+
+    cgen = self->nextthink->codegen;
+    if (!(*cgen)((ast_expression*)(self->nextthink), func, false, &thinkval))
+        return false;
+    if (!frameval)
+        return false;
+
+    if (!ir_block_create_state_op(func->curblock, ast_ctx(self), frameval, thinkval)) {
+        compile_error(ast_ctx(self), "failed to create STATE instruction");
+        return false;
+    }
+
+    self->expression.outr = (ir_value*)1;
+    return true;
+}
+
 bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out)
 {
     ast_expression_codegen *cgen;