From: Dale Weiler Date: Wed, 14 Aug 2013 06:02:15 +0000 (+0000) Subject: Made intrinsics seperate from the parser. X-Git-Tag: v0.3.0~27 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=73d9aa29c468a12096c0c7efc6535ffb45d85507;p=xonotic%2Fgmqcc.git Made intrinsics seperate from the parser. --- diff --git a/Makefile b/Makefile index 3396ec6..e6ff4be 100644 --- a/Makefile +++ b/Makefile @@ -150,7 +150,7 @@ stat.o: gmqcc.h opts.def test.o: gmqcc.h opts.def main.o: gmqcc.h opts.def lexer.h lexer.o: gmqcc.h opts.def lexer.h -parser.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h intrin.h +parser.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h code.o: gmqcc.h opts.def ast.o: gmqcc.h opts.def ast.h ir.h parser.h lexer.h ir.o: gmqcc.h opts.def ir.h diff --git a/include.mk b/include.mk index 77342d9..a1d7430 100644 --- a/include.mk +++ b/include.mk @@ -14,7 +14,7 @@ LDFLAGS += LIBS += -lm #objects -OBJ_C = main.o lexer.o parser.o fs.o stat.o util.o code.o ast.o ir.o conout.o ftepp.o opts.o utf8.o correct.o fold.o +OBJ_C = main.o lexer.o parser.o fs.o stat.o util.o code.o ast.o ir.o conout.o ftepp.o opts.o utf8.o correct.o fold.o intrin.o OBJ_P = util.o fs.o conout.o opts.o pak.o stat.o OBJ_T = test.o util.o opts.o conout.o fs.o stat.o OBJ_X = exec-standalone.o util.o opts.o conout.o fs.o stat.o diff --git a/intrin.c b/intrin.c new file mode 100644 index 0000000..26012e7 --- /dev/null +++ b/intrin.c @@ -0,0 +1,415 @@ +/* + * 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 +#include "parser.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. + */ +#define INTRIN_VAL(VALUE, NAME, FUNC, STYPE, VTYPE) \ + do { \ + (VALUE) = ast_value_new ( \ + parser_ctx(intrin->parser), \ + "__builtin_" NAME, \ + TYPE_FUNCTION \ + ); \ + (VALUE)->expression.next = (ast_expression*)ast_value_new ( \ + parser_ctx(intrin->parser), \ + STYPE, \ + VTYPE \ + ); \ + (FUNC) = ast_function_new ( \ + parser_ctx(intrin->parser), \ + "__builtin_" NAME, \ + (VALUE) \ + ); \ + } while (0) + +#define INTRIN_REG(FUNC, VALUE) \ + do { \ + vec_push(intrin->parser->functions, (FUNC)); \ + vec_push(intrin->parser->globals, (ast_expression*)(VALUE)); \ + } while (0) + +#define QC_M_E 2.71828182845905 + +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; + * } + */ + static ast_value *value = NULL; + + if (!value) { + ast_value *arg1 = ast_value_new(parser_ctx(intrin->parser), "x", TYPE_FLOAT); + ast_value *arg2 = ast_value_new(parser_ctx(intrin->parser), "y", TYPE_FLOAT); + ast_value *local = ast_value_new(parser_ctx(intrin->parser), "local", TYPE_FLOAT); + ast_block *body = ast_block_new(parser_ctx(intrin->parser)); + ast_block *l1b = ast_block_new(parser_ctx(intrin->parser)); /* loop 1 body */ + ast_block *l2b = ast_block_new(parser_ctx(intrin->parser)); /* loop 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(intrin->parser), + INSTR_STORE_F, + (ast_expression*)local, + (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */ + ) + ); + + /* y >>= 2 */ + vec_push(l2b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(intrin->parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)arg2, + (ast_expression*)fold_constgen_float(intrin->parser->fold, 0.25f) + ) + ); + + /* x *= x */ + vec_push(l2b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(intrin->parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)arg1, + (ast_expression*)arg1 + ) + ); + + /* while (!(y&1)) */ + loop2 = ast_loop_new ( + parser_ctx(intrin->parser), + NULL, + (ast_expression*)ast_binary_new ( + parser_ctx(intrin->parser), + INSTR_AND, + (ast_expression*)arg2, + (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */ + ), + 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(intrin->parser), + INSTR_STORE_F, + INSTR_SUB_F, + (ast_expression*)arg2, + (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */ + ) + ); + /* local *= x */ + vec_push(l1b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(intrin->parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)local, + (ast_expression*)arg1 + ) + ); + + /* while (y > 0) */ + loop1 = ast_loop_new ( + parser_ctx(intrin->parser), + NULL, + (ast_expression*)ast_binary_new ( + parser_ctx(intrin->parser), + INSTR_GT, + (ast_expression*)arg2, + (ast_expression*)intrin->fold->imm_float[0] /* 0 == 0.0f */ + ), + 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(intrin->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(intrin_t *intrin) { + /* + * 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(intrin->parser), intrin_func(intrin, "floor")); + ast_value *arg1 = ast_value_new(parser_ctx(intrin->parser), "x", TYPE_FLOAT); + ast_value *arg2 = ast_value_new(parser_ctx(intrin->parser), "y", TYPE_FLOAT); + ast_block *body = ast_block_new(parser_ctx(intrin->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(intrin->parser), + INSTR_DIV_F, + (ast_expression*)arg1, + (ast_expression*)arg2 + ) + ); + + vec_push(body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(intrin->parser), + (ast_expression*)ast_binary_new( + parser_ctx(intrin->parser), + INSTR_SUB_F, + (ast_expression*)arg1, + (ast_expression*)ast_binary_new( + parser_ctx(intrin->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(intrin_t *intrin) { + /* + * 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(intrin->parser), intrin_func(intrin, "pow")); + ast_value *arg1 = ast_value_new(parser_ctx(intrin->parser), "x", TYPE_FLOAT); + ast_block *body = ast_block_new(parser_ctx(intrin->parser)); + ast_function *func = NULL; + + INTRIN_VAL(value, "exp", func, "", TYPE_FLOAT); + + /* 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); + + /* return pow(QC_M_E, x) */ + vec_push(body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(intrin->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(intrin_t *intrin) { + /* + * 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(intrin->parser), "x", TYPE_FLOAT); + ast_value *local = ast_value_new(parser_ctx(intrin->parser), "local", TYPE_FLOAT); + ast_block *body = ast_block_new(parser_ctx(intrin->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(intrin->parser), + INSTR_STORE_F, + (ast_expression*)local, + (ast_expression*)arg1 + ) + ); + + vec_push(body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(intrin->parser), + (ast_expression*)ast_binary_new( + parser_ctx(intrin->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; +} +#undef INTRIN_REG +#undef INTRIN_VAL + +/* + * TODO: make static (and handle ast_type_string) here for the builtin + * instead of in SYA parse close. + */ +ast_expression *intrin_debug_typestring(intrin_t *intrin) { + (void)intrin; + return (ast_expression*)0x1; +} + +static const intrin_func_t intrinsics[] = { + {&intrin_exp, "__builtin_exp", "exp"}, + {&intrin_mod, "__builtin_mod", "mod"}, + {&intrin_pow, "__builtin_pow", "pow"}, + {&intrin_isnan, "__builtin_isnan", "isnan"}, + {&intrin_debug_typestring, "__builtin_debug_typestring", ""} +}; + +static void intrin_error(intrin_t *intrin, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vcompile_error(intrin->parser->lex->tok.ctx, fmt, ap); + va_end(ap); +} + +/* exposed */ +intrin_t *intrin_init(parser_t *parser) { + intrin_t *intrin = (intrin_t*)mem_a(sizeof(intrin_t)); + intrin->parser = parser; + intrin->fold = parser->fold; + intrin->intrinsics = NULL; + + vec_append(intrin->intrinsics, sizeof(intrinsics)/sizeof(*intrinsics), intrinsics); + + return intrin; +} + +void intrin_cleanup(intrin_t *intrin) { + vec_free(intrin->intrinsics); + mem_d(intrin); +} + +ast_expression *intrin_func(intrin_t *intrin, const char *name) { + size_t i = 0; + void *find; + + /* try current first */ + if ((find = (void*)parser_find_global(intrin->parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION) + for (i = 0; i < vec_size(intrin->parser->functions); ++i) + if (((ast_value*)find)->name && !strcmp(intrin->parser->functions[i]->name, ((ast_value*)find)->name) && intrin->parser->functions[i]->builtin < 0) + return (ast_expression*)find; + /* try name second */ + for (i = 0; i < vec_size(intrin->intrinsics); i++) + if (!strcmp(intrin->intrinsics[i].name, name)) + return intrin->intrinsics[i].intrin(intrin); + /* try alias third */ + for (i = 0; i < vec_size(intrin->intrinsics); i++) + if (!strcmp(intrin->intrinsics[i].alias, name)) + return intrin->intrinsics[i].intrin(intrin); + + intrin_error(intrin, "need function: `%s` compiler depends on it", name); + return NULL; +} diff --git a/intrin.h b/intrin.h deleted file mode 100644 index 0c0e861..0000000 --- a/intrin.h +++ /dev/null @@ -1,425 +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. - */ - -/* - * 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; - -static ht intrin_intrinsics(void) { - static ht intrinsics = NULL; - if (!intrinsics) - intrinsics = util_htnew(PARSER_HT_SIZE); - - return 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) - -#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->fold->imm_float[1] /* 1 == 1.0f */ - ) - ); - - /* 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*)fold_constgen_float(parser->fold, 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->fold->imm_float[1] /* 1 == 1.0f */ - ), - 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->fold->imm_float[1] /* 1 == 1.0f */ - ) - ); - /* 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->fold->imm_float[0] /* 0 == 0.0f */ - ), - 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*)fold_constgen_float(parser->fold, 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 ast_expression *intrin_debug_typestring(parser_t *parser) { - (void)parser; - return (ast_expression*)0x1; -} - -static intrin_t intrinsics[] = { - {&intrin_exp, "__builtin_exp", "exp"}, - {&intrin_mod, "__builtin_mod", "mod"}, - {&intrin_pow, "__builtin_pow", "pow"}, - {&intrin_isnan, "__builtin_isnan", "isnan"}, - {&intrin_debug_typestring, "__builtin_debug_typestring", ""} -}; - -void intrin_intrinsics_destroy(parser_t *parser) { - /*size_t i;*/ - (void)parser; - util_htdel(intrin_intrinsics()); -} - - -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].name, &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; - - 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); - } - - /* - * check aliases now to see if there is an implementation of it. - */ - for (i = 0; i < sizeof(intrinsics) / sizeof(*intrinsics); i++) - if (!strcmp(intrinsics[i].alias, name)) - return intrinsics[i].intrin(parser); - - parseerror(parser, "need function: `%s` compiler depends on it", name); - return NULL; -} diff --git a/parser.c b/parser.c index bbca3cf..9b7c75d 100644 --- a/parser.c +++ b/parser.c @@ -109,7 +109,7 @@ static ast_expression* parser_find_label(parser_t *parser, const char *name) return NULL; } -static ast_expression* parser_find_global(parser_t *parser, const char *name) +ast_expression* parser_find_global(parser_t *parser, const char *name) { ast_expression *var = (ast_expression*)util_htget(parser->aliases, parser_tokval(parser)); if (var) @@ -171,9 +171,6 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t return NULL; } -/* include intrinsics */ -#include "intrin.h" - typedef struct { size_t etype; /* 0 = expression, others are operators */ @@ -608,7 +605,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } else if (!(out = fold_op(parser->fold, op, exprs))) { /* generate a call to __builtin_mod */ - ast_expression *mod = intrin_func(parser, "mod"); + ast_expression *mod = intrin_func(parser->intrin, "mod"); ast_call *call = NULL; if (!mod) return false; /* can return null for missing floor */ @@ -810,7 +807,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } if (!(out = fold_op(parser->fold, op, exprs))) { - ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow")); + ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser->intrin, "pow")); vec_push(gencall->params, exprs[0]); vec_push(gencall->params, exprs[1]); out = (ast_expression*)gencall; @@ -1199,7 +1196,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) * TODO handle this at the intrinsic level with an ast_intrinsic * node and codegen. */ - if ((fun = sy->out[fid].out) == intrin_debug_typestring(parser)) { + if ((fun = sy->out[fid].out) == intrin_debug_typestring(parser->intrin)) { char ty[1024]; if (fid+2 != vec_size(sy->out) || vec_last(sy->out).block) @@ -1561,7 +1558,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) * use the identifier as is. */ if (!strncmp(parser_tokval(parser), "__builtin_", 10)) { - var = intrin_func(parser, parser_tokval(parser)); + var = intrin_func(parser->intrin, parser_tokval(parser)); } if (!var) { @@ -5868,7 +5865,8 @@ parser_t *parser_create() parser->reserved_version = NULL; } - parser->fold = fold_init(parser); + parser->fold = fold_init (parser); + parser->intrin = intrin_init(parser); return parser; } @@ -5986,9 +5984,9 @@ static void parser_remove_ast(parser_t *parser) if (parser->reserved_version) ast_value_delete(parser->reserved_version); - util_htdel(parser->aliases); - intrin_intrinsics_destroy(parser); + util_htdel(parser->aliases); fold_cleanup(parser->fold); + intrin_cleanup(parser->intrin); } void parser_cleanup(parser_t *parser) diff --git a/parser.h b/parser.h index 15c0ce4..3bb1178 100644 --- a/parser.h +++ b/parser.h @@ -27,6 +27,9 @@ #include "lexer.h" #include "ast.h" +typedef struct intrin_s intrin_t; +typedef struct parser_s parser_t; + typedef struct { struct parser_s *parser; ast_value **imm_float; /* vector */ @@ -36,9 +39,21 @@ typedef struct { hash_table_t *imm_string_dotranslate; /* map */ } fold_t; +typedef struct { + ast_expression *(*intrin)(intrin_t *); + const char *name; + const char *alias; +} intrin_func_t; + +struct intrin_s { + intrin_func_t *intrinsics; /* vector */ + parser_t *parser; + fold_t *fold; +}; + #define parser_ctx(p) ((p)->lex->tok.ctx) -typedef struct parser_s { +struct parser_s { lex_file *lex; int tok; @@ -98,11 +113,14 @@ typedef struct parser_s { /* collected information */ size_t max_param_count; - fold_t *fold; -} parser_t; + fold_t *fold; + intrin_t *intrin; +}; -char *parser_strdup(const char *str); +/* parser.c */ +char *parser_strdup (const char *str); +ast_expression *parser_find_global(parser_t *parser, const char *name); /* fold.c */ fold_t *fold_init (parser_t *); @@ -115,4 +133,10 @@ ast_expression *fold_op (fold_t *, const oper_info *, ast_expression int fold_cond (ir_value *, ast_function *, ast_ifthen *); +/* intrin.c */ +intrin_t *intrin_init (parser_t *parser); +void intrin_cleanup (intrin_t *intrin); +ast_expression *intrin_func (intrin_t *intrin, const char *name); +ast_expression *intrin_debug_typestring(intrin_t *intrin); + #endif