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