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