[spirv-writer] Generate function parameters.

This CL adds generation of OpFunctionParameter entries for function
parameters.

Bug: tint:5
Change-Id: I7af6cb756e20674f32737f2ef362fda12c9d2ef2
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/23620
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 3f2d289..de986e7 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -104,8 +104,6 @@
   }
 
   auto* last = stmts.back().get();
-  // TODO(dneto): Conditional break and conditional continue should return
-  // false.
   return last->IsBreak() || last->IsContinue() || last->IsReturn() ||
          last->IsKill() || last->IsFallthrough();
 }
@@ -391,16 +389,32 @@
     return false;
   }
 
-  // TODO(dsinclair): Handle parameters
+  scope_stack_.push_scope();
 
   auto definition_inst = Instruction{
       spv::Op::OpFunction,
       {Operand::Int(ret_id), func_op, Operand::Int(SpvFunctionControlMaskNone),
        Operand::Int(func_type_id)}};
-  std::vector<Instruction> params;
-  push_function(Function{definition_inst, result_op(), std::move(params)});
 
-  scope_stack_.push_scope();
+  std::vector<Instruction> params;
+  for (const auto& param : func->params()) {
+    auto param_op = result_op();
+    auto param_id = param_op.to_i();
+
+    auto param_type_id = GenerateTypeIfNeeded(param->type());
+    if (param_type_id == 0) {
+      return false;
+    }
+
+    push_debug(spv::Op::OpName,
+               {Operand::Int(param_id), Operand::String(param->name())});
+    params.push_back(Instruction{spv::Op::OpFunctionParameter,
+                                 {Operand::Int(param_type_id), param_op}});
+
+    scope_stack_.set(param->name(), param_id);
+  }
+
+  push_function(Function{definition_inst, result_op(), std::move(params)});
 
   for (const auto& stmt : func->body()) {
     if (!GenerateStatement(stmt.get())) {
@@ -428,8 +442,16 @@
     return 0;
   }
 
-  // TODO(dsinclair): Handle parameters
-  push_type(spv::Op::OpTypeFunction, {func_op, Operand::Int(ret_id)});
+  std::vector<Operand> ops = {func_op, Operand::Int(ret_id)};
+  for (const auto& param : func->params()) {
+    auto param_type_id = GenerateTypeIfNeeded(param->type());
+    if (param_type_id == 0) {
+      return 0;
+    }
+    ops.push_back(Operand::Int(param_type_id));
+  }
+
+  push_type(spv::Op::OpTypeFunction, std::move(ops));
 
   type_name_to_id_[func->type_name()] = func_type_id;
   return func_type_id;
diff --git a/src/writer/spirv/builder_function_test.cc b/src/writer/spirv/builder_function_test.cc
index 4ee1863..a0fb76f 100644
--- a/src/writer/spirv/builder_function_test.cc
+++ b/src/writer/spirv/builder_function_test.cc
@@ -18,8 +18,12 @@
 #include "spirv/unified1/spirv.h"
 #include "spirv/unified1/spirv.hpp11"
 #include "src/ast/function.h"
+#include "src/ast/identifier_expression.h"
 #include "src/ast/return_statement.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
 #include "src/ast/type/void_type.h"
+#include "src/ast/variable.h"
 #include "src/writer/spirv/builder.h"
 #include "src/writer/spirv/spv_dump.h"
 
@@ -50,7 +54,41 @@
 )");
 }
 
-TEST_F(BuilderTest, DISABLED_Function_WithParams) {}
+TEST_F(BuilderTest, Function_WithParams) {
+  ast::type::VoidType void_type;
+  ast::type::F32Type f32;
+  ast::type::I32Type i32;
+
+  ast::VariableList params;
+  params.push_back(
+      std::make_unique<ast::Variable>("a", ast::StorageClass::kFunction, &f32));
+  params.push_back(
+      std::make_unique<ast::Variable>("b", ast::StorageClass::kFunction, &i32));
+
+  ast::Function func("a_func", std::move(params), &f32);
+
+  ast::StatementList body;
+  body.push_back(std::make_unique<ast::ReturnStatement>(
+      std::make_unique<ast::IdentifierExpression>("a")));
+  func.set_body(std::move(body));
+
+  ast::Module mod;
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func));
+  EXPECT_EQ(DumpBuilder(b), R"(OpName %4 "a_func"
+OpName %5 "a"
+OpName %6 "b"
+%2 = OpTypeFloat 32
+%3 = OpTypeInt 32 1
+%1 = OpTypeFunction %2 %2 %3
+%4 = OpFunction %2 None %1
+%5 = OpFunctionParameter %2
+%6 = OpFunctionParameter %3
+%7 = OpLabel
+OpReturnValue %5
+OpFunctionEnd
+)");
+}
 
 TEST_F(BuilderTest, Function_WithBody) {
   ast::type::VoidType void_type;