]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ir.c
Fix unary negation (-)
[xonotic/gmqcc.git] / ir.c
diff --git a/ir.c b/ir.c
index d77f0110e62a87a6f6d152f3309e1b2cb2be5390..73cbc9af06abec83170abbf9e3b98c9bb867f28a 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014, 2015
  *     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;
@@ -355,6 +355,8 @@ ir_builder* ir_builder_new(const char *modulename)
     }
 
     self->reserved_va_count = NULL;
+    self->coverage_func     = NULL;
+
     self->code              = code_init();
 
     return self;
@@ -602,6 +604,10 @@ ir_block* ir_function_create_block(lex_ctx_t ctx, ir_function *self, const char
     ir_block* bn = ir_block_new(self, label);
     bn->context = ctx;
     vec_push(self->blocks, bn);
+
+    if ((self->flags & IR_FLAG_BLOCK_COVERAGE) && self->owner->coverage_func)
+        (void)ir_block_create_call(bn, ctx, NULL, self->owner->coverage_func, false);
+
     return bn;
 }
 
@@ -613,7 +619,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 +648,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 +656,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 */
@@ -1127,6 +1121,20 @@ ir_value* ir_value_var(const char *name, int storetype, int vtype)
     return self;
 }
 
+/*  helper function */
+static ir_value* ir_builder_imm_float(ir_builder *self, float value, bool add_to_list) {
+    ir_value *v = ir_value_var("#IMMEDIATE", store_global, TYPE_FLOAT);
+    v->flags |= IR_FLAG_ERASABLE;
+    v->hasvalue = true;
+    v->cvq = CV_CONST;
+    v->constval.vfloat = value;
+
+    vec_push(self->globals, v);
+    if (add_to_list)
+        vec_push(self->const_floats, v);
+    return v;
+}
+
 ir_value* ir_value_vector_member(ir_value *self, unsigned int member)
 {
     char     *name;
@@ -1212,9 +1220,11 @@ void ir_value_delete(ir_value* self)
         if (self->vtype == TYPE_STRING)
             mem_d((void*)self->constval.vstring);
     }
-    for (i = 0; i < 3; ++i) {
-        if (self->members[i])
-            ir_value_delete(self->members[i]);
+    if (!(self->flags & IR_FLAG_SPLIT_VECTOR)) {
+        for (i = 0; i < 3; ++i) {
+            if (self->members[i])
+                ir_value_delete(self->members[i]);
+        }
     }
     vec_free(self->reads);
     vec_free(self->writes);
@@ -1543,6 +1553,26 @@ bool ir_block_create_store_op(ir_block *self, lex_ctx_t ctx, int op, ir_value *t
     return true;
 }
 
+bool ir_block_create_state_op(ir_block *self, lex_ctx_t ctx, ir_value *frame, ir_value *think)
+{
+    ir_instr *in;
+    if (!ir_check_unreachable(self))
+        return false;
+
+    in = ir_instr_new(ctx, self, INSTR_STATE);
+    if (!in)
+        return false;
+
+    if (!ir_instr_op(in, 0, frame, false) ||
+        !ir_instr_op(in, 1, think, false))
+    {
+        ir_instr_delete(in);
+        return false;
+    }
+    vec_push(self->instr, in);
+    return true;
+}
+
 static bool ir_block_create_store(ir_block *self, lex_ctx_t ctx, ir_value *target, ir_value *what)
 {
     int op = 0;
@@ -1595,7 +1625,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 +1859,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:
@@ -1884,21 +1917,24 @@ ir_value* ir_block_create_unary(ir_block *self, lex_ctx_t ctx,
                                 ir_value *operand)
 {
     int ot = TYPE_FLOAT;
+    ir_value *minus_1 = NULL;
+    if (opcode == VINSTR_NEG_F || opcode == VINSTR_NEG_V)
+        minus_1 = ir_builder_imm_float(self->owner->owner, -1.0f, false);
     switch (opcode) {
         case INSTR_NOT_F:
         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 is implemented as -1 * <operand> */
+        case VINSTR_NEG_F:
+            return ir_block_create_general_instr(self, ctx, label, INSTR_MUL_F, minus_1, operand, TYPE_FLOAT);
+        case VINSTR_NEG_V:
+            return ir_block_create_general_instr(self, ctx, label, INSTR_MUL_FV, minus_1, operand, TYPE_VECTOR);
+
         default:
             ot = operand->vtype;
             break;
@@ -2526,11 +2562,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 +2576,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 */
@@ -2822,7 +2865,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             stmt.o2.s1 = 0;
             stmt.o3.s1 = 0;
             if (stmt.o1.s1 != 1)
-                code_push_statement(code, &stmt, instr->context.line);
+                code_push_statement(code, &stmt, instr->context);
 
             /* no further instructions can be in this block */
             return true;
@@ -2833,17 +2876,17 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             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);
+            code_push_statement(code, &stmt, instr->context);
             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);
+            code_push_statement(code, &stmt, instr->context);
             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);
+            code_push_statement(code, &stmt, instr->context);
 
             /* instruction generated */
             continue;
@@ -2854,15 +2897,15 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             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);
+            code_push_statement(code, &stmt, instr->context);
             ++stmt.o1.s1;
             ++stmt.o2.s1;
             ++stmt.o3.s1;
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
             ++stmt.o1.s1;
             ++stmt.o2.s1;
             ++stmt.o3.s1;
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
 
             /* instruction generated */
             continue;
