]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ir.c
Support vector bitor/bitand/bitxor.
[xonotic/gmqcc.git] / ir.c
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>";
     }
 }