1 #include <msp/core/algorithm.h>
2 #include <msp/core/raii.h>
3 #include <msp/strings/format.h>
4 #include <msp/strings/utils.h>
14 ConstantSpecializer::ConstantSpecializer():
18 void ConstantSpecializer::apply(Stage &stage, const map<string, int> &v)
21 stage.content.visit(*this);
24 void ConstantSpecializer::visit(VariableDeclaration &var)
26 bool specializable = false;
29 vector<Layout::Qualifier> &qualifiers = var.layout->qualifiers;
30 auto i = find_member(qualifiers, string("constant_id"), &Layout::Qualifier::name);
31 if(i!=qualifiers.end())
35 if(qualifiers.empty())
42 auto 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 = any_of(func.parameters.begin(), func.parameters.end(),
89 [](const RefPtr<VariableDeclaration> &p){ return p->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(const RefPtr<VariableDeclaration> &p: source_func->parameters)
145 RefPtr<VariableDeclaration> var = p->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(const RefPtr<Statement> &s: source_func->body.body)
157 r_inlined_statement = 0;
159 if(!r_inlined_statement)
160 r_inlined_statement = s->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 auto 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(auto i=block.body.begin(); (!r_inlined_here && i!=block.body.end()); ++i)
298 void FunctionInliner::visit(FunctionCall &call)
300 for(auto 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():
356 iteration_init(false),
361 bool ExpressionInliner::apply(Stage &s)
363 s.content.visit(*this);
365 bool any_inlined = false;
366 for(ExpressionInfo &e: expressions)
367 if(e.expression && (e.trivial || e.uses.size()==1))
369 for(ExpressionUse &u: e.uses)
372 *u.reference = e.expression->clone();
380 void ExpressionInliner::visit(RefPtr<Expression> &expr)
384 if(r_ref_info && r_ref_info->expression)
387 use.reference = &expr;
388 use.ref_scope = current_block;
389 use.blocked = access_write;
391 if(iteration_body && !r_ref_info->trivial)
393 /* Block inlining of non-trivial expressions assigned outside an
394 iteration statement. The iteration may run multiple times, which
395 would cause the expression to also be evaluated multiple times. */
396 for(Block *i=iteration_body->parent; (!use.blocked && i); i=i->parent)
397 use.blocked = (i==r_ref_info->assign_scope);
400 /* Block inlining assignments from from inner scopes. The assignment may
401 depend on local variables of that scope or may not always be executed. */
402 for(Block *i=r_ref_info->assign_scope->parent; (!use.blocked && i); i=i->parent)
403 use.blocked = (i==current_block);
405 r_ref_info->uses.push_back(use);
411 void ExpressionInliner::visit(VariableReference &var)
413 if(var.declaration && access_read)
415 auto i = assignments.find(var.declaration);
416 if(i!=assignments.end())
417 r_ref_info = i->second;
421 void ExpressionInliner::visit(MemberAccess &memacc)
427 void ExpressionInliner::visit(Swizzle &swizzle)
433 void ExpressionInliner::visit(UnaryExpression &unary)
435 SetFlag set_write(access_write, access_write || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
436 visit(unary.expression);
440 void ExpressionInliner::visit(BinaryExpression &binary)
444 SetFlag clear_write(access_write, false);
450 void ExpressionInliner::visit(Assignment &assign)
453 SetFlag set_read(access_read, assign.oper->token[0]!='=');
454 SetFlag set_write(access_write);
461 auto i = assignments.find(assign.target);
462 if(i!=assignments.end())
464 if(iteration_body && i->second->expression)
466 /* Block inlining into previous references within the iteration
467 statement. On iterations after the first they would refer to the
468 assignment within the iteration. */
469 for(ExpressionUse &u: i->second->uses)
470 for(Block *k=u.ref_scope; (!u.blocked && k); k=k->parent)
471 u.blocked = (k==iteration_body);
474 expressions.push_back(ExpressionInfo());
475 ExpressionInfo &info = expressions.back();
476 info.target = assign.target;
477 // Self-referencing assignments can't be inlined without additional work.
478 if(!assign.self_referencing)
479 info.expression = assign.right;
480 info.assign_scope = current_block;
481 info.trivial = r_trivial;
489 void ExpressionInliner::visit(TernaryExpression &ternary)
491 visit(ternary.condition);
492 visit(ternary.true_expr);
493 visit(ternary.false_expr);
497 void ExpressionInliner::visit(FunctionCall &call)
499 TraversingVisitor::visit(call);
503 void ExpressionInliner::visit(VariableDeclaration &var)
507 TraversingVisitor::visit(var);
509 bool constant = var.constant;
510 if(constant && var.layout)
512 constant = !any_of(var.layout->qualifiers.begin(), var.layout->qualifiers.end(),
513 [](const Layout::Qualifier &q){ return q.name=="constant_id"; });
516 /* Only inline global variables if they're constant and have trivial
517 initializers. Non-constant variables could change in ways which are hard to
518 analyze and non-trivial expressions could be expensive to inline. */
519 if((current_block->parent || (constant && r_trivial)) && var.interface.empty())
521 expressions.push_back(ExpressionInfo());
522 ExpressionInfo &info = expressions.back();
524 /* Assume variables declared in an iteration initialization statement
525 will have their values change throughout the iteration. */
527 info.expression = var.init_expression;
528 info.assign_scope = current_block;
529 info.trivial = r_trivial;
531 assignments[&var] = &info;
535 void ExpressionInliner::visit(Iteration &iter)
537 SetForScope<Block *> set_block(current_block, &iter.body);
538 if(iter.init_statement)
540 SetFlag set_init(iteration_init);
541 iter.init_statement->visit(*this);
544 SetForScope<Block *> set_body(iteration_body, &iter.body);
546 visit(iter.condition);
547 iter.body.visit(*this);
548 if(iter.loop_expression)
549 visit(iter.loop_expression);
554 T ConstantFolder::evaluate_logical(char oper, T left, T right)
558 case '&': return left&right;
559 case '|': return left|right;
560 case '^': return left^right;
566 bool ConstantFolder::evaluate_relation(const char *oper, T left, T right)
568 switch(oper[0]|oper[1])
570 case '<': return left<right;
571 case '<'|'=': return left<=right;
572 case '>': return left>right;
573 case '>'|'=': return left>=right;
574 default: return false;
579 T ConstantFolder::evaluate_arithmetic(char oper, T left, T right)
583 case '+': return left+right;
584 case '-': return left-right;
585 case '*': return left*right;
586 case '/': return left/right;
592 T ConstantFolder::evaluate_int_special_op(char oper, T left, T right)
596 case '%': return left%right;
597 case '<': return left<<right;
598 case '>': return left>>right;
604 void ConstantFolder::convert_to_result(const Variant &value)
606 if(value.check_type<bool>())
607 set_result(static_cast<T>(value.value<bool>()));
608 else if(value.check_type<int>())
609 set_result(static_cast<T>(value.value<int>()));
610 else if(value.check_type<unsigned>())
611 set_result(static_cast<T>(value.value<unsigned>()));
612 else if(value.check_type<float>())
613 set_result(static_cast<T>(value.value<float>()));
616 void ConstantFolder::set_result(const Variant &value, bool literal)
618 r_constant_value = value;
623 void ConstantFolder::visit(RefPtr<Expression> &expr)
625 r_constant_value = Variant();
628 r_uses_iter_var = false;
630 /* Don't replace literals since they'd only be replaced with an identical
631 literal. Also skip anything that uses an iteration variable, but pass on
632 the result so the Iteration visiting function can handle it. */
633 if(!r_constant || r_literal || r_uses_iter_var)
636 RefPtr<Literal> literal = new Literal;
637 if(r_constant_value.check_type<bool>())
638 literal->token = (r_constant_value.value<bool>() ? "true" : "false");
639 else if(r_constant_value.check_type<int>())
640 literal->token = lexical_cast<string>(r_constant_value.value<int>());
641 else if(r_constant_value.check_type<unsigned>())
642 literal->token = lexical_cast<string>(r_constant_value.value<unsigned>())+"u";
643 else if(r_constant_value.check_type<float>())
645 literal->token = lexical_cast<string>(r_constant_value.value<float>(), Fmt().precision(8));
646 if(literal->token.find('.')==string::npos && literal->token.find('e')==string::npos)
647 literal->token += ".0";
654 literal->value = r_constant_value;
659 void ConstantFolder::visit(Literal &literal)
661 set_result(literal.value, true);
664 void ConstantFolder::visit(VariableReference &var)
666 /* If an iteration variable is initialized with a constant value, return
667 that value here for the purpose of evaluating the loop condition for the
669 if(var.declaration==iteration_var)
671 set_result(iter_init_value);
672 r_uses_iter_var = true;
676 void ConstantFolder::visit(MemberAccess &memacc)
678 TraversingVisitor::visit(memacc);
682 void ConstantFolder::visit(Swizzle &swizzle)
684 TraversingVisitor::visit(swizzle);
688 void ConstantFolder::visit(UnaryExpression &unary)
690 TraversingVisitor::visit(unary);
691 bool can_fold = r_constant;
696 char oper = unary.oper->token[0];
697 char oper2 = unary.oper->token[1];
700 if(r_constant_value.check_type<bool>())
701 set_result(!r_constant_value.value<bool>());
705 if(r_constant_value.check_type<int>())
706 set_result(~r_constant_value.value<int>());
707 else if(r_constant_value.check_type<unsigned>())
708 set_result(~r_constant_value.value<unsigned>());
710 else if(oper=='-' && !oper2)
712 if(r_constant_value.check_type<int>())
713 set_result(-r_constant_value.value<int>());
714 else if(r_constant_value.check_type<unsigned>())
715 set_result(-r_constant_value.value<unsigned>());
716 else if(r_constant_value.check_type<float>())
717 set_result(-r_constant_value.value<float>());
721 void ConstantFolder::visit(BinaryExpression &binary)
724 bool left_constant = r_constant;
725 bool left_iter_var = r_uses_iter_var;
726 Variant left_value = r_constant_value;
729 r_uses_iter_var = true;
731 bool can_fold = (left_constant && r_constant);
736 // Currently only expressions with both sides of equal types are handled.
737 if(!left_value.check_same_type(r_constant_value))
740 char oper = binary.oper->token[0];
741 char oper2 = binary.oper->token[1];
742 if(oper=='&' || oper=='|' || oper=='^')
744 if(oper2==oper && left_value.check_type<bool>())
745 set_result(evaluate_logical(oper, left_value.value<bool>(), r_constant_value.value<bool>()));
746 else if(!oper2 && left_value.check_type<int>())
747 set_result(evaluate_logical(oper, left_value.value<int>(), r_constant_value.value<int>()));
748 else if(!oper2 && left_value.check_type<unsigned>())
749 set_result(evaluate_logical(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
751 else if((oper=='<' || oper=='>') && oper2!=oper)
753 if(left_value.check_type<int>())
754 set_result(evaluate_relation(binary.oper->token, left_value.value<int>(), r_constant_value.value<int>()));
755 else if(left_value.check_type<unsigned>())
756 set_result(evaluate_relation(binary.oper->token, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
757 else if(left_value.check_type<float>())
758 set_result(evaluate_relation(binary.oper->token, left_value.value<float>(), r_constant_value.value<float>()));
760 else if((oper=='=' || oper=='!') && oper2=='=')
762 if(left_value.check_type<int>())
763 set_result((left_value.value<int>()==r_constant_value.value<int>()) == (oper=='='));
764 else if(left_value.check_type<unsigned>())
765 set_result((left_value.value<unsigned>()==r_constant_value.value<unsigned>()) == (oper=='='));
766 else if(left_value.check_type<float>())
767 set_result((left_value.value<float>()==r_constant_value.value<float>()) == (oper=='='));
769 else if(oper=='+' || oper=='-' || oper=='*' || oper=='/')
771 if(left_value.check_type<int>())
772 set_result(evaluate_arithmetic(oper, left_value.value<int>(), r_constant_value.value<int>()));
773 else if(left_value.check_type<unsigned>())
774 set_result(evaluate_arithmetic(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
775 else if(left_value.check_type<float>())
776 set_result(evaluate_arithmetic(oper, left_value.value<float>(), r_constant_value.value<float>()));
778 else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper))
780 if(left_value.check_type<int>())
781 set_result(evaluate_int_special_op(oper, left_value.value<int>(), r_constant_value.value<int>()));
782 else if(left_value.check_type<unsigned>())
783 set_result(evaluate_int_special_op(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
787 void ConstantFolder::visit(Assignment &assign)
789 TraversingVisitor::visit(assign);
793 void ConstantFolder::visit(TernaryExpression &ternary)
795 TraversingVisitor::visit(ternary);
799 void ConstantFolder::visit(FunctionCall &call)
801 if(call.constructor && call.type && call.arguments.size()==1)
803 const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(call.type);
806 visit(call.arguments[0]);
807 bool can_fold = r_constant;
812 if(basic->kind==BasicTypeDeclaration::BOOL)
813 convert_to_result<bool>(r_constant_value);
814 else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && basic->sign)
815 convert_to_result<int>(r_constant_value);
816 else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && !basic->sign)
817 convert_to_result<unsigned>(r_constant_value);
818 else if(basic->kind==BasicTypeDeclaration::FLOAT && basic->size==32)
819 convert_to_result<float>(r_constant_value);
825 TraversingVisitor::visit(call);
829 void ConstantFolder::visit(VariableDeclaration &var)
831 if(iteration_init && var.init_expression)
833 visit(var.init_expression);
836 /* Record the value of a constant initialization expression of an
837 iteration, so it can be used to evaluate the loop condition. */
838 iteration_var = &var;
839 iter_init_value = r_constant_value;
843 TraversingVisitor::visit(var);
846 void ConstantFolder::visit(Iteration &iter)
848 SetForScope<Block *> set_block(current_block, &iter.body);
850 /* The iteration variable is not normally inlined into expressions, so we
851 process it specially here. If the initial value causes the loop condition
852 to evaluate to false, then the expression can be folded. */
854 if(iter.init_statement)
856 SetFlag set_init(iteration_init);
857 iter.init_statement->visit(*this);
862 visit(iter.condition);
863 if(r_constant && r_constant_value.check_type<bool>() && !r_constant_value.value<bool>())
865 RefPtr<Literal> literal = new Literal;
866 literal->token = "false";
867 literal->value = r_constant_value;
868 iter.condition = literal;
873 iter.body.visit(*this);
874 if(iter.loop_expression)
875 visit(iter.loop_expression);
879 void ConstantConditionEliminator::apply(Stage &stage)
881 stage.content.visit(*this);
882 NodeRemover().apply(stage, nodes_to_remove);
885 ConstantConditionEliminator::ConstantStatus ConstantConditionEliminator::check_constant_condition(const Expression &expr)
887 if(const Literal *literal = dynamic_cast<const Literal *>(&expr))
888 if(literal->value.check_type<bool>())
889 return (literal->value.value<bool>() ? CONSTANT_TRUE : CONSTANT_FALSE);
893 void ConstantConditionEliminator::visit(Block &block)
895 SetForScope<Block *> set_block(current_block, &block);
896 for(auto i=block.body.begin(); i!=block.body.end(); ++i)
903 void ConstantConditionEliminator::visit(RefPtr<Expression> &expr)
905 r_ternary_result = 0;
908 expr = r_ternary_result;
909 r_ternary_result = 0;
912 void ConstantConditionEliminator::visit(UnaryExpression &unary)
914 if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
915 if(const VariableReference *var = dynamic_cast<const VariableReference *>(unary.expression.get()))
917 auto i = current_block->variables.find(var->name);
918 r_external_side_effects = (i==current_block->variables.end() || i->second!=var->declaration);
922 TraversingVisitor::visit(unary);
925 void ConstantConditionEliminator::visit(Assignment &assign)
927 auto i = find_if(current_block->variables, [&assign](const pair<string, VariableDeclaration *> &kvp){ return kvp.second==assign.target.declaration; });
928 if(i==current_block->variables.end())
929 r_external_side_effects = true;
930 TraversingVisitor::visit(assign);
933 void ConstantConditionEliminator::visit(TernaryExpression &ternary)
935 ConstantStatus result = check_constant_condition(*ternary.condition);
936 if(result!=NOT_CONSTANT)
937 r_ternary_result = (result==CONSTANT_TRUE ? ternary.true_expr : ternary.false_expr);
939 r_ternary_result = 0;
942 void ConstantConditionEliminator::visit(FunctionCall &call)
944 r_external_side_effects = true;
945 TraversingVisitor::visit(call);
948 void ConstantConditionEliminator::visit(Conditional &cond)
950 ConstantStatus result = check_constant_condition(*cond.condition);
951 if(result!=NOT_CONSTANT)
953 Block &block = (result==CONSTANT_TRUE ? cond.body : cond.else_body);
954 // TODO should check variable names for conflicts. Potentially reuse InlineContentInjector?
955 current_block->body.splice(insert_point, block.body);
956 nodes_to_remove.insert(&cond);
960 r_external_side_effects = false;
961 TraversingVisitor::visit(cond);
963 if(cond.body.body.empty() && cond.else_body.body.empty() && !r_external_side_effects)
964 nodes_to_remove.insert(&cond);
967 void ConstantConditionEliminator::visit(Iteration &iter)
971 ConstantStatus result = check_constant_condition(*iter.condition);
972 if(result==CONSTANT_FALSE)
974 nodes_to_remove.insert(&iter);
979 r_external_side_effects = false;
980 TraversingVisitor::visit(iter);
981 if(iter.body.body.empty() && !r_external_side_effects)
982 nodes_to_remove.insert(&iter);
986 UnreachableCodeRemover::UnreachableCodeRemover():
990 bool UnreachableCodeRemover::apply(Stage &stage)
992 stage.content.visit(*this);
993 NodeRemover().apply(stage, unreachable_nodes);
994 return !unreachable_nodes.empty();
997 void UnreachableCodeRemover::visit(Block &block)
999 auto i = block.body.begin();
1000 for(; (reachable && i!=block.body.end()); ++i)
1002 for(; i!=block.body.end(); ++i)
1003 unreachable_nodes.insert(i->get());
1006 void UnreachableCodeRemover::visit(FunctionDeclaration &func)
1008 TraversingVisitor::visit(func);
1012 void UnreachableCodeRemover::visit(Conditional &cond)
1014 cond.body.visit(*this);
1015 bool reachable_if_true = reachable;
1017 cond.else_body.visit(*this);
1019 reachable |= reachable_if_true;
1022 void UnreachableCodeRemover::visit(Iteration &iter)
1024 TraversingVisitor::visit(iter);
1026 /* Always consider code after a loop reachable, since there's no checking
1027 for whether the loop executes. */
1032 bool UnusedTypeRemover::apply(Stage &stage)
1034 stage.content.visit(*this);
1035 NodeRemover().apply(stage, unused_nodes);
1036 return !unused_nodes.empty();
1039 void UnusedTypeRemover::visit(RefPtr<Expression> &expr)
1041 unused_nodes.erase(expr->type);
1042 TraversingVisitor::visit(expr);
1045 void UnusedTypeRemover::visit(BasicTypeDeclaration &type)
1048 unused_nodes.erase(type.base_type);
1049 unused_nodes.insert(&type);
1052 void UnusedTypeRemover::visit(ImageTypeDeclaration &type)
1055 unused_nodes.erase(type.base_type);
1056 unused_nodes.insert(&type);
1059 void UnusedTypeRemover::visit(StructDeclaration &strct)
1061 unused_nodes.insert(&strct);
1062 TraversingVisitor::visit(strct);
1065 void UnusedTypeRemover::visit(VariableDeclaration &var)
1067 unused_nodes.erase(var.type_declaration);
1068 TraversingVisitor::visit(var);
1071 void UnusedTypeRemover::visit(InterfaceBlock &iface)
1073 unused_nodes.erase(iface.type_declaration);
1076 void UnusedTypeRemover::visit(FunctionDeclaration &func)
1078 unused_nodes.erase(func.return_type_declaration);
1079 TraversingVisitor::visit(func);
1083 UnusedVariableRemover::UnusedVariableRemover():
1087 assignment_target(false),
1088 r_side_effects(false),
1090 composite_reference(false),
1094 bool UnusedVariableRemover::apply(Stage &s)
1097 s.content.visit(*this);
1099 for(const AssignmentInfo &a: assignments)
1100 if(a.used_by.empty())
1101 unused_nodes.insert(a.node);
1103 for(const auto &kvp: variables)
1105 if(kvp.second.output)
1107 /* The last visible assignments of output variables are used by the
1108 next stage or the API. */
1109 for(AssignmentInfo *a: kvp.second.assignments)
1110 unused_nodes.erase(a->node);
1113 if(!kvp.second.output && !kvp.second.referenced)
1115 // Don't remove variables from inside interface blocks.
1116 if(!kvp.second.interface_block)
1117 unused_nodes.insert(kvp.first);
1119 else if(kvp.second.interface_block)
1120 // Interface blocks are kept if even one member is used.
1121 unused_nodes.erase(kvp.second.interface_block);
1124 NodeRemover().apply(s, unused_nodes);
1126 return !unused_nodes.empty();
1129 void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &node)
1131 VariableInfo &var_info = variables[target.declaration];
1132 var_info.referenced = true;
1133 if(!assignment_target)
1135 bool loop_external = false;
1136 for(AssignmentInfo *a: var_info.assignments)
1138 bool covered = true;
1139 for(unsigned j=0; (covered && j<a->target.chain_len && j<target.chain_len); ++j)
1141 Assignment::Target::ChainType type1 = static_cast<Assignment::Target::ChainType>(a->target.chain[j]&0xC0);
1142 Assignment::Target::ChainType type2 = static_cast<Assignment::Target::ChainType>(target.chain[j]&0xC0);
1143 if(type1==Assignment::Target::SWIZZLE || type2==Assignment::Target::SWIZZLE)
1145 unsigned index1 = a->target.chain[j]&0x3F;
1146 unsigned index2 = target.chain[j]&0x3F;
1147 if(type1==Assignment::Target::SWIZZLE && type2==Assignment::Target::SWIZZLE)
1148 covered = index1&index2;
1149 else if(type1==Assignment::Target::ARRAY && index1<4)
1150 covered = index2&(1<<index1);
1151 else if(type2==Assignment::Target::ARRAY && index2<4)
1152 covered = index1&(1<<index2);
1153 /* If it's some other combination (shouldn't happen), leave
1157 covered = (a->target.chain[j]==target.chain[j]);
1162 a->used_by.push_back(&node);
1163 if(a->in_loop<in_loop)
1164 loop_external = true;
1169 loop_ext_refs.push_back(&node);
1173 void UnusedVariableRemover::visit(VariableReference &var)
1175 if(composite_reference)
1176 r_reference.declaration = var.declaration;
1178 referenced(var.declaration, var);
1181 void UnusedVariableRemover::visit(InterfaceBlockReference &iface)
1183 if(composite_reference)
1184 r_reference.declaration = iface.declaration;
1186 referenced(iface.declaration, iface);
1189 void UnusedVariableRemover::visit_composite(Expression &expr)
1191 if(!composite_reference)
1192 r_reference = Assignment::Target();
1194 SetFlag set_composite(composite_reference);
1198 void UnusedVariableRemover::visit(MemberAccess &memacc)
1200 visit_composite(*memacc.left);
1202 add_to_chain(r_reference, Assignment::Target::MEMBER, memacc.index);
1204 if(!composite_reference && r_reference.declaration)
1205 referenced(r_reference, memacc);
1208 void UnusedVariableRemover::visit(Swizzle &swizzle)
1210 visit_composite(*swizzle.left);
1213 for(unsigned i=0; i<swizzle.count; ++i)
1214 mask |= 1<<swizzle.components[i];
1215 add_to_chain(r_reference, Assignment::Target::SWIZZLE, mask);
1217 if(!composite_reference && r_reference.declaration)
1218 referenced(r_reference, swizzle);
1221 void UnusedVariableRemover::visit(UnaryExpression &unary)
1223 TraversingVisitor::visit(unary);
1224 if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
1225 r_side_effects = true;
1228 void UnusedVariableRemover::visit(BinaryExpression &binary)
1230 if(binary.oper->token[0]=='[')
1232 visit_composite(*binary.left);
1235 SetFlag clear_assignment(assignment_target, false);
1236 SetFlag clear_composite(composite_reference, false);
1237 binary.right->visit(*this);
1240 add_to_chain(r_reference, Assignment::Target::ARRAY, 0x3F);
1242 if(!composite_reference && r_reference.declaration)
1243 referenced(r_reference, binary);
1247 SetFlag clear_composite(composite_reference, false);
1248 TraversingVisitor::visit(binary);
1252 void UnusedVariableRemover::visit(TernaryExpression &ternary)
1254 SetFlag clear_composite(composite_reference, false);
1255 TraversingVisitor::visit(ternary);
1258 void UnusedVariableRemover::visit(Assignment &assign)
1261 SetFlag set(assignment_target, (assign.oper->token[0]=='='));
1262 assign.left->visit(*this);
1264 assign.right->visit(*this);
1265 r_assignment = &assign;
1266 r_side_effects = true;
1269 void UnusedVariableRemover::visit(FunctionCall &call)
1271 SetFlag clear_composite(composite_reference, false);
1272 TraversingVisitor::visit(call);
1273 /* Treat function calls as having side effects so expression statements
1274 consisting of nothing but a function call won't be optimized away. */
1275 r_side_effects = true;
1277 if(stage->type==Stage::GEOMETRY && call.name=="EmitVertex")
1279 for(const auto &kvp: variables)
1280 if(kvp.second.output)
1281 referenced(kvp.first, call);
1285 void UnusedVariableRemover::record_assignment(const Assignment::Target &target, Node &node)
1287 assignments.push_back(AssignmentInfo());
1288 AssignmentInfo &assign_info = assignments.back();
1289 assign_info.node = &node;
1290 assign_info.target = target;
1291 assign_info.in_loop = in_loop;
1293 /* An assignment to the target hides any assignments to the same target or
1295 VariableInfo &var_info = variables[target.declaration];
1296 for(unsigned i=0; i<var_info.assignments.size(); )
1298 const Assignment::Target &t = var_info.assignments[i]->target;
1300 bool subfield = (t.chain_len>=target.chain_len);
1301 for(unsigned j=0; (subfield && j<target.chain_len); ++j)
1302 subfield = (t.chain[j]==target.chain[j]);
1305 var_info.assignments.erase(var_info.assignments.begin()+i);
1310 var_info.assignments.push_back(&assign_info);
1313 void UnusedVariableRemover::visit(ExpressionStatement &expr)
1316 r_side_effects = false;
1317 TraversingVisitor::visit(expr);
1318 if(r_assignment && r_assignment->target.declaration)
1319 record_assignment(r_assignment->target, expr);
1321 unused_nodes.insert(&expr);
1324 void UnusedVariableRemover::visit(StructDeclaration &strct)
1326 SetFlag set_struct(in_struct);
1327 TraversingVisitor::visit(strct);
1330 void UnusedVariableRemover::visit(VariableDeclaration &var)
1332 TraversingVisitor::visit(var);
1337 VariableInfo &var_info = variables[&var];
1338 var_info.interface_block = interface_block;
1340 /* Mark variables as output if they're used by the next stage or the
1343 var_info.output = (interface_block->interface=="out" && (interface_block->linked_block || !interface_block->block_name.compare(0, 3, "gl_")));
1345 var_info.output = (var.interface=="out" && (stage->type==Stage::FRAGMENT || var.linked_declaration || !var.name.compare(0, 3, "gl_")));
1347 if(var.init_expression)
1349 var_info.initialized = true;
1350 record_assignment(&var, *var.init_expression);
1354 void UnusedVariableRemover::visit(InterfaceBlock &iface)
1356 VariableInfo &var_info = variables[&iface];
1357 var_info.output = (iface.interface=="out" && (iface.linked_block || !iface.block_name.compare(0, 3, "gl_")));
1360 void UnusedVariableRemover::merge_variables(const BlockVariableMap &other_vars)
1362 for(const auto &kvp: other_vars)
1364 auto j = variables.find(kvp.first);
1365 if(j!=variables.end())
1367 /* The merged blocks started as copies of each other so any common
1368 assignments must be in the beginning. */
1370 for(; (k<kvp.second.assignments.size() && k<j->second.assignments.size()); ++k)
1371 if(kvp.second.assignments[k]!=j->second.assignments[k])
1374 // Remaining assignments are unique to each block; merge them.
1375 j->second.assignments.insert(j->second.assignments.end(), kvp.second.assignments.begin()+k, kvp.second.assignments.end());
1376 j->second.referenced |= kvp.second.referenced;
1379 variables.insert(kvp);
1383 void UnusedVariableRemover::visit(FunctionDeclaration &func)
1385 if(func.body.body.empty())
1388 BlockVariableMap saved_vars = variables;
1389 // Assignments from other functions should not be visible.
1390 for(auto &kvp: variables)
1391 kvp.second.assignments.resize(kvp.second.initialized);
1392 TraversingVisitor::visit(func);
1393 swap(variables, saved_vars);
1394 merge_variables(saved_vars);
1396 /* Always treat function parameters as referenced. Removing unused
1397 parameters is not currently supported. */
1398 for(const RefPtr<VariableDeclaration> &p: func.parameters)
1400 auto j = variables.find(p.get());
1401 if(j!=variables.end())
1402 j->second.referenced = true;
1406 void UnusedVariableRemover::visit(Conditional &cond)
1408 cond.condition->visit(*this);
1409 BlockVariableMap saved_vars = variables;
1410 cond.body.visit(*this);
1411 swap(saved_vars, variables);
1412 cond.else_body.visit(*this);
1414 /* Visible assignments after the conditional is the union of those visible
1415 at the end of the if and else blocks. If there was no else block, then it's
1416 the union of the if block and the state before it. */
1417 merge_variables(saved_vars);
1420 void UnusedVariableRemover::visit(Iteration &iter)
1422 BlockVariableMap saved_vars = variables;
1423 vector<Node *> saved_refs;
1424 swap(loop_ext_refs, saved_refs);
1426 SetForScope<unsigned> set_loop(in_loop, in_loop+1);
1427 TraversingVisitor::visit(iter);
1429 swap(loop_ext_refs, saved_refs);
1431 /* Visit the external references of the loop again to record assignments
1432 done in the loop as used. */
1433 for(Node *n: saved_refs)
1436 /* Merge assignments from the iteration, without clearing previous state.
1437 Further analysis is needed to determine which parts of the iteration body
1438 are always executed, if any. */
1439 merge_variables(saved_vars);
1443 bool UnusedFunctionRemover::apply(Stage &stage)
1445 stage.content.visit(*this);
1446 NodeRemover().apply(stage, unused_nodes);
1447 return !unused_nodes.empty();
1450 void UnusedFunctionRemover::visit(FunctionCall &call)
1452 TraversingVisitor::visit(call);
1454 unused_nodes.erase(call.declaration);
1455 if(call.declaration && call.declaration->definition!=call.declaration)
1456 used_definitions.insert(call.declaration->definition);
1459 void UnusedFunctionRemover::visit(FunctionDeclaration &func)
1461 TraversingVisitor::visit(func);
1463 if((func.name!="main" || func.body.body.empty()) && !used_definitions.count(&func))
1464 unused_nodes.insert(&func);