1fd4e5da5Sopenharmony_ci// Copyright (c) 2019 Google LLC 2fd4e5da5Sopenharmony_ci// 3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License. 5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at 6fd4e5da5Sopenharmony_ci// 7fd4e5da5Sopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 8fd4e5da5Sopenharmony_ci// 9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software 10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and 13fd4e5da5Sopenharmony_ci// limitations under the License. 14fd4e5da5Sopenharmony_ci 15fd4e5da5Sopenharmony_ci#include "test/fuzz/fuzz_test_util.h" 16fd4e5da5Sopenharmony_ci 17fd4e5da5Sopenharmony_ci#include "gtest/gtest.h" 18fd4e5da5Sopenharmony_ci 19fd4e5da5Sopenharmony_ci#include <fstream> 20fd4e5da5Sopenharmony_ci#include <iostream> 21fd4e5da5Sopenharmony_ci 22fd4e5da5Sopenharmony_ci#include "source/opt/def_use_manager.h" 23fd4e5da5Sopenharmony_ci#include "tools/io.h" 24fd4e5da5Sopenharmony_ci 25fd4e5da5Sopenharmony_cinamespace spvtools { 26fd4e5da5Sopenharmony_cinamespace fuzz { 27fd4e5da5Sopenharmony_ci 28fd4e5da5Sopenharmony_ciconst spvtools::MessageConsumer kConsoleMessageConsumer = 29fd4e5da5Sopenharmony_ci [](spv_message_level_t level, const char*, const spv_position_t& position, 30fd4e5da5Sopenharmony_ci const char* message) -> void { 31fd4e5da5Sopenharmony_ci switch (level) { 32fd4e5da5Sopenharmony_ci case SPV_MSG_FATAL: 33fd4e5da5Sopenharmony_ci case SPV_MSG_INTERNAL_ERROR: 34fd4e5da5Sopenharmony_ci case SPV_MSG_ERROR: 35fd4e5da5Sopenharmony_ci std::cerr << "error: line " << position.index << ": " << message 36fd4e5da5Sopenharmony_ci << std::endl; 37fd4e5da5Sopenharmony_ci break; 38fd4e5da5Sopenharmony_ci case SPV_MSG_WARNING: 39fd4e5da5Sopenharmony_ci std::cout << "warning: line " << position.index << ": " << message 40fd4e5da5Sopenharmony_ci << std::endl; 41fd4e5da5Sopenharmony_ci break; 42fd4e5da5Sopenharmony_ci case SPV_MSG_INFO: 43fd4e5da5Sopenharmony_ci std::cout << "info: line " << position.index << ": " << message 44fd4e5da5Sopenharmony_ci << std::endl; 45fd4e5da5Sopenharmony_ci break; 46fd4e5da5Sopenharmony_ci default: 47fd4e5da5Sopenharmony_ci break; 48fd4e5da5Sopenharmony_ci } 49fd4e5da5Sopenharmony_ci}; 50fd4e5da5Sopenharmony_ci 51fd4e5da5Sopenharmony_cibool IsEqual(const spv_target_env env, 52fd4e5da5Sopenharmony_ci const std::vector<uint32_t>& expected_binary, 53fd4e5da5Sopenharmony_ci const std::vector<uint32_t>& actual_binary) { 54fd4e5da5Sopenharmony_ci if (expected_binary == actual_binary) { 55fd4e5da5Sopenharmony_ci return true; 56fd4e5da5Sopenharmony_ci } 57fd4e5da5Sopenharmony_ci SpirvTools t(env); 58fd4e5da5Sopenharmony_ci std::string expected_disassembled; 59fd4e5da5Sopenharmony_ci std::string actual_disassembled; 60fd4e5da5Sopenharmony_ci if (!t.Disassemble(expected_binary, &expected_disassembled, 61fd4e5da5Sopenharmony_ci kFuzzDisassembleOption)) { 62fd4e5da5Sopenharmony_ci return false; 63fd4e5da5Sopenharmony_ci } 64fd4e5da5Sopenharmony_ci if (!t.Disassemble(actual_binary, &actual_disassembled, 65fd4e5da5Sopenharmony_ci kFuzzDisassembleOption)) { 66fd4e5da5Sopenharmony_ci return false; 67fd4e5da5Sopenharmony_ci } 68fd4e5da5Sopenharmony_ci // Using expect gives us a string diff if the strings are not the same. 69fd4e5da5Sopenharmony_ci EXPECT_EQ(expected_disassembled, actual_disassembled); 70fd4e5da5Sopenharmony_ci // We then return the result of the equality comparison, to be used by an 71fd4e5da5Sopenharmony_ci // assertion in the test root function. 72fd4e5da5Sopenharmony_ci return expected_disassembled == actual_disassembled; 73fd4e5da5Sopenharmony_ci} 74fd4e5da5Sopenharmony_ci 75fd4e5da5Sopenharmony_cibool IsEqual(const spv_target_env env, const std::string& expected_text, 76fd4e5da5Sopenharmony_ci const std::vector<uint32_t>& actual_binary) { 77fd4e5da5Sopenharmony_ci std::vector<uint32_t> expected_binary; 78fd4e5da5Sopenharmony_ci SpirvTools t(env); 79fd4e5da5Sopenharmony_ci if (!t.Assemble(expected_text, &expected_binary, kFuzzAssembleOption)) { 80fd4e5da5Sopenharmony_ci return false; 81fd4e5da5Sopenharmony_ci } 82fd4e5da5Sopenharmony_ci return IsEqual(env, expected_binary, actual_binary); 83fd4e5da5Sopenharmony_ci} 84fd4e5da5Sopenharmony_ci 85fd4e5da5Sopenharmony_cibool IsEqual(const spv_target_env env, const std::string& expected_text, 86fd4e5da5Sopenharmony_ci const opt::IRContext* actual_ir) { 87fd4e5da5Sopenharmony_ci std::vector<uint32_t> actual_binary; 88fd4e5da5Sopenharmony_ci actual_ir->module()->ToBinary(&actual_binary, false); 89fd4e5da5Sopenharmony_ci return IsEqual(env, expected_text, actual_binary); 90fd4e5da5Sopenharmony_ci} 91fd4e5da5Sopenharmony_ci 92fd4e5da5Sopenharmony_cibool IsEqual(const spv_target_env env, const opt::IRContext* ir_1, 93fd4e5da5Sopenharmony_ci const opt::IRContext* ir_2) { 94fd4e5da5Sopenharmony_ci std::vector<uint32_t> binary_1; 95fd4e5da5Sopenharmony_ci ir_1->module()->ToBinary(&binary_1, false); 96fd4e5da5Sopenharmony_ci std::vector<uint32_t> binary_2; 97fd4e5da5Sopenharmony_ci ir_2->module()->ToBinary(&binary_2, false); 98fd4e5da5Sopenharmony_ci return IsEqual(env, binary_1, binary_2); 99fd4e5da5Sopenharmony_ci} 100fd4e5da5Sopenharmony_ci 101fd4e5da5Sopenharmony_cibool IsEqual(const spv_target_env env, const std::vector<uint32_t>& binary_1, 102fd4e5da5Sopenharmony_ci const opt::IRContext* ir_2) { 103fd4e5da5Sopenharmony_ci std::vector<uint32_t> binary_2; 104fd4e5da5Sopenharmony_ci ir_2->module()->ToBinary(&binary_2, false); 105fd4e5da5Sopenharmony_ci return IsEqual(env, binary_1, binary_2); 106fd4e5da5Sopenharmony_ci} 107fd4e5da5Sopenharmony_ci 108fd4e5da5Sopenharmony_cistd::string ToString(spv_target_env env, const opt::IRContext* ir) { 109fd4e5da5Sopenharmony_ci std::vector<uint32_t> binary; 110fd4e5da5Sopenharmony_ci ir->module()->ToBinary(&binary, false); 111fd4e5da5Sopenharmony_ci return ToString(env, binary); 112fd4e5da5Sopenharmony_ci} 113fd4e5da5Sopenharmony_ci 114fd4e5da5Sopenharmony_cistd::string ToString(spv_target_env env, const std::vector<uint32_t>& binary) { 115fd4e5da5Sopenharmony_ci SpirvTools t(env); 116fd4e5da5Sopenharmony_ci std::string result; 117fd4e5da5Sopenharmony_ci t.Disassemble(binary, &result, kFuzzDisassembleOption); 118fd4e5da5Sopenharmony_ci return result; 119fd4e5da5Sopenharmony_ci} 120fd4e5da5Sopenharmony_ci 121fd4e5da5Sopenharmony_civoid DumpShader(opt::IRContext* context, const char* filename) { 122fd4e5da5Sopenharmony_ci std::vector<uint32_t> binary; 123fd4e5da5Sopenharmony_ci context->module()->ToBinary(&binary, false); 124fd4e5da5Sopenharmony_ci DumpShader(binary, filename); 125fd4e5da5Sopenharmony_ci} 126fd4e5da5Sopenharmony_ci 127fd4e5da5Sopenharmony_civoid DumpShader(const std::vector<uint32_t>& binary, const char* filename) { 128fd4e5da5Sopenharmony_ci auto write_file_succeeded = 129fd4e5da5Sopenharmony_ci WriteFile(filename, "wb", &binary[0], binary.size()); 130fd4e5da5Sopenharmony_ci if (!write_file_succeeded) { 131fd4e5da5Sopenharmony_ci std::cerr << "Failed to dump shader" << std::endl; 132fd4e5da5Sopenharmony_ci } 133fd4e5da5Sopenharmony_ci} 134fd4e5da5Sopenharmony_ci 135fd4e5da5Sopenharmony_civoid DumpTransformationsBinary( 136fd4e5da5Sopenharmony_ci const protobufs::TransformationSequence& transformations, 137fd4e5da5Sopenharmony_ci const char* filename) { 138fd4e5da5Sopenharmony_ci std::ofstream transformations_file; 139fd4e5da5Sopenharmony_ci transformations_file.open(filename, std::ios::out | std::ios::binary); 140fd4e5da5Sopenharmony_ci transformations.SerializeToOstream(&transformations_file); 141fd4e5da5Sopenharmony_ci transformations_file.close(); 142fd4e5da5Sopenharmony_ci} 143fd4e5da5Sopenharmony_ci 144fd4e5da5Sopenharmony_civoid DumpTransformationsJson( 145fd4e5da5Sopenharmony_ci const protobufs::TransformationSequence& transformations, 146fd4e5da5Sopenharmony_ci const char* filename) { 147fd4e5da5Sopenharmony_ci std::string json_string; 148fd4e5da5Sopenharmony_ci auto json_options = google::protobuf::util::JsonOptions(); 149fd4e5da5Sopenharmony_ci json_options.add_whitespace = true; 150fd4e5da5Sopenharmony_ci auto json_generation_status = google::protobuf::util::MessageToJsonString( 151fd4e5da5Sopenharmony_ci transformations, &json_string, json_options); 152fd4e5da5Sopenharmony_ci if (json_generation_status.ok()) { 153fd4e5da5Sopenharmony_ci std::ofstream transformations_json_file(filename); 154fd4e5da5Sopenharmony_ci transformations_json_file << json_string; 155fd4e5da5Sopenharmony_ci transformations_json_file.close(); 156fd4e5da5Sopenharmony_ci } 157fd4e5da5Sopenharmony_ci} 158fd4e5da5Sopenharmony_ci 159fd4e5da5Sopenharmony_civoid ApplyAndCheckFreshIds( 160fd4e5da5Sopenharmony_ci const Transformation& transformation, opt::IRContext* ir_context, 161fd4e5da5Sopenharmony_ci TransformationContext* transformation_context, 162fd4e5da5Sopenharmony_ci const std::unordered_set<uint32_t>& issued_overflow_ids) { 163fd4e5da5Sopenharmony_ci // To ensure that we cover all ToMessage and message-based constructor methods 164fd4e5da5Sopenharmony_ci // in our tests, we turn this into a message and back into a transformation, 165fd4e5da5Sopenharmony_ci // and use the reconstructed transformation in the rest of the function. 166fd4e5da5Sopenharmony_ci auto message = transformation.ToMessage(); 167fd4e5da5Sopenharmony_ci auto reconstructed_transformation = Transformation::FromMessage(message); 168fd4e5da5Sopenharmony_ci 169fd4e5da5Sopenharmony_ci opt::analysis::DefUseManager::IdToDefMap before_transformation = 170fd4e5da5Sopenharmony_ci ir_context->get_def_use_mgr()->id_to_defs(); 171fd4e5da5Sopenharmony_ci reconstructed_transformation->Apply(ir_context, transformation_context); 172fd4e5da5Sopenharmony_ci opt::analysis::DefUseManager::IdToDefMap after_transformation = 173fd4e5da5Sopenharmony_ci ir_context->get_def_use_mgr()->id_to_defs(); 174fd4e5da5Sopenharmony_ci std::unordered_set<uint32_t> fresh_ids_for_transformation = 175fd4e5da5Sopenharmony_ci reconstructed_transformation->GetFreshIds(); 176fd4e5da5Sopenharmony_ci for (auto& entry : after_transformation) { 177fd4e5da5Sopenharmony_ci uint32_t id = entry.first; 178fd4e5da5Sopenharmony_ci bool introduced_by_transformation_message = 179fd4e5da5Sopenharmony_ci fresh_ids_for_transformation.count(id); 180fd4e5da5Sopenharmony_ci bool introduced_by_overflow_ids = issued_overflow_ids.count(id); 181fd4e5da5Sopenharmony_ci ASSERT_FALSE(introduced_by_transformation_message && 182fd4e5da5Sopenharmony_ci introduced_by_overflow_ids); 183fd4e5da5Sopenharmony_ci if (before_transformation.count(entry.first)) { 184fd4e5da5Sopenharmony_ci ASSERT_FALSE(introduced_by_transformation_message || 185fd4e5da5Sopenharmony_ci introduced_by_overflow_ids); 186fd4e5da5Sopenharmony_ci } else { 187fd4e5da5Sopenharmony_ci ASSERT_TRUE(introduced_by_transformation_message || 188fd4e5da5Sopenharmony_ci introduced_by_overflow_ids); 189fd4e5da5Sopenharmony_ci } 190fd4e5da5Sopenharmony_ci } 191fd4e5da5Sopenharmony_ci} 192fd4e5da5Sopenharmony_ci 193fd4e5da5Sopenharmony_ci} // namespace fuzz 194fd4e5da5Sopenharmony_ci} // namespace spvtools 195