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