ir/spirv-writer: Add support for scalar types
Also add a test helper header with some base classes that derive from
the IR builder.
Bug: tint:1906
Change-Id: If642bc64a50b6cae10363018a8dea8547ab9f542
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/131441
Commit-Queue: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index e3fed4d..a866575 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -1858,7 +1858,10 @@
]
if (tint_build_ir) {
- sources += [ "writer/spirv/generator_impl_ir_test.cc" ]
+ sources += [
+ "writer/spirv/generator_impl_ir_test.cc",
+ "writer/spirv/generator_impl_type_test.cc",
+ ]
deps += [ ":libtint_ir_src" ]
}
}
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index d0cf092..daafbb4 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -1228,6 +1228,8 @@
if(${TINT_BUILD_IR})
list(APPEND TINT_TEST_SRCS
writer/spirv/generator_impl_ir_test.cc
+ writer/spirv/generator_impl_type_test.cc
+ writer/spirv/test_helper_ir.h
)
endif()
endif()
diff --git a/src/tint/writer/spirv/generator_impl_ir.cc b/src/tint/writer/spirv/generator_impl_ir.cc
index 23842b6..fabb3a9 100644
--- a/src/tint/writer/spirv/generator_impl_ir.cc
+++ b/src/tint/writer/spirv/generator_impl_ir.cc
@@ -16,6 +16,14 @@
#include "spirv/unified1/spirv.h"
#include "src/tint/ir/module.h"
+#include "src/tint/switch.h"
+#include "src/tint/type/bool.h"
+#include "src/tint/type/f16.h"
+#include "src/tint/type/f32.h"
+#include "src/tint/type/i32.h"
+#include "src/tint/type/type.h"
+#include "src/tint/type/u32.h"
+#include "src/tint/type/void.h"
#include "src/tint/writer/spirv/module.h"
namespace tint::writer::spirv {
@@ -45,4 +53,30 @@
return true;
}
+uint32_t GeneratorImplIr::Type(const type::Type* ty) {
+ return types_.GetOrCreate(ty, [&]() {
+ auto id = module_.NextId();
+ Switch(
+ ty, //
+ [&](const type::Void*) { module_.PushType(spv::Op::OpTypeVoid, {id}); },
+ [&](const type::Bool*) { module_.PushType(spv::Op::OpTypeBool, {id}); },
+ [&](const type::I32*) {
+ module_.PushType(spv::Op::OpTypeInt, {id, 32u, 1u});
+ },
+ [&](const type::U32*) {
+ module_.PushType(spv::Op::OpTypeInt, {id, 32u, 0u});
+ },
+ [&](const type::F32*) {
+ module_.PushType(spv::Op::OpTypeFloat, {id, 32u});
+ },
+ [&](const type::F16*) {
+ module_.PushType(spv::Op::OpTypeFloat, {id, 16u});
+ },
+ [&](Default) {
+ TINT_ICE(Writer, diagnostics_) << "unhandled type: " << ty->FriendlyName();
+ });
+ return id;
+ });
+}
+
} // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/generator_impl_ir.h b/src/tint/writer/spirv/generator_impl_ir.h
index 7da1184..44fefb5 100644
--- a/src/tint/writer/spirv/generator_impl_ir.h
+++ b/src/tint/writer/spirv/generator_impl_ir.h
@@ -18,6 +18,7 @@
#include <vector>
#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/utils/hashmap.h"
#include "src/tint/writer/spirv/binary_writer.h"
#include "src/tint/writer/spirv/module.h"
@@ -25,6 +26,9 @@
namespace tint::ir {
class Module;
} // namespace tint::ir
+namespace tint::type {
+class Type;
+} // namespace tint::type
namespace tint::writer::spirv {
@@ -49,12 +53,20 @@
/// @returns the list of diagnostics raised by the generator
diag::List Diagnostics() const { return diagnostics_; }
+ /// Get the result ID of the type `ty`, emitting a type declaration instruction if necessary.
+ /// @param ty the type to get the ID for
+ /// @returns the result ID of the type
+ uint32_t Type(const type::Type* ty);
+
private:
const ir::Module* ir_;
spirv::Module module_;
BinaryWriter writer_;
diag::List diagnostics_;
+ /// The map of types to their result IDs.
+ utils::Hashmap<const type::Type*, uint32_t, 8> types_;
+
bool zero_init_workgroup_memory_ = false;
};
diff --git a/src/tint/writer/spirv/generator_impl_ir_test.cc b/src/tint/writer/spirv/generator_impl_ir_test.cc
index 8e8f8e7..a202eea 100644
--- a/src/tint/writer/spirv/generator_impl_ir_test.cc
+++ b/src/tint/writer/spirv/generator_impl_ir_test.cc
@@ -12,22 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "gtest/gtest.h"
-
-#include "src/tint/ir/module.h"
-#include "src/tint/writer/spirv/generator_impl_ir.h"
-#include "src/tint/writer/spirv/spv_dump.h"
+#include "src/tint/writer/spirv/test_helper_ir.h"
namespace tint::writer::spirv {
namespace {
-using SpvGeneratorImplTest = testing::Test;
-
TEST_F(SpvGeneratorImplTest, ModuleHeader) {
- ir::Module module;
- GeneratorImplIr generator(&module, false);
- ASSERT_TRUE(generator.Generate()) << generator.Diagnostics().str();
- auto got = Disassemble(generator.Result());
+ ASSERT_TRUE(generator_.Generate()) << generator_.Diagnostics().str();
+ auto got = Disassemble(generator_.Result());
EXPECT_EQ(got, R"(OpCapability Shader
OpMemoryModel Logical GLSL450
)");
diff --git a/src/tint/writer/spirv/generator_impl_type_test.cc b/src/tint/writer/spirv/generator_impl_type_test.cc
new file mode 100644
index 0000000..c8a2e8e
--- /dev/null
+++ b/src/tint/writer/spirv/generator_impl_type_test.cc
@@ -0,0 +1,87 @@
+// Copyright 2023 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/tint/type/bool.h"
+#include "src/tint/type/f16.h"
+#include "src/tint/type/f32.h"
+#include "src/tint/type/i32.h"
+#include "src/tint/type/type.h"
+#include "src/tint/type/u32.h"
+#include "src/tint/type/void.h"
+#include "src/tint/writer/spirv/test_helper_ir.h"
+
+namespace tint::writer::spirv {
+namespace {
+
+TEST_F(SpvGeneratorImplTest, Type_Void) {
+ auto id = generator_.Type(ir.types.Get<type::Void>());
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), "%1 = OpTypeVoid\n");
+}
+
+TEST_F(SpvGeneratorImplTest, Type_Bool) {
+ auto id = generator_.Type(ir.types.Get<type::Bool>());
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), "%1 = OpTypeBool\n");
+}
+
+TEST_F(SpvGeneratorImplTest, Type_I32) {
+ auto id = generator_.Type(ir.types.Get<type::I32>());
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), "%1 = OpTypeInt 32 1\n");
+}
+
+TEST_F(SpvGeneratorImplTest, Type_U32) {
+ auto id = generator_.Type(ir.types.Get<type::U32>());
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), "%1 = OpTypeInt 32 0\n");
+}
+
+TEST_F(SpvGeneratorImplTest, Type_F32) {
+ auto id = generator_.Type(ir.types.Get<type::F32>());
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), "%1 = OpTypeFloat 32\n");
+}
+
+TEST_F(SpvGeneratorImplTest, Type_F16) {
+ auto id = generator_.Type(ir.types.Get<type::F16>());
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), "%1 = OpTypeFloat 16\n");
+}
+
+// Test that we do can emit multiple types.
+// Includes types with the same opcode but different parameters.
+TEST_F(SpvGeneratorImplTest, Type_Multiple) {
+ EXPECT_EQ(generator_.Type(ir.types.Get<type::I32>()), 1u);
+ EXPECT_EQ(generator_.Type(ir.types.Get<type::U32>()), 2u);
+ EXPECT_EQ(generator_.Type(ir.types.Get<type::F32>()), 3u);
+ EXPECT_EQ(generator_.Type(ir.types.Get<type::F16>()), 4u);
+ EXPECT_EQ(DumpTypes(), R"(%1 = OpTypeInt 32 1
+%2 = OpTypeInt 32 0
+%3 = OpTypeFloat 32
+%4 = OpTypeFloat 16
+)");
+}
+
+// Test that we do not emit the same type more than once.
+TEST_F(SpvGeneratorImplTest, Type_Deduplicate) {
+ auto* i32 = ir.types.Get<type::I32>();
+ EXPECT_EQ(generator_.Type(i32), 1u);
+ EXPECT_EQ(generator_.Type(i32), 1u);
+ EXPECT_EQ(generator_.Type(i32), 1u);
+ EXPECT_EQ(DumpTypes(), "%1 = OpTypeInt 32 1\n");
+}
+
+} // namespace
+} // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/test_helper_ir.h b/src/tint/writer/spirv/test_helper_ir.h
new file mode 100644
index 0000000..6de6f48
--- /dev/null
+++ b/src/tint/writer/spirv/test_helper_ir.h
@@ -0,0 +1,48 @@
+// Copyright 2023 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_TINT_WRITER_SPIRV_TEST_HELPER_IR_H_
+#define SRC_TINT_WRITER_SPIRV_TEST_HELPER_IR_H_
+
+#include <string>
+
+#include "gtest/gtest.h"
+#include "src/tint/ir/builder.h"
+#include "src/tint/writer/spirv/generator_impl_ir.h"
+#include "src/tint/writer/spirv/spv_dump.h"
+
+namespace tint::writer::spirv {
+
+/// Base helper class for testing the SPIR-V generator implementation.
+template <typename BASE>
+class SpvGeneratorTestHelperBase : public ir::Builder, public BASE {
+ public:
+ SpvGeneratorTestHelperBase() : generator_(&ir, false) {}
+
+ protected:
+ /// The SPIR-V generator.
+ GeneratorImplIr generator_;
+
+ /// @returns the disassembled types from the generated module.
+ std::string DumpTypes() { return DumpInstructions(generator_.Module().Types()); }
+};
+
+using SpvGeneratorImplTest = SpvGeneratorTestHelperBase<testing::Test>;
+
+template <typename T>
+using SpvGeneratorImplTestWithParam = SpvGeneratorTestHelperBase<testing::TestWithParam<T>>;
+
+} // namespace tint::writer::spirv
+
+#endif // SRC_TINT_WRITER_SPIRV_TEST_HELPER_IR_H_