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