]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ir.c
Happy new year!
[xonotic/gmqcc.git] / ir.c
diff --git a/ir.c b/ir.c
index 56c91e5032e9848b2257f1f01d997eb64dec25e9..65bed5e943cdb6716f24400506a02f5e65e8773c 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
@@ -248,7 +248,7 @@ static void irerror(lex_ctx_t ctx, const char *msg, ...)
     va_end(ap);
 }
 
-static bool irwarning(lex_ctx_t ctx, int warntype, const char *fmt, ...)
+static bool GMQCC_WARN irwarning(lex_ctx_t ctx, int warntype, const char *fmt, ...)
 {
     bool    r;
     va_list ap;
@@ -613,7 +613,7 @@ 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_V && op <= VINSTR_BITXOR_VF) );
+             (op >= VINSTR_BITAND_V && op <= VINSTR_NEG_V) );
 }
 
 static bool ir_function_pass_peephole(ir_function *self)
@@ -642,7 +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. */
+                /* Don't change semantics of MUL_VF in engines where these may not alias. */
                 if (OPTS_FLAG(LEGACY_VECTOR_MATHS)) {
                     if (oper->opcode == INSTR_MUL_VF && oper->_ops[2]->memberof == oper->_ops[1])
                         continue;
@@ -650,18 +650,6 @@ 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 */
@@ -1595,7 +1583,9 @@ bool ir_block_create_return(ir_block *self, lex_ctx_t ctx, ir_value *v)
     ir_instr *in;
     if (!ir_check_unreachable(self))
         return false;
+
     self->final = true;
+
     self->is_return = true;
     in = ir_instr_new(ctx, self, INSTR_RETURN);
     if (!in)
@@ -1827,6 +1817,7 @@ ir_value* ir_block_create_binop(ir_block *self, lex_ctx_t ctx,
         case VINSTR_BITAND_VF:
         case VINSTR_BITOR_VF:
         case VINSTR_BITXOR_VF:
+        case VINSTR_CROSS:
 #if 0
         case INSTR_DIV_VF:
         case INSTR_MUL_IV:
@@ -1889,16 +1880,20 @@ ir_value* ir_block_create_unary(ir_block *self, lex_ctx_t ctx,
         case INSTR_NOT_V:
         case INSTR_NOT_S:
         case INSTR_NOT_ENT:
-        case INSTR_NOT_FNC:
-#if 0
-        case INSTR_NOT_I:
-#endif
+        case INSTR_NOT_FNC: /*
+        case INSTR_NOT_I:   */
             ot = TYPE_FLOAT;
             break;
-        /* QC doesn't have other unary operations. We expect extensions to fill
-         * the above list, otherwise we assume out-type = in-type, eg for an
-         * unary minus
+
+        /*
+         * Negation for virtual instructions is emulated with 0-value. Thankfully
+         * the operand for 0 already exists so we just source it from here.
          */
+        case VINSTR_NEG_F:
+            return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_F, NULL, operand, ot);
+        case VINSTR_NEG_V:
+            return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, NULL, operand, TYPE_VECTOR);
+
         default:
             ot = operand->vtype;
             break;
@@ -2526,11 +2521,12 @@ 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_BITXOR ||
             instr->opcode == VINSTR_BITXOR_VF ||
-            instr->opcode == VINSTR_BITXOR_V)
+            instr->opcode == VINSTR_BITXOR_V ||
+            instr->opcode == VINSTR_CROSS)
         {
             value = instr->_ops[2];
             /* the float source will get an additional lifetime */
@@ -2539,7 +2535,13 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
             if (value->memberof && ir_value_life_merge(value->memberof, instr->eid+1))
                 *changed = true;
         }
-        else if (instr->opcode == INSTR_MUL_FV || instr->opcode == INSTR_LOAD_V)
+
+        if (instr->opcode == INSTR_MUL_FV ||
+            instr->opcode == INSTR_LOAD_V ||
+            instr->opcode == VINSTR_BITXOR ||
+            instr->opcode == VINSTR_BITXOR_VF ||
+            instr->opcode == VINSTR_BITXOR_V ||
+            instr->opcode == VINSTR_CROSS)
         {
             value = instr->_ops[1];
             /* the float source will get an additional lifetime */
@@ -2967,6 +2969,28 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             continue;
         }
 
+        if (instr->opcode == VINSTR_CROSS) {
+            stmt.opcode = INSTR_MUL_F;
+            for (j = 0; j < 3; ++j) {
+                stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + (j + 1) % 3;
+                stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + (j + 2) % 3;
+                stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]) + j;
+                code_push_statement(code, &stmt, instr->context);
+                stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + (j + 2) % 3;
+                stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + (j + 1) % 3;
+                stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]) + j;
+                code_push_statement(code, &stmt, instr->context);
+            }
+            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);
+
+            /* instruction generated */
+            continue;
+        }
+
         if (instr->opcode == VINSTR_COND) {
             ontrue  = instr->bops[0];
             onfalse = instr->bops[1];
@@ -3180,7 +3204,6 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                 continue;
             }
         }
