• 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::__anon722545740111::Range51   explicit Range(bool inverse = false) : inverse_(inverse) {}
operator ()spvtools::val::__anon722545740111::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            OpDecorate %void Volatile ; bogus, but makes the example short
542 %void    = OpTypeVoid
543 )";
544 
545   CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
546   ASSERT_EQ(SPV_ERROR_WRONG_VERSION,
547             ValidateInstructions(SPV_ENV_UNIVERSAL_1_0));
548   // In a 1.0 environment the version check fails.
549   EXPECT_THAT(getDiagnosticString(),
550               HasSubstr("Invalid SPIR-V binary version 1.1 for target "
551                         "environment SPIR-V 1.0."));
552 }
553 
TEST_F(ValidateLayout,ModuleProcessedValidIn11)554 TEST_F(ValidateLayout, ModuleProcessedValidIn11) {
555   char str[] = R"(
556            OpCapability Shader
557            OpCapability Linkage
558            OpMemoryModel Logical GLSL450
559            OpName %void "void"
560            OpModuleProcessed "this is ok in 1.1 and later"
561            OpDecorate %void Volatile ; bogus, but makes the example short
562 %void    = OpTypeVoid
563 )";
564 
565   CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
566   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
567   EXPECT_THAT(getDiagnosticString(), Eq(""));
568 }
569 
TEST_F(ValidateLayout,ModuleProcessedBeforeLastNameIsTooEarly)570 TEST_F(ValidateLayout, ModuleProcessedBeforeLastNameIsTooEarly) {
571   char str[] = R"(
572            OpCapability Shader
573            OpCapability Linkage
574            OpMemoryModel Logical GLSL450
575            OpModuleProcessed "this is too early"
576            OpName %void "void"
577 %void    = OpTypeVoid
578 )";
579 
580   CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
581   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
582             ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
583   // By the mechanics of the validator, we assume ModuleProcessed is in the
584   // right spot, but then that OpName is in the wrong spot.
585   EXPECT_THAT(getDiagnosticString(),
586               HasSubstr("Name cannot appear in a function declaration"));
587 }
588 
TEST_F(ValidateLayout,ModuleProcessedInvalidAfterFirstAnnotation)589 TEST_F(ValidateLayout, ModuleProcessedInvalidAfterFirstAnnotation) {
590   char str[] = R"(
591            OpCapability Shader
592            OpCapability Linkage
593            OpMemoryModel Logical GLSL450
594            OpDecorate %void Volatile ; this is bogus, but keeps the example short
595            OpModuleProcessed "this is too late"
596 %void    = OpTypeVoid
597 )";
598 
599   CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
600   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
601             ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
602   EXPECT_THAT(
603       getDiagnosticString(),
604       HasSubstr("ModuleProcessed cannot appear in a function declaration"));
605 }
606 
TEST_F(ValidateLayout,ModuleProcessedInvalidInFunctionBeforeLabel)607 TEST_F(ValidateLayout, ModuleProcessedInvalidInFunctionBeforeLabel) {
608   char str[] = R"(
609            OpCapability Shader
610            OpMemoryModel Logical GLSL450
611            OpEntryPoint GLCompute %main "main"
612 %void    = OpTypeVoid
613 %voidfn  = OpTypeFunction %void
614 %main    = OpFunction %void None %voidfn
615            OpModuleProcessed "this is too late, in function before label"
616 %entry  =  OpLabel
617            OpReturn
618            OpFunctionEnd
619 )";
620 
621   CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
622   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
623             ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
624   EXPECT_THAT(
625       getDiagnosticString(),
626       HasSubstr("ModuleProcessed cannot appear in a function declaration"));
627 }
628 
TEST_F(ValidateLayout,ModuleProcessedInvalidInBasicBlock)629 TEST_F(ValidateLayout, ModuleProcessedInvalidInBasicBlock) {
630   char str[] = R"(
631            OpCapability Shader
632            OpMemoryModel Logical GLSL450
633            OpEntryPoint GLCompute %main "main"
634 %void    = OpTypeVoid
635 %voidfn  = OpTypeFunction %void
636 %main    = OpFunction %void None %voidfn
637 %entry   = OpLabel
638            OpModuleProcessed "this is too late, in basic block"
639            OpReturn
640            OpFunctionEnd
641 )";
642 
643   CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1);
644   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
645             ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
646   EXPECT_THAT(
647       getDiagnosticString(),
648       HasSubstr("ModuleProcessed cannot appear in a function declaration"));
649 }
650 
TEST_F(ValidateLayout,WebGPUCallerBeforeCalleeBad)651 TEST_F(ValidateLayout, WebGPUCallerBeforeCalleeBad) {
652   char str[] = R"(
653            OpCapability Shader
654            OpCapability VulkanMemoryModelKHR
655            OpExtension "SPV_KHR_vulkan_memory_model"
656            OpMemoryModel Logical VulkanKHR
657            OpEntryPoint GLCompute %main "main"
658 %void    = OpTypeVoid
659 %voidfn  = OpTypeFunction %void
660 %main    = OpFunction %void None %voidfn
661 %1       = OpLabel
662 %2       = OpFunctionCall %void %callee
663            OpReturn
664            OpFunctionEnd
665 %callee  = OpFunction %void None %voidfn
666 %3       = OpLabel
667            OpReturn
668            OpFunctionEnd
669 )";
670 
671   CompileSuccessfully(str, SPV_ENV_WEBGPU_0);
672   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPV_ENV_WEBGPU_0));
673   EXPECT_THAT(getDiagnosticString(),
674               HasSubstr("For WebGPU, functions need to be defined before being "
675                         "called.\n  %5 = OpFunctionCall %void %6\n"));
676 }
677 
TEST_F(ValidateLayout,WebGPUCalleeBeforeCallerGood)678 TEST_F(ValidateLayout, WebGPUCalleeBeforeCallerGood) {
679   char str[] = R"(
680            OpCapability Shader
681            OpCapability VulkanMemoryModelKHR
682            OpExtension "SPV_KHR_vulkan_memory_model"
683            OpMemoryModel Logical VulkanKHR
684            OpEntryPoint GLCompute %main "main"
685 %void    = OpTypeVoid
686 %voidfn  = OpTypeFunction %void
687 %callee  = OpFunction %void None %voidfn
688 %3       = OpLabel
689            OpReturn
690            OpFunctionEnd
691 %main    = OpFunction %void None %voidfn
692 %1       = OpLabel
693 %2       = OpFunctionCall %void %callee
694            OpReturn
695            OpFunctionEnd
696 )";
697 
698   CompileSuccessfully(str, SPV_ENV_WEBGPU_0);
699   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
700 }
701 
702 // TODO(umar): Test optional instructions
703 
704 }  // namespace
705 }  // namespace val
706 }  // namespace spvtools
707