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