GITINFO != git describe --always
.endif
-CFLAGS += -Wall -Wextra -Werror -Wstrict-aliasing
+CFLAGS += -Wall -Wextra -Werror -Wstrict-aliasing -Wno-attributes
.if $(CC) == clang
CFLAGS += -Weverything\
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
-Copyright (C) 2012, 2013
+Copyright (C) 2012, 2013, 2014
Dale Weiler
Wolfgang Bumiller
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)
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
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).
- 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.
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.
/*
- * 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
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
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)
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;
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,
self->fixedparams = NULL;
self->return_value = NULL;
+ self->static_names = NULL;
+ self->static_count = 0;
+
return self;
cleanup:
*/
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);
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;
}
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
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 {
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;
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Dale Weiler
* Wolfgang Bumiller
*
/*
- * 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
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Dale Weiler
* Wolfgang Bumiller
*
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
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
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
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
/*
- * 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
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;
}
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;
}
}
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) {
(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));
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)));
}
}
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,
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;
}
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))
* 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];
/*
- * 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
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
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))
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;
ftepp_t *ftepp;
char minor[32];
char major[32];
+ size_t i;
ftepp = ftepp_new();
if (!ftepp)
*/
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;
}
/*
- * 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)
" 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
# 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);
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
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 */
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
--- /dev/null
+/*
+ * 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
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
/*
- * 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
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.
}
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, ...) {
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;
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);
+}
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
}
self->reserved_va_count = NULL;
+ self->coverage_func = NULL;
+
self->code = code_init();
return self;
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;
}
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;
/*
- * 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
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),
/* 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.
*/
/*
- * 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
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);
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);
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;
}
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) {
* 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;
if (lex->flags.preprocessing) {
haswhite = true;
- /*
- lex_tokench(lex, '/');
- lex_tokench(lex, '/');
- */
lex_tokench(lex, ' ');
lex_tokench(lex, ' ');
}
/* multiline comment */
if (lex->flags.preprocessing) {
haswhite = true;
- /*
- lex_tokench(lex, '/');
- lex_tokench(lex, '*');
- */
lex_tokench(lex, ' ');
lex_tokench(lex, ' ');
}
ch = lex_getch(lex);
if (ch == '/') {
if (lex->flags.preprocessing) {
- /*
- lex_tokench(lex, '*');
- lex_tokench(lex, '/');
- */
lex_tokench(lex, ' ');
lex_tokench(lex, ' ');
}
if (ch == '\n')
lex_tokench(lex, '\n');
else
- lex_tokench(lex, ' '); /* ch); */
+ lex_tokench(lex, ' ');
}
}
ch = ' '; /* cause TRUE in the isspace check */
bool hadwhite = false;
lex_token_new(lex);
-#if 0
- if (!lex->tok)
- return TOKEN_FATAL;
-#endif
while (true) {
ch = lex_skipwhite(lex, hadwhite);
*/
switch (ch)
{
- /*
- case '+':
- case '-':
- */
case '*':
case '/':
case '<':
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)) == '>')
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);
/*
- * 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
*/
#ifndef GMQCC_LEXER_HDR
#define GMQCC_LEXER_HDR
+#include "gmqcc.h"
+
typedef struct token_s token;
struct token_s {
{ "++", 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},
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Dale Weiler
* Wolfgang Bumiller
*
" -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;
}
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 */
/*
- * 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
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
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);
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
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)
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 */
/*
- * Copyright (C) 2013
+ * Copyright (C) 2013, 2014
* Dale Weiler
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
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'):
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('|','|'):
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;
/* 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;
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 */
*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 `]]`");
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;
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 ]] */
}
else
break;
+
+ leave:
if (!parser_next(parser))
goto onerr;
}
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) {
}
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;
/*
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");
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;
}
*/
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);
/* 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 */
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;
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");
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))
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
/*
- * 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
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Dale Weiler
* Wolfgang Bumiller
*
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;
* 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;
/*
- * 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
--- /dev/null
+float here;
+[[last]] float here;
+#ifdef MAKE_IT_FAIL
+float here;
+#endif
--- /dev/null
+I: last.qc
+D: last attribute
+T: -compile
+C: -std=gmqcc -fftepp
+F: -no-defs
--- /dev/null
+I: last.qc
+D: last attribute
+T: -fail
+C: -std=gmqcc -fftepp -DMAKE_IT_FAIL
+F: -no-defs
/*
- * 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
/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Dale Weiler
* Wolfgang Bumiller
*
}
/*
- * 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])