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