• 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 #include <algorithm>
16 #include <limits>
17 #include <sstream>
18 #include <string>
19 #include <vector>
20 
21 #include "gmock/gmock.h"
22 #include "source/latest_version_opencl_std_header.h"
23 #include "source/table.h"
24 #include "source/util/string_utils.h"
25 #include "test/test_fixture.h"
26 #include "test/unit_spirv.h"
27 
28 // Returns true if two spv_parsed_operand_t values are equal.
29 // To use this operator, this definition must appear in the same namespace
30 // as spv_parsed_operand_t.
operator ==(const spv_parsed_operand_t & a,const spv_parsed_operand_t & b)31 static bool operator==(const spv_parsed_operand_t& a,
32                        const spv_parsed_operand_t& b) {
33   return a.offset == b.offset && a.num_words == b.num_words &&
34          a.type == b.type && a.number_kind == b.number_kind &&
35          a.number_bit_width == b.number_bit_width;
36 }
37 
38 namespace spvtools {
39 namespace {
40 
41 using ::spvtest::Concatenate;
42 using ::spvtest::MakeInstruction;
43 using utils::MakeVector;
44 using ::spvtest::ScopedContext;
45 using ::testing::_;
46 using ::testing::AnyOf;
47 using ::testing::Eq;
48 using ::testing::InSequence;
49 using ::testing::Return;
50 
51 // An easily-constructible and comparable object for the contents of an
52 // spv_parsed_instruction_t.  Unlike spv_parsed_instruction_t, owns the memory
53 // of its components.
54 struct ParsedInstruction {
ParsedInstructionspvtools::__anonb4c518230111::ParsedInstruction55   explicit ParsedInstruction(const spv_parsed_instruction_t& inst)
56       : words(inst.words, inst.words + inst.num_words),
57         opcode(static_cast<SpvOp>(inst.opcode)),
58         ext_inst_type(inst.ext_inst_type),
59         type_id(inst.type_id),
60         result_id(inst.result_id),
61         operands(inst.operands, inst.operands + inst.num_operands) {}
62 
63   std::vector<uint32_t> words;
64   SpvOp opcode;
65   spv_ext_inst_type_t ext_inst_type;
66   uint32_t type_id;
67   uint32_t result_id;
68   std::vector<spv_parsed_operand_t> operands;
69 
operator ==spvtools::__anonb4c518230111::ParsedInstruction70   bool operator==(const ParsedInstruction& b) const {
71     return words == b.words && opcode == b.opcode &&
72            ext_inst_type == b.ext_inst_type && type_id == b.type_id &&
73            result_id == b.result_id && operands == b.operands;
74   }
75 };
76 
77 // Prints a ParsedInstruction object to the given output stream, and returns
78 // the stream.
operator <<(std::ostream & os,const ParsedInstruction & inst)79 std::ostream& operator<<(std::ostream& os, const ParsedInstruction& inst) {
80   os << "\nParsedInstruction( {";
81   spvtest::PrintTo(spvtest::WordVector(inst.words), &os);
82   os << "}, opcode: " << int(inst.opcode)
83      << " ext_inst_type: " << int(inst.ext_inst_type)
84      << " type_id: " << inst.type_id << " result_id: " << inst.result_id;
85   for (const auto& operand : inst.operands) {
86     os << " { offset: " << operand.offset << " num_words: " << operand.num_words
87        << " type: " << int(operand.type)
88        << " number_kind: " << int(operand.number_kind)
89        << " number_bit_width: " << int(operand.number_bit_width) << "}";
90   }
91   os << ")";
92   return os;
93 }
94 
95 // Basic check for the equality operator on ParsedInstruction.
TEST(ParsedInstruction,ZeroInitializedAreEqual)96 TEST(ParsedInstruction, ZeroInitializedAreEqual) {
97   spv_parsed_instruction_t pi = {};
98   ParsedInstruction a(pi);
99   ParsedInstruction b(pi);
100   EXPECT_THAT(a, ::testing::TypedEq<ParsedInstruction>(b));
101 }
102 
103 // Googlemock class receiving Header/Instruction calls from spvBinaryParse().
104 class MockParseClient {
105  public:
106   MOCK_METHOD6(Header, spv_result_t(spv_endianness_t endian, uint32_t magic,
107                                     uint32_t version, uint32_t generator,
108                                     uint32_t id_bound, uint32_t reserved));
109   MOCK_METHOD1(Instruction, spv_result_t(const ParsedInstruction&));
110 };
111 
112 // Casts user_data as MockParseClient and invokes its Header().
invoke_header(void * user_data,spv_endianness_t endian,uint32_t magic,uint32_t version,uint32_t generator,uint32_t id_bound,uint32_t reserved)113 spv_result_t invoke_header(void* user_data, spv_endianness_t endian,
114                            uint32_t magic, uint32_t version, uint32_t generator,
115                            uint32_t id_bound, uint32_t reserved) {
116   return static_cast<MockParseClient*>(user_data)->Header(
117       endian, magic, version, generator, id_bound, reserved);
118 }
119 
120 // Casts user_data as MockParseClient and invokes its Instruction().
invoke_instruction(void * user_data,const spv_parsed_instruction_t * parsed_instruction)121 spv_result_t invoke_instruction(
122     void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
123   return static_cast<MockParseClient*>(user_data)->Instruction(
124       ParsedInstruction(*parsed_instruction));
125 }
126 
127 // The SPIR-V module header words for the Khronos Assembler generator,
128 // for a module with an ID bound of 1.
129 const uint32_t kHeaderForBound1[] = {
130     SpvMagicNumber, SpvVersion,
131     SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), 1 /*bound*/,
132     0 /*schema*/};
133 
134 // Returns the expected SPIR-V module header words for the Khronos
135 // Assembler generator, and with a given Id bound.
ExpectedHeaderForBound(uint32_t bound)136 std::vector<uint32_t> ExpectedHeaderForBound(uint32_t bound) {
137   return {SpvMagicNumber, 0x10000,
138           SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), bound, 0};
139 }
140 
141 // Returns a parsed operand for a non-number value at the given word offset
142 // within an instruction.
MakeSimpleOperand(uint16_t offset,spv_operand_type_t type)143 spv_parsed_operand_t MakeSimpleOperand(uint16_t offset,
144                                        spv_operand_type_t type) {
145   return {offset, 1, type, SPV_NUMBER_NONE, 0};
146 }
147 
148 // Returns a parsed operand for a literal unsigned integer value at the given
149 // word offset within an instruction.
MakeLiteralNumberOperand(uint16_t offset)150 spv_parsed_operand_t MakeLiteralNumberOperand(uint16_t offset) {
151   return {offset, 1, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_NUMBER_UNSIGNED_INT,
152           32};
153 }
154 
155 // Returns a parsed operand for a literal string value at the given
156 // word offset within an instruction.
MakeLiteralStringOperand(uint16_t offset,uint16_t length)157 spv_parsed_operand_t MakeLiteralStringOperand(uint16_t offset,
158                                               uint16_t length) {
159   return {offset, length, SPV_OPERAND_TYPE_LITERAL_STRING, SPV_NUMBER_NONE, 0};
160 }
161 
162 // Returns a ParsedInstruction for an OpTypeVoid instruction that would
163 // generate the given result Id.
MakeParsedVoidTypeInstruction(uint32_t result_id)164 ParsedInstruction MakeParsedVoidTypeInstruction(uint32_t result_id) {
165   const auto void_inst = MakeInstruction(SpvOpTypeVoid, {result_id});
166   const auto void_operands = std::vector<spv_parsed_operand_t>{
167       MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID)};
168   const spv_parsed_instruction_t parsed_void_inst = {
169       void_inst.data(),
170       static_cast<uint16_t>(void_inst.size()),
171       SpvOpTypeVoid,
172       SPV_EXT_INST_TYPE_NONE,
173       0,  // type id
174       result_id,
175       void_operands.data(),
176       static_cast<uint16_t>(void_operands.size())};
177   return ParsedInstruction(parsed_void_inst);
178 }
179 
180 // Returns a ParsedInstruction for an OpTypeInt instruction that generates
181 // the given result Id for a 32-bit signed integer scalar type.
MakeParsedInt32TypeInstruction(uint32_t result_id)182 ParsedInstruction MakeParsedInt32TypeInstruction(uint32_t result_id) {
183   const auto i32_inst = MakeInstruction(SpvOpTypeInt, {result_id, 32, 1});
184   const auto i32_operands = std::vector<spv_parsed_operand_t>{
185       MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID),
186       MakeLiteralNumberOperand(2), MakeLiteralNumberOperand(3)};
187   spv_parsed_instruction_t parsed_i32_inst = {
188       i32_inst.data(),
189       static_cast<uint16_t>(i32_inst.size()),
190       SpvOpTypeInt,
191       SPV_EXT_INST_TYPE_NONE,
192       0,  // type id
193       result_id,
194       i32_operands.data(),
195       static_cast<uint16_t>(i32_operands.size())};
196   return ParsedInstruction(parsed_i32_inst);
197 }
198 
199 class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> {
200  protected:
~BinaryParseTest()201   ~BinaryParseTest() override { spvDiagnosticDestroy(diagnostic_); }
202 
Parse(const SpirvVector & words,spv_result_t expected_result,bool flip_words=false)203   void Parse(const SpirvVector& words, spv_result_t expected_result,
204              bool flip_words = false) {
205     SpirvVector flipped_words(words);
206     SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness");
207     if (flip_words) {
208       std::transform(flipped_words.begin(), flipped_words.end(),
209                      flipped_words.begin(), [](const uint32_t raw_word) {
210                        return spvFixWord(raw_word,
211                                          I32_ENDIAN_HOST == I32_ENDIAN_BIG
212                                              ? SPV_ENDIANNESS_LITTLE
213                                              : SPV_ENDIANNESS_BIG);
214                      });
215     }
216     EXPECT_EQ(expected_result,
217               spvBinaryParse(ScopedContext().context, &client_,
218                              flipped_words.data(), flipped_words.size(),
219                              invoke_header, invoke_instruction, &diagnostic_));
220   }
221 
222   spv_diagnostic diagnostic_ = nullptr;
223   MockParseClient client_;
224 };
225 
226 // Adds an EXPECT_CALL to client_->Header() with appropriate parameters,
227 // including bound.  Returns the EXPECT_CALL result.
228 #define EXPECT_HEADER(bound)                                                   \
229   EXPECT_CALL(                                                                 \
230       client_,                                                                 \
231       Header(AnyOf(SPV_ENDIANNESS_LITTLE, SPV_ENDIANNESS_BIG), SpvMagicNumber, \
232              0x10000, SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0),  \
233              bound, 0 /*reserved*/))
234 
235 static const bool kSwapEndians[] = {false, true};
236 
TEST_F(BinaryParseTest,EmptyModuleHasValidHeaderAndNoInstructionCallbacks)237 TEST_F(BinaryParseTest, EmptyModuleHasValidHeaderAndNoInstructionCallbacks) {
238   for (bool endian_swap : kSwapEndians) {
239     const auto words = CompileSuccessfully("");
240     EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
241     EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
242     Parse(words, SPV_SUCCESS, endian_swap);
243     EXPECT_EQ(nullptr, diagnostic_);
244   }
245 }
246 
TEST_F(BinaryParseTest,NullDiagnosticsIsOkForGoodParse)247 TEST_F(BinaryParseTest, NullDiagnosticsIsOkForGoodParse) {
248   const auto words = CompileSuccessfully("");
249   EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
250   EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
251   EXPECT_EQ(
252       SPV_SUCCESS,
253       spvBinaryParse(ScopedContext().context, &client_, words.data(),
254                      words.size(), invoke_header, invoke_instruction, nullptr));
255 }
256 
TEST_F(BinaryParseTest,NullDiagnosticsIsOkForBadParse)257 TEST_F(BinaryParseTest, NullDiagnosticsIsOkForBadParse) {
258   auto words = CompileSuccessfully("");
259   words.push_back(0xffffffff);  // Certainly invalid instruction header.
260   EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
261   EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
262   EXPECT_EQ(
263       SPV_ERROR_INVALID_BINARY,
264       spvBinaryParse(ScopedContext().context, &client_, words.data(),
265                      words.size(), invoke_header, invoke_instruction, nullptr));
266 }
267 
268 // Make sure that we don't blow up when both the consumer and the diagnostic are
269 // null.
TEST_F(BinaryParseTest,NullConsumerNullDiagnosticsForBadParse)270 TEST_F(BinaryParseTest, NullConsumerNullDiagnosticsForBadParse) {
271   auto words = CompileSuccessfully("");
272 
273   auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
274   ctx.SetMessageConsumer(nullptr);
275 
276   words.push_back(0xffffffff);  // Certainly invalid instruction header.
277   EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
278   EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
279   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
280             spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
281                            invoke_header, invoke_instruction, nullptr));
282 }
283 
TEST_F(BinaryParseTest,SpecifyConsumerNullDiagnosticsForGoodParse)284 TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) {
285   const auto words = CompileSuccessfully("");
286 
287   auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
288   int invocation = 0;
289   ctx.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
290                                        const spv_position_t&,
291                                        const char*) { ++invocation; });
292 
293   EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
294   EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
295   EXPECT_EQ(SPV_SUCCESS,
296             spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
297                            invoke_header, invoke_instruction, nullptr));
298   EXPECT_EQ(0, invocation);
299 }
300 
TEST_F(BinaryParseTest,SpecifyConsumerNullDiagnosticsForBadParse)301 TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) {
302   auto words = CompileSuccessfully("");
303 
304   auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
305   int invocation = 0;
306   ctx.SetMessageConsumer(
307       [&invocation](spv_message_level_t level, const char* source,
308                     const spv_position_t& position, const char* message) {
309         ++invocation;
310         EXPECT_EQ(SPV_MSG_ERROR, level);
311         EXPECT_STREQ("input", source);
312         EXPECT_EQ(0u, position.line);
313         EXPECT_EQ(0u, position.column);
314         EXPECT_EQ(1u, position.index);
315         EXPECT_STREQ("Invalid opcode: 65535", message);
316       });
317 
318   words.push_back(0xffffffff);  // Certainly invalid instruction header.
319   EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
320   EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
321   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
322             spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
323                            invoke_header, invoke_instruction, nullptr));
324   EXPECT_EQ(1, invocation);
325 }
326 
TEST_F(BinaryParseTest,SpecifyConsumerSpecifyDiagnosticsForGoodParse)327 TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) {
328   const auto words = CompileSuccessfully("");
329 
330   auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
331   int invocation = 0;
332   ctx.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
333                                        const spv_position_t&,
334                                        const char*) { ++invocation; });
335 
336   EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
337   EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
338   EXPECT_EQ(SPV_SUCCESS,
339             spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
340                            invoke_header, invoke_instruction, &diagnostic_));
341   EXPECT_EQ(0, invocation);
342   EXPECT_EQ(nullptr, diagnostic_);
343 }
344 
TEST_F(BinaryParseTest,SpecifyConsumerSpecifyDiagnosticsForBadParse)345 TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) {
346   auto words = CompileSuccessfully("");
347 
348   auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
349   int invocation = 0;
350   ctx.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
351                                        const spv_position_t&,
352                                        const char*) { ++invocation; });
353 
354   words.push_back(0xffffffff);  // Certainly invalid instruction header.
355   EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
356   EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
357   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
358             spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
359                            invoke_header, invoke_instruction, &diagnostic_));
360   EXPECT_EQ(0, invocation);
361   EXPECT_STREQ("Invalid opcode: 65535", diagnostic_->error);
362 }
363 
TEST_F(BinaryParseTest,ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback)364 TEST_F(BinaryParseTest,
365        ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback) {
366   for (bool endian_swap : kSwapEndians) {
367     const auto words = CompileSuccessfully("%1 = OpTypeVoid");
368     InSequence calls_expected_in_specific_order;
369     EXPECT_HEADER(2).WillOnce(Return(SPV_SUCCESS));
370     EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
371         .WillOnce(Return(SPV_SUCCESS));
372     Parse(words, SPV_SUCCESS, endian_swap);
373     EXPECT_EQ(nullptr, diagnostic_);
374   }
375 }
376 
TEST_F(BinaryParseTest,NullHeaderCallbackIsIgnored)377 TEST_F(BinaryParseTest, NullHeaderCallbackIsIgnored) {
378   const auto words = CompileSuccessfully("%1 = OpTypeVoid");
379   EXPECT_CALL(client_, Header(_, _, _, _, _, _))
380       .Times(0);  // No header callback.
381   EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
382       .WillOnce(Return(SPV_SUCCESS));
383   EXPECT_EQ(SPV_SUCCESS, spvBinaryParse(ScopedContext().context, &client_,
384                                         words.data(), words.size(), nullptr,
385                                         invoke_instruction, &diagnostic_));
386   EXPECT_EQ(nullptr, diagnostic_);
387 }
388 
TEST_F(BinaryParseTest,NullInstructionCallbackIsIgnored)389 TEST_F(BinaryParseTest, NullInstructionCallbackIsIgnored) {
390   const auto words = CompileSuccessfully("%1 = OpTypeVoid");
391   EXPECT_HEADER((2)).WillOnce(Return(SPV_SUCCESS));
392   EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
393   EXPECT_EQ(SPV_SUCCESS,
394             spvBinaryParse(ScopedContext().context, &client_, words.data(),
395                            words.size(), invoke_header, nullptr, &diagnostic_));
396   EXPECT_EQ(nullptr, diagnostic_);
397 }
398 
399 // Check the result of multiple instruction callbacks.
400 //
401 // This test exercises non-default values for the following members of the
402 // spv_parsed_instruction_t struct: words, num_words, opcode, result_id,
403 // operands, num_operands.
TEST_F(BinaryParseTest,TwoScalarTypesGenerateTwoInstructionCallbacks)404 TEST_F(BinaryParseTest, TwoScalarTypesGenerateTwoInstructionCallbacks) {
405   for (bool endian_swap : kSwapEndians) {
406     const auto words = CompileSuccessfully(
407         "%1 = OpTypeVoid "
408         "%2 = OpTypeInt 32 1");
409     InSequence calls_expected_in_specific_order;
410     EXPECT_HEADER(3).WillOnce(Return(SPV_SUCCESS));
411     EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
412         .WillOnce(Return(SPV_SUCCESS));
413     EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
414         .WillOnce(Return(SPV_SUCCESS));
415     Parse(words, SPV_SUCCESS, endian_swap);
416     EXPECT_EQ(nullptr, diagnostic_);
417   }
418 }
419 
TEST_F(BinaryParseTest,EarlyReturnWithZeroPassingCallbacks)420 TEST_F(BinaryParseTest, EarlyReturnWithZeroPassingCallbacks) {
421   for (bool endian_swap : kSwapEndians) {
422     const auto words = CompileSuccessfully(
423         "%1 = OpTypeVoid "
424         "%2 = OpTypeInt 32 1");
425     InSequence calls_expected_in_specific_order;
426     EXPECT_HEADER(3).WillOnce(Return(SPV_ERROR_INVALID_BINARY));
427     // Early exit means no calls to Instruction().
428     EXPECT_CALL(client_, Instruction(_)).Times(0);
429     Parse(words, SPV_ERROR_INVALID_BINARY, endian_swap);
430     // On error, the binary parser doesn't generate its own diagnostics.
431     EXPECT_EQ(nullptr, diagnostic_);
432   }
433 }
434 
TEST_F(BinaryParseTest,EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode)435 TEST_F(BinaryParseTest,
436        EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode) {
437   for (bool endian_swap : kSwapEndians) {
438     const auto words = CompileSuccessfully(
439         "%1 = OpTypeVoid "
440         "%2 = OpTypeInt 32 1");
441     InSequence calls_expected_in_specific_order;
442     EXPECT_HEADER(3).WillOnce(Return(SPV_REQUESTED_TERMINATION));
443     // Early exit means no calls to Instruction().
444     EXPECT_CALL(client_, Instruction(_)).Times(0);
445     Parse(words, SPV_REQUESTED_TERMINATION, endian_swap);
446     // On early termination, the binary parser doesn't generate its own
447     // diagnostics.
448     EXPECT_EQ(nullptr, diagnostic_);
449   }
450 }
451 
TEST_F(BinaryParseTest,EarlyReturnWithOnePassingCallback)452 TEST_F(BinaryParseTest, EarlyReturnWithOnePassingCallback) {
453   for (bool endian_swap : kSwapEndians) {
454     const auto words = CompileSuccessfully(
455         "%1 = OpTypeVoid "
456         "%2 = OpTypeInt 32 1 "
457         "%3 = OpTypeFloat 32");
458     InSequence calls_expected_in_specific_order;
459     EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
460     EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
461         .WillOnce(Return(SPV_REQUESTED_TERMINATION));
462     Parse(words, SPV_REQUESTED_TERMINATION, endian_swap);
463     // On early termination, the binary parser doesn't generate its own
464     // diagnostics.
465     EXPECT_EQ(nullptr, diagnostic_);
466   }
467 }
468 
TEST_F(BinaryParseTest,EarlyReturnWithTwoPassingCallbacks)469 TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
470   for (bool endian_swap : kSwapEndians) {
471     const auto words = CompileSuccessfully(
472         "%1 = OpTypeVoid "
473         "%2 = OpTypeInt 32 1 "
474         "%3 = OpTypeFloat 32");
475     InSequence calls_expected_in_specific_order;
476     EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
477     EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
478         .WillOnce(Return(SPV_SUCCESS));
479     EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
480         .WillOnce(Return(SPV_REQUESTED_TERMINATION));
481     Parse(words, SPV_REQUESTED_TERMINATION, endian_swap);
482     // On early termination, the binary parser doesn't generate its own
483     // diagnostics.
484     EXPECT_EQ(nullptr, diagnostic_);
485   }
486 }
487 
TEST_F(BinaryParseTest,InstructionWithStringOperand)488 TEST_F(BinaryParseTest, InstructionWithStringOperand) {
489   const std::string str =
490       "the future is already here, it's just not evenly distributed";
491   const auto str_words = MakeVector(str);
492   const auto instruction = MakeInstruction(SpvOpName, {99}, str_words);
493   const auto words = Concatenate({ExpectedHeaderForBound(100), instruction});
494   InSequence calls_expected_in_specific_order;
495   EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS));
496   const auto operands = std::vector<spv_parsed_operand_t>{
497       MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID),
498       MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))};
499   EXPECT_CALL(client_,
500               Instruction(ParsedInstruction(spv_parsed_instruction_t{
501                   instruction.data(), static_cast<uint16_t>(instruction.size()),
502                   SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/,
503                   0 /* No result id for OpName*/, operands.data(),
504                   static_cast<uint16_t>(operands.size())})))
505       .WillOnce(Return(SPV_SUCCESS));
506   // Since we are actually checking the output, don't test the
507   // endian-swapped version.
508   Parse(words, SPV_SUCCESS, false);
509   EXPECT_EQ(nullptr, diagnostic_);
510 }
511 
512 // Checks for non-zero values for the result_id and ext_inst_type members
513 // spv_parsed_instruction_t.
TEST_F(BinaryParseTest,ExtendedInstruction)514 TEST_F(BinaryParseTest, ExtendedInstruction) {
515   const auto words = CompileSuccessfully(
516       "%extcl = OpExtInstImport \"OpenCL.std\" "
517       "%result = OpExtInst %float %extcl sqrt %x");
518   EXPECT_HEADER(5).WillOnce(Return(SPV_SUCCESS));
519   EXPECT_CALL(client_, Instruction(_)).WillOnce(Return(SPV_SUCCESS));
520   // We're only interested in the second call to Instruction():
521   const auto operands = std::vector<spv_parsed_operand_t>{
522       MakeSimpleOperand(1, SPV_OPERAND_TYPE_TYPE_ID),
523       MakeSimpleOperand(2, SPV_OPERAND_TYPE_RESULT_ID),
524       MakeSimpleOperand(3,
525                         SPV_OPERAND_TYPE_ID),  // Extended instruction set Id
526       MakeSimpleOperand(4, SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER),
527       MakeSimpleOperand(5, SPV_OPERAND_TYPE_ID),  // Id of the argument
528   };
529   const auto instruction = MakeInstruction(
530       SpvOpExtInst,
531       {2, 3, 1, static_cast<uint32_t>(OpenCLLIB::Entrypoints::Sqrt), 4});
532   EXPECT_CALL(client_,
533               Instruction(ParsedInstruction(spv_parsed_instruction_t{
534                   instruction.data(), static_cast<uint16_t>(instruction.size()),
535                   SpvOpExtInst, SPV_EXT_INST_TYPE_OPENCL_STD, 2 /*type id*/,
536                   3 /*result id*/, operands.data(),
537                   static_cast<uint16_t>(operands.size())})))
538       .WillOnce(Return(SPV_SUCCESS));
539   // Since we are actually checking the output, don't test the
540   // endian-swapped version.
541   Parse(words, SPV_SUCCESS, false);
542   EXPECT_EQ(nullptr, diagnostic_);
543 }
544 
545 // A binary parser diagnostic test case where we provide the words array
546 // pointer and word count explicitly.
547 struct WordsAndCountDiagnosticCase {
548   const uint32_t* words;
549   size_t num_words;
550   std::string expected_diagnostic;
551 };
552 
553 using BinaryParseWordsAndCountDiagnosticTest = spvtest::TextToBinaryTestBase<
554     ::testing::TestWithParam<WordsAndCountDiagnosticCase>>;
555 
TEST_P(BinaryParseWordsAndCountDiagnosticTest,WordAndCountCases)556 TEST_P(BinaryParseWordsAndCountDiagnosticTest, WordAndCountCases) {
557   EXPECT_EQ(
558       SPV_ERROR_INVALID_BINARY,
559       spvBinaryParse(ScopedContext().context, nullptr, GetParam().words,
560                      GetParam().num_words, nullptr, nullptr, &diagnostic));
561   ASSERT_NE(nullptr, diagnostic);
562   EXPECT_THAT(diagnostic->error, Eq(GetParam().expected_diagnostic));
563 }
564 
565 INSTANTIATE_TEST_SUITE_P(
566     BinaryParseDiagnostic, BinaryParseWordsAndCountDiagnosticTest,
567     ::testing::ValuesIn(std::vector<WordsAndCountDiagnosticCase>{
568         {nullptr, 0, "Missing module."},
569         {kHeaderForBound1, 0,
570          "Module has incomplete header: only 0 words instead of 5"},
571         {kHeaderForBound1, 1,
572          "Module has incomplete header: only 1 words instead of 5"},
573         {kHeaderForBound1, 2,
574          "Module has incomplete header: only 2 words instead of 5"},
575         {kHeaderForBound1, 3,
576          "Module has incomplete header: only 3 words instead of 5"},
577         {kHeaderForBound1, 4,
578          "Module has incomplete header: only 4 words instead of 5"},
579     }));
580 
581 // A binary parser diagnostic test case where a vector of words is
582 // provided.  We'll use this to express cases that can't be created
583 // via the assembler.  Either we want to make a malformed instruction,
584 // or an invalid case the assembler would reject.
585 struct WordVectorDiagnosticCase {
586   std::vector<uint32_t> words;
587   std::string expected_diagnostic;
588 };
589 
590 using BinaryParseWordVectorDiagnosticTest = spvtest::TextToBinaryTestBase<
591     ::testing::TestWithParam<WordVectorDiagnosticCase>>;
592 
TEST_P(BinaryParseWordVectorDiagnosticTest,WordVectorCases)593 TEST_P(BinaryParseWordVectorDiagnosticTest, WordVectorCases) {
594   const auto& words = GetParam().words;
595   EXPECT_THAT(spvBinaryParse(ScopedContext().context, nullptr, words.data(),
596                              words.size(), nullptr, nullptr, &diagnostic),
597               AnyOf(SPV_ERROR_INVALID_BINARY, SPV_ERROR_INVALID_ID));
598   ASSERT_NE(nullptr, diagnostic);
599   EXPECT_THAT(diagnostic->error, Eq(GetParam().expected_diagnostic));
600 }
601 
602 INSTANTIATE_TEST_SUITE_P(
603     BinaryParseDiagnostic, BinaryParseWordVectorDiagnosticTest,
604     ::testing::ValuesIn(std::vector<WordVectorDiagnosticCase>{
605         {Concatenate({ExpectedHeaderForBound(1), {spvOpcodeMake(0, SpvOpNop)}}),
606          "Invalid instruction word count: 0"},
607         {Concatenate(
608              {ExpectedHeaderForBound(1),
609               {spvOpcodeMake(1, static_cast<SpvOp>(
610                                     std::numeric_limits<uint16_t>::max()))}}),
611          "Invalid opcode: 65535"},
612         {Concatenate({ExpectedHeaderForBound(1),
613                       MakeInstruction(SpvOpNop, {42})}),
614          "Invalid instruction OpNop starting at word 5: expected "
615          "no more operands after 1 words, but stated word count is 2."},
616         // Supply several more unexpectd words.
617         {Concatenate({ExpectedHeaderForBound(1),
618                       MakeInstruction(SpvOpNop, {42, 43, 44, 45, 46, 47})}),
619          "Invalid instruction OpNop starting at word 5: expected "
620          "no more operands after 1 words, but stated word count is 7."},
621         {Concatenate({ExpectedHeaderForBound(1),
622                       MakeInstruction(SpvOpTypeVoid, {1, 2})}),
623          "Invalid instruction OpTypeVoid starting at word 5: expected "
624          "no more operands after 2 words, but stated word count is 3."},
625         {Concatenate({ExpectedHeaderForBound(1),
626                       MakeInstruction(SpvOpTypeVoid, {1, 2, 5, 9, 10})}),
627          "Invalid instruction OpTypeVoid starting at word 5: expected "
628          "no more operands after 2 words, but stated word count is 6."},
629         {Concatenate({ExpectedHeaderForBound(1),
630                       MakeInstruction(SpvOpTypeInt, {1, 32, 1, 9})}),
631          "Invalid instruction OpTypeInt starting at word 5: expected "
632          "no more operands after 4 words, but stated word count is 5."},
633         {Concatenate({ExpectedHeaderForBound(1),
634                       MakeInstruction(SpvOpTypeInt, {1})}),
635          "End of input reached while decoding OpTypeInt starting at word 5:"
636          " expected more operands after 2 words."},
637 
638         // Check several cases for running off the end of input.
639 
640         // Detect a missing single word operand.
641         {Concatenate({ExpectedHeaderForBound(1),
642                       {spvOpcodeMake(2, SpvOpTypeStruct)}}),
643          "End of input reached while decoding OpTypeStruct starting at word"
644          " 5: missing result ID operand at word offset 1."},
645         // Detect this a missing a multi-word operand to OpConstant.
646         // We also lie and say the OpConstant instruction has 5 words when
647         // it only has 3.  Corresponds to something like this:
648         //    %1 = OpTypeInt 64 0
649         //    %2 = OpConstant %1 <missing>
650         {Concatenate({ExpectedHeaderForBound(3),
651                       {MakeInstruction(SpvOpTypeInt, {1, 64, 0})},
652                       {spvOpcodeMake(5, SpvOpConstant), 1, 2}}),
653          "End of input reached while decoding OpConstant starting at word"
654          " 9: missing possibly multi-word literal number operand at word "
655          "offset 3."},
656         // Detect when we provide only one word from the 64-bit literal,
657         // and again lie about the number of words in the instruction.
658         {Concatenate({ExpectedHeaderForBound(3),
659                       {MakeInstruction(SpvOpTypeInt, {1, 64, 0})},
660                       {spvOpcodeMake(5, SpvOpConstant), 1, 2, 42}}),
661          "End of input reached while decoding OpConstant starting at word"
662          " 9: truncated possibly multi-word literal number operand at word "
663          "offset 3."},
664         // Detect when a required string operand is missing.
665         // Also, lie about the length of the instruction.
666         {Concatenate({ExpectedHeaderForBound(3),
667                       {spvOpcodeMake(3, SpvOpString), 1}}),
668          "End of input reached while decoding OpString starting at word"
669          " 5: missing literal string operand at word offset 2."},
670         // Detect when a required string operand is truncated: it's missing
671         // a null terminator.  Catching the error avoids a buffer overrun.
672         {Concatenate({ExpectedHeaderForBound(3),
673                       {spvOpcodeMake(4, SpvOpString), 1, 0x41414141,
674                        0x41414141}}),
675          "End of input reached while decoding OpString starting at word"
676          " 5: truncated literal string operand at word offset 2."},
677         // Detect when an optional string operand is truncated: it's missing
678         // a null terminator.  Catching the error avoids a buffer overrun.
679         // (It is valid for an optional string operand to be absent.)
680         {Concatenate({ExpectedHeaderForBound(3),
681                       {spvOpcodeMake(6, SpvOpSource),
682                        static_cast<uint32_t>(SpvSourceLanguageOpenCL_C), 210,
683                        1 /* file id */,
684                        /*start of string*/ 0x41414141, 0x41414141}}),
685          "End of input reached while decoding OpSource starting at word"
686          " 5: truncated literal string operand at word offset 4."},
687 
688         // (End of input exhaustion test cases.)
689 
690         // In this case the instruction word count is too small, where
691         // it would truncate a multi-word operand to OpConstant.
692         {Concatenate({ExpectedHeaderForBound(3),
693                       {MakeInstruction(SpvOpTypeInt, {1, 64, 0})},
694                       {spvOpcodeMake(4, SpvOpConstant), 1, 2, 44, 44}}),
695          "Invalid word count: OpConstant starting at word 9 says it has 4"
696          " words, but found 5 words instead."},
697         // Word count is to small, where it would truncate a literal string.
698         {Concatenate({ExpectedHeaderForBound(2),
699                       {spvOpcodeMake(3, SpvOpString), 1, 0x41414141, 0}}),
700          "Invalid word count: OpString starting at word 5 says it has 3"
701          " words, but found 4 words instead."},
702         // Word count is too large.  The string terminates before the last
703         // word.
704         {Concatenate({ExpectedHeaderForBound(2),
705                       {spvOpcodeMake(4, SpvOpString), 1 /* result id */},
706                       MakeVector("abc"),
707                       {0 /* this word does not belong*/}}),
708          "Invalid instruction OpString starting at word 5: expected no more"
709          " operands after 3 words, but stated word count is 4."},
710         // Word count is too large.  There are too many words after the string
711         // literal.  A linkage attribute decoration is the only case in SPIR-V
712         // where a string operand is followed by another operand.
713         {Concatenate({ExpectedHeaderForBound(2),
714                       {spvOpcodeMake(6, SpvOpDecorate), 1 /* target id */,
715                        static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
716                       MakeVector("abc"),
717                       {static_cast<uint32_t>(SpvLinkageTypeImport),
718                        0 /* does not belong */}}),
719          "Invalid instruction OpDecorate starting at word 5: expected no more"
720          " operands after 5 words, but stated word count is 6."},
721         // Like the previous case, but with 5 extra words.
722         {Concatenate({ExpectedHeaderForBound(2),
723                       {spvOpcodeMake(10, SpvOpDecorate), 1 /* target id */,
724                        static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
725                       MakeVector("abc"),
726                       {static_cast<uint32_t>(SpvLinkageTypeImport),
727                        /* don't belong */ 0, 1, 2, 3, 4}}),
728          "Invalid instruction OpDecorate starting at word 5: expected no more"
729          " operands after 5 words, but stated word count is 10."},
730         // Like the previous two cases, but with OpMemberDecorate.
731         {Concatenate({ExpectedHeaderForBound(2),
732                       {spvOpcodeMake(7, SpvOpMemberDecorate), 1 /* target id */,
733                        42 /* member index */,
734                        static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
735                       MakeVector("abc"),
736                       {static_cast<uint32_t>(SpvLinkageTypeImport),
737                        0 /* does not belong */}}),
738          "Invalid instruction OpMemberDecorate starting at word 5: expected no"
739          " more operands after 6 words, but stated word count is 7."},
740         {Concatenate({ExpectedHeaderForBound(2),
741                       {spvOpcodeMake(11, SpvOpMemberDecorate),
742                        1 /* target id */, 42 /* member index */,
743                        static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
744                       MakeVector("abc"),
745                       {static_cast<uint32_t>(SpvLinkageTypeImport),
746                        /* don't belong */ 0, 1, 2, 3, 4}}),
747          "Invalid instruction OpMemberDecorate starting at word 5: expected no"
748          " more operands after 6 words, but stated word count is 11."},
749         // Word count is too large.  There should be no more words
750         // after the RelaxedPrecision decoration.
751         {Concatenate({ExpectedHeaderForBound(2),
752                       {spvOpcodeMake(4, SpvOpDecorate), 1 /* target id */,
753                        static_cast<uint32_t>(SpvDecorationRelaxedPrecision),
754                        0 /* does not belong */}}),
755          "Invalid instruction OpDecorate starting at word 5: expected no"
756          " more operands after 3 words, but stated word count is 4."},
757         // Word count is too large.  There should be only one word after
758         // the SpecId decoration enum word.
759         {Concatenate({ExpectedHeaderForBound(2),
760                       {spvOpcodeMake(5, SpvOpDecorate), 1 /* target id */,
761                        static_cast<uint32_t>(SpvDecorationSpecId),
762                        42 /* the spec id */, 0 /* does not belong */}}),
763          "Invalid instruction OpDecorate starting at word 5: expected no"
764          " more operands after 4 words, but stated word count is 5."},
765         {Concatenate({ExpectedHeaderForBound(2),
766                       {spvOpcodeMake(2, SpvOpTypeVoid), 0}}),
767          "Error: Result Id is 0"},
768         {Concatenate({
769              ExpectedHeaderForBound(2),
770              {spvOpcodeMake(2, SpvOpTypeVoid), 1},
771              {spvOpcodeMake(2, SpvOpTypeBool), 1},
772          }),
773          "Id 1 is defined more than once"},
774         {Concatenate({ExpectedHeaderForBound(3),
775                       MakeInstruction(SpvOpExtInst, {2, 3, 100, 4, 5})}),
776          "OpExtInst set Id 100 does not reference an OpExtInstImport result "
777          "Id"},
778         {Concatenate({ExpectedHeaderForBound(101),
779                       MakeInstruction(SpvOpExtInstImport, {100},
780                                       MakeVector("OpenCL.std")),
781                       // OpenCL cos is #14
782                       MakeInstruction(SpvOpExtInst, {2, 3, 100, 14, 5, 999})}),
783          "Invalid instruction OpExtInst starting at word 10: expected no "
784          "more operands after 6 words, but stated word count is 7."},
785         // In this case, the OpSwitch selector refers to an invalid ID.
786         {Concatenate({ExpectedHeaderForBound(3),
787                       MakeInstruction(SpvOpSwitch, {1, 2, 42, 3})}),
788          "Invalid OpSwitch: selector id 1 has no type"},
789         // In this case, the OpSwitch selector refers to an ID that has
790         // no type.
791         {Concatenate({ExpectedHeaderForBound(3),
792                       MakeInstruction(SpvOpLabel, {1}),
793                       MakeInstruction(SpvOpSwitch, {1, 2, 42, 3})}),
794          "Invalid OpSwitch: selector id 1 has no type"},
795         {Concatenate({ExpectedHeaderForBound(3),
796                       MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
797                       MakeInstruction(SpvOpSwitch, {1, 3, 42, 3})}),
798          "Invalid OpSwitch: selector id 1 is a type, not a value"},
799         {Concatenate({ExpectedHeaderForBound(3),
800                       MakeInstruction(SpvOpTypeFloat, {1, 32}),
801                       MakeInstruction(SpvOpConstant, {1, 2, 0x78f00000}),
802                       MakeInstruction(SpvOpSwitch, {2, 3, 42, 3})}),
803          "Invalid OpSwitch: selector id 2 is not a scalar integer"},
804         {Concatenate({ExpectedHeaderForBound(3),
805                       MakeInstruction(SpvOpExtInstImport, {1},
806                                       MakeVector("invalid-import"))}),
807          "Invalid extended instruction import 'invalid-import'"},
808         {Concatenate({
809              ExpectedHeaderForBound(3),
810              MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
811              MakeInstruction(SpvOpConstant, {2, 2, 42}),
812          }),
813          "Type Id 2 is not a type"},
814         {Concatenate({
815              ExpectedHeaderForBound(3),
816              MakeInstruction(SpvOpTypeBool, {1}),
817              MakeInstruction(SpvOpConstant, {1, 2, 42}),
818          }),
819          "Type Id 1 is not a scalar numeric type"},
820     }));
821 
822 // A binary parser diagnostic case generated from an assembly text input.
823 struct AssemblyDiagnosticCase {
824   std::string assembly;
825   std::string expected_diagnostic;
826 };
827 
828 using BinaryParseAssemblyDiagnosticTest = spvtest::TextToBinaryTestBase<
829     ::testing::TestWithParam<AssemblyDiagnosticCase>>;
830 
TEST_P(BinaryParseAssemblyDiagnosticTest,AssemblyCases)831 TEST_P(BinaryParseAssemblyDiagnosticTest, AssemblyCases) {
832   auto words = CompileSuccessfully(GetParam().assembly);
833   EXPECT_THAT(spvBinaryParse(ScopedContext().context, nullptr, words.data(),
834                              words.size(), nullptr, nullptr, &diagnostic),
835               AnyOf(SPV_ERROR_INVALID_BINARY, SPV_ERROR_INVALID_ID));
836   ASSERT_NE(nullptr, diagnostic);
837   EXPECT_THAT(diagnostic->error, Eq(GetParam().expected_diagnostic));
838 }
839 
840 INSTANTIATE_TEST_SUITE_P(
841     BinaryParseDiagnostic, BinaryParseAssemblyDiagnosticTest,
842     ::testing::ValuesIn(std::vector<AssemblyDiagnosticCase>{
843         {"%1 = OpConstant !0 42", "Error: Type Id is 0"},
844         // A required id is 0.
845         {"OpName !0 \"foo\"", "Id is 0"},
846         // An optional id is 0, in this case the optional
847         // initializer.
848         {"%2 = OpVariable %1 CrossWorkgroup !0", "Id is 0"},
849         {"OpControlBarrier !0 %1 %2", "scope ID is 0"},
850         {"OpControlBarrier %1 !0 %2", "scope ID is 0"},
851         {"OpControlBarrier %1 %2 !0", "memory semantics ID is 0"},
852         {"%import = OpExtInstImport \"GLSL.std.450\" "
853          "%result = OpExtInst %type %import !999999 %x",
854          "Invalid extended instruction number: 999999"},
855         {"%2 = OpSpecConstantOp %1 !1000 %2",
856          "Invalid OpSpecConstantOp opcode: 1000"},
857         {"OpCapability !9999", "Invalid capability operand: 9999"},
858         {"OpSource !9999 100", "Invalid source language operand: 9999"},
859         {"OpEntryPoint !9999", "Invalid execution model operand: 9999"},
860         {"OpMemoryModel !9999", "Invalid addressing model operand: 9999"},
861         {"OpMemoryModel Logical !9999", "Invalid memory model operand: 9999"},
862         {"OpExecutionMode %1 !9999", "Invalid execution mode operand: 9999"},
863         {"OpTypeForwardPointer %1 !9999",
864          "Invalid storage class operand: 9999"},
865         {"%2 = OpTypeImage %1 !9999", "Invalid dimensionality operand: 9999"},
866         {"%2 = OpTypeImage %1 1D 0 0 0 0 !9999",
867          "Invalid image format operand: 9999"},
868         {"OpDecorate %1 FPRoundingMode !9999",
869          "Invalid floating-point rounding mode operand: 9999"},
870         {"OpDecorate %1 LinkageAttributes \"C\" !9999",
871          "Invalid linkage type operand: 9999"},
872         {"%1 = OpTypePipe !9999", "Invalid access qualifier operand: 9999"},
873         {"OpDecorate %1 FuncParamAttr !9999",
874          "Invalid function parameter attribute operand: 9999"},
875         {"OpDecorate %1 !9999", "Invalid decoration operand: 9999"},
876         {"OpDecorate %1 BuiltIn !9999", "Invalid built-in operand: 9999"},
877         {"%2 = OpGroupIAdd %1 %3 !9999",
878          "Invalid group operation operand: 9999"},
879         {"OpDecorate %1 FPFastMathMode !63",
880          "Invalid floating-point fast math mode operand: 63 has invalid mask "
881          "component 32"},
882         {"%2 = OpFunction %2 !31",
883          "Invalid function control operand: 31 has invalid mask component 16"},
884         {"OpLoopMerge %1 %2 !1027",
885          "Invalid loop control operand: 1027 has invalid mask component 1024"},
886         {"%2 = OpImageFetch %1 %image %coord !32770",
887          "Invalid image operand: 32770 has invalid mask component 32768"},
888         {"OpSelectionMerge %1 !7",
889          "Invalid selection control operand: 7 has invalid mask component 4"},
890     }));
891 
892 }  // namespace
893 }  // namespace spvtools
894