]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
More bug fixes and dead code elimination
[xonotic/gmqcc.git] / parser.c
index c92e51c9b005bbcee77bb378ac57de3d2dfc7222..f9793471f94f93bc4314c3be5f9d3ea88dd221a8 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -104,9 +104,6 @@ typedef struct parser_s {
 
     /* collected information */
     size_t     max_param_count;
-
-    /* code generator */
-    code_t     *code;
 } parser_t;
 
 static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
@@ -1035,55 +1032,126 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
         case opid1('^'):
             /*
-             * ^ can be implemented as:
-             * (LHS | RHS) & ~(LHS & RHS)
-             * to implement ~ we need to use -1-X, as you can see the
-             * whole process ends up becoming:
-             * (LHS | RHS) & (-1 - (LHS & RHS))
+             * Okay lets designate what the hell is an acceptable use
+             * of the ^ operator. In many vector processing units, XOR
+             * is allowed to be used on vectors, but only if the first
+             * operand is a vector, the second operand can be a float
+             * or vector. It's never legal for the first operand to be
+             * a float, and then the following operand to be a vector.
+             * Further more, the only time it is legal to do XOR otherwise
+             * is when both operand are floats. This nicely crafted if
+             * statement catches them all.
+             * 
+             * In the event that the first operand is a vector, two
+             * possible situations can arise, thus, each element of
+             * vector A (operand A) is exclusive-ORed with the corresponding
+             * element of vector B (operand B), If B is scalar, the
+             * scalar value is first replicated for each element.
+             * 
+             * The QCVM itself lacks a BITXOR instruction. Thus emulating
+             * the mathematics of it is required. The following equation
+             * is used: (LHS | RHS) & ~(LHS & RHS). However, due to the
+             * QCVM also lacking a BITNEG instruction, we need to emulate
+             * ~FOO with -1 - FOO, the whole process becoming this nicely
+             * crafted expression: (LHS | RHS) & (-1 - (LHS & RHS)).
+             * 
+             * When A is not scalar, this process is repeated for all
+             * components of vector A with the value in operand B,
+             * only if operand B is scalar. When A is not scalar, and B
+             * is also not scalar, this process is repeated for all
+             * components of the vector A with the components of vector B.
+             * Finally when A is scalar and B is scalar, this process is
+             * simply used once for A and B being LHS and RHS respectfully.
+             * 
+             * Yes the semantics are a bit strange (no pun intended).
+             * But then again BITXOR is strange itself, consdering it's
+             * commutative, assocative, and elements of the BITXOR operation
+             * are their own inverse.
              */
-            #define TRY_TYPE(I)                                       \
-                if (exprs[(I)]->vtype != TYPE_FLOAT) {                \
-                    ast_type_to_string(exprs[(I)], ty1, sizeof(ty1)); \
-                    compile_error (                                   \
-                        ast_ctx(exprs[(I)]),                          \
-                        "invalid type `%s` for bit-xor in %s",        \
-                        ty1,                                          \
-                        ((I) == 0)                                    \
-                            ? "left-hand-side of expression"          \
-                            : "right-hand-side of expression"         \
-                    );                                                \
-                    return false;                                     \
-                }
-            TRY_TYPE(0)
-            TRY_TYPE(1)
-            #undef TRY_TYPE
+            if ( !(exprs[0]->vtype == TYPE_FLOAT  && exprs[1]->vtype == TYPE_FLOAT) &&
+                 !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_FLOAT) &&
+                 !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_VECTOR))
+            {
+                compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s",
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
+                return false;
+            }
 
-            if(CanConstFold(exprs[0], exprs[1])) {
-                out = (ast_expression*)parser_const_float(parser, (float)((qcint)(ConstF(0)) ^ ((qcint)(ConstF(1)))));
-            } else {
-                out = (ast_expression*)
-                    ast_binary_new(
+            /*
+             * IF the first expression is float, the following will be too
+             * since scalar ^ vector is not allowed.
+             */
+            if (exprs[0]->vtype == TYPE_FLOAT) {
+                if(CanConstFold(exprs[0], exprs[1])) {
+                    out = (ast_expression*)parser_const_float(parser, (float)((qcint)(ConstF(0)) ^ ((qcint)(ConstF(1)))));
+                } else {
+                    ast_binary *expr = ast_binary_new(
                         ctx,
-                        INSTR_BITAND,
+                        INSTR_SUB_F,
+                        (ast_expression*)parser_const_float_neg1(parser),
                         (ast_expression*)ast_binary_new(
                             ctx,
-                            INSTR_BITOR,
+                            INSTR_BITAND,
                             exprs[0],
                             exprs[1]
-                        ),
-                        (ast_expression*)ast_binary_new(
+                        )
+                    );
+                    expr->refs = AST_REF_NONE;
+                    
+                    out = (ast_expression*)
+                        ast_binary_new(
                             ctx,
-                            INSTR_SUB_F,
-                            (ast_expression*)parser_const_float_neg1(parser),
+                            INSTR_BITAND,
                             (ast_expression*)ast_binary_new(
                                 ctx,
-                                INSTR_BITAND,
+                                INSTR_BITOR,
                                 exprs[0],
                                 exprs[1]
-                            )
-                        )
-                    );
+                            ),
+                            (ast_expression*)expr
+                        );
+                }
+            } else {
+                /*
+                 * The first is a vector: vector is allowed to xor with vector and
+                 * with scalar, branch here for the second operand.
+                 */
+                if (exprs[1]->vtype == TYPE_VECTOR) {
+                    /*
+                     * Xor all the values of the vector components against the
+                     * vectors components in question.
+                     */
+                    if (CanConstFold(exprs[0], exprs[1])) {
+                        out = (ast_expression*)parser_const_vector_f(
+                            parser,
+                            (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstV(1).x))),
+                            (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstV(1).y))),
+                            (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstV(1).z)))
+                        );
+                    } else {
+                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
+                        return false;
+                    }
+                } else {
+                    /*
+                     * Xor all the values of the vector components against the
+                     * scalar in question.
+                     */
+                    if (CanConstFold(exprs[0], exprs[1])) {
+                        out = (ast_expression*)parser_const_vector_f(
+                            parser,
+                            (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstF(1)))),
+                            (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstF(1)))),
+                            (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstF(1))))
+                        );
+                    } else {
+                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
+                        return false;
+                    }
+                }
             }
+                
             break;
 
         case opid2('<','<'):
