writer/hlsl: Move the coord/arrayidx packing to utility function

So we can also use this for the `spirv` backend

Bug: tint:146
Change-Id: I26f70125a5015946d2428a6e669da32bdea23bcd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33780
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 5199a59..bf1d393 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -426,6 +426,8 @@
     "src/validator/validator_test_helper.h",
     "src/writer/float_to_string.cc",
     "src/writer/float_to_string.h",
+    "src/writer/pack_coord_arrayidx.cc",
+    "src/writer/pack_coord_arrayidx.h",
     "src/writer/text.cc",
     "src/writer/text.h",
     "src/writer/text_generator.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0ff1ee2..e41d3b7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -247,6 +247,8 @@
   validator/validator_test_helper.h
   writer/float_to_string.cc
   writer/float_to_string.h
+  writer/pack_coord_arrayidx.cc
+  writer/pack_coord_arrayidx.h
   writer/text.cc
   writer/text.h
   writer/text_generator.cc
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 5eb1a3b..86edcf7 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -52,6 +52,7 @@
 #include "src/ast/unary_op_expression.h"
 #include "src/ast/variable_decl_statement.h"
 #include "src/writer/float_to_string.h"
+#include "src/writer/pack_coord_arrayidx.h"
 
 namespace tint {
 namespace writer {
@@ -103,20 +104,6 @@
   return 0;
 }
 
-ast::TypeConstructorExpression* AsVectorConstructor(ast::Expression* expr) {
-  if (!expr->IsConstructor())
-    return nullptr;
-  auto* constructor = expr->AsConstructor();
-  if (!constructor->IsTypeConstructor()) {
-    return nullptr;
-  }
-  auto* type_constructor = constructor->AsTypeConstructor();
-  if (!type_constructor->type()->IsVector()) {
-    return nullptr;
-  }
-  return type_constructor;
-}
-
 }  // namespace
 
 GeneratorImpl::GeneratorImpl(Context* ctx, ast::Module* module)
@@ -775,41 +762,12 @@
     // Array index needs to be appended to the coordinates.
     auto* param_coords = params[pidx.coords];
     auto* param_array_index = params[pidx.array_index];
