Support vector bitor/bitand/bitxor. divVerent/submit/vector-bitops
authorRudolf Polzer <divverent@xonotic.org>
Mon, 26 Aug 2013 08:25:29 +0000 (10:25 +0200)
committerRudolf Polzer <divverent@xonotic.org>
Mon, 26 Aug 2013 16:32:28 +0000 (18:32 +0200)
Signed-off-by: Rudolf Polzer <divverent@xonotic.org>
fold.c
gmqcc.h
ir.c
ir.h
parser.c
tests/bitnot.qc
tests/bitnot.tmpl
tests/vec_ops.qc
tests/vec_ops.tmpl
tests/xor.qc
tests/xor.tmpl

diff --git a/fold.c b/fold.c
index b1e89c34940c70037e3953b16eafd420d0afe6f6..4019794c3669db8204eb9aff76b54bcce27ca4c9 100644 (file)
--- a/fold.c
+++ b/fold.c
@@ -73,6 +73,38 @@ static GMQCC_INLINE vec3_t vec3_neg(vec3_t a) {
     return out;
 }
 
+static GMQCC_INLINE vec3_t vec3_or(vec3_t a, vec3_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)(((qcint_t)a.x) | ((qcint_t)b.x));
+    out.y = (qcfloat_t)(((qcint_t)a.y) | ((qcint_t)b.y));
+    out.z = (qcfloat_t)(((qcint_t)a.z) | ((qcint_t)b.z));
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_orvf(vec3_t a, qcfloat_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)(((qcint_t)a.x) | ((qcint_t)b));
+    out.y = (qcfloat_t)(((qcint_t)a.y) | ((qcint_t)b));
+    out.z = (qcfloat_t)(((qcint_t)a.z) | ((qcint_t)b));
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_and(vec3_t a, vec3_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)(((qcint_t)a.x) & ((qcint_t)b.x));
+    out.y = (qcfloat_t)(((qcint_t)a.y) & ((qcint_t)b.y));
+    out.z = (qcfloat_t)(((qcint_t)a.z) & ((qcint_t)b.z));
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_andvf(vec3_t a, qcfloat_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)(((qcint_t)a.x) & ((qcint_t)b));
+    out.y = (qcfloat_t)(((qcint_t)a.y) & ((qcint_t)b));
+    out.z = (qcfloat_t)(((qcint_t)a.z) & ((qcint_t)b));
+    return out;
+}
+
 static GMQCC_INLINE vec3_t vec3_xor(vec3_t a, vec3_t b) {
     vec3_t out;
     out.x = (qcfloat_t)(((qcint_t)a.x) ^ ((qcint_t)b.x));
@@ -89,6 +121,14 @@ static GMQCC_INLINE vec3_t vec3_xorvf(vec3_t a, qcfloat_t b) {
     return out;
 }
 
+static GMQCC_INLINE vec3_t vec3_not(vec3_t a) {
+    vec3_t out;
+    out.x = (qcfloat_t)(~((qcint_t)a.x));
+    out.y = (qcfloat_t)(~((qcint_t)a.y));
+    out.z = (qcfloat_t)(~((qcint_t)a.z));
+    return out;
+}
+
 static GMQCC_INLINE qcfloat_t vec3_mulvv(vec3_t a, vec3_t b) {
     return (a.x * b.x + a.y * b.y + a.z * b.z);
 }
@@ -184,6 +224,7 @@ fold_t *fold_init(parser_t *parser) {
     (void)fold_constgen_float (fold, -1.0f);
 
     (void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.0f));
+    (void)fold_constgen_vector(fold, vec3_create(-1.0f, -1.0f, -1.0f));
 
     return fold;
 }
