mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-03-21 02:10:53 +00:00
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:
parent
bbe4927a20
commit
3b4a5667ea
6 changed files with 133 additions and 5 deletions
3
ast.c
3
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;
|
||||
}
|
||||
|
|
3
ast.h
3
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);
|
||||
|
|
62
fold.c
62
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
|
||||
|
|
14
intrin.c
14
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;
|
||||
|
|
52
parser.c
52
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));
|
||||
|
|
4
parser.h
4
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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue