]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merge branch 'master' into cooking
authorWolfgang Bumiller <wry.git@bumiller.com>
Sun, 26 Jan 2014 11:38:00 +0000 (12:38 +0100)
committerWolfgang Bumiller <wry.git@bumiller.com>
Sun, 26 Jan 2014 11:38:00 +0000 (12:38 +0100)
40 files changed:
BSDmakefile
LICENSE
Makefile
TODO
ansi.c
ast.c
ast.h
code.c
conout.c
correct.c
distro/Makefile
doc/gmqcc.1
exec.c
fold.c
fs.c
ftepp.c
gmqcc.h
gmqcc.ini.example
hash.c [new file with mode: 0644]
include.mk
intrin.c
ir.c
ir.h
lexer.c
lexer.h
main.c
msvc.c
opts.c
opts.def
pak.c
parser.c
parser.h
platform.h
stat.c
test.c
tests/last.qc [new file with mode: 0644]
tests/last.tmpl [new file with mode: 0644]
tests/last2.tmpl [new file with mode: 0644]
utf8.c
util.c

index 7ebc66eecd56a9076e76381c65838e68a7275fc7..49385413b3e126023284533bb66adc0940c8e71a 100644 (file)
@@ -11,7 +11,7 @@ GITINFO  :=
     GITINFO != git describe --always
 .endif
 
-CFLAGS   +=  -Wall -Wextra -Werror -Wstrict-aliasing
+CFLAGS   +=  -Wall -Wextra -Werror -Wstrict-aliasing -Wno-attributes
 
 .if $(CC) == clang
     CFLAGS +=   -Weverything\
@@ -115,6 +115,7 @@ exec.o: gmqcc.h opts.def
 fold.o: ast.h ir.h gmqcc.h opts.def parser.h lexer.h
 fs.o: gmqcc.h opts.def platform.h
 ftepp.o: gmqcc.h opts.def lexer.h
+hash.o: gmqcc.h opts.def
 intrin.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h
 ir.o: gmqcc.h opts.def ir.h
 lexer.o: gmqcc.h opts.def lexer.h
diff --git a/LICENSE b/LICENSE
index 3fd91811023d99d637608a954cfc574e7f294c5b..03eeedb63309ce7766e2f6962b9873de67c26f40 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (C) 2012, 2013
+Copyright (C) 2012, 2013, 2014
     Dale Weiler
     Wolfgang Bumiller
 
index 12151070539d078e9b932d61b0ae232834d078f1..3097368802bca32cf61b1835241ca93bb2d5cbad 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ UNAME  ?= $(shell uname)
 CYGWIN  = $(findstring CYGWIN,  $(UNAME))
 MINGW   = $(findstring MINGW32, $(UNAME))
 
-CFLAGS += -Wall -Wextra -Werror -Wstrict-aliasing
+CFLAGS += -Wall -Wextra -Werror -Wstrict-aliasing -Wno-attributes
 #turn on tons of warnings if clang is present
 # but also turn off the STUPID ONES
 ifeq ($(CC), clang)
@@ -143,6 +143,7 @@ install-doc:
 
 ansi.o: platform.h gmqcc.h opts.def
 util.o: gmqcc.h opts.def platform.h
+hash.o: gmqcc.h opts.def
 stat.o: gmqcc.h opts.def
 fs.o: gmqcc.h opts.def platform.h
 opts.o: gmqcc.h opts.def
diff --git a/TODO b/TODO
index 8652ed228b22b6dbe660b14f53fb12a1fb661ad7..356c4b8e386c856d4a627cd058a7f07d279d9f57 100644 (file)
--- a/TODO
+++ b/TODO
@@ -15,11 +15,6 @@ Optimizations:
             are determined via underlying equivalence, opposed to lexically identical
             expressions (CSE).
 
-        Spare Conditional Constant Propagation:
-            Simultaneously remove dead code and propagates constants. This is
-            not the same as individual dead code elimination and constant propagation
-            passes.  This is multipass.
-
     The following are optimizations that can be implemented before the
     transformation into a binary (code generator).
 
@@ -44,16 +39,6 @@ Language Features:
             - No virtuals / pure virtuals
             - Essentially "C structs but with operators" :)
 
-    Arrays:
-        They're currently implemented, but support in the engine
-        plus implicit bounds checks (and ability to turn the bounds
-        checking off)
-
-    Exceptions:
-        I feel like all languages suck at implementing this.  This would
-        require support from the engine, but it would help catch bugs. We
-        could make it fast using a neat method of "frame pointers".
-
     Overloaded Functions:
         Ability to make individual functions with the same name, but take
         different amount of arguments or type of arguments.
@@ -65,80 +50,15 @@ Language Features:
         become "default", otherwise if two arguments are specified then
         the "default" string is overrode with what ever the user passes.
 
-    Character Type:
-        A char type would be nice to have.  Essentially implemented as a
-        string, we can both "get" and "set" indices inside strings with
-        the help of builtin functions.
-
-        {
-            string foo = "test";
-            foo[0] = 'r';
-
-            print("it's time to ", foo);
-        }
-
-    Array Accessor With C-Semantics:
-        Also the ability to use them as array accessors:
-
-        {
-            float hugearray['Z'];
-
-            hugearray['a'] = 100.0f;
-        }
-
-        Keep existing "pointer-like" semantics as well.  In C arrays
-        simple work as pointers, a[1] -> *(a+1), or 1[a] -> *(1+a)
-        so we should allow both forms of syntax.  As well as operand
-        reversal.
-
-        {
-            float h['Z'];
-            *(h+'a') = 100;
-            *('a'+h) = 'a'[h];
-        }
-
-    FTEQCC Inline Assembly:
-        This is still up for debate, mainly because a) it's syntax is
-        just utter crap. b) If we do an assembler, it should be nice.
-        we could provide a -std=fteqcc for the assembler itself :P
-        just like the compiler; although I think that's just insane.
-
-        Please see Assembler below.
-
     Namespaces:
         There is already a ticket open on this. They'd work just like C++
         identically even.
 
-Standalone QCVM:
-    The following are QCVM additions:
-
-        Proper ASM disassembly:
-            Proper disassembly of compiled .dat files. Annotated if possible
-            when -g (is used during compilation)
-
-        Debugging:
-            A step-through debugger -d (with separate compilation as well)
-            Called -> qcdb  Optionally alias to qcvm -d :)
-
-            We should be able to see the assembly and source it matches to
-            and the state of OFS_* and calls.
-
 Testsuite:
     The following are things we'd like to see added to the testsuite
     in the distant future:
 
