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():
357 iteration_init(false),
362 bool ExpressionInliner::apply(Stage &s)
364 s.content.visit(*this);
366 bool any_inlined = false;
367 for(list<ExpressionInfo>::iterator i=expressions.begin(); i!=expressions.end(); ++i)
368 if(i->expression && (i->trivial || i->uses.size()==1))
370 for(vector<ExpressionUse>::iterator j=i->uses.begin(); j!=i->uses.end(); ++j)
373 *j->reference = i->expression->clone();
381 void ExpressionInliner::visit(RefPtr<Expression> &expr)
385 if(r_ref_info && r_ref_info->expression)
388 use.reference = &expr;
389 use.ref_scope = current_block;
390 use.blocked = access_write;
392 if(iteration_body && !r_ref_info->trivial)
394 /* Block inlining of non-trivial expressions assigned outside an
395 iteration statement. The iteration may run multiple times, which
396 would cause the expression to also be evaluated multiple times. */
397 for(Block *i=iteration_body->parent; (!use.blocked && i); i=i->parent)
398 use.blocked = (i==r_ref_info->assign_scope);
401 /* Block inlining assignments from from inner scopes. The assignment may
402 depend on local variables of that scope or may not always be executed. */
403 for(Block *i=r_ref_info->assign_scope->parent; (!use.blocked && i); i=i->parent)
404 use.blocked = (i==current_block);
406 r_ref_info->uses.push_back(use);
412 void ExpressionInliner::visit(VariableReference &var)
414 if(var.declaration && access_read)
416 map<Assignment::Target, ExpressionInfo *>::iterator i = assignments.find(var.declaration);
417 if(i!=assignments.end())
418 r_ref_info = i->second;
422 void ExpressionInliner::visit(MemberAccess &memacc)
428 void ExpressionInliner::visit(Swizzle &swizzle)
434 void ExpressionInliner::visit(UnaryExpression &unary)
436 SetFlag set_write(access_write, access_write || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
437 visit(unary.expression);
441 void ExpressionInliner::visit(BinaryExpression &binary)
445 SetFlag clear_write(access_write, false);
451 void ExpressionInliner::visit(Assignment &assign)
454 SetFlag set_read(access_read, assign.oper->token[0]!='=');
455 SetFlag set_write(access_write);
462 map<Assignment::Target, ExpressionInfo *>::iterator i = assignments.find(assign.target);
463 if(i!=assignments.end())
465 if(iteration_body && i->second->expression)
467 /* Block inlining into previous references within the iteration
468 statement. On iterations after the first they would refer to the
469 assignment within the iteration. */
470 for(vector<ExpressionUse>::iterator j=i->second->uses.begin(); j!=i->second->uses.end(); ++j)
471 for(Block *k=j->ref_scope; (!j->blocked && k); k=k->parent)
472 j->blocked = (k==iteration_body);
475 expressions.push_back(ExpressionInfo());
476 ExpressionInfo &info = expressions.back();
477 info.target = assign.target;
478 // Self-referencing assignments can't be inlined without additional work.
479 if(!assign.self_referencing)
480 info.expression = assign.right;
481 info.assign_scope = current_block;
482 info.trivial = r_trivial;
490 void ExpressionInliner::visit(TernaryExpression &ternary)
492 visit(ternary.condition);
493 visit(ternary.true_expr);
494 visit(ternary.false_expr);
498 void ExpressionInliner::visit(FunctionCall &call)
500 TraversingVisitor::visit(call);
504 void ExpressionInliner::visit(VariableDeclaration &var)
508 TraversingVisitor::visit(var);
510 bool constant = var.constant;
511 if(constant && var.layout)
513 for(vector<Layout::Qualifier>::const_iterator i=var.layout->qualifiers.begin(); (constant && i!=var.layout->qualifiers.end()); ++i)
514 constant = (i->name!="constant_id");
517 /* Only inline global variables if they're constant and have trivial
518 initializers. Non-constant variables could change in ways which are hard to
519 analyze and non-trivial expressions could be expensive to inline. */
520 if((current_block->parent || (constant && r_trivial)) && var.interface.empty())
522 expressions.push_back(ExpressionInfo());
523 ExpressionInfo &info = expressions.back();
525 /* Assume variables declared in an iteration initialization statement
526 will have their values change throughout the iteration. */
528 info.expression = var.init_expression;
529 info.assign_scope = current_block;
530 info.trivial = r_trivial;
532 assignments[&var] = &info;
536 void ExpressionInliner::visit(Iteration &iter)
538 SetForScope<Block *> set_block(current_block, &iter.body);
539 if(iter.init_statement)
541 SetFlag set_init(iteration_init);
542 iter.init_statement->visit(*this);
545 SetForScope<Block *> set_body(iteration_body, &iter.body);
547 visit(iter.condition);
548 iter.body.visit(*this);
549 if(iter.loop_expression)
550 visit(iter.loop_expression);
555 T ConstantFolder::evaluate_logical(char oper, T left, T right)
559 case '&': return left&right;
560 case '|': return left|right;
561 case '^': return left^right;
567 bool ConstantFolder::evaluate_relation(const char *oper, T left, T right)
569 switch(oper[0]|oper[1])
571 case '<': return left<right;
572 case '<'|'=': return left<=right;
573 case '>': return left>right;
574 case '>'|'=': return left>=right;
575 default: return false;
580 T ConstantFolder::evaluate_arithmetic(char oper, T left, T right)
584 case '+': return left+right;
585 case '-': return left-right;
586 case '*': return left*right;
587 case '/': return left/right;
593 T ConstantFolder::evaluate_int_special_op(char oper, T left, T right)
597 case '%': return left%right;
598 case '<': return left<<right;
599 case '>': return left>>right;
605 void ConstantFolder::convert_to_result(const Variant &value)
607 if(value.check_type<bool>())
608 set_result(static_cast<T>(value.value<bool>()));
609 else if(value.check_type<int>())
610 set_result(static_cast<T>(value.value<int>()));
611 else if(value.check_type<unsigned>())
612 set_result(static_cast<T>(value.value<unsigned>()));
613 else if(value.check_type<float>())
614 set_result(static_cast<T>(value.value<float>()));
617 void ConstantFolder::set_result(const Variant &value, bool literal)
619 r_constant_value = value;
624 void ConstantFolder::visit(RefPtr<Expression> &expr)
626 r_constant_value = Variant();
629 r_uses_iter_var = false;
631 /* Don't replace literals since they'd only be replaced with an identical
632 literal. Also skip anything that uses an iteration variable, but pass on
633 the result so the Iteration visiting function can handle it. */
634 if(!r_constant || r_literal || r_uses_iter_var)
637 RefPtr<Literal> literal = new Literal;
638 if(r_constant_value.check_type<bool>())
639 literal->token = (r_constant_value.value<bool>() ? "true" : "false");
640 else if(r_constant_value.check_type<int>())
641 literal->token = lexical_cast<string>(r_constant_value.value<int>());
642 else if(r_constant_value.check_type<unsigned>())
643 literal->token = lexical_cast<string>(r_constant_value.value<unsigned>())+"u";
644 else if(r_constant_value.check_type<float>())
646 literal->token = lexical_cast<string>(r_constant_value.value<float>(), Fmt().precision(8));
647 if(literal->token.find('.')==string::npos && literal->token.find('e')==string::npos)
648 literal->token += ".0";
655 literal->value = r_constant_value;
660 void ConstantFolder::visit(Literal &literal)
662 set_result(literal.value, true);
665 void ConstantFolder::visit(VariableReference &var)
667 /* If an iteration variable is initialized with a constant value, return
668 that value here for the purpose of evaluating the loop condition for the
670 if(var.declaration==iteration_var)
672 set_result(iter_init_value);
673 r_uses_iter_var = true;
677 void ConstantFolder::visit(MemberAccess &memacc)
679 TraversingVisitor::visit(memacc);
683 void ConstantFolder::visit(Swizzle &swizzle)
685 TraversingVisitor::visit(swizzle);
689 void ConstantFolder::visit(UnaryExpression &unary)
691 TraversingVisitor::visit(unary);
692 bool can_fold = r_constant;
697 char oper = unary.oper->token[0];
698 char oper2 = unary.oper->token[1];
701 if(r_constant_value.check_type<bool>())
702 set_result(!r_constant_value.value<bool>());
706 if(r_constant_value.check_type<int>())
707 set_result(~r_constant_value.value<int>());
708 else if(r_constant_value.check_type<unsigned>())
709 set_result(~r_constant_value.value<unsigned>());
711 else if(oper=='-' && !oper2)
713 if(r_constant_value.check_type<int>())
714 set_result(-r_constant_value.value<int>());
715 else if(r_constant_value.check_type<unsigned>())
716 set_result(-r_constant_value.value<unsigned>());
717 else if(r_constant_value.check_type<float>())
718 set_result(-r_constant_value.value<float>());
722 void ConstantFolder::visit(BinaryExpression &binary)
725 bool left_constant = r_constant;
726 bool left_iter_var = r_uses_iter_var;
727 Variant left_value = r_constant_value;
730 r_uses_iter_var = true;
732 bool can_fold = (left_constant && r_constant);
737 // Currently only expressions with both sides of equal types are handled.
738 if(!left_value.check_same_type(r_constant_value))
741 char oper = binary.oper->token[0];
742 char oper2 = binary.oper->token[1];
743 if(oper=='&' || oper=='|' || oper=='^')
745 if(oper2==oper && left_value.check_type<bool>())
746 set_result(evaluate_logical(oper, left_value.value<bool>(), r_constant_value.value<bool>()));
747 else if(!oper2 && left_value.check_type<int>())
748 set_result(evaluate_logical(oper, left_value.value<int>(), r_constant_value.value<int>()));
749 else if(!oper2 && left_value.check_type<unsigned>())
750 set_result(evaluate_logical(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
752 else if((oper=='<' || oper=='>') && oper2!=oper)
754 if(left_value.check_type<int>())
755 set_result(evaluate_relation(binary.oper->token, left_value.value<int>(), r_constant_value.value<int>()));
756 else if(left_value.check_type<unsigned>())
757 set_result(evaluate_relation(binary.oper->token, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
758 else if(left_value.check_type<float>())
759 set_result(evaluate_relation(binary.oper->token, left_value.value<float>(), r_constant_value.value<float>()));
761 else if((oper=='=' || oper=='!') && oper2=='=')
763 if(left_value.check_type<int>())
764 set_result((left_value.value<int>()==r_constant_value.value<int>()) == (oper=='='));
765 else if(left_value.check_type<unsigned>())
766 set_result((left_value.value<unsigned>()==r_constant_value.value<unsigned>()) == (oper=='='));
767 else if(left_value.check_type<float>())
768 set_result((left_value.value<float>()==r_constant_value.value<float>()) == (oper=='='));
770 else if(oper=='+' || oper=='-' || oper=='*' || oper=='/')
772 if(left_value.check_type<int>())
773 set_result(evaluate_arithmetic(oper, left_value.value<int>(), r_constant_value.value<int>()));
774 else if(left_value.check_type<unsigned>())
775 set_result(evaluate_arithmetic(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
776 else if(left_value.check_type<float>())
777 set_result(evaluate_arithmetic(oper, left_value.value<float>(), r_constant_value.value<float>()));
779 else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper))
781 if(left_value.check_type<int>())
782 set_result(evaluate_int_special_op(oper, left_value.value<int>(), r_constant_value.value<int>()));
783 else if(left_value.check_type<unsigned>())
784 set_result(evaluate_int_special_op(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
788 void ConstantFolder::visit(Assignment &assign)
790 TraversingVisitor::visit(assign);
794 void ConstantFolder::visit(TernaryExpression &ternary)
796 TraversingVisitor::visit(ternary);
800 void ConstantFolder::visit(FunctionCall &call)
802 if(call.constructor && call.type && call.arguments.size()==1)
804 const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(call.type);
807 visit(call.arguments[0]);
808 bool can_fold = r_constant;
813 if(basic->kind==BasicTypeDeclaration::BOOL)
814 convert_to_result<bool>(r_constant_value);
815 else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && basic->sign)
816 convert_to_result<int>(r_constant_value);
817 else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && !basic->sign)
818 convert_to_result<unsigned>(r_constant_value);
819 else if(basic->kind==BasicTypeDeclaration::FLOAT && basic->size==32)
820 convert_to_result<float>(r_constant_value);
826 TraversingVisitor::visit(call);
830 void ConstantFolder::visit(VariableDeclaration &var)
832 if(iteration_init && var.init_expression)
834 visit(var.init_expression);
837 /* Record the value of a constant initialization expression of an
838 iteration, so it can be used to evaluate the loop condition. */
839 iteration_var = &var;
840 iter_init_value = r_constant_value;
844 TraversingVisitor::visit(var);
847 void ConstantFolder::visit(Iteration &iter)
849 SetForScope<Block *> set_block(current_block, &iter.body);
851 /* The iteration variable is not normally inlined into expressions, so we
852 process it specially here. If the initial value causes the loop condition
853 to evaluate to false, then the expression can be folded. */
855 if(iter.init_statement)
857 SetFlag set_init(iteration_init);
858 iter.init_statement->visit(*this);
863 visit(iter.condition);
864 if(r_constant && r_constant_value.check_type<bool>() && !r_constant_value.value<bool>())
866 RefPtr<Literal> literal = new Literal;
867 literal->token = "false";
868 literal->value = r_constant_value;
869 iter.condition = literal;
874 iter.body.visit(*this);
875 if(iter.loop_expression)
876 visit(iter.loop_expression);
880 void ConstantConditionEliminator::apply(Stage &stage)
882 stage.content.visit(*this);
883 NodeRemover().apply(stage, nodes_to_remove);
886 ConstantConditionEliminator::ConstantStatus ConstantConditionEliminator::check_constant_condition(const Expression &expr)
888 if(const Literal *literal = dynamic_cast<const Literal *>(&expr))
889 if(literal->value.check_type<bool>())
890 return (literal->value.value<bool>() ? CONSTANT_TRUE : CONSTANT_FALSE);
894 void ConstantConditionEliminator::visit(Block &block)
896 SetForScope<Block *> set_block(current_block, &block);
897 for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
904 void ConstantConditionEliminator::visit(RefPtr<Expression> &expr)
906 r_ternary_result = 0;
909 expr = r_ternary_result;
910 r_ternary_result = 0;
913 void ConstantConditionEliminator::visit(TernaryExpression &ternary)
915 ConstantStatus result = check_constant_condition(*ternary.condition);
916 if(result!=NOT_CONSTANT)
917 r_ternary_result = (result==CONSTANT_TRUE ? ternary.true_expr : ternary.false_expr);
919 r_ternary_result = 0;
922 void ConstantConditionEliminator::visit(Conditional &cond)
924 ConstantStatus result = check_constant_condition(*cond.condition);
925 if(result!=NOT_CONSTANT)
927 Block &block = (result==CONSTANT_TRUE ? cond.body : cond.else_body);
928 // TODO should check variable names for conflicts. Potentially reuse InlineContentInjector?
929 current_block->body.splice(insert_point, block.body);
930 nodes_to_remove.insert(&cond);
934 TraversingVisitor::visit(cond);
937 void ConstantConditionEliminator::visit(Iteration &iter)
941 ConstantStatus result = check_constant_condition(*iter.condition);
942 if(result==CONSTANT_FALSE)
944 nodes_to_remove.insert(&iter);
949 TraversingVisitor::visit(iter);
953 UnreachableCodeRemover::UnreachableCodeRemover():
957 bool UnreachableCodeRemover::apply(Stage &stage)
959 stage.content.visit(*this);
960 NodeRemover().apply(stage, unreachable_nodes);
961 return !unreachable_nodes.empty();
964 void UnreachableCodeRemover::visit(Block &block)
966 NodeList<Statement>::iterator i = block.body.begin();
967 for(; (reachable && i!=block.body.end()); ++i)
969 for(; i!=block.body.end(); ++i)
970 unreachable_nodes.insert(i->get());
973 void UnreachableCodeRemover::visit(FunctionDeclaration &func)
975 TraversingVisitor::visit(func);
979 void UnreachableCodeRemover::visit(Conditional &cond)
981 cond.body.visit(*this);
982 bool reachable_if_true = reachable;
984 cond.else_body.visit(*this);
986 reachable |= reachable_if_true;
989 void UnreachableCodeRemover::visit(Iteration &iter)
991 TraversingVisitor::visit(iter);
993 /* Always consider code after a loop reachable, since there's no checking
994 for whether the loop executes. */
999 bool UnusedTypeRemover::apply(Stage &stage)
1001 stage.content.visit(*this);
1002 NodeRemover().apply(stage, unused_nodes);
1003 return !unused_nodes.empty();
1006 void UnusedTypeRemover::visit(RefPtr<Expression> &expr)
1008 unused_nodes.erase(expr->type);
1009 TraversingVisitor::visit(expr);
1012 void UnusedTypeRemover::visit(BasicTypeDeclaration &type)
1015 unused_nodes.erase(type.base_type);
1016 unused_nodes.insert(&type);
1019 void UnusedTypeRemover::visit(ImageTypeDeclaration &type)
1022 unused_nodes.erase(type.base_type);
1023 unused_nodes.insert(&type);
1026 void UnusedTypeRemover::visit(StructDeclaration &strct)
1028 unused_nodes.insert(&strct);
1029 TraversingVisitor::visit(strct);
1032 void UnusedTypeRemover::visit(VariableDeclaration &var)
1034 unused_nodes.erase(var.type_declaration);
1035 TraversingVisitor::visit(var);
1038 void UnusedTypeRemover::visit(InterfaceBlock &iface)
1040 unused_nodes.erase(iface.type_declaration);
1043 void UnusedTypeRemover::visit(FunctionDeclaration &func)
1045 unused_nodes.erase(func.return_type_declaration);
1046 TraversingVisitor::visit(func);
1050 UnusedVariableRemover::UnusedVariableRemover():
1054 assignment_target(false),
1055 r_side_effects(false),
1057 composite_reference(false),
1061 bool UnusedVariableRemover::apply(Stage &s)
1064 s.content.visit(*this);
1066 for(list<AssignmentInfo>::const_iterator i=assignments.begin(); i!=assignments.end(); ++i)
1067 if(i->used_by.empty())
1068 unused_nodes.insert(i->node);
1070 for(BlockVariableMap::const_iterator i=variables.begin(); i!=variables.end(); ++i)
1072 if(i->second.output)
1074 /* The last visible assignments of output variables are used by the
1075 next stage or the API. */
1076 for(vector<AssignmentInfo *>::const_iterator j=i->second.assignments.begin(); j!=i->second.assignments.end(); ++j)
1077 unused_nodes.erase((*j)->node);
1080 if(!i->second.output && !i->second.referenced)
1082 // Don't remove variables from inside interface blocks.
1083 if(!i->second.interface_block)
1084 unused_nodes.insert(i->first);
1086 else if(i->second.interface_block)
1087 // Interface blocks are kept if even one member is used.
1088 unused_nodes.erase(i->second.interface_block);
1091 NodeRemover().apply(s, unused_nodes);
1093 return !unused_nodes.empty();
1096 void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &node)
1098 VariableInfo &var_info = variables[target.declaration];
1099 var_info.referenced = true;
1100 if(!assignment_target)
1102 bool loop_external = false;
1103 for(vector<AssignmentInfo *>::const_iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i)
1105 bool covered = true;
1106 for(unsigned j=0; (covered && j<(*i)->target.chain_len && j<target.chain_len); ++j)
1108 Assignment::Target::ChainType type1 = static_cast<Assignment::Target::ChainType>((*i)->target.chain[j]&0xC0);
1109 Assignment::Target::ChainType type2 = static_cast<Assignment::Target::ChainType>(target.chain[j]&0xC0);
1110 if(type1==Assignment::Target::SWIZZLE || type2==Assignment::Target::SWIZZLE)
1112 unsigned index1 = (*i)->target.chain[j]&0x3F;
1113 unsigned index2 = target.chain[j]&0x3F;
1114 if(type1==Assignment::Target::SWIZZLE && type2==Assignment::Target::SWIZZLE)
1115 covered = index1&index2;
1116 else if(type1==Assignment::Target::ARRAY && index1<4)
1117 covered = index2&(1<<index1);
1118 else if(type2==Assignment::Target::ARRAY && index2<4)
1119 covered = index1&(1<<index2);
1120 /* If it's some other combination (shouldn't happen), leave
1124 covered = ((*i)->target.chain[j]==target.chain[j]);
1129 (*i)->used_by.push_back(&node);
1130 if((*i)->in_loop<in_loop)
1131 loop_external = true;
1136 loop_ext_refs.push_back(&node);
1140 void UnusedVariableRemover::visit(VariableReference &var)
1142 if(composite_reference)
1143 r_reference.declaration = var.declaration;
1145 referenced(var.declaration, var);
1148 void UnusedVariableRemover::visit(InterfaceBlockReference &iface)
1150 if(composite_reference)
1151 r_reference.declaration = iface.declaration;
1153 referenced(iface.declaration, iface);
1156 void UnusedVariableRemover::visit_composite(Expression &expr)
1158 if(!composite_reference)
1159 r_reference = Assignment::Target();
1161 SetFlag set_composite(composite_reference);
1165 void UnusedVariableRemover::visit(MemberAccess &memacc)
1167 visit_composite(*memacc.left);
1169 add_to_chain(r_reference, Assignment::Target::MEMBER, memacc.index);
1171 if(!composite_reference && r_reference.declaration)
1172 referenced(r_reference, memacc);
1175 void UnusedVariableRemover::visit(Swizzle &swizzle)
1177 visit_composite(*swizzle.left);
1180 for(unsigned i=0; i<swizzle.count; ++i)
1181 mask |= 1<<swizzle.components[i];
1182 add_to_chain(r_reference, Assignment::Target::SWIZZLE, mask);
1184 if(!composite_reference && r_reference.declaration)
1185 referenced(r_reference, swizzle);
1188 void UnusedVariableRemover::visit(UnaryExpression &unary)
1190 TraversingVisitor::visit(unary);
1191 if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
1192 r_side_effects = true;
1195 void UnusedVariableRemover::visit(BinaryExpression &binary)
1197 if(binary.oper->token[0]=='[')
1199 visit_composite(*binary.left);
1202 SetFlag clear_assignment(assignment_target, false);
1203 SetFlag clear_composite(composite_reference, false);
1204 binary.right->visit(*this);
1207 add_to_chain(r_reference, Assignment::Target::ARRAY, 0x3F);
1209 if(!composite_reference && r_reference.declaration)
1210 referenced(r_reference, binary);
1214 SetFlag clear_composite(composite_reference, false);
1215 TraversingVisitor::visit(binary);
1219 void UnusedVariableRemover::visit(TernaryExpression &ternary)
1221 SetFlag clear_composite(composite_reference, false);
1222 TraversingVisitor::visit(ternary);
1225 void UnusedVariableRemover::visit(Assignment &assign)
1228 SetFlag set(assignment_target, (assign.oper->token[0]=='='));
1229 assign.left->visit(*this);
1231 assign.right->visit(*this);
1232 r_assignment = &assign;
1233 r_side_effects = true;
1236 void UnusedVariableRemover::visit(FunctionCall &call)
1238 SetFlag clear_composite(composite_reference, false);
1239 TraversingVisitor::visit(call);
1240 /* Treat function calls as having side effects so expression statements
1241 consisting of nothing but a function call won't be optimized away. */
1242 r_side_effects = true;
1244 if(stage->type==Stage::GEOMETRY && call.name=="EmitVertex")
1246 for(map<Statement *, VariableInfo>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
1247 if(i->second.output)
1248 referenced(i->first, call);
1252 void UnusedVariableRemover::record_assignment(const Assignment::Target &target, Node &node)
1254 assignments.push_back(AssignmentInfo());
1255 AssignmentInfo &assign_info = assignments.back();
1256 assign_info.node = &node;
1257 assign_info.target = target;
1258 assign_info.in_loop = in_loop;
1260 /* An assignment to the target hides any assignments to the same target or
1262 VariableInfo &var_info = variables[target.declaration];
1263 for(unsigned i=0; i<var_info.assignments.size(); )
1265 const Assignment::Target &t = var_info.assignments[i]->target;
1267 bool subfield = (t.chain_len>=target.chain_len);
1268 for(unsigned j=0; (subfield && j<target.chain_len); ++j)
1269 subfield = (t.chain[j]==target.chain[j]);
1272 var_info.assignments.erase(var_info.assignments.begin()+i);
1277 var_info.assignments.push_back(&assign_info);
1280 void UnusedVariableRemover::visit(ExpressionStatement &expr)
1283 r_side_effects = false;
1284 TraversingVisitor::visit(expr);
1285 if(r_assignment && r_assignment->target.declaration)
1286 record_assignment(r_assignment->target, expr);
1288 unused_nodes.insert(&expr);
1291 void UnusedVariableRemover::visit(StructDeclaration &strct)
1293 SetFlag set_struct(in_struct);
1294 TraversingVisitor::visit(strct);
1297 void UnusedVariableRemover::visit(VariableDeclaration &var)
1299 TraversingVisitor::visit(var);
1304 VariableInfo &var_info = variables[&var];
1305 var_info.interface_block = interface_block;
1307 /* Mark variables as output if they're used by the next stage or the
1310 var_info.output = (interface_block->interface=="out" && (interface_block->linked_block || !interface_block->block_name.compare(0, 3, "gl_")));
1312 var_info.output = (var.interface=="out" && (stage->type==Stage::FRAGMENT || var.linked_declaration || !var.name.compare(0, 3, "gl_")));
1314 if(var.init_expression)
1316 var_info.initialized = true;
1317 record_assignment(&var, *var.init_expression);
1321 void UnusedVariableRemover::visit(InterfaceBlock &iface)
1323 VariableInfo &var_info = variables[&iface];
1324 var_info.output = (iface.interface=="out" && (iface.linked_block || !iface.block_name.compare(0, 3, "gl_")));
1327 void UnusedVariableRemover::merge_variables(const BlockVariableMap &other_vars)
1329 for(BlockVariableMap::const_iterator i=other_vars.begin(); i!=other_vars.end(); ++i)
1331 BlockVariableMap::iterator j = variables.find(i->first);
1332 if(j!=variables.end())
1334 /* The merged blocks started as copies of each other so any common
1335 assignments must be in the beginning. */
1337 for(; (k<i->second.assignments.size() && k<j->second.assignments.size()); ++k)
1338 if(i->second.assignments[k]!=j->second.assignments[k])
1341 // Remaining assignments are unique to each block; merge them.
1342 j->second.assignments.insert(j->second.assignments.end(), i->second.assignments.begin()+k, i->second.assignments.end());
1343 j->second.referenced |= i->second.referenced;
1346 variables.insert(*i);
1350 void UnusedVariableRemover::visit(FunctionDeclaration &func)
1352 if(func.body.body.empty())
1355 BlockVariableMap saved_vars = variables;
1356 // Assignments from other functions should not be visible.
1357 for(BlockVariableMap::iterator i=variables.begin(); i!=variables.end(); ++i)
1358 i->second.assignments.resize(i->second.initialized);
1359 TraversingVisitor::visit(func);
1360 swap(variables, saved_vars);
1361 merge_variables(saved_vars);
1363 /* Always treat function parameters as referenced. Removing unused
1364 parameters is not currently supported. */
1365 for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
1367 BlockVariableMap::iterator j = variables.find(i->get());
1368 if(j!=variables.end())
1369 j->second.referenced = true;
1373 void UnusedVariableRemover::visit(Conditional &cond)
1375 cond.condition->visit(*this);
1376 BlockVariableMap saved_vars = variables;
1377 cond.body.visit(*this);
1378 swap(saved_vars, variables);
1379 cond.else_body.visit(*this);
1381 /* Visible assignments after the conditional is the union of those visible
1382 at the end of the if and else blocks. If there was no else block, then it's
1383 the union of the if block and the state before it. */
1384 merge_variables(saved_vars);
1387 void UnusedVariableRemover::visit(Iteration &iter)
1389 BlockVariableMap saved_vars = variables;
1390 vector<Node *> saved_refs;
1391 swap(loop_ext_refs, saved_refs);
1393 SetForScope<unsigned> set_loop(in_loop, in_loop+1);
1394 TraversingVisitor::visit(iter);
1396 swap(loop_ext_refs, saved_refs);
1398 /* Visit the external references of the loop again to record assignments
1399 done in the loop as used. */
1400 for(vector<Node *>::const_iterator i=saved_refs.begin(); i!=saved_refs.end(); ++i)
1403 /* Merge assignments from the iteration, without clearing previous state.
1404 Further analysis is needed to determine which parts of the iteration body
1405 are always executed, if any. */
1406 merge_variables(saved_vars);
1410 bool UnusedFunctionRemover::apply(Stage &stage)
1412 stage.content.visit(*this);
1413 NodeRemover().apply(stage, unused_nodes);
1414 return !unused_nodes.empty();
1417 void UnusedFunctionRemover::visit(FunctionCall &call)
1419 TraversingVisitor::visit(call);
1421 unused_nodes.erase(call.declaration);
1422 if(call.declaration && call.declaration->definition!=call.declaration)
1423 used_definitions.insert(call.declaration->definition);
1426 void UnusedFunctionRemover::visit(FunctionDeclaration &func)
1428 TraversingVisitor::visit(func);
1430 if((func.name!="main" || func.body.body.empty()) && !used_definitions.count(&func))
1431 unused_nodes.insert(&func);