X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Fglsl%2Foptimize.cpp;h=593a3222533874b6d8fde6265608efd9642e46e1;hb=59347f76bc985e8c9c769d4a3eee672cba9c920b;hp=475e8b646de280261b0b1347bf1339a84de578b8;hpb=dfcf8ebc329b79c369b88bdbeb982480aa5fc12a;p=libs%2Fgl.git diff --git a/source/glsl/optimize.cpp b/source/glsl/optimize.cpp index 475e8b64..593a3222 100644 --- a/source/glsl/optimize.cpp +++ b/source/glsl/optimize.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "optimize.h" #include "reflect.h" @@ -75,7 +76,7 @@ void InlineableFunctionLocator::visit(FunctionCall &call) ++count; /* Don't inline functions which are called more than once or are called recursively. */ - if(count>1 || def==current_function) + if((count>1 && def->source!=BUILTIN_SOURCE) || def==current_function) inlineable.erase(def); } @@ -89,7 +90,7 @@ void InlineableFunctionLocator::visit(FunctionDeclaration &func) has_out_params = ((*i)->interface=="out"); unsigned &count = refcounts[func.definition]; - if(count<=1 && !has_out_params) + if((count<=1 || func.source==BUILTIN_SOURCE) && !has_out_params) inlineable.insert(func.definition); SetForScope set(current_function, &func); @@ -123,7 +124,7 @@ InlineContentInjector::InlineContentInjector(): pass(REFERENCED) { } -const string &InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList::iterator &ins_pt, FunctionCall &call) +string InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList::iterator &ins_pt, FunctionCall &call) { source_func = call.declaration->definition; @@ -138,7 +139,7 @@ const string &InlineContentInjector::apply(Stage &stage, FunctionDeclaration &ta staging_block.parent = &tgt_blk; staging_block.variables.clear(); - std::vector > params; + vector > params; params.reserve(source_func->parameters.size()); for(NodeArray::iterator i=source_func->parameters.begin(); i!=source_func->parameters.end(); ++i) { @@ -350,9 +351,9 @@ void FunctionInliner::visit(Iteration &iter) ExpressionInliner::ExpressionInliner(): r_ref_info(0), - r_any_inlined(false), r_trivial(false), - mutating(false), + access_read(true), + access_write(false), iteration_init(false), iteration_body(0), r_oper(0) @@ -361,62 +362,48 @@ ExpressionInliner::ExpressionInliner(): bool ExpressionInliner::apply(Stage &s) { s.content.visit(*this); - return r_any_inlined; -} -void ExpressionInliner::inline_expression(Expression &expr, RefPtr &ptr) -{ - ptr = expr.clone(); - r_any_inlined = true; -} - -void ExpressionInliner::visit(Block &block) -{ - TraversingVisitor::visit(block); - - for(map::iterator i=block.variables.begin(); i!=block.variables.end(); ++i) - { - map::iterator j = expressions.lower_bound(i->second); - for(; (j!=expressions.end() && j->first.declaration==i->second); ) + bool any_inlined = false; + for(list::iterator i=expressions.begin(); i!=expressions.end(); ++i) + if(i->expression && (i->trivial || i->uses.size()==1)) { - if(j->second.expression && j->second.inline_point) - inline_expression(*j->second.expression, *j->second.inline_point); - - expressions.erase(j++); + for(vector::iterator j=i->uses.begin(); j!=i->uses.end(); ++j) + if(!j->blocked) + { + *j->reference = i->expression->clone(); + any_inlined = true; + } } - } - /* Expressions assigned in this block may depend on local variables of the - block. If this is a conditionally executed block, the assignments might not - always happen. Mark the expressions as not available to any outer blocks. */ - for(map::iterator i=expressions.begin(); i!=expressions.end(); ++i) - if(i->second.assign_scope==&block) - i->second.available = false; + return any_inlined; } void ExpressionInliner::visit(RefPtr &expr) { r_ref_info = 0; expr->visit(*this); - if(r_ref_info && r_ref_info->expression && r_ref_info->available) + if(r_ref_info && r_ref_info->expression) { + ExpressionUse use; + use.reference = &expr; + use.ref_scope = current_block; + use.blocked = access_write; + if(iteration_body && !r_ref_info->trivial) { - /* Don't inline non-trivial expressions which were assigned outside - an iteration statement. The iteration may run multiple times, which + /* Block inlining of non-trivial expressions assigned outside an + iteration statement. The iteration may run multiple times, which would cause the expression to also be evaluated multiple times. */ - Block *i = r_ref_info->assign_scope; - for(; (i && i!=iteration_body); i=i->parent) ; - if(!i) - return; + for(Block *i=iteration_body->parent; (!use.blocked && i); i=i->parent) + use.blocked = (i==r_ref_info->assign_scope); } - if(r_ref_info->trivial) - inline_expression(*r_ref_info->expression, expr); - else - /* Record the inline point for a non-trivial expression but don't - inline it yet. It might turn out it shouldn't be inlined after all. */ - r_ref_info->inline_point = &expr; + /* Block inlining assignments from from inner scopes. The assignment may + depend on local variables of that scope or may not always be executed. */ + for(Block *i=r_ref_info->assign_scope->parent; (!use.blocked && i); i=i->parent) + use.blocked = (i==current_block); + + r_ref_info->uses.push_back(use); } r_oper = expr->oper; r_ref_info = 0; @@ -424,21 +411,11 @@ void ExpressionInliner::visit(RefPtr &expr) void ExpressionInliner::visit(VariableReference &var) { - if(var.declaration) + if(var.declaration && access_read) { - map::iterator i = expressions.find(var.declaration); - if(i!=expressions.end()) - { - /* If a non-trivial expression is referenced multiple times, don't - inline it. */ - if(i->second.inline_point && !i->second.trivial) - i->second.expression = 0; - /* Mutating expressions are analogous to self-referencing assignments - and prevent inlining. */ - if(mutating) - i->second.expression = 0; - r_ref_info = &i->second; - } + map::iterator i = assignments.find(var.declaration); + if(i!=assignments.end()) + r_ref_info = i->second; } } @@ -456,7 +433,7 @@ void ExpressionInliner::visit(Swizzle &swizzle) void ExpressionInliner::visit(UnaryExpression &unary) { - SetFlag set_target(mutating, mutating || unary.oper->token[1]=='+' || unary.oper->token[1]=='-'); + SetFlag set_write(access_write, access_write || unary.oper->token[1]=='+' || unary.oper->token[1]=='-'); visit(unary.expression); r_trivial = false; } @@ -465,7 +442,7 @@ void ExpressionInliner::visit(BinaryExpression &binary) { visit(binary.left); { - SetFlag clear_target(mutating, false); + SetFlag clear_write(access_write, false); visit(binary.right); } r_trivial = false; @@ -474,21 +451,37 @@ void ExpressionInliner::visit(BinaryExpression &binary) void ExpressionInliner::visit(Assignment &assign) { { - SetFlag set_target(mutating); + SetFlag set_read(access_read, assign.oper->token[0]!='='); + SetFlag set_write(access_write); visit(assign.left); } r_oper = 0; + r_trivial = true; visit(assign.right); - map::iterator i = expressions.find(assign.target); - if(i!=expressions.end()) + map::iterator i = assignments.find(assign.target); + if(i!=assignments.end()) { - /* Self-referencing assignments can't be inlined without additional - work. Just clear any previous expression. */ - i->second.expression = (assign.self_referencing ? 0 : assign.right.get()); - i->second.assign_scope = current_block; - i->second.inline_point = 0; - i->second.available = true; + if(iteration_body && i->second->expression) + { + /* Block inlining into previous references within the iteration + statement. On iterations after the first they would refer to the + assignment within the iteration. */ + for(vector::iterator j=i->second->uses.begin(); j!=i->second->uses.end(); ++j) + for(Block *k=j->ref_scope; (!j->blocked && k); k=k->parent) + j->blocked = (k==iteration_body); + } + + expressions.push_back(ExpressionInfo()); + ExpressionInfo &info = expressions.back(); + info.target = assign.target; + // Self-referencing assignments can't be inlined without additional work. + if(!assign.self_referencing) + info.expression = assign.right; + info.assign_scope = current_block; + info.trivial = r_trivial; + + i->second = &info; } r_trivial = false; @@ -526,12 +519,17 @@ void ExpressionInliner::visit(VariableDeclaration &var) analyze and non-trivial expressions could be expensive to inline. */ if((current_block->parent || (constant && r_trivial)) && var.interface.empty()) { - ExpressionInfo &info = expressions[&var]; + expressions.push_back(ExpressionInfo()); + ExpressionInfo &info = expressions.back(); + info.target = &var; /* Assume variables declared in an iteration initialization statement will have their values change throughout the iteration. */ - info.expression = (iteration_init ? 0 : var.init_expression.get()); + if(!iteration_init) + info.expression = var.init_expression; info.assign_scope = current_block; info.trivial = r_trivial; + + assignments[&var] = &info; } } @@ -553,18 +551,6 @@ void ExpressionInliner::visit(Iteration &iter) } -BasicTypeDeclaration::Kind ConstantFolder::get_value_kind(const Variant &value) -{ - if(value.check_type()) - return BasicTypeDeclaration::BOOL; - else if(value.check_type()) - return BasicTypeDeclaration::INT; - else if(value.check_type()) - return BasicTypeDeclaration::FLOAT; - else - return BasicTypeDeclaration::VOID; -} - template T ConstantFolder::evaluate_logical(char oper, T left, T right) { @@ -603,6 +589,31 @@ T ConstantFolder::evaluate_arithmetic(char oper, T left, T right) } } +template +T ConstantFolder::evaluate_int_special_op(char oper, T left, T right) +{ + switch(oper) + { + case '%': return left%right; + case '<': return left<': return left>>right; + default: return T(); + } +} + +template +void ConstantFolder::convert_to_result(const Variant &value) +{ + if(value.check_type()) + set_result(static_cast(value.value())); + else if(value.check_type()) + set_result(static_cast(value.value())); + else if(value.check_type()) + set_result(static_cast(value.value())); + else if(value.check_type()) + set_result(static_cast(value.value())); +} + void ConstantFolder::set_result(const Variant &value, bool literal) { r_constant_value = value; @@ -623,22 +634,27 @@ void ConstantFolder::visit(RefPtr &expr) if(!r_constant || r_literal || r_uses_iter_var) return; - BasicTypeDeclaration::Kind kind = get_value_kind(r_constant_value); - if(kind==BasicTypeDeclaration::VOID) + RefPtr literal = new Literal; + if(r_constant_value.check_type()) + literal->token = (r_constant_value.value() ? "true" : "false"); + else if(r_constant_value.check_type()) + literal->token = lexical_cast(r_constant_value.value()); + else if(r_constant_value.check_type()) + literal->token = lexical_cast(r_constant_value.value())+"u"; + else if(r_constant_value.check_type()) + { + literal->token = lexical_cast(r_constant_value.value(), Fmt().precision(8)); + if(literal->token.find('.')==string::npos && literal->token.find('e')==string::npos) + literal->token += ".0"; + } + else { r_constant = false; return; } - - RefPtr literal = new Literal; - if(kind==BasicTypeDeclaration::BOOL) - literal->token = (r_constant_value.value() ? "true" : "false"); - else if(kind==BasicTypeDeclaration::INT) - literal->token = lexical_cast(r_constant_value.value()); - else if(kind==BasicTypeDeclaration::FLOAT) - literal->token = lexical_cast(r_constant_value.value()); literal->value = r_constant_value; expr = literal; + r_any_folded = true; } void ConstantFolder::visit(Literal &literal) @@ -678,25 +694,27 @@ void ConstantFolder::visit(UnaryExpression &unary) if(!can_fold) return; - BasicTypeDeclaration::Kind kind = get_value_kind(r_constant_value); - char oper = unary.oper->token[0]; char oper2 = unary.oper->token[1]; if(oper=='!') { - if(kind==BasicTypeDeclaration::BOOL) + if(r_constant_value.check_type()) set_result(!r_constant_value.value()); } else if(oper=='~') { - if(kind==BasicTypeDeclaration::INT) + if(r_constant_value.check_type()) set_result(~r_constant_value.value()); + else if(r_constant_value.check_type()) + set_result(~r_constant_value.value()); } else if(oper=='-' && !oper2) { - if(kind==BasicTypeDeclaration::INT) + if(r_constant_value.check_type()) set_result(-r_constant_value.value()); - else if(kind==BasicTypeDeclaration::FLOAT) + else if(r_constant_value.check_type()) + set_result(-r_constant_value.value()); + else if(r_constant_value.check_type()) set_result(-r_constant_value.value()); } } @@ -716,53 +734,54 @@ void ConstantFolder::visit(BinaryExpression &binary) if(!can_fold) return; - BasicTypeDeclaration::Kind left_kind = get_value_kind(left_value); - BasicTypeDeclaration::Kind right_kind = get_value_kind(r_constant_value); // Currently only expressions with both sides of equal types are handled. - if(left_kind!=right_kind) + if(!left_value.check_same_type(r_constant_value)) return; char oper = binary.oper->token[0]; char oper2 = binary.oper->token[1]; if(oper=='&' || oper=='|' || oper=='^') { - if(oper2==oper && left_kind==BasicTypeDeclaration::BOOL) + if(oper2==oper && left_value.check_type()) set_result(evaluate_logical(oper, left_value.value(), r_constant_value.value())); - else if(!oper2 && left_kind==BasicTypeDeclaration::INT) + else if(!oper2 && left_value.check_type()) set_result(evaluate_logical(oper, left_value.value(), r_constant_value.value())); + else if(!oper2 && left_value.check_type()) + set_result(evaluate_logical(oper, left_value.value(), r_constant_value.value())); } else if((oper=='<' || oper=='>') && oper2!=oper) { - if(left_kind==BasicTypeDeclaration::INT) + if(left_value.check_type()) set_result(evaluate_relation(binary.oper->token, left_value.value(), r_constant_value.value())); - else if(left_kind==BasicTypeDeclaration::FLOAT) + else if(left_value.check_type()) + set_result(evaluate_relation(binary.oper->token, left_value.value(), r_constant_value.value())); + else if(left_value.check_type()) set_result(evaluate_relation(binary.oper->token, left_value.value(), r_constant_value.value())); } else if((oper=='=' || oper=='!') && oper2=='=') { - if(left_kind==BasicTypeDeclaration::INT) + if(left_value.check_type()) set_result((left_value.value()==r_constant_value.value()) == (oper=='=')); - if(left_kind==BasicTypeDeclaration::FLOAT) + else if(left_value.check_type()) + set_result((left_value.value()==r_constant_value.value()) == (oper=='=')); + else if(left_value.check_type()) set_result((left_value.value()==r_constant_value.value()) == (oper=='=')); } else if(oper=='+' || oper=='-' || oper=='*' || oper=='/') { - if(left_kind==BasicTypeDeclaration::INT) + if(left_value.check_type()) set_result(evaluate_arithmetic(oper, left_value.value(), r_constant_value.value())); - else if(left_kind==BasicTypeDeclaration::FLOAT) + else if(left_value.check_type()) + set_result(evaluate_arithmetic(oper, left_value.value(), r_constant_value.value())); + else if(left_value.check_type()) set_result(evaluate_arithmetic(oper, left_value.value(), r_constant_value.value())); } else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper)) { - if(left_kind!=BasicTypeDeclaration::INT) - return; - - if(oper=='%') - set_result(left_value.value()%r_constant_value.value()); - else if(oper=='<') - set_result(left_value.value()<()); - else if(oper=='>') - set_result(left_value.value()>>r_constant_value.value()); + if(left_value.check_type()) + set_result(evaluate_int_special_op(oper, left_value.value(), r_constant_value.value())); + else if(left_value.check_type()) + set_result(evaluate_int_special_op(oper, left_value.value(), r_constant_value.value())); } } @@ -780,6 +799,30 @@ void ConstantFolder::visit(TernaryExpression &ternary) void ConstantFolder::visit(FunctionCall &call) { + if(call.constructor && call.type && call.arguments.size()==1) + { + const BasicTypeDeclaration *basic = dynamic_cast(call.type); + if(basic) + { + visit(call.arguments[0]); + bool can_fold = r_constant; + r_constant = false; + if(!can_fold) + return; + + if(basic->kind==BasicTypeDeclaration::BOOL) + convert_to_result(r_constant_value); + else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && basic->sign) + convert_to_result(r_constant_value); + else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && !basic->sign) + convert_to_result(r_constant_value); + else if(basic->kind==BasicTypeDeclaration::FLOAT && basic->size==32) + convert_to_result(r_constant_value); + + return; + } + } + TraversingVisitor::visit(call); r_constant = false; } @@ -907,40 +950,63 @@ void ConstantConditionEliminator::visit(Iteration &iter) } -bool UnusedTypeRemover::apply(Stage &stage) +UnreachableCodeRemover::UnreachableCodeRemover(): + reachable(true) +{ } + +bool UnreachableCodeRemover::apply(Stage &stage) { stage.content.visit(*this); - NodeRemover().apply(stage, unused_nodes); - return !unused_nodes.empty(); + NodeRemover().apply(stage, unreachable_nodes); + return !unreachable_nodes.empty(); } -void UnusedTypeRemover::visit(Literal &literal) +void UnreachableCodeRemover::visit(Block &block) { - unused_nodes.erase(literal.type); + NodeList::iterator i = block.body.begin(); + for(; (reachable && i!=block.body.end()); ++i) + (*i)->visit(*this); + for(; i!=block.body.end(); ++i) + unreachable_nodes.insert(i->get()); } -void UnusedTypeRemover::visit(UnaryExpression &unary) +void UnreachableCodeRemover::visit(FunctionDeclaration &func) { - unused_nodes.erase(unary.type); - TraversingVisitor::visit(unary); + TraversingVisitor::visit(func); + reachable = true; } -void UnusedTypeRemover::visit(BinaryExpression &binary) +void UnreachableCodeRemover::visit(Conditional &cond) { - unused_nodes.erase(binary.type); - TraversingVisitor::visit(binary); + cond.body.visit(*this); + bool reachable_if_true = reachable; + reachable = true; + cond.else_body.visit(*this); + + reachable |= reachable_if_true; } -void UnusedTypeRemover::visit(TernaryExpression &ternary) +void UnreachableCodeRemover::visit(Iteration &iter) { - unused_nodes.erase(ternary.type); - TraversingVisitor::visit(ternary); + TraversingVisitor::visit(iter); + + /* Always consider code after a loop reachable, since there's no checking + for whether the loop executes. */ + reachable = true; } -void UnusedTypeRemover::visit(FunctionCall &call) + +bool UnusedTypeRemover::apply(Stage &stage) { - unused_nodes.erase(call.type); - TraversingVisitor::visit(call); + stage.content.visit(*this); + NodeRemover().apply(stage, unused_nodes); + return !unused_nodes.empty(); +} + +void UnusedTypeRemover::visit(RefPtr &expr) +{ + unused_nodes.erase(expr->type); + TraversingVisitor::visit(expr); } void UnusedTypeRemover::visit(BasicTypeDeclaration &type) @@ -966,6 +1032,7 @@ void UnusedTypeRemover::visit(StructDeclaration &strct) void UnusedTypeRemover::visit(VariableDeclaration &var) { unused_nodes.erase(var.type_declaration); + TraversingVisitor::visit(var); } void UnusedTypeRemover::visit(InterfaceBlock &iface) @@ -985,7 +1052,10 @@ UnusedVariableRemover::UnusedVariableRemover(): interface_block(0), r_assignment(0), assignment_target(false), - r_side_effects(false) + r_side_effects(false), + in_struct(false), + composite_reference(false), + in_loop(0) { } bool UnusedVariableRemover::apply(Stage &s) @@ -1029,19 +1099,90 @@ void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &n var_info.referenced = true; if(!assignment_target) { + bool loop_external = false; for(vector::const_iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i) - (*i)->used_by.push_back(&node); + { + bool covered = true; + for(unsigned j=0; (covered && j<(*i)->target.chain_len && j((*i)->target.chain[j]&0xC0); + Assignment::Target::ChainType type2 = static_cast(target.chain[j]&0xC0); + if(type1==Assignment::Target::SWIZZLE || type2==Assignment::Target::SWIZZLE) + { + unsigned index1 = (*i)->target.chain[j]&0x3F; + unsigned index2 = target.chain[j]&0x3F; + if(type1==Assignment::Target::SWIZZLE && type2==Assignment::Target::SWIZZLE) + covered = index1&index2; + else if(type1==Assignment::Target::ARRAY && index1<4) + covered = index2&(1<target.chain[j]==target.chain[j]); + } + + if(covered) + { + (*i)->used_by.push_back(&node); + if((*i)->in_looptoken[0]=='[') { - binary.left->visit(*this); - SetFlag set(assignment_target, false); - binary.right->visit(*this); + visit_composite(*binary.left); + + { + SetFlag clear_assignment(assignment_target, false); + SetFlag clear_composite(composite_reference, false); + binary.right->visit(*this); + } + + add_to_chain(r_reference, Assignment::Target::ARRAY, 0x3F); + + if(!composite_reference && r_reference.declaration) + referenced(r_reference, binary); } else + { + SetFlag clear_composite(composite_reference, false); TraversingVisitor::visit(binary); + } +} + +void UnusedVariableRemover::visit(TernaryExpression &ternary) +{ + SetFlag clear_composite(composite_reference, false); + TraversingVisitor::visit(ternary); } void UnusedVariableRemover::visit(Assignment &assign) @@ -1076,6 +1235,7 @@ void UnusedVariableRemover::visit(Assignment &assign) void UnusedVariableRemover::visit(FunctionCall &call) { + SetFlag clear_composite(composite_reference, false); TraversingVisitor::visit(call); /* Treat function calls as having side effects so expression statements consisting of nothing but a function call won't be optimized away. */ @@ -1095,11 +1255,12 @@ void UnusedVariableRemover::record_assignment(const Assignment::Target &target, AssignmentInfo &assign_info = assignments.back(); assign_info.node = &node; assign_info.target = target; + assign_info.in_loop = in_loop; /* An assignment to the target hides any assignments to the same target or its subfields. */ VariableInfo &var_info = variables[target.declaration]; - for(unsigned i=0; itarget; @@ -1127,8 +1288,19 @@ void UnusedVariableRemover::visit(ExpressionStatement &expr) unused_nodes.insert(&expr); } +void UnusedVariableRemover::visit(StructDeclaration &strct) +{ + SetFlag set_struct(in_struct); + TraversingVisitor::visit(strct); +} + void UnusedVariableRemover::visit(VariableDeclaration &var) { + TraversingVisitor::visit(var); + + if(in_struct) + return; + VariableInfo &var_info = variables[&var]; var_info.interface_block = interface_block; @@ -1144,7 +1316,6 @@ void UnusedVariableRemover::visit(VariableDeclaration &var) var_info.initialized = true; record_assignment(&var, *var.init_expression); } - TraversingVisitor::visit(var); } void UnusedVariableRemover::visit(InterfaceBlock &iface) @@ -1216,7 +1387,18 @@ void UnusedVariableRemover::visit(Conditional &cond) void UnusedVariableRemover::visit(Iteration &iter) { BlockVariableMap saved_vars = variables; - TraversingVisitor::visit(iter); + vector saved_refs; + swap(loop_ext_refs, saved_refs); + { + SetForScope set_loop(in_loop, in_loop+1); + TraversingVisitor::visit(iter); + } + swap(loop_ext_refs, saved_refs); + + /* Visit the external references of the loop again to record assignments + done in the loop as used. */ + for(vector::const_iterator i=saved_refs.begin(); i!=saved_refs.end(); ++i) + (*i)->visit(*this); /* Merge assignments from the iteration, without clearing previous state. Further analysis is needed to determine which parts of the iteration body