From 3b4a5667ea8b1b7aa8a10734c57c02d1561fcdd7 Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Wed, 28 Aug 2013 12:46:22 -0400 Subject: [PATCH] Constant fold intrinsics if their arguments are constant. TODO: reference count intrinsics such that they're not generated unless they're used, currently when an intrinsic can be folded-away it's marked for generation and makes it to the final output binary even though it isn't used. --- ast.c | 3 ++- ast.h | 3 +++ fold.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ intrin.c | 14 +++++++++++++ parser.c | 52 ++++++++++++++++++++++++++++++++++++++++++++--- parser.h | 4 +++- 6 files changed, 133 insertions(+), 5 deletions(-) diff --git a/ast.c b/ast.c index 099f7b8..a228f3f 100644 --- a/ast.c +++ b/ast.c @@ -369,7 +369,8 @@ ast_value* ast_value_new(lex_ctx_t ctx, const char *name, int t) self->getter = NULL; self->desc = NULL; - self->argcounter = NULL; + self->argcounter = NULL; + self->intrinsic = false; return self; } diff --git a/ast.h b/ast.h index 9b7e558..11644ff 100644 --- a/ast.h +++ b/ast.h @@ -207,6 +207,9 @@ struct ast_value_s /* ONLY for arrays in progs version up to 6 */ ast_value *setter; ast_value *getter; + + + bool intrinsic; /* true if associated with intrinsic */ }; ast_value* ast_value_new(lex_ctx_t ctx, const char *name, int qctype); diff --git a/fold.c b/fold.c index 02335f2..360b4f0 100644 --- a/fold.c +++ b/fold.c @@ -673,12 +673,74 @@ ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **op return NULL; } +#define expect(X) \ + do { \ + if (vec_size(params) != (X)) { \ + compile_error( \ + fold_ctx(fold), \ + "internal error: attempted to constant-fold with invalid paramaters for intrinsic `%s`", \ + intrin \ + ); \ + return NULL; \ + } \ + } while (0) + +ast_expression *fold_intrin(fold_t *fold, const char *intrin, ast_expression **params) { + if (!fold) return NULL; + if (!intrin) return NULL; + + if (!strcmp(intrin, "__builtin_exp")) { + expect(1); + ++opts_optimizationcount[OPTIM_CONST_FOLD]; + return fold_constgen_float(fold, exp(fold_immvalue_float((ast_value*)params[0]))); + } + + if (!strcmp(intrin, "__builtin_mod")) { + expect(2); + ++opts_optimizationcount[OPTIM_CONST_FOLD]; + return fold_constgen_float( + fold, + fmodf( + fold_immvalue_float((ast_value*)params[0]), + fold_immvalue_float((ast_value*)params[1]) + ) + ); + } + + if (!strcmp(intrin, "__builtin_pow")) { + expect(2); + ++opts_optimizationcount[OPTIM_CONST_FOLD]; + return fold_constgen_float( + fold, + powf( + fold_immvalue_float((ast_value*)params[0]), + fold_immvalue_float((ast_value*)params[1]) + ) + ); + } + + if (!strcmp(intrin, "__builtin_isnan")) { + expect(1); + ++opts_optimizationcount[OPTIM_CONST_FOLD]; + return fold_constgen_float(fold, isnan(fold_immvalue_float((ast_value*)params[0])) != 0.0f); + } + + if (!strcmp(intrin, "__builtin_fabs")) { + expect(1); + ++opts_optimizationcount[OPTIM_CONST_FOLD]; + return fold_constgen_float(fold, fabs(fold_immvalue_float((ast_value*)params[0]))); + } + + return NULL; +} + /* * These are all the actual constant folding methods that happen in between * the AST/IR stage of the compiler , i.e eliminating branches for const * expressions, which is the only supported thing so far. We undefine the * testing macros here because an ir_value is differant than an ast_value. */ +#undef expect #undef isfloat #undef isstring #undef isvector diff --git a/intrin.c b/intrin.c index 50f90bf..0075ece 100644 --- a/intrin.c +++ b/intrin.c @@ -39,6 +39,7 @@ "__builtin_" NAME, \ TYPE_FUNCTION \ ); \ + (VALUE)->intrinsic = true; \ (VALUE)->expression.next = (ast_expression*)ast_value_new ( \ parser_ctx(intrin->parser), \ STYPE, \ @@ -439,6 +440,19 @@ void intrin_cleanup(intrin_t *intrin) { 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)) + return fold_intrin(intrin->fold, value->name, exprs); + + return NULL; +} + ast_expression *intrin_func(intrin_t *intrin, const char *name) { size_t i = 0; void *find; diff --git a/parser.c b/parser.c index 9cbd30e..8c585f5 100644 --- a/parser.c +++ b/parser.c @@ -1119,6 +1119,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) size_t fid; size_t paramcount, i; + bool fold = true; fid = vec_last(sy->ops).off; vec_shrinkby(sy->ops, 1); @@ -1161,14 +1162,59 @@ static bool parser_close_call(parser_t *parser, shunt *sy) vec_shrinkby(sy->out, 1); return true; } + + /* + * Now we need to determine if the function that is being called is + * an intrinsic so we can evaluate if the arguments to it are constant + * and than fruitfully fold them. + */ +#define fold_can_1(X) \ + (ast_istype(((ast_expression*)(X)), ast_value) && (X)->hasvalue && ((X)->cvq == CV_CONST) && \ + ((ast_expression*)(X))->vtype != TYPE_FUNCTION) + + if (fid + 1 < vec_size(sy->out)) + ++paramcount; + + for (i = 0; i < paramcount; ++i) { + if (!fold_can_1((ast_value*)sy->out[fid + 1 + i].out)) { + fold = false; + break; + } + } + + /* + * All is well which ends well, if we make it into here we can ignore the + * intrinsic call and just evaluate it i.e constant fold it. + */ + if (fold && ast_istype(fun, ast_value) && ((ast_value*)fun)->intrinsic) { + ast_expression **exprs = NULL; + ast_expression *fold = NULL; + + for (i = 0; i < paramcount; i++) + vec_push(exprs, sy->out[fid+1 + i].out); + + if (!(fold = intrin_fold(parser->intrin, (ast_value*)fun, exprs))) { + vec_free(exprs); + goto fold_leave; + } + + /* + * Blub: what sorts of unreffing and resizing of + * sy->out should I be doing here? + */ + sy->out[fid] = syexp(fold->node.context, fold); + vec_shrinkby(sy->out, 1); + vec_free(exprs); + + return true; + } + + fold_leave: call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun); if (!call) return false; - if (fid+1 < vec_size(sy->out)) - ++paramcount; - if (fid+1 + paramcount != vec_size(sy->out)) { parseerror(parser, "internal error: parameter count mismatch: (%lu+1+%lu), %lu", (unsigned long)fid, (unsigned long)paramcount, (unsigned long)vec_size(sy->out)); diff --git a/parser.h b/parser.h index 3bb1178..d4c8ddd 100644 --- a/parser.h +++ b/parser.h @@ -129,13 +129,15 @@ ast_expression *fold_constgen_float (fold_t *, qcfloat_t); ast_expression *fold_constgen_vector(fold_t *, vec3_t); ast_expression *fold_constgen_string(fold_t *, const char *, bool); bool fold_generate (fold_t *, ir_builder *); -ast_expression *fold_op (fold_t *, const oper_info *, ast_expression**); +ast_expression *fold_op (fold_t *, const oper_info *, ast_expression **); +ast_expression *fold_intrin (fold_t *, const char *, 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_fold (intrin_t *intrin, ast_value *, ast_expression **); ast_expression *intrin_func (intrin_t *intrin, const char *name); ast_expression *intrin_debug_typestring(intrin_t *intrin); -- 2.39.2