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(CompactIds,InstructionResultIsUpdated)95 TEST(CompactIds, InstructionResultIsUpdated) {
96 // For https://github.com/KhronosGroup/SPIRV-Tools/issues/827
97 // In that bug, the compact Ids pass was directly updating the result Id
98 // word for an OpFunction instruction, but not updating the cached
99 // result_id_ in that Instruction object.
100 //
101 // This test is a bit cheesy. We don't expose internal interfaces enough
102 // to see the inconsistency. So reproduce the original scenario, with
103 // compact ids followed by a pass that trips up on the inconsistency.
104
105 const std::string input(R"(OpCapability Shader
106 OpMemoryModel Logical Simple
107 OpEntryPoint GLCompute %100 "main"
108 %200 = OpTypeVoid
109 %300 = OpTypeFunction %200
110 %100 = OpFunction %200 None %300
111 %400 = OpLabel
112 OpReturn
113 OpFunctionEnd
114 )");
115
116 std::vector<uint32_t> binary;
117 const spv_target_env env = SPV_ENV_UNIVERSAL_1_0;
118 spvtools::SpirvTools tools(env);
119 auto assembled = tools.Assemble(
120 input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
121 EXPECT_TRUE(assembled);
122
123 spvtools::Optimizer optimizer(env);
124 optimizer.RegisterPass(CreateCompactIdsPass());
125 // The exhaustive inliner will use the result_id
126 optimizer.RegisterPass(CreateInlineExhaustivePass());
127
128 // This should not crash!
129 optimizer.Run(binary.data(), binary.size(), &binary);
130
131 std::string disassembly;
132 tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
133
134 const std::string expected(R"(OpCapability Shader
135 OpMemoryModel Logical Simple
136 OpEntryPoint GLCompute %1 "main"
137 %2 = OpTypeVoid
138 %3 = OpTypeFunction %2
139 %1 = OpFunction %2 None %3
140 %4 = OpLabel
141 OpReturn
142 OpFunctionEnd
143 )");
144
145 EXPECT_THAT(disassembly, ::testing::Eq(expected));
146 }
147
TEST(CompactIds,HeaderIsUpdated)148 TEST(CompactIds, HeaderIsUpdated) {
149 const std::string input(R"(OpCapability Shader
150 OpMemoryModel Logical Simple
151 OpEntryPoint GLCompute %100 "main"
152 %200 = OpTypeVoid
153 %300 = OpTypeFunction %200
154 %100 = OpFunction %200 None %300
155 %400 = OpLabel
156 OpReturn
157 OpFunctionEnd
158 )");
159
160 std::vector<uint32_t> binary;
161 const spv_target_env env = SPV_ENV_UNIVERSAL_1_0;
162 spvtools::SpirvTools tools(env);
163 auto assembled = tools.Assemble(
164 input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
165 EXPECT_TRUE(assembled);
166
167 spvtools::Optimizer optimizer(env);
168 optimizer.RegisterPass(CreateCompactIdsPass());
169 // The exhaustive inliner will use the result_id
170 optimizer.RegisterPass(CreateInlineExhaustivePass());
171
172 // This should not crash!
173 optimizer.Run(binary.data(), binary.size(), &binary);
174
175 std::string disassembly;
176 tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NONE);
177
178 const std::string expected(R"(; SPIR-V
179 ; Version: 1.0
180 ; Generator: Khronos SPIR-V Tools Assembler; 0
181 ; Bound: 5
182 ; Schema: 0
183 OpCapability Shader
184 OpMemoryModel Logical Simple
185 OpEntryPoint GLCompute %1 "main"
186 %2 = OpTypeVoid
187 %3 = OpTypeFunction %2
188 %1 = OpFunction %2 None %3
189 %4 = OpLabel
190 OpReturn
191 OpFunctionEnd
192 )");
193
194 EXPECT_THAT(disassembly, ::testing::Eq(expected));
195 }
196
197 // Test context consistency check after invalidating
198 // CFG and others by compact IDs Pass.
199 // Uses a GLSL shader with named labels for variety
TEST(CompactIds,ConsistentCheck)200 TEST(CompactIds, ConsistentCheck) {
201 const std::string input(R"(OpCapability Shader
202 OpMemoryModel Logical GLSL450
203 OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET
204 OpExecutionMode %main OriginUpperLeft
205 OpSource HLSL 600
206 OpName %main "main"
207 OpName %in_var_A "in.var.A"
208 OpName %out_var_SV_TARGET "out.var.SV_TARGET"
209 OpDecorate %in_var_A Location 0
210 OpDecorate %out_var_SV_TARGET Location 0
211 %void = OpTypeVoid
212 %3 = OpTypeFunction %void
213 %float = OpTypeFloat 32
214 %v4float = OpTypeVector %float 4
215 %_ptr_Input_v4float = OpTypePointer Input %v4float
216 %_ptr_Output_v4float = OpTypePointer Output %v4float
217 %in_var_A = OpVariable %_ptr_Input_v4float Input
218 %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
219 %main = OpFunction %void None %3
220 %5 = OpLabel
221 %12 = OpLoad %v4float %in_var_A
222 %23 = OpVectorShuffle %v4float %12 %12 0 0 0 1
223 OpStore %out_var_SV_TARGET %23
224 OpReturn
225 OpFunctionEnd
226 )");
227
228 spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
229 std::unique_ptr<IRContext> context =
230 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input,
231 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
232 ASSERT_NE(context, nullptr);
233
234 CompactIdsPass compact_id_pass;
235 context->BuildInvalidAnalyses(compact_id_pass.GetPreservedAnalyses());
236 const auto status = compact_id_pass.Run(context.get());
237 ASSERT_NE(status, Pass::Status::Failure);
238 EXPECT_TRUE(context->IsConsistent());
239
240 // Test output just in case
241 std::vector<uint32_t> binary;
242 context->module()->ToBinary(&binary, false);
243 std::string disassembly;
244 tools.Disassemble(binary, &disassembly,
245 SpirvTools::kDefaultDisassembleOption);
246
247 const std::string expected(R"(OpCapability Shader
248 OpMemoryModel Logical GLSL450
249 OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET
250 OpExecutionMode %main OriginUpperLeft
251 OpSource HLSL 600
252 OpName %main "main"
253 OpName %in_var_A "in.var.A"
254 OpName %out_var_SV_TARGET "out.var.SV_TARGET"
255 OpDecorate %in_var_A Location 0
256 OpDecorate %out_var_SV_TARGET Location 0
257 %void = OpTypeVoid
258 %5 = OpTypeFunction %void
259 %float = OpTypeFloat 32
260 %v4float = OpTypeVector %float 4
261 %_ptr_Input_v4float = OpTypePointer Input %v4float
262 %_ptr_Output_v4float = OpTypePointer Output %v4float
263 %in_var_A = OpVariable %_ptr_Input_v4float Input
264 %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
265 %main = OpFunction %void None %5
266 %10 = OpLabel
267 %11 = OpLoad %v4float %in_var_A
268 %12 = OpVectorShuffle %v4float %11 %11 0 0 0 1
269 OpStore %out_var_SV_TARGET %12
270 OpReturn
271 OpFunctionEnd
272 )");
273
274 EXPECT_THAT(disassembly, ::testing::Eq(expected));
275 }
276
277 } // namespace
278 } // namespace opt
279 } // namespace spvtools
280