]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ir.c
added --nocolor and fixed bug
[xonotic/gmqcc.git] / ir.c
diff --git a/ir.c b/ir.c
index 0dc785a0d4b565d6008df65e2ac13b9f75ddcc3f..4ae49d93968cd638f9c0328ecaae895811cf1f81 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -38,10 +38,11 @@ const char *type_name[TYPE_COUNT] = {
     "field",
     "function",
     "pointer",
-#if 0
     "integer",
-#endif
-    "variant"
+    "variant",
+    "struct",
+    "union",
+    "array"
 };
 
 size_t type_sizeof[TYPE_COUNT] = {
@@ -53,10 +54,11 @@ size_t type_sizeof[TYPE_COUNT] = {
     1, /* TYPE_FIELD    */
     1, /* TYPE_FUNCTION */
     1, /* TYPE_POINTER  */
-#if 0
     1, /* TYPE_INTEGER  */
-#endif
     3, /* TYPE_VARIANT  */
+    0, /* TYPE_STRUCT   */
+    0, /* TYPE_UNION    */
+    0, /* TYPE_ARRAY    */
 };
 
 uint16_t type_store_instr[TYPE_COUNT] = {
@@ -70,9 +72,37 @@ uint16_t type_store_instr[TYPE_COUNT] = {
     INSTR_STORE_ENT, /* should use I */
 #if 0
     INSTR_STORE_I, /* integer type */
+#else
+    INSTR_STORE_F,
 #endif
 
     INSTR_STORE_V, /* variant, should never be accessed */
+
+    AINSTR_END, /* struct */
+    AINSTR_END, /* union  */
+    AINSTR_END, /* array  */
+};
+
+uint16_t field_store_instr[TYPE_COUNT] = {
+    INSTR_STORE_FLD,
+    INSTR_STORE_FLD,
+    INSTR_STORE_FLD,
+    INSTR_STORE_V,
+    INSTR_STORE_FLD,
+    INSTR_STORE_FLD,
+    INSTR_STORE_FLD,
+    INSTR_STORE_FLD,
+#if 0
+    INSTR_STORE_FLD, /* integer type */
+#else
+    INSTR_STORE_FLD,
+#endif
+
+    INSTR_STORE_V, /* variant, should never be accessed */
+
+    AINSTR_END, /* struct */
+    AINSTR_END, /* union  */
+    AINSTR_END, /* array  */
 };
 
 uint16_t type_storep_instr[TYPE_COUNT] = {
@@ -86,9 +116,15 @@ uint16_t type_storep_instr[TYPE_COUNT] = {
     INSTR_STOREP_ENT, /* should use I */
 #if 0
     INSTR_STOREP_ENT, /* integer type */
+#else
+    INSTR_STOREP_F,
 #endif
 
     INSTR_STOREP_V, /* variant, should never be accessed */
+
+    AINSTR_END, /* struct */
+    AINSTR_END, /* union  */
+    AINSTR_END, /* array  */
 };
 
 uint16_t type_eq_instr[TYPE_COUNT] = {
@@ -102,9 +138,15 @@ uint16_t type_eq_instr[TYPE_COUNT] = {
     INSTR_EQ_E, /* should use I */
 #if 0
     INSTR_EQ_I,
+#else
+    INSTR_EQ_F,
 #endif
 
     INSTR_EQ_V, /* variant, should never be accessed */
+
+    AINSTR_END, /* struct */
+    AINSTR_END, /* union  */
+    AINSTR_END, /* array  */
 };
 
 uint16_t type_ne_instr[TYPE_COUNT] = {
@@ -118,9 +160,15 @@ uint16_t type_ne_instr[TYPE_COUNT] = {
     INSTR_NE_E, /* should use I */
 #if 0
     INSTR_NE_I,
+#else
+    INSTR_NE_F,
 #endif
 
     INSTR_NE_V, /* variant, should never be accessed */
+
+    AINSTR_END, /* struct */
+    AINSTR_END, /* union  */
+    AINSTR_END, /* array  */
 };
 
 MEM_VEC_FUNCTIONS(ir_value_vector, ir_value*, v)
@@ -129,7 +177,7 @@ static void irerror(lex_ctx ctx, const char *msg, ...)
 {
     va_list ap;
     va_start(ap, msg);
-    cvprintmsg(ctx, LVL_ERROR, "internal error", msg, ap);
+    con_cvprintmsg((void*)&ctx, LVL_ERROR, "internal error", msg, ap);
     va_end(ap);
 }
 
@@ -138,14 +186,14 @@ static bool irwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
        va_list ap;
        int lvl = LVL_WARNING;
 
-    if (!OPTS_WARN(warntype))
+    if (warntype && !OPTS_WARN(warntype))
         return false;
 
     if (opts_werror)
            lvl = LVL_ERROR;
 
        va_start(ap, fmt);
-    vprintmsg(lvl, ctx.file, ctx.line, "warning", fmt, ap);
+    con_vprintmsg(lvl, ctx.file, ctx.line, "warning", fmt, ap);
        va_end(ap);
 
        return opts_werror;
@@ -155,6 +203,10 @@ static bool irwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
  *IR Builder
  */
 
+static void ir_block_delete_quick(ir_block* self);
+static void ir_instr_delete_quick(ir_instr *self);
+static void ir_function_delete_quick(ir_function *self);
+
 ir_builder* ir_builder_new(const char *modulename)
 {
     ir_builder* self;
@@ -166,6 +218,9 @@ ir_builder* ir_builder_new(const char *modulename)
     MEM_VECTOR_INIT(self, functions);
     MEM_VECTOR_INIT(self, globals);
     MEM_VECTOR_INIT(self, fields);
+    MEM_VECTOR_INIT(self, filenames);
+    MEM_VECTOR_INIT(self, filestrings);
+    self->str_immediate = 0;
     self->name = NULL;
     if (!ir_builder_set_name(self, modulename)) {
         mem_d(self);
@@ -175,16 +230,18 @@ ir_builder* ir_builder_new(const char *modulename)
     return self;
 }
 
-MEM_VEC_FUNCTIONS(ir_builder, ir_value*, globals)
-MEM_VEC_FUNCTIONS(ir_builder, ir_value*, fields)
+MEM_VEC_FUNCTIONS(ir_builder, ir_value*,    globals)
+MEM_VEC_FUNCTIONS(ir_builder, ir_value*,    fields)
 MEM_VEC_FUNCTIONS(ir_builder, ir_function*, functions)
+MEM_VEC_FUNCTIONS(ir_builder, const char*,  filenames)
+MEM_VEC_FUNCTIONS(ir_builder, qcint,        filestrings)
 
 void ir_builder_delete(ir_builder* self)
 {
     size_t i;
     mem_d((void*)self->name);
     for (i = 0; i != self->functions_count; ++i) {
-        ir_function_delete(self->functions[i]);
+        ir_function_delete_quick(self->functions[i]);
     }
     MEM_VECTOR_CLEAR(self, functions);
     for (i = 0; i != self->globals_count; ++i) {
@@ -195,6 +252,8 @@ void ir_builder_delete(ir_builder* self)
         ir_value_delete(self->fields[i]);
     }
     MEM_VECTOR_CLEAR(self, fields);
+    MEM_VECTOR_CLEAR(self, filenames);
+    MEM_VECTOR_CLEAR(self, filestrings);
     mem_d(self);
 }
 
@@ -356,6 +415,30 @@ bool ir_function_set_name(ir_function *self, const char *name)
     return !!self->name;
 }
 
+static void ir_function_delete_quick(ir_function *self)
+{
+    size_t i;
+    mem_d((void*)self->name);
+
+    for (i = 0; i != self->blocks_count; ++i)
+        ir_block_delete_quick(self->blocks[i]);
+    MEM_VECTOR_CLEAR(self, blocks);
+
+    MEM_VECTOR_CLEAR(self, params);
+
+    for (i = 0; i != self->values_count; ++i)
+        ir_value_delete(self->values[i]);
+    MEM_VECTOR_CLEAR(self, values);
+
+    for (i = 0; i != self->locals_count; ++i)
+        ir_value_delete(self->locals[i]);
+    MEM_VECTOR_CLEAR(self, locals);
+
+    /* self->value is deleted by the builder */
+
+    mem_d(self);
+}
+
 void ir_function_delete(ir_function *self)
 {
     size_t i;
@@ -426,10 +509,12 @@ ir_value* ir_function_get_local(ir_function *self, const char *name)
 
 ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param)
 {
-    ir_value *ve = ir_function_get_local(self, name);
-    if (ve) {
+    ir_value *ve;
+
+    /*
+    if (ir_function_get_local(self, name))
         return NULL;
-    }
+    */
 
     if (param &&
         self->locals_count &&
@@ -460,7 +545,7 @@ ir_block* ir_block_new(ir_function* owner, const char *name)
     memset(self, 0, sizeof(*self));
 
     self->label = NULL;
-    if (!ir_block_set_label(self, name)) {
+    if (name && !ir_block_set_label(self, name)) {
         mem_d(self);
         return NULL;
     }
@@ -486,10 +571,23 @@ MEM_VEC_FUNCTIONS_ALL(ir_block, ir_block*, entries)
 MEM_VEC_FUNCTIONS_ALL(ir_block, ir_block*, exits)
 MEM_VEC_FUNCTIONS_ALL(ir_block, ir_value*, living)
 
+static void ir_block_delete_quick(ir_block* self)
+{
+    size_t i;
+    if (self->label) mem_d(self->label);
+    for (i = 0; i != self->instr_count; ++i)
+        ir_instr_delete_quick(self->instr[i]);
+    MEM_VECTOR_CLEAR(self, instr);
+    MEM_VECTOR_CLEAR(self, entries);
+    MEM_VECTOR_CLEAR(self, exits);
+    MEM_VECTOR_CLEAR(self, living);
+    mem_d(self);
+}
+
 void ir_block_delete(ir_block* self)
 {
     size_t i;
-    mem_d(self->label);
+    if (self->label) mem_d(self->label);
     for (i = 0; i != self->instr_count; ++i)
         ir_instr_delete(self->instr[i]);
     MEM_VECTOR_CLEAR(self, instr);
@@ -536,6 +634,13 @@ ir_instr* ir_instr_new(ir_block* owner, int op)
 MEM_VEC_FUNCTIONS(ir_instr, ir_phi_entry_t, phi)
 MEM_VEC_FUNCTIONS(ir_instr, ir_value*, params)
 
+static void ir_instr_delete_quick(ir_instr *self)
+{
+    MEM_VECTOR_CLEAR(self, phi);
+    MEM_VECTOR_CLEAR(self, params);
+    mem_d(self);
+}
+
 void ir_instr_delete(ir_instr *self)
 {
     size_t i;
@@ -628,7 +733,11 @@ ir_value* ir_value_var(const char *name, int storetype, int vtype)
     self->context.file = "<@no context>";
     self->context.line = 0;
     self->name = NULL;
-    ir_value_set_name(self, name);
+    if (name && !ir_value_set_name(self, name)) {
+        irerror(self->context, "out of memory");
+        mem_d(self);
+        return NULL;
+    }
 
     memset(&self->constval, 0, sizeof(self->constval));
     memset(&self->code,     0, sizeof(self->code));
@@ -721,11 +830,12 @@ void ir_value_delete(ir_value* self)
     mem_d(self);
 }
 
-void ir_value_set_name(ir_value *self, const char *name)
+bool ir_value_set_name(ir_value *self, const char *name)
 {
     if (self->name)
         mem_d((void*)self->name);
     self->name = util_strdup(name);
+    return !!self->name;
 }
 
 bool ir_value_set_float(ir_value *self, float f)
@@ -917,14 +1027,14 @@ bool ir_value_life_merge_into(ir_value *self, const ir_value *other)
             }
 
             if (life->start <  entry->start &&
-                life->end   >= entry->start)
+                life->end+1 >= entry->start)
             {
                 /* starts earlier and overlaps */
                 entry->start = life->start;
             }
 
-            if (life->end     >  entry->end &&
-                life->start-1 <= entry->end)
+            if (life->end   >  entry->end &&
+                life->start <= entry->end+1)
             {
                 /* ends later and overlaps */
                 entry->end = life->end;
@@ -1483,6 +1593,7 @@ ir_value* ir_block_create_load_from_ent(ir_block *self, const char *label, ir_va
         case TYPE_INTEGER: op = INSTR_LOAD_I;   break;
 #endif
         default:
+            irerror(self->context, "invalid type for ir_block_create_load_from_ent: %s", type_name[outype]);
             return NULL;
     }
 
@@ -1499,6 +1610,7 @@ ir_value* ir_block_create_add(ir_block *self,
     if (l == r) {
         switch (l) {
             default:
+                irerror(self->context, "invalid type for ir_block_create_add: %s", type_name[l]);
                 return NULL;
             case TYPE_FLOAT:
                 op = INSTR_ADD_F;
@@ -1520,7 +1632,10 @@ ir_value* ir_block_create_add(ir_block *self,
             op = INSTR_ADD_IF;
         else
 #endif
+        {
+            irerror(self->context, "invalid type for ir_block_create_add: %s", type_name[l]);
             return NULL;
+        }
     }
     return ir_block_create_binop(self, label, op, left, right);
 }
@@ -1536,6 +1651,7 @@ ir_value* ir_block_create_sub(ir_block *self,
 
         switch (l) {
             default:
+                irerror(self->context, "invalid type for ir_block_create_sub: %s", type_name[l]);
                 return NULL;
             case TYPE_FLOAT:
                 op = INSTR_SUB_F;
@@ -1557,7 +1673,10 @@ ir_value* ir_block_create_sub(ir_block *self,
             op = INSTR_SUB_IF;
         else
 #endif
+        {
+            irerror(self->context, "invalid type for ir_block_create_sub: %s", type_name[l]);
             return NULL;
+        }
     }
     return ir_block_create_binop(self, label, op, left, right);
 }
@@ -1573,6 +1692,7 @@ ir_value* ir_block_create_mul(ir_block *self,
 
         switch (l) {
             default:
+                irerror(self->context, "invalid type for ir_block_create_mul: %s", type_name[l]);
                 return NULL;
             case TYPE_FLOAT:
                 op = INSTR_MUL_F;
@@ -1601,8 +1721,10 @@ ir_value* ir_block_create_mul(ir_block *self,
         else if ( (l == TYPE_INTEGER && r == TYPE_FLOAT) )
             op = INSTR_MUL_IF;
 #endif
-        else
+        else {
+            irerror(self->context, "invalid type for ir_block_create_mul: %s", type_name[l]);
             return NULL;
+        }
     }
     return ir_block_create_binop(self, label, op, left, right);
 }
@@ -1618,6 +1740,7 @@ ir_value* ir_block_create_div(ir_block *self,
 
         switch (l) {
             default:
+                irerror(self->context, "invalid type for ir_block_create_div: %s", type_name[l]);
                 return NULL;
             case TYPE_FLOAT:
                 op = INSTR_DIV_F;
@@ -1638,7 +1761,10 @@ ir_value* ir_block_create_div(ir_block *self,
             op = INSTR_DIV_IF;
         else
 #endif
+        {
+            irerror(self->context, "invalid type for ir_block_create_div: %s", type_name[l]);
             return NULL;
+        }
     }
     return ir_block_create_binop(self, label, op, left, right);
 }
@@ -2109,6 +2235,19 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
         /* See which operands are read and write operands */
         ir_op_read_write(instr->opcode, &read, &write);
 
+        if (instr->opcode == INSTR_MUL_VF)
+        {
+            /* the float source will get an additional lifetime */
+            tempbool = ir_value_life_merge(instr->_ops[2], instr->eid+1);
+            *changed = *changed || tempbool;
+        }
+        else if (instr->opcode == INSTR_MUL_FV)
+        {
+            /* the float source will get an additional lifetime */
+            tempbool = ir_value_life_merge(instr->_ops[1], instr->eid+1);
+            *changed = *changed || tempbool;
+        }
+
         /* Go through the 3 main operands */
         for (o = 0; o < 3; ++o)
         {
@@ -2156,7 +2295,7 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
                      * since this function is run multiple times.
                      */
                     /* For now: debug info: */
-                    /* fprintf(stderr, "Value only written %s\n", value->name); */
+                    /* con_err( "Value only written %s\n", value->name); */
                     tempbool = ir_value_life_merge(value, instr->eid);
                     *changed = *changed || tempbool;
                     /*
@@ -2171,7 +2310,7 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
                     tempbool = ir_value_life_merge(value, instr->eid);
                     /*
                     if (tempbool)
-                        fprintf(stderr, "value added id %s %i\n", value->name, (int)instr->eid);
+                        con_err( "value added id %s %i\n", value->name, (int)instr->eid);
                     */
                     *changed = *changed || tempbool;
                     /* Then remove */
@@ -2182,7 +2321,7 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
         }
         /* (A) */
         tempbool = ir_block_living_add_instr(self, instr->eid);
-        /*fprintf(stderr, "living added values\n");*/
+        /*con_err( "living added values\n");*/
         *changed = *changed || tempbool;
 
     }
@@ -2216,7 +2355,7 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
  *
  * Breaking conventions is annoying...
  */
-static bool ir_builder_gen_global(ir_builder *self, ir_value *global);
+static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool islocal);
 
 static bool gen_global_field(ir_value *global)
 {
@@ -2427,7 +2566,10 @@ tailcall:
                 stmt.opcode = INSTR_STORE_F;
                 stmt.o3.u1 = 0;
 
-                stmt.opcode = type_store_instr[param->vtype];
+                if (param->vtype == TYPE_FIELD)
+                    stmt.opcode = field_store_instr[param->fieldtype];
+                else
+                    stmt.opcode = type_store_instr[param->vtype];
                 stmt.o1.u1 = ir_value_code_addr(param);
                 stmt.o2.u1 = OFS_PARM0 + 3 * p;
                 if (code_statements_add(stmt) < 0)
@@ -2446,7 +2588,10 @@ tailcall:
             if (retvalue && retvalue->store != store_return && retvalue->life_count)
             {
                 /* not to be kept in OFS_RETURN */
-                stmt.opcode = type_store_instr[retvalue->vtype];
+                if (retvalue->vtype == TYPE_FIELD)
+                    stmt.opcode = field_store_instr[retvalue->vtype];
+                else
+                    stmt.opcode = type_store_instr[retvalue->vtype];
                 stmt.o1.u1 = OFS_RETURN;
                 stmt.o2.u1 = ir_value_code_addr(retvalue);
                 stmt.o3.u1 = 0;
@@ -2529,6 +2674,27 @@ static bool gen_function_code(ir_function *self)
     return true;
 }
 
+static qcint ir_builder_filestring(ir_builder *ir, const char *filename)
+{
+    /* NOTE: filename pointers are copied, we never strdup them,
+     * thus we can use pointer-comparison to find the string.
+     */
+    size_t i;
+    qcint  str;
+
+    for (i = 0; i < ir->filenames_count; ++i) {
+        if (ir->filenames[i] == filename)
+            return ir->filestrings[i];
+    }
+
+    str = code_genstring(filename);
+    if (!ir_builder_filenames_add(ir, filename))
+        return 0;
+    if (!ir_builder_filestrings_add(ir, str))
+        ir->filenames_count--;
+    return str;
+}
+
 static bool gen_global_function(ir_builder *ir, ir_value *global)
 {
     prog_section_function fun;
@@ -2546,7 +2712,7 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
     irfun = global->constval.vfunc;
 
     fun.name    = global->code.name;
-    fun.file    = code_cachedstring(global->context.file);
+    fun.file    = ir_builder_filestring(ir, global->context.file);
     fun.profile = 0; /* always 0 */
     fun.nargs   = irfun->params_count;
 
@@ -2558,11 +2724,10 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
     }
 
     fun.firstlocal = code_globals_elements;
-    fun.locals     = irfun->allocated_locals + irfun->locals_count;
 
     local_var_end = fun.firstlocal;
     for (i = 0; i < irfun->locals_count; ++i) {
-        if (!ir_builder_gen_global(ir, irfun->locals[i])) {
+        if (!ir_builder_gen_global(ir, irfun->locals[i], true)) {
             irerror(irfun->locals[i]->context, "Failed to generate local %s", irfun->locals[i]->name);
             return false;
         }
@@ -2583,6 +2748,8 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
         code_globals_add(0);
     }
 
+    fun.locals = code_globals_elements - fun.firstlocal;
+
     if (irfun->builtin)
         fun.entry = irfun->builtin;
     else {
@@ -2623,7 +2790,7 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global)
     return true;
 }
 
-static bool ir_builder_gen_global(ir_builder *self, ir_value *global)
+static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool islocal)
 {
     size_t           i;
     int32_t         *iptr;
@@ -2631,7 +2798,18 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global)
 
     def.type   = global->vtype;
     def.offset = code_globals_elements;
-    def.name   = global->code.name       = code_genstring(global->name);
+
+    if (global->name) {
+        if (global->name[0] == '#') {
+            if (!self->str_immediate)
+                self->str_immediate = code_genstring("IMMEDIATE");
+            def.name = global->code.name = self->str_immediate;
+        }
+        else
+            def.name = global->code.name = code_genstring(global->name);
+    }
+    else
+        def.name   = 0;
 
     switch (global->vtype)
     {
@@ -2651,8 +2829,9 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global)
         /* 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
+         * fteqcc creates data for end_sys_* - of size 1, so let's do the same
          */
-        ir_value_code_setaddr(global, def.offset);
+        ir_value_code_setaddr(global, code_globals_add(0));
         /* Add the def */
         if (code_defs_add(def) < 0)
             return false;
@@ -2669,35 +2848,37 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global)
         /* fall through */
     case TYPE_FLOAT:
     {
-        if (code_defs_add(def) < 0)
-            return false;
-
         if (global->isconst) {
-            iptr = (int32_t*)&global->constval.vfloat;
+            iptr = (int32_t*)&global->constval.ivec[0];
             ir_value_code_setaddr(global, code_globals_add(*iptr));
-        } else
+        } else {
             ir_value_code_setaddr(global, code_globals_add(0));
+            if (!islocal)
+                def.type |= DEF_SAVEGLOBAL;
+        }
+        if (code_defs_add(def) < 0)
+            return false;
 
         return global->code.globaladdr >= 0;
     }
     case TYPE_STRING:
     {
-        if (code_defs_add(def) < 0)
-            return false;
         if (global->isconst)
-            ir_value_code_setaddr(global, code_globals_add(code_cachedstring(global->constval.vstring)));
-        else
+            ir_value_code_setaddr(global, code_globals_add(code_genstring(global->constval.vstring)));
+        else {
             ir_value_code_setaddr(global, code_globals_add(0));
+            if (!islocal)
+                def.type |= DEF_SAVEGLOBAL;
+        }
+        if (code_defs_add(def) < 0)
+            return false;
         return global->code.globaladdr >= 0;
     }
     case TYPE_VECTOR:
     {
         size_t d;
-        if (code_defs_add(def) < 0)
-            return false;
-
         if (global->isconst) {
-            iptr = (int32_t*)&global->constval.vvec;
+            iptr = (int32_t*)&global->constval.ivec[0];
             ir_value_code_setaddr(global, code_globals_add(iptr[0]));
             if (global->code.globaladdr < 0)
                 return false;
@@ -2715,20 +2896,30 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global)
                 if (code_globals_add(0) < 0)
                     return false;
             }
+            if (!islocal)
+                def.type |= DEF_SAVEGLOBAL;
         }
+
+        if (code_defs_add(def) < 0)
+            return false;
         return global->code.globaladdr >= 0;
     }
     case TYPE_FUNCTION:
-        if (code_defs_add(def) < 0)
-            return false;
         if (!global->isconst) {
             ir_value_code_setaddr(global, code_globals_add(0));
-            return global->code.globaladdr >= 0;
+            if (global->code.globaladdr < 0)
+                return false;
         } else {
             ir_value_code_setaddr(global, code_globals_elements);
             code_globals_add(code_functions_elements);
-            return gen_global_function(self, global);
+            if (!gen_global_function(self, global))
+                return false;
+            if (!islocal)
+                def.type |= DEF_SAVEGLOBAL;
         }
+        if (code_defs_add(def) < 0)
+            return false;
+        return true;
     case TYPE_VARIANT:
         /* assume biggest type */
             ir_value_code_setaddr(global, code_globals_add(0));
@@ -2820,7 +3011,7 @@ bool ir_builder_generate(ir_builder *self, const char *filename)
 
     for (i = 0; i < self->globals_count; ++i)
     {
-        if (!ir_builder_gen_global(self, self->globals[i])) {
+        if (!ir_builder_gen_global(self, self->globals[i], false)) {
             return false;
         }
     }
@@ -2865,8 +3056,6 @@ bool ir_builder_generate(ir_builder *self, const char *filename)
 
 #ifdef WIN32
 # define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE)
-#else
-# define strncat strncat
 #endif
 
 const char *qc_opname(int op)
@@ -2926,7 +3115,7 @@ void ir_function_dump(ir_function *f, char *ind,
     for (i = 0; i < f->locals_count; ++i) {
         size_t l;
         ir_value *v = f->locals[i];
-        oprintf("%s\t%s: ", ind, v->name);
+        oprintf("%s\t%s: unique ", ind, v->name);
         for (l = 0; l < v->life_count; ++l) {
             oprintf("[%i,%i] ", v->life[l].start, v->life[l].end);
         }
@@ -2935,7 +3124,7 @@ void ir_function_dump(ir_function *f, char *ind,
     for (i = 0; i < f->values_count; ++i) {
         size_t l;
         ir_value *v = f->values[i];
-        oprintf("%s\t%s: (%i)", ind, v->name, (int)v->life_count);
+        oprintf("%s\t%s: @%i ", ind, v->name, (int)v->code.local);
         for (l = 0; l < v->life_count; ++l) {
             oprintf("[%i,%i] ", v->life[l].start, v->life[l].end);
         }
@@ -3080,10 +3269,10 @@ void ir_value_dump(ir_value* v, int (*oprintf)(const char*, ...))
     }
 }
 
-void ir_value_dump_life(ir_value *self, int (*oprintf)(const char*,...))
+void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...))
 {
     size_t i;
-    oprintf("Life of %s:\n", self->name);
+    oprintf("Life of %12s:", self->name);
     for (i = 0; i < self->life_count; ++i)
     {
         oprintf(" + [%i, %i]\n", self->life[i].start, self->life[i].end);