@@ -444,14 +485,34 @@ static GMQCC_INLINE ast_expression *fold_op_mod(fold_t *fold, ast_value *a, ast_
 }
 
 static GMQCC_INLINE ast_expression *fold_op_bor(fold_t *fold, ast_value *a, ast_value *b) {
-    if (fold_can_2(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))));
+    if (isfloat(a)) {
+        if (fold_can_2(a, b))
+            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))));
+    } else {
+        if (isvector(b)) {
+            if (fold_can_2(a, b))
+                return fold_constgen_vector(fold, vec3_or(fold_immvalue_vector(a), fold_immvalue_vector(b)));
+        } else {
+            if (fold_can_2(a, b))
+                return fold_constgen_vector(fold, vec3_orvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
+        }
+    }
     return NULL;
 }
 
 static GMQCC_INLINE ast_expression *fold_op_band(fold_t *fold, ast_value *a, ast_value *b) {
-    if (fold_can_2(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))));
+    if (isfloat(a)) {
+        if (fold_can_2(a, b))
+            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))));
+    } else {
+        if (isvector(b)) {
+            if (fold_can_2(a, b))
+                return fold_constgen_vector(fold, vec3_and(fold_immvalue_vector(a), fold_immvalue_vector(b)));
+        } else {
+            if (fold_can_2(a, b))
+                return fold_constgen_vector(fold, vec3_andvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
+        }
+    }
     return NULL;
 }
 
@@ -537,8 +598,15 @@ static GMQCC_INLINE ast_expression *fold_op_cmp(fold_t *fold, ast_value *a, ast_
 }
 
 static GMQCC_INLINE ast_expression *fold_op_bnot(fold_t *fold, ast_value *a) {
-    if (fold_can_1(a))
-        return fold_constgen_float(fold, ~((qcint_t)fold_immvalue_float(a)));
+    if (isfloat(a)) {
+        if (fold_can_1(a))
+            return fold_constgen_float(fold, ~((qcint_t)fold_immvalue_float(a)));
+    } else {
+        if (isvector(a)) {
+            if (fold_can_1(a))
+                return fold_constgen_vector(fold, vec3_not(fold_immvalue_vector(a)));
+        }
+    }
     return NULL;
 }
 
diff --git a/gmqcc.h b/gmqcc.h
index 36406a26ea22020ea0f37e7343aa16940b1dd98f..5ac193533538bc9c0805e1c754cb268e360f138b 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -717,7 +717,16 @@ enum {
      * Creating this causes IR blocks to be marked as 'final'.
      * No-Return-Call
      */
-    VINSTR_NRCALL
+    VINSTR_NRCALL,
+
+    /* Emulated instructions. */
+    VINSTR_BITAND_V, /* BITAND_V must be the first emulated bitop */
+    VINSTR_BITAND_VF,
+    VINSTR_BITOR_V,
+    VINSTR_BITOR_VF,
+    VINSTR_BITXOR,
+    VINSTR_BITXOR_V,
+    VINSTR_BITXOR_VF /* BITXOR_VF must be the last emulated bitop */
 };
 
 /* TODO: elide */
diff --git a/ir.c b/ir.c
index ee07a449efba2d102fdddc40cba41dc7a0e7d405..d77f0110e62a87a6f6d152f3309e1b2cb2be5390 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -312,6 +312,7 @@ static void ir_function_delete_quick(ir_function *self);
 ir_builder* ir_builder_new(const char *modulename)
 {
     ir_builder* self;
+    size_t      i;
 
     self = (ir_builder*)mem_a(sizeof(*self));
     if (!self)
@@ -344,6 +345,15 @@ ir_builder* ir_builder_new(const char *modulename)
     self->nil = ir_value_var("nil", store_value, TYPE_NIL);
     self->nil->cvq = CV_CONST;
 
+    for (i = 0; i != IR_MAX_VINSTR_TEMPS; ++i) {
+        /* we write to them, but they're not supposed to be used outside the IR, so
+         * let's not allow the generation of ir_instrs which use these.
+         * So it's a constant noexpr.
+         */
+        self->vinstr_temp[i] = ir_value_var("vinstr_temp", store_value, TYPE_NOEXPR);
+        self->vinstr_temp[i]->cvq = CV_CONST;
+    }
+
     self->reserved_va_count = NULL;
     self->code              = code_init();
 
@@ -374,6 +384,9 @@ void ir_builder_delete(ir_builder* self)
         ir_value_delete(self->fields[i]);
     }
     ir_value_delete(self->nil);
+    for (i = 0; i != IR_MAX_VINSTR_TEMPS; ++i) {
+        ir_value_delete(self->vinstr_temp[i]);
+    }
     vec_free(self->fields);
     vec_free(self->filenames);
     vec_free(self->filestrings);
@@ -599,7 +612,8 @@ static bool instr_is_operation(uint16_t op)
              (op == INSTR_ADDRESS) ||
              (op >= INSTR_NOT_F  && op <= INSTR_NOT_FNC) ||
              (op >= INSTR_AND    && op <= INSTR_BITOR) ||