-    Multithreading:
-        Chances are when we start adding more and more tests, executing
-        them individually will be midly slow (even if that's a whole minute)
-        It would be nice to add a -j paramater to allow multiple threads to
-        be used and so we can execute many tests in parallel.
-
     Interface:
         Ability to select individual tests, or set parameters manually
         opposed to using the static task-template files. (A method to
         override them rather).
-
-
-Assembler:
-    Possibly support for a future assembler for QCASM.  But we're not
-    entirely sure if it makes sense.
diff --git a/ansi.c b/ansi.c
index 3cf4508a31f604363b4609b714ccc2506dbc938d..029508a95ca2e84254412e4a3046a02829910878 100644 (file)
--- a/ansi.c
+++ b/ansi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/ast.c b/ast.c
index 3c70a73721e49e726c1e010b3bbc0b77e94bc225..d7e3d7a198cae3df58a8e2c0e0d87372e534e074 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
@@ -113,8 +113,10 @@ static void ast_expression_init(ast_expression *self,
     self->outr     = NULL;
     self->params   = NULL;
     self->count    = 0;
-    self->flags    = 0;
     self->varparam = NULL;
+    self->flags    = 0;
+    if (OPTS_OPTION_BOOL(OPTION_COVERAGE))
+        self->flags |= AST_FLAG_BLOCK_COVERAGE;
 }
 
 static void ast_expression_delete(ast_expression *self)
@@ -441,6 +443,24 @@ ast_binary* ast_binary_new(lex_ctx_t ctx, int op,
     ast_instantiate(ast_binary, ctx, ast_binary_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_binary_codegen);
 
+    if (ast_istype(right, ast_unary) && OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) {
+        ast_unary      *unary  = ((ast_unary*)right);
+        ast_expression *normal = unary->operand;
+
+        /* make a-(-b) => a + b */
+        if (unary->op == VINSTR_NEG_F || unary->op == VINSTR_NEG_V) {
+            if (op == INSTR_SUB_F) {
+                op = INSTR_ADD_F;
+                right = normal;
+                ++opts_optimizationcount[OPTIM_PEEPHOLE];
+            } else if (op == INSTR_SUB_V) {
+                op = INSTR_ADD_V;
+                right = normal;
+                ++opts_optimizationcount[OPTIM_PEEPHOLE];
+            }
+        }
+    }
+
     self->op = op;
     self->left = left;
     self->right = right;
@@ -1177,7 +1197,6 @@ ast_function* ast_function_new(lex_ctx_t ctx, const char *name, ast_value *vtype
     if (!vtype) {
         compile_error(ast_ctx(self), "internal error: ast_function_new condition 0");
         goto cleanup;
-    } else if (vtype->hasvalue || vtype->expression.vtype != TYPE_FUNCTION) {
     } else if (vtype->hasvalue || vtype->expression.vtype != TYPE_FUNCTION) {
         compile_error(ast_ctx(self), "internal error: ast_function_new condition %i %i type=%i (probably 2 bodies?)",
                  (int)!vtype,
@@ -1207,6 +1226,9 @@ ast_function* ast_function_new(lex_ctx_t ctx, const char *name, ast_value *vtype
     self->fixedparams      = NULL;
     self->return_value     = NULL;
 
+    self->static_names     = NULL;
+    self->static_count     = 0;
+
     return self;
 
 cleanup:
@@ -1228,6 +1250,9 @@ void ast_function_delete(ast_function *self)
          */
         ast_unref(self->vtype);
     }
+    for (i = 0; i < vec_size(self->static_names); ++i)
+        mem_d(self->static_names[i]);
+    vec_free(self->static_names);
     for (i = 0; i < vec_size(self->blocks); ++i)
         ast_delete(self->blocks[i]);
     vec_free(self->blocks);
@@ -1407,6 +1432,8 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
             self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
         if (self->expression.flags & AST_FLAG_ERASEABLE)
             self->ir_v->flags |= IR_FLAG_ERASEABLE;
+        if (self->expression.flags & AST_FLAG_BLOCK_COVERAGE)
+            func->flags |= IR_FLAG_BLOCK_COVERAGE;
         /* The function is filled later on ast_function_codegen... */
         return true;
     }
diff --git a/ast.h b/ast.h
index 52858ac7f132ce14372fe7e92570593e11d2071f..7cc7a6c89b13975549307d2a269c3fde4bb4ec85 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
@@ -56,25 +56,37 @@ typedef struct ast_goto_s        ast_goto;
 typedef struct ast_argpipe_s     ast_argpipe;
 
 enum {
-    AST_FLAG_VARIADIC      = 1 << 0,
-    AST_FLAG_NORETURN      = 1 << 1,
-    AST_FLAG_INLINE        = 1 << 2,
-    AST_FLAG_INITIALIZED   = 1 << 3,
-    AST_FLAG_DEPRECATED    = 1 << 4,
-    AST_FLAG_INCLUDE_DEF   = 1 << 5,
-    AST_FLAG_IS_VARARG     = 1 << 6,
-    AST_FLAG_ALIAS         = 1 << 7,
-    AST_FLAG_ERASEABLE     = 1 << 8,
-    AST_FLAG_ACCUMULATE    = 1 << 9,
-
-    /*
-     * An array declared as []
+    AST_FLAG_VARIADIC       = 1 << 0,
+    AST_FLAG_NORETURN       = 1 << 1,
+    AST_FLAG_INLINE         = 1 << 2,
+    AST_FLAG_INITIALIZED    = 1 << 3,
+    AST_FLAG_DEPRECATED     = 1 << 4,
+    AST_FLAG_INCLUDE_DEF    = 1 << 5,
+    AST_FLAG_IS_VARARG      = 1 << 6,
+    AST_FLAG_ALIAS          = 1 << 7,
+    AST_FLAG_ERASEABLE      = 1 << 8,
+    AST_FLAG_ACCUMULATE     = 1 << 9,
+
+    /* An array declared as []
      * so that the size is taken from the initializer
      */
-    AST_FLAG_ARRAY_INIT    = 1 << 10,
+    AST_FLAG_ARRAY_INIT     = 1 << 10,
+
+    AST_FLAG_FINAL_DECL     = 1 << 11,
+
+    /* Several coverage options
+     * AST_FLAG_COVERAGE means there was an explicit [[coverage]] attribute,
+     * which will overwrite the default set via the commandline switches.
+     * BLOCK_COVERAGE inserts coverage() calls into every basic block.
+     * In the future there might be more options like tracking variable access
+     * by creating get/set wrapper functions.
+     */
+    AST_FLAG_COVERAGE       = 1 << 12,
+    AST_FLAG_BLOCK_COVERAGE = 1 << 13,
 
     AST_FLAG_LAST,
-    AST_FLAG_TYPE_MASK     = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN)
+    AST_FLAG_TYPE_MASK      = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN),
+    AST_FLAG_COVERAGE_MASK  = (AST_FLAG_BLOCK_COVERAGE)
 };
 
 enum {
@@ -625,6 +637,14 @@ struct ast_function_s
 
     int builtin;
 
+    /* list of used-up names for statics without the count suffix */
+    char       **static_names;
+    /* number of static variables, by convention this includes the
+     * ones without the count-suffix - remember this when dealing
+     * with savegames. uint instead of size_t as %zu in printf is
+     * C99, so no windows support. */
+    unsigned int static_count;
+
     ir_function *ir_func;
     ir_block    *curblock;
     ir_block    **breakblocks;
diff --git a/code.c b/code.c
index e4c2fc13fdaa553fb4a15d5dbb90e51c3558073c..0c3e5a36611c3a62ed4f3326570ac96e4737bcf2 100644 (file)
--- a/code.c
+++ b/code.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *     Wolfgang Bumiller
  *
index 1d34b164842a54731ba3499642f3dba875094e61..746275d3cf870d2372b35212cb8b55649a5f3571 100644 (file)
--- a/conout.c
+++ b/conout.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
index f501817acc86582223c524df96a202a614a08bc8..074277eecb48b6cd7aa5222c52b2373338314e8c 100644 (file)
--- a/correct.c
+++ b/correct.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *     Wolfgang Bumiller
  *
index 49555b895f43ebf8b4f4b1f5367b5224bea420bb..384b1fac61516a40f94b15a6b1239e565532016e 100644 (file)
@@ -1,7 +1,7 @@
 DROPBOX := dropbox_uploader.sh
 UNAME   := $(shell uname -m)
 DOWNLOAD:= ../doc/html/download.c
-BRANCH  := $(shell git branch | sed -n -e 's/^\* \(.*\)/\1/p')
+BRANCH  := $(shell git rev-parse --abbrev-ref HEAD)
 ifneq ($(shell uname -m), x86_64)
     $(error Cannot build packages without an x86_64 capable CPU)
 endif
index 557995e02fb6c628011fd583f87c5349a7b8ddf5..b5d39eab4578f22ed95fade87d0162def05e98e1 100644 (file)
@@ -338,6 +338,12 @@ for QuakeWorld to compile it needs to be treated as a warning
 instead, as such this warning only works when \-std=qcc.
 .It Fl W Ns Cm directive-inmacro
 Warn about the use of preprocessor directives inside macros.
+.It Fl W Ns Cm builtins
+When using a function that is not explicitly defined, the compiler
+will search its intrinsics table for something that matches that
+function name by appending "__builtin_" to it. This behaviour may
+be unexpected, so enabling this will produce a diagnostic when
+such a function is resolved to a builtin.
 .El
 .Sh COMPILE FLAGS
 .Bl -tag -width Ds
@@ -391,6 +397,26 @@ only the first component will be 0, while the other two will become
 the first to of the global return value. This behavior is odd and
 relying on it should be discouraged, and thus is not supported by
 gmqcc.
+.It Fl f Ns Cm ftepp-mathdefs
+Enable math constant definitions. This only works in combination
+with \'\-fftepp' and is currently not included by '\-std=fteqcc'.
+The following macros will be added:
+.Bd -literal -offset indent
+M_E
+M_LOG2E
+M_LOG10E
+M_LN2
+M_LN10
+M_PI
+M_PI_2
+M_PI_4
+M_1_PI
+M_2_PI
+M_2_SQRTPI
+M_SQRT2
+M_SQRT1_2
+M_TAU
+.Ed
 .It Fl f Ns Cm relaxed-switch
 Allow switch cases to use non constant variables.
 .It Fl f Ns Cm short-logic
diff --git a/exec.c b/exec.c
index 43d1980ed1a12895b516a2c29cbf776def1ff710..d4e75b82e8b580394a8d0300ead3705b1364486f 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
diff --git a/fold.c b/fold.c
index 0128dece56cf030039463c7c0619c482a3ac455e..d90887da2ae2012ab17f55fe193a7b70d8866e9b 100644 (file)
--- a/fold.c
+++ b/fold.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
@@ -59,9 +59,9 @@ static GMQCC_INLINE vec3_t vec3_add(vec3_t a, vec3_t b) {
 
 static GMQCC_INLINE vec3_t vec3_sub(vec3_t a, vec3_t b) {
     vec3_t out;
-    out.x = a.x + b.x;
-    out.y = a.y + b.y;
-    out.z = a.z + b.z;
+    out.x = a.x - b.x;
+    out.y = a.y - b.y;
+    out.z = a.z - b.z;
     return out;
 }
 
@@ -123,9 +123,9 @@ static GMQCC_INLINE vec3_t vec3_xorvf(vec3_t a, qcfloat_t b) {
 
 static GMQCC_INLINE vec3_t vec3_not(vec3_t a) {
     vec3_t out;
-    out.x = (qcfloat_t)(~((qcint_t)a.x));
-    out.y = (qcfloat_t)(~((qcint_t)a.y));
-    out.z = (qcfloat_t)(~((qcint_t)a.z));
+    out.x = -1-a.x;
+    out.y = -1-a.y;
+    out.z = -1-a.z;
     return out;
 }
 
@@ -160,7 +160,7 @@ static GMQCC_INLINE qcfloat_t vec3_notf(vec3_t a) {
 }
 
 static GMQCC_INLINE bool vec3_pbool(vec3_t a) {
-    return (a.x && a.y && a.z);
+    return (a.x || a.y || a.z);
 }
 
 static GMQCC_INLINE vec3_t vec3_cross(vec3_t a, vec3_t b) {
@@ -230,6 +230,7 @@ fold_t *fold_init(parser_t *parser) {
     (void)fold_constgen_float (fold,  0.0f);
     (void)fold_constgen_float (fold,  1.0f);
     (void)fold_constgen_float (fold, -1.0f);
+    (void)fold_constgen_float (fold,  2.0f);
 
     (void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.0f));
     (void)fold_constgen_vector(fold, vec3_create(-1.0f, -1.0f, -1.0f));
@@ -537,11 +538,10 @@ static GMQCC_INLINE ast_expression *fold_op_xor(fold_t *fold, ast_value *a, ast_
         if (fold_can_2(a, b))
             return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) ^ ((qcint_t)fold_immvalue_float(b))));
     } else {
-        if (isvector(b)) {
-            if (fold_can_2(a, b))
+        if (fold_can_2(a, b)) {
+            if (isvector(b))
                 return fold_constgen_vector(fold, vec3_xor(fold_immvalue_vector(a), fold_immvalue_vector(b)));
-        } else {
-            if (fold_can_2(a, b))
+            else
                 return fold_constgen_vector(fold, vec3_xorvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
         }
     }
@@ -550,21 +550,23 @@ static GMQCC_INLINE ast_expression *fold_op_xor(fold_t *fold, ast_value *a, ast_
 
 static GMQCC_INLINE ast_expression *fold_op_lshift(fold_t *fold, ast_value *a, ast_value *b) {
     if (fold_can_2(a, b) && isfloats(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)((qcuint_t)(fold_immvalue_float(a)) << (qcuint_t)(fold_immvalue_float(b))));
+        return fold_constgen_float(fold, (qcfloat_t)(((qcuint_t)(floorf(fold_immvalue_float(a) * powf(2.0f, fold_immvalue_float(b))))) & 0xFFFFFF));
     return NULL;
 }
 
 static GMQCC_INLINE ast_expression *fold_op_rshift(fold_t *fold, ast_value *a, ast_value *b) {
     if (fold_can_2(a, b) && isfloats(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)((qcuint_t)(fold_immvalue_float(a)) >> (qcuint_t)(fold_immvalue_float(b))));
+        return fold_constgen_float(fold, (qcfloat_t)(((qcuint_t)(floorf(fold_immvalue_float(a) / powf(2.0f, fold_immvalue_float(b))))) & 0xFFFFFF));
     return NULL;
 }
 
 static GMQCC_INLINE ast_expression *fold_op_andor(fold_t *fold, ast_value *a, ast_value *b, float expr) {
     if (fold_can_2(a, b)) {
         if (OPTS_FLAG(PERL_LOGIC)) {
-            if (fold_immediate_true(fold, a))
-                return (ast_expression*)b;
+            if (expr)
+                return (fold_immediate_true(fold, a)) ? (ast_expression*)a : (ast_expression*)b;
+            else
+                return (fold_immediate_true(fold, a)) ? (ast_expression*)b : (ast_expression*)a;
         } else {
             return fold_constgen_float (
                 fold,
@@ -604,11 +606,15 @@ static GMQCC_INLINE ast_expression *fold_op_lteqgt(fold_t *fold, ast_value *a, a
 
 static GMQCC_INLINE ast_expression *fold_op_cmp(fold_t *fold, ast_value *a, ast_value *b, bool ne) {
     if (fold_can_2(a, b)) {
-        return fold_constgen_float(
-                    fold,
-                    (ne) ? (fold_immvalue_float(a) != fold_immvalue_float(b))
-                         : (fold_immvalue_float(a) == fold_immvalue_float(b))
-                );
+        if (isfloat(a) && isfloat(b)) {
+            float la = fold_immvalue_float(a);
+            float lb = fold_immvalue_float(b);
+            return (ast_expression*)fold->imm_float[!(ne ? la == lb : la != lb)];
+        } if (isvector(a) && isvector(b)) {
+            vec3_t la = fold_immvalue_vector(a);
+            vec3_t lb = fold_immvalue_vector(b);
+            return (ast_expression*)fold->imm_float[!(ne ? vec3_cmp(la, lb) : !vec3_cmp(la, lb))];
+        }
     }
     return NULL;
 }
@@ -616,7 +622,7 @@ static GMQCC_INLINE ast_expression *fold_op_cmp(fold_t *fold, ast_value *a, ast_
 static GMQCC_INLINE ast_expression *fold_op_bnot(fold_t *fold, ast_value *a) {
     if (isfloat(a)) {
         if (fold_can_1(a))
-            return fold_constgen_float(fold, ~((qcint_t)fold_immvalue_float(a)));
+            return fold_constgen_float(fold, -1-fold_immvalue_float(a));
     } else {
         if (isvector(a)) {
             if (fold_can_1(a))
@@ -701,46 +707,69 @@ ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **op
  * folding, primarly: individual functions for each intrinsics to fold,
  * and a generic selection function.
  */
+static GMQCC_INLINE ast_expression *fold_intrin_isfinite(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, isfinite(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_isinf(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, isinf(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_isnan(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, isnan(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_isnormal(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, isnormal(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_signbit(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, signbit(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intirn_acosh(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, acoshf(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_asinh(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, asinhf(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_atanh(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, atanhf(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_exp(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, expf(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_exp2(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, exp2f(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_expm1(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, expm1f(fold_immvalue_float(a)));
+}
 static GMQCC_INLINE ast_expression *fold_intrin_mod(fold_t *fold, ast_value *lhs, ast_value *rhs) {
-    return fold_constgen_float(
-                fold,
-                fmodf(
-                    fold_immvalue_float(lhs),
-                    fold_immvalue_float(rhs)
-                )
-            );
+    return fold_constgen_float(fold, fmodf(fold_immvalue_float(lhs), fold_immvalue_float(rhs)));
 }
-
 static GMQCC_INLINE ast_expression *fold_intrin_pow(fold_t *fold, ast_value *lhs, ast_value *rhs) {
-    return fold_constgen_float(
-                fold,
-                powf(
-                    fold_immvalue_float(lhs),
-                    fold_immvalue_float(rhs)
-                )
-            );
-}
-
-static GMQCC_INLINE ast_expression *fold_intrin_exp(fold_t *fold, ast_value *value) {
-    return fold_constgen_float(fold, exp(fold_immvalue_float(value)));
+    return fold_constgen_float(fold, powf(fold_immvalue_float(lhs), fold_immvalue_float(rhs)));
 }
-
-static GMQCC_INLINE ast_expression *fold_intrin_isnan(fold_t *fold, ast_value *value) {
-    return fold_constgen_float(fold, isnan(fold_immvalue_float(value)) != 0.0f);
+static GMQCC_INLINE ast_expression *fold_intrin_fabs(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, fabsf(fold_immvalue_float(a)));
 }
 
-static GMQCC_INLINE ast_expression *fold_intrin_fabs(fold_t *fold, ast_value *value) {
-    return fold_constgen_float(fold, fabs(fold_immvalue_float(value)));
-}
 
 ast_expression *fold_intrin(fold_t *fold, const char *intrin, ast_expression **arg) {
     ast_expression *ret = NULL;
-
-    if (!strcmp(intrin, "mod"))   ret = fold_intrin_mod  (fold, (ast_value*)arg[0], (ast_value*)arg[1]);
-    if (!strcmp(intrin, "pow"))   ret = fold_intrin_pow  (fold, (ast_value*)arg[0], (ast_value*)arg[1]);
-    if (!strcmp(intrin, "exp"))   ret = fold_intrin_exp  (fold, (ast_value*)arg[0]);
-    if (!strcmp(intrin, "isnan")) ret = fold_intrin_isnan(fold, (ast_value*)arg[0]);
-    if (!strcmp(intrin, "fabs"))  ret = fold_intrin_fabs (fold, (ast_value*)arg[0]);
+    ast_value      *a   = (ast_value*)arg[0];
+    ast_value      *b   = (ast_value*)arg[1];
+
+    if (!strcmp(intrin, "isfinite")) ret = fold_intrin_isfinite(fold, a);
+    if (!strcmp(intrin, "isinf"))    ret = fold_intrin_isinf(fold, a);
+    if (!strcmp(intrin, "isnan"))    ret = fold_intrin_isnan(fold, a);
+    if (!strcmp(intrin, "isnormal")) ret = fold_intrin_isnormal(fold, a);
+    if (!strcmp(intrin, "signbit"))  ret = fold_intrin_signbit(fold, a);
+    if (!strcmp(intrin, "acosh"))    ret = fold_intirn_acosh(fold, a);
+    if (!strcmp(intrin, "asinh"))    ret = fold_intrin_asinh(fold, a);
+    if (!strcmp(intrin, "atanh"))    ret = fold_intrin_atanh(fold, a);
+    if (!strcmp(intrin, "exp"))      ret = fold_intrin_exp(fold, a);
+    if (!strcmp(intrin, "exp2"))     ret = fold_intrin_exp2(fold, a);
+    if (!strcmp(intrin, "expm1"))    ret = fold_intrin_expm1(fold, a);
+    if (!strcmp(intrin, "mod"))      ret = fold_intrin_mod(fold, a, b);
+    if (!strcmp(intrin, "pow"))      ret = fold_intrin_pow(fold, a, b);
+    if (!strcmp(intrin, "fabs"))     ret = fold_intrin_fabs(fold, a);
 
     if (ret)
         ++opts_optimizationcount[OPTIM_CONST_FOLD];
diff --git a/fs.c b/fs.c
index 55e4edced16558d92eb1a69a098826b465247d96..17319de834de41888d5099e21b3ef9b4fa57b26e 100644 (file)
--- a/fs.c
+++ b/fs.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/ftepp.c b/ftepp.c
index a34b58c0e152d6077e669ac12d1215cfbf643389..44c1ebb1b5392bde5f5842a0a1d23ff8955d23f4 100644 (file)
--- a/ftepp.c
+++ b/ftepp.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
@@ -498,10 +498,29 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
     return true;
 }
 
+static const char *ftepp_math_constants[][2] = {
+    { "M_E",        "2.7182818284590452354"  }, /* e          */
+    { "M_LOG2E",    "1.4426950408889634074"  }, /* log_2 e    */
+    { "M_LOG10E",   "0.43429448190325182765" }, /* log_10 e   */
+    { "M_LN2",      "0.69314718055994530942" }, /* log_e 2    */
+    { "M_LN10",     "2.30258509299404568402" }, /* log_e 10   */
+    { "M_PI",       "3.14159265358979323846" }, /* pi         */
+    { "M_PI_2",     "1.57079632679489661923" }, /* pi/2       */
+    { "M_PI_4",     "0.78539816339744830962" }, /* pi/4       */
+    { "M_1_PI",     "0.31830988618379067154" }, /* 1/pi       */
+    { "M_2_PI",     "0.63661977236758134308" }, /* 2/pi       */
+    { "M_2_SQRTPI", "1.12837916709551257390" }, /* 2/sqrt(pi) */
+    { "M_SQRT2",    "1.41421356237309504880" }, /* sqrt(2)    */
+    { "M_SQRT1_2",  "0.70710678118654752440" }, /* 1/sqrt(2)  */
+    { "M_TAU",      "6.28318530717958647692" }  /* pi*2       */
+};
+
 static bool ftepp_define(ftepp_t *ftepp)
 {
     ppmacro *macro = NULL;
     size_t l = ftepp_ctx(ftepp).line;
+    size_t i;
+    bool   mathconstant = false;
 
     (void)ftepp_next(ftepp);
     if (!ftepp_skipspace(ftepp))
@@ -511,7 +530,25 @@ static bool ftepp_define(ftepp_t *ftepp)
         case TOKEN_IDENT:
         case TOKEN_TYPENAME:
         case TOKEN_KEYWORD:
+            if (OPTS_FLAG(FTEPP_MATHDEFS)) {
+                for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++) {
+                    if (!strcmp(ftepp_math_constants[i][0], ftepp_tokval(ftepp))) {
+                        mathconstant = true;
+                        break;
+                    }
+                }
+            }
+
             macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
+
+            if (OPTS_FLAG(FTEPP_MATHDEFS)) {
+                /* user defined ones take precedence */
+                if (macro && mathconstant) {
+                    ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
+                    macro = NULL;
+                }
+            }
+
             if (macro && ftepp->output_on) {
                 if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp)))
                     return false;
@@ -1816,6 +1853,7 @@ ftepp_t *ftepp_create()
     ftepp_t *ftepp;
     char minor[32];
     char major[32];
+    size_t i;
 
     ftepp = ftepp_new();
     if (!ftepp)
@@ -1865,6 +1903,13 @@ ftepp_t *ftepp_create()
      */
     ftepp_add_macro(ftepp, "__NULL__", "nil");
 
+    /* add all the math constants if they can be */
+    if (OPTS_FLAG(FTEPP_MATHDEFS)) {
+        for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++)
+            if (!ftepp_macro_find(ftepp, ftepp_math_constants[i][0]))
+                ftepp_add_macro(ftepp, ftepp_math_constants[i][0], ftepp_math_constants[i][1]);
+    }
+
     return ftepp;
 }
 
diff --git a/gmqcc.h b/gmqcc.h
index 1285072a1790a623899b79b6d24bcc1849626539..8925a09f23d34d8ead889d7efc0857246993fb39 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *     Wolfgang Bumiller
  *
 #define GMQCC_HDR
 #include <stdarg.h>
 #include <stddef.h>
-#include <time.h>   /* TODO: remove?*/
-
-/*
- * Disable some over protective warnings in visual studio because fixing them is a waste
- * of my time.
- */
-#ifdef _MSC_VER
-#   pragma warning(disable : 4244 ) /* conversion from 'int' to 'float', possible loss of data */
-#endif /*! _MSC_VER */
+#include <time.h>
 
 #define GMQCC_VERSION_MAJOR 0
 #define GMQCC_VERSION_MINOR 3
 #define GMQCC_VERSION_PATCH 6
-#define GMQCC_VERSION_BUILD(J,N,P) (((J)<<16)|((N)<<8)|(P))
-#define GMQCC_VERSION \
-    GMQCC_VERSION_BUILD(GMQCC_VERSION_MAJOR, GMQCC_VERSION_MINOR, GMQCC_VERSION_PATCH)
-/* Undefine the following on a release-tag: */
-#define GMQCC_VERSION_TYPE_DEVEL
-
-/* Full version string in case we need it */
-#ifdef GMQCC_VERSION_TYPE_DEVEL
+#define GMQCC_VERSION ((GMQCC_VERSION_MAJOR<<16)|(GMQCC_VERSION_MINOR<<8)|GMQCC_VERSION_PATCH)
+
+#ifdef GMQCC_VERSION_TYPE_RELEASE
 #    ifdef GMQCC_GITINFO
 #        define GMQCC_DEV_VERSION_STRING "git build: " GMQCC_GITINFO "\n"
 #    elif defined(GMQCC_VERSION_TYPE_DEVEL)
@@ -67,117 +54,52 @@ GMQCC_IND_STRING(GMQCC_VERSION_PATCH) \
 " Built " __DATE__ " " __TIME__ \
 "\n" GMQCC_DEV_VERSION_STRING
 
-/*
- * We cannot rely on C99 at all, since compilers like MSVC
- * simply don't support it.  We define our own boolean type
- * as a result (since we cannot include <stdbool.h>). For
- * compilers that are in 1999 mode (C99 compliant) we can use
- * the language keyword _Bool which can allow for better code
- * on GCC and GCC-like compilers, opposed to `int`.
- */
 #ifndef __cplusplus
-#   ifdef  false
-#       undef  false
-#   endif /*! false */
-#   ifdef  true
-#       undef true
-#   endif /*! true  */
-#   define false (unsigned)(0)
-#   define true  (unsigned)(1)
-#   ifdef __STDC_VERSION__
-#       if __STDC_VERSION__ < 199901L && __GNUC__ < 3
-            typedef int  bool;
-#       else
-            typedef _Bool bool;
-#       endif /*! __STDC_VERSION__ < 199901L && __GNUC__ < 3 */
-#   else
-        typedef int bool;
-#   endif /*! __STDC_VERSION__ */
-#endif /*! __cplusplus      */
+#   define false (unsigned char)(0)
+#   define true  (unsigned char)(1)
+    typedef unsigned char bool;
+#endif
 
-/*
- * Of some functions which are generated we want to make sure
- * that the result isn't ignored. To find such function calls,
- * we use this macro.
- */
 #if defined(__GNUC__) || defined(__CLANG__)
-#   define GMQCC_WARN __attribute__((warn_unused_result))
-#   define GMQCC_USED __attribute__((used))
-#else
-#   define GMQCC_WARN
-#   define GMQCC_USED
-#endif /*! defined(__GNUC__) || defined (__CLANG__) */
-
-/*
- * Inline is not supported in < C90, however some compilers
- * like gcc and clang might have an inline attribute we can
- * use if present.
- */
-#ifdef __STDC_VERSION__
-#    if __STDC_VERSION__ < 199901L
-#       if defined(__GNUC__) || defined (__CLANG__)
-#           if __GNUC__ < 2
-#               define GMQCC_INLINE
-#           else
-#               define GMQCC_INLINE __attribute__ ((always_inline))
-#           endif /*! __GNUC__ < 2 */
-#       else
-#           define GMQCC_INLINE
-#       endif /*! defined(__GNUC__) || defined (__CLANG__) */
-#    else
-#       define GMQCC_INLINE inline
-#    endif /*! __STDC_VERSION < 199901L */
-/*
- * Visual studio has __forcinline we can use.  So lets use that
- * I suspect it also has just __inline of some sort, but our use
- * of inline is correct (not guessed), WE WANT IT TO BE INLINE
- */
-#elif defined(_MSC_VER)
-#    define GMQCC_INLINE __forceinline
-#else
-#    define GMQCC_INLINE
-#endif /*! __STDC_VERSION__ */
-
-/*
- * noreturn is present in GCC and clang
- * it's required for _ast_node_destory otherwise -Wmissing-noreturn
- * in clang complains about there being no return since abort() is
- * called.
- */
-#if (defined(__GNUC__) && __GNUC__ >= 2) || defined(__CLANG__)
-#    define GMQCC_NORETURN __attribute__ ((noreturn))
-#else
-#    define GMQCC_NORETURN
-#endif /*! (defined(__GNUC__) && __GNUC__ >= 2) || defined (__CLANG__) */
-
-#if (defined(__GNUC__)) || defined(__CLANG__)
+#   include <stdint.h>
+#   if (__GNUC__ >= 2) || defined(__CLANG__)
+#       define GMQCC_NORETURN    __attribute__((noreturn))
+#       define GMQCC_FORCEINLINE __attribute__((always_inline))
+#       define GMQCC_INLINE      __inline
+#   endif
 #   define GMQCC_LIKELY(X)   __builtin_expect((X), 1)
 #   define GMQCC_UNLIKELY(X) __builtin_expect((X), 0)
+#   define GMQCC_WARN        __attribute__((warn_unused_result))
+#   define GMQCC_USED        __attribute__((used))
+#   define GMQCC_RESTRICT    __restrict__
 #else
+#   ifdef _MSC_VER
+        /* conversion from 'int' to 'float', possible loss of data */
+#       pragma warning(disable : 4244)
+
+        typedef unsigned __int8  uint8_t;
+        typedef unsigned __int16 uint16_t;
+        typedef unsigned __int32 uint32_t;
+        typedef unsigned __int64 uint64_t;
+        typedef __int16          int16_t;
+        typedef __int32          int32_t;
+        typedef __int64          int64_t;
+#       define GMQCC_NORETURN    __declspec(noreturn)
+#       define GMQCC_FORCEINLINE __forceinline
+#       define GMQCC_INLINE      __inline
+#       define GMQCC_RESTRICT    __restrict
+#   else
+#       define GMQCC_NORETURN
+#       define GMQCC_FORCEINLINE
+#       define GMQCC_INLINE
+#       define GMQCC_RESTRICT
+#   endif
 #   define GMQCC_LIKELY(X)   (X)
 #   define GMQCC_UNLIKELY(X) (X)
+#   define GMQCC_WARN
+#   define GMQCC_USED
 #endif
 
-#define GMQCC_ARRAY_COUNT(X) (sizeof(X) / sizeof((X)[0]))
-
-#ifndef _MSC_VER
-#   include <stdint.h>
-#else
-    typedef unsigned __int8  uint8_t;
-    typedef unsigned __int16 uint16_t;
-    typedef unsigned __int32 uint32_t;
-    typedef unsigned __int64 uint64_t;
-
-    typedef __int16          int16_t;
-    typedef __int32          int32_t;
-    typedef __int64          int64_t;
-#endif /*! _MSC_VER */
-
-/*
- * Very roboust way at determining endianess at compile time: this handles
- * almost every possible situation.  Otherwise a runtime check has to be
- * performed.
- */
 #define GMQCC_BYTE_ORDER_LITTLE 1234
 #define GMQCC_BYTE_ORDER_BIG    4321
 
@@ -251,7 +173,9 @@ GMQCC_IND_STRING(GMQCC_VERSION_PATCH) \
 #   else
 #       define PLATFORM_BYTE_ORDER -1
 #   endif
-#endif /*! !defined (PLATFORM_BYTE_ORDER) */
+#endif
+
+#define GMQCC_ARRAY_COUNT(X) (sizeof(X) / sizeof((X)[0]))
 
 /* stat.c */
 void  stat_info          (void);
@@ -315,6 +239,7 @@ const char      *util_ctime    (const time_t *timer);
 typedef struct fs_file_s fs_file_t;
 
 bool             util_isatty(fs_file_t *);
+size_t           hash(const char *key);
 
 /*
  * A flexible vector implementation: all vector pointers contain some
@@ -1041,15 +966,17 @@ typedef struct {
 extern opts_cmd_t opts;
 
 #define OPTS_GENERIC(f,i)    (!! (((f)[(i)/32]) & (1<< (unsigned)((i)%32))))
+
 #define OPTS_FLAG(i)         OPTS_GENERIC(opts.flags,        (i))
 #define OPTS_WARN(i)         OPTS_GENERIC(opts.warn,         (i))
 #define OPTS_WERROR(i)       OPTS_GENERIC(opts.werror,       (i))
 #define OPTS_OPTIMIZATION(i) OPTS_GENERIC(opts.optimization, (i))
+
 #define OPTS_OPTION_DUPED(X) (opts.options[X].allocated)
-#define OPTS_OPTION_BOOL(X) (opts.options[X].data.b)
-#define OPTS_OPTION_U16(X)  (opts.options[X].data.u16)
-#define OPTS_OPTION_U32(X)  (opts.options[X].data.u32)
-#define OPTS_OPTION_DUP(X) *(OPTS_OPTION_DUPED(X)=true, &(opts.options[X].data.str.p))
-#define OPTS_OPTION_STR(X)  (opts.options[X].data.str.c)
+#define OPTS_OPTION_BOOL(X)  (opts.options[X].data.b)
+#define OPTS_OPTION_U16(X)   (opts.options[X].data.u16)
+#define OPTS_OPTION_U32(X)   (opts.options[X].data.u32)
+#define OPTS_OPTION_STR(X)   (opts.options[X].data.str.c)
+#define OPTS_OPTION_DUP(X)  *(OPTS_OPTION_DUPED(X)=true, &(opts.options[X].data.str.p))
 
 #endif /*! GMQCC_HDR */
index acff3b3daaecadc5868f60b1363d71b4c5f8040d..20680d372771cc2d0972e8508fa3632ff2cc5849 100644 (file)
     FTEPP_PREDEFS = false
 
 
+    #Enable math constant definitions. This only works in combination
+    #with '-fftepp' and is currently not included by '-std=fteqcc'.
+    #The following macros will be added:
+    #
+    # M_E
+    # M_LOG2E
+    # M_LOG10E
+    # M_LN2
+    # M_LN10
+    # M_PI
+    # M_PI_2
+    # M_PI_4
+    # M_1_PI
+    # M_2_PI
+    # M_2_SQRTPI
+    # M_SQRT2
+    # M_SQRT1_2
+    # M_TAU
+
+    FTEPP_MATHDEFS = false
+
+
     #Allow switch cases to use non constant variables.
 
     RELAXED_SWITCH = true
     DIRECTIVE_INMACRO = true
 
 
+    #When using a function that is not explicitly defined, the compiler
+    #will search its intrinsics table for something that matches that
+    #function name by appending "__builtin_" to it. This behaviour may
+    #be unexpected, so enabling this will produce a diagnostic when
+    #such a function is resolved to a builtin.
+
+    BUILTINS = true
+
 
 [optimizations]
     #Some general peephole optimizations. For instance the code `a = b
diff --git a/hash.c b/hash.c
new file mode 100644 (file)
index 0000000..bc65b7f
--- /dev/null
+++ b/hash.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2012, 2013, 2014
+ *     Dale Weiler
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "gmqcc.h"
+#include <limits.h>
+
+#if defined(_MSC_VER)
+#   define HASH_ROTL32(X, Y) _rotl((X), (Y))
+#elif defined (__GNUC__) && (defined(__i386__) || defined(__amd64__))
+    static GMQCC_FORCEINLINE uint32_t hash_rotl32(volatile uint32_t x, int8_t r) {
+        __asm__ __volatile__ ("roll %1,%0" : "+r"(x) : "c"(r));
+        return x;
+    }
+#   define HASH_ROTL32(X, Y) hash_rotl32((volatile uint32_t)(X), (Y))
+#else
+#   define HASH_ROTL32(X, Y) (((X) << (Y)) | ((X) >> (32 - (Y))))
+#endif
+
+/*
+ * This is a version of the Murmur3 hashing function optimized for various
+ * compilers/architectures. It uses the traditional Murmur2 mix staging
+ * but fixes the mix staging inner loops.
+ *
+ * Murmur 2 contains an inner loop such as:
+ * while (l >= 4) {
+ *      u32 k = *(u32*)d;
+ *      k *= m;
+ *      k ^= k >> r;
+ *      k *= m;
+ *
+ *      h *= m;
+ *      h ^= k;
+ *      d += 4;
+ *      l -= 4;
+ * }
+ *
+ * The two u32s that form the key are the same value for x
+ * this premix stage will perform the same results for both values. Unrolled
+ * this produces just:
+ *  x *= m;
+ *  x ^= x >> r;
+ *  x *= m;
+ *
+ *  h *= m;
+ *  h ^= x;
+ *  h *= m;
+ *  h ^= x;
+ *
+ * This appears to be fine, except what happens when m == 1? well x
+ * cancels out entierly, leaving just:
+ *  x ^= x >> r;
+ *  h ^= x;
+ *  h ^= x;
+ *
+ * So all keys hash to the same value, but how often does m == 1?
+ * well, it turns out testing x for all possible values yeilds only
+ * 172,013,942 unique results instead of 2^32. So nearly ~4.6 bits
+ * are cancelled out on average!
+ *
+ * This means we have a 14.5% higher chance of collision. This is where
+ * Murmur3 comes in to save the day.
+ */
+static GMQCC_FORCEINLINE uint32_t hash_murmur_mix32(uint32_t hash) {
+    hash ^= hash >> 16;
+    hash *= 0x85EBCA6B;
+    hash ^= hash >> 13;
+    hash *= 0xC2B2AE35;
+    hash ^= hash >> 16;
+    return hash;
+}
+
+/*
+ * These constants were calculated with SMHasher to determine the best
+ * case senario for Murmur3:
+ *  http://code.google.com/p/smhasher/
+ */
+#define HASH_MURMUR_MASK1 0xCC9E2D51
+#define HASH_MURMUR_MASK2 0x1B873593
+#define HASH_MURMUR_SEED  0x9747B28C
+
+#if PLATFORM_BYTE_ORDER == GMQCC_BYTE_ORDER_LITTLE
+#   define HASH_MURMUR_SAFEREAD(PTR) (*((uint32_t*)(PTR)))
+#elif PLATFORM_BYTE_ORDER == GMQCC_BYTE_ORDER_BIG
+#   if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 3))
+#       define HASH_MURMUR_SAFEREAD(PTR) (__builtin_bswap32(*((uint32_t*)(PTR))))
+#   endif
+#endif
+/* Process individual bytes at this point since the endianess isn't known. */
+#ifndef HASH_MURMUR_SAFEREAD
+#   define HASH_MURMUR_SAFEREAD(PTR) ((PTR)[0] | (PTR)[1] << 8 | (PTR)[2] << 16 | (PTR)[3] << 24)
+#endif
+
+#define HASH_MURMUR_BLOCK(H, K)                        \
+    do {                                               \
+        K *= HASH_MURMUR_MASK1;                        \
+        K  = HASH_ROTL32(K, 15);                       \
+        K *= HASH_MURMUR_MASK2;                        \
+        H ^= K;                                        \
+        H  = HASH_ROTL32(H, 13);                       \
+        H  = H * 5 + 0xE6546B64;                       \
+    } while (0)
+#define HASH_MURMUR_BYTES(COUNT, H, C, N, PTR, LENGTH) \
+    do {                                               \
+        int i = COUNT;                                 \
+        while (i--) {                                  \
+            C = C >> 8 | *PTR++ << 24;                 \
+            N++;                                       \
+            LENGTH--;                                  \
+            if (N == 4) {                              \
+                HASH_MURMUR_BLOCK(H, C);               \
+                N = 0;                                 \
+            }                                          \
+        }                                              \
+    } while (0)
+#define HASH_MURMUR_TAIL(P, Z, H, C, N, PTR, LEN)      \
+    do {                                               \
+        LEN -= LEN/4*4;                                \
+        HASH_MURMUR_BYTES(LEN, H, C, N, PTR, LEN);     \
+        *P = H;                                        \
+        *Z = ((C) & ~0xFF) | (N);                      \
+    } while (0)
+
+#if PLATFORM_BYTE_ORDER == GMQCC_BYTE_ORDER_LITTLE
+static GMQCC_FORCEINLINE void hash_murmur_process(uint32_t *ph1, uint32_t *carry, const void *key, int length) {
+    uint32_t h1 = *ph1;
+    uint32_t c  = *carry;
+
+    const uint8_t *ptr = (uint8_t*)key;
+    const uint8_t *end;
+
+    int n  = c & 3;
+    int it = (4 - n) & 3;
+    if (it && it <= length)
+        HASH_MURMUR_BYTES(it, h1, c, n, ptr, length);
+
+    end = ptr + length/4*4;
+    for (; ptr < end; ptr += 4) {
+        uint32_t k1 = HASH_MURMUR_SAFEREAD(ptr);
+        HASH_MURMUR_BLOCK(h1, k1);
+    }
+    HASH_MURMUR_TAIL(ph1, carry, h1, c, n, ptr, length);
+}
+#else
+static GMQCC_FORCEINLINE void hash_murmur_process(uint32_t *ph1, uint32_t *carry, const void *key, int length) {
+    uint32_t k1;
+    uint32_t h1 = *ph1;
+    uint32_t c  = *carry;
+
+    const uint8_t *ptr = (uint8_t*)key;
+    const uint8_t *end;
+
+    int n  = c & 3;
+    int it = -(long)ptr & 3;
+    if (it && it <= length)
+        HASH_MURMUR_BYTES(it, h1, c, n, ptr, length);
+
+    end = ptr + length / 4 * 4;
+    switch (n) {
+        case 0:
+            for (; ptr < end; ptr += 4) {
+                k1 = HASH_MURMUR_SAFEREAD(ptr);
+                HASH_MURMUR_BLOCK(h1, k1);
+            }
+            break;
+
+#       define NEXT(N, RIGHT, LEFT)                  \
+            case N:                                  \
+                for (; ptr < end; ptr += 4) {        \
+                    k1  = c >> RIGHT;                \
+                    c   = HASH_MURMUR_SAFEREAD(ptr); \
+                    k1 |= c << LEFT;                 \
+                    HASH_MURMUR_BLOCK(h1, k1);       \
+                }                                    \
+                break
+        NEXT(1, 24, 8);
+        NEXT(2, 16, 16);
+        NEXT(3, 8,  24);
+        #undef NEXT
+    }
+    HASH_MURMUR_TAIL(ph1, carry, h1, c, n, ptr, length);
+}
+#endif
+
+static GMQCC_FORCEINLINE uint32_t hash_murmur_result(uint32_t hash, uint32_t carry, size_t length) {
+    uint32_t k1;
+    int n = carry & 3;
+    if (GMQCC_LIKELY(n)) {
+        k1    = carry >> (4 - n) * 8;
+        k1   *= HASH_MURMUR_MASK1;
+        k1    = HASH_ROTL32(k1, 15);
+        k1   *= HASH_MURMUR_MASK2;
+        hash ^= k1;
+    }
+    hash ^= length;
+    hash  = hash_murmur_mix32(hash);
+
+    return hash;
+}
+
+static GMQCC_FORCEINLINE uint32_t hash_murmur(const void *GMQCC_RESTRICT key, size_t length) {
+    uint32_t hash  = HASH_MURMUR_SEED;
+    uint32_t carry = 0;
+    hash_murmur_process(&hash, &carry, key, length);
+    return hash_murmur_result(hash, carry, length);
+}
+
+size_t hash(const char *key) {
+    const char   *s = key;
+    const char   *a = s;
+    const size_t *w;
+
+    for (; (uintptr_t)s % sizeof(size_t); s++)
+        if (!*s)
+            return hash_murmur((const void *)key, s-a);
+
+    for (w = (const size_t*)s; !((*w-(size_t)-1/UCHAR_MAX) & ~*w & ((size_t)-1/UCHAR_MAX) * (UCHAR_MAX / 2 + 1)); w++);
+    for (s = (const char *)w; *s; s++);
+
+    return hash_murmur((const void *)key, s-a);
+}
+
+#undef HASH_ROTL32
+#undef HASH_MURMUR_MASK1
+#undef HASH_MURMUR_MASK2
+#undef HASH_MURMUR_SEED
+#undef HASH_MURMUR_SAFEREAD
+#undef HASH_MURMUR_BLOCK
+#undef HASH_MURMUR_BYTES
+#undef HASH_MURMUR_TAIL
index 2bc2a839a339161f796b9721ccddb5dec6b1d11b..efb4a89706b9ac34df686a2ce37ce09a02176747 100644 (file)
@@ -14,7 +14,7 @@ LDFLAGS +=
 LIBS    += -lm
 
 #common objects
-COMMON   = ansi.o util.o stat.o fs.o opts.o conout.o
+COMMON   = ansi.o util.o hash.o stat.o fs.o opts.o conout.o
 
 #objects
 OBJ_C = $(COMMON) main.o lexer.o parser.o code.o ast.o ir.o ftepp.o utf8.o correct.o fold.o intrin.o
index 20d82c6585e51e9c89e5acbed05d859996f3a1e2..8c78ba744a17dde3e6b213d9badf5c899e23954c 100644 (file)
--- a/intrin.c
+++ b/intrin.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
@@ -58,319 +58,1939 @@ static GMQCC_INLINE void intrin_reg(intrin_t *intrin, ast_value *const value, as
     vec_push(intrin->parser->globals,   (ast_expression*)value);
 }
 
-#define QC_M_E 2.71828182845905f
+#define QC_POW_EPSILON 0.00001f
+
+/*
+ * since some intrinsics depend on each other there is the possibility
+ * that an intrinsic will fail to get a 'depended' function that a
+ * builtin needs, causing some dependency in the chain to have a NULL
+ * function. This will cause a segmentation fault at code generation,
+ * even though an error was raised. To contiue to allow it (instead
+ * of stopping compilation right away). We need to return from the
+ * parser, before compilation stops after all the collected errors.
+ */
+static ast_expression *intrin_func_self(intrin_t *intrin, const char *name, const char *from);
+static ast_expression *intrin_nullfunc(intrin_t *intrin) {
+    ast_value    *value = NULL;
+    ast_function *func  = intrin_value(intrin, &value, NULL, TYPE_VOID);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_isfinite(intrin_t *intrin) {
+    /*
+     * float isfinite(float x) {
+     *     return !(isnan(x) || isinf(x));
+     * }
+     */
+    ast_value    *value     = NULL;
+    ast_value    *x         = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_function *func      = intrin_value(intrin, &value, "isfinite", TYPE_FLOAT);
+    ast_call     *callisnan = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "isnan", "isfinite"));
+    ast_call     *callisinf = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "isinf", "isfinite"));
+    ast_block    *block     = ast_block_new(intrin_ctx(intrin));
+
+    /* float x; */
+    vec_push(value->expression.params, x);
+
+    /* <callisnan> = isnan(x); */
+    vec_push(callisnan->params, (ast_expression*)x);
+
+    /* <callisinf> = isinf(x); */
+    vec_push(callisinf->params, (ast_expression*)x);
+
+    /* return (!<callisnan> || <callisinf>); */
+    vec_push(block->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_unary_new(
+                intrin_ctx(intrin),
+                INSTR_NOT_F,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_OR,
+                    (ast_expression*)callisnan,
+                    (ast_expression*)callisinf
+                )
+            )
+        )
+    );
+
+    vec_push(func->blocks, block);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;;
+}
+
+static ast_expression *intrin_isinf(intrin_t *intrin) {
+    /*
+     * float isinf(float x) {
+     *     return (x != 0.0) && (x + x == x);
+     * }
+     */
+    ast_value    *value = NULL;
+    ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
+    ast_function *func  = intrin_value(intrin, &value, "isinf", TYPE_FLOAT);
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_AND,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_NE_F,
+                    (ast_expression*)x,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                ),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_EQ_F,
+                    (ast_expression*)ast_binary_new(
+                        intrin_ctx(intrin),
+                        INSTR_ADD_F,
+                        (ast_expression*)x,
+                        (ast_expression*)x
+                    ),
+                    (ast_expression*)x
+                )
+            )
+        )
+    );
+
+    vec_push(value->expression.params, x);
+    vec_push(func->blocks, body);
+
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_isnan(intrin_t *intrin) {
+    /*
+     * float isnan(float x) {
+     *   float local;
+     *   local = x;
+     *
+     *   return (x != local);
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x",     TYPE_FLOAT);
+    ast_value    *local  = ast_value_new(intrin_ctx(intrin), "local", TYPE_FLOAT);
+    ast_block    *body   = ast_block_new(intrin_ctx(intrin));
+    ast_function *func   = intrin_value(intrin, &value, "isnan", TYPE_FLOAT);
+
+    vec_push(body->locals, local);
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)local,
+            (ast_expression*)arg1
+        )
+    );
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_NE_F,
+                (ast_expression*)arg1,
+                (ast_expression*)local
+            )
+        )
+    );
+
+    vec_push(value->expression.params, arg1);
+    vec_push(func->blocks, body);
+
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_isnormal(intrin_t *intrin) {
+    /*
+     * float isnormal(float x) {
+     *     return isfinite(x);
+     * }
+     */
+    ast_value    *value         = NULL;
+    ast_call     *callisfinite  = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "isfinite", "isnormal"));
+    ast_value    *x             = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body          = ast_block_new(intrin_ctx(intrin));
+    ast_function *func          = intrin_value(intrin, &value, "isnormal", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+    vec_push(callisfinite->params, (ast_expression*)x);
+
+    /* return <callisfinite> */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)callisfinite
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_signbit(intrin_t *intrin) {
+    /*
+     * float signbit(float x) {
+     *     return (x < 0);
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *x      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body   = ast_block_new(intrin_ctx(intrin));
+    ast_function *func   = intrin_value(intrin, &value, "signbit", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* return (x < 0); */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_ternary_new(
+                intrin_ctx(intrin),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LT,
+                    (ast_expression*)x,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                ),
+                (ast_expression*)intrin->fold->imm_float[1],
+                (ast_expression*)intrin->fold->imm_float[0]
+            )
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_acosh(intrin_t *intrin) {
+    /*
+     * float acosh(float x) {
+     *     return log(x + sqrt((x * x) - 1));
+     * }
+     */
+    ast_value    *value    = NULL;
+    ast_value    *x        = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_call     *calllog  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "log", "acosh"));
+    ast_call     *callsqrt = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "acosh"));
+    ast_block    *body     = ast_block_new(intrin_ctx(intrin));
+    ast_function *func     = intrin_value(intrin, &value, "acosh", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* <callsqrt> = sqrt((x * x) - 1); */
+    vec_push(callsqrt->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_SUB_F,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_MUL_F,
+                (ast_expression*)x,
+                (ast_expression*)x
+            ),
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /* <calllog> = log(x + <callsqrt>); */
+    vec_push(calllog->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_ADD_F,
+            (ast_expression*)x,
+            (ast_expression*)callsqrt
+        )
+    );
+
+    /* return <calllog>; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)calllog
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_asinh(intrin_t *intrin) {
+    /*
+     * float asinh(float x) {
+     *     return log(x + sqrt((x * x) + 1));
+     * }
+     */
+    ast_value    *value    = NULL;
+    ast_value    *x        = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_call     *calllog  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "log", "asinh"));
+    ast_call     *callsqrt = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "asinh"));
+    ast_block    *body     = ast_block_new(intrin_ctx(intrin));
+    ast_function *func     = intrin_value(intrin, &value, "asinh", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* <callsqrt> = sqrt((x * x) + 1); */
+    vec_push(callsqrt->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_ADD_F,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_MUL_F,
+                (ast_expression*)x,
+                (ast_expression*)x
+            ),
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /* <calllog> = log(x + <callsqrt>); */
+    vec_push(calllog->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_ADD_F,
+            (ast_expression*)x,
+            (ast_expression*)callsqrt
+        )
+    );
+
+    /* return <calllog>; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)calllog
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_atanh(intrin_t *intrin) {
+    /*
+     * float atanh(float x) {
+     *     return 0.5 * log((1 + x) / (1 - x))
+     * }
+     */
+    ast_value    *value   = NULL;
+    ast_value    *x       = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_call     *calllog = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "log", "atanh"));
+    ast_block    *body    = ast_block_new(intrin_ctx(intrin));
+    ast_function *func    = intrin_value(intrin, &value, "atanh", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* <callog> = log((1 + x) / (1 - x)); */
+    vec_push(calllog->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_DIV_F,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_ADD_F,
+                (ast_expression*)intrin->fold->imm_float[1],
+                (ast_expression*)x
+            ),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_SUB_F,
+                (ast_expression*)intrin->fold->imm_float[1],
+                (ast_expression*)x
+            )
+        )
+    );
+
+    /* return 0.5 * <calllog>; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_MUL_F,
+            (ast_expression*)fold_constgen_float(intrin->fold, 0.5),
+            (ast_expression*)calllog
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_exp(intrin_t *intrin) {
+    /*
+     * float exp(float x) {
+     *     float sum = 1.0;
+     *     float acc = 1.0;
+     *     float i;
+     *     for (i = 1; i < 200; ++i)
+     *         sum += (acc *= x / i);
+     *
+     *     return sum;
+     * }
+     */
+    ast_value    *value = NULL;
+    ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x",   TYPE_FLOAT);
+    ast_value    *sum   = ast_value_new(intrin_ctx(intrin), "sum", TYPE_FLOAT);
+    ast_value    *acc   = ast_value_new(intrin_ctx(intrin), "acc", TYPE_FLOAT);
+    ast_value    *i     = ast_value_new(intrin_ctx(intrin), "i",   TYPE_FLOAT);
+    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
+    ast_function *func  = intrin_value(intrin, &value, "exp", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+    vec_push(body->locals, sum);
+    vec_push(body->locals, acc);
+    vec_push(body->locals, i);
+
+    /* sum = 1.0; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)sum,
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /* acc = 1.0; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)acc,
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /*
+     * for (i = 1; i < 200; ++i)
+     *     sum += (acc *= x / i);
+     */
+    vec_push(body->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            /* i = 1; */
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)i,
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            /* i < 200; */
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_LT,
+                (ast_expression*)i,
+                (ast_expression*)fold_constgen_float(intrin->fold, 200.0f)
+            ),
+            false,
+            NULL,
+            false,
+            /* ++i; */
+            (ast_expression*)ast_binstore_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                INSTR_ADD_F,
+                (ast_expression*)i,
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            /* sum += (acc *= (x / i)) */
+            (ast_expression*)ast_binstore_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                INSTR_ADD_F,
+                (ast_expression*)sum,
+                (ast_expression*)ast_binstore_new(
+                    intrin_ctx(intrin),
+                    INSTR_STORE_F,
+                    INSTR_MUL_F,
+                    (ast_expression*)acc,
+                    (ast_expression*)ast_binary_new(
+                        intrin_ctx(intrin),
+                        INSTR_DIV_F,
+                        (ast_expression*)x,
+                        (ast_expression*)i
+                    )
+                )
+            )
+        )
+    );
+
+    /* return sum; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)sum
+        )
+    );
+
+    vec_push(func->blocks, body);
+
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_exp2(intrin_t *intrin) {
+    /*
+     * float exp2(float x) {
+     *     return pow(2, x);
+     * }
+     */
+    ast_value    *value     = NULL;
+    ast_call     *callpow   = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "pow", "exp2"));
+    ast_value    *arg1      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body      = ast_block_new(intrin_ctx(intrin));
+    ast_function *func      = intrin_value(intrin, &value, "exp2", TYPE_FLOAT);
+
+    vec_push(value->expression.params, arg1);
+
+    vec_push(callpow->params, (ast_expression*)intrin->fold->imm_float[3]);
+    vec_push(callpow->params, (ast_expression*)arg1);
+
+    /* return <callpow> */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)callpow
+        )
+    );
+
+    vec_push(func->blocks, body);
+
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_expm1(intrin_t *intrin) {
+    /*
+     * float expm1(float x) {
+     *     return exp(x) - 1;
+     * }
+     */
+    ast_value    *value    = NULL;
+    ast_call     *callexp  = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "exp", "expm1"));
+    ast_value    *x        = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body     = ast_block_new(intrin_ctx(intrin));
+    ast_function *func     = intrin_value(intrin, &value, "expm1", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* <callexp> = exp(x); */
+    vec_push(callexp->params, (ast_expression*)x);
+
+    /* return <callexp> - 1; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_SUB_F,
+                (ast_expression*)callexp,
+                (ast_expression*)intrin->fold->imm_float[1]
+            )
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_pow(intrin_t *intrin) {
+    /*
+     *
+     * float pow(float base, float exp) {
+     *     float result;
+     *     float low;
+     *     float high;
+     *     float mid;
+     *     float square;
+     *     float accumulate;
+     *
+     *     if (exp == 0.0)
+     *         return 1;
+     *     if (exp == 1.0)
+     *         return base;
+     *     if (exp < 0)
+     *         return 1.0 / pow(base, -exp);
+     *     if (exp >= 1) {
+     *         result = pow(base, exp / 2);
+     *         return result * result;
+     *     }
+     *
+     *     low        = 0.0f;
+     *     high       = 1.0f;
+     *     square     = sqrt(base);
+     *     accumulate = square;
+     *     mid        = high / 2.0f
+     *
+     *     while (fabs(mid - exp) > QC_POW_EPSILON) {
+     *         square = sqrt(square);
+     *         if (mid < exp) {
+     *             low         = mid;
+     *             accumulate *= square;
+     *         } else {
+     *             high        = mid;
+     *             accumulate *= (1.0f / square);
+     *         }
+     *         mid = (low + high) / 2;
+     *     }
+     *     return accumulate;
+     * }
+     */
+    ast_value    *value = NULL;
+    ast_function *func = intrin_value(intrin, &value, "pow", TYPE_FLOAT);
+
+    /* prepare some calls for later */
+    ast_call *callpow1  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);                  /* for pow(base, -exp)    */
+    ast_call *callpow2  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);                  /* for pow(vase, exp / 2) */
+    ast_call *callsqrt1 = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "pow")); /* for sqrt(base)         */
+    ast_call *callsqrt2 = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "pow")); /* for sqrt(square)       */
+    ast_call *callfabs  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "fabs", "pow")); /* for fabs(mid - exp)    */
+
+    /* prepare some blocks for later */
+    ast_block *expgt1       = ast_block_new(intrin_ctx(intrin));
+    ast_block *midltexp     = ast_block_new(intrin_ctx(intrin));
+    ast_block *midltexpelse = ast_block_new(intrin_ctx(intrin));
+    ast_block *whileblock   = ast_block_new(intrin_ctx(intrin));
+
+    /* float pow(float base, float exp) */
+    ast_value    *base = ast_value_new(intrin_ctx(intrin), "base", TYPE_FLOAT);
+    ast_value    *exp  = ast_value_new(intrin_ctx(intrin), "exp",  TYPE_FLOAT);
+    /* { */
+    ast_block    *body = ast_block_new(intrin_ctx(intrin));
+
+    /*
+     * float result;
+     * float low;
+     * float high;
+     * float square;
+     * float accumulate;
+     * float mid;
+     */
+    ast_value *result     = ast_value_new(intrin_ctx(intrin), "result",     TYPE_FLOAT);
+    ast_value *low        = ast_value_new(intrin_ctx(intrin), "low",        TYPE_FLOAT);
+    ast_value *high       = ast_value_new(intrin_ctx(intrin), "high",       TYPE_FLOAT);
+    ast_value *square     = ast_value_new(intrin_ctx(intrin), "square",     TYPE_FLOAT);
+    ast_value *accumulate = ast_value_new(intrin_ctx(intrin), "accumulate", TYPE_FLOAT);
+    ast_value *mid        = ast_value_new(intrin_ctx(intrin), "mid",        TYPE_FLOAT);
+    vec_push(body->locals, result);
+    vec_push(body->locals, low);
+    vec_push(body->locals, high);
+    vec_push(body->locals, square);
+    vec_push(body->locals, accumulate);
+    vec_push(body->locals, mid);
+
+    vec_push(value->expression.params, base);
+    vec_push(value->expression.params, exp);
+
+    /*
+     * if (exp == 0.0)
+     *     return 1;
+     */
+    vec_push(body->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_EQ_F,
+                (ast_expression*)exp,
+                (ast_expression*)intrin->fold->imm_float[0]
+            ),
+            (ast_expression*)ast_return_new(
+                intrin_ctx(intrin),
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            NULL
+        )
+    );
+
+    /*
+     * if (exp == 1.0)
+     *     return base;
+     */
+    vec_push(body->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_EQ_F,
+                (ast_expression*)exp,
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            (ast_expression*)ast_return_new(
+                intrin_ctx(intrin),
+                (ast_expression*)base
+            ),
+            NULL
+        )
+    );
+
+    /* <callpow1> = pow(base, -exp) */
+    vec_push(callpow1->params, (ast_expression*)base);
+    vec_push(callpow1->params,
+        (ast_expression*)ast_unary_new(
+            intrin_ctx(intrin),
+            VINSTR_NEG_F,
+            (ast_expression*)exp
+        )
+    );
+
+    /*
+     * if (exp < 0)
+     *     return 1.0 / <callpow1>;
+     */
+    vec_push(body->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_LT,
+                (ast_expression*)exp,
+                (ast_expression*)intrin->fold->imm_float[0]
+            ),
+            (ast_expression*)ast_return_new(
+                intrin_ctx(intrin),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_DIV_F,
+                    (ast_expression*)intrin->fold->imm_float[1],
+                    (ast_expression*)callpow1
+                )
+            ),
+            NULL
+        )
+    );
+
+    /* <callpow2> = pow(base, exp / 2) */
+    vec_push(callpow2->params, (ast_expression*)base);
+    vec_push(callpow2->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_DIV_F,
+            (ast_expression*)exp,
+            (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
+        )
+    );
+
+    /*
+     * <expgt1> = {
+     *     result = <callpow2>;
+     *     return result * result;
+     * }
+     */
+    vec_push(expgt1->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)result,
+            (ast_expression*)callpow2
+        )
+    );
+    vec_push(expgt1->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_MUL_F,
+                (ast_expression*)result,
+                (ast_expression*)result
+            )
+        )
+    );
+
+    /*
+     * if (exp >= 1) {
+     *     <expgt1>
+     * }
+     */
+    vec_push(body->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_GE,
+                (ast_expression*)exp,
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            (ast_expression*)expgt1,
+            NULL
+        )
+    );
+
+    /*
+     * <callsqrt1> = sqrt(base)
+     */
+    vec_push(callsqrt1->params, (ast_expression*)base);
+
+    /*
+     * low        = 0.0f;
+     * high       = 1.0f;
+     * square     = sqrt(base);
+     * accumulate = square;
+     * mid        = high / 2.0f;
+     */
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)low,
+            (ast_expression*)intrin->fold->imm_float[0]
+        )
+    );
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)high,
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)square,
+            (ast_expression*)callsqrt1
+        )
+    );
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)accumulate,
+            (ast_expression*)square
+        )
+    );
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)mid,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_DIV_F,
+                (ast_expression*)high,
+                (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
+            )
+        )
+    );
+
+    /*
+     * <midltexp> = {
+     *     low         = mid;
+     *     accumulate *= square;
+     * }
+     */
+    vec_push(midltexp->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)low,
+            (ast_expression*)mid
+        )
+    );
+    vec_push(midltexp->exprs,
+        (ast_expression*)ast_binstore_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            INSTR_MUL_F,
+            (ast_expression*)accumulate,
+            (ast_expression*)square
+        )
+    );
+
+    /*
+     * <midltexpelse> = {
+     *     high        = mid;
+     *     accumulate *= (1.0 / square);
+     * }
+     */
+    vec_push(midltexpelse->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)high,
+            (ast_expression*)mid
+        )
+    );
+    vec_push(midltexpelse->exprs,
+        (ast_expression*)ast_binstore_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            INSTR_MUL_F,
+            (ast_expression*)accumulate,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_DIV_F,
+                (ast_expression*)intrin->fold->imm_float[1],
+                (ast_expression*)square
+            )
+        )
+    );
+
+    /*
+     * <callsqrt2> = sqrt(square)
+     */
+    vec_push(callsqrt2->params, (ast_expression*)square);
+
+    /*
+     * <whileblock> = {
+     *     square = <callsqrt2>;
+     *     if (mid < exp)
+     *          <midltexp>;
+     *     else
+     *          <midltexpelse>;
+     *
+     *     mid = (low + high) / 2;
+     * }
+     */
+    vec_push(whileblock->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)square,
+            (ast_expression*)callsqrt2
+        )
+    );
+    vec_push(whileblock->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_LT,
+                (ast_expression*)mid,
+                (ast_expression*)exp
+            ),
+            (ast_expression*)midltexp,
+            (ast_expression*)midltexpelse
+        )
+    );
+    vec_push(whileblock->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)mid,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_DIV_F,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_ADD_F,
+                    (ast_expression*)low,
+                    (ast_expression*)high
+                ),
+                (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
+            )
+        )
+    );
+
+    /*
+     * <callabs> = fabs(mid - exp)
+     */
+    vec_push(callfabs->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_SUB_F,
+            (ast_expression*)mid,
+            (ast_expression*)exp
+        )
+    );
+
+    /*
+     * while (<callfabs>  > epsilon)
+     *     <whileblock>
+     */
+    vec_push(body->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            /* init */
+            NULL,
+            /* pre condition */
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_GT,
+                (ast_expression*)callfabs,
+                (ast_expression*)fold_constgen_float(intrin->fold, QC_POW_EPSILON)
+            ),
+            /* pre not */
+            false,
+            /* post condition */
+            NULL,
+            /* post not */
+            false,
+            /* increment expression */
+            NULL,
+            /* code block */
+            (ast_expression*)whileblock
+        )
+    );
+
+    /* return accumulate */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)accumulate
+        )
+    );
+
+    /* } */
+    vec_push(func->blocks, body);
+
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_mod(intrin_t *intrin) {
+    /*
+     * float mod(float a, float b) {
+     *     float div = a / b;
+     *     float sign = (div < 0.0f) ? -1 : 1;
+     *     return a - b * sign * floor(sign * div);
+     * }
+     */
+    ast_value    *value = NULL;
+    ast_call     *call  = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "floor", "mod"));
+    ast_value    *a     = ast_value_new(intrin_ctx(intrin), "a",    TYPE_FLOAT);
+    ast_value    *b     = ast_value_new(intrin_ctx(intrin), "b",    TYPE_FLOAT);
+    ast_value    *div   = ast_value_new(intrin_ctx(intrin), "div",  TYPE_FLOAT);
+    ast_value    *sign  = ast_value_new(intrin_ctx(intrin), "sign", TYPE_FLOAT);
+    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
+    ast_function *func  = intrin_value(intrin, &value, "mod", TYPE_FLOAT);
+
+    vec_push(value->expression.params, a);
+    vec_push(value->expression.params, b);
+
+    vec_push(body->locals, div);
+    vec_push(body->locals, sign);
+
+    /* div = a / b; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)div,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_DIV_F,
+                (ast_expression*)a,
+                (ast_expression*)b
+            )
+        )
+    );
+
+    /* sign = (div < 0.0f) ? -1 : 1; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)sign,
+            (ast_expression*)ast_ternary_new(
+                intrin_ctx(intrin),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LT,
+                    (ast_expression*)div,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                ),
+                (ast_expression*)intrin->fold->imm_float[2],
+                (ast_expression*)intrin->fold->imm_float[1]
+            )
+        )
+    );
+
+    /* floor(sign * div) */
+    vec_push(call->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_MUL_F,
+            (ast_expression*)sign,
+            (ast_expression*)div
+        )
+    );
+
+    /* return a - b * sign * <call> */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_SUB_F,
+                (ast_expression*)a,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_MUL_F,
+                    (ast_expression*)b,
+                    (ast_expression*)ast_binary_new(
+                        intrin_ctx(intrin),
+                        INSTR_MUL_F,
+                        (ast_expression*)sign,
+                        (ast_expression*)call
+                    )
+                )
+            )
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_fabs(intrin_t *intrin) {
+    /*
+     * float fabs(float x) {
+     *     return x < 0 ? -x : x;
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body   = ast_block_new(intrin_ctx(intrin));
+    ast_function *func   = intrin_value(intrin, &value, "fabs", TYPE_FLOAT);
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_ternary_new(
+                intrin_ctx(intrin),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LE,
+                    (ast_expression*)arg1,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                ),
+                (ast_expression*)ast_unary_new(
+                    intrin_ctx(intrin),
+                    VINSTR_NEG_F,
+                    (ast_expression*)arg1
+                ),
+                (ast_expression*)arg1
+            )
+        )
+    );
+
+    vec_push(value->expression.params, arg1);
+    vec_push(func->blocks, body);
+
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_epsilon(intrin_t *intrin) {
+    /*
+     * float epsilon(void) {
+     *     float eps = 1.0f;
+     *     do { eps /= 2.0f; } while ((1.0f + (eps / 2.0f)) != 1.0f);
+     *     return eps;
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *eps    = ast_value_new(intrin_ctx(intrin), "eps", TYPE_FLOAT);
+    ast_block    *body   = ast_block_new(intrin_ctx(intrin));
+    ast_function *func   = intrin_value(intrin, &value, "epsilon", TYPE_FLOAT);
+
+    vec_push(body->locals, eps);
+
+    /* eps = 1.0f; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)eps,
+            (ast_expression*)intrin->fold->imm_float[0]
+        )
+    );
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            NULL,
+            NULL,
+            false,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_NE_F,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_ADD_F,
+                    (ast_expression*)intrin->fold->imm_float[1],
+                    (ast_expression*)ast_binary_new(
+                        intrin_ctx(intrin),
+                        INSTR_MUL_F,
+                        (ast_expression*)eps,
+                        (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
+                    )
+                ),
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            false,
+            NULL,
+            (ast_expression*)ast_binstore_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                INSTR_DIV_F,
+                (ast_expression*)eps,
+                (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
+            )
+        )
+    );
+
+    /* return eps; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)eps
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_nan(intrin_t *intrin) {
+    /*
+     * float nan(void) {
+     *     float x = 0.0f;
+     *     return x / x;
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *x      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_function *func   = intrin_value(intrin, &value, "nan", TYPE_FLOAT);
+    ast_block    *block  = ast_block_new(intrin_ctx(intrin));
+
+    vec_push(block->locals, x);
+
+    vec_push(block->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)x,
+            (ast_expression*)intrin->fold->imm_float[0]
+        )
+    );
+
+    vec_push(block->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_DIV_F,
+                (ast_expression*)x,
+                (ast_expression*)x
+            )
+        )
+    );
+
+    vec_push(func->blocks, block);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_inf(intrin_t *intrin) {
+    /*
+     * float inf(void) {
+     *     float x = 1.0f;
+     *     float y = 0.0f;
+     *     return x / y;
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *x      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_value    *y      = ast_value_new(intrin_ctx(intrin), "y", TYPE_FLOAT);
+    ast_function *func   = intrin_value(intrin, &value, "inf", TYPE_FLOAT);
+    ast_block    *block  = ast_block_new(intrin_ctx(intrin));
+    size_t        i;
+
+    vec_push(block->locals, x);
+    vec_push(block->locals, y);
+
+    /* to keep code size down */
+    for (i = 0; i <= 1; i++) {
+        vec_push(block->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i == 0) ? x : y),
+                (ast_expression*)intrin->fold->imm_float[i]
+            )
+        );
+    }
+
+    vec_push(block->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_DIV_F,
+                (ast_expression*)x,
+                (ast_expression*)y
+            )
+        )
+    );
+
+    vec_push(func->blocks, block);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_ln(intrin_t *intrin) {
+    /*
+     * float log(float power, float base) {
+     *   float whole;
+     *   float nth
+     *   float sign = 1.0f;
+     *   float eps  = epsilon();
+     *
+     *   if (power <= 1.0f || bbase <= 1.0) {
+     *       if (power <= 0.0f || base <= 0.0f)
+     *           return nan();
+     *
+     *       if (power < 1.0f) {
+     *           power = 1.0f / power;
+     *           sign *= -1.0f;
+     *       }
+     *
+     *       if (base < 1.0f) {
+     *           sign *= -1.0f;
+     *           base  = 1.0f / base;
+     *       }
+     *   }
+     *
+     *   float A_i       = 1;
+     *   float B_i       = 0;
+     *   float A_iminus1 = 0;
+     *   float B_iminus1 = 1;
+     *
+     *   for (;;) {
+     *       whole = power;
+     *       nth   = 0.0f;
+     *
+     *       while (whole >= base) {
+     *           float base2    = base;
+     *           float n2       = 1.0f;
+     *           float newbase2 = base2 * base2;
+     *
+     *           while (whole >= newbase2) {
+     *               base2     = newbase2;
+     *               n2       *= 2;
+     *               newbase2 *= newbase2;
+     *           }
+     *
+     *           whole /= base2;
+     *           nth += n2;
+     *       }
+     *
+     *       float b_iplus1 = n;
+     *       float A_iplus1 = b_iplus1 * A_i + A_iminus1;
+     *       float B_iplus1 = b_iplus1 * B_i + B_iminus1;
+     *
+     *       A_iminus1 = A_i;
+     *       B_iminus1 = B_i;
+     *       A_i       = A_iplus1;
+     *       B_i       = B_iplus1;
+     *
+     *       if (whole <= 1.0f + eps)
+     *           break;
+     *
+     *       power = base;
+     *       bower = whole;
+     *   }
+     *   return sign * A_i / B_i;
+     * }
+     */
+
+    ast_value    *value      = NULL;
+    ast_value    *power      = ast_value_new(intrin_ctx(intrin), "power",     TYPE_FLOAT);
+    ast_value    *base       = ast_value_new(intrin_ctx(intrin), "base",      TYPE_FLOAT);
+    ast_value    *whole      = ast_value_new(intrin_ctx(intrin), "whole",     TYPE_FLOAT);
+    ast_value    *nth        = ast_value_new(intrin_ctx(intrin), "nth",       TYPE_FLOAT);
+    ast_value    *sign       = ast_value_new(intrin_ctx(intrin), "sign",      TYPE_FLOAT);
+    ast_value    *A_i        = ast_value_new(intrin_ctx(intrin), "A_i",       TYPE_FLOAT);
+    ast_value    *B_i        = ast_value_new(intrin_ctx(intrin), "B_i",       TYPE_FLOAT);
+    ast_value    *A_iminus1  = ast_value_new(intrin_ctx(intrin), "A_iminus1", TYPE_FLOAT);
+    ast_value    *B_iminus1  = ast_value_new(intrin_ctx(intrin), "B_iminus1", TYPE_FLOAT);
+    ast_value    *b_iplus1   = ast_value_new(intrin_ctx(intrin), "b_iplus1",  TYPE_FLOAT);
+    ast_value    *A_iplus1   = ast_value_new(intrin_ctx(intrin), "A_iplus1",  TYPE_FLOAT);
+    ast_value    *B_iplus1   = ast_value_new(intrin_ctx(intrin), "B_iplus1",  TYPE_FLOAT);
+    ast_value    *eps        = ast_value_new(intrin_ctx(intrin), "eps",       TYPE_FLOAT);
+    ast_value    *base2      = ast_value_new(intrin_ctx(intrin), "base2",     TYPE_FLOAT);
+    ast_value    *n2         = ast_value_new(intrin_ctx(intrin), "n2",        TYPE_FLOAT);
+    ast_value    *newbase2   = ast_value_new(intrin_ctx(intrin), "newbase2",  TYPE_FLOAT);
+    ast_block    *block      = ast_block_new(intrin_ctx(intrin));
+    ast_block    *plt1orblt1 = ast_block_new(intrin_ctx(intrin)); /* (power <= 1.0f || base <= 1.0f) */
+    ast_block    *plt1       = ast_block_new(intrin_ctx(intrin)); /* (power < 1.0f) */
+    ast_block    *blt1       = ast_block_new(intrin_ctx(intrin)); /* (base  < 1.0f) */
+    ast_block    *forloop    = ast_block_new(intrin_ctx(intrin)); /* for(;;) */
+    ast_block    *whileloop  = ast_block_new(intrin_ctx(intrin)); /* while (whole >= base) */
+    ast_block    *nestwhile  = ast_block_new(intrin_ctx(intrin)); /* while (whole >= newbase2) */
+    ast_function *func       = intrin_value(intrin, &value, "ln", TYPE_FLOAT);
+    size_t        i;
+
+    vec_push(value->expression.params, power);
+    vec_push(value->expression.params, base);
+
+    vec_push(block->locals, whole);
+    vec_push(block->locals, nth);
+    vec_push(block->locals, sign);
+    vec_push(block->locals, eps);
+    vec_push(block->locals, A_i);
+    vec_push(block->locals, B_i);
+    vec_push(block->locals, A_iminus1);
+    vec_push(block->locals, B_iminus1);
+
+    /* sign = 1.0f; */
+    vec_push(block->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)sign,
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /* eps = __builtin_epsilon(); */
+    vec_push(block->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)eps,
+            (ast_expression*)ast_call_new(
+                intrin_ctx(intrin),
+                intrin_func_self(intrin, "__builtin_epsilon", "ln")
+            )
+        )
+    );
 
