• 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 <string>
16 #include <vector>
17 
18 #include "gmock/gmock.h"
19 #include "spirv-tools/libspirv.hpp"
20 #include "spirv-tools/optimizer.hpp"
21 #include "test/opt/pass_fixture.h"
22 
23 namespace spvtools {
24 namespace opt {
25 namespace {
26 
27 using ::testing::Eq;
28 
29 // Return a string that contains the minimum instructions needed to form
30 // a valid module.  Other instructions can be appended to this string.
Header()31 std::string Header() {
32   return R"(OpCapability Shader
33 OpCapability Linkage
34 OpMemoryModel Logical GLSL450
35 )";
36 }
37 
TEST(Optimizer,CanRunNullPassWithDistinctInputOutputVectors)38 TEST(Optimizer, CanRunNullPassWithDistinctInputOutputVectors) {
39   SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
40   std::vector<uint32_t> binary_in;
41   tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid",
42                  &binary_in);
43 
44   Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
45   opt.RegisterPass(CreateNullPass());
46   std::vector<uint32_t> binary_out;
47   opt.Run(binary_in.data(), binary_in.size(), &binary_out);
48 
49   std::string disassembly;
50   tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
51   EXPECT_THAT(disassembly,
52               Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
53 }
54 
TEST(Optimizer,CanRunTransformingPassWithDistinctInputOutputVectors)55 TEST(Optimizer, CanRunTransformingPassWithDistinctInputOutputVectors) {
56   SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
57   std::vector<uint32_t> binary_in;
58   tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid",
59                  &binary_in);
60 
61   Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
62   opt.RegisterPass(CreateStripDebugInfoPass());
63   std::vector<uint32_t> binary_out;
64   opt.Run(binary_in.data(), binary_in.size(), &binary_out);
65 
66   std::string disassembly;
67   tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
68   EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n"));
69 }
70 
TEST(Optimizer,CanRunNullPassWithAliasedVectors)71 TEST(Optimizer, CanRunNullPassWithAliasedVectors) {
72   SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
73   std::vector<uint32_t> binary;
74   tools.Assemble("OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
75 
76   Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
77   opt.RegisterPass(CreateNullPass());
78   opt.Run(binary.data(), binary.size(), &binary);  // This is the key.
79 
80   std::string disassembly;
81   tools.Disassemble(binary.data(), binary.size(), &disassembly);
82   EXPECT_THAT(disassembly, Eq("OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
83 }
84 
TEST(Optimizer,CanRunNullPassWithAliasedVectorDataButDifferentSize)85 TEST(Optimizer, CanRunNullPassWithAliasedVectorDataButDifferentSize) {
86   SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
87   std::vector<uint32_t> binary;
88   tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
89 
90   Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
91   opt.RegisterPass(CreateNullPass());
92   auto orig_size = binary.size();
93   // Now change the size.  Add a word that will be ignored
94   // by the optimizer.
95   binary.push_back(42);
96   EXPECT_THAT(orig_size + 1, Eq(binary.size()));
97   opt.Run(binary.data(), orig_size, &binary);  // This is the key.
98   // The binary vector should have been rewritten.
99   EXPECT_THAT(binary.size(), Eq(orig_size));
100 
101   std::string disassembly;
102   tools.Disassemble(binary.data(), binary.size(), &disassembly);
103   EXPECT_THAT(disassembly,
104               Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
105 }
106 
TEST(Optimizer,CanRunTransformingPassWithAliasedVectors)107 TEST(Optimizer, CanRunTransformingPassWithAliasedVectors) {
108   SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
109   std::vector<uint32_t> binary;
110   tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
111 
112   Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
113   opt.RegisterPass(CreateStripDebugInfoPass());
114   opt.Run(binary.data(), binary.size(), &binary);  // This is the key
115 
116   std::string disassembly;
117   tools.Disassemble(binary.data(), binary.size(), &disassembly);
118   EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n"));
119 }
120 
TEST(Optimizer,CanValidateFlags)121 TEST(Optimizer, CanValidateFlags) {
122   Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
123   EXPECT_FALSE(opt.FlagHasValidForm("bad-flag"));
124   EXPECT_TRUE(opt.FlagHasValidForm("-O"));
125   EXPECT_TRUE(opt.FlagHasValidForm("-Os"));
126   EXPECT_FALSE(opt.FlagHasValidForm("-O2"));
127   EXPECT_TRUE(opt.FlagHasValidForm("--this_flag"));
128 }
129 
TEST(Optimizer,CanRegisterPassesFromFlags)130 TEST(Optimizer, CanRegisterPassesFromFlags) {
131   SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
132   Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
133 
134   spv_message_level_t msg_level;
135   const char* msg_fname;
136   spv_position_t msg_position;
137   const char* msg;
138   auto examine_message = [&msg_level, &msg_fname, &msg_position, &msg](
139                              spv_message_level_t ml, const char* f,
140                              const spv_position_t& p, const char* m) {
141     msg_level = ml;
142     msg_fname = f;
143     msg_position = p;
144     msg = m;
145   };
146   opt.SetMessageConsumer(examine_message);
147 
148   std::vector<std::string> pass_flags = {
149       "--strip-debug",
150       "--strip-nonsemantic",
151       "--set-spec-const-default-value=23:42 21:12",
152       "--if-conversion",
153       "--freeze-spec-const",
154       "--inline-entry-points-exhaustive",
155       "--inline-entry-points-opaque",
156       "--convert-local-access-chains",
157       "--eliminate-dead-code-aggressive",
158       "--eliminate-insert-extract",
159       "--eliminate-local-single-block",
160       "--eliminate-local-single-store",
161       "--merge-blocks",
162       "--merge-return",
163       "--eliminate-dead-branches",
164       "--eliminate-dead-functions",
165       "--eliminate-local-multi-store",
166       "--eliminate-dead-const",
167       "--eliminate-dead-inserts",
168       "--eliminate-dead-variables",
169       "--fold-spec-const-op-composite",
170       "--loop-unswitch",
171       "--scalar-replacement=300",
172       "--scalar-replacement",
173       "--strength-reduction",
174       "--unify-const",
175       "--flatten-decorations",
176       "--compact-ids",
177       "--cfg-cleanup",
178       "--local-redundancy-elimination",
179       "--loop-invariant-code-motion",
180       "--reduce-load-size",
181       "--redundancy-elimination",
182       "--private-to-local",
183       "--remove-duplicates",
184       "--workaround-1209",
185       "--replace-invalid-opcode",
186       "--simplify-instructions",
187       "--ssa-rewrite",
188       "--copy-propagate-arrays",
189       "--loop-fission=20",
190       "--loop-fusion=2",
191       "--loop-unroll",
192       "--vector-dce",
193       "--loop-unroll-partial=3",
194       "--loop-peeling",
195       "--ccp",
196       "-O",
197       "-Os",
198       "--legalize-hlsl"};
199   EXPECT_TRUE(opt.RegisterPassesFromFlags(pass_flags));
200 
201   // Test some invalid flags.
202   EXPECT_FALSE(opt.RegisterPassFromFlag("-O2"));
203   EXPECT_EQ(msg_level, SPV_MSG_ERROR);
204 
205   EXPECT_FALSE(opt.RegisterPassFromFlag("-loop-unroll"));
206   EXPECT_EQ(msg_level, SPV_MSG_ERROR);
207 
208   EXPECT_FALSE(opt.RegisterPassFromFlag("--set-spec-const-default-value"));
209   EXPECT_EQ(msg_level, SPV_MSG_ERROR);
210 
211   EXPECT_FALSE(opt.RegisterPassFromFlag("--scalar-replacement=s"));
212   EXPECT_EQ(msg_level, SPV_MSG_ERROR);
213 
214   EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fission=-4"));
215   EXPECT_EQ(msg_level, SPV_MSG_ERROR);
216 
217   EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fusion=xx"));
218   EXPECT_EQ(msg_level, SPV_MSG_ERROR);
219 
220   EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-unroll-partial"));
221   EXPECT_EQ(msg_level, SPV_MSG_ERROR);
222 }
223 
224 
TEST(Optimizer,RemoveNop)225 TEST(Optimizer, RemoveNop) {
226   // Test that OpNops are removed even if no optimizations are run.
227   const std::string before = R"(OpCapability Shader
228 OpCapability Linkage
229 OpMemoryModel Logical GLSL450
230 %void = OpTypeVoid
231 %2 = OpTypeFunction %void
232 %3 = OpFunction %void None %2
233 %4 = OpLabel
234 OpNop
235 OpReturn
236 OpFunctionEnd
237 )";
238 
239   const std::string after = R"(OpCapability Shader
240 OpCapability Linkage
241 OpMemoryModel Logical GLSL450
242 %void = OpTypeVoid
243 %2 = OpTypeFunction %void
244 %3 = OpFunction %void None %2
245 %4 = OpLabel
246 OpReturn
247 OpFunctionEnd
248 )";
249 
250   std::vector<uint32_t> binary;
251   {
252     SpirvTools tools(SPV_ENV_VULKAN_1_1);
253     tools.Assemble(before, &binary);
254   }
255 
256   Optimizer opt(SPV_ENV_VULKAN_1_1);
257 
258   std::vector<uint32_t> optimized;
259   class ValidatorOptions validator_options;
260   ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
261                       validator_options, true))
262       << before << "\n";
263   std::string disassembly;
264   {
265     SpirvTools tools(SPV_ENV_VULKAN_1_1);
266     tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
267   }
268 
269   EXPECT_EQ(after, disassembly)
270       << "Was expecting the OpNop to have been removed.";
271 }
272 
TEST(Optimizer,AvoidIntegrityCheckForExtraLineInfo)273 TEST(Optimizer, AvoidIntegrityCheckForExtraLineInfo) {
274   // Test that it avoids the integrity check when no optimizations are run and
275   // OpLines are propagated.
276   const std::string before = R"(OpCapability Shader
277 OpCapability Linkage
278 OpMemoryModel Logical GLSL450
279 %1 = OpString "Test"
280 %void = OpTypeVoid
281 %3 = OpTypeFunction %void
282 %uint = OpTypeInt 32 0
283 %_ptr_Function_uint = OpTypePointer Function %uint
284 %6 = OpFunction %void None %3
285 %7 = OpLabel
286 OpLine %1 10 0
287 %8 = OpVariable %_ptr_Function_uint Function
288 OpLine %1 10 0
289 %9 = OpVariable %_ptr_Function_uint Function
290 OpLine %1 20 0
291 OpReturn
292 OpFunctionEnd
293 )";
294 
295   const std::string after = R"(OpCapability Shader
296 OpCapability Linkage
297 OpMemoryModel Logical GLSL450
298 %1 = OpString "Test"
299 %void = OpTypeVoid
300 %3 = OpTypeFunction %void
301 %uint = OpTypeInt 32 0
302 %_ptr_Function_uint = OpTypePointer Function %uint
303 %6 = OpFunction %void None %3
304 %7 = OpLabel
305 OpLine %1 10 0
306 %8 = OpVariable %_ptr_Function_uint Function
307 %9 = OpVariable %_ptr_Function_uint Function
308 OpLine %1 20 0
309 OpReturn
310 OpFunctionEnd
311 )";
312 
313   std::vector<uint32_t> binary;
314   SpirvTools tools(SPV_ENV_VULKAN_1_1);
315   tools.Assemble(before, &binary);
316 
317   Optimizer opt(SPV_ENV_VULKAN_1_1);
318 
319   std::vector<uint32_t> optimized;
320   class ValidatorOptions validator_options;
321   ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
322                       validator_options, true))
323       << before << "\n";
324 
325   std::string disassembly;
326   tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
327 
328   EXPECT_EQ(after, disassembly)
329       << "Was expecting the OpLine to have been propagated.";
330 }
331 
TEST(Optimizer,AvoidIntegrityCheckForDebugScope)332 TEST(Optimizer, AvoidIntegrityCheckForDebugScope) {
333   // Test that it avoids the integrity check when the code contains DebugScope.
334   const std::string before = R"(OpCapability Shader
335 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
336 OpMemoryModel Logical GLSL450
337 OpEntryPoint Fragment %main "main"
338 OpExecutionMode %main OriginUpperLeft
339 %3 = OpString "simple_vs.hlsl"
340 OpSource HLSL 600 %3
341 OpName %main "main"
342 %void = OpTypeVoid
343 %5 = OpTypeFunction %void
344 %6 = OpExtInst %void %1 DebugSource %3
345 %7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL
346 %main = OpFunction %void None %5
347 %14 = OpLabel
348 %26 = OpExtInst %void %1 DebugScope %7
349 OpReturn
350 %27 = OpExtInst %void %1 DebugNoScope
351 OpFunctionEnd
352 )";
353 
354   const std::string after = R"(OpCapability Shader
355 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
356 OpMemoryModel Logical GLSL450
357 OpEntryPoint Fragment %main "main"
358 OpExecutionMode %main OriginUpperLeft
359 %3 = OpString "simple_vs.hlsl"
360 OpSource HLSL 600 %3
361 OpName %main "main"
362 %void = OpTypeVoid
363 %5 = OpTypeFunction %void
364 %6 = OpExtInst %void %1 DebugSource %3
365 %7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL
366 %main = OpFunction %void None %5
367 %8 = OpLabel
368 %11 = OpExtInst %void %1 DebugScope %7
369 OpReturn
370 %12 = OpExtInst %void %1 DebugNoScope
371 OpFunctionEnd
372 )";
373 
374   std::vector<uint32_t> binary;
375   SpirvTools tools(SPV_ENV_VULKAN_1_1);
376   tools.Assemble(before, &binary);
377 
378   Optimizer opt(SPV_ENV_VULKAN_1_1);
379 
380   std::vector<uint32_t> optimized;
381   ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized))
382       << before << "\n";
383 
384   std::string disassembly;
385   tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
386 
387   EXPECT_EQ(after, disassembly)
388       << "Was expecting the result id of DebugScope to have been changed.";
389 }
390 
391 }  // namespace
392 }  // namespace opt
393 }  // namespace spvtools
394