]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merge branch 'cooking' into threading
authorDale Weiler <killfieldengine@gmail.com>
Fri, 26 Jul 2013 14:00:03 +0000 (14:00 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Fri, 26 Jul 2013 14:00:03 +0000 (14:00 +0000)
Conflicts:
Makefile

1  2 
gmqcc.h
include.mk
main.c
opts.c
opts.def
parser.c

diff --combined gmqcc.h
index 9a3e432f16b9677f818aef9c57a9e5e593a8e0b5,202ae14f9da3ddf7a431ff65e3e95fc146bbb48e..55349f68c452fa7c4c727956c0638c97ff196490
+++ b/gmqcc.h
@@@ -106,11 -106,6 +106,6 @@@ GMQCC_IND_STRING(GMQCC_VERSION_PATCH) 
  #   define GMQCC_WARN
  #   define GMQCC_USED
  #endif /*! defined(__GNUC__) || defined (__CLANG__) */
- /*
-  * This is a hack to silent clang regarding empty
-  * body if statements.
-  */
- #define GMQCC_SUPPRESS_EMPTY_BODY do { } while (0)
  
  /*
   * Inline is not supported in < C90, however some compilers
  #   include <fcntl.h>
  
      struct dirent {
-         long d_ino;
+         long               d_ino;
          unsigned short     d_reclen;
          unsigned short     d_namlen;
          char               d_name[FILENAME_MAX];
@@@ -305,6 -300,25 +300,25 @@@ void *stat_mem_allocate  (size_t, size_
  /*===================================================================*/
  /*=========================== util.c ================================*/
  /*===================================================================*/
+ /*
+  * Microsoft implements against the spec versions of ctype.h. Which
+  * means what ever the current set locale is will render the actual
+  * results of say isalpha('A') wrong for what ever retarded locale
+  * is used. Simalerly these are also implemented inefficently on
+  * some toolchains and end up becoming actual library calls. Perhaps
+  * this is why tools like yacc provide their own? Regardless implementing
+  * these as functions is equally as silly, the call overhead is not
+  * justified when this could happen on every character from an input
+  * stream. We provide our own as macros for absolute inlinability.
+  */
+ #define util_isalpha(a) ((((unsigned)(a)|32)-'a') < 26)
+ #define util_isdigit(a) (((unsigned)(a)-'0') < 10)
+ #define util_islower(a) (((unsigned)(a)-'a') < 26)
+ #define util_isupper(a) (((unsigned)(a)-'A') < 26)
+ #define util_isprint(a) (((unsigned)(a)-0x20) < 0x5F)
+ #define util_isspace(a) (((a) >= 9 && (a) <= 13) || (a) == ' ')
  bool  util_filexists     (const char *);
  bool  util_strupper      (const char *);
  bool  util_strdigit      (const char *);
@@@ -422,11 -436,6 +436,11 @@@ void          util_htrm  (hash_table_t 
  void         *util_htget (hash_table_t *ht, const char *key);
  void         *util_htgeth(hash_table_t *ht, const char *key, size_t hash);
  
 +/*===================================================================*/
 +/*=========================== thread.c ==============================*/
 +/*===================================================================*/
 +GMQCC_INLINE uint32_t util_atomic_xadd32(volatile uint32_t *x, uint32_t v);
 +
  /*===================================================================*/
  /*============================ file.c ===============================*/
  /*===================================================================*/
@@@ -726,7 -735,7 +740,7 @@@ typedef struct 
   * code_genstrin       -- generates string for code
   * code_alloc_field    -- allocated a field
   * code_push_statement -- keeps statements and linenumbers together
-  * code_pop_statement  -- keeps statements and linenumbers together 
+  * code_pop_statement  -- keeps statements and linenumbers together
   */
  bool      code_write         (code_t *, const char *filename, const char *lno);
  GMQCC_WARN
diff --combined include.mk
index 0000000000000000000000000000000000000000,d8ee6fe33d49577dff0cffa71c475005e31bb313..f83f3c097cab458a264d76fbaf102bb69eb959ef
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,109 +1,109 @@@
 -LIBS    += -lm
+ # default directories and paths
+ DESTDIR :=
+ OPTIONAL:=
+ PREFIX  := /usr/local
+ BINDIR  := $(PREFIX)/bin
+ DATADIR := $(PREFIX)/share
+ MANDIR  := $(DATADIR)/man
+ # compiler
+ CC      ?= clang
+ # linker flags and optional additional libraries if required
+ LDFLAGS +=
 -OBJ_C = main.o lexer.o parser.o fs.o stat.o util.o code.o ast.o ir.o conout.o ftepp.o opts.o utf8.o correct.o
++LIBS    += -lm -lpthread
+ #objects
++OBJ_C = main.o lexer.o parser.o thread.o fs.o stat.o util.o code.o ast.o ir.o conout.o ftepp.o opts.o utf8.o correct.o
+ OBJ_P = util.o fs.o conout.o opts.o pak.o stat.o
+ OBJ_T = test.o util.o conout.o fs.o stat.o
+ OBJ_X = exec-standalone.o util.o conout.o fs.o stat.o
+ #gource flags
+ GOURCEFLAGS =                 \
+     --date-format "%d %B, %Y" \
+     --seconds-per-day 0.01    \
+     --auto-skip-seconds 1     \
+     --title "GMQCC"           \
+     --key                     \
+     --camera-mode overview    \
+     --highlight-all-users     \
+     --file-idle-time 0        \
+     --hide progress,mouse     \
+     --stop-at-end             \
+     --max-files 99999999999   \
+     --max-file-lag 0.000001   \
+     --bloom-multiplier 1.3    \
+     --logo doc/html/gmqcc.png \
+     -1280x720
+ #ffmpeg flags for gource
+ FFMPEGFLAGS=                  \
+     -y                        \
+     -r 60                     \
+     -f image2pipe             \
+     -vcodec ppm               \
+     -i -                      \
+     -vcodec libx264           \
+     -preset ultrafast         \
+     -crf 1                    \
+     -threads 0                \
+     -bf 0
+ #splint flags
+ SPLINTFLAGS =                 \
+     -redef                    \
+     -noeffect                 \
+     -nullderef                \
+     -usedef                   \
+     -type                     \
+     -mustfreeonly             \
+     -nullstate                \
+     -varuse                   \
+     -mustfreefresh            \
+     -compdestroy              \
+     -compmempass              \
+     -nullpass                 \
+     -onlytrans                \
+     -predboolint              \
+     -boolops                  \
+     -incondefs                \
+     -macroredef               \
+     -retvalint                \
+     -nullret                  \
+     -predboolothers           \
+     -globstate                \
+     -dependenttrans           \
+     -branchstate              \
+     -compdef                  \
+     -temptrans                \
+     -usereleased              \
+     -warnposix                \
+     +charindex                \
+     -kepttrans                \
+     -unqualifiedtrans         \
+     +matchanyintegral         \
+     +voidabstract             \
+     -nullassign               \
+     -unrecog                  \
+     -casebreak                \
+     -retvalbool               \
+     -retvalother              \
+     -mayaliasunique           \
+     -realcompare              \
+     -observertrans            \
+     -abstract                 \
+     -statictrans              \
+     -castfcnptr
+     
+ #always the right rule
+ default: all
+ #uninstall rule
+ uninstall:
+       rm -f $(DESTDIR)$(BINDIR)/gmqcc
+       rm -f $(DESTDIR)$(BINDIR)/qcvm
+       rm -f $(DESTDIR)$(BINDIR)/gmqpak
+       rm -f $(DESTDIR)$(MANDIR)/man1/doc/gmqcc.1
+       rm -f $(DESTDIR)$(MANDIR)/man1/doc/qcvm.1
+       rm -f $(DESTDIR)$(MANDIR)/man1/doc/gmqpak.1
diff --combined main.c
index 03da18f54bbbd1994c5e0e09930f4c7fbcdc907b,feb8aa3886ab128491f9e9af401beea9775e7ba2..042103b50c1bc7565d292a6a7eab5b286489e053
--- 1/main.c
--- 2/main.c
+++ b/main.c
@@@ -25,8 -25,6 +25,6 @@@
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
- #include <ctype.h>
  
  #include "gmqcc.h"
  #include "lexer.h"
@@@ -218,11 -216,6 +216,11 @@@ static bool options_parse(int argc, cha
                  OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, NULL, 0);
                  continue;
              }
 +            if (options_long_gcc("jobs", &argc, &argv, &argarg)) {
 +
 +                OPTS_OPTION_U32 (OPTION_J) = strtol(argarg, NULL, 0);
 +                continue;
 +            }
              if (options_long_gcc("redirout", &argc, &argv, &redirout)) {
                  con_change(redirout, redirerr);
                  continue;
                          con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n");
                          return false;
                      }
-                     if (isdigit(argarg[0])) {
+                     if (util_isdigit(argarg[0])) {
                          uint32_t val = (uint32_t)strtol(argarg, NULL, 10);
                          OPTS_OPTION_U32(OPTION_O) = val;
                          opts_setoptimlevel(val);
                      vec_push(items, item);
                      break;
  
 +                case 'j':
 +                    if (!options_witharg(&argc, &argv, &argarg)) {
 +                        con_out("option -j requires a parameter\n");
 +                        return false;
 +                    }
 +                    OPTS_OPTION_U32 (OPTION_J) = strtol(argarg, NULL, 0);
 +                    break;
 +
                  case '-':
                      if (!argv[0][2]) {
                          /* anything following -- is considered a non-option argument */
@@@ -551,9 -536,9 +549,9 @@@ static bool progs_nextline(char **out, 
          return false;
  
      /* start at first non-blank */
-     for (start = line; isspace(*start); ++start) {}
+     for (start = line; util_isspace(*start); ++start) {}
      /* end at the first non-blank */
-     for (end = start;  *end && !isspace(*end);  ++end)   {}
+     for (end = start; *end && !util_isspace(*end);  ++end)   {}
  
      *out = line;
      /* move the actual filename to the beginning */
@@@ -712,9 -697,6 +710,6 @@@ int main(int argc, char **argv) 
          mem_d(line);
      }
  
-     if (retval)
-         goto cleanup;
      if (vec_size(items)) {
          if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
              !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
@@@ -806,7 -788,7 +801,7 @@@ cleanup
      vec_free(ppems);
  
      if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
-         parser_cleanup(parser);
+         if(parser) parser_cleanup(parser);
      if (opts_output_free)
          mem_d(OPTS_OPTION_STR(OPTION_OUTPUT));
      if (operators_free)
diff --combined opts.c
index 97a3597df54e9c4c62e1305b33777b4521184f09,286b1686b66c72a496f656664ffaba3e6497b75a..ceeb7a419fcdb1cde0bbb26b1b9c6809e216b87d
--- 1/opts.c
--- 2/opts.c
+++ b/opts.c
@@@ -23,7 -23,6 +23,6 @@@
   */
  #include <string.h>
  #include <stdlib.h>
- #include <ctype.h>
  
  #include "gmqcc.h"
  
@@@ -103,7 -102,6 +102,7 @@@ void opts_init(const char *output, int 
      OPTS_OPTION_U32(OPTION_STANDARD)       = standard;
      OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize;
      OPTS_OPTION_U16(OPTION_MEMDUMPCOLS)    = 16;
 +    OPTS_OPTION_U32(OPTION_J)              = 2;
  }
  
  static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
@@@ -164,13 -162,13 +163,13 @@@ void opts_setoptimlevel(unsigned int le
   */
  static char *opts_ini_rstrip(char *s) {
      char *p = s + strlen(s);
-     while(p > s && isspace(*--p))
+     while(p > s && util_isspace(*--p))
          *p = '\0';
      return s;
  }
  
  static char *opts_ini_lskip(const char *s) {
-     while (*s && isspace(*s))
+     while (*s && util_isspace(*s))
          s++;
      return (char*)s;
  }
  static char *opts_ini_next(const char *s, char c) {
      bool last = false;
      while (*s && *s != c && !(last && *s == ';'))
-         last = !!isspace(*s), s++;
+         last = !!util_isspace(*s), s++;
  
      return (char*)s;
  }
@@@ -349,7 -347,7 +348,7 @@@ void opts_ini_init(const char *file) 
       *  gmqcc.ini
       *  gmqcc.cfg
       */
-     char       *error;
+     char       *error = NULL;
      size_t     line;
      FILE       *ini;
  
diff --combined opts.def
index 7efe5317b0d94a391f92171ba064035cecf6be12,61226ae3719a52d7bbc2dbe3d2890b86322f26bb..78f0ef99f3ddd9f9c65f52c34c4b4e32eebdbd2b
+++ b/opts.def
@@@ -52,6 -52,7 +52,7 @@@
      GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS)
      GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS)
      GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS)
+     GMQCC_DEFINE_FLAG(UNSAFE_VARARGS)
  #endif
  
  /* warning flags */
@@@ -89,6 -90,7 +90,7 @@@
      GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES)
      GMQCC_DEFINE_FLAG(DEPRECATED)
      GMQCC_DEFINE_FLAG(PARENTHESIS)
+     GMQCC_DEFINE_FLAG(UNSAFE_TYPES)
      GMQCC_DEFINE_FLAG(BREAKDEF)
  #endif
  
      GMQCC_DEFINE_FLAG(MAX_ARRAY_SIZE)
      GMQCC_DEFINE_FLAG(ADD_INFO)
      GMQCC_DEFINE_FLAG(CORRECTION)
 +    GMQCC_DEFINE_FLAG(J)
      GMQCC_DEFINE_FLAG(STATISTICS)
  #endif
  
diff --combined parser.c
index 8bf4086cf6b85f6879a8c51afa28361ece84a9e2,c9f093fdc2e2e7d2177ff55e743956607c01ba88..e5dcf4dbac495875c1514818e7c4ff44bc7f9936
+++ b/parser.c
@@@ -23,7 -23,6 +23,7 @@@
   */
  #include <string.h>
  #include <math.h>
 +#include <pthread.h>
  
  #include "gmqcc.h"
  #include "lexer.h"
@@@ -50,6 -49,7 +50,7 @@@ typedef struct parser_s 
      size_t         translated;
  
      ht ht_imm_string;
+     ht ht_imm_string_dotranslate;
  
      /* must be deleted first, they reference immediates and values */
      ast_value    **accessors;
  
      /* collected information */
      size_t     max_param_count;
-     /* code generator */
-     code_t     *code;
  } parser_t;
  
  static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
@@@ -261,17 -258,15 +259,15 @@@ static char *parser_strdup(const char *
  
  static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate)
  {
-     size_t hash = util_hthash(parser->ht_imm_string, str);
+     ht ht_string = (dotranslate)
+         ? parser->ht_imm_string_dotranslate
+         : parser->ht_imm_string;
      ast_value *out;
-     if ( (out = (ast_value*)util_htgeth(parser->ht_imm_string, str, hash)) ) {
-         if (dotranslate && out->name[0] == '#') {
-             char name[32];
-             util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
-             ast_value_set_name(out, name);
-             out->expression.flags |= AST_FLAG_INCLUDE_DEF;
-         }
+     size_t     hash = util_hthash(ht_string, str);
+     if ((out = (ast_value*)util_htgeth(ht_string, str, hash)))
          return out;
-     }
      /*
      for (i = 0; i < vec_size(parser->imm_string); ++i) {
          if (!strcmp(parser->imm_string[i]->constval.vstring, str))
          out->expression.flags |= AST_FLAG_INCLUDE_DEF;
      } else
          out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING);
      out->cvq      = CV_CONST;
      out->hasvalue = true;
      out->isimm    = true;
      out->constval.vstring = parser_strdup(str);
      vec_push(parser->imm_string, out);
-     util_htseth(parser->ht_imm_string, str, hash, out);
+     util_htseth(ht_string, str, hash, out);
      return out;
  }
  
@@@ -953,7 -950,7 +951,7 @@@ static bool parser_sy_apply_operator(pa
              if (exprs[1]->vtype != TYPE_FLOAT) {
                  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: cannot divide tyeps %s and %s", ty1, ty2);
+                 compile_error(ctx, "invalid types used in expression: cannot divide types %s and %s", ty1, ty2);
                  return false;
              }
              if (exprs[0]->vtype == TYPE_FLOAT) {
              {
                  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: cannot divide tyeps %s and %s", ty1, ty2);
+                 compile_error(ctx, "invalid types used in expression: cannot divide types %s and %s", ty1, ty2);
                  return false;
              }
              break;
                      exprs[0], exprs[1]);
              break;
          case opid1('^'):
-             compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^");
-             return false;
+             /*
+              * 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))
+             {
+                 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 the first expression is float, the following will be too
+              * since scalar ^ vector is not allowed.
+              */
+             if (exprs[0]->vtype == TYPE_FLOAT) {
+                 if(CanConstFold(exprs[0], exprs[1])) {
+                     out = (ast_expression*)parser_const_float(parser, (float)((qcint)(ConstF(0)) ^ ((qcint)(ConstF(1)))));
+                 } else {
+                     ast_binary *expr = ast_binary_new(
+                         ctx,
+                         INSTR_SUB_F,
+                         (ast_expression*)parser_const_float_neg1(parser),
+                         (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
+                         );
+                 }
+             } else {
+                 /*
+                  * The first is a vector: vector is allowed to xor 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
+                      * vectors components in question.
+                      */
+                     if (CanConstFold(exprs[0], exprs[1])) {
+                         out = (ast_expression*)parser_const_vector_f(
+                             parser,
+                             (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstV(1).x))),
+                             (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstV(1).y))),
+                             (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstV(1).z)))
+                         );
+                     } else {
+                         compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
+                         return false;
+                     }
+                 } else {
+                     /*
+                      * Xor all the values of the vector components against the
+                      * scalar in question.
+                      */
+                     if (CanConstFold(exprs[0], exprs[1])) {
+                         out = (ast_expression*)parser_const_vector_f(
+                             parser,
+                             (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstF(1)))),
+                             (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstF(1)))),
+                             (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstF(1))))
+                         );
+                     } else {
+                         compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
+                         return false;
+                     }
+                 }
+             }
+                 
+             break;
  
          case opid2('<','<'):
          case opid2('>','>'):
              } else {
                  ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
  
-                 eq->refs = (ast_binary_ref)false; /* references nothing */
+                 eq->refs = AST_REF_NONE;
  
                      /* if (lt) { */
                  out = (ast_expression*)ast_ternary_new(ctx,
@@@ -1569,7 -1686,7 +1687,7 @@@ static bool parser_close_call(parser_t 
      for (i = 0; i < paramcount; ++i)
          vec_push(call->params, sy->out[fid+1 + i].out);
      vec_shrinkby(sy->out, paramcount);
-     (void)!ast_call_check_types(call);
+     (void)!ast_call_check_types(call, parser->function->vtype->expression.varparam);
      if (parser->max_param_count < paramcount)
          parser->max_param_count = paramcount;
  
@@@ -1706,8 -1823,12 +1824,12 @@@ static ast_expression* parse_vararg_do(
      ast_expression *idx, *out;
      ast_value      *typevar;
      ast_value      *funtype = parser->function->vtype;
+     lex_ctx         ctx     = parser_ctx(parser);
  
-     lex_ctx ctx = parser_ctx(parser);
+     if (!parser->function->varargs) {
+         parseerror(parser, "function has no variable argument list");
+         return NULL;
+     }
  
      if (!parser_next(parser) || parser->tok != '(') {
          parseerror(parser, "expected parameter index and type in parenthesis");
          return NULL;
  
      if (parser->tok != ',') {
-         ast_unref(idx);
-         parseerror(parser, "expected comma after parameter index");
-         return NULL;
+         if (parser->tok != ')') {
+             ast_unref(idx);
+             parseerror(parser, "expected comma after parameter index");
+             return NULL;
+         }
+         /* vararg piping: ...(start) */
+         out = (ast_expression*)ast_argpipe_new(ctx, idx);
+         return out;
      }
  
      if (!parser_next(parser) || (parser->tok != TOKEN_IDENT && parser->tok != TOKEN_TYPENAME)) {
          return NULL;
      }
  
- #if 0
-     if (!parser_next(parser)) {
-         ast_unref(idx);
-         ast_delete(typevar);
-         parseerror(parser, "parse error after vararg");
-         return NULL;
-     }
- #endif
-     if (!parser->function->varargs) {
-         ast_unref(idx);
-         ast_delete(typevar);
-         parseerror(parser, "function has no variable argument list");
-         return NULL;
-     }
      if (funtype->expression.varparam &&
          !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam))
      {
@@@ -1928,7 -2038,7 +2039,7 @@@ static bool parse_sya_operand(parser_t 
                   * it in the predef table.  And diagnose it better :)
                   */
                  if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) {
-                     parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
+                     parseerror(parser, "unexpected identifier: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
                      return false;
                  }
  
                          correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser));
                          if (strcmp(correct, parser_tokval(parser))) {
                              break;
-                         } else if (correct) {
+                         } else  {
                              mem_d(correct);
                              correct = NULL;
                          }
                      correct_free(&corr);
  
                      if (correct) {
-                         parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
+                         parseerror(parser, "unexpected identifier: %s (did you mean %s?)", parser_tokval(parser), correct);
                          mem_d(correct);
                          return false;
                      }
                  }
-                 parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
+                 parseerror(parser, "unexpected identifier: %s", parser_tokval(parser));
                  return false;
              }
          }
