]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ast.c
Removing unused _tokennames from lexer.h
[xonotic/gmqcc.git] / ast.c
diff --git a/ast.c b/ast.c
index dfed367c29544371136a971491719664d9b3d1aa..069151283bbde3ff0cc86058954a8cd1a0d10822 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -651,7 +651,11 @@ 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;
+
+    if (!ast_type_adopt(self, ontrue)) {
+        ast_ternary_delete(self);
+        return NULL;
+    }
 
     return self;
 }
@@ -700,6 +704,49 @@ 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)
 {
@@ -741,8 +788,12 @@ bool ast_call_check_types(ast_call *self)
 
     for (i = 0; i < count; ++i) {
         if (!ast_compare_type(self->params[i], (ast_expression*)(func->expression.params[i]))) {
-            asterror(ast_ctx(self), "invalid type for parameter %u in function call",
-                     (unsigned int)(i+1));
+            char texp[1024];
+            char tgot[1024];
+            ast_type_to_string(self->params[i], tgot, sizeof(tgot));
+            ast_type_to_string((ast_expression*)func->expression.params[i], texp, sizeof(texp));
+            asterror(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s",
+                     (unsigned int)(i+1), texp, tgot);
             /* we don't immediately return */
             retval = false;
         }
@@ -809,10 +860,10 @@ void ast_block_delete(ast_block *self)
     vec_free(self->exprs);
     for (i = 0; i < vec_size(self->locals); ++i)
         ast_delete(self->locals[i]);
-    vec_free(self->exprs);
+    vec_free(self->locals);
     for (i = 0; i < vec_size(self->collect); ++i)
         ast_delete(self->collect[i]);
-    vec_free(self->exprs);
+    vec_free(self->collect);
     ast_expression_delete((ast_expression*)self);
     mem_d(self);
 }
@@ -889,7 +940,7 @@ const char* ast_function_label(ast_function *self, const char *prefix)
     size_t len;
     char  *from;
 
-    if (!opts_dump)
+    if (!opts_dump && !opts_dumpfin)
         return NULL;
 
     id  = (self->labelcount++);
@@ -1345,6 +1396,10 @@ 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 = 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;
     }
@@ -1466,13 +1521,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;
 
@@ -1779,10 +1926,11 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
     self->expression.outr = (ir_value*)1;
 
     /* generate the condition */
-    func->curblock = cond;
     cgen = self->cond->expression.codegen;
     if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval))
         return false;
+    /* update the block which will get the jump - because short-logic or ternaries may have changed this */
+    cond = func->curblock;
 
     /* on-true path */
 
@@ -1829,7 +1977,6 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
     merge = ir_function_create_block(func->ir_func, ast_function_label(func, "endif"));
     if (!merge)
         return false;
-
     /* add jumps ot the merge block */
     if (ontrue && !ontrue_endblock->final && !ir_block_create_jump(ontrue_endblock, merge))
         return false;
@@ -1873,8 +2020,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;
     }
 
@@ -1948,8 +2095,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;
 }
@@ -2184,6 +2331,185 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
     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;