[Mesa-dev] [PATCH 4/6] glsl: add support for complie-time constant expressions

Timothy Arceri t_arceri at yahoo.com.au
Thu Nov 5 03:17:34 PST 2015


From: Timothy Arceri <timothy.arceri at collabora.com>

In this patch we introduce a new ast type for holding the new
compile-time constant expressions. The main reason for this is that
we can no longer do merging of layout qualifiers before they have been
converted into GLSL IR.

The remainder of the patch replaces all the old interger constant
qualifiers with either the new ast_layout_expression type if
the qualifier requires merging or ast_expression if the qualifier
can't have mulitple declorations or if all but he newest qualifier
is simply ignored.

There are three main helper functions introduced in this patch:

- process_qualifier_constant()

 Used to evaluate qualifiers of type ast_expression

- ast_layout_expression::process_qualifier_constant()

 Used to merge and then evaluate qualifiers of type ast_layout_expression

- ast_layout_expression::merge_qualifier()

 Simply appends a qualifier to a list to be merged later by
 process_qualifier_constant()

In order to avoid cascading error messages the process_qualifier_constant()
helpers return a bool.
---
 src/glsl/ast.h                  |  60 ++++++---
 src/glsl/ast_to_hir.cpp         | 274 ++++++++++++++++++++++++++++------------
 src/glsl/ast_type.cpp           | 128 +++++++++++--------
 src/glsl/glsl_parser.yy         |  25 ++--
 src/glsl/glsl_parser_extras.cpp |  68 ++++++----
 5 files changed, 363 insertions(+), 192 deletions(-)

diff --git a/src/glsl/ast.h b/src/glsl/ast.h
index afd2d41..8fbb5dd 100644
--- a/src/glsl/ast.h
+++ b/src/glsl/ast.h
@@ -350,6 +350,26 @@ public:
    exec_list array_dimensions;
 };
 
+class ast_layout_expression : public ast_node {
+public:
+   ast_layout_expression(const struct YYLTYPE &locp, ast_expression *expr)
+   {
+      set_location(locp);
+      layout_const_expressions.push_tail(&expr->link);
+   }
+
+   bool process_qualifier_constant(struct _mesa_glsl_parse_state *state,
+                                   const char *qual_indentifier,
+                                   int *value);
+
+   void merge_qualifier(ast_layout_expression *l_expr)
+   {
+      layout_const_expressions.append_list(&l_expr->layout_const_expressions);
+   }
+
+   exec_list layout_const_expressions;
+};
+
 /**
  * C-style aggregate initialization class
  *
@@ -553,13 +573,11 @@ struct ast_type_qualifier {
       uint64_t i;
    } flags;
 
-   struct YYLTYPE *loc;
-
    /** Precision of the type (highp/medium/lowp). */
    unsigned precision:2;
 
    /** Geometry shader invocations for GL_ARB_gpu_shader5. */
-   int invocations;
+   ast_layout_expression *invocations;
 
    /**
     * Location specified via GL_ARB_explicit_attrib_location layout
@@ -567,20 +585,20 @@ struct ast_type_qualifier {
     * \note
     * This field is only valid if \c explicit_location is set.
     */
-   int location;
+   ast_expression *location;
    /**
     * Index specified via GL_ARB_explicit_attrib_location layout
     *
     * \note
     * This field is only valid if \c explicit_index is set.
     */
-   int index;
+   ast_expression *index;
 
    /** Maximum output vertices in GLSL 1.50 geometry shaders. */
-   int max_vertices;
+   ast_layout_expression *max_vertices;
 
    /** Stream in GLSL 1.50 geometry shaders. */
-   unsigned stream;
+   ast_expression *stream;
 
    /**
     * Input or output primitive type in GLSL 1.50 geometry shaders
@@ -594,7 +612,7 @@ struct ast_type_qualifier {
     * \note
     * This field is only valid if \c explicit_binding is set.
     */
-   int binding;
+   ast_expression *binding;
 
    /**
     * Offset specified via GL_ARB_shader_atomic_counter's "offset"
@@ -603,14 +621,14 @@ struct ast_type_qualifier {
     * \note
     * This field is only valid if \c explicit_offset is set.
     */
-   int offset;
+   ast_expression *offset;
 
    /**
     * Local size specified via GL_ARB_compute_shader's "local_size_{x,y,z}"
     * layout qualifier.  Element i of this array is only valid if
     * flags.q.local_size & (1 << i) is set.
     */
-   int local_size[3];
+   ast_layout_expression *local_size[3];
 
    /** Tessellation evaluation shader: vertex spacing (equal, fractional even/odd) */
    GLenum vertex_spacing;
@@ -622,7 +640,7 @@ struct ast_type_qualifier {
    bool point_mode;
 
    /** Tessellation control shader: number of output vertices */
-   int vertices;
+   ast_layout_expression *vertices;
 
    /**
     * Image format specified with an ARB_shader_image_load_store
@@ -1094,17 +1112,13 @@ public:
 class ast_tcs_output_layout : public ast_node
 {
 public:
-   ast_tcs_output_layout(const struct YYLTYPE &locp, int vertices)
-      : vertices(vertices)
+   ast_tcs_output_layout(const struct YYLTYPE &locp)
    {
       set_location(locp);
    }
 
    virtual ir_rvalue *hir(exec_list *instructions,
                           struct _mesa_glsl_parse_state *state);
-
-private:
-   const int vertices;
 };
 
 
@@ -1136,9 +1150,10 @@ private:
 class ast_cs_input_layout : public ast_node
 {
 public:
-   ast_cs_input_layout(const struct YYLTYPE &locp, const unsigned *local_size)
+   ast_cs_input_layout(const struct YYLTYPE &locp,
+                       ast_layout_expression **local_size)
    {
-      memcpy(this->local_size, local_size, sizeof(this->local_size));
+      memcpy(this->local_size, *local_size, sizeof(this->local_size));
       set_location(locp);
    }
 
@@ -1146,11 +1161,18 @@ public:
                           struct _mesa_glsl_parse_state *state);
 
 private:
-   unsigned local_size[3];
+   ast_layout_expression *local_size[3];
 };
 
 /*@}*/
 
+bool
+process_qualifier_constant(struct _mesa_glsl_parse_state *state,
+                           YYLTYPE *loc,
+                           const char *qual_indentifier,
+                           ast_expression *const_expression,
+                           int *value);
+
 extern void
 _mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state);
 
diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp
index 8fb8875..e71c539 100644
--- a/src/glsl/ast_to_hir.cpp
+++ b/src/glsl/ast_to_hir.cpp
@@ -2261,6 +2261,54 @@ validate_matrix_layout_for_type(struct _mesa_glsl_parse_state *state,
    }
 }
 
+bool
+process_qualifier_constant(struct _mesa_glsl_parse_state *state,
+                           YYLTYPE *loc,
+                           const char *qual_indentifier,
+                           ast_expression *const_expression,
+                           int *value)
+{
+   exec_list dummy_instructions;
+
+   if (const_expression == NULL) {
+      *value = 0;
+      return true;
+   }
+
+   ir_rvalue *const ir = const_expression->hir(&dummy_instructions, state);
+
+   ir_constant *const const_int = ir->constant_expression_value();
+   if (const_int == NULL || !const_int->type->is_integer()) {
+      _mesa_glsl_error(loc, state, "%s must be an integral constant "
+                       "expression", qual_indentifier);
+      return false;
+   }
+
+   /* If the location is const (and we've verified that
+    * it is) then no instructions should have been emitted
+    * when we converted it to HIR. If they were emitted,
+    * then either the location isn't const after all, or
+    * we are emitting unnecessary instructions.
+    */
+   assert(dummy_instructions.is_empty());
+
+   *value = const_int->value.i[0];
+   return true;
+}
+
+static void
+validate_stream_qualifier(YYLTYPE *loc, struct _mesa_glsl_parse_state *state,
+                          int stream)
+{
+   if (stream < 0 ||
+       (unsigned) stream >= state->ctx->Const.MaxVertexStreams) {
+      _mesa_glsl_error(loc, state,
+                       "invalid stream specified %d is larger than "
+                       "MAX_VERTEX_STREAMS - 1 (%d) or less than 0.",
+                       stream, state->ctx->Const.MaxVertexStreams - 1);
+   }
+}
+
 static bool
 validate_binding_qualifier(struct _mesa_glsl_parse_state *state,
                            YYLTYPE *loc,
@@ -2274,14 +2322,20 @@ validate_binding_qualifier(struct _mesa_glsl_parse_state *state,
       return false;
    }
 
-   if (qual->binding < 0) {
+   int qual_binding;
+   if (!process_qualifier_constant(state, loc, "binding",
+                                   qual->binding, &qual_binding)) {
+      return false;
+   }
+
+   if (qual_binding < 0) {
       _mesa_glsl_error(loc, state, "binding values must be >= 0");
       return false;
    }
 
    const struct gl_context *const ctx = state->ctx;
    unsigned elements = type->is_array() ? type->arrays_of_arrays_size() : 1;
-   unsigned max_index = qual->binding + elements - 1;
+   unsigned max_index = qual_binding + elements - 1;
    const glsl_type *base_type = type->without_array();
 
    if (base_type->is_interface()) {
@@ -2299,7 +2353,7 @@ validate_binding_qualifier(struct _mesa_glsl_parse_state *state,
          max_index >= ctx->Const.MaxUniformBufferBindings) {
          _mesa_glsl_error(loc, state, "layout(binding = %d) for %d UBOs exceeds "
                           "the maximum number of UBO binding points (%d)",
-                          qual->binding, elements,
+                          qual_binding, elements,
                           ctx->Const.MaxUniformBufferBindings);
          return false;
       }
@@ -2317,7 +2371,7 @@ validate_binding_qualifier(struct _mesa_glsl_parse_state *state,
          max_index >= ctx->Const.MaxShaderStorageBufferBindings) {
          _mesa_glsl_error(loc, state, "layout(binding = %d) for %d SSBOs exceeds "
                           "the maximum number of SSBO binding points (%d)",
-                          qual->binding, elements,
+                          qual_binding, elements,
                           ctx->Const.MaxShaderStorageBufferBindings);
          return false;
       }
@@ -2334,16 +2388,16 @@ validate_binding_qualifier(struct _mesa_glsl_parse_state *state,
       if (max_index >= limit) {
          _mesa_glsl_error(loc, state, "layout(binding = %d) for %d samplers "
                           "exceeds the maximum number of texture image units "
-                          "(%d)", qual->binding, elements, limit);
+                          "(%d)", qual_binding, elements, limit);
 
          return false;
       }
    } else if (base_type->contains_atomic()) {
       assert(ctx->Const.MaxAtomicBufferBindings <= MAX_COMBINED_ATOMIC_BUFFERS);
-      if (unsigned(qual->binding) >= ctx->Const.MaxAtomicBufferBindings) {
+      if (unsigned(qual_binding) >= ctx->Const.MaxAtomicBufferBindings) {
          _mesa_glsl_error(loc, state, "layout(binding = %d) exceeds the "
                           " maximum number of atomic counter buffer bindings"
-                          "(%d)", qual->binding,
+                          "(%d)", qual_binding,
                           ctx->Const.MaxAtomicBufferBindings);
 
          return false;
@@ -2413,10 +2467,16 @@ validate_explicit_location(const struct ast_type_qualifier *qual,
                            YYLTYPE *loc)
 {
    bool fail = false;
+   int qual_location;
 
-   if (qual->location < 0) {
+   if (!process_qualifier_constant(state, loc, "location",
+                                   qual->location, &qual_location)) {
+      return;
+   }
+
+   if (qual_location < 0) {
        _mesa_glsl_error(loc, state, "invalid location %d specified",
-                        qual->location);
+                        qual_location);
        return;
    }
 
@@ -2426,7 +2486,7 @@ validate_explicit_location(const struct ast_type_qualifier *qual,
          return;
 
       const struct gl_context *const ctx = state->ctx;
-      unsigned max_loc = qual->location + var->type->uniform_locations() - 1;
+      unsigned max_loc = qual_location + var->type->uniform_locations() - 1;
 
       if (max_loc >= ctx->Const.MaxUserAssignableUniformLocations) {
          _mesa_glsl_error(loc, state, "location(s) consumed by uniform %s "
@@ -2436,7 +2496,7 @@ validate_explicit_location(const struct ast_type_qualifier *qual,
       }
 
       var->data.explicit_location = true;
-      var->data.location = qual->location;
+      var->data.location = qual_location;
       return;
    }
 
@@ -2521,23 +2581,23 @@ validate_explicit_location(const struct ast_type_qualifier *qual,
       switch (state->stage) {
       case MESA_SHADER_VERTEX:
          var->data.location = (var->data.mode == ir_var_shader_in)
-            ? (qual->location + VERT_ATTRIB_GENERIC0)
-            : (qual->location + VARYING_SLOT_VAR0);
+            ? (qual_location + VERT_ATTRIB_GENERIC0)
+            : (qual_location + VARYING_SLOT_VAR0);
          break;
 
       case MESA_SHADER_TESS_CTRL:
       case MESA_SHADER_TESS_EVAL:
       case MESA_SHADER_GEOMETRY:
          if (var->data.patch)
-            var->data.location = qual->location + VARYING_SLOT_PATCH0;
+            var->data.location = qual_location + VARYING_SLOT_PATCH0;
          else
-            var->data.location = qual->location + VARYING_SLOT_VAR0;
+            var->data.location = qual_location + VARYING_SLOT_VAR0;
          break;
 
       case MESA_SHADER_FRAGMENT:
          var->data.location = (var->data.mode == ir_var_shader_out)
-            ? (qual->location + FRAG_RESULT_DATA0)
-            : (qual->location + VARYING_SLOT_VAR0);
+            ? (qual_location + FRAG_RESULT_DATA0)
+            : (qual_location + VARYING_SLOT_VAR0);
          break;
       case MESA_SHADER_COMPUTE:
          assert(!"Unexpected shader type");
@@ -2566,12 +2626,15 @@ validate_layout_qualifiers(const struct ast_type_qualifier *qual,
           * Older specifications don't mandate a behavior; we take
           * this as a clarification and always generate the error.
           */
-         if (qual->index < 0 || qual->index > 1) {
+         int qual_index;
+         if (process_qualifier_constant(state, loc, "index",
+                                        qual->index, &qual_index) &&
+             (qual_index < 0 || qual_index > 1)) {
             _mesa_glsl_error(loc, state,
                              "explicit index may only be 0 or 1");
          } else {
             var->data.explicit_index = true;
-            var->data.index = qual->index;
+            var->data.index = qual_index;
          }
       }
    } else if (qual->flags.q.explicit_index) {
@@ -2580,8 +2643,11 @@ validate_layout_qualifiers(const struct ast_type_qualifier *qual,
 
    if (qual->flags.q.explicit_binding &&
        validate_binding_qualifier(state, loc, var->type, qual)) {
+      int qual_binding;
+      process_qualifier_constant(state, loc, "binding",
+                                 qual->binding, &qual_binding);
       var->data.explicit_binding = true;
-      var->data.binding = qual->binding;
+      var->data.binding = qual_binding;
    }
 
    if (var->type->contains_atomic()) {
@@ -2785,7 +2851,12 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
 
    if (state->stage == MESA_SHADER_GEOMETRY &&
        qual->flags.q.out && qual->flags.q.stream) {
-      var->data.stream = qual->stream;
+      int qual_stream;
+      if (process_qualifier_constant(state, loc, "stream", qual->stream,
+                                     &qual_stream)) {
+         validate_stream_qualifier(loc, state, qual_stream);
+         var->data.stream = qual_stream;
+      }
    }
 
    if (qual->flags.q.patch)
@@ -3544,10 +3615,14 @@ static void
 handle_tess_ctrl_shader_output_decl(struct _mesa_glsl_parse_state *state,
                                     YYLTYPE loc, ir_variable *var)
 {
-   unsigned num_vertices = 0;
+   int num_vertices = 0;
 
    if (state->tcs_output_vertices_specified) {
-      num_vertices = state->out_qualifier->vertices;
+      if (!state->out_qualifier->vertices->
+             process_qualifier_constant(state, "vertices",
+                                        &num_vertices)) {
+         return;
+      }
    }
 
    if (!var->type->is_array() && !var->data.patch) {
@@ -3561,7 +3636,8 @@ handle_tess_ctrl_shader_output_decl(struct _mesa_glsl_parse_state *state,
    if (var->data.patch)
       return;
 
-   validate_layout_qualifier_vertex_count(state, loc, var, num_vertices,
+   validate_layout_qualifier_vertex_count(state, loc, var,
+                                          (unsigned) num_vertices,
                                           &state->tcs_output_size,
                                           "tessellation control shader output");
 }
@@ -3817,9 +3893,20 @@ ast_declarator_list::hir(exec_list *instructions,
     */
    if (decl_type && decl_type->contains_atomic()) {
       if (type->qualifier.flags.q.explicit_binding &&
-          type->qualifier.flags.q.explicit_offset)
-         state->atomic_counter_offsets[type->qualifier.binding] =
-            type->qualifier.offset;
+          type->qualifier.flags.q.explicit_offset) {
+         int qual_binding;
+         if (process_qualifier_constant(state, &loc, "binding",
+                                        type->qualifier.binding,
+                                        &qual_binding)) {
+            int qual_offset;
+            if (process_qualifier_constant(state, &loc, "offset",
+                                           type->qualifier.offset,
+                                           &qual_offset)) {
+               state->atomic_counter_offsets[qual_binding] =
+                  (unsigned) qual_offset;
+            }
+         }
+      }
    }
 
    if (this->declarations.is_empty()) {
@@ -5980,17 +6067,18 @@ ast_process_structure_or_interface_block(exec_list *instructions,
          fields[i].sample = qual->flags.q.sample ? 1 : 0;
          fields[i].patch = qual->flags.q.patch ? 1 : 0;
 
+         /* Only save explicitly defined streams in block's field */
          if (qual->flags.q.explicit_stream) {
-            if (qual->stream < 0) {
-               YYLTYPE loc = decl_list->get_location();
-               _mesa_glsl_error(&loc, state, "invalid stream %d specified",
-                                qual->stream);
+            int qual_stream;
+            if (process_qualifier_constant(state, &loc, "stream",
+                                           qual->stream, &qual_stream)) {
+               validate_stream_qualifier(&loc, state, qual_stream);
+               fields[i].stream = qual_stream;
             }
+         } else {
+            fields[i].stream = -1;
          }
 
-         /* Only save explicitly defined streams in block's field */
-         fields[i].stream = qual->flags.q.explicit_stream ? qual->stream : -1;
-
          if (qual->flags.q.row_major || qual->flags.q.column_major) {
             if (!qual->flags.q.uniform && !qual->flags.q.buffer) {
                _mesa_glsl_error(&loc, state,
@@ -6291,15 +6379,19 @@ ast_interface_block::hir(exec_list *instructions,
 
    state->struct_specifier_depth--;
 
-   for (unsigned i = 0; i < num_variables; i++) {
-      if (fields[i].stream != -1 &&
-          (unsigned) fields[i].stream != this->layout.stream) {
-         _mesa_glsl_error(&loc, state,
-                          "stream layout qualifier on "
-                          "interface block member `%s' does not match "
-                          "the interface block (%d vs %d)",
-                          fields[i].name, fields[i].stream,
-                          this->layout.stream);
+   int qual_stream;
+   if (process_qualifier_constant(state, &loc, "stream", this->layout.stream,
+                                  &qual_stream)) {
+      validate_stream_qualifier(&loc, state, qual_stream);
+
+      for (unsigned i = 0; i < num_variables; i++) {
+         if (fields[i].stream != -1 && fields[i].stream != qual_stream) {
+            _mesa_glsl_error(&loc, state,
+                             "stream layout qualifier on "
+                             "interface block member `%s' does not match "
+                             "the interface block (%d vs %d)",
+                             fields[i].name, fields[i].stream, qual_stream);
+         }
       }
    }
 
@@ -6641,9 +6733,15 @@ ast_interface_block::hir(exec_list *instructions,
           * has an instance name.  This is ugly.
           */
          var->data.explicit_binding = this->layout.flags.q.explicit_binding;
-         var->data.binding = this->layout.binding;
+         int qual_binding;
+         if (this->layout.flags.q.explicit_binding &&
+             process_qualifier_constant(state, &loc, "binding",
+                                        this->layout.binding,
+                                        &qual_binding)) {
+            var->data.binding = qual_binding;
+         }
 
-         var->data.stream = this->layout.stream;
+         var->data.stream = qual_stream;
 
          state->symbols->add_variable(var);
          instructions->push_tail(var);
@@ -6663,7 +6761,7 @@ ast_interface_block::hir(exec_list *instructions,
          var->data.centroid = fields[i].centroid;
          var->data.sample = fields[i].sample;
          var->data.patch = fields[i].patch;
-         var->data.stream = this->layout.stream;
+         var->data.stream = qual_stream;
          var->init_interface_type(block_type);
 
          if (var_mode == ir_var_shader_in || var_mode == ir_var_uniform)
@@ -6714,7 +6812,13 @@ ast_interface_block::hir(exec_list *instructions,
           * has an instance name.  This is ugly.
           */
          var->data.explicit_binding = this->layout.flags.q.explicit_binding;
-         var->data.binding = this->layout.binding;
+         int qual_binding;
+         if (this->layout.flags.q.explicit_binding &&
+             process_qualifier_constant(state, &loc, "binding",
+                                        this->layout.binding,
+                                        &qual_binding)) {
+            var->data.binding = qual_binding;
+         }
 
          if (var->type->is_unsized_array()) {
             if (var->is_in_shader_storage_block()) {
@@ -6796,14 +6900,10 @@ ast_tcs_output_layout::hir(exec_list *instructions,
 {
    YYLTYPE loc = this->get_location();
 
-   /* If any tessellation control output layout declaration preceded this
-    * one, make sure it was consistent with this one.
-    */
-   if (state->tcs_output_vertices_specified &&
-       state->out_qualifier->vertices != this->vertices) {
-      _mesa_glsl_error(&loc, state,
-		       "tessellation control shader output layout does not "
-		       "match previous declaration");
+   int num_vertices;
+   if (!state->out_qualifier->vertices->
+          process_qualifier_constant(state, "vertices", &num_vertices)) {
+      /* return here to stop cascading incorrect error messages */
       return NULL;
    }
 
@@ -6811,8 +6911,8 @@ ast_tcs_output_layout::hir(exec_list *instructions,
     * array size, make sure the size they specified is consistent with the
     * primitive type.
     */
-   unsigned num_vertices = this->vertices;
-   if (state->tcs_output_size != 0 && state->tcs_output_size != num_vertices) {
+   if (state->tcs_output_size != 0 &&
+       state->tcs_output_size != (unsigned) num_vertices) {
       _mesa_glsl_error(&loc, state,
 		       "this tessellation control shader output layout "
 		       "specifies %u vertices, but a previous output "
@@ -6835,15 +6935,15 @@ ast_tcs_output_layout::hir(exec_list *instructions,
       if (!var->type->is_unsized_array() || var->data.patch)
          continue;
 
-      if (var->data.max_array_access >= num_vertices) {
+      if (var->data.max_array_access >= (unsigned) num_vertices) {
 	 _mesa_glsl_error(&loc, state,
 			  "this tessellation control shader output layout "
-			  "specifies %u vertices, but an access to element "
+			  "specifies %d vertices, but an access to element "
 			  "%u of output `%s' already exists", num_vertices,
 			  var->data.max_array_access, var->name);
       } else {
 	 var->type = glsl_type::get_array_instance(var->type->fields.array,
-						   num_vertices);
+						   (unsigned) num_vertices);
       }
    }
 
@@ -6919,20 +7019,6 @@ ast_cs_input_layout::hir(exec_list *instructions,
 {
    YYLTYPE loc = this->get_location();
 
-   /* If any compute input layout declaration preceded this one, make sure it
-    * was consistent with this one.
-    */
-   if (state->cs_input_local_size_specified) {
-      for (int i = 0; i < 3; i++) {
-         if (state->cs_input_local_size[i] != this->local_size[i]) {
-            _mesa_glsl_error(&loc, state,
-                             "compute shader input layout does not match"
-                             " previous declaration");
-            return NULL;
-         }
-      }
-   }
-
    /* From the ARB_compute_shader specification:
     *
     *     If the local size of the shader in any dimension is greater
@@ -6945,22 +7031,34 @@ ast_cs_input_layout::hir(exec_list *instructions,
     * report it at compile time as well.
     */
    GLuint64 total_invocations = 1;
+   int qual_local_size[3];
    for (int i = 0; i < 3; i++) {
-      if (this->local_size[i] <= 0) {
-         _mesa_glsl_error(state->in_qualifier->loc, state,
+      char *local_size_str = ralloc_asprintf(NULL, "invalid local_size_%c",
+                                             'x' + i);
+      if (!this->local_size[i]->
+             process_qualifier_constant(state, local_size_str,
+                                        &qual_local_size[i])) {
+         ralloc_free(local_size_str);
+         break;
+      }
+      ralloc_free(local_size_str);
+
+      if (qual_local_size[i] <= 0) {
+         _mesa_glsl_error(&loc, state,
                           "invalid local_size_%c of %d specified",
-                          'x' + i, this->local_size[i]);
+                          'x' + i, qual_local_size[i]);
          break;
       }
 
-      if (this->local_size[i] > state->ctx->Const.MaxComputeWorkGroupSize[i]) {
+      if ((unsigned) qual_local_size[i] >
+          state->ctx->Const.MaxComputeWorkGroupSize[i]) {
          _mesa_glsl_error(&loc, state,
                           "local_size_%c exceeds MAX_COMPUTE_WORK_GROUP_SIZE"
                           " (%d)", 'x' + i,
                           state->ctx->Const.MaxComputeWorkGroupSize[i]);
          break;
       }
-      total_invocations *= this->local_size[i];
+      total_invocations *= qual_local_size[i];
       if (total_invocations >
           state->ctx->Const.MaxComputeWorkGroupInvocations) {
          _mesa_glsl_error(&loc, state,
@@ -6971,9 +7069,23 @@ ast_cs_input_layout::hir(exec_list *instructions,
       }
    }
 
+   /* If any compute input layout declaration preceded this one, make sure it
+    * was consistent with this one.
+    */
+   if (state->cs_input_local_size_specified) {
+      for (int i = 0; i < 3; i++) {
+         if (state->cs_input_local_size[i] != (unsigned) qual_local_size[i]) {
+            _mesa_glsl_error(&loc, state,
+                             "compute shader input layout does not match"
+                             " previous declaration");
+            return NULL;
+         }
+      }
+   }
+
    state->cs_input_local_size_specified = true;
    for (int i = 0; i < 3; i++)
-      state->cs_input_local_size[i] = this->local_size[i];
+      state->cs_input_local_size[i] = qual_local_size[i];
 
    /* We may now declare the built-in constant gl_WorkGroupSize (see
     * builtin_variable_generator::generate_constants() for why we didn't
@@ -6988,7 +7100,7 @@ ast_cs_input_layout::hir(exec_list *instructions,
    ir_constant_data data;
    memset(&data, 0, sizeof(data));
    for (int i = 0; i < 3; i++)
-      data.u[i] = this->local_size[i];
+      data.u[i] = qual_local_size[i];
    var->constant_value = new(var) ir_constant(glsl_type::uvec3_type, &data);
    var->constant_initializer =
       new(var) ir_constant(glsl_type::uvec3_type, &data);
diff --git a/src/glsl/ast_type.cpp b/src/glsl/ast_type.cpp
index 53d1023..d90b473 100644
--- a/src/glsl/ast_type.cpp
+++ b/src/glsl/ast_type.cpp
@@ -168,41 +168,23 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc,
    }
 
    if (q.flags.q.max_vertices) {
-      if (this->flags.q.max_vertices && this->max_vertices != q.max_vertices) {
-	 _mesa_glsl_error(loc, state,
-			  "geometry shader set conflicting max_vertices "
-			  "(%d and %d)", this->max_vertices, q.max_vertices);
-	 return false;
+      if (this->max_vertices) {
+         this->max_vertices->merge_qualifier(q.max_vertices);
+      } else {
+         this->max_vertices = q.max_vertices;
       }
-      this->max_vertices = q.max_vertices;
    }
 
    if (q.flags.q.invocations) {
-      if (this->flags.q.invocations && this->invocations != q.invocations) {
-         _mesa_glsl_error(loc, state,
-                          "geometry shader set conflicting invocations "
-                          "(%d and %d)", this->invocations, q.invocations);
-         return false;
+      if (this->invocations) {
+         this->invocations->merge_qualifier(q.invocations);
+      } else {
+         this->invocations = q.invocations;
       }
-      this->invocations = q.invocations;
    }
 
    if (state->stage == MESA_SHADER_GEOMETRY &&
        state->has_explicit_attrib_stream()) {
-      if (q.flags.q.stream && q.stream >= state->ctx->Const.MaxVertexStreams) {
-         _mesa_glsl_error(loc, state,
-                          "`stream' value is larger than MAX_VERTEX_STREAMS - 1 "
-                          "(%d > %d)",
-                          q.stream, state->ctx->Const.MaxVertexStreams - 1);
-      }
-      if (this->flags.q.explicit_stream &&
-          this->stream >= state->ctx->Const.MaxVertexStreams) {
-         _mesa_glsl_error(loc, state,
-                          "`stream' value is larger than MAX_VERTEX_STREAMS - 1 "
-                          "(%d > %d)",
-                          this->stream, state->ctx->Const.MaxVertexStreams - 1);
-      }
-
       if (!this->flags.q.explicit_stream) {
          if (q.flags.q.stream) {
             this->flags.q.stream = 1;
@@ -221,14 +203,11 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc,
    }
 
    if (q.flags.q.vertices) {
-      if (this->flags.q.vertices && this->vertices != q.vertices) {
-	 _mesa_glsl_error(loc, state,
-			  "tessellation control shader set conflicting "
-			  "vertices (%d and %d)",
-			  this->vertices, q.vertices);
-	 return false;
+      if (this->vertices) {
+         this->vertices->merge_qualifier(q.vertices);
+      } else {
+         this->vertices = q.vertices;
       }
-      this->vertices = q.vertices;
    }
 
    if (q.flags.q.vertex_spacing) {
@@ -265,14 +244,6 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc,
 
    for (int i = 0; i < 3; i++) {
       if (q.flags.q.local_size & (1 << i)) {
-         if ((this->flags.q.local_size & (1 << i)) &&
-             this->local_size[i] != q.local_size[i]) {
-            _mesa_glsl_error(loc, state,
-                             "compute shader set conflicting values for "
-                             "local_size_%c (%d and %d)", 'x' + i,
-                             this->local_size[i], q.local_size[i]);
-            return false;
-         }
          this->local_size[i] = q.local_size[i];
       }
    }
@@ -310,10 +281,9 @@ ast_type_qualifier::merge_out_qualifier(YYLTYPE *loc,
 {
    void *mem_ctx = state;
    const bool r = this->merge_qualifier(loc, state, q);
-   this->loc = loc;
 
    if (state->stage == MESA_SHADER_TESS_CTRL) {
-      node = new(mem_ctx) ast_tcs_output_layout(*loc, q.vertices);
+      node = new(mem_ctx) ast_tcs_output_layout(*loc);
    }
 
    return r;
@@ -330,7 +300,6 @@ ast_type_qualifier::merge_in_qualifier(YYLTYPE *loc,
    bool create_cs_ast = false;
    ast_type_qualifier valid_in_mask;
    valid_in_mask.flags.i = 0;
-   this->loc = loc;
 
    switch (state->stage) {
    case MESA_SHADER_TESS_EVAL:
@@ -418,15 +387,13 @@ ast_type_qualifier::merge_in_qualifier(YYLTYPE *loc,
       state->in_qualifier->prim_type = q.prim_type;
    }
 
-   if (this->flags.q.invocations &&
-       q.flags.q.invocations &&
-       this->invocations != q.invocations) {
-      _mesa_glsl_error(loc, state,
-                       "conflicting invocations counts specified");
-      return false;
-   } else if (q.flags.q.invocations) {
+   if (q.flags.q.invocations) {
       this->flags.q.invocations = 1;
-      this->invocations = q.invocations;
+      if (this->invocations) {
+         this->invocations->merge_qualifier(q.invocations);
+      } else {
+         this->invocations = q.invocations;
+      }
    }
 
    if (q.flags.q.early_fragment_tests) {
@@ -470,15 +437,66 @@ ast_type_qualifier::merge_in_qualifier(YYLTYPE *loc,
       node = new(mem_ctx) ast_gs_input_layout(*loc, q.prim_type);
    } else if (create_cs_ast) {
       /* Infer a local_size of 1 for every unspecified dimension */
-      unsigned local_size[3];
+      ast_layout_expression *local_size[3];
       for (int i = 0; i < 3; i++) {
          if (q.flags.q.local_size & (1 << i))
             local_size[i] = q.local_size[i];
-         else
-            local_size[i] = 1;
+         else {
+            ast_expression *const_expr = new(mem_ctx)
+               ast_expression(ast_uint_constant, NULL, NULL, NULL);
+            const_expr->primary_expression.uint_constant = 1;
+            local_size[i]->layout_const_expressions.push_tail(&const_expr->link);
+         }
       }
       node = new(mem_ctx) ast_cs_input_layout(*loc, local_size);
    }
 
    return true;
 }
+
+bool
+ast_layout_expression::process_qualifier_constant(struct _mesa_glsl_parse_state *state,
+                                                  const char *qual_indentifier,
+                                                  int *value)
+{
+   bool first_pass = true;
+   *value = 0;
+
+   for (exec_node *node = layout_const_expressions.head;
+           !node->is_tail_sentinel(); node = node->next) {
+
+      exec_list dummy_instructions;
+      ast_node *const_expression = exec_node_data(ast_node, node, link);
+
+      ir_rvalue *const ir = const_expression->hir(&dummy_instructions, state);
+
+      ir_constant *const const_int = ir->constant_expression_value();
+      if (const_int == NULL || !const_int->type->is_integer()) {
+         YYLTYPE loc = const_expression->get_location();
+         _mesa_glsl_error(&loc, state, "%s must be an integral constant "
+                          "expression", qual_indentifier);
+         return false;
+      }
+
+      if (!first_pass && *value != const_int->value.i[0]) {
+         YYLTYPE loc = const_expression->get_location();
+         _mesa_glsl_error(&loc, state, "%s layout qualifier does not "
+		          "match previous declaration (%d vs %d)",
+                          qual_indentifier, *value, const_int->value.i[0]);
+         return false;
+      } else {
+         first_pass = false;
+         *value = const_int->value.i[0];
+      }
+
+      /* If the location is const (and we've verified that
+       * it is) then no instructions should have been emitted
+       * when we converted it to HIR. If they were emitted,
+       * then either the location isn't const after all, or
+       * we are emitting unnecessary instructions.
+       */
+      assert(dummy_instructions.is_empty());
+   }
+
+   return true;
+}
diff --git a/src/glsl/glsl_parser.yy b/src/glsl/glsl_parser.yy
index 44853b0..4186022 100644
--- a/src/glsl/glsl_parser.yy
+++ b/src/glsl/glsl_parser.yy
@@ -297,7 +297,6 @@ static bool match_layout_qualifier(const char *s1, const char *s2,
 %type <node> conditionopt
 %type <node> for_init_statement
 %type <for_rest_statement> for_rest_statement
-%type <n> integer_constant
 %type <node> layout_defaults
 
 %right THEN ELSE
@@ -1139,11 +1138,6 @@ layout_qualifier_id_list:
    }
    ;
 
-integer_constant:
-   INTCONSTANT { $$ = $1; }
-   | UINTCONSTANT { $$ = $1; }
-   ;
-
 layout_qualifier_id:
    any_identifier
    {
@@ -1440,9 +1434,18 @@ layout_qualifier_id:
          YYERROR;
       }
    }
-   | any_identifier '=' integer_constant
+   | any_identifier '=' constant_expression
    {
       memset(& $$, 0, sizeof($$));
+      void *ctx = state;
+
+      if ($3->oper != ast_int_constant &&
+          $3->oper != ast_uint_constant &&
+          !state->has_enhanced_layouts()) {
+         _mesa_glsl_error(& @1, state,
+                          "compile-time constant expressions require "
+                          "GLSL 4.40 or ARB_enhanced_layouts");
+      }
 
       if (match_layout_qualifier("location", $1, state) == 0) {
          $$.flags.q.explicit_location = 1;
@@ -1477,7 +1480,7 @@ layout_qualifier_id:
 
       if (match_layout_qualifier("max_vertices", $1, state) == 0) {
          $$.flags.q.max_vertices = 1;
-         $$.max_vertices = $3;
+         $$.max_vertices = new(ctx) ast_layout_expression(@1, $3);
 
          if (!state->is_version(150, 0)) {
             _mesa_glsl_error(& @1, state,
@@ -1511,7 +1514,7 @@ layout_qualifier_id:
                YYERROR;
             } else {
                $$.flags.q.local_size |= (1 << i);
-               $$.local_size[i] = $3;
+               $$.local_size[i] = new(ctx) ast_layout_expression(@1, $3);
             }
             break;
          }
@@ -1519,7 +1522,7 @@ layout_qualifier_id:
 
       if (match_layout_qualifier("invocations", $1, state) == 0) {
          $$.flags.q.invocations = 1;
-         $$.invocations = $3;
+         $$.invocations = new(ctx) ast_layout_expression(@1, $3);
          if (!state->is_version(400, 0) &&
              !state->ARB_gpu_shader5_enable) {
             _mesa_glsl_error(& @1, state,
@@ -1531,7 +1534,7 @@ layout_qualifier_id:
       /* Layout qualifiers for tessellation control shaders. */
       if (match_layout_qualifier("vertices", $1, state) == 0) {
          $$.flags.q.vertices = 1;
-         $$.vertices = $3;
+         $$.vertices = new(ctx) ast_layout_expression(@1, $3);
          if (!state->ARB_tessellation_shader_enable &&
              !state->is_version(400, 0)) {
             _mesa_glsl_error(& @1, state,
diff --git a/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp
index 1ecfcdb..0cb2068 100644
--- a/src/glsl/glsl_parser_extras.cpp
+++ b/src/glsl/glsl_parser_extras.cpp
@@ -1653,17 +1653,20 @@ set_shader_inout_layout(struct gl_shader *shader,
    case MESA_SHADER_TESS_CTRL:
       shader->TessCtrl.VerticesOut = 0;
       if (state->tcs_output_vertices_specified) {
-         if (state->out_qualifier->vertices <= 0) {
-            _mesa_glsl_error(state->out_qualifier->loc, state,
-                             "invalid vertices (%d) specified",
-                             state->out_qualifier->vertices);
-         } else if (state->out_qualifier->vertices >
-                    (int)state->Const.MaxPatchVertices) {
-            _mesa_glsl_error(state->out_qualifier->loc, state,
-                             "vertices (%d) exceeds GL_MAX_PATCH_VERTICES",
-                             state->out_qualifier->vertices);
+         int qual_vertices;
+         if (state->out_qualifier->vertices->
+               process_qualifier_constant(state, "vertices",
+                                          &qual_vertices)) {
+            YYLTYPE loc = state->out_qualifier->vertices->get_location();
+            if (qual_vertices <= 0) {
+               _mesa_glsl_error(&loc, state, "invalid vertices (%d) specified",
+                                qual_vertices);
+            } else if (qual_vertices > (int)state->Const.MaxPatchVertices) {
+               _mesa_glsl_error(&loc, state, "vertices (%d) exceeds "
+                                "GL_MAX_PATCH_VERTICES", qual_vertices);
+            }
+            shader->TessCtrl.VerticesOut = qual_vertices;
          }
-         shader->TessCtrl.VerticesOut = state->out_qualifier->vertices;
       }
       break;
    case MESA_SHADER_TESS_EVAL:
@@ -1686,12 +1689,19 @@ set_shader_inout_layout(struct gl_shader *shader,
    case MESA_SHADER_GEOMETRY:
       shader->Geom.VerticesOut = 0;
       if (state->out_qualifier->flags.q.max_vertices) {
-         if (state->out_qualifier->max_vertices < 0) {
-            _mesa_glsl_error(state->out_qualifier->loc, state,
-                             "invalid max_vertices %d specified",
-                             state->out_qualifier->max_vertices);
+         int qual_max_vertices;
+         if (state->out_qualifier->max_vertices->
+               process_qualifier_constant(state, "max_vertices",
+                                          &qual_max_vertices)) {
+            if (qual_max_vertices < 0) {
+               YYLTYPE loc = state->out_qualifier->max_vertices->
+                  get_location();
+               _mesa_glsl_error(&loc, state,
+                                "invalid max_vertices %d specified",
+                                qual_max_vertices);
+            }
+            shader->Geom.VerticesOut = qual_max_vertices;
          }
-         shader->Geom.VerticesOut = state->out_qualifier->max_vertices;
       }
 
       if (state->gs_input_prim_type_specified) {
@@ -1708,18 +1718,24 @@ set_shader_inout_layout(struct gl_shader *shader,
 
       shader->Geom.Invocations = 0;
       if (state->in_qualifier->flags.q.invocations) {
-         if (state->in_qualifier->invocations <= 0) {
-            _mesa_glsl_error(state->in_qualifier->loc, state,
-                             "invalid invocations %d specified",
-                             state->in_qualifier->invocations);
-         } else if (state->in_qualifier->invocations >
-                    MAX_GEOMETRY_SHADER_INVOCATIONS) {
-            _mesa_glsl_error(state->in_qualifier->loc, state,
-                             "invocations (%d) exceeds "
-                             "GL_MAX_GEOMETRY_SHADER_INVOCATIONS",
-                             state->in_qualifier->invocations);
+         int invocations;
+         if (state->in_qualifier->invocations->
+               process_qualifier_constant(state, "invocations",
+                                          &invocations)) {
+
+            YYLTYPE loc = state->in_qualifier->invocations->get_location();
+            if (invocations <= 0) {
+               _mesa_glsl_error(&loc, state,
+                                "invalid invocations %d specified",
+                                invocations);
+            } else if (invocations > MAX_GEOMETRY_SHADER_INVOCATIONS) {
+               _mesa_glsl_error(&loc, state,
+                                "invocations (%d) exceeds "
+                                "GL_MAX_GEOMETRY_SHADER_INVOCATIONS",
+                                invocations);
+            }
+            shader->Geom.Invocations = invocations;
          }
-         shader->Geom.Invocations = state->in_qualifier->invocations;
       }
       break;
 
-- 
2.4.3



More information about the mesa-dev mailing list