binary_parse_test.cpp 53 KB

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