-static ast_expression *intrin_pow (intrin_t *intrin) {
     /*
-     * float pow(float x, float y) {
-     *   float local = 1.0f;
-     *   while (y > 0) {
-     *     while (!(y & 1)) {
-     *       y >>= 2;
-     *       x *=  x;
-     *     }
-     *     y--;
-     *     local *= x;
-     *   }
-     *   return local;
+     * A_i       = 1;
+     * B_i       = 0;
+     * A_iminus1 = 0;
+     * B_iminus1 = 1;
+     */
+    for (i = 0; i <= 1; i++) {
+        int j;
+        for (j = 1; j >= 0; j--) {
+            vec_push(block->exprs,
+                (ast_expression*)ast_store_new(
+                    intrin_ctx(intrin),
+                    INSTR_STORE_F,
+                    (ast_expression*)((j) ? ((i) ? B_iminus1 : A_i)
+                                          : ((i) ? A_iminus1 : B_i)),
+                    (ast_expression*)intrin->fold->imm_float[j]
+                )
+            );
+        }
+    }
+
+    /*
+     * <plt1> = {
+     *     power = 1.0f / power;
+     *     sign *= -1.0f;
+     * }
+     * <blt1> = {
+     *     base  = 1.0f / base;
+     *     sign *= -1.0f;
      * }
      */
-    ast_value    *value = NULL;
-    ast_value    *arg1  = ast_value_new(intrin_ctx(intrin), "x",     TYPE_FLOAT);
-    ast_value    *arg2  = ast_value_new(intrin_ctx(intrin), "y",     TYPE_FLOAT);
-    ast_value    *local = ast_value_new(intrin_ctx(intrin), "local", TYPE_FLOAT);
-    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
-    ast_block    *l1b   = ast_block_new(intrin_ctx(intrin)); /* loop 1 body */
-    ast_block    *l2b   = ast_block_new(intrin_ctx(intrin)); /* loop 2 body */
-    ast_loop     *loop1 = NULL;
-    ast_loop     *loop2 = NULL;
-    ast_function *func  = intrin_value(intrin, &value, "pow", TYPE_FLOAT);
+    for (i = 0; i <= 1; i++) {
+        vec_push(((i) ? blt1 : plt1)->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? base : power),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_DIV_F,
+                    (ast_expression*)intrin->fold->imm_float[1],
+                    (ast_expression*)((i) ? base : power)
+                )
+            )
+        );
+        vec_push(plt1->exprs,
+            (ast_expression*)ast_binstore_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                INSTR_MUL_F,
+                (ast_expression*)sign,
+                (ast_expression*)intrin->fold->imm_float[2]
+            )
+        );
+    }
 
