blob: 9950498147c56d877bfc6cd6f555b7f6fcd5f085 [file] [log] [blame]
// 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