Constant fold intrinsics if their arguments are constant. TODO: reference count intri...
authorDale Weiler <killfieldengine@gmail.com>
Wed, 28 Aug 2013 16:46:22 +0000 (12:46 -0400)
committerDale Weiler <killfieldengine@gmail.com>
Wed, 28 Aug 2013 16:46:22 +0000 (12:46 -0400)
ast.c
ast.h
fold.c
intrin.c
parser.c
parser.h

diff --git a/ast.c b/ast.c
index 099f7b8..a228f3f 100644 (file)
--- 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 (file)
--- 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 (file)
--- 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
index 50f90bf..0075ece 100644 (file)
--- 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;
index 9cbd30e..8c585f5 100644 (file)
--- 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));
index 3bb1178..d4c8ddd 100644 (file)
--- 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);