[spirv-reader] Support function calls, except returning void

Functions returning void are blocked on https://crbug.com/tint/45

Change-Id: I15ec9cf0e267571bbfab921c678a59e25d0e3619
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/25280
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 67d7557..1c0b35a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -729,6 +729,7 @@
     "src/reader/spirv/fail_stream_test.cc",
     "src/reader/spirv/function_arithmetic_test.cc",
     "src/reader/spirv/function_bit_test.cc",
+    "src/reader/spirv/function_call_test.cc",
     "src/reader/spirv/function_cfg_test.cc",
     "src/reader/spirv/function_composite_test.cc",
     "src/reader/spirv/function_conversion_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 23f3988..ed8c279 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -344,6 +344,7 @@
     reader/spirv/function_arithmetic_test.cc
     reader/spirv/function_bit_test.cc
     reader/spirv/function_cfg_test.cc
+    reader/spirv/function_call_test.cc
     reader/spirv/function_composite_test.cc
     reader/spirv/function_conversion_test.cc
     reader/spirv/function_decl_test.cc
@@ -361,8 +362,8 @@
     reader/spirv/parser_impl_import_test.cc
     reader/spirv/parser_impl_module_var_test.cc
     reader/spirv/parser_impl_named_types_test.cc
-    reader/spirv/parser_impl_user_name_test.cc
     reader/spirv/parser_impl_test.cc
+    reader/spirv/parser_impl_user_name_test.cc
     reader/spirv/parser_test.cc
     reader/spirv/spirv_tools_helpers_test.cc
     reader/spirv/spirv_tools_helpers_test.h
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 38e4e60..3137cc4 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -569,6 +569,8 @@
         if (ast_type != nullptr) {
           ast_params.emplace_back(parser_impl_.MakeVariable(
               param->result_id(), ast::StorageClass::kNone, ast_type));
+          // The value is accessible by name.
+          identifier_values_.insert(param->result_id());
         } else {
           // We've already logged an error and emitted a diagnostic. Do nothing
           // here.
@@ -2599,8 +2601,7 @@
       return EmitConstDefOrWriteToHoistedVar(inst, std::move(expr));
     }
     case SpvOpFunctionCall:
-      // TODO(dneto): Fill this out.  Make this pass, for existing tests
-      return success();
+      return EmitFunctionCall(inst);
     default:
       break;
   }
@@ -3350,6 +3351,30 @@
                               requested_type, std::move(result.expr))};
 }
 
+bool FunctionEmitter::EmitFunctionCall(const spvtools::opt::Instruction& inst) {
+  // We ignore function attributes such as Inline, DontInline, Pure, Const.
+  auto function = std::make_unique<ast::IdentifierExpression>(
+      namer_.Name(inst.GetSingleWordInOperand(0)));
+
+  ast::ExpressionList params;
+  for (uint32_t iarg = 1; iarg < inst.NumInOperands(); ++iarg) {
+    params.emplace_back(MakeOperand(inst, iarg).expr);
+  }
+  TypedExpression expr{parser_impl_.ConvertType(inst.type_id()),
+                       std::make_unique<ast::CallExpression>(
+                           std::move(function), std::move(params))};
+
+  if (expr.type->IsVoid()) {
+    // TODO(dneto): Tint AST needs support for function call as a statement
+    // https://bugs.chromium.org/p/tint/issues/detail?id=45
+    return Fail() << "missing support for function call as a statement: can't "
+                     "generate code for function call returning void: "
+                  << inst.PrettyPrint();
+  }
+
+  return EmitConstDefOrWriteToHoistedVar(inst, std::move(expr));
+}
+
 }  // namespace spirv
 }  // namespace reader
 }  // namespace tint
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index e3bde3a..7c9def4 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -669,6 +669,11 @@
   /// @returns an expression
   TypedExpression MakeAccessChain(const spvtools::opt::Instruction& inst);
 
+  /// Emits a function call.  On failure, emits a diagnostic and returns false.
+  /// @param inst the SPIR-V function call instruction
+  /// @returns false if emission failed
+  bool EmitFunctionCall(const spvtools::opt::Instruction& inst);
+
   /// Finds the header block for a structured construct that we can "break"
   /// out from, from deeply nested control flow, if such a block exists.
   /// If the construct is:
