spirv-writer: support identity cast for vectors

Pending WGSL spec update https://github.com/gpuweb/gpuweb/issues/1103

Bug: tint:352
Change-Id: Ice44066ffcbb64de2544f82489e07b19a2423f72
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33601
Commit-Queue: David Neto <dneto@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 081a3a2..f9dd6b4 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -1224,7 +1224,7 @@
          result_type->AsVector()->size() == value_type->AsVector()->size());
   }
   if (can_cast_or_copy) {
-    return GenerateCastOrCopy(result_type, values[0]);
+    return GenerateCastOrCopyOrPassthrough(result_type, values[0]);
   }
 
   auto type_id = GenerateTypeIfNeeded(init->type());
@@ -1268,7 +1268,7 @@
     // Both scalars, but not the same type so we need to generate a conversion
     // of the value.
     if (value_type->is_scalar() && result_type->is_scalar()) {
-      id = GenerateCastOrCopy(result_type, values[0]);
+      id = GenerateCastOrCopyOrPassthrough(result_type, values[0]);
       out << "_" << id;
       ops.push_back(Operand::Int(id));
       continue;
@@ -1351,8 +1351,8 @@
   return result.to_i();
 }
 
-uint32_t Builder::GenerateCastOrCopy(ast::type::Type* to_type,
-                                     ast::Expression* from_expr) {
+uint32_t Builder::GenerateCastOrCopyOrPassthrough(ast::type::Type* to_type,
+                                                  ast::Expression* from_expr) {
   auto result = result_op();
   auto result_id = result.to_i();
 
@@ -1388,8 +1388,9 @@
   } else if ((from_type->IsBool() && to_type->IsBool()) ||
              (from_type->IsU32() && to_type->IsU32()) ||
              (from_type->IsI32() && to_type->IsI32()) ||
-             (from_type->IsF32() && to_type->IsF32())) {
-    op = spv::Op::OpCopyObject;
+             (from_type->IsF32() && to_type->IsF32()) ||
+             (from_type->IsVector() && (from_type == to_type))) {
+    return val_id;
   } else if ((from_type->IsI32() && to_type->IsU32()) ||
              (from_type->IsU32() && to_type->IsI32()) ||
              (from_type->is_signed_integer_vector() &&
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index ac3f452..7be24c6 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -350,12 +350,14 @@
   uint32_t GenerateSampledImage(ast::type::Type* texture_type,
                                 Operand texture_operand,
                                 Operand sampler_operand);
-  /// Generates a cast or object copy for the expression result
+  /// Generates a cast or object copy for the expression result,
+  /// or return the ID generated the expression if it is already
+  /// of the right type.
   /// @param to_type the type we're casting too
   /// @param from_expr the expression to cast
   /// @returns the expression ID on success or 0 otherwise
-  uint32_t GenerateCastOrCopy(ast::type::Type* to_type,
-                              ast::Expression* from_expr);
+  uint32_t GenerateCastOrCopyOrPassthrough(ast::type::Type* to_type,
+                                           ast::Expression* from_expr);
   /// Generates a loop statement
   /// @param stmt the statement to generate
   /// @returns true on successful generation
diff --git a/src/writer/spirv/builder_constructor_expression_test.cc b/src/writer/spirv/builder_constructor_expression_test.cc
index 308dd50..4ac92c0 100644
--- a/src/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/writer/spirv/builder_constructor_expression_test.cc
@@ -193,15 +193,13 @@
 
   b.push_function(Function{});
 
-  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+  EXPECT_EQ(b.GenerateExpression(&cast), 3u);
   ASSERT_FALSE(b.has_error()) << b.error();
 
   EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeBool
 %3 = OpConstantTrue %2
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpCopyObject %2 %3
-)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_I32_With_I32) {
@@ -210,14 +208,12 @@
   ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
 
   b.push_function(Function{});
-  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+  EXPECT_EQ(b.GenerateExpression(&cast), 3u);
 
   EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
 %3 = OpConstant %2 2
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpCopyObject %2 %3
-)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_U32_With_U32) {
@@ -226,14 +222,12 @@
   ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
 
   b.push_function(Function{});
-  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+  EXPECT_EQ(b.GenerateExpression(&cast), 3u);
 
   EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 0
 %3 = OpConstant %2 2
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpCopyObject %2 %3
-)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_F32_With_F32) {
@@ -242,14 +236,12 @@
   ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
 
   b.push_function(Function{});
-  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+  EXPECT_EQ(b.GenerateExpression(&cast), 3u);
 
   EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %3 = OpConstant %2 2
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpCopyObject %2 %3
-)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F32_F32) {
@@ -267,6 +259,23 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_Vec2) {
+  auto* value = vec2<f32>(2.0f, 2.0f);
+  auto* cast = vec2<f32>(value);
+
+  ASSERT_TRUE(td.DetermineResultType(cast)) << td.error();
+
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(cast), 5u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
+%2 = OpTypeVector %3 2
+%4 = OpConstant %3 2
+%5 = OpConstantComposite %2 %4 %4
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F32_F32_F32) {
   auto* cast = vec3<f32>(2.0f, 2.0f, 2.0f);
 
@@ -324,6 +333,23 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_Vec3) {
+  auto* value = vec3<f32>(2.0f, 2.0f, 2.0f);
+  auto* cast = vec3<f32>(value);
+
+  ASSERT_TRUE(td.DetermineResultType(cast)) << td.error();
+
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(cast), 5u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
+%2 = OpTypeVector %3 3
+%4 = OpConstant %3 2
+%5 = OpConstantComposite %2 %4 %4 %4
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_F32_F32_F32) {
   auto* cast = vec4<f32>(2.0f, 2.0f, 2.0f, 2.0f);
 
@@ -469,6 +495,68 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec4) {
+  auto* value = vec4<f32>(2.0f, 2.0f, 2.0f, 2.0f);
+  auto* cast = vec4<f32>(value);
+
+  ASSERT_TRUE(td.DetermineResultType(cast)) << td.error();
+
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(cast), 5u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
+%2 = OpTypeVector %3 4
+%4 = OpConstant %3 2
+%5 = OpConstantComposite %2 %4 %4 %4 %4
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec2_With_Vec2) {
+  auto* cast = vec2<f32>(vec2<f32>(2.0f, 2.0f));
+
+  ASSERT_TRUE(td.DetermineResultType(cast)) << td.error();
+
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(nullptr, cast, true), 5u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
+%2 = OpTypeVector %3 2
+%4 = OpConstant %3 2
+%5 = OpConstantComposite %2 %4 %4
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_Vec3) {
+  auto* cast = vec3<f32>(vec3<f32>(2.0f, 2.0f, 2.0f));
+
+  ASSERT_TRUE(td.DetermineResultType(cast)) << td.error();
+
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(nullptr, cast, true), 5u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
+%2 = OpTypeVector %3 3
+%4 = OpConstant %3 2
+%5 = OpConstantComposite %2 %4 %4 %4
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_Vec4) {
+  auto* cast = vec4<f32>(vec4<f32>(2.0f, 2.0f, 2.0f, 2.0f));
+
+  ASSERT_TRUE(td.DetermineResultType(cast)) << td.error();
+
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(nullptr, cast, true), 5u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
+%2 = OpTypeVector %3 4
+%4 = OpConstant %3 2
+%5 = OpConstantComposite %2 %4 %4 %4 %4
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_F32_Vec2) {
   auto* cast = vec3<f32>(2.0f, vec2<f32>(2.0f, 2.0f));