-
         code_push_statement(code, &stmt, instr->context);
     }
     return true;
@@ -3461,8 +3484,16 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global)
     irfun = global->constval.vfunc;
     if (!irfun) {
         if (global->cvq == CV_NONE) {
-            irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER,
-                      "function `%s` has no body and in QC implicitly becomes a function-pointer", global->name);
+            if (irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER,
+                          "function `%s` has no body and in QC implicitly becomes a function-pointer",
+                          global->name))
+            {
+                /* Not bailing out just now. If this happens a lot you don't want to have
+                 * to rerun gmqcc for each such function.
+                 */
+
+                /* return false; */
+            }
         }
         /* this was a function pointer, don't generate code for those */
         return true;
@@ -3471,6 +3502,14 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global)
     if (irfun->builtin)
         return true;
 
+    /*
+     * If there is no definition and the thing is eraseable, we can ignore
+     * outputting the function to begin with.
+     */
+    if (global->flags & IR_FLAG_ERASEABLE && irfun->code_function_def < 0) {
+        return true;
+    }
+
     if (irfun->code_function_def < 0) {
         irerror(irfun->context, "`%s`: IR global wasn't generated, failed to access function-def", irfun->name);
         return false;
@@ -3571,6 +3610,14 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc
     {
         pushdef = true;
 
+        /*
+         * if we're eraseable and the function isn't referenced ignore outputting
+         * the function.
+         */
+        if (global->flags & IR_FLAG_ERASEABLE && vec_size(global->reads) == 0) {
+            return true;
+        }
+
         if (OPTS_OPTIMIZATION(OPTIM_STRIP_CONSTANT_NAMES) &&
             !(global->flags & IR_FLAG_INCLUDE_DEF) &&
             (global->name[0] == '#' || global->cvq == CV_CONST))
@@ -3614,9 +3661,12 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc
             /* TODO: same as above but for entity-fields rather than globsl
              */
         }
-        else
-            irwarning(global->context, WARN_VOID_VARIABLES, "unrecognized variable of type void `%s`",
-                      global->name);
+        else if(irwarning(global->context, WARN_VOID_VARIABLES, "unrecognized variable of type void `%s`",
+                          global->name))
+        {
+            /* Not bailing out */
+            /* return false; */
+        }
         /* I'd argue setting it to 0 is sufficient, but maybe some depend on knowing how far
          * the system fields actually go? Though the engine knows this anyway...
          * Maybe this could be an -foption
@@ -3929,10 +3979,6 @@ bool ir_builder_generate(ir_builder *self, const char *filename)
 
 #define IND_BUFSZ 1024
 
-#ifdef _MSC_VER
-#   define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE)
-#endif
-
 static const char *qc_opname(int op)
 {
     if (op < 0) return "<INVALID>";
@@ -3950,6 +3996,9 @@ static const char *qc_opname(int op)
         case VINSTR_BITAND_VF: return "BITAND_VF";
         case VINSTR_BITOR_VF:  return "BITOR_VF";
         case VINSTR_BITXOR_VF: return "BITXOR_VF";
+        case VINSTR_CROSS:     return "CROSS";
+        case VINSTR_NEG_F:     return "NEG_F";
+        case VINSTR_NEG_V:     return "NEG_V";
         default:               return "<UNK>";
     }
 }
@@ -3988,7 +4037,7 @@ void ir_function_dump(ir_function *f, char *ind,
         return;
     }
     oprintf("%sfunction %s\n", ind, f->name);
-    strncat(ind, "\t", IND_BUFSZ-1);
+    util_strncat(ind, "\t", IND_BUFSZ-1);
     if (vec_size(f->locals))
     {
         oprintf("%s%i locals:\n", ind, (int)vec_size(f->locals));
@@ -4084,7 +4133,7 @@ void ir_block_dump(ir_block* b, char *ind,
 {
     size_t i;
     oprintf("%s:%s\n", ind, b->label);
-    strncat(ind, "\t", IND_BUFSZ-1);
+    util_strncat(ind, "\t", IND_BUFSZ-1);
 
     if (b->instr && b->instr[0])
         oprintf("%s (%i) [entry]\n", ind, (int)(b->instr[0]->eid-1));
@@ -4118,7 +4167,7 @@ void ir_instr_dump(ir_instr *in, char *ind,
         return;
     }
 
-    strncat(ind, "\t", IND_BUFSZ-1);
+    util_strncat(ind, "\t", IND_BUFSZ-1);
 
     if (in->_ops[0] && (in->_ops[1] || in->_ops[2])) {
         ir_value_dump(in->_ops[0], oprintf);