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