[spirv-reader] Emit stores

Bug: tint:3
Change-Id: Ibda57e58ac13abb650eb0f3e01adbd40b439a82b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/19120
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3426625..3d2e507 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -330,6 +330,7 @@
     reader/spirv/fail_stream_test.cc
     reader/spirv/function_decl_test.cc
     reader/spirv/function_var_test.cc
+    reader/spirv/function_memory_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.cc b/src/reader/spirv/function.cc
index 97626f8..b92c64f 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -20,9 +20,8 @@
 #include "source/opt/function.h"
 #include "source/opt/instruction.h"
 #include "source/opt/module.h"
-#include "src/ast/bool_literal.h"
-#include "src/ast/float_literal.h"
-#include "src/ast/int_literal.h"
+#include "src/ast/assignment_statement.h"
+#include "src/ast/identifier_expression.h"
 #include "src/ast/scalar_constructor_expression.h"
 #include "src/ast/uint_literal.h"
 #include "src/ast/variable.h"
@@ -61,9 +60,7 @@
     return false;
   }
 
-  // Start populating the body.
-
-  if (!EmitFunctionVariables()) {
+  if (!EmitBody()) {
     return false;
   }
 
@@ -134,6 +131,16 @@
   return parser_impl_.ConvertType(var_store_type_id);
 }
 
+bool FunctionEmitter::EmitBody() {
+  if (!EmitFunctionVariables()) {
+    return false;
+  }
+  if (!EmitFunctionBodyStatements()) {
+    return false;
+  }
+  return success();
+}
+
 bool FunctionEmitter::EmitFunctionVariables() {
   if (failed()) {
     return false;
@@ -171,10 +178,66 @@
   if (spirv_constant) {
     return parser_impl_.MakeConstantExpression(id);
   }
+  const auto* inst = def_use_mgr_->GetDef(id);
+  if (inst == nullptr) {
+    Fail() << "ID " << id << " does not have a defining SPIR-V instruction";
+    return nullptr;
+  }
+  switch (inst->opcode()) {
+    case SpvOpVariable:
+      // This is not a declaration, but a use of an identifier.
+      return std::make_unique<ast::IdentifierExpression>(namer_.Name(id));
+    default:
+      break;
+  }
   Fail() << "unhandled expression for ID " << id;
   return nullptr;
 }
 
+bool FunctionEmitter::EmitFunctionBodyStatements() {
+  // TODO(dneto): For now, emit only regular statements in the entry block.
+  // We'll use assignments as markers in the tests, to be able to tell where
+  // code is placed in control flow. First prove that we can emit assignments.
+  return EmitStatementsInBasicBlock(*function_.entry());
+}
+
+bool FunctionEmitter::EmitStatementsInBasicBlock(
+    const spvtools::opt::BasicBlock& bb) {
+  const auto* terminator = bb.terminator();
+  const auto* merge = bb.GetMergeInst();  // Might be nullptr
+  // Emit regular statements.
+  for (auto& inst : bb) {
+    if (&inst == terminator || &inst == merge || inst.opcode() == SpvOpLabel ||
+        inst.opcode() == SpvOpVariable) {
+      continue;
+    }
+    if (!EmitStatement(inst)) {
+      return false;
+    }
+  }
+  // TODO(dneto): Handle the terminator
+  return true;
+}
+
+bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
+  switch (inst.opcode()) {
+    case SpvOpStore: {
+      // TODO(dneto): Order of evaluation?
+      auto lhs = MakeExpression(inst.GetSingleWordInOperand(0));
+      auto rhs = MakeExpression(inst.GetSingleWordInOperand(1));
+      ast_body_.emplace_back(std::make_unique<ast::AssignmentStatement>(
+          std::move(lhs), std::move(rhs)));
+      return success();
+    }
+    case SpvOpFunctionCall:
+      // TODO(dneto): Fill this out.  Make this pass, for existing tests
+      return success();
+    default:
+      break;
+  }
+  return Fail() << "unhandled instruction with opcode " << inst.opcode();
+}
+
 }  // namespace spirv
 }  // namespace reader
 }  // namespace tint
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index 2b7e58d..dd4c85a 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -18,8 +18,10 @@
 #include <memory>
 #include <vector>
 
+#include "source/opt/basic_block.h"
 #include "source/opt/constants.h"
 #include "source/opt/function.h"
+#include "source/opt/instruction.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/type_manager.h"
 #include "src/ast/expression.h"
@@ -65,10 +67,29 @@
   /// @returns true if emission has not yet failed.
   bool EmitFunctionDeclaration();
 
+  /// Emits the function body, populating |ast_body_|
+  /// @returns false if emission failed.
+  bool EmitBody();
+
   /// Emits declarations of function variables.
   /// @returns false if emission failed.
   bool EmitFunctionVariables();
 
+  /// Emits statements in the body.
+  /// @returns false if emission failed.
+  bool EmitFunctionBodyStatements();
+
+  /// Emits a basic block
+  /// @param bb internal representation of the basic block
+  /// @returns false if emission failed.
+  bool EmitStatementsInBasicBlock(const spvtools::opt::BasicBlock& bb);
+
+  /// Emits a normal instruction: not a terminator, label, or variable
+  /// declaration.
+  /// @param inst the instruction
+  /// @returns false if emission failed.
+  bool EmitStatement(const spvtools::opt::Instruction& inst);
+
   /// Makes an expression
   /// @param id the SPIR-V ID of the value
   /// @returns true if emission has not yet failed.
diff --git a/src/reader/spirv/function_memory_test.cc b/src/reader/spirv/function_memory_test.cc
new file mode 100644
index 0000000..6f43419
--- /dev/null
+++ b/src/reader/spirv/function_memory_test.cc
@@ -0,0 +1,154 @@
+// 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;
+
+TEST_F(SpvParserTest, EmitFunctionVariables_StoreBoolConst) {
+  auto p = parser(test::Assemble(R"(
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+     %ty = OpTypeBool
+     %true = OpConstantTrue %ty
+     %false = OpConstantFalse %ty
+     %null = OpConstantNull %ty
+     %ptr_ty = OpTypePointer Function %ty
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpVariable %ptr_ty Function
+     OpStore %1 %true
+     OpStore %1 %false
+     OpStore %1 %null
+     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"(Assignment{
+  Identifier{x_1}
+  ScalarConstructor{true}
+}
+Assignment{
+  Identifier{x_1}
+  ScalarConstructor{false}
+}
+Assignment{
+  Identifier{x_1}
+  ScalarConstructor{false}
+})"));
+}
+
+TEST_F(SpvParserTest, EmitFunctionVariables_StoreUintConst) {
+  auto p = parser(test::Assemble(R"(
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+     %ty = OpTypeInt 32 0
+     %val = OpConstant %ty 42
+     %null = OpConstantNull %ty
+     %ptr_ty = OpTypePointer Function %ty
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpVariable %ptr_ty Function
+     OpStore %1 %val
+     OpStore %1 %null
+     OpReturn
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitBody());
+  EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(Assignment{
+  Identifier{x_1}
+  ScalarConstructor{42}
+}
+Assignment{
+  Identifier{x_1}
+  ScalarConstructor{0}
+})"));
+}
+
+TEST_F(SpvParserTest, EmitFunctionVariables_StoreIntConst) {
+  auto p = parser(test::Assemble(R"(
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+     %ty = OpTypeInt 32 1
+     %val = OpConstant %ty 42
+     %null = OpConstantNull %ty
+     %ptr_ty = OpTypePointer Function %ty
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpVariable %ptr_ty Function
+     OpStore %1 %val
+     OpStore %1 %null
+     OpReturn
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitBody());
+  EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(Assignment{
+  Identifier{x_1}
+  ScalarConstructor{42}
+}
+Assignment{
+  Identifier{x_1}
+  ScalarConstructor{0}
+})"));
+}
+
+TEST_F(SpvParserTest, EmitFunctionVariables_StoreFloatConst) {
+  auto p = parser(test::Assemble(R"(
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+     %ty = OpTypeFloat 32
+     %val = OpConstant %ty 42
+     %null = OpConstantNull %ty
+     %ptr_ty = OpTypePointer Function %ty
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpVariable %ptr_ty Function
+     OpStore %1 %val
+     OpStore %1 %null
+     OpReturn
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitBody());
+  EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(Assignment{
+  Identifier{x_1}
+  ScalarConstructor{42.000000}
+}
+Assignment{
+  Identifier{x_1}
+  ScalarConstructor{0.000000}
+})"));
+}
+
+}  // namespace
+}  // namespace spirv
+}  // namespace reader
+}  // namespace tint