self->getter = NULL;
self->desc = NULL;
- self->argcounter = NULL;
+ self->argcounter = NULL;
+ self->intrinsic = false;
return self;
}
/* 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);
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
"__builtin_" NAME, \
TYPE_FUNCTION \
); \
+ (VALUE)->intrinsic = true; \
(VALUE)->expression.next = (ast_expression*)ast_value_new ( \
parser_ctx(intrin->parser), \
STYPE, \
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;
size_t fid;
size_t paramcount, i;
+ bool fold = true;
fid = vec_last(sy->ops).off;
vec_shrinkby(sy->ops, 1);
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));
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);