• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 LunarG 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 <sstream>
16 #include <string>
17 
18 #include "gmock/gmock.h"
19 #include "test/unit_spirv.h"
20 #include "test/val/val_fixtures.h"
21 
22 namespace spvtools {
23 namespace val {
24 namespace {
25 
26 using ::testing::HasSubstr;
27 using ::testing::Not;
28 
29 using ValidateAdjacency = spvtest::ValidateBase<bool>;
30 
TEST_F(ValidateAdjacency,OpPhiBeginsModuleFail)31 TEST_F(ValidateAdjacency, OpPhiBeginsModuleFail) {
32   const std::string module = R"(
33 %result = OpPhi %bool %true %true_label %false %false_label
34 OpCapability Shader
35 OpMemoryModel Logical GLSL450
36 OpEntryPoint Fragment %main "main"
37 OpExecutionMode %main OriginUpperLeft
38 %void = OpTypeVoid
39 %bool = OpTypeBool
40 %true = OpConstantTrue %bool
41 %false = OpConstantFalse %bool
42 %func = OpTypeFunction %void
43 %main = OpFunction %void None %func
44 %main_entry = OpLabel
45 OpBranch %true_label
46 %true_label = OpLabel
47 OpBranch %false_label
48 %false_label = OpLabel
49 OpBranch %end_label
50 OpReturn
51 OpFunctionEnd
52 )";
53 
54   CompileSuccessfully(module);
55   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
56   EXPECT_THAT(getDiagnosticString(),
57               HasSubstr("ID 1[%bool] has not been defined"));
58 }
59 
TEST_F(ValidateAdjacency,OpLoopMergeEndsModuleFail)60 TEST_F(ValidateAdjacency, OpLoopMergeEndsModuleFail) {
61   const std::string module = R"(
62 OpCapability Shader
63 OpMemoryModel Logical GLSL450
64 OpEntryPoint Fragment %main "main"
65 OpExecutionMode %main OriginUpperLeft
66 %void = OpTypeVoid
67 %func = OpTypeFunction %void
68 %main = OpFunction %void None %func
69 %main_entry = OpLabel
70 OpBranch %loop
71 %loop = OpLabel
72 OpLoopMerge %end %loop None
73 )";
74 
75   CompileSuccessfully(module);
76   EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
77   EXPECT_THAT(getDiagnosticString(),
78               HasSubstr("Missing OpFunctionEnd at end of module"));
79 }
80 
TEST_F(ValidateAdjacency,OpSelectionMergeEndsModuleFail)81 TEST_F(ValidateAdjacency, OpSelectionMergeEndsModuleFail) {
82   const std::string module = R"(
83 OpCapability Shader
84 OpMemoryModel Logical GLSL450
85 OpEntryPoint Fragment %main "main"
86 OpExecutionMode %main OriginUpperLeft
87 %void = OpTypeVoid
88 %func = OpTypeFunction %void
89 %main = OpFunction %void None %func
90 %main_entry = OpLabel
91 OpBranch %merge
92 %merge = OpLabel
93 OpSelectionMerge %merge None
94 )";
95 
96   CompileSuccessfully(module);
97   EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
98   EXPECT_THAT(getDiagnosticString(),
99               HasSubstr("Missing OpFunctionEnd at end of module"));
100 }
101 
GenerateShaderCode(const std::string & body,const std::string & capabilities_and_extensions="OpCapability Shader",const std::string & execution_model="Fragment")102 std::string GenerateShaderCode(
103     const std::string& body,
104     const std::string& capabilities_and_extensions = "OpCapability Shader",
105     const std::string& execution_model = "Fragment") {
106   std::ostringstream ss;
107   ss << capabilities_and_extensions << "\n";
108   ss << "OpMemoryModel Logical GLSL450\n";
109   ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
110   if (execution_model == "Fragment") {
111     ss << "OpExecutionMode %main OriginUpperLeft\n";
112   }
113 
114   ss << R"(
115 %string = OpString ""
116 %void = OpTypeVoid
117 %bool = OpTypeBool
118 %int = OpTypeInt 32 0
119 %true = OpConstantTrue %bool
120 %false = OpConstantFalse %bool
121 %zero = OpConstant %int 0
122 %int_1 = OpConstant %int 1
123 %func = OpTypeFunction %void
124 %func_int = OpTypePointer Function %int
125 %main = OpFunction %void None %func
126 %main_entry = OpLabel
127 )";
128 
129   ss << body;
130 
131   ss << R"(
132 OpReturn
133 OpFunctionEnd)";
134 
135   return ss.str();
136 }
137 
TEST_F(ValidateAdjacency,OpPhiPreceededByOpLabelSuccess)138 TEST_F(ValidateAdjacency, OpPhiPreceededByOpLabelSuccess) {
139   const std::string body = R"(
140 OpSelectionMerge %end_label None
141 OpBranchConditional %true %true_label %false_label
142 %true_label = OpLabel
143 OpBranch %end_label
144 %false_label = OpLabel
145 OpBranch %end_label
146 %end_label = OpLabel
147 OpLine %string 0 0
148 %result = OpPhi %bool %true %true_label %false %false_label
149 )";
150 
151   CompileSuccessfully(GenerateShaderCode(body));
152   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
153 }
154 
TEST_F(ValidateAdjacency,OpPhiPreceededByOpPhiSuccess)155 TEST_F(ValidateAdjacency, OpPhiPreceededByOpPhiSuccess) {
156   const std::string body = R"(
157 OpSelectionMerge %end_label None
158 OpBranchConditional %true %true_label %false_label
159 %true_label = OpLabel
160 OpBranch %end_label
161 %false_label = OpLabel
162 OpBranch %end_label
163 %end_label = OpLabel
164 %1 = OpPhi %bool %true %true_label %false %false_label
165 %2 = OpPhi %bool %true %true_label %false %false_label
166 )";
167 
168   CompileSuccessfully(GenerateShaderCode(body));
169   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
170 }
171 
TEST_F(ValidateAdjacency,OpPhiPreceededByOpLineSuccess)172 TEST_F(ValidateAdjacency, OpPhiPreceededByOpLineSuccess) {
173   const std::string body = R"(
174 OpSelectionMerge %end_label None
175 OpBranchConditional %true %true_label %false_label
176 %true_label = OpLabel
177 OpBranch %end_label
178 %false_label = OpLabel
179 OpBranch %end_label
180 %end_label = OpLabel
181 OpLine %string 0 0
182 %result = OpPhi %bool %true %true_label %false %false_label
183 )";
184 
185   CompileSuccessfully(GenerateShaderCode(body));
186   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
187 }
188 
TEST_F(ValidateAdjacency,OpPhiPreceededByBadOpFail)189 TEST_F(ValidateAdjacency, OpPhiPreceededByBadOpFail) {
190   const std::string body = R"(
191 OpSelectionMerge %end_label None
192 OpBranchConditional %true %true_label %false_label
193 %true_label = OpLabel
194 OpBranch %end_label
195 %false_label = OpLabel
196 OpBranch %end_label
197 %end_label = OpLabel
198 OpNop
199 %result = OpPhi %bool %true %true_label %false %false_label
200 )";
201 
202   CompileSuccessfully(GenerateShaderCode(body));
203   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
204   EXPECT_THAT(getDiagnosticString(),
205               HasSubstr("OpPhi must appear within a non-entry block before all "
206                         "non-OpPhi instructions"));
207 }
208 
TEST_F(ValidateAdjacency,OpPhiPreceededByOpLineAndBadOpFail)209 TEST_F(ValidateAdjacency, OpPhiPreceededByOpLineAndBadOpFail) {
210   const std::string body = R"(
211 OpSelectionMerge %end_label None
212 OpBranchConditional %true %true_label %false_label
213 %true_label = OpLabel
214 OpBranch %end_label
215 %false_label = OpLabel
216 OpBranch %end_label
217 %end_label = OpLabel
218 OpNop
219 OpLine %string 1 1
220 %result = OpPhi %bool %true %true_label %false %false_label
221 )";
222 
223   CompileSuccessfully(GenerateShaderCode(body));
224   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
225   EXPECT_THAT(getDiagnosticString(),
226               HasSubstr("OpPhi must appear within a non-entry block before all "
227                         "non-OpPhi instructions"));
228 }
229 
TEST_F(ValidateAdjacency,OpPhiFollowedByOpLineGood)230 TEST_F(ValidateAdjacency, OpPhiFollowedByOpLineGood) {
231   const std::string body = R"(
232 OpSelectionMerge %end_label None
233 OpBranchConditional %true %true_label %false_label
234 %true_label = OpLabel
235 OpBranch %end_label
236 %false_label = OpLabel
237 OpBranch %end_label
238 %end_label = OpLabel
239 %result = OpPhi %bool %true %true_label %false %false_label
240 OpLine %string 1 1
241 OpNop
242 OpNop
243 OpLine %string 2 1
244 OpNop
245 OpLine %string 3 1
246 )";
247 
248   CompileSuccessfully(GenerateShaderCode(body));
249   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
250 }
251 
TEST_F(ValidateAdjacency,OpPhiMultipleOpLineAndOpPhiFail)252 TEST_F(ValidateAdjacency, OpPhiMultipleOpLineAndOpPhiFail) {
253   const std::string body = R"(
254 OpSelectionMerge %end_label None
255 OpBranchConditional %true %true_label %false_label
256 %true_label = OpLabel
257 OpBranch %end_label
258 %false_label = OpLabel
259 OpBranch %end_label
260 %end_label = OpLabel
261 OpLine %string 1 1
262 %value = OpPhi %int %zero %true_label %int_1 %false_label
263 OpNop
264 OpLine %string 2 1
265 OpNop
266 OpLine %string 3 1
267 %result = OpPhi %bool %true %true_label %false %false_label
268 )";
269 
270   CompileSuccessfully(GenerateShaderCode(body));
271   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
272   EXPECT_THAT(getDiagnosticString(),
273               HasSubstr("OpPhi must appear within a non-entry block before all "
274                         "non-OpPhi instructions"));
275 }
276 
TEST_F(ValidateAdjacency,OpPhiMultipleOpLineAndOpPhiGood)277 TEST_F(ValidateAdjacency, OpPhiMultipleOpLineAndOpPhiGood) {
278   const std::string body = R"(
279 OpSelectionMerge %end_label None
280 OpBranchConditional %true %true_label %false_label
281 %true_label = OpLabel
282 OpBranch %end_label
283 %false_label = OpLabel
284 OpBranch %end_label
285 %end_label = OpLabel
286 OpLine %string 1 1
287 %value = OpPhi %int %zero %true_label %int_1 %false_label
288 OpLine %string 2 1
289 %result = OpPhi %bool %true %true_label %false %false_label
290 OpLine %string 3 1
291 OpNop
292 OpNop
293 OpLine %string 4 1
294 OpNop
295 )";
296 
297   CompileSuccessfully(GenerateShaderCode(body));
298   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
299 }
300 
TEST_F(ValidateAdjacency,OpPhiInEntryBlockBad)301 TEST_F(ValidateAdjacency, OpPhiInEntryBlockBad) {
302   const std::string body = R"(
303 OpLine %string 1 1
304 %value = OpPhi %int
305 OpLine %string 2 1
306 OpNop
307 OpLine %string 3 1
308 OpNop
309 )";
310 
311   CompileSuccessfully(GenerateShaderCode(body));
312   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
313   EXPECT_THAT(getDiagnosticString(),
314               HasSubstr("OpPhi must appear within a non-entry block before all "
315                         "non-OpPhi instructions"));
316 }
317 
TEST_F(ValidateAdjacency,NonSemanticBeforeOpPhiBad)318 TEST_F(ValidateAdjacency, NonSemanticBeforeOpPhiBad) {
319   const std::string body = R"(
320 OpSelectionMerge %end_label None
321 OpBranchConditional %true %true_label %false_label
322 %true_label = OpLabel
323 OpBranch %end_label
324 %false_label = OpLabel
325 OpBranch %end_label
326 %end_label = OpLabel
327 %placeholder = OpExtInst %void %extinst 123 %int_1
328 %result = OpPhi %bool %true %true_label %false %false_label
329 )";
330 
331   const std::string extra = R"(OpCapability Shader
332 OpExtension "SPV_KHR_non_semantic_info"
333 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
334 )";
335 
336   CompileSuccessfully(GenerateShaderCode(body, extra));
337   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
338   EXPECT_THAT(getDiagnosticString(),
339               HasSubstr("OpPhi must appear within a non-entry block before all "
340                         "non-OpPhi instructions"));
341 }
342 
TEST_F(ValidateAdjacency,NonSemanticBetweenOpPhiBad)343 TEST_F(ValidateAdjacency, NonSemanticBetweenOpPhiBad) {
344   const std::string body = R"(
345 OpSelectionMerge %end_label None
346 OpBranchConditional %true %true_label %false_label
347 %true_label = OpLabel
348 OpBranch %end_label
349 %false_label = OpLabel
350 OpBranch %end_label
351 %end_label = OpLabel
352 %result1 = OpPhi %bool %true %true_label %false %false_label
353 %placeholder = OpExtInst %void %extinst 123 %int_1
354 %result2 = OpPhi %bool %true %true_label %false %false_label
355 )";
356 
357   const std::string extra = R"(OpCapability Shader
358 OpExtension "SPV_KHR_non_semantic_info"
359 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
360 )";
361 
362   CompileSuccessfully(GenerateShaderCode(body, extra));
363   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
364   EXPECT_THAT(getDiagnosticString(),
365               HasSubstr("OpPhi must appear within a non-entry block before all "
366                         "non-OpPhi instructions"));
367 }
368 
TEST_F(ValidateAdjacency,NonSemanticAfterOpPhiGood)369 TEST_F(ValidateAdjacency, NonSemanticAfterOpPhiGood) {
370   const std::string body = R"(
371 OpSelectionMerge %end_label None
372 OpBranchConditional %true %true_label %false_label
373 %true_label = OpLabel
374 OpBranch %end_label
375 %false_label = OpLabel
376 OpBranch %end_label
377 %end_label = OpLabel
378 OpLine %string 0 0
379 %result = OpPhi %bool %true %true_label %false %false_label
380 %placeholder = OpExtInst %void %extinst 123 %int_1
381 )";
382 
383   const std::string extra = R"(OpCapability Shader
384 OpExtension "SPV_KHR_non_semantic_info"
385 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
386 )";
387 
388   CompileSuccessfully(GenerateShaderCode(body, extra));
389   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
390 }
391 
TEST_F(ValidateAdjacency,NonSemanticBeforeOpFunctionParameterBad)392 TEST_F(ValidateAdjacency, NonSemanticBeforeOpFunctionParameterBad) {
393   const std::string body = R"(
394 OpCapability Shader
395 OpExtension "SPV_KHR_non_semantic_info"
396 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
397 OpMemoryModel Logical GLSL450
398 OpEntryPoint Fragment %main "main"
399 OpExecutionMode %main OriginUpperLeft
400 
401 %string = OpString ""
402 %void = OpTypeVoid
403 %bool = OpTypeBool
404 %int = OpTypeInt 32 0
405 %true = OpConstantTrue %bool
406 %false = OpConstantFalse %bool
407 %zero = OpConstant %int 0
408 %int_1 = OpConstant %int 1
409 %func = OpTypeFunction %void
410 %func_int = OpTypePointer Function %int
411 %paramfunc_type = OpTypeFunction %void %int %int
412 
413 %paramfunc = OpFunction %void None %paramfunc_type
414 %placeholder = OpExtInst %void %extinst 123 %int_1
415 %a = OpFunctionParameter %int
416 %b = OpFunctionParameter %int
417 %paramfunc_entry = OpLabel
418 OpReturn
419 OpFunctionEnd
420 
421 %main = OpFunction %void None %func
422 %main_entry = OpLabel
423 OpReturn
424 OpFunctionEnd
425 )";
426 
427   CompileSuccessfully(body);
428   EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
429   EXPECT_THAT(getDiagnosticString(),
430               HasSubstr("Non-semantic OpExtInst within function definition "
431                         "must appear in a block"));
432 }
433 
TEST_F(ValidateAdjacency,NonSemanticBetweenOpFunctionParameterBad)434 TEST_F(ValidateAdjacency, NonSemanticBetweenOpFunctionParameterBad) {
435   const std::string body = R"(
436 OpCapability Shader
437 OpExtension "SPV_KHR_non_semantic_info"
438 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
439 OpMemoryModel Logical GLSL450
440 OpEntryPoint Fragment %main "main"
441 OpExecutionMode %main OriginUpperLeft
442 
443 %string = OpString ""
444 %void = OpTypeVoid
445 %bool = OpTypeBool
446 %int = OpTypeInt 32 0
447 %true = OpConstantTrue %bool
448 %false = OpConstantFalse %bool
449 %zero = OpConstant %int 0
450 %int_1 = OpConstant %int 1
451 %func = OpTypeFunction %void
452 %func_int = OpTypePointer Function %int
453 %paramfunc_type = OpTypeFunction %void %int %int
454 
455 %paramfunc = OpFunction %void None %paramfunc_type
456 %a = OpFunctionParameter %int
457 %placeholder = OpExtInst %void %extinst 123 %int_1
458 %b = OpFunctionParameter %int
459 %paramfunc_entry = OpLabel
460 OpReturn
461 OpFunctionEnd
462 
463 %main = OpFunction %void None %func
464 %main_entry = OpLabel
465 OpReturn
466 OpFunctionEnd
467 )";
468 
469   CompileSuccessfully(body);
470   EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
471   EXPECT_THAT(getDiagnosticString(),
472               HasSubstr("Non-semantic OpExtInst within function definition "
473                         "must appear in a block"));
474 }
475 
TEST_F(ValidateAdjacency,NonSemanticAfterOpFunctionParameterGood)476 TEST_F(ValidateAdjacency, NonSemanticAfterOpFunctionParameterGood) {
477   const std::string body = R"(
478 OpCapability Shader
479 OpExtension "SPV_KHR_non_semantic_info"
480 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
481 OpMemoryModel Logical GLSL450
482 OpEntryPoint Fragment %main "main"
483 OpExecutionMode %main OriginUpperLeft
484 
485 %string = OpString ""
486 %void = OpTypeVoid
487 %bool = OpTypeBool
488 %int = OpTypeInt 32 0
489 %true = OpConstantTrue %bool
490 %false = OpConstantFalse %bool
491 %zero = OpConstant %int 0
492 %int_1 = OpConstant %int 1
493 %func = OpTypeFunction %void
494 %func_int = OpTypePointer Function %int
495 %paramfunc_type = OpTypeFunction %void %int %int
496 
497 %paramfunc = OpFunction %void None %paramfunc_type
498 %a = OpFunctionParameter %int
499 %b = OpFunctionParameter %int
500 %paramfunc_entry = OpLabel
501 %placeholder = OpExtInst %void %extinst 123 %int_1
502 OpReturn
503 OpFunctionEnd
504 
505 %main = OpFunction %void None %func
506 %main_entry = OpLabel
507 OpReturn
508 OpFunctionEnd
509 )";
510 
511   CompileSuccessfully(body);
512   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
513 }
514 
TEST_F(ValidateAdjacency,NonSemanticBetweenFunctionsGood)515 TEST_F(ValidateAdjacency, NonSemanticBetweenFunctionsGood) {
516   const std::string body = R"(
517 OpCapability Shader
518 OpExtension "SPV_KHR_non_semantic_info"
519 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
520 OpMemoryModel Logical GLSL450
521 OpEntryPoint Fragment %main "main"
522 OpExecutionMode %main OriginUpperLeft
523 
524 %string = OpString ""
525 %void = OpTypeVoid
526 %bool = OpTypeBool
527 %int = OpTypeInt 32 0
528 %true = OpConstantTrue %bool
529 %false = OpConstantFalse %bool
530 %zero = OpConstant %int 0
531 %int_1 = OpConstant %int 1
532 %func = OpTypeFunction %void
533 %func_int = OpTypePointer Function %int
534 %paramfunc_type = OpTypeFunction %void %int %int
535 
536 %paramfunc = OpFunction %void None %paramfunc_type
537 %a = OpFunctionParameter %int
538 %b = OpFunctionParameter %int
539 %paramfunc_entry = OpLabel
540 OpReturn
541 OpFunctionEnd
542 
543 %placeholder = OpExtInst %void %extinst 123 %int_1
544 
545 %main = OpFunction %void None %func
546 %main_entry = OpLabel
547 OpReturn
548 OpFunctionEnd
549 )";
550 
551   CompileSuccessfully(body);
552   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
553 }
554 
TEST_F(ValidateAdjacency,OpVariableInFunctionGood)555 TEST_F(ValidateAdjacency, OpVariableInFunctionGood) {
556   const std::string body = R"(
557 OpLine %string 1 1
558 %var = OpVariable %func_int Function
559 OpLine %string 2 1
560 OpNop
561 OpLine %string 3 1
562 OpNop
563 )";
564 
565   CompileSuccessfully(GenerateShaderCode(body));
566   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
567 }
568 
TEST_F(ValidateAdjacency,OpVariableInFunctionMultipleGood)569 TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleGood) {
570   const std::string body = R"(
571 OpLine %string 1 1
572 %1 = OpVariable %func_int Function
573 OpLine %string 2 1
574 %2 = OpVariable %func_int Function
575 %3 = OpVariable %func_int Function
576 OpNop
577 OpLine %string 3 1
578 OpNop
579 )";
580 
581   CompileSuccessfully(GenerateShaderCode(body));
582   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
583 }
584 
TEST_F(ValidateAdjacency,OpVariableInFunctionBad)585 TEST_F(ValidateAdjacency, OpVariableInFunctionBad) {
586   const std::string body = R"(
587 %1 = OpUndef %int
588 %2 = OpVariable %func_int Function
589 )";
590 
591   CompileSuccessfully(GenerateShaderCode(body));
592   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
593   EXPECT_THAT(getDiagnosticString(),
594               HasSubstr("All OpVariable instructions in a function must be the "
595                         "first instructions"));
596 }
597 
TEST_F(ValidateAdjacency,OpVariableInFunctionMultipleBad)598 TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleBad) {
599   const std::string body = R"(
600 OpNop
601 %1 = OpVariable %func_int Function
602 OpLine %string 1 1
603 %2 = OpVariable %func_int Function
604 OpNop
605 OpNop
606 OpLine %string 2 1
607 %3 = OpVariable %func_int Function
608 )";
609 
610   CompileSuccessfully(GenerateShaderCode(body));
611   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
612   EXPECT_THAT(getDiagnosticString(),
613               HasSubstr("All OpVariable instructions in a function must be the "
614                         "first instructions"));
615 }
616 
TEST_F(ValidateAdjacency,OpLoopMergePreceedsOpBranchSuccess)617 TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchSuccess) {
618   const std::string body = R"(
619 OpBranch %loop
620 %loop = OpLabel
621 OpLoopMerge %end %loop None
622 OpBranch %loop
623 %end = OpLabel
624 )";
625 
626   CompileSuccessfully(GenerateShaderCode(body));
627   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
628 }
629 
TEST_F(ValidateAdjacency,OpLoopMergePreceedsOpBranchConditionalSuccess)630 TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchConditionalSuccess) {
631   const std::string body = R"(
632 OpBranch %loop
633 %loop = OpLabel
634 OpLoopMerge %end %loop None
635 OpBranchConditional %true %loop %end
636 %end = OpLabel
637 )";
638 
639   CompileSuccessfully(GenerateShaderCode(body));
640   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
641 }
642 
TEST_F(ValidateAdjacency,OpLoopMergePreceedsBadOpFail)643 TEST_F(ValidateAdjacency, OpLoopMergePreceedsBadOpFail) {
644   const std::string body = R"(
645 OpBranch %loop
646 %loop = OpLabel
647 OpLoopMerge %end %loop None
648 OpNop
649 OpBranchConditional %true %loop %end
650 %end = OpLabel
651 )";
652 
653   CompileSuccessfully(GenerateShaderCode(body));
654   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
655   EXPECT_THAT(getDiagnosticString(),
656               HasSubstr("OpLoopMerge must immediately precede either an "
657                         "OpBranch or OpBranchConditional instruction."));
658 }
659 
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsOpBranchConditionalSuccess)660 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpBranchConditionalSuccess) {
661   const std::string body = R"(
662 OpSelectionMerge %end_label None
663 OpBranchConditional %true %true_label %false_label
664 %true_label = OpLabel
665 OpBranch %end_label
666 %false_label = OpLabel
667 OpBranch %end_label
668 %end_label = OpLabel
669 )";
670 
671   CompileSuccessfully(GenerateShaderCode(body));
672   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
673 }
674 
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsOpSwitchSuccess)675 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpSwitchSuccess) {
676   const std::string body = R"(
677 OpSelectionMerge %merge None
678 OpSwitch %zero %merge 0 %label
679 %label = OpLabel
680 OpBranch %merge
681 %merge = OpLabel
682 )";
683 
684   CompileSuccessfully(GenerateShaderCode(body));
685   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
686 }
687 
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsBadOpFail)688 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsBadOpFail) {
689   const std::string body = R"(
690 OpSelectionMerge %merge None
691 OpNop
692 OpSwitch %zero %merge 0 %label
693 %label = OpLabel
694 OpBranch %merge
695 %merge = OpLabel
696 )";
697 
698   CompileSuccessfully(GenerateShaderCode(body));
699   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
700   EXPECT_THAT(getDiagnosticString(),
701               HasSubstr("OpSelectionMerge must immediately precede either an "
702                         "OpBranchConditional or OpSwitch instruction"));
703 }
704 
705 }  // namespace
706 }  // namespace val
707 }  // namespace spvtools
708