diff --git a/src/reader/spirv/function_call_test.cc b/src/reader/spirv/function_call_test.cc
new file mode 100644
index 0000000..1f0b0f4
--- /dev/null
+++ b/src/reader/spirv/function_call_test.cc
@@ -0,0 +1,258 @@
+// 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::Eq;
+using ::testing::HasSubstr;
+
+TEST_F(SpvParserTest, EmitStatement_VoidCallNoParams) {
+  auto p = parser(test::Assemble(R"(
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+
+     %50 = OpFunction %void None %voidfn
+     %entry_50 = OpLabel
+     OpReturn
+     OpFunctionEnd
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpFunctionCall %void %50
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_FALSE(fe.EmitBody());
+  EXPECT_THAT(
+      p->error(),
+      Eq("missing support for function call as a statement: can't generate "
+         "code for function call returning void: %1 = OpFunctionCall %2 %50"));
+}
+
+TEST_F(SpvParserTest, EmitStatement_ScalarCallNoParams) {
+  auto p = parser(test::Assemble(R"(
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+     %uint = OpTypeInt 32 0
+     %uintfn = OpTypeFunction %uint
+     %val = OpConstant %uint 42
+
+     %50 = OpFunction %uint None %uintfn
+     %entry_50 = OpLabel
+     OpReturnValue %val
+     OpFunctionEnd
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpFunctionCall %uint %50
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  {
+    FunctionEmitter fe(p, *spirv_function(100));
+    EXPECT_TRUE(fe.EmitBody());
+    EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{
+  Variable{
+    x_1
+    none
+    __u32
+    {
+      Call{
+        Identifier{x_50}
+        (
+        )
+      }
+    }
+  }
+}
+Return{})"))
+        << ToString(fe.ast_body());
+  }
+
+  {
+    FunctionEmitter fe(p, *spirv_function(50));
+    EXPECT_TRUE(fe.EmitBody());
+    EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(Return{
+  {
+    ScalarConstructor{42}
+  }
+})")) << ToString(fe.ast_body());
+  }
+}
+
+TEST_F(SpvParserTest, EmitStatement_ScalarCallNoParamsUsedTwice) {
+  auto p = parser(test::Assemble(R"(
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+     %uint = OpTypeInt 32 0
+     %uintfn = OpTypeFunction %uint
+     %val = OpConstant %uint 42
+     %ptr_uint = OpTypePointer Function %uint
+
+     %50 = OpFunction %uint None %uintfn
+     %entry_50 = OpLabel
+     OpReturnValue %val
+     OpFunctionEnd
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %10 = OpVariable %ptr_uint Function
+     %1 = OpFunctionCall %uint %50
+     OpStore %10 %1
+     OpStore %10 %1
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  {
+    FunctionEmitter fe(p, *spirv_function(100));
+    EXPECT_TRUE(fe.EmitBody()) << p->error();
+    EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{
+  Variable{
+    x_10
+    function
+    __u32
+  }
+}
+VariableDeclStatement{
+  Variable{
+    x_1
+    none
+    __u32
+    {
+      Call{
+        Identifier{x_50}
+        (
+        )
+      }
+    }
+  }
+}
+Assignment{
+  Identifier{x_10}
+  Identifier{x_1}
+}
+Assignment{
+  Identifier{x_10}
+  Identifier{x_1}
+}
+Return{})"))
+        << ToString(fe.ast_body());
+  }
+  {
+    FunctionEmitter fe(p, *spirv_function(50));
+    EXPECT_TRUE(fe.EmitBody()) << p->error();
+    EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(Return{
+  {
+    ScalarConstructor{42}
+  }
+})")) << ToString(fe.ast_body());
+  }
+}
+
+TEST_F(SpvParserTest, EmitStatement_CallWithParams) {
+  auto p = parser(test::Assemble(R"(
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+     %uint = OpTypeInt 32 0
+     %uintfn_uint_uint = OpTypeFunction %uint %uint %uint
+     %val = OpConstant %uint 42
+     %val2 = OpConstant %uint 84
+
+     %50 = OpFunction %uint None %uintfn_uint_uint
+     %51 = OpFunctionParameter %uint
+     %52 = OpFunctionParameter %uint
+     %entry_50 = OpLabel
+     %sum = OpIAdd %uint %51 %52
+     OpReturnValue %sum
+     OpFunctionEnd
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpFunctionCall %uint %50 %val %val2
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error();
+  EXPECT_TRUE(p->error().empty());
+  const auto module_ast_str = p->module().to_str();
+  EXPECT_THAT(module_ast_str, HasSubstr(R"(Module{
+  Function x_50 -> __u32
+  (
+    Variable{
+      x_51
+      none
+      __u32
+    }
+    Variable{
+      x_52
+      none
+      __u32
+    }
+  )
+  {
+    Return{
+      {
+        Binary{
+          Identifier{x_51}
+          add
+          Identifier{x_52}
+        }
+      }
+    }
+  }
+  Function x_100 -> __void
+  ()
+  {
+    VariableDeclStatement{
+      Variable{
+        x_1
+        none
+        __u32
+        {
+          Call{
+            Identifier{x_50}
+            (
+              ScalarConstructor{42}
+              ScalarConstructor{84}
+            )
+          }
+        }
+      }
+    }
+    Return{}
+  }
+})")) << module_ast_str;
+}
+
+}  // namespace
+}  // namespace spirv
+}  // namespace reader
+}  // namespace tint
diff --git a/src/reader/spirv/parser_impl_function_decl_test.cc b/src/reader/spirv/parser_impl_function_decl_test.cc
index 03ef876..7fc86d4 100644
--- a/src/reader/spirv/parser_impl_function_decl_test.cc
+++ b/src/reader/spirv/parser_impl_function_decl_test.cc
@@ -85,47 +85,85 @@
 }
 
 TEST_F(SpvParserTest, EmitFunctions_CalleePrecedesCaller) {
-  auto* p = parser(
-      test::Assemble(Names({"root", "branch", "leaf"}) + CommonTypes() + R"(
+  auto* p = parser(test::Assemble(
+      Names({"root", "branch", "leaf", "leaf_result", "branch_result"}) +
+      CommonTypes() + R"(
+     %uintfn = OpTypeFunction %uint
+     %uint_0 = OpConstant %uint 0
+
      %root = OpFunction %void None %voidfn
      %root_entry = OpLabel
-     %branch_result = OpFunctionCall %void %branch
+     %branch_result = OpFunctionCall %uint %branch
      OpReturn
      OpFunctionEnd
 
-     %branch = OpFunction %void None %voidfn
+     %branch = OpFunction %uint None %uintfn
      %branch_entry = OpLabel
-     %leaf_result = OpFunctionCall %void %leaf
-     OpReturn
+     %leaf_result = OpFunctionCall %uint %leaf
+     OpReturnValue %leaf_result
      OpFunctionEnd
 
-     %leaf = OpFunction %void None %voidfn
+     %leaf = OpFunction %uint None %uintfn
      %leaf_entry = OpLabel
-     OpReturn
+     OpReturnValue %uint_0
      OpFunctionEnd
   )"));
   EXPECT_TRUE(p->BuildAndParseInternalModule());
   EXPECT_TRUE(p->error().empty());
   const auto module_ast = p->module().to_str();
-  // TODO(dneto): This will need to be updated when function calls are
-  // supported. Otherwise, use more general matching instead of substring
-  // equality.
   EXPECT_THAT(module_ast, HasSubstr(R"(
-  Function leaf -> __void
+  Function leaf -> __u32
   ()
   {
-    Return{}
+    Return{
+      {
+        ScalarConstructor{0}
+      }
+    }
   }
-  Function branch -> __void
+  Function branch -> __u32
   ()
   {
-    Return{}
+    VariableDeclStatement{
+      Variable{
+        leaf_result
+        none
+        __u32
+        {
+          Call{
+            Identifier{leaf}
+            (
+            )
+          }
+        }
+      }
+    }
+    Return{
+      {
+        Identifier{leaf_result}
+      }
+    }
   }
   Function root -> __void
   ()
   {
+    VariableDeclStatement{
+      Variable{
+        branch_result
+        none
+        __u32
+        {
+          Call{
+            Identifier{branch}
+            (
+            )
+          }
+        }
+      }
+    }
     Return{}
-  })"));
+  }
+})")) << module_ast;
 }
 
 TEST_F(SpvParserTest, EmitFunctions_NonVoidResultType) {
@@ -229,7 +267,8 @@
   )
   {
     Return{}
-  })"));
+  })"))
+      << module_ast;
 }
 
 }  // namespace