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