-             (op >= INSTR_CALL0  && op <= INSTR_CALL8) );
+             (op >= INSTR_CALL0  && op <= INSTR_CALL8) ||
+             (op >= VINSTR_BITAND_V && op <= VINSTR_BITXOR_VF) );
 }
 
 static bool ir_function_pass_peephole(ir_function *self)
@@ -628,6 +642,7 @@ static bool ir_function_pass_peephole(ir_function *self)
                 if (!instr_is_operation(oper->opcode))
                     continue;
 
+                /* Old engine's mul for vector+float cannot deal with aliased inputs. */
                 if (OPTS_FLAG(LEGACY_VECTOR_MATHS)) {
                     if (oper->opcode == INSTR_MUL_VF && oper->_ops[2]->memberof == oper->_ops[1])
                         continue;
@@ -635,6 +650,18 @@ 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];
 
                 /* only do it for SSA values */
@@ -1019,6 +1046,11 @@ static void ir_instr_delete(ir_instr *self)
 
 static bool ir_instr_op(ir_instr *self, int op, ir_value *v, bool writing)
 {
+    if (v && v->vtype == TYPE_NOEXPR) {
+        irerror(self->context, "tried to use a NOEXPR value");
+        return false;
+    }
+
     if (self->_ops[op]) {
         size_t idx;
         if (writing && vec_ir_instr_find(self->_ops[op]->writes, self, &idx))
@@ -1752,6 +1784,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:
@@ -1788,6 +1821,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_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:
@@ -2483,7 +2522,15 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
             }
         }
 
-        if (instr->opcode == INSTR_MUL_VF)
+        /* These operations need a special case as they can break when using
+         * 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_BITXOR_VF ||
+            instr->opcode == VINSTR_BITXOR_V)
         {
             value = instr->_ops[2];
             /* the float source will get an additional lifetime */
@@ -2748,6 +2795,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);
@@ -2780,6 +2828,145 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             return true;
         }
 
+        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]);
+            stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]);
+            code_push_statement(code, &stmt, instr->context.line);
+            ++stmt.o1.s1;
+            ++stmt.o2.s1;
+            ++stmt.o3.s1;
+            code_push_statement(code, &stmt, instr->context.line);
+            ++stmt.o1.s1;
+            ++stmt.o2.s1;
+            ++stmt.o3.s1;
+            code_push_statement(code, &stmt, instr->context.line);
+
+            /* instruction generated */
+            continue;
+        }
+
+        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]);
+            stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]);
+            code_push_statement(code, &stmt, instr->context.line);
+            ++stmt.o1.s1;
+            ++stmt.o2.s1;
+            ++stmt.o3.s1;
+            code_push_statement(code, &stmt, instr->context.line);
+            ++stmt.o1.s1;
+            ++stmt.o2.s1;
+            ++stmt.o3.s1;
+            code_push_statement(code, &stmt, instr->context.line);
+
+            /* instruction generated */
+            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]);
+            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.o1.s1;
+            ++stmt.o3.s1;
+            code_push_statement(code, &stmt, instr->context.line);
+            ++stmt.o1.s1;
+            ++stmt.o3.s1;
+            code_push_statement(code, &stmt, instr->context.line);
+
+            /* instruction generated */
+            continue;
+        }
+
+        if (instr->opcode == VINSTR_BITOR_VF) {
+            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.o1.s1;
+            ++stmt.o3.s1;
+            code_push_statement(code, &stmt, instr->context.line);
+            ++stmt.o1.s1;
+            ++stmt.o3.s1;
+            code_push_statement(code, &stmt, instr->context.line);
+
+            /* instruction generated */
+            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];
@@ -3652,6 +3839,14 @@ bool ir_builder_generate(ir_builder *self, const char *filename)
     vec_push(self->code->globals, 0);
     vec_push(self->code->globals, 0);
 
