]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ast.c
removed phi_out from ast_ternary since we have a place in ast_expression_common for...
[xonotic/gmqcc.git] / ast.c
diff --git a/ast.c b/ast.c
index 8083b36e0ca6e8f5783d489e8e2b2d7073108174..b1ceb1c6b990414800bf6d0619af358e17b01b3f 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -651,7 +651,6 @@ ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression *
     self->cond     = cond;
     self->on_true  = ontrue;
     self->on_false = onfalse;
-    self->phi_out  = NULL;
 
     return self;
 }
@@ -1513,13 +1512,105 @@ 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;
+        uint16_t  notop;
+
+        /* Note about casting to true boolean values:
+         * We use a single NOT for sub expressions, and an
+         * overall NOT at the end, and for that purpose swap
+         * all the jump conditions in order for the NOT to get
+         * doubled.
+         * ie: (a && b) usually becomes (!!a ? !!b : !!a)
+         * but we translate this to (!(!a ? !a : !b))
+         */
+
+        merge_id = vec_size(func->ir_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;
+        if (!OPTS_FLAG(PERL_LOGIC)) {
+            notop = type_not_instr[left->vtype];
+            if (notop == AINSTR_END) {
+                asterror(ast_ctx(self), "don't know how to cast to bool...");
+                return false;
+            }
+            left = ir_block_create_unary(func->curblock,
+                                         ast_function_label(func, "sce_not"),
+                                         notop,
+                                         left);
+        }
+        from_left = func->curblock;
+
+        other = ir_function_create_block(func->ir_func, ast_function_label(func, "sce_other"));
+        if ( !(self->op == INSTR_OR) != !OPTS_FLAG(PERL_LOGIC) ) {
+            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 likely flag */
+        vec_last(func->curblock->instr)->likely = true;
+
+        func->curblock = other;
+        cgen = self->right->expression.codegen;
+        if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
+            return false;
+        if (!OPTS_FLAG(PERL_LOGIC)) {
+            notop = type_not_instr[right->vtype];
+            if (notop == AINSTR_END) {
+                asterror(ast_ctx(self), "don't know how to cast to bool...");
+                return false;
+            }
+            right = ir_block_create_unary(func->curblock,
+                                          ast_function_label(func, "sce_not"),
+                                          notop,
+                                          right);
+        }
+        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);
+        if (!OPTS_FLAG(PERL_LOGIC)) {
+            notop = type_not_instr[(*out)->vtype];
+            if (notop == AINSTR_END) {
+                asterror(ast_ctx(self), "don't know how to cast to bool...");
+                return false;
+            }
+            *out = ir_block_create_unary(func->curblock,
+                                         ast_function_label(func, "sce_final_not"),
+                                         notop,
+                                         *out);
+        }
+        if (!*out)
+            return false;
+        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;
 
@@ -1920,8 +2011,8 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
      * may still happen, thus we remember a created ir_value and simply return one
      * if it already exists.
      */
-    if (self->phi_out) {
-        *out = self->phi_out;
+    if (self->expression.outr) {
+        *out = self->expression.outr;
         return true;
     }
 
@@ -1995,8 +2086,8 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
     ir_phi_add(phi, ontrue,  trueval);
     ir_phi_add(phi, onfalse, falseval);
 
-    self->phi_out = ir_phi_value(phi);
-    *out = self->phi_out;
+    self->expression.outr = ir_phi_value(phi);
+    *out = self->expression.outr;
 
     return true;
 }
@@ -2331,8 +2422,8 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
                 return false;
 
             bcase = ir_function_create_block(func->ir_func, ast_function_label(func, "case"));
-            bnot = ir_function_create_block(func->ir_func, ast_function_label(func, "not_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))
@@ -2367,6 +2458,14 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
         }
     }
 
+    /* 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;
@@ -2389,6 +2488,8 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
     /* 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;