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.

This commit is contained in:
Dale Weiler 2013-08-28 12:46:22 -04:00
parent bbe4927a20
commit 3b4a5667ea
6 changed files with 133 additions and 5 deletions

3
ast.c
View file

@ -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;
}

3
ast.h
View file

@ -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);

62
fold.c
View file

@ -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

View file

@ -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;

View file

@ -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));

View file

@ -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);