1 // Copyright (c) 2015-2016 The Khronos Group 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 // Validation tests for Logical Layout
16
17 #include <algorithm>
18 #include <functional>
19 #include <sstream>
20 #include <string>
21 #include <tuple>
22 #include <utility>
23 #include <vector>
24
25 #include "gmock/gmock.h"
26 #include "source/diagnostic.h"
27 #include "test/unit_spirv.h"
28 #include "test/val/val_fixtures.h"
29
30 namespace spvtools {
31 namespace val {
32 namespace {
33
34 using ::testing::Eq;
35 using ::testing::HasSubstr;
36 using ::testing::StrEq;
37
38 using pred_type = std::function<spv_result_t(int)>;
39 using ValidateLayout = spvtest::ValidateBase<
40 std::tuple<int, std::tuple<std::string, pred_type, pred_type>>>;
41
42 // returns true if order is equal to VAL
43 template <int VAL, spv_result_t RET = SPV_ERROR_INVALID_LAYOUT>
Equals(int order)44 spv_result_t Equals(int order) {
45 return order == VAL ? SPV_SUCCESS : RET;
46 }
47
48 // returns true if order is between MIN and MAX(inclusive)
49 template <int MIN, int MAX, spv_result_t RET = SPV_ERROR_INVALID_LAYOUT>
50 struct Range {
Rangespvtools::val::__anon18443c3a0111::Range51 explicit Range(bool inverse = false) : inverse_(inverse) {}
operator ()spvtools::val::__anon18443c3a0111::Range52 spv_result_t operator()(int order) {
53 return (inverse_ ^ (order >= MIN && order <= MAX)) ? SPV_SUCCESS : RET;
54 }
55
56 private:
57 bool inverse_;
58 };
59
60 template <typename... T>
InvalidSet(int order)61 spv_result_t InvalidSet(int order) {
62 for (spv_result_t val : {T(true)(order)...})
63 if (val != SPV_SUCCESS) return val;
64 return SPV_SUCCESS;
65 }
66
67 // SPIRV source used to test the logical layout
getInstructions()68 const std::vector<std::string>& getInstructions() {
69 // clang-format off
70 static const std::vector<std::string> instructions = {
71 "OpCapability Shader",
72 "OpExtension \"TestExtension\"",
73 "%inst = OpExtInstImport \"GLSL.std.450\"",
74 "OpMemoryModel Logical GLSL450",
75 "OpEntryPoint GLCompute %func \"\"",
76 "OpExecutionMode %func LocalSize 1 1 1",
77 "OpExecutionModeId %func LocalSizeId %one %one %one",
78 "%str = OpString \"Test String\"",
79 "%str2 = OpString \"blabla\"",
80 "OpSource GLSL 450 %str \"uniform vec3 var = vec3(4.0);\"",
81 "OpSourceContinued \"void main(){return;}\"",
82 "OpSourceExtension \"Test extension\"",
83 "OpName %func \"MyFunction\"",
84 "OpMemberName %struct 1 \"my_member\"",
85 "OpDecorate %dgrp RowMajor",
86 "OpMemberDecorate %struct 1 RowMajor",
87 "%dgrp = OpDecorationGroup",
88 "OpGroupDecorate %dgrp %mat33 %mat44",
89 "%intt = OpTypeInt 32 1",
90 "%floatt = OpTypeFloat 32",
91 "%voidt = OpTypeVoid",
92 "%boolt = OpTypeBool",
93 "%vec4 = OpTypeVector %floatt 4",
94 "%vec3 = OpTypeVector %floatt 3",
95 "%mat33 = OpTypeMatrix %vec3 3",
96 "%mat44 = OpTypeMatrix %vec4 4",
97 "%struct = OpTypeStruct %intt %mat33",
98 "%vfunct = OpTypeFunction %voidt",
99 "%viifunct = OpTypeFunction %voidt %intt %intt",
100 "%one = OpConstant %intt 1",
101 // TODO(umar): OpConstant fails because the type is not defined
102 // TODO(umar): OpGroupMemberDecorate
103 "OpLine %str 3 4",
104 "OpNoLine",
105 "%func = OpFunction %voidt None %vfunct",
106 "%l = OpLabel",
107 "OpReturn ; %func return",
108 "OpFunctionEnd ; %func end",
109 "%func2 = OpFunction %voidt None %viifunct",
110 "%funcp1 = OpFunctionParameter %intt",
111 "%funcp2 = OpFunctionParameter %intt",
112 "%fLabel = OpLabel",
113 "OpNop",
114 "OpReturn ; %func2 return",
115 "OpFunctionEnd"
116 };
117 return instructions;
118 }
119
120 static const int kRangeEnd = 1000;
121 pred_type All = Range<0, kRangeEnd>();
122
123 INSTANTIATE_TEST_SUITE_P(InstructionsOrder,
124 ValidateLayout,
125 ::testing::Combine(::testing::Range((int)0, (int)getInstructions().size()),
126 // Note: Because of ID dependencies between instructions, some instructions
127 // are not free to be placed anywhere without triggering an non-layout
128 // validation error. Therefore, "Lines to compile" for some instructions
129 // are not "All" in the below.
130 //
131 // | Instruction | Line(s) valid | Lines to compile
132 ::testing::Values(std::make_tuple(std::string("OpCapability") , Equals<0> , Range<0, 2>())
133 , std::make_tuple(std::string("OpExtension") , Equals<1> , All)
134 , std::make_tuple(std::string("OpExtInstImport") , Equals<2> , All)
135 , std::make_tuple(std::string("OpMemoryModel") , Equals<3> , Range<1, kRangeEnd>())
136 , std::make_tuple(std::string("OpEntryPoint") , Equals<4> , All)
137 , std::make_tuple(std::string("OpExecutionMode ") , Range<5, 6>() , All)
138 , std::make_tuple(std::string("OpExecutionModeId") , Range<5, 6>() , All)
139 , std::make_tuple(std::string("OpSource ") , Range<7, 11>() , Range<8, kRangeEnd>())
140 , std::make_tuple(std::string("OpSourceContinued ") , Range<7, 11>() , All)
141 , std::make_tuple(std::string("OpSourceExtension ") , Range<7, 11>() , All)
142 , std::make_tuple(std::string("%str2 = OpString ") , Range<7, 11>() , All)
143 , std::make_tuple(std::string("OpName ") , Range<12, 13>() , All)
144 , std::make_tuple(std::string("OpMemberName ") , Range<12, 13>() , All)
145 , std::make_tuple(std::string("OpDecorate ") , Range<14, 17>() , All)
146 , std::make_tuple(std::string("OpMemberDecorate ") , Range<14, 17>() , All)
147 , std::make_tuple(std::string("OpGroupDecorate ") , Range<14, 17>() , Range<17, kRangeEnd>())
148 , std::make_tuple(std::string("OpDecorationGroup") , Range<14, 17>() , Range<0, 16>())
149 , std::make_tuple(std::string("OpTypeBool") , Range<18, 31>() , All)
150 , std::make_tuple(std::string("OpTypeVoid") , Range<18, 31>() , Range<0, 26>())
151 , std::make_tuple(std::string("OpTypeFloat") , Range<18, 31>() , Range<0,21>())
152 , std::make_tuple(std::string("OpTypeInt") , Range<18, 31>() , Range<0, 21>())
153 , std::make_tuple(std::string("OpTypeVector %floatt 4") , Range<18, 31>() , Range<20, 24>())
154 , std::make_tuple(std::string("OpTypeMatrix %vec4 4") , Range<18, 31>() , Range<23, kRangeEnd>())
155 , std::make_tuple(std::string("OpTypeStruct") , Range<18, 31>() , Range<25, kRangeEnd>())
156 , std::make_tuple(std::string("%vfunct = OpTypeFunction"), Range<18, 31>() , Range<21, 31>())
157 , std::make_tuple(std::string("OpConstant") , Range<18, 31>() , Range<21, kRangeEnd>())
158 , std::make_tuple(std::string("OpLine ") , Range<18, kRangeEnd>() , Range<8, kRangeEnd>())
159 , std::make_tuple(std::string("OpNoLine") , Range<18, kRangeEnd>() , All)
160 , std::make_tuple(std::string("%fLabel = OpLabel") , Equals<39> , All)
161 , std::make_tuple(std::string("OpNop") , Equals<40> , Range<40,kRangeEnd>())
162 , std::make_tuple(std::string("OpReturn ; %func2 return") , Equals<41> , All)
163 )));
164 // clang-format on
165
166 // Creates a new vector which removes the string if the substr is found in the
167 // instructions vector and reinserts it in the location specified by order.
168 // NOTE: This will not work correctly if there are two instances of substr in
169 // instructions
GenerateCode(std::string substr,int order)170 std::vector<std::string> GenerateCode(std::string substr, int order) {
171 std::vector<std::string> code(getInstructions().size());
172 std::vector<std::string> inst(1);
173 partition_copy(std::begin(getInstructions()), std::end(getInstructions()),
174 std::begin(code), std::begin(inst),
175 [=](const std::string& str) {
176 return std::string::npos == str.find(substr);
177 });
178
179 code.insert(std::begin(code) + order, inst.front());
180 return code;
181 }
182
183 // This test will check the logical layout of a binary by removing each
184 // instruction in the pair of the INSTANTIATE_TEST_SUITE_P call and moving it in
185 // the SPIRV source formed by combining the vector "instructions".
TEST_P(ValidateLayout,Layout)186 TEST_P(ValidateLayout, Layout) {
187 int order;
188 std::string instruction;
189 pred_type pred;
190 pred_type test_pred; // Predicate to determine if the test should be build
191 std::tuple<std::string, pred_type, pred_type> testCase;
192
193 std::tie(order, testCase) = GetParam();
194 std::tie(instruction, pred, test_pred) = testCase;
195
196 // Skip test which break the code generation
197 if (test_pred(order)) return;
198
199 std::vector<std::string> code = GenerateCode(instruction, order);
200
201 std::stringstream ss;
202 std::copy(std::begin(code), std::end(code),
203 std::ostream_iterator<std::string>(ss, "\n"));
204
205 const auto env = SPV_ENV_UNIVERSAL_1_3;
206 // printf("code: \n%s\n", ss.str().c_str());
207 CompileSuccessfully(ss.str(), env);
208 spv_result_t result;
209 // clang-format off
210 ASSERT_EQ(pred(order), result = ValidateInstructions(env))
211 << "Actual: " << spvResultToString(result)
212 << "\nExpected: " << spvResultToString(pred(order))
213 << "\nOrder: " << order
214 << "\nInstruction: " << instruction
215 << "\nCode: \n" << ss.str();
216 // clang-format on
217 }
218
TEST_F(ValidateLayout,MemoryModelMissingBeforeEntryPoint)219 TEST_F(ValidateLayout, MemoryModelMissingBeforeEntryPoint) {
220 std::string str = R"(
221 OpCapability Matrix
222 OpExtension "TestExtension"
223 %inst = OpExtInstImport "GLSL.std.450"
224 OpEntryPoint GLCompute %func ""
225 OpExecutionMode %func LocalSize 1 1 1
226 )";
227
228 CompileSuccessfully(str);
229 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
230 EXPECT_THAT(
231 getDiagnosticString(),
232 HasSubstr(
233 "EntryPoint cannot appear before the memory model instruction"));
234 }
235
TEST_F(ValidateLayout,MemoryModelMissing)236 TEST_F(ValidateLayout, MemoryModelMissing) {
237 char str[] = R"(OpCapability Linkage)";
238 CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
239 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
240 ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
241 EXPECT_THAT(getDiagnosticString(),
242 HasSubstr("Missing required OpMemoryModel instruction"));
243 }
244
TEST_F(ValidateLayout,MemoryModelSpecifiedTwice)245 TEST_F(ValidateLayout, MemoryModelSpecifiedTwice) {
246 char str[] = R"(
247 OpCapability Linkage
248 OpCapability Shader
249 OpMemoryModel Logical Simple
250 OpMemoryModel Logical Simple
251 )";
252
253 CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
254 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
255 ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
256 EXPECT_THAT(getDiagnosticString(),
257 HasSubstr("OpMemoryModel should only be provided once"));
258 }
259
TEST_F(ValidateLayout,FunctionDefinitionBeforeDeclarationBad)260 TEST_F(ValidateLayout, FunctionDefinitionBeforeDeclarationBad) {
261 char str[] = R"(
262 OpCapability Shader
263 OpMemoryModel Logical GLSL450
264 OpDecorate %var Restrict
265 %intt = OpTypeInt 32 1
266 %voidt = OpTypeVoid
267 %vfunct = OpTypeFunction %voidt
268 %vifunct = OpTypeFunction %voidt %intt
269 %ptrt = OpTypePointer Function %intt
270 %func = OpFunction %voidt None %vfunct
271 %funcl = OpLabel
272 OpNop
273 OpReturn
274 OpFunctionEnd
275 %func2 = OpFunction %voidt None %vifunct ; must appear before definition
276 %func2p = OpFunctionParameter %intt
277 OpFunctionEnd
278 )";
279
280 CompileSuccessfully(str);
281 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
282 EXPECT_THAT(
283 getDiagnosticString(),
284 HasSubstr(
285 "Function declarations must appear before function definitions."));
286 }
287
288 // TODO(umar): Passes but gives incorrect error message. Should be fixed after
289 // type checking
TEST_F(ValidateLayout,LabelBeforeFunctionParameterBad)290 TEST_F(ValidateLayout, LabelBeforeFunctionParameterBad) {
291 char str[] = R"(
292 OpCapability Shader
293 OpMemoryModel Logical GLSL450
294 OpDecorate %var Restrict
295 %intt = OpTypeInt 32 1
296 %voidt = OpTypeVoid
297 %vfunct = OpTypeFunction %voidt
298 %vifunct = OpTypeFunction %voidt %intt
299 %ptrt = OpTypePointer Function %intt
300 %func = OpFunction %voidt None %vifunct
301 %funcl = OpLabel ; Label appears before function parameter
302 %func2p = OpFunctionParameter %intt
303 OpNop
304 OpReturn
305 OpFunctionEnd
306 )";
307
308 CompileSuccessfully(str);
309 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
310 EXPECT_THAT(getDiagnosticString(),
311 HasSubstr("Function parameters must only appear immediately "
312 "after the function definition"));
313 }
314
TEST_F(ValidateLayout,FuncParameterNotImmediatlyAfterFuncBad)315 TEST_F(ValidateLayout, FuncParameterNotImmediatlyAfterFuncBad) {
316 char str[] = R"(
317 OpCapability Shader
318 OpMemoryModel Logical GLSL450
319 OpDecorate %var Restrict
320 %intt = OpTypeInt 32 1
321 %voidt = OpTypeVoid
322 %vfunct = OpTypeFunction %voidt
323 %vifunct = OpTypeFunction %voidt %intt
324 %ptrt = OpTypePointer Function %intt
325 %func = OpFunction %voidt None %vifunct
326 %funcl = OpLabel
327 OpNop
328 OpBranch %next
329 %func2p = OpFunctionParameter %intt ;FunctionParameter appears in a function but not immediately afterwards
330 %next = OpLabel
331 OpNop
332 OpReturn
333 OpFunctionEnd
334 )";
335
336 CompileSuccessfully(str);
337 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
338 EXPECT_THAT(getDiagnosticString(),
339 HasSubstr("Function parameters must only appear immediately "
340 "after the function definition"));
341 }
342
TEST_F(ValidateLayout,OpUndefCanAppearInTypeDeclarationSection)343 TEST_F(ValidateLayout, OpUndefCanAppearInTypeDeclarationSection) {
344 std::string str = R"(
345 OpCapability Kernel
346 OpCapability Linkage
347 OpMemoryModel Logical OpenCL
348 %voidt = OpTypeVoid
349 %uintt = OpTypeInt 32 0
350 %funct = OpTypeFunction %voidt
351 %udef = OpUndef %uintt
352 %func = OpFunction %voidt None %funct
353 %entry = OpLabel
354 OpReturn
355 OpFunctionEnd
356 )";
357
358 CompileSuccessfully(str);
359 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
360 }
361
TEST_F(ValidateLayout,OpUndefCanAppearInBlock)362 TEST_F(ValidateLayout, OpUndefCanAppearInBlock) {
363 std::string str = R"(
364 OpCapability Kernel
365 OpCapability Linkage
366 OpMemoryModel Logical OpenCL
367 %voidt = OpTypeVoid
368 %uintt = OpTypeInt 32 0
369 %funct = OpTypeFunction %voidt
370 %func = OpFunction %voidt None %funct
371 %entry = OpLabel
372 %udef = OpUndef %uintt
373 OpReturn
374 OpFunctionEnd
375 )";
376
377 CompileSuccessfully(str);
378 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
379 }
380
TEST_F(ValidateLayout,MissingFunctionEndForFunctionWithBody)381 TEST_F(ValidateLayout, MissingFunctionEndForFunctionWithBody) {
382 const auto s = R"(
383 OpCapability Shader
384 OpCapability Linkage
385 OpMemoryModel Logical GLSL450
386 %void = OpTypeVoid
387 %tf = OpTypeFunction %void
388 %f = OpFunction %void None %tf
389 %l = OpLabel
390 OpReturn
391 )";
392
393 CompileSuccessfully(s);
394 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
395 EXPECT_THAT(getDiagnosticString(),
396 StrEq("Missing OpFunctionEnd at end of module."));
397 }
398
TEST_F(ValidateLayout,MissingFunctionEndForFunctionPrototype)399 TEST_F(ValidateLayout, MissingFunctionEndForFunctionPrototype) {
400 const auto s = R"(
401 OpCapability Shader
402 OpCapability Linkage
403 OpMemoryModel Logical GLSL450
404 %void = OpTypeVoid
405 %tf = OpTypeFunction %void
406 %f = OpFunction %void None %tf
407 )";
408
409 CompileSuccessfully(s);
410 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
411 EXPECT_THAT(getDiagnosticString(),
412 StrEq("Missing OpFunctionEnd at end of module."));
413 }
414
415 using ValidateOpFunctionParameter = spvtest::ValidateBase<int>;
416
TEST_F(ValidateOpFunctionParameter,OpLineBetweenParameters)417 TEST_F(ValidateOpFunctionParameter, OpLineBetweenParameters) {
418 const auto s = R"(
419 OpCapability Shader
420 OpCapability Linkage
421 OpMemoryModel Logical GLSL450
422 %foo_frag = OpString "foo.frag"
423 %i32 = OpTypeInt 32 1
424 %tf = OpTypeFunction %i32 %i32 %i32
425 %c = OpConstant %i32 123
426 %f = OpFunction %i32 None %tf
427 OpLine %foo_frag 1 1
428 %p1 = OpFunctionParameter %i32
429 OpNoLine
430 %p2 = OpFunctionParameter %i32
431 %l = OpLabel
432 OpReturnValue %c
433 OpFunctionEnd
434 )";
435 CompileSuccessfully(s);
436 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
437 }
438
TEST_F(ValidateOpFunctionParameter,TooManyParameters)439 TEST_F(ValidateOpFunctionParameter, TooManyParameters) {
440 const auto s = R"(
441 OpCapability Shader
442 OpCapability Linkage
443 OpMemoryModel Logical GLSL450
444 %i32 = OpTypeInt 32 1
445 %tf = OpTypeFunction %i32 %i32 %i32
446 %c = OpConstant %i32 123
447 %f = OpFunction %i32 None %tf
448 %p1 = OpFunctionParameter %i32
449 %p2 = OpFunctionParameter %i32
450 %xp3 = OpFunctionParameter %i32
451 %xp4 = OpFunctionParameter %i32
452 %xp5 = OpFunctionParameter %i32
453 %xp6 = OpFunctionParameter %i32
454 %xp7 = OpFunctionParameter %i32
455 %l = OpLabel
456 OpReturnValue %c
457 OpFunctionEnd
458 )";
459 CompileSuccessfully(s);
460 ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
461 }
462
463 using ValidateEntryPoint = spvtest::ValidateBase<bool>;
464
465 // Tests that not having OpEntryPoint causes an error.
TEST_F(ValidateEntryPoint,NoEntryPointBad)466 TEST_F(ValidateEntryPoint, NoEntryPointBad) {
467 std::string spirv = R"(
468 OpCapability Shader
469 OpMemoryModel Logical GLSL450)";
470 CompileSuccessfully(spirv);
471 EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
472 EXPECT_THAT(getDiagnosticString(),
473 HasSubstr("No OpEntryPoint instruction was found. This is only "
474 "allowed if the Linkage capability is being used."));
475 }
476
477 // Invalid. A function may not be a target of both OpEntryPoint and
478 // OpFunctionCall.
TEST_F(ValidateEntryPoint,FunctionIsTargetOfEntryPointAndFunctionCallBad)479 TEST_F(ValidateEntryPoint, FunctionIsTargetOfEntryPointAndFunctionCallBad) {
480 std::string spirv = R"(
481 OpCapability Shader
482 OpMemoryModel Logical GLSL450
483 OpEntryPoint Fragment %foo "foo"
484 OpExecutionMode %foo OriginUpperLeft
485 %voidt = OpTypeVoid
486 %funct = OpTypeFunction %voidt
487 %foo = OpFunction %voidt None %funct
488 %entry = OpLabel
489 %recurse = OpFunctionCall %voidt %foo
490 OpReturn
491 OpFunctionEnd
492 )";
493 CompileSuccessfully(spirv);
494 EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
495 EXPECT_THAT(
496 getDiagnosticString(),
497 HasSubstr("A function (1) may not be targeted by both an OpEntryPoint "
498 "instruction and an OpFunctionCall instruction."));
499 }
500
501 // Invalid. Must be within a function to make a function call.
TEST_F(ValidateEntryPoint,FunctionCallOutsideFunctionBody)502 TEST_F(ValidateEntryPoint, FunctionCallOutsideFunctionBody) {
503 std::string spirv = R"(
504 OpCapability Shader
505 %1 = OpExtInstImport "GLSL.std.450"
506 OpMemoryModel Logical GLSL450
507 OpName %variableName "variableName"
508 %34 = OpFunctionCall %variableName %1
509 )";
510 CompileSuccessfully(spirv);
511 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
512 EXPECT_THAT(getDiagnosticString(),
513 HasSubstr("FunctionCall must happen within a function body."));
514 }
515
516 // Valid. Module with a function but no entry point is valid when Linkage
517 // Capability is used.
TEST_F(ValidateEntryPoint,NoEntryPointWithLinkageCapGood)518 TEST_F(ValidateEntryPoint, NoEntryPointWithLinkageCapGood) {
519 std::string spirv = R"(
520 OpCapability Shader
521 OpCapability Linkage
522 OpMemoryModel Logical GLSL450
523 %voidt = OpTypeVoid
524 %funct = OpTypeFunction %voidt
525 %foo = OpFunction %voidt None %funct
526 %entry = OpLabel
527 OpReturn
528 OpFunctionEnd
529 )";
530 CompileSuccessfully(spirv);
531 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
532 }
533
TEST_F(ValidateLayout,ModuleProcessedInvalidIn10)534 TEST_F(ValidateLayout, ModuleProcessedInvalidIn10) {
535 char str[] = R"(
536 OpCapability Shader
537 OpCapability Linkage
538 OpMemoryModel Logical GLSL450
539 OpName %void "void"
540 OpModuleProcessed "this is ok in 1.1 and later"
541 %void = OpTypeVoid
542 )";
543
544 CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
545 ASSERT_EQ(SPV_ERROR_WRONG_VERSION,
546 ValidateInstructions(SPV_ENV_UNIVERSAL_1_0));
547 // In a 1.0 environment the version check fails.
548 EXPECT_THAT(getDiagnosticString(),
549 HasSubstr("Invalid SPIR-V binary version 1.1 for target "
550 "environment SPIR-V 1.0."));
551 }
552
TEST_F(ValidateLayout,ModuleProcessedValidIn11)553 TEST_F(ValidateLayout, ModuleProcessedValidIn11) {
554 char str[] = R"(
555 OpCapability Shader
556 OpCapability Linkage
557 OpMemoryModel Logical GLSL450
558 OpName %void "void"
559 OpModuleProcessed "this is ok in 1.1 and later"
560 %void = OpTypeVoid
561 )";
562
563 CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
564 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
565 EXPECT_THAT(getDiagnosticString(), Eq(""));
566 }
567
TEST_F(ValidateLayout,LayoutOrderMixedUp)568 TEST_F(ValidateLayout, LayoutOrderMixedUp) {
569 char str[] = R"(
570 OpCapability Shader
571 OpCapability Linkage
572 OpMemoryModel Logical GLSL450
573 OpEntryPoint Fragment %fragmentFloat "fragmentFloat"
574 OpExecutionMode %fragmentFloat OriginUpperLeft
575 OpEntryPoint Fragment %fragmentUint "fragmentUint"
576 OpExecutionMode %fragmentUint OriginUpperLeft
577 )";
578
579 CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
580 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
581 ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
582 // By the mechanics of the validator, we assume ModuleProcessed is in the
583 // right spot, but then that OpName is in the wrong spot.
584 EXPECT_THAT(getDiagnosticString(),
585 HasSubstr("EntryPoint is in an invalid layout section"));
586 }
587
TEST_F(ValidateLayout,ModuleProcessedBeforeLastNameIsTooEarly)588 TEST_F(ValidateLayout, ModuleProcessedBeforeLastNameIsTooEarly) {
589 char str[] = R"(
590 OpCapability Shader
591 OpCapability Linkage
592 OpMemoryModel Logical GLSL450
593 OpModuleProcessed "this is too early"
594 OpName %void "void"
595 %void = OpTypeVoid
596 )";
597
598 CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
599 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
600 ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
601 // By the mechanics of the validator, we assume ModuleProcessed is in the
602 // right spot, but then that OpName is in the wrong spot.
603 EXPECT_THAT(getDiagnosticString(),
604 HasSubstr("Name is in an invalid layout section"));
605 }
606
TEST_F(ValidateLayout,ModuleProcessedInvalidAfterFirstAnnotation)607 TEST_F(ValidateLayout, ModuleProcessedInvalidAfterFirstAnnotation) {
608 char str[] = R"(
609 OpCapability Shader
610 OpCapability Linkage
611 OpMemoryModel Logical GLSL450
612 OpDecorate %void Volatile ; this is bogus, but keeps the example short
613 OpModuleProcessed "this is too late"
614 %void = OpTypeVoid
615 )";
616
617 CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
618 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
619 ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
620 EXPECT_THAT(getDiagnosticString(),
621 HasSubstr("ModuleProcessed is in an invalid layout section"));
622 }
623
TEST_F(ValidateLayout,ModuleProcessedInvalidInFunctionBeforeLabel)624 TEST_F(ValidateLayout, ModuleProcessedInvalidInFunctionBeforeLabel) {
625 char str[] = R"(
626 OpCapability Shader
627 OpMemoryModel Logical GLSL450
628 OpEntryPoint GLCompute %main "main"
629 %void = OpTypeVoid
630 %voidfn = OpTypeFunction %void
631 %main = OpFunction %void None %voidfn
632 OpModuleProcessed "this is too late, in function before label"
633 %entry = OpLabel
634 OpReturn
635 OpFunctionEnd
636 )";
637
638 CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
639 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
640 ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
641 EXPECT_THAT(
642 getDiagnosticString(),
643 HasSubstr("ModuleProcessed cannot appear in a function declaration"));
644 }
645
TEST_F(ValidateLayout,ModuleProcessedInvalidInBasicBlock)646 TEST_F(ValidateLayout, ModuleProcessedInvalidInBasicBlock) {
647 char str[] = R"(
648 OpCapability Shader
649 OpMemoryModel Logical GLSL450
650 OpEntryPoint GLCompute %main "main"
651 %void = OpTypeVoid
652 %voidfn = OpTypeFunction %void
653 %main = OpFunction %void None %voidfn
654 %entry = OpLabel
655 OpModuleProcessed "this is too late, in basic block"
656 OpReturn
657 OpFunctionEnd
658 )";
659
660 CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
661 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
662 ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
663 EXPECT_THAT(
664 getDiagnosticString(),
665 HasSubstr("ModuleProcessed cannot appear in a function declaration"));
666 }
667
668 // TODO(umar): Test optional instructions
669
TEST_F(ValidateLayout,ValidNVBindlessTexturelayout)670 TEST_F(ValidateLayout, ValidNVBindlessTexturelayout) {
671 std::string str = R"(
672 OpCapability Shader
673 OpCapability BindlessTextureNV
674 OpExtension "SPV_NV_bindless_texture"
675 OpMemoryModel Logical GLSL450
676 OpSamplerImageAddressingModeNV 64
677 OpEntryPoint GLCompute %func "main"
678 %voidt = OpTypeVoid
679 %uintt = OpTypeInt 32 0
680 %funct = OpTypeFunction %voidt
681 %func = OpFunction %voidt None %funct
682 %entry = OpLabel
683 %udef = OpUndef %uintt
684 OpReturn
685 OpFunctionEnd
686 )";
687
688 CompileSuccessfully(str);
689 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
690 }
691
TEST_F(ValidateLayout,InvalidValidNVBindlessTexturelayout)692 TEST_F(ValidateLayout, InvalidValidNVBindlessTexturelayout) {
693 std::string str = R"(
694 OpCapability Shader
695 OpCapability BindlessTextureNV
696 OpExtension "SPV_NV_bindless_texture"
697 OpMemoryModel Logical GLSL450
698 OpEntryPoint GLCompute %func "main"
699 OpSamplerImageAddressingModeNV 64
700 %voidt = OpTypeVoid
701 %uintt = OpTypeInt 32 0
702 %funct = OpTypeFunction %voidt
703 %func = OpFunction %voidt None %funct
704 %entry = OpLabel
705 %udef = OpUndef %uintt
706 OpReturn
707 OpFunctionEnd
708 )";
709
710 CompileSuccessfully(str);
711 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
712 ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
713 EXPECT_THAT(
714 getDiagnosticString(),
715 HasSubstr(
716 "SamplerImageAddressingModeNV is in an invalid layout section"));
717 }
718
TEST_F(ValidateLayout,MissingNVBindlessAddressModeFromLayout)719 TEST_F(ValidateLayout, MissingNVBindlessAddressModeFromLayout) {
720 std::string str = R"(
721 OpCapability Shader
722 OpCapability BindlessTextureNV
723 OpExtension "SPV_NV_bindless_texture"
724 OpMemoryModel Logical GLSL450
725 OpEntryPoint GLCompute %func "main"
726 %voidt = OpTypeVoid
727 %uintt = OpTypeInt 32 0
728 %funct = OpTypeFunction %voidt
729 %func = OpFunction %voidt None %funct
730 %entry = OpLabel
731 %udef = OpUndef %uintt
732 OpReturn
733 OpFunctionEnd
734 )";
735
736 CompileSuccessfully(str);
737 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
738 ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
739 EXPECT_THAT(
740 getDiagnosticString(),
741 HasSubstr("Missing required OpSamplerImageAddressingModeNV instruction"));
742 }
743
TEST_F(ValidateLayout,NVBindlessAddressModeFromLayoutSpecifiedTwice)744 TEST_F(ValidateLayout, NVBindlessAddressModeFromLayoutSpecifiedTwice) {
745 std::string str = R"(
746 OpCapability Shader
747 OpCapability BindlessTextureNV
748 OpExtension "SPV_NV_bindless_texture"
749 OpMemoryModel Logical GLSL450
750 OpSamplerImageAddressingModeNV 64
751 OpSamplerImageAddressingModeNV 64
752 )";
753
754 CompileSuccessfully(str);
755 ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
756 ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
757 EXPECT_THAT(
758 getDiagnosticString(),
759 HasSubstr("OpSamplerImageAddressingModeNV should only be provided once"));
760 }
761
762 } // namespace
763 } // namespace val
764 } // namespace spvtools
765