glsl: In ir_set_program_inouts, handle indexing outside array/matrix bounds.
authorPaul Berry <stereotype441@gmail.com>
Wed, 31 Jul 2013 18:25:13 +0000 (11:25 -0700)
committerPaul Berry <stereotype441@gmail.com>
Fri, 2 Aug 2013 03:19:39 +0000 (20:19 -0700)
According to GLSL, indexing into an array or matrix with an
out-of-range constant results in a compile error.  However, indexing
with an out-of-range value that isn't constant merely results in
undefined results.

Since optimization passes (e.g. loop unrolling) can convert
non-constant array indices into constant array indices, it's possible
that ir_set_program_inouts will encounter a constant array index that
is out of range; if this happens, just mark the whole array as used.

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
src/glsl/ir_set_program_inouts.cpp

index ea55447ea05bc27c386b2abe2ed1e6f8903d0b52..27c3ca70e5aab39974fcc7b828aa55bdd9e88388 100644 (file)
@@ -171,13 +171,34 @@ ir_set_program_inouts_visitor::try_mark_partial_variable(ir_variable *var,
    if (!index_as_constant)
       return false;
 
-   int width = 1;
-   if (type->is_array() && type->fields.array->is_matrix()) {
-      width = type->fields.array->matrix_columns;
+   unsigned elem_width;
+   unsigned num_elems;
+   if (type->is_array()) {
+      num_elems = type->length;
+      if (type->fields.array->is_matrix())
+         elem_width = type->fields.array->matrix_columns;
+      else
+         elem_width = 1;
+   } else {
+      num_elems = type->matrix_columns;
+      elem_width = 1;
    }
 
-   mark(this->prog, var, index_as_constant->value.u[0] * width, width,
-        this->shader_type == GL_FRAGMENT_SHADER);
+   if (index_as_constant->value.u[0] >= num_elems) {
+      /* Constant index outside the bounds of the matrix/array.  This could
+       * arise as a result of constant folding of a legal GLSL program.
+       *
+       * Even though the spec says that indexing outside the bounds of a
+       * matrix/array results in undefined behaviour, we don't want to pass
+       * out-of-range values to mark() (since this could result in slots that
+       * don't exist being marked as used), so just let the caller mark the
+       * whole variable as used.
+       */
+      return false;
+   }
+
+   mark(this->prog, var, index_as_constant->value.u[0] * elem_width,
+        elem_width, this->shader_type == GL_FRAGMENT_SHADER);
    return true;
 }