[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>
This commit is contained in:
David Neto 2020-04-08 20:06:26 +00:00
parent 7456f4258a
commit a1a86007c9
4 changed files with 245 additions and 6 deletions

View File

@ -330,6 +330,7 @@ if(${TINT_BUILD_SPV_READER})
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

View File

@ -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 @@ bool FunctionEmitter::Emit() {
return false;
}
// Start populating the body.
if (!EmitFunctionVariables()) {
if (!EmitBody()) {
return false;
}
@ -134,6 +131,16 @@ ast::type::Type* FunctionEmitter::GetVariableStoreType(
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 @@ std::unique_ptr<ast::Expression> FunctionEmitter::MakeExpression(uint32_t id) {
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

View File

@ -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 @@ class FunctionEmitter {
/// @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.

View File

@ -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