mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-15 17:45:58 +00:00
This CL updates the SPIR-V writer to emit the OpTypeFunction and OpFunction instructions. Bug: tint:5 Change-Id: I85ead161ca37304a977213257a825ff268d29f2d Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17741 Reviewed-by: David Neto <dneto@google.com>
340 lines
9.1 KiB
C++
340 lines
9.1 KiB
C++
// 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/writer/spirv/builder.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "spirv/unified1/spirv.h"
|
|
#include "src/ast/struct.h"
|
|
#include "src/ast/struct_member.h"
|
|
#include "src/ast/struct_member_offset_decoration.h"
|
|
#include "src/ast/type/matrix_type.h"
|
|
#include "src/ast/type/struct_type.h"
|
|
#include "src/ast/type/vector_type.h"
|
|
|
|
namespace tint {
|
|
namespace writer {
|
|
namespace spirv {
|
|
namespace {
|
|
|
|
uint32_t size_of(const std::vector<Instruction>& instructions) {
|
|
uint32_t size = 0;
|
|
for (const auto& inst : instructions)
|
|
size += inst.word_length();
|
|
|
|
return size;
|
|
}
|
|
|
|
uint32_t pipeline_stage_to_execution_model(ast::PipelineStage stage) {
|
|
SpvExecutionModel model = SpvExecutionModelVertex;
|
|
|
|
switch (stage) {
|
|
case ast::PipelineStage::kFragment:
|
|
model = SpvExecutionModelFragment;
|
|
break;
|
|
case ast::PipelineStage::kVertex:
|
|
model = SpvExecutionModelVertex;
|
|
break;
|
|
case ast::PipelineStage::kCompute:
|
|
model = SpvExecutionModelGLCompute;
|
|
break;
|
|
case ast::PipelineStage::kNone:
|
|
model = SpvExecutionModelMax;
|
|
break;
|
|
}
|
|
return model;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Builder::Builder() = default;
|
|
|
|
Builder::~Builder() = default;
|
|
|
|
bool Builder::Build(const ast::Module& m) {
|
|
push_preamble(spv::Op::OpCapability, {Operand::Int(SpvCapabilityShader)});
|
|
push_preamble(spv::Op::OpCapability,
|
|
{Operand::Int(SpvCapabilityVulkanMemoryModel)});
|
|
|
|
for (const auto& imp : m.imports()) {
|
|
GenerateImport(imp.get());
|
|
}
|
|
|
|
push_preamble(spv::Op::OpMemoryModel,
|
|
{Operand::Int(SpvAddressingModelLogical),
|
|
Operand::Int(SpvMemoryModelVulkanKHR)});
|
|
|
|
for (const auto& func : m.functions()) {
|
|
if (!GenerateFunction(func.get())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (const auto& ep : m.entry_points()) {
|
|
if (!GenerateEntryPoint(ep.get())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Operand Builder::result_op() {
|
|
return Operand::Int(next_id());
|
|
}
|
|
|
|
uint32_t Builder::total_size() const {
|
|
// The 5 covers the magic, version, generator, id bound and reserved.
|
|
uint32_t size = 5;
|
|
|
|
size += size_of(preamble_);
|
|
size += size_of(debug_);
|
|
size += size_of(annotations_);
|
|
size += size_of(types_);
|
|
size += size_of(instructions_);
|
|
|
|
return size;
|
|
}
|
|
|
|
void Builder::iterate(std::function<void(const Instruction&)> cb) const {
|
|
for (const auto& inst : preamble_) {
|
|
cb(inst);
|
|
}
|
|
for (const auto& inst : debug_) {
|
|
cb(inst);
|
|
}
|
|
for (const auto& inst : annotations_) {
|
|
cb(inst);
|
|
}
|
|
for (const auto& inst : types_) {
|
|
cb(inst);
|
|
}
|
|
for (const auto& inst : instructions_) {
|
|
cb(inst);
|
|
}
|
|
}
|
|
|
|
bool Builder::GenerateEntryPoint(ast::EntryPoint* ep) {
|
|
auto name = ep->name();
|
|
if (name.empty()) {
|
|
name = ep->function_name();
|
|
}
|
|
|
|
auto id = id_for_func_name(ep->function_name());
|
|
if (id == 0) {
|
|
error_ = "unable to find ID for function: " + ep->function_name();
|
|
return false;
|
|
}
|
|
|
|
auto stage = pipeline_stage_to_execution_model(ep->stage());
|
|
if (stage == SpvExecutionModelMax) {
|
|
error_ = "Unknown pipeline stage provided";
|
|
return false;
|
|
}
|
|
|
|
push_preamble(spv::Op::OpEntryPoint,
|
|
{Operand::Int(stage), Operand::Int(id), Operand::String(name)});
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Builder::GenerateFunction(ast::Function* func) {
|
|
uint32_t func_type_id = GenerateFunctionTypeIfNeeded(func);
|
|
if (func_type_id == 0) {
|
|
return false;
|
|
}
|
|
|
|
auto func_op = result_op();
|
|
auto func_id = func_op.to_i();
|
|
|
|
push_debug(spv::Op::OpName,
|
|
{Operand::Int(func_id), Operand::String(func->name())});
|
|
|
|
auto ret_id = GenerateTypeIfNeeded(func->return_type());
|
|
if (ret_id == 0) {
|
|
return false;
|
|
}
|
|
|
|
// TODO(dsinclair): Handle parameters
|
|
push_inst(spv::Op::OpFunction, {Operand::Int(ret_id), func_op,
|
|
Operand::Int(SpvFunctionControlMaskNone),
|
|
Operand::Int(func_type_id)});
|
|
push_inst(spv::Op::OpLabel, {result_op()});
|
|
|
|
// TODO(dsinclair): Function body ...
|
|
|
|
push_inst(spv::Op::OpFunctionEnd, {});
|
|
|
|
func_name_to_id_[func->name()] = func_id;
|
|
return true;
|
|
}
|
|
|
|
uint32_t Builder::GenerateFunctionTypeIfNeeded(ast::Function* func) {
|
|
auto val = type_name_to_id_.find(func->type_name());
|
|
if (val != type_name_to_id_.end()) {
|
|
return val->second;
|
|
}
|
|
|
|
auto func_op = result_op();
|
|
auto func_type_id = func_op.to_i();
|
|
|
|
auto ret_id = GenerateTypeIfNeeded(func->return_type());
|
|
if (ret_id == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// TODO(dsinclair): Handle parameters
|
|
push_type(spv::Op::OpTypeFunction, {func_op, Operand::Int(ret_id)});
|
|
|
|
type_name_to_id_[func->type_name()] = func_type_id;
|
|
return func_type_id;
|
|
}
|
|
|
|
void Builder::GenerateImport(ast::Import* imp) {
|
|
auto result = result_op();
|
|
auto id = result.to_i();
|
|
|
|
push_preamble(spv::Op::OpExtInstImport,
|
|
{result, Operand::String(imp->path())});
|
|
|
|
import_name_to_id_[imp->name()] = id;
|
|
}
|
|
|
|
uint32_t Builder::GenerateTypeIfNeeded(ast::type::Type* type) {
|
|
if (type->IsAlias()) {
|
|
return GenerateTypeIfNeeded(type->AsAlias()->type());
|
|
}
|
|
|
|
auto val = type_name_to_id_.find(type->type_name());
|
|
if (val != type_name_to_id_.end()) {
|
|
return val->second;
|
|
}
|
|
|
|
auto result = result_op();
|
|
auto id = result.to_i();
|
|
|
|
if (type->IsBool()) {
|
|
push_type(spv::Op::OpTypeBool, {result});
|
|
} else if (type->IsF32()) {
|
|
push_type(spv::Op::OpTypeFloat, {result, Operand::Int(32)});
|
|
} else if (type->IsI32()) {
|
|
push_type(spv::Op::OpTypeInt, {result, Operand::Int(32), Operand::Int(1)});
|
|
} else if (type->IsMatrix()) {
|
|
if (!GenerateMatrixType(type->AsMatrix(), result)) {
|
|
return 0;
|
|
}
|
|
} else if (type->IsStruct()) {
|
|
if (!GenerateStructType(type->AsStruct(), result)) {
|
|
return 0;
|
|
}
|
|
} else if (type->IsU32()) {
|
|
push_type(spv::Op::OpTypeInt, {result, Operand::Int(32), Operand::Int(0)});
|
|
} else if (type->IsVector()) {
|
|
if (!GenerateVectorType(type->AsVector(), result)) {
|
|
return 0;
|
|
}
|
|
} else if (type->IsVoid()) {
|
|
push_type(spv::Op::OpTypeVoid, {result});
|
|
} else {
|
|
error_ = "unable to convert type: " + type->type_name();
|
|
return 0;
|
|
}
|
|
|
|
type_name_to_id_[type->type_name()] = id;
|
|
return id;
|
|
}
|
|
|
|
bool Builder::GenerateMatrixType(ast::type::MatrixType* mat,
|
|
const Operand& result) {
|
|
ast::type::VectorType col_type(mat->type(), mat->rows());
|
|
auto col_type_id = GenerateTypeIfNeeded(&col_type);
|
|
if (has_error()) {
|
|
return false;
|
|
}
|
|
|
|
push_type(spv::Op::OpTypeMatrix,
|
|
{result, Operand::Int(col_type_id), Operand::Int(mat->columns())});
|
|
return true;
|
|
}
|
|
|
|
bool Builder::GenerateStructType(ast::type::StructType* struct_type,
|
|
const Operand& result) {
|
|
auto struct_id = result.to_i();
|
|
auto impl = struct_type->impl();
|
|
|
|
std::vector<Operand> ops;
|
|
ops.push_back(result);
|
|
|
|
if (impl->decoration() == ast::StructDecoration::kBlock) {
|
|
push_annot(spv::Op::OpDecorate,
|
|
{Operand::Int(struct_id), Operand::Int(SpvDecorationBlock)});
|
|
} else {
|
|
if (impl->decoration() != ast::StructDecoration::kNone) {
|
|
error_ = "unknown struct decoration";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
auto& members = impl->members();
|
|
for (uint32_t i = 0; i < members.size(); ++i) {
|
|
auto mem_id = GenerateStructMember(struct_id, i, members[i].get());
|
|
if (mem_id == 0) {
|
|
return false;
|
|
}
|
|
|
|
ops.push_back(Operand::Int(mem_id));
|
|
}
|
|
|
|
push_type(spv::Op::OpTypeStruct, std::move(ops));
|
|
return true;
|
|
}
|
|
|
|
uint32_t Builder::GenerateStructMember(uint32_t struct_id,
|
|
uint32_t idx,
|
|
ast::StructMember* member) {
|
|
push_debug(spv::Op::OpMemberName, {Operand::Int(struct_id), Operand::Int(idx),
|
|
Operand::String(member->name())});
|
|
|
|
for (const auto& deco : member->decorations()) {
|
|
if (deco->IsOffset()) {
|
|
push_annot(spv::Op::OpMemberDecorate,
|
|
{Operand::Int(struct_id), Operand::Int(idx),
|
|
Operand::Int(SpvDecorationOffset),
|
|
Operand::Int(deco->AsOffset()->offset())});
|
|
} else {
|
|
error_ = "unknown struct member decoration";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return GenerateTypeIfNeeded(member->type());
|
|
}
|
|
|
|
bool Builder::GenerateVectorType(ast::type::VectorType* vec,
|
|
const Operand& result) {
|
|
auto type_id = GenerateTypeIfNeeded(vec->type());
|
|
if (has_error()) {
|
|
return false;
|
|
}
|
|
|
|
push_type(spv::Op::OpTypeVector,
|
|
{result, Operand::Int(type_id), Operand::Int(vec->size())});
|
|
return true;
|
|
}
|
|
|
|
} // namespace spirv
|
|
} // namespace writer
|
|
} // namespace tint
|