@@ -2873,15 +2916,15 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             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);
+            code_push_statement(code, &stmt, instr->context);
             ++stmt.o1.s1;
             ++stmt.o2.s1;
             ++stmt.o3.s1;
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
             ++stmt.o1.s1;
             ++stmt.o2.s1;
             ++stmt.o3.s1;
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
 
             /* instruction generated */
             continue;
@@ -2893,18 +2936,18 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                 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);
+                code_push_statement(code, &stmt, instr->context);
                 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);
+                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.line);
+            code_push_statement(code, &stmt, instr->context);
 
             /* instruction generated */
             continue;
@@ -2915,13 +2958,13 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             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);
+            code_push_statement(code, &stmt, instr->context);
             ++stmt.o1.s1;
             ++stmt.o3.s1;
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
             ++stmt.o1.s1;
             ++stmt.o3.s1;
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
 
             /* instruction generated */
             continue;
@@ -2932,13 +2975,13 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             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);
+            code_push_statement(code, &stmt, instr->context);
             ++stmt.o1.s1;
             ++stmt.o3.s1;
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
             ++stmt.o1.s1;
             ++stmt.o3.s1;
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
 
             /* instruction generated */
             continue;
@@ -2950,18 +2993,40 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                 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);
+                code_push_statement(code, &stmt, instr->context);
                 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);
+                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.line);
+            code_push_statement(code, &stmt, instr->context);
+
+            /* instruction generated */
+            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;
@@ -2982,13 +3047,13 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                 stmt.opcode = INSTR_IF;
                 stmt.o2.s1 = (ontrue->code_start) - vec_size(code->statements);
                 if (stmt.o2.s1 != 1)
-                    code_push_statement(code, &stmt, instr->context.line);
+                    code_push_statement(code, &stmt, instr->context);
             }
             if (onfalse->generated) {
                 stmt.opcode = INSTR_IFNOT;
                 stmt.o2.s1 = (onfalse->code_start) - vec_size(code->statements);
                 if (stmt.o2.s1 != 1)
-                    code_push_statement(code, &stmt, instr->context.line);
+                    code_push_statement(code, &stmt, instr->context);
             }
             if (!ontrue->generated) {
                 if (onfalse->generated)
@@ -3008,7 +3073,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                 ontrue = tmp;
             }
             stidx = vec_size(code->statements);
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
             /* on false we jump, so add ontrue-path */
             if (!gen_blocks_recursive(code, func, ontrue))
                 return false;
@@ -3040,7 +3105,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                 stmt.o2.s1 = 0;
                 stmt.o3.s1 = 0;
                 if (stmt.o1.s1 != 1)
