]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Parsing of suffix operators, NOTE: applied like prefix operators just to get it commi...
[xonotic/gmqcc.git] / parser.c
index c4cdf2f89f88114af5c31d89ec3261e97bb0f475..3c5d237e17500ed86acd2e66ad80f937e55e552b 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -51,6 +51,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 static ast_block* parse_block(parser_t *parser, bool warnreturn);
 static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn);
 static ast_expression* parse_statement_or_block(parser_t *parser);
+static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases);
 static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma);
 static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
 
@@ -166,7 +167,8 @@ static ast_value* parser_const_float(parser_t *parser, double d)
     size_t i;
     ast_value *out;
     for (i = 0; i < vec_size(parser->imm_float); ++i) {
-        if (parser->imm_float[i]->constval.vfloat == d)
+        const double compare = parser->imm_float[i]->constval.vfloat;
+        if (memcmp((const void*)&compare, (const void *)&d, sizeof(double)) == 0)
             return parser->imm_float[i];
     }
     out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_FLOAT);
@@ -411,7 +413,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
     ast_expression *exprs[3];
     ast_block      *blocks[3];
     ast_value      *asvalue[3];
-    size_t i, assignop;
+    size_t i, assignop, addop;
     qcint  generated_op = 0;
 
     char ty1[1024];
@@ -422,13 +424,13 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
         return false;
     }
 
-    if (sy->ops[vec_size(sy->ops)-1].paren) {
+    if (vec_last(sy->ops).paren) {
         parseerror(parser, "unmatched parenthesis");
         return false;
     }
 
-    op = &operators[sy->ops[vec_size(sy->ops)-1].etype - 1];
-    ctx = sy->ops[vec_size(sy->ops)-1].ctx;
+    op = &operators[vec_last(sy->ops).etype - 1];
+    ctx = vec_last(sy->ops).ctx;
 
     DEBUGSHUNTDO(con_out("apply %s\n", op->op));
 
@@ -764,6 +766,19 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                 out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
             break;
 
+        case opid2('?',':'):
+            if (exprs[1]->expression.vtype != exprs[2]->expression.vtype) {
+                ast_type_to_string(exprs[1], ty1, sizeof(ty1));
+                ast_type_to_string(exprs[2], ty2, sizeof(ty2));
+                parseerror(parser, "iperands of ternary expression must have the same type, got %s and %s", ty1, ty2);
+                return false;
+            }
+            if (CanConstFold1(exprs[0]))
+                out = (ConstF(0) ? exprs[1] : exprs[2]);
+            else
+                out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
+            break;
+
         case opid1('>'):
             generated_op += 1; /* INSTR_GT */
         case opid1('<'):
@@ -863,14 +878,59 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
             }
             out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]);
             break;
+        case opid3('+','+','P'):
+        case opid3('-','-','P'):
+            /* prefix ++ */
+            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                parseerror(parser, "invalid type for prefix increment: %s", ty1);
+                return false;
+            }
+            if (op->id == opid3('+','+','P'))
+                addop = INSTR_ADD_F;
+            else
+                addop = INSTR_SUB_F;
+            if (ast_istype(exprs[0], ast_entfield)) {
+                out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
+                                                        exprs[0],
+                                                        (ast_expression*)parser_const_float(parser, 1));
+            } else {
+                out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop,
+                                                        exprs[0],
+                                                        (ast_expression*)parser_const_float(parser, 1));
+            }
+            break;
+        case opid3('S','+','+'):
+        case opid3('S','-','-'):
+            /* prefix ++ */
+            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                parseerror(parser, "invalid type for prefix increment: %s", ty1);
+                return false;
+            }
+            if (op->id == opid3('+','+','P'))
+                addop = INSTR_ADD_F;
+            else
+                addop = INSTR_SUB_F;
+            if (ast_istype(exprs[0], ast_entfield)) {
+                out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
+                                                        exprs[0],
+                                                        (ast_expression*)parser_const_float(parser, 1));
+            } else {
+                out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop,
+                                                        exprs[0],
+                                                        (ast_expression*)parser_const_float(parser, 1));
+            }
+            break;
         case opid2('+','='):
         case opid2('-','='):
             if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
                 (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
             {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                 parseerror(parser, "invalid types used in expression: cannot add or subtract type %s and %s",
-                           type_name[exprs[0]->expression.vtype],
-                           type_name[exprs[1]->expression.vtype]);
+                           ty1, ty2);
                 return false;
             }
             if (ast_istype(exprs[0], ast_entfield))
