From: Dale Weiler Date: Sat, 9 Mar 2013 08:57:37 +0000 (+0000) Subject: Uh wrong file. X-Git-Tag: before-library~91 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=965f8b9041f8b868ac62153f7a410f268ec8dc4b;p=xonotic%2Fgmqcc.git Uh wrong file. --- diff --git a/intrin.c b/intrin.c deleted file mode 100644 index dbf9670..0000000 --- a/intrin.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2012, 2013 - * 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 "ast.h" - - -/* - * Provides all the "intrinsics" / "builtins" for GMQCC. These can do - * a few things, they can provide fall back implementations for math - * functions if the definitions don't exist for some given engine. Or - * then can determine definitions for existing builtins, and simply - * wrap back to them instead. This is like a "portable" intrface that - * is entered when -fintrin is used (causing all existing builtins to - * be ignored by the compiler and instead interface through here. - */ -typedef struct { - ast_expression (*intrin)(parser_t *); - const char *name; - const char *alias; -} intrin_t; - - -/* - * Some helper macros for generating def, doing ast_value from func, with - * assignment, and a nice aggregate builder for generating the intrinsic - * table at the bottom of this file, and for registering functions and - * globals with the parser (so that they can be codegen'ed) - */ -#define INTRIN_IMP(NAME) \ - ast_expression intrin_##NAME (parser_t *parser) - -/* - * For intrinsics that are not to take precedence over a builtin, leave - * ALIAS as an empty string. - */ -#define INTRIN_DEF(NAME, ALIAS) \ - { &intrin_##NAME, #NAME, ALIAS } - - -#define INTRIN_VAL(NAME, FUNC, STYPE, VTYPE) \ - do { \ - (NAME) = ast_value_new ( \ - parser_ctx(parser), \ - "__builtin_" #NAME, \ - TYPE_FUNCTION \ - ); \ - (NAME)->expression.next = (ast_expression*)ast_value_new ( \ - parser_ctx(parser), \ - STYPE, \ - VTYPE \ - ); \ - (FUNC) = ast_function_new ( \ - parser_ctx(parser), \ - "__builtin_" #NAME, \ - (NAME) \ - ); \ - } while (0) - -#define INTRIN_REG(FUNC, VALUE) \ - do { \ - vec_push(parser->functions, (FUNC)); \ - vec_push(parser->globals, (ast_expression*)(VALUE)); \ - } while (0) - - -typedef enum { - QC_FP_NAN, - QC_FP_INFINITE, - QC_FP_ZERO, - QC_FP_SUBNORMAL, - QC_FP_NORMAL -} intrin_fp_t; - - -#if 0 -/* - * Implementation of intrinsics. Each new intrinsic needs to be added - * to the intrinsic table below. - */ -INTRIN_IMP(isnan) { - /* - * float isnan(float x) { - * float y; - * - * y = x; - * return (x != y); - */ -} - -INTRIN_IMP(isinf) { - /* - * float isinf(float x) { - * return (x != 0) && (x + x == x); - * } - */ -} - -INTRIN_IMP(fpclassify) { - /* - * float __builtin_fpclassify(float x) { - * if (isnan(x)) { - * return QC_FP_NAN; - * } - * if (isinf(x)) { - * return QC_FP_INFINITE; - * } - * if (x == 0.0f) { - * return QC_FP_ZERO; - * } - * return QC_FP_NORMAL; - * } - */ -} -#endif - - -INTRIN_IMP(exp) { - /* - * float __builtin_exp(float x) { - * return __builtin_pow(QC_M_E, x); - * } - */ - static ast_value *value = NULL; - - if (!value) { - ast_call *call = ast_call_new (parser_ctx(parser), intrin_func(parser, "pow")); - ast_value *arg1 = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); - ast_block *body = ast_block_new (parser_ctx(parser)); - ast_function *func = NULL; - - INTRIN_VAL("exp", func, "", TYPE_FLOAT); - - /* push arguments for params to call */ - vec_push(call->params, (ast_expression*)parser_const_float(parser, QC_M_E)); - vec_push(call->params, (ast_expression*)arg1); - - /* return pow(QC_M_E, x) */ - vec_push(body->exprs, - (ast_expression*)ast_return_new( - parser_ctx(parser), - (ast_expression*)call - ) - ); - - vec_push(value->expressions.param, arg1); /* float x (for param) */ - vec_push(func ->blocks, body); /* {{{ body }}} */ - - INTRIN_REG(func, value); - } - - return (ast_expression*)value; -} - -#if 0 -INTRIN_IMP(exp2) { - /* - * float __builtin_exp2(float x) { - * return __builin_pow(2, x); - * } - */ -} - -INTRIN_IMP(expm1) { - /* - * float __builtin_expm1(float x) { - * return __builtin_exp(x) - 1; - * } - */ -} -#endif - -intrin_t intrin_intrinsics[] = { - INTRIN_DEF(exp, "exp") -}; diff --git a/intrin.h b/intrin.h new file mode 100644 index 0000000..2b797f6 --- /dev/null +++ b/intrin.h @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2012, 2013 + * 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. + */ + +/* + * Provides all the "intrinsics" / "builtins" for GMQCC. These can do + * a few things, they can provide fall back implementations for math + * functions if the definitions don't exist for some given engine. Or + * then can determine definitions for existing builtins, and simply + * wrap back to them instead. This is like a "portable" intrface that + * is entered when -fintrin is used (causing all existing builtins to + * be ignored by the compiler and instead interface through here. + */ +typedef struct { + ast_expression *(*intrin)(parser_t *); + const char *name; + const char *alias; +} intrin_t; + +ht intrin_intrinsics() { + static ht intrinsics = NULL; + if (!intrinsics) + intrinsics = util_htnew(PARSER_HT_SIZE); + + return intrinsics; +} + +void intrin_intrinsics_destroy() { + util_htdel(intrin_intrinsics()); +} + +#define INTRIN_VAL(VALUE, NAME, FUNC, STYPE, VTYPE) \ + do { \ + (VALUE) = ast_value_new ( \ + parser_ctx(parser), \ + "__builtin_" NAME, \ + TYPE_FUNCTION \ + ); \ + (VALUE)->expression.next = (ast_expression*)ast_value_new ( \ + parser_ctx(parser), \ + STYPE, \ + VTYPE \ + ); \ + (FUNC) = ast_function_new ( \ + parser_ctx(parser), \ + "__builtin_" NAME, \ + (VALUE) \ + ); \ + } while (0) + +#define INTRIN_REG(FUNC, VALUE) \ + do { \ + vec_push(parser->functions, (FUNC)); \ + vec_push(parser->globals, (ast_expression*)(VALUE)); \ + } while (0) + + +ast_expression *intrin_func (parser_t *parser, const char *name); + +#define QC_M_E 2.71828182845905 + +ast_expression *intrin_pow(parser_t *parser) { + static ast_value *value = NULL; + + if (!value) { + ast_value *arg1 = ast_value_new(parser_ctx(parser), "x", TYPE_FLOAT); + ast_value *arg2 = ast_value_new(parser_ctx(parser), "y", TYPE_FLOAT); + ast_value *local = ast_value_new(parser_ctx(parser), "local", TYPE_FLOAT); + ast_block *body = ast_block_new(parser_ctx(parser)); + ast_block *l1b = ast_block_new(parser_ctx(parser)); /* loop 1 body */ + ast_block *l2b = ast_block_new(parser_ctx(parser)); /* looo 2 body */ + ast_loop *loop1 = NULL; + ast_loop *loop2 = NULL; + ast_function *func = NULL; + + INTRIN_VAL(value, "pow", func, "", TYPE_FLOAT); + + /* arguments */ + vec_push(value->expression.params, arg1); + vec_push(value->expression.params, arg2); + + /* local */ + vec_push(body->locals, local); + + /* assignment to local of value 1.0f */ + vec_push(body->exprs, + (ast_expression*)ast_store_new ( + parser_ctx(parser), + INSTR_STORE_F, + (ast_expression*)local, + (ast_expression*)parser_const_float_1(parser) + ) + ); + + /* y >>= 2 */ + vec_push(l2b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)arg2, + (ast_expression*)parser_const_float(parser, 0.25f) + ) + ); + + /* x *= x */ + vec_push(l2b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)arg1, + (ast_expression*)arg1 + ) + ); + + /* while (!(y&1)) */ + loop2 = ast_loop_new ( + parser_ctx(parser), + NULL, + (ast_expression*)ast_binary_new ( + parser_ctx(parser), + INSTR_AND, + (ast_expression*)arg2, + (ast_expression*)parser_const_float_1(parser) + ), + true, /* ! not */ + NULL, + false, + NULL, + (ast_expression*)l2b + ); + + /* push nested loop into loop expressions */ + vec_push(l1b->exprs, (ast_expression*)loop2); + + /* y-- */ + vec_push(l1b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_SUB_F, + (ast_expression*)arg2, + (ast_expression*)parser_const_float_1(parser) + ) + ); + /* local *= x */ + vec_push(l1b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)local, + (ast_expression*)arg1 + ) + ); + + /* while (y > 0) */ + loop1 = ast_loop_new ( + parser_ctx(parser), + NULL, + (ast_expression*)ast_binary_new ( + parser_ctx(parser), + INSTR_GT, + (ast_expression*)arg2, + (ast_expression*)parser_const_float_0(parser) + ), + false, + NULL, + false, + NULL, + (ast_expression*)l1b + ); + + /* 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 ( + parser_ctx(parser), + (ast_expression*)local + ) + ); + + /* push block and register intrin for codegen */ + vec_push(func->blocks, body); + + INTRIN_REG(func, value); + } + + return (ast_expression*)value; +} + +ast_expression *intrin_mod(parser_t *parser) { + static ast_value *value = NULL; + + if (!value) { + ast_call *call = ast_call_new (parser_ctx(parser), intrin_func(parser, "floor")); + ast_value *arg1 = ast_value_new(parser_ctx(parser), "x", TYPE_FLOAT); + ast_value *arg2 = ast_value_new(parser_ctx(parser), "y", TYPE_FLOAT); + ast_block *body = ast_block_new(parser_ctx(parser)); + ast_function *func = NULL; + + INTRIN_VAL(value, "mod", func, "", TYPE_FLOAT); + + /* floor(x/y) */ + vec_push(call->params, + (ast_expression*)ast_binary_new ( + parser_ctx(parser), + INSTR_DIV_F, + (ast_expression*)arg1, + (ast_expression*)arg2 + ) + ); + + vec_push(body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(parser), + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_SUB_F, + (ast_expression*)arg1, + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_MUL_F, + (ast_expression*)arg2, + (ast_expression*)call + ) + ) + ) + ); + + 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 }}} */ + + INTRIN_REG(func, value); + } + + return (ast_expression*)value; +} + +ast_expression *intrin_exp(parser_t *parser) { + /* + * float __builtin_exp(float x) { + * return __builtin_pow(QC_M_E, x); + * } + */ + static ast_value *value = NULL; + + if (!value) { + ast_call *call = ast_call_new (parser_ctx(parser), intrin_func(parser, "pow")); + ast_value *arg1 = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + ast_block *body = ast_block_new (parser_ctx(parser)); + ast_function *func = NULL; + + INTRIN_VAL(value, "exp", func, "", TYPE_FLOAT); + + /* push arguments for params to call */ + vec_push(call->params, (ast_expression*)parser_const_float(parser, QC_M_E)); + vec_push(call->params, (ast_expression*)arg1); + + /* return pow(QC_M_E, x) */ + vec_push(body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(parser), + (ast_expression*)call + ) + ); + + vec_push(value->expression.params, arg1); /* float x (for param) */ + + vec_push(func->blocks, body); /* {{{ body }}} */ + + INTRIN_REG(func, value); + } + + return (ast_expression*)value; +} + +static intrin_t intrinsics[] = { + {&intrin_exp, "__builtin_exp", "exp"}, + {&intrin_mod, "__builtin_mod", "mod"}, + {&intrin_pow, "__builtin_pow", "pow"} +}; + +ast_expression *intrin_func(parser_t *parser, const char *name) { + static bool init = false; + size_t i = 0; + void *find; + + /* register the intrinsics in the hashtable for O(1) lookup */ + if (!init) { + for (i = 0; i < sizeof(intrinsics)/sizeof(*intrinsics); i++) + util_htset(intrin_intrinsics(), intrinsics[i].alias, &intrinsics[i]); + + init = true; /* only once */ + } + + /* jesus fucking christ all might Blub design something less fucking + * impossible to use. + */ + if ((find = (void*)parser_find_global(parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION) + for (i = 0; i < vec_size(parser->functions); ++i) + if (((ast_value*)find)->name && !strcmp(parser->functions[i]->name, ((ast_value*)find)->name) && parser->functions[i]->builtin < 0) + return find; + + if ((find = util_htget(intrin_intrinsics(), name))) { + /* intrinsic is in table. This will "generate the function" so + * to speak (if it's not already generated). + */ + return ((intrin_t*)find)->intrin(parser); + } + + parseerror(parser, "need function: `%s` compiler depends on it", name); + return NULL; +}