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 %line = 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 %line = 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,OpVariableInFunctionGood)318 TEST_F(ValidateAdjacency, OpVariableInFunctionGood) {
319   const std::string body = R"(
320 OpLine %string 1 1
321 %var = OpVariable %func_int Function
322 OpLine %string 2 1
323 OpNop
324 OpLine %string 3 1
325 OpNop
326 )";
327 
328   CompileSuccessfully(GenerateShaderCode(body));
329   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
330 }
331 
TEST_F(ValidateAdjacency,OpVariableInFunctionMultipleGood)332 TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleGood) {
333   const std::string body = R"(
334 OpLine %string 1 1
335 %1 = OpVariable %func_int Function
336 OpLine %string 2 1
337 %2 = OpVariable %func_int Function
338 %3 = OpVariable %func_int Function
339 OpNop
340 OpLine %string 3 1
341 OpNop
342 )";
343 
344   CompileSuccessfully(GenerateShaderCode(body));
345   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
346 }
347 
TEST_F(ValidateAdjacency,OpVariableInFunctionBad)348 TEST_F(ValidateAdjacency, OpVariableInFunctionBad) {
349   const std::string body = R"(
350 %1 = OpUndef %int
351 %2 = OpVariable %func_int Function
352 )";
353 
354   CompileSuccessfully(GenerateShaderCode(body));
355   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
356   EXPECT_THAT(getDiagnosticString(),
357               HasSubstr("All OpVariable instructions in a function must be the "
358                         "first instructions"));
359 }
360 
TEST_F(ValidateAdjacency,OpVariableInFunctionMultipleBad)361 TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleBad) {
362   const std::string body = R"(
363 OpNop
364 %1 = OpVariable %func_int Function
365 OpLine %string 1 1
366 %2 = OpVariable %func_int Function
367 OpNop
368 OpNop
369 OpLine %string 2 1
370 %3 = OpVariable %func_int Function
371 )";
372 
373   CompileSuccessfully(GenerateShaderCode(body));
374   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
375   EXPECT_THAT(getDiagnosticString(),
376               HasSubstr("All OpVariable instructions in a function must be the "
377                         "first instructions"));
378 }
379 
TEST_F(ValidateAdjacency,OpLoopMergePreceedsOpBranchSuccess)380 TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchSuccess) {
381   const std::string body = R"(
382 OpBranch %loop
383 %loop = OpLabel
384 OpLoopMerge %end %loop None
385 OpBranch %loop
386 %end = OpLabel
387 )";
388 
389   CompileSuccessfully(GenerateShaderCode(body));
390   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
391 }
392 
TEST_F(ValidateAdjacency,OpLoopMergePreceedsOpBranchConditionalSuccess)393 TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchConditionalSuccess) {
394   const std::string body = R"(
395 OpBranch %loop
396 %loop = OpLabel
397 OpLoopMerge %end %loop None
398 OpBranchConditional %true %loop %end
399 %end = OpLabel
400 )";
401 
402   CompileSuccessfully(GenerateShaderCode(body));
403   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
404 }
405 
TEST_F(ValidateAdjacency,OpLoopMergePreceedsBadOpFail)406 TEST_F(ValidateAdjacency, OpLoopMergePreceedsBadOpFail) {
407   const std::string body = R"(
408 OpBranch %loop
409 %loop = OpLabel
410 OpLoopMerge %end %loop None
411 OpNop
412 OpBranchConditional %true %loop %end
413 %end = OpLabel
414 )";
415 
416   CompileSuccessfully(GenerateShaderCode(body));
417   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
418   EXPECT_THAT(getDiagnosticString(),
419               HasSubstr("OpLoopMerge must immediately precede either an "
420                         "OpBranch or OpBranchConditional instruction."));
421 }
422 
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsOpBranchConditionalSuccess)423 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpBranchConditionalSuccess) {
424   const std::string body = R"(
425 OpSelectionMerge %end_label None
426 OpBranchConditional %true %true_label %false_label
427 %true_label = OpLabel
428 OpBranch %end_label
429 %false_label = OpLabel
430 OpBranch %end_label
431 %end_label = OpLabel
432 )";
433 
434   CompileSuccessfully(GenerateShaderCode(body));
435   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
436 }
437 
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsOpSwitchSuccess)438 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpSwitchSuccess) {
439   const std::string body = R"(
440 OpSelectionMerge %merge None
441 OpSwitch %zero %merge 0 %label
442 %label = OpLabel
443 OpBranch %merge
444 %merge = OpLabel
445 )";
446 
447   CompileSuccessfully(GenerateShaderCode(body));
448   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
449 }
450 
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsBadOpFail)451 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsBadOpFail) {
452   const std::string body = R"(
453 OpSelectionMerge %merge None
454 OpNop
455 OpSwitch %zero %merge 0 %label
456 %label = OpLabel
457 OpBranch %merge
458 %merge = OpLabel
459 )";
460 
461   CompileSuccessfully(GenerateShaderCode(body));
462   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
463   EXPECT_THAT(getDiagnosticString(),
464               HasSubstr("OpSelectionMerge must immediately precede either an "
465                         "OpBranchConditional or OpSwitch instruction"));
466 }
467 
468 }  // namespace
469 }  // namespace val
470 }  // namespace spvtools
471