-    /* arguments */
-    vec_push(value->expression.params, arg1);
-    vec_push(value->expression.params, arg2);
+    /*
+     * <plt1orblt1> = {
+     *     if (power <= 0.0 || base <= 0.0f)
+     *         return __builtin_nan();
+     *     if (power < 1.0f)
+     *         <plt1>
+     *     if (base < 1.0f)
+     *         <blt1>
+     * }
+     */
+    vec_push(plt1orblt1->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_OR,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LE,
+                    (ast_expression*)power,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                ),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LE,
+                    (ast_expression*)base,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                )
+            ),
+            (ast_expression*)ast_return_new(
+                intrin_ctx(intrin),
+                (ast_expression*)ast_call_new(
+                    intrin_ctx(intrin),
+                    intrin_func_self(intrin, "__builtin_nan", "ln")
+                )
+            ),
+            NULL
+        )
+    );
 
-    /* local */
-    vec_push(body->locals, local);
+    for (i = 0; i <= 1; i++) {
+        vec_push(plt1orblt1->exprs,
+            (ast_expression*)ast_ifthen_new(
+                intrin_ctx(intrin),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LT,
+                    (ast_expression*)((i) ? base : power),
+                    (ast_expression*)intrin->fold->imm_float[1]
+                ),
+                (ast_expression*)((i) ? blt1 : plt1),
+                NULL
+            )
+        );
+    }
 
