binary_parse_test.cpp 41 KB

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