X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=intrin.h;h=e0151d8bdabb33c1ba87b39f9240cfe5a3a26a39;hp=00464aaa5da1a64fb6be63bd318a16959c1c619d;hb=db9c37d18bcced1c3b0f1421044155b1d88f0b25;hpb=144672fada7324651eabbbd0eaec68ed4dd031c1 diff --git a/intrin.h b/intrin.h index 00464aa..e0151d8 100644 --- a/intrin.h +++ b/intrin.h @@ -1,416 +1,74 @@ -/* - * 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. - */ +#ifndef GMQCC_INTRIN_HDR +#define GMQCC_INTRIN_HDR +#include "gmqcc.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; +struct fold; +struct parser_t; -static ht intrin_intrinsics(void) { - static ht intrinsics = NULL; - if (!intrinsics) - intrinsics = util_htnew(PARSER_HT_SIZE); +struct ast_function; +struct ast_expression; +struct ast_value; - return intrinsics; -} +struct intrin; -#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) - -#define QC_M_E 2.71828182845905 - -static ast_expression *intrin_func(parser_t *parser, const char *name); -static ast_expression *intrin_pow (parser_t *parser) { - /* - * 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; - * } - */ - 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; -} - -static ast_expression *intrin_mod(parser_t *parser) { - /* - * float mod(float x, float y) { - * return x - y * floor(x / y); - * } - */ - 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; -} - -static ast_expression *intrin_exp(parser_t *parser) { - /* - * float exp(float x) { - * return 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 ast_expression *intrin_isnan(parser_t *parser) { - /* - * float isnan(float x) { - * float local; - * local = x; - * - * return (x != local); - * } - */ - static ast_value *value = NULL; - - if (!value) { - ast_value *arg1 = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); - ast_value *local = ast_value_new (parser_ctx(parser), "local", TYPE_FLOAT); - ast_block *body = ast_block_new (parser_ctx(parser)); - ast_function *func = NULL; - - INTRIN_VAL(value, "isnan", func, "", TYPE_FLOAT); - - vec_push(body->locals, local); - vec_push(body->exprs, - (ast_expression*)ast_store_new( - parser_ctx(parser), - INSTR_STORE_F, - (ast_expression*)local, - (ast_expression*)arg1 - ) - ); - - vec_push(body->exprs, - (ast_expression*)ast_return_new( - parser_ctx(parser), - (ast_expression*)ast_binary_new( - parser_ctx(parser), - INSTR_NE_F, - (ast_expression*)arg1, - (ast_expression*)local - ) - ) - ); - - vec_push(value->expression.params, arg1); - - vec_push(func->blocks, 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"}, - {&intrin_isnan, "__builtin_isnan", "isnan"} +struct intrin_func_t { + ast_expression *(intrin::*function)(); + const char *name; + const char *alias; + size_t args; }; -void intrin_intrinsics_destroy(parser_t *parser) { - /*size_t i;*/ - (void)parser; - util_htdel(intrin_intrinsics()); -#if 0 - for (i = 0; i < sizeof(intrinsics)/sizeof(intrin_t); i++) - ast_value_delete( (ast_value*) intrinsics[i].intrin(parser)); -#endif -} - - -static 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, Blub design something less fucking - * impossible to use, like a ast_is_builtin(ast_expression *), also - * use a hashtable :P - */ - 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 (ast_expression*)find; +struct intrin { + intrin() = default; + intrin(parser_t *parser); + + ast_expression *debug_typestring(); + ast_expression *do_fold(ast_value *val, ast_expression **exprs); + ast_expression *func_try(size_t offset, const char *compare); + ast_expression *func_self(const char *name, const char *from); + ast_expression *func(const char *name); + +protected: + lex_ctx_t ctx() const; + ast_function *value(ast_value **out, const char *name, qcint_t vtype); + void reg(ast_value *const value, ast_function *const func); + + ast_expression *nullfunc(); + ast_expression *isfinite_(); + ast_expression *isinf_(); + ast_expression *isnan_(); + ast_expression *isnormal_(); + ast_expression *signbit_(); + ast_expression *acosh_(); + ast_expression *asinh_(); + ast_expression *atanh_(); + ast_expression *exp_(); + ast_expression *exp2_(); + ast_expression *expm1_(); + ast_expression *pow_(); + ast_expression *mod_(); + ast_expression *fabs_(); + ast_expression *epsilon_(); + ast_expression *nan_(); + ast_expression *inf_(); + ast_expression *ln_(); + ast_expression *log_variant(const char *name, float base); + ast_expression *log_(); + ast_expression *log10_(); + ast_expression *log2_(); + ast_expression *logb_(); + ast_expression *shift_variant(const char *name, size_t instr); + ast_expression *lshift(); + ast_expression *rshift(); + + void error(const char *fmt, ...); + +private: + parser_t *m_parser; + fold *m_fold; + std::vector m_intrinsics; + std::vector m_generated; +}; - 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; -} +#endif