[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