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