nir/spirv: Move CF emit code into vtn_cfg.c
[mesa.git] / src / glsl / link_interface_blocks.cpp
index ffb44530f5d734797432be776c762602c546345b..64c30fea9a3c7aae5086247aca598abe05c3aae7 100644 (file)
 #include "glsl_symbol_table.h"
 #include "linker.h"
 #include "main/macros.h"
+#include "util/hash_table.h"
+
+
+namespace {
+
+/**
+ * Check if two interfaces match, according to intrastage interface matching
+ * rules.  If they do, and the first interface uses an unsized array, it will
+ * be updated to reflect the array size declared in the second interface.
+ */
+bool
+intrastage_match(ir_variable *a,
+                 ir_variable *b,
+                 struct gl_shader_program *prog)
+{
+   /* Types must match. */
+   if (a->get_interface_type() != b->get_interface_type()) {
+      /* Exception: if both the interface blocks are implicitly declared,
+       * don't force their types to match.  They might mismatch due to the two
+       * shaders using different GLSL versions, and that's ok.
+       */
+      if (a->data.how_declared != ir_var_declared_implicitly ||
+          b->data.how_declared != ir_var_declared_implicitly)
+         return false;
+   }
+
+   /* Presence/absence of interface names must match. */
+   if (a->is_interface_instance() != b->is_interface_instance())
+      return false;
+
+   /* For uniforms, instance names need not match.  For shader ins/outs,
+    * it's not clear from the spec whether they need to match, but
+    * Mesa's implementation relies on them matching.
+    */
+   if (a->is_interface_instance() && b->data.mode != ir_var_uniform &&
+       b->data.mode != ir_var_shader_storage &&
+       strcmp(a->name, b->name) != 0) {
+      return false;
+   }
+
+   /* If a block is an array then it must match across the shader.
+    * Unsized arrays are also processed and matched agaist sized arrays.
+    */
+   if (b->type != a->type &&
+       (b->is_interface_instance() || a->is_interface_instance()) &&
+       !validate_intrastage_arrays(prog, b, a))
+      return false;
+
+   return true;
+}
+
+
+/**
+ * Check if two interfaces match, according to interstage (in/out) interface
+ * matching rules.
+ *
+ * If \c extra_array_level is true, the consumer interface is required to be
+ * an array and the producer interface is required to be a non-array.
+ * This is used for tessellation control and geometry shader consumers.
+ */
+bool
+interstage_match(ir_variable *producer,
+                 ir_variable *consumer,
+                 bool extra_array_level)
+{
+   /* Unsized arrays should not occur during interstage linking.  They
+    * should have all been assigned a size by link_intrastage_shaders.
+    */
+   assert(!consumer->type->is_unsized_array());
+   assert(!producer->type->is_unsized_array());
+
+   /* Types must match. */
+   if (consumer->get_interface_type() != producer->get_interface_type()) {
+      /* Exception: if both the interface blocks are implicitly declared,
+       * don't force their types to match.  They might mismatch due to the two
+       * shaders using different GLSL versions, and that's ok.
+       */
+      if (consumer->data.how_declared != ir_var_declared_implicitly ||
+          producer->data.how_declared != ir_var_declared_implicitly)
+         return false;
+   }
+
+   /* Ignore outermost array if geom shader */
+   const glsl_type *consumer_instance_type;
+   if (extra_array_level) {
+      consumer_instance_type = consumer->type->fields.array;
+   } else {
+      consumer_instance_type = consumer->type;
+   }
+
+   /* If a block is an array then it must match across shaders.
+    * Since unsized arrays have been ruled out, we can check this by just
+    * making sure the types are equal.
+    */
+   if ((consumer->is_interface_instance() &&
+        consumer_instance_type->is_array()) ||
+       (producer->is_interface_instance() &&
+        producer->type->is_array())) {
+      if (consumer_instance_type != producer->type)
+         return false;
+   }
+
+   return true;
+}
+
+
+/**
+ * This class keeps track of a mapping from an interface block name to the
+ * necessary information about that interface block to determine whether to
+ * generate a link error.
+ *
+ * Note: this class is expected to be short lived, so it doesn't make copies
+ * of the strings it references; it simply borrows the pointers from the
+ * ir_variable class.
+ */
+class interface_block_definitions
+{
+public:
+   interface_block_definitions()
+      : mem_ctx(ralloc_context(NULL)),
+        ht(_mesa_hash_table_create(NULL, _mesa_key_hash_string,
+                                   _mesa_key_string_equal))
+   {
+   }
+
+   ~interface_block_definitions()
+   {
+      ralloc_free(mem_ctx);
+      _mesa_hash_table_destroy(ht, NULL);
+   }
+
+   /**
+    * Lookup the interface definition. Return NULL if none is found.
+    */
+   ir_variable *lookup(ir_variable *var)
+   {
+      if (var->data.explicit_location &&
+          var->data.location >= VARYING_SLOT_VAR0) {
+         char location_str[11];
+         snprintf(location_str, 11, "%d", var->data.location);
+
+         const struct hash_entry *entry =
+            _mesa_hash_table_search(ht, location_str);
+         return entry ? (ir_variable *) entry->data : NULL;
+      } else {
+         const struct hash_entry *entry =
+            _mesa_hash_table_search(ht, var->get_interface_type()->name);
+         return entry ? (ir_variable *) entry->data : NULL;
+      }
+   }
+
+   /**
+    * Add a new interface definition.
+    */
+   void store(ir_variable *var)
+   {
+      if (var->data.explicit_location &&
+          var->data.location >= VARYING_SLOT_VAR0) {
+         /* If explicit location is given then lookup the variable by location.
+          * We turn the location into a string and use this as the hash key
+          * rather than the name. Note: We allocate enough space for a 32-bit
+          * unsigned location value which is overkill but future proof.
+          */
+         char location_str[11];
+         snprintf(location_str, 11, "%d", var->data.location);
+         _mesa_hash_table_insert(ht, ralloc_strdup(mem_ctx, location_str), var);
+      } else {
+         _mesa_hash_table_insert(ht, var->get_interface_type()->name, var);
+      }
+   }
+
+private:
+   /**
+    * Ralloc context for data structures allocated by this class.
+    */
+   void *mem_ctx;
+
+   /**
+    * Hash table mapping interface block name to an \c
+    * ir_variable.
+    */
+   hash_table *ht;
+};
+
+
+}; /* anonymous namespace */
+
 
 void
 validate_intrastage_interface_blocks(struct gl_shader_program *prog,
                                      const gl_shader **shader_list,
                                      unsigned num_shaders)
 {
-   glsl_symbol_table interfaces;
+   interface_block_definitions in_interfaces;
+   interface_block_definitions out_interfaces;
+   interface_block_definitions uniform_interfaces;
+   interface_block_definitions buffer_interfaces;
 
    for (unsigned int i = 0; i < num_shaders; i++) {
       if (shader_list[i] == NULL)
          continue;
 
-      foreach_list(node, shader_list[i]->ir) {
-         ir_variable *var = ((ir_instruction *) node)->as_variable();
+      foreach_in_list(ir_instruction, node, shader_list[i]->ir) {
+         ir_variable *var = node->as_variable();
          if (!var)
             continue;
 
-         const glsl_type *iface_type = var->interface_type;
+         const glsl_type *iface_type = var->get_interface_type();
 
          if (iface_type == NULL)
             continue;
 
-         const glsl_type *old_iface_type =
-            interfaces.get_interface(iface_type->name,
-                                     (enum ir_variable_mode) var->mode);
+         interface_block_definitions *definitions;
+         switch (var->data.mode) {
+         case ir_var_shader_in:
+            definitions = &in_interfaces;
+            break;
+         case ir_var_shader_out:
+            definitions = &out_interfaces;
+            break;
+         case ir_var_uniform:
+            definitions = &uniform_interfaces;
+            break;
+         case ir_var_shader_storage:
+            definitions = &buffer_interfaces;
+            break;
+         default:
+            /* Only in, out, and uniform interfaces are legal, so we should
+             * never get here.
+             */
+            assert(!"illegal interface type");
+            continue;
+         }
 
-         if (old_iface_type == NULL) {
+         ir_variable *prev_def = definitions->lookup(var);
+         if (prev_def == NULL) {
             /* This is the first time we've seen the interface, so save
-             * it into our symbol table.
+             * it into the appropriate data structure.
              */
-            interfaces.add_interface(iface_type->name, iface_type,
-                                     (enum ir_variable_mode) var->mode);
-         } else if (old_iface_type != iface_type) {
+            definitions->store(var);
+         } else if (!intrastage_match(prev_def, var, prog)) {
             linker_error(prog, "definitions of interface block `%s' do not"
                          " match\n", iface_type->name);
             return;
@@ -72,41 +280,78 @@ validate_intrastage_interface_blocks(struct gl_shader_program *prog,
 }
 
 void
-validate_interstage_interface_blocks(struct gl_shader_program *prog,
-                                     const gl_shader *producer,
-                                     const gl_shader *consumer)
+validate_interstage_inout_blocks(struct gl_shader_program *prog,
+                                 const gl_shader *producer,
+                                 const gl_shader *consumer)
 {
-   glsl_symbol_table interfaces;
+   interface_block_definitions definitions;
+   /* VS -> GS, VS -> TCS, VS -> TES, TES -> GS */
+   const bool extra_array_level = (producer->Stage == MESA_SHADER_VERTEX &&
+                                   consumer->Stage != MESA_SHADER_FRAGMENT) ||
+                                  consumer->Stage == MESA_SHADER_GEOMETRY;
 
-   /* Add non-output interfaces from the consumer to the symbol table. */
-   foreach_list(node, consumer->ir) {
-      ir_variable *var = ((ir_instruction *) node)->as_variable();
-      if (!var || !var->interface_type || var->mode == ir_var_shader_out)
+   /* Add input interfaces from the consumer to the symbol table. */
+   foreach_in_list(ir_instruction, node, consumer->ir) {
+      ir_variable *var = node->as_variable();
+      if (!var || !var->get_interface_type() || var->data.mode != ir_var_shader_in)
          continue;
 
-      interfaces.add_interface(var->interface_type->name,
-                               var->interface_type,
-                               (enum ir_variable_mode) var->mode);
+      definitions.store(var);
    }
 
-   /* Verify that the producer's interfaces match. */
-   foreach_list(node, producer->ir) {
-      ir_variable *var = ((ir_instruction *) node)->as_variable();
-      if (!var || !var->interface_type || var->mode == ir_var_shader_in)
+   /* Verify that the producer's output interfaces match. */
+   foreach_in_list(ir_instruction, node, producer->ir) {
+      ir_variable *var = node->as_variable();
+      if (!var || !var->get_interface_type() || var->data.mode != ir_var_shader_out)
          continue;
 
-      enum ir_variable_mode consumer_mode =
-         var->mode == ir_var_uniform ? ir_var_uniform : ir_var_shader_in;
-      const glsl_type *expected_type =
-         interfaces.get_interface(var->interface_type->name, consumer_mode);
+      ir_variable *consumer_def = definitions.lookup(var);
 
       /* The consumer doesn't use this output block.  Ignore it. */
-      if (expected_type == NULL)
+      if (consumer_def == NULL)
          continue;
 
-      if (var->interface_type != expected_type) {
-         linker_error(prog, "interface block mismatch between shader stages\n");
+      if (!interstage_match(var, consumer_def, extra_array_level)) {
+         linker_error(prog, "definitions of interface block `%s' do not "
+                      "match\n", var->get_interface_type()->name);
          return;
       }
    }
 }
+
+
+void
+validate_interstage_uniform_blocks(struct gl_shader_program *prog,
+                                   gl_shader **stages, int num_stages)
+{
+   interface_block_definitions definitions;
+
+   for (int i = 0; i < num_stages; i++) {
+      if (stages[i] == NULL)
+         continue;
+
+      const gl_shader *stage = stages[i];
+      foreach_in_list(ir_instruction, node, stage->ir) {
+         ir_variable *var = node->as_variable();
+         if (!var || !var->get_interface_type() ||
+             (var->data.mode != ir_var_uniform &&
+              var->data.mode != ir_var_shader_storage))
+            continue;
+
+         ir_variable *old_def = definitions.lookup(var);
+         if (old_def == NULL) {
+            definitions.store(var);
+         } else {
+            /* Interstage uniform matching rules are the same as intrastage
+             * uniform matchin rules (for uniforms, it is as though all
+             * shaders are in the same shader stage).
+             */
+            if (!intrastage_match(old_def, var, prog)) {
+               linker_error(prog, "definitions of interface block `%s' do not "
+                            "match\n", var->get_interface_type()->name);
+               return;
+            }
+         }
+      }
+   }
+}