spirv-reader: support OpVectorInsertDynamic

Change-Id: I30b9ad14faf921c3ec0bf47729480d4f7d37f561
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/39161
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index fd3aff3..e5ede5f 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -3021,6 +3021,10 @@
       // Synthesize an outer product expression in its own statement.
       return EmitConstDefOrWriteToHoistedVar(inst, MakeOuterProduct(inst));
 
+    case SpvOpVectorInsertDynamic:
+      // Synthesize a vector insertion in its own statements.
+      return MakeVectorInsertDynamic(inst);
+
     case SpvOpFunctionCall:
       return EmitFunctionCall(inst);
 
@@ -3171,7 +3175,6 @@
   //    OpGenericCastToPtrExplicit // Not in Vulkan
   //
   //    OpArrayLength
-  //    OpVectorInsertDynamic
   //    OpCompositeInsert
 
   return {};
@@ -4646,6 +4649,47 @@
                                                             result_columns)};
 }
 
+bool FunctionEmitter::MakeVectorInsertDynamic(
+    const spvtools::opt::Instruction& inst) {
+  // For
+  //    %result = OpVectorInsertDynamic %type %src_vector %component %index
+  // generate statements like this:
+  //
+  //    var temp : type = src_vector;
+  //    temp[index] = component;
+  //    const result : type = temp;
+  //
+  // Then use result everywhere the original SPIR-V id is used.  Using a const
+  // like this avoids constantly reloading the value many times.
+
+  auto* ast_type = parser_impl_.ConvertType(inst.type_id());
+  auto src_vector = MakeOperand(inst, 0);
+  auto component = MakeOperand(inst, 1);
+  auto index = MakeOperand(inst, 2);
+
+  // Synthesize the temporary variable.
+  // It doesn't correspond to a SPIR-V ID, so we don't use the ordinary
+  // API in parser_impl_.
+  auto result_name = namer_.Name(inst.result_id());
+  auto temp_name = namer_.MakeDerivedName(result_name);
+  auto registered_temp_name = builder_.Symbols().Register(temp_name);
+
+  auto* temp_var = create<ast::Variable>(
+      Source{}, registered_temp_name, ast::StorageClass::kFunction, ast_type,
+      false, src_vector.expr, ast::VariableDecorationList{});
+  AddStatement(create<ast::VariableDeclStatement>(Source{}, temp_var));
+
+  auto* lhs = create<ast::ArrayAccessorExpression>(
+      Source{}, create<ast::IdentifierExpression>(registered_temp_name),
+      index.expr);
+
+  AddStatement(create<ast::AssignmentStatement>(Source{}, lhs, component.expr));
+
+  return EmitConstDefinition(
+      inst,
+      {ast_type, create<ast::IdentifierExpression>(registered_temp_name)});
+}
+
 FunctionEmitter::FunctionDeclaration::FunctionDeclaration() = default;
 FunctionEmitter::FunctionDeclaration::~FunctionDeclaration() = default;
 
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index eaafa68..5eff0df 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -860,6 +860,12 @@
   /// @returns an expression
   TypedExpression MakeOuterProduct(const spvtools::opt::Instruction& inst);
 
+  /// Generates statements for a SPIR-V OpVectorInsertDynamic instruction.
+  /// Registers a const declaration for the result.
+  /// @param inst the SPIR-V instruction
+  /// @returns an expression
+  bool MakeVectorInsertDynamic(const spvtools::opt::Instruction& inst);
+
   /// Get the SPIR-V instruction for the image memory object declaration for
   /// the image operand to the given instruction.
   /// @param inst the SPIR-V instruction
diff --git a/src/reader/spirv/function_composite_test.cc b/src/reader/spirv/function_composite_test.cc
index e5ff9b4..adff130 100644
--- a/src/reader/spirv/function_composite_test.cc
+++ b/src/reader/spirv/function_composite_test.cc
@@ -32,6 +32,11 @@
 
 std::string Preamble() {
   return R"(
+  OpCapability Shader
+  OpMemoryModel Logical Simple
+  OpEntryPoint GLCompute %100 "main"
+  OpExecutionMode %100 LocalSize 1 1 1
+
   %void = OpTypeVoid
   %voidfn = OpTypeFunction %void
 
@@ -864,6 +869,56 @@
 })")) << got;
 }
 
+using SpvParserTest_VectorInsertDynamic = SpvParserTest;
+
+TEST_F(SpvParserTest_VectorExtractDynamic, Sample) {
+  const auto assembly = Preamble() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpCopyObject %v2uint %v2uint_3_4
+     %2 = OpCopyObject %uint %uint_3
+     %3 = OpCopyObject %int %int_1
+     %10 = OpVectorInsertDynamic %v2uint %1 %2 %3
+     OpReturn
+     OpFunctionEnd
+)";
+
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  const auto got = ToString(p->builder(), fe.ast_body());
+  EXPECT_THAT(got, HasSubstr(R"(
+VariableDeclStatement{
+  Variable{
+    x_10_1
+    function
+    __vec_2__u32
+    {
+      Identifier[not set]{x_1}
+    }
+  }
+}
+Assignment{
+  ArrayAccessor[not set]{
+    Identifier[not set]{x_10_1}
+    Identifier[not set]{x_3}
+  }
+  Identifier[not set]{x_2}
+}
+VariableDeclStatement{
+  VariableConst{
+    x_10
+    none
+    __vec_2__u32
+    {
+      Identifier[not set]{x_10_1}
+    }
+  }
+})")) << got
+      << assembly;
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace reader