spirv-reader: support OpImageQuerySize

Change-Id: I27ad580ae84f18a69b31700f56bbbcf59d3818e6
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/39680
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index e5ede5f..e2e5e41 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -524,6 +524,18 @@
     case SpvOpImageRead:
     case SpvOpImageWrite:
     case SpvOpImageFetch:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
+// @param opcode a SPIR-V opcode
+// @returns true if the given instruction is an image query instruction
+bool IsImageQuery(SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpImageQuerySize:
     case SpvOpImageQuerySizeLod:
     case SpvOpImageQueryLevels:
     case SpvOpImageQuerySamples:
@@ -2937,6 +2949,10 @@
     return EmitImageAccess(inst);
   }
 
+  if (IsImageQuery(inst.opcode())) {
+    return EmitImageQuery(inst);
+  }
+
   switch (inst.opcode()) {
     case SpvOpNop:
       return true;
@@ -4349,6 +4365,48 @@
   return success();
 }
 
+bool FunctionEmitter::EmitImageQuery(const spvtools::opt::Instruction& inst) {
+  const spvtools::opt::Instruction* image = GetImage(inst);
+  if (!image) {
+    return false;
+  }
+  auto* texture_type = GetImageType(*image);
+  if (!texture_type) {
+    return false;
+  }
+
+  const auto opcode = inst.opcode();
+  switch (opcode) {
+    case SpvOpImageQuerySize: {
+      ast::ExpressionList exprs;
+      // Invoke textureDimensions.
+      // If the texture is arrayed, combine with the result from
+      // textureNumLayers.
+      auto* dims_ident = create<ast::IdentifierExpression>(
+          Source{}, builder_.Symbols().Register("textureDimensions"));
+      exprs.push_back(create<ast::CallExpression>(
+          Source{}, dims_ident, ast::ExpressionList{GetImageExpression(inst)}));
+      if (type::IsTextureArray(texture_type->dim())) {
+        auto* layers_ident = create<ast::IdentifierExpression>(
+            Source{}, builder_.Symbols().Register("textureNumLayers"));
+        exprs.push_back(create<ast::CallExpression>(
+            Source{}, layers_ident, ast::ExpressionList{GetImageExpression(inst)}));
+      }
+      auto* result_type = parser_impl_.ConvertType(inst.type_id());
+      TypedExpression expr = {
+          result_type,
+          create<ast::TypeConstructorExpression>(Source{}, result_type, exprs)};
+      return EmitConstDefOrWriteToHoistedVar(inst, expr);
+    }
+    case SpvOpImageQuerySizeLod:  // TODO(dneto)
+    case SpvOpImageQueryLevels:   // TODO(dneto)
+    case SpvOpImageQuerySamples:  // TODO(dneto)
+    default:
+      break;
+  }
+  return Fail() << "unhandled image query: " << inst.PrettyPrint();
+}
+
 ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
     const spvtools::opt::Instruction& inst) {
   if (!parser_impl_.success()) {
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index 5eff0df..fe6ceaf 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -897,6 +897,11 @@
   /// @returns an expression
   bool EmitImageAccess(const spvtools::opt::Instruction& inst);
 
+  /// Emits statements to implement a SPIR-V image query.
+  /// @param inst the SPIR-V instruction
+  /// @returns an expression
+  bool EmitImageQuery(const spvtools::opt::Instruction& inst);
+
   /// Converts the given texel to match the type required for the storage
   /// texture with the given type. This can generate a swizzle to retain
   /// only the first few components of the texel vector, and maybe a bitcast
diff --git a/src/reader/spirv/parser_impl_handle_test.cc b/src/reader/spirv/parser_impl_handle_test.cc
index 4a226ac..0a7143e 100644
--- a/src/reader/spirv/parser_impl_handle_test.cc
+++ b/src/reader/spirv/parser_impl_handle_test.cc
@@ -3670,6 +3670,260 @@
       }
     })"}}));
 
+INSTANTIATE_TEST_SUITE_P(
+    ImageQuerySize_NonArrayed_SignedResult,
+    // ImageQuerySize requires storage image or multisampled
+    // For storage image, use another instruction to indicate whether it
+    // is readonly or writeonly.
+    SpvParserTest_SampledImageAccessTest,
+    ::testing::ValuesIn(std::vector<ImageAccessCase>{
+        // 1D storage image
+        {"%float 1D 0 0 0 2 Rgba32f",
+         "%99 = OpImageQuerySize %int %im \n"
+         "%98 = OpImageRead %v4float %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __access_control_read_only__storage_texture_1d_rgba32float
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __i32
+        {
+          TypeConstructor[not set]{
+            __i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"},
+        // 2D storage image
+        {"%float 2D 0 0 0 2 Rgba32f",
+         "%99 = OpImageQuerySize %v2int %im \n"
+         "%98 = OpImageRead %v4float %im %vi12\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __access_control_read_only__storage_texture_2d_rgba32float
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_2__i32
+        {
+          TypeConstructor[not set]{
+            __vec_2__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"},
+        // 3D storage image
+        {"%float 3D 0 0 0 2 Rgba32f",
+         "%99 = OpImageQuerySize %v3int %im \n"
+         "%98 = OpImageRead %v4float %im %vi123\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __access_control_read_only__storage_texture_3d_rgba32float
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_3__i32
+        {
+          TypeConstructor[not set]{
+            __vec_3__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // Multisampled
+        {"%float 2D 0 0 1 1 Unknown",
+         "%99 = OpImageQuerySize %v2int %im \n"
+         "%98 = OpImageRead %v4float %im %vi12\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __multisampled_texture_2d__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_2__i32
+        {
+          TypeConstructor[not set]{
+            __vec_2__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    ImageQuerySize_Arrayed_SignedResult,
+    // ImageQuerySize requires storage image or multisampled
+    // For storage image, use another instruction to indicate whether it
+    // is readonly or writeonly.
+    SpvParserTest_SampledImageAccessTest,
+    ::testing::ValuesIn(std::vector<ImageAccessCase>{
+        // 1D array storage image
+        {"%float 1D 0 1 0 2 Rgba32f",
+         "%99 = OpImageQuerySize %v2int %im \n"
+         "%98 = OpImageRead %v4float %im %vi12\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __access_control_read_only__storage_texture_1d_array_rgba32float
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_2__i32
+        {
+          TypeConstructor[not set]{
+            __vec_2__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+            Call[not set]{
+              Identifier[not set]{textureNumLayers}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"},
+        // 2D array storage image
+        {"%float 2D 0 1 0 2 Rgba32f",
+         "%99 = OpImageQuerySize %v3int %im \n"
+         "%98 = OpImageRead %v4float %im %vi123\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __access_control_read_only__storage_texture_2d_array_rgba32float
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_3__i32
+        {
+          TypeConstructor[not set]{
+            __vec_3__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+            Call[not set]{
+              Identifier[not set]{textureNumLayers}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"},
+        // 3D array storage image doesn't exist.
+
+        // Multisampled array
+        {"%float 2D 0 1 1 1 Unknown",
+         "%99 = OpImageQuerySize %v3int %im \n"
+         "%98 = OpImageRead %v4float %im %vi123\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __multisampled_texture_2d_array__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_3__i32
+        {
+          TypeConstructor[not set]{
+            __vec_3__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+            Call[not set]{
+              Identifier[not set]{textureNumLayers}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"}}));
+
 struct ImageCoordsCase {
   // SPIR-V image type, excluding result ID and opcode
   std::string spirv_image_type_details;