• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TEST_F(CompactIdsTest,PassOff)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 
TEST_F(CompactIdsTest,PassOn)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 
TEST_F(CompactIdsTest,DebugScope)95 TEST_F(CompactIdsTest, DebugScope) {
96   const std::string text =
97       R"(OpCapability Addresses
98 OpCapability Kernel
99 OpCapability GenericPointer
100 OpCapability Linkage
101 %5 = OpExtInstImport "OpenCL.DebugInfo.100"
102 OpMemoryModel Physical32 OpenCL
103 OpEntryPoint Kernel %3 "simple_kernel"
104 %2 = OpString "test"
105 %99 = OpTypeInt 32 0
106 %10 = OpTypeVector %99 2
107 %20 = OpConstant %99 2
108 %30 = OpTypeArray %99 %20
109 %40 = OpTypeVoid
110 %50 = OpTypeFunction %40
111 %11 = OpExtInst %40 %5 DebugSource %2
112 %12 = OpExtInst %40 %5 DebugCompilationUnit 1 4 %11 HLSL
113 %13 = OpExtInst %40 %5 DebugTypeFunction FlagIsProtected|FlagIsPrivate %40
114 
115 ; CHECK: [[fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction
116 %14 = OpExtInst %40 %5 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %3
117  %3 = OpFunction %40 None %50
118 %70 = OpLabel
119 
120 ; CHECK: DebugScope [[fn]]
121 %19 = OpExtInst %40 %5 DebugScope %14
122 OpReturn
123 OpFunctionEnd
124 )";
125 
126   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
127   SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
128   SinglePassRunAndMatch<CompactIdsPass>(text, true);
129 }
130 
TEST(CompactIds,InstructionResultIsUpdated)131 TEST(CompactIds, InstructionResultIsUpdated) {
132   // For https://github.com/KhronosGroup/SPIRV-Tools/issues/827
133   // In that bug, the compact Ids pass was directly updating the result Id
134   // word for an OpFunction instruction, but not updating the cached
135   // result_id_ in that Instruction object.
136   //
137   // This test is a bit cheesy.  We don't expose internal interfaces enough
138   // to see the inconsistency.  So reproduce the original scenario, with
139   // compact ids followed by a pass that trips up on the inconsistency.
140 
141   const std::string input(R"(OpCapability Shader
142 OpMemoryModel Logical Simple
143 OpEntryPoint GLCompute %100 "main"
144 %200 = OpTypeVoid
145 %300 = OpTypeFunction %200
146 %100 = OpFunction %200 None %300
147 %400 = OpLabel
148 OpReturn
149 OpFunctionEnd
150 )");
151 
152   std::vector<uint32_t> binary;
153   const spv_target_env env = SPV_ENV_UNIVERSAL_1_0;
154   spvtools::SpirvTools tools(env);
155   auto assembled = tools.Assemble(
156       input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
157   EXPECT_TRUE(assembled);
158 
159   spvtools::Optimizer optimizer(env);
160   optimizer.RegisterPass(CreateCompactIdsPass());
161   // The exhaustive inliner will use the result_id
162   optimizer.RegisterPass(CreateInlineExhaustivePass());
163 
164   // This should not crash!
165   optimizer.Run(binary.data(), binary.size(), &binary);
166 
167   std::string disassembly;
168   tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
169 
170   const std::string expected(R"(OpCapability Shader
171 OpMemoryModel Logical Simple
172 OpEntryPoint GLCompute %1 "main"
173 %2 = OpTypeVoid
174 %3 = OpTypeFunction %2
175 %1 = OpFunction %2 None %3
176 %4 = OpLabel
177 OpReturn
178 OpFunctionEnd
179 )");
180 
181   EXPECT_THAT(disassembly, ::testing::Eq(expected));
182 }
183 
TEST(CompactIds,HeaderIsUpdated)184 TEST(CompactIds, HeaderIsUpdated) {
185   const std::string input(R"(OpCapability Shader
186 OpMemoryModel Logical Simple
187 OpEntryPoint GLCompute %100 "main"
188 %200 = OpTypeVoid
189 %300 = OpTypeFunction %200
190 %100 = OpFunction %200 None %300
191 %400 = OpLabel
192 OpReturn
193 OpFunctionEnd
194 )");
195 
196   std::vector<uint32_t> binary;
197   const spv_target_env env = SPV_ENV_UNIVERSAL_1_0;
198   spvtools::SpirvTools tools(env);
199   auto assembled = tools.Assemble(
200       input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
201   EXPECT_TRUE(assembled);
202 
203   spvtools::Optimizer optimizer(env);
204   optimizer.RegisterPass(CreateCompactIdsPass());
205   // The exhaustive inliner will use the result_id
206   optimizer.RegisterPass(CreateInlineExhaustivePass());
207 
208   // This should not crash!
209   optimizer.Run(binary.data(), binary.size(), &binary);
210 
211   std::string disassembly;
212   tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NONE);
213 
214   const std::string expected(R"(; SPIR-V
215 ; Version: 1.0
216 ; Generator: Khronos SPIR-V Tools Assembler; 0
217 ; Bound: 5
218 ; Schema: 0
219 OpCapability Shader
220 OpMemoryModel Logical Simple
221 OpEntryPoint GLCompute %1 "main"
222 %2 = OpTypeVoid
223 %3 = OpTypeFunction %2
224 %1 = OpFunction %2 None %3
225 %4 = OpLabel
226 OpReturn
227 OpFunctionEnd
228 )");
229 
230   EXPECT_THAT(disassembly, ::testing::Eq(expected));
231 }
232 
233 // Test context consistency check after invalidating
234 // CFG and others by compact IDs Pass.
235 // Uses a GLSL shader with named labels for variety
TEST(CompactIds,ConsistentCheck)236 TEST(CompactIds, ConsistentCheck) {
237   const std::string input(R"(OpCapability Shader
238 OpMemoryModel Logical GLSL450
239 OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET
240 OpExecutionMode %main OriginUpperLeft
241 OpSource HLSL 600
242 OpName %main "main"
243 OpName %in_var_A "in.var.A"
244 OpName %out_var_SV_TARGET "out.var.SV_TARGET"
245 OpDecorate %in_var_A Location 0
246 OpDecorate %out_var_SV_TARGET Location 0
247 %void = OpTypeVoid
248 %3 = OpTypeFunction %void
249 %float = OpTypeFloat 32
250 %v4float = OpTypeVector %float 4
251 %_ptr_Input_v4float = OpTypePointer Input %v4float
252 %_ptr_Output_v4float = OpTypePointer Output %v4float
253 %in_var_A = OpVariable %_ptr_Input_v4float Input
254 %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
255 %main = OpFunction %void None %3
256 %5 = OpLabel
257 %12 = OpLoad %v4float %in_var_A
258 %23 = OpVectorShuffle %v4float %12 %12 0 0 0 1
259 OpStore %out_var_SV_TARGET %23
260 OpReturn
261 OpFunctionEnd
262 )");
263 
264   spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
265   std::unique_ptr<IRContext> context =
266       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input,
267                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
268   ASSERT_NE(context, nullptr);
269 
270   CompactIdsPass compact_id_pass;
271   context->BuildInvalidAnalyses(compact_id_pass.GetPreservedAnalyses());
272   const auto status = compact_id_pass.Run(context.get());
273   ASSERT_NE(status, Pass::Status::Failure);
274   EXPECT_TRUE(context->IsConsistent());
275 
276   // Test output just in case
277   std::vector<uint32_t> binary;
278   context->module()->ToBinary(&binary, false);
279   std::string disassembly;
280   tools.Disassemble(binary, &disassembly,
281                     SpirvTools::kDefaultDisassembleOption);
282 
283   const std::string expected(R"(OpCapability Shader
284 OpMemoryModel Logical GLSL450
285 OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET
286 OpExecutionMode %main OriginUpperLeft
287 OpSource HLSL 600
288 OpName %main "main"
289 OpName %in_var_A "in.var.A"
290 OpName %out_var_SV_TARGET "out.var.SV_TARGET"
291 OpDecorate %in_var_A Location 0
292 OpDecorate %out_var_SV_TARGET Location 0
293 %void = OpTypeVoid
294 %5 = OpTypeFunction %void
295 %float = OpTypeFloat 32
296 %v4float = OpTypeVector %float 4
297 %_ptr_Input_v4float = OpTypePointer Input %v4float
298 %_ptr_Output_v4float = OpTypePointer Output %v4float
299 %in_var_A = OpVariable %_ptr_Input_v4float Input
300 %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
301 %main = OpFunction %void None %5
302 %10 = OpLabel
303 %11 = OpLoad %v4float %in_var_A
304 %12 = OpVectorShuffle %v4float %11 %11 0 0 0 1
305 OpStore %out_var_SV_TARGET %12
306 OpReturn
307 OpFunctionEnd
308 )");
309 
310   EXPECT_THAT(disassembly, ::testing::Eq(expected));
311 }
312 
TEST(CompactIds,ResetIdBound)313 TEST(CompactIds, ResetIdBound) {
314   const std::string input(R"(OpCapability Shader
315 OpMemoryModel Logical GLSL450
316 OpEntryPoint Fragment %1 "main"
317 OpExecutionMode %1 OriginUpperLeft
318 %void = OpTypeVoid
319 %3 = OpTypeFunction %void
320 %1 = OpFunction %void None %3
321 %4 = OpLabel
322 OpReturn
323 OpFunctionEnd
324 )");
325 
326   spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
327   std::unique_ptr<IRContext> context =
328       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input,
329                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
330   ASSERT_NE(context, nullptr);
331 
332   CompactIdsPass compact_id_pass;
333   context->module()->SetIdBound(20000);
334   const auto status = compact_id_pass.Run(context.get());
335   EXPECT_EQ(status, Pass::Status::SuccessWithChange);
336   EXPECT_EQ(context->module()->id_bound(), 5);
337 
338   // Test output just in case
339   std::vector<uint32_t> binary;
340   context->module()->ToBinary(&binary, false);
341   std::string disassembly;
342   tools.Disassemble(binary, &disassembly,
343                     SpirvTools::kDefaultDisassembleOption);
344 
345   EXPECT_THAT(disassembly, ::testing::Eq(input));
346 }
347 
348 }  // namespace
349 }  // namespace opt
350 }  // namespace spvtools
351