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