1 #include <msp/core/raii.h>
2 #include <msp/strings/format.h>
3 #include <msp/strings/utils.h>
13 ConstantSpecializer::ConstantSpecializer():
17 void ConstantSpecializer::apply(Stage &stage, const map<string, int> &v)
20 stage.content.visit(*this);
23 void ConstantSpecializer::visit(VariableDeclaration &var)
25 bool specializable = false;
28 vector<Layout::Qualifier> &qualifiers = var.layout->qualifiers;
29 for(vector<Layout::Qualifier>::iterator i=qualifiers.begin(); (!specializable && i!=qualifiers.end()); ++i)
30 if(i->name=="constant_id")
36 if(qualifiers.empty())
42 map<string, int>::const_iterator i = values->find(var.name);
45 RefPtr<Literal> literal = new Literal;
48 literal->token = (i->second ? "true" : "false");
49 literal->value = static_cast<bool>(i->second);
51 else if(var.type=="int")
53 literal->token = lexical_cast<string>(i->second);
54 literal->value = i->second;
56 var.init_expression = literal;
62 InlineableFunctionLocator::InlineableFunctionLocator():
67 void InlineableFunctionLocator::visit(FunctionCall &call)
69 FunctionDeclaration *def = call.declaration;
71 def = def->definition;
75 unsigned &count = refcounts[def];
77 /* Don't inline functions which are called more than once or are called
79 if((count>1 && def->source!=BUILTIN_SOURCE) || def==current_function)
80 inlineable.erase(def);
83 TraversingVisitor::visit(call);
86 void InlineableFunctionLocator::visit(FunctionDeclaration &func)
88 bool has_out_params = false;
89 for(NodeArray<VariableDeclaration>::const_iterator i=func.parameters.begin(); (!has_out_params && i!=func.parameters.end()); ++i)
90 has_out_params = ((*i)->interface=="out");
92 unsigned &count = refcounts[func.definition];
93 if((count<=1 || func.source==BUILTIN_SOURCE) && !has_out_params)
94 inlineable.insert(func.definition);
96 SetForScope<FunctionDeclaration *> set(current_function, &func);
98 TraversingVisitor::visit(func);
101 void InlineableFunctionLocator::visit(Conditional &cond)
103 TraversingVisitor::visit(cond);
104 inlineable.erase(current_function);
107 void InlineableFunctionLocator::visit(Iteration &iter)
109 TraversingVisitor::visit(iter);
110 inlineable.erase(current_function);
113 void InlineableFunctionLocator::visit(Return &ret)
115 TraversingVisitor::visit(ret);
117 inlineable.erase(current_function);
122 InlineContentInjector::InlineContentInjector():
127 string InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList<Statement>::iterator &ins_pt, FunctionCall &call)
129 source_func = call.declaration->definition;
131 /* Populate referenced_names from the target function so we can rename
132 variables from the inlined function that would conflict. */
134 target_func.visit(*this);
136 /* Inline and rename passes must be interleaved so used variable names are
137 known when inlining the return statement. */
139 staging_block.parent = &tgt_blk;
140 staging_block.variables.clear();
142 vector<RefPtr<VariableDeclaration> > params;
143 params.reserve(source_func->parameters.size());
144 for(NodeArray<VariableDeclaration>::iterator i=source_func->parameters.begin(); i!=source_func->parameters.end(); ++i)
146 RefPtr<VariableDeclaration> var = (*i)->clone();
147 var->interface.clear();
149 SetForScope<Pass> set_pass(pass, RENAME);
152 staging_block.body.push_back_nocopy(var);
153 params.push_back(var);
156 for(NodeList<Statement>::iterator i=source_func->body.body.begin(); i!=source_func->body.body.end(); ++i)
158 r_inlined_statement = 0;
160 if(!r_inlined_statement)
161 r_inlined_statement = (*i)->clone();
163 SetForScope<Pass> set_pass(pass, RENAME);
164 r_inlined_statement->visit(*this);
166 staging_block.body.push_back_nocopy(r_inlined_statement);
169 /* Now collect names from the staging block. Local variables that would
170 have conflicted with the target function were renamed earlier. */
172 referenced_names.clear();
173 staging_block.variables.clear();
174 staging_block.visit(*this);
176 /* Rename variables in the target function so they don't interfere with
177 global identifiers used by the source function. */
179 staging_block.parent = source_func->body.parent;
180 target_func.visit(*this);
182 // Put the argument expressions in place after all renaming has been done.
183 for(unsigned i=0; i<source_func->parameters.size(); ++i)
184 params[i]->init_expression = call.arguments[i]->clone();
186 tgt_blk.body.splice(ins_pt, staging_block.body);
188 NodeReorderer().apply(stage, target_func, DependencyCollector().apply(*source_func));
190 return r_result_name;
193 void InlineContentInjector::visit(VariableReference &var)
197 map<string, VariableDeclaration *>::const_iterator i = staging_block.variables.find(var.name);
198 if(i!=staging_block.variables.end())
199 var.name = i->second->name;
201 else if(pass==REFERENCED)
202 referenced_names.insert(var.name);
205 void InlineContentInjector::visit(InterfaceBlockReference &iface)
208 referenced_names.insert(iface.name);
211 void InlineContentInjector::visit(FunctionCall &call)
214 referenced_names.insert(call.name);
215 TraversingVisitor::visit(call);
218 void InlineContentInjector::visit(VariableDeclaration &var)
220 TraversingVisitor::visit(var);
224 /* Check against conflicts with the other context as well as variables
225 already renamed here. */
226 bool conflict = (staging_block.variables.count(var.name) || referenced_names.count(var.name));
227 staging_block.variables[var.name] = &var;
230 string mapped_name = get_unused_variable_name(staging_block, var.name);
231 if(mapped_name!=var.name)
233 staging_block.variables[mapped_name] = &var;
234 var.name = mapped_name;
238 else if(pass==REFERENCED)
239 referenced_names.insert(var.type);
242 void InlineContentInjector::visit(Return &ret)
244 TraversingVisitor::visit(ret);
246 if(pass==INLINE && ret.expression)
248 // Create a new variable to hold the return value of the inlined function.
249 r_result_name = get_unused_variable_name(staging_block, "_return");
250 RefPtr<VariableDeclaration> var = new VariableDeclaration;
251 var->source = ret.source;
252 var->line = ret.line;
253 var->type = source_func->return_type;
254 var->name = r_result_name;
255 var->init_expression = ret.expression->clone();
256 r_inlined_statement = var;
261 FunctionInliner::FunctionInliner():
263 r_any_inlined(false),
264 r_inlined_here(false)
267 bool FunctionInliner::apply(Stage &s)
270 inlineable = InlineableFunctionLocator().apply(s);
271 r_any_inlined = false;
272 s.content.visit(*this);
273 return r_any_inlined;
276 void FunctionInliner::visit(RefPtr<Expression> &ptr)
282 ptr = r_inline_result;
283 r_any_inlined = true;
288 void FunctionInliner::visit(Block &block)
290 SetForScope<Block *> set_block(current_block, &block);
291 SetForScope<NodeList<Statement>::iterator> save_insert_point(insert_point, block.body.begin());
292 for(NodeList<Statement>::iterator i=block.body.begin(); (!r_inlined_here && i!=block.body.end()); ++i)
299 void FunctionInliner::visit(FunctionCall &call)
301 for(NodeArray<Expression>::iterator i=call.arguments.begin(); (!r_inlined_here && i!=call.arguments.end()); ++i)
307 FunctionDeclaration *def = call.declaration;
309 def = def->definition;
311 if(def && inlineable.count(def))
313 string result_name = InlineContentInjector().apply(*stage, *current_function, *current_block, insert_point, call);
315 // This will later get removed by UnusedVariableRemover.
316 if(result_name.empty())
317 result_name = "_msp_unused_from_inline";
319 RefPtr<VariableReference> ref = new VariableReference;
320 ref->name = result_name;
321 r_inline_result = ref;
323 /* Inlined variables need to be resolved before this function can be
325 inlineable.erase(current_function);
326 r_inlined_here = true;
330 void FunctionInliner::visit(FunctionDeclaration &func)
332 SetForScope<FunctionDeclaration *> set_func(current_function, &func);
333 TraversingVisitor::visit(func);
334 r_inlined_here = false;
337 void FunctionInliner::visit(Iteration &iter)
339 /* Visit the initialization statement before entering the loop body so the
340 inlined statements get inserted outside. */
341 if(iter.init_statement)
342 iter.init_statement->visit(*this);
344 SetForScope<Block *> set_block(current_block, &iter.body);
345 /* Skip the condition and loop expression parts because they're not properly
346 inside the body block. Inlining anything into them will require a more
347 comprehensive transformation. */
348 iter.body.visit(*this);
352 ExpressionInliner::ExpressionInliner():
354 r_any_inlined(false),
357 iteration_init(false),
362 bool ExpressionInliner::apply(Stage &s)
364 s.content.visit(*this);
365 return r_any_inlined;
368 void ExpressionInliner::inline_expression(Expression &expr, RefPtr<Expression> &ptr)
371 r_any_inlined = true;
374 void ExpressionInliner::visit(Block &block)
376 TraversingVisitor::visit(block);
378 for(map<string, VariableDeclaration *>::iterator i=block.variables.begin(); i!=block.variables.end(); ++i)
380 map<Assignment::Target, ExpressionInfo>::iterator j = expressions.lower_bound(i->second);
381 for(; (j!=expressions.end() && j->first.declaration==i->second); )
383 if(j->second.expression && j->second.inline_point)
384 inline_expression(*j->second.expression, *j->second.inline_point);
386 expressions.erase(j++);
390 /* Expressions assigned in this block may depend on local variables of the
391 block. If this is a conditionally executed block, the assignments might not
392 always happen. Mark the expressions as not available to any outer blocks. */
393 for(map<Assignment::Target, ExpressionInfo>::iterator i=expressions.begin(); i!=expressions.end(); ++i)
394 if(i->second.assign_scope==&block)
395 i->second.available = false;
398 void ExpressionInliner::visit(RefPtr<Expression> &expr)
402 if(r_ref_info && r_ref_info->expression && r_ref_info->available)
404 if(iteration_body && !r_ref_info->trivial)
406 /* Don't inline non-trivial expressions which were assigned outside
407 an iteration statement. The iteration may run multiple times, which
408 would cause the expression to also be evaluated multiple times. */
409 Block *i = r_ref_info->assign_scope;
410 for(; (i && i!=iteration_body); i=i->parent) ;
415 if(r_ref_info->trivial)
416 inline_expression(*r_ref_info->expression, expr);
418 /* Record the inline point for a non-trivial expression but don't
419 inline it yet. It might turn out it shouldn't be inlined after all. */
420 r_ref_info->inline_point = &expr;
426 void ExpressionInliner::visit(VariableReference &var)
430 map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(var.declaration);
431 if(i!=expressions.end())
433 /* If a non-trivial expression is referenced multiple times, don't
435 if(i->second.inline_point && !i->second.trivial)
436 i->second.expression = 0;
437 /* Mutating expressions are analogous to self-referencing assignments
438 and prevent inlining. */
440 i->second.expression = 0;
441 r_ref_info = &i->second;
446 void ExpressionInliner::visit(MemberAccess &memacc)
452 void ExpressionInliner::visit(Swizzle &swizzle)
458 void ExpressionInliner::visit(UnaryExpression &unary)
460 SetFlag set_target(mutating, mutating || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
461 visit(unary.expression);
465 void ExpressionInliner::visit(BinaryExpression &binary)
469 SetFlag clear_target(mutating, false);
475 void ExpressionInliner::visit(Assignment &assign)
478 SetFlag set_target(mutating);
484 map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(assign.target);
485 if(i!=expressions.end())
487 /* Self-referencing assignments can't be inlined without additional
488 work. Just clear any previous expression. */
489 i->second.expression = (assign.self_referencing ? 0 : assign.right.get());
490 i->second.assign_scope = current_block;
491 i->second.inline_point = 0;
492 i->second.available = true;
498 void ExpressionInliner::visit(TernaryExpression &ternary)
500 visit(ternary.condition);
501 visit(ternary.true_expr);
502 visit(ternary.false_expr);
506 void ExpressionInliner::visit(FunctionCall &call)
508 TraversingVisitor::visit(call);
512 void ExpressionInliner::visit(VariableDeclaration &var)
516 TraversingVisitor::visit(var);
518 bool constant = var.constant;
519 if(constant && var.layout)
521 for(vector<Layout::Qualifier>::const_iterator i=var.layout->qualifiers.begin(); (constant && i!=var.layout->qualifiers.end()); ++i)
522 constant = (i->name!="constant_id");
525 /* Only inline global variables if they're constant and have trivial
526 initializers. Non-constant variables could change in ways which are hard to
527 analyze and non-trivial expressions could be expensive to inline. */
528 if((current_block->parent || (constant && r_trivial)) && var.interface.empty())
530 ExpressionInfo &info = expressions[&var];
531 /* Assume variables declared in an iteration initialization statement
532 will have their values change throughout the iteration. */
533 info.expression = (iteration_init ? 0 : var.init_expression.get());
534 info.assign_scope = current_block;
535 info.trivial = r_trivial;
539 void ExpressionInliner::visit(Iteration &iter)
541 SetForScope<Block *> set_block(current_block, &iter.body);
542 if(iter.init_statement)
544 SetFlag set_init(iteration_init);
545 iter.init_statement->visit(*this);
548 SetForScope<Block *> set_body(iteration_body, &iter.body);
550 visit(iter.condition);
551 iter.body.visit(*this);
552 if(iter.loop_expression)
553 visit(iter.loop_expression);
558 T ConstantFolder::evaluate_logical(char oper, T left, T right)
562 case '&': return left&right;
563 case '|': return left|right;
564 case '^': return left^right;
570 bool ConstantFolder::evaluate_relation(const char *oper, T left, T right)
572 switch(oper[0]|oper[1])
574 case '<': return left<right;
575 case '<'|'=': return left<=right;
576 case '>': return left>right;
577 case '>'|'=': return left>=right;
578 default: return false;
583 T ConstantFolder::evaluate_arithmetic(char oper, T left, T right)
587 case '+': return left+right;
588 case '-': return left-right;
589 case '*': return left*right;
590 case '/': return left/right;
596 T ConstantFolder::evaluate_int_special_op(char oper, T left, T right)
600 case '%': return left%right;
601 case '<': return left<<right;
602 case '>': return left>>right;
607 void ConstantFolder::set_result(const Variant &value, bool literal)
609 r_constant_value = value;
614 void ConstantFolder::visit(RefPtr<Expression> &expr)
616 r_constant_value = Variant();
619 r_uses_iter_var = false;
621 /* Don't replace literals since they'd only be replaced with an identical
622 literal. Also skip anything that uses an iteration variable, but pass on
623 the result so the Iteration visiting function can handle it. */
624 if(!r_constant || r_literal || r_uses_iter_var)
627 RefPtr<Literal> literal = new Literal;
628 if(r_constant_value.check_type<bool>())
629 literal->token = (r_constant_value.value<bool>() ? "true" : "false");
630 else if(r_constant_value.check_type<int>())
631 literal->token = lexical_cast<string>(r_constant_value.value<int>());
632 else if(r_constant_value.check_type<float>())
634 literal->token = lexical_cast<string>(r_constant_value.value<float>());
635 if(isnumrc(literal->token))
636 literal->token += ".0";
643 literal->value = r_constant_value;
647 void ConstantFolder::visit(Literal &literal)
649 set_result(literal.value, true);
652 void ConstantFolder::visit(VariableReference &var)
654 /* If an iteration variable is initialized with a constant value, return
655 that value here for the purpose of evaluating the loop condition for the
657 if(var.declaration==iteration_var)
659 set_result(iter_init_value);
660 r_uses_iter_var = true;
664 void ConstantFolder::visit(MemberAccess &memacc)
666 TraversingVisitor::visit(memacc);
670 void ConstantFolder::visit(Swizzle &swizzle)
672 TraversingVisitor::visit(swizzle);
676 void ConstantFolder::visit(UnaryExpression &unary)
678 TraversingVisitor::visit(unary);
679 bool can_fold = r_constant;
684 char oper = unary.oper->token[0];
685 char oper2 = unary.oper->token[1];
688 if(r_constant_value.check_type<bool>())
689 set_result(!r_constant_value.value<bool>());
693 if(r_constant_value.check_type<int>())
694 set_result(~r_constant_value.value<int>());
695 else if(r_constant_value.check_type<unsigned>())
696 set_result(~r_constant_value.value<unsigned>());
698 else if(oper=='-' && !oper2)
700 if(r_constant_value.check_type<int>())
701 set_result(-r_constant_value.value<int>());
702 else if(r_constant_value.check_type<unsigned>())
703 set_result(-r_constant_value.value<unsigned>());
704 else if(r_constant_value.check_type<float>())
705 set_result(-r_constant_value.value<float>());
709 void ConstantFolder::visit(BinaryExpression &binary)
712 bool left_constant = r_constant;
713 bool left_iter_var = r_uses_iter_var;
714 Variant left_value = r_constant_value;
717 r_uses_iter_var = true;
719 bool can_fold = (left_constant && r_constant);
724 // Currently only expressions with both sides of equal types are handled.
725 if(!left_value.check_same_type(r_constant_value))
728 char oper = binary.oper->token[0];
729 char oper2 = binary.oper->token[1];
730 if(oper=='&' || oper=='|' || oper=='^')
732 if(oper2==oper && left_value.check_type<bool>())
733 set_result(evaluate_logical(oper, left_value.value<bool>(), r_constant_value.value<bool>()));
734 else if(!oper2 && left_value.check_type<int>())
735 set_result(evaluate_logical(oper, left_value.value<int>(), r_constant_value.value<int>()));
737 else if((oper=='<' || oper=='>') && oper2!=oper)
739 if(left_value.check_type<int>())
740 set_result(evaluate_relation(binary.oper->token, left_value.value<int>(), r_constant_value.value<int>()));
741 else if(left_value.check_type<float>())
742 set_result(evaluate_relation(binary.oper->token, left_value.value<float>(), r_constant_value.value<float>()));
744 else if((oper=='=' || oper=='!') && oper2=='=')
746 if(left_value.check_type<int>())
747 set_result((left_value.value<int>()==r_constant_value.value<int>()) == (oper=='='));
748 if(left_value.check_type<float>())
749 set_result((left_value.value<float>()==r_constant_value.value<float>()) == (oper=='='));
751 else if(oper=='+' || oper=='-' || oper=='*' || oper=='/')
753 if(left_value.check_type<int>())
754 set_result(evaluate_arithmetic(oper, left_value.value<int>(), r_constant_value.value<int>()));
755 else if(left_value.check_type<float>())
756 set_result(evaluate_arithmetic(oper, left_value.value<float>(), r_constant_value.value<float>()));
758 else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper))
760 if(left_value.check_type<int>())
761 set_result(evaluate_int_special_op(oper, left_value.value<int>(), r_constant_value.value<int>()));
765 void ConstantFolder::visit(Assignment &assign)
767 TraversingVisitor::visit(assign);
771 void ConstantFolder::visit(TernaryExpression &ternary)
773 TraversingVisitor::visit(ternary);
777 void ConstantFolder::visit(FunctionCall &call)
779 TraversingVisitor::visit(call);
783 void ConstantFolder::visit(VariableDeclaration &var)
785 if(iteration_init && var.init_expression)
787 visit(var.init_expression);
790 /* Record the value of a constant initialization expression of an
791 iteration, so it can be used to evaluate the loop condition. */
792 iteration_var = &var;
793 iter_init_value = r_constant_value;
797 TraversingVisitor::visit(var);
800 void ConstantFolder::visit(Iteration &iter)
802 SetForScope<Block *> set_block(current_block, &iter.body);
804 /* The iteration variable is not normally inlined into expressions, so we
805 process it specially here. If the initial value causes the loop condition
806 to evaluate to false, then the expression can be folded. */
808 if(iter.init_statement)
810 SetFlag set_init(iteration_init);
811 iter.init_statement->visit(*this);
816 visit(iter.condition);
817 if(r_constant && r_constant_value.check_type<bool>() && !r_constant_value.value<bool>())
819 RefPtr<Literal> literal = new Literal;
820 literal->token = "false";
821 literal->value = r_constant_value;
822 iter.condition = literal;
827 iter.body.visit(*this);
828 if(iter.loop_expression)
829 visit(iter.loop_expression);
833 void ConstantConditionEliminator::apply(Stage &stage)
835 stage.content.visit(*this);
836 NodeRemover().apply(stage, nodes_to_remove);
839 ConstantConditionEliminator::ConstantStatus ConstantConditionEliminator::check_constant_condition(const Expression &expr)
841 if(const Literal *literal = dynamic_cast<const Literal *>(&expr))
842 if(literal->value.check_type<bool>())
843 return (literal->value.value<bool>() ? CONSTANT_TRUE : CONSTANT_FALSE);
847 void ConstantConditionEliminator::visit(Block &block)
849 SetForScope<Block *> set_block(current_block, &block);
850 for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
857 void ConstantConditionEliminator::visit(RefPtr<Expression> &expr)
859 r_ternary_result = 0;
862 expr = r_ternary_result;
863 r_ternary_result = 0;
866 void ConstantConditionEliminator::visit(TernaryExpression &ternary)
868 ConstantStatus result = check_constant_condition(*ternary.condition);
869 if(result!=NOT_CONSTANT)
870 r_ternary_result = (result==CONSTANT_TRUE ? ternary.true_expr : ternary.false_expr);
872 r_ternary_result = 0;
875 void ConstantConditionEliminator::visit(Conditional &cond)
877 ConstantStatus result = check_constant_condition(*cond.condition);
878 if(result!=NOT_CONSTANT)
880 Block &block = (result==CONSTANT_TRUE ? cond.body : cond.else_body);
881 // TODO should check variable names for conflicts. Potentially reuse InlineContentInjector?
882 current_block->body.splice(insert_point, block.body);
883 nodes_to_remove.insert(&cond);
887 TraversingVisitor::visit(cond);
890 void ConstantConditionEliminator::visit(Iteration &iter)
894 ConstantStatus result = check_constant_condition(*iter.condition);
895 if(result==CONSTANT_FALSE)
897 nodes_to_remove.insert(&iter);
902 TraversingVisitor::visit(iter);
906 UnreachableCodeRemover::UnreachableCodeRemover():
910 bool UnreachableCodeRemover::apply(Stage &stage)
912 stage.content.visit(*this);
913 NodeRemover().apply(stage, unreachable_nodes);
914 return !unreachable_nodes.empty();
917 void UnreachableCodeRemover::visit(Block &block)
919 NodeList<Statement>::iterator i = block.body.begin();
920 for(; (reachable && i!=block.body.end()); ++i)
922 for(; i!=block.body.end(); ++i)
923 unreachable_nodes.insert(i->get());
926 void UnreachableCodeRemover::visit(FunctionDeclaration &func)
928 TraversingVisitor::visit(func);
932 void UnreachableCodeRemover::visit(Conditional &cond)
934 cond.body.visit(*this);
935 bool reachable_if_true = reachable;
937 cond.else_body.visit(*this);
939 reachable |= reachable_if_true;
942 void UnreachableCodeRemover::visit(Iteration &iter)
944 TraversingVisitor::visit(iter);
946 /* Always consider code after a loop reachable, since there's no checking
947 for whether the loop executes. */
952 bool UnusedTypeRemover::apply(Stage &stage)
954 stage.content.visit(*this);
955 NodeRemover().apply(stage, unused_nodes);
956 return !unused_nodes.empty();
959 void UnusedTypeRemover::visit(RefPtr<Expression> &expr)
961 unused_nodes.erase(expr->type);
962 TraversingVisitor::visit(expr);
965 void UnusedTypeRemover::visit(BasicTypeDeclaration &type)
968 unused_nodes.erase(type.base_type);
969 unused_nodes.insert(&type);
972 void UnusedTypeRemover::visit(ImageTypeDeclaration &type)
975 unused_nodes.erase(type.base_type);
976 unused_nodes.insert(&type);
979 void UnusedTypeRemover::visit(StructDeclaration &strct)
981 unused_nodes.insert(&strct);
982 TraversingVisitor::visit(strct);
985 void UnusedTypeRemover::visit(VariableDeclaration &var)
987 unused_nodes.erase(var.type_declaration);
988 TraversingVisitor::visit(var);
991 void UnusedTypeRemover::visit(InterfaceBlock &iface)
993 unused_nodes.erase(iface.type_declaration);
996 void UnusedTypeRemover::visit(FunctionDeclaration &func)
998 unused_nodes.erase(func.return_type_declaration);
999 TraversingVisitor::visit(func);
1003 UnusedVariableRemover::UnusedVariableRemover():
1007 assignment_target(false),
1008 r_side_effects(false),
1010 composite_reference(false)
1013 bool UnusedVariableRemover::apply(Stage &s)
1016 s.content.visit(*this);
1018 for(list<AssignmentInfo>::const_iterator i=assignments.begin(); i!=assignments.end(); ++i)
1019 if(i->used_by.empty())
1020 unused_nodes.insert(i->node);
1022 for(BlockVariableMap::const_iterator i=variables.begin(); i!=variables.end(); ++i)
1024 if(i->second.output)
1026 /* The last visible assignments of output variables are used by the
1027 next stage or the API. */
1028 for(vector<AssignmentInfo *>::const_iterator j=i->second.assignments.begin(); j!=i->second.assignments.end(); ++j)
1029 unused_nodes.erase((*j)->node);
1032 if(!i->second.output && !i->second.referenced)
1034 // Don't remove variables from inside interface blocks.
1035 if(!i->second.interface_block)
1036 unused_nodes.insert(i->first);
1038 else if(i->second.interface_block)
1039 // Interface blocks are kept if even one member is used.
1040 unused_nodes.erase(i->second.interface_block);
1043 NodeRemover().apply(s, unused_nodes);
1045 return !unused_nodes.empty();
1048 void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &node)
1050 VariableInfo &var_info = variables[target.declaration];
1051 var_info.referenced = true;
1052 if(!assignment_target)
1054 for(vector<AssignmentInfo *>::const_iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i)
1056 bool covered = true;
1057 for(unsigned j=0; (covered && j<(*i)->target.chain_len && j<target.chain_len); ++j)
1059 Assignment::Target::ChainType type1 = static_cast<Assignment::Target::ChainType>((*i)->target.chain[j]&0xC0);
1060 Assignment::Target::ChainType type2 = static_cast<Assignment::Target::ChainType>(target.chain[j]&0xC0);
1061 if(type1==Assignment::Target::SWIZZLE || type2==Assignment::Target::SWIZZLE)
1063 unsigned index1 = (*i)->target.chain[j]&0x3F;
1064 unsigned index2 = target.chain[j]&0x3F;
1065 if(type1==Assignment::Target::SWIZZLE && type2==Assignment::Target::SWIZZLE)
1066 covered = index1&index2;
1067 else if(type1==Assignment::Target::ARRAY && index1<4)
1068 covered = index2&(1<<index1);
1069 else if(type2==Assignment::Target::ARRAY && index2<4)
1070 covered = index1&(1<<index2);
1071 /* If it's some other combination (shouldn't happen), leave
1075 covered = ((*i)->target.chain[j]==target.chain[j]);
1078 (*i)->used_by.push_back(&node);
1083 void UnusedVariableRemover::visit(VariableReference &var)
1085 if(composite_reference)
1086 r_reference.declaration = var.declaration;
1088 referenced(var.declaration, var);
1091 void UnusedVariableRemover::visit(InterfaceBlockReference &iface)
1093 if(composite_reference)
1094 r_reference.declaration = iface.declaration;
1096 referenced(iface.declaration, iface);
1099 void UnusedVariableRemover::visit_composite(Expression &expr)
1101 if(!composite_reference)
1102 r_reference = Assignment::Target();
1104 SetFlag set_composite(composite_reference);
1108 void UnusedVariableRemover::visit(MemberAccess &memacc)
1110 visit_composite(*memacc.left);
1112 add_to_chain(r_reference, Assignment::Target::MEMBER, memacc.index);
1114 if(!composite_reference && r_reference.declaration)
1115 referenced(r_reference, memacc);
1118 void UnusedVariableRemover::visit(Swizzle &swizzle)
1120 visit_composite(*swizzle.left);
1123 for(unsigned i=0; i<swizzle.count; ++i)
1124 mask |= 1<<swizzle.components[i];
1125 add_to_chain(r_reference, Assignment::Target::SWIZZLE, mask);
1127 if(!composite_reference && r_reference.declaration)
1128 referenced(r_reference, swizzle);
1131 void UnusedVariableRemover::visit(UnaryExpression &unary)
1133 TraversingVisitor::visit(unary);
1134 if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
1135 r_side_effects = true;
1138 void UnusedVariableRemover::visit(BinaryExpression &binary)
1140 if(binary.oper->token[0]=='[')
1142 visit_composite(*binary.left);
1145 SetFlag clear_assignment(assignment_target, false);
1146 SetFlag clear_composite(composite_reference, false);
1147 binary.right->visit(*this);
1150 add_to_chain(r_reference, Assignment::Target::ARRAY, 0x3F);
1152 if(!composite_reference && r_reference.declaration)
1153 referenced(r_reference, binary);
1157 SetFlag clear_composite(composite_reference, false);
1158 TraversingVisitor::visit(binary);
1162 void UnusedVariableRemover::visit(TernaryExpression &ternary)
1164 SetFlag clear_composite(composite_reference, false);
1165 TraversingVisitor::visit(ternary);
1168 void UnusedVariableRemover::visit(Assignment &assign)
1171 SetFlag set(assignment_target, (assign.oper->token[0]=='='));
1172 assign.left->visit(*this);
1174 assign.right->visit(*this);
1175 r_assignment = &assign;
1176 r_side_effects = true;
1179 void UnusedVariableRemover::visit(FunctionCall &call)
1181 SetFlag clear_composite(composite_reference, false);
1182 TraversingVisitor::visit(call);
1183 /* Treat function calls as having side effects so expression statements
1184 consisting of nothing but a function call won't be optimized away. */
1185 r_side_effects = true;
1187 if(stage->type==Stage::GEOMETRY && call.name=="EmitVertex")
1189 for(map<Statement *, VariableInfo>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
1190 if(i->second.output)
1191 referenced(i->first, call);
1195 void UnusedVariableRemover::record_assignment(const Assignment::Target &target, Node &node)
1197 assignments.push_back(AssignmentInfo());
1198 AssignmentInfo &assign_info = assignments.back();
1199 assign_info.node = &node;
1200 assign_info.target = target;
1202 /* An assignment to the target hides any assignments to the same target or
1204 VariableInfo &var_info = variables[target.declaration];
1205 for(unsigned i=0; i<var_info.assignments.size(); ++i)
1207 const Assignment::Target &t = var_info.assignments[i]->target;
1209 bool subfield = (t.chain_len>=target.chain_len);
1210 for(unsigned j=0; (subfield && j<target.chain_len); ++j)
1211 subfield = (t.chain[j]==target.chain[j]);
1214 var_info.assignments.erase(var_info.assignments.begin()+i);
1219 var_info.assignments.push_back(&assign_info);
1222 void UnusedVariableRemover::visit(ExpressionStatement &expr)
1225 r_side_effects = false;
1226 TraversingVisitor::visit(expr);
1227 if(r_assignment && r_assignment->target.declaration)
1228 record_assignment(r_assignment->target, expr);
1230 unused_nodes.insert(&expr);
1233 void UnusedVariableRemover::visit(StructDeclaration &strct)
1235 SetFlag set_struct(in_struct);
1236 TraversingVisitor::visit(strct);
1239 void UnusedVariableRemover::visit(VariableDeclaration &var)
1241 TraversingVisitor::visit(var);
1246 VariableInfo &var_info = variables[&var];
1247 var_info.interface_block = interface_block;
1249 /* Mark variables as output if they're used by the next stage or the
1252 var_info.output = (interface_block->interface=="out" && (interface_block->linked_block || !interface_block->block_name.compare(0, 3, "gl_")));
1254 var_info.output = (var.interface=="out" && (stage->type==Stage::FRAGMENT || var.linked_declaration || !var.name.compare(0, 3, "gl_")));
1256 if(var.init_expression)
1258 var_info.initialized = true;
1259 record_assignment(&var, *var.init_expression);
1263 void UnusedVariableRemover::visit(InterfaceBlock &iface)
1265 VariableInfo &var_info = variables[&iface];
1266 var_info.output = (iface.interface=="out" && (iface.linked_block || !iface.block_name.compare(0, 3, "gl_")));
1269 void UnusedVariableRemover::merge_variables(const BlockVariableMap &other_vars)
1271 for(BlockVariableMap::const_iterator i=other_vars.begin(); i!=other_vars.end(); ++i)
1273 BlockVariableMap::iterator j = variables.find(i->first);
1274 if(j!=variables.end())
1276 /* The merged blocks started as copies of each other so any common
1277 assignments must be in the beginning. */
1279 for(; (k<i->second.assignments.size() && k<j->second.assignments.size()); ++k)
1280 if(i->second.assignments[k]!=j->second.assignments[k])
1283 // Remaining assignments are unique to each block; merge them.
1284 j->second.assignments.insert(j->second.assignments.end(), i->second.assignments.begin()+k, i->second.assignments.end());
1285 j->second.referenced |= i->second.referenced;
1288 variables.insert(*i);
1292 void UnusedVariableRemover::visit(FunctionDeclaration &func)
1294 if(func.body.body.empty())
1297 BlockVariableMap saved_vars = variables;
1298 // Assignments from other functions should not be visible.
1299 for(BlockVariableMap::iterator i=variables.begin(); i!=variables.end(); ++i)
1300 i->second.assignments.resize(i->second.initialized);
1301 TraversingVisitor::visit(func);
1302 swap(variables, saved_vars);
1303 merge_variables(saved_vars);
1305 /* Always treat function parameters as referenced. Removing unused
1306 parameters is not currently supported. */
1307 for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
1309 BlockVariableMap::iterator j = variables.find(i->get());
1310 if(j!=variables.end())
1311 j->second.referenced = true;
1315 void UnusedVariableRemover::visit(Conditional &cond)
1317 cond.condition->visit(*this);
1318 BlockVariableMap saved_vars = variables;
1319 cond.body.visit(*this);
1320 swap(saved_vars, variables);
1321 cond.else_body.visit(*this);
1323 /* Visible assignments after the conditional is the union of those visible
1324 at the end of the if and else blocks. If there was no else block, then it's
1325 the union of the if block and the state before it. */
1326 merge_variables(saved_vars);
1329 void UnusedVariableRemover::visit(Iteration &iter)
1331 BlockVariableMap saved_vars = variables;
1332 TraversingVisitor::visit(iter);
1334 /* Merge assignments from the iteration, without clearing previous state.
1335 Further analysis is needed to determine which parts of the iteration body
1336 are always executed, if any. */
1337 merge_variables(saved_vars);
1341 bool UnusedFunctionRemover::apply(Stage &stage)
1343 stage.content.visit(*this);
1344 NodeRemover().apply(stage, unused_nodes);
1345 return !unused_nodes.empty();
1348 void UnusedFunctionRemover::visit(FunctionCall &call)
1350 TraversingVisitor::visit(call);
1352 unused_nodes.erase(call.declaration);
1353 if(call.declaration && call.declaration->definition!=call.declaration)
1354 used_definitions.insert(call.declaration->definition);
1357 void UnusedFunctionRemover::visit(FunctionDeclaration &func)
1359 TraversingVisitor::visit(func);
1361 if((func.name!="main" || func.body.body.empty()) && !used_definitions.count(&func))
1362 unused_nodes.insert(&func);