-                    code_push_statement(code, &stmt, instr->context.line);
+                    code_push_statement(code, &stmt, instr->context);
                 return true;
             }
             else if (stidx+2 == vec_size(code->statements) && code->statements[stidx].o2.s1 == 1) {
@@ -3079,7 +3144,21 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                     stmt.opcode = type_store_instr[param->vtype];
                 stmt.o1.u1 = ir_value_code_addr(param);
                 stmt.o2.u1 = OFS_PARM0 + 3 * p;
-                code_push_statement(code, &stmt, instr->context.line);
+
+                if (param->vtype == TYPE_VECTOR && (param->flags & IR_FLAG_SPLIT_VECTOR)) {
+                    /* fetch 3 separate floats */
+                    stmt.opcode = INSTR_STORE_F;
+                    stmt.o1.u1 = ir_value_code_addr(param->members[0]);
+                    code_push_statement(code, &stmt, instr->context);
+                    stmt.o2.u1++;
+                    stmt.o1.u1 = ir_value_code_addr(param->members[1]);
+                    code_push_statement(code, &stmt, instr->context);
+                    stmt.o2.u1++;
+                    stmt.o1.u1 = ir_value_code_addr(param->members[2]);
+                    code_push_statement(code, &stmt, instr->context);
+                }
+                else
+                    code_push_statement(code, &stmt, instr->context);
             }
             /* Now handle extparams */
             first = vec_size(instr->params);
@@ -3108,7 +3187,20 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                     stmt.opcode = type_store_instr[param->vtype];
                 stmt.o1.u1 = ir_value_code_addr(param);
                 stmt.o2.u1 = ir_value_code_addr(targetparam);
-                code_push_statement(code, &stmt, instr->context.line);
+                if (param->vtype == TYPE_VECTOR && (param->flags & IR_FLAG_SPLIT_VECTOR)) {
+                    /* fetch 3 separate floats */
+                    stmt.opcode = INSTR_STORE_F;
+                    stmt.o1.u1 = ir_value_code_addr(param->members[0]);
+                    code_push_statement(code, &stmt, instr->context);
+                    stmt.o2.u1++;
+                    stmt.o1.u1 = ir_value_code_addr(param->members[1]);
+                    code_push_statement(code, &stmt, instr->context);
+                    stmt.o2.u1++;
+                    stmt.o1.u1 = ir_value_code_addr(param->members[2]);
+                    code_push_statement(code, &stmt, instr->context);
+                }
+                else
+                    code_push_statement(code, &stmt, instr->context);
             }
 
             stmt.opcode = INSTR_CALL0 + vec_size(instr->params);
@@ -3117,7 +3209,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             stmt.o1.u1 = ir_value_code_addr(instr->_ops[1]);
             stmt.o2.u1 = 0;
             stmt.o3.u1 = 0;
-            code_push_statement(code, &stmt, instr->context.line);
+            code_push_statement(code, &stmt, instr->context);
 
             retvalue = instr->_ops[0];
             if (retvalue && retvalue->store != store_return &&
@@ -3131,14 +3223,20 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                 stmt.o1.u1 = OFS_RETURN;
                 stmt.o2.u1 = ir_value_code_addr(retvalue);
                 stmt.o3.u1 = 0;
-                code_push_statement(code, &stmt, instr->context.line);
+                code_push_statement(code, &stmt, instr->context);
             }
             continue;
         }
 
         if (instr->opcode == INSTR_STATE) {
-            irerror(block->context, "TODO: state instruction");
-            return false;
+            stmt.opcode = instr->opcode;
+            if (instr->_ops[0])
+                stmt.o1.u1 = ir_value_code_addr(instr->_ops[0]);
+            if (instr->_ops[1])
+                stmt.o2.u1 = ir_value_code_addr(instr->_ops[1]);
+            stmt.o3.u1 = 0;
+            code_push_statement(code, &stmt, instr->context);
+            continue;
         }
 
         stmt.opcode = instr->opcode;
@@ -3180,8 +3278,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                 continue;
             }
         }
-
-        code_push_statement(code, &stmt, instr->context.line);
+        code_push_statement(code, &stmt, instr->context);
     }
     return true;
 }
@@ -3218,11 +3315,16 @@ static bool gen_function_code(code_t *code, ir_function *self)
         retst->opcode = INSTR_DONE;
         ++opts_optimizationcount[OPTIM_VOID_RETURN];
     } else {
+        lex_ctx_t last;
+
         stmt.opcode = INSTR_DONE;
-        stmt.o1.u1 = 0;
-        stmt.o2.u1 = 0;
-        stmt.o3.u1 = 0;
-        code_push_statement(code, &stmt, vec_last(code->linenums));
+        stmt.o1.u1  = 0;
+        stmt.o2.u1  = 0;
+        stmt.o3.u1  = 0;
+        last.line   = vec_last(code->linenums);
+        last.column = vec_last(code->columnnums);
+
+        code_push_statement(code, &stmt, last);
     }
     return true;
 }
