1// Copyright (c) 2022 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 "source/diff/diff.h"
16
17#include "diff_test_utils.h"
18
19#include "source/opt/build_module.h"
20#include "source/opt/ir_context.h"
21#include "source/spirv_constant.h"
22#include "spirv-tools/libspirv.hpp"
23#include "tools/io.h"
24#include "tools/util/cli_consumer.h"
25
26#include <fstream>
27#include <string>
28
29#include "gtest/gtest.h"
30
31namespace spvtools {
32namespace diff {
33namespace {
34
35constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
36
37std::unique_ptr<spvtools::opt::IRContext> Assemble(const std::string& spirv) {
38  spvtools::SpirvTools t(kDefaultEnvironment);
39  t.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
40  std::vector<uint32_t> binary;
41  if (!t.Assemble(spirv, &binary,
42                  spvtools::SpirvTools::kDefaultAssembleOption |
43                      SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS))
44    return nullptr;
45  return spvtools::BuildModule(kDefaultEnvironment,
46                               spvtools::utils::CLIMessageConsumer,
47                               binary.data(), binary.size());
48}
49
50TEST(DiffIndentTest, Diff) {
51  const std::string src = R"(OpCapability Shader
52    %ext_inst = OpExtInstImport "GLSL.std.450"
53    OpMemoryModel Logical GLSL450
54    OpEntryPoint Fragment %main "main"
55    OpExecutionMode %main OriginUpperLeft
56    %void = OpTypeVoid
57    %func = OpTypeFunction %void
58
59    %main = OpFunction %void None %func
60    %main_entry = OpLabel
61    OpReturn
62    OpFunctionEnd;)";
63
64  const std::string dst = R"(OpCapability Shader
65    OpMemoryModel Logical GLSL450
66    OpEntryPoint Fragment %main "main"
67    OpExecutionMode %main OriginUpperLeft
68    %void = OpTypeVoid
69    %func = OpTypeFunction %void
70
71    %main = OpFunction %void None %func
72    %main_entry = OpLabel
73    OpReturn
74    OpFunctionEnd;)";
75
76  const std::string diff = R"( ; SPIR-V
77 ; Version: 1.6
78 ; Generator: Khronos SPIR-V Tools Assembler; 0
79 ; Bound: 6
80 ; Schema: 0
81                OpCapability Shader
82-          %1 = OpExtInstImport "GLSL.std.450"
83                OpMemoryModel Logical GLSL450
84                OpEntryPoint Fragment %2 "main"
85                OpExecutionMode %2 OriginUpperLeft
86           %3 = OpTypeVoid
87           %4 = OpTypeFunction %3
88           %2 = OpFunction %3 None %4
89           %5 = OpLabel
90                OpReturn
91                OpFunctionEnd
92)";
93
94  Options options;
95  options.indent = true;
96  DoStringDiffTest(src, dst, diff, options);
97}
98
99TEST(DiffNoHeaderTest, Diff) {
100  const std::string src = R"(OpCapability Shader
101    %ext_inst = OpExtInstImport "GLSL.std.450"
102    OpMemoryModel Logical GLSL450
103    OpEntryPoint Fragment %main "main"
104    OpExecutionMode %main OriginUpperLeft
105    %void = OpTypeVoid
106    %func = OpTypeFunction %void
107
108    %main = OpFunction %void None %func
109    %main_entry = OpLabel
110    OpReturn
111    OpFunctionEnd;)";
112
113  const std::string dst = R"(OpCapability Shader
114    OpMemoryModel Logical GLSL450
115    OpEntryPoint Fragment %main "main"
116    OpExecutionMode %main OriginUpperLeft
117    %void = OpTypeVoid
118    %func = OpTypeFunction %void
119
120    %main = OpFunction %void None %func
121    %main_entry = OpLabel
122    OpReturn
123    OpFunctionEnd;)";
124
125  const std::string diff = R"( OpCapability Shader
126-%1 = OpExtInstImport "GLSL.std.450"
127 OpMemoryModel Logical GLSL450
128 OpEntryPoint Fragment %2 "main"
129 OpExecutionMode %2 OriginUpperLeft
130 %3 = OpTypeVoid
131 %4 = OpTypeFunction %3
132 %2 = OpFunction %3 None %4
133 %5 = OpLabel
134 OpReturn
135 OpFunctionEnd
136)";
137
138  Options options;
139  options.no_header = true;
140  DoStringDiffTest(src, dst, diff, options);
141}
142
143TEST(DiffHeaderTest, Diff) {
144  const std::string src_spirv = R"(OpCapability Shader
145    %ext_inst = OpExtInstImport "GLSL.std.450"
146    OpMemoryModel Logical GLSL450
147    OpEntryPoint Fragment %main "main"
148    OpExecutionMode %main OriginUpperLeft
149    %void = OpTypeVoid
150    %func = OpTypeFunction %void
151
152    %main = OpFunction %void None %func
153    %main_entry = OpLabel
154    OpReturn
155    OpFunctionEnd;)";
156
157  const std::string dst_spirv = R"(OpCapability Shader
158    OpMemoryModel Logical GLSL450
159    OpEntryPoint Fragment %main "main"
160    OpExecutionMode %main OriginUpperLeft
161    %void = OpTypeVoid
162    %func = OpTypeFunction %void
163
164    %main = OpFunction %void None %func
165    %main_entry = OpLabel
166    OpReturn
167    OpFunctionEnd;)";
168
169  const std::string diff = R"( ; SPIR-V
170-; Version: 1.3
171+; Version: 1.2
172-; Generator: Khronos SPIR-V Tools Assembler; 3
173+; Generator: Khronos Glslang Reference Front End; 10
174 ; Bound: 6
175 ; Schema: 0
176 OpCapability Shader
177-%1 = OpExtInstImport "GLSL.std.450"
178 OpMemoryModel Logical GLSL450
179 OpEntryPoint Fragment %2 "main"
180 OpExecutionMode %2 OriginUpperLeft
181 %3 = OpTypeVoid
182 %4 = OpTypeFunction %3
183 %2 = OpFunction %3 None %4
184 %5 = OpLabel
185 OpReturn
186 OpFunctionEnd
187)";
188
189  // Load the src and dst modules
190  std::unique_ptr<spvtools::opt::IRContext> src = Assemble(src_spirv);
191  ASSERT_TRUE(src);
192
193  std::unique_ptr<spvtools::opt::IRContext> dst = Assemble(dst_spirv);
194  ASSERT_TRUE(dst);
195
196  // Differentiate them in the header.
197  const spvtools::opt::ModuleHeader src_header = {
198      spv::MagicNumber,
199      SPV_SPIRV_VERSION_WORD(1, 3),
200      SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 3),
201      src->module()->IdBound(),
202      src->module()->schema(),
203  };
204  const spvtools::opt::ModuleHeader dst_header = {
205      spv::MagicNumber,
206      SPV_SPIRV_VERSION_WORD(1, 2),
207      SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_GLSLANG, 10),
208      dst->module()->IdBound(),
209      dst->module()->schema(),
210  };
211
212  src->module()->SetHeader(src_header);
213  dst->module()->SetHeader(dst_header);
214
215  // Take the diff
216  Options options;
217  std::ostringstream diff_result;
218  spv_result_t result =
219      spvtools::diff::Diff(src.get(), dst.get(), diff_result, options);
220  ASSERT_EQ(result, SPV_SUCCESS);
221
222  // Expect they match
223  EXPECT_EQ(diff_result.str(), diff);
224}
225
226}  // namespace
227}  // namespace diff
228}  // namespace spvtools
229