| // Copyright 2022 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/bench/benchmark.h" | 
 |  | 
 | #include <filesystem> | 
 | #include <iostream> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "src/tint/utils/string.h" | 
 | #include "src/tint/utils/string_stream.h" | 
 |  | 
 | namespace tint::bench { | 
 | namespace { | 
 |  | 
 | std::filesystem::path kInputFileDir; | 
 |  | 
 | /// 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> | 
 | std::variant<std::vector<T>, Error> ReadFile(const std::string& input_file) { | 
 |     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) { | 
 |         return Error{"Failed to open " + input_file}; | 
 |     } | 
 |  | 
 |     fseek(file, 0, SEEK_END); | 
 |     const auto file_size = static_cast<size_t>(ftell(file)); | 
 |     if (0 != (file_size % sizeof(T))) { | 
 |         utils::StringStream err; | 
 |         err << "File " << input_file | 
 |             << " does not contain an integral number of objects: " << file_size | 
 |             << " bytes in the file, require " << sizeof(T) << " bytes per object"; | 
 |         fclose(file); | 
 |         return Error{err.str()}; | 
 |     } | 
 |     fseek(file, 0, SEEK_SET); | 
 |  | 
 |     std::vector<T> buffer; | 
 |     buffer.resize(file_size / sizeof(T)); | 
 |  | 
 |     size_t bytes_read = fread(buffer.data(), 1, file_size, file); | 
 |     fclose(file); | 
 |     if (bytes_read != file_size) { | 
 |         return Error{"Failed to read " + input_file}; | 
 |     } | 
 |  | 
 |     return buffer; | 
 | } | 
 |  | 
 | bool FindBenchmarkInputDir() { | 
 |     // Attempt to find the benchmark input files by searching up from the current | 
 |     // working directory. | 
 |     auto path = std::filesystem::current_path(); | 
 |     while (std::filesystem::is_directory(path)) { | 
 |         auto test = path / "test" / "tint" / "benchmark"; | 
 |         if (std::filesystem::is_directory(test)) { | 
 |             kInputFileDir = test; | 
 |             return true; | 
 |         } | 
 |         auto parent = path.parent_path(); | 
 |         if (path == parent) { | 
 |             break; | 
 |         } | 
 |         path = parent; | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | std::variant<tint::Source::File, Error> LoadInputFile(std::string name) { | 
 |     auto path = std::filesystem::path(name).is_absolute() ? name : (kInputFileDir / name).string(); | 
 |     if (utils::HasSuffix(path, ".wgsl")) { | 
 |         auto data = ReadFile<uint8_t>(path); | 
 |         if (auto* buf = std::get_if<std::vector<uint8_t>>(&data)) { | 
 |             return tint::Source::File(path, std::string(buf->begin(), buf->end())); | 
 |         } | 
 |         return std::get<Error>(data); | 
 |     } | 
 |     if (utils::HasSuffix(path, ".spv")) { | 
 |         auto spirv = ReadFile<uint32_t>(path); | 
 |         if (auto* buf = std::get_if<std::vector<uint32_t>>(&spirv)) { | 
 |             auto program = tint::reader::spirv::Parse(*buf, {}); | 
 |             if (!program.IsValid()) { | 
 |                 return Error{program.Diagnostics().str()}; | 
 |             } | 
 |             auto result = tint::writer::wgsl::Generate(&program, {}); | 
 |             if (!result.success) { | 
 |                 return Error{result.error}; | 
 |             } | 
 |             return tint::Source::File(path, result.wgsl); | 
 |         } | 
 |         return std::get<Error>(spirv); | 
 |     } | 
 |     return Error{"unsupported file extension: '" + name + "'"}; | 
 | } | 
 |  | 
 | std::variant<ProgramAndFile, Error> LoadProgram(std::string name) { | 
 |     auto res = bench::LoadInputFile(name); | 
 |     if (auto err = std::get_if<bench::Error>(&res)) { | 
 |         return *err; | 
 |     } | 
 |     auto file = std::make_unique<Source::File>(std::move(std::get<Source::File>(res))); | 
 |     auto program = reader::wgsl::Parse(file.get()); | 
 |     if (program.Diagnostics().contains_errors()) { | 
 |         return Error{program.Diagnostics().str()}; | 
 |     } | 
 |     return ProgramAndFile{std::move(program), std::move(file)}; | 
 | } | 
 |  | 
 | }  // namespace tint::bench | 
 |  | 
 | int main(int argc, char** argv) { | 
 |     benchmark::Initialize(&argc, argv); | 
 |     if (benchmark::ReportUnrecognizedArguments(argc, argv)) { | 
 |         return 1; | 
 |     } | 
 |     if (!tint::bench::FindBenchmarkInputDir()) { | 
 |         std::cerr << "failed to locate benchmark input files" << std::endl; | 
 |         return 1; | 
 |     } | 
 |     benchmark::RunSpecifiedBenchmarks(); | 
 | } |