1 // Copyright (c) 2017 Google Inc. 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 <memory> 16 #include <string> 17 #include <vector> 18 19 #include "gmock/gmock.h" 20 #include "spirv-tools/libspirv.hpp" 21 #include "spirv-tools/optimizer.hpp" 22 #include "test/opt/pass_fixture.h" 23 #include "test/opt/pass_utils.h" 24 25 namespace spvtools { 26 namespace opt { 27 namespace { 28 29 using CompactIdsTest = PassTest<::testing::Test>; 30 31 TEST_F(CompactIdsTest, PassOff) { 32 const std::string before = 33 R"(OpCapability Addresses 34 OpCapability Kernel 35 OpCapability GenericPointer 36 OpCapability Linkage 37 OpMemoryModel Physical32 OpenCL 38 %99 = OpTypeInt 32 0 39 %10 = OpTypeVector %99 2 40 %20 = OpConstant %99 2 41 %30 = OpTypeArray %99 %20 42 )"; 43 44 const std::string after = before; 45 46 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 47 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 48 SinglePassRunAndCheck<NullPass>(before, after, false, false); 49 } 50 51 TEST_F(CompactIdsTest, PassOn) { 52 const std::string before = 53 R"(OpCapability Addresses 54 OpCapability Kernel 55 OpCapability GenericPointer 56 OpCapability Linkage 57 OpMemoryModel Physical32 OpenCL 58 OpEntryPoint Kernel %3 "simple_kernel" 59 %99 = OpTypeInt 32 0 60 %10 = OpTypeVector %99 2 61 %20 = OpConstant %99 2 62 %30 = OpTypeArray %99 %20 63 %40 = OpTypeVoid 64 %50 = OpTypeFunction %40 65 %3 = OpFunction %40 None %50 66 %70 = OpLabel 67 OpReturn 68 OpFunctionEnd 69 )"; 70 71 const std::string after = 72 R"(OpCapability Addresses 73 OpCapability Kernel 74 OpCapability GenericPointer 75 OpCapability Linkage 76 OpMemoryModel Physical32 OpenCL 77 OpEntryPoint Kernel %1 "simple_kernel" 78 %2 = OpTypeInt 32 0 79 %3 = OpTypeVector %2 2 80 %4 = OpConstant %2 2 81 %5 = OpTypeArray %2 %4 82 %6 = OpTypeVoid 83 %7 = OpTypeFunction %6 84 %1 = OpFunction %6 None %7 85 %8 = OpLabel 86 OpReturn 87 OpFunctionEnd 88 )"; 89 90 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 91 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 92 SinglePassRunAndCheck<CompactIdsPass>(before, after, false, false); 93 } 94 95 TEST(CompactIds, InstructionResultIsUpdated) { 96 // For https://github.com/KhronosGroup/SPIRV-Tools/issues/827 97 // In that bug, the compact Ids pass was directly updating the result Id 98 // word for an OpFunction instruction, but not updating the cached 99 // result_id_ in that Instruction object. 100 // 101 // This test is a bit cheesy. We don't expose internal interfaces enough 102 // to see the inconsistency. So reproduce the original scenario, with 103 // compact ids followed by a pass that trips up on the inconsistency. 104 105 const std::string input(R"(OpCapability Shader 106 OpMemoryModel Logical Simple 107 OpEntryPoint GLCompute %100 "main" 108 %200 = OpTypeVoid 109 %300 = OpTypeFunction %200 110 %100 = OpFunction %200 None %300 111 %400 = OpLabel 112 OpReturn 113 OpFunctionEnd 114 )"); 115 116 std::vector<uint32_t> binary; 117 const spv_target_env env = SPV_ENV_UNIVERSAL_1_0; 118 spvtools::SpirvTools tools(env); 119 auto assembled = tools.Assemble( 120 input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 121 EXPECT_TRUE(assembled); 122 123 spvtools::Optimizer optimizer(env); 124 optimizer.RegisterPass(CreateCompactIdsPass()); 125 // The exhaustive inliner will use the result_id 126 optimizer.RegisterPass(CreateInlineExhaustivePass()); 127 128 // This should not crash! 129 optimizer.Run(binary.data(), binary.size(), &binary); 130 131 std::string disassembly; 132 tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 133 134 const std::string expected(R"(OpCapability Shader 135 OpMemoryModel Logical Simple 136 OpEntryPoint GLCompute %1 "main" 137 %2 = OpTypeVoid 138 %3 = OpTypeFunction %2 139 %1 = OpFunction %2 None %3 140 %4 = OpLabel 141 OpReturn 142 OpFunctionEnd 143 )"); 144 145 EXPECT_THAT(disassembly, ::testing::Eq(expected)); 146 } 147 148 TEST(CompactIds, HeaderIsUpdated) { 149 const std::string input(R"(OpCapability Shader 150 OpMemoryModel Logical Simple 151 OpEntryPoint GLCompute %100 "main" 152 %200 = OpTypeVoid 153 %300 = OpTypeFunction %200 154 %100 = OpFunction %200 None %300 155 %400 = OpLabel 156 OpReturn 157 OpFunctionEnd 158 )"); 159 160 std::vector<uint32_t> binary; 161 const spv_target_env env = SPV_ENV_UNIVERSAL_1_0; 162 spvtools::SpirvTools tools(env); 163 auto assembled = tools.Assemble( 164 input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 165 EXPECT_TRUE(assembled); 166 167 spvtools::Optimizer optimizer(env); 168 optimizer.RegisterPass(CreateCompactIdsPass()); 169 // The exhaustive inliner will use the result_id 170 optimizer.RegisterPass(CreateInlineExhaustivePass()); 171 172 // This should not crash! 173 optimizer.Run(binary.data(), binary.size(), &binary); 174 175 std::string disassembly; 176 tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NONE); 177 178 const std::string expected(R"(; SPIR-V 179 ; Version: 1.0 180 ; Generator: Khronos SPIR-V Tools Assembler; 0 181 ; Bound: 5 182 ; Schema: 0 183 OpCapability Shader 184 OpMemoryModel Logical Simple 185 OpEntryPoint GLCompute %1 "main" 186 %2 = OpTypeVoid 187 %3 = OpTypeFunction %2 188 %1 = OpFunction %2 None %3 189 %4 = OpLabel 190 OpReturn 191 OpFunctionEnd 192 )"); 193 194 EXPECT_THAT(disassembly, ::testing::Eq(expected)); 195 } 196 197 // Test context consistency check after invalidating 198 // CFG and others by compact IDs Pass. 199 // Uses a GLSL shader with named labels for variety 200 TEST(CompactIds, ConsistentCheck) { 201 const std::string input(R"(OpCapability Shader 202 OpMemoryModel Logical GLSL450 203 OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET 204 OpExecutionMode %main OriginUpperLeft 205 OpSource HLSL 600 206 OpName %main "main" 207 OpName %in_var_A "in.var.A" 208 OpName %out_var_SV_TARGET "out.var.SV_TARGET" 209 OpDecorate %in_var_A Location 0 210 OpDecorate %out_var_SV_TARGET Location 0 211 %void = OpTypeVoid 212 %3 = OpTypeFunction %void 213 %float = OpTypeFloat 32 214 %v4float = OpTypeVector %float 4 215 %_ptr_Input_v4float = OpTypePointer Input %v4float 216 %_ptr_Output_v4float = OpTypePointer Output %v4float 217 %in_var_A = OpVariable %_ptr_Input_v4float Input 218 %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output 219 %main = OpFunction %void None %3 220 %5 = OpLabel 221 %12 = OpLoad %v4float %in_var_A 222 %23 = OpVectorShuffle %v4float %12 %12 0 0 0 1 223 OpStore %out_var_SV_TARGET %23 224 OpReturn 225 OpFunctionEnd 226 )"); 227 228 spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); 229 std::unique_ptr<IRContext> context = 230 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input, 231 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 232 ASSERT_NE(context, nullptr); 233 234 CompactIdsPass compact_id_pass; 235 context->BuildInvalidAnalyses(compact_id_pass.GetPreservedAnalyses()); 236 const auto status = compact_id_pass.Run(context.get()); 237 ASSERT_NE(status, Pass::Status::Failure); 238 EXPECT_TRUE(context->IsConsistent()); 239 240 // Test output just in case 241 std::vector<uint32_t> binary; 242 context->module()->ToBinary(&binary, false); 243 std::string disassembly; 244 tools.Disassemble(binary, &disassembly, 245 SpirvTools::kDefaultDisassembleOption); 246 247 const std::string expected(R"(OpCapability Shader 248 OpMemoryModel Logical GLSL450 249 OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET 250 OpExecutionMode %main OriginUpperLeft 251 OpSource HLSL 600 252 OpName %main "main" 253 OpName %in_var_A "in.var.A" 254 OpName %out_var_SV_TARGET "out.var.SV_TARGET" 255 OpDecorate %in_var_A Location 0 256 OpDecorate %out_var_SV_TARGET Location 0 257 %void = OpTypeVoid 258 %5 = OpTypeFunction %void 259 %float = OpTypeFloat 32 260 %v4float = OpTypeVector %float 4 261 %_ptr_Input_v4float = OpTypePointer Input %v4float 262 %_ptr_Output_v4float = OpTypePointer Output %v4float 263 %in_var_A = OpVariable %_ptr_Input_v4float Input 264 %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output 265 %main = OpFunction %void None %5 266 %10 = OpLabel 267 %11 = OpLoad %v4float %in_var_A 268 %12 = OpVectorShuffle %v4float %11 %11 0 0 0 1 269 OpStore %out_var_SV_TARGET %12 270 OpReturn 271 OpFunctionEnd 272 )"); 273 274 EXPECT_THAT(disassembly, ::testing::Eq(expected)); 275 } 276 277 } // namespace 278 } // namespace opt 279 } // namespace spvtools 280