+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;
+
+ char oper = unary.oper->token[0];
+ char oper2 = unary.oper->token[1];
+ if(oper=='!')
+ {
+ if(r_constant_value.check_type<bool>())
+ set_result(!r_constant_value.value<bool>());
+ }
+ else if(oper=='~')
+ {
+ if(r_constant_value.check_type<int>())
+ set_result(~r_constant_value.value<int>());
+ else if(r_constant_value.check_type<unsigned>())
+ set_result(~r_constant_value.value<unsigned>());
+ }
+ else if(oper=='-' && !oper2)
+ {
+ if(r_constant_value.check_type<int>())
+ set_result(-r_constant_value.value<int>());
+ else if(r_constant_value.check_type<unsigned>())
+ set_result(-r_constant_value.value<unsigned>());
+ else if(r_constant_value.check_type<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;
+
+ // Currently only expressions with both sides of equal types are handled.
+ if(!left_value.check_same_type(r_constant_value))
+ return;
+
+ char oper = binary.oper->token[0];
+ char oper2 = binary.oper->token[1];
+ if(oper=='&' || oper=='|' || oper=='^')
+ {
+ if(oper2==oper && left_value.check_type<bool>())
+ set_result(evaluate_logical(oper, left_value.value<bool>(), r_constant_value.value<bool>()));
+ else if(!oper2 && left_value.check_type<int>())
+ set_result(evaluate_logical(oper, left_value.value<int>(), r_constant_value.value<int>()));
+ else if(!oper2 && left_value.check_type<unsigned>())
+ set_result(evaluate_logical(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
+ }
+ else if((oper=='<' || oper=='>') && oper2!=oper)
+ {
+ if(left_value.check_type<int>())
+ set_result(evaluate_relation(binary.oper->token, left_value.value<int>(), r_constant_value.value<int>()));
+ else if(left_value.check_type<unsigned>())
+ set_result(evaluate_relation(binary.oper->token, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
+ else if(left_value.check_type<float>())
+ set_result(evaluate_relation(binary.oper->token, left_value.value<float>(), r_constant_value.value<float>()));
+ }
+ else if((oper=='=' || oper=='!') && oper2=='=')
+ {
+ if(left_value.check_type<int>())
+ set_result((left_value.value<int>()==r_constant_value.value<int>()) == (oper=='='));
+ else if(left_value.check_type<unsigned>())
+ set_result((left_value.value<unsigned>()==r_constant_value.value<unsigned>()) == (oper=='='));
+ else if(left_value.check_type<float>())
+ set_result((left_value.value<float>()==r_constant_value.value<float>()) == (oper=='='));
+ }
+ else if(oper=='+' || oper=='-' || oper=='*' || oper=='/')
+ {
+ if(left_value.check_type<int>())
+ set_result(evaluate_arithmetic(oper, left_value.value<int>(), r_constant_value.value<int>()));
+ else if(left_value.check_type<unsigned>())
+ set_result(evaluate_arithmetic(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
+ else if(left_value.check_type<float>())
+ set_result(evaluate_arithmetic(oper, left_value.value<float>(), r_constant_value.value<float>()));
+ }
+ else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper))
+ {
+ if(left_value.check_type<int>())
+ set_result(evaluate_int_special_op(oper, left_value.value<int>(), r_constant_value.value<int>()));
+ else if(left_value.check_type<unsigned>())
+ set_result(evaluate_int_special_op(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
+ }
+}
+
+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)
+{
+ if(call.constructor && call.type && call.arguments.size()==1)
+ {
+ const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(call.type);
+ if(basic)
+ {
+ visit(call.arguments[0]);
+ bool can_fold = r_constant;
+ r_constant = false;
+ if(!can_fold)
+ return;
+
+ if(basic->kind==BasicTypeDeclaration::BOOL)
+ convert_to_result<bool>(r_constant_value);
+ else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && basic->sign)
+ convert_to_result<int>(r_constant_value);
+ else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && !basic->sign)
+ convert_to_result<unsigned>(r_constant_value);
+ else if(basic->kind==BasicTypeDeclaration::FLOAT && basic->size==32)
+ convert_to_result<float>(r_constant_value);
+
+ return;
+ }
+ }
+
+ 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);
+ for(auto i=block.body.begin(); i!=block.body.end(); ++i)
+ {
+ insert_point = i;
+ (*i)->visit(*this);
+ }
+}
+
+void ConstantConditionEliminator::visit(RefPtr<Expression> &expr)
+{
+ r_ternary_result = 0;
+ expr->visit(*this);
+ if(r_ternary_result)
+ expr = r_ternary_result;
+ r_ternary_result = 0;
+}
+
+void ConstantConditionEliminator::visit(UnaryExpression &unary)
+{
+ if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
+ if(const VariableReference *var = dynamic_cast<const VariableReference *>(unary.expression.get()))
+ {
+ auto i = current_block->variables.find(var->name);
+ r_external_side_effects = (i==current_block->variables.end() || i->second!=var->declaration);
+ return;
+ }
+
+ TraversingVisitor::visit(unary);
+}
+
+void ConstantConditionEliminator::visit(Assignment &assign)
+{
+ auto i = find_if(current_block->variables, [&assign](const pair<string, VariableDeclaration *> &kvp){ return kvp.second==assign.target.declaration; });
+ if(i==current_block->variables.end())
+ r_external_side_effects = true;
+ TraversingVisitor::visit(assign);
+}
+
+void ConstantConditionEliminator::visit(TernaryExpression &ternary)
+{
+ ConstantStatus result = check_constant_condition(*ternary.condition);
+ if(result!=NOT_CONSTANT)
+ r_ternary_result = (result==CONSTANT_TRUE ? ternary.true_expr : ternary.false_expr);
+ else
+ r_ternary_result = 0;
+}
+
+void ConstantConditionEliminator::visit(FunctionCall &call)
+{
+ r_external_side_effects = true;
+ TraversingVisitor::visit(call);
+}
+
+void ConstantConditionEliminator::visit(Conditional &cond)
+{
+ 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;
+ }
+
+ r_external_side_effects = false;
+ TraversingVisitor::visit(cond);
+
+ if(cond.body.body.empty() && cond.else_body.body.empty() && !r_external_side_effects)
+ nodes_to_remove.insert(&cond);
+}
+
+void ConstantConditionEliminator::visit(Iteration &iter)
+{
+ if(iter.condition)
+ {
+ ConstantStatus result = check_constant_condition(*iter.condition);
+ if(result==CONSTANT_FALSE)
+ {
+ nodes_to_remove.insert(&iter);
+ return;
+ }
+ }
+
+ r_external_side_effects = false;
+ TraversingVisitor::visit(iter);
+ if(iter.body.body.empty() && !r_external_side_effects)
+ nodes_to_remove.insert(&iter);
+}
+
+
+bool UnreachableCodeRemover::apply(Stage &stage)
+{
+ stage.content.visit(*this);
+ NodeRemover().apply(stage, unreachable_nodes);
+ return !unreachable_nodes.empty();
+}
+
+void UnreachableCodeRemover::visit(Block &block)
+{
+ auto i = block.body.begin();
+ for(; (reachable && i!=block.body.end()); ++i)
+ (*i)->visit(*this);
+ for(; i!=block.body.end(); ++i)
+ unreachable_nodes.insert(i->get());
+}
+
+void UnreachableCodeRemover::visit(FunctionDeclaration &func)
+{
+ TraversingVisitor::visit(func);
+ reachable = true;
+}
+
+void UnreachableCodeRemover::visit(Conditional &cond)
+{
+ cond.body.visit(*this);
+ bool reachable_if_true = reachable;
+ reachable = true;