1 #include <msp/core/raii.h>
2 #include <msp/strings/format.h>
11 InlineableFunctionLocator::InlineableFunctionLocator():
16 void InlineableFunctionLocator::visit(FunctionCall &call)
18 FunctionDeclaration *def = call.declaration;
20 def = def->definition;
24 unsigned &count = refcounts[def];
26 if(count>1 || def==current_function)
27 inlineable.erase(def);
30 TraversingVisitor::visit(call);
33 void InlineableFunctionLocator::visit(FunctionDeclaration &func)
35 unsigned &count = refcounts[func.definition];
36 if(count<=1 && func.parameters.empty())
37 inlineable.insert(func.definition);
39 SetForScope<FunctionDeclaration *> set(current_function, &func);
41 TraversingVisitor::visit(func);
44 void InlineableFunctionLocator::visit(Conditional &cond)
46 TraversingVisitor::visit(cond);
47 inlineable.erase(current_function);
50 void InlineableFunctionLocator::visit(Iteration &iter)
52 TraversingVisitor::visit(iter);
53 inlineable.erase(current_function);
56 void InlineableFunctionLocator::visit(Return &ret)
58 TraversingVisitor::visit(ret);
60 inlineable.erase(current_function);
65 InlineContentInjector::InlineContentInjector():
71 const string &InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgtb, const NodeList<Statement>::iterator &ins_pt, FunctionDeclaration &src)
75 for(NodeList<Statement>::iterator i=src.body.body.begin(); i!=src.body.body.end(); ++i)
77 r_inlined_statement = 0;
79 if(!r_inlined_statement)
80 r_inlined_statement = (*i)->clone();
82 SetFlag set_remap(remap_names);
83 r_inlined_statement->visit(*this);
84 tgtb.body.insert(ins_pt, r_inlined_statement);
87 NodeReorderer().apply(stage, target_func, dependencies);
92 string InlineContentInjector::create_unused_name(const string &base, bool always_prefix)
95 if(always_prefix || target_block->variables.count(result))
96 result = format("_%s_%s", source_func->name, base);
97 unsigned initial_size = result.size();
98 for(unsigned i=1; target_block->variables.count(result); ++i)
100 result.erase(initial_size);
101 result += format("_%d", i);
106 void InlineContentInjector::visit(VariableReference &var)
110 map<string, VariableDeclaration *>::const_iterator i = variable_map.find(var.name);
111 if(i!=variable_map.end())
112 var.name = i->second->name;
114 else if(var.declaration)
116 SetFlag set_deps(deps_only);
117 dependencies.insert(var.declaration);
118 var.declaration->visit(*this);
122 void InlineContentInjector::visit(InterfaceBlockReference &iface)
124 if(!remap_names && iface.declaration)
126 SetFlag set_deps(deps_only);
127 dependencies.insert(iface.declaration);
128 iface.declaration->visit(*this);
132 void InlineContentInjector::visit(FunctionCall &call)
134 if(!remap_names && call.declaration)
135 dependencies.insert(call.declaration);
136 TraversingVisitor::visit(call);
139 void InlineContentInjector::visit(VariableDeclaration &var)
141 TraversingVisitor::visit(var);
143 if(var.type_declaration)
145 SetFlag set_deps(deps_only);
146 dependencies.insert(var.type_declaration);
147 var.type_declaration->visit(*this);
150 if(!remap_names && !deps_only)
152 RefPtr<VariableDeclaration> inlined_var = var.clone();
153 inlined_var->name = create_unused_name(var.name, false);
154 r_inlined_statement = inlined_var;
156 variable_map[var.name] = inlined_var.get();
160 void InlineContentInjector::visit(Return &ret)
162 TraversingVisitor::visit(ret);
166 r_result_name = create_unused_name("return", true);
167 RefPtr<VariableDeclaration> var = new VariableDeclaration;
168 var->source = ret.source;
169 var->line = ret.line;
170 var->type = source_func->return_type;
171 var->name = r_result_name;
172 var->init_expression = ret.expression->clone();
173 r_inlined_statement = var;
178 FunctionInliner::FunctionInliner():
183 bool FunctionInliner::apply(Stage &s)
186 inlineable = InlineableFunctionLocator().apply(s);
187 r_any_inlined = false;
188 s.content.visit(*this);
189 return r_any_inlined;
192 void FunctionInliner::visit_and_inline(RefPtr<Expression> &ptr)
198 ptr = r_inline_result;
199 r_any_inlined = true;
203 void FunctionInliner::visit(Block &block)
205 SetForScope<Block *> set_block(current_block, &block);
206 SetForScope<NodeList<Statement>::iterator> save_insert_point(insert_point, block.body.begin());
207 for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
214 void FunctionInliner::visit(UnaryExpression &unary)
216 visit_and_inline(unary.expression);
220 void FunctionInliner::visit(BinaryExpression &binary)
222 visit_and_inline(binary.left);
223 visit_and_inline(binary.right);
227 void FunctionInliner::visit(MemberAccess &memacc)
229 visit_and_inline(memacc.left);
233 void FunctionInliner::visit(FunctionCall &call)
235 for(NodeArray<Expression>::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
236 visit_and_inline(*i);
238 FunctionDeclaration *def = call.declaration;
240 def = def->definition;
242 if(def && inlineable.count(def))
244 string result_name = InlineContentInjector().apply(*stage, *current_function, *current_block, insert_point, *def);
246 // This will later get removed by UnusedVariableRemover
247 if(result_name.empty())
248 result_name = "msp_unused_from_inline";
250 RefPtr<VariableReference> ref = new VariableReference;
251 ref->name = result_name;
252 r_inline_result = ref;
254 /* Inlined variables need to be resolved before this function can be
256 inlineable.erase(current_function);
262 void FunctionInliner::visit(ExpressionStatement &expr)
264 visit_and_inline(expr.expression);
267 void FunctionInliner::visit(VariableDeclaration &var)
269 if(var.init_expression)
270 visit_and_inline(var.init_expression);
274 void FunctionInliner::visit(FunctionDeclaration &func)
276 SetForScope<FunctionDeclaration *> set_func(current_function, &func);
277 TraversingVisitor::visit(func);
280 void FunctionInliner::visit(Conditional &cond)
282 visit_and_inline(cond.condition);
283 cond.body.visit(*this);
286 void FunctionInliner::visit(Iteration &iter)
288 SetForScope<Block *> set_block(current_block, &iter.body);
289 if(iter.init_statement)
290 iter.init_statement->visit(*this);
291 /* Skip the condition and loop expression parts because they're executed on
292 every iteration of the loop */
293 iter.body.visit(*this);
296 void FunctionInliner::visit(Return &ret)
299 visit_and_inline(ret.expression);
303 ConstantConditionEliminator::ConstantConditionEliminator():
307 void ConstantConditionEliminator::apply(Stage &stage)
309 stage.content.visit(*this);
310 NodeRemover().apply(stage, nodes_to_remove);
313 void ConstantConditionEliminator::visit(Block &block)
315 SetForScope<Block *> set_block(current_block, &block);
316 for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
322 for(map<string, VariableDeclaration *>::const_iterator i=block.variables.begin(); i!=block.variables.end(); ++i)
323 variable_values.erase(i->second);
326 void ConstantConditionEliminator::visit(UnaryExpression &unary)
328 if(VariableReference *var = dynamic_cast<VariableReference *>(unary.expression.get()))
329 if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
330 variable_values.erase(var->declaration);
333 void ConstantConditionEliminator::visit(Assignment &assign)
335 variable_values.erase(assign.target_declaration);
338 void ConstantConditionEliminator::visit(VariableDeclaration &var)
340 bool constant = var.constant;
341 if(constant && var.layout)
343 for(vector<Layout::Qualifier>::const_iterator i=var.layout->qualifiers.begin(); (constant && i!=var.layout->qualifiers.end()); ++i)
344 constant = (i->name!="constant_id");
346 if((constant || current_block->parent) && var.init_expression)
347 variable_values[&var] = var.init_expression.get();
350 void ConstantConditionEliminator::visit(Conditional &cond)
354 ExpressionEvaluator eval(variable_values);
355 cond.condition->visit(eval);
356 if(eval.is_result_valid())
358 Block &block = (eval.get_result() ? cond.body : cond.else_body);
359 current_block->body.splice(insert_point, block.body);
360 nodes_to_remove.insert(&cond);
365 TraversingVisitor::visit(cond);
368 void ConstantConditionEliminator::visit(Iteration &iter)
374 /* If the loop condition is always false on the first iteration, the
375 entire loop can be removed */
376 if(iter.init_statement)
377 iter.init_statement->visit(*this);
378 ExpressionEvaluator eval(variable_values);
379 iter.condition->visit(eval);
380 if(eval.is_result_valid() && !eval.get_result())
382 nodes_to_remove.insert(&iter);
387 /* Record all assignments that occur inside the loop body so those
388 variables won't be considered as constant */
389 SetFlag set_record(record_only);
390 TraversingVisitor::visit(iter);
393 TraversingVisitor::visit(iter);
395 if(VariableDeclaration *init_decl = dynamic_cast<VariableDeclaration *>(iter.init_statement.get()))
396 variable_values.erase(init_decl);
400 UnusedVariableRemover::VariableInfo::VariableInfo():
402 conditionally_assigned(false),
407 UnusedVariableRemover::UnusedVariableRemover():
410 assignment_target(false),
411 r_assign_to_subscript(false),
412 r_side_effects(false)
415 bool UnusedVariableRemover::apply(Stage &stage)
417 variables.push_back(BlockVariableMap());
418 stage.content.visit(*this);
419 BlockVariableMap &global_variables = variables.back();
420 for(BlockVariableMap::iterator i=global_variables.begin(); i!=global_variables.end(); ++i)
422 if(i->first->interface=="out" && (stage.type==Stage::FRAGMENT || i->first->linked_declaration || !i->first->name.compare(0, 3, "gl_")))
424 if(!i->second.referenced)
426 unused_nodes.insert(i->first);
427 clear_assignments(i->second, true);
430 variables.pop_back();
432 NodeRemover().apply(stage, unused_nodes);
434 return !unused_nodes.empty();
437 void UnusedVariableRemover::visit(VariableReference &var)
439 map<VariableDeclaration *, Node *>::iterator i = aggregates.find(var.declaration);
440 if(i!=aggregates.end())
441 unused_nodes.erase(i->second);
443 if(var.declaration && !assignment_target)
445 VariableInfo &var_info = variables.back()[var.declaration];
446 clear_assignments(var_info, false);
447 var_info.referenced = true;
451 void UnusedVariableRemover::visit(InterfaceBlockReference &iface)
453 unused_nodes.erase(iface.declaration);
456 void UnusedVariableRemover::visit(MemberAccess &memacc)
458 TraversingVisitor::visit(memacc);
459 unused_nodes.erase(memacc.declaration);
462 void UnusedVariableRemover::visit(UnaryExpression &unary)
464 TraversingVisitor::visit(unary);
465 if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
466 r_side_effects = true;
469 void UnusedVariableRemover::visit(BinaryExpression &binary)
471 if(binary.oper->token[0]=='[')
473 if(assignment_target)
474 r_assign_to_subscript = true;
475 binary.left->visit(*this);
476 SetForScope<bool> set(assignment_target, false);
477 binary.right->visit(*this);
480 TraversingVisitor::visit(binary);
483 void UnusedVariableRemover::visit(Assignment &assign)
486 r_assign_to_subscript = false;
487 SetForScope<bool> set(assignment_target, !assign.self_referencing);
488 assign.left->visit(*this);
490 assign.right->visit(*this);
491 r_assignment = &assign;
492 r_side_effects = true;
495 void UnusedVariableRemover::visit(FunctionCall &call)
497 TraversingVisitor::visit(call);
498 r_side_effects = true;
501 void UnusedVariableRemover::record_assignment(VariableDeclaration &var, Node &node, bool chained)
503 VariableInfo &var_info = variables.back()[&var];
505 clear_assignments(var_info, true);
506 var_info.assignments.push_back(&node);
507 var_info.conditionally_assigned = false;
510 void UnusedVariableRemover::clear_assignments(VariableInfo &var_info, bool mark_unused)
514 for(vector<Node *>::iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i)
515 unused_nodes.insert(*i);
517 var_info.assignments.clear();
520 void UnusedVariableRemover::visit(ExpressionStatement &expr)
523 r_side_effects = false;
524 TraversingVisitor::visit(expr);
525 if(r_assignment && r_assignment->target_declaration)
526 record_assignment(*r_assignment->target_declaration, expr, (r_assignment->self_referencing || r_assign_to_subscript));
528 unused_nodes.insert(&expr);
531 void UnusedVariableRemover::visit(StructDeclaration &strct)
533 SetForScope<Node *> set(aggregate, &strct);
534 unused_nodes.insert(&strct);
535 TraversingVisitor::visit(strct);
538 void UnusedVariableRemover::visit(VariableDeclaration &var)
541 aggregates[&var] = aggregate;
544 variables.back()[&var].local = true;
545 if(var.init_expression)
546 record_assignment(var, *var.init_expression, false);
548 unused_nodes.erase(var.type_declaration);
549 TraversingVisitor::visit(var);
552 void UnusedVariableRemover::visit(InterfaceBlock &iface)
554 SetForScope<Node *> set(aggregate, &iface);
555 unused_nodes.insert(&iface);
556 TraversingVisitor::visit(iface);
559 void UnusedVariableRemover::visit(FunctionDeclaration &func)
561 variables.push_back(BlockVariableMap());
563 for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
565 func.body.visit(*this);
567 BlockVariableMap &block_variables = variables.back();
568 for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
569 i->second.conditionally_assigned = true;
570 for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
571 block_variables[i->get()].referenced = true;
572 merge_down_variables();
575 void UnusedVariableRemover::merge_down_variables()
577 BlockVariableMap &parent_variables = variables[variables.size()-2];
578 BlockVariableMap &block_variables = variables.back();
579 for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
583 if(!i->second.referenced)
584 unused_nodes.insert(i->first);
585 clear_assignments(i->second, i->first->interface!="out");
589 BlockVariableMap::iterator j = parent_variables.find(i->first);
590 if(j==parent_variables.end())
591 parent_variables.insert(*i);
594 if(i->second.referenced || !i->second.conditionally_assigned)
595 clear_assignments(j->second, !i->second.referenced);
596 j->second.conditionally_assigned = i->second.conditionally_assigned;
597 j->second.referenced |= i->second.referenced;
598 j->second.assignments.insert(j->second.assignments.end(), i->second.assignments.begin(), i->second.assignments.end());
601 variables.pop_back();
604 void UnusedVariableRemover::visit(Conditional &cond)
606 cond.condition->visit(*this);
607 variables.push_back(BlockVariableMap());
608 cond.body.visit(*this);
610 BlockVariableMap if_variables;
611 swap(variables.back(), if_variables);
612 cond.else_body.visit(*this);
614 BlockVariableMap &else_variables = variables.back();
615 for(BlockVariableMap::iterator i=else_variables.begin(); i!=else_variables.end(); ++i)
617 BlockVariableMap::iterator j = if_variables.find(i->first);
618 if(j!=if_variables.end())
620 i->second.assignments.insert(i->second.assignments.end(), j->second.assignments.begin(), j->second.assignments.end());
621 i->second.conditionally_assigned |= j->second.conditionally_assigned;
622 if_variables.erase(j);
625 i->second.conditionally_assigned = true;
628 for(BlockVariableMap::iterator i=if_variables.begin(); i!=if_variables.end(); ++i)
630 i->second.conditionally_assigned = true;
631 else_variables.insert(*i);
634 merge_down_variables();
637 void UnusedVariableRemover::visit(Iteration &iter)
639 variables.push_back(BlockVariableMap());
640 TraversingVisitor::visit(iter);
642 BlockVariableMap &block_variables = variables.back();
643 for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
644 if(!i->second.local && i->second.referenced)
645 clear_assignments(i->second, false);
647 merge_down_variables();
651 bool UnusedFunctionRemover::apply(Stage &stage)
653 stage.content.visit(*this);
654 NodeRemover().apply(stage, unused_nodes);
655 return !unused_nodes.empty();
658 void UnusedFunctionRemover::visit(FunctionCall &call)
660 TraversingVisitor::visit(call);
662 unused_nodes.erase(call.declaration);
663 if(call.declaration && call.declaration->definition!=call.declaration)
664 used_definitions.insert(call.declaration->definition);
667 void UnusedFunctionRemover::visit(FunctionDeclaration &func)
669 TraversingVisitor::visit(func);
671 if((func.name!="main" || func.body.body.empty()) && !used_definitions.count(&func))
672 unused_nodes.insert(&func);