| // 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 "source/opt/basic_block.h" |
| #include "source/opt/function.h" |
| #include "source/opt/instruction.h" |
| #include "source/opt/module.h" |
| #include "src/ast/variable.h" |
| #include "src/ast/variable_decl_statement.h" |
| #include "src/reader/spirv/fail_stream.h" |
| #include "src/reader/spirv/parser_impl.h" |
| |
| namespace tint { |
| namespace reader { |
| namespace spirv { |
| |
| FunctionEmitter::FunctionEmitter(ParserImpl* pi, |
| const spvtools::opt::Function& function) |
| : parser_impl_(*pi), |
| ast_module_(pi->get_module()), |
| ir_context_(*(pi->ir_context())), |
| type_mgr_(ir_context_.get_type_mgr()), |
| fail_stream_(pi->fail_stream()), |
| namer_(pi->namer()), |
| function_(function) {} |
| |
| FunctionEmitter::~FunctionEmitter() = default; |
| |
| bool FunctionEmitter::Emit() { |
| if (failed()) { |
| return false; |
| } |
| // We only care about functions with bodies. |
| if (function_.cbegin() == function_.cend()) { |
| return true; |
| } |
| |
| if (!EmitFunctionDeclaration()) { |
| return false; |
| } |
| |
| // Start populating the body. |
| |
| if (!EmitFunctionVariables()) { |
| return false; |
| } |
| |
| // Set the body of the AST function node. |
| parser_impl_.get_module().functions().back()->set_body(std::move(ast_body_)); |
| |
| return success(); |
| } |
| |
| bool FunctionEmitter::EmitFunctionDeclaration() { |
| if (failed()) { |
| return false; |
| } |
| |
| const auto name = namer_.Name(function_.result_id()); |
| // Surprisingly, the "type id" on an OpFunction is the result type of the |
| // function, not the type of the function. This is the one exceptional case |
| // in SPIR-V where the type ID is not the type of the result ID. |
| auto* ret_ty = parser_impl_.ConvertType(function_.type_id()); |
| if (failed()) { |
| return false; |
| } |
| if (ret_ty == nullptr) { |
| return Fail() |
| << "internal error: unregistered return type for function with ID " |
| << function_.result_id(); |
| } |
| |
| std::vector<std::unique_ptr<ast::Variable>> ast_params; |
| function_.ForEachParam( |
| [this, &ast_params](const spvtools::opt::Instruction* param) { |
| auto* ast_type = parser_impl_.ConvertType(param->type_id()); |
| if (ast_type != nullptr) { |
| ast_params.emplace_back(parser_impl_.MakeVariable( |
| param->result_id(), ast::StorageClass::kNone, ast_type)); |
| } else { |
| // We've already logged an error and emitted a diagnostic. Do nothing |
| // here. |
| } |
| }); |
| if (failed()) { |
| return false; |
| } |
| |
| auto ast_fn = |
| std::make_unique<ast::Function>(name, std::move(ast_params), ret_ty); |
| ast_module_.AddFunction(std::move(ast_fn)); |
| |
| return success(); |
| } |
| |
| ast::type::Type* FunctionEmitter::GetVariableStoreType( |
| const spvtools::opt::Instruction& var_decl_inst) { |
| const auto type_id = var_decl_inst.type_id(); |
| auto* var_ref_type = type_mgr_->GetType(type_id); |
| if (!var_ref_type) { |
| Fail() << "internal error: variable type id " << type_id |
| << " has no registered type"; |
| return nullptr; |
| } |
| auto* var_ref_ptr_type = var_ref_type->AsPointer(); |
| if (!var_ref_ptr_type) { |
| Fail() << "internal error: variable type id " << type_id |
| << " is not a pointer type"; |
| return nullptr; |
| } |
| auto var_store_type_id = type_mgr_->GetId(var_ref_ptr_type->pointee_type()); |
| return parser_impl_.ConvertType(var_store_type_id); |
| } |
| |
| bool FunctionEmitter::EmitFunctionVariables() { |
| if (failed()) { |
| return false; |
| } |
| for (auto& inst : *function_.entry()) { |
| if (inst.opcode() != SpvOpVariable) { |
| continue; |
| } |
| auto* var_store_type = GetVariableStoreType(inst); |
| if (failed()) { |
| return false; |
| } |
| // Use StorageClass::kNone because function variables should not explicitly mention |
| // their storage class. |
| auto var = |
| parser_impl_.MakeVariable(inst.result_id(), |
| ast::StorageClass::kNone, var_store_type); |
| // TODO(dneto): Add the initializer via Variable::set_constructor. |
| auto var_decl_stmt = std::make_unique<ast::VariableDeclStatement>(std::move(var)); |
| ast_body_.emplace_back(std::move(var_decl_stmt)); |
| } |
| return success(); |
| } |
| |
| } // namespace spirv |
| } // namespace reader |
| } // namespace tint |