summaryrefslogtreecommitdiffstats
path: root/src/compiler/glsl
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/glsl')
-rw-r--r--src/compiler/glsl/Makefile.sources1
-rw-r--r--src/compiler/glsl/ast_to_hir.cpp4
-rw-r--r--src/compiler/glsl/glsl_parser_extras.cpp1
-rw-r--r--src/compiler/glsl/ir.h7
-rw-r--r--src/compiler/glsl/ir_optimization.h4
-rw-r--r--src/compiler/glsl/link_varyings.cpp210
-rw-r--r--src/compiler/glsl/lower_packed_varyings.cpp34
-rw-r--r--src/compiler/glsl/opt_algebraic.cpp19
-rw-r--r--src/compiler/glsl/opt_rebalance_tree.cpp16
-rw-r--r--src/compiler/glsl/propagate_invariance.cpp125
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);
+}