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