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::__anon4d67167e0111::Range51 explicit Range(bool inverse = false) : inverse_(inverse) {}
operator ()spvtools::val::__anon4d67167e0111::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_CASE_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_CASE_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