[spirv-reader] Test EmitFunctionDeclaration

Tests FunctionEmitter directly.
This is mostly refactoring to be able to selectively run parts of the
parsing flow, and to access relevant internal data.

Bug: tint:3
Change-Id: Ic2b166a2e9623a7e30e6769806088d12e78dcf45
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/18704
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f8c57cd..456661e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -328,6 +328,7 @@
   list(APPEND TINT_TEST_SRCS
     reader/spirv/enum_converter_test.cc
     reader/spirv/fail_stream_test.cc
+    reader/spirv/function_decl_test.cc
     reader/spirv/namer_test.cc
     reader/spirv/parser_impl_convert_member_decoration_test.cc
     reader/spirv/parser_impl_convert_type_test.cc
diff --git a/src/reader/spirv/function_decl_test.cc b/src/reader/spirv/function_decl_test.cc
new file mode 100644
index 0000000..2e2c35c
--- /dev/null
+++ b/src/reader/spirv/function_decl_test.cc
@@ -0,0 +1,170 @@
+// 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/reader/spirv/function.h"
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "src/reader/spirv/parser_impl.h"
+#include "src/reader/spirv/parser_impl_test_helper.h"
+#include "src/reader/spirv/spirv_tools_helpers_test.h"
+
+namespace tint {
+namespace reader {
+namespace spirv {
+namespace {
+
+using ::testing::HasSubstr;
+
+/// @returns a SPIR-V assembly segment which assigns debug names
+/// to particular IDs.
+std::string Names(std::vector<std::string> ids) {
+  std::ostringstream outs;
+  for (auto& id : ids) {
+    outs << "    OpName %" << id << " \"" << id << "\"\n";
+  }
+  return outs.str();
+}
+
+std::string CommonTypes() {
+  return R"(
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+    %float = OpTypeFloat 32
+    %uint = OpTypeInt 32 0
+    %int = OpTypeInt 32 1
+    %float_0 = OpConstant %float 0.0
+  )";
+}
+
+TEST_F(SpvParserTest, EmitFunctionDeclaration_VoidFunctionWithoutParams) {
+  auto p = parser(test::Assemble(CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitFunctionDeclaration());
+  EXPECT_THAT(p->module().to_str(), HasSubstr(R"(
+  Function x_100 -> __void
+  ()
+  {
+  })"));
+}
+
+TEST_F(SpvParserTest, EmitFunctionDeclaration_NonVoidResultType) {
+  auto p = parser(test::Assemble(CommonTypes() + R"(
+     %fn_ret_float = OpTypeFunction %float
+     %100 = OpFunction %float None %fn_ret_float
+     %entry = OpLabel
+     OpReturnValue %float_0
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitFunctionDeclaration());
+
+  EXPECT_THAT(p->module().to_str(), HasSubstr(R"(
+  Function x_100 -> __f32
+  ()
+  {
+  })"));
+}
+
+TEST_F(SpvParserTest, EmitFunctionDeclaration_MixedParamTypes) {
+  auto p = parser(test::Assemble(Names({"a", "b", "c"}) + CommonTypes() + R"(
+     %fn_mixed_params = OpTypeFunction %float %uint %float %int
+
+     %100 = OpFunction %void None %fn_mixed_params
+     %a = OpFunctionParameter %uint
+     %b = OpFunctionParameter %float
+     %c = OpFunctionParameter %int
+     %mixed_entry = OpLabel
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitFunctionDeclaration());
+
+  EXPECT_THAT(p->module().to_str(), HasSubstr(R"(
+  Function x_100 -> __void
+  (
+    Variable{
+      a
+      none
+      __u32
+    }
+    Variable{
+      b
+      none
+      __f32
+    }
+    Variable{
+      c
+      none
+      __i32
+    }
+  )
+  {
+  })"));
+}
+
+TEST_F(SpvParserTest, EmitFunctionDeclaration_GenerateParamNames) {
+  auto p = parser(test::Assemble(CommonTypes() + R"(
+     %fn_mixed_params = OpTypeFunction %float %uint %float %int
+
+     %100 = OpFunction %void None %fn_mixed_params
+     %14 = OpFunctionParameter %uint
+     %15 = OpFunctionParameter %float
+     %16 = OpFunctionParameter %int
+     %mixed_entry = OpLabel
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitFunctionDeclaration());
+
+  EXPECT_THAT(p->module().to_str(), HasSubstr(R"(
+  Function x_100 -> __void
+  (
+    Variable{
+      x_14
+      none
+      __u32
+    }
+    Variable{
+      x_15
+      none
+      __f32
+    }
+    Variable{
+      x_16
+      none
+      __i32
+    }
+  )
+  {
+  })"));
+}
+
+}  // namespace
+}  // namespace spirv
+}  // namespace reader
+}  // namespace tint
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index ca89a05..fc00865 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -336,6 +336,19 @@
   if (!success_) {
     return false;
   }
+  if (!ParseInternalModuleExceptFunctions()) {
+    return false;
+  }
+  if (!EmitFunctions()) {
+    return false;
+  }
+  return success_;
+}
+
+bool ParserImpl::ParseInternalModuleExceptFunctions() {
+  if (!success_) {
+    return false;
+  }
   if (!RegisterExtendedInstructionImports()) {
     return false;
   }
@@ -354,9 +367,6 @@
   if (!EmitModuleScopeVariables()) {
     return false;
   }
-  if (!EmitFunctions()) {
-    return false;
-  }
   return success_;
 }
 
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index 46d9c7f..dc42e75 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -90,6 +90,13 @@
   bool BuildAndParseInternalModule() {
     return BuildInternalModule() && ParseInternalModule();
   }
+  /// Builds an internal representation of the SPIR-V binary,
+  /// and parses the module, except functions, into a Tint AST module.
+  /// Diagnostics are emitted to the error stream.
+  /// @returns true if it was successful.
+  bool BuildAndParseInternalModuleExceptFunctions() {
+    return BuildInternalModule() && ParseInternalModuleExceptFunctions();
+  }
 
   /// @returns the set of SPIR-V IDs for imports of the "GLSL.std.450"
   /// extended instruction set.
@@ -149,6 +156,12 @@
   /// @returns true if the parser is still successful.
   bool ParseInternalModule();
 
+  /// Walks the internal representation of the module, except for function
+  /// definitions, to populate the AST form of the module.
+  /// This is a no-op if the parser has already failed.
+  /// @returns true if the parser is still successful.
+  bool ParseInternalModuleExceptFunctions();
+
   /// Destroys the internal representation of the SPIR-V module.
   void ResetInternalModule();
 
diff --git a/src/reader/spirv/parser_impl_test_helper.h b/src/reader/spirv/parser_impl_test_helper.h
index 66a48df..4f52e09 100644
--- a/src/reader/spirv/parser_impl_test_helper.h
+++ b/src/reader/spirv/parser_impl_test_helper.h
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "gtest/gtest.h"
+#include "source/opt/ir_context.h"
 #include "src/context.h"
 #include "src/reader/spirv/parser_impl.h"
 
@@ -47,6 +48,14 @@
     return impl_.get();
   }
 
+  /// Gets the internal representation of the function with the given ID.
+  /// Assumes ParserImpl::BuildInternalRepresentation has been run and
+  /// succeeded.
+  /// @returns the internal representation of the function
+  spvtools::opt::Function* spirv_function(uint32_t id) {
+    return impl_->ir_context()->GetFunction(id);
+  }
+
  private:
   std::unique_ptr<ParserImpl> impl_;
   Context ctx_;