glsl: Indirect array indexing on non-last SSBO member must fail compilation
authorIago Toral Quiroga <itoral@igalia.com>
Tue, 18 Oct 2016 12:15:36 +0000 (14:15 +0200)
committerIago Toral Quiroga <itoral@igalia.com>
Thu, 20 Oct 2016 06:26:51 +0000 (08:26 +0200)
After the changes in comit 5b2675093e863a52, we moved this check to the
linker, but the spec expects this to be checked at compile-time. There are
dEQP tests that expect an error at compile time and the spec seems to confirm
that expectation:

"Except for the last declared member of a shader storage block (section 4.3.9
 “Interface Blocks”), the size of an array must be declared (explicitly sized)
 before it is indexed with anything other than an integral constant expression.
 The size of any array must be declared before passing it as an argument to a
 function. Violation of any of these rules result in compile-time errors. It
 is legal to declare an array without a size (unsized) and then later
 redeclare the same name as an array of the same type and specify a size, or
 index it only with integral constant expressions (implicitly sized)."

Commit 5b2675093e863a52 tries to take care of the case where we have implicitly
sized arrays in SSBOs and it does so by checking the max_array_access field
in ir_variable during linking. In this patch we change the approach: we look
for indirect access on SSBO arrays, and when we find one, we emit a
compile-time error if the accessed member is not the last in the SSBO
definition.

There is a corner case that the specs do not address directly though and that
dEQP checks for: the case of an unsized array in an SSBO definition that is
not defined last but is never used in the shader code either. The following
dEQP tests expect a compile-time error in this scenario:

dEQP-GLES31.functional.debug.negative_coverage.callbacks.shader.compile_compute_shader
dEQP-GLES31.functional.debug.negative_coverage.get_error.shader.compile_compute_shader
dEQP-GLES31.functional.debug.negative_coverage.log.shader.compile_compute_shader

However, since the unsized array is never used it is never indexed with a
non-constant expression, so by the spec quotation above, it should be valid and
the tests are probably incorrect.

Reviewed-by: Nicolai Hähnle <nicolai.haehnle@amd.com>
src/compiler/glsl/ast_array_index.cpp
src/compiler/glsl/link_uniform_blocks.cpp

index e29dafb79074f0acaa61243eba7ea5045953e401..dfa44b703d42a71ed4c625dfbbf2842b910a2de1 100644 (file)
@@ -233,6 +233,20 @@ _mesa_ast_array_index_to_hir(void *mem_ctx,
          else if (array->variable_referenced()->data.mode !=
                   ir_var_shader_storage) {
             _mesa_glsl_error(&loc, state, "unsized array index must be constant");
          else if (array->variable_referenced()->data.mode !=
                   ir_var_shader_storage) {
             _mesa_glsl_error(&loc, state, "unsized array index must be constant");
+         } else {
+            /* Unsized array non-constant indexing on SSBO is allowed only for
+             * the last member of the SSBO definition.
+             */
+            ir_variable *var = array->variable_referenced();
+            const glsl_type *iface_type = var->get_interface_type();
+            int field_index = iface_type->field_index(var->name);
+            /* Field index can be < 0 for instance arrays */
+            if (field_index >= 0 &&
+                field_index != (int) iface_type->length - 1) {
+               _mesa_glsl_error(&loc, state, "Indirect access on unsized "
+                                "array is limited to the last member of "
+                                "SSBO.");
+            }
          }
       } else if (array->type->without_array()->is_interface()
                  && ((array->variable_referenced()->data.mode == ir_var_uniform
          }
       } else if (array->type->without_array()->is_interface()
                  && ((array->variable_referenced()->data.mode == ir_var_uniform
index 5b0dff6aa19f20d30da3a4ed22adc52d3f41b936..bb423c55410f5a56c18be557e075eefd15cb1bde 100644 (file)
@@ -150,13 +150,7 @@ private:
        */
       const glsl_type *type_for_size = type;
       if (type->is_unsized_array()) {
        */
       const glsl_type *type_for_size = type;
       if (type->is_unsized_array()) {
-         if (!last_field) {
-            linker_error(prog, "unsized array `%s' definition: "
-                         "only last member of a shader storage block "
-                         "can be defined as unsized array",
-                         name);
-         }
-
+         assert(last_field);
          type_for_size = type->without_array();
       }
 
          type_for_size = type->without_array();
       }