• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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