-    /* assignment to local of value 1.0f */
-    vec_push(body->exprs,
-        (ast_expression*)ast_store_new (
+    vec_push(block->exprs, (ast_expression*)plt1orblt1);
+
+
+    /* whole = power; */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_store_new(
             intrin_ctx(intrin),
             INSTR_STORE_F,
-            (ast_expression*)local,
-            (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
+            (ast_expression*)whole,
+            (ast_expression*)power
         )
     );
 
-    /* y >>= 2 */
-    vec_push(l2b->exprs,
-        (ast_expression*)ast_binstore_new (
+    /* nth = 0.0f; */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_store_new(
             intrin_ctx(intrin),
             INSTR_STORE_F,
-            INSTR_MUL_F,
-            (ast_expression*)arg2,
-            (ast_expression*)fold_constgen_float(intrin->parser->fold, 0.25f)
+            (ast_expression*)nth,
+            (ast_expression*)intrin->fold->imm_float[0]
         )
     );
 
-    /* x *= x */
-    vec_push(l2b->exprs,
-        (ast_expression*)ast_binstore_new (
+    /* base2 = base; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_store_new(
             intrin_ctx(intrin),
             INSTR_STORE_F,
-            INSTR_MUL_F,
-            (ast_expression*)arg1,
-            (ast_expression*)arg1
+            (ast_expression*)base2,
+            (ast_expression*)base
+        )
+    );
+
+    /* n2 = 1.0f; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)n2,
+            (ast_expression*)intrin->fold->imm_float[1]
         )
     );
 
-    /* while (!(y&1)) */
-    loop2 = ast_loop_new (
-        intrin_ctx(intrin),
-        NULL,
-        (ast_expression*)ast_binary_new (
+    /* newbase2 = base2 * base2; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_store_new(
             intrin_ctx(intrin),
-            INSTR_AND,
-            (ast_expression*)arg2,
-            (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
-        ),
-        true, /* ! not */
-        NULL,
-        false,
-        NULL,
-        (ast_expression*)l2b
+            INSTR_STORE_F,
+            (ast_expression*)newbase2,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_MUL_F,
+                (ast_expression*)base2,
+                (ast_expression*)base2
+            )
+        )
     );
 
-    /* push nested loop into loop expressions */
-    vec_push(l1b->exprs, (ast_expression*)loop2);
+    /* while loop locals */
+    vec_push(whileloop->locals, base2);
+    vec_push(whileloop->locals, n2);
+    vec_push(whileloop->locals, newbase2);
 
-    /* y-- */
-    vec_push(l1b->exprs,
-        (ast_expression*)ast_binstore_new (
+    /* base2 = newbase2; */
+    vec_push(nestwhile->exprs,
+        (ast_expression*)ast_store_new(
             intrin_ctx(intrin),
             INSTR_STORE_F,
-            INSTR_SUB_F,
-            (ast_expression*)arg2,
-            (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
+            (ast_expression*)base2,
+            (ast_expression*)newbase2
         )
     );
-    /* local *= x */
-    vec_push(l1b->exprs,
-        (ast_expression*)ast_binstore_new (
+
+    /* n2 *= 2; */
+    vec_push(nestwhile->exprs,
+        (ast_expression*)ast_binstore_new(
             intrin_ctx(intrin),
             INSTR_STORE_F,
             INSTR_MUL_F,
-            (ast_expression*)local,
-            (ast_expression*)arg1
+            (ast_expression*)n2,
+            (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
         )
     );
 
-    /* while (y > 0) */
-    loop1 = ast_loop_new (
-        intrin_ctx(intrin),
-        NULL,
-        (ast_expression*)ast_binary_new (
+    /* newbase2 *= newbase2; */
+    vec_push(nestwhile->exprs,
+        (ast_expression*)ast_binstore_new(
             intrin_ctx(intrin),
-            INSTR_GT,
-            (ast_expression*)arg2,
-            (ast_expression*)intrin->fold->imm_float[0] /* 0 == 0.0f */
-        ),
-        false,
-        NULL,
-        false,
-        NULL,
-        (ast_expression*)l1b
+            INSTR_STORE_F,
+            INSTR_MUL_F,
+            (ast_expression*)newbase2,
+            (ast_expression*)newbase2
+        )
     );
 
-    /* push the loop1 into the body for the function */
-    vec_push(body->exprs, (ast_expression*)loop1);
-
-    /* return local; */
-    vec_push(body->exprs,
-        (ast_expression*)ast_return_new (
+    /* while (whole >= newbase2) */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_loop_new(
             intrin_ctx(intrin),
-            (ast_expression*)local
+            NULL,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_GE,
+                (ast_expression*)whole,
+                (ast_expression*)newbase2
+            ),
+            false,
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)nestwhile
         )
     );
 
-    /* push block and register intrin for codegen */
-    vec_push(func->blocks, body);
+    /* whole /= base2; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_binstore_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            INSTR_DIV_F,
+            (ast_expression*)whole,
+            (ast_expression*)base2
+        )
+    );
 
-    intrin_reg(intrin, value, func);
+    /* nth += n2; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_binstore_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            INSTR_ADD_F,
+            (ast_expression*)nth,
+            (ast_expression*)n2
+        )
+    );
 
-    return (ast_expression*)value;
-}
+    /* while (whole >= base) */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            NULL,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_GE,
+                (ast_expression*)whole,
+                (ast_expression*)base
+            ),
+            false,
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)whileloop
+        )
+    );
 
-static ast_expression *intrin_mod(intrin_t *intrin) {
-    /*
-     * float mod(float x, float y) {
-     *   return x - y * floor(x / y);
-     * }
-     */
-    ast_value    *value = NULL;
-    ast_call     *call  = ast_call_new (intrin_ctx(intrin), intrin_func(intrin, "floor"));
-    ast_value    *arg1  = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
-    ast_value    *arg2  = ast_value_new(intrin_ctx(intrin), "y", TYPE_FLOAT);
-    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
-    ast_function *func  = intrin_value(intrin, &value, "mod", TYPE_FLOAT);
+    vec_push(forloop->locals, b_iplus1);
+    vec_push(forloop->locals, A_iplus1);
+    vec_push(forloop->locals, B_iplus1);
 
-    /* floor(x/y) */
-    vec_push(call->params,
-        (ast_expression*)ast_binary_new (
+    /* b_iplus1 = nth; */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_store_new(
             intrin_ctx(intrin),
-            INSTR_DIV_F,
-            (ast_expression*)arg1,
-            (ast_expression*)arg2
+            INSTR_STORE_F,
+            (ast_expression*)b_iplus1,
+            (ast_expression*)nth
         )
     );
 
-    vec_push(body->exprs,
-        (ast_expression*)ast_return_new(
-            intrin_ctx(intrin),
-            (ast_expression*)ast_binary_new(
+    /*
+     * A_iplus1 = b_iplus1 * A_i + A_iminus1;
+     * B_iplus1 = b_iplus1 * B_i + B_iminus1;
+     */
+    for (i = 0; i <= 1; i++) {
+        vec_push(forloop->exprs,
+            (ast_expression*)ast_store_new(
                 intrin_ctx(intrin),
-                INSTR_SUB_F,
-                (ast_expression*)arg1,
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? B_iplus1 : A_iplus1),
                 (ast_expression*)ast_binary_new(
                     intrin_ctx(intrin),
-                    INSTR_MUL_F,
-                    (ast_expression*)arg2,
-                    (ast_expression*)call
+                    INSTR_ADD_F,
+                    (ast_expression*)ast_binary_new(
+                        intrin_ctx(intrin),
+                        INSTR_MUL_F,
+                        (ast_expression*)b_iplus1,
+                        (ast_expression*) ((i) ? B_i : A_i)
+                    ),
+                    (ast_expression*)((i) ? B_iminus1 : A_iminus1)
                 )
             )
-        )
-    );
-
-    vec_push(value->expression.params, arg1); /* float x (for param) */
-    vec_push(value->expression.params, arg2); /* float y (for param) */
+        );
+    }
 
-    vec_push(func->blocks,             body); /* {{{ body }}} */
+    /*
+     * A_iminus1 = A_i;
+     * B_iminus1 = B_i;
+     */
+    for (i = 0; i <= 1; i++) {
+        vec_push(forloop->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? B_iminus1 : A_iminus1),
+                (ast_expression*)((i) ? B_i       : A_i)
+            )
+        );
+    }
 
-    intrin_reg(intrin, value, func);
+    /*
+     * A_i = A_iplus1;
+     * B_i = B_iplus1;
+     */
+    for (i = 0; i <= 1; i++) {
+        vec_push(forloop->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? B_i      : A_i),
+                (ast_expression*)((i) ? B_iplus1 : A_iplus1)
+            )
+        );
+    }
 
-    return (ast_expression*)value;
-}
+    /*
+     * if (whole <= 1.0f + eps)
+     *     break;
+     */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_LE,
+                (ast_expression*)whole,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_ADD_F,
+                    (ast_expression*)intrin->fold->imm_float[1],
+                    (ast_expression*)eps
+                )
+            ),
+            (ast_expression*)ast_breakcont_new(
+                intrin_ctx(intrin),
+                false,
+                0
+            ),
+            NULL
+        )
+    );
 
-static ast_expression *intrin_exp(intrin_t *intrin) {
     /*
-     * float exp(float x) {
-     *     return pow(QC_M_E, x);
-     * }
+     * power = base;
+     * base  = whole;
      */
-    ast_value    *value = NULL;
-    ast_call     *call  = ast_call_new (intrin_ctx(intrin), intrin_func(intrin, "pow"));
-    ast_value    *arg1  = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
-    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
-    ast_function *func  = intrin_value(intrin, &value, "exp", TYPE_FLOAT);
+    for (i = 0; i <= 1; i++) {
+        vec_push(forloop->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? base  : power),
+                (ast_expression*)((i) ? whole : base)
+            )
+        );
+    }
 
-    /* push arguments for params to call */
-    vec_push(call->params, (ast_expression*)fold_constgen_float(intrin->fold, QC_M_E));
-    vec_push(call->params, (ast_expression*)arg1);
+    /* add the for loop block */
+    vec_push(block->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            NULL,
+            /* for(; 1; ) ?? (can this be NULL too?) */
+            (ast_expression*)intrin->fold->imm_float[1],
+            false,
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)forloop
+        )
+    );
 
-    /* return pow(QC_M_E, x) */
-    vec_push(body->exprs,
+    /* return sign * A_i / B_il */
+    vec_push(block->exprs,
         (ast_expression*)ast_return_new(
             intrin_ctx(intrin),
-            (ast_expression*)call
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_MUL_F,
+                (ast_expression*)sign,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_DIV_F,
+                    (ast_expression*)A_i,
+                    (ast_expression*)B_i
+                )
+            )
         )
     );
 
-    vec_push(value->expression.params, arg1); /* float x (for param) */
-
-    vec_push(func->blocks,             body); /* {{{ body }}} */
-
+    vec_push(func->blocks, block);
     intrin_reg(intrin, value, func);
 
     return (ast_expression*)value;
 }
 
-static ast_expression *intrin_isnan(intrin_t *intrin) {
-    /*
-     * float isnan(float x) {
-     *   float local;
-     *   local = x;
-     *
-     *   return (x != local);
-     * }
-     */
+static ast_expression *intrin_log_variant(intrin_t *intrin, const char *name, float base) {
     ast_value    *value  = NULL;
-    ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x",     TYPE_FLOAT);
-    ast_value    *local  = ast_value_new(intrin_ctx(intrin), "local", TYPE_FLOAT);
+    ast_call     *callln = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "__builtin_ln", name));
+    ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
     ast_block    *body   = ast_block_new(intrin_ctx(intrin));
-    ast_function *func   = intrin_value(intrin, &value, "isnan", TYPE_FLOAT);
+    ast_function *func   = intrin_value(intrin, &value, name, TYPE_FLOAT);
 
-    vec_push(body->locals, local);
-    vec_push(body->exprs,
-        (ast_expression*)ast_store_new(
-            intrin_ctx(intrin),
-            INSTR_STORE_F,
-            (ast_expression*)local,
-            (ast_expression*)arg1
-        )
-    );
+    vec_push(value->expression.params, arg1);
+
+    vec_push(callln->params, (ast_expression*)arg1);
+    vec_push(callln->params, (ast_expression*)fold_constgen_float(intrin->fold, base));
 
     vec_push(body->exprs,
         (ast_expression*)ast_return_new(
             intrin_ctx(intrin),
-            (ast_expression*)ast_binary_new(
-                intrin_ctx(intrin),
-                INSTR_NE_F,
-                (ast_expression*)arg1,
-                (ast_expression*)local
-            )
+            (ast_expression*)callln
         )
     );
 
-    vec_push(value->expression.params, arg1);
     vec_push(func->blocks, body);
-
     intrin_reg(intrin, value, func);
-
     return (ast_expression*)value;
 }
 
-static ast_expression *intrin_fabs(intrin_t *intrin) {
+static ast_expression *intrin_log(intrin_t *intrin) {
+    return intrin_log_variant(intrin, "log", 2.7182818284590452354);
+}
+static ast_expression *intrin_log10(intrin_t *intrin) {
+    return intrin_log_variant(intrin, "log10", 10);
+}
+static ast_expression *intrin_log2(intrin_t *intrin) {
+    return intrin_log_variant(intrin, "log2", 2);
+}
+static ast_expression *intrin_logb(intrin_t *intrin) {
+    /* FLT_RADIX == 2 for now */
+    return intrin_log_variant(intrin, "log2", 2);
+}
+
+static ast_expression *intrin_shift_variant(intrin_t *intrin, const char *name, size_t instr) {
     /*
-     * float fabs(float x) {
-     *     return x < 0 ? -x : x;
-     * }
+     * float [shift] (float a, float b) {
+     *   return floor(a [instr] pow(2, b));
      */
-    ast_value    *value  = NULL;
-    ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
-    ast_block    *body   = ast_block_new(intrin_ctx(intrin));
-    ast_function *func   = intrin_value(intrin, &value, "fabs", TYPE_FLOAT);
+    ast_value    *value     = NULL;
+    ast_call     *callpow   = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "pow", name));
+    ast_call     *callfloor = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "floor", name));
+    ast_value    *a         = ast_value_new(intrin_ctx(intrin), "a", TYPE_FLOAT);
+    ast_value    *b         = ast_value_new(intrin_ctx(intrin), "b", TYPE_FLOAT);
+    ast_block    *body      = ast_block_new(intrin_ctx(intrin));
+    ast_function *func      = intrin_value(intrin, &value, name, TYPE_FLOAT);
 
+    vec_push(value->expression.params, a);
+    vec_push(value->expression.params, b);
+
+    /* <callpow> = pow(2, b) */
+    vec_push(callpow->params, (ast_expression*)intrin->fold->imm_float[3]);
+    vec_push(callpow->params, (ast_expression*)b);
+
+    /* <callfloor> = floor(a [instr] <callpow>) */
+    vec_push(
+        callfloor->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            instr,
+            (ast_expression*)a,
+            (ast_expression*)callpow
+        )
+    );
+
+    /* return <callfloor> */
     vec_push(body->exprs,
         (ast_expression*)ast_return_new(
             intrin_ctx(intrin),
-            (ast_expression*)ast_ternary_new(
-                intrin_ctx(intrin),
-                (ast_expression*)ast_binary_new(
-                    intrin_ctx(intrin),
-                    INSTR_LE,
-                    (ast_expression*)arg1,
-                    (ast_expression*)intrin->fold->imm_float[0]
-                ),
-                (ast_expression*)ast_binary_new(
-                    intrin_ctx(intrin),
-                    INSTR_SUB_F,
-                    (ast_expression*)intrin->fold->imm_float[0],
-                    (ast_expression*)arg1
-                ),
-                (ast_expression*)arg1
-            )
+            (ast_expression*)callfloor
         )
     );
 
-    vec_push(value->expression.params, arg1);
     vec_push(func->blocks, body);
-
     intrin_reg(intrin, value, func);
-
     return (ast_expression*)value;
 }
 
+static ast_expression *intrin_lshift(intrin_t *intrin) {
+    return intrin_shift_variant(intrin, "lshift", INSTR_MUL_F);
+}
+
+static ast_expression *intrin_rshift(intrin_t *intrin) {
+    return intrin_shift_variant(intrin, "rshift", INSTR_DIV_F);
+}
+
 /*
  * TODO: make static (and handle ast_type_string) here for the builtin
  * instead of in SYA parse close.
@@ -381,12 +2001,32 @@ ast_expression *intrin_debug_typestring(intrin_t *intrin) {
 }
 
 static const intrin_func_t intrinsics[] = {
-    {&intrin_exp,              "__builtin_exp",              "exp",   1},
-    {&intrin_mod,              "__builtin_mod",              "mod",   2},
-    {&intrin_pow,              "__builtin_pow",              "pow",   2},
-    {&intrin_isnan,            "__builtin_isnan",            "isnan", 1},
-    {&intrin_fabs,             "__builtin_fabs",             "fabs",  1},
-    {&intrin_debug_typestring, "__builtin_debug_typestring", "",      0}
+    {&intrin_isfinite,         "__builtin_isfinite",         "isfinite", 1},
+    {&intrin_isinf,            "__builtin_isinf",            "isinf",    1},
+    {&intrin_isnan,            "__builtin_isnan",            "isnan",    1},
+    {&intrin_isnormal,         "__builtin_isnormal",         "isnormal", 1},
+    {&intrin_signbit,          "__builtin_signbit",          "signbit",  1},
+    {&intrin_acosh,            "__builtin_acosh",            "acosh",    1},
+    {&intrin_asinh,            "__builtin_asinh",            "asinh",    1},
+    {&intrin_atanh,            "__builtin_atanh",            "atanh",    1},
+    {&intrin_exp,              "__builtin_exp",              "exp",      1},
+    {&intrin_exp2,             "__builtin_exp2",             "exp2",     1},
+    {&intrin_expm1,            "__builtin_expm1",            "expm1",    1},
+    {&intrin_mod,              "__builtin_mod",              "mod",      2},
+    {&intrin_pow,              "__builtin_pow",              "pow",      2},
+    {&intrin_fabs,             "__builtin_fabs",             "fabs",     1},
+    {&intrin_log,              "__builtin_log",              "log",      1},
+    {&intrin_log10,            "__builtin_log10",            "log10",    1},
+    {&intrin_log2,             "__builtin_log2",             "log2",     1},
+    {&intrin_logb,             "__builtin_logb",             "logb",     1},
+    {&intrin_lshift,           "__builtin_lshift",           "",         2},
+    {&intrin_rshift,           "__builtin_rshift",           "",         2},
+    {&intrin_epsilon,          "__builtin_epsilon",          "",         0},
+    {&intrin_nan,              "__builtin_nan",              "",         0},
+    {&intrin_inf,              "__builtin_inf",              "",         0},
+    {&intrin_ln,               "__builtin_ln",               "",         2},
+    {&intrin_debug_typestring, "__builtin_debug_typestring", "",         0},
+    {&intrin_nullfunc,         "#nullfunc",                  "",         0}
 };
 
 static void intrin_error(intrin_t *intrin, const char *fmt, ...) {
@@ -445,7 +2085,7 @@ static GMQCC_INLINE ast_expression *intrin_func_try(intrin_t *intrin, size_t off
     return NULL;
 }
 
-ast_expression *intrin_func(intrin_t *intrin, const char *name) {
+static ast_expression *intrin_func_self(intrin_t *intrin, const char *name, const char *from) {
     size_t           i;
     ast_expression  *find;
 
@@ -461,6 +2101,13 @@ ast_expression *intrin_func(intrin_t *intrin, const char *name) {
     if ((find = intrin_func_try(intrin, offsetof(intrin_func_t, alias), name)))
         return find;
 
-    intrin_error(intrin, "need function: `%s` compiler depends on it", name);
+    if (from) {
+        intrin_error(intrin, "need function `%s', compiler depends on it for `__builtin_%s'", name, from);
+        return intrin_func_self(intrin, "#nullfunc", NULL);
+    }
     return NULL;
 }
+
+ast_expression *intrin_func(intrin_t *intrin, const char *name) {
+    return intrin_func_self(intrin, name, NULL);
+}
diff --git a/ir.c b/ir.c
index 7d83720c4d4afb3cd56ec90a08b9cc1ad7dde9d3..7df8bdbff6d7a51b33561a80edb4c96097dc167c 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
@@ -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;
 }
 
@@ -1892,7 +1898,7 @@ ir_value* ir_block_create_unary(ir_block *self, lex_ctx_t ctx,
         case VINSTR_NEG_F:
             return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_F, NULL, operand, ot);
         case VINSTR_NEG_V:
-            return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, NULL, operand, ot);
+            return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, NULL, operand, TYPE_VECTOR);
 
         default:
             ot = operand->vtype;
diff --git a/ir.h b/ir.h
index 1d3503872efefb16a0d6328d822b82f0e76a5e19..5b948358fdee732766bf818a66653350460c823a 100644 (file)
--- a/ir.h
+++ b/ir.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
@@ -48,6 +48,7 @@ enum {
     IR_FLAG_HAS_GOTO             = 1 << 2,
     IR_FLAG_INCLUDE_DEF          = 1 << 3,
     IR_FLAG_ERASEABLE            = 1 << 4,
+    IR_FLAG_BLOCK_COVERAGE       = 1 << 5,
 
     IR_FLAG_LAST,
     IR_FLAG_MASK_NO_OVERLAP      = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED),
@@ -273,6 +274,7 @@ struct ir_builder_s {
     /* there should just be this one nil */
     ir_value    *nil;
     ir_value    *reserved_va_count;
+    ir_value    *coverage_func;
     /* some virtual instructions require temps, and their code is isolated
      * so that we don't need to keep track of their liveness.
      */
diff --git a/lexer.c b/lexer.c
index b6d5ceb6d3bd94aed4e4cc0bb0d2926d932b9b6a..5ad0a90a537f558e2ba0512e79a771f8181426a6 100644 (file)
--- a/lexer.c
+++ b/lexer.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
@@ -82,91 +82,8 @@ static bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
     return r;
 }
 
-
-#if 0
-token* token_new()
-{
-    token *tok = (token*)mem_a(sizeof(token));
-    if (!tok)
-        return NULL;
-    memset(tok, 0, sizeof(*tok));
-    return tok;
-}
-
-void token_delete(token *self)
-{
-    if (self->next && self->next->prev == self)
-        self->next->prev = self->prev;
-    if (self->prev && self->prev->next == self)
-        self->prev->next = self->next;
-    MEM_VECTOR_CLEAR(self, value);
-    mem_d(self);
-}
-
-token* token_copy(const token *cp)
-{
-    token* self = token_new();
-    if (!self)
-        return NULL;
-    /* copy the value */
-    self->value_alloc = cp->value_count + 1;
-    self->value_count = cp->value_count;
-    self->value = (char*)mem_a(self->value_alloc);
-    if (!self->value) {
-        mem_d(self);
-        return NULL;
-    }
-    memcpy(self->value, cp->value, cp->value_count);
-    self->value[self->value_alloc-1] = 0;
-
-    /* rest */
-    self->ctx = cp->ctx;
-    self->ttype = cp->ttype;
-    memcpy(&self->constval, &cp->constval, sizeof(self->constval));
-    return self;
-}
-
-void token_delete_all(token *t)
-{
-    token *n;
-
-    do {
-        n = t->next;
-        token_delete(t);
-        t = n;
-    } while(t);
-}
-
-token* token_copy_all(const token *cp)
-{
-    token *cur;
-    token *out;
-
-    out = cur = token_copy(cp);
-    if (!out)
-        return NULL;
-
-    while (cp->next) {
-        cp = cp->next;
-        cur->next = token_copy(cp);
-        if (!cur->next) {
-            token_delete_all(out);
-            return NULL;
-        }
-        cur->next->prev = cur;
-        cur = cur->next;
-    }
-
-    return out;
-}
-#else
 static void lex_token_new(lex_file *lex)
 {
-#if 0
-    if (lex->tok)
-        token_delete(lex->tok);
-    lex->tok = token_new();
-#else
     if (lex->tok.value)
         vec_shrinkto(lex->tok.value, 0);
 
@@ -174,14 +91,16 @@ static void lex_token_new(lex_file *lex)
     lex->tok.ctx.line    = lex->sline;
     lex->tok.ctx.file    = lex->name;
     lex->tok.ctx.column  = lex->column;
-#endif
 }