@@ -3355,7 +3457,7 @@ static bool gen_function_extparam_copy(code_t *code, ir_function *self)
         }
         stmt.o1.u1 = ir_value_code_addr(ep);
         stmt.o2.u1 = ir_value_code_addr(self->locals[i]);
-        code_push_statement(code, &stmt, self->context.line);
+        code_push_statement(code, &stmt, self->context);
     }
 
     return true;
@@ -3380,7 +3482,7 @@ static bool gen_function_varargs_copy(code_t *code, ir_function *self)
         if (i < 8) {
             stmt.o1.u1 = OFS_PARM0 + 3*i;
             stmt.o2.u1 = ir_value_code_addr(self->locals[i]);
-            code_push_statement(code, &stmt, self->context.line);
+            code_push_statement(code, &stmt, self->context);
             continue;
         }
         ext = i - 8;
@@ -3391,7 +3493,7 @@ static bool gen_function_varargs_copy(code_t *code, ir_function *self)
 
         stmt.o1.u1 = ir_value_code_addr(ep);
         stmt.o2.u1 = ir_value_code_addr(self->locals[i]);
-        code_push_statement(code, &stmt, self->context.line);
+        code_push_statement(code, &stmt, self->context);
     }
 
     return true;
@@ -3456,8 +3558,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;
@@ -3466,6 +3576,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_ERASABLE && 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;
@@ -3559,6 +3677,10 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc
     prog_section_def_t def;
     bool               pushdef = opts.optimizeoff;
 
+    /* we don't generate split-vectors */
+    if (global->vtype == TYPE_VECTOR && (global->flags & IR_FLAG_SPLIT_VECTOR))
+        return true;
+
     def.type   = global->vtype;
     def.offset = vec_size(self->code->globals);
     def.name   = 0;