@@ -1079,6 +1139,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
      * end of a condition is an unmatched closing paren
      */
     int parens = 0;
+    int ternaries = 0;
 
     sy.out = NULL;
     sy.ops = NULL;
@@ -1240,13 +1301,12 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
         else
         {
             /* classify the operator */
-            /* TODO: suffix operators */
             const oper_info *op;
             const oper_info *olast = NULL;
             size_t o;
             for (o = 0; o < operator_count; ++o) {
                 if ((!(operators[o].flags & OP_PREFIX) == wantop) &&
-                    !(operators[o].flags & OP_SUFFIX) && /* remove this */
+                    /* !(operators[o].flags & OP_SUFFIX) && / * remove this */
                     !strcmp(parser_tokval(parser), operators[o].op))
                 {
                     break;
@@ -1266,6 +1326,12 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 break;
             }
 
+            /* a colon without a pervious question mark cannot be a ternary */
+            if (!ternaries && op->id == opid2(':','?')) {
+                parser->tok = ':';
+                break;
+            }
+
             if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
                 olast = &operators[vec_last(sy.ops).etype-1];
 
@@ -1322,16 +1388,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
                 vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0));
                 wantop = false;
+            } else if (op->id == opid2('?',':')) {
+                wantop = false;
+                vec_push(sy.ops, syop(parser_ctx(parser), op));
+                wantop = false;
+                --ternaries;
+            } else if (op->id == opid2(':','?')) {
+                /* we don't push this operator */
+                wantop = false;
+                ++ternaries;
             } else {
                 DEBUGSHUNTDO(con_out("push operator %s\n", op->op));
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
-                wantop = false;
+                wantop = !!(op->flags & OP_SUFFIX);
             }
         }
         if (!parser_next(parser)) {
             goto onerr;
         }
-        if (parser->tok == ';' || (!parens && parser->tok == ']')) {
+        if (parser->tok == ';' ||
+            (!parens && parser->tok == ']'))
+        {
             break;
         }
     }
@@ -1379,6 +1456,8 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
 
     lex_ctx ctx = parser_ctx(parser);
 