-#endif
+
+static void lex_ungetch(lex_file *lex, int ch);
+static int lex_getch(lex_file *lex);
 
 lex_file* lex_open(const char *file)
 {
     lex_file  *lex;
     fs_file_t *in = fs_file_open(file, "rb");
+    uint32_t   read;
 
     if (!in) {
         lexerror(NULL, "open failed: '%s'\n", file);
@@ -204,6 +123,19 @@ lex_file* lex_open(const char *file)
     lex->peekpos = 0;
     lex->eof     = false;
 
+    /* handle BOM */
+    if ((read = (lex_getch(lex) << 16) | (lex_getch(lex) << 8) | lex_getch(lex)) != 0xEFBBBF) {
+        lex_ungetch(lex, (read & 0x0000FF));
+        lex_ungetch(lex, (read & 0x00FF00) >> 8);
+        lex_ungetch(lex, (read & 0xFF0000) >> 16);
+    } else {
+        /*
+         * otherwise the lexer has advanced 3 bytes for the BOM, we need
+         * to set the column back to 0
+         */
+        lex->column = 0;
+    }
+
     vec_push(lex_filenames, lex->name);
     return lex;
 }
@@ -256,16 +188,15 @@ void lex_close(lex_file *lex)
 
     if (lex->file)
         fs_file_close(lex->file);
-#if 0
-    if (lex->tok)
-        token_delete(lex->tok);
-#else
+
     vec_free(lex->tok.value);
-#endif
+
     /* mem_d(lex->name); collected in lex_filenames */
     mem_d(lex);
 }
 
+
+
 static int lex_fgetc(lex_file *lex)
 {
     if (lex->file) {
@@ -286,7 +217,6 @@ static int lex_fgetc(lex_file *lex)
  * are working on.
  * The are merely wrapping get/put in order to count line numbers.
  */
-static void lex_ungetch(lex_file *lex, int ch);
 static int lex_try_trigraph(lex_file *lex, int old)
 {
     int c2, c3;
@@ -606,10 +536,6 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
 
                 if (lex->flags.preprocessing) {
                     haswhite = true;
-                    /*
-                    lex_tokench(lex, '/');
-                    lex_tokench(lex, '/');
-                    */
                     lex_tokench(lex, ' ');
                     lex_tokench(lex, ' ');
                 }
@@ -631,10 +557,6 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
                 /* multiline comment */
                 if (lex->flags.preprocessing) {
                     haswhite = true;
-                    /*
-                    lex_tokench(lex, '/');
-                    lex_tokench(lex, '*');
-                    */
                     lex_tokench(lex, ' ');
                     lex_tokench(lex, ' ');
                 }
@@ -646,10 +568,6 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
                         ch = lex_getch(lex);
                         if (ch == '/') {
                             if (lex->flags.preprocessing) {
-                                /*
-                                lex_tokench(lex, '*');
-                                lex_tokench(lex, '/');
-                                */
                                 lex_tokench(lex, ' ');
                                 lex_tokench(lex, ' ');
                             }
@@ -661,7 +579,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
                         if (ch == '\n')
                             lex_tokench(lex, '\n');
                         else
-                            lex_tokench(lex, ' '); /* ch); */
+                            lex_tokench(lex, ' ');
                     }
                 }
                 ch = ' '; /* cause TRUE in the isspace check */
@@ -1008,10 +926,6 @@ int lex_do(lex_file *lex)
     bool hadwhite = false;
 
     lex_token_new(lex);
-#if 0
-    if (!lex->tok)
-        return TOKEN_FATAL;
-#endif
 
     while (true) {
         ch = lex_skipwhite(lex, hadwhite);
@@ -1256,10 +1170,6 @@ int lex_do(lex_file *lex)
          */
         switch (ch)
         {
-            /*
-            case '+':
-            case '-':
-            */
             case '*':
             case '/':
             case '<':
@@ -1323,12 +1233,16 @@ int lex_do(lex_file *lex)
         ch == '~' || ch == '^'    /* ~=, ~, ^                        */
     )  {
         lex_tokench(lex, ch);
-
         nextch = lex_getch(lex);
-        if ((nextch == '=' && ch != '<') ||
-            (nextch == ch  && ch != '!') ||
-            (nextch == '<' && ch == '>')) {
+
+        if ((nextch == '=' && ch != '<') || (nextch == '<' && ch == '>'))
+            lex_tokench(lex, nextch);
+        else if (nextch == ch && ch != '!') {
             lex_tokench(lex, nextch);
+            if ((thirdch = lex_getch(lex)) == '=')
+                lex_tokench(lex, thirdch);
+            else
+                lex_ungetch(lex, thirdch);
         } else if (ch == '<' && nextch == '=') {
             lex_tokench(lex, nextch);
             if ((thirdch = lex_getch(lex)) == '>')
@@ -1367,15 +1281,6 @@ int lex_do(lex_file *lex)
         return (lex->tok.ttype = TOKEN_OPERATOR);
     }
 
-    /*
-    if (ch == '^' || ch == '~' || ch == '!')
-    {
-        lex_tokench(lex, ch);
-        lex_endtoken(lex);
-        return (lex->tok.ttype = TOKEN_OPERATOR);
-    }
-    */
-
     if (ch == '*' || ch == '/') /* *=, /= */
     {
         lex_tokench(lex, ch);
diff --git a/lexer.h b/lexer.h
index 44f6491725c17a5dc458cdf789a0e2b8cf248450..41a135549f5cce35b22aeb20203ab6ecfd4e8a2e 100644 (file)
--- a/lexer.h
+++ b/lexer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
@@ -22,6 +22,8 @@
  */
 #ifndef GMQCC_LEXER_HDR
 #define GMQCC_LEXER_HDR
+#include "gmqcc.h"
+
 typedef struct token_s token;
 
 struct token_s {
@@ -185,8 +187,7 @@ static const oper_info c_operators[] = {
     { "++",  1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
     { "--",  1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
 
-    { "**",  2, opid2('*','*'),     ASSOC_RIGHT, 15, 0,         true},
-
+    { "**",  2, opid2('*','*'),     ASSOC_RIGHT, 14, 0,         true},
     { "!",   1, opid2('!','P'),     ASSOC_RIGHT, 14, OP_PREFIX, true},
     { "~",   1, opid2('~','P'),     ASSOC_RIGHT, 14, OP_PREFIX, true},
     { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX, false},
diff --git a/main.c b/main.c
index 78293d6d7615a3682a6153ff6b221381e5dc44cf..156a3f0c1af5c81c4c72d7ac70dc64087ea5c29c 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *     Wolfgang Bumiller
  *
@@ -85,6 +85,7 @@ static int usage(void) {
             "  -Ono-<name>            disable specific optimization\n"
             "  -Ohelp                 list optimizations\n");
     con_out("  -force-crc=num         force a specific checksum into the header\n");
+    con_out("  -coverage              add coverage support\n");
     return -1;
 }
 
@@ -280,6 +281,10 @@ static bool options_parse(int argc, char **argv) {
                 con_color(0);
                 continue;
             }
+            if (!strcmp(argv[0]+1, "coverage")) {
+                OPTS_OPTION_BOOL(OPTION_COVERAGE) = true;
+                continue;
+            }
 
             switch (argv[0][1]) {
                 /* -h, show usage but exit with 0 */
diff --git a/msvc.c b/msvc.c
index 3369cd6f8e7b00fe115abe5dc4840a20ed740a2e..b565cfaf9da8c0fef7aa9cef4797d6bda3b5e991 100644 (file)
--- a/msvc.c
+++ b/msvc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/opts.c b/opts.c
index e50bd7441791925861f046a3420b1d90e6d0454a..79980247333fecd7aa105ef4f0bc52f79e0db62f 100644 (file)
--- a/opts.c
+++ b/opts.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
@@ -92,6 +92,7 @@ static void opts_setdefault(void) {
     opts_set(opts.warn,  WARN_PARENTHESIS,               true);
     opts_set(opts.warn,  WARN_CONST_OVERWRITE,           true);
     opts_set(opts.warn,  WARN_DIRECTIVE_INMACRO,         true);
+    opts_set(opts.warn,  WARN_BUILTINS,                  true);
 
     /* flags */
     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,           true);
index 57625ddb3ce23bde3bbfde21b48e364971cd3bf1..b2292452b2a63ec940c89325bd59d1d0ad9ee107 100644 (file)
--- a/opts.def
+++ b/opts.def
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
@@ -31,6 +31,7 @@
     GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
     GMQCC_DEFINE_FLAG(FTEPP)
     GMQCC_DEFINE_FLAG(FTEPP_PREDEFS)
+    GMQCC_DEFINE_FLAG(FTEPP_MATHDEFS)
     GMQCC_DEFINE_FLAG(RELAXED_SWITCH)
     GMQCC_DEFINE_FLAG(SHORT_LOGIC)
     GMQCC_DEFINE_FLAG(PERL_LOGIC)
@@ -96,6 +97,7 @@
     GMQCC_DEFINE_FLAG(BREAKDEF)
     GMQCC_DEFINE_FLAG(CONST_OVERWRITE)
     GMQCC_DEFINE_FLAG(DIRECTIVE_INMACRO)
+    GMQCC_DEFINE_FLAG(BUILTINS)
 #endif
 
 #ifdef GMQCC_TYPE_OPTIMIZATIONS
     GMQCC_DEFINE_FLAG(CORRECTION)
     GMQCC_DEFINE_FLAG(STATISTICS)
     GMQCC_DEFINE_FLAG(PROGSRC)
+    GMQCC_DEFINE_FLAG(COVERAGE)
 #endif
 
 /* some cleanup so we don't have to */
diff --git a/pak.c b/pak.c
index 6fdd88b83a61b9e288b0849f2151c250ab288272..7de0ec6008c6d2a41beb868ea2aad4e9cf57da21 100644 (file)
--- a/pak.c
+++ b/pak.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013
+ * Copyright (C) 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
index 2dc25508449e2771220d08d5907c3f01799fb183..eaa14903d7681416e69b53fdbbc76c121bc678a0 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
@@ -476,7 +476,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                                   type_name[exprs[0]->vtype]);
                 return false;
             }
-            out = (ast_expression*)ast_unary_new(ctx, (VINSTR_NEG_F-TYPE_FLOAT) + exprs[0]->vtype, exprs[0]);
+            if (exprs[0]->vtype == TYPE_FLOAT)
+                out = (ast_expression*)ast_unary_new(ctx, VINSTR_NEG_F, exprs[0]);
+            else
+                out = (ast_expression*)ast_unary_new(ctx, VINSTR_NEG_V, exprs[0]);
             break;
 
         case opid2('!','P'):
@@ -684,12 +687,44 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 
         case opid2('<','<'):
         case opid2('>','>'):
+            if (NotSameType(TYPE_FLOAT)) {
+                compile_error(ctx, "invalid types used in expression: cannot perform shift between types %s and %s",
+                    type_name[exprs[0]->vtype],
+                    type_name[exprs[1]->vtype]);
+                return false;
+            }
+
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                ast_expression *shift = intrin_func(parser->intrin, (op->id == opid2('<','<')) ? "__builtin_lshift" : "__builtin_rshift");
+                ast_call       *call  = ast_call_new(parser_ctx(parser), shift);
+                vec_push(call->params, exprs[0]);
+                vec_push(call->params, exprs[1]);
+                out = (ast_expression*)call;
+            }
+            break;
+
         case opid3('<','<','='):
         case opid3('>','>','='):
-            if(!(out = fold_op(parser->fold, op, exprs))) {
-                compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
+            if (NotSameType(TYPE_FLOAT)) {
+                compile_error(ctx, "invalid types used in expression: cannot perform shift operation between types %s and %s",
+                    type_name[exprs[0]->vtype],
+                    type_name[exprs[1]->vtype]);
                 return false;
             }
+
+            if(!(out = fold_op(parser->fold, op, exprs))) {
+                ast_expression *shift = intrin_func(parser->intrin, (op->id == opid3('<','<','=')) ? "__builtin_lshift" : "__builtin_rshift");
+                ast_call       *call  = ast_call_new(parser_ctx(parser), shift);
+                vec_push(call->params, exprs[0]);
+                vec_push(call->params, exprs[1]);
+                out = (ast_expression*)ast_store_new(
+                    parser_ctx(parser),
+                    INSTR_STORE_F,
+                    exprs[0],
+                    (ast_expression*)call
+                );
+            }
+
             break;
 
         case opid2('|','|'):
@@ -1581,6 +1616,23 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                 var = intrin_func(parser->intrin, parser_tokval(parser));
             }
 
+            /*
+             * Try it again, intrin_func deals with the alias method as well
+             * the first one masks for __builtin though, we emit warning here.
+             */
+            if (!var) {
+                if ((var = intrin_func(parser->intrin, parser_tokval(parser)))) {
+                    (void)!compile_warning(
+                        parser_ctx(parser),
+                        WARN_BUILTINS,
+                        "using implicitly defined builtin `__builtin_%s' for `%s'",
+                        parser_tokval(parser),
+                        parser_tokval(parser)
+                    );
+                }
+            }
+
+
             if (!var) {
                 char *correct = NULL;
                 size_t i;
@@ -2751,6 +2803,11 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
 /* returns true when it was a variable qualifier, false otherwise!
  * on error, cvq is set to CV_WRONG
  */
+typedef struct {
+    const char *name;
+    size_t      flag;
+} attribute_t;
+
 static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags, char **message)
 {
     bool had_const    = false;
@@ -2760,8 +2817,18 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
     bool had_static   = false;
     uint32_t flags    = 0;
 
-    *cvq = CV_NONE;
+    static attribute_t attributes[] = {
+        { "noreturn",   AST_FLAG_NORETURN   },
+        { "inline",     AST_FLAG_INLINE     },
+        { "eraseable",  AST_FLAG_ERASEABLE  },
+        { "accumulate", AST_FLAG_ACCUMULATE },
+        { "last",       AST_FLAG_FINAL_DECL }
+    };
+
+   *cvq = CV_NONE;
+
     for (;;) {
+        size_t i;
         if (parser->tok == TOKEN_ATTRIBUTE_OPEN) {
             had_attrib = true;
             /* parse an attribute */
@@ -2770,15 +2837,25 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                 *cvq = CV_WRONG;
                 return false;
             }
-            if (!strcmp(parser_tokval(parser), "noreturn")) {
-                flags |= AST_FLAG_NORETURN;
-                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
-                    parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`");
-                    *cvq = CV_WRONG;
-                    return false;
+
+            for (i = 0; i < GMQCC_ARRAY_COUNT(attributes); i++) {
+                if (!strcmp(parser_tokval(parser), attributes[i].name)) {
+                    flags |= attributes[i].flag;
+                    if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                        parseerror(parser, "`%s` attribute has no parameters, expected `]]`",
+                            attributes[i].name);
+                        *cvq = CV_WRONG;
+                        return false;
+                    }
+                    break;
                 }
             }
-            else if (!strcmp(parser_tokval(parser), "noref")) {
+
+            if (i != GMQCC_ARRAY_COUNT(attributes))
+                goto leave;
+
+
+            if (!strcmp(parser_tokval(parser), "noref")) {
                 had_noref = true;
                 if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
                     parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
@@ -2786,30 +2863,6 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                     return false;
                 }
             }
-            else if (!strcmp(parser_tokval(parser), "inline")) {
-                flags |= AST_FLAG_INLINE;
-                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
-                    parseerror(parser, "`inline` attribute has no parameters, expected `]]`");
-                    *cvq = CV_WRONG;
-                    return false;
-                }
-            }
-            else if (!strcmp(parser_tokval(parser), "eraseable")) {
-                flags |= AST_FLAG_ERASEABLE;
-                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
-                    parseerror(parser, "`eraseable` attribute has no parameters, expected `]]`");
-                    *cvq = CV_WRONG;
-                    return false;
-                }
-            }
-            else if (!strcmp(parser_tokval(parser), "accumulate")) {
-                flags |= AST_FLAG_ACCUMULATE;
-                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
-                    parseerror(parser, "`accumulate` attribute has no parameters, expected `]]`");
-                    *cvq = CV_WRONG;
-                    return false;
-                }
-            }
             else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
                 flags   |= AST_FLAG_ALIAS;
                 *message = NULL;
@@ -2891,6 +2944,47 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                     return false;
                 }
             }
+            else if (!strcmp(parser_tokval(parser), "coverage") && !(flags & AST_FLAG_COVERAGE)) {
+                flags |= AST_FLAG_COVERAGE;
+                if (!parser_next(parser)) {
+                    error_in_coverage:
+                    parseerror(parser, "parse error in coverage attribute");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+                if (parser->tok == '(') {
+                    if (!parser_next(parser)) {
+                        bad_coverage_arg:
+                        parseerror(parser, "invalid parameter for coverage() attribute\n"
+                                           "valid are: block");
+                        *cvq = CV_WRONG;
+                        return false;
+                    }
+                    if (parser->tok != ')') {
+                        do {
+                            if (parser->tok != TOKEN_IDENT)
+                                goto bad_coverage_arg;
+                            if (!strcmp(parser_tokval(parser), "block"))
+                                flags |= AST_FLAG_BLOCK_COVERAGE;
+                            else if (!strcmp(parser_tokval(parser), "none"))
+                                flags &= ~(AST_FLAG_COVERAGE_MASK);
+                            else
+                                goto bad_coverage_arg;
+                            if (!parser_next(parser))
+                                goto error_in_coverage;
+                            if (parser->tok == ',') {
+                                if (!parser_next(parser))
+                                    goto error_in_coverage;
+                            }
+                        } while (parser->tok != ')');
+                    }
+                    if (parser->tok != ')' || !parser_next(parser))
+                        goto error_in_coverage;
+                } else {
+                    /* without parameter [[coverage]] equals [[coverage(block)]] */
+                    flags |= AST_FLAG_BLOCK_COVERAGE;
+                }
+            }
             else
             {
                 /* Skip tokens until we hit a ]] */
@@ -2919,6 +3013,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
         }
         else
             break;
+
+        leave:
         if (!parser_next(parser))
             goto onerr;
     }
@@ -4311,6 +4407,7 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con
         parseerror(parser, "failed to create accessor function value");
         return false;
     }
+    fval->expression.flags &= ~(AST_FLAG_COVERAGE_MASK);
 
     func = ast_function_new(ast_ctx(array), funcname, fval);
     if (!func) {
@@ -5108,6 +5205,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         }
 
         var->cvq = qualifier;
+        if (qflags & AST_FLAG_COVERAGE) /* specified in QC, drop our default */
+            var->expression.flags &= ~(AST_FLAG_COVERAGE_MASK);
         var->expression.flags |= qflags;
 
         /*
@@ -5237,6 +5336,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             retval = false;
                             goto cleanup;
                         }
+                        if (old->flags & AST_FLAG_FINAL_DECL) {
+                            parseerror(parser, "cannot redeclare variable `%s`, declared final here: %s:%i",
+                                       var->name, ast_ctx(old).file, ast_ctx(old).line);
+                            retval = false;
+                            goto cleanup;
+                        }
                         proto = (ast_value*)old;
                         if (!ast_istype(old, ast_value)) {
                             parseerror(parser, "internal error: not an ast_value");
@@ -5250,6 +5355,11 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             goto cleanup;
                         }
                         proto->expression.flags |= var->expression.flags;
+                        /* copy the context for finals,
+                         * so the error can show where it was actually made 'final'
+                         */
+                        if (proto->expression.flags & AST_FLAG_FINAL_DECL)
+                            ast_ctx(old) = ast_ctx(var);
                         ast_delete(var);
                         var = proto;
                     }
@@ -5421,6 +5531,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                      */
                     char   *defname = NULL;
                     size_t  prefix_len, ln;
+                    size_t  sn, sn_size;
 
                     ln = strlen(parser->function->name);
                     vec_append(defname, ln, parser->function->name);
@@ -5442,6 +5553,24 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     /* now rename the global */
                     ln = strlen(var->name);
                     vec_append(defname, ln, var->name);
+                    /* if a variable of that name already existed, add the
+                     * counter value.
+                     * The counter is incremented either way.
+                     */
+                    sn_size = vec_size(parser->function->static_names);
+                    for (sn = 0; sn != sn_size; ++sn) {
+                        if (strcmp(parser->function->static_names[sn], var->name) == 0)
+                            break;
+                    }
+                    if (sn != sn_size) {
+                        char *num = NULL;
+                        int   len = util_asprintf(&num, "#%u", parser->function->static_count);
+                        vec_append(defname, len, num);
+                        mem_d(num);
+                    }
+                    else
+                        vec_push(parser->function->static_names, util_strdup(var->name));
+                    parser->function->static_count++;
                     ast_value_set_name(var, defname);
 
                     /* push it to the to-be-generated globals */
@@ -5692,17 +5821,18 @@ skipvar:
             if (!cexp)
                 break;
 
-            if (!localblock) {
+            if (!localblock || is_static) {
                 cval = (ast_value*)cexp;
                 if (cval != parser->nil &&
                     (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
                    )
                 {
-                    parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
+                    parseerror(parser, "initializer is non constant");
                 }
                 else
                 {
-                    if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
+                    if (!is_static &&
+                        !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
                         qualifier != CV_VAR)
                     {
                         var->cvq = CV_CONST;
@@ -6115,11 +6245,51 @@ void parser_cleanup(parser_t *parser)
     mem_d(parser);
 }
 
+static bool parser_set_coverage_func(parser_t *parser, ir_builder *ir) {
+    size_t          i;
+    ast_expression *expr;
+    ast_value      *cov;
+    ast_function   *func;
+
+    if (!OPTS_OPTION_BOOL(OPTION_COVERAGE))
+        return true;
+
+    func = NULL;
+    for (i = 0; i != vec_size(parser->functions); ++i) {
+        if (!strcmp(parser->functions[i]->name, "coverage")) {
+            func = parser->functions[i];
+            break;
+        }
+    }
+    if (!func) {
+        if (OPTS_OPTION_BOOL(OPTION_COVERAGE)) {
+            con_out("coverage support requested but no coverage() builtin declared\n");
+            ir_builder_delete(ir);
+            return false;
+        }
+        return true;
+    }
+
+    cov  = func->vtype;
+    expr = (ast_expression*)cov;
+
+    if (expr->vtype != TYPE_FUNCTION || vec_size(expr->params) != 0) {
+        char ty[1024];
+        ast_type_to_string(expr, ty, sizeof(ty));
+        con_out("invalid type for coverage(): %s\n", ty);
+        ir_builder_delete(ir);
+        return false;
+    }
+
+    ir->coverage_func = func->ir_func->value;
+    return true;
+}
+
 bool parser_finish(parser_t *parser, const char *output)
 {
-    size_t i;
-    ir_builder *ir;
-    bool retval = true;
+    size_t          i;
+    ir_builder     *ir;
+    bool            retval = true;
 
     if (compile_errors) {
         con_out("*** there were compile errors\n");
@@ -6201,6 +6371,10 @@ bool parser_finish(parser_t *parser, const char *output)
     if (!fold_generate(parser->fold, ir))
         return false;
 
+    /* before generating any functions we need to set the coverage_func */
+    if (!parser_set_coverage_func(parser, ir))
+        return false;
+
     for (i = 0; i < vec_size(parser->globals); ++i) {
         ast_value *asvalue;
         if (!ast_istype(parser->globals[i], ast_value))
index 0396a7e17b3af01ac8e1fdf1f1004a18c8bbed57..51140be9781325ed369c37acfadad595575d7498 100644 (file)
--- a/parser.h
+++ b/parser.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
index acb632ee2eba9a1927b4ebf094bc9ce6d5c15f99..025aa980b6e7811c16a2b677b4b44642f24d1894 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/stat.c b/stat.c
index e46102da51d3ec8e337cfb49217615be63af7f56..24a3466a16e9c7f4f83b3354b157bb5bbb778aeb 100644 (file)
--- a/stat.c
+++ b/stat.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *     Wolfgang Bumiller
  *
@@ -353,139 +353,11 @@ typedef struct hash_node_t {
     struct hash_node_t *next;  /* next node (linked list)        */
 } hash_node_t;
 
-/*
- * This is a patched version of the Murmur2 hashing function to use
- * a proper pre-mix and post-mix setup. Infact this is Murmur3 for
- * the most part just reinvented.
- *
- * Murmur 2 contains an inner loop such as:
- * while (l >= 4) {
- *      u32 k = *(u32*)d;
- *      k *= m;
- *      k ^= k >> r;
- *      k *= m;
- *
- *      h *= m;
- *      h ^= k;
- *      d += 4;
- *      l -= 4;
- * }
- *
- * The two u32s that form the key are the same value x (pulled from data)
- * this premix stage will perform the same results for both values. Unrolled
- * this produces just:
- *  x *= m;
- *  x ^= x >> r;
- *  x *= m;
- *
- *  h *= m;
- *  h ^= x;
- *  h *= m;
- *  h ^= x;
- *
- * This appears to be fine, except what happens when m == 1? well x
- * cancels out entierly, leaving just:
- *  x ^= x >> r;
- *  h ^= x;
- *  h ^= x;
- *
- * So all keys hash to the same value, but how often does m == 1?
- * well, it turns out testing x for all possible values yeilds only
- * 172,013,942 unique results instead of 2^32. So nearly ~4.6 bits
- * are cancelled out on average!
- *
- * This means we have a 14.5% (rounded) chance of colliding more, which
- * results in another bucket/chain for the hashtable.
- *
- * We fix it buy upgrading the pre and post mix ssystems to align with murmur
- * hash 3.
- */
-#if 1
-#define GMQCC_ROTL32(X, R) (((X) << (R)) | ((X) >> (32 - (R))))
-GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) {
-    const unsigned char *data   = (const unsigned char *)key;
-    const size_t         len    = strlen(key);
-    const size_t         block  = len / 4;
-    const uint32_t       mask1  = 0xCC9E2D51;
-    const uint32_t       mask2  = 0x1B873593;
-    const uint32_t      *blocks = (const uint32_t*)(data + block * 4);
-    const unsigned char *tail   = (const unsigned char *)(data + block * 4);
-
-    size_t   i;
-    uint32_t k;
-    uint32_t h = 0x1EF0 ^ len;
-
-    for (i = -((int)block); i; i++) {
-        k  = blocks[i];
-        k *= mask1;
-        k  = GMQCC_ROTL32(k, 15);
-        k *= mask2;
-        h ^= k;
-        h  = GMQCC_ROTL32(h, 13);
-        h  = h * 5 + 0xE6546B64;
-    }
 
-    k = 0;
-    switch (len & 3) {
-        case 3:
-            k ^= tail[2] << 16;
-        case 2:
-            k ^= tail[1] << 8;
-        case 1:
-            k ^= tail[0];
-            k *= mask1;
-            k  = GMQCC_ROTL32(k, 15);
-            k *= mask2;
-            h ^= k;
-    }
-
-    h ^= len;
-    h ^= h >> 16;
-    h *= 0x85EBCA6B;
-    h ^= h >> 13;
-    h *= 0xC2B2AE35;
-    h ^= h >> 16;
-
-    return (size_t) (h % ht->size);
+size_t hash(const char *key);
+size_t util_hthash(hash_table_t *ht, const char *key) {
+    return hash(key) % ht->size;
 }
-#undef GMQCC_ROTL32
-#else
-/* We keep the old for reference */
-GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) {
-    const uint32_t       mix   = 0x5BD1E995;
-    const uint32_t       rot   = 24;
-    size_t               size  = strlen(key);
-    uint32_t             hash  = 0x1EF0 /* LICRC TAB */  ^ size;
-    uint32_t             alias = 0;
-    const unsigned char *data  = (const unsigned char*)key;
-
-    while (size >= 4) {
-        alias  = (data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
-        alias *= mix;
-        alias ^= alias >> rot;
-        alias *= mix;
-
-        hash  *= mix;
-        hash  ^= alias;
-
-        data += 4;
-        size -= 4;
-    }
-
-    switch (size) {
-        case 3: hash ^= data[2] << 16;
-        case 2: hash ^= data[1] << 8;
-        case 1: hash ^= data[0];
-                hash *= mix;
-    }
-
-    hash ^= hash >> 13;
-    hash *= mix;
-    hash ^= hash >> 15;
-
-    return (size_t) (hash % ht->size);
-}
-#endif
 
 static hash_node_t *_util_htnewpair(const char *key, void *value) {
     hash_node_t *node;
@@ -685,7 +557,7 @@ void util_htdel(hash_table_t *ht) {
  * information.
  */
 static void stat_dump_mem_contents(stat_mem_block_t *block, uint16_t cols) {
-    unsigned char *buffer = mem_a(cols);
+    unsigned char *buffer = (unsigned char *)mem_a(cols);
     unsigned char *memory = (unsigned char *)(block + 1);
     size_t         i;
 
diff --git a/test.c b/test.c
index 6ce99964b24f408b5fd482a2ddff7eb83d4d7ce9..c726b02f1ebe0999779525f7e2be087477bb93d2 100644 (file)
--- a/test.c
+++ b/test.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/tests/last.qc b/tests/last.qc
new file mode 100644 (file)
index 0000000..b501578
--- /dev/null
@@ -0,0 +1,5 @@
+float here;
+[[last]] float here;
+#ifdef MAKE_IT_FAIL
+float here;
+#endif
diff --git a/tests/last.tmpl b/tests/last.tmpl
new file mode 100644 (file)
index 0000000..6ca7151
--- /dev/null
@@ -0,0 +1,5 @@
+I: last.qc
+D: last attribute
+T: -compile
+C: -std=gmqcc -fftepp
+F: -no-defs
diff --git a/tests/last2.tmpl b/tests/last2.tmpl
new file mode 100644 (file)
index 0000000..8632710
--- /dev/null
@@ -0,0 +1,5 @@
+I: last.qc
+D: last attribute
+T: -fail
+C: -std=gmqcc -fftepp -DMAKE_IT_FAIL
+F: -no-defs
diff --git a/utf8.c b/utf8.c
index 126ca311ae0bf65624eb6f4c3e4cb023ad2843e5..06a651ac5fada4e3cbdc6e9629c2b1deade8abc5 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/util.c b/util.c
index 466729eceb5631f7b399a5978ebd7910b044086b..4e1f4f013074b4f93928d8b55d196f7ea6211df1 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
  *     Dale Weiler
  *     Wolfgang Bumiller
  *
@@ -127,81 +127,380 @@ void util_endianswap(void *_data, size_t length, unsigned int typesize) {
 }
 
 /*
- * CRC algorithms vary in the width of the polynomial, the value of said polynomial,
- * the initial value used for the register, weather the bits of each byte are reflected
- * before being processed, weather the algorithm itself feeds input bytes through the
- * register or XORs them with a byte from one end and then straight into the table, as
- * well as (but not limited to the idea of reflected versions) where the final register
- * value becomes reversed, and finally weather the value itself is used to XOR the final
- * register value.  AS such you can already imagine how painfully annoying CRCs are,
- * of course we stand to target Quake, which expects it's certain set of rules for proper
- * calculation of a CRC.
- *
- * In most traditional CRC algorithms on uses a reflected table driven method where a value
- * or register is reflected if it's bits are swapped around it's center.  For example:
- * take the bits 0101 is the 4-bit reflection of 1010, and respectfully 0011 would be the
- * reflection of 1100. Quake however expects a NON-Reflected CRC on the output, but still
- * requires a final XOR on the values (0xFFFF and 0x0000) this is a standard CCITT CRC-16
- * which I respectfully as a programmer don't agree with.
+* Based On:
+*   Slicing-by-8 algorithms by Michael E.
+*       Kounavis and Frank L. Berry from Intel Corp.
+*       http://www.intel.com/technology/comms/perfnet/download/CRC_generators.pdf
+*
+*   This code was made to be slightly less confusing with macros, which
+*   I suppose is somewhat ironic.
+*
+*   The code had to be changed for non reflected on the output register
+*   since that's the method Quake uses.
+*
+*   The code also had to be changed for CRC16, which is slightly harder
+*   since the CRC32 method in the original Intel paper used a different
+*   bit order convention.
+*
+* Notes about the table:
+*   - It's exactly 4K in size
+*   - 64 elements fit in a cache line
+*   - can do 8 iterations unrolled 8 times for free
+*   - The first 256 elements of the table are standard CRC16 table
+*
+* Table can be generated with the following utility:
+*/
+#if 0
+#include <stdio.h>
+#include <stdint.h>
+int main(void) {
+    for (unsigned i = 0; i < 0x100; ++i) {
+        uint16_t x = i << 8;
+        for (int j = 0; j < 8; ++j)
+            x = (x << 1) ^ ((x & 0x8000) ? 0x1021 : 0);
+        tab[0][i] = x;
+    }
+    for (unsigned i = 0; i < 0x100; ++i) {
+        uint16_t c = tab[0][i];
+        for (unsigned j = 1; j < 8; ++j) {
+            c = tab[0][c >> 8] ^ (c << 8);
+            tab[j][i] = c;
+        }
+    }
+    printf("static const uint16_t util_crc16_table[8][256] = {");
+    for (int i = 0; i < 8; ++i) {
+        printf("{\n");
+        for (int j = 0; j < 0x100; ++j) {
+            printf((j & 7) ? " " : "    ");
+            printf((j != 0x100-1) ? "0x%04X," : "0x%04X", tab[i][j]);
+            if ((j & 7) == 7)
+                printf("\n");
+        }
+        printf((i != 7) ? "}," : "}");
+    }
+    printf("};\n");
+    return 0;
+}
+#endif
+/*
+ * Non-Reflective version is present as well as a reference.
  *
- * So now you know what we target, and why we target it, despite how unsettling it may seem
- * but those are what Quake seems to request.
+ * TODO:
+ *  combine the crc16 into u32s and mask off low high for byte order
+ *  to make the arrays smaller.
  */
 
-static const uint16_t util_crc16_table[] = {
-    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
-    0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
-    0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
-    0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
-    0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
-    0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
-    0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
-    0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
-    0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
-    0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
-    0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
-    0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
-    0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
-    0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
-    0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
-    0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
-    0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
-    0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
-    0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
-    0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
-    0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
-    0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
-    0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
-    0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
-    0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
-    0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
-    0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
-    0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
-    0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
-};
+static const uint16_t util_crc16_table[8][256] = {{
+    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+    0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+    0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+    0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+    0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+    0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+    0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+    0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+    0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+    0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+    0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+    0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+    0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+    0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+    0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+    0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+    0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+    0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+    0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+    0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+    0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+    0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+    0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+    0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+    0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+    0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+    0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+    0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+    0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+    0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+    0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+    0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+},{
+    0x0000, 0x3331, 0x6662, 0x5553, 0xCCC4, 0xFFF5, 0xAAA6, 0x9997,
+    0x89A9, 0xBA98, 0xEFCB, 0xDCFA, 0x456D, 0x765C, 0x230F, 0x103E,
+    0x0373, 0x3042, 0x6511, 0x5620, 0xCFB7, 0xFC86, 0xA9D5, 0x9AE4,
+    0x8ADA, 0xB9EB, 0xECB8, 0xDF89, 0x461E, 0x752F, 0x207C, 0x134D,
+    0x06E6, 0x35D7, 0x6084, 0x53B5, 0xCA22, 0xF913, 0xAC40, 0x9F71,
+    0x8F4F, 0xBC7E, 0xE92D, 0xDA1C, 0x438B, 0x70BA, 0x25E9, 0x16D8,
+    0x0595, 0x36A4, 0x63F7, 0x50C6, 0xC951, 0xFA60, 0xAF33, 0x9C02,
+    0x8C3C, 0xBF0D, 0xEA5E, 0xD96F, 0x40F8, 0x73C9, 0x269A, 0x15AB,
+    0x0DCC, 0x3EFD, 0x6BAE, 0x589F, 0xC108, 0xF239, 0xA76A, 0x945B,
+    0x8465, 0xB754, 0xE207, 0xD136, 0x48A1, 0x7B90, 0x2EC3, 0x1DF2,
+    0x0EBF, 0x3D8E, 0x68DD, 0x5BEC, 0xC27B, 0xF14A, 0xA419, 0x9728,
+    0x8716, 0xB427, 0xE174, 0xD245, 0x4BD2, 0x78E3, 0x2DB0, 0x1E81,
+    0x0B2A, 0x381B, 0x6D48, 0x5E79, 0xC7EE, 0xF4DF, 0xA18C, 0x92BD,
+    0x8283, 0xB1B2, 0xE4E1, 0xD7D0, 0x4E47, 0x7D76, 0x2825, 0x1B14,
+    0x0859, 0x3B68, 0x6E3B, 0x5D0A, 0xC49D, 0xF7AC, 0xA2FF, 0x91CE,
+    0x81F0, 0xB2C1, 0xE792, 0xD4A3, 0x4D34, 0x7E05, 0x2B56, 0x1867,
+    0x1B98, 0x28A9, 0x7DFA, 0x4ECB, 0xD75C, 0xE46D, 0xB13E, 0x820F,
+    0x9231, 0xA100, 0xF453, 0xC762, 0x5EF5, 0x6DC4, 0x3897, 0x0BA6,
+    0x18EB, 0x2BDA, 0x7E89, 0x4DB8, 0xD42F, 0xE71E, 0xB24D, 0x817C,
+    0x9142, 0xA273, 0xF720, 0xC411, 0x5D86, 0x6EB7, 0x3BE4, 0x08D5,
+    0x1D7E, 0x2E4F, 0x7B1C, 0x482D, 0xD1BA, 0xE28B, 0xB7D8, 0x84E9,
+    0x94D7, 0xA7E6, 0xF2B5, 0xC184, 0x5813, 0x6B22, 0x3E71, 0x0D40,
+    0x1E0D, 0x2D3C, 0x786F, 0x4B5E, 0xD2C9, 0xE1F8, 0xB4AB, 0x879A,
+    0x97A4, 0xA495, 0xF1C6, 0xC2F7, 0x5B60, 0x6851, 0x3D02, 0x0E33,
+    0x1654, 0x2565, 0x7036, 0x4307, 0xDA90, 0xE9A1, 0xBCF2, 0x8FC3,
+    0x9FFD, 0xACCC, 0xF99F, 0xCAAE, 0x5339, 0x6008, 0x355B, 0x066A,
+    0x1527, 0x2616, 0x7345, 0x4074, 0xD9E3, 0xEAD2, 0xBF81, 0x8CB0,
+    0x9C8E, 0xAFBF, 0xFAEC, 0xC9DD, 0x504A, 0x637B, 0x3628, 0x0519,
+    0x10B2, 0x2383, 0x76D0, 0x45E1, 0xDC76, 0xEF47, 0xBA14, 0x8925,
+    0x991B, 0xAA2A, 0xFF79, 0xCC48, 0x55DF, 0x66EE, 0x33BD, 0x008C,
+    0x13C1, 0x20F0, 0x75A3, 0x4692, 0xDF05, 0xEC34, 0xB967, 0x8A56,
+    0x9A68, 0xA959, 0xFC0A, 0xCF3B, 0x56AC, 0x659D, 0x30CE, 0x03FF
+},{
+    0x0000, 0x3730, 0x6E60, 0x5950, 0xDCC0, 0xEBF0, 0xB2A0, 0x8590,
+    0xA9A1, 0x9E91, 0xC7C1, 0xF0F1, 0x7561, 0x4251, 0x1B01, 0x2C31,
+    0x4363, 0x7453, 0x2D03, 0x1A33, 0x9FA3, 0xA893, 0xF1C3, 0xC6F3,
+    0xEAC2, 0xDDF2, 0x84A2, 0xB392, 0x3602, 0x0132, 0x5862, 0x6F52,
+    0x86C6, 0xB1F6, 0xE8A6, 0xDF96, 0x5A06, 0x6D36, 0x3466, 0x0356,
+    0x2F67, 0x1857, 0x4107, 0x7637, 0xF3A7, 0xC497, 0x9DC7, 0xAAF7,
+    0xC5A5, 0xF295, 0xABC5, 0x9CF5, 0x1965, 0x2E55, 0x7705, 0x4035,
+    0x6C04, 0x5B34, 0x0264, 0x3554, 0xB0C4, 0x87F4, 0xDEA4, 0xE994,
+    0x1DAD, 0x2A9D, 0x73CD, 0x44FD, 0xC16D, 0xF65D, 0xAF0D, 0x983D,
+    0xB40C, 0x833C, 0xDA6C, 0xED5C, 0x68CC, 0x5FFC, 0x06AC, 0x319C,
+    0x5ECE, 0x69FE, 0x30AE, 0x079E, 0x820E, 0xB53E, 0xEC6E, 0xDB5E,
+    0xF76F, 0xC05F, 0x990F, 0xAE3F, 0x2BAF, 0x1C9F, 0x45CF, 0x72FF,
+    0x9B6B, 0xAC5B, 0xF50B, 0xC23B, 0x47AB, 0x709B, 0x29CB, 0x1EFB,
+    0x32CA, 0x05FA, 0x5CAA, 0x6B9A, 0xEE0A, 0xD93A, 0x806A, 0xB75A,
+    0xD808, 0xEF38, 0xB668, 0x8158, 0x04C8, 0x33F8, 0x6AA8, 0x5D98,
+    0x71A9, 0x4699, 0x1FC9, 0x28F9, 0xAD69, 0x9A59, 0xC309, 0xF439,
+    0x3B5A, 0x0C6A, 0x553A, 0x620A, 0xE79A, 0xD0AA, 0x89FA, 0xBECA,
+    0x92FB, 0xA5CB, 0xFC9B, 0xCBAB, 0x4E3B, 0x790B, 0x205B, 0x176B,
+    0x7839, 0x4F09, 0x1659, 0x2169, 0xA4F9, 0x93C9, 0xCA99, 0xFDA9,
+    0xD198, 0xE6A8, 0xBFF8, 0x88C8, 0x0D58, 0x3A68, 0x6338, 0x5408,
+    0xBD9C, 0x8AAC, 0xD3FC, 0xE4CC, 0x615C, 0x566C, 0x0F3C, 0x380C,
+    0x143D, 0x230D, 0x7A5D, 0x4D6D, 0xC8FD, 0xFFCD, 0xA69D, 0x91AD,
+    0xFEFF, 0xC9CF, 0x909F, 0xA7AF, 0x223F, 0x150F, 0x4C5F, 0x7B6F,
+    0x575E, 0x606E, 0x393E, 0x0E0E, 0x8B9E, 0xBCAE, 0xE5FE, 0xD2CE,
+    0x26F7, 0x11C7, 0x4897, 0x7FA7, 0xFA37, 0xCD07, 0x9457, 0xA367,
+    0x8F56, 0xB866, 0xE136, 0xD606, 0x5396, 0x64A6, 0x3DF6, 0x0AC6,
+    0x6594, 0x52A4, 0x0BF4, 0x3CC4, 0xB954, 0x8E64, 0xD734, 0xE004,
+    0xCC35, 0xFB05, 0xA255, 0x9565, 0x10F5, 0x27C5, 0x7E95, 0x49A5,
+    0xA031, 0x9701, 0xCE51, 0xF961, 0x7CF1, 0x4BC1, 0x1291, 0x25A1,
+    0x0990, 0x3EA0, 0x67F0, 0x50C0, 0xD550, 0xE260, 0xBB30, 0x8C00,
+    0xE352, 0xD462, 0x8D32, 0xBA02, 0x3F92, 0x08A2, 0x51F2, 0x66C2,
+    0x4AF3, 0x7DC3, 0x2493, 0x13A3, 0x9633, 0xA103, 0xF853, 0xCF63
+},{
+    0x0000, 0x76B4, 0xED68, 0x9BDC, 0xCAF1, 0xBC45, 0x2799, 0x512D,
+    0x85C3, 0xF377, 0x68AB, 0x1E1F, 0x4F32, 0x3986, 0xA25A, 0xD4EE,
+    0x1BA7, 0x6D13, 0xF6CF, 0x807B, 0xD156, 0xA7E2, 0x3C3E, 0x4A8A,
+    0x9E64, 0xE8D0, 0x730C, 0x05B8, 0x5495, 0x2221, 0xB9FD, 0xCF49,
+    0x374E, 0x41FA, 0xDA26, 0xAC92, 0xFDBF, 0x8B0B, 0x10D7, 0x6663,
+    0xB28D, 0xC439, 0x5FE5, 0x2951, 0x787C, 0x0EC8, 0x9514, 0xE3A0,
+    0x2CE9, 0x5A5D, 0xC181, 0xB735, 0xE618, 0x90AC, 0x0B70, 0x7DC4,
+    0xA92A, 0xDF9E, 0x4442, 0x32F6, 0x63DB, 0x156F, 0x8EB3, 0xF807,
+    0x6E9C, 0x1828, 0x83F4, 0xF540, 0xA46D, 0xD2D9, 0x4905, 0x3FB1,
+    0xEB5F, 0x9DEB, 0x0637, 0x7083, 0x21AE, 0x571A, 0xCCC6, 0xBA72,
+    0x753B, 0x038F, 0x9853, 0xEEE7, 0xBFCA, 0xC97E, 0x52A2, 0x2416,
+    0xF0F8, 0x864C, 0x1D90, 0x6B24, 0x3A09, 0x4CBD, 0xD761, 0xA1D5,
+    0x59D2, 0x2F66, 0xB4BA, 0xC20E, 0x9323, 0xE597, 0x7E4B, 0x08FF,
+    0xDC11, 0xAAA5, 0x3179, 0x47CD, 0x16E0, 0x6054, 0xFB88, 0x8D3C,
+    0x4275, 0x34C1, 0xAF1D, 0xD9A9, 0x8884, 0xFE30, 0x65EC, 0x1358,
+    0xC7B6, 0xB102, 0x2ADE, 0x5C6A, 0x0D47, 0x7BF3, 0xE02F, 0x969B,
+    0xDD38, 0xAB8C, 0x3050, 0x46E4, 0x17C9, 0x617D, 0xFAA1, 0x8C15,
+    0x58FB, 0x2E4F, 0xB593, 0xC327, 0x920A, 0xE4BE, 0x7F62, 0x09D6,
+    0xC69F, 0xB02B, 0x2BF7, 0x5D43, 0x0C6E, 0x7ADA, 0xE106, 0x97B2,
+    0x435C, 0x35E8, 0xAE34, 0xD880, 0x89AD, 0xFF19, 0x64C5, 0x1271,
+    0xEA76, 0x9CC2, 0x071E, 0x71AA, 0x2087, 0x5633, 0xCDEF, 0xBB5B,
+    0x6FB5, 0x1901, 0x82DD, 0xF469, 0xA544, 0xD3F0, 0x482C, 0x3E98,
+    0xF1D1, 0x8765, 0x1CB9, 0x6A0D, 0x3B20, 0x4D94, 0xD648, 0xA0FC,
+    0x7412, 0x02A6, 0x997A, 0xEFCE, 0xBEE3, 0xC857, 0x538B, 0x253F,
+    0xB3A4, 0xC510, 0x5ECC, 0x2878, 0x7955, 0x0FE1, 0x943D, 0xE289,
+    0x3667, 0x40D3, 0xDB0F, 0xADBB, 0xFC96, 0x8A22, 0x11FE, 0x674A,
+    0xA803, 0xDEB7, 0x456B, 0x33DF, 0x62F2, 0x1446, 0x8F9A, 0xF92E,
+    0x2DC0, 0x5B74, 0xC0A8, 0xB61C, 0xE731, 0x9185, 0x0A59, 0x7CED,
+    0x84EA, 0xF25E, 0x6982, 0x1F36, 0x4E1B, 0x38AF, 0xA373, 0xD5C7,
+    0x0129, 0x779D, 0xEC41, 0x9AF5, 0xCBD8, 0xBD6C, 0x26B0, 0x5004,
+    0x9F4D, 0xE9F9, 0x7225, 0x0491, 0x55BC, 0x2308, 0xB8D4, 0xCE60,
+    0x1A8E, 0x6C3A, 0xF7E6, 0x8152, 0xD07F, 0xA6CB, 0x3D17, 0x4BA3
+},{
+    0x0000, 0xAA51, 0x4483, 0xEED2, 0x8906, 0x2357, 0xCD85, 0x67D4,
+    0x022D, 0xA87C, 0x46AE, 0xECFF, 0x8B2B, 0x217A, 0xCFA8, 0x65F9,
+    0x045A, 0xAE0B, 0x40D9, 0xEA88, 0x8D5C, 0x270D, 0xC9DF, 0x638E,
+    0x0677, 0xAC26, 0x42F4, 0xE8A5, 0x8F71, 0x2520, 0xCBF2, 0x61A3,
+    0x08B4, 0xA2E5, 0x4C37, 0xE666, 0x81B2, 0x2BE3, 0xC531, 0x6F60,
+    0x0A99, 0xA0C8, 0x4E1A, 0xE44B, 0x839F, 0x29CE, 0xC71C, 0x6D4D,
+    0x0CEE, 0xA6BF, 0x486D, 0xE23C, 0x85E8, 0x2FB9, 0xC16B, 0x6B3A,
+    0x0EC3, 0xA492, 0x4A40, 0xE011, 0x87C5, 0x2D94, 0xC346, 0x6917,
+    0x1168, 0xBB39, 0x55EB, 0xFFBA, 0x986E, 0x323F, 0xDCED, 0x76BC,
+    0x1345, 0xB914, 0x57C6, 0xFD97, 0x9A43, 0x3012, 0xDEC0, 0x7491,
+    0x1532, 0xBF63, 0x51B1, 0xFBE0, 0x9C34, 0x3665, 0xD8B7, 0x72E6,
+    0x171F, 0xBD4E, 0x539C, 0xF9CD, 0x9E19, 0x3448, 0xDA9A, 0x70CB,
+    0x19DC, 0xB38D, 0x5D5F, 0xF70E, 0x90DA, 0x3A8B, 0xD459, 0x7E08,
+    0x1BF1, 0xB1A0, 0x5F72, 0xF523, 0x92F7, 0x38A6, 0xD674, 0x7C25,
+    0x1D86, 0xB7D7, 0x5905, 0xF354, 0x9480, 0x3ED1, 0xD003, 0x7A52,
+    0x1FAB, 0xB5FA, 0x5B28, 0xF179, 0x96AD, 0x3CFC, 0xD22E, 0x787F,
+    0x22D0, 0x8881, 0x6653, 0xCC02, 0xABD6, 0x0187, 0xEF55, 0x4504,
+    0x20FD, 0x8AAC, 0x647E, 0xCE2F, 0xA9FB, 0x03AA, 0xED78, 0x4729,
+    0x268A, 0x8CDB, 0x6209, 0xC858, 0xAF8C, 0x05DD, 0xEB0F, 0x415E,
+    0x24A7, 0x8EF6, 0x6024, 0xCA75, 0xADA1, 0x07F0, 0xE922, 0x4373,
+    0x2A64, 0x8035, 0x6EE7, 0xC4B6, 0xA362, 0x0933, 0xE7E1, 0x4DB0,
+    0x2849, 0x8218, 0x6CCA, 0xC69B, 0xA14F, 0x0B1E, 0xE5CC, 0x4F9D,
+    0x2E3E, 0x846F, 0x6ABD, 0xC0EC, 0xA738, 0x0D69, 0xE3BB, 0x49EA,
+    0x2C13, 0x8642, 0x6890, 0xC2C1, 0xA515, 0x0F44, 0xE196, 0x4BC7,
+    0x33B8, 0x99E9, 0x773B, 0xDD6A, 0xBABE, 0x10EF, 0xFE3D, 0x546C,
+    0x3195, 0x9BC4, 0x7516, 0xDF47, 0xB893, 0x12C2, 0xFC10, 0x5641,
+    0x37E2, 0x9DB3, 0x7361, 0xD930, 0xBEE4, 0x14B5, 0xFA67, 0x5036,
+    0x35CF, 0x9F9E, 0x714C, 0xDB1D, 0xBCC9, 0x1698, 0xF84A, 0x521B,
+    0x3B0C, 0x915D, 0x7F8F, 0xD5DE, 0xB20A, 0x185B, 0xF689, 0x5CD8,
+    0x3921, 0x9370, 0x7DA2, 0xD7F3, 0xB027, 0x1A76, 0xF4A4, 0x5EF5,
+    0x3F56, 0x9507, 0x7BD5, 0xD184, 0xB650, 0x1C01, 0xF2D3, 0x5882,
+    0x3D7B, 0x972A, 0x79F8, 0xD3A9, 0xB47D, 0x1E2C, 0xF0FE, 0x5AAF
+},{
+    0x0000, 0x45A0, 0x8B40, 0xCEE0, 0x06A1, 0x4301, 0x8DE1, 0xC841,
+    0x0D42, 0x48E2, 0x8602, 0xC3A2, 0x0BE3, 0x4E43, 0x80A3, 0xC503,
+    0x1A84, 0x5F24, 0x91C4, 0xD464, 0x1C25, 0x5985, 0x9765, 0xD2C5,
+    0x17C6, 0x5266, 0x9C86, 0xD926, 0x1167, 0x54C7, 0x9A27, 0xDF87,
+    0x3508, 0x70A8, 0xBE48, 0xFBE8, 0x33A9, 0x7609, 0xB8E9, 0xFD49,
+    0x384A, 0x7DEA, 0xB30A, 0xF6AA, 0x3EEB, 0x7B4B, 0xB5AB, 0xF00B,
+    0x2F8C, 0x6A2C, 0xA4CC, 0xE16C, 0x292D, 0x6C8D, 0xA26D, 0xE7CD,
+    0x22CE, 0x676E, 0xA98E, 0xEC2E, 0x246F, 0x61CF, 0xAF2F, 0xEA8F,
+    0x6A10, 0x2FB0, 0xE150, 0xA4F0, 0x6CB1, 0x2911, 0xE7F1, 0xA251,
+    0x6752, 0x22F2, 0xEC12, 0xA9B2, 0x61F3, 0x2453, 0xEAB3, 0xAF13,
+    0x7094, 0x3534, 0xFBD4, 0xBE74, 0x7635, 0x3395, 0xFD75, 0xB8D5,
+    0x7DD6, 0x3876, 0xF696, 0xB336, 0x7B77, 0x3ED7, 0xF037, 0xB597,
+    0x5F18, 0x1AB8, 0xD458, 0x91F8, 0x59B9, 0x1C19, 0xD2F9, 0x9759,
+    0x525A, 0x17FA, 0xD91A, 0x9CBA, 0x54FB, 0x115B, 0xDFBB, 0x9A1B,
+    0x459C, 0x003C, 0xCEDC, 0x8B7C, 0x433D, 0x069D, 0xC87D, 0x8DDD,
+    0x48DE, 0x0D7E, 0xC39E, 0x863E, 0x4E7F, 0x0BDF, 0xC53F, 0x809F,
+    0xD420, 0x9180, 0x5F60, 0x1AC0, 0xD281, 0x9721, 0x59C1, 0x1C61,
+    0xD962, 0x9CC2, 0x5222, 0x1782, 0xDFC3, 0x9A63, 0x5483, 0x1123,
+    0xCEA4, 0x8B04, 0x45E4, 0x0044, 0xC805, 0x8DA5, 0x4345, 0x06E5,
+    0xC3E6, 0x8646, 0x48A6, 0x0D06, 0xC547, 0x80E7, 0x4E07, 0x0BA7,
+    0xE128, 0xA488, 0x6A68, 0x2FC8, 0xE789, 0xA229, 0x6CC9, 0x2969,
+    0xEC6A, 0xA9CA, 0x672A, 0x228A, 0xEACB, 0xAF6B, 0x618B, 0x242B,
+    0xFBAC, 0xBE0C, 0x70EC, 0x354C, 0xFD0D, 0xB8AD, 0x764D, 0x33ED,
+    0xF6EE, 0xB34E, 0x7DAE, 0x380E, 0xF04F, 0xB5EF, 0x7B0F, 0x3EAF,
+    0xBE30, 0xFB90, 0x3570, 0x70D0, 0xB891, 0xFD31, 0x33D1, 0x7671,
+    0xB372, 0xF6D2, 0x3832, 0x7D92, 0xB5D3, 0xF073, 0x3E93, 0x7B33,
+    0xA4B4, 0xE114, 0x2FF4, 0x6A54, 0xA215, 0xE7B5, 0x2955, 0x6CF5,
+    0xA9F6, 0xEC56, 0x22B6, 0x6716, 0xAF57, 0xEAF7, 0x2417, 0x61B7,
+    0x8B38, 0xCE98, 0x0078, 0x45D8, 0x8D99, 0xC839, 0x06D9, 0x4379,
+    0x867A, 0xC3DA, 0x0D3A, 0x489A, 0x80DB, 0xC57B, 0x0B9B, 0x4E3B,
+    0x91BC, 0xD41C, 0x1AFC, 0x5F5C, 0x971D, 0xD2BD, 0x1C5D, 0x59FD,
+    0x9CFE, 0xD95E, 0x17BE, 0x521E, 0x9A5F, 0xDFFF, 0x111F, 0x54BF
+},{
+    0x0000, 0xB861, 0x60E3, 0xD882, 0xC1C6, 0x79A7, 0xA125, 0x1944,
+    0x93AD, 0x2BCC, 0xF34E, 0x4B2F, 0x526B, 0xEA0A, 0x3288, 0x8AE9,
+    0x377B, 0x8F1A, 0x5798, 0xEFF9, 0xF6BD, 0x4EDC, 0x965E, 0x2E3F,
+    0xA4D6, 0x1CB7, 0xC435, 0x7C54, 0x6510, 0xDD71, 0x05F3, 0xBD92,
+    0x6EF6, 0xD697, 0x0E15, 0xB674, 0xAF30, 0x1751, 0xCFD3, 0x77B2,
+    0xFD5B, 0x453A, 0x9DB8, 0x25D9, 0x3C9D, 0x84FC, 0x5C7E, 0xE41F,
+    0x598D, 0xE1EC, 0x396E, 0x810F, 0x984B, 0x202A, 0xF8A8, 0x40C9,
+    0xCA20, 0x7241, 0xAAC3, 0x12A2, 0x0BE6, 0xB387, 0x6B05, 0xD364,
+    0xDDEC, 0x658D, 0xBD0F, 0x056E, 0x1C2A, 0xA44B, 0x7CC9, 0xC4A8,
+    0x4E41, 0xF620, 0x2EA2, 0x96C3, 0x8F87, 0x37E6, 0xEF64, 0x5705,
+    0xEA97, 0x52F6, 0x8A74, 0x3215, 0x2B51, 0x9330, 0x4BB2, 0xF3D3,
+    0x793A, 0xC15B, 0x19D9, 0xA1B8, 0xB8FC, 0x009D, 0xD81F, 0x607E,
+    0xB31A, 0x0B7B, 0xD3F9, 0x6B98, 0x72DC, 0xCABD, 0x123F, 0xAA5E,
+    0x20B7, 0x98D6, 0x4054, 0xF835, 0xE171, 0x5910, 0x8192, 0x39F3,
+    0x8461, 0x3C00, 0xE482, 0x5CE3, 0x45A7, 0xFDC6, 0x2544, 0x9D25,
+    0x17CC, 0xAFAD, 0x772F, 0xCF4E, 0xD60A, 0x6E6B, 0xB6E9, 0x0E88,
+    0xABF9, 0x1398, 0xCB1A, 0x737B, 0x6A3F, 0xD25E, 0x0ADC, 0xB2BD,
+    0x3854, 0x8035, 0x58B7, 0xE0D6, 0xF992, 0x41F3, 0x9971, 0x2110,
+    0x9C82, 0x24E3, 0xFC61, 0x4400, 0x5D44, 0xE525, 0x3DA7, 0x85C6,
+    0x0F2F, 0xB74E, 0x6FCC, 0xD7AD, 0xCEE9, 0x7688, 0xAE0A, 0x166B,
+    0xC50F, 0x7D6E, 0xA5EC, 0x1D8D, 0x04C9, 0xBCA8, 0x642A, 0xDC4B,
+    0x56A2, 0xEEC3, 0x3641, 0x8E20, 0x9764, 0x2F05, 0xF787, 0x4FE6,
+    0xF274, 0x4A15, 0x9297, 0x2AF6, 0x33B2, 0x8BD3, 0x5351, 0xEB30,
+    0x61D9, 0xD9B8, 0x013A, 0xB95B, 0xA01F, 0x187E, 0xC0FC, 0x789D,
+    0x7615, 0xCE74, 0x16F6, 0xAE97, 0xB7D3, 0x0FB2, 0xD730, 0x6F51,
+    0xE5B8, 0x5DD9, 0x855B, 0x3D3A, 0x247E, 0x9C1F, 0x449D, 0xFCFC,
+    0x416E, 0xF90F, 0x218D, 0x99EC, 0x80A8, 0x38C9, 0xE04B, 0x582A,
+    0xD2C3, 0x6AA2, 0xB220, 0x0A41, 0x1305, 0xAB64, 0x73E6, 0xCB87,
+    0x18E3, 0xA082, 0x7800, 0xC061, 0xD925, 0x6144, 0xB9C6, 0x01A7,
+    0x8B4E, 0x332F, 0xEBAD, 0x53CC, 0x4A88, 0xF2E9, 0x2A6B, 0x920A,
+    0x2F98, 0x97F9, 0x4F7B, 0xF71A, 0xEE5E, 0x563F, 0x8EBD, 0x36DC,
+    0xBC35, 0x0454, 0xDCD6, 0x64B7, 0x7DF3, 0xC592, 0x1D10, 0xA571
+},{
+    0x0000, 0x47D3, 0x8FA6, 0xC875, 0x0F6D, 0x48BE, 0x80CB, 0xC718,
+    0x1EDA, 0x5909, 0x917C, 0xD6AF, 0x11B7, 0x5664, 0x9E11, 0xD9C2,
+    0x3DB4, 0x7A67, 0xB212, 0xF5C1, 0x32D9, 0x750A, 0xBD7F, 0xFAAC,
+    0x236E, 0x64BD, 0xACC8, 0xEB1B, 0x2C03, 0x6BD0, 0xA3A5, 0xE476,
+    0x7B68, 0x3CBB, 0xF4CE, 0xB31D, 0x7405, 0x33D6, 0xFBA3, 0xBC70,
+    0x65B2, 0x2261, 0xEA14, 0xADC7, 0x6ADF, 0x2D0C, 0xE579, 0xA2AA,
+    0x46DC, 0x010F, 0xC97A, 0x8EA9, 0x49B1, 0x0E62, 0xC617, 0x81C4,
+    0x5806, 0x1FD5, 0xD7A0, 0x9073, 0x576B, 0x10B8, 0xD8CD, 0x9F1E,
+    0xF6D0, 0xB103, 0x7976, 0x3EA5, 0xF9BD, 0xBE6E, 0x761B, 0x31C8,
+    0xE80A, 0xAFD9, 0x67AC, 0x207F, 0xE767, 0xA0B4, 0x68C1, 0x2F12,
+    0xCB64, 0x8CB7, 0x44C2, 0x0311, 0xC409, 0x83DA, 0x4BAF, 0x0C7C,
+    0xD5BE, 0x926D, 0x5A18, 0x1DCB, 0xDAD3, 0x9D00, 0x5575, 0x12A6,
+    0x8DB8, 0xCA6B, 0x021E, 0x45CD, 0x82D5, 0xC506, 0x0D73, 0x4AA0,
+    0x9362, 0xD4B1, 0x1CC4, 0x5B17, 0x9C0F, 0xDBDC, 0x13A9, 0x547A,
+    0xB00C, 0xF7DF, 0x3FAA, 0x7879, 0xBF61, 0xF8B2, 0x30C7, 0x7714,
+    0xAED6, 0xE905, 0x2170, 0x66A3, 0xA1BB, 0xE668, 0x2E1D, 0x69CE,
+    0xFD81, 0xBA52, 0x7227, 0x35F4, 0xF2EC, 0xB53F, 0x7D4A, 0x3A99,
+    0xE35B, 0xA488, 0x6CFD, 0x2B2E, 0xEC36, 0xABE5, 0x6390, 0x2443,
+    0xC035, 0x87E6, 0x4F93, 0x0840, 0xCF58, 0x888B, 0x40FE, 0x072D,
+    0xDEEF, 0x993C, 0x5149, 0x169A, 0xD182, 0x9651, 0x5E24, 0x19F7,
+    0x86E9, 0xC13A, 0x094F, 0x4E9C, 0x8984, 0xCE57, 0x0622, 0x41F1,
+    0x9833, 0xDFE0, 0x1795, 0x5046, 0x975E, 0xD08D, 0x18F8, 0x5F2B,
+    0xBB5D, 0xFC8E, 0x34FB, 0x7328, 0xB430, 0xF3E3, 0x3B96, 0x7C45,
+    0xA587, 0xE254, 0x2A21, 0x6DF2, 0xAAEA, 0xED39, 0x254C, 0x629F,
+    0x0B51, 0x4C82, 0x84F7, 0xC324, 0x043C, 0x43EF, 0x8B9A, 0xCC49,
+    0x158B, 0x5258, 0x9A2D, 0xDDFE, 0x1AE6, 0x5D35, 0x9540, 0xD293,
+    0x36E5, 0x7136, 0xB943, 0xFE90, 0x3988, 0x7E5B, 0xB62E, 0xF1FD,
+    0x283F, 0x6FEC, 0xA799, 0xE04A, 0x2752, 0x6081, 0xA8F4, 0xEF27,
+    0x7039, 0x37EA, 0xFF9F, 0xB84C, 0x7F54, 0x3887, 0xF0F2, 0xB721,
+    0x6EE3, 0x2930, 0xE145, 0xA696, 0x618E, 0x265D, 0xEE28, 0xA9FB,
+    0x4D8D, 0x0A5E, 0xC22B, 0x85F8, 0x42E0, 0x0533, 0xCD46, 0x8A95,
+    0x5357, 0x1484, 0xDCF1, 0x9B22, 0x5C3A, 0x1BE9, 0xD39C, 0x944F
+}};
 
 /* Non - Reflected */
-uint16_t util_crc16(uint16_t current, const char *k, size_t len) {
+uint16_t util_crc16(uint16_t current, const char *GMQCC_RESTRICT k, size_t len) {
     register uint16_t h = current;
-    for (; len; --len, ++k)
-        h = util_crc16_table[(h>>8)^((unsigned char)*k)]^(h<<8);
+
+    /* don't load twice */
+    const uint8_t *GMQCC_RESTRICT data = (const uint8_t *GMQCC_RESTRICT)k;
+    size_t n;
+
+    /* deal with the first bytes as bytes until we reach an 8 byte boundary */
+    while (len & 7) {
+        h = (uint16_t)(h << 8) ^ (*util_crc16_table)[(h >> 8) ^ *data++];
+        --len;
+    }
+
+    #define SELECT_BULK(X, MOD) util_crc16_table[(X)][data[7-(X)] ^ (MOD)]
+    #define SELECT_DATA(X)      util_crc16_table[(X)][data[7-(X)]]
+
+    for (n = len / 8; n; --n) {
+        h = SELECT_BULK(7, (h >> 8))   ^
+            SELECT_BULK(6, (h & 0xFF)) ^
+            SELECT_DATA(5) ^
+            SELECT_DATA(4) ^
+            SELECT_DATA(3) ^
+            SELECT_DATA(2) ^
+            SELECT_DATA(1) ^
+            SELECT_DATA(0);
+        data += 8;
+        len  -= 8;
+    }
+
+    #undef SELECT_BULK
+    #undef SELECT_DATA
+
+    /* deal with the rest with the byte method */
+    for (n = len & 7; n; --n)
+        h = (uint16_t)(h << 8) ^ (*util_crc16_table)[(h >> 8) ^ *data++];
+
     return h;
 }
-/* Reflective Variation (for reference) */
-#if 0
-uint16_t util_crc16(const char *k, int len, const short clamp) {
-    register uint16_t h= (uint16_t)0xFFFFFFFF;
-    for (; len; --len, ++k)
-        h = util_crc16_table[(h^((unsigned char)*k))&0xFF]^(h>>8);
-    return (~h)%clamp;
-}
-#endif
 
 /*
  * modifier is the match to make and the transposition from it, while add is the upper-value that determines the
  * transposition from uppercase to lower case.
  */
-static GMQCC_INLINE size_t util_strtransform(const char *in, char *out, size_t outsz, const char *mod, int add) {
+static size_t util_strtransform(const char *in, char *out, size_t outsz, const char *mod, int add) {
     size_t sz = 1;
     for (; *in && sz < outsz; ++in, ++out, ++sz) {
         *out = (*in == mod[0])