diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ad3e2c558..6f09163c86 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -420,6 +420,7 @@ if(${TINT_BUILD_SPV_WRITER}) writer/spirv/builder_function_test.cc writer/spirv/builder_function_variable_test.cc writer/spirv/builder_global_variable_test.cc + writer/spirv/builder_ident_expression_test.cc writer/spirv/builder_literal_test.cc writer/spirv/builder_return_test.cc writer/spirv/builder_test.cc diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc index 5a794f76ff..be9c570dca 100644 --- a/src/writer/spirv/builder.cc +++ b/src/writer/spirv/builder.cc @@ -19,6 +19,7 @@ #include "spirv/unified1/spirv.h" #include "src/ast/binding_decoration.h" #include "src/ast/bool_literal.h" +#include "src/ast/identifier_expression.h" #include "src/ast/builtin_decoration.h" #include "src/ast/constructor_expression.h" #include "src/ast/decorated_variable.h" @@ -76,7 +77,10 @@ uint32_t pipeline_stage_to_execution_model(ast::PipelineStage stage) { } // namespace -Builder::Builder() = default; +Builder::Builder() { + // Push the global variable map onto the stack + variable_stack_.push_back({}); +} Builder::~Builder() = default; @@ -176,6 +180,9 @@ bool Builder::GenerateEntryPoint(ast::EntryPoint* ep) { } uint32_t Builder::GenerateExpression(ast::Expression* expr) { + if (expr->IsIdentifier()) { + return GenerateIdentifierExpression(expr->AsIdentifier()); + } if (expr->IsConstructor()) { return GenerateConstructorExpression(expr->AsConstructor(), false); } @@ -210,12 +217,16 @@ bool Builder::GenerateFunction(ast::Function* func) { std::vector params; push_function(Function{definition_inst, result_op(), std::move(params)}); + variable_stack_.push_back({}); + for (const auto& stmt : func->body()) { if (!GenerateStatement(stmt.get())) { return false; } } + variable_stack_.pop_back(); + func_name_to_id_[func->name()] = func_id; return true; } @@ -255,8 +266,7 @@ bool Builder::GenerateFunctionVariable(ast::Variable* var) { error_ = "missing constructor for constant"; return false; } - - // TODO(dsinclair): Store variable name to id + variable_stack_.back()[var->name()] = init_id; return true; } @@ -282,7 +292,7 @@ bool Builder::GenerateFunctionVariable(ast::Variable* var) { {Operand::Int(var_id), Operand::Int(init_id)}); } - // TODO(dsinclair) Mapping of variable name to id + variable_stack_.back()[var->name()] = var_id; return true; } @@ -307,8 +317,7 @@ bool Builder::GenerateGlobalVariable(ast::Variable* var) { error_ = "missing constructor for constant"; return false; } - - // TODO(dsinclair): Store variable name to id + variable_stack_[0][var->name()] = init_id; return true; } @@ -361,12 +370,26 @@ bool Builder::GenerateGlobalVariable(ast::Variable* var) { } } } - - // TODO(dsinclair) Mapping of variable name to id - + variable_stack_[0][var->name()] = var_id; return true; } +uint32_t Builder::GenerateIdentifierExpression(ast::IdentifierExpression* expr) { + for (auto iter = variable_stack_.rbegin(); iter != variable_stack_.rend(); ++iter) { + auto& map = *iter; + + // TODO(dsinclair): handle names with namespaces in them ... + + auto val = map.find(expr->name()[0]); + if (val != map.end()) { + return val->second; + } + } + + error_ = "unable to find name for identifier: " + expr->name()[0]; + return 0; +} + void Builder::GenerateImport(ast::Import* imp) { auto result = result_op(); auto id = result.to_i(); diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h index e6f0d179b2..479da1b33c 100644 --- a/src/writer/spirv/builder.h +++ b/src/writer/spirv/builder.h @@ -167,6 +167,10 @@ class Builder { /// @param var the variable to generate /// @returns true if the variable is emited. bool GenerateGlobalVariable(ast::Variable* var); + /// Generates an identifier expression + /// @param expr the expresssion to generate + /// @returns the id of the expression or 0 on failure + uint32_t GenerateIdentifierExpression(ast::IdentifierExpression* expr); /// Generates an import instruction /// @param imp the import void GenerateImport(ast::Import* imp); @@ -248,6 +252,7 @@ class Builder { std::unordered_map func_name_to_id_; std::unordered_map type_name_to_id_; std::unordered_map const_to_id_; + std::vector> variable_stack_; }; } // namespace spirv diff --git a/src/writer/spirv/builder_ident_expression_test.cc b/src/writer/spirv/builder_ident_expression_test.cc new file mode 100644 index 0000000000..ebf0159597 --- /dev/null +++ b/src/writer/spirv/builder_ident_expression_test.cc @@ -0,0 +1,147 @@ +// 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 + +#include "gtest/gtest.h" +#include "src/ast/float_literal.h" +#include "src/ast/scalar_constructor_expression.h" +#include "src/ast/type/f32_type.h" +#include "src/ast/type_constructor_expression.h" +#include "src/ast/type/vector_type.h" +#include "src/writer/spirv/builder.h" +#include "src/writer/spirv/spv_dump.h" +#include "src/ast/identifier_expression.h" +#include "src/ast/variable.h" + +namespace tint { +namespace writer { +namespace spirv { +namespace { + +using BuilderTest = testing::Test; + +TEST_F(BuilderTest, IdentifierExpression_GlobalConst) { + ast::type::F32Type f32; + ast::type::VectorType vec(&f32, 3); + + std::vector> vals; + vals.push_back(std::make_unique( + std::make_unique(&f32, 1.0f))); + vals.push_back(std::make_unique( + std::make_unique(&f32, 1.0f))); + vals.push_back(std::make_unique( + std::make_unique(&f32, 3.0f))); + + auto init = + std::make_unique(&vec, std::move(vals)); + + ast::Variable v("var", ast::StorageClass::kOutput, &f32); + v.set_constructor(std::move(init)); + v.set_is_const(true); + + Builder b; + EXPECT_TRUE(b.GenerateGlobalVariable(&v)) << b.error(); + ASSERT_FALSE(b.has_error()) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32 +%1 = OpTypeVector %2 3 +%3 = OpConstant %2 1 +%4 = OpConstant %2 3 +%5 = OpConstantComposite %1 %3 %3 %4 +)"); + + ast::IdentifierExpression expr("var"); + EXPECT_EQ(b.GenerateIdentifierExpression(&expr), 5); +} + +TEST_F(BuilderTest, IdentifierExpression_GlobalVar) { + ast::type::F32Type f32; + ast::Variable v("var", ast::StorageClass::kOutput, &f32); + + Builder b; + EXPECT_TRUE(b.GenerateGlobalVariable(&v)) << b.error(); + EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var" +)"); + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 +%2 = OpTypePointer Output %3 +%1 = OpVariable %2 Output +)"); + + ast::IdentifierExpression expr("var"); + EXPECT_EQ(b.GenerateIdentifierExpression(&expr), 1); +} + +TEST_F(BuilderTest, IdentifierExpression_FunctionConst) { + ast::type::F32Type f32; + ast::type::VectorType vec(&f32, 3); + + std::vector> vals; + vals.push_back(std::make_unique( + std::make_unique(&f32, 1.0f))); + vals.push_back(std::make_unique( + std::make_unique(&f32, 1.0f))); + vals.push_back(std::make_unique( + std::make_unique(&f32, 3.0f))); + + auto init = + std::make_unique(&vec, std::move(vals)); + + ast::Variable v("var", ast::StorageClass::kOutput, &f32); + v.set_constructor(std::move(init)); + v.set_is_const(true); + + Builder b; + EXPECT_TRUE(b.GenerateFunctionVariable(&v)) << b.error(); + ASSERT_FALSE(b.has_error()) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32 +%1 = OpTypeVector %2 3 +%3 = OpConstant %2 1 +%4 = OpConstant %2 3 +%5 = OpConstantComposite %1 %3 %3 %4 +)"); + + ast::IdentifierExpression expr("var"); + EXPECT_EQ(b.GenerateIdentifierExpression(&expr), 5); +} + +TEST_F(BuilderTest, IdentifierExpression_FunctionVar) { + ast::type::F32Type f32; + ast::Variable v("var", ast::StorageClass::kNone, &f32); + + Builder b; + b.push_function(Function{}); + EXPECT_TRUE(b.GenerateFunctionVariable(&v)) << b.error(); + EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var" +)"); + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 +%2 = OpTypePointer Function %3 +)"); + + const auto& func = b.functions()[0]; + EXPECT_EQ(DumpInstructions(func.variables()), R"(%1 = OpVariable %2 Function +)"); + + ast::IdentifierExpression expr("var"); + EXPECT_EQ(b.GenerateIdentifierExpression(&expr), 1); +} + +TEST_F(BuilderTest, DISABLED_IdentifierExpression_MultiName) {} + +} // namespace +} // namespace spirv +} // namespace writer +} // namespace tint +