+    (void)block; /* not touching */
+
     /* skip the 'if', parse an optional 'not' and check for an opening paren */
     if (!parser_next(parser)) {
         parseerror(parser, "expected condition or 'not'");
@@ -1453,6 +1532,8 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
 
     lex_ctx ctx = parser_ctx(parser);
 
+    (void)block; /* not touching */
+
     /* skip the 'while' and check for opening paren */
     if (!parser_next(parser) || parser->tok != '(') {
         parseerror(parser, "expected 'while' condition in parenthesis");
@@ -1497,6 +1578,8 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
 
     lex_ctx ctx = parser_ctx(parser);
 
+    (void)block; /* not touching */
+
     /* skip the 'do' and get the body */
     if (!parser_next(parser)) {
         parseerror(parser, "expected loop body");
@@ -1678,7 +1761,214 @@ onerr:
     return false;
 }
 
-static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out)
+static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    ast_expression *exp = NULL;
+    ast_return     *ret = NULL;
+    ast_value      *expected = parser->function->vtype;
+
+    (void)block; /* not touching */
+
+    if (!parser_next(parser)) {
+        parseerror(parser, "expected return expression");
+        return false;
+    }
+
+    if (parser->tok != ';') {
+        exp = parse_expression(parser, false);
+        if (!exp)
+            return false;
+
+        if (exp->expression.vtype != expected->expression.next->expression.vtype) {
+            parseerror(parser, "return with invalid expression");
+        }
+
+        ret = ast_return_new(exp->expression.node.context, exp);
+        if (!ret) {
+            ast_delete(exp);
+            return false;
+        }
+    } else {
+        if (!parser_next(parser))
+            parseerror(parser, "parse error");
+        if (expected->expression.next->expression.vtype != TYPE_VOID) {
+            if (opts_standard != COMPILER_GMQCC)
+                (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
+            else
+                parseerror(parser, "return without value");
+        }
+        ret = ast_return_new(parser_ctx(parser), NULL);
+    }
+    *out = (ast_expression*)ret;
+    return true;
+}
+
+static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue)
+{
+    lex_ctx ctx = parser_ctx(parser);
+
+    (void)block; /* not touching */
+
+    if (!parser_next(parser) || parser->tok != ';') {
+        parseerror(parser, "expected semicolon");
+        return false;
+    }
+
+    if (!parser_next(parser))
+        parseerror(parser, "parse error");
+
+    *out = (ast_expression*)ast_breakcont_new(ctx, is_continue);
+    return true;
+}
+
+static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    ast_expression *operand;
+    ast_value      *opval;
+    ast_switch     *switchnode;
+    ast_switch_case swcase;
+
+    lex_ctx ctx = parser_ctx(parser);
+
+    (void)block; /* not touching */
+
+    /* parse over the opening paren */
+    if (!parser_next(parser) || parser->tok != '(') {
+        parseerror(parser, "expected switch operand in parenthesis");
+        return false;
+    }
+
+    /* parse into the expression */
+    if (!parser_next(parser)) {
+        parseerror(parser, "expected switch operand");
+        return false;
+    }
+    /* parse the operand */
+    operand = parse_expression_leave(parser, false);
+    if (!operand)
+        return false;
+
+    if (!OPTS_FLAG(RELAXED_SWITCH)) {
+        opval = (ast_value*)operand;
+        if (!ast_istype(operand, ast_value) || !opval->isconst) {
+            parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch");
+            ast_unref(operand);
+            return false;
+        }
+    }
+
+    switchnode = ast_switch_new(ctx, operand);
+
+    /* closing paren */
+    if (parser->tok != ')') {
+        ast_delete(switchnode);
+        parseerror(parser, "expected closing paren after 'switch' operand");
+        return false;
+    }
+
+    /* parse over the opening paren */
+    if (!parser_next(parser) || parser->tok != '{') {
+        ast_delete(switchnode);
+        parseerror(parser, "expected list of cases");
+        return false;
+    }
+
+    if (!parser_next(parser)) {
+        ast_delete(switchnode);
+        parseerror(parser, "expected 'case' or 'default'");
+        return false;
+    }
+
+    /* case list! */
+    while (parser->tok != '}') {
+        ast_block *caseblock;
+
+        if (parser->tok != TOKEN_KEYWORD) {
+            ast_delete(switchnode);
+            parseerror(parser, "expected 'case' or 'default'");
+            return false;
+        }
+        if (!strcmp(parser_tokval(parser), "case")) {
+            if (!parser_next(parser)) {
+                ast_delete(switchnode);
+                parseerror(parser, "expected expression for case");
+                return false;
+            }
+            swcase.value = parse_expression_leave(parser, false);
+            if (!swcase.value) {
+                ast_delete(switchnode);
+                parseerror(parser, "expected expression for case");
+                return false;
+            }
+        }
+        else if (!strcmp(parser_tokval(parser), "default")) {
+            swcase.value = NULL;
+            if (!parser_next(parser)) {
+                ast_delete(switchnode);
+                parseerror(parser, "expected colon");
+                return false;
+            }
+        }
+
+        /* Now the colon and body */
+        if (parser->tok != ':') {
+            if (swcase.value) ast_unref(swcase.value);
+            ast_delete(switchnode);
+            parseerror(parser, "expected colon");
+            return false;
+        }
+
+        if (!parser_next(parser)) {
+            if (swcase.value) ast_unref(swcase.value);
+            ast_delete(switchnode);
+            parseerror(parser, "expected statements or case");
+            return false;
+        }
+        caseblock = ast_block_new(parser_ctx(parser));
+        if (!caseblock) {
+            if (swcase.value) ast_unref(swcase.value);
+            ast_delete(switchnode);
+            return false;
+        }
+        swcase.code = (ast_expression*)caseblock;
+        vec_push(switchnode->cases, swcase);
+        while (true) {
+            ast_expression *expr;
+            if (parser->tok == '}')
+                break;
+            if (parser->tok == TOKEN_KEYWORD) {
+                if (!strcmp(parser_tokval(parser), "case") ||
+                    !strcmp(parser_tokval(parser), "default"))
+                {
+                    break;
+                }
+            }
+            if (!parse_statement(parser, caseblock, &expr, true)) {
+                ast_delete(switchnode);
+                return false;
+            }
+            if (!expr)
+                continue;
+            vec_push(caseblock->exprs, expr);
+        }
+    }
+
+    /* closing paren */
+    if (parser->tok != '}') {
+        ast_delete(switchnode);
+        parseerror(parser, "expected closing paren of case list");
+        return false;
+    }
+    if (!parser_next(parser)) {
+        ast_delete(switchnode);
+        parseerror(parser, "parse error after switch");
+        return false;
+    }
+    *out = (ast_expression*)switchnode;
+    return true;
+}
+
+static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
 {
     if (parser->tok == TOKEN_TYPENAME || parser->tok == '.')
     {
@@ -1715,42 +2005,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
         }
         else if (!strcmp(parser_tokval(parser), "return"))
         {
-            ast_expression *exp = NULL;
-            ast_return     *ret = NULL;
-            ast_value      *expected = parser->function->vtype;
-
-            if (!parser_next(parser)) {
-                parseerror(parser, "expected return expression");
-                return false;
-            }
-
-            if (parser->tok != ';') {
-                exp = parse_expression(parser, false);
-                if (!exp)
-                    return false;
-
-                if (exp->expression.vtype != expected->expression.next->expression.vtype) {
-                    parseerror(parser, "return with invalid expression");
-                }
-
-                ret = ast_return_new(exp->expression.node.context, exp);
-                if (!ret) {
-                    ast_delete(exp);
-                    return false;
-                }
-            } else {
-                if (!parser_next(parser))
-                    parseerror(parser, "parse error");
-                if (expected->expression.next->expression.vtype != TYPE_VOID) {
-                    if (opts_standard != COMPILER_GMQCC)
-                        (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
-                    else
-                        parseerror(parser, "return without value");
-                }
-                ret = ast_return_new(parser_ctx(parser), NULL);
-            }
-            *out = (ast_expression*)ret;
-            return true;
+            return parse_return(parser, block, out);
         }
         else if (!strcmp(parser_tokval(parser), "if"))
         {
@@ -1772,6 +2027,27 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             }
             return parse_for(parser, block, out);
         }
+        else if (!strcmp(parser_tokval(parser), "break"))
+        {
+            return parse_break_continue(parser, block, out, false);
+        }
+        else if (!strcmp(parser_tokval(parser), "continue"))
+        {
+            return parse_break_continue(parser, block, out, true);
+        }
+        else if (!strcmp(parser_tokval(parser), "switch"))
+        {
+            return parse_switch(parser, block, out);
+        }
+        else if (!strcmp(parser_tokval(parser), "case") ||
+                 !strcmp(parser_tokval(parser), "default"))
+        {
+            if (!allow_cases) {
+                parseerror(parser, "unexpected 'case' label");
+                return false;
+            }
+            return true;
+        }
         parseerror(parser, "Unexpected keyword");
         return false;
     }
