Import Tint changes from Dawn
Changes:
- aa97bb5327c375e89ec1f0ee612b0e00093b9c34 [ir] Add assignment statements by dan sinclair <dsinclair@chromium.org>
- 5b541ff3c287f06b74350827628249152293f407 [ir] Mark const variable as skipped. by dan sinclair <dsinclair@chromium.org>
- 5aa7ef2bc077134fca8afbe584016916adecc064 [ir] Add Unary expressions by dan sinclair <dsinclair@chromium.org>
- 339719967ebfb3493cb351bc6a8a82388bca1fd3 [ir] Remove internal `bool` returns. by dan sinclair <dsinclair@chromium.org>
- 69108d048b56073ccc0ac7d46c254c62a87ac205 [ir] Add ir::Discard by dan sinclair <dsinclair@chromium.org>
- ae39e6d62808b1b3899430c38f41f618ed9337d7 spirv-reader: Declare multiuse constant composites by James Price <jrprice@google.com>
- a41693babb6069d4487a1cd2ab114aa87ee0fe78 tint/msl: Avoid cloning a built-in struct by James Price <jrprice@google.com>
- 99a0ded622559bd21f063415299a2ee7b7514233 tint: Use type::Struct::Name in CreateASTTypeFor by James Price <jrprice@google.com>
- 03f9f5f538121da7bc3c6175de9fd38fa2aaf0e0 [ir] Rename ir::Temp by dan sinclair <dsinclair@chromium.org>
- d36740509fa6daa1200ee47f6dbbed66df652aeb spirv-reader: Error on multiple Position built-ins by James Price <jrprice@google.com>
- bdc2d249007af5b71584ae45f61ca568d9bbfb32 Fix MSL invariant translation. by dan sinclair <dsinclair@chromium.org>
- 378a1f51a289147d5fa8065c4f2ef25a757ef7ae Use optional in Lexer. by dan sinclair <dsinclair@chromium.org>
- 889edb18b2740faa5efb35f5020103f7db2fb216 Add simple program for perf testing. by dan sinclair <dsinclair@chromium.org>
- d14a9fbb6e3d398cbca9d7404eb6ffaa0fbe5646 tint/resolver: Fix null deref by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: aa97bb5327c375e89ec1f0ee612b0e00093b9c34
Change-Id: I8fb8032fcea440d70d07e0fcd94bb4c05973e2ad
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/129040
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 7bac6ed..f715428 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -720,6 +720,8 @@
ir/debug.h
ir/disassembler.cc
ir/disassembler.h
+ ir/discard.cc
+ ir/discard.h
ir/flow_node.cc
ir/flow_node.h
ir/function.cc
@@ -732,12 +734,16 @@
ir/loop.h
ir/module.cc
ir/module.h
+ ir/runtime.cc
+ ir/runtime.h
+ ir/store.cc
+ ir/store.h
ir/switch.cc
ir/switch.h
- ir/temp.cc
- ir/temp.h
ir/terminator.cc
ir/terminator.h
+ ir/unary.cc
+ ir/unary.h
ir/user_call.cc
ir/user_call.h
ir/value.cc
@@ -1413,8 +1419,11 @@
ir/bitcast_test.cc
ir/builder_impl_test.cc
ir/constant_test.cc
- ir/temp_test.cc
+ ir/discard_test.cc
+ ir/runtime_test.cc
+ ir/store_test.cc
ir/test_helper.h
+ ir/unary_test.cc
)
endif()
diff --git a/src/tint/cmd/CMakeLists.txt b/src/tint/cmd/CMakeLists.txt
index 142595b..441954d 100644
--- a/src/tint/cmd/CMakeLists.txt
+++ b/src/tint/cmd/CMakeLists.txt
@@ -53,3 +53,15 @@
if (${TINT_BUILD_SPV_READER})
target_link_libraries(tint_info SPIRV-Tools)
endif()
+
+add_executable(tint-loopy "")
+target_sources(tint-loopy PRIVATE
+ "generate_external_texture_bindings.cc"
+ "generate_external_texture_bindings.h"
+ "helper.cc"
+ "helper.h"
+ "loopy.cc"
+)
+tint_default_compile_options(tint-loopy)
+target_link_libraries(tint-loopy libtint tint_val)
+
diff --git a/src/tint/cmd/helper.cc b/src/tint/cmd/helper.cc
index 6ff1e7a..a00e24a 100644
--- a/src/tint/cmd/helper.cc
+++ b/src/tint/cmd/helper.cc
@@ -45,54 +45,6 @@
return input_format;
}
-/// Copies the content from the file named `input_file` to `buffer`,
-/// assuming each element in the file is of type `T`. If any error occurs,
-/// writes error messages to the standard error stream and returns false.
-/// Assumes the size of a `T` object is divisible by its required alignment.
-/// @returns true if we successfully read the file.
-template <typename T>
-bool ReadFile(const std::string& input_file, std::vector<T>* buffer) {
- if (!buffer) {
- std::cerr << "The buffer pointer was null" << std::endl;
- return false;
- }
-
- FILE* file = nullptr;
-#if defined(_MSC_VER)
- fopen_s(&file, input_file.c_str(), "rb");
-#else
- file = fopen(input_file.c_str(), "rb");
-#endif
- if (!file) {
- std::cerr << "Failed to open " << input_file << std::endl;
- return false;
- }
-
- fseek(file, 0, SEEK_END);
- const auto file_size = static_cast<size_t>(ftell(file));
- if (0 != (file_size % sizeof(T))) {
- std::cerr << "File " << input_file
- << " does not contain an integral number of objects: " << file_size
- << " bytes in the file, require " << sizeof(T) << " bytes per object"
- << std::endl;
- fclose(file);
- return false;
- }
- fseek(file, 0, SEEK_SET);
-
- buffer->clear();
- buffer->resize(file_size / sizeof(T));
-
- size_t bytes_read = fread(buffer->data(), 1, file_size, file);
- fclose(file);
- if (bytes_read != file_size) {
- std::cerr << "Failed to read " << input_file << std::endl;
- return false;
- }
-
- return true;
-}
-
void PrintBindings(tint::inspector::Inspector& inspector, const std::string& ep_name) {
auto bindings = inspector.GetResourceBindings(ep_name);
if (!inspector.error().empty()) {
diff --git a/src/tint/cmd/helper.h b/src/tint/cmd/helper.h
index 3a48ea4..6878288 100644
--- a/src/tint/cmd/helper.h
+++ b/src/tint/cmd/helper.h
@@ -15,8 +15,10 @@
#ifndef SRC_TINT_CMD_HELPER_H_
#define SRC_TINT_CMD_HELPER_H_
+#include <iostream>
#include <memory>
#include <string>
+#include <vector>
#include "tint/tint.h"
@@ -102,6 +104,54 @@
/// @return the text name
std::string OverrideTypeToString(tint::inspector::Override::Type type);
+/// Copies the content from the file named `input_file` to `buffer`,
+/// assuming each element in the file is of type `T`. If any error occurs,
+/// writes error messages to the standard error stream and returns false.
+/// Assumes the size of a `T` object is divisible by its required alignment.
+/// @returns true if we successfully read the file.
+template <typename T>
+bool ReadFile(const std::string& input_file, std::vector<T>* buffer) {
+ if (!buffer) {
+ std::cerr << "The buffer pointer was null" << std::endl;
+ return false;
+ }
+
+ FILE* file = nullptr;
+#if defined(_MSC_VER)
+ fopen_s(&file, input_file.c_str(), "rb");
+#else
+ file = fopen(input_file.c_str(), "rb");
+#endif
+ if (!file) {
+ std::cerr << "Failed to open " << input_file << std::endl;
+ return false;
+ }
+
+ fseek(file, 0, SEEK_END);
+ const auto file_size = static_cast<size_t>(ftell(file));
+ if (0 != (file_size % sizeof(T))) {
+ std::cerr << "File " << input_file
+ << " does not contain an integral number of objects: " << file_size
+ << " bytes in the file, require " << sizeof(T) << " bytes per object"
+ << std::endl;
+ fclose(file);
+ return false;
+ }
+ fseek(file, 0, SEEK_SET);
+
+ buffer->clear();
+ buffer->resize(file_size / sizeof(T));
+
+ size_t bytes_read = fread(buffer->data(), 1, file_size, file);
+ fclose(file);
+ if (bytes_read != file_size) {
+ std::cerr << "Failed to read " << input_file << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
} // namespace tint::cmd
#endif // SRC_TINT_CMD_HELPER_H_
diff --git a/src/tint/cmd/loopy.cc b/src/tint/cmd/loopy.cc
new file mode 100644
index 0000000..1714e3b
--- /dev/null
+++ b/src/tint/cmd/loopy.cc
@@ -0,0 +1,433 @@
+// 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 <iostream>
+
+#include "src/tint/cmd/generate_external_texture_bindings.h"
+#include "src/tint/cmd/helper.h"
+#include "tint/tint.h"
+
+#if TINT_BUILD_IR
+#include "src/tint/ir/module.h"
+#endif // TINT_BUILD_IR
+
+namespace {
+
+enum class Format {
+ kUnknown,
+ kNone,
+ kSpirv,
+ kWgsl,
+ kMsl,
+ kHlsl,
+ kGlsl,
+};
+
+enum class Looper {
+ kLoad,
+ kIRGenerate,
+ kWriter,
+};
+
+struct Options {
+ bool show_help = false;
+
+ std::string input_filename;
+ Format format = Format::kUnknown;
+
+ Looper loop = Looper::kLoad;
+ uint32_t loop_count = 100;
+};
+
+const char kUsage[] = R"(Usage: tint-loopy [options] <input-file>
+
+ options:
+ --format <spirv|wgsl|msl|hlsl|none> -- Generation format. Default SPIR-V.
+ --loop <load,ir-gen,writer> -- Item to loop
+ --loop-count <num> -- Number of loops to run, default 100.
+)";
+
+Format parse_format(const std::string& fmt) {
+ (void)fmt;
+
+#if TINT_BUILD_SPV_WRITER
+ if (fmt == "spirv") {
+ return Format::kSpirv;
+ }
+#endif // TINT_BUILD_SPV_WRITER
+
+#if TINT_BUILD_WGSL_WRITER
+ if (fmt == "wgsl") {
+ return Format::kWgsl;
+ }
+#endif // TINT_BUILD_WGSL_WRITER
+
+#if TINT_BUILD_MSL_WRITER
+ if (fmt == "msl") {
+ return Format::kMsl;
+ }
+#endif // TINT_BUILD_MSL_WRITER
+
+#if TINT_BUILD_HLSL_WRITER
+ if (fmt == "hlsl") {
+ return Format::kHlsl;
+ }
+#endif // TINT_BUILD_HLSL_WRITER
+
+#if TINT_BUILD_GLSL_WRITER
+ if (fmt == "glsl") {
+ return Format::kGlsl;
+ }
+#endif // TINT_BUILD_GLSL_WRITER
+
+ if (fmt == "none") {
+ return Format::kNone;
+ }
+
+ return Format::kUnknown;
+}
+
+bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
+ for (size_t i = 1; i < args.size(); ++i) {
+ const std::string& arg = args[i];
+ if (arg == "--format") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for --format argument." << std::endl;
+ return false;
+ }
+ opts->format = parse_format(args[i]);
+
+ if (opts->format == Format::kUnknown) {
+ std::cerr << "Unknown output format: " << args[i] << std::endl;
+ return false;
+ }
+ } else if (arg == "-h" || arg == "--help") {
+ opts->show_help = true;
+ } else if (arg == "--loop") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for --loop argument." << std::endl;
+ return false;
+ }
+ if (args[i] == "load") {
+ opts->loop = Looper::kLoad;
+ } else if (args[i] == "ir-gen") {
+ opts->loop = Looper::kIRGenerate;
+ } else if (args[i] == "writer") {
+ opts->loop = Looper::kWriter;
+ } else {
+ std::cerr << "Invalid loop value" << std::endl;
+ return false;
+ }
+ } else if (arg == "--loop-count") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for --loop-count argument." << std::endl;
+ return false;
+ }
+ int32_t val = atoi(args[i].c_str());
+ if (val <= 0) {
+ std::cerr << "Loop count must be greater then 0" << std::endl;
+ return false;
+ }
+ opts->loop_count = static_cast<uint32_t>(val);
+ } else if (!arg.empty()) {
+ if (arg[0] == '-') {
+ std::cerr << "Unrecognized option: " << arg << std::endl;
+ return false;
+ }
+ if (!opts->input_filename.empty()) {
+ std::cerr << "More than one input file specified: '" << opts->input_filename
+ << "' and '" << arg << "'" << std::endl;
+ return false;
+ }
+ opts->input_filename = arg;
+ }
+ }
+ return true;
+}
+
+/// Generate SPIR-V code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateSpirv(const tint::Program* program) {
+#if TINT_BUILD_SPV_WRITER
+ tint::writer::spirv::Options gen_options;
+ gen_options.external_texture_options.bindings_map =
+ tint::cmd::GenerateExternalTextureBindings(program);
+ auto result = tint::writer::spirv::Generate(program, gen_options);
+ if (!result.success) {
+ tint::cmd::PrintWGSL(std::cerr, *program);
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+ return true;
+#else
+ (void)program;
+ std::cerr << "SPIR-V writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_SPV_WRITER
+}
+
+/// Generate WGSL code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateWgsl(const tint::Program* program) {
+#if TINT_BUILD_WGSL_WRITER
+ tint::writer::wgsl::Options gen_options;
+ auto result = tint::writer::wgsl::Generate(program, gen_options);
+ if (!result.success) {
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+
+ return true;
+#else
+ (void)program;
+ std::cerr << "WGSL writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_WGSL_WRITER
+}
+
+/// Generate MSL code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateMsl(const tint::Program* program) {
+#if TINT_BUILD_MSL_WRITER
+ // Remap resource numbers to a flat namespace.
+ // TODO(crbug.com/tint/1501): Do this via Options::BindingMap.
+ const tint::Program* input_program = program;
+ auto flattened = tint::writer::FlattenBindings(program);
+ if (flattened) {
+ input_program = &*flattened;
+ }
+
+ tint::writer::msl::Options gen_options;
+ gen_options.external_texture_options.bindings_map =
+ tint::cmd::GenerateExternalTextureBindings(input_program);
+ gen_options.array_length_from_uniform.ubo_binding = tint::writer::BindingPoint{0, 30};
+ gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(
+ tint::writer::BindingPoint{0, 0}, 0);
+ gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(
+ tint::writer::BindingPoint{0, 1}, 1);
+ auto result = tint::writer::msl::Generate(input_program, gen_options);
+ if (!result.success) {
+ tint::cmd::PrintWGSL(std::cerr, *program);
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+
+ return true;
+#else
+ (void)program;
+ std::cerr << "MSL writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_MSL_WRITER
+}
+
+/// Generate HLSL code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateHlsl(const tint::Program* program) {
+#if TINT_BUILD_HLSL_WRITER
+ tint::writer::hlsl::Options gen_options;
+ gen_options.external_texture_options.bindings_map =
+ tint::cmd::GenerateExternalTextureBindings(program);
+ auto result = tint::writer::hlsl::Generate(program, gen_options);
+ if (!result.success) {
+ tint::cmd::PrintWGSL(std::cerr, *program);
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+
+ return true;
+#else
+ (void)program;
+ std::cerr << "HLSL writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_HLSL_WRITER
+}
+
+/// Generate GLSL code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateGlsl(const tint::Program* program) {
+#if TINT_BUILD_GLSL_WRITER
+ tint::writer::glsl::Options gen_options;
+ gen_options.external_texture_options.bindings_map =
+ tint::cmd::GenerateExternalTextureBindings(program);
+ auto result = tint::writer::glsl::Generate(program, gen_options, "");
+ if (!result.success) {
+ tint::cmd::PrintWGSL(std::cerr, *program);
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+
+ return true;
+
+#else
+ (void)program;
+ std::cerr << "GLSL writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_GLSL_WRITER
+}
+
+} // namespace
+
+int main(int argc, const char** argv) {
+ std::vector<std::string> args(argv, argv + argc);
+ Options options;
+
+ tint::SetInternalCompilerErrorReporter(&tint::cmd::TintInternalCompilerErrorReporter);
+
+#if TINT_BUILD_WGSL_WRITER
+ tint::Program::printer = [](const tint::Program* program) {
+ auto result = tint::writer::wgsl::Generate(program, {});
+ if (!result.error.empty()) {
+ return "error: " + result.error;
+ }
+ return result.wgsl;
+ };
+#endif // TINT_BUILD_WGSL_WRITER
+
+ if (!ParseArgs(args, &options)) {
+ std::cerr << "Failed to parse arguments." << std::endl;
+ return 1;
+ }
+
+ if (options.show_help) {
+ std::cout << kUsage << std::endl;
+ return 0;
+ }
+
+ // Implement output format defaults.
+ if (options.format == Format::kUnknown) {
+ options.format = Format::kSpirv;
+ }
+
+ auto diag_printer = tint::diag::Printer::create(stderr, true);
+ tint::diag::Formatter diag_formatter;
+
+ std::unique_ptr<tint::Program> program;
+ std::unique_ptr<tint::Source::File> source_file;
+
+ if (options.loop == Looper::kLoad) {
+ if (options.input_filename.size() > 5 &&
+ options.input_filename.substr(options.input_filename.size() - 5) == ".wgsl") {
+#if TINT_BUILD_WGSL_READER
+ std::vector<uint8_t> data;
+ if (!tint::cmd::ReadFile<uint8_t>(options.input_filename, &data)) {
+ exit(1);
+ }
+ source_file = std::make_unique<tint::Source::File>(
+ options.input_filename, std::string(data.begin(), data.end()));
+
+ uint32_t loop_count = options.loop_count;
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ program =
+ std::make_unique<tint::Program>(tint::reader::wgsl::Parse(source_file.get()));
+ }
+#else
+ std::cerr << "Tint not built with the WGSL reader enabled" << std::endl;
+ exit(1);
+#endif // TINT_BUILD_WGSL_READER
+ } else {
+#if TINT_BUILD_SPV_READER
+ std::vector<uint32_t> data;
+ if (!tint::cmd::ReadFile<uint32_t>(options.input_filename, &data)) {
+ exit(1);
+ }
+
+ uint32_t loop_count = options.loop_count;
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ program = std::make_unique<tint::Program>(tint::reader::spirv::Parse(data, {}));
+ }
+#else
+ std::cerr << "Tint not built with the SPIR-V reader enabled" << std::endl;
+ exit(1);
+#endif // TINT_BUILD_SPV_READER
+ }
+ }
+
+ // Load the program that will actually be used
+ {
+ tint::cmd::LoadProgramOptions opts;
+ opts.filename = options.input_filename;
+
+ auto info = tint::cmd::LoadProgramInfo(opts);
+ program = std::move(info.program);
+ source_file = std::move(info.source_file);
+ }
+#if TINT_BUILD_IR
+ {
+ uint32_t loop_count = 1;
+ if (options.loop == Looper::kIRGenerate) {
+ loop_count = options.loop_count;
+ }
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ auto result = tint::ir::Module::FromProgram(program.get());
+ if (!result) {
+ std::cerr << "Failed to build IR from program: " << result.Failure() << std::endl;
+ }
+ }
+ }
+#endif // TINT_BUILD_IR
+
+ bool success = false;
+ {
+ uint32_t loop_count = 1;
+ if (options.loop == Looper::kWriter) {
+ loop_count = options.loop_count;
+ }
+
+ switch (options.format) {
+ case Format::kSpirv:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateSpirv(program.get());
+ }
+ break;
+ case Format::kWgsl:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateWgsl(program.get());
+ }
+ break;
+ case Format::kMsl:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateMsl(program.get());
+ }
+ break;
+ case Format::kHlsl:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateHlsl(program.get());
+ }
+ break;
+ case Format::kGlsl:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateGlsl(program.get());
+ }
+ break;
+ case Format::kNone:
+ break;
+ default:
+ std::cerr << "Unknown output format specified" << std::endl;
+ return 1;
+ }
+ }
+ if (!success) {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/tint/ir/binary.h b/src/tint/ir/binary.h
index e39758f..400e381 100644
--- a/src/tint/ir/binary.h
+++ b/src/tint/ir/binary.h
@@ -16,8 +16,6 @@
#define SRC_TINT_IR_BINARY_H_
#include "src/tint/ir/instruction.h"
-#include "src/tint/symbol_table.h"
-#include "src/tint/type/type.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
diff --git a/src/tint/ir/binary_test.cc b/src/tint/ir/binary_test.cc
index 31972ce..6be82f9 100644
--- a/src/tint/ir/binary_test.cc
+++ b/src/tint/ir/binary_test.cc
@@ -26,15 +26,15 @@
TEST_F(IR_InstructionTest, CreateAnd) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.And(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i),
b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kAnd);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
ASSERT_NE(instr->Result()->Type(), nullptr);
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -54,14 +54,14 @@
TEST_F(IR_InstructionTest, CreateOr) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.Or(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i),
b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kOr);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -81,14 +81,14 @@
TEST_F(IR_InstructionTest, CreateXor) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.Xor(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i),
b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kXor);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -108,14 +108,14 @@
TEST_F(IR_InstructionTest, CreateLogicalAnd) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.LogicalAnd(b.builder.ir.types.Get<type::Bool>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kLogicalAnd);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -135,14 +135,14 @@
TEST_F(IR_InstructionTest, CreateLogicalOr) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.LogicalOr(b.builder.ir.types.Get<type::Bool>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kLogicalOr);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -162,14 +162,14 @@
TEST_F(IR_InstructionTest, CreateEqual) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.Equal(b.builder.ir.types.Get<type::Bool>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kEqual);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -189,14 +189,14 @@
TEST_F(IR_InstructionTest, CreateNotEqual) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.NotEqual(b.builder.ir.types.Get<type::Bool>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kNotEqual);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -216,14 +216,14 @@
TEST_F(IR_InstructionTest, CreateLessThan) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.LessThan(b.builder.ir.types.Get<type::Bool>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kLessThan);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -243,14 +243,14 @@
TEST_F(IR_InstructionTest, CreateGreaterThan) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.GreaterThan(b.builder.ir.types.Get<type::Bool>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kGreaterThan);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -270,14 +270,14 @@
TEST_F(IR_InstructionTest, CreateLessThanEqual) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.LessThanEqual(b.builder.ir.types.Get<type::Bool>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kLessThanEqual);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -297,14 +297,14 @@
TEST_F(IR_InstructionTest, CreateGreaterThanEqual) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.GreaterThanEqual(
b.builder.ir.types.Get<type::Bool>(), b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kGreaterThanEqual);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -324,14 +324,14 @@
TEST_F(IR_InstructionTest, CreateShiftLeft) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.ShiftLeft(b.builder.ir.types.Get<type::I32>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kShiftLeft);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -351,14 +351,14 @@
TEST_F(IR_InstructionTest, CreateShiftRight) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.ShiftRight(b.builder.ir.types.Get<type::I32>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kShiftRight);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -378,14 +378,14 @@
TEST_F(IR_InstructionTest, CreateAdd) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.Add(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i),
b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kAdd);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -405,14 +405,14 @@
TEST_F(IR_InstructionTest, CreateSubtract) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.Subtract(b.builder.ir.types.Get<type::I32>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kSubtract);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -432,14 +432,14 @@
TEST_F(IR_InstructionTest, CreateMultiply) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.Multiply(b.builder.ir.types.Get<type::I32>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kMultiply);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -459,14 +459,14 @@
TEST_F(IR_InstructionTest, CreateDivide) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.Divide(b.builder.ir.types.Get<type::I32>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kDivide);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -486,14 +486,14 @@
TEST_F(IR_InstructionTest, CreateModulo) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.Modulo(b.builder.ir.types.Get<type::I32>(),
b.builder.Constant(4_i), b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kModulo);
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>()->value;
@@ -513,7 +513,7 @@
TEST_F(IR_InstructionTest, Binary_Usage) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.And(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i),
b.builder.Constant(2_i));
@@ -537,7 +537,7 @@
auto val = b.builder.Constant(4_i);
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr = b.builder.And(b.builder.ir.types.Get<type::I32>(), val, val);
EXPECT_EQ(instr->GetKind(), Binary::Kind::kAnd);
diff --git a/src/tint/ir/bitcast.h b/src/tint/ir/bitcast.h
index 7dcb495..c7d9cb8 100644
--- a/src/tint/ir/bitcast.h
+++ b/src/tint/ir/bitcast.h
@@ -16,8 +16,6 @@
#define SRC_TINT_IR_BITCAST_H_
#include "src/tint/ir/instruction.h"
-#include "src/tint/symbol_table.h"
-#include "src/tint/type/type.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
diff --git a/src/tint/ir/bitcast_test.cc b/src/tint/ir/bitcast_test.cc
index b46c334..2e6ca9d 100644
--- a/src/tint/ir/bitcast_test.cc
+++ b/src/tint/ir/bitcast_test.cc
@@ -26,12 +26,12 @@
TEST_F(IR_InstructionTest, Bitcast) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr =
b.builder.Bitcast(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
- ASSERT_TRUE(instr->Result()->Is<Temp>());
- EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_NE(instr->Result()->Type(), nullptr);
ASSERT_TRUE(instr->Val()->Is<Constant>());
@@ -47,7 +47,7 @@
TEST_F(IR_InstructionTest, Bitcast_Usage) {
auto& b = CreateEmptyBuilder();
- b.builder.next_temp_id = Temp::Id(42);
+ b.builder.next_runtime_id = Runtime::Id(42);
const auto* instr =
b.builder.Bitcast(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index a3d2a31..13ef345 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -93,12 +93,12 @@
to->inbound_branches.Push(from);
}
-Temp::Id Builder::AllocateTempId() {
- return next_temp_id++;
+Runtime::Id Builder::AllocateRuntimeId() {
+ return next_runtime_id++;
}
Binary* Builder::CreateBinary(Binary::Kind kind, const type::Type* type, Value* lhs, Value* rhs) {
- return ir.instructions.Create<ir::Binary>(kind, Temp(type), lhs, rhs);
+ return ir.instructions.Create<ir::Binary>(kind, Runtime(type), lhs, rhs);
}
Binary* Builder::And(const type::Type* type, Value* lhs, Value* rhs) {
@@ -173,30 +173,62 @@
return CreateBinary(Binary::Kind::kModulo, type, lhs, rhs);
}
+Unary* Builder::CreateUnary(Unary::Kind kind, const type::Type* type, Value* val) {
+ return ir.instructions.Create<ir::Unary>(kind, Runtime(type), val);
+}
+
+Unary* Builder::AddressOf(const type::Type* type, Value* val) {
+ return CreateUnary(Unary::Kind::kAddressOf, type, val);
+}
+
+Unary* Builder::Complement(const type::Type* type, Value* val) {
+ return CreateUnary(Unary::Kind::kComplement, type, val);
+}
+
+Unary* Builder::Indirection(const type::Type* type, Value* val) {
+ return CreateUnary(Unary::Kind::kIndirection, type, val);
+}
+
+Unary* Builder::Negation(const type::Type* type, Value* val) {
+ return CreateUnary(Unary::Kind::kNegation, type, val);
+}
+
+Unary* Builder::Not(const type::Type* type, Value* val) {
+ return CreateUnary(Unary::Kind::kNot, type, val);
+}
+
ir::Bitcast* Builder::Bitcast(const type::Type* type, Value* val) {
- return ir.instructions.Create<ir::Bitcast>(Temp(type), val);
+ return ir.instructions.Create<ir::Bitcast>(Runtime(type), val);
+}
+
+ir::Discard* Builder::Discard() {
+ return ir.instructions.Create<ir::Discard>(Runtime(ir.types.Get<type::Void>()));
}
ir::UserCall* Builder::UserCall(const type::Type* type,
Symbol name,
utils::VectorRef<Value*> args) {
- return ir.instructions.Create<ir::UserCall>(Temp(type), name, std::move(args));
+ return ir.instructions.Create<ir::UserCall>(Runtime(type), name, std::move(args));
}
ir::Convert* Builder::Convert(const type::Type* to,
const type::Type* from,
utils::VectorRef<Value*> args) {
- return ir.instructions.Create<ir::Convert>(Temp(to), from, std::move(args));
+ return ir.instructions.Create<ir::Convert>(Runtime(to), from, std::move(args));
}
ir::Construct* Builder::Construct(const type::Type* to, utils::VectorRef<Value*> args) {
- return ir.instructions.Create<ir::Construct>(Temp(to), std::move(args));
+ return ir.instructions.Create<ir::Construct>(Runtime(to), std::move(args));
}
ir::Builtin* Builder::Builtin(const type::Type* type,
builtin::Function func,
utils::VectorRef<Value*> args) {
- return ir.instructions.Create<ir::Builtin>(Temp(type), func, args);
+ return ir.instructions.Create<ir::Builtin>(Runtime(type), func, args);
+}
+
+ir::Store* Builder::Store(Value* to, Value* from) {
+ return ir.instructions.Create<ir::Store>(to, from);
}
} // namespace tint::ir
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index b090bdf..3fc78d2 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -24,13 +24,16 @@
#include "src/tint/ir/constant.h"
#include "src/tint/ir/construct.h"
#include "src/tint/ir/convert.h"
+#include "src/tint/ir/discard.h"
#include "src/tint/ir/function.h"
#include "src/tint/ir/if.h"
#include "src/tint/ir/loop.h"
#include "src/tint/ir/module.h"
+#include "src/tint/ir/runtime.h"
+#include "src/tint/ir/store.h"
#include "src/tint/ir/switch.h"
-#include "src/tint/ir/temp.h"
#include "src/tint/ir/terminator.h"
+#include "src/tint/ir/unary.h"
#include "src/tint/ir/user_call.h"
#include "src/tint/ir/value.h"
#include "src/tint/type/bool.h"
@@ -38,6 +41,7 @@
#include "src/tint/type/f32.h"
#include "src/tint/type/i32.h"
#include "src/tint/type/u32.h"
+#include "src/tint/type/void.h"
namespace tint::ir {
@@ -137,11 +141,11 @@
return Constant(create<constant::Scalar<bool>>(ir.types.Get<type::Bool>(), v));
}
- /// Creates a new Temporary
+ /// Creates a new Runtime value
/// @param type the type of the temporary
/// @returns the new temporary
- ir::Temp* Temp(const type::Type* type) {
- return ir.values.Create<ir::Temp>(type, AllocateTempId());
+ ir::Runtime* Runtime(const type::Type* type) {
+ return ir.values.Create<ir::Runtime>(type, AllocateRuntimeId());
}
/// Creates an op for `lhs kind rhs`
@@ -278,12 +282,53 @@
/// @returns the operation
Binary* Modulo(const type::Type* type, Value* lhs, Value* rhs);
+ /// Creates an op for `kind val`
+ /// @param kind the kind of operation
+ /// @param type the result type of the binary expression
+ /// @param val the value of the operation
+ /// @returns the operation
+ Unary* CreateUnary(Unary::Kind kind, const type::Type* type, Value* val);
+
+ /// Creates an AddressOf operation
+ /// @param type the result type of the expression
+ /// @param val the value
+ /// @returns the operation
+ Unary* AddressOf(const type::Type* type, Value* val);
+
+ /// Creates a Complement operation
+ /// @param type the result type of the expression
+ /// @param val the value
+ /// @returns the operation
+ Unary* Complement(const type::Type* type, Value* val);
+
+ /// Creates an Indirection operation
+ /// @param type the result type of the expression
+ /// @param val the value
+ /// @returns the operation
+ Unary* Indirection(const type::Type* type, Value* val);
+
+ /// Creates a Negation operation
+ /// @param type the result type of the expression
+ /// @param val the value
+ /// @returns the operation
+ Unary* Negation(const type::Type* type, Value* val);
+
+ /// Creates a Not operation
+ /// @param type the result type of the expression
+ /// @param val the value
+ /// @returns the operation
+ Unary* Not(const type::Type* type, Value* val);
+
/// Creates a bitcast instruction
/// @param type the result type of the bitcast
/// @param val the value being bitcast
/// @returns the instruction
ir::Bitcast* Bitcast(const type::Type* type, Value* val);
+ /// Creates a discard instruction
+ /// @returns the instruction
+ ir::Discard* Discard();
+
/// Creates a user function call instruction
/// @param type the return type of the call
/// @param name the name of the function being called
@@ -315,14 +360,20 @@
builtin::Function func,
utils::VectorRef<Value*> args);
- /// @returns a unique temp id
- Temp::Id AllocateTempId();
+ /// Creates an store instruction
+ /// @param to the expression being stored too
+ /// @param from the expression being stored
+ /// @returns the instruction
+ ir::Store* Store(Value* to, Value* from);
+
+ /// @returns a unique runtime id
+ Runtime::Id AllocateRuntimeId();
/// The IR module.
Module ir;
/// The next temporary number to allocate
- Temp::Id next_temp_id = 1;
+ Runtime::Id next_runtime_id = 1;
};
} // namespace tint::ir
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index c6c6ed0..1f0f79a 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -17,6 +17,7 @@
#include <iostream>
#include "src/tint/ast/alias.h"
+#include "src/tint/ast/assignment_statement.h"
#include "src/tint/ast/binary_expression.h"
#include "src/tint/ast/bitcast_expression.h"
#include "src/tint/ast/block_statement.h"
@@ -25,8 +26,11 @@
#include "src/tint/ast/break_statement.h"
#include "src/tint/ast/call_expression.h"
#include "src/tint/ast/call_statement.h"
+#include "src/tint/ast/compound_assignment_statement.h"
+#include "src/tint/ast/const.h"
#include "src/tint/ast/const_assert.h"
#include "src/tint/ast/continue_statement.h"
+#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/float_literal_expression.h"
#include "src/tint/ast/for_loop_statement.h"
#include "src/tint/ast/function.h"
@@ -45,14 +49,17 @@
#include "src/tint/ast/struct_member_size_attribute.h"
#include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/templated_identifier.h"
+#include "src/tint/ast/unary_op_expression.h"
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/ast/while_statement.h"
#include "src/tint/ir/function.h"
#include "src/tint/ir/if.h"
#include "src/tint/ir/loop.h"
#include "src/tint/ir/module.h"
+#include "src/tint/ir/store.h"
#include "src/tint/ir/switch.h"
#include "src/tint/ir/terminator.h"
+#include "src/tint/ir/value.h"
#include "src/tint/program.h"
#include "src/tint/sem/builtin.h"
#include "src/tint/sem/call.h"
@@ -153,16 +160,14 @@
auto* sem = program_->Sem().Module();
for (auto* decl : sem->DependencyOrderedDeclarations()) {
- bool ok = tint::Switch(
+ tint::Switch(
decl, //
[&](const ast::Struct*) {
// Will be encoded into the `type::Struct` when used. We will then hoist all
// used structs up to module scope when converting IR.
- return true;
},
[&](const ast::Alias*) {
// Folded away and doesn't appear in the IR.
- return true;
},
// [&](const ast::Variable* var) {
// TODO(dsinclair): Implement
@@ -174,21 +179,19 @@
// },
[&](const ast::ConstAssert*) {
// Evaluated by the resolver, drop from the IR.
- return true;
},
[&](Default) {
add_error(decl->source, "unknown type: " + std::string(decl->TypeInfo().name));
- return true;
});
- if (!ok) {
- return utils::Failure;
- }
+ }
+ if (!diagnostics_.empty()) {
+ return utils::Failure;
}
return ResultType{std::move(builder.ir)};
}
-bool BuilderImpl::EmitFunction(const ast::Function* ast_func) {
+void BuilderImpl::EmitFunction(const ast::Function* ast_func) {
// The flow stack should have been emptied when the previous function finished building.
TINT_ASSERT(IR, flow_stack.IsEmpty());
@@ -207,9 +210,7 @@
FlowStackScope scope(this, ir_func);
current_flow_block = ir_func->start_target;
- if (!EmitStatements(ast_func->body->statements)) {
- return false;
- }
+ EmitStatements(ast_func->body->statements);
// TODO(dsinclair): Store return type and attributes
// TODO(dsinclair): Store parameters
@@ -223,15 +224,11 @@
TINT_ASSERT(IR, flow_stack.IsEmpty());
current_flow_block = nullptr;
current_function_ = nullptr;
-
- return true;
}
-bool BuilderImpl::EmitStatements(utils::VectorRef<const ast::Statement*> stmts) {
+void BuilderImpl::EmitStatements(utils::VectorRef<const ast::Statement*> stmts) {
for (auto* s : stmts) {
- if (!EmitStatement(s)) {
- return false;
- }
+ EmitStatement(s);
// If the current flow block has a branch target then the rest of the statements in this
// block are dead code. Skip them.
@@ -239,59 +236,141 @@
break;
}
}
- return true;
}
-bool BuilderImpl::EmitStatement(const ast::Statement* stmt) {
- return tint::Switch(
- stmt,
- // [&](const ast::AssignmentStatement* a) {
- // TODO(dsinclair): Implement
- // },
- [&](const ast::BlockStatement* b) { return EmitBlock(b); },
- [&](const ast::BreakStatement* b) { return EmitBreak(b); },
- [&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
- [&](const ast::CallStatement* c) { return EmitCall(c); },
- // [&](const ast::CompoundAssignmentStatement* c) {
- // TODO(dsinclair): Implement
- // },
- [&](const ast::ContinueStatement* c) { return EmitContinue(c); },
- // [&](const ast::DiscardStatement* d) {
- // TODO(dsinclair): Implement
- // },
- [&](const ast::IfStatement* i) { return EmitIf(i); },
- [&](const ast::LoopStatement* l) { return EmitLoop(l); },
- [&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
- [&](const ast::WhileStatement* l) { return EmitWhile(l); },
- [&](const ast::ReturnStatement* r) { return EmitReturn(r); },
- [&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
- [&](const ast::VariableDeclStatement* v) { return EmitVariable(v->variable); },
+void BuilderImpl::EmitStatement(const ast::Statement* stmt) {
+ tint::Switch(
+ stmt, //
+ [&](const ast::AssignmentStatement* a) { EmitAssignment(a); },
+ [&](const ast::BlockStatement* b) { EmitBlock(b); },
+ [&](const ast::BreakStatement* b) { EmitBreak(b); },
+ [&](const ast::BreakIfStatement* b) { EmitBreakIf(b); },
+ [&](const ast::CallStatement* c) { EmitCall(c); },
+ [&](const ast::CompoundAssignmentStatement* c) { EmitCompoundAssignment(c); },
+ [&](const ast::ContinueStatement* c) { EmitContinue(c); },
+ [&](const ast::DiscardStatement* d) { EmitDiscard(d); },
+ [&](const ast::IfStatement* i) { EmitIf(i); },
+ [&](const ast::LoopStatement* l) { EmitLoop(l); },
+ [&](const ast::ForLoopStatement* l) { EmitForLoop(l); },
+ [&](const ast::WhileStatement* l) { EmitWhile(l); },
+ [&](const ast::ReturnStatement* r) { EmitReturn(r); },
+ [&](const ast::SwitchStatement* s) { EmitSwitch(s); },
+ [&](const ast::VariableDeclStatement* v) { EmitVariable(v->variable); },
[&](const ast::ConstAssert*) {
- return true; // Not emitted
+ // Not emitted
},
[&](Default) {
add_error(stmt->source,
"unknown statement type: " + std::string(stmt->TypeInfo().name));
- // TODO(dsinclair): This should return `false`, switch back when all
- // the cases are handled.
- return true;
});
}
-bool BuilderImpl::EmitBlock(const ast::BlockStatement* block) {
+void BuilderImpl::EmitAssignment(const ast::AssignmentStatement* stmt) {
+ auto lhs = EmitExpression(stmt->lhs);
+ if (!lhs) {
+ return;
+ }
+
+ auto rhs = EmitExpression(stmt->rhs);
+ if (!rhs) {
+ return;
+ }
+ auto store = builder.Store(lhs.Get(), rhs.Get());
+ current_flow_block->instructions.Push(store);
+}
+
+void BuilderImpl::EmitCompoundAssignment(const ast::CompoundAssignmentStatement* stmt) {
+ auto lhs = EmitExpression(stmt->lhs);
+ if (!lhs) {
+ return;
+ }
+
+ auto rhs = EmitExpression(stmt->rhs);
+ if (!rhs) {
+ return;
+ }
+
+ auto* ty = lhs.Get()->Type();
+ Binary* instr = nullptr;
+ switch (stmt->op) {
+ case ast::BinaryOp::kAnd:
+ instr = builder.And(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kOr:
+ instr = builder.Or(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kXor:
+ instr = builder.Xor(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kLogicalAnd:
+ instr = builder.LogicalAnd(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kLogicalOr:
+ instr = builder.LogicalOr(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kEqual:
+ instr = builder.Equal(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kNotEqual:
+ instr = builder.NotEqual(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kLessThan:
+ instr = builder.LessThan(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kGreaterThan:
+ instr = builder.GreaterThan(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kLessThanEqual:
+ instr = builder.LessThanEqual(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kGreaterThanEqual:
+ instr = builder.GreaterThanEqual(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kShiftLeft:
+ instr = builder.ShiftLeft(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kShiftRight:
+ instr = builder.ShiftRight(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kAdd:
+ instr = builder.Add(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kSubtract:
+ instr = builder.Subtract(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kMultiply:
+ instr = builder.Multiply(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kDivide:
+ instr = builder.Divide(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kModulo:
+ instr = builder.Modulo(ty, lhs.Get(), rhs.Get());
+ break;
+ case ast::BinaryOp::kNone:
+ TINT_ICE(IR, diagnostics_) << "missing binary operand type";
+ return;
+ }
+ current_flow_block->instructions.Push(instr);
+
+ auto store = builder.Store(lhs.Get(), instr->Result());
+ current_flow_block->instructions.Push(store);
+}
+
+void BuilderImpl::EmitBlock(const ast::BlockStatement* block) {
// Note, this doesn't need to emit a Block as the current block flow node should be
// sufficient as the blocks all get flattened. Each flow control node will inject the basic
// blocks it requires.
- return EmitStatements(block->statements);
+ EmitStatements(block->statements);
}
-bool BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
+void BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
auto* if_node = builder.CreateIf();
// Emit the if condition into the end of the preceding block
auto reg = EmitExpression(stmt->condition);
if (!reg) {
- return false;
+ return;
}
if_node->condition = reg.Get();
@@ -303,16 +382,16 @@
FlowStackScope scope(this, if_node);
current_flow_block = if_node->true_.target->As<Block>();
- if (!EmitStatement(stmt->body)) {
- return false;
- }
+ EmitStatement(stmt->body);
+
// If the true branch did not execute control flow, then go to the merge target
BranchToIfNeeded(if_node->merge.target);
current_flow_block = if_node->false_.target->As<Block>();
- if (stmt->else_statement && !EmitStatement(stmt->else_statement)) {
- return false;
+ if (stmt->else_statement) {
+ EmitStatement(stmt->else_statement);
}
+
// If the false branch did not execute control flow, then go to the merge target
BranchToIfNeeded(if_node->merge.target);
}
@@ -324,11 +403,9 @@
if (IsConnected(if_node->merge.target)) {
current_flow_block = if_node->merge.target->As<Block>();
}
-
- return true;
}
-bool BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
+void BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
auto* loop_node = builder.CreateLoop();
BranchTo(loop_node);
@@ -339,18 +416,14 @@
FlowStackScope scope(this, loop_node);
current_flow_block = loop_node->start.target->As<Block>();
- if (!EmitStatement(stmt->body)) {
- return false;
- }
+ EmitStatement(stmt->body);
// The current block didn't `break`, `return` or `continue`, go to the continuing block.
BranchToIfNeeded(loop_node->continuing.target);
current_flow_block = loop_node->continuing.target->As<Block>();
if (stmt->continuing) {
- if (!EmitStatement(stmt->continuing)) {
- return false;
- }
+ EmitStatement(stmt->continuing);
}
// Branch back to the start node if the continue target didn't branch out already
@@ -363,10 +436,9 @@
if (!IsConnected(loop_node->merge.target)) {
current_flow_block = nullptr;
}
- return true;
}
-bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
+void BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
auto* loop_node = builder.CreateLoop();
// Continue is always empty, just go back to the start
TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>());
@@ -385,7 +457,7 @@
// Emit the while condition into the start target of the loop
auto reg = EmitExpression(stmt->condition);
if (!reg) {
- return false;
+ return;
}
// Create an `if (cond) {} else {break;}` control flow
@@ -400,19 +472,16 @@
BranchTo(if_node);
current_flow_block = if_node->merge.target->As<Block>();
- if (!EmitStatement(stmt->body)) {
- return false;
- }
+ EmitStatement(stmt->body);
BranchToIfNeeded(loop_node->continuing.target);
}
// The while loop always has a path to the merge target as the break statement comes before
// anything inside the loop.
current_flow_block = loop_node->merge.target->As<Block>();
- return true;
}
-bool BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
+void BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
auto* loop_node = builder.CreateLoop();
TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>());
builder.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target,
@@ -420,9 +489,7 @@
if (stmt->initializer) {
// Emit the for initializer before branching to the loop
- if (!EmitStatement(stmt->initializer)) {
- return false;
- }
+ EmitStatement(stmt->initializer);
}
BranchTo(loop_node);
@@ -438,7 +505,7 @@
// Emit the condition into the target target of the loop
auto reg = EmitExpression(stmt->condition);
if (!reg) {
- return false;
+ return;
}
// Create an `if (cond) {} else {break;}` control flow
@@ -455,32 +522,26 @@
current_flow_block = if_node->merge.target->As<Block>();
}
- if (!EmitStatement(stmt->body)) {
- return false;
- }
-
+ EmitStatement(stmt->body);
BranchToIfNeeded(loop_node->continuing.target);
if (stmt->continuing) {
current_flow_block = loop_node->continuing.target->As<Block>();
- if (!EmitStatement(stmt->continuing)) {
- return false;
- }
+ EmitStatement(stmt->continuing);
}
}
// The while loop always has a path to the merge target as the break statement comes before
// anything inside the loop.
current_flow_block = loop_node->merge.target->As<Block>();
- return true;
}
-bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
+void BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
auto* switch_node = builder.CreateSwitch();
// Emit the condition into the preceding block
auto reg = EmitExpression(stmt->condition);
if (!reg) {
- return false;
+ return;
}
switch_node->condition = reg.Get();
@@ -503,9 +564,7 @@
}
current_flow_block = builder.CreateCase(switch_node, selectors);
- if (!EmitStatement(c->Body()->Declaration())) {
- return false;
- }
+ EmitStatement(c->Body()->Declaration());
BranchToIfNeeded(switch_node->merge.target);
}
}
@@ -514,25 +573,22 @@
if (IsConnected(switch_node->merge.target)) {
current_flow_block = switch_node->merge.target->As<Block>();
}
-
- return true;
}
-bool BuilderImpl::EmitReturn(const ast::ReturnStatement* stmt) {
+void BuilderImpl::EmitReturn(const ast::ReturnStatement* stmt) {
utils::Vector<Value*, 1> ret_value;
if (stmt->value) {
auto ret = EmitExpression(stmt->value);
if (!ret) {
- return false;
+ return;
}
ret_value.Push(ret.Get());
}
BranchTo(current_function_->end_target, std::move(ret_value));
- return true;
}
-bool BuilderImpl::EmitBreak(const ast::BreakStatement*) {
+void BuilderImpl::EmitBreak(const ast::BreakStatement*) {
auto* current_control = FindEnclosingControl(ControlFlags::kNone);
TINT_ASSERT(IR, current_control);
@@ -542,13 +598,10 @@
BranchTo(s->merge.target);
} else {
TINT_UNREACHABLE(IR, diagnostics_);
- return false;
}
-
- return true;
}
-bool BuilderImpl::EmitContinue(const ast::ContinueStatement*) {
+void BuilderImpl::EmitContinue(const ast::ContinueStatement*) {
auto* current_control = FindEnclosingControl(ControlFlags::kExcludeSwitch);
TINT_ASSERT(IR, current_control);
@@ -557,17 +610,24 @@
} else {
TINT_UNREACHABLE(IR, diagnostics_);
}
-
- return true;
}
-bool BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
+// Discard is being treated as an instruction. The semantics in WGSL is demote_to_helper, so the
+// code has to continue as before it just predicates writes. If WGSL grows some kind of terminating
+// discard that would probably make sense as a FlowNode but would then require figuring out the
+// multi-level exit that is triggered.
+void BuilderImpl::EmitDiscard(const ast::DiscardStatement*) {
+ auto* instr = builder.Discard();
+ current_flow_block->instructions.Push(instr);
+}
+
+void BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
auto* if_node = builder.CreateIf();
// Emit the break-if condition into the end of the preceding block
auto reg = EmitExpression(stmt->condition);
if (!reg) {
- return false;
+ return;
}
if_node->condition = reg.Get();
@@ -592,8 +652,6 @@
// The `break-if` has to be the last item in the continuing block. The false branch of the
// `break-if` will always take us back to the start of the loop.
BranchTo(loop->start.target);
-
- return true;
}
utils::Result<Value*> BuilderImpl::EmitExpression(const ast::Expression* expr) {
@@ -615,20 +673,15 @@
// [&](const ast::PhonyExpression*) {
// TODO(dsinclair): Implement. The call may have side effects so has to be made.
// },
- // [&](const ast::UnaryOpExpression* u) {
- // TODO(dsinclair): Implement
- // },
+ [&](const ast::UnaryOpExpression* u) { return EmitUnary(u); },
[&](Default) {
add_error(expr->source,
"unknown expression type: " + std::string(expr->TypeInfo().name));
- // TODO(dsinclair): This should return utils::Failure; Switch back
- // once all the above cases are handled.
- auto* v = builder.ir.types.Get<type::Void>();
- return builder.Temp(v);
+ return utils::Failure;
});
}
-bool BuilderImpl::EmitVariable(const ast::Variable* var) {
+void BuilderImpl::EmitVariable(const ast::Variable* var) {
return tint::Switch( //
var,
// [&](const ast::Var* var) {
@@ -641,20 +694,53 @@
add_error(var->source,
"found an `Override` variable. The SubstituteOverrides "
"transform must be run before converting to IR");
- return false;
},
- // [&](const ast::Const* c) {
- // TODO(dsinclair): Implement
- // },
+ [&](const ast::Const*) {
+ // Skip. This should be handled by const-eval already, so the const will be a
+ // `constant::` value at the usage sites. Can just ignore the `const` variable as it
+ // should never be used.
+ //
+ // TODO(dsinclair): Probably want to store the const variable somewhere and then in
+ // identifier expression log an error if we ever see a const identifier. Add this when
+ // identifiers and variables are supported.
+ },
[&](Default) {
add_error(var->source, "unknown variable: " + std::string(var->TypeInfo().name));
-
- // TODO(dsinclair): This should return `false`, switch back when all
- // the cases are handled.
- return true;
});
}
+utils::Result<Value*> BuilderImpl::EmitUnary(const ast::UnaryOpExpression* expr) {
+ auto val = EmitExpression(expr->expr);
+ if (!val) {
+ return utils::Failure;
+ }
+
+ auto* sem = program_->Sem().Get(expr);
+ auto* ty = sem->Type()->Clone(clone_ctx_.type_ctx);
+
+ Unary* instr = nullptr;
+ switch (expr->op) {
+ case ast::UnaryOp::kAddressOf:
+ instr = builder.AddressOf(ty, val.Get());
+ break;
+ case ast::UnaryOp::kComplement:
+ instr = builder.Complement(ty, val.Get());
+ break;
+ case ast::UnaryOp::kIndirection:
+ instr = builder.Indirection(ty, val.Get());
+ break;
+ case ast::UnaryOp::kNegation:
+ instr = builder.Negation(ty, val.Get());
+ break;
+ case ast::UnaryOp::kNot:
+ instr = builder.Not(ty, val.Get());
+ break;
+ }
+
+ current_flow_block->instructions.Push(instr);
+ return instr->Result();
+}
+
utils::Result<Value*> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
auto lhs = EmitExpression(expr->lhs);
if (!lhs) {
@@ -748,8 +834,8 @@
return instr->Result();
}
-utils::Result<Value*> BuilderImpl::EmitCall(const ast::CallStatement* stmt) {
- return EmitCall(stmt->expr);
+void BuilderImpl::EmitCall(const ast::CallStatement* stmt) {
+ (void)EmitCall(stmt->expr);
}
utils::Result<Value*> BuilderImpl::EmitCall(const ast::CallExpression* expr) {
@@ -830,17 +916,14 @@
return builder.Constant(cv);
}
-bool BuilderImpl::EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs) {
+void BuilderImpl::EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs) {
for (auto* attr : attrs) {
- if (!EmitAttribute(attr)) {
- return false;
- }
+ EmitAttribute(attr);
}
- return true;
}
-bool BuilderImpl::EmitAttribute(const ast::Attribute* attr) {
- return tint::Switch( //
+void BuilderImpl::EmitAttribute(const ast::Attribute* attr) {
+ tint::Switch( //
attr,
// [&](const ast::WorkgroupAttribute* wg) {
// TODO(dsinclair): Implement
@@ -873,17 +956,14 @@
add_error(attr->source,
"found an `Id` attribute. The SubstituteOverrides transform "
"must be run before converting to IR");
- return false;
},
[&](const ast::StructMemberSizeAttribute*) {
TINT_ICE(IR, diagnostics_)
<< "StructMemberSizeAttribute encountered during IR conversion";
- return false;
},
[&](const ast::StructMemberAlignAttribute*) {
TINT_ICE(IR, diagnostics_)
<< "StructMemberAlignAttribute encountered during IR conversion";
- return false;
},
// [&](const ast::StrideAttribute* s) {
// TODO(dsinclair): Implement
@@ -893,7 +973,6 @@
// },
[&](Default) {
add_error(attr->source, "unknown attribute: " + std::string(attr->TypeInfo().name));
- return false;
});
}
diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h
index 2e38ef7..7b18492 100644
--- a/src/tint/ir/builder_impl.h
+++ b/src/tint/ir/builder_impl.h
@@ -34,6 +34,7 @@
} // namespace tint
namespace tint::ast {
class Attribute;
+class AssignmentStatement;
class BinaryExpression;
class BitcastExpression;
class BlockStatement;
@@ -41,7 +42,9 @@
class BreakStatement;
class CallExpression;
class CallStatement;
+class CompoundAssignmentStatement;
class ContinueStatement;
+class DiscardStatement;
class Expression;
class ForLoopStatement;
class Function;
@@ -52,6 +55,7 @@
class ReturnStatement;
class Statement;
class SwitchStatement;
+class UnaryOpExpression;
class WhileStatement;
class Variable;
} // namespace tint::ast
@@ -82,73 +86,71 @@
/// @returns true on success, false otherwise
utils::Result<Module> Build();
- /// @returns the error
- std::string error() const { return diagnostics_.str(); }
+ /// @returns the diagnostics
+ diag::List Diagnostics() const { return diagnostics_; }
/// Emits a function to the IR.
/// @param func the function to emit
- /// @returns true if successful, false otherwise
- bool EmitFunction(const ast::Function* func);
+ void EmitFunction(const ast::Function* func);
/// Emits a set of statements to the IR.
/// @param stmts the statements to emit
- /// @returns true if successful, false otherwise.
- bool EmitStatements(utils::VectorRef<const ast::Statement*> stmts);
+ void EmitStatements(utils::VectorRef<const ast::Statement*> stmts);
/// Emits a statement to the IR
/// @param stmt the statment to emit
- /// @returns true on success, false otherwise.
- bool EmitStatement(const ast::Statement* stmt);
+ void EmitStatement(const ast::Statement* stmt);
/// Emits a block statement to the IR.
/// @param block the block to emit
- /// @returns true if successful, false otherwise.
- bool EmitBlock(const ast::BlockStatement* block);
+ void EmitBlock(const ast::BlockStatement* block);
/// Emits an if control node to the IR.
/// @param stmt the if statement
- /// @returns true if successful, false otherwise.
- bool EmitIf(const ast::IfStatement* stmt);
+ void EmitIf(const ast::IfStatement* stmt);
/// Emits a return node to the IR.
/// @param stmt the return AST statement
- /// @returns true if successful, false otherwise.
- bool EmitReturn(const ast::ReturnStatement* stmt);
+ void EmitReturn(const ast::ReturnStatement* stmt);
/// Emits a loop control node to the IR.
/// @param stmt the loop statement
- /// @returns true if successful, false otherwise.
- bool EmitLoop(const ast::LoopStatement* stmt);
+ void EmitLoop(const ast::LoopStatement* stmt);
/// Emits a loop control node to the IR.
/// @param stmt the while statement
- /// @returns true if successful, false otherwise.
- bool EmitWhile(const ast::WhileStatement* stmt);
+ void EmitWhile(const ast::WhileStatement* stmt);
/// Emits a loop control node to the IR.
/// @param stmt the for loop statement
- /// @returns true if successful, false otherwise.
- bool EmitForLoop(const ast::ForLoopStatement* stmt);
+ void EmitForLoop(const ast::ForLoopStatement* stmt);
/// Emits a switch statement
/// @param stmt the switch statement
- /// @returns true if successful, false otherwise.
- bool EmitSwitch(const ast::SwitchStatement* stmt);
+ void EmitSwitch(const ast::SwitchStatement* stmt);
/// Emits a break statement
/// @param stmt the break statement
- /// @returns true if successful, false otherwise.
- bool EmitBreak(const ast::BreakStatement* stmt);
+ void EmitBreak(const ast::BreakStatement* stmt);
/// Emits a continue statement
/// @param stmt the continue statement
- /// @returns true if successful, false otherwise.
- bool EmitContinue(const ast::ContinueStatement* stmt);
+ void EmitContinue(const ast::ContinueStatement* stmt);
+
+ /// Emits a discard statement
+ void EmitDiscard(const ast::DiscardStatement*);
/// Emits a break-if statement
/// @param stmt the break-if statement
- /// @returns true if successful, false otherwise.
- bool EmitBreakIf(const ast::BreakIfStatement* stmt);
+ void EmitBreakIf(const ast::BreakIfStatement* stmt);
+
+ /// Emits an assignment statement
+ /// @param stmt the statement
+ void EmitAssignment(const ast::AssignmentStatement* stmt);
+
+ /// Emits a compound assignment statement
+ /// @param stmt the statement
+ void EmitCompoundAssignment(const ast::CompoundAssignmentStatement* stmt);
/// Emits an expression
/// @param expr the expression to emit
@@ -157,8 +159,12 @@
/// Emits a variable
/// @param var the variable to emit
- /// @returns true if successful, false otherwise
- bool EmitVariable(const ast::Variable* var);
+ void EmitVariable(const ast::Variable* var);
+
+ /// Emits a Unary expression
+ /// @param expr the unary expression
+ /// @returns the value storing the result if successful, utils::Failure otherwise
+ utils::Result<Value*> EmitUnary(const ast::UnaryOpExpression* expr);
/// Emits a binary expression
/// @param expr the binary expression
@@ -172,8 +178,7 @@
/// Emits a call expression
/// @param stmt the call statement
- /// @returns the value storing the result if successful, utils::Failure otherwise
- utils::Result<Value*> EmitCall(const ast::CallStatement* stmt);
+ void EmitCall(const ast::CallStatement* stmt);
/// Emits a call expression
/// @param expr the call expression
@@ -187,13 +192,11 @@
/// Emits a set of attributes
/// @param attrs the attributes to emit
- /// @returns true if successful, false otherwise
- bool EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs);
+ void EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs);
/// Emits an attribute
/// @param attr the attribute to emit
- /// @returns true if successful, false otherwise
- bool EmitAttribute(const ast::Attribute* attr);
+ void EmitAttribute(const ast::Attribute* attr);
/// Retrieve the IR Flow node for a given AST node.
/// @param n the node to lookup
diff --git a/src/tint/ir/builder_impl_test.cc b/src/tint/ir/builder_impl_test.cc
index 4e02875..cd6bf0b 100644
--- a/src/tint/ir/builder_impl_test.cc
+++ b/src/tint/ir/builder_impl_test.cc
@@ -14,6 +14,7 @@
#include "src/tint/ir/test_helper.h"
+#include "gmock/gmock.h"
#include "src/tint/ast/case_selector.h"
#include "src/tint/ast/int_literal_expression.h"
#include "src/tint/constant/scalar.h"
@@ -1472,7 +1473,7 @@
auto& b = CreateBuilder();
auto r = b.EmitLiteral(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>());
auto* val = r.Get()->As<Constant>()->value;
@@ -1486,7 +1487,7 @@
auto& b = CreateBuilder();
auto r = b.EmitLiteral(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>());
auto* val = r.Get()->As<Constant>()->value;
@@ -1500,7 +1501,7 @@
auto& b = CreateBuilder();
auto r = b.EmitLiteral(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>());
auto* val = r.Get()->As<Constant>()->value;
@@ -1515,7 +1516,7 @@
auto& b = CreateBuilder();
auto r = b.EmitLiteral(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>());
auto* val = r.Get()->As<Constant>()->value;
@@ -1529,7 +1530,7 @@
auto& b = CreateBuilder();
auto r = b.EmitLiteral(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>());
auto* val = r.Get()->As<Constant>()->value;
@@ -1543,7 +1544,7 @@
auto& b = CreateBuilder();
auto r = b.EmitLiteral(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>());
auto* val = r.Get()->As<Constant>()->value;
@@ -1558,7 +1559,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1573,7 +1575,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1588,7 +1591,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1603,7 +1607,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1618,7 +1623,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1633,7 +1639,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1648,7 +1655,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1663,7 +1671,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1678,7 +1687,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1693,7 +1703,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1708,7 +1719,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1723,7 +1735,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1738,7 +1751,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1753,7 +1767,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1768,7 +1783,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1783,7 +1799,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1798,7 +1815,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1813,7 +1831,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1829,7 +1848,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1850,7 +1870,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1858,6 +1879,24 @@
)");
}
+TEST_F(IR_BuilderImplTest, EmitStatement_Discard) {
+ auto* expr = Discard();
+ Func("test_function", {}, ty.void_(), expr,
+ utils::Vector{
+ create<ast::StageAttribute>(ast::PipelineStage::kFragment),
+ });
+
+ auto& b = CreateBuilder();
+ InjectFlowBlock();
+ b.EmitStatement(expr);
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+
+ Disassembler d(b.builder.ir);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
+ EXPECT_EQ(d.AsString(), R"(%1 (void) = discard
+)");
+}
+
TEST_F(IR_BuilderImplTest, EmitStatement_UserFunction) {
Func("my_func", utils::Vector{Param("p", ty.f32())}, ty.void_(), utils::Empty);
@@ -1866,8 +1905,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
- auto r = b.EmitStatement(stmt);
- ASSERT_TRUE(r) << b.error();
+ b.EmitStatement(stmt);
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1886,7 +1925,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1894,7 +1934,8 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Construct) {
+// Requires identifier expressions
+TEST_F(IR_BuilderImplTest, DISABLED_EmitExpression_Construct) {
auto i = GlobalVar("i", builtin::AddressSpace::kPrivate, Expr(1_f));
auto* expr = vec3(ty.f32(), 2_f, 3_f, i);
WrapInFunction(expr);
@@ -1902,7 +1943,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1910,7 +1952,8 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Convert) {
+// Requires identifier expressions
+TEST_F(IR_BuilderImplTest, DISABLED_EmitExpression_Convert) {
auto i = GlobalVar("i", builtin::AddressSpace::kPrivate, Expr(1_i));
auto* expr = Call(ty.f32(), i);
WrapInFunction(expr);
@@ -1918,7 +1961,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
@@ -1943,7 +1987,8 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Builtin) {
+// Requires identifier expressions
+TEST_F(IR_BuilderImplTest, DISABLED_EmitExpression_Builtin) {
auto i = GlobalVar("i", builtin::AddressSpace::kPrivate, Expr(1_f));
auto* expr = Call("asin", i);
WrapInFunction(expr);
@@ -1951,7 +1996,8 @@
auto& b = CreateBuilder();
InjectFlowBlock();
auto r = b.EmitExpression(expr);
- ASSERT_TRUE(r) << b.error();
+ ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
+ ASSERT_TRUE(r);
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
diff --git a/src/tint/ir/builtin.h b/src/tint/ir/builtin.h
index ee73ec6..b1fd61d 100644
--- a/src/tint/ir/builtin.h
+++ b/src/tint/ir/builtin.h
@@ -17,8 +17,6 @@
#include "src/tint/builtin/function.h"
#include "src/tint/ir/call.h"
-#include "src/tint/symbol_table.h"
-#include "src/tint/type/type.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
diff --git a/src/tint/ir/call.h b/src/tint/ir/call.h
index 89c07cd..e7bf684 100644
--- a/src/tint/ir/call.h
+++ b/src/tint/ir/call.h
@@ -16,8 +16,6 @@
#define SRC_TINT_IR_CALL_H_
#include "src/tint/ir/instruction.h"
-#include "src/tint/symbol_table.h"
-#include "src/tint/type/type.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
diff --git a/src/tint/ir/constant.h b/src/tint/ir/constant.h
index dd50fac..207af5d 100644
--- a/src/tint/ir/constant.h
+++ b/src/tint/ir/constant.h
@@ -17,7 +17,6 @@
#include "src/tint/constant/value.h"
#include "src/tint/ir/value.h"
-#include "src/tint/symbol_table.h"
#include "src/tint/utils/string_stream.h"
namespace tint::ir {
diff --git a/src/tint/ir/construct.h b/src/tint/ir/construct.h
index f6c7a0a..c9d1819 100644
--- a/src/tint/ir/construct.h
+++ b/src/tint/ir/construct.h
@@ -16,8 +16,6 @@
#define SRC_TINT_IR_CONSTRUCT_H_
#include "src/tint/ir/call.h"
-#include "src/tint/symbol_table.h"
-#include "src/tint/type/type.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
diff --git a/src/tint/ir/convert.h b/src/tint/ir/convert.h
index 15d118e..d157e03 100644
--- a/src/tint/ir/convert.h
+++ b/src/tint/ir/convert.h
@@ -16,7 +16,6 @@
#define SRC_TINT_IR_CONVERT_H_
#include "src/tint/ir/call.h"
-#include "src/tint/symbol_table.h"
#include "src/tint/type/type.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
diff --git a/src/tint/ir/temp.cc b/src/tint/ir/discard.cc
similarity index 62%
copy from src/tint/ir/temp.cc
copy to src/tint/ir/discard.cc
index b6c9a8e..1d179b5 100644
--- a/src/tint/ir/temp.cc
+++ b/src/tint/ir/discard.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Tint Authors.
+// 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.
@@ -12,20 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/tint/ir/temp.h"
+#include "src/tint/ir/discard.h"
+#include "src/tint/debug.h"
-#include <string>
-
-TINT_INSTANTIATE_TYPEINFO(tint::ir::Temp);
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Discard);
namespace tint::ir {
-Temp::Temp(const type::Type* type, Id id) : type_(type), id_(id) {}
+Discard::Discard(Value* result) : Base(result) {}
-Temp::~Temp() = default;
+Discard::~Discard() = default;
-utils::StringStream& Temp::ToString(utils::StringStream& out) const {
- out << "%" << std::to_string(AsId()) << " (" << type_->FriendlyName() << ")";
+utils::StringStream& Discard::ToString(utils::StringStream& out) const {
+ Result()->ToString(out);
+ out << " = discard";
return out;
}
diff --git a/src/tint/ir/discard.h b/src/tint/ir/discard.h
new file mode 100644
index 0000000..b27dc9d
--- /dev/null
+++ b/src/tint/ir/discard.h
@@ -0,0 +1,45 @@
+// 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_IR_DISCARD_H_
+#define SRC_TINT_IR_DISCARD_H_
+
+#include "src/tint/ir/instruction.h"
+#include "src/tint/utils/castable.h"
+#include "src/tint/utils/string_stream.h"
+
+namespace tint::ir {
+
+/// A discard instruction in the IR.
+class Discard : public utils::Castable<Discard, Instruction> {
+ public:
+ /// Constructor
+ /// @param result the result id
+ explicit Discard(Value* result);
+ Discard(const Discard& instr) = delete;
+ Discard(Discard&& instr) = delete;
+ ~Discard() override;
+
+ Discard& operator=(const Discard& instr) = delete;
+ Discard& operator=(Discard&& instr) = delete;
+
+ /// Write the instruction to the given stream
+ /// @param out the stream to write to
+ /// @returns the stream
+ utils::StringStream& ToString(utils::StringStream& out) const override;
+};
+
+} // namespace tint::ir
+
+#endif // SRC_TINT_IR_DISCARD_H_
diff --git a/src/tint/ir/discard_test.cc b/src/tint/ir/discard_test.cc
new file mode 100644
index 0000000..0f2fb5d
--- /dev/null
+++ b/src/tint/ir/discard_test.cc
@@ -0,0 +1,41 @@
+// 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/ir/instruction.h"
+#include "src/tint/ir/test_helper.h"
+#include "src/tint/utils/string_stream.h"
+
+namespace tint::ir {
+namespace {
+
+using IR_InstructionTest = TestHelper;
+
+TEST_F(IR_InstructionTest, Discard) {
+ auto& b = CreateEmptyBuilder();
+
+ b.builder.next_runtime_id = Runtime::Id(42);
+ const auto* instr = b.builder.Discard();
+
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
+ ASSERT_NE(instr->Result()->Type(), nullptr);
+ ASSERT_NE(instr->Result()->Type()->As<type::Void>(), nullptr);
+
+ utils::StringStream str;
+ instr->ToString(str);
+ EXPECT_EQ(str.str(), "%42 (void) = discard");
+}
+
+} // namespace
+} // namespace tint::ir
diff --git a/src/tint/ir/instruction.h b/src/tint/ir/instruction.h
index 8f58e54..df965e4 100644
--- a/src/tint/ir/instruction.h
+++ b/src/tint/ir/instruction.h
@@ -16,7 +16,6 @@
#define SRC_TINT_IR_INSTRUCTION_H_
#include "src/tint/ir/value.h"
-#include "src/tint/symbol_table.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
diff --git a/src/tint/ir/module.cc b/src/tint/ir/module.cc
index bf05e08..e434888 100644
--- a/src/tint/ir/module.cc
+++ b/src/tint/ir/module.cc
@@ -28,7 +28,7 @@
BuilderImpl b(program);
auto r = b.Build();
if (!r) {
- return b.error();
+ return b.Diagnostics().str();
}
return Result{r.Move()};
diff --git a/src/tint/ir/temp.cc b/src/tint/ir/runtime.cc
similarity index 74%
rename from src/tint/ir/temp.cc
rename to src/tint/ir/runtime.cc
index b6c9a8e..a1f485d 100644
--- a/src/tint/ir/temp.cc
+++ b/src/tint/ir/runtime.cc
@@ -12,19 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/tint/ir/temp.h"
+#include "src/tint/ir/runtime.h"
#include <string>
-TINT_INSTANTIATE_TYPEINFO(tint::ir::Temp);
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Runtime);
namespace tint::ir {
-Temp::Temp(const type::Type* type, Id id) : type_(type), id_(id) {}
+Runtime::Runtime(const type::Type* type, Id id) : type_(type), id_(id) {}
-Temp::~Temp() = default;
+Runtime::~Runtime() = default;
-utils::StringStream& Temp::ToString(utils::StringStream& out) const {
+utils::StringStream& Runtime::ToString(utils::StringStream& out) const {
out << "%" << std::to_string(AsId()) << " (" << type_->FriendlyName() << ")";
return out;
}
diff --git a/src/tint/ir/temp.h b/src/tint/ir/runtime.h
similarity index 69%
rename from src/tint/ir/temp.h
rename to src/tint/ir/runtime.h
index 31bc284..6e2558c 100644
--- a/src/tint/ir/temp.h
+++ b/src/tint/ir/runtime.h
@@ -12,42 +12,41 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef SRC_TINT_IR_TEMP_H_
-#define SRC_TINT_IR_TEMP_H_
+#ifndef SRC_TINT_IR_RUNTIME_H_
+#define SRC_TINT_IR_RUNTIME_H_
#include "src/tint/ir/value.h"
-#include "src/tint/symbol_table.h"
#include "src/tint/utils/string_stream.h"
namespace tint::ir {
-/// Temporary value in the IR.
-class Temp : public utils::Castable<Temp, Value> {
+/// Runtime value in the IR.
+class Runtime : public utils::Castable<Runtime, Value> {
public:
/// A value id.
using Id = uint32_t;
/// Constructor
- /// @param type the type of the temporary
+ /// @param type the type of the value
/// @param id the id for the value
- Temp(const type::Type* type, Id id);
+ Runtime(const type::Type* type, Id id);
/// Destructor
- ~Temp() override;
+ ~Runtime() override;
- Temp(const Temp&) = delete;
- Temp(Temp&&) = delete;
+ Runtime(const Runtime&) = delete;
+ Runtime(Runtime&&) = delete;
- Temp& operator=(const Temp&) = delete;
- Temp& operator=(Temp&&) = delete;
+ Runtime& operator=(const Runtime&) = delete;
+ Runtime& operator=(Runtime&&) = delete;
/// @returns the value data as an `Id`.
Id AsId() const { return id_; }
- /// @returns the type of the temporary
+ /// @returns the type of the value
const type::Type* Type() const override { return type_; }
- /// Write the temp to the given stream
+ /// Write the id to the given stream
/// @param out the stream to write to
/// @returns the stream
utils::StringStream& ToString(utils::StringStream& out) const override;
@@ -59,4 +58,4 @@
} // namespace tint::ir
-#endif // SRC_TINT_IR_TEMP_H_
+#endif // SRC_TINT_IR_RUNTIME_H_
diff --git a/src/tint/ir/temp_test.cc b/src/tint/ir/runtime_test.cc
similarity index 81%
rename from src/tint/ir/temp_test.cc
rename to src/tint/ir/runtime_test.cc
index edd002b..7959909 100644
--- a/src/tint/ir/temp_test.cc
+++ b/src/tint/ir/runtime_test.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/tint/ir/temp.h"
+#include "src/tint/ir/runtime.h"
#include "src/tint/ir/test_helper.h"
#include "src/tint/utils/string_stream.h"
@@ -21,15 +21,15 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_TempTest = TestHelper;
+using IR_RuntimeTest = TestHelper;
-TEST_F(IR_TempTest, id) {
+TEST_F(IR_RuntimeTest, id) {
auto& b = CreateEmptyBuilder();
utils::StringStream str;
- b.builder.next_temp_id = Temp::Id(4);
- auto* val = b.builder.Temp(b.builder.ir.types.Get<type::I32>());
+ b.builder.next_runtime_id = Runtime::Id(4);
+ auto* val = b.builder.Runtime(b.builder.ir.types.Get<type::I32>());
EXPECT_EQ(4u, val->AsId());
val->ToString(str);
diff --git a/src/tint/ir/store.cc b/src/tint/ir/store.cc
new file mode 100644
index 0000000..32162c3
--- /dev/null
+++ b/src/tint/ir/store.cc
@@ -0,0 +1,36 @@
+// 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/ir/store.h"
+#include "src/tint/debug.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Store);
+
+namespace tint::ir {
+
+Store::Store(Value* to, Value* from) : Base(to), from_(from) {
+ TINT_ASSERT(IR, from_);
+ from_->AddUsage(this);
+}
+
+Store::~Store() = default;
+
+utils::StringStream& Store::ToString(utils::StringStream& out) const {
+ Result()->ToString(out);
+ out << " = ";
+ from_->ToString(out);
+ return out;
+}
+
+} // namespace tint::ir
diff --git a/src/tint/ir/store.h b/src/tint/ir/store.h
new file mode 100644
index 0000000..57544fc
--- /dev/null
+++ b/src/tint/ir/store.h
@@ -0,0 +1,52 @@
+// 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_IR_STORE_H_
+#define SRC_TINT_IR_STORE_H_
+
+#include "src/tint/ir/instruction.h"
+#include "src/tint/utils/castable.h"
+#include "src/tint/utils/string_stream.h"
+
+namespace tint::ir {
+
+/// An instruction in the IR.
+class Store : public utils::Castable<Store, Instruction> {
+ public:
+ /// Constructor
+ /// @param to the value to store too
+ /// @param from the value being stored from
+ Store(Value* to, Value* from);
+ Store(const Store& instr) = delete;
+ Store(Store&& instr) = delete;
+ ~Store() override;
+
+ Store& operator=(const Store& instr) = delete;
+ Store& operator=(Store&& instr) = delete;
+
+ /// @returns the value being stored
+ const Value* from() const { return from_; }
+
+ /// Write the instruction to the given stream
+ /// @param out the stream to write to
+ /// @returns the stream
+ utils::StringStream& ToString(utils::StringStream& out) const override;
+
+ private:
+ Value* from_ = nullptr;
+};
+
+} // namespace tint::ir
+
+#endif // SRC_TINT_IR_STORE_H_
diff --git a/src/tint/ir/store_test.cc b/src/tint/ir/store_test.cc
new file mode 100644
index 0000000..e0632dd
--- /dev/null
+++ b/src/tint/ir/store_test.cc
@@ -0,0 +1,65 @@
+// 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/ir/instruction.h"
+#include "src/tint/ir/test_helper.h"
+#include "src/tint/utils/string_stream.h"
+
+namespace tint::ir {
+namespace {
+
+using namespace tint::number_suffixes; // NOLINT
+
+using IR_InstructionTest = TestHelper;
+
+TEST_F(IR_InstructionTest, CreateStore) {
+ auto& b = CreateEmptyBuilder();
+
+ b.builder.next_runtime_id = Runtime::Id(42);
+
+ auto* rt = b.builder.Runtime(b.builder.ir.types.Get<type::I32>());
+ const auto* instr = b.builder.Store(rt, b.builder.Constant(4_i));
+
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ ASSERT_NE(instr->Result()->Type(), nullptr);
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
+
+ ASSERT_TRUE(instr->from()->Is<Constant>());
+ auto lhs = instr->from()->As<Constant>()->value;
+ ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
+ EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
+
+ utils::StringStream str;
+ instr->ToString(str);
+ EXPECT_EQ(str.str(), "%42 (i32) = 4");
+}
+
+TEST_F(IR_InstructionTest, Store_Usage) {
+ auto& b = CreateEmptyBuilder();
+
+ b.builder.next_runtime_id = Runtime::Id(42);
+ auto* rt = b.builder.Runtime(b.builder.ir.types.Get<type::I32>());
+ const auto* instr = b.builder.Store(rt, b.builder.Constant(4_i));
+
+ ASSERT_NE(instr->Result(), nullptr);
+ ASSERT_EQ(instr->Result()->Usage().Length(), 1u);
+ EXPECT_EQ(instr->Result()->Usage()[0], instr);
+
+ ASSERT_NE(instr->from(), nullptr);
+ ASSERT_EQ(instr->from()->Usage().Length(), 1u);
+ EXPECT_EQ(instr->from()->Usage()[0], instr);
+}
+
+} // namespace
+} // namespace tint::ir
diff --git a/src/tint/ir/test_helper.h b/src/tint/ir/test_helper.h
index 6e36ed3..47f8821 100644
--- a/src/tint/ir/test_helper.h
+++ b/src/tint/ir/test_helper.h
@@ -79,7 +79,7 @@
auto m = b.Build();
// Store the error away in case we need it
- error_ = b.error();
+ error_ = b.Diagnostics().str();
// Explicitly remove program to guard against pointers back to ast. Note, this does mean the
// BuilderImpl is pointing to an invalid program. We keep the BuilderImpl around because we
diff --git a/src/tint/ir/unary.cc b/src/tint/ir/unary.cc
new file mode 100644
index 0000000..532efcc
--- /dev/null
+++ b/src/tint/ir/unary.cc
@@ -0,0 +1,53 @@
+// 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/ir/unary.h"
+#include "src/tint/debug.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Unary);
+
+namespace tint::ir {
+
+Unary::Unary(Kind kind, Value* result, Value* val) : Base(result), kind_(kind), val_(val) {
+ TINT_ASSERT(IR, val_);
+ val_->AddUsage(this);
+}
+
+Unary::~Unary() = default;
+
+utils::StringStream& Unary::ToString(utils::StringStream& out) const {
+ Result()->ToString(out) << " = ";
+ switch (GetKind()) {
+ case Unary::Kind::kAddressOf:
+ out << "&";
+ break;
+ case Unary::Kind::kComplement:
+ out << "~";
+ break;
+ case Unary::Kind::kIndirection:
+ out << "*";
+ break;
+ case Unary::Kind::kNegation:
+ out << "-";
+ break;
+ case Unary::Kind::kNot:
+ out << "!";
+ break;
+ }
+ val_->ToString(out);
+
+ return out;
+}
+
+} // namespace tint::ir
diff --git a/src/tint/ir/unary.h b/src/tint/ir/unary.h
new file mode 100644
index 0000000..0337b4f
--- /dev/null
+++ b/src/tint/ir/unary.h
@@ -0,0 +1,66 @@
+// 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_IR_UNARY_H_
+#define SRC_TINT_IR_UNARY_H_
+
+#include "src/tint/ir/instruction.h"
+#include "src/tint/utils/castable.h"
+#include "src/tint/utils/string_stream.h"
+
+namespace tint::ir {
+
+/// An instruction in the IR.
+class Unary : public utils::Castable<Unary, Instruction> {
+ public:
+ /// The kind of instruction.
+ enum class Kind {
+ kAddressOf,
+ kComplement,
+ kIndirection,
+ kNegation,
+ kNot,
+ };
+
+ /// Constructor
+ /// @param kind the kind of unary instruction
+ /// @param result the result value
+ /// @param val the lhs of the instruction
+ Unary(Kind kind, Value* result, Value* val);
+ Unary(const Unary& instr) = delete;
+ Unary(Unary&& instr) = delete;
+ ~Unary() override;
+
+ Unary& operator=(const Unary& instr) = delete;
+ Unary& operator=(Unary&& instr) = delete;
+
+ /// @returns the kind of instruction
+ Kind GetKind() const { return kind_; }
+
+ /// @returns the value for the instruction
+ const Value* Val() const { return val_; }
+
+ /// Write the instruction to the given stream
+ /// @param out the stream to write to
+ /// @returns the stream
+ utils::StringStream& ToString(utils::StringStream& out) const override;
+
+ private:
+ Kind kind_;
+ Value* val_ = nullptr;
+};
+
+} // namespace tint::ir
+
+#endif // SRC_TINT_IR_UNARY_H_
diff --git a/src/tint/ir/unary_test.cc b/src/tint/ir/unary_test.cc
new file mode 100644
index 0000000..cfc5579
--- /dev/null
+++ b/src/tint/ir/unary_test.cc
@@ -0,0 +1,161 @@
+// 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/ir/instruction.h"
+#include "src/tint/ir/test_helper.h"
+#include "src/tint/utils/string_stream.h"
+
+namespace tint::ir {
+namespace {
+
+using namespace tint::number_suffixes; // NOLINT
+
+using IR_InstructionTest = TestHelper;
+
+TEST_F(IR_InstructionTest, CreateAddressOf) {
+ auto& b = CreateEmptyBuilder();
+
+ b.builder.next_runtime_id = Runtime::Id(42);
+ // TODO(dsinclair): This would be better as an identifier, but works for now.
+ const auto* instr =
+ b.builder.AddressOf(b.builder.ir.types.Get<type::Pointer>(
+ b.builder.ir.types.Get<type::I32>(),
+ builtin::AddressSpace::kPrivate, builtin::Access::kReadWrite),
+ b.builder.Constant(4_i));
+
+ EXPECT_EQ(instr->GetKind(), Unary::Kind::kAddressOf);
+
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ ASSERT_NE(instr->Result()->Type(), nullptr);
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
+
+ ASSERT_TRUE(instr->Val()->Is<Constant>());
+ auto lhs = instr->Val()->As<Constant>()->value;
+ ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
+ EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
+
+ utils::StringStream str;
+ instr->ToString(str);
+ EXPECT_EQ(str.str(), "%42 (ptr<private, i32, read_write>) = &4");
+}
+
+TEST_F(IR_InstructionTest, CreateComplement) {
+ auto& b = CreateEmptyBuilder();
+
+ b.builder.next_runtime_id = Runtime::Id(42);
+ const auto* instr =
+ b.builder.Complement(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
+
+ EXPECT_EQ(instr->GetKind(), Unary::Kind::kComplement);
+
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
+
+ ASSERT_TRUE(instr->Val()->Is<Constant>());
+ auto lhs = instr->Val()->As<Constant>()->value;
+ ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
+ EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
+
+ utils::StringStream str;
+ instr->ToString(str);
+ EXPECT_EQ(str.str(), "%42 (i32) = ~4");
+}
+
+TEST_F(IR_InstructionTest, CreateIndirection) {
+ auto& b = CreateEmptyBuilder();
+
+ b.builder.next_runtime_id = Runtime::Id(42);
+ // TODO(dsinclair): This would be better as an identifier, but works for now.
+ const auto* instr =
+ b.builder.Indirection(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
+
+ EXPECT_EQ(instr->GetKind(), Unary::Kind::kIndirection);
+
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
+
+ ASSERT_TRUE(instr->Val()->Is<Constant>());
+ auto lhs = instr->Val()->As<Constant>()->value;
+ ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
+ EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
+
+ utils::StringStream str;
+ instr->ToString(str);
+ EXPECT_EQ(str.str(), "%42 (i32) = *4");
+}
+
+TEST_F(IR_InstructionTest, CreateNegation) {
+ auto& b = CreateEmptyBuilder();
+
+ b.builder.next_runtime_id = Runtime::Id(42);
+ const auto* instr =
+ b.builder.Negation(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
+
+ EXPECT_EQ(instr->GetKind(), Unary::Kind::kNegation);
+
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
+
+ ASSERT_TRUE(instr->Val()->Is<Constant>());
+ auto lhs = instr->Val()->As<Constant>()->value;
+ ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
+ EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
+
+ utils::StringStream str;
+ instr->ToString(str);
+ EXPECT_EQ(str.str(), "%42 (i32) = -4");
+}
+
+TEST_F(IR_InstructionTest, CreateNot) {
+ auto& b = CreateEmptyBuilder();
+
+ b.builder.next_runtime_id = Runtime::Id(42);
+ const auto* instr =
+ b.builder.Not(b.builder.ir.types.Get<type::Bool>(), b.builder.Constant(true));
+
+ EXPECT_EQ(instr->GetKind(), Unary::Kind::kNot);
+
+ ASSERT_TRUE(instr->Result()->Is<Runtime>());
+ EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
+
+ ASSERT_TRUE(instr->Val()->Is<Constant>());
+ auto lhs = instr->Val()->As<Constant>()->value;
+ ASSERT_TRUE(lhs->Is<constant::Scalar<bool>>());
+ EXPECT_TRUE(lhs->As<constant::Scalar<bool>>()->ValueAs<bool>());
+
+ utils::StringStream str;
+ instr->ToString(str);
+ EXPECT_EQ(str.str(), "%42 (bool) = !true");
+}
+
+TEST_F(IR_InstructionTest, Unary_Usage) {
+ auto& b = CreateEmptyBuilder();
+
+ b.builder.next_runtime_id = Runtime::Id(42);
+ const auto* instr =
+ b.builder.Negation(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
+
+ EXPECT_EQ(instr->GetKind(), Unary::Kind::kNegation);
+
+ ASSERT_NE(instr->Result(), nullptr);
+ ASSERT_EQ(instr->Result()->Usage().Length(), 1u);
+ EXPECT_EQ(instr->Result()->Usage()[0], instr);
+
+ ASSERT_NE(instr->Val(), nullptr);
+ ASSERT_EQ(instr->Val()->Usage().Length(), 1u);
+ EXPECT_EQ(instr->Val()->Usage()[0], instr);
+}
+
+} // namespace
+} // namespace tint::ir
diff --git a/src/tint/ir/user_call.h b/src/tint/ir/user_call.h
index 8c99d89..0662455 100644
--- a/src/tint/ir/user_call.h
+++ b/src/tint/ir/user_call.h
@@ -16,8 +16,7 @@
#define SRC_TINT_IR_USER_CALL_H_
#include "src/tint/ir/call.h"
-#include "src/tint/symbol_table.h"
-#include "src/tint/type/type.h"
+#include "src/tint/symbol.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
diff --git a/src/tint/ir/value.cc b/src/tint/ir/value.cc
index 93040cf..750fe19 100644
--- a/src/tint/ir/value.cc
+++ b/src/tint/ir/value.cc
@@ -15,7 +15,7 @@
#include "src/tint/ir/value.h"
#include "src/tint/ir/constant.h"
-#include "src/tint/ir/temp.h"
+#include "src/tint/ir/runtime.h"
TINT_INSTANTIATE_TYPEINFO(tint::ir::Value);
diff --git a/src/tint/ir/value.h b/src/tint/ir/value.h
index b498e9d..e2a642b 100644
--- a/src/tint/ir/value.h
+++ b/src/tint/ir/value.h
@@ -15,7 +15,6 @@
#ifndef SRC_TINT_IR_VALUE_H_
#define SRC_TINT_IR_VALUE_H_
-#include "src/tint/symbol_table.h"
#include "src/tint/type/type.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
diff --git a/src/tint/reader/spirv/function_arithmetic_test.cc b/src/tint/reader/spirv/function_arithmetic_test.cc
index 8f29a91..1f14633 100644
--- a/src/tint/reader/spirv/function_arithmetic_test.cc
+++ b/src/tint/reader/spirv/function_arithmetic_test.cc
@@ -30,6 +30,11 @@
OpEntryPoint Fragment %100 "main"
OpExecutionMode %100 OriginUpperLeft
+ OpName %v2float_50_60 "v2float_50_60"
+ OpName %v2float_60_50 "v2float_60_50"
+ OpName %v3float_50_60_70 "v3float_50_60_70"
+ OpName %v3float_60_70_50 "v3float_60_70_50"
+
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
@@ -94,10 +99,10 @@
return "bitcast<vec2<u32>>(vec2<i32>(40i, 30i))";
}
if (assembly == "v2float_50_60") {
- return "vec2<f32>(50.0f, 60.0f)";
+ return "v2float_50_60";
}
if (assembly == "v2float_60_50") {
- return "vec2<f32>(60.0f, 50.0f)";
+ return "v2float_60_50";
}
return "bad case";
}
@@ -271,7 +276,7 @@
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
EXPECT_THAT(test::ToString(p->program(), ast_body),
- HasSubstr("let x_1 : vec2<f32> = -(vec2<f32>(50.0f, 60.0f));"));
+ HasSubstr("let x_1 : vec2<f32> = -(v2float_50_60);"));
}
struct BinaryData {
@@ -704,10 +709,9 @@
auto fe = p->function_emitter(100);
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
- EXPECT_THAT(
- test::ToString(p->program(), ast_body),
- HasSubstr(
- R"(let x_1 : vec2<f32> = (vec2<f32>(50.0f, 60.0f) - (vec2<f32>(60.0f, 50.0f) * floor((vec2<f32>(50.0f, 60.0f) / vec2<f32>(60.0f, 50.0f)))));)"));
+ EXPECT_THAT(test::ToString(p->program(), ast_body),
+ HasSubstr("let x_1 : vec2<f32> = (v2float_50_60 - (v2float_60_50 * "
+ "floor((v2float_50_60 / v2float_60_50))));"));
}
TEST_F(SpvBinaryArithTestBasic, VectorTimesScalar) {
diff --git a/src/tint/reader/spirv/function_bit_test.cc b/src/tint/reader/spirv/function_bit_test.cc
index 2a12f01..0401a63 100644
--- a/src/tint/reader/spirv/function_bit_test.cc
+++ b/src/tint/reader/spirv/function_bit_test.cc
@@ -877,8 +877,7 @@
auto body = test::ToString(p->program(), ast_body);
EXPECT_THAT(
body,
- HasSubstr(
- R"(let x_1 : vec2<i32> = insertBits(vec2<i32>(30i, 40i), vec2<i32>(40i, 30i), 10u, 20u);)"))
+ HasSubstr(R"(let x_1 : vec2<i32> = insertBits(x_28, vec2<i32>(40i, 30i), 10u, 20u);)"))
<< body;
}
@@ -897,7 +896,7 @@
EXPECT_THAT(
body,
HasSubstr(
- R"(let x_1 : vec2<i32> = insertBits(vec2<i32>(30i, 40i), vec2<i32>(40i, 30i), u32(10i), u32(20i));)"))
+ R"(let x_1 : vec2<i32> = insertBits(x_28, vec2<i32>(40i, 30i), u32(10i), u32(20i));)"))
<< body;
}
@@ -946,8 +945,7 @@
auto body = test::ToString(p->program(), ast_body);
EXPECT_THAT(
body,
- HasSubstr(
- R"(let x_1 : vec2<u32> = insertBits(vec2<u32>(10u, 20u), vec2<u32>(20u, 10u), 10u, 20u);)"))
+ HasSubstr(R"(let x_1 : vec2<u32> = insertBits(x_26, vec2<u32>(20u, 10u), 10u, 20u);)"))
<< body;
}
@@ -966,7 +964,7 @@
EXPECT_THAT(
body,
HasSubstr(
- R"(let x_1 : vec2<u32> = insertBits(vec2<u32>(10u, 20u), vec2<u32>(20u, 10u), u32(10i), u32(20i));)"))
+ R"(let x_1 : vec2<u32> = insertBits(x_26, vec2<u32>(20u, 10u), u32(10i), u32(20i));)"))
<< body;
}
@@ -1012,9 +1010,7 @@
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
auto body = test::ToString(p->program(), ast_body);
- EXPECT_THAT(body,
- HasSubstr("let x_1 : vec2<i32> = extractBits(vec2<i32>(30i, 40i), 10u, 20u);"))
- << body;
+ EXPECT_THAT(body, HasSubstr("let x_1 : vec2<i32> = extractBits(x_28, 10u, 20u);")) << body;
}
TEST_F(SpvUnaryBitTest, ExtractBits_IntVector_SignedOffsetAndCount) {
@@ -1029,9 +1025,7 @@
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
auto body = test::ToString(p->program(), ast_body);
- EXPECT_THAT(
- body,
- HasSubstr("let x_1 : vec2<i32> = extractBits(vec2<i32>(30i, 40i), u32(10i), u32(20i));"))
+ EXPECT_THAT(body, HasSubstr("let x_1 : vec2<i32> = extractBits(x_28, u32(10i), u32(20i));"))
<< body;
}
@@ -1077,9 +1071,7 @@
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
auto body = test::ToString(p->program(), ast_body);
- EXPECT_THAT(body,
- HasSubstr("let x_1 : vec2<u32> = extractBits(vec2<u32>(10u, 20u), 10u, 20u);"))
- << body;
+ EXPECT_THAT(body, HasSubstr("let x_1 : vec2<u32> = extractBits(x_26, 10u, 20u);")) << body;
}
TEST_F(SpvUnaryBitTest, ExtractBits_UintVector_SignedOffsetAndCount) {
@@ -1094,9 +1086,7 @@
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
auto body = test::ToString(p->program(), ast_body);
- EXPECT_THAT(
- body,
- HasSubstr("let x_1 : vec2<u32> = extractBits(vec2<u32>(10u, 20u), u32(10i), u32(20i));"))
+ EXPECT_THAT(body, HasSubstr("let x_1 : vec2<u32> = extractBits(x_26, u32(10i), u32(20i));"))
<< body;
}
diff --git a/src/tint/reader/spirv/parser_impl.cc b/src/tint/reader/spirv/parser_impl.cc
index 9f5afdb..6f6bf1e 100644
--- a/src/tint/reader/spirv/parser_impl.cc
+++ b/src/tint/reader/spirv/parser_impl.cc
@@ -1438,6 +1438,12 @@
if ((type_id == builtin_position_.pointer_type_id) &&
((spirv_storage_class == spv::StorageClass::Input) ||
(spirv_storage_class == spv::StorageClass::Output))) {
+ // TODO(crbug.com/tint/103): Support modules that contain multiple Position built-ins.
+ if (builtin_position_.per_vertex_var_id != 0) {
+ return Fail()
+ << "unsupported: multiple Position built-in variables in the same module";
+ }
+
// Skip emitting gl_PerVertex.
builtin_position_.per_vertex_var_id = var.result_id();
builtin_position_.per_vertex_var_init_id =
@@ -1910,6 +1916,13 @@
case spv::Op::OpConstantComposite: {
// Handle vector, matrix, array, and struct
+ auto itr = declared_constant_composites_.find(id);
+ if (itr != declared_constant_composites_.end()) {
+ // We've already declared this constant value as a module-scope const, so just
+ // reference that identifier.
+ return {original_ast_type, builder_.Expr(itr->second)};
+ }
+
// Generate a composite from explicit components.
ExpressionList ast_components;
if (!inst->WhileEachInId([&](const uint32_t* id_ref) -> bool {
@@ -1924,8 +1937,20 @@
// We've already emitted a diagnostic.
return {};
}
- return {original_ast_type, builder_.Call(source, original_ast_type->Build(builder_),
- std::move(ast_components))};
+
+ auto* expr = builder_.Call(source, original_ast_type->Build(builder_),
+ std::move(ast_components));
+
+ if (def_use_mgr_->NumUses(id) == 1) {
+ // The constant is only used once, so just inline its use.
+ return {original_ast_type, expr};
+ }
+
+ // Create a module-scope const declaration for the constant.
+ auto name = namer_.Name(id);
+ auto* decl = builder_.GlobalConst(name, expr);
+ declared_constant_composites_.insert({id, decl->name->symbol});
+ return {original_ast_type, builder_.Expr(name)};
}
default:
break;
diff --git a/src/tint/reader/spirv/parser_impl.h b/src/tint/reader/spirv/parser_impl.h
index 141df61..e0ef655 100644
--- a/src/tint/reader/spirv/parser_impl.h
+++ b/src/tint/reader/spirv/parser_impl.h
@@ -880,6 +880,9 @@
// The ast::Struct type names with only read-only members.
std::unordered_set<Symbol> read_only_struct_types_;
+ // Maps from OpConstantComposite IDs to identifiers of module-scope const declarations.
+ std::unordered_map<uint32_t, Symbol> declared_constant_composites_;
+
// The IDs of scalar spec constants
std::unordered_set<uint32_t> scalar_spec_constants_;
diff --git a/src/tint/reader/spirv/parser_impl_handle_test.cc b/src/tint/reader/spirv/parser_impl_handle_test.cc
index 39d5498..dd572b6 100644
--- a/src/tint/reader/spirv/parser_impl_handle_test.cc
+++ b/src/tint/reader/spirv/parser_impl_handle_test.cc
@@ -1504,7 +1504,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- "textureGather(1i, x_20, x_10, coords12, vec2<i32>(3i, 4i))"},
+ "textureGather(1i, x_20, x_10, coords12, offsets2d)"},
// OpImageGather 2D ConstOffset unsigned
ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
"%result = OpImageGather "
@@ -1514,7 +1514,7 @@
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
"textureGather(1i, x_20, x_10, coords12, "
- "vec2<i32>(vec2<u32>(3u, 4u)))"},
+ "vec2<i32>(u_offsets2d))"},
// OpImageGather 2D Array
ImageAccessCase{"%float 2D 0 1 0 1 Unknown",
"%result = OpImageGather "
@@ -1532,7 +1532,7 @@
@group(2) @binding(1) var x_20 : texture_2d_array<f32>;)",
"textureGather(1i, x_20, x_10, coords123.xy, "
- "i32(round(coords123.z)), vec2<i32>(3i, 4i))"},
+ "i32(round(coords123.z)), offsets2d)"},
// OpImageGather 2D Array ConstOffset unsigned
ImageAccessCase{"%float 2D 0 1 0 1 Unknown",
"%result = OpImageGather "
@@ -1543,7 +1543,7 @@
@group(2) @binding(1) var x_20 : texture_2d_array<f32>;)",
"textureGather(1i, x_20, x_10, coords123.xy, "
"i32(round(coords123.z)), "
- "vec2<i32>(vec2<u32>(3u, 4u)))"},
+ "vec2<i32>(u_offsets2d))"},
// OpImageGather Cube
ImageAccessCase{"%float Cube 0 0 0 1 Unknown",
"%result = OpImageGather "
@@ -1576,7 +1576,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_depth_2d;)",
- "textureGather(x_20, x_10, coords12, vec2<i32>(3i, 4i))"},
+ "textureGather(x_20, x_10, coords12, offsets2d)"},
// OpImageGather 2DDepth ConstOffset unsigned
ImageAccessCase{"%float 2D 1 0 0 1 Unknown",
"%result = OpImageGather "
@@ -1586,7 +1586,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d;)",
"textureGather(x_20, x_10, coords12, "
- "vec2<i32>(vec2<u32>(3u, 4u)))"},
+ "vec2<i32>(u_offsets2d))"},
// OpImageGather 2DDepth Array
ImageAccessCase{"%float 2D 1 1 0 1 Unknown",
"%result = OpImageGather "
@@ -1604,7 +1604,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d_array;)",
"textureGather(x_20, x_10, coords123.xy, "
- "i32(round(coords123.z)), vec2<i32>(3i, 4i))"},
+ "i32(round(coords123.z)), offsets2d)"},
// OpImageGather 2DDepth Array ConstOffset unsigned
ImageAccessCase{"%float 2D 1 1 0 1 Unknown",
"%result = OpImageGather "
@@ -1615,7 +1615,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d_array;)",
"textureGather(x_20, x_10, coords123.xy, "
"i32(round(coords123.z)), "
- "vec2<i32>(vec2<u32>(3u, 4u)))"},
+ "vec2<i32>(u_offsets2d))"},
// OpImageGather DepthCube
ImageAccessCase{"%float Cube 1 0 0 1 Unknown",
"%result = OpImageGather "
@@ -1654,7 +1654,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d;)",
"textureGatherCompare(x_20, x_10, coords12, 0.20000000298023223877f, "
- "vec2<i32>(3i, 4i))"},
+ "offsets2d)"},
// OpImageDrefGather 2DDepth ConstOffset unsigned
ImageAccessCase{"%float 2D 1 0 0 1 Unknown",
"%result = OpImageDrefGather "
@@ -1664,7 +1664,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d;)",
"textureGatherCompare(x_20, x_10, coords12, 0.20000000298023223877f, "
- "vec2<i32>(vec2<u32>(3u, 4u)))"},
+ "vec2<i32>(u_offsets2d))"},
// OpImageDrefGather 2DDepth Array
ImageAccessCase{"%float 2D 1 1 0 1 Unknown",
"%result = OpImageDrefGather "
@@ -1682,7 +1682,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d_array;)",
"textureGatherCompare(x_20, x_10, coords123.xy, "
- "i32(round(coords123.z)), 0.20000000298023223877f, vec2<i32>(3i, 4i))"},
+ "i32(round(coords123.z)), 0.20000000298023223877f, offsets2d)"},
// OpImageDrefGather 2DDepth Array ConstOffset unsigned
ImageAccessCase{"%float 2D 1 1 0 1 Unknown",
"%result = OpImageDrefGather "
@@ -1693,7 +1693,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d_array;)",
"textureGatherCompare(x_20, x_10, coords123.xy, "
"i32(round(coords123.z)), 0.20000000298023223877f, "
- "vec2<i32>(vec2<u32>(3u, 4u)))"},
+ "vec2<i32>(u_offsets2d))"},
// OpImageDrefGather DepthCube
ImageAccessCase{"%float Cube 1 0 0 1 Unknown",
"%result = OpImageDrefGather "
@@ -1742,7 +1742,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- "textureSample(x_20, x_10, coords12, vec2<i32>(3i, 4i))"},
+ "textureSample(x_20, x_10, coords12, offsets2d)"},
// OpImageSampleImplicitLod arrayed with ConstOffset
ImageAccessCase{
@@ -1752,7 +1752,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d_array<f32>;)",
- R"(textureSample(x_20, x_10, coords123.xy, i32(round(coords123.z)), vec2<i32>(3i, 4i)))"},
+ R"(textureSample(x_20, x_10, coords123.xy, i32(round(coords123.z)), offsets2d))"},
// OpImageSampleImplicitLod with Bias
ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
@@ -1781,19 +1781,18 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleBias(x_20, x_10, coords12, 7.0f, vec2<i32>(3i, 4i))"},
+ R"(textureSampleBias(x_20, x_10, coords12, 7.0f, offsets2d)"},
// OpImageSampleImplicitLod with Bias and unsigned ConstOffset
// Convert ConstOffset to signed
- ImageAccessCase{
- "%float 2D 0 0 0 1 Unknown",
- "%result = OpImageSampleImplicitLod "
- "%v4float %sampled_image %coords12 Bias|ConstOffset "
- "%float_7 %u_offsets2d",
- R"(@group(0) @binding(0) var x_10 : sampler;
+ ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
+ "%result = OpImageSampleImplicitLod "
+ "%v4float %sampled_image %coords12 Bias|ConstOffset "
+ "%float_7 %u_offsets2d",
+ R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleBias(x_20, x_10, coords12, 7.0f, vec2<i32>(vec2<u32>(3u, 4u)))"},
+ R"(textureSampleBias(x_20, x_10, coords12, 7.0f, vec2<i32>(u_offsets2d))"},
// OpImageSampleImplicitLod arrayed with Bias
ImageAccessCase{
"%float 2D 0 1 0 1 Unknown",
@@ -1803,7 +1802,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d_array<f32>;)",
- R"(textureSampleBias(x_20, x_10, coords123.xy, i32(round(coords123.z)), 7.0f, vec2<i32>(3i, 4i))"}));
+ R"(textureSampleBias(x_20, x_10, coords123.xy, i32(round(coords123.z)), 7.0f, offsets2d)"}));
INSTANTIATE_TEST_SUITE_P(
// This test shows the use of a sampled image used with both regular
@@ -1863,7 +1862,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d;
)",
- R"(textureSampleCompare(x_20, x_10, coords12, 0.20000000298023223877f, vec2<i32>(3i, 4i)))"},
+ R"(textureSampleCompare(x_20, x_10, coords12, 0.20000000298023223877f, offsets2d))"},
// ImageSampleDrefImplicitLod arrayed with ConstOffset
ImageAccessCase{
"%float 2D 0 1 0 1 Unknown",
@@ -1872,7 +1871,7 @@
R"(@group(0) @binding(0) var x_10 : sampler_comparison;
@group(2) @binding(1) var x_20 : texture_depth_2d_array;)",
- R"(textureSampleCompare(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.20000000298023223877f, vec2<i32>(3i, 4i)))"}));
+ R"(textureSampleCompare(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.20000000298023223877f, offsets2d))"}));
INSTANTIATE_TEST_SUITE_P(
ImageSampleDrefExplicitLod,
@@ -1909,7 +1908,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d;
)",
- R"(textureSampleCompareLevel(x_20, x_10, coords12, 0.20000000298023223877f, vec2<i32>(3i, 4i)))"},
+ R"(textureSampleCompareLevel(x_20, x_10, coords12, 0.20000000298023223877f, offsets2d))"},
// 2D array, ConstOffset
ImageAccessCase{
"%float 2D 1 1 0 1 Unknown",
@@ -1919,7 +1918,7 @@
R"(@group(0) @binding(0) var x_10 : sampler_comparison;
@group(2) @binding(1) var x_20 : texture_depth_2d_array;)",
- R"(textureSampleCompareLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.20000000298023223877f, vec2<i32>(3i, 4i)))"},
+ R"(textureSampleCompareLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.20000000298023223877f, offsets2d))"},
// Cube
ImageAccessCase{
"%float Cube 1 0 0 1 Unknown",
@@ -1971,19 +1970,18 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleLevel(x_20, x_10, coords12, 0.0f, vec2<i32>(3i, 4i)))"},
+ R"(textureSampleLevel(x_20, x_10, coords12, 0.0f, offsets2d))"},
// OpImageSampleExplicitLod - using Lod and unsigned ConstOffset
// Convert the ConstOffset operand to signed
- ImageAccessCase{
- "%float 2D 0 0 0 1 Unknown",
- "%result = OpImageSampleExplicitLod "
- "%v4float %sampled_image %coords12 Lod|ConstOffset "
- "%float_null %u_offsets2d",
- R"(@group(0) @binding(0) var x_10 : sampler;
+ ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
+ "%result = OpImageSampleExplicitLod "
+ "%v4float %sampled_image %coords12 Lod|ConstOffset "
+ "%float_null %u_offsets2d",
+ R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleLevel(x_20, x_10, coords12, 0.0f, vec2<i32>(vec2<u32>(3u, 4u)))"},
+ R"(textureSampleLevel(x_20, x_10, coords12, 0.0f, vec2<i32>(u_offsets2d))"},
// OpImageSampleExplicitLod arrayed - using Lod and ConstOffset
ImageAccessCase{
@@ -1994,7 +1992,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d_array<f32>;)",
- R"(textureSampleLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.0f, vec2<i32>(3i, 4i)))"}));
+ R"(textureSampleLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.0f, offsets2d))"}));
INSTANTIATE_TEST_SUITE_P(
ImageSampleExplicitLod_UsingGrad,
@@ -2021,15 +2019,14 @@
R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21))"},
// OpImageSampleExplicitLod - using Grad and ConstOffset
- ImageAccessCase{
- "%float 2D 0 0 0 1 Unknown",
- "%result = OpImageSampleExplicitLod "
- "%v4float %sampled_image %coords12 Grad|ConstOffset "
- "%vf12 %vf21 %offsets2d",
- R"(@group(0) @binding(0) var x_10 : sampler;
+ ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
+ "%result = OpImageSampleExplicitLod "
+ "%v4float %sampled_image %coords12 Grad|ConstOffset "
+ "%vf12 %vf21 %offsets2d",
+ R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleGrad(x_20, x_10, coords12, vf12, vf21, vec2<i32>(3i, 4i)))"},
+ R"(textureSampleGrad(x_20, x_10, coords12, vf12, vf21, offsets2d))"},
// OpImageSampleExplicitLod - using Grad and unsigned ConstOffset
ImageAccessCase{
@@ -2040,7 +2037,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleGrad(x_20, x_10, coords12, vf12, vf21, vec2<i32>(vec2<u32>(3u, 4u)))"},
+ R"(textureSampleGrad(x_20, x_10, coords12, vf12, vf21, vec2<i32>(u_offsets2d))"},
// OpImageSampleExplicitLod arrayed - using Grad and ConstOffset
ImageAccessCase{
@@ -2051,7 +2048,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d_array<f32>;)",
- R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21, vec2<i32>(3i, 4i)))"},
+ R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21, offsets2d))"},
// OpImageSampleExplicitLod arrayed - using Grad and unsigned
// ConstOffset
@@ -2063,7 +2060,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d_array<f32>;)",
- R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21, vec2<i32>(vec2<u32>(3u, 4u))))"}));
+ R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21, vec2<i32>(u_offsets2d)))"}));
// Test crbug.com/378:
// In WGSL, sampling from depth texture with explicit level of detail
@@ -2153,14 +2150,13 @@
// OpImageSampleProjImplicitLod 2D with ConstOffset
// (Don't need to test with 1D or 3D, as the hard part was the splatted
// division.) This case tests handling of the ConstOffset
- ImageAccessCase{
- "%float 2D 0 0 0 1 Unknown",
- "%result = OpImageSampleProjImplicitLod "
- "%v4float %sampled_image %coords123 ConstOffset %offsets2d",
- R"(@group(0) @binding(0) var x_10 : sampler;
+ ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
+ "%result = OpImageSampleProjImplicitLod "
+ "%v4float %sampled_image %coords123 ConstOffset %offsets2d",
+ R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSample(x_20, x_10, (coords123.xy / coords123.z), vec2<i32>(3i, 4i)))"}));
+ R"(textureSample(x_20, x_10, (coords123.xy / coords123.z), offsets2d))"}));
INSTANTIATE_TEST_SUITE_P(
ImageSampleProjImplicitLod_Bias,
@@ -2186,7 +2182,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleBias(x_20, x_10, (coords123.xy / coords123.z), 7.0f, vec2<i32>(3i, 4i)))"},
+ R"(textureSampleBias(x_20, x_10, (coords123.xy / coords123.z), 7.0f, offsets2d))"},
// OpImageSampleProjImplicitLod with Bias and unsigned ConstOffset
// Convert ConstOffset to signed
@@ -2198,7 +2194,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleBias(x_20, x_10, (coords123.xy / coords123.z), 7.0f, vec2<i32>(vec2<u32>(3u, 4u))))"}));
+ R"(textureSampleBias(x_20, x_10, (coords123.xy / coords123.z), 7.0f, vec2<i32>(u_offsets2d)))"}));
INSTANTIATE_TEST_SUITE_P(
ImageSampleProjExplicitLod_Lod,
@@ -2221,7 +2217,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleLevel(x_20, x_10, (coords123.xy / coords123.z), f1, vec2<i32>(3i, 4i)))"}));
+ R"(textureSampleLevel(x_20, x_10, (coords123.xy / coords123.z), f1, offsets2d))"}));
INSTANTIATE_TEST_SUITE_P(
ImageSampleProjExplicitLod_Grad,
@@ -2246,7 +2242,7 @@
R"(@group(0) @binding(0) var x_10 : sampler;
@group(2) @binding(1) var x_20 : texture_2d<f32>;)",
- R"(textureSampleGrad(x_20, x_10, (coords123.xy / coords123.z), vf12, vf21, vec2<i32>(3i, 4i)))"}));
+ R"(textureSampleGrad(x_20, x_10, (coords123.xy / coords123.z), vf12, vf21, offsets2d))"}));
INSTANTIATE_TEST_SUITE_P(
// Ordinary (non-comparison) sampling on a depth texture.
@@ -2291,7 +2287,7 @@
@group(2) @binding(1) var x_20 : texture_depth_2d;
)",
- R"(textureSampleCompare(x_20, x_10, (coords123.xy / coords123.z), f1, vec2<i32>(3i, 4i)))"}));
+ R"(textureSampleCompare(x_20, x_10, (coords123.xy / coords123.z), f1, offsets2d))"}));
INSTANTIATE_TEST_SUITE_P(
DISABLED_ImageSampleProjDrefExplicitLod_Lod,
diff --git a/src/tint/reader/spirv/parser_impl_module_var_test.cc b/src/tint/reader/spirv/parser_impl_module_var_test.cc
index c731fbf..2ae0435 100644
--- a/src/tint/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/tint/reader/spirv/parser_impl_module_var_test.cc
@@ -4132,6 +4132,35 @@
EXPECT_EQ(got, expected) << got;
}
+TEST_F(SpvModuleScopeVarParserTest, BuiltinPosition_MultiplePerVertexVariables) {
+ // This is not currently supported, so just make sure we produce a meaningful error instead of
+ // crashing.
+ const std::string assembly = R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint Vertex %main "main" %1
+ OpDecorate %struct Block
+ OpMemberDecorate %struct 0 BuiltIn Position
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %f32 = OpTypeFloat 32
+ %vec4f = OpTypeVector %f32 4
+ %struct = OpTypeStruct %vec4f
+ %struct_out_ptr = OpTypePointer Output %struct
+ %1 = OpVariable %struct_out_ptr Output
+ %2 = OpVariable %struct_out_ptr Output
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ auto p = parser(test::Assemble(assembly));
+
+ EXPECT_FALSE(p->Parse());
+ EXPECT_FALSE(p->success());
+ EXPECT_EQ(p->error(), "unsupported: multiple Position built-in variables in the same module");
+}
+
TEST_F(SpvModuleScopeVarParserTest, Input_FlattenArray_OneLevel) {
const std::string assembly = R"(
OpCapability Shader
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index 1fc7e84..1e1dad4 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -39,7 +39,9 @@
"tint::reader::wgsl requires the size of a std::string element "
"to be a single byte");
-static constexpr size_t kDefaultListSize = 512;
+// A token is ~80bytes. The 4k here comes from looking at the number of tokens in the benchmark
+// programs and being a bit bigger then those need (atan2-const-eval is the outlier here).
+static constexpr size_t kDefaultListSize = 4092;
bool read_blankspace(std::string_view str, size_t i, bool* is_blankspace, size_t* blankspace_size) {
// See https://www.w3.org/TR/WGSL/#blankspace
@@ -95,8 +97,12 @@
std::vector<Token> Lexer::Lex() {
std::vector<Token> tokens;
tokens.reserve(kDefaultListSize);
+
while (true) {
tokens.emplace_back(next());
+ if (tokens.back().IsEof() || tokens.back().IsError()) {
+ break;
+ }
// If the token can be split, we insert a placeholder element(s) into the stream to hold the
// split character.
@@ -104,11 +110,7 @@
for (size_t i = 0; i < num_placeholders; i++) {
auto src = tokens.back().source();
src.range.begin.column++;
- tokens.emplace_back(Token(Token::Type::kPlaceholder, src));
- }
-
- if (tokens.back().IsEof() || tokens.back().IsError()) {
- break;
+ tokens.emplace_back(Token::Type::kPlaceholder, src);
}
}
return tokens;
@@ -167,32 +169,32 @@
}
Token Lexer::next() {
- if (auto t = skip_blankspace_and_comments(); !t.IsUninitialized()) {
- return t;
+ if (auto t = skip_blankspace_and_comments(); t.has_value() && !t->IsUninitialized()) {
+ return std::move(t.value());
}
- if (auto t = try_hex_float(); !t.IsUninitialized()) {
- return t;
+ if (auto t = try_hex_float(); t.has_value() && !t->IsUninitialized()) {
+ return std::move(t.value());
}
- if (auto t = try_hex_integer(); !t.IsUninitialized()) {
- return t;
+ if (auto t = try_hex_integer(); t.has_value() && !t->IsUninitialized()) {
+ return std::move(t.value());
}
- if (auto t = try_float(); !t.IsUninitialized()) {
- return t;
+ if (auto t = try_float(); t.has_value() && !t->IsUninitialized()) {
+ return std::move(t.value());
}
- if (auto t = try_integer(); !t.IsUninitialized()) {
- return t;
+ if (auto t = try_integer(); t.has_value() && !t->IsUninitialized()) {
+ return std::move(t.value());
}
- if (auto t = try_ident(); !t.IsUninitialized()) {
- return t;
+ if (auto t = try_ident(); t.has_value() && !t->IsUninitialized()) {
+ return std::move(t.value());
}
- if (auto t = try_punctuation(); !t.IsUninitialized()) {
- return t;
+ if (auto t = try_punctuation(); t.has_value() && !t->IsUninitialized()) {
+ return std::move(t.value());
}
return {Token::Type::kError, begin_source(),
@@ -237,7 +239,7 @@
return line()[pos] == ch;
}
-Token Lexer::skip_blankspace_and_comments() {
+std::optional<Token> Lexer::skip_blankspace_and_comments() {
for (;;) {
auto loc = location_;
while (!is_eof()) {
@@ -249,7 +251,7 @@
bool is_blankspace;
size_t blankspace_size;
if (!read_blankspace(line(), pos(), &is_blankspace, &blankspace_size)) {
- return {Token::Type::kError, begin_source(), "invalid UTF-8"};
+ return Token{Token::Type::kError, begin_source(), "invalid UTF-8"};
}
if (!is_blankspace) {
break;
@@ -259,7 +261,7 @@
}
auto t = skip_comment();
- if (!t.IsUninitialized()) {
+ if (t.has_value() && !t->IsUninitialized()) {
return t;
}
@@ -270,18 +272,18 @@
}
}
if (is_eof()) {
- return {Token::Type::kEOF, begin_source()};
+ return Token{Token::Type::kEOF, begin_source()};
}
return {};
}
-Token Lexer::skip_comment() {
+std::optional<Token> Lexer::skip_comment() {
if (matches(pos(), "//")) {
// Line comment: ignore everything until the end of line.
while (!is_eol()) {
if (is_null()) {
- return {Token::Type::kError, begin_source(), "null character found"};
+ return Token{Token::Type::kError, begin_source(), "null character found"};
}
advance();
}
@@ -311,20 +313,20 @@
// Newline: skip and update source location.
advance_line();
} else if (is_null()) {
- return {Token::Type::kError, begin_source(), "null character found"};
+ return Token{Token::Type::kError, begin_source(), "null character found"};
} else {
// Anything else: skip and update source location.
advance();
}
}
if (depth > 0) {
- return {Token::Type::kError, source, "unterminated block comment"};
+ return Token{Token::Type::kError, source, "unterminated block comment"};
}
}
return {};
}
-Token Lexer::try_float() {
+std::optional<Token> Lexer::try_float() {
auto start = pos();
auto end = pos();
@@ -385,8 +387,8 @@
// If an 'e' or 'E' was present, then the number part must also be present.
if (!has_digits) {
const auto str = std::string{substr(start, end - start)};
- return {Token::Type::kError, source,
- "incomplete exponent for floating point literal: " + str};
+ return Token{Token::Type::kError, source,
+ "incomplete exponent for floating point literal: " + str};
}
}
@@ -452,9 +454,9 @@
if (!overflow && f) {
advance(1);
end_source(source);
- return {Token::Type::kFloatLiteral_F, source, static_cast<double>(f.Get())};
+ return Token{Token::Type::kFloatLiteral_F, source, static_cast<double>(f.Get())};
}
- return {Token::Type::kError, source, "value cannot be represented as 'f32'"};
+ return Token{Token::Type::kError, source, "value cannot be represented as 'f32'"};
}
if (has_h_suffix) {
@@ -462,23 +464,24 @@
if (!overflow && f) {
advance(1);
end_source(source);
- return {Token::Type::kFloatLiteral_H, source, static_cast<double>(f.Get())};
+ return Token{Token::Type::kFloatLiteral_H, source, static_cast<double>(f.Get())};
}
- return {Token::Type::kError, source, "value cannot be represented as 'f16'"};
+ return Token{Token::Type::kError, source, "value cannot be represented as 'f16'"};
}
end_source(source);
TINT_BEGIN_DISABLE_WARNING(FLOAT_EQUAL);
if (overflow || value == HUGE_VAL || -value == HUGE_VAL) {
- return {Token::Type::kError, source, "value cannot be represented as 'abstract-float'"};
+ return Token{Token::Type::kError, source,
+ "value cannot be represented as 'abstract-float'"};
} else {
- return {Token::Type::kFloatLiteral, source, value};
+ return Token{Token::Type::kFloatLiteral, source, value};
}
TINT_END_DISABLE_WARNING(FLOAT_EQUAL);
}
-Token Lexer::try_hex_float() {
+std::optional<Token> Lexer::try_hex_float() {
constexpr uint64_t kExponentBits = 11;
constexpr uint64_t kMantissaBits = 52;
constexpr uint64_t kTotalBits = 1 + kExponentBits + kMantissaBits;
@@ -593,7 +596,8 @@
// Skip leading 0s and the first 1
if (seen_prior_one_bits) {
if (!set_next_mantissa_bit_to(v != 0, true)) {
- return {Token::Type::kError, source, "mantissa is too large for hex float"};
+ return Token{Token::Type::kError, source,
+ "mantissa is too large for hex float"};
}
++exponent;
} else {
@@ -622,7 +626,8 @@
--exponent;
} else {
if (!set_next_mantissa_bit_to(v != 0, false)) {
- return {Token::Type::kError, source, "mantissa is too large for hex float"};
+ return Token{Token::Type::kError, source,
+ "mantissa is too large for hex float"};
}
}
}
@@ -663,7 +668,7 @@
// Check if we've overflowed input_exponent. This only matters when
// the mantissa is non-zero.
if (!is_zero && (prev_exponent > input_exponent)) {
- return {Token::Type::kError, source, "exponent is too large for hex float"};
+ return Token{Token::Type::kError, source, "exponent is too large for hex float"};
}
end++;
}
@@ -680,7 +685,7 @@
}
if (!has_exponent_digits) {
- return {Token::Type::kError, source, "expected an exponent value for hex float"};
+ return Token{Token::Type::kError, source, "expected an exponent value for hex float"};
}
}
@@ -696,7 +701,7 @@
const uint64_t kIntMax = static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
const uint64_t kMaxInputExponent = kIntMax - kExponentBias;
if (input_exponent > kMaxInputExponent) {
- return {Token::Type::kError, source, "exponent is too large for hex float"};
+ return Token{Token::Type::kError, source, "exponent is too large for hex float"};
}
// Compute exponent so far
@@ -746,7 +751,7 @@
if (signed_exponent >= kExponentMax || (signed_exponent == kExponentMax && mantissa != 0)) {
std::string type = has_f_suffix ? "f32" : (has_h_suffix ? "f16" : "abstract-float");
- return {Token::Type::kError, source, "value cannot be represented as '" + type + "'"};
+ return Token{Token::Type::kError, source, "value cannot be represented as '" + type + "'"};
}
// Combine sign, mantissa, and exponent
@@ -762,7 +767,7 @@
// Check value fits in f32
if (result_f64 < static_cast<double>(f32::kLowestValue) ||
result_f64 > static_cast<double>(f32::kHighestValue)) {
- return {Token::Type::kError, source, "value cannot be represented as 'f32'"};
+ return Token{Token::Type::kError, source, "value cannot be represented as 'f32'"};
}
// Check the value can be exactly represented, i.e. only high 23 mantissa bits are valid for
// normal f32 values, and less for subnormal f32 values. The rest low mantissa bits must be
@@ -803,19 +808,21 @@
} else if (abs_result_f64 != 0.0) {
// The result is smaller than the smallest subnormal f32 value, but not equal to zero.
// Such value will never be exactly represented by f32.
- return {Token::Type::kError, source, "value cannot be exactly represented as 'f32'"};
+ return Token{Token::Type::kError, source,
+ "value cannot be exactly represented as 'f32'"};
}
// Check the low 52-valid_mantissa_bits mantissa bits must be 0.
TINT_ASSERT(Reader, (0 <= valid_mantissa_bits) && (valid_mantissa_bits <= 23));
if (result_u64 & ((uint64_t(1) << (52 - valid_mantissa_bits)) - 1)) {
- return {Token::Type::kError, source, "value cannot be exactly represented as 'f32'"};
+ return Token{Token::Type::kError, source,
+ "value cannot be exactly represented as 'f32'"};
}
- return {Token::Type::kFloatLiteral_F, source, result_f64};
+ return Token{Token::Type::kFloatLiteral_F, source, result_f64};
} else if (has_h_suffix) {
// Check value fits in f16
if (result_f64 < static_cast<double>(f16::kLowestValue) ||
result_f64 > static_cast<double>(f16::kHighestValue)) {
- return {Token::Type::kError, source, "value cannot be represented as 'f16'"};
+ return Token{Token::Type::kError, source, "value cannot be represented as 'f16'"};
}
// Check the value can be exactly represented, i.e. only high 10 mantissa bits are valid for
// normal f16 values, and less for subnormal f16 values. The rest low mantissa bits must be
@@ -854,17 +861,19 @@
} else if (abs_result_f64 != 0.0) {
// The result is smaller than the smallest subnormal f16 value, but not equal to zero.
// Such value will never be exactly represented by f16.
- return {Token::Type::kError, source, "value cannot be exactly represented as 'f16'"};
+ return Token{Token::Type::kError, source,
+ "value cannot be exactly represented as 'f16'"};
}
// Check the low 52-valid_mantissa_bits mantissa bits must be 0.
TINT_ASSERT(Reader, (0 <= valid_mantissa_bits) && (valid_mantissa_bits <= 10));
if (result_u64 & ((uint64_t(1) << (52 - valid_mantissa_bits)) - 1)) {
- return {Token::Type::kError, source, "value cannot be exactly represented as 'f16'"};
+ return Token{Token::Type::kError, source,
+ "value cannot be exactly represented as 'f16'"};
}
- return {Token::Type::kFloatLiteral_H, source, result_f64};
+ return Token{Token::Type::kFloatLiteral_H, source, result_f64};
}
- return {Token::Type::kFloatLiteral, source, result_f64};
+ return Token{Token::Type::kFloatLiteral, source, result_f64};
}
Token Lexer::build_token_from_int_if_possible(Source source,
@@ -911,7 +920,7 @@
return {Token::Type::kIntLiteral, source, value};
}
-Token Lexer::try_hex_integer() {
+std::optional<Token> Lexer::try_hex_integer() {
auto start = pos();
auto curr = start;
@@ -924,14 +933,14 @@
}
if (!is_hex(at(curr))) {
- return {Token::Type::kError, source,
- "integer or float hex literal has no significant digits"};
+ return Token{Token::Type::kError, source,
+ "integer or float hex literal has no significant digits"};
}
return build_token_from_int_if_possible(source, curr, curr - start, 16);
}
-Token Lexer::try_integer() {
+std::optional<Token> Lexer::try_integer() {
auto start = pos();
auto curr = start;
@@ -945,14 +954,14 @@
// are not allowed.
if (auto next = curr + 1; next < length()) {
if (at(curr) == '0' && is_digit(at(next))) {
- return {Token::Type::kError, source, "integer literal cannot have leading 0s"};
+ return Token{Token::Type::kError, source, "integer literal cannot have leading 0s"};
}
}
return build_token_from_int_if_possible(source, start, 0, 10);
}
-Token Lexer::try_ident() {
+std::optional<Token> Lexer::try_ident() {
auto source = begin_source();
auto start = pos();
@@ -962,7 +971,7 @@
auto [code_point, n] = utils::utf8::Decode(utf8, length() - pos());
if (n == 0) {
advance(); // Skip the bad byte.
- return {Token::Type::kError, source, "invalid UTF-8"};
+ return Token{Token::Type::kError, source, "invalid UTF-8"};
}
if (code_point != utils::CodePoint('_') && !code_point.IsXIDStart()) {
return {};
@@ -977,7 +986,7 @@
auto [code_point, n] = utils::utf8::Decode(utf8, line().size() - pos());
if (n == 0) {
advance(); // Skip the bad byte.
- return {Token::Type::kError, source, "invalid UTF-8"};
+ return Token{Token::Type::kError, source, "invalid UTF-8"};
}
if (!code_point.IsXIDContinue()) {
break;
@@ -999,15 +1008,14 @@
auto str = substr(start, pos() - start);
end_source(source);
- auto t = check_keyword(source, str);
- if (!t.IsUninitialized()) {
+ if (auto t = check_keyword(source, str); t.has_value() && !t->IsUninitialized()) {
return t;
}
- return {Token::Type::kIdentifier, source, str};
+ return Token{Token::Type::kIdentifier, source, str};
}
-Token Lexer::try_punctuation() {
+std::optional<Token> Lexer::try_punctuation() {
auto source = begin_source();
auto type = Token::Type::kUninitialized;
@@ -1177,97 +1185,99 @@
type = Token::Type::kXor;
advance(1);
}
+ } else {
+ return {};
}
end_source(source);
- return {type, source};
+ return Token{type, source};
}
-Token Lexer::check_keyword(const Source& source, std::string_view str) {
+std::optional<Token> Lexer::check_keyword(const Source& source, std::string_view str) {
if (str == "alias") {
- return {Token::Type::kAlias, source, "alias"};
+ return Token{Token::Type::kAlias, source, "alias"};
}
if (str == "bitcast") {
- return {Token::Type::kBitcast, source, "bitcast"};
+ return Token{Token::Type::kBitcast, source, "bitcast"};
}
if (str == "break") {
- return {Token::Type::kBreak, source, "break"};
+ return Token{Token::Type::kBreak, source, "break"};
}
if (str == "case") {
- return {Token::Type::kCase, source, "case"};
+ return Token{Token::Type::kCase, source, "case"};
}
if (str == "const") {
- return {Token::Type::kConst, source, "const"};
+ return Token{Token::Type::kConst, source, "const"};
}
if (str == "const_assert") {
- return {Token::Type::kConstAssert, source, "const_assert"};
+ return Token{Token::Type::kConstAssert, source, "const_assert"};
}
if (str == "continue") {
- return {Token::Type::kContinue, source, "continue"};
+ return Token{Token::Type::kContinue, source, "continue"};
}
if (str == "continuing") {
- return {Token::Type::kContinuing, source, "continuing"};
+ return Token{Token::Type::kContinuing, source, "continuing"};
}
if (str == "diagnostic") {
- return {Token::Type::kDiagnostic, source, "diagnostic"};
+ return Token{Token::Type::kDiagnostic, source, "diagnostic"};
}
if (str == "discard") {
- return {Token::Type::kDiscard, source, "discard"};
+ return Token{Token::Type::kDiscard, source, "discard"};
}
if (str == "default") {
- return {Token::Type::kDefault, source, "default"};
+ return Token{Token::Type::kDefault, source, "default"};
}
if (str == "else") {
- return {Token::Type::kElse, source, "else"};
+ return Token{Token::Type::kElse, source, "else"};
}
if (str == "enable") {
- return {Token::Type::kEnable, source, "enable"};
+ return Token{Token::Type::kEnable, source, "enable"};
}
if (str == "fallthrough") {
- return {Token::Type::kFallthrough, source, "fallthrough"};
+ return Token{Token::Type::kFallthrough, source, "fallthrough"};
}
if (str == "false") {
- return {Token::Type::kFalse, source, "false"};
+ return Token{Token::Type::kFalse, source, "false"};
}
if (str == "fn") {
- return {Token::Type::kFn, source, "fn"};
+ return Token{Token::Type::kFn, source, "fn"};
}
if (str == "for") {
- return {Token::Type::kFor, source, "for"};
+ return Token{Token::Type::kFor, source, "for"};
}
if (str == "if") {
- return {Token::Type::kIf, source, "if"};
+ return Token{Token::Type::kIf, source, "if"};
}
if (str == "let") {
- return {Token::Type::kLet, source, "let"};
+ return Token{Token::Type::kLet, source, "let"};
}
if (str == "loop") {
- return {Token::Type::kLoop, source, "loop"};
+ return Token{Token::Type::kLoop, source, "loop"};
}
if (str == "override") {
- return {Token::Type::kOverride, source, "override"};
+ return Token{Token::Type::kOverride, source, "override"};
}
if (str == "return") {
- return {Token::Type::kReturn, source, "return"};
+ return Token{Token::Type::kReturn, source, "return"};
}
if (str == "requires") {
- return {Token::Type::kRequires, source, "requires"};
+ return Token{Token::Type::kRequires, source, "requires"};
}
if (str == "struct") {
- return {Token::Type::kStruct, source, "struct"};
+ return Token{Token::Type::kStruct, source, "struct"};
}
if (str == "switch") {
- return {Token::Type::kSwitch, source, "switch"};
+ return Token{Token::Type::kSwitch, source, "switch"};
}
if (str == "true") {
- return {Token::Type::kTrue, source, "true"};
+ return Token{Token::Type::kTrue, source, "true"};
}
if (str == "var") {
- return {Token::Type::kVar, source, "var"};
+ return Token{Token::Type::kVar, source, "var"};
}
if (str == "while") {
- return {Token::Type::kWhile, source, "while"};
+ return Token{Token::Type::kWhile, source, "while"};
}
return {};
}
diff --git a/src/tint/reader/wgsl/lexer.h b/src/tint/reader/wgsl/lexer.h
index ac41dae..e56a79a 100644
--- a/src/tint/reader/wgsl/lexer.h
+++ b/src/tint/reader/wgsl/lexer.h
@@ -15,6 +15,7 @@
#ifndef SRC_TINT_READER_WGSL_LEXER_H_
#define SRC_TINT_READER_WGSL_LEXER_H_
+#include <optional>
#include <string>
#include <vector>
@@ -40,19 +41,19 @@
/// Advances past blankspace and comments, if present at the current position.
/// @returns error token, EOF, or uninitialized
- Token skip_blankspace_and_comments();
+ std::optional<Token> skip_blankspace_and_comments();
/// Advances past a comment at the current position, if one exists.
/// Returns an error if there was an unterminated block comment,
/// or a null character was present.
/// @returns uninitialized token on success, or error
- Token skip_comment();
+ std::optional<Token> skip_comment();
Token build_token_from_int_if_possible(Source source,
size_t start,
size_t prefix_count,
int32_t base);
- Token check_keyword(const Source&, std::string_view);
+ std::optional<Token> check_keyword(const Source&, std::string_view);
/// The try_* methods have the following in common:
/// - They assume there is at least one character to be consumed,
@@ -62,12 +63,12 @@
/// - Some can return an error token.
/// - Otherwise they return an uninitialized token when they did not
/// match a token of the specfied kind.
- Token try_float();
- Token try_hex_float();
- Token try_hex_integer();
- Token try_ident();
- Token try_integer();
- Token try_punctuation();
+ std::optional<Token> try_float();
+ std::optional<Token> try_hex_float();
+ std::optional<Token> try_hex_integer();
+ std::optional<Token> try_ident();
+ std::optional<Token> try_integer();
+ std::optional<Token> try_punctuation();
Source begin_source() const;
void end_source(Source&) const;
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 19571a7..58050b9 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -3115,13 +3115,14 @@
}
sem::ValueExpression* Resolver::MemberAccessor(const ast::MemberAccessorExpression* expr) {
- auto* structure = sem_.TypeOf(expr->object);
- auto* storage_ty = structure->UnwrapRef();
auto* object = sem_.GetVal(expr->object);
if (!object) {
return nullptr;
}
+ auto* object_ty = object->Type();
+ auto* storage_ty = object_ty->UnwrapRef();
+
auto* root_ident = object->RootIdentifier();
const type::Type* ty = nullptr;
@@ -3152,7 +3153,7 @@
ty = member->Type();
// If we're extracting from a reference, we return a reference.
- if (auto* ref = structure->As<type::Reference>()) {
+ if (auto* ref = object_ty->As<type::Reference>()) {
ty = builder_->create<type::Reference>(ty, ref->AddressSpace(), ref->Access());
}
@@ -3221,7 +3222,7 @@
// A single element swizzle is just the type of the vector.
ty = vec->type();
// If we're extracting from a reference, we return a reference.
- if (auto* ref = structure->As<type::Reference>()) {
+ if (auto* ref = object_ty->As<type::Reference>()) {
ty = builder_->create<type::Reference>(ty, ref->AddressSpace(), ref->Access());
}
} else {
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index a601ef0..5df2fae 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -1239,6 +1239,15 @@
EXPECT_EQ(func_sem->WorkgroupSize()[2], 3u);
}
+TEST_F(ResolverTest, Expr_MemberAccessor_Type) {
+ auto* mem = MemberAccessor(Ident(Source{{12, 34}}, "f32"), "member");
+ WrapInFunction(mem);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), R"(12:34 error: cannot use type 'f32' as value
+12:34 note: are you missing '()' for value constructor?)");
+}
+
TEST_F(ResolverTest, Expr_MemberAccessor_Struct) {
auto* st = Structure(
"S", utils::Vector{Member("first_member", ty.i32()), Member("second_member", ty.f32())});
diff --git a/src/tint/resolver/sem_helper.cc b/src/tint/resolver/sem_helper.cc
index 2357ce1..dd2341f 100644
--- a/src/tint/resolver/sem_helper.cc
+++ b/src/tint/resolver/sem_helper.cc
@@ -105,8 +105,7 @@
ErrorUnexpectedExprKind(expr, "value");
if (auto* ty_expr = expr->As<sem::TypeExpression>()) {
if (auto* ident = ty_expr->Declaration()->As<ast::IdentifierExpression>()) {
- AddNote("are you missing '()' for value constructor?",
- Source{{ident->source.range.end}});
+ AddNote("are you missing '()' for value constructor?", ident->source.End());
}
}
}
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param.cc b/src/tint/transform/module_scope_var_to_entry_point_param.cc
index e82b1bd..a394ef6 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param.cc
@@ -85,6 +85,11 @@
return;
}
+ if (!str->Declaration()) {
+ // The struct is a built-in structure that we do not need to declare.
+ return;
+ }
+
// Recurse into members.
for (auto* member : str->Members()) {
CloneStructTypes(member->Type());
diff --git a/src/tint/transform/transform.cc b/src/tint/transform/transform.cc
index 73ee3ae..2002d02 100644
--- a/src/tint/transform/transform.cc
+++ b/src/tint/transform/transform.cc
@@ -144,7 +144,7 @@
return ctx.dst->ty.array(el, u32(count.value()), std::move(attrs));
}
if (auto* s = ty->As<sem::Struct>()) {
- return ctx.dst->ty(ctx.Clone(s->Declaration()->name->symbol));
+ return ctx.dst->ty(ctx.Clone(s->Name()));
}
if (auto* s = ty->As<type::Reference>()) {
return CreateASTTypeFor(ctx, s->StoreType());
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index df9ff4b..0435918 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -347,7 +347,7 @@
// WGSL can ignore the invariant attribute on pre MSL 2.1 devices.
// See: https://github.com/gpuweb/gpuweb/issues/893#issuecomment-745537465
line(&helpers_) << "#if __METAL_VERSION__ >= 210";
- line(&helpers_) << "#define " << invariant_define_name_ << " @invariant";
+ line(&helpers_) << "#define " << invariant_define_name_ << " [[invariant]]";
line(&helpers_) << "#else";
line(&helpers_) << "#define " << invariant_define_name_;
line(&helpers_) << "#endif";
diff --git a/src/tint/writer/msl/generator_impl_test.cc b/src/tint/writer/msl/generator_impl_test.cc
index 4772d46..41333db 100644
--- a/src/tint/writer/msl/generator_impl_test.cc
+++ b/src/tint/writer/msl/generator_impl_test.cc
@@ -119,7 +119,7 @@
using namespace metal;
#if __METAL_VERSION__ >= 210
-#define TINT_INVARIANT @invariant
+#define TINT_INVARIANT [[invariant]]
#else
#define TINT_INVARIANT
#endif