1 // Copyright (c) 2016 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 <algorithm>
16 #include <memory>
17 #include <string>
18 #include <unordered_set>
19 #include <utility>
20 #include <vector>
21 
22 #include "gtest/gtest.h"
23 #include "source/opt/build_module.h"
24 #include "source/opt/ir_context.h"
25 #include "spirv-tools/libspirv.hpp"
26 
27 namespace spvtools {
28 namespace opt {
29 namespace {
30 
DoRoundTripCheck(const std::string & text)31 void DoRoundTripCheck(const std::string& text) {
32   SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
33   std::unique_ptr<IRContext> context =
34       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
35   ASSERT_NE(nullptr, context) << "Failed to assemble\n" << text;
36 
37   std::vector<uint32_t> binary;
38   context->module()->ToBinary(&binary, /* skip_nop = */ false);
39 
40   std::string disassembled_text;
41   EXPECT_TRUE(t.Disassemble(binary, &disassembled_text));
42   EXPECT_EQ(text, disassembled_text);
43 }
44 
TEST(IrBuilder,RoundTrip)45 TEST(IrBuilder, RoundTrip) {
46   // #version 310 es
47   // int add(int a, int b) { return a + b; }
48   // void main() { add(1, 2); }
49   DoRoundTripCheck(
50       // clang-format off
51                "OpCapability Shader\n"
52           "%1 = OpExtInstImport \"GLSL.std.450\"\n"
53                "OpMemoryModel Logical GLSL450\n"
54                "OpEntryPoint Vertex %main \"main\"\n"
55                "OpSource ESSL 310\n"
56                "OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"\n"
57                "OpSourceExtension \"GL_GOOGLE_include_directive\"\n"
58                "OpName %main \"main\"\n"
59                "OpName %add_i1_i1_ \"add(i1;i1;\"\n"
60                "OpName %a \"a\"\n"
61                "OpName %b \"b\"\n"
62                "OpName %param \"param\"\n"
63                "OpName %param_0 \"param\"\n"
64        "%void = OpTypeVoid\n"
65           "%9 = OpTypeFunction %void\n"
66         "%int = OpTypeInt 32 1\n"
67  "%_ptr_Function_int = OpTypePointer Function %int\n"
68          "%12 = OpTypeFunction %int %_ptr_Function_int %_ptr_Function_int\n"
69       "%int_1 = OpConstant %int 1\n"
70       "%int_2 = OpConstant %int 2\n"
71        "%main = OpFunction %void None %9\n"
72          "%15 = OpLabel\n"
73       "%param = OpVariable %_ptr_Function_int Function\n"
74     "%param_0 = OpVariable %_ptr_Function_int Function\n"
75                "OpStore %param %int_1\n"
76                "OpStore %param_0 %int_2\n"
77          "%16 = OpFunctionCall %int %add_i1_i1_ %param %param_0\n"
78                "OpReturn\n"
79                "OpFunctionEnd\n"
80  "%add_i1_i1_ = OpFunction %int None %12\n"
81           "%a = OpFunctionParameter %_ptr_Function_int\n"
82           "%b = OpFunctionParameter %_ptr_Function_int\n"
83          "%17 = OpLabel\n"
84          "%18 = OpLoad %int %a\n"
85          "%19 = OpLoad %int %b\n"
86          "%20 = OpIAdd %int %18 %19\n"
87                "OpReturnValue %20\n"
88                "OpFunctionEnd\n");
89   // clang-format on
90 }
91 
TEST(IrBuilder,RoundTripIncompleteBasicBlock)92 TEST(IrBuilder, RoundTripIncompleteBasicBlock) {
93   DoRoundTripCheck(
94       "%2 = OpFunction %1 None %3\n"
95       "%4 = OpLabel\n"
96       "OpNop\n");
97 }
98 
TEST(IrBuilder,RoundTripIncompleteFunction)99 TEST(IrBuilder, RoundTripIncompleteFunction) {
100   DoRoundTripCheck("%2 = OpFunction %1 None %3\n");
101 }
102 
TEST(IrBuilder,KeepLineDebugInfo)103 TEST(IrBuilder, KeepLineDebugInfo) {
104   // #version 310 es
105   // void main() {}
106   DoRoundTripCheck(
107       // clang-format off
108                "OpCapability Shader\n"
109           "%1 = OpExtInstImport \"GLSL.std.450\"\n"
110                "OpMemoryModel Logical GLSL450\n"
111                "OpEntryPoint Vertex %main \"main\"\n"
112           "%3 = OpString \"minimal.vert\"\n"
113                "OpSource ESSL 310\n"
114                "OpName %main \"main\"\n"
115                "OpLine %3 10 10\n"
116        "%void = OpTypeVoid\n"
117                "OpLine %3 100 100\n"
118           "%5 = OpTypeFunction %void\n"
119        "%main = OpFunction %void None %5\n"
120                "OpLine %3 1 1\n"
121                "OpNoLine\n"
122                "OpLine %3 2 2\n"
123                "OpLine %3 3 3\n"
124           "%6 = OpLabel\n"
125                "OpLine %3 4 4\n"
126                "OpNoLine\n"
127                "OpReturn\n"
128                "OpFunctionEnd\n");
129   // clang-format on
130 }
131 
TEST(IrBuilder,LocalGlobalVariables)132 TEST(IrBuilder, LocalGlobalVariables) {
133   // #version 310 es
134   //
135   // float gv1 = 10.;
136   // float gv2 = 100.;
137   //
138   // float f() {
139   //   float lv1 = gv1 + gv2;
140   //   float lv2 = gv1 * gv2;
141   //   return lv1 / lv2;
142   // }
143   //
144   // void main() {
145   //   float lv1 = gv1 - gv2;
146   // }
147   DoRoundTripCheck(
148       // clang-format off
149                "OpCapability Shader\n"
150           "%1 = OpExtInstImport \"GLSL.std.450\"\n"
151                "OpMemoryModel Logical GLSL450\n"
152                "OpEntryPoint Vertex %main \"main\"\n"
153                "OpSource ESSL 310\n"
154                "OpName %main \"main\"\n"
155                "OpName %f_ \"f(\"\n"
156                "OpName %gv1 \"gv1\"\n"
157                "OpName %gv2 \"gv2\"\n"
158                "OpName %lv1 \"lv1\"\n"
159                "OpName %lv2 \"lv2\"\n"
160                "OpName %lv1_0 \"lv1\"\n"
161        "%void = OpTypeVoid\n"
162          "%10 = OpTypeFunction %void\n"
163       "%float = OpTypeFloat 32\n"
164          "%12 = OpTypeFunction %float\n"
165  "%_ptr_Private_float = OpTypePointer Private %float\n"
166         "%gv1 = OpVariable %_ptr_Private_float Private\n"
167    "%float_10 = OpConstant %float 10\n"
168         "%gv2 = OpVariable %_ptr_Private_float Private\n"
169   "%float_100 = OpConstant %float 100\n"
170  "%_ptr_Function_float = OpTypePointer Function %float\n"
171        "%main = OpFunction %void None %10\n"
172          "%17 = OpLabel\n"
173       "%lv1_0 = OpVariable %_ptr_Function_float Function\n"
174                "OpStore %gv1 %float_10\n"
175                "OpStore %gv2 %float_100\n"
176          "%18 = OpLoad %float %gv1\n"
177          "%19 = OpLoad %float %gv2\n"
178          "%20 = OpFSub %float %18 %19\n"
179                "OpStore %lv1_0 %20\n"
180                "OpReturn\n"
181                "OpFunctionEnd\n"
182          "%f_ = OpFunction %float None %12\n"
183          "%21 = OpLabel\n"
184         "%lv1 = OpVariable %_ptr_Function_float Function\n"
185         "%lv2 = OpVariable %_ptr_Function_float Function\n"
186          "%22 = OpLoad %float %gv1\n"
187          "%23 = OpLoad %float %gv2\n"
188          "%24 = OpFAdd %float %22 %23\n"
189                "OpStore %lv1 %24\n"
190          "%25 = OpLoad %float %gv1\n"
191          "%26 = OpLoad %float %gv2\n"
192          "%27 = OpFMul %float %25 %26\n"
193                "OpStore %lv2 %27\n"
194          "%28 = OpLoad %float %lv1\n"
195          "%29 = OpLoad %float %lv2\n"
196          "%30 = OpFDiv %float %28 %29\n"
197                "OpReturnValue %30\n"
198                "OpFunctionEnd\n");
199   // clang-format on
200 }
201 
TEST(IrBuilder,OpUndefOutsideFunction)202 TEST(IrBuilder, OpUndefOutsideFunction) {
203   // #version 310 es
204   // void main() {}
205   const std::string text =
206       // clang-format off
207                "OpMemoryModel Logical GLSL450\n"
208         "%int = OpTypeInt 32 1\n"
209        "%uint = OpTypeInt 32 0\n"
210       "%float = OpTypeFloat 32\n"
211           "%4 = OpUndef %int\n"
212      "%int_10 = OpConstant %int 10\n"
213           "%6 = OpUndef %uint\n"
214        "%bool = OpTypeBool\n"
215           "%8 = OpUndef %float\n"
216      "%double = OpTypeFloat 64\n";
217   // clang-format on
218 
219   SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
220   std::unique_ptr<IRContext> context =
221       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
222   ASSERT_NE(nullptr, context);
223 
224   const auto opundef_count = std::count_if(
225       context->module()->types_values_begin(),
226       context->module()->types_values_end(),
227       [](const Instruction& inst) { return inst.opcode() == SpvOpUndef; });
228   EXPECT_EQ(3, opundef_count);
229 
230   std::vector<uint32_t> binary;
231   context->module()->ToBinary(&binary, /* skip_nop = */ false);
232 
233   std::string disassembled_text;
234   EXPECT_TRUE(t.Disassemble(binary, &disassembled_text));
235   EXPECT_EQ(text, disassembled_text);
236 }
237 
TEST(IrBuilder,OpUndefInBasicBlock)238 TEST(IrBuilder, OpUndefInBasicBlock) {
239   DoRoundTripCheck(
240       // clang-format off
241                "OpMemoryModel Logical GLSL450\n"
242                "OpName %main \"main\"\n"
243        "%void = OpTypeVoid\n"
244        "%uint = OpTypeInt 32 0\n"
245      "%double = OpTypeFloat 64\n"
246           "%5 = OpTypeFunction %void\n"
247        "%main = OpFunction %void None %5\n"
248           "%6 = OpLabel\n"
249           "%7 = OpUndef %uint\n"
250           "%8 = OpUndef %double\n"
251                "OpReturn\n"
252                "OpFunctionEnd\n");
253   // clang-format on
254 }
255 
TEST(IrBuilder,KeepLineDebugInfoBeforeType)256 TEST(IrBuilder, KeepLineDebugInfoBeforeType) {
257   DoRoundTripCheck(
258       // clang-format off
259                "OpCapability Shader\n"
260                "OpMemoryModel Logical GLSL450\n"
261           "%1 = OpString \"minimal.vert\"\n"
262                "OpLine %1 1 1\n"
263                "OpNoLine\n"
264        "%void = OpTypeVoid\n"
265                "OpLine %1 2 2\n"
266           "%3 = OpTypeFunction %void\n");
267   // clang-format on
268 }
269 
TEST(IrBuilder,KeepLineDebugInfoBeforeLabel)270 TEST(IrBuilder, KeepLineDebugInfoBeforeLabel) {
271   DoRoundTripCheck(
272       // clang-format off
273                "OpCapability Shader\n"
274                "OpMemoryModel Logical GLSL450\n"
275           "%1 = OpString \"minimal.vert\"\n"
276        "%void = OpTypeVoid\n"
277           "%3 = OpTypeFunction %void\n"
278        "%4 = OpFunction %void None %3\n"
279           "%5 = OpLabel\n"
280    "OpBranch %6\n"
281                "OpLine %1 1 1\n"
282                "OpLine %1 2 2\n"
283           "%6 = OpLabel\n"
284                "OpBranch %7\n"
285                "OpLine %1 100 100\n"
286           "%7 = OpLabel\n"
287                "OpReturn\n"
288                "OpFunctionEnd\n");
289   // clang-format on
290 }
291 
TEST(IrBuilder,KeepLineDebugInfoBeforeFunctionEnd)292 TEST(IrBuilder, KeepLineDebugInfoBeforeFunctionEnd) {
293   DoRoundTripCheck(
294       // clang-format off
295                "OpCapability Shader\n"
296                "OpMemoryModel Logical GLSL450\n"
297           "%1 = OpString \"minimal.vert\"\n"
298        "%void = OpTypeVoid\n"
299           "%3 = OpTypeFunction %void\n"
300        "%4 = OpFunction %void None %3\n"
301                "OpLine %1 1 1\n"
302                "OpLine %1 2 2\n"
303                "OpFunctionEnd\n");
304   // clang-format on
305 }
306 
TEST(IrBuilder,KeepModuleProcessedInRightPlace)307 TEST(IrBuilder, KeepModuleProcessedInRightPlace) {
308   DoRoundTripCheck(
309       // clang-format off
310                "OpCapability Shader\n"
311                "OpMemoryModel Logical GLSL450\n"
312           "%1 = OpString \"minimal.vert\"\n"
313                "OpName %void \"void\"\n"
314                "OpModuleProcessed \"Made it faster\"\n"
315                "OpModuleProcessed \".. and smaller\"\n"
316        "%void = OpTypeVoid\n");
317   // clang-format on
318 }
319 
320 // Checks the given |error_message| is reported when trying to build a module
321 // from the given |assembly|.
DoErrorMessageCheck(const std::string & assembly,const std::string & error_message,uint32_t line_num)322 void DoErrorMessageCheck(const std::string& assembly,
323                          const std::string& error_message, uint32_t line_num) {
324   auto consumer = [error_message, line_num](spv_message_level_t, const char*,
325                                             const spv_position_t& position,
326                                             const char* m) {
327     EXPECT_EQ(error_message, m);
328     EXPECT_EQ(line_num, position.line);
329   };
330 
331   SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
332   std::unique_ptr<IRContext> context =
333       BuildModule(SPV_ENV_UNIVERSAL_1_1, std::move(consumer), assembly);
334   EXPECT_EQ(nullptr, context);
335 }
336 
TEST(IrBuilder,FunctionInsideFunction)337 TEST(IrBuilder, FunctionInsideFunction) {
338   DoErrorMessageCheck("%2 = OpFunction %1 None %3\n%5 = OpFunction %4 None %6",
339                       "function inside function", 2);
340 }
341 
TEST(IrBuilder,MismatchOpFunctionEnd)342 TEST(IrBuilder, MismatchOpFunctionEnd) {
343   DoErrorMessageCheck("OpFunctionEnd",
344                       "OpFunctionEnd without corresponding OpFunction", 1);
345 }
346 
TEST(IrBuilder,OpFunctionEndInsideBasicBlock)347 TEST(IrBuilder, OpFunctionEndInsideBasicBlock) {
348   DoErrorMessageCheck(
349       "%2 = OpFunction %1 None %3\n"
350       "%4 = OpLabel\n"
351       "OpFunctionEnd",
352       "OpFunctionEnd inside basic block", 3);
353 }
354 
TEST(IrBuilder,BasicBlockOutsideFunction)355 TEST(IrBuilder, BasicBlockOutsideFunction) {
356   DoErrorMessageCheck("OpCapability Shader\n%1 = OpLabel",
357                       "OpLabel outside function", 2);
358 }
359 
TEST(IrBuilder,OpLabelInsideBasicBlock)360 TEST(IrBuilder, OpLabelInsideBasicBlock) {
361   DoErrorMessageCheck(
362       "%2 = OpFunction %1 None %3\n"
363       "%4 = OpLabel\n"
364       "%5 = OpLabel",
365       "OpLabel inside basic block", 3);
366 }
367 
TEST(IrBuilder,TerminatorOutsideFunction)368 TEST(IrBuilder, TerminatorOutsideFunction) {
369   DoErrorMessageCheck("OpReturn", "terminator instruction outside function", 1);
370 }
371 
TEST(IrBuilder,TerminatorOutsideBasicBlock)372 TEST(IrBuilder, TerminatorOutsideBasicBlock) {
373   DoErrorMessageCheck("%2 = OpFunction %1 None %3\nOpReturn",
374                       "terminator instruction outside basic block", 2);
375 }
376 
TEST(IrBuilder,NotAllowedInstAppearingInFunction)377 TEST(IrBuilder, NotAllowedInstAppearingInFunction) {
378   DoErrorMessageCheck("%2 = OpFunction %1 None %3\n%5 = OpVariable %4 Function",
379                       "Non-OpFunctionParameter (opcode: 59) found inside "
380                       "function but outside basic block",
381                       2);
382 }
383 
TEST(IrBuilder,UniqueIds)384 TEST(IrBuilder, UniqueIds) {
385   const std::string text =
386       // clang-format off
387                "OpCapability Shader\n"
388           "%1 = OpExtInstImport \"GLSL.std.450\"\n"
389                "OpMemoryModel Logical GLSL450\n"
390                "OpEntryPoint Vertex %main \"main\"\n"
391                "OpSource ESSL 310\n"
392                "OpName %main \"main\"\n"
393                "OpName %f_ \"f(\"\n"
394                "OpName %gv1 \"gv1\"\n"
395                "OpName %gv2 \"gv2\"\n"
396                "OpName %lv1 \"lv1\"\n"
397                "OpName %lv2 \"lv2\"\n"
398                "OpName %lv1_0 \"lv1\"\n"
399        "%void = OpTypeVoid\n"
400          "%10 = OpTypeFunction %void\n"
401       "%float = OpTypeFloat 32\n"
402          "%12 = OpTypeFunction %float\n"
403  "%_ptr_Private_float = OpTypePointer Private %float\n"
404         "%gv1 = OpVariable %_ptr_Private_float Private\n"
405    "%float_10 = OpConstant %float 10\n"
406         "%gv2 = OpVariable %_ptr_Private_float Private\n"
407   "%float_100 = OpConstant %float 100\n"
408  "%_ptr_Function_float = OpTypePointer Function %float\n"
409        "%main = OpFunction %void None %10\n"
410          "%17 = OpLabel\n"
411       "%lv1_0 = OpVariable %_ptr_Function_float Function\n"
412                "OpStore %gv1 %float_10\n"
413                "OpStore %gv2 %float_100\n"
414          "%18 = OpLoad %float %gv1\n"
415          "%19 = OpLoad %float %gv2\n"
416          "%20 = OpFSub %float %18 %19\n"
417                "OpStore %lv1_0 %20\n"
418                "OpReturn\n"
419                "OpFunctionEnd\n"
420          "%f_ = OpFunction %float None %12\n"
421          "%21 = OpLabel\n"
422         "%lv1 = OpVariable %_ptr_Function_float Function\n"
423         "%lv2 = OpVariable %_ptr_Function_float Function\n"
424          "%22 = OpLoad %float %gv1\n"
425          "%23 = OpLoad %float %gv2\n"
426          "%24 = OpFAdd %float %22 %23\n"
427                "OpStore %lv1 %24\n"
428          "%25 = OpLoad %float %gv1\n"
429          "%26 = OpLoad %float %gv2\n"
430          "%27 = OpFMul %float %25 %26\n"
431                "OpStore %lv2 %27\n"
432          "%28 = OpLoad %float %lv1\n"
433          "%29 = OpLoad %float %lv2\n"
434          "%30 = OpFDiv %float %28 %29\n"
435                "OpReturnValue %30\n"
436                "OpFunctionEnd\n";
437   // clang-format on
438 
439   std::unique_ptr<IRContext> context =
440       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
441   ASSERT_NE(nullptr, context);
442 
443   std::unordered_set<uint32_t> ids;
444   context->module()->ForEachInst([&ids](const Instruction* inst) {
445     EXPECT_TRUE(ids.insert(inst->unique_id()).second);
446   });
447 }
448 
449 }  // namespace
450 }  // namespace opt
451 }  // namespace spvtools
452