1 #include <msp/core/raii.h>
2 #include <msp/strings/format.h>
12 ConstantSpecializer::ConstantSpecializer():
16 void ConstantSpecializer::apply(Stage &stage, const map<string, int> &v)
19 stage.content.visit(*this);
22 void ConstantSpecializer::visit(VariableDeclaration &var)
24 bool specializable = false;
27 vector<Layout::Qualifier> &qualifiers = var.layout->qualifiers;
28 for(vector<Layout::Qualifier>::iterator i=qualifiers.begin(); (!specializable && i!=qualifiers.end()); ++i)
29 if(i->name=="constant_id")
35 if(qualifiers.empty())
41 map<string, int>::const_iterator i = values->find(var.name);
44 RefPtr<Literal> literal = new Literal;
47 literal->token = (i->second ? "true" : "false");
48 literal->value = static_cast<bool>(i->second);
50 else if(var.type=="int")
52 literal->token = lexical_cast<string>(i->second);
53 literal->value = i->second;
55 var.init_expression = literal;
61 InlineableFunctionLocator::InlineableFunctionLocator():
66 void InlineableFunctionLocator::visit(FunctionCall &call)
68 FunctionDeclaration *def = call.declaration;
70 def = def->definition;
74 unsigned &count = refcounts[def];
76 /* Don't inline functions which are called more than once or are called
78 if((count>1 && def->source!=BUILTIN_SOURCE) || def==current_function)
79 inlineable.erase(def);
82 TraversingVisitor::visit(call);
85 void InlineableFunctionLocator::visit(FunctionDeclaration &func)
87 bool has_out_params = false;
88 for(NodeArray<VariableDeclaration>::const_iterator i=func.parameters.begin(); (!has_out_params && i!=func.parameters.end()); ++i)
89 has_out_params = ((*i)->interface=="out");
91 unsigned &count = refcounts[func.definition];
92 if((count<=1 || func.source==BUILTIN_SOURCE) && !has_out_params)
93 inlineable.insert(func.definition);
95 SetForScope<FunctionDeclaration *> set(current_function, &func);
97 TraversingVisitor::visit(func);
100 void InlineableFunctionLocator::visit(Conditional &cond)
102 TraversingVisitor::visit(cond);
103 inlineable.erase(current_function);
106 void InlineableFunctionLocator::visit(Iteration &iter)
108 TraversingVisitor::visit(iter);
109 inlineable.erase(current_function);
112 void InlineableFunctionLocator::visit(Return &ret)
114 TraversingVisitor::visit(ret);
116 inlineable.erase(current_function);
121 InlineContentInjector::InlineContentInjector():
126 string InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList<Statement>::iterator &ins_pt, FunctionCall &call)
128 source_func = call.declaration->definition;
130 /* Populate referenced_names from the target function so we can rename
131 variables from the inlined function that would conflict. */
133 target_func.visit(*this);
135 /* Inline and rename passes must be interleaved so used variable names are
136 known when inlining the return statement. */
138 staging_block.parent = &tgt_blk;
139 staging_block.variables.clear();
141 vector<RefPtr<VariableDeclaration> > params;
142 params.reserve(source_func->parameters.size());
143 for(NodeArray<VariableDeclaration>::iterator i=source_func->parameters.begin(); i!=source_func->parameters.end(); ++i)
145 RefPtr<VariableDeclaration> var = (*i)->clone();
146 var->interface.clear();
148 SetForScope<Pass> set_pass(pass, RENAME);
151 staging_block.body.push_back_nocopy(var);
152 params.push_back(var);
155 for(NodeList<Statement>::iterator i=source_func->body.body.begin(); i!=source_func->body.body.end(); ++i)
157 r_inlined_statement = 0;
159 if(!r_inlined_statement)
160 r_inlined_statement = (*i)->clone();
162 SetForScope<Pass> set_pass(pass, RENAME);
163 r_inlined_statement->visit(*this);
165 staging_block.body.push_back_nocopy(r_inlined_statement);
168 /* Now collect names from the staging block. Local variables that would
169 have conflicted with the target function were renamed earlier. */
171 referenced_names.clear();
172 staging_block.variables.clear();
173 staging_block.visit(*this);
175 /* Rename variables in the target function so they don't interfere with
176 global identifiers used by the source function. */
178 staging_block.parent = source_func->body.parent;
179 target_func.visit(*this);
181 // Put the argument expressions in place after all renaming has been done.
182 for(unsigned i=0; i<source_func->parameters.size(); ++i)
183 params[i]->init_expression = call.arguments[i]->clone();
185 tgt_blk.body.splice(ins_pt, staging_block.body);
187 NodeReorderer().apply(stage, target_func, DependencyCollector().apply(*source_func));
189 return r_result_name;
192 void InlineContentInjector::visit(VariableReference &var)
196 map<string, VariableDeclaration *>::const_iterator i = staging_block.variables.find(var.name);
197 if(i!=staging_block.variables.end())
198 var.name = i->second->name;
200 else if(pass==REFERENCED)
201 referenced_names.insert(var.name);
204 void InlineContentInjector::visit(InterfaceBlockReference &iface)
207 referenced_names.insert(iface.name);
210 void InlineContentInjector::visit(FunctionCall &call)
213 referenced_names.insert(call.name);
214 TraversingVisitor::visit(call);
217 void InlineContentInjector::visit(VariableDeclaration &var)
219 TraversingVisitor::visit(var);
223 /* Check against conflicts with the other context as well as variables
224 already renamed here. */
225 bool conflict = (staging_block.variables.count(var.name) || referenced_names.count(var.name));
226 staging_block.variables[var.name] = &var;
229 string mapped_name = get_unused_variable_name(staging_block, var.name);
230 if(mapped_name!=var.name)
232 staging_block.variables[mapped_name] = &var;
233 var.name = mapped_name;
237 else if(pass==REFERENCED)
238 referenced_names.insert(var.type);
241 void InlineContentInjector::visit(Return &ret)
243 TraversingVisitor::visit(ret);
245 if(pass==INLINE && ret.expression)
247 // Create a new variable to hold the return value of the inlined function.
248 r_result_name = get_unused_variable_name(staging_block, "_return");
249 RefPtr<VariableDeclaration> var = new VariableDeclaration;
250 var->source = ret.source;
251 var->line = ret.line;
252 var->type = source_func->return_type;
253 var->name = r_result_name;
254 var->init_expression = ret.expression->clone();
255 r_inlined_statement = var;
260 FunctionInliner::FunctionInliner():
262 r_any_inlined(false),
263 r_inlined_here(false)
266 bool FunctionInliner::apply(Stage &s)
269 inlineable = InlineableFunctionLocator().apply(s);
270 r_any_inlined = false;
271 s.content.visit(*this);
272 return r_any_inlined;
275 void FunctionInliner::visit(RefPtr<Expression> &ptr)
281 ptr = r_inline_result;
282 r_any_inlined = true;
287 void FunctionInliner::visit(Block &block)
289 SetForScope<Block *> set_block(current_block, &block);
290 SetForScope<NodeList<Statement>::iterator> save_insert_point(insert_point, block.body.begin());
291 for(NodeList<Statement>::iterator i=block.body.begin(); (!r_inlined_here && i!=block.body.end()); ++i)
298 void FunctionInliner::visit(FunctionCall &call)
300 for(NodeArray<Expression>::iterator i=call.arguments.begin(); (!r_inlined_here && i!=call.arguments.end()); ++i)
306 FunctionDeclaration *def = call.declaration;
308 def = def->definition;
310 if(def && inlineable.count(def))
312 string result_name = InlineContentInjector().apply(*stage, *current_function, *current_block, insert_point, call);
314 // This will later get removed by UnusedVariableRemover.
315 if(result_name.empty())
316 result_name = "_msp_unused_from_inline";
318 RefPtr<VariableReference> ref = new VariableReference;
319 ref->name = result_name;
320 r_inline_result = ref;
322 /* Inlined variables need to be resolved before this function can be
324 inlineable.erase(current_function);
325 r_inlined_here = true;
329 void FunctionInliner::visit(FunctionDeclaration &func)
331 SetForScope<FunctionDeclaration *> set_func(current_function, &func);
332 TraversingVisitor::visit(func);
333 r_inlined_here = false;
336 void FunctionInliner::visit(Iteration &iter)
338 /* Visit the initialization statement before entering the loop body so the
339 inlined statements get inserted outside. */
340 if(iter.init_statement)
341 iter.init_statement->visit(*this);
343 SetForScope<Block *> set_block(current_block, &iter.body);
344 /* Skip the condition and loop expression parts because they're not properly
345 inside the body block. Inlining anything into them will require a more
346 comprehensive transformation. */
347 iter.body.visit(*this);
351 ExpressionInliner::ExpressionInliner():
353 r_any_inlined(false),
356 iteration_init(false),
361 bool ExpressionInliner::apply(Stage &s)
363 s.content.visit(*this);
364 return r_any_inlined;
367 void ExpressionInliner::inline_expression(Expression &expr, RefPtr<Expression> &ptr)
370 r_any_inlined = true;
373 void ExpressionInliner::visit(Block &block)
375 TraversingVisitor::visit(block);
377 for(map<string, VariableDeclaration *>::iterator i=block.variables.begin(); i!=block.variables.end(); ++i)
379 map<Assignment::Target, ExpressionInfo>::iterator j = expressions.lower_bound(i->second);
380 for(; (j!=expressions.end() && j->first.declaration==i->second); )
382 if(j->second.expression && j->second.inline_point)
383 inline_expression(*j->second.expression, *j->second.inline_point);
385 expressions.erase(j++);
389 /* Expressions assigned in this block may depend on local variables of the
390 block. If this is a conditionally executed block, the assignments might not
391 always happen. Mark the expressions as not available to any outer blocks. */
392 for(map<Assignment::Target, ExpressionInfo>::iterator i=expressions.begin(); i!=expressions.end(); ++i)
393 if(i->second.assign_scope==&block)
394 i->second.available = false;
397 void ExpressionInliner::visit(RefPtr<Expression> &expr)
401 if(r_ref_info && r_ref_info->expression && r_ref_info->available)
403 if(iteration_body && !r_ref_info->trivial)
405 /* Don't inline non-trivial expressions which were assigned outside
406 an iteration statement. The iteration may run multiple times, which
407 would cause the expression to also be evaluated multiple times. */
408 Block *i = r_ref_info->assign_scope;
409 for(; (i && i!=iteration_body); i=i->parent) ;
414 if(r_ref_info->trivial)
415 inline_expression(*r_ref_info->expression, expr);
417 /* Record the inline point for a non-trivial expression but don't
418 inline it yet. It might turn out it shouldn't be inlined after all. */
419 r_ref_info->inline_point = &expr;
425 void ExpressionInliner::visit(VariableReference &var)
429 map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(var.declaration);
430 if(i!=expressions.end())
432 /* If a non-trivial expression is referenced multiple times, don't
434 if(i->second.inline_point && !i->second.trivial)
435 i->second.expression = 0;
436 /* Mutating expressions are analogous to self-referencing assignments
437 and prevent inlining. */
439 i->second.expression = 0;
440 r_ref_info = &i->second;
445 void ExpressionInliner::visit(MemberAccess &memacc)
451 void ExpressionInliner::visit(Swizzle &swizzle)
457 void ExpressionInliner::visit(UnaryExpression &unary)
459 SetFlag set_target(mutating, mutating || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
460 visit(unary.expression);
464 void ExpressionInliner::visit(BinaryExpression &binary)
468 SetFlag clear_target(mutating, false);
474 void ExpressionInliner::visit(Assignment &assign)
477 SetFlag set_target(mutating);
483 map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(assign.target);
484 if(i!=expressions.end())
486 /* Self-referencing assignments can't be inlined without additional
487 work. Just clear any previous expression. */
488 i->second.expression = (assign.self_referencing ? 0 : assign.right.get());
489 i->second.assign_scope = current_block;
490 i->second.inline_point = 0;
491 i->second.available = true;
497 void ExpressionInliner::visit(TernaryExpression &ternary)
499 visit(ternary.condition);
500 visit(ternary.true_expr);
501 visit(ternary.false_expr);
505 void ExpressionInliner::visit(FunctionCall &call)
507 TraversingVisitor::visit(call);
511 void ExpressionInliner::visit(VariableDeclaration &var)
515 TraversingVisitor::visit(var);
517 bool constant = var.constant;
518 if(constant && var.layout)
520 for(vector<Layout::Qualifier>::const_iterator i=var.layout->qualifiers.begin(); (constant && i!=var.layout->qualifiers.end()); ++i)
521 constant = (i->name!="constant_id");
524 /* Only inline global variables if they're constant and have trivial
525 initializers. Non-constant variables could change in ways which are hard to
526 analyze and non-trivial expressions could be expensive to inline. */
527 if((current_block->parent || (constant && r_trivial)) && var.interface.empty())
529 ExpressionInfo &info = expressions[&var];
530 /* Assume variables declared in an iteration initialization statement
531 will have their values change throughout the iteration. */
532 info.expression = (iteration_init ? 0 : var.init_expression.get());
533 info.assign_scope = current_block;
534 info.trivial = r_trivial;
538 void ExpressionInliner::visit(Iteration &iter)
540 SetForScope<Block *> set_block(current_block, &iter.body);
541 if(iter.init_statement)
543 SetFlag set_init(iteration_init);
544 iter.init_statement->visit(*this);
547 SetForScope<Block *> set_body(iteration_body, &iter.body);
549 visit(iter.condition);
550 iter.body.visit(*this);
551 if(iter.loop_expression)
552 visit(iter.loop_expression);
557 T ConstantFolder::evaluate_logical(char oper, T left, T right)
561 case '&': return left&right;
562 case '|': return left|right;
563 case '^': return left^right;
569 bool ConstantFolder::evaluate_relation(const char *oper, T left, T right)
571 switch(oper[0]|oper[1])
573 case '<': return left<right;
574 case '<'|'=': return left<=right;
575 case '>': return left>right;
576 case '>'|'=': return left>=right;
577 default: return false;
582 T ConstantFolder::evaluate_arithmetic(char oper, T left, T right)
586 case '+': return left+right;
587 case '-': return left-right;
588 case '*': return left*right;
589 case '/': return left/right;
594 void ConstantFolder::set_result(const Variant &value, bool literal)
596 r_constant_value = value;
601 void ConstantFolder::visit(RefPtr<Expression> &expr)
603 r_constant_value = Variant();
606 r_uses_iter_var = false;
608 /* Don't replace literals since they'd only be replaced with an identical
609 literal. Also skip anything that uses an iteration variable, but pass on
610 the result so the Iteration visiting function can handle it. */
611 if(!r_constant || r_literal || r_uses_iter_var)
614 RefPtr<Literal> literal = new Literal;
615 if(r_constant_value.check_type<bool>())
616 literal->token = (r_constant_value.value<bool>() ? "true" : "false");
617 else if(r_constant_value.check_type<int>())
618 literal->token = lexical_cast<string>(r_constant_value.value<int>());
619 else if(r_constant_value.check_type<float>())
620 literal->token = lexical_cast<string>(r_constant_value.value<float>());
626 literal->value = r_constant_value;
630 void ConstantFolder::visit(Literal &literal)
632 set_result(literal.value, true);
635 void ConstantFolder::visit(VariableReference &var)
637 /* If an iteration variable is initialized with a constant value, return
638 that value here for the purpose of evaluating the loop condition for the
640 if(var.declaration==iteration_var)
642 set_result(iter_init_value);
643 r_uses_iter_var = true;
647 void ConstantFolder::visit(MemberAccess &memacc)
649 TraversingVisitor::visit(memacc);
653 void ConstantFolder::visit(Swizzle &swizzle)
655 TraversingVisitor::visit(swizzle);
659 void ConstantFolder::visit(UnaryExpression &unary)
661 TraversingVisitor::visit(unary);
662 bool can_fold = r_constant;
667 char oper = unary.oper->token[0];
668 char oper2 = unary.oper->token[1];
671 if(r_constant_value.check_type<bool>())
672 set_result(!r_constant_value.value<bool>());
676 if(r_constant_value.check_type<int>())
677 set_result(~r_constant_value.value<int>());
678 else if(r_constant_value.check_type<unsigned>())
679 set_result(~r_constant_value.value<unsigned>());
681 else if(oper=='-' && !oper2)
683 if(r_constant_value.check_type<int>())
684 set_result(-r_constant_value.value<int>());
685 else if(r_constant_value.check_type<unsigned>())
686 set_result(-r_constant_value.value<unsigned>());
687 else if(r_constant_value.check_type<float>())
688 set_result(-r_constant_value.value<float>());
692 void ConstantFolder::visit(BinaryExpression &binary)
695 bool left_constant = r_constant;
696 bool left_iter_var = r_uses_iter_var;
697 Variant left_value = r_constant_value;
700 r_uses_iter_var = true;
702 bool can_fold = (left_constant && r_constant);
707 // Currently only expressions with both sides of equal types are handled.
708 if(!left_value.check_same_type(r_constant_value))
711 char oper = binary.oper->token[0];
712 char oper2 = binary.oper->token[1];
713 if(oper=='&' || oper=='|' || oper=='^')
715 if(oper2==oper && left_value.check_type<bool>())
716 set_result(evaluate_logical(oper, left_value.value<bool>(), r_constant_value.value<bool>()));
717 else if(!oper2 && left_value.check_type<int>())
718 set_result(evaluate_logical(oper, left_value.value<int>(), r_constant_value.value<int>()));
720 else if((oper=='<' || oper=='>') && oper2!=oper)
722 if(left_value.check_type<int>())
723 set_result(evaluate_relation(binary.oper->token, left_value.value<int>(), r_constant_value.value<int>()));
724 else if(left_value.check_type<float>())
725 set_result(evaluate_relation(binary.oper->token, left_value.value<float>(), r_constant_value.value<float>()));
727 else if((oper=='=' || oper=='!') && oper2=='=')
729 if(left_value.check_type<int>())
730 set_result((left_value.value<int>()==r_constant_value.value<int>()) == (oper=='='));
731 if(left_value.check_type<float>())
732 set_result((left_value.value<float>()==r_constant_value.value<float>()) == (oper=='='));
734 else if(oper=='+' || oper=='-' || oper=='*' || oper=='/')
736 if(left_value.check_type<int>())
737 set_result(evaluate_arithmetic(oper, left_value.value<int>(), r_constant_value.value<int>()));
738 else if(left_value.check_type<float>())
739 set_result(evaluate_arithmetic(oper, left_value.value<float>(), r_constant_value.value<float>()));
741 else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper))
743 if(!left_value.check_type<int>())
747 set_result(left_value.value<int>()%r_constant_value.value<int>());
749 set_result(left_value.value<int>()<<r_constant_value.value<int>());
751 set_result(left_value.value<int>()>>r_constant_value.value<int>());
755 void ConstantFolder::visit(Assignment &assign)
757 TraversingVisitor::visit(assign);
761 void ConstantFolder::visit(TernaryExpression &ternary)
763 TraversingVisitor::visit(ternary);
767 void ConstantFolder::visit(FunctionCall &call)
769 TraversingVisitor::visit(call);
773 void ConstantFolder::visit(VariableDeclaration &var)
775 if(iteration_init && var.init_expression)
777 visit(var.init_expression);
780 /* Record the value of a constant initialization expression of an
781 iteration, so it can be used to evaluate the loop condition. */
782 iteration_var = &var;
783 iter_init_value = r_constant_value;
787 TraversingVisitor::visit(var);
790 void ConstantFolder::visit(Iteration &iter)
792 SetForScope<Block *> set_block(current_block, &iter.body);
794 /* The iteration variable is not normally inlined into expressions, so we
795 process it specially here. If the initial value causes the loop condition
796 to evaluate to false, then the expression can be folded. */
798 if(iter.init_statement)
800 SetFlag set_init(iteration_init);
801 iter.init_statement->visit(*this);
806 visit(iter.condition);
807 if(r_constant && r_constant_value.check_type<bool>() && !r_constant_value.value<bool>())
809 RefPtr<Literal> literal = new Literal;
810 literal->token = "false";
811 literal->value = r_constant_value;
812 iter.condition = literal;
817 iter.body.visit(*this);
818 if(iter.loop_expression)
819 visit(iter.loop_expression);
823 void ConstantConditionEliminator::apply(Stage &stage)
825 stage.content.visit(*this);
826 NodeRemover().apply(stage, nodes_to_remove);
829 ConstantConditionEliminator::ConstantStatus ConstantConditionEliminator::check_constant_condition(const Expression &expr)
831 if(const Literal *literal = dynamic_cast<const Literal *>(&expr))
832 if(literal->value.check_type<bool>())
833 return (literal->value.value<bool>() ? CONSTANT_TRUE : CONSTANT_FALSE);
837 void ConstantConditionEliminator::visit(Block &block)
839 SetForScope<Block *> set_block(current_block, &block);
840 for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
847 void ConstantConditionEliminator::visit(RefPtr<Expression> &expr)
849 r_ternary_result = 0;
852 expr = r_ternary_result;
853 r_ternary_result = 0;
856 void ConstantConditionEliminator::visit(TernaryExpression &ternary)
858 ConstantStatus result = check_constant_condition(*ternary.condition);
859 if(result!=NOT_CONSTANT)
860 r_ternary_result = (result==CONSTANT_TRUE ? ternary.true_expr : ternary.false_expr);
862 r_ternary_result = 0;
865 void ConstantConditionEliminator::visit(Conditional &cond)
867 ConstantStatus result = check_constant_condition(*cond.condition);
868 if(result!=NOT_CONSTANT)
870 Block &block = (result==CONSTANT_TRUE ? cond.body : cond.else_body);
871 // TODO should check variable names for conflicts. Potentially reuse InlineContentInjector?
872 current_block->body.splice(insert_point, block.body);
873 nodes_to_remove.insert(&cond);
877 TraversingVisitor::visit(cond);
880 void ConstantConditionEliminator::visit(Iteration &iter)
884 ConstantStatus result = check_constant_condition(*iter.condition);
885 if(result==CONSTANT_FALSE)
887 nodes_to_remove.insert(&iter);
892 TraversingVisitor::visit(iter);
896 UnreachableCodeRemover::UnreachableCodeRemover():
900 bool UnreachableCodeRemover::apply(Stage &stage)
902 stage.content.visit(*this);
903 NodeRemover().apply(stage, unreachable_nodes);
904 return !unreachable_nodes.empty();
907 void UnreachableCodeRemover::visit(Block &block)
909 NodeList<Statement>::iterator i = block.body.begin();
910 for(; (reachable && i!=block.body.end()); ++i)
912 for(; i!=block.body.end(); ++i)
913 unreachable_nodes.insert(i->get());
916 void UnreachableCodeRemover::visit(FunctionDeclaration &func)
918 TraversingVisitor::visit(func);
922 void UnreachableCodeRemover::visit(Conditional &cond)
924 cond.body.visit(*this);
925 bool reachable_if_true = reachable;
927 cond.else_body.visit(*this);
929 reachable |= reachable_if_true;
932 void UnreachableCodeRemover::visit(Iteration &iter)
934 TraversingVisitor::visit(iter);
936 /* Always consider code after a loop reachable, since there's no checking
937 for whether the loop executes. */
942 bool UnusedTypeRemover::apply(Stage &stage)
944 stage.content.visit(*this);
945 NodeRemover().apply(stage, unused_nodes);
946 return !unused_nodes.empty();
949 void UnusedTypeRemover::visit(RefPtr<Expression> &expr)
951 unused_nodes.erase(expr->type);
952 TraversingVisitor::visit(expr);
955 void UnusedTypeRemover::visit(BasicTypeDeclaration &type)
958 unused_nodes.erase(type.base_type);
959 unused_nodes.insert(&type);
962 void UnusedTypeRemover::visit(ImageTypeDeclaration &type)
965 unused_nodes.erase(type.base_type);
966 unused_nodes.insert(&type);
969 void UnusedTypeRemover::visit(StructDeclaration &strct)
971 unused_nodes.insert(&strct);
972 TraversingVisitor::visit(strct);
975 void UnusedTypeRemover::visit(VariableDeclaration &var)
977 unused_nodes.erase(var.type_declaration);
978 TraversingVisitor::visit(var);
981 void UnusedTypeRemover::visit(InterfaceBlock &iface)
983 unused_nodes.erase(iface.type_declaration);
986 void UnusedTypeRemover::visit(FunctionDeclaration &func)
988 unused_nodes.erase(func.return_type_declaration);
989 TraversingVisitor::visit(func);
993 UnusedVariableRemover::UnusedVariableRemover():
997 assignment_target(false),
998 r_side_effects(false),
1000 composite_reference(false)
1003 bool UnusedVariableRemover::apply(Stage &s)
1006 s.content.visit(*this);
1008 for(list<AssignmentInfo>::const_iterator i=assignments.begin(); i!=assignments.end(); ++i)
1009 if(i->used_by.empty())
1010 unused_nodes.insert(i->node);
1012 for(BlockVariableMap::const_iterator i=variables.begin(); i!=variables.end(); ++i)
1014 if(i->second.output)
1016 /* The last visible assignments of output variables are used by the
1017 next stage or the API. */
1018 for(vector<AssignmentInfo *>::const_iterator j=i->second.assignments.begin(); j!=i->second.assignments.end(); ++j)
1019 unused_nodes.erase((*j)->node);
1022 if(!i->second.output && !i->second.referenced)
1024 // Don't remove variables from inside interface blocks.
1025 if(!i->second.interface_block)
1026 unused_nodes.insert(i->first);
1028 else if(i->second.interface_block)
1029 // Interface blocks are kept if even one member is used.
1030 unused_nodes.erase(i->second.interface_block);
1033 NodeRemover().apply(s, unused_nodes);
1035 return !unused_nodes.empty();
1038 void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &node)
1040 VariableInfo &var_info = variables[target.declaration];
1041 var_info.referenced = true;
1042 if(!assignment_target)
1044 for(vector<AssignmentInfo *>::const_iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i)
1046 bool covered = true;
1047 for(unsigned j=0; (covered && j<(*i)->target.chain_len && j<target.chain_len); ++j)
1049 Assignment::Target::ChainType type1 = static_cast<Assignment::Target::ChainType>((*i)->target.chain[j]&0xC0);
1050 Assignment::Target::ChainType type2 = static_cast<Assignment::Target::ChainType>(target.chain[j]&0xC0);
1051 if(type1==Assignment::Target::SWIZZLE || type2==Assignment::Target::SWIZZLE)
1053 unsigned index1 = (*i)->target.chain[j]&0x3F;
1054 unsigned index2 = target.chain[j]&0x3F;
1055 if(type1==Assignment::Target::SWIZZLE && type2==Assignment::Target::SWIZZLE)
1056 covered = index1&index2;
1057 else if(type1==Assignment::Target::ARRAY && index1<4)
1058 covered = index2&(1<<index1);
1059 else if(type2==Assignment::Target::ARRAY && index2<4)
1060 covered = index1&(1<<index2);
1061 /* If it's some other combination (shouldn't happen), leave
1065 covered = ((*i)->target.chain[j]==target.chain[j]);
1068 (*i)->used_by.push_back(&node);
1073 void UnusedVariableRemover::visit(VariableReference &var)
1075 if(composite_reference)
1076 r_reference.declaration = var.declaration;
1078 referenced(var.declaration, var);
1081 void UnusedVariableRemover::visit(InterfaceBlockReference &iface)
1083 if(composite_reference)
1084 r_reference.declaration = iface.declaration;
1086 referenced(iface.declaration, iface);
1089 void UnusedVariableRemover::visit_composite(Expression &expr)
1091 if(!composite_reference)
1092 r_reference = Assignment::Target();
1094 SetFlag set_composite(composite_reference);
1098 void UnusedVariableRemover::visit(MemberAccess &memacc)
1100 visit_composite(*memacc.left);
1102 add_to_chain(r_reference, Assignment::Target::MEMBER, memacc.index);
1104 if(!composite_reference && r_reference.declaration)
1105 referenced(r_reference, memacc);
1108 void UnusedVariableRemover::visit(Swizzle &swizzle)
1110 visit_composite(*swizzle.left);
1113 for(unsigned i=0; i<swizzle.count; ++i)
1114 mask |= 1<<swizzle.components[i];
1115 add_to_chain(r_reference, Assignment::Target::SWIZZLE, mask);
1117 if(!composite_reference && r_reference.declaration)
1118 referenced(r_reference, swizzle);
1121 void UnusedVariableRemover::visit(UnaryExpression &unary)
1123 TraversingVisitor::visit(unary);
1124 if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
1125 r_side_effects = true;
1128 void UnusedVariableRemover::visit(BinaryExpression &binary)
1130 if(binary.oper->token[0]=='[')
1132 visit_composite(*binary.left);
1135 SetFlag clear_assignment(assignment_target, false);
1136 SetFlag clear_composite(composite_reference, false);
1137 binary.right->visit(*this);
1140 add_to_chain(r_reference, Assignment::Target::ARRAY, 0x3F);
1142 if(!composite_reference && r_reference.declaration)
1143 referenced(r_reference, binary);
1147 SetFlag clear_composite(composite_reference, false);
1148 TraversingVisitor::visit(binary);
1152 void UnusedVariableRemover::visit(TernaryExpression &ternary)
1154 SetFlag clear_composite(composite_reference, false);
1155 TraversingVisitor::visit(ternary);
1158 void UnusedVariableRemover::visit(Assignment &assign)
1161 SetFlag set(assignment_target, (assign.oper->token[0]=='='));
1162 assign.left->visit(*this);
1164 assign.right->visit(*this);
1165 r_assignment = &assign;
1166 r_side_effects = true;
1169 void UnusedVariableRemover::visit(FunctionCall &call)
1171 SetFlag clear_composite(composite_reference, false);
1172 TraversingVisitor::visit(call);
1173 /* Treat function calls as having side effects so expression statements
1174 consisting of nothing but a function call won't be optimized away. */
1175 r_side_effects = true;
1177 if(stage->type==Stage::GEOMETRY && call.name=="EmitVertex")
1179 for(map<Statement *, VariableInfo>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
1180 if(i->second.output)
1181 referenced(i->first, call);
1185 void UnusedVariableRemover::record_assignment(const Assignment::Target &target, Node &node)
1187 assignments.push_back(AssignmentInfo());
1188 AssignmentInfo &assign_info = assignments.back();
1189 assign_info.node = &node;
1190 assign_info.target = target;
1192 /* An assignment to the target hides any assignments to the same target or
1194 VariableInfo &var_info = variables[target.declaration];
1195 for(unsigned i=0; i<var_info.assignments.size(); ++i)
1197 const Assignment::Target &t = var_info.assignments[i]->target;
1199 bool subfield = (t.chain_len>=target.chain_len);
1200 for(unsigned j=0; (subfield && j<target.chain_len); ++j)
1201 subfield = (t.chain[j]==target.chain[j]);
1204 var_info.assignments.erase(var_info.assignments.begin()+i);
1209 var_info.assignments.push_back(&assign_info);
1212 void UnusedVariableRemover::visit(ExpressionStatement &expr)
1215 r_side_effects = false;
1216 TraversingVisitor::visit(expr);
1217 if(r_assignment && r_assignment->target.declaration)
1218 record_assignment(r_assignment->target, expr);
1220 unused_nodes.insert(&expr);
1223 void UnusedVariableRemover::visit(StructDeclaration &strct)
1225 SetFlag set_struct(in_struct);
1226 TraversingVisitor::visit(strct);
1229 void UnusedVariableRemover::visit(VariableDeclaration &var)
1231 TraversingVisitor::visit(var);
1236 VariableInfo &var_info = variables[&var];
1237 var_info.interface_block = interface_block;
1239 /* Mark variables as output if they're used by the next stage or the
1242 var_info.output = (interface_block->interface=="out" && (interface_block->linked_block || !interface_block->block_name.compare(0, 3, "gl_")));
1244 var_info.output = (var.interface=="out" && (stage->type==Stage::FRAGMENT || var.linked_declaration || !var.name.compare(0, 3, "gl_")));
1246 if(var.init_expression)
1248 var_info.initialized = true;
1249 record_assignment(&var, *var.init_expression);
1253 void UnusedVariableRemover::visit(InterfaceBlock &iface)
1255 VariableInfo &var_info = variables[&iface];
1256 var_info.output = (iface.interface=="out" && (iface.linked_block || !iface.block_name.compare(0, 3, "gl_")));
1259 void UnusedVariableRemover::merge_variables(const BlockVariableMap &other_vars)
1261 for(BlockVariableMap::const_iterator i=other_vars.begin(); i!=other_vars.end(); ++i)
1263 BlockVariableMap::iterator j = variables.find(i->first);
1264 if(j!=variables.end())
1266 /* The merged blocks started as copies of each other so any common
1267 assignments must be in the beginning. */
1269 for(; (k<i->second.assignments.size() && k<j->second.assignments.size()); ++k)
1270 if(i->second.assignments[k]!=j->second.assignments[k])
1273 // Remaining assignments are unique to each block; merge them.
1274 j->second.assignments.insert(j->second.assignments.end(), i->second.assignments.begin()+k, i->second.assignments.end());
1275 j->second.referenced |= i->second.referenced;
1278 variables.insert(*i);
1282 void UnusedVariableRemover::visit(FunctionDeclaration &func)
1284 if(func.body.body.empty())
1287 BlockVariableMap saved_vars = variables;
1288 // Assignments from other functions should not be visible.
1289 for(BlockVariableMap::iterator i=variables.begin(); i!=variables.end(); ++i)
1290 i->second.assignments.resize(i->second.initialized);
1291 TraversingVisitor::visit(func);
1292 swap(variables, saved_vars);
1293 merge_variables(saved_vars);
1295 /* Always treat function parameters as referenced. Removing unused
1296 parameters is not currently supported. */
1297 for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
1299 BlockVariableMap::iterator j = variables.find(i->get());
1300 if(j!=variables.end())
1301 j->second.referenced = true;
1305 void UnusedVariableRemover::visit(Conditional &cond)
1307 cond.condition->visit(*this);
1308 BlockVariableMap saved_vars = variables;
1309 cond.body.visit(*this);
1310 swap(saved_vars, variables);
1311 cond.else_body.visit(*this);
1313 /* Visible assignments after the conditional is the union of those visible
1314 at the end of the if and else blocks. If there was no else block, then it's
1315 the union of the if block and the state before it. */
1316 merge_variables(saved_vars);
1319 void UnusedVariableRemover::visit(Iteration &iter)
1321 BlockVariableMap saved_vars = variables;
1322 TraversingVisitor::visit(iter);
1324 /* Merge assignments from the iteration, without clearing previous state.
1325 Further analysis is needed to determine which parts of the iteration body
1326 are always executed, if any. */
1327 merge_variables(saved_vars);
1331 bool UnusedFunctionRemover::apply(Stage &stage)
1333 stage.content.visit(*this);
1334 NodeRemover().apply(stage, unused_nodes);
1335 return !unused_nodes.empty();
1338 void UnusedFunctionRemover::visit(FunctionCall &call)
1340 TraversingVisitor::visit(call);
1342 unused_nodes.erase(call.declaration);
1343 if(call.declaration && call.declaration->definition!=call.declaration)
1344 used_definitions.insert(call.declaration->definition);
1347 void UnusedFunctionRemover::visit(FunctionDeclaration &func)
1349 TraversingVisitor::visit(func);
1351 if((func.name!="main" || func.body.body.empty()) && !used_definitions.count(&func))
1352 unused_nodes.insert(&func);