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