libspirv.cpp: adds c++ api for spvBinaryParse (#5109)
This commit adds a C++ wrapper above the current spvBinaryParse function. I tried to match it 1:1, except for 2 things: - std::function<>& are used. No more function pointers, allowing context capture. - spv_result_t replaced with a boolean, to match other C++ apis. Callbacks still return a spv_result_t because the underlying implem relies on that. The convertion from spv_result_t to boolean is only done at the boundary. Signed-off-by: Nathan Gauër <brioche@google.com>
This commit is contained in:
Родитель
e150e716ff
Коммит
b84c86f718
|
@ -402,6 +402,19 @@ typedef struct spv_parsed_instruction_t {
|
|||
uint16_t num_operands;
|
||||
} spv_parsed_instruction_t;
|
||||
|
||||
typedef struct spv_parsed_header_t {
|
||||
// The magic number of the SPIR-V module.
|
||||
uint32_t magic;
|
||||
// Version number.
|
||||
uint32_t version;
|
||||
// Generator's magic number.
|
||||
uint32_t generator;
|
||||
// IDs bound for this module (0 < id < bound).
|
||||
uint32_t bound;
|
||||
// reserved.
|
||||
uint32_t reserved;
|
||||
} spv_parsed_header_t;
|
||||
|
||||
typedef struct spv_const_binary_t {
|
||||
const uint32_t* code;
|
||||
const size_t wordCount;
|
||||
|
|
|
@ -31,6 +31,11 @@ using MessageConsumer = std::function<void(
|
|||
const spv_position_t& /* position */, const char* /* message */
|
||||
)>;
|
||||
|
||||
using HeaderParser = std::function<spv_result_t(
|
||||
const spv_endianness_t endianess, const spv_parsed_header_t& instruction)>;
|
||||
using InstructionParser =
|
||||
std::function<spv_result_t(const spv_parsed_instruction_t& instruction)>;
|
||||
|
||||
// C++ RAII wrapper around the C context object spv_context.
|
||||
class Context {
|
||||
public:
|
||||
|
@ -336,6 +341,23 @@ class SpirvTools {
|
|||
std::string* text,
|
||||
uint32_t options = kDefaultDisassembleOption) const;
|
||||
|
||||
// Parses a SPIR-V binary, specified as counted sequence of 32-bit words.
|
||||
// Parsing feedback is provided via two callbacks provided as std::function.
|
||||
// In a valid parse the parsed-header callback is called once, and
|
||||
// then the parsed-instruction callback is called once for each instruction
|
||||
// in the stream.
|
||||
// Returns true on successful parsing.
|
||||
// If diagnostic is non-null, a diagnostic is emitted on failed parsing.
|
||||
// If diagnostic is null the context's message consumer
|
||||
// will be used to emit any errors. If a callback returns anything other than
|
||||
// SPV_SUCCESS, then that status code is returned, no further callbacks are
|
||||
// issued, and no additional diagnostics are emitted.
|
||||
// This is a wrapper around the C API spvBinaryParse.
|
||||
bool Parse(const std::vector<uint32_t>& binary,
|
||||
const HeaderParser& header_parser,
|
||||
const InstructionParser& instruction_parser,
|
||||
spv_diagnostic* diagnostic = nullptr);
|
||||
|
||||
// Validates the given SPIR-V |binary|. Returns true if no issues are found.
|
||||
// Otherwise, returns false and communicates issues via the message consumer
|
||||
// registered.
|
||||
|
|
|
@ -108,6 +108,40 @@ bool SpirvTools::Disassemble(const uint32_t* binary, const size_t binary_size,
|
|||
return status == SPV_SUCCESS;
|
||||
}
|
||||
|
||||
struct CxxParserContext {
|
||||
const HeaderParser& header_parser;
|
||||
const InstructionParser& instruction_parser;
|
||||
};
|
||||
|
||||
bool SpirvTools::Parse(const std::vector<uint32_t>& binary,
|
||||
const HeaderParser& header_parser,
|
||||
const InstructionParser& instruction_parser,
|
||||
spv_diagnostic* diagnostic) {
|
||||
CxxParserContext parser_context = {header_parser, instruction_parser};
|
||||
|
||||
spv_parsed_header_fn_t header_fn_wrapper =
|
||||
[](void* user_data, spv_endianness_t endianness, uint32_t magic,
|
||||
uint32_t version, uint32_t generator, uint32_t id_bound,
|
||||
uint32_t reserved) {
|
||||
CxxParserContext* ctx = reinterpret_cast<CxxParserContext*>(user_data);
|
||||
spv_parsed_header_t header = {magic, version, generator, id_bound,
|
||||
reserved};
|
||||
|
||||
return ctx->header_parser(endianness, header);
|
||||
};
|
||||
|
||||
spv_parsed_instruction_fn_t instruction_fn_wrapper =
|
||||
[](void* user_data, const spv_parsed_instruction_t* instruction) {
|
||||
CxxParserContext* ctx = reinterpret_cast<CxxParserContext*>(user_data);
|
||||
return ctx->instruction_parser(*instruction);
|
||||
};
|
||||
|
||||
spv_result_t status = spvBinaryParse(
|
||||
impl_->context, &parser_context, binary.data(), binary.size(),
|
||||
header_fn_wrapper, instruction_fn_wrapper, diagnostic);
|
||||
return status == SPV_SUCCESS;
|
||||
}
|
||||
|
||||
bool SpirvTools::Validate(const std::vector<uint32_t>& binary) const {
|
||||
return Validate(binary.data(), binary.size());
|
||||
}
|
||||
|
|
|
@ -214,6 +214,40 @@ class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> {
|
|||
MockParseClient client_;
|
||||
};
|
||||
|
||||
class CxxBinaryParseTest
|
||||
: public spvtest::TextToBinaryTestBase<::testing::Test> {
|
||||
protected:
|
||||
CxxBinaryParseTest() {
|
||||
header_parser_ = [this](const spv_endianness_t endianness,
|
||||
const spv_parsed_header_t& header) {
|
||||
return this->client_.Header(endianness, header.magic, header.version,
|
||||
header.generator, header.bound,
|
||||
header.reserved);
|
||||
};
|
||||
|
||||
instruction_parser_ = [this](const spv_parsed_instruction_t& instruction) {
|
||||
return this->client_.Instruction(ParsedInstruction(instruction));
|
||||
};
|
||||
}
|
||||
|
||||
~CxxBinaryParseTest() override { spvDiagnosticDestroy(diagnostic_); }
|
||||
|
||||
void Parse(const SpirvVector& words, bool expected_result,
|
||||
bool flip_words = false,
|
||||
spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
|
||||
SpirvVector flipped_words(words);
|
||||
MaybeFlipWords(flip_words, flipped_words.begin(), flipped_words.end());
|
||||
spvtools::SpirvTools tools(env);
|
||||
EXPECT_EQ(expected_result, tools.Parse(flipped_words, header_parser_,
|
||||
instruction_parser_, &diagnostic_));
|
||||
}
|
||||
|
||||
spv_diagnostic diagnostic_ = nullptr;
|
||||
MockParseClient client_;
|
||||
HeaderParser header_parser_;
|
||||
InstructionParser instruction_parser_;
|
||||
};
|
||||
|
||||
// Adds an EXPECT_CALL to client_->Header() with appropriate parameters,
|
||||
// including bound. Returns the EXPECT_CALL result.
|
||||
#define EXPECT_HEADER(bound) \
|
||||
|
@ -235,6 +269,16 @@ TEST_F(BinaryParseTest, EmptyModuleHasValidHeaderAndNoInstructionCallbacks) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, EmptyModuleHasValidHeaderAndNoInstructionCallbacks) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully("");
|
||||
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
|
||||
Parse(words, true, endian_swap);
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, NullDiagnosticsIsOkForGoodParse) {
|
||||
const auto words = CompileSuccessfully("");
|
||||
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
|
||||
|
@ -245,6 +289,15 @@ TEST_F(BinaryParseTest, NullDiagnosticsIsOkForGoodParse) {
|
|||
words.size(), invoke_header, invoke_instruction, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, NullDiagnosticsIsOkForGoodParse) {
|
||||
const auto words = CompileSuccessfully("");
|
||||
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
|
||||
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
|
||||
EXPECT_EQ(true,
|
||||
tools.Parse(words, header_parser_, instruction_parser_, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, NullDiagnosticsIsOkForBadParse) {
|
||||
auto words = CompileSuccessfully("");
|
||||
words.push_back(0xffffffff); // Certainly invalid instruction header.
|
||||
|
@ -256,6 +309,16 @@ TEST_F(BinaryParseTest, NullDiagnosticsIsOkForBadParse) {
|
|||
words.size(), invoke_header, invoke_instruction, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, NullDiagnosticsIsOkForBadParse) {
|
||||
auto words = CompileSuccessfully("");
|
||||
words.push_back(0xffffffff); // Certainly invalid instruction header.
|
||||
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
|
||||
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
|
||||
EXPECT_EQ(false,
|
||||
tools.Parse(words, header_parser_, instruction_parser_, nullptr));
|
||||
}
|
||||
|
||||
// Make sure that we don't blow up when both the consumer and the diagnostic are
|
||||
// null.
|
||||
TEST_F(BinaryParseTest, NullConsumerNullDiagnosticsForBadParse) {
|
||||
|
@ -272,6 +335,18 @@ TEST_F(BinaryParseTest, NullConsumerNullDiagnosticsForBadParse) {
|
|||
invoke_header, invoke_instruction, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, NullConsumerNullDiagnosticsForBadParse) {
|
||||
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
|
||||
tools.SetMessageConsumer(nullptr);
|
||||
|
||||
auto words = CompileSuccessfully("");
|
||||
words.push_back(0xffffffff); // Certainly invalid instruction header.
|
||||
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
|
||||
EXPECT_EQ(false,
|
||||
tools.Parse(words, header_parser_, instruction_parser_, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) {
|
||||
const auto words = CompileSuccessfully("");
|
||||
|
||||
|
@ -289,6 +364,21 @@ TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) {
|
|||
EXPECT_EQ(0, invocation);
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) {
|
||||
const auto words = CompileSuccessfully("");
|
||||
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
|
||||
int invocation = 0;
|
||||
tools.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
|
||||
const spv_position_t&,
|
||||
const char*) { ++invocation; });
|
||||
|
||||
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
|
||||
EXPECT_EQ(true,
|
||||
tools.Parse(words, header_parser_, instruction_parser_, nullptr));
|
||||
EXPECT_EQ(0, invocation);
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) {
|
||||
auto words = CompileSuccessfully("");
|
||||
|
||||
|
@ -315,6 +405,30 @@ TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) {
|
|||
EXPECT_EQ(1, invocation);
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) {
|
||||
auto words = CompileSuccessfully("");
|
||||
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
|
||||
int invocation = 0;
|
||||
tools.SetMessageConsumer(
|
||||
[&invocation](spv_message_level_t level, const char* source,
|
||||
const spv_position_t& position, const char* message) {
|
||||
++invocation;
|
||||
EXPECT_EQ(SPV_MSG_ERROR, level);
|
||||
EXPECT_STREQ("input", source);
|
||||
EXPECT_EQ(0u, position.line);
|
||||
EXPECT_EQ(0u, position.column);
|
||||
EXPECT_EQ(1u, position.index);
|
||||
EXPECT_STREQ("Invalid opcode: 65535", message);
|
||||
});
|
||||
|
||||
words.push_back(0xffffffff); // Certainly invalid instruction header.
|
||||
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
|
||||
EXPECT_EQ(false,
|
||||
tools.Parse(words, header_parser_, instruction_parser_, nullptr));
|
||||
EXPECT_EQ(1, invocation);
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) {
|
||||
const auto words = CompileSuccessfully("");
|
||||
|
||||
|
@ -333,6 +447,22 @@ TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) {
|
|||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) {
|
||||
const auto words = CompileSuccessfully("");
|
||||
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
|
||||
int invocation = 0;
|
||||
tools.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
|
||||
const spv_position_t&,
|
||||
const char*) { ++invocation; });
|
||||
|
||||
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
|
||||
EXPECT_EQ(true, tools.Parse(words, header_parser_, instruction_parser_,
|
||||
&diagnostic_));
|
||||
EXPECT_EQ(0, invocation);
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) {
|
||||
auto words = CompileSuccessfully("");
|
||||
|
||||
|
@ -352,6 +482,23 @@ TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) {
|
|||
EXPECT_STREQ("Invalid opcode: 65535", diagnostic_->error);
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) {
|
||||
auto words = CompileSuccessfully("");
|
||||
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
|
||||
int invocation = 0;
|
||||
tools.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
|
||||
const spv_position_t&,
|
||||
const char*) { ++invocation; });
|
||||
|
||||
words.push_back(0xffffffff); // Certainly invalid instruction header.
|
||||
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
|
||||
EXPECT_EQ(false, tools.Parse(words, header_parser_, instruction_parser_,
|
||||
&diagnostic_));
|
||||
EXPECT_EQ(0, invocation);
|
||||
EXPECT_STREQ("Invalid opcode: 65535", diagnostic_->error);
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest,
|
||||
ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
|
@ -365,6 +512,19 @@ TEST_F(BinaryParseTest,
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest,
|
||||
ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully("%1 = OpTypeVoid");
|
||||
InSequence calls_expected_in_specific_order;
|
||||
EXPECT_HEADER(2).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
|
||||
.WillOnce(Return(SPV_SUCCESS));
|
||||
Parse(words, true, endian_swap);
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, NullHeaderCallbackIsIgnored) {
|
||||
const auto words = CompileSuccessfully("%1 = OpTypeVoid");
|
||||
EXPECT_CALL(client_, Header(_, _, _, _, _, _))
|
||||
|
@ -408,6 +568,22 @@ TEST_F(BinaryParseTest, TwoScalarTypesGenerateTwoInstructionCallbacks) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, TwoScalarTypesGenerateTwoInstructionCallbacks) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully(
|
||||
"%1 = OpTypeVoid "
|
||||
"%2 = OpTypeInt 32 1");
|
||||
InSequence calls_expected_in_specific_order;
|
||||
EXPECT_HEADER(3).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
|
||||
.WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
|
||||
.WillOnce(Return(SPV_SUCCESS));
|
||||
Parse(words, true, endian_swap);
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, EarlyReturnWithZeroPassingCallbacks) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully(
|
||||
|
@ -423,6 +599,21 @@ TEST_F(BinaryParseTest, EarlyReturnWithZeroPassingCallbacks) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, EarlyReturnWithZeroPassingCallbacks) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully(
|
||||
"%1 = OpTypeVoid "
|
||||
"%2 = OpTypeInt 32 1");
|
||||
InSequence calls_expected_in_specific_order;
|
||||
EXPECT_HEADER(3).WillOnce(Return(SPV_ERROR_INVALID_BINARY));
|
||||
// Early exit means no calls to Instruction().
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0);
|
||||
Parse(words, false, endian_swap);
|
||||
// On error, the binary parser doesn't generate its own diagnostics.
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest,
|
||||
EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
|
@ -440,6 +631,23 @@ TEST_F(BinaryParseTest,
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest,
|
||||
EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully(
|
||||
"%1 = OpTypeVoid "
|
||||
"%2 = OpTypeInt 32 1");
|
||||
InSequence calls_expected_in_specific_order;
|
||||
EXPECT_HEADER(3).WillOnce(Return(SPV_REQUESTED_TERMINATION));
|
||||
// Early exit means no calls to Instruction().
|
||||
EXPECT_CALL(client_, Instruction(_)).Times(0);
|
||||
Parse(words, false, endian_swap);
|
||||
// On early termination, the binary parser doesn't generate its own
|
||||
// diagnostics.
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, EarlyReturnWithOnePassingCallback) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully(
|
||||
|
@ -457,6 +665,23 @@ TEST_F(BinaryParseTest, EarlyReturnWithOnePassingCallback) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, EarlyReturnWithOnePassingCallback) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully(
|
||||
"%1 = OpTypeVoid "
|
||||
"%2 = OpTypeInt 32 1 "
|
||||
"%3 = OpTypeFloat 32");
|
||||
InSequence calls_expected_in_specific_order;
|
||||
EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
|
||||
.WillOnce(Return(SPV_REQUESTED_TERMINATION));
|
||||
Parse(words, false, endian_swap);
|
||||
// On early termination, the binary parser doesn't generate its own
|
||||
// diagnostics.
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully(
|
||||
|
@ -476,6 +701,25 @@ TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const auto words = CompileSuccessfully(
|
||||
"%1 = OpTypeVoid "
|
||||
"%2 = OpTypeInt 32 1 "
|
||||
"%3 = OpTypeFloat 32");
|
||||
InSequence calls_expected_in_specific_order;
|
||||
EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
|
||||
.WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
|
||||
.WillOnce(Return(SPV_REQUESTED_TERMINATION));
|
||||
Parse(words, false, endian_swap);
|
||||
// On early termination, the binary parser doesn't generate its own
|
||||
// diagnostics.
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BinaryParseTest, InstructionWithStringOperand) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const std::string str =
|
||||
|
@ -501,6 +745,31 @@ TEST_F(BinaryParseTest, InstructionWithStringOperand) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, InstructionWithStringOperand) {
|
||||
for (bool endian_swap : kSwapEndians) {
|
||||
const std::string str =
|
||||
"the future is already here, it's just not evenly distributed";
|
||||
const auto str_words = MakeVector(str);
|
||||
const auto instruction = MakeInstruction(spv::Op::OpName, {99}, str_words);
|
||||
const auto words = Concatenate({ExpectedHeaderForBound(100), instruction});
|
||||
InSequence calls_expected_in_specific_order;
|
||||
EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS));
|
||||
const auto operands = std::vector<spv_parsed_operand_t>{
|
||||
MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID),
|
||||
MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))};
|
||||
EXPECT_CALL(
|
||||
client_,
|
||||
Instruction(ParsedInstruction(spv_parsed_instruction_t{
|
||||
instruction.data(), static_cast<uint16_t>(instruction.size()),
|
||||
uint16_t(spv::Op::OpName), SPV_EXT_INST_TYPE_NONE, 0 /*type id*/,
|
||||
0 /* No result id for OpName*/, operands.data(),
|
||||
static_cast<uint16_t>(operands.size())})))
|
||||
.WillOnce(Return(SPV_SUCCESS));
|
||||
Parse(words, true, endian_swap);
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks for non-zero values for the result_id and ext_inst_type members
|
||||
// spv_parsed_instruction_t.
|
||||
TEST_F(BinaryParseTest, ExtendedInstruction) {
|
||||
|
@ -534,6 +803,37 @@ TEST_F(BinaryParseTest, ExtendedInstruction) {
|
|||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
|
||||
TEST_F(CxxBinaryParseTest, ExtendedInstruction) {
|
||||
const auto words = CompileSuccessfully(
|
||||
"%extcl = OpExtInstImport \"OpenCL.std\" "
|
||||
"%result = OpExtInst %float %extcl sqrt %x");
|
||||
EXPECT_HEADER(5).WillOnce(Return(SPV_SUCCESS));
|
||||
EXPECT_CALL(client_, Instruction(_)).WillOnce(Return(SPV_SUCCESS));
|
||||
// We're only interested in the second call to Instruction():
|
||||
const auto operands = std::vector<spv_parsed_operand_t>{
|
||||
MakeSimpleOperand(1, SPV_OPERAND_TYPE_TYPE_ID),
|
||||
MakeSimpleOperand(2, SPV_OPERAND_TYPE_RESULT_ID),
|
||||
MakeSimpleOperand(3,
|
||||
SPV_OPERAND_TYPE_ID), // Extended instruction set Id
|
||||
MakeSimpleOperand(4, SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER),
|
||||
MakeSimpleOperand(5, SPV_OPERAND_TYPE_ID), // Id of the argument
|
||||
};
|
||||
const auto instruction = MakeInstruction(
|
||||
spv::Op::OpExtInst,
|
||||
{2, 3, 1, static_cast<uint32_t>(OpenCLLIB::Entrypoints::Sqrt), 4});
|
||||
EXPECT_CALL(client_,
|
||||
Instruction(ParsedInstruction(spv_parsed_instruction_t{
|
||||
instruction.data(), static_cast<uint16_t>(instruction.size()),
|
||||
uint16_t(spv::Op::OpExtInst), SPV_EXT_INST_TYPE_OPENCL_STD,
|
||||
2 /*type id*/, 3 /*result id*/, operands.data(),
|
||||
static_cast<uint16_t>(operands.size())})))
|
||||
.WillOnce(Return(SPV_SUCCESS));
|
||||
// Since we are actually checking the output, don't test the
|
||||
// endian-swapped version.
|
||||
Parse(words, true, false);
|
||||
EXPECT_EQ(nullptr, diagnostic_);
|
||||
}
|
||||
|
||||
// A binary parser diagnostic test case where we provide the words array
|
||||
// pointer and word count explicitly.
|
||||
struct WordsAndCountDiagnosticCase {
|
||||
|
|
Загрузка…
Ссылка в новой задаче