@@ -1807,9 +2083,11 @@ static bool GMQCC_WARN parser_pop_local(parser_t *parser)
     varentry_t *ve;
 
     ve = &vec_last(parser->locals);
-    if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) {
-        if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name))
-            rv = false;
+    if (!parser->errors) {
+        if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) {
+            if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name))
+                rv = false;
+        }
     }
     mem_d(ve->name);
     vec_pop(parser->locals);
@@ -1835,7 +2113,7 @@ static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn
         if (parser->tok == '}')
             break;
 
-        if (!parse_statement(parser, block, &expr)) {
+        if (!parse_statement(parser, block, &expr, false)) {
             /* parseerror(parser, "parse error"); */
             block = NULL;
             goto cleanup;
@@ -1887,13 +2165,13 @@ static ast_expression* parse_statement_or_block(parser_t *parser)
     ast_expression *expr = NULL;
     if (parser->tok == '{')
         return (ast_expression*)parse_block(parser, false);
-    if (!parse_statement(parser, NULL, &expr))
+    if (!parse_statement(parser, NULL, &expr, false))
         return NULL;
     return expr;
 }
 
 /* loop method */
-static bool create_vector_members(parser_t *parser, ast_value *var, varentry_t *ve)
+static bool create_vector_members(ast_value *var, varentry_t *ve)
 {
     size_t i;
     size_t len = strlen(var->name);
@@ -2155,7 +2433,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             continue;
         }
 
-        if (!create_vector_members(parser, param, ve)) {
+        if (!create_vector_members(param, ve)) {
             ast_block_delete(block);
             return false;
         }
@@ -2252,10 +2530,11 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast
     lex_ctx ctx = ast_ctx(array);
 
     if (from+1 == afterend) {
-        // set this value
+        /* set this value */
         ast_block       *block;
         ast_return      *ret;
         ast_array_index *subscript;
+        ast_store       *st;
         int assignop = type_store_instr[value->expression.vtype];
 
         if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
@@ -2265,7 +2544,7 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast
         if (!subscript)
             return NULL;
 
-        ast_store *st = ast_store_new(ctx, assignop, (ast_expression*)subscript, (ast_expression*)value);
+        st = ast_store_new(ctx, assignop, (ast_expression*)subscript, (ast_expression*)value);
         if (!st) {
             ast_delete(subscript);
             return NULL;
@@ -2310,11 +2589,12 @@ static ast_expression *array_field_setter_node(
     lex_ctx ctx = ast_ctx(array);
 
     if (from+1 == afterend) {
-        // set this value
+        /* set this value */
         ast_block       *block;
         ast_return      *ret;
         ast_entfield    *entfield;
         ast_array_index *subscript;
+        ast_store       *st;
         int assignop = type_storep_instr[value->expression.vtype];
 
         if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
@@ -2333,7 +2613,7 @@ static ast_expression *array_field_setter_node(
             return NULL;
         }
 
-        ast_store *st = ast_store_new(ctx, assignop, (ast_expression*)entfield, (ast_expression*)value);
+        st = ast_store_new(ctx, assignop, (ast_expression*)entfield, (ast_expression*)value);
         if (!st) {
             ast_delete(entfield);
             return NULL;
@@ -2637,8 +2917,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
     }
 
     /* sanity check */
-    if (vec_size(params) > 8)
-        parseerror(parser, "more than 8 parameters are currently not supported");
+    if (vec_size(params) > 8 && opts_standard == COMPILER_QCC)
+        (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard");
 
     /* parse-out */
     if (!parser_next(parser)) {
@@ -3057,7 +3337,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 isvector = true;
 
             if (isvector) {
-                if (!create_vector_members(parser, var, ve)) {
+                if (!create_vector_members(var, ve)) {
                     retval = false;
                     goto cleanup;
                 }
@@ -3358,6 +3638,79 @@ static bool parser_global_statement(parser_t *parser)
     return true;
 }
 
+static uint16_t progdefs_crc_sum(uint16_t old, const char *str)
+{
+    return util_crc16(old, str, strlen(str));
+}
+
+static void progdefs_crc_file(const char *str)
+{
+    /* write to progdefs.h here */
+    (void)str;
+}
+
+static uint16_t progdefs_crc_both(uint16_t old, const char *str)
+{
+    old = progdefs_crc_sum(old, str);
+    progdefs_crc_file(str);
+    return old;
+}
+
+static void generate_checksum(parser_t *parser)
+{
+    uint16_t crc = 0xFFFF;
+    size_t i;
+
+       crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{");
+       crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n");
+       /*
+       progdefs_crc_file("\tint\tpad;\n");
+       progdefs_crc_file("\tint\tofs_return[3];\n");
+       progdefs_crc_file("\tint\tofs_parm0[3];\n");
+       progdefs_crc_file("\tint\tofs_parm1[3];\n");
+       progdefs_crc_file("\tint\tofs_parm2[3];\n");
+       progdefs_crc_file("\tint\tofs_parm3[3];\n");
+       progdefs_crc_file("\tint\tofs_parm4[3];\n");
+       progdefs_crc_file("\tint\tofs_parm5[3];\n");
+       progdefs_crc_file("\tint\tofs_parm6[3];\n");
+       progdefs_crc_file("\tint\tofs_parm7[3];\n");
+       */
+       for (i = 0; i < parser->crc_globals; ++i) {
+           if (!ast_istype(parser->globals[i].var, ast_value))
+               continue;
+           switch (parser->globals[i].var->expression.vtype) {
+               case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
+               case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
+               case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
+               case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
+               default:
+                   crc = progdefs_crc_both(crc, "\tint\t");
+                   break;
+           }
+           crc = progdefs_crc_both(crc, parser->globals[i].name);
+           crc = progdefs_crc_both(crc, ";\n");
+       }
+       crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n");
+       for (i = 0; i < parser->crc_fields; ++i) {
+           if (!ast_istype(parser->fields[i].var, ast_value))
+               continue;
+           switch (parser->fields[i].var->expression.next->expression.vtype) {
+               case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
+               case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
+               case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
+               case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
+               default:
+                   crc = progdefs_crc_both(crc, "\tint\t");
+                   break;
+           }
+           crc = progdefs_crc_both(crc, parser->fields[i].name);
+           crc = progdefs_crc_both(crc, ";\n");
+       }
+       crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
+
+       code_crc = crc;
+}
+
 static parser_t *parser;
 
 bool parser_init()
@@ -3485,78 +3838,6 @@ void parser_cleanup()
     mem_d(parser);
 }
 
-static uint16_t progdefs_crc_sum(uint16_t old, const char *str)
-{
-    return util_crc16(old, str, strlen(str));
-}
-
-static void progdefs_crc_file(const char *str)
-{
-    /* write to progdefs.h here */
-}
-
-static uint16_t progdefs_crc_both(uint16_t old, const char *str)
-{
-    old = progdefs_crc_sum(old, str);
-    progdefs_crc_file(str);
-    return old;
-}
-
-static void generate_checksum(parser_t *parser)
-{
-    uint16_t crc = 0xFFFF;
-    size_t i;
-
-       crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{");
-       crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n");
-       /*
-       progdefs_crc_file("\tint\tpad;\n");
-       progdefs_crc_file("\tint\tofs_return[3];\n");
-       progdefs_crc_file("\tint\tofs_parm0[3];\n");
-       progdefs_crc_file("\tint\tofs_parm1[3];\n");
-       progdefs_crc_file("\tint\tofs_parm2[3];\n");
-       progdefs_crc_file("\tint\tofs_parm3[3];\n");
-       progdefs_crc_file("\tint\tofs_parm4[3];\n");
-       progdefs_crc_file("\tint\tofs_parm5[3];\n");
-       progdefs_crc_file("\tint\tofs_parm6[3];\n");
-       progdefs_crc_file("\tint\tofs_parm7[3];\n");
-       */
-       for (i = 0; i < parser->crc_globals; ++i) {
-           if (!ast_istype(parser->globals[i].var, ast_value))
-               continue;
-           switch (parser->globals[i].var->expression.vtype) {
-               case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
-               case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
-               case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
-               case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
-               default:
-                   crc = progdefs_crc_both(crc, "\tint\t");
-                   break;
-           }
-           crc = progdefs_crc_both(crc, parser->globals[i].name);
-           crc = progdefs_crc_both(crc, ";\n");
-       }
-       crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n");
-       for (i = 0; i < parser->crc_fields; ++i) {
-           if (!ast_istype(parser->fields[i].var, ast_value))
-               continue;
-           switch (parser->fields[i].var->expression.next->expression.vtype) {
-               case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
-               case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
-               case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
-               case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
-               default:
-                   crc = progdefs_crc_both(crc, "\tint\t");
-                   break;
-           }
-           crc = progdefs_crc_both(crc, parser->fields[i].name);
-           crc = progdefs_crc_both(crc, ";\n");
-       }
-       crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
-
-       code_crc = crc;
-}
-
 bool parser_finish(const char *output)
 {
     size_t i;
@@ -3698,6 +3979,10 @@ bool parser_finish(const char *output)
                 ir_builder_delete(ir);
                 return false;
             }
+        }
+        if (opts_dump)
+            ir_builder_dump(ir, con_out);
+        for (i = 0; i < vec_size(parser->functions); ++i) {
             if (!ir_function_finalize(parser->functions[i]->ir_func)) {
                 con_out("failed to finalize function %s\n", parser->functions[i]->name);
                 ir_builder_delete(ir);
@@ -3706,7 +3991,7 @@ bool parser_finish(const char *output)
         }
 
         if (retval) {
-            if (opts_dump)
+            if (opts_dumpfin)
                 ir_builder_dump(ir, con_out);
 
             generate_checksum(parser);