-
-    uint32_t packed_coords_size;
-    ast::type::Type* packed_coords_el_ty;  // Currenly must be f32.
-    if (param_coords->result_type()->IsVector()) {
-      auto* vec = param_coords->result_type()->AsVector();
-      packed_coords_size = vec->size() + 1;
-      packed_coords_el_ty = vec->type();
-    } else {
-      packed_coords_size = 2;
-      packed_coords_el_ty = param_coords->result_type();
-    }
-
-    // Cast param_array_index to the vector element type
-    ast::TypeConstructorExpression array_index_cast(packed_coords_el_ty,
-                                                    {param_array_index});
-    array_index_cast.set_result_type(packed_coords_el_ty);
-
-    ast::type::VectorType packed_coords_ty(packed_coords_el_ty,
-                                           packed_coords_size);
-
-    ast::ExpressionList coords;
-    // If the coordinates are already passed in a vector constructor, extract
-    // the elements into the new vector instead of nesting a vector-in-vector.
-    if (auto* vc = AsVectorConstructor(param_coords)) {
-      coords = vc->values();
-    } else {
-      coords.emplace_back(param_coords);
-    }
-    coords.emplace_back(&array_index_cast);
-
-    ast::TypeConstructorExpression constructor{&packed_coords_ty,
-                                               std::move(coords)};
-
-    if (!EmitExpression(pre, out, &constructor))
+    if (!PackCoordAndArrayIndex(param_coords, param_array_index,
+                                [&](ast::TypeConstructorExpression* packed) {
+                                  return EmitExpression(pre, out, packed);
+                                })) {
       return false;
+    }
 
   } else {
     if (!EmitExpression(pre, out, params[pidx.coords]))
diff --git a/src/writer/pack_coord_arrayidx.cc b/src/writer/pack_coord_arrayidx.cc
new file mode 100644
index 0000000..3384ee6
--- /dev/null
+++ b/src/writer/pack_coord_arrayidx.cc
@@ -0,0 +1,85 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/writer/pack_coord_arrayidx.h"
+
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/type/vector_type.h"
+#include "src/ast/type_constructor_expression.h"
+
+namespace tint {
+namespace writer {
+
+namespace {
+
+ast::TypeConstructorExpression* AsVectorConstructor(ast::Expression* expr) {
+  if (!expr->IsConstructor())
+    return nullptr;
+  auto* constructor = expr->AsConstructor();
+  if (!constructor->IsTypeConstructor()) {
+    return nullptr;
+  }
+  auto* type_constructor = constructor->AsTypeConstructor();
+  if (!type_constructor->type()->IsVector()) {
+    return nullptr;
+  }
+  return type_constructor;
+}
+
+}  // namespace
+
+bool PackCoordAndArrayIndex(
+    ast::Expression* coords,
+    ast::Expression* array_idx,
+    std::function<bool(ast::TypeConstructorExpression*)> callback) {
+  uint32_t packed_size;
+  ast::type::Type* packed_el_ty;  // Currenly must be f32.
+  if (coords->result_type()->IsVector()) {
+    auto* vec = coords->result_type()->AsVector();
+    packed_size = vec->size() + 1;
+    packed_el_ty = vec->type();
+  } else {
+    packed_size = 2;
+    packed_el_ty = coords->result_type();
+  }
+
+  if (!packed_el_ty) {
+    return false;  // missing type info
+  }
+
+  // Cast array_idx to the vector element type
+  ast::TypeConstructorExpression array_index_cast(packed_el_ty, {array_idx});
+  array_index_cast.set_result_type(packed_el_ty);
+
+  ast::type::VectorType packed_ty(packed_el_ty, packed_size);
+
+  // If the coordinates are already passed in a vector constructor, extract
+  // the elements into the new vector instead of nesting a vector-in-vector.
+  ast::ExpressionList packed;
+  if (auto* vc = AsVectorConstructor(coords)) {
+    packed = vc->values();
+  } else {
+    packed.emplace_back(coords);
+  }
+  packed.emplace_back(&array_index_cast);
+
+  ast::TypeConstructorExpression constructor{&packed_ty, std::move(packed)};
+
+  return callback(&constructor);
+}
+
+}  // namespace writer
+}  // namespace tint
diff --git a/src/writer/pack_coord_arrayidx.h b/src/writer/pack_coord_arrayidx.h
new file mode 100644
index 0000000..d31728a
--- /dev/null
+++ b/src/writer/pack_coord_arrayidx.h
@@ -0,0 +1,52 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_WRITER_PACK_COORD_ARRAYIDX_H_
+#define SRC_WRITER_PACK_COORD_ARRAYIDX_H_
+
+#include <functional>
+
+#include "src/source.h"
+
+namespace tint {
+
+namespace ast {
+class Expression;
+class TypeConstructorExpression;
+}  // namespace ast
+
+namespace writer {
+
+/// A helper function use to generate texture intrinsic function calls for
+/// backends that expect the texture coordinate and array index to be packed
+/// together into a single 'coordinate' parameter.
+/// PackCoordAndArrayIndex() calls the @p callback function with a vector
+/// expression containing the elements of @p coords followed by the single
+/// element of @p array_idx cast to the @p coords element type.
+/// All types must have been assigned to the expressions and their child nodes
+/// before calling.
+/// @param coords the texture coordinates. May be a scalar, `vec2` or `vec3`.
+/// @param array_idx the texture array index. Must be a scalar.
+/// @param callback the function called with the packed result. Note that the
+/// pointer argument is only valid for the duration of the call.
+/// @returns the value returned by `callback` to indicate success
+bool PackCoordAndArrayIndex(
+    ast::Expression* coords,
+    ast::Expression* array_idx,
+    std::function<bool(ast::TypeConstructorExpression*)> callback);
+
+}  // namespace writer
+}  // namespace tint
+
+#endif  // SRC_WRITER_PACK_COORD_ARRAYIDX_H_