@@ -1211,7 +1279,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             } else {
                 ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
 
-                eq->refs = (ast_binary_ref)false; /* references nothing */
+                eq->refs = AST_REF_NONE;
 
                     /* if (lt) { */
                 out = (ast_expression*)ast_ternary_new(ctx,
@@ -1739,6 +1807,8 @@ static bool parser_close_paren(parser_t *parser, shunt *sy)
 static void parser_reclassify_token(parser_t *parser)
 {
     size_t i;
+    if (parser->tok >= TOKEN_START)
+        return;
     for (i = 0; i < operator_count; ++i) {
         if (!strcmp(parser_tokval(parser), operators[i].op)) {
             parser->tok = TOKEN_OPERATOR;
@@ -1986,7 +2056,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                         correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser));
                         if (strcmp(correct, parser_tokval(parser))) {
                             break;
-                        } else if (correct) {
+                        } else  {
                             mem_d(correct);
                             correct = NULL;
                         }
@@ -2444,7 +2514,8 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond,
     }
 
     unary = (ast_unary*)cond;
-    while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
+    /* ast_istype dereferences cond, should test here for safety */
+    while (cond && ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
     {
         cond = unary->operand;
         unary->operand = NULL;
@@ -2684,7 +2755,12 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
     if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) {
         parseerror(parser, "internal error: label stack corrupted");
         rv = false;
-        ast_delete(*out);
+        /*
+         * Test for NULL otherwise ast_delete dereferences null pointer
+         * and boom.
+         */
+        if (*out)
+            ast_delete(*out);
         *out = NULL;
     }
     else {
@@ -3552,7 +3628,8 @@ static bool parse_goto(parser_t *parser, ast_expression **out)
         if (!(expression = parse_expression(parser, false, true)) ||
             !(*out = parse_goto_computed(parser, &expression))) {
             parseerror(parser, "invalid goto expression");
-            ast_unref(expression);
+            if(expression)
+                ast_unref(expression);
             return false;
         }
 
@@ -5814,8 +5891,16 @@ skipvar:
 
         if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) {
             if (parser->tok != '=') {
-                parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
-                break;
+                if (!strcmp(parser_tokval(parser), "break")) {
+                    if (!parser_next(parser)) {
+                        parseerror(parser, "error parsing break definition");
+                        break;
+                    }
+                    (void)!!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)");
+                } else {
+                    parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
+                    break;
+                }
             }
 
             if (!parser_next(parser)) {
@@ -6131,7 +6216,7 @@ static uint16_t progdefs_crc_both(uint16_t old, const char *str)
     return old;
 }
 
-static void generate_checksum(parser_t *parser)
+static void generate_checksum(parser_t *parser, ir_builder *ir)
 {
     uint16_t   crc = 0xFFFF;
     size_t     i;
@@ -6185,8 +6270,7 @@ static void generate_checksum(parser_t *parser)
         crc = progdefs_crc_both(crc, ";\n");
     }
     crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
-
-    parser->code->crc = crc;
+    ir->code->crc = crc;
 }
 
 parser_t *parser_create()
@@ -6201,11 +6285,6 @@ parser_t *parser_create()
 
     memset(parser, 0, sizeof(*parser));
 
-    if (!(parser->code = code_init())) {
-        mem_d(parser);
-        return NULL;
-    }
-
     for (i = 0; i < operator_count; ++i) {
         if (operators[i].id == opid1('=')) {
             parser->assign_op = operators+i;
@@ -6231,8 +6310,9 @@ parser_t *parser_create()
     vec_push(parser->correct_variables, correct_trie_new());
     vec_push(parser->correct_variables_score, NULL);
 
-    empty_ctx.file = "<internal>";
-    empty_ctx.line = 0;
+    empty_ctx.file   = "<internal>";
+    empty_ctx.line   = 0;
+    empty_ctx.column = 0;
     parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL);
     parser->nil->cvq = CV_CONST;
     if (OPTS_FLAG(UNTYPED_NIL))
@@ -6360,7 +6440,6 @@ static void parser_remove_ast(parser_t *parser)
     vec_free(parser->correct_variables);
     vec_free(parser->correct_variables_score);
 
-
     for (i = 0; i < vec_size(parser->_typedefs); ++i)
         ast_delete(parser->_typedefs[i]);
     vec_free(parser->_typedefs);
@@ -6381,6 +6460,9 @@ static void parser_remove_ast(parser_t *parser)
     ast_value_delete(parser->const_vec[0]);
     ast_value_delete(parser->const_vec[1]);
     ast_value_delete(parser->const_vec[2]);
+    
+    if (parser->reserved_version)
+        ast_value_delete(parser->reserved_version);
 
     util_htdel(parser->aliases);
     intrin_intrinsics_destroy(parser);
@@ -6389,8 +6471,6 @@ static void parser_remove_ast(parser_t *parser)
 void parser_cleanup(parser_t *parser)
 {
     parser_remove_ast(parser);
-    code_cleanup(parser->code);
-
     mem_d(parser);
 }
 
@@ -6548,7 +6628,8 @@ bool parser_finish(parser_t *parser, const char *output)
         }
     }
 
-    generate_checksum(parser);
+    generate_checksum(parser, ir);
+
     if (OPTS_OPTION_BOOL(OPTION_DUMP))
         ir_builder_dump(ir, con_out);
     for (i = 0; i < vec_size(parser->functions); ++i) {
@@ -6570,7 +6651,7 @@ bool parser_finish(parser_t *parser, const char *output)
         if (OPTS_OPTION_BOOL(OPTION_DUMPFIN))
             ir_builder_dump(ir, con_out);
 
-        if (!ir_builder_generate(parser->code, ir, output)) {
+        if (!ir_builder_generate(ir, output)) {
             con_out("*** failed to generate output file\n");
             ir_builder_delete(ir);
             return false;