]> git.rm.cloudns.org Git - xonotic/gmqcc.git/commitdiff
XOR!
authorRudolf Polzer <divverent@xonotic.org>
Mon, 26 Aug 2013 09:40:43 +0000 (11:40 +0200)
committerRudolf Polzer <divverent@xonotic.org>
Mon, 26 Aug 2013 09:40:43 +0000 (11:40 +0200)
gmqcc.h
ir.c
parser.c
tests/xor.qc
tests/xor.tmpl

diff --git a/gmqcc.h b/gmqcc.h
index 13ce4f6b0f009d86e5ed425ea8d379162f98a601..f7e9502f11ab81c9577530c778f46ab4968f19ab 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -719,10 +719,13 @@ enum {
      */
     VINSTR_NRCALL,
     /* Emulated instructions. */
-    VINSTR_BITAND_VV,
-    VINSTR_BITOR_VV,
+    VINSTR_BITXOR,
+    VINSTR_BITAND_V,
+    VINSTR_BITOR_V,
+    VINSTR_BITXOR_V,
     VINSTR_BITAND_VF,
-    VINSTR_BITOR_VF
+    VINSTR_BITOR_VF,
+    VINSTR_BITXOR_VF
 };
 
 /* TODO: elide */
diff --git a/ir.c b/ir.c
index 94f49c296c488b1164244b89fb0e2adf66eba3d5..e60dfbd1ce0f35f9acf446be8b2b4f095ec06edc 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -613,10 +613,13 @@ static bool instr_is_operation(uint16_t op)
              (op >= INSTR_NOT_F  && op <= INSTR_NOT_FNC) ||
              (op >= INSTR_AND    && op <= INSTR_BITOR) ||
              (op >= INSTR_CALL0  && op <= INSTR_CALL8) ||
-             (op == VINSTR_BITAND_VV) ||
+             (op == VINSTR_BITXOR) ||
+             (op == VINSTR_BITAND_V) ||
              (op == VINSTR_BITAND_VF) ||
-             (op == VINSTR_BITOR_VV) ||
-             (op == VINSTR_BITOR_VF));
+             (op == VINSTR_BITOR_V) ||
+             (op == VINSTR_BITOR_VF) ||
+             (op == VINSTR_BITXOR_V) ||
+             (op == VINSTR_BITXOR_VF));
 }
 
 static bool ir_function_pass_peephole(ir_function *self)
@@ -653,11 +656,17 @@ static bool ir_function_pass_peephole(ir_function *self)
                         continue;
                 }
 
+                /* Emulated bitxor cannot deal with aliased inputs. */
+                if (oper->opcode == VINSTR_BITXOR && oper->_ops[2]->memberof == oper->_ops[1])
+                    continue;
+
                 /* Emulated bitand/bitor for vector+float cannot deal with aliased inputs. */
                 if (oper->opcode == VINSTR_BITAND_VF && oper->_ops[2]->memberof == oper->_ops[1])
                     continue;
                 if (oper->opcode == VINSTR_BITOR_VF && oper->_ops[2]->memberof == oper->_ops[1])
                     continue;
+                if (oper->opcode == VINSTR_BITXOR_VF && oper->_ops[2]->memberof == oper->_ops[1])
+                    continue;
 
                 value = oper->_ops[0];
 
@@ -1781,6 +1790,7 @@ ir_value* ir_block_create_binop(ir_block *self, lex_ctx_t ctx,
 #endif
         case INSTR_BITAND:
         case INSTR_BITOR:
+        case VINSTR_BITXOR:
 #if 0
         case INSTR_SUB_S: /* -- offset of string as float */
         case INSTR_MUL_IF:
@@ -1817,10 +1827,12 @@ ir_value* ir_block_create_binop(ir_block *self, lex_ctx_t ctx,
         case INSTR_SUB_V:
         case INSTR_MUL_VF:
         case INSTR_MUL_FV:
-        case VINSTR_BITAND_VV:
-        case VINSTR_BITOR_VV:
+        case VINSTR_BITAND_V:
+        case VINSTR_BITOR_V:
+        case VINSTR_BITXOR_V:
         case VINSTR_BITAND_VF:
         case VINSTR_BITOR_VF:
+        case VINSTR_BITXOR_VF:
 #if 0
         case INSTR_DIV_VF:
         case INSTR_MUL_IV:
@@ -2520,8 +2532,11 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
          * same source and destination operand otherwise, as the engine may
          * read the source multiple times. */
         if (instr->opcode == INSTR_MUL_VF ||
+            instr->opcode == VINSTR_BITXOR ||
             instr->opcode == VINSTR_BITAND_VF ||
-            instr->opcode == VINSTR_BITOR_VF)
+            instr->opcode == VINSTR_BITOR_VF ||
+            instr->opcode == VINSTR_BITXOR_VF ||
+            instr->opcode == VINSTR_BITXOR_V)
         {
             value = instr->_ops[2];
             /* the float source will get an additional lifetime */
@@ -2786,6 +2801,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
     ir_block *onfalse;
     size_t    stidx;
     size_t    i;
+    int       j;
 
     block->generated = true;
     block->code_start = vec_size(code->statements);
@@ -2818,7 +2834,28 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             return true;
         }
 
-        if (instr->opcode == VINSTR_BITAND_VV) {
+        if (instr->opcode == VINSTR_BITXOR) {
+            stmt.opcode = INSTR_BITOR;
+            stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]);
+            stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]);
+            stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]);
+            code_push_statement(code, &stmt, instr->context.line);
+            stmt.opcode = INSTR_BITAND;
+            stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]);
+            stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]);
+            stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]);
+            code_push_statement(code, &stmt, instr->context.line);
+            stmt.opcode = INSTR_SUB_F;
+            stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]);
+            stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]);
+            stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]);
+            code_push_statement(code, &stmt, instr->context.line);
+
+            /* instruction generated */
+            continue;
+        }
+
+        if (instr->opcode == VINSTR_BITAND_V) {
             stmt.opcode = INSTR_BITAND;
             stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]);
             stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]);
@@ -2837,7 +2874,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             continue;
         }
 
-        if (instr->opcode == VINSTR_BITOR_VV) {
+        if (instr->opcode == VINSTR_BITOR_V) {
             stmt.opcode = INSTR_BITOR;
             stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]);
             stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]);
@@ -2856,6 +2893,29 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             continue;
         }
 
+        if (instr->opcode == VINSTR_BITXOR_V) {
+            for (j = 0; j < 3; ++j) {
+                stmt.opcode = INSTR_BITOR;
+                stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j;
+                stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + j;
+                stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]) + j;
+                code_push_statement(code, &stmt, instr->context.line);
+                stmt.opcode = INSTR_BITAND;
+                stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j;
+                stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + j;
+                stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]) + j;
+                code_push_statement(code, &stmt, instr->context.line);
+            }
+            stmt.opcode = INSTR_SUB_V;
+            stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]);
+            stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]);
+            stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]);
+            code_push_statement(code, &stmt, instr->context.line);
+
+            /* instruction generated */
+            continue;
+        }
+
         if (instr->opcode == VINSTR_BITAND_VF) {
             stmt.opcode = INSTR_BITAND;
             stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]);
@@ -2890,6 +2950,29 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             continue;
         }
 
+        if (instr->opcode == VINSTR_BITXOR_VF) {
+            for (j = 0; j < 3; ++j) {
+                stmt.opcode = INSTR_BITOR;
+                stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j;
+                stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]);
+                stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]) + j;
+                code_push_statement(code, &stmt, instr->context.line);
+                stmt.opcode = INSTR_BITAND;
+                stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j;
+                stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]);
+                stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]) + j;
+                code_push_statement(code, &stmt, instr->context.line);
+            }
+            stmt.opcode = INSTR_SUB_V;
+            stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]);
+            stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]);
+            stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]);
+            code_push_statement(code, &stmt, instr->context.line);
+
+            /* instruction generated */
+            continue;
+        }
+
         if (instr->opcode == VINSTR_COND) {
             ontrue  = instr->bops[0];
             onfalse = instr->bops[1];
@@ -3856,10 +3939,13 @@ static const char *qc_opname(int op)
         case VINSTR_PHI:       return "PHI";
         case VINSTR_JUMP:      return "JUMP";
         case VINSTR_COND:      return "COND";
-        case VINSTR_BITAND_VV: return "BITAND_VV";
-        case VINSTR_BITOR_VV:  return "BITOR_VV";
+        case VINSTR_BITXOR:    return "BITXOR";
+        case VINSTR_BITAND_V:  return "BITAND_V";
+        case VINSTR_BITOR_V:   return "BITOR_V";
+        case VINSTR_BITXOR_V:  return "BITXOR_V";
         case VINSTR_BITAND_VF: return "BITAND_VF";
         case VINSTR_BITOR_VF:  return "BITOR_VF";
+        case VINSTR_BITXOR_VF: return "BITXOR_VF";
         default:               return "<UNK>";
     }
 }
index 05aac15aa16d190eb7d5b1308502ae17a116564d..1d94e921cf53ec989b4e57bf9cac0f37ba03afe7 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -623,6 +623,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 
         case opid1('|'):
         case opid1('&'):
+        case opid1('^'):
             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))
@@ -640,127 +641,29 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                  */
                 if (exprs[0]->vtype == TYPE_FLOAT) {
                     out = (ast_expression*)ast_binary_new(ctx,
-                        (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
+                        (op->id == opid1('^') ? VINSTR_BITXOR : op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
                         exprs[0], exprs[1]);
                 } else {
                     /*
-                     * The first is a vector: vector is allowed to xor with vector and
+                     * The first is a vector: vector is allowed to bitop 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
+                         * Bitop all the values of the vector components against the
                          * vectors components in question.
                          */
                         out = (ast_expression*)ast_binary_new(ctx,
-                            (op->id == opid1('|') ? VINSTR_BITOR_VV : VINSTR_BITAND_VV),
+                            (op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V),
                             exprs[0], exprs[1]);
                     } else {
                         out = (ast_expression*)ast_binary_new(ctx,
-                            (op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF),
+                            (op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF),
                             exprs[0], exprs[1]);
                     }
                 }
             }
             break;
-        case opid1('^'):
-            /*
-             * 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.
-             */
-            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 (!(out = fold_op(parser->fold, op, exprs))) {
-                /*
-                 * IF the first expression is float, the following will be too
-                 * since scalar ^ vector is not allowed.
-                 */
-                if (exprs[0]->vtype == TYPE_FLOAT) {
-                    ast_binary *expr = ast_binary_new(
-                        ctx,
-                        INSTR_SUB_F,
-                        (ast_expression*)parser->fold->imm_float[2],
-                        (ast_expression*)ast_binary_new(
-                            ctx,
-                            INSTR_BITAND,
-                            exprs[0],
-                            exprs[1]
-                        )
-                    );
-                    expr->refs = AST_REF_NONE;
-
-                    out = (ast_expression*)
-                        ast_binary_new(
-                            ctx,
-                            INSTR_BITAND,
-                            (ast_expression*)ast_binary_new(
-                                ctx,
-                                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.
-                         */
-                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
-                        return false;
-                    } else {
-                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
-                        return false;
-                    }
-                }
-            }
-            break;
 
         case opid2('<','<'):
         case opid2('>','>'):
index 41d02e95ee366a80a48fdb96975ed55d9a9d9267..cb79972734259e5a633f07929b91f437258a518b 100644 (file)
@@ -38,6 +38,12 @@ void main() {
     // vector ^ vector
     // vector ^ float
     // are legal in constant expressions (currently)
+    vector v1 = '5 2 5';
+    vector v2 = '3 10 3';
+
+    print("vv: ", vtos(v1 ^ v2), "\n");
+    print("vf: ", vtos(v1 ^ 10), "\n");
+    
     const vector v3 = '5 2 5' ^ '3 10 3';
     const vector v4 = '5 2 5' ^ 10;
     
index 5394f3d62e258b2a84664d08375206be5a04f11e..2f0ff10875017397a016f53785d94ecc247b811c 100644 (file)
@@ -10,4 +10,6 @@ M: assocative
 M: inverse
 M: vv: '6 8 6'
 M: vf: '15 8 15'
+M: vv: '6 8 6'
+M: vf: '15 8 15'
 M: 100:200 swapped is: 200:100