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
25 namespace spvtools {
26 namespace fuzz {
27
28 const spvtools::MessageConsumer kConsoleMessageConsumer =
29 [](spv_message_level_t level, const char*, const spv_position_t& position,
__anone52228ce0102(spv_message_level_t level, const char*, const spv_position_t& position, const char* message) 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
IsEqual(const spv_target_env env,const std::vector<uint32_t> & expected_binary,const std::vector<uint32_t> & actual_binary)51 bool 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
IsEqual(const spv_target_env env,const std::string & expected_text,const std::vector<uint32_t> & actual_binary)75 bool 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
IsEqual(const spv_target_env env,const std::string & expected_text,const opt::IRContext * actual_ir)85 bool 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
IsEqual(const spv_target_env env,const opt::IRContext * ir_1,const opt::IRContext * ir_2)92 bool 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
IsEqual(const spv_target_env env,const std::vector<uint32_t> & binary_1,const opt::IRContext * ir_2)101 bool 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
ToString(spv_target_env env,const opt::IRContext * ir)108 std::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
ToString(spv_target_env env,const std::vector<uint32_t> & binary)114 std::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
DumpShader(opt::IRContext * context,const char * filename)121 void 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
DumpShader(const std::vector<uint32_t> & binary,const char * filename)127 void 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
DumpTransformationsBinary(const protobufs::TransformationSequence & transformations,const char * filename)135 void 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
DumpTransformationsJson(const protobufs::TransformationSequence & transformations,const char * filename)144 void 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 == google::protobuf::util::Status::OK) {
153 std::ofstream transformations_json_file(filename);
154 transformations_json_file << json_string;
155 transformations_json_file.close();
156 }
157 }
158
ApplyAndCheckFreshIds(const Transformation & transformation,opt::IRContext * ir_context,TransformationContext * transformation_context,const std::unordered_set<uint32_t> & issued_overflow_ids)159 void ApplyAndCheckFreshIds(
160 const Transformation& transformation, opt::IRContext* ir_context,
161 TransformationContext* transformation_context,
162 const std::unordered_set<uint32_t>& issued_overflow_ids) {
163 opt::analysis::DefUseManager::IdToDefMap before_transformation =
164 ir_context->get_def_use_mgr()->id_to_defs();
165 transformation.Apply(ir_context, transformation_context);
166 opt::analysis::DefUseManager::IdToDefMap after_transformation =
167 ir_context->get_def_use_mgr()->id_to_defs();
168 std::unordered_set<uint32_t> fresh_ids_for_transformation =
169 transformation.GetFreshIds();
170 for (auto& entry : after_transformation) {
171 uint32_t id = entry.first;
172 bool introduced_by_transformation_message =
173 fresh_ids_for_transformation.count(id);
174 bool introduced_by_overflow_ids = issued_overflow_ids.count(id);
175 ASSERT_FALSE(introduced_by_transformation_message &&
176 introduced_by_overflow_ids);
177 if (before_transformation.count(entry.first)) {
178 ASSERT_FALSE(introduced_by_transformation_message ||
179 introduced_by_overflow_ids);
180 } else {
181 ASSERT_TRUE(introduced_by_transformation_message ||
182 introduced_by_overflow_ids);
183 }
184 }
185 }
186
187 } // namespace fuzz
188 } // namespace spvtools
189