@@ -3566,6 +3688,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_ERASABLE && 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))
@@ -3609,9 +3739,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
@@ -3638,6 +3771,8 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc
     {
         ir_value_code_setaddr(global, vec_size(self->code->globals));
         if (global->hasvalue) {
+            if (global->cvq == CV_CONST && !vec_size(global->reads))
+                return true;
             iptr = (int32_t*)&global->constval.ivec[0];
             vec_push(self->code->globals, *iptr);
         } else {
@@ -3653,7 +3788,10 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc
     {
         ir_value_code_setaddr(global, vec_size(self->code->globals));
         if (global->hasvalue) {
-            uint32_t load = code_genstring(self->code, global->constval.vstring);
+            uint32_t load;
+            if (global->cvq == CV_CONST && !vec_size(global->reads))
+                return true;
+            load = code_genstring(self->code, global->constval.vstring);
             vec_push(self->code->globals, load);
         } else {
             vec_push(self->code->globals, 0);
@@ -3798,12 +3936,113 @@ static bool ir_builder_gen_field(ir_builder *self, ir_value *field)
     return field->code.globaladdr >= 0;
 }
 
+static void ir_builder_collect_reusables(ir_builder *builder) {
+    size_t i;
+    ir_value **reusables = NULL;
+    for (i = 0; i < vec_size(builder->globals); ++i) {
+        ir_value *value = builder->globals[i];
+        if (value->vtype != TYPE_FLOAT || !value->hasvalue)
+            continue;
+        if (value->cvq == CV_CONST || (value->name && value->name[0] == '#')) {
+            vec_push(reusables, value);
+        }
+    }
+    builder->const_floats = reusables;
+}
+
+static void ir_builder_split_vector(ir_builder *self, ir_value *vec) {
+    size_t i, count;
+    ir_value* found[3] = { NULL, NULL, NULL };
+
+    /* must not be written to */
+    if (vec_size(vec->writes))
+        return;
+    /* must not be trying to access individual members */
+    if (vec->members[0] || vec->members[1] || vec->members[2])
+        return;
+    /* should be actually used otherwise it won't be generated anyway */
+    count = vec_size(vec->reads);
+    if (!count)
+        return;
+
+    /* may only be used directly as function parameters, so if we find some other instruction cancel */
+    for (i = 0; i != count; ++i) {
+        /* we only split vectors if they're used directly as parameter to a call only! */
+        ir_instr *user = vec->reads[i];
+        if ((user->opcode < INSTR_CALL0 || user->opcode > INSTR_CALL8) && user->opcode != VINSTR_NRCALL)
+            return;
+    }
+
+    vec->flags |= IR_FLAG_SPLIT_VECTOR;
+
+    /* find existing floats making up the split */
+    count = vec_size(self->const_floats);
+    for (i = 0; i != count; ++i) {
+        ir_value *c = self->const_floats[i];
+        if (!found[0] && c->constval.vfloat == vec->constval.vvec.x)
+            found[0] = c;
+        if (!found[1] && c->constval.vfloat == vec->constval.vvec.y)
+            found[1] = c;
+        if (!found[2] && c->constval.vfloat == vec->constval.vvec.z)
+            found[2] = c;
+        if (found[0] && found[1] && found[2])
+            break;
+    }
+
+    /* generate floats for not yet found components */
+    if (!found[0])
+        found[0] = ir_builder_imm_float(self, vec->constval.vvec.x, true);
+    if (!found[1]) {
+        if (vec->constval.vvec.y == vec->constval.vvec.x)
+            found[1] = found[0];
+        else
+            found[1] = ir_builder_imm_float(self, vec->constval.vvec.y, true);
+    }
+    if (!found[2]) {
+        if (vec->constval.vvec.z == vec->constval.vvec.x)
+            found[2] = found[0];
+        else if (vec->constval.vvec.z == vec->constval.vvec.y)
+            found[2] = found[1];
+        else
+            found[2] = ir_builder_imm_float(self, vec->constval.vvec.z, true);
+    }
+
+    /* the .members array should be safe to use here. */
+    vec->members[0] = found[0];
+    vec->members[1] = found[1];
+    vec->members[2] = found[2];
+
+    /* register the readers for these floats */
+    count = vec_size(vec->reads);
+    for (i = 0; i != count; ++i) {
+        vec_push(found[0]->reads, vec->reads[i]);
+        vec_push(found[1]->reads, vec->reads[i]);
+        vec_push(found[2]->reads, vec->reads[i]);
+    }
+}
+
+static void ir_builder_split_vectors(ir_builder *self) {
+    size_t i, count = vec_size(self->globals);
+    for (i = 0; i != count; ++i) {
+        ir_value *v = self->globals[i];
+        if (v->vtype != TYPE_VECTOR || !v->name || v->name[0] != '#')
+            continue;
+        ir_builder_split_vector(self, self->globals[i]);
+    }
+}
+
 bool ir_builder_generate(ir_builder *self, const char *filename)
 {
     prog_section_statement_t stmt;
     size_t i;
     char  *lnofile = NULL;
 
+    if (OPTS_FLAG(SPLIT_VECTOR_PARAMETERS)) {
+        ir_builder_collect_reusables(self);
+        if (vec_size(self->const_floats) > 0)
+            ir_builder_split_vectors(self);
+    }
+
     for (i = 0; i < vec_size(self->fields); ++i)
     {
         ir_builder_prepare_field(self->code, self->fields[i]);
@@ -3869,18 +4108,23 @@ bool ir_builder_generate(ir_builder *self, const char *filename)
     }
 
     if (vec_size(self->code->globals) >= 65536) {
-        irerror(vec_last(self->globals)->context, "This progs file would require more globals than the metadata can handle. Bailing out.");
+        irerror(vec_last(self->globals)->context, "This progs file would require more globals than the metadata can handle (%u). Bailing out.", (unsigned int)vec_size(self->code->globals));
         return false;
     }
 
     /* DP errors if the last instruction is not an INSTR_DONE. */
     if (vec_last(self->code->statements).opcode != INSTR_DONE)
     {
+        lex_ctx_t last;
+
         stmt.opcode = INSTR_DONE;
-        stmt.o1.u1 = 0;
-        stmt.o2.u1 = 0;
-        stmt.o3.u1 = 0;
-        code_push_statement(self->code, &stmt, vec_last(self->code->linenums));
+        stmt.o1.u1  = 0;
+        stmt.o2.u1  = 0;
+        stmt.o3.u1  = 0;
+        last.line   = vec_last(self->code->linenums);
+        last.column = vec_last(self->code->columnnums);
+
+        code_push_statement(self->code, &stmt, last);
     }
 
     if (OPTS_OPTION_BOOL(OPTION_PP_ONLY))
@@ -3919,10 +4163,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>";
@@ -3940,6 +4180,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>";
     }
 }
@@ -3978,7 +4221,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));
@@ -4074,7 +4317,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));
@@ -4108,7 +4351,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);