@@@ -2007,7 -2117,7 +2118,7 @@@ static ast_expression* parse_expression
      while (true)
      {
          if (parser->tok == TOKEN_TYPENAME) {
-             parseerror(parser, "unexpected typename");
+             parseerror(parser, "unexpected typename `%s`", parser_tokval(parser));
              goto onerr;
          }
  
@@@ -2405,7 -2515,8 +2516,8 @@@ static ast_expression* process_conditio
      }
  
      unary = (ast_unary*)cond;
-     while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
+     /* ast_istype dereferences cond, should test here for safety */
+     while (cond && ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
      {
          cond = unary->operand;
          unary->operand = NULL;
@@@ -2645,7 -2756,12 +2757,12 @@@ static bool parse_dowhile(parser_t *par
      if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) {
          parseerror(parser, "internal error: label stack corrupted");
          rv = false;
-         ast_delete(*out);
+         /*
+          * Test for NULL otherwise ast_delete dereferences null pointer
+          * and boom.
+          */
+         if (*out)
+             ast_delete(*out);
          *out = NULL;
      }
      else {
@@@ -3513,7 -3629,8 +3630,8 @@@ static bool parse_goto(parser_t *parser
          if (!(expression = parse_expression(parser, false, true)) ||
              !(*out = parse_goto_computed(parser, &expression))) {
              parseerror(parser, "invalid goto expression");
-             ast_unref(expression);
+             if(expression)
+                 ast_unref(expression);
              return false;
          }
  
@@@ -4342,7 -4459,7 +4460,7 @@@ static bool parse_function_body(parser_
      }
  
      vec_push(func->blocks, block);
-     
  
      parser->function = old;
      if (!parser_leaveblock(parser))
@@@ -4920,32 -5037,44 +5038,44 @@@ static ast_value *parse_arraysize(parse
          return NULL;
      }
  
-     cexp = parse_expression_leave(parser, true, false, false);
+     if (parser->tok != ']') {
+         cexp = parse_expression_leave(parser, true, false, false);
  
-     if (!cexp || !ast_istype(cexp, ast_value)) {
-         if (cexp)
-             ast_unref(cexp);
-         ast_delete(var);
-         parseerror(parser, "expected array-size as constant positive integer");
-         return NULL;
+         if (!cexp || !ast_istype(cexp, ast_value)) {
+             if (cexp)
+                 ast_unref(cexp);
+             ast_delete(var);
+             parseerror(parser, "expected array-size as constant positive integer");
+             return NULL;
+         }
+         cval = (ast_value*)cexp;
+     }
+     else {
+         cexp = NULL;
+         cval = NULL;
      }
-     cval = (ast_value*)cexp;
  
      tmp = ast_value_new(ctx, "<type[]>", TYPE_ARRAY);
      tmp->expression.next = (ast_expression*)var;
      var = tmp;
  
-     if (cval->expression.vtype == TYPE_INTEGER)
-         tmp->expression.count = cval->constval.vint;
-     else if (cval->expression.vtype == TYPE_FLOAT)
-         tmp->expression.count = cval->constval.vfloat;
-     else {
+     if (cval) {
+         if (cval->expression.vtype == TYPE_INTEGER)
+             tmp->expression.count = cval->constval.vint;
+         else if (cval->expression.vtype == TYPE_FLOAT)
+             tmp->expression.count = cval->constval.vfloat;
+         else {
+             ast_unref(cexp);
+             ast_delete(var);
+             parseerror(parser, "array-size must be a positive integer constant");
+             return NULL;
+         }
          ast_unref(cexp);
-         ast_delete(var);
-         parseerror(parser, "array-size must be a positive integer constant");
-         return NULL;
+     } else {
+         var->expression.count = -1;
+         var->expression.flags |= AST_FLAG_ARRAY_INIT;
      }
-     ast_unref(cexp);
  
      if (parser->tok != ']') {
          ast_delete(var);
@@@ -5195,6 -5324,75 +5325,75 @@@ static bool parser_check_qualifiers(par
      return true;
  }
  
+ static bool create_array_accessors(parser_t *parser, ast_value *var)
+ {
+     char name[1024];
+     util_snprintf(name, sizeof(name), "%s##SET", var->name);
+     if (!parser_create_array_setter(parser, var, name))
+         return false;
+     util_snprintf(name, sizeof(name), "%s##GET", var->name);
+     if (!parser_create_array_getter(parser, var, var->expression.next, name))
+         return false;
+     return true;
+ }
+ static bool parse_array(parser_t *parser, ast_value *array)
+ {
+     size_t i;
+     if (array->initlist) {
+         parseerror(parser, "array already initialized elsewhere");
+         return false;
+     }
+     if (!parser_next(parser)) {
+         parseerror(parser, "parse error in array initializer");
+         return false;
+     }
+     i = 0;
+     while (parser->tok != '}') {
+         ast_value *v = (ast_value*)parse_expression_leave(parser, true, false, false);
+         if (!v)
+             return false;
+         if (!ast_istype(v, ast_value) || !v->hasvalue || v->cvq != CV_CONST) {
+             ast_unref(v);
+             parseerror(parser, "initializing element must be a compile time constant");
+             return false;
+         }
+         vec_push(array->initlist, v->constval);
+         if (v->expression.vtype == TYPE_STRING) {
+             array->initlist[i].vstring = util_strdupe(array->initlist[i].vstring);
+             ++i;
+         }
+         ast_unref(v);
+         if (parser->tok == '}')
+             break;
+         if (parser->tok != ',' || !parser_next(parser)) {
+             parseerror(parser, "expected comma or '}' in element list");
+             return false;
+         }
+     }
+     if (!parser_next(parser) || parser->tok != ';') {
+         parseerror(parser, "expected semicolon after initializer, got %s");
+         return false;
+     }
+     /*
+     if (!parser_next(parser)) {
+         parseerror(parser, "parse error after initializer");
+         return false;
+     }
+     */
+     if (array->expression.flags & AST_FLAG_ARRAY_INIT) {
+         if (array->expression.count != (size_t)-1) {
+             parseerror(parser, "array `%s' has already been initialized with %u elements",
+                        array->name, (unsigned)array->expression.count);
+         }
+         array->expression.count = vec_size(array->initlist);
+         if (!create_array_accessors(parser, array))
+             return false;
+     }
+     return true;
+ }
  static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring)
  {
      ast_value *var;
           * deal with arrays
           */
          if (var->expression.vtype == TYPE_ARRAY) {
-             char name[1024];
-             util_snprintf(name, sizeof(name), "%s##SET", var->name);
-             if (!parser_create_array_setter(parser, var, name))
-                 goto cleanup;
-             util_snprintf(name, sizeof(name), "%s##GET", var->name);
-             if (!parser_create_array_getter(parser, var, var->expression.next, name))
-                 goto cleanup;
+             if (var->expression.count != (size_t)-1) {
+                 if (!create_array_accessors(parser, var))
+                     goto cleanup;
+             }
          }
          else if (!localblock && !nofields &&
                   var->expression.vtype == TYPE_FIELD &&
@@@ -5814,11 -6009,10 +6010,10 @@@ skipvar
                  parseerror(parser, "TODO: initializers for local arrays");
                  break;
              }
-             /*
- static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
- */
-             parseerror(parser, "TODO: initializing global arrays is not supported yet!");
-             break;
+             var->hasvalue = true;
+             if (!parse_array(parser, var))
+                 break;
          }
          else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '['))
          {
@@@ -6023,7 -6217,7 +6218,7 @@@ static uint16_t progdefs_crc_both(uint1
      return old;
  }
  
- static void generate_checksum(parser_t *parser)
+ static void generate_checksum(parser_t *parser, ir_builder *ir)
  {
      uint16_t   crc = 0xFFFF;
      size_t     i;
          crc = progdefs_crc_both(crc, ";\n");
      }
      crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
-     parser->code->crc = crc;
+     ir->code->crc = crc;
  }
  
  parser_t *parser_create()
  
      memset(parser, 0, sizeof(*parser));
  
-     if (!(parser->code = code_init())) {
-         mem_d(parser);
-         return NULL;
-     }
      for (i = 0; i < operator_count; ++i) {
          if (operators[i].id == opid1('=')) {
              parser->assign_op = operators+i;
      parser->aliases = util_htnew(PARSER_HT_SIZE);
  
      parser->ht_imm_string = util_htnew(512);
+     parser->ht_imm_string_dotranslate = util_htnew(512);
  
      /* corrector */
      vec_push(parser->correct_variables, correct_trie_new());
      vec_push(parser->correct_variables_score, NULL);
  
-     empty_ctx.file = "<internal>";
-     empty_ctx.line = 0;
+     empty_ctx.file   = "<internal>";
+     empty_ctx.line   = 0;
+     empty_ctx.column = 0;
      parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL);
      parser->nil->cvq = CV_CONST;
      if (OPTS_FLAG(UNTYPED_NIL))
@@@ -6160,7 -6350,7 +6351,7 @@@ static bool parser_compile(parser_t *pa
          {
              if (!parser_global_statement(parser)) {
                  if (parser->tok == TOKEN_EOF)
-                     parseerror(parser, "unexpected eof");
+                     parseerror(parser, "unexpected end of file");
                  else if (compile_errors)
                      parseerror(parser, "there have been errors, bailing out");
                  lex_close(parser->lex);
@@@ -6234,6 -6424,7 +6425,7 @@@ static void parser_remove_ast(parser_t 
      vec_free(parser->functions);
      vec_free(parser->imm_vector);
      vec_free(parser->imm_string);
+     util_htdel(parser->ht_imm_string_dotranslate);
      util_htdel(parser->ht_imm_string);
      vec_free(parser->imm_float);
      vec_free(parser->globals);
      vec_free(parser->correct_variables);
      vec_free(parser->correct_variables_score);
  
      for (i = 0; i < vec_size(parser->_typedefs); ++i)
          ast_delete(parser->_typedefs[i]);
      vec_free(parser->_typedefs);
      ast_value_delete(parser->const_vec[0]);
      ast_value_delete(parser->const_vec[1]);
      ast_value_delete(parser->const_vec[2]);
+     
+     if (parser->reserved_version)
+         ast_value_delete(parser->reserved_version);
  
      util_htdel(parser->aliases);
      intrin_intrinsics_destroy(parser);
  void parser_cleanup(parser_t *parser)
  {
      parser_remove_ast(parser);
-     code_cleanup(parser->code);
      mem_d(parser);
  }
  
 +static void function_finalize_worker_do(ast_function **list, size_t count, volatile uint32_t *counter, bool *failure)
 +{
 +    do {
 +        uint32_t idx = util_atomic_xadd32(counter, 1);
 +        if (idx >= count) {
 +            *counter = count;
 +            return;
 +        }
 +        if (!ir_function_finalize(list[idx]->ir_func)) {
 +            con_out("failed to finalize function %s\n", list[idx]->name);
 +            *failure = true;
 +            return;
 +        }
 +    } while (true);
 +}
 +
 +typedef struct {
 +    ast_function     **list;
 +    size_t             count;
 +    volatile uint32_t *counter;
 +    bool              *failure;
 +} function_finalize_worker_data;
 +static void* function_finalize_worker(void *_d) {
 +    function_finalize_worker_data *d = (function_finalize_worker_data*)_d;
 +    function_finalize_worker_do(d->list, d->count, d->counter, d->failure);
 +    return NULL;
 +}
 +
 +static bool function_finalize_all_threaded(ast_function **list, size_t count)
 +{
 +    volatile uint32_t counter = 0;
 +    function_finalize_worker_data wd;
 +
 +    size_t poolsize = OPTS_OPTION_U32(OPTION_J);
 +    bool   failure = false;
 +    size_t i, j;
 +
 +    pthread_t *threads;
 +
 +    if (!list || !count)
 +        return true;
 +
 +    wd.list    = list;
 +    wd.count   = count;
 +    wd.counter = &counter;
 +    wd.failure = &failure;
 +
 +    threads = (pthread_t*)mem_a(poolsize*sizeof(pthread_t));
 +
 +    for (i = 0; i < poolsize; ++i) {
 +        if (pthread_create(threads+i, NULL, &function_finalize_worker, &wd) != 0)
 +            break;
 +    }
 +    if (i < poolsize) {
 +        con_out("failed to spawn worker threads\n");
 +        for (j = 0; j <= i; ++j)
 +            pthread_join(threads[j], NULL);
 +        return false;
 +    }
 +    for (i = 0; i < poolsize; ++i)
 +        pthread_join(threads[i], NULL);
 +    return !failure;
 +}
 +
 +bool function_finalize_all(ast_function **list, size_t count)
 +{
 +    size_t i;
 +    if (OPTS_OPTION_U32(OPTION_J) > 1)
 +        return function_finalize_all_threaded(list, count);
 +
 +    for (i = 0; i < count; ++i) {
 +        if (!ir_function_finalize(list[i]->ir_func)) {
 +            con_out("failed to finalize function %s\n", list[i]->name);
 +            return false;
 +        }
 +    }
 +    return true;
 +}
 +
  bool parser_finish(parser_t *parser, const char *output)
  {
      size_t i;
          }
      }
  
-     generate_checksum(parser);
+     generate_checksum(parser, ir);
      if (OPTS_OPTION_BOOL(OPTION_DUMP))
          ir_builder_dump(ir, con_out);
 +
 +#if 0
      for (i = 0; i < vec_size(parser->functions); ++i) {
          if (!ir_function_finalize(parser->functions[i]->ir_func)) {
              con_out("failed to finalize function %s\n", parser->functions[i]->name);
              return false;
          }
      }
 +
 +#else
 +    if (!function_finalize_all(parser->functions, vec_size(parser->functions))) {
 +        ir_builder_delete(ir);
 +        return false;
 +    }
 +#endif
 +
      parser_remove_ast(parser);
  
      if (compile_Werrors) {