+    /* generate virtual-instruction temps */
+    for (i = 0; i < IR_MAX_VINSTR_TEMPS; ++i) {
+        ir_value_code_setaddr(self->vinstr_temp[i], vec_size(self->code->globals));
+        vec_push(self->code->globals, 0);
+        vec_push(self->code->globals, 0);
+        vec_push(self->code->globals, 0);
+    }
+
     /* generate global temps */
     self->first_common_globaltemp = vec_size(self->code->globals);
     for (i = 0; i < self->max_globaltemps; ++i) {
@@ -3734,11 +3929,18 @@ static const char *qc_opname(int op)
     if (op < VINSTR_END)
         return util_instr_str[op];
     switch (op) {
-        case VINSTR_END:  return "END";
-        case VINSTR_PHI:  return "PHI";
-        case VINSTR_JUMP: return "JUMP";
-        case VINSTR_COND: return "COND";
-        default:          return "<UNK>";
+        case VINSTR_END:       return "END";
+        case VINSTR_PHI:       return "PHI";
+        case VINSTR_JUMP:      return "JUMP";
+        case VINSTR_COND:      return "COND";
+        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>";
     }
 }
 
diff --git a/ir.h b/ir.h
index b62b09dc8757729d31ced38e9c258e1a30ffffbe..46ac464b808f3db6b47e46cb03dae49ea1aefc6e 100644 (file)
--- a/ir.h
+++ b/ir.h
@@ -240,6 +240,7 @@ ir_block*       ir_function_create_block(lex_ctx_t ctx, ir_function*, const char
 
 /* builder */
 #define IR_HT_SIZE 1024
+#define IR_MAX_VINSTR_TEMPS 1
 typedef struct ir_builder_s
 {
     char *name;
@@ -261,12 +262,16 @@ typedef struct ir_builder_s
     uint32_t      first_common_globaltemp;
 
     const char **filenames;
-    qcint_t       *filestrings;
+    qcint_t     *filestrings;
     /* we cache the #IMMEDIATE string here */
-    qcint_t        str_immediate;
+    qcint_t      str_immediate;
     /* there should just be this one nil */
     ir_value    *nil;
     ir_value    *reserved_va_count;
+    /* some virtual instructions require temps, and their code is isolated
+     * so that we don't need to keep track of their liveness.
+     */
+    ir_value    *vinstr_temp[IR_MAX_VINSTR_TEMPS];
 
     /* code generator */
     code_t      *code;
index a2ab3ca9b903a103e079039a9edc1550c94ecc11..9ed80431364fc6b04692362effdad2bfd265dfcf 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -623,55 +623,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 
         case opid1('|'):
         case opid1('&'):
-            if (NotSameType(TYPE_FLOAT)) {
-                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)))
-                out = (ast_expression*)ast_binary_new(ctx,
-                    (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
-                    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))
@@ -688,46 +640,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                  * 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
-                        );
+                    out = (ast_expression*)ast_binary_new(ctx,
+                        (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.
                          */
-                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
-                        return false;
+                        out = (ast_expression*)ast_binary_new(ctx,
+                            (op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V),
+                            exprs[0], exprs[1]);
                     } else {
-                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
-                        return false;
+                        out = (ast_expression*)ast_binary_new(ctx,
+                            (op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF),
+                            exprs[0], exprs[1]);
                     }
                 }
             }
@@ -1096,7 +1028,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
         case opid2('&','='):
         case opid2('|','='):
-            if (NotSameType(TYPE_FLOAT)) {
+        case opid2('^','='):
+            if (NotSameType(TYPE_FLOAT) && NotSameType(TYPE_VECTOR)) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                 compile_error(ctx, "invalid types used in expression: %s and %s",
@@ -1110,16 +1043,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
                 assignop = type_store_instr[exprs[0]->vtype];
-            out = (ast_expression*)ast_binstore_new(ctx, assignop,
-                                                    (op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
-                                                    exprs[0], exprs[1]);
+            if (exprs[0]->vtype == TYPE_FLOAT)
+                out = (ast_expression*)ast_binstore_new(ctx, assignop,
+                                                        (op->id == opid2('^','=') ? VINSTR_BITXOR : op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
+                                                        exprs[0], exprs[1]);
+            else
+                out = (ast_expression*)ast_binstore_new(ctx, assignop,
+                                                        (op->id == opid2('^','=') ? VINSTR_BITXOR_V : op->id == opid2('&','=') ? VINSTR_BITAND_V : VINSTR_BITOR_V),
+                                                        exprs[0], exprs[1]);
             break;
         case opid3('&','~','='):
             /* This is like: a &= ~(b);
              * But QC has no bitwise-not, so we implement it as
              * a -= a & (b);
              */
-            if (NotSameType(TYPE_FLOAT)) {
+            if (NotSameType(TYPE_FLOAT) && NotSameType(TYPE_VECTOR)) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                 compile_error(ctx, "invalid types used in expression: %s and %s",
@@ -1130,25 +1068,36 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
                 assignop = type_store_instr[exprs[0]->vtype];
-            out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
+            if (exprs[0]->vtype == TYPE_FLOAT)
+                out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
+            else
+                out = (ast_expression*)ast_binary_new(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]);
             if (!out)
                 return false;
             if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
                 compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
             }
-            asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
+            if (exprs[0]->vtype == TYPE_FLOAT)
+                asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
+            else
+                asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_V, exprs[0], out);
             asbinstore->keep_dest = true;
             out = (ast_expression*)asbinstore;
             break;
 
         case opid2('~', 'P'):
-            if (exprs[0]->vtype != TYPE_FLOAT) {
+            if (exprs[0]->vtype != TYPE_FLOAT && exprs[0]->vtype != TYPE_VECTOR) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
                 return false;
             }
-            if (!(out = fold_op(parser->fold, op, exprs)))
-                out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                if (exprs[0]->vtype == TYPE_FLOAT) {
+                    out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
+                } else {
+                    out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]);
+                }
+            }
             break;
     }
 #undef NotSameType
index c6e875d2170a54229572b395d63725a9e5bb4e0d..17ba6cd0e09dc7330caef5c33a56191de0bbbc90 100644 (file)
@@ -3,12 +3,18 @@ void main() {
     float b; b = 1;
     float c; c = 1;
     float d; d = 1;
+    vector e; e = '1 1 1';
+    vector f; f = '1 1 1';
 
     a &~= 1; // 0
     b &= ~1; // 0
     c &= ~d; // 0
+    f &~= e; // '0 0 0'
+    e &= ~e; // '0 0 0'
 
     print("a: ", ftos(a), "\nb: ",
                  ftos(b), "\nc: ",
                  ftos(c), "\n");
+    print("e: ", vtos(e), "\n");
+    print("f: ", vtos(f), "\n");
 }
index 75ae64ac4167ee3edbcdc9b41571e98e300960b0..b6a1eaa503c8003f61efd07347585712cc0514b5 100644 (file)
@@ -7,3 +7,5 @@ E: $null
 M: a: 0
 M: b: 0
 M: c: 0
+M: e: '0 0 0'
+M: f: '0 0 0'
index ac84ffcc7768b1cd9b794ea96b9fce55cf6c0ec2..509dc3a6a1c36a42fbf532ad1b63b2a44c4ce641 100644 (file)
@@ -4,4 +4,8 @@ void main(vector v) {
     print(vtos(v), "\n");
     print(vtos(v / 2), "\n");
     print(vtos(v), "\n");
+    print(vtos(v | 16), "\n");
+    print(vtos(v & 16), "\n");
+    print(vtos(v | '25 42 51'), "\n");
+    print(vtos(v & '25 42 51'), "\n");
 }
index 7d8a501a45c6dcd96f5ff8116f0731174b6fea5a..79432325d3b52ae2032fca3d7a300074245c76c1 100644 (file)
@@ -7,3 +7,7 @@ M: '8 16 32'
 M: '4 8 16'
 M: '2 4 8'
 M: '4 8 16'
+M: '20 24 16'
+M: '0 0 16'
+M: '29 42 51'
+M: '0 8 16'
index 41d02e95ee366a80a48fdb96975ed55d9a9d9267..531a5fb42be3427daa939816274a14658e12089b 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;
     
@@ -49,4 +55,12 @@ void main() {
     float swap_y = 200;
     vector swaps = swap(swap_x, swap_y);
     print("100:200 swapped is: ", ftos(swaps.x), ":", ftos(swaps.y), "\n");
+
+    // good olde xor swap test too
+    vector swap_u = '1 2 3';
+    vector swap_v = '4 5 6';
+    swap_u ^= swap_v;
+    swap_v ^= swap_u;
+    swap_u ^= swap_v;
+    print("'1 2 3':'4 5 6' swapped is: ", vtos(swap_u), ":", vtos(swap_v), "\n");
 }
index 5394f3d62e258b2a84664d08375206be5a04f11e..a08d82487ef5e4105ebb984b38cc39b6328d730c 100644 (file)
@@ -10,4 +10,7 @@ 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
+M: '1 2 3':'4 5 6' swapped is: '4 5 6':'1 2 3'