X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=intrin.c;h=ae6edaa4df446cb837147e0505518c48ecb8cd64;hb=5138a25420ce56408486f14778dba17f252411c7;hp=dbf9670f27098e2018c33fef35d1f52853a47cd7;hpb=cbeac3e5f2e3128ea9828bfd61d4d450c9a002a6;p=xonotic%2Fgmqcc.git diff --git a/intrin.c b/intrin.c index dbf9670..ae6edaa 100644 --- a/intrin.c +++ b/intrin.c @@ -20,8 +20,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "ast.h" +#include +#include "parser.h" +#include "platform.h" /* * Provides all the "intrinsics" / "builtins" for GMQCC. These can do @@ -32,162 +34,435 @@ * 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; +#define intrin_ctx(I) parser_ctx((I)->parser) +static GMQCC_INLINE ast_function *intrin_value(intrin_t *intrin, ast_value **value, const char *name, qcint_t vtype) { + ast_function *func = NULL; + char buffer[1024]; + char stype [1024]; -/* - * 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) + platform_snprintf(buffer, sizeof(buffer), "__builtin_%s", name); + platform_snprintf(stype, sizeof(stype), "<%s>", type_name[vtype]); -/* - * 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); - */ + *value = ast_value_new(intrin_ctx(intrin), buffer, TYPE_FUNCTION); + (*value)->intrinsic = true; + (*value)->expression.next = (ast_expression*)ast_value_new(intrin_ctx(intrin), stype, vtype); + func = ast_function_new(intrin_ctx(intrin), buffer, *value); + (*value)->expression.flags |= AST_FLAG_ERASEABLE; + + return func; } -INTRIN_IMP(isinf) { - /* - * float isinf(float x) { - * return (x != 0) && (x + x == x); - * } - */ +static GMQCC_INLINE void intrin_reg(intrin_t *intrin, ast_value *const value, ast_function *const func) { + vec_push(intrin->parser->functions, func); + vec_push(intrin->parser->globals, (ast_expression*)value); } -INTRIN_IMP(fpclassify) { +#define QC_M_E 2.71828182845905f + +static ast_expression *intrin_pow (intrin_t *intrin) { /* - * 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; + * float pow(float x, float y) { + * float local = 1.0f; + * while (y > 0) { + * while (!(y & 1)) { + * y >>= 2; + * x *= x; * } - * return QC_FP_NORMAL; + * y--; + * local *= x; + * } + * return local; * } - */ -} -#endif + */ + 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 = intrin_value(intrin, &value, "pow", 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); -INTRIN_IMP(exp) { + /* 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(intrin, value, func); + } + + return (ast_expression*)value; +} + +static ast_expression *intrin_mod(intrin_t *intrin) { /* - * float __builtin_exp(float x) { - * return __builtin_pow(QC_M_E, x); + * 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, "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; + 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 = intrin_value(intrin, &value, "mod", 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(intrin, value, func); + } + + 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; - INTRIN_VAL("exp", func, "", TYPE_FLOAT); + 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 = intrin_value(intrin, &value, "exp", 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*)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(parser), + parser_ctx(intrin->parser), (ast_expression*)call ) ); - vec_push(value->expressions.param, arg1); /* float x (for param) */ - vec_push(func ->blocks, body); /* {{{ body }}} */ + vec_push(value->expression.params, arg1); /* float x (for param) */ + + vec_push(func->blocks, body); /* {{{ body }}} */ - INTRIN_REG(func, value); + intrin_reg(intrin, value, func); } return (ast_expression*)value; } -#if 0 -INTRIN_IMP(exp2) { +static ast_expression *intrin_isnan(intrin_t *intrin) { /* - * float __builtin_exp2(float x) { - * return __builin_pow(2, x); + * 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 = intrin_value(intrin, &value, "isnan", 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(intrin, value, func); + } + + return (ast_expression*)value; } -INTRIN_IMP(expm1) { +static ast_expression *intrin_fabs(intrin_t *intrin) { /* - * float __builtin_expm1(float x) { - * return __builtin_exp(x) - 1; + * float fabs(float x) { + * return x < 0 ? -x : x; * } - */ + */ + static ast_value *value = NULL; + if (!value) { + 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 = intrin_value(intrin, &value, "fabs", TYPE_FLOAT); + + vec_push(body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(intrin->parser), + (ast_expression*)ast_ternary_new( + parser_ctx(intrin->parser), + (ast_expression*)ast_binary_new( + parser_ctx(intrin->parser), + INSTR_LE, + (ast_expression*)arg1, + (ast_expression*)intrin->fold->imm_float[0] + ), + (ast_expression*)ast_binary_new( + parser_ctx(intrin->parser), + INSTR_SUB_F, + (ast_expression*)intrin->fold->imm_float[0], + (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; +} + +/* + * 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; } -#endif -intrin_t intrin_intrinsics[] = { - INTRIN_DEF(exp, "exp") +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} }; + +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_fold(intrin_t *intrin, ast_value *value, ast_expression **exprs) { + size_t i; + + if (!value || !value->name) + return NULL; + + for (i = 0; i < vec_size(intrin->intrinsics); i++) { + if (!strcmp(value->name, intrin->intrinsics[i].name)) { + if (intrin->intrinsics[i].args != vec_size(exprs)) + return NULL; + /* +10 to skip the "__builtin_" substring in the string */ + return fold_intrin(intrin->fold, value->name + 10, exprs); + } + } + + return NULL; +} + +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; +}