#include "gmqcc.h"
#include "ast.h"
+#include "parser.h"
#define ast_instantiate(T, ctx, destroyfn) \
T* self = (T*)mem_a(sizeof(T)); \
mem_d(self);
}
-static const char* ast_function_label(ast_function *self, const char *prefix)
+const char* ast_function_label(ast_function *self, const char *prefix)
{
size_t id;
size_t len;
ir_block *ontrue_endblock = NULL;
ir_block *onfalse_endblock = NULL;
ir_block *merge = NULL;
+ int fold = 0;
/* We don't output any value, thus also don't care about r/lvalue */
(void)out;
/* update the block which will get the jump - because short-logic or ternaries may have changed this */
cond = func->curblock;
- /* eliminate branches if value is constant */
- if (condval->vtype == TYPE_FLOAT && condval->hasvalue && condval->cvq == CV_CONST) {
- /* don't generate if statements */
- if (condval->constval.vfloat == 1.0f && self->on_true) {
- if (!(ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "ontrue"))))
- return false;
- /* generate */
- if (!(*(cgen = self->on_true->codegen))((ast_expression*)(self->on_true), func, false, &dummy))
- return false;
- if (!ir_block_create_jump(func->curblock, ast_ctx(self), ontrue))
- return false;
- func->curblock = ontrue;
- return true;
- } else if (condval->constval.vfloat == 0.0f && self->on_false) {
- if (!(onfalse = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "onfalse"))))
- return false;
- /* generate */
- if (!(*(cgen = self->on_false->codegen))((ast_expression*)(self->on_false), func, false, &dummy))
- return false;
- if (!ir_block_create_jump(func->curblock, ast_ctx(self), onfalse))
- return false;
- func->curblock = onfalse;
- return true;
- }
- }
- /* on-true path */
-
+ /* try constant folding away the if */
+ if ((fold = fold_cond((ast_value*)condval, func, self)) != -1)
+ return fold;
+
if (self->on_true) {
/* create on-true block */
ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "ontrue"));
/* For "optimized" builds this can just keep returning "foo"...
* or whatever...
*/
-/*const char* ast_function_label(ast_function*, const char *prefix);*/
+const char* ast_function_label(ast_function*, const char *prefix);
bool ast_function_codegen(ast_function *self, ir_builder *builder);
bool ast_generate_accessors(ast_value *asvalue, ir_builder *ir);
compile_error(fold_ctx(fold), "internal error: attempted to constant for unsupported operator");
return NULL;
}
+
+/*
+ * These are all the actual constant folding methods that happen in the AST
+ * stage of the compiler, i.e eliminating branches for const expressions,
+ * which is the only supported thing so far.
+ */
+int fold_cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
+ if (condval->vtype == TYPE_FLOAT && condval->hasvalue && condval->cvq == CV_CONST) {
+ ast_expression_codegen *cgen;
+ ir_block *elide;
+ ir_value *dummy;
+ bool istrue = (fold_immvalue_float(condval) == 1.0f && branch->on_true);
+ bool isfalse = (fold_immvalue_float(condval) == 0.0f && branch->on_false);
+ ast_expression *path = (istrue) ? branch->on_true :
+ (isfalse) ? branch->on_false : NULL;
+ if (!path)
+ return false;
+ if (!(elide = ir_function_create_block(ast_ctx(branch), func->ir_func, ast_function_label(func, ((istrue) ? "ontrue" : "onfalse")))))
+ return false;
+ if (!(*(cgen = path->codegen))((ast_expression*)path, func, false, &dummy))
+ return false;
+ if (!ir_block_create_jump(func->curblock, ast_ctx(branch), elide))
+ return false;
+ /*
+ * now the branch has been eliminates, and the correct block for the constant evaluation
+ * is expanded into the current block for the function.
+ */
+ func->curblock = elide;
+ return true;
+ }
+ return -1; /* nothing done */
+}
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**);
+
+int fold_cond (ir_value *, ast_function *, ast_ifthen *);
+
#endif