}
+BasicTypeDeclaration::Kind ConstantFolder::get_value_kind(const Variant &value)
+{
+ if(value.check_type<bool>())
+ return BasicTypeDeclaration::BOOL;
+ else if(value.check_type<int>())
+ return BasicTypeDeclaration::INT;
+ else if(value.check_type<float>())
+ return BasicTypeDeclaration::FLOAT;
+ else
+ return BasicTypeDeclaration::VOID;
+}
+
+template<typename T>
+T ConstantFolder::evaluate_logical(char oper, T left, T right)
+{
+ switch(oper)
+ {
+ case '&': return left&right;
+ case '|': return left|right;
+ case '^': return left^right;
+ default: return T();
+ }
+}
+
+template<typename T>
+bool ConstantFolder::evaluate_relation(const char *oper, T left, T right)
+{
+ switch(oper[0]|oper[1])
+ {
+ case '<': return left<right;
+ case '<'|'=': return left<=right;
+ case '>': return left>right;
+ case '>'|'=': return left>=right;
+ default: return false;
+ }
+}
+
+template<typename T>
+T ConstantFolder::evaluate_arithmetic(char oper, T left, T right)
+{
+ switch(oper)
+ {
+ case '+': return left+right;
+ case '-': return left-right;
+ case '*': return left*right;
+ case '/': return left/right;
+ default: return T();
+ }
+}
+
+void ConstantFolder::set_result(const Variant &value, bool literal)
+{
+ r_constant_value = value;
+ r_constant = true;
+ r_literal = literal;
+}
+
+void ConstantFolder::visit(RefPtr<Expression> &expr)
+{
+ r_constant_value = Variant();
+ r_constant = false;
+ r_literal = false;
+ r_uses_iter_var = false;
+ expr->visit(*this);
+ /* Don't replace literals since they'd only be replaced with an identical
+ literal. Also skip anything that uses an iteration variable, but pass on
+ the result so the Iteration visiting function can handle it. */
+ if(!r_constant || r_literal || r_uses_iter_var)
+ return;
+
+ BasicTypeDeclaration::Kind kind = get_value_kind(r_constant_value);
+ if(kind==BasicTypeDeclaration::VOID)
+ {
+ r_constant = false;
+ return;
+ }
+
+ RefPtr<Literal> literal = new Literal;
+ if(kind==BasicTypeDeclaration::BOOL)
+ literal->token = (r_constant_value.value<bool>() ? "true" : "false");
+ else if(kind==BasicTypeDeclaration::INT)
+ literal->token = lexical_cast<string>(r_constant_value.value<int>());
+ else if(kind==BasicTypeDeclaration::FLOAT)
+ literal->token = lexical_cast<string>(r_constant_value.value<float>());
+ literal->value = r_constant_value;
+ expr = literal;
+}
+
+void ConstantFolder::visit(Literal &literal)
+{
+ set_result(literal.value, true);
+}
+
+void ConstantFolder::visit(VariableReference &var)
+{
+ /* If an iteration variable is initialized with a constant value, return
+ that value here for the purpose of evaluating the loop condition for the
+ first iteration. */
+ if(var.declaration==iteration_var)
+ {
+ set_result(iter_init_value);
+ r_uses_iter_var = true;
+ }
+}
+
+void ConstantFolder::visit(MemberAccess &memacc)
+{
+ TraversingVisitor::visit(memacc);
+ r_constant = false;
+}
+
+void ConstantFolder::visit(Swizzle &swizzle)
+{
+ TraversingVisitor::visit(swizzle);
+ r_constant = false;
+}
+
+void ConstantFolder::visit(UnaryExpression &unary)
+{
+ TraversingVisitor::visit(unary);
+ bool can_fold = r_constant;
+ r_constant = false;
+ if(!can_fold)
+ return;
+
+ BasicTypeDeclaration::Kind kind = get_value_kind(r_constant_value);
+
+ char oper = unary.oper->token[0];
+ char oper2 = unary.oper->token[1];
+ if(oper=='!')
+ {
+ if(kind==BasicTypeDeclaration::BOOL)
+ set_result(!r_constant_value.value<bool>());
+ }
+ else if(oper=='~')
+ {
+ if(kind==BasicTypeDeclaration::INT)
+ set_result(~r_constant_value.value<int>());
+ }
+ else if(oper=='-' && !oper2)
+ {
+ if(kind==BasicTypeDeclaration::INT)
+ set_result(-r_constant_value.value<int>());
+ else if(kind==BasicTypeDeclaration::FLOAT)
+ set_result(-r_constant_value.value<float>());
+ }
+}
+
+void ConstantFolder::visit(BinaryExpression &binary)
+{
+ visit(binary.left);
+ bool left_constant = r_constant;
+ bool left_iter_var = r_uses_iter_var;
+ Variant left_value = r_constant_value;
+ visit(binary.right);
+ if(left_iter_var)
+ r_uses_iter_var = true;
+
+ bool can_fold = (left_constant && r_constant);
+ r_constant = false;
+ if(!can_fold)
+ return;
+
+ BasicTypeDeclaration::Kind left_kind = get_value_kind(left_value);
+ BasicTypeDeclaration::Kind right_kind = get_value_kind(r_constant_value);
+ // Currently only expressions with both sides of equal types are handled.
+ if(left_kind!=right_kind)
+ return;
+
+ char oper = binary.oper->token[0];
+ char oper2 = binary.oper->token[1];
+ if(oper=='&' || oper=='|' || oper=='^')
+ {
+ if(oper2==oper && left_kind==BasicTypeDeclaration::BOOL)
+ set_result(evaluate_logical(oper, left_value.value<bool>(), r_constant_value.value<bool>()));
+ else if(!oper2 && left_kind==BasicTypeDeclaration::INT)
+ set_result(evaluate_logical(oper, left_value.value<int>(), r_constant_value.value<int>()));
+ }
+ else if((oper=='<' || oper=='>') && oper2!=oper)
+ {
+ if(left_kind==BasicTypeDeclaration::INT)
+ set_result(evaluate_relation(binary.oper->token, left_value.value<int>(), r_constant_value.value<int>()));
+ else if(left_kind==BasicTypeDeclaration::FLOAT)
+ set_result(evaluate_relation(binary.oper->token, left_value.value<float>(), r_constant_value.value<float>()));
+ }
+ else if((oper=='=' || oper=='!') && oper2=='=')
+ {
+ if(left_kind==BasicTypeDeclaration::INT)
+ set_result((left_value.value<int>()==r_constant_value.value<int>()) == (oper=='='));
+ if(left_kind==BasicTypeDeclaration::FLOAT)
+ set_result((left_value.value<float>()==r_constant_value.value<float>()) == (oper=='='));
+ }
+ else if(oper=='+' || oper=='-' || oper=='*' || oper=='/')
+ {
+ if(left_kind==BasicTypeDeclaration::INT)
+ set_result(evaluate_arithmetic(oper, left_value.value<int>(), r_constant_value.value<int>()));
+ else if(left_kind==BasicTypeDeclaration::FLOAT)
+ set_result(evaluate_arithmetic(oper, left_value.value<float>(), r_constant_value.value<float>()));
+ }
+ else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper))
+ {
+ if(left_kind!=BasicTypeDeclaration::INT)
+ return;
+
+ if(oper=='%')
+ set_result(left_value.value<int>()%r_constant_value.value<int>());
+ else if(oper=='<')
+ set_result(left_value.value<int>()<<r_constant_value.value<int>());
+ else if(oper=='>')
+ set_result(left_value.value<int>()>>r_constant_value.value<int>());
+ }
+}
+
+void ConstantFolder::visit(Assignment &assign)
+{
+ TraversingVisitor::visit(assign);
+ r_constant = false;
+}
+
+void ConstantFolder::visit(TernaryExpression &ternary)
+{
+ TraversingVisitor::visit(ternary);
+ r_constant = false;
+}
+
+void ConstantFolder::visit(FunctionCall &call)
+{
+ TraversingVisitor::visit(call);
+ r_constant = false;
+}
+
+void ConstantFolder::visit(VariableDeclaration &var)
+{
+ if(iteration_init && var.init_expression)
+ {
+ visit(var.init_expression);
+ if(r_constant)
+ {
+ /* Record the value of a constant initialization expression of an
+ iteration, so it can be used to evaluate the loop condition. */
+ iteration_var = &var;
+ iter_init_value = r_constant_value;
+ }
+ }
+ else
+ TraversingVisitor::visit(var);
+}
+
+void ConstantFolder::visit(Iteration &iter)
+{
+ SetForScope<Block *> set_block(current_block, &iter.body);
+
+ /* The iteration variable is not normally inlined into expressions, so we
+ process it specially here. If the initial value causes the loop condition
+ to evaluate to false, then the expression can be folded. */
+ iteration_var = 0;
+ if(iter.init_statement)
+ {
+ SetFlag set_init(iteration_init);
+ iter.init_statement->visit(*this);
+ }
+
+ if(iter.condition)
+ {
+ visit(iter.condition);
+ if(r_constant && r_constant_value.check_type<bool>() && !r_constant_value.value<bool>())
+ {
+ RefPtr<Literal> literal = new Literal;
+ literal->token = "false";
+ literal->value = r_constant_value;
+ iter.condition = literal;
+ }
+ }
+ iteration_var = 0;
+
+ iter.body.visit(*this);
+ if(iter.loop_expression)
+ visit(iter.loop_expression);
+}
+
+
void ConstantConditionEliminator::apply(Stage &stage)
{
stage.content.visit(*this);
NodeRemover().apply(stage, nodes_to_remove);
}
+ConstantConditionEliminator::ConstantStatus ConstantConditionEliminator::check_constant_condition(const Expression &expr)
+{
+ if(const Literal *literal = dynamic_cast<const Literal *>(&expr))
+ if(literal->value.check_type<bool>())
+ return (literal->value.value<bool>() ? CONSTANT_TRUE : CONSTANT_FALSE);
+ return NOT_CONSTANT;
+}
+
void ConstantConditionEliminator::visit(Block &block)
{
SetForScope<Block *> set_block(current_block, &block);
void ConstantConditionEliminator::visit(Conditional &cond)
{
- if(Literal *literal = dynamic_cast<Literal *>(cond.condition.get()))
- if(literal->value.check_type<bool>())
- {
- Block &block = (literal->value.value<bool>() ? cond.body : cond.else_body);
- current_block->body.splice(insert_point, block.body);
- nodes_to_remove.insert(&cond);
- return;
- }
+ ConstantStatus result = check_constant_condition(*cond.condition);
+ if(result!=NOT_CONSTANT)
+ {
+ Block &block = (result==CONSTANT_TRUE ? cond.body : cond.else_body);
+ // TODO should check variable names for conflicts. Potentially reuse InlineContentInjector?
+ current_block->body.splice(insert_point, block.body);
+ nodes_to_remove.insert(&cond);
+ return;
+ }
TraversingVisitor::visit(cond);
}
{
if(iter.condition)
{
- /* If the loop condition is always false on the first iteration, the
- entire loop can be removed */
- ExpressionEvaluator::ValueMap values;
- if(VariableDeclaration *var = dynamic_cast<VariableDeclaration *>(iter.init_statement.get()))
- values[var] = var->init_expression.get();
- ExpressionEvaluator eval(values);
- iter.condition->visit(eval);
- if(eval.is_result_valid() && !eval.get_result())
+ ConstantStatus result = check_constant_condition(*iter.condition);
+ if(result==CONSTANT_FALSE)
{
nodes_to_remove.insert(&iter);
return;
#include <map>
#include <set>
-#include "evaluate.h"
#include "visitor.h"
namespace Msp {
virtual void visit(Iteration &);
};
+/** Replaces expressions consisting entirely of literals with the results of
+evaluating the expression.*/
+class ConstantFolder: private TraversingVisitor
+{
+private:
+ VariableDeclaration *iteration_var;
+ Variant iter_init_value;
+ Variant r_constant_value;
+ bool iteration_init;
+ bool r_constant;
+ bool r_literal;
+ bool r_uses_iter_var;
+ bool r_any_folded;
+
+public:
+ bool apply(Stage &s) { s.content.visit(*this); return r_any_folded; }
+
+private:
+ static BasicTypeDeclaration::Kind get_value_kind(const Variant &);
+ template<typename T>
+ static T evaluate_logical(char, T, T);
+ template<typename T>
+ static bool evaluate_relation(const char *, T, T);
+ template<typename T>
+ static T evaluate_arithmetic(char, T, T);
+ void set_result(const Variant &, bool = false);
+
+ virtual void visit(RefPtr<Expression> &);
+ virtual void visit(Literal &);
+ virtual void visit(VariableReference &);
+ virtual void visit(MemberAccess &);
+ virtual void visit(Swizzle &);
+ virtual void visit(UnaryExpression &);
+ virtual void visit(BinaryExpression &);
+ virtual void visit(Assignment &);
+ virtual void visit(TernaryExpression &);
+ virtual void visit(FunctionCall &);
+ virtual void visit(VariableDeclaration &);
+ virtual void visit(Iteration &);
+};
+
/** Removes conditional statements and loops where the condition can be
determined as constant at compile time. */
class ConstantConditionEliminator: private TraversingVisitor
{
private:
+ enum ConstantStatus
+ {
+ CONSTANT_FALSE,
+ CONSTANT_TRUE,
+ NOT_CONSTANT
+ };
+
NodeList<Statement>::iterator insert_point;
std::set<Node *> nodes_to_remove;
void apply(Stage &);
private:
+ ConstantStatus check_constant_condition(const Expression &);
+
virtual void visit(Block &);
virtual void visit(Conditional &);
virtual void visit(Iteration &);