Create SPIR-V writer skeleton.
This CL creates the skeleton for the SPIR-V writer.
Bug: tint:5
Change-Id: I849d8766d32d48314a51096710272f9821e2c1c4
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/16641
Reviewed-by: David Neto <dneto@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d6c60a2..bb60a1a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,6 +48,8 @@
include_directories("${PROJECT_SOURCE_DIR}/third_party/spirv-tools/include")
endif()
+include_directories("${PROJECT_SOURCE_DIR}/third_party/spirv-headers/include")
+
if(("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") OR
("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") OR
(("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") AND
diff --git a/samples/main.cc b/samples/main.cc
index dc30009..708ab75 100644
--- a/samples/main.cc
+++ b/samples/main.cc
@@ -22,7 +22,7 @@
#include "src/reader/wgsl/parser.h"
#include "src/type_determiner.h"
#include "src/validator.h"
-#include "src/writer/spv/generator.h"
+#include "src/writer/spirv/generator.h"
#include "src/writer/wgsl/generator.h"
#include "src/writer/writer.h"
@@ -280,7 +280,8 @@
std::unique_ptr<tint::writer::Writer> writer;
if (options.format == Format::kSpirv || options.format == Format::kSpvAsm) {
- writer = std::make_unique<tint::writer::spv::Generator>(std::move(module));
+ writer =
+ std::make_unique<tint::writer::spirv::Generator>(std::move(module));
} else if (options.format == Format::kWgsl) {
writer = std::make_unique<tint::writer::wgsl::Generator>(std::move(module));
} else {
@@ -294,12 +295,12 @@
}
if (options.format == Format::kSpvAsm) {
- auto w = static_cast<tint::writer::spv::Generator*>(writer.get());
+ auto w = static_cast<tint::writer::spirv::Generator*>(writer.get());
auto str = Disassemble(w->result());
// TODO(dsinclair): Write to file if output_file given
std::cout << str << std::endl;
} else if (options.format == Format::kSpirv) {
- // auto w = static_cast<tint::writer::spv::Generator*>(writer.get());
+ // auto w = static_cast<tint::writer::spirv::Generator*>(writer.get());
// TODO(dsincliair): Write to to file
} else if (options.format == Format::kWgsl) {
auto w = static_cast<tint::writer::wgsl::Generator*>(writer.get());
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c9f494c..6501ac1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -177,8 +177,16 @@
validator_impl.cc
validator_impl.h
# TODO(dsinclair): The writers should all be optional
- writer/spv/generator.cc
- writer/spv/generator.h
+ writer/spirv/binary_writer.cc
+ writer/spirv/binary_writer.h
+ writer/spirv/builder.cc
+ writer/spirv/builder.h
+ writer/spirv/generator.cc
+ writer/spirv/generator.h
+ writer/spirv/instruction.cc
+ writer/spirv/instruction.h
+ writer/spirv/operand.cc
+ writer/spirv/operand.h
writer/wgsl/generator.cc
writer/wgsl/generator.h
writer/wgsl/generator_impl.cc
@@ -319,6 +327,10 @@
reader/wgsl/token_test.cc
type_manager_test.cc
validator_impl_import_test.cc
+ writer/spirv/binary_writer_test.cc
+ writer/spirv/builder_test.cc
+ writer/spirv/instruction_test.cc
+ writer/spirv/operand_test.cc
writer/wgsl/generator_impl_test.cc
)
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 2c5e17f..2c9ae57 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "src/reader/spv/parser_impl.h"
+
#include <cstring>
#include "source/opt/build_module.h"
#include "spirv-tools/libspirv.hpp"
-#include "src/reader/spirv/parser_impl.h"
namespace tint {
namespace reader {
diff --git a/src/reader/spirv/parser_impl_import_test.cc b/src/reader/spirv/parser_impl_import_test.cc
index e1823c8..772c299 100644
--- a/src/reader/spirv/parser_impl_import_test.cc
+++ b/src/reader/spirv/parser_impl_import_test.cc
@@ -12,12 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/reader/spirv/parser_impl.h"
-
#include <memory>
#include <sstream>
#include "gmock/gmock.h"
+#include "src/reader/spirv/parser_impl.h"
#include "src/reader/spirv/spirv_tools_helpers_test.h"
namespace tint {
diff --git a/src/writer/spirv/binary_writer.cc b/src/writer/spirv/binary_writer.cc
new file mode 100644
index 0000000..a5d8a3f
--- /dev/null
+++ b/src/writer/spirv/binary_writer.cc
@@ -0,0 +1,71 @@
+// 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/binary_writer.h"
+
+#include <cstring>
+
+namespace tint {
+namespace writer {
+namespace spirv {
+namespace {
+
+// TODO(dsinclair): Reserve a generator ID for Tint.
+// https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/spir-v.xml#L75
+const uint32_t kGeneratorId = 0;
+
+} // namespace
+
+BinaryWriter::BinaryWriter() = default;
+
+BinaryWriter::~BinaryWriter() = default;
+
+bool BinaryWriter::Write(const Builder& builder) {
+ out_.resize(builder.total_size(), 0);
+
+ out_[idx_++] = spv::MagicNumber;
+ out_[idx_++] = 0x00010300; // Version 1.3
+ out_[idx_++] = kGeneratorId;
+ out_[idx_++] = builder.id_bound();
+ out_[idx_++] = 0;
+
+ builder.iterate([this](const Instruction& inst) {
+ out_[idx_++] =
+ inst.word_length() << 16 | static_cast<uint32_t>(inst.opcode());
+
+ for (const auto& op : inst.operands()) {
+ process_op(op);
+ }
+ });
+ return true;
+}
+
+void BinaryWriter::process_op(const Operand& op) {
+ if (op.IsFloat()) {
+ auto f = op.to_f();
+ memcpy(out_.data() + idx_, &f, 4);
+ } else if (op.IsInt()) {
+ out_[idx_] = op.to_i();
+ } else {
+ const auto& str = op.to_s();
+ // This depends on the vector being initialized to 0 values so the string
+ // is correctly padded.
+ memcpy(out_.data() + idx_, str.c_str(), str.size() + 1);
+ }
+ idx_ += op.length();
+}
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
diff --git a/src/writer/spirv/binary_writer.h b/src/writer/spirv/binary_writer.h
new file mode 100644
index 0000000..972f001
--- /dev/null
+++ b/src/writer/spirv/binary_writer.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef SRC_WRITER_SPIRV_BINARY_WRITER_H_
+#define SRC_WRITER_SPIRV_BINARY_WRITER_H_
+
+#include <vector>
+
+#include "src/writer/spirv/builder.h"
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+/// Writer to convert from builder to SPIR-V binary
+class BinaryWriter {
+ public:
+ /// Constructor
+ BinaryWriter();
+ ~BinaryWriter();
+
+ /// Writes the given builder data into a binary
+ /// @param builder the builder to assemble from
+ /// @returns true on success
+ bool Write(const Builder& builder);
+
+ /// @returns the assembled SPIR-V
+ const std::vector<uint32_t>& result() const { return out_; }
+
+ private:
+ void process_op(const Operand& op);
+
+ /// Word index of the next word to fill.
+ size_t idx_ = 0;
+ std::vector<uint32_t> out_;
+};
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
+
+#endif // SRC_WRITER_SPIRV_BINARY_WRITER_H_
diff --git a/src/writer/spirv/binary_writer_test.cc b/src/writer/spirv/binary_writer_test.cc
new file mode 100644
index 0000000..6e6265b
--- /dev/null
+++ b/src/writer/spirv/binary_writer_test.cc
@@ -0,0 +1,117 @@
+// 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/binary_writer.h"
+
+#include <cstring>
+
+#include "gtest/gtest.h"
+#include "spirv/unified1/spirv.hpp11"
+#include "src/writer/spirv/builder.h"
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+using BinaryWriterTest = testing::Test;
+
+TEST_F(BinaryWriterTest, Preamble) {
+ Builder b;
+ BinaryWriter bw;
+ ASSERT_TRUE(bw.Write(b));
+
+ auto res = bw.result();
+ ASSERT_EQ(res.size(), 5);
+ EXPECT_EQ(res[0], spv::MagicNumber);
+ EXPECT_EQ(res[1], 0x00010300); // SPIR-V 1.3
+ EXPECT_EQ(res[2], 0); // Generator ID
+ EXPECT_EQ(res[3], 1); // ID Bound
+ EXPECT_EQ(res[4], 0); // Reserved
+}
+
+TEST_F(BinaryWriterTest, Float) {
+ Builder b;
+ b.push_preamble(spv::Op::OpNop, {Operand::Float(2.4f)});
+ BinaryWriter bw;
+ ASSERT_TRUE(bw.Write(b));
+
+ auto res = bw.result();
+ ASSERT_EQ(res.size(), 7);
+ float f;
+ memcpy(&f, res.data() + 6, 4);
+ EXPECT_EQ(f, 2.4f);
+}
+
+TEST_F(BinaryWriterTest, Int) {
+ Builder b;
+ b.push_preamble(spv::Op::OpNop, {Operand::Int(2)});
+ BinaryWriter bw;
+ ASSERT_TRUE(bw.Write(b));
+
+ auto res = bw.result();
+ ASSERT_EQ(res.size(), 7);
+ EXPECT_EQ(res[6], 2);
+}
+
+TEST_F(BinaryWriterTest, String) {
+ Builder b;
+ b.push_preamble(spv::Op::OpNop, {Operand::String("my_string")});
+ BinaryWriter bw;
+ ASSERT_TRUE(bw.Write(b));
+
+ auto res = bw.result();
+ ASSERT_EQ(res.size(), 9);
+
+ uint8_t* v = reinterpret_cast<uint8_t*>(res.data()) + (6 * 4);
+ EXPECT_EQ(v[0], 'm');
+ EXPECT_EQ(v[1], 'y');
+ EXPECT_EQ(v[2], '_');
+ EXPECT_EQ(v[3], 's');
+ EXPECT_EQ(v[4], 't');
+ EXPECT_EQ(v[5], 'r');
+ EXPECT_EQ(v[6], 'i');
+ EXPECT_EQ(v[7], 'n');
+ EXPECT_EQ(v[8], 'g');
+ EXPECT_EQ(v[9], '\0');
+ EXPECT_EQ(v[10], '\0');
+ EXPECT_EQ(v[11], '\0');
+}
+
+TEST_F(BinaryWriterTest, String_Multiple4Length) {
+ Builder b;
+ b.push_preamble(spv::Op::OpNop, {Operand::String("mystring")});
+ BinaryWriter bw;
+ ASSERT_TRUE(bw.Write(b));
+
+ auto res = bw.result();
+ ASSERT_EQ(res.size(), 9);
+
+ uint8_t* v = reinterpret_cast<uint8_t*>(res.data()) + (6 * 4);
+ EXPECT_EQ(v[0], 'm');
+ EXPECT_EQ(v[1], 'y');
+ EXPECT_EQ(v[2], 's');
+ EXPECT_EQ(v[3], 't');
+ EXPECT_EQ(v[4], 'r');
+ EXPECT_EQ(v[5], 'i');
+ EXPECT_EQ(v[6], 'n');
+ EXPECT_EQ(v[7], 'g');
+ EXPECT_EQ(v[8], '\0');
+ EXPECT_EQ(v[9], '\0');
+ EXPECT_EQ(v[10], '\0');
+ EXPECT_EQ(v[11], '\0');
+}
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
new file mode 100644
index 0000000..4c6f0bd
--- /dev/null
+++ b/src/writer/spirv/builder.cc
@@ -0,0 +1,85 @@
+// 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 "spirv/unified1/spirv.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;
+}
+
+} // namespace
+
+Builder::Builder() = default;
+
+Builder::~Builder() = default;
+
+bool Builder::Build(const ast::Module&) {
+ push_preamble(spv::Op::OpCapability, {Operand::Int(SpvCapabilityShader)});
+ push_preamble(spv::Op::OpExtInstImport,
+ {result_op(), Operand::String("GLSL.std.450")});
+ push_preamble(spv::Op::OpMemoryModel,
+ {Operand::Int(SpvAddressingModelLogical),
+ Operand::Int(SpvMemoryModelVulkanKHR)});
+ 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);
+ }
+}
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
new file mode 100644
index 0000000..7b27b18
--- /dev/null
+++ b/src/writer/spirv/builder.h
@@ -0,0 +1,112 @@
+// 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.
+
+#ifndef SRC_WRITER_SPIRV_BUILDER_H_
+#define SRC_WRITER_SPIRV_BUILDER_H_
+
+#include <functional>
+#include <vector>
+
+#include "src/ast/module.h"
+#include "src/writer/spirv/instruction.h"
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+/// Builder class to create SPIR-V instructions from a module.
+class Builder {
+ public:
+ /// Constructor
+ Builder();
+ ~Builder();
+
+ /// Generates the SPIR-V instructions for the given module
+ /// @param module the module to generate from
+ /// @returns true if the SPIR-V was successfully built
+ bool Build(const ast::Module& module);
+
+ /// @returns the number of uint32_t's needed to make up the results
+ uint32_t total_size() const;
+
+ /// @returns the id bound for this module
+ uint32_t id_bound() const { return next_id_; }
+
+ /// Iterates over all the instructions in the correct order and calls the
+ /// given callback
+ /// @param cb the callback to execute
+ void iterate(std::function<void(const Instruction&)> cb) const;
+
+ /// Adds an instruction to the preamble
+ /// @param op the op to set
+ /// @param operands the operands for the instruction
+ void push_preamble(spv::Op op, const std::vector<Operand>& operands) {
+ preamble_.push_back(Instruction{op, operands});
+ }
+ /// @returns the preamble
+ const std::vector<Instruction>& preamble() const { return preamble_; }
+ /// Adds an instruction to the debug
+ /// @param op the op to set
+ /// @param operands the operands for the instruction
+ void push_debug(spv::Op op, const std::vector<Operand>& operands) {
+ debug_.push_back(Instruction{op, operands});
+ }
+ /// @returns the debug instructions
+ const std::vector<Instruction>& debug() const { return debug_; }
+ /// Adds an instruction to the types
+ /// @param op the op to set
+ /// @param operands the operands for the instruction
+ void push_type(spv::Op op, const std::vector<Operand>& operands) {
+ types_.push_back(Instruction{op, operands});
+ }
+ /// @returns the type instructions
+ const std::vector<Instruction>& type() const { return types_; }
+ /// Adds an instruction to the instruction list
+ /// @param op the op to set
+ /// @param operands the operands for the instruction
+ void push_inst(spv::Op op, const std::vector<Operand>& operands) {
+ instructions_.push_back(Instruction{op, operands});
+ }
+ /// @returns the instruction list
+ const std::vector<Instruction>& inst() const { return instructions_; }
+ /// Adds an instruction to the annotations
+ /// @param op the op to set
+ /// @param operands the operands for the instruction
+ void push_annot(spv::Op op, const std::vector<Operand>& operands) {
+ annotations_.push_back(Instruction{op, operands});
+ }
+ /// @returns the annotations
+ const std::vector<Instruction>& annot() const { return annotations_; }
+
+ private:
+ Operand result_op();
+ uint32_t next_id() {
+ auto id = next_id_;
+ next_id_ += 1;
+ return id;
+ }
+
+ uint32_t next_id_ = 1;
+ std::vector<Instruction> preamble_;
+ std::vector<Instruction> debug_;
+ std::vector<Instruction> types_;
+ std::vector<Instruction> instructions_;
+ std::vector<Instruction> annotations_;
+};
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
+
+#endif // SRC_WRITER_SPIRV_BUILDER_H_
diff --git a/src/writer/spirv/builder_test.cc b/src/writer/spirv/builder_test.cc
new file mode 100644
index 0000000..5e44c73
--- /dev/null
+++ b/src/writer/spirv/builder_test.cc
@@ -0,0 +1,40 @@
+// 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 "gtest/gtest.h"
+#include "spirv/unified1/spirv.hpp11"
+#include "src/ast/module.h"
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+using BuilderTest = testing::Test;
+
+TEST_F(BuilderTest, InsertsPreamble) {
+ ast::Module m;
+ Builder b;
+ ASSERT_TRUE(b.Build(m));
+ ASSERT_EQ(b.preamble().size(), 3);
+ auto pre = b.preamble();
+ EXPECT_EQ(pre[0].opcode(), spv::Op::OpCapability);
+ EXPECT_EQ(pre[1].opcode(), spv::Op::OpExtInstImport);
+ EXPECT_EQ(pre[2].opcode(), spv::Op::OpMemoryModel);
+}
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
diff --git a/src/writer/spv/generator.cc b/src/writer/spirv/generator.cc
similarity index 83%
rename from src/writer/spv/generator.cc
rename to src/writer/spirv/generator.cc
index 8d15522..b47838c 100644
--- a/src/writer/spv/generator.cc
+++ b/src/writer/spirv/generator.cc
@@ -12,22 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/writer/spv/generator.h"
+#include "src/writer/spirv/generator.h"
#include <utility>
namespace tint {
namespace writer {
-namespace spv {
+namespace spirv {
Generator::Generator(ast::Module module) : writer::Writer(std::move(module)) {}
Generator::~Generator() = default;
bool Generator::Generate() {
- return true;
+ if (!builder_.Build(module_))
+ return false;
+
+ return writer_.Write(builder_);
}
-} // namespace spv
+} // namespace spirv
} // namespace writer
} // namespace tint
diff --git a/src/writer/spv/generator.h b/src/writer/spirv/generator.h
similarity index 74%
rename from src/writer/spv/generator.h
rename to src/writer/spirv/generator.h
index e677019..3a14ec5 100644
--- a/src/writer/spv/generator.h
+++ b/src/writer/spirv/generator.h
@@ -12,16 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef SRC_WRITER_SPV_GENERATOR_H_
-#define SRC_WRITER_SPV_GENERATOR_H_
+#ifndef SRC_WRITER_SPIRV_GENERATOR_H_
+#define SRC_WRITER_SPIRV_GENERATOR_H_
#include <vector>
+#include "src/ast/module.h"
+#include "src/writer/spirv/binary_writer.h"
+#include "src/writer/spirv/builder.h"
#include "src/writer/writer.h"
namespace tint {
namespace writer {
-namespace spv {
+namespace spirv {
/// Class to generate SPIR-V from a Tint module
class Generator : public writer::Writer {
@@ -36,14 +39,15 @@
bool Generate() override;
/// @returns the result data
- const std::vector<uint32_t>& result() const { return result_; }
+ const std::vector<uint32_t>& result() const { return writer_.result(); }
private:
- std::vector<uint32_t> result_;
+ Builder builder_;
+ BinaryWriter writer_;
};
-} // namespace spv
+} // namespace spirv
} // namespace writer
} // namespace tint
-#endif // SRC_WRITER_SPV_GENERATOR_H_
+#endif // SRC_WRITER_SPIRV_GENERATOR_H_
diff --git a/src/writer/spv/generator.cc b/src/writer/spirv/instruction.cc
similarity index 62%
copy from src/writer/spv/generator.cc
copy to src/writer/spirv/instruction.cc
index 8d15522..d4994d6 100644
--- a/src/writer/spv/generator.cc
+++ b/src/writer/spirv/instruction.cc
@@ -12,22 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/writer/spv/generator.h"
+#include "src/writer/spirv/instruction.h"
#include <utility>
namespace tint {
namespace writer {
-namespace spv {
+namespace spirv {
-Generator::Generator(ast::Module module) : writer::Writer(std::move(module)) {}
+Instruction::Instruction(spv::Op op, std::vector<Operand> operands)
+ : op_(op), operands_(std::move(operands)) {}
-Generator::~Generator() = default;
+Instruction::~Instruction() = default;
-bool Generator::Generate() {
- return true;
+uint32_t Instruction::word_length() const {
+ uint32_t size = 1; // Initial 1 for the op and size
+ for (const auto& op : operands_) {
+ size += op.length();
+ }
+ return size;
}
-} // namespace spv
+} // namespace spirv
} // namespace writer
} // namespace tint
diff --git a/src/writer/spirv/instruction.h b/src/writer/spirv/instruction.h
new file mode 100644
index 0000000..4632eeb
--- /dev/null
+++ b/src/writer/spirv/instruction.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef SRC_WRITER_SPIRV_INSTRUCTION_H_
+#define SRC_WRITER_SPIRV_INSTRUCTION_H_
+
+#include <vector>
+
+#include "spirv/unified1/spirv.hpp11"
+#include "src/writer/spirv/operand.h"
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+/// A single SPIR-V instruction
+class Instruction {
+ public:
+ /// Constructor
+ /// @param op the op to generate
+ /// @param operands the operand values for the instruction
+ Instruction(spv::Op op, std::vector<Operand> operands);
+ /// Copy Constructor
+ Instruction(const Instruction&) = default;
+ ~Instruction();
+
+ /// @returns the instructions op
+ spv::Op opcode() const { return op_; }
+
+ /// @returns the instructions operands
+ const std::vector<Operand>& operands() const { return operands_; }
+
+ /// @returns the number of uint32_t's needed to hold the instruction
+ uint32_t word_length() const;
+
+ private:
+ spv::Op op_ = spv::Op::OpNop;
+ std::vector<Operand> operands_;
+};
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
+
+#endif // SRC_WRITER_SPIRV_INSTRUCTION_H_
diff --git a/src/writer/spirv/instruction_test.cc b/src/writer/spirv/instruction_test.cc
new file mode 100644
index 0000000..21b6e20
--- /dev/null
+++ b/src/writer/spirv/instruction_test.cc
@@ -0,0 +1,51 @@
+// 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/instruction.h"
+
+#include "gtest/gtest.h"
+#include "src/writer/spirv/operand.h"
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+using InstructionTest = testing::Test;
+
+TEST_F(InstructionTest, Create) {
+ Instruction i(spv::Op::OpEntryPoint, {Operand::Float(1.2f), Operand::Int(1),
+ Operand::String("my_str")});
+ EXPECT_EQ(i.opcode(), spv::Op::OpEntryPoint);
+ ASSERT_EQ(i.operands().size(), 3);
+
+ const auto& ops = i.operands();
+ EXPECT_TRUE(ops[0].IsFloat());
+ EXPECT_FLOAT_EQ(ops[0].to_f(), 1.2);
+
+ EXPECT_TRUE(ops[1].IsInt());
+ EXPECT_EQ(ops[1].to_i(), 1);
+
+ EXPECT_TRUE(ops[2].IsString());
+ EXPECT_EQ(ops[2].to_s(), "my_str");
+}
+
+TEST_F(InstructionTest, Length) {
+ Instruction i(spv::Op::OpEntryPoint, {Operand::Float(1.2f), Operand::Int(1),
+ Operand::String("my_str")});
+ EXPECT_EQ(i.word_length(), 5);
+}
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
diff --git a/src/writer/spirv/operand.cc b/src/writer/spirv/operand.cc
new file mode 100644
index 0000000..1a991ad
--- /dev/null
+++ b/src/writer/spirv/operand.cc
@@ -0,0 +1,66 @@
+// 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/operand.h"
+
+#include <cmath>
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+// static
+Operand Operand::Float(float val) {
+ Operand o(Kind::kFloat);
+ o.set_float(val);
+ return o;
+}
+
+// static
+Operand Operand::Int(uint32_t val) {
+ Operand o(Kind::kInt);
+ o.set_int(val);
+ return o;
+}
+
+// static
+Operand Operand::String(const std::string& val) {
+ Operand o(Kind::kString);
+ o.set_string(val);
+ return o;
+}
+
+Operand::Operand(Kind kind) : kind_(kind) {}
+
+Operand::~Operand() = default;
+
+uint32_t Operand::length() const {
+ uint32_t val = 0;
+ switch (kind_) {
+ case Kind::kFloat:
+ case Kind::kInt:
+ val = 1;
+ break;
+ case Kind::kString:
+ // SPIR-V always nul-terminates strings. The length is rounded up to a
+ // multiple of 4 bytes with 0 bytes padding the end.
+ val = static_cast<uint32_t>(ceil((str_val_.length() + 1) / 4.0));
+ break;
+ }
+ return val;
+}
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
diff --git a/src/writer/spirv/operand.h b/src/writer/spirv/operand.h
new file mode 100644
index 0000000..161f263
--- /dev/null
+++ b/src/writer/spirv/operand.h
@@ -0,0 +1,90 @@
+// 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.
+
+#ifndef SRC_WRITER_SPIRV_OPERAND_H_
+#define SRC_WRITER_SPIRV_OPERAND_H_
+
+#include <string>
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+/// A single SPIR-V instruction operand
+class Operand {
+ public:
+ /// The kind of the operand
+ // Note, the `kInt` will cover most cases as things like IDs in SPIR-V are
+ // just ints for the purpose of converting to binary.
+ enum Kind { kInt = 0, kFloat, kString };
+
+ /// Creates a float operand
+ /// @param val the float value
+ /// @returns the operand
+ static Operand Float(float val);
+ /// Creates an int operand
+ /// @param val the int value
+ /// @returns the operand
+ static Operand Int(uint32_t val);
+ /// Creates a string operand
+ /// @param val the string value
+ /// @returns the operand
+ static Operand String(const std::string& val);
+
+ /// Constructor
+ /// @param kind the type of operand
+ explicit Operand(Kind kind);
+ /// Copy Constructor
+ Operand(const Operand&) = default;
+ ~Operand();
+
+ /// Sets the float value
+ /// @param val the value to set
+ void set_float(float val) { float_val_ = val; }
+ /// Sets the int value
+ /// @param val the value to set
+ void set_int(uint32_t val) { int_val_ = val; }
+ /// Sets the string value
+ /// @param val the value to set
+ void set_string(const std::string& val) { str_val_ = val; }
+
+ /// @returns true if this is a float operand
+ bool IsFloat() const { return kind_ == Kind::kFloat; }
+ /// @returns true if this is an integer operand
+ bool IsInt() const { return kind_ == Kind::kInt; }
+ /// @returns true if this is a string operand
+ bool IsString() const { return kind_ == Kind::kString; }
+
+ /// @returns the number of uint32_t's needed for this operand
+ uint32_t length() const;
+
+ /// @returns the float value
+ float to_f() const { return float_val_; }
+ /// @returns the int value
+ uint32_t to_i() const { return int_val_; }
+ /// @returns the string value
+ const std::string& to_s() const { return str_val_; }
+
+ private:
+ Kind kind_ = Kind::kInt;
+ float float_val_ = 0.0;
+ uint32_t int_val_ = 0;
+ std::string str_val_;
+};
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint
+
+#endif // SRC_WRITER_SPIRV_OPERAND_H_
diff --git a/src/writer/spirv/operand_test.cc b/src/writer/spirv/operand_test.cc
new file mode 100644
index 0000000..04db16f
--- /dev/null
+++ b/src/writer/spirv/operand_test.cc
@@ -0,0 +1,65 @@
+// 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/operand.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+using OperandTest = testing::Test;
+
+TEST_F(OperandTest, CreateFloat) {
+ auto o = Operand::Float(1.2f);
+ EXPECT_TRUE(o.IsFloat());
+ EXPECT_FLOAT_EQ(o.to_f(), 1.2);
+}
+
+TEST_F(OperandTest, CreateInt) {
+ auto o = Operand::Int(1);
+ EXPECT_TRUE(o.IsInt());
+ EXPECT_EQ(o.to_i(), 1);
+}
+
+TEST_F(OperandTest, CreateString) {
+ auto o = Operand::String("my string");
+ EXPECT_TRUE(o.IsString());
+ EXPECT_EQ(o.to_s(), "my string");
+}
+
+TEST_F(OperandTest, Length_Float) {
+ auto o = Operand::Float(1.2f);
+ EXPECT_EQ(o.length(), 1);
+}
+
+TEST_F(OperandTest, Length_Int) {
+ auto o = Operand::Int(1);
+ EXPECT_EQ(o.length(), 1);
+}
+
+TEST_F(OperandTest, Length_String) {
+ auto o = Operand::String("my string");
+ EXPECT_EQ(o.length(), 3);
+}
+
+TEST_F(OperandTest, Length_String_Empty) {
+ auto o = Operand::String("");
+ EXPECT_EQ(o.length(), 1);
+}
+
+} // namespace spirv
+} // namespace writer
+} // namespace tint