]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/optimize.cpp
Transform interface block contents into structs
[libs/gl.git] / source / glsl / optimize.cpp
1 #include <msp/core/raii.h>
2 #include <msp/strings/format.h>
3 #include "optimize.h"
4
5 using namespace std;
6
7 namespace Msp {
8 namespace GL {
9 namespace SL {
10
11 InlineableFunctionLocator::InlineableFunctionLocator():
12         current_function(0),
13         return_count(0)
14 { }
15
16 void InlineableFunctionLocator::visit(FunctionCall &call)
17 {
18         FunctionDeclaration *def = call.declaration;
19         if(def)
20                 def = def->definition;
21
22         if(def)
23         {
24                 unsigned &count = refcounts[def];
25                 ++count;
26                 /* Don't inline functions which are called more than once or are called
27                 recursively. */
28                 if(count>1 || def==current_function)
29                         inlineable.erase(def);
30         }
31
32         TraversingVisitor::visit(call);
33 }
34
35 void InlineableFunctionLocator::visit(FunctionDeclaration &func)
36 {
37         unsigned &count = refcounts[func.definition];
38         if(count<=1 && func.parameters.empty())
39                 inlineable.insert(func.definition);
40
41         SetForScope<FunctionDeclaration *> set(current_function, &func);
42         return_count = 0;
43         TraversingVisitor::visit(func);
44 }
45
46 void InlineableFunctionLocator::visit(Conditional &cond)
47 {
48         TraversingVisitor::visit(cond);
49         inlineable.erase(current_function);
50 }
51
52 void InlineableFunctionLocator::visit(Iteration &iter)
53 {
54         TraversingVisitor::visit(iter);
55         inlineable.erase(current_function);
56 }
57
58 void InlineableFunctionLocator::visit(Return &ret)
59 {
60         TraversingVisitor::visit(ret);
61         if(return_count)
62                 inlineable.erase(current_function);
63         ++return_count;
64 }
65
66
67 InlineContentInjector::InlineContentInjector():
68         source_func(0),
69         remap_names(false),
70         deps_only(false)
71 { }
72
73 const string &InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList<Statement>::iterator &ins_pt, FunctionDeclaration &src)
74 {
75         target_block = &tgt_blk;
76         source_func = &src;
77         for(NodeList<Statement>::iterator i=src.body.body.begin(); i!=src.body.body.end(); ++i)
78         {
79                 r_inlined_statement = 0;
80                 (*i)->visit(*this);
81                 if(!r_inlined_statement)
82                         r_inlined_statement = (*i)->clone();
83
84                 SetFlag set_remap(remap_names);
85                 r_inlined_statement->visit(*this);
86                 tgt_blk.body.insert(ins_pt, r_inlined_statement);
87         }
88
89         NodeReorderer().apply(stage, target_func, dependencies);
90
91         return r_result_name;
92 }
93
94 string InlineContentInjector::create_unused_name(const string &base, bool always_prefix)
95 {
96         string result = base;
97         if(always_prefix || target_block->variables.count(result))
98                 result = format("_%s_%s", source_func->name, base);
99         unsigned initial_size = result.size();
100         for(unsigned i=1; target_block->variables.count(result); ++i)
101         {
102                 result.erase(initial_size);
103                 result += format("_%d", i);
104         }
105         return result;
106 }
107
108 void InlineContentInjector::visit(VariableReference &var)
109 {
110         if(remap_names)
111         {
112                 map<string, VariableDeclaration *>::const_iterator i = variable_map.find(var.name);
113                 if(i!=variable_map.end())
114                         var.name = i->second->name;
115         }
116         else if(var.declaration)
117         {
118                 SetFlag set_deps(deps_only);
119                 dependencies.insert(var.declaration);
120                 var.declaration->visit(*this);
121         }
122 }
123
124 void InlineContentInjector::visit(InterfaceBlockReference &iface)
125 {
126         if(!remap_names && iface.declaration)
127         {
128                 SetFlag set_deps(deps_only);
129                 dependencies.insert(iface.declaration);
130                 iface.declaration->visit(*this);
131         }
132 }
133
134 void InlineContentInjector::visit(FunctionCall &call)
135 {
136         if(!remap_names && call.declaration)
137                 dependencies.insert(call.declaration);
138         TraversingVisitor::visit(call);
139 }
140
141 void InlineContentInjector::visit(VariableDeclaration &var)
142 {
143         TraversingVisitor::visit(var);
144
145         if(var.type_declaration)
146         {
147                 SetFlag set_deps(deps_only);
148                 dependencies.insert(var.type_declaration);
149                 var.type_declaration->visit(*this);
150         }
151
152         if(!remap_names && !deps_only)
153         {
154                 RefPtr<VariableDeclaration> inlined_var = var.clone();
155                 inlined_var->name = create_unused_name(var.name, false);
156                 r_inlined_statement = inlined_var;
157
158                 variable_map[var.name] = inlined_var.get();
159         }
160 }
161
162 void InlineContentInjector::visit(Return &ret)
163 {
164         TraversingVisitor::visit(ret);
165
166         if(ret.expression)
167         {
168                 /* Create a new variable to hold the return value of the inlined
169                 function. */
170                 r_result_name = create_unused_name("return", true);
171                 RefPtr<VariableDeclaration> var = new VariableDeclaration;
172                 var->source = ret.source;
173                 var->line = ret.line;
174                 var->type = source_func->return_type;
175                 var->name = r_result_name;
176                 var->init_expression = ret.expression->clone();
177                 r_inlined_statement = var;
178         }
179 }
180
181
182 FunctionInliner::FunctionInliner():
183         current_function(0),
184         r_any_inlined(false)
185 { }
186
187 bool FunctionInliner::apply(Stage &s)
188 {
189         stage = &s;
190         inlineable = InlineableFunctionLocator().apply(s);
191         r_any_inlined = false;
192         s.content.visit(*this);
193         return r_any_inlined;
194 }
195
196 void FunctionInliner::visit_and_inline(RefPtr<Expression> &ptr)
197 {
198         r_inline_result = 0;
199         ptr->visit(*this);
200         if(r_inline_result)
201         {
202                 ptr = r_inline_result;
203                 r_any_inlined = true;
204         }
205         r_inline_result = 0;
206 }
207
208 void FunctionInliner::visit(Block &block)
209 {
210         SetForScope<Block *> set_block(current_block, &block);
211         SetForScope<NodeList<Statement>::iterator> save_insert_point(insert_point, block.body.begin());
212         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
213         {
214                 insert_point = i;
215                 (*i)->visit(*this);
216         }
217 }
218
219 void FunctionInliner::visit(UnaryExpression &unary)
220 {
221         visit_and_inline(unary.expression);
222 }
223
224 void FunctionInliner::visit(BinaryExpression &binary)
225 {
226         visit_and_inline(binary.left);
227         visit_and_inline(binary.right);
228 }
229
230 void FunctionInliner::visit(MemberAccess &memacc)
231 {
232         visit_and_inline(memacc.left);
233 }
234
235 void FunctionInliner::visit(FunctionCall &call)
236 {
237         for(NodeArray<Expression>::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
238                 visit_and_inline(*i);
239
240         FunctionDeclaration *def = call.declaration;
241         if(def)
242                 def = def->definition;
243
244         if(def && inlineable.count(def))
245         {
246                 string result_name = InlineContentInjector().apply(*stage, *current_function, *current_block, insert_point, *def);
247
248                 // This will later get removed by UnusedVariableRemover.
249                 if(result_name.empty())
250                         result_name = "msp_unused_from_inline";
251
252                 RefPtr<VariableReference> ref = new VariableReference;
253                 ref->name = result_name;
254                 r_inline_result = ref;
255
256                 /* Inlined variables need to be resolved before this function can be
257                 inlined further. */
258                 inlineable.erase(current_function);
259         }
260 }
261
262 void FunctionInliner::visit(ExpressionStatement &expr)
263 {
264         visit_and_inline(expr.expression);
265 }
266
267 void FunctionInliner::visit(VariableDeclaration &var)
268 {
269         if(var.init_expression)
270                 visit_and_inline(var.init_expression);
271 }
272
273 void FunctionInliner::visit(FunctionDeclaration &func)
274 {
275         SetForScope<FunctionDeclaration *> set_func(current_function, &func);
276         TraversingVisitor::visit(func);
277 }
278
279 void FunctionInliner::visit(Conditional &cond)
280 {
281         visit_and_inline(cond.condition);
282         cond.body.visit(*this);
283 }
284
285 void FunctionInliner::visit(Iteration &iter)
286 {
287         /* Visit the initialization statement before entering the loop body so the
288         inlined statements get inserted outside. */
289         if(iter.init_statement)
290                 iter.init_statement->visit(*this);
291
292         SetForScope<Block *> set_block(current_block, &iter.body);
293         /* Skip the condition and loop expression parts because they're not properly
294         inside the body block.  Inlining anything into them will require a more
295         comprehensive transformation. */
296         iter.body.visit(*this);
297 }
298
299 void FunctionInliner::visit(Return &ret)
300 {
301         if(ret.expression)
302                 visit_and_inline(ret.expression);
303 }
304
305
306 ExpressionInliner::ExpressionInfo::ExpressionInfo():
307         expression(0),
308         assign_scope(0),
309         inline_point(0),
310         inner_oper(0),
311         outer_oper(0),
312         inline_on_rhs(false),
313         trivial(false),
314         available(true)
315 { }
316
317
318 ExpressionInliner::ExpressionInliner():
319         r_ref_info(0),
320         r_any_inlined(false),
321         r_trivial(false),
322         mutating(false),
323         iteration_init(false),
324         iteration_body(0),
325         r_oper(0)
326 { }
327
328 bool ExpressionInliner::apply(Stage &s)
329 {
330         s.content.visit(*this);
331         return r_any_inlined;
332 }
333
334 void ExpressionInliner::visit_and_record(RefPtr<Expression> &ptr, const Operator *outer_oper, bool on_rhs)
335 {
336         r_ref_info = 0;
337         ptr->visit(*this);
338         if(r_ref_info && r_ref_info->expression && r_ref_info->available)
339         {
340                 if(iteration_body && !r_ref_info->trivial)
341                 {
342                         /* Don't inline non-trivial expressions which were assigned outside
343                         an iteration statement.  The iteration may run multiple times, which
344                         would cause the expression to also be evaluated multiple times. */
345                         Block *i = r_ref_info->assign_scope;
346                         for(; (i && i!=iteration_body); i=i->parent) ;
347                         if(!i)
348                                 return;
349                 }
350
351                 r_ref_info->outer_oper = outer_oper;
352                 if(r_ref_info->trivial)
353                         inline_expression(*r_ref_info->expression, ptr, outer_oper, r_ref_info->inner_oper, on_rhs);
354                 else
355                 {
356                         /* Record the inline point for a non-trivial expression but don't
357                         inline it yet.  It might turn out it shouldn't be inlined after all. */
358                         r_ref_info->inline_point = &ptr;
359                         r_ref_info->inline_on_rhs = on_rhs;
360                 }
361         }
362         r_ref_info = 0;
363 }
364
365 void ExpressionInliner::inline_expression(Expression &expr, RefPtr<Expression> &ptr, const Operator *outer_oper, const Operator *inner_oper, bool on_rhs)
366 {
367         unsigned outer_precedence = (outer_oper ? outer_oper->precedence : 20);
368         unsigned inner_precedence = (inner_oper ? inner_oper->precedence : 0);
369
370         bool needs_parentheses = (inner_precedence>=outer_precedence);
371         if(inner_oper && inner_oper==outer_oper)
372                 // Omit parentheses if the operator's natural grouping works out.
373                 needs_parentheses = (inner_oper->assoc!=Operator::ASSOCIATIVE && on_rhs!=(inner_oper->assoc==Operator::RIGHT_TO_LEFT));
374
375         if(needs_parentheses)
376         {
377                 RefPtr<ParenthesizedExpression> parexpr = new ParenthesizedExpression;
378                 parexpr->expression = expr.clone();
379                 ptr = parexpr;
380         }
381         else
382                 ptr = expr.clone();
383
384         r_any_inlined = true;
385 }
386
387 void ExpressionInliner::visit(Block &block)
388 {
389         TraversingVisitor::visit(block);
390
391         for(map<VariableDeclaration *, ExpressionInfo>::iterator i=expressions.begin(); i!=expressions.end(); )
392         {
393                 map<string, VariableDeclaration *>::iterator j = block.variables.find(i->first->name);
394                 if(j!=block.variables.end() && j->second==i->first)
395                 {
396                         if(i->second.expression && i->second.inline_point)
397                                 inline_expression(*i->second.expression, *i->second.inline_point, i->second.outer_oper, i->second.inner_oper, i->second.inline_on_rhs);
398
399                         expressions.erase(i++);
400                 }
401                 else
402                 {
403                         /* The expression was assigned in this block and may depend on local
404                         variables of the block.  If this is a conditionally executed block,
405                         the assignment might not always happen.  Mark the expression as not
406                         available to any outer blocks. */
407                         if(i->second.assign_scope==&block)
408                                 i->second.available = false;
409
410                         ++i;
411                 }
412         }
413 }
414
415 void ExpressionInliner::visit(VariableReference &var)
416 {
417         if(var.declaration)
418         {
419                 map<VariableDeclaration *, ExpressionInfo>::iterator i = expressions.find(var.declaration);
420                 if(i!=expressions.end())
421                 {
422                         /* If a non-trivial expression is referenced multiple times, don't
423                         inline it. */
424                         if(i->second.inline_point && !i->second.trivial)
425                                 i->second.expression = 0;
426                         /* Mutating expressions are analogous to self-referencing assignments
427                         and prevent inlining. */
428                         if(mutating)
429                                 i->second.expression = 0;
430                         r_ref_info = &i->second;
431                 }
432         }
433 }
434
435 void ExpressionInliner::visit(MemberAccess &memacc)
436 {
437         visit_and_record(memacc.left, memacc.oper, false);
438         r_oper = memacc.oper;
439         r_trivial = false;
440 }
441
442 void ExpressionInliner::visit(UnaryExpression &unary)
443 {
444         SetFlag set_target(mutating, mutating || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
445         visit_and_record(unary.expression, unary.oper, false);
446         r_oper = unary.oper;
447         r_trivial = false;
448 }
449
450 void ExpressionInliner::visit(BinaryExpression &binary)
451 {
452         visit_and_record(binary.left, binary.oper, false);
453         {
454                 SetFlag clear_target(mutating, false);
455                 visit_and_record(binary.right, binary.oper, true);
456         }
457         r_oper = binary.oper;
458         r_trivial = false;
459 }
460
461 void ExpressionInliner::visit(Assignment &assign)
462 {
463         {
464                 SetFlag set_target(mutating);
465                 visit_and_record(assign.left, assign.oper, false);
466         }
467         r_oper = 0;
468         visit_and_record(assign.right, assign.oper, true);
469
470         if(assign.target_declaration)
471         {
472                 map<VariableDeclaration *, ExpressionInfo>::iterator i = expressions.find(assign.target_declaration);
473                 if(i!=expressions.end())
474                 {
475                         /* Self-referencing assignments can't be inlined without additional
476                         work.  Just clear any previous expression. */
477                         i->second.expression = (assign.self_referencing ? 0 : assign.right.get());
478                         i->second.assign_scope = current_block;
479                         i->second.inline_point = 0;
480                         i->second.inner_oper = r_oper;
481                         i->second.available = true;
482                 }
483         }
484
485         r_oper = assign.oper;
486         r_trivial = false;
487 }
488
489 void ExpressionInliner::visit(FunctionCall &call)
490 {
491         for(NodeArray<Expression>::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
492                 visit_and_record(*i, 0, false);
493         r_oper = 0;
494         r_trivial = false;
495 }
496
497 void ExpressionInliner::visit(VariableDeclaration &var)
498 {
499         r_oper = 0;
500         r_trivial = true;
501         if(var.init_expression)
502                 visit_and_record(var.init_expression, 0, false);
503
504         bool constant = var.constant;
505         if(constant && var.layout)
506         {
507                 for(vector<Layout::Qualifier>::const_iterator i=var.layout->qualifiers.begin(); (constant && i!=var.layout->qualifiers.end()); ++i)
508                         constant = (i->name!="constant_id");
509         }
510
511         /* Only inline global variables if they're constant and have trivial
512         initializers.  Non-constant variables could change in ways which are hard to
513         analyze and non-trivial expressions could be expensive to inline.  */
514         if((current_block->parent || (constant && r_trivial)) && var.interface.empty())
515         {
516                 ExpressionInfo &info = expressions[&var];
517                 /* Assume variables declared in an iteration initialization statement
518                 will have their values change throughout the iteration. */
519                 info.expression = (iteration_init ? 0 : var.init_expression.get());
520                 info.assign_scope = current_block;
521                 info.inner_oper = r_oper;
522                 info.trivial = r_trivial;
523         }
524 }
525
526 void ExpressionInliner::visit(Conditional &cond)
527 {
528         visit_and_record(cond.condition, 0, false);
529         cond.body.visit(*this);
530 }
531
532 void ExpressionInliner::visit(Iteration &iter)
533 {
534         SetForScope<Block *> set_block(current_block, &iter.body);
535         if(iter.init_statement)
536         {
537                 SetFlag set_init(iteration_init);
538                 iter.init_statement->visit(*this);
539         }
540
541         SetForScope<Block *> set_body(iteration_body, &iter.body);
542         if(iter.condition)
543                 iter.condition->visit(*this);
544         iter.body.visit(*this);
545         if(iter.loop_expression)
546                 iter.loop_expression->visit(*this);
547 }
548
549 void ExpressionInliner::visit(Return &ret)
550 {
551         if(ret.expression)
552                 visit_and_record(ret.expression, 0, false);
553 }
554
555
556 void ConstantConditionEliminator::apply(Stage &stage)
557 {
558         stage.content.visit(*this);
559         NodeRemover().apply(stage, nodes_to_remove);
560 }
561
562 void ConstantConditionEliminator::visit(Block &block)
563 {
564         SetForScope<Block *> set_block(current_block, &block);
565         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
566         {
567                 insert_point = i;
568                 (*i)->visit(*this);
569         }
570 }
571
572 void ConstantConditionEliminator::visit(Conditional &cond)
573 {
574         ExpressionEvaluator eval;
575         cond.condition->visit(eval);
576         if(eval.is_result_valid())
577         {
578                 Block &block = (eval.get_result() ? cond.body : cond.else_body);
579                 current_block->body.splice(insert_point, block.body);
580                 nodes_to_remove.insert(&cond);
581                 return;
582         }
583
584         TraversingVisitor::visit(cond);
585 }
586
587 void ConstantConditionEliminator::visit(Iteration &iter)
588 {
589         if(iter.condition)
590         {
591                 /* If the loop condition is always false on the first iteration, the
592                 entire loop can be removed */
593                 ExpressionEvaluator::ValueMap values;
594                 if(VariableDeclaration *var = dynamic_cast<VariableDeclaration *>(iter.init_statement.get()))
595                         values[var] = var->init_expression.get();
596                 ExpressionEvaluator eval(values);
597                 iter.condition->visit(eval);
598                 if(eval.is_result_valid() && !eval.get_result())
599                 {
600                         nodes_to_remove.insert(&iter);
601                         return;
602                 }
603         }
604
605         TraversingVisitor::visit(iter);
606 }
607
608
609 UnusedVariableRemover::VariableInfo::VariableInfo():
610         local(false),
611         conditionally_assigned(false),
612         referenced(false)
613 { }
614
615
616 bool UnusedTypeRemover::apply(Stage &stage)
617 {
618         stage.content.visit(*this);
619         NodeRemover().apply(stage, unused_nodes);
620         return !unused_nodes.empty();
621 }
622
623 void UnusedTypeRemover::visit(Literal &literal)
624 {
625         unused_nodes.erase(literal.type);
626 }
627
628 void UnusedTypeRemover::visit(UnaryExpression &unary)
629 {
630         unused_nodes.erase(unary.type);
631         TraversingVisitor::visit(unary);
632 }
633
634 void UnusedTypeRemover::visit(BinaryExpression &binary)
635 {
636         unused_nodes.erase(binary.type);
637         TraversingVisitor::visit(binary);
638 }
639
640 void UnusedTypeRemover::visit(FunctionCall &call)
641 {
642         unused_nodes.erase(call.type);
643         TraversingVisitor::visit(call);
644 }
645
646 void UnusedTypeRemover::visit(BasicTypeDeclaration &type)
647 {
648         if(type.base_type)
649                 unused_nodes.erase(type.base_type);
650         unused_nodes.insert(&type);
651 }
652
653 void UnusedTypeRemover::visit(ImageTypeDeclaration &type)
654 {
655         if(type.base_type)
656                 unused_nodes.erase(type.base_type);
657         unused_nodes.insert(&type);
658 }
659
660 void UnusedTypeRemover::visit(StructDeclaration &strct)
661 {
662         unused_nodes.insert(&strct);
663         TraversingVisitor::visit(strct);
664 }
665
666 void UnusedTypeRemover::visit(VariableDeclaration &var)
667 {
668         unused_nodes.erase(var.type_declaration);
669 }
670
671 void UnusedTypeRemover::visit(InterfaceBlock &iface)
672 {
673         unused_nodes.erase(iface.type_declaration);
674 }
675
676 void UnusedTypeRemover::visit(FunctionDeclaration &func)
677 {
678         unused_nodes.erase(func.return_type_declaration);
679         TraversingVisitor::visit(func);
680 }
681
682
683 UnusedVariableRemover::UnusedVariableRemover():
684         aggregate(0),
685         r_assignment(0),
686         assignment_target(false),
687         r_assign_to_subfield(false),
688         r_side_effects(false)
689 { }
690
691 bool UnusedVariableRemover::apply(Stage &stage)
692 {
693         variables.push_back(BlockVariableMap());
694         stage.content.visit(*this);
695         BlockVariableMap &global_variables = variables.back();
696         for(BlockVariableMap::iterator i=global_variables.begin(); i!=global_variables.end(); ++i)
697         {
698                 string interface = i->first->interface;
699                 bool linked = i->first->linked_declaration;
700                 map<VariableDeclaration *, Node *>::iterator j = aggregates.find(i->first);
701                 if(j!=aggregates.end())
702                         if(InterfaceBlock *iface = dynamic_cast<InterfaceBlock *>(j->second))
703                         {
704                                 interface = iface->interface;
705                                 linked = iface->linked_block;
706                         }
707
708                 /* Don't remove output variables which are used by the next stage or the
709                 graphics API. */
710                 if(interface=="out" && (stage.type==Stage::FRAGMENT || linked || !i->first->name.compare(0, 3, "gl_")))
711                         continue;
712
713                 // Mark other unreferenced global variables as unused.
714                 if(!i->second.referenced)
715                 {
716                         unused_nodes.insert(i->first);
717                         clear_assignments(i->second, true);
718                 }
719         }
720         variables.pop_back();
721
722         NodeRemover().apply(stage, unused_nodes);
723
724         return !unused_nodes.empty();
725 }
726
727 void UnusedVariableRemover::visit(VariableReference &var)
728 {
729         map<VariableDeclaration *, Node *>::iterator i = aggregates.find(var.declaration);
730         if(i!=aggregates.end())
731                 unused_nodes.erase(i->second);
732
733         if(var.declaration && !assignment_target)
734         {
735                 VariableInfo &var_info = variables.back()[var.declaration];
736                 // Previous assignments are used by this reference.
737                 clear_assignments(var_info, false);
738                 var_info.referenced = true;
739         }
740 }
741
742 void UnusedVariableRemover::visit(InterfaceBlockReference &iface)
743 {
744         unused_nodes.erase(iface.declaration);
745 }
746
747 void UnusedVariableRemover::visit(MemberAccess &memacc)
748 {
749         if(assignment_target)
750                 r_assign_to_subfield = true;
751         TraversingVisitor::visit(memacc);
752         unused_nodes.erase(memacc.declaration);
753 }
754
755 void UnusedVariableRemover::visit(UnaryExpression &unary)
756 {
757         TraversingVisitor::visit(unary);
758         if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
759                 r_side_effects = true;
760 }
761
762 void UnusedVariableRemover::visit(BinaryExpression &binary)
763 {
764         if(binary.oper->token[0]=='[')
765         {
766                 if(assignment_target)
767                         r_assign_to_subfield = true;
768                 binary.left->visit(*this);
769                 SetFlag set(assignment_target, false);
770                 binary.right->visit(*this);
771         }
772         else
773                 TraversingVisitor::visit(binary);
774 }
775
776 void UnusedVariableRemover::visit(Assignment &assign)
777 {
778         {
779                 SetFlag set(assignment_target, !assign.self_referencing);
780                 assign.left->visit(*this);
781         }
782         assign.right->visit(*this);
783         r_assignment = &assign;
784         r_side_effects = true;
785 }
786
787 void UnusedVariableRemover::visit(FunctionCall &call)
788 {
789         TraversingVisitor::visit(call);
790         /* Treat function calls as having side effects so expression statements
791         consisting of nothing but a function call won't be optimized away. */
792         r_side_effects = true;
793 }
794
795 void UnusedVariableRemover::record_assignment(VariableDeclaration &var, Node &node, bool chained)
796 {
797         VariableInfo &var_info = variables.back()[&var];
798         /* An assignment which completely replaces the value of the variable causes
799         any previous unreferenced assignments to be unused. */
800         if(!chained)
801                 clear_assignments(var_info, true);
802         var_info.assignments.push_back(&node);
803         var_info.conditionally_assigned = false;
804 }
805
806 void UnusedVariableRemover::clear_assignments(VariableInfo &var_info, bool mark_unused)
807 {
808         if(mark_unused)
809         {
810                 for(vector<Node *>::iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i)
811                         unused_nodes.insert(*i);
812         }
813         var_info.assignments.clear();
814 }
815
816 void UnusedVariableRemover::visit(ExpressionStatement &expr)
817 {
818         r_assignment = 0;
819         r_assign_to_subfield = false;
820         r_side_effects = false;
821         TraversingVisitor::visit(expr);
822         if(r_assignment && r_assignment->target_declaration)
823                 record_assignment(*r_assignment->target_declaration, expr, (r_assignment->self_referencing || r_assign_to_subfield));
824         if(!r_side_effects)
825                 unused_nodes.insert(&expr);
826 }
827
828 void UnusedVariableRemover::visit(StructDeclaration &strct)
829 {
830         SetForScope<Node *> set(aggregate, &strct);
831         TraversingVisitor::visit(strct);
832 }
833
834 void UnusedVariableRemover::visit(VariableDeclaration &var)
835 {
836         if(aggregate)
837                 aggregates[&var] = aggregate;
838         else
839         {
840                 variables.back()[&var].local = true;
841                 if(var.init_expression)
842                         record_assignment(var, *var.init_expression, false);
843         }
844         TraversingVisitor::visit(var);
845 }
846
847 void UnusedVariableRemover::visit(InterfaceBlock &iface)
848 {
849         SetForScope<Node *> set(aggregate, &iface);
850         unused_nodes.insert(&iface);
851         iface.struct_declaration->members.visit(*this);
852 }
853
854 void UnusedVariableRemover::visit(FunctionDeclaration &func)
855 {
856         variables.push_back(BlockVariableMap());
857
858         for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
859                 (*i)->visit(*this);
860         func.body.visit(*this);
861
862         BlockVariableMap &block_variables = variables.back();
863
864         /* Mark global variables as conditionally assigned so assignments in other
865         functions won't be removed. */
866         for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
867                 if(!i->second.local)
868                         i->second.conditionally_assigned = true;
869
870         /* Always treat function parameters as referenced.  Removing unused
871         parameters is not currently supported. */
872         for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
873                 block_variables[i->get()].referenced = true;
874
875         merge_down_variables();
876 }
877
878 void UnusedVariableRemover::merge_down_variables()
879 {
880         BlockVariableMap &parent_variables = variables[variables.size()-2];
881         BlockVariableMap &block_variables = variables.back();
882         for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
883         {
884                 if(i->second.local)
885                 {
886                         if(!i->second.referenced)
887                                 unused_nodes.insert(i->first);
888                         /* Any unreferenced assignments when a variable runs out of scope
889                         become unused. */
890                         clear_assignments(i->second, true);
891                         continue;
892                 }
893
894                 BlockVariableMap::iterator j = parent_variables.find(i->first);
895                 if(j==parent_variables.end())
896                         parent_variables.insert(*i);
897                 else
898                 {
899                         // Merge a non-local variable's state into the parent scope.
900                         if(i->second.referenced || !i->second.conditionally_assigned)
901                                 clear_assignments(j->second, !i->second.referenced);
902                         j->second.conditionally_assigned = i->second.conditionally_assigned;
903                         j->second.referenced |= i->second.referenced;
904                         j->second.assignments.insert(j->second.assignments.end(), i->second.assignments.begin(), i->second.assignments.end());
905                 }
906         }
907         variables.pop_back();
908 }
909
910 void UnusedVariableRemover::visit(Conditional &cond)
911 {
912         cond.condition->visit(*this);
913         variables.push_back(BlockVariableMap());
914         cond.body.visit(*this);
915
916         BlockVariableMap if_variables;
917         swap(variables.back(), if_variables);
918         cond.else_body.visit(*this);
919
920         // Combine variables from both branches.
921         BlockVariableMap &else_variables = variables.back();
922         for(BlockVariableMap::iterator i=else_variables.begin(); i!=else_variables.end(); ++i)
923         {
924                 BlockVariableMap::iterator j = if_variables.find(i->first);
925                 if(j!=if_variables.end())
926                 {
927                         // The variable was found in both branches.
928                         i->second.assignments.insert(i->second.assignments.end(), j->second.assignments.begin(), j->second.assignments.end());
929                         i->second.conditionally_assigned |= j->second.conditionally_assigned;
930                         if_variables.erase(j);
931                 }
932                 else
933                         // Mark variables found in only one branch as conditionally assigned.
934                         i->second.conditionally_assigned = true;
935         }
936
937         /* Move variables which were only used in the if block into the combined
938         block. */
939         for(BlockVariableMap::iterator i=if_variables.begin(); i!=if_variables.end(); ++i)
940         {
941                 i->second.conditionally_assigned = true;
942                 else_variables.insert(*i);
943         }
944
945         merge_down_variables();
946 }
947
948 void UnusedVariableRemover::visit(Iteration &iter)
949 {
950         variables.push_back(BlockVariableMap());
951         TraversingVisitor::visit(iter);
952         merge_down_variables();
953 }
954
955
956 bool UnusedFunctionRemover::apply(Stage &stage)
957 {
958         stage.content.visit(*this);
959         NodeRemover().apply(stage, unused_nodes);
960         return !unused_nodes.empty();
961 }
962
963 void UnusedFunctionRemover::visit(FunctionCall &call)
964 {
965         TraversingVisitor::visit(call);
966
967         unused_nodes.erase(call.declaration);
968         if(call.declaration && call.declaration->definition!=call.declaration)
969                 used_definitions.insert(call.declaration->definition);
970 }
971
972 void UnusedFunctionRemover::visit(FunctionDeclaration &func)
973 {
974         TraversingVisitor::visit(func);
975
976         if((func.name!="main" || func.body.body.empty()) && !used_definitions.count(&func))
977                 unused_nodes.insert(&func);
978 }
979
980 } // namespace SL
981 } // namespace GL
982 } // namespace Msp