diff options
Diffstat (limited to 'src/compiler/glsl')
-rw-r--r-- | src/compiler/glsl/Makefile.sources | 1 | ||||
-rw-r--r-- | src/compiler/glsl/ast_to_hir.cpp | 4 | ||||
-rw-r--r-- | src/compiler/glsl/glsl_parser_extras.cpp | 1 | ||||
-rw-r--r-- | src/compiler/glsl/ir.h | 7 | ||||
-rw-r--r-- | src/compiler/glsl/ir_optimization.h | 4 | ||||
-rw-r--r-- | src/compiler/glsl/link_varyings.cpp | 210 | ||||
-rw-r--r-- | src/compiler/glsl/lower_packed_varyings.cpp | 34 | ||||
-rw-r--r-- | src/compiler/glsl/opt_algebraic.cpp | 19 | ||||
-rw-r--r-- | src/compiler/glsl/opt_rebalance_tree.cpp | 16 | ||||
-rw-r--r-- | src/compiler/glsl/propagate_invariance.cpp | 125 |
10 files changed, 361 insertions, 60 deletions
diff --git a/src/compiler/glsl/Makefile.sources b/src/compiler/glsl/Makefile.sources index 3f537d5..970fab0 100644 --- a/src/compiler/glsl/Makefile.sources +++ b/src/compiler/glsl/Makefile.sources @@ -217,6 +217,7 @@ LIBGLSL_FILES = \ opt_tree_grafting.cpp \ opt_vectorize.cpp \ program.h \ + propagate_invariance.cpp \ s_expression.cpp \ s_expression.h diff --git a/src/compiler/glsl/ast_to_hir.cpp b/src/compiler/glsl/ast_to_hir.cpp index 5262bd8..35def8e 100644 --- a/src/compiler/glsl/ast_to_hir.cpp +++ b/src/compiler/glsl/ast_to_hir.cpp @@ -2125,7 +2125,9 @@ process_array_size(exec_node *node, } ir_constant *const size = ir->constant_expression_value(); - if (size == NULL || array_size->has_sequence_subexpression()) { + if (size == NULL || + (state->is_version(120, 300) && + array_size->has_sequence_subexpression())) { _mesa_glsl_error(& loc, state, "array size must be a " "constant valued expression"); return 0; diff --git a/src/compiler/glsl/glsl_parser_extras.cpp b/src/compiler/glsl/glsl_parser_extras.cpp index 1ac8489..5d010fd 100644 --- a/src/compiler/glsl/glsl_parser_extras.cpp +++ b/src/compiler/glsl/glsl_parser_extras.cpp @@ -1887,6 +1887,7 @@ do_common_optimization(exec_list *ir, bool linked, OPT(do_dead_functions, ir); OPT(do_structure_splitting, ir); } + propagate_invariance(ir); OPT(do_if_simplification, ir); OPT(opt_flatten_nested_if_blocks, ir); OPT(opt_conditional_discard, ir); diff --git a/src/compiler/glsl/ir.h b/src/compiler/glsl/ir.h index f451967..b74d68a 100644 --- a/src/compiler/glsl/ir.h +++ b/src/compiler/glsl/ir.h @@ -720,6 +720,13 @@ public: unsigned is_unmatched_generic_inout:1; /** + * Is this varying used only by transform feedback? + * + * This is used by the linker to decide if its safe to pack the varying. + */ + unsigned is_xfb_only:1; + + /** * If non-zero, then this variable may be packed along with other variables * into a single varying slot, so this offset should be applied when * accessing components. For example, an offset of 1 means that the x diff --git a/src/compiler/glsl/ir_optimization.h b/src/compiler/glsl/ir_optimization.h index b56413a..f9599a3 100644 --- a/src/compiler/glsl/ir_optimization.h +++ b/src/compiler/glsl/ir_optimization.h @@ -124,7 +124,8 @@ void lower_shared_reference(struct gl_shader *shader, unsigned *shared_size); void lower_ubo_reference(struct gl_shader *shader); void lower_packed_varyings(void *mem_ctx, unsigned locations_used, ir_variable_mode mode, - unsigned gs_input_vertices, gl_shader *shader); + unsigned gs_input_vertices, gl_shader *shader, + bool disable_varying_packing, bool xfb_enabled); bool lower_vector_insert(exec_list *instructions, bool lower_nonconstant_index); bool lower_vector_derefs(gl_shader *shader); void lower_named_interface_blocks(void *mem_ctx, gl_shader *shader); @@ -138,6 +139,7 @@ bool lower_tess_level(gl_shader *shader); bool lower_vertex_id(gl_shader *shader); bool lower_subroutine(exec_list *instructions, struct _mesa_glsl_parse_state *state); +void propagate_invariance(exec_list *instructions); ir_rvalue * compare_index_block(exec_list *instructions, ir_variable *index, diff --git a/src/compiler/glsl/link_varyings.cpp b/src/compiler/glsl/link_varyings.cpp index 34eb848..44fc8f6 100644 --- a/src/compiler/glsl/link_varyings.cpp +++ b/src/compiler/glsl/link_varyings.cpp @@ -826,7 +826,7 @@ namespace { class varying_matches { public: - varying_matches(bool disable_varying_packing, + varying_matches(bool disable_varying_packing, bool xfb_enabled, gl_shader_stage producer_stage, gl_shader_stage consumer_stage); ~varying_matches(); @@ -836,14 +836,30 @@ public: void store_locations() const; private: + bool is_varying_packing_safe(const glsl_type *type, + const ir_variable *var); + /** * If true, this driver disables varying packing, so all varyings need to * be aligned on slot boundaries, and take up a number of slots equal to * their number of matrix columns times their array size. + * + * Packing may also be disabled because our current packing method is not + * safe in SSO or versions of OpenGL where interpolation qualifiers are not + * guaranteed to match across stages. */ const bool disable_varying_packing; /** + * If true, this driver has transform feedback enabled. The transform + * feedback code requires at least some packing be done even when varying + * packing is disabled, fortunately where transform feedback requires + * packing it's safe to override the disabled setting. See + * is_varying_packing_safe(). + */ + const bool xfb_enabled; + + /** * Enum representing the order in which varyings are packed within a * packing class. * @@ -862,6 +878,7 @@ private: static unsigned compute_packing_class(const ir_variable *var); static packing_order_enum compute_packing_order(const ir_variable *var); static int match_comparator(const void *x_generic, const void *y_generic); + static int xfb_comparator(const void *x_generic, const void *y_generic); /** * Structure recording the relationship between a single producer output @@ -917,9 +934,11 @@ private: } /* anonymous namespace */ varying_matches::varying_matches(bool disable_varying_packing, + bool xfb_enabled, gl_shader_stage producer_stage, gl_shader_stage consumer_stage) : disable_varying_packing(disable_varying_packing), + xfb_enabled(xfb_enabled), producer_stage(producer_stage), consumer_stage(consumer_stage) { @@ -942,6 +961,24 @@ varying_matches::~varying_matches() /** + * Packing is always safe on individual arrays, structure and matices. It is + * also safe if the varying is only used for transform feedback. + */ +bool +varying_matches::is_varying_packing_safe(const glsl_type *type, + const ir_variable *var) +{ + if (consumer_stage == MESA_SHADER_TESS_EVAL || + consumer_stage == MESA_SHADER_TESS_CTRL || + producer_stage == MESA_SHADER_TESS_CTRL) + return false; + + return xfb_enabled && (type->is_array() || type->is_record() || + type->is_matrix() || var->data.is_xfb_only); +} + + +/** * Record the given producer/consumer variable pair in the list of variables * that should later be assigned locations. * @@ -1020,7 +1057,7 @@ varying_matches::record(ir_variable *producer_var, ir_variable *consumer_var) = this->compute_packing_class(var); this->matches[this->num_matches].packing_order = this->compute_packing_order(var); - if (this->disable_varying_packing) { + if (this->disable_varying_packing && !is_varying_packing_safe(type, var)) { unsigned slots = type->count_attribute_slots(false); this->matches[this->num_matches].num_components = slots * 4; } else { @@ -1046,37 +1083,28 @@ varying_matches::assign_locations(struct gl_shader_program *prog, uint64_t reserved_slots, bool separate_shader) { - /* We disable varying sorting for separate shader programs for the - * following reasons: - * - * 1/ All programs must sort the code in the same order to guarantee the - * interface matching. However varying_matches::record() will change the - * interpolation qualifier of some stages. - * - * 2/ GLSL version 4.50 removes the matching constrain on the interpolation - * qualifier. - * - * From Section 4.5 (Interpolation Qualifiers) of the GLSL 4.40 spec: - * - * "The type and presence of interpolation qualifiers of variables with - * the same name declared in all linked shaders for the same cross-stage - * interface must match, otherwise the link command will fail. - * - * When comparing an output from one stage to an input of a subsequent - * stage, the input and output don't match if their interpolation - * qualifiers (or lack thereof) are not the same." - * - * "It is a link-time error if, within the same stage, the interpolation - * qualifiers of variables of the same name do not match." + /* If packing has been disabled then we cannot safely sort the varyings by + * class as it may mean we are using a version of OpenGL where + * interpolation qualifiers are not guaranteed to be matching across + * shaders, sorting in this case could result in mismatching shader + * interfaces. + * When packing is disabled the sort orders varyings used by transform + * feedback first, but also depends on *undefined behaviour* of qsort to + * reverse the order of the varyings. See: xfb_comparator(). */ - if (!separate_shader) { + if (!this->disable_varying_packing) { /* Sort varying matches into an order that makes them easy to pack. */ qsort(this->matches, this->num_matches, sizeof(*this->matches), &varying_matches::match_comparator); + } else { + /* Only sort varyings that are only used by transform feedback. */ + qsort(this->matches, this->num_matches, sizeof(*this->matches), + &varying_matches::xfb_comparator); } unsigned generic_location = 0; unsigned generic_patch_location = MAX_VARYING*4; + bool previous_var_xfb_only = false; for (unsigned i = 0; i < this->num_matches; i++) { unsigned *location = &generic_location; @@ -1100,16 +1128,30 @@ varying_matches::assign_locations(struct gl_shader_program *prog, /* Advance to the next slot if this varying has a different packing * class than the previous one, and we're not already on a slot * boundary. + * + * Also advance to the next slot if packing is disabled. This makes sure + * we don't assign varyings the same locations which is possible + * because we still pack individual arrays, records and matrices even + * when packing is disabled. Note we don't advance to the next slot if + * we can pack varyings together that are only used for transform + * feedback. */ - if (i > 0 && - this->matches[i - 1].packing_class - != this->matches[i].packing_class) { + if ((this->disable_varying_packing && + !(previous_var_xfb_only && var->data.is_xfb_only)) || + (i > 0 && this->matches[i - 1].packing_class + != this->matches[i].packing_class )) { *location = ALIGN(*location, 4); } + previous_var_xfb_only = var->data.is_xfb_only; + unsigned num_elements = type->count_attribute_slots(is_vertex_input); - unsigned slot_end = this->disable_varying_packing ? 4 : - type->without_array()->vector_elements; + unsigned slot_end; + if (this->disable_varying_packing && + !is_varying_packing_safe(type, var)) + slot_end = 4; + else + slot_end = type->without_array()->vector_elements; slot_end += *location - 1; /* FIXME: We could be smarter in the below code and loop back over @@ -1133,7 +1175,8 @@ varying_matches::assign_locations(struct gl_shader_program *prog, /* Increase the slot to make sure there is enough room for next * array element. */ - if (this->disable_varying_packing) + if (this->disable_varying_packing && + !is_varying_packing_safe(type, var)) slot_end += 4; else slot_end += type->without_array()->vector_elements; @@ -1259,6 +1302,32 @@ varying_matches::match_comparator(const void *x_generic, const void *y_generic) /** + * Comparison function passed to qsort() to sort varyings used only by + * transform feedback when packing of other varyings is disabled. + */ +int +varying_matches::xfb_comparator(const void *x_generic, const void *y_generic) +{ + const match *x = (const match *) x_generic; + + if (x->producer_var != NULL && x->producer_var->data.is_xfb_only) + return match_comparator(x_generic, y_generic); + + /* FIXME: When the comparator returns 0 it means the elements being + * compared are equivalent. However the qsort documentation says: + * + * "The order of equivalent elements is undefined." + * + * In practice the sort ends up reversing the order of the varyings which + * means locations are also assigned in this reversed order and happens to + * be what we want. This is also whats happening in + * varying_matches::match_comparator(). + */ + return 0; +} + + +/** * Is the given variable a varying variable to be counted against the * limit in ctx->Const.MaxVarying? * This includes variables such as texcoords, colors and generic @@ -1573,26 +1642,60 @@ assign_varying_locations(struct gl_context *ctx, unsigned num_tfeedback_decls, tfeedback_decl *tfeedback_decls) { - if (ctx->Const.DisableVaryingPacking) { - /* Transform feedback code assumes varyings are packed, so if the driver - * has disabled varying packing, make sure it does not support transform - * feedback. - */ - assert(!ctx->Extensions.EXT_transform_feedback); - } - /* Tessellation shaders treat inputs and outputs as shared memory and can * access inputs and outputs of other invocations. * Therefore, they can't be lowered to temps easily (and definitely not * efficiently). */ - bool disable_varying_packing = - ctx->Const.DisableVaryingPacking || + bool unpackable_tess = (consumer && consumer->Stage == MESA_SHADER_TESS_EVAL) || (consumer && consumer->Stage == MESA_SHADER_TESS_CTRL) || (producer && producer->Stage == MESA_SHADER_TESS_CTRL); - varying_matches matches(disable_varying_packing, + /* Transform feedback code assumes varying arrays are packed, so if the + * driver has disabled varying packing, make sure to at least enable + * packing required by transform feedback. + */ + bool xfb_enabled = + ctx->Extensions.EXT_transform_feedback && !unpackable_tess; + + /* Disable varying packing for GL 4.4+ as there is no guarantee + * that interpolation qualifiers will match between shaders in these + * versions. We also disable packing on outerward facing interfaces for + * SSO because in ES we need to retain the unpacked varying information + * for draw time validation. For desktop GL we could allow packing for + * versions < 4.4 but its just safer not to do packing. + * + * Packing is still enabled on individual arrays, structs, and matrices as + * these are required by the transform feedback code and it is still safe + * to do so. We also enable packing when a varying is only used for + * transform feedback and its not a SSO. + * + * Varying packing currently only packs together varyings with matching + * interpolation qualifiers as the backends assume all packed components + * are to be processed in the same way. Therefore we cannot do packing in + * these versions of GL without the risk of mismatching interfaces. + * + * From Section 4.5 (Interpolation Qualifiers) of the GLSL 4.30 spec: + * + * "The type and presence of interpolation qualifiers of variables with + * the same name declared in all linked shaders for the same cross-stage + * interface must match, otherwise the link command will fail. + * + * When comparing an output from one stage to an input of a subsequent + * stage, the input and output don't match if their interpolation + * qualifiers (or lack thereof) are not the same." + * + * This text was also in at least revison 7 of the 4.40 spec but is no + * longer in revision 9 and not in the 4.50 spec. + */ + bool disable_varying_packing = + ctx->Const.DisableVaryingPacking || unpackable_tess; + if ((ctx->API == API_OPENGL_CORE && ctx->Version >= 44) || + (prog->SeparateShader && (producer == NULL || consumer == NULL))) + disable_varying_packing = true; + + varying_matches matches(disable_varying_packing, xfb_enabled, producer ? producer->Stage : (gl_shader_stage)-1, consumer ? consumer->Stage : (gl_shader_stage)-1); hash_table *tfeedback_candidates @@ -1711,8 +1814,10 @@ assign_varying_locations(struct gl_context *ctx, return false; } - if (matched_candidate->toplevel_var->data.is_unmatched_generic_inout) + if (matched_candidate->toplevel_var->data.is_unmatched_generic_inout) { + matched_candidate->toplevel_var->data.is_xfb_only = 1; matches.record(matched_candidate->toplevel_var, NULL); + } } const uint64_t reserved_slots = @@ -1784,15 +1889,16 @@ assign_varying_locations(struct gl_context *ctx, ir_var_shader_in); } - if (!disable_varying_packing) { - if (producer) { - lower_packed_varyings(mem_ctx, slots_used, ir_var_shader_out, - 0, producer); - } - if (consumer) { - lower_packed_varyings(mem_ctx, slots_used, ir_var_shader_in, - consumer_vertices, consumer); - } + if (producer) { + lower_packed_varyings(mem_ctx, slots_used, ir_var_shader_out, + 0, producer, disable_varying_packing, + xfb_enabled); + } + + if (consumer) { + lower_packed_varyings(mem_ctx, slots_used, ir_var_shader_in, + consumer_vertices, consumer, + disable_varying_packing, xfb_enabled); } return true; diff --git a/src/compiler/glsl/lower_packed_varyings.cpp b/src/compiler/glsl/lower_packed_varyings.cpp index 8d1eb17..825cc9e 100644 --- a/src/compiler/glsl/lower_packed_varyings.cpp +++ b/src/compiler/glsl/lower_packed_varyings.cpp @@ -168,7 +168,9 @@ public: ir_variable_mode mode, unsigned gs_input_vertices, exec_list *out_instructions, - exec_list *out_variables); + exec_list *out_variables, + bool disable_varying_packing, + bool xfb_enabled); void run(struct gl_shader *shader); @@ -231,6 +233,9 @@ private: * Exec list into which the visitor should insert any new variables. */ exec_list *out_variables; + + bool disable_varying_packing; + bool xfb_enabled; }; } /* anonymous namespace */ @@ -238,7 +243,8 @@ private: lower_packed_varyings_visitor::lower_packed_varyings_visitor( void *mem_ctx, unsigned locations_used, ir_variable_mode mode, unsigned gs_input_vertices, exec_list *out_instructions, - exec_list *out_variables) + exec_list *out_variables, bool disable_varying_packing, + bool xfb_enabled) : mem_ctx(mem_ctx), locations_used(locations_used), packed_varyings((ir_variable **) @@ -247,7 +253,9 @@ lower_packed_varyings_visitor::lower_packed_varyings_visitor( mode(mode), gs_input_vertices(gs_input_vertices), out_instructions(out_instructions), - out_variables(out_variables) + out_variables(out_variables), + disable_varying_packing(disable_varying_packing), + xfb_enabled(xfb_enabled) { } @@ -656,7 +664,18 @@ lower_packed_varyings_visitor::needs_lowering(ir_variable *var) if (var->data.explicit_location) return false; - const glsl_type *type = var->type->without_array(); + /* Override disable_varying_packing if the var is only used by transform + * feedback. Also override it if transform feedback is enabled and the + * variable is an array, struct or matrix as the elements of these types + * will always has the same interpolation and therefore asre safe to pack. + */ + const glsl_type *type = var->type; + if (disable_varying_packing && !var->data.is_xfb_only && + !((type->is_array() || type->is_record() || type->is_matrix()) && + xfb_enabled)) + return false; + + type = type->without_array(); if (type->vector_elements == 4 && !type->is_double()) return false; return true; @@ -709,7 +728,8 @@ lower_packed_varyings_gs_splicer::visit_leave(ir_emit_vertex *ev) void lower_packed_varyings(void *mem_ctx, unsigned locations_used, ir_variable_mode mode, unsigned gs_input_vertices, - gl_shader *shader) + gl_shader *shader, bool disable_varying_packing, + bool xfb_enabled) { exec_list *instructions = shader->ir; ir_function *main_func = shader->symbols->get_function("main"); @@ -720,7 +740,9 @@ lower_packed_varyings(void *mem_ctx, unsigned locations_used, lower_packed_varyings_visitor visitor(mem_ctx, locations_used, mode, gs_input_vertices, &new_instructions, - &new_variables); + &new_variables, + disable_varying_packing, + xfb_enabled); visitor.run(shader); if (mode == ir_var_shader_out) { if (shader->Stage == MESA_SHADER_GEOMETRY) { diff --git a/src/compiler/glsl/opt_algebraic.cpp b/src/compiler/glsl/opt_algebraic.cpp index 1e58062..f5858c8 100644 --- a/src/compiler/glsl/opt_algebraic.cpp +++ b/src/compiler/glsl/opt_algebraic.cpp @@ -58,6 +58,8 @@ public: { } + virtual ir_visitor_status visit_enter(ir_assignment *ir); + ir_rvalue *handle_expression(ir_expression *ir); void handle_rvalue(ir_rvalue **rvalue); bool reassociate_constant(ir_expression *ir1, @@ -80,6 +82,23 @@ public: } /* unnamed namespace */ +ir_visitor_status +ir_algebraic_visitor::visit_enter(ir_assignment *ir) +{ + ir_variable *var = ir->lhs->variable_referenced(); + if (var->data.invariant || var->data.precise) { + /* If we're assigning to an invariant or precise variable, just bail. + * Most of the algebraic optimizations aren't precision-safe. + * + * FINISHME: Find out which optimizations are precision-safe and enable + * then only for invariant or precise trees. + */ + return visit_continue_with_parent; + } else { + return visit_continue; + } +} + static inline bool is_vec_zero(ir_constant *ir) { diff --git a/src/compiler/glsl/opt_rebalance_tree.cpp b/src/compiler/glsl/opt_rebalance_tree.cpp index 095f2d7..8045d51 100644 --- a/src/compiler/glsl/opt_rebalance_tree.cpp +++ b/src/compiler/glsl/opt_rebalance_tree.cpp @@ -131,6 +131,8 @@ public: progress = false; } + virtual ir_visitor_status visit_enter(ir_assignment *ir); + void handle_rvalue(ir_rvalue **rvalue); bool progress; @@ -146,6 +148,20 @@ struct is_reduction_data { } /* anonymous namespace */ +ir_visitor_status +ir_rebalance_visitor::visit_enter(ir_assignment *ir) +{ + ir_variable *var = ir->lhs->variable_referenced(); + if (var->data.invariant || var->data.precise) { + /* If we're assigning to an invariant variable, just bail. Tree + * rebalancing (reassociation) isn't precision-safe. + */ + return visit_continue_with_parent; + } else { + return visit_continue; + } +} + static bool is_reduction_operation(ir_expression_operation operation) { diff --git a/src/compiler/glsl/propagate_invariance.cpp b/src/compiler/glsl/propagate_invariance.cpp new file mode 100644 index 0000000..c137ff3 --- /dev/null +++ b/src/compiler/glsl/propagate_invariance.cpp @@ -0,0 +1,125 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * \file propagate_invariance.cpp + * Propagate the "invariant" and "precise" qualifiers to variables used to + * compute invariant or precise values. + * + * The GLSL spec (depending on what version you read) says, among the + * conditions for geting bit-for-bit the same values on an invariant output: + * + * "All operations in the consuming expressions and any intermediate + * expressions must be the same, with the same order of operands and same + * associativity, to give the same order of evaluation." + * + * This effectively means that if a variable is used to compute an invariant + * value then that variable becomes invariant. The same should apply to the + * "precise" qualifier. + */ + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_rvalue_visitor.h" +#include "ir_optimization.h" +#include "compiler/glsl_types.h" + +namespace { + +class ir_invariance_propagation_visitor : public ir_hierarchical_visitor { +public: + ir_invariance_propagation_visitor() + { + this->progress = false; + this->dst_var = NULL; + } + + virtual ~ir_invariance_propagation_visitor() + { + /* empty */ + } + + virtual ir_visitor_status visit_enter(ir_assignment *ir); + virtual ir_visitor_status visit_leave(ir_assignment *ir); + virtual ir_visitor_status visit(ir_dereference_variable *ir); + + ir_variable *dst_var; + bool progress; +}; + +} /* unnamed namespace */ + +ir_visitor_status +ir_invariance_propagation_visitor::visit_enter(ir_assignment *ir) +{ + assert(this->dst_var == NULL); + ir_variable *var = ir->lhs->variable_referenced(); + if (var->data.invariant || var->data.precise) { + this->dst_var = var; + return visit_continue; + } else { + return visit_continue_with_parent; + } +} + +ir_visitor_status +ir_invariance_propagation_visitor::visit_leave(ir_assignment *ir) +{ + this->dst_var = NULL; + + return visit_continue; +} + +ir_visitor_status +ir_invariance_propagation_visitor::visit(ir_dereference_variable *ir) +{ + if (this->dst_var == NULL) + return visit_continue; + + if (this->dst_var->data.invariant) { + if (!ir->var->data.invariant) + this->progress = true; + + ir->var->data.invariant = true; + } + + if (this->dst_var->data.precise) { + if (!ir->var->data.precise) + this->progress = true; + + ir->var->data.precise = true; + } + + return visit_continue; +} + +void +propagate_invariance(exec_list *instructions) +{ + ir_invariance_propagation_visitor visitor; + + do { + visitor.progress = false; + visit_list_elements(&visitor, instructions); + } while (visitor.progress); +} |