spirv: Handle OpTypeForwardPointer
authorJason Ekstrand <jason.ekstrand@intel.com>
Sat, 19 Jan 2019 16:21:28 +0000 (10:21 -0600)
committerJason Ekstrand <jason.ekstrand@intel.com>
Sat, 26 Jan 2019 19:41:50 +0000 (13:41 -0600)
We handle forward declarations by creating the pointer type with it's
storage type based on storage class and just waiting to fill out the
actual deref type until we get the OpTypePointer.  Because any
composites using the forward declared type only care about the storage
type (i.e. uint64_t, uvec2, etc.) when creating their glsl_type, this
works fine and we can defer the actual deref_type as far as we need.

Reviewed-by: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Reviewed-by: Karol Herbst <kherbst@redhat.com>
src/compiler/spirv/spirv_to_nir.c

index b54d259e14b33894b5fad9a73eac170ffef0af1d..8c85eac587534bd6cbede0315cf8a435635dcec4 100644 (file)
@@ -1101,10 +1101,18 @@ static void
 vtn_handle_type(struct vtn_builder *b, SpvOp opcode,
                 const uint32_t *w, unsigned count)
 {
-   struct vtn_value *val = vtn_push_value(b, w[1], vtn_value_type_type);
+   struct vtn_value *val = NULL;
 
-   val->type = rzalloc(b, struct vtn_type);
-   val->type->id = w[1];
+   /* In order to properly handle forward declarations, we have to defer
+    * allocation for pointer types.
+    */
+   if (opcode != SpvOpTypePointer && opcode != SpvOpTypeForwardPointer) {
+      val = vtn_push_value(b, w[1], vtn_value_type_type);
+      vtn_fail_if(val->type != NULL,
+                  "Only pointers can have forward declarations");
+      val->type = rzalloc(b, struct vtn_type);
+      val->type->id = w[1];
+   }
 
    switch (opcode) {
    case SpvOpTypeVoid:
@@ -1274,46 +1282,70 @@ vtn_handle_type(struct vtn_builder *b, SpvOp opcode,
       break;
    }
 
-   case SpvOpTypePointer: {
+   case SpvOpTypePointer:
+   case SpvOpTypeForwardPointer: {
+      /* We can't blindly push the value because it might be a forward
+       * declaration.
+       */
+      val = vtn_untyped_value(b, w[1]);
+
       SpvStorageClass storage_class = w[2];
-      struct vtn_type *deref_type =
-         vtn_value(b, w[3], vtn_value_type_type)->type;
 
-      val->type->base_type = vtn_base_type_pointer;
-      val->type->storage_class = storage_class;
-      val->type->deref = deref_type;
+      if (val->value_type == vtn_value_type_invalid) {
+         val->value_type = vtn_value_type_type;
+         val->type = rzalloc(b, struct vtn_type);
+         val->type->id = w[1];
+         val->type->base_type = vtn_base_type_pointer;
+         val->type->storage_class = storage_class;
 
-      vtn_foreach_decoration(b, val, array_stride_decoration_cb, NULL);
+         /* These can actually be stored to nir_variables and used as SSA
+          * values so they need a real glsl_type.
+          */
+         switch (storage_class) {
+         case SpvStorageClassUniform:
+            val->type->type = b->options->ubo_ptr_type;
+            break;
+         case SpvStorageClassStorageBuffer:
+            val->type->type = b->options->ssbo_ptr_type;
+            break;
+         case SpvStorageClassPushConstant:
+            val->type->type = b->options->push_const_ptr_type;
+            break;
+         case SpvStorageClassWorkgroup:
+            val->type->type = b->options->shared_ptr_type;
+            break;
+         default:
+            /* In this case, no variable pointers are allowed so all deref
+             * chains are complete back to the variable and it doesn't matter
+             * what type gets used so we leave it NULL.
+             */
+            break;
+         }
+      } else {
+         vtn_fail_if(val->type->storage_class != storage_class,
+                     "The storage classes of an OpTypePointer and any "
+                     "OpTypeForwardPointers that provide forward "
+                     "declarations of it must match.");
+      }
 
-      /* These can actually be stored to nir_variables and used as SSA
-       * values so they need a real glsl_type.
-       */
-      switch (storage_class) {
-      case SpvStorageClassUniform:
-         val->type->type = b->options->ubo_ptr_type;
-         break;
-      case SpvStorageClassStorageBuffer:
-         val->type->type = b->options->ssbo_ptr_type;
-         break;
-      case SpvStorageClassPushConstant:
-         val->type->type = b->options->push_const_ptr_type;
-         break;
-      case SpvStorageClassWorkgroup:
-         val->type->type = b->options->shared_ptr_type;
-         if (b->options->lower_workgroup_access_to_offsets) {
+      if (opcode == SpvOpTypePointer) {
+         vtn_fail_if(val->type->deref != NULL,
+                     "While OpTypeForwardPointer can be used to provide a "
+                     "forward declaration of a pointer, OpTypePointer can "
+                     "only be used once for a given id.");
+
+         val->type->deref = vtn_value(b, w[3], vtn_value_type_type)->type;
+
+         vtn_foreach_decoration(b, val, array_stride_decoration_cb, NULL);
+
+         if (storage_class == SpvStorageClassWorkgroup &&
+             b->options->lower_workgroup_access_to_offsets) {
             uint32_t size, align;
             val->type->deref = vtn_type_layout_std430(b, val->type->deref,
                                                       &size, &align);
             val->type->length = size;
             val->type->align = align;
          }
-         break;
-      default:
-         /* In this case, no variable pointers are allowed so all deref chains
-          * are complete back to the variable and it doesn't matter what type
-          * gets used so we leave it NULL.
-          */
-         break;
       }
       break;
    }
@@ -3931,6 +3963,7 @@ vtn_handle_variable_or_type_instruction(struct vtn_builder *b, SpvOp opcode,
    case SpvOpTypeStruct:
    case SpvOpTypeOpaque:
    case SpvOpTypePointer:
+   case SpvOpTypeForwardPointer:
    case SpvOpTypeFunction:
    case SpvOpTypeEvent:
    case SpvOpTypeDeviceEvent: