]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/optimize.cpp
Fold type conversions of constants
[libs/gl.git] / source / glsl / optimize.cpp
1 #include <msp/core/raii.h>
2 #include <msp/strings/format.h>
3 #include <msp/strings/utils.h>
4 #include "optimize.h"
5 #include "reflect.h"
6
7 using namespace std;
8
9 namespace Msp {
10 namespace GL {
11 namespace SL {
12
13 ConstantSpecializer::ConstantSpecializer():
14         values(0)
15 { }
16
17 void ConstantSpecializer::apply(Stage &stage, const map<string, int> &v)
18 {
19         values = &v;
20         stage.content.visit(*this);
21 }
22
23 void ConstantSpecializer::visit(VariableDeclaration &var)
24 {
25         bool specializable = false;
26         if(var.layout)
27         {
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")
31                         {
32                                 specializable = true;
33                                 qualifiers.erase(i);
34                         }
35
36                 if(qualifiers.empty())
37                         var.layout = 0;
38         }
39
40         if(specializable)
41         {
42                 map<string, int>::const_iterator i = values->find(var.name);
43                 if(i!=values->end())
44                 {
45                         RefPtr<Literal> literal = new Literal;
46                         if(var.type=="bool")
47                         {
48                                 literal->token = (i->second ? "true" : "false");
49                                 literal->value = static_cast<bool>(i->second);
50                         }
51                         else if(var.type=="int")
52                         {
53                                 literal->token = lexical_cast<string>(i->second);
54                                 literal->value = i->second;
55                         }
56                         var.init_expression = literal;
57                 }
58         }
59 }
60
61
62 InlineableFunctionLocator::InlineableFunctionLocator():
63         current_function(0),
64         return_count(0)
65 { }
66
67 void InlineableFunctionLocator::visit(FunctionCall &call)
68 {
69         FunctionDeclaration *def = call.declaration;
70         if(def)
71                 def = def->definition;
72
73         if(def)
74         {
75                 unsigned &count = refcounts[def];
76                 ++count;
77                 /* Don't inline functions which are called more than once or are called
78                 recursively. */
79                 if((count>1 && def->source!=BUILTIN_SOURCE) || def==current_function)
80                         inlineable.erase(def);
81         }
82
83         TraversingVisitor::visit(call);
84 }
85
86 void InlineableFunctionLocator::visit(FunctionDeclaration &func)
87 {
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");
91
92         unsigned &count = refcounts[func.definition];
93         if((count<=1 || func.source==BUILTIN_SOURCE) && !has_out_params)
94                 inlineable.insert(func.definition);
95
96         SetForScope<FunctionDeclaration *> set(current_function, &func);
97         return_count = 0;
98         TraversingVisitor::visit(func);
99 }
100
101 void InlineableFunctionLocator::visit(Conditional &cond)
102 {
103         TraversingVisitor::visit(cond);
104         inlineable.erase(current_function);
105 }
106
107 void InlineableFunctionLocator::visit(Iteration &iter)
108 {
109         TraversingVisitor::visit(iter);
110         inlineable.erase(current_function);
111 }
112
113 void InlineableFunctionLocator::visit(Return &ret)
114 {
115         TraversingVisitor::visit(ret);
116         if(return_count)
117                 inlineable.erase(current_function);
118         ++return_count;
119 }
120
121
122 InlineContentInjector::InlineContentInjector():
123         source_func(0),
124         pass(REFERENCED)
125 { }
126
127 string InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList<Statement>::iterator &ins_pt, FunctionCall &call)
128 {
129         source_func = call.declaration->definition;
130
131         /* Populate referenced_names from the target function so we can rename
132         variables from the inlined function that would conflict. */
133         pass = REFERENCED;
134         target_func.visit(*this);
135
136         /* Inline and rename passes must be interleaved so used variable names are
137         known when inlining the return statement. */
138         pass = INLINE;
139         staging_block.parent = &tgt_blk;
140         staging_block.variables.clear();
141
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)
145         {
146                 RefPtr<VariableDeclaration> var = (*i)->clone();
147                 var->interface.clear();
148
149                 SetForScope<Pass> set_pass(pass, RENAME);
150                 var->visit(*this);
151
152                 staging_block.body.push_back_nocopy(var);
153                 params.push_back(var);
154         }
155
156         for(NodeList<Statement>::iterator i=source_func->body.body.begin(); i!=source_func->body.body.end(); ++i)
157         {
158                 r_inlined_statement = 0;
159                 (*i)->visit(*this);
160                 if(!r_inlined_statement)
161                         r_inlined_statement = (*i)->clone();
162
163                 SetForScope<Pass> set_pass(pass, RENAME);
164                 r_inlined_statement->visit(*this);
165
166                 staging_block.body.push_back_nocopy(r_inlined_statement);
167         }
168
169         /* Now collect names from the staging block.  Local variables that would
170         have conflicted with the target function were renamed earlier. */
171         pass = REFERENCED;
172         referenced_names.clear();
173         staging_block.variables.clear();
174         staging_block.visit(*this);
175
176         /* Rename variables in the target function so they don't interfere with
177         global identifiers used by the source function. */
178         pass = RENAME;
179         staging_block.parent = source_func->body.parent;
180         target_func.visit(*this);
181
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();
185
186         tgt_blk.body.splice(ins_pt, staging_block.body);
187
188         NodeReorderer().apply(stage, target_func, DependencyCollector().apply(*source_func));
189
190         return r_result_name;
191 }
192
193 void InlineContentInjector::visit(VariableReference &var)
194 {
195         if(pass==RENAME)
196         {
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;
200         }
201         else if(pass==REFERENCED)
202                 referenced_names.insert(var.name);
203 }
204
205 void InlineContentInjector::visit(InterfaceBlockReference &iface)
206 {
207         if(pass==REFERENCED)
208                 referenced_names.insert(iface.name);
209 }
210
211 void InlineContentInjector::visit(FunctionCall &call)
212 {
213         if(pass==REFERENCED)
214                 referenced_names.insert(call.name);
215         TraversingVisitor::visit(call);
216 }
217
218 void InlineContentInjector::visit(VariableDeclaration &var)
219 {
220         TraversingVisitor::visit(var);
221
222         if(pass==RENAME)
223         {
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;
228                 if(conflict)
229                 {
230                         string mapped_name = get_unused_variable_name(staging_block, var.name);
231                         if(mapped_name!=var.name)
232                         {
233                                 staging_block.variables[mapped_name] = &var;
234                                 var.name = mapped_name;
235                         }
236                 }
237         }
238         else if(pass==REFERENCED)
239                 referenced_names.insert(var.type);
240 }
241
242 void InlineContentInjector::visit(Return &ret)
243 {
244         TraversingVisitor::visit(ret);
245
246         if(pass==INLINE && ret.expression)
247         {
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;
257         }
258 }
259
260
261 FunctionInliner::FunctionInliner():
262         current_function(0),
263         r_any_inlined(false),
264         r_inlined_here(false)
265 { }
266
267 bool FunctionInliner::apply(Stage &s)
268 {
269         stage = &s;
270         inlineable = InlineableFunctionLocator().apply(s);
271         r_any_inlined = false;
272         s.content.visit(*this);
273         return r_any_inlined;
274 }
275
276 void FunctionInliner::visit(RefPtr<Expression> &ptr)
277 {
278         r_inline_result = 0;
279         ptr->visit(*this);
280         if(r_inline_result)
281         {
282                 ptr = r_inline_result;
283                 r_any_inlined = true;
284         }
285         r_inline_result = 0;
286 }
287
288 void FunctionInliner::visit(Block &block)
289 {
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)
293         {
294                 insert_point = i;
295                 (*i)->visit(*this);
296         }
297 }
298
299 void FunctionInliner::visit(FunctionCall &call)
300 {
301         for(NodeArray<Expression>::iterator i=call.arguments.begin(); (!r_inlined_here && i!=call.arguments.end()); ++i)
302                 visit(*i);
303
304         if(r_inlined_here)
305                 return;
306
307         FunctionDeclaration *def = call.declaration;
308         if(def)
309                 def = def->definition;
310
311         if(def && inlineable.count(def))
312         {
313                 string result_name = InlineContentInjector().apply(*stage, *current_function, *current_block, insert_point, call);
314
315                 // This will later get removed by UnusedVariableRemover.
316                 if(result_name.empty())
317                         result_name = "_msp_unused_from_inline";
318
319                 RefPtr<VariableReference> ref = new VariableReference;
320                 ref->name = result_name;
321                 r_inline_result = ref;
322
323                 /* Inlined variables need to be resolved before this function can be
324                 inlined further. */
325                 inlineable.erase(current_function);
326                 r_inlined_here = true;
327         }
328 }
329
330 void FunctionInliner::visit(FunctionDeclaration &func)
331 {
332         SetForScope<FunctionDeclaration *> set_func(current_function, &func);
333         TraversingVisitor::visit(func);
334         r_inlined_here = false;
335 }
336
337 void FunctionInliner::visit(Iteration &iter)
338 {
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);
343
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);
349 }
350
351
352 ExpressionInliner::ExpressionInliner():
353         r_ref_info(0),
354         r_any_inlined(false),
355         r_trivial(false),
356         mutating(false),
357         iteration_init(false),
358         iteration_body(0),
359         r_oper(0)
360 { }
361
362 bool ExpressionInliner::apply(Stage &s)
363 {
364         s.content.visit(*this);
365         return r_any_inlined;
366 }
367
368 void ExpressionInliner::inline_expression(Expression &expr, RefPtr<Expression> &ptr)
369 {
370         ptr = expr.clone();
371         r_any_inlined = true;
372 }
373
374 void ExpressionInliner::visit(Block &block)
375 {
376         TraversingVisitor::visit(block);
377
378         for(map<string, VariableDeclaration *>::iterator i=block.variables.begin(); i!=block.variables.end(); ++i)
379         {
380                 map<Assignment::Target, ExpressionInfo>::iterator j = expressions.lower_bound(i->second);
381                 for(; (j!=expressions.end() && j->first.declaration==i->second); )
382                 {
383                         if(j->second.expression && j->second.inline_point)
384                                 inline_expression(*j->second.expression, *j->second.inline_point);
385
386                         expressions.erase(j++);
387                 }
388         }
389
390         /* Expressions assigned in this block may depend on local variables of the
391         block.  If this is a conditionally executed block, the assignments might not
392         always happen.  Mark the expressions as not available to any outer blocks. */
393         for(map<Assignment::Target, ExpressionInfo>::iterator i=expressions.begin(); i!=expressions.end(); ++i)
394                 if(i->second.assign_scope==&block)
395                         i->second.available = false;
396 }
397
398 void ExpressionInliner::visit(RefPtr<Expression> &expr)
399 {
400         r_ref_info = 0;
401         expr->visit(*this);
402         if(r_ref_info && r_ref_info->expression && r_ref_info->available)
403         {
404                 if(iteration_body && !r_ref_info->trivial)
405                 {
406                         /* Don't inline non-trivial expressions which were assigned outside
407                         an iteration statement.  The iteration may run multiple times, which
408                         would cause the expression to also be evaluated multiple times. */
409                         Block *i = r_ref_info->assign_scope;
410                         for(; (i && i!=iteration_body); i=i->parent) ;
411                         if(!i)
412                                 return;
413                 }
414
415                 if(r_ref_info->trivial)
416                         inline_expression(*r_ref_info->expression, expr);
417                 else
418                         /* Record the inline point for a non-trivial expression but don't
419                         inline it yet.  It might turn out it shouldn't be inlined after all. */
420                         r_ref_info->inline_point = &expr;
421         }
422         r_oper = expr->oper;
423         r_ref_info = 0;
424 }
425
426 void ExpressionInliner::visit(VariableReference &var)
427 {
428         if(var.declaration)
429         {
430                 map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(var.declaration);
431                 if(i!=expressions.end())
432                 {
433                         /* If a non-trivial expression is referenced multiple times, don't
434                         inline it. */
435                         if(i->second.inline_point && !i->second.trivial)
436                                 i->second.expression = 0;
437                         /* Mutating expressions are analogous to self-referencing assignments
438                         and prevent inlining. */
439                         if(mutating)
440                                 i->second.expression = 0;
441                         r_ref_info = &i->second;
442                 }
443         }
444 }
445
446 void ExpressionInliner::visit(MemberAccess &memacc)
447 {
448         visit(memacc.left);
449         r_trivial = false;
450 }
451
452 void ExpressionInliner::visit(Swizzle &swizzle)
453 {
454         visit(swizzle.left);
455         r_trivial = false;
456 }
457
458 void ExpressionInliner::visit(UnaryExpression &unary)
459 {
460         SetFlag set_target(mutating, mutating || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
461         visit(unary.expression);
462         r_trivial = false;
463 }
464
465 void ExpressionInliner::visit(BinaryExpression &binary)
466 {
467         visit(binary.left);
468         {
469                 SetFlag clear_target(mutating, false);
470                 visit(binary.right);
471         }
472         r_trivial = false;
473 }
474
475 void ExpressionInliner::visit(Assignment &assign)
476 {
477         {
478                 SetFlag set_target(mutating);
479                 visit(assign.left);
480         }
481         r_oper = 0;
482         visit(assign.right);
483
484         map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(assign.target);
485         if(i!=expressions.end())
486         {
487                 /* Self-referencing assignments can't be inlined without additional
488                 work.  Just clear any previous expression. */
489                 i->second.expression = (assign.self_referencing ? 0 : assign.right.get());
490                 i->second.assign_scope = current_block;
491                 i->second.inline_point = 0;
492                 i->second.available = true;
493         }
494
495         r_trivial = false;
496 }
497
498 void ExpressionInliner::visit(TernaryExpression &ternary)
499 {
500         visit(ternary.condition);
501         visit(ternary.true_expr);
502         visit(ternary.false_expr);
503         r_trivial = false;
504 }
505
506 void ExpressionInliner::visit(FunctionCall &call)
507 {
508         TraversingVisitor::visit(call);
509         r_trivial = false;
510 }
511
512 void ExpressionInliner::visit(VariableDeclaration &var)
513 {
514         r_oper = 0;
515         r_trivial = true;
516         TraversingVisitor::visit(var);
517
518         bool constant = var.constant;
519         if(constant && var.layout)
520         {
521                 for(vector<Layout::Qualifier>::const_iterator i=var.layout->qualifiers.begin(); (constant && i!=var.layout->qualifiers.end()); ++i)
522                         constant = (i->name!="constant_id");
523         }
524
525         /* Only inline global variables if they're constant and have trivial
526         initializers.  Non-constant variables could change in ways which are hard to
527         analyze and non-trivial expressions could be expensive to inline.  */
528         if((current_block->parent || (constant && r_trivial)) && var.interface.empty())
529         {
530                 ExpressionInfo &info = expressions[&var];
531                 /* Assume variables declared in an iteration initialization statement
532                 will have their values change throughout the iteration. */
533                 info.expression = (iteration_init ? 0 : var.init_expression.get());
534                 info.assign_scope = current_block;
535                 info.trivial = r_trivial;
536         }
537 }
538
539 void ExpressionInliner::visit(Iteration &iter)
540 {
541         SetForScope<Block *> set_block(current_block, &iter.body);
542         if(iter.init_statement)
543         {
544                 SetFlag set_init(iteration_init);
545                 iter.init_statement->visit(*this);
546         }
547
548         SetForScope<Block *> set_body(iteration_body, &iter.body);
549         if(iter.condition)
550                 visit(iter.condition);
551         iter.body.visit(*this);
552         if(iter.loop_expression)
553                 visit(iter.loop_expression);
554 }
555
556
557 template<typename T>
558 T ConstantFolder::evaluate_logical(char oper, T left, T right)
559 {
560         switch(oper)
561         {
562         case '&': return left&right;
563         case '|': return left|right;
564         case '^': return left^right;
565         default: return T();
566         }
567 }
568
569 template<typename T>
570 bool ConstantFolder::evaluate_relation(const char *oper, T left, T right)
571 {
572         switch(oper[0]|oper[1])
573         {
574         case '<': return left<right;
575         case '<'|'=': return left<=right;
576         case '>': return left>right;
577         case '>'|'=': return left>=right;
578         default: return false;
579         }
580 }
581
582 template<typename T>
583 T ConstantFolder::evaluate_arithmetic(char oper, T left, T right)
584 {
585         switch(oper)
586         {
587         case '+': return left+right;
588         case '-': return left-right;
589         case '*': return left*right;
590         case '/': return left/right;
591         default: return T();
592         }
593 }
594
595 template<typename T>
596 T ConstantFolder::evaluate_int_special_op(char oper, T left, T right)
597 {
598         switch(oper)
599         {
600         case '%': return left%right;
601         case '<': return left<<right;
602         case '>': return left>>right;
603         default: return T();
604         }
605 }
606
607 template<typename T>
608 void ConstantFolder::convert_to_result(const Variant &value)
609 {
610         if(value.check_type<bool>())
611                 set_result(static_cast<T>(value.value<bool>()));
612         else if(value.check_type<int>())
613                 set_result(static_cast<T>(value.value<int>()));
614         else if(value.check_type<float>())
615                 set_result(static_cast<T>(value.value<float>()));
616 }
617
618 void ConstantFolder::set_result(const Variant &value, bool literal)
619 {
620         r_constant_value = value;
621         r_constant = true;
622         r_literal = literal;
623 }
624
625 void ConstantFolder::visit(RefPtr<Expression> &expr)
626 {
627         r_constant_value = Variant();
628         r_constant = false;
629         r_literal = false;
630         r_uses_iter_var = false;
631         expr->visit(*this);
632         /* Don't replace literals since they'd only be replaced with an identical
633         literal.  Also skip anything that uses an iteration variable, but pass on
634         the result so the Iteration visiting function can handle it. */
635         if(!r_constant || r_literal || r_uses_iter_var)
636                 return;
637
638         RefPtr<Literal> literal = new Literal;
639         if(r_constant_value.check_type<bool>())
640                 literal->token = (r_constant_value.value<bool>() ? "true" : "false");
641         else if(r_constant_value.check_type<int>())
642                 literal->token = lexical_cast<string>(r_constant_value.value<int>());
643         else if(r_constant_value.check_type<float>())
644         {
645                 literal->token = lexical_cast<string>(r_constant_value.value<float>());
646                 if(isnumrc(literal->token))
647                         literal->token += ".0";
648         }
649         else
650         {
651                 r_constant = false;
652                 return;
653         }
654         literal->value = r_constant_value;
655         expr = literal;
656 }
657
658 void ConstantFolder::visit(Literal &literal)
659 {
660         set_result(literal.value, true);
661 }
662
663 void ConstantFolder::visit(VariableReference &var)
664 {
665         /* If an iteration variable is initialized with a constant value, return
666         that value here for the purpose of evaluating the loop condition for the
667         first iteration. */
668         if(var.declaration==iteration_var)
669         {
670                 set_result(iter_init_value);
671                 r_uses_iter_var = true;
672         }
673 }
674
675 void ConstantFolder::visit(MemberAccess &memacc)
676 {
677         TraversingVisitor::visit(memacc);
678         r_constant = false;
679 }
680
681 void ConstantFolder::visit(Swizzle &swizzle)
682 {
683         TraversingVisitor::visit(swizzle);
684         r_constant = false;
685 }
686
687 void ConstantFolder::visit(UnaryExpression &unary)
688 {
689         TraversingVisitor::visit(unary);
690         bool can_fold = r_constant;
691         r_constant = false;
692         if(!can_fold)
693                 return;
694
695         char oper = unary.oper->token[0];
696         char oper2 = unary.oper->token[1];
697         if(oper=='!')
698         {
699                 if(r_constant_value.check_type<bool>())
700                         set_result(!r_constant_value.value<bool>());
701         }
702         else if(oper=='~')
703         {
704                 if(r_constant_value.check_type<int>())
705                         set_result(~r_constant_value.value<int>());
706                 else if(r_constant_value.check_type<unsigned>())
707                         set_result(~r_constant_value.value<unsigned>());
708         }
709         else if(oper=='-' && !oper2)
710         {
711                 if(r_constant_value.check_type<int>())
712                         set_result(-r_constant_value.value<int>());
713                 else if(r_constant_value.check_type<unsigned>())
714                         set_result(-r_constant_value.value<unsigned>());
715                 else if(r_constant_value.check_type<float>())
716                         set_result(-r_constant_value.value<float>());
717         }
718 }
719
720 void ConstantFolder::visit(BinaryExpression &binary)
721 {
722         visit(binary.left);
723         bool left_constant = r_constant;
724         bool left_iter_var = r_uses_iter_var;
725         Variant left_value = r_constant_value;
726         visit(binary.right);
727         if(left_iter_var)
728                 r_uses_iter_var = true;
729
730         bool can_fold = (left_constant && r_constant);
731         r_constant = false;
732         if(!can_fold)
733                 return;
734
735         // Currently only expressions with both sides of equal types are handled.
736         if(!left_value.check_same_type(r_constant_value))
737                 return;
738
739         char oper = binary.oper->token[0];
740         char oper2 = binary.oper->token[1];
741         if(oper=='&' || oper=='|' || oper=='^')
742         {
743                 if(oper2==oper && left_value.check_type<bool>())
744                         set_result(evaluate_logical(oper, left_value.value<bool>(), r_constant_value.value<bool>()));
745                 else if(!oper2 && left_value.check_type<int>())
746                         set_result(evaluate_logical(oper, left_value.value<int>(), r_constant_value.value<int>()));
747         }
748         else if((oper=='<' || oper=='>') && oper2!=oper)
749         {
750                 if(left_value.check_type<int>())
751                         set_result(evaluate_relation(binary.oper->token, left_value.value<int>(), r_constant_value.value<int>()));
752                 else if(left_value.check_type<float>())
753                         set_result(evaluate_relation(binary.oper->token, left_value.value<float>(), r_constant_value.value<float>()));
754         }
755         else if((oper=='=' || oper=='!') && oper2=='=')
756         {
757                 if(left_value.check_type<int>())
758                         set_result((left_value.value<int>()==r_constant_value.value<int>()) == (oper=='='));
759                 if(left_value.check_type<float>())
760                         set_result((left_value.value<float>()==r_constant_value.value<float>()) == (oper=='='));
761         }
762         else if(oper=='+' || oper=='-' || oper=='*' || oper=='/')
763         {
764                 if(left_value.check_type<int>())
765                         set_result(evaluate_arithmetic(oper, left_value.value<int>(), r_constant_value.value<int>()));
766                 else if(left_value.check_type<float>())
767                         set_result(evaluate_arithmetic(oper, left_value.value<float>(), r_constant_value.value<float>()));
768         }
769         else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper))
770         {
771                 if(left_value.check_type<int>())
772                         set_result(evaluate_int_special_op(oper, left_value.value<int>(), r_constant_value.value<int>()));
773         }
774 }
775
776 void ConstantFolder::visit(Assignment &assign)
777 {
778         TraversingVisitor::visit(assign);
779         r_constant = false;
780 }
781
782 void ConstantFolder::visit(TernaryExpression &ternary)
783 {
784         TraversingVisitor::visit(ternary);
785         r_constant = false;
786 }
787
788 void ConstantFolder::visit(FunctionCall &call)
789 {
790         if(call.constructor && call.type && call.arguments.size()==1)
791         {
792                 const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(call.type);
793                 if(basic)
794                 {
795                         call.arguments[0]->visit(*this);
796                         bool can_fold = r_constant;
797                         r_constant = false;
798                         if(!can_fold)
799                                 return;
800
801                         if(basic->kind==BasicTypeDeclaration::BOOL)
802                                 convert_to_result<bool>(r_constant_value);
803                         else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32)
804                                 convert_to_result<int>(r_constant_value);
805                         else if(basic->kind==BasicTypeDeclaration::FLOAT && basic->size==32)
806                                 convert_to_result<float>(r_constant_value);
807
808                         return;
809                 }
810         }
811
812         TraversingVisitor::visit(call);
813         r_constant = false;
814 }
815
816 void ConstantFolder::visit(VariableDeclaration &var)
817 {
818         if(iteration_init && var.init_expression)
819         {
820                 visit(var.init_expression);
821                 if(r_constant)
822                 {
823                         /* Record the value of a constant initialization expression of an
824                         iteration, so it can be used to evaluate the loop condition. */
825                         iteration_var = &var;
826                         iter_init_value = r_constant_value;
827                 }
828         }
829         else
830                 TraversingVisitor::visit(var);
831 }
832
833 void ConstantFolder::visit(Iteration &iter)
834 {
835         SetForScope<Block *> set_block(current_block, &iter.body);
836
837         /* The iteration variable is not normally inlined into expressions, so we
838         process it specially here.  If the initial value causes the loop condition
839         to evaluate to false, then the expression can be folded. */
840         iteration_var = 0;
841         if(iter.init_statement)
842         {
843                 SetFlag set_init(iteration_init);
844                 iter.init_statement->visit(*this);
845         }
846
847         if(iter.condition)
848         {
849                 visit(iter.condition);
850                 if(r_constant && r_constant_value.check_type<bool>() && !r_constant_value.value<bool>())
851                 {
852                         RefPtr<Literal> literal = new Literal;
853                         literal->token = "false";
854                         literal->value = r_constant_value;
855                         iter.condition = literal;
856                 }
857         }
858         iteration_var = 0;
859
860         iter.body.visit(*this);
861         if(iter.loop_expression)
862                 visit(iter.loop_expression);
863 }
864
865
866 void ConstantConditionEliminator::apply(Stage &stage)
867 {
868         stage.content.visit(*this);
869         NodeRemover().apply(stage, nodes_to_remove);
870 }
871
872 ConstantConditionEliminator::ConstantStatus ConstantConditionEliminator::check_constant_condition(const Expression &expr)
873 {
874         if(const Literal *literal = dynamic_cast<const Literal *>(&expr))
875                 if(literal->value.check_type<bool>())
876                         return (literal->value.value<bool>() ? CONSTANT_TRUE : CONSTANT_FALSE);
877         return NOT_CONSTANT;
878 }
879
880 void ConstantConditionEliminator::visit(Block &block)
881 {
882         SetForScope<Block *> set_block(current_block, &block);
883         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
884         {
885                 insert_point = i;
886                 (*i)->visit(*this);
887         }
888 }
889
890 void ConstantConditionEliminator::visit(RefPtr<Expression> &expr)
891 {
892         r_ternary_result = 0;
893         expr->visit(*this);
894         if(r_ternary_result)
895                 expr = r_ternary_result;
896         r_ternary_result = 0;
897 }
898
899 void ConstantConditionEliminator::visit(TernaryExpression &ternary)
900 {
901         ConstantStatus result = check_constant_condition(*ternary.condition);
902         if(result!=NOT_CONSTANT)
903                 r_ternary_result = (result==CONSTANT_TRUE ? ternary.true_expr : ternary.false_expr);
904         else
905                 r_ternary_result = 0;
906 }
907
908 void ConstantConditionEliminator::visit(Conditional &cond)
909 {
910         ConstantStatus result = check_constant_condition(*cond.condition);
911         if(result!=NOT_CONSTANT)
912         {
913                 Block &block = (result==CONSTANT_TRUE ? cond.body : cond.else_body);
914                 // TODO should check variable names for conflicts.  Potentially reuse InlineContentInjector?
915                 current_block->body.splice(insert_point, block.body);
916                 nodes_to_remove.insert(&cond);
917                 return;
918         }
919
920         TraversingVisitor::visit(cond);
921 }
922
923 void ConstantConditionEliminator::visit(Iteration &iter)
924 {
925         if(iter.condition)
926         {
927                 ConstantStatus result = check_constant_condition(*iter.condition);
928                 if(result==CONSTANT_FALSE)
929                 {
930                         nodes_to_remove.insert(&iter);
931                         return;
932                 }
933         }
934
935         TraversingVisitor::visit(iter);
936 }
937
938
939 UnreachableCodeRemover::UnreachableCodeRemover():
940         reachable(true)
941 { }
942
943 bool UnreachableCodeRemover::apply(Stage &stage)
944 {
945         stage.content.visit(*this);
946         NodeRemover().apply(stage, unreachable_nodes);
947         return !unreachable_nodes.empty();
948 }
949
950 void UnreachableCodeRemover::visit(Block &block)
951 {
952         NodeList<Statement>::iterator i = block.body.begin();
953         for(; (reachable && i!=block.body.end()); ++i)
954                 (*i)->visit(*this);
955         for(; i!=block.body.end(); ++i)
956                 unreachable_nodes.insert(i->get());
957 }
958
959 void UnreachableCodeRemover::visit(FunctionDeclaration &func)
960 {
961         TraversingVisitor::visit(func);
962         reachable = true;
963 }
964
965 void UnreachableCodeRemover::visit(Conditional &cond)
966 {
967         cond.body.visit(*this);
968         bool reachable_if_true = reachable;
969         reachable = true;
970         cond.else_body.visit(*this);
971
972         reachable |= reachable_if_true;
973 }
974
975 void UnreachableCodeRemover::visit(Iteration &iter)
976 {
977         TraversingVisitor::visit(iter);
978
979         /* Always consider code after a loop reachable, since there's no checking
980         for whether the loop executes. */
981         reachable = true;
982 }
983
984
985 bool UnusedTypeRemover::apply(Stage &stage)
986 {
987         stage.content.visit(*this);
988         NodeRemover().apply(stage, unused_nodes);
989         return !unused_nodes.empty();
990 }
991
992 void UnusedTypeRemover::visit(RefPtr<Expression> &expr)
993 {
994         unused_nodes.erase(expr->type);
995         TraversingVisitor::visit(expr);
996 }
997
998 void UnusedTypeRemover::visit(BasicTypeDeclaration &type)
999 {
1000         if(type.base_type)
1001                 unused_nodes.erase(type.base_type);
1002         unused_nodes.insert(&type);
1003 }
1004
1005 void UnusedTypeRemover::visit(ImageTypeDeclaration &type)
1006 {
1007         if(type.base_type)
1008                 unused_nodes.erase(type.base_type);
1009         unused_nodes.insert(&type);
1010 }
1011
1012 void UnusedTypeRemover::visit(StructDeclaration &strct)
1013 {
1014         unused_nodes.insert(&strct);
1015         TraversingVisitor::visit(strct);
1016 }
1017
1018 void UnusedTypeRemover::visit(VariableDeclaration &var)
1019 {
1020         unused_nodes.erase(var.type_declaration);
1021         TraversingVisitor::visit(var);
1022 }
1023
1024 void UnusedTypeRemover::visit(InterfaceBlock &iface)
1025 {
1026         unused_nodes.erase(iface.type_declaration);
1027 }
1028
1029 void UnusedTypeRemover::visit(FunctionDeclaration &func)
1030 {
1031         unused_nodes.erase(func.return_type_declaration);
1032         TraversingVisitor::visit(func);
1033 }
1034
1035
1036 UnusedVariableRemover::UnusedVariableRemover():
1037         stage(0),
1038         interface_block(0),
1039         r_assignment(0),
1040         assignment_target(false),
1041         r_side_effects(false),
1042         in_struct(false),
1043         composite_reference(false)
1044 { }
1045
1046 bool UnusedVariableRemover::apply(Stage &s)
1047 {
1048         stage = &s;
1049         s.content.visit(*this);
1050
1051         for(list<AssignmentInfo>::const_iterator i=assignments.begin(); i!=assignments.end(); ++i)
1052                 if(i->used_by.empty())
1053                         unused_nodes.insert(i->node);
1054
1055         for(BlockVariableMap::const_iterator i=variables.begin(); i!=variables.end(); ++i)
1056         {
1057                 if(i->second.output)
1058                 {
1059                         /* The last visible assignments of output variables are used by the
1060                         next stage or the API. */
1061                         for(vector<AssignmentInfo *>::const_iterator j=i->second.assignments.begin(); j!=i->second.assignments.end(); ++j)
1062                                 unused_nodes.erase((*j)->node);
1063                 }
1064
1065                 if(!i->second.output && !i->second.referenced)
1066                 {
1067                         // Don't remove variables from inside interface blocks.
1068                         if(!i->second.interface_block)
1069                                 unused_nodes.insert(i->first);
1070                 }
1071                 else if(i->second.interface_block)
1072                         // Interface blocks are kept if even one member is used.
1073                         unused_nodes.erase(i->second.interface_block);
1074         }
1075
1076         NodeRemover().apply(s, unused_nodes);
1077
1078         return !unused_nodes.empty();
1079 }
1080
1081 void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &node)
1082 {
1083         VariableInfo &var_info = variables[target.declaration];
1084         var_info.referenced = true;
1085         if(!assignment_target)
1086         {
1087                 for(vector<AssignmentInfo *>::const_iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i)
1088                 {
1089                         bool covered = true;
1090                         for(unsigned j=0; (covered && j<(*i)->target.chain_len && j<target.chain_len); ++j)
1091                         {
1092                                 Assignment::Target::ChainType type1 = static_cast<Assignment::Target::ChainType>((*i)->target.chain[j]&0xC0);
1093                                 Assignment::Target::ChainType type2 = static_cast<Assignment::Target::ChainType>(target.chain[j]&0xC0);
1094                                 if(type1==Assignment::Target::SWIZZLE || type2==Assignment::Target::SWIZZLE)
1095                                 {
1096                                         unsigned index1 = (*i)->target.chain[j]&0x3F;
1097                                         unsigned index2 = target.chain[j]&0x3F;
1098                                         if(type1==Assignment::Target::SWIZZLE && type2==Assignment::Target::SWIZZLE)
1099                                                 covered = index1&index2;
1100                                         else if(type1==Assignment::Target::ARRAY && index1<4)
1101                                                 covered = index2&(1<<index1);
1102                                         else if(type2==Assignment::Target::ARRAY && index2<4)
1103                                                 covered = index1&(1<<index2);
1104                                         /* If it's some other combination (shouldn't happen), leave
1105                                         covered as true */
1106                                 }
1107                                 else
1108                                         covered = ((*i)->target.chain[j]==target.chain[j]);
1109                         }
1110                         if(covered)
1111                                 (*i)->used_by.push_back(&node);
1112                 }
1113         }
1114 }
1115
1116 void UnusedVariableRemover::visit(VariableReference &var)
1117 {
1118         if(composite_reference)
1119                 r_reference.declaration = var.declaration;
1120         else
1121                 referenced(var.declaration, var);
1122 }
1123
1124 void UnusedVariableRemover::visit(InterfaceBlockReference &iface)
1125 {
1126         if(composite_reference)
1127                 r_reference.declaration = iface.declaration;
1128         else
1129                 referenced(iface.declaration, iface);
1130 }
1131
1132 void UnusedVariableRemover::visit_composite(Expression &expr)
1133 {
1134         if(!composite_reference)
1135                 r_reference = Assignment::Target();
1136
1137         SetFlag set_composite(composite_reference);
1138         expr.visit(*this);
1139 }
1140
1141 void UnusedVariableRemover::visit(MemberAccess &memacc)
1142 {
1143         visit_composite(*memacc.left);
1144
1145         add_to_chain(r_reference, Assignment::Target::MEMBER, memacc.index);
1146
1147         if(!composite_reference && r_reference.declaration)
1148                 referenced(r_reference, memacc);
1149 }
1150
1151 void UnusedVariableRemover::visit(Swizzle &swizzle)
1152 {
1153         visit_composite(*swizzle.left);
1154
1155         unsigned mask = 0;
1156         for(unsigned i=0; i<swizzle.count; ++i)
1157                 mask |= 1<<swizzle.components[i];
1158         add_to_chain(r_reference, Assignment::Target::SWIZZLE, mask);
1159
1160         if(!composite_reference && r_reference.declaration)
1161                 referenced(r_reference, swizzle);
1162 }
1163
1164 void UnusedVariableRemover::visit(UnaryExpression &unary)
1165 {
1166         TraversingVisitor::visit(unary);
1167         if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
1168                 r_side_effects = true;
1169 }
1170
1171 void UnusedVariableRemover::visit(BinaryExpression &binary)
1172 {
1173         if(binary.oper->token[0]=='[')
1174         {
1175                 visit_composite(*binary.left);
1176
1177                 {
1178                         SetFlag clear_assignment(assignment_target, false);
1179                         SetFlag clear_composite(composite_reference, false);
1180                         binary.right->visit(*this);
1181                 }
1182
1183                 add_to_chain(r_reference, Assignment::Target::ARRAY, 0x3F);
1184
1185                 if(!composite_reference && r_reference.declaration)
1186                         referenced(r_reference, binary);
1187         }
1188         else
1189         {
1190                 SetFlag clear_composite(composite_reference, false);
1191                 TraversingVisitor::visit(binary);
1192         }
1193 }
1194
1195 void UnusedVariableRemover::visit(TernaryExpression &ternary)
1196 {
1197         SetFlag clear_composite(composite_reference, false);
1198         TraversingVisitor::visit(ternary);
1199 }
1200
1201 void UnusedVariableRemover::visit(Assignment &assign)
1202 {
1203         {
1204                 SetFlag set(assignment_target, (assign.oper->token[0]=='='));
1205                 assign.left->visit(*this);
1206         }
1207         assign.right->visit(*this);
1208         r_assignment = &assign;
1209         r_side_effects = true;
1210 }
1211
1212 void UnusedVariableRemover::visit(FunctionCall &call)
1213 {
1214         SetFlag clear_composite(composite_reference, false);
1215         TraversingVisitor::visit(call);
1216         /* Treat function calls as having side effects so expression statements
1217         consisting of nothing but a function call won't be optimized away. */
1218         r_side_effects = true;
1219
1220         if(stage->type==Stage::GEOMETRY && call.name=="EmitVertex")
1221         {
1222                 for(map<Statement *, VariableInfo>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
1223                         if(i->second.output)
1224                                 referenced(i->first, call);
1225         }
1226 }
1227
1228 void UnusedVariableRemover::record_assignment(const Assignment::Target &target, Node &node)
1229 {
1230         assignments.push_back(AssignmentInfo());
1231         AssignmentInfo &assign_info = assignments.back();
1232         assign_info.node = &node;
1233         assign_info.target = target;
1234
1235         /* An assignment to the target hides any assignments to the same target or
1236         its subfields. */
1237         VariableInfo &var_info = variables[target.declaration];
1238         for(unsigned i=0; i<var_info.assignments.size(); ++i)
1239         {
1240                 const Assignment::Target &t = var_info.assignments[i]->target;
1241
1242                 bool subfield = (t.chain_len>=target.chain_len);
1243                 for(unsigned j=0; (subfield && j<target.chain_len); ++j)
1244                         subfield = (t.chain[j]==target.chain[j]);
1245
1246                 if(subfield)
1247                         var_info.assignments.erase(var_info.assignments.begin()+i);
1248                 else
1249                         ++i;
1250         }
1251
1252         var_info.assignments.push_back(&assign_info);
1253 }
1254
1255 void UnusedVariableRemover::visit(ExpressionStatement &expr)
1256 {
1257         r_assignment = 0;
1258         r_side_effects = false;
1259         TraversingVisitor::visit(expr);
1260         if(r_assignment && r_assignment->target.declaration)
1261                 record_assignment(r_assignment->target, expr);
1262         if(!r_side_effects)
1263                 unused_nodes.insert(&expr);
1264 }
1265
1266 void UnusedVariableRemover::visit(StructDeclaration &strct)
1267 {
1268         SetFlag set_struct(in_struct);
1269         TraversingVisitor::visit(strct);
1270 }
1271
1272 void UnusedVariableRemover::visit(VariableDeclaration &var)
1273 {
1274         TraversingVisitor::visit(var);
1275
1276         if(in_struct)
1277                 return;
1278
1279         VariableInfo &var_info = variables[&var];
1280         var_info.interface_block = interface_block;
1281
1282         /* Mark variables as output if they're used by the next stage or the
1283         graphics API. */
1284         if(interface_block)
1285                 var_info.output = (interface_block->interface=="out" && (interface_block->linked_block || !interface_block->block_name.compare(0, 3, "gl_")));
1286         else
1287                 var_info.output = (var.interface=="out" && (stage->type==Stage::FRAGMENT || var.linked_declaration || !var.name.compare(0, 3, "gl_")));
1288
1289         if(var.init_expression)
1290         {
1291                 var_info.initialized = true;
1292                 record_assignment(&var, *var.init_expression);
1293         }
1294 }
1295
1296 void UnusedVariableRemover::visit(InterfaceBlock &iface)
1297 {
1298         VariableInfo &var_info = variables[&iface];
1299         var_info.output = (iface.interface=="out" && (iface.linked_block || !iface.block_name.compare(0, 3, "gl_")));
1300 }
1301
1302 void UnusedVariableRemover::merge_variables(const BlockVariableMap &other_vars)
1303 {
1304         for(BlockVariableMap::const_iterator i=other_vars.begin(); i!=other_vars.end(); ++i)
1305         {
1306                 BlockVariableMap::iterator j = variables.find(i->first);
1307                 if(j!=variables.end())
1308                 {
1309                         /* The merged blocks started as copies of each other so any common
1310                         assignments must be in the beginning. */
1311                         unsigned k = 0;
1312                         for(; (k<i->second.assignments.size() && k<j->second.assignments.size()); ++k)
1313                                 if(i->second.assignments[k]!=j->second.assignments[k])
1314                                         break;
1315
1316                         // Remaining assignments are unique to each block; merge them.
1317                         j->second.assignments.insert(j->second.assignments.end(), i->second.assignments.begin()+k, i->second.assignments.end());
1318                         j->second.referenced |= i->second.referenced;
1319                 }
1320                 else
1321                         variables.insert(*i);
1322         }
1323 }
1324
1325 void UnusedVariableRemover::visit(FunctionDeclaration &func)
1326 {
1327         if(func.body.body.empty())
1328                 return;
1329
1330         BlockVariableMap saved_vars = variables;
1331         // Assignments from other functions should not be visible.
1332         for(BlockVariableMap::iterator i=variables.begin(); i!=variables.end(); ++i)
1333                 i->second.assignments.resize(i->second.initialized);
1334         TraversingVisitor::visit(func);
1335         swap(variables, saved_vars);
1336         merge_variables(saved_vars);
1337
1338         /* Always treat function parameters as referenced.  Removing unused
1339         parameters is not currently supported. */
1340         for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
1341         {
1342                 BlockVariableMap::iterator j = variables.find(i->get());
1343                 if(j!=variables.end())
1344                         j->second.referenced = true;
1345         }
1346 }
1347
1348 void UnusedVariableRemover::visit(Conditional &cond)
1349 {
1350         cond.condition->visit(*this);
1351         BlockVariableMap saved_vars = variables;
1352         cond.body.visit(*this);
1353         swap(saved_vars, variables);
1354         cond.else_body.visit(*this);
1355
1356         /* Visible assignments after the conditional is the union of those visible
1357         at the end of the if and else blocks.  If there was no else block, then it's
1358         the union of the if block and the state before it. */
1359         merge_variables(saved_vars);
1360 }
1361
1362 void UnusedVariableRemover::visit(Iteration &iter)
1363 {
1364         BlockVariableMap saved_vars = variables;
1365         TraversingVisitor::visit(iter);
1366
1367         /* Merge assignments from the iteration, without clearing previous state.
1368         Further analysis is needed to determine which parts of the iteration body
1369         are always executed, if any. */
1370         merge_variables(saved_vars);
1371 }
1372
1373
1374 bool UnusedFunctionRemover::apply(Stage &stage)
1375 {
1376         stage.content.visit(*this);
1377         NodeRemover().apply(stage, unused_nodes);
1378         return !unused_nodes.empty();
1379 }
1380
1381 void UnusedFunctionRemover::visit(FunctionCall &call)
1382 {
1383         TraversingVisitor::visit(call);
1384
1385         unused_nodes.erase(call.declaration);
1386         if(call.declaration && call.declaration->definition!=call.declaration)
1387                 used_definitions.insert(call.declaration->definition);
1388 }
1389
1390 void UnusedFunctionRemover::visit(FunctionDeclaration &func)
1391 {
1392         TraversingVisitor::visit(func);
1393
1394         if((func.name!="main" || func.body.body.empty()) && !used_definitions.count(&func))
1395                 unused_nodes.insert(&func);
1396 }
1397
1398 } // namespace SL
1399 } // namespace GL
1400 } // namespace Msp