From dfc50086a6d30487979fca2249b05a0f3bf43f75 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Fri, 21 Aug 2015 11:50:55 -0400 Subject: [PATCH] Support " = .." format. --- include/libspirv/libspirv.h | 2 + source/opcode.cpp | 7 +++ source/opcode.h | 12 ++++ source/text.cpp | 147 ++++++++++++++++++++++++++++++++------------ test/NamedId.cpp | 18 +++--- test/TextToBinary.cpp | 85 +++++++++++++++---------- 6 files changed, 191 insertions(+), 80 deletions(-) diff --git a/include/libspirv/libspirv.h b/include/libspirv/libspirv.h index ace25b8..eed681b 100644 --- a/include/libspirv/libspirv.h +++ b/include/libspirv/libspirv.h @@ -95,6 +95,8 @@ extern "C" { #define SPV_FORCE_16_BIT_ENUM(name) _##name = 0x7fff #define SPV_FORCE_32_BIT_ENUM(name) _##name = 0x7fffffff +#define SPV_OPERAND_INVALID_RESULT_ID_INDEX -1 + // Enumerations typedef enum spv_generator_t { diff --git a/source/opcode.cpp b/source/opcode.cpp index de0c817..74e17f6 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -1730,6 +1730,13 @@ int32_t spvOpcodeIsVariable(spv_opcode_desc entry) { (SPV_OPCODE_FLAGS_VARIABLE & entry->flags); } +int16_t spvOpcodeResultIdIndex(spv_opcode_desc entry) { + for (int16_t i = 0; i < entry->wordCount; ++i) { + if (SPV_OPERAND_TYPE_RESULT_ID == entry->operandTypes[i]) return i; + } + return SPV_OPERAND_INVALID_RESULT_ID_INDEX; +} + int32_t spvOpcodeRequiresCapabilities(spv_opcode_desc entry) { return SPV_OPCODE_FLAGS_CAPABILITIES == (SPV_OPCODE_FLAGS_CAPABILITIES & entry->flags); diff --git a/source/opcode.h b/source/opcode.h index 32c225e..cfdf96a 100644 --- a/source/opcode.h +++ b/source/opcode.h @@ -82,6 +82,18 @@ spv_result_t spvOpcodeTableValueLookup(const spv_opcode_table table, /// @return zero if false, non-zero otherwise int32_t spvOpcodeIsVariable(spv_opcode_desc entry); +/// @brief Get the argument index for the operand, if any. +/// +/// @param[in] entry the Opcode entry +/// +/// @return index for the operand, or +/// SPV_OPERAND_INVALID_RESULT_ID_INDEX if the given opcode +/// doesn't have a operand. +// +/// For example, 0 means is the first argument, i.e. right after +/// the wordcount/opcode word. +int16_t spvOpcodeResultIdIndex(spv_opcode_desc entry); + /// @brief Determine if the Opcode has capaspvity requirements /// /// This function does not check if @a entry is valid. diff --git a/source/text.cpp b/source/text.cpp index e2c1d89..f33d828 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -170,6 +170,36 @@ spv_result_t spvTextWordGet(const spv_text text, } } +// Returns true if the string at the given position in text starts with "Op". +static bool spvStartsWithOp(const spv_text text, const spv_position position) { + if (text->length < position->index + 2) return false; + return ('O' == text->str[position->index] && + 'p' == text->str[position->index + 1]); +} + +// Returns true if a new instruction begins at the given position in text. +static bool spvTextIsStartOfNewInst(const spv_text text, + const spv_position position) { + spv_position_t nextPosition = *position; + if (spvTextAdvance(text, &nextPosition)) return false; + if (spvStartsWithOp(text, position)) return true; + + std::string word; + spv_position_t startPosition = nextPosition; + if (spvTextWordGet(text, &startPosition, word, &nextPosition)) return false; + if ('%' != word.front()) return false; + + if (spvTextAdvance(text, &nextPosition)) return false; + startPosition = nextPosition; + if (spvTextWordGet(text, &startPosition, word, &nextPosition)) return false; + if ("=" != word) return false; + + if (spvTextAdvance(text, &nextPosition)) return false; + startPosition = nextPosition; + if (spvStartsWithOp(text, &startPosition)) return true; + return false; +} + spv_result_t spvTextStringGet(const spv_text text, const spv_position startPosition, std::string &string, spv_position endPosition) { @@ -452,20 +482,20 @@ spv_result_t spvTextEncodeOpcode( const spv_operand_table operandTable, const spv_ext_inst_table extInstTable, spv_named_id_table namedIdTable, uint32_t *pBound, spv_instruction_t *pInst, spv_position position, spv_diagnostic *pDiagnostic) { + // An assembly instruction has two possible formats: + // 1. .., e.g., "OpMemoryModel Physical64 OpenCL". + // 2. = .., e.g., "%void = OpTypeVoid". + + // Assume it's the first format at the beginning. std::string opcodeName; spv_position_t nextPosition = {}; - spvCheck(spvTextWordGet(text, position, opcodeName, &nextPosition), - return SPV_ERROR_INTERNAL); + spv_result_t error = + spvTextWordGet(text, position, opcodeName, &nextPosition); + spvCheck(error, return error); + // NOTE: Handle insertion of an immediate integer into the binary stream bool immediate = false; spvCheck('!' == text->str[position->index], immediate = true); - if (!immediate) { - spvCheck('O' != opcodeName[0] || 'p' != opcodeName[1], - DIAGNOSTIC << "Invalid Opcode prefix '" << opcodeName << "'."; - return SPV_ERROR_INVALID_TEXT); - } - - // NOTE: Handle insertion of an immediate integer into the binary stream if (immediate) { const char *begin = opcodeName.data() + 1; char *end = nullptr; @@ -481,12 +511,45 @@ spv_result_t spvTextEncodeOpcode( return SPV_SUCCESS; } + // Handle value generating instructions (the second format above) here. + std::string result_id; + spv_position_t result_id_position = {}; + // If the word we get doesn't start with "Op", assume it's an + // from now. + spvCheck(!spvStartsWithOp(text, position), result_id = opcodeName); + if (!result_id.empty()) { + spvCheck('%' != result_id.front(), + DIAGNOSTIC << "Expected or at the beginning " + "of an instruction, found '" + << result_id << "'."; + return SPV_ERROR_INVALID_TEXT); + result_id_position = *position; + *position = nextPosition; + spvCheck(spvTextAdvance(text, position), + DIAGNOSTIC << "Expected '=', found end of stream."; + return SPV_ERROR_INVALID_TEXT); + // The '=' sign. + std::string equal_sign; + error = spvTextWordGet(text, position, equal_sign, &nextPosition); + spvCheck("=" != equal_sign, DIAGNOSTIC << "'=' expected after result id."; + return SPV_ERROR_INVALID_TEXT); + + // The after the '=' sign. + *position = nextPosition; + spvCheck(spvTextAdvance(text, position), + DIAGNOSTIC << "Expected opcode, found end of stream."; + return SPV_ERROR_INVALID_TEXT); + spvCheck(!spvStartsWithOp(text, position), + DIAGNOSTIC << "Invalid Opcode prefix '" << opcodeName << "'."; + return SPV_ERROR_INVALID_TEXT); + error = spvTextWordGet(text, position, opcodeName, &nextPosition); + } + // NOTE: The table contains Opcode names without the "Op" prefix. const char *pInstName = opcodeName.data() + 2; spv_opcode_desc opcodeEntry; - spv_result_t error = - spvOpcodeTableNameLookup(opcodeTable, pInstName, &opcodeEntry); + error = spvOpcodeTableNameLookup(opcodeTable, pInstName, &opcodeEntry); spvCheck(error, DIAGNOSTIC << "Invalid Opcode name '" << getWord(text->str + position->index) << "'"; return error); @@ -494,10 +557,23 @@ spv_result_t spvTextEncodeOpcode( *position = nextPosition; pInst->wordCount++; + // Get the arugment index for . Used for handling the + // for value generating instructions below. + const int16_t result_id_index = spvOpcodeResultIdIndex(opcodeEntry); + // NOTE: Process the fixed size operands const spv_operand_type_t *extraOperandTypes = nullptr; for (int32_t operandIndex = 0; operandIndex < (opcodeEntry->wordCount - 1); ++operandIndex) { + if (operandIndex == result_id_index && !result_id.empty()) { + // Handling the for value generating instructions. + error = spvTextEncodeOperand( + SPV_OPERAND_TYPE_RESULT_ID, result_id.c_str(), operandTable, + extInstTable, namedIdTable, pInst, &extraOperandTypes, pBound, + &result_id_position, pDiagnostic); + spvCheck(error, return error); + continue; + } spvCheck(spvTextAdvance(text, position), DIAGNOSTIC << "Expected operand, found end of stream."; return SPV_ERROR_INVALID_TEXT); @@ -523,38 +599,33 @@ spv_result_t spvTextEncodeOpcode( opcodeEntry->operandTypes[opcodeEntry->wordCount - 1]; while (!spvTextAdvance(text, position)) { + // NOTE: If this is the end of the current instruction stream and we + // break out of this loop. + if (spvTextIsStartOfNewInst(text, position)) break; + std::string textValue; spvTextWordGet(text, position, textValue, &nextPosition); - // NOTE: Check if the next text word is an Opcode - if ('O' == textValue[0] && 'p' == textValue[1]) { - // NOTE: This is the end of the current instruction stream and we - // break out of this loop - break; + if (SPV_OPERAND_TYPE_LITERAL_STRING == type) { + spvCheck(spvTextAdvance(text, position), + DIAGNOSTIC << "Invalid string, found end of stream."; + return SPV_ERROR_INVALID_TEXT); + + std::string string; + spvCheck(spvTextStringGet(text, position, string, &nextPosition), + DIAGNOSTIC << "Invalid string, new line or end of stream."; + return SPV_ERROR_INVALID_TEXT); + spvCheck(spvTextEncodeOperand(type, string.c_str(), operandTable, + extInstTable, namedIdTable, pInst, + nullptr, pBound, position, pDiagnostic), + return SPV_ERROR_INVALID_TEXT); } else { - if (SPV_OPERAND_TYPE_LITERAL_STRING == type) { - spvCheck(spvTextAdvance(text, position), - DIAGNOSTIC << "Invalid string, found end of stream."; - return SPV_ERROR_INVALID_TEXT); - - std::string string; - spvCheck(spvTextStringGet(text, position, string, &nextPosition), - DIAGNOSTIC << "Invalid string, new line or end of stream."; - return SPV_ERROR_INVALID_TEXT); - spvCheck( - spvTextEncodeOperand(type, string.c_str(), operandTable, - extInstTable, namedIdTable, pInst, nullptr, - pBound, position, pDiagnostic), - return SPV_ERROR_INVALID_TEXT); - } else { - spvCheck( - spvTextEncodeOperand(type, textValue.c_str(), operandTable, - extInstTable, namedIdTable, pInst, nullptr, - pBound, position, pDiagnostic), - return SPV_ERROR_INVALID_TEXT); - } - *position = nextPosition; + spvCheck(spvTextEncodeOperand(type, textValue.c_str(), operandTable, + extInstTable, namedIdTable, pInst, + nullptr, pBound, position, pDiagnostic), + return SPV_ERROR_INVALID_TEXT); } + *position = nextPosition; } } else { // NOTE: Process the variable size operands defined by an immediate diff --git a/test/NamedId.cpp b/test/NamedId.cpp index bae3a91..3822d64 100644 --- a/test/NamedId.cpp +++ b/test/NamedId.cpp @@ -28,15 +28,15 @@ TEST(NamedId, Default) { const char *spirv = R"( -OpCapability Shader -OpMemoryModel Logical Simple -OpEntryPoint Vertex $main -OpTypeVoid %void -OpTypeFunction %fnMain $void -OpFunction $void %main None $fnMain -OpLabel %lbMain -OpReturn -OpFunctionEnd)"; + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Vertex $main + %void = OpTypeVoid +%fnMain = OpTypeFunction $void + %main = OpFunction $void None $fnMain +%lbMain = OpLabel + OpReturn + OpFunctionEnd)"; spv_text_t text; text.str = spirv; text.length = strlen(spirv); diff --git a/test/TextToBinary.cpp b/test/TextToBinary.cpp index 5d98651..f68ca66 100644 --- a/test/TextToBinary.cpp +++ b/test/TextToBinary.cpp @@ -42,26 +42,26 @@ TEST(TextToBinary, Default) { spv_endianness_t endian = SPV_ENDIANNESS_LITTLE; const char *textStr = R"( -OpSource OpenCL 12 -OpMemoryModel Physical64 OpenCL -OpSourceExtension "PlaceholderExtensionName" -OpEntryPoint Kernel $1 -OpExecutionMode $1 LocalSizeHint 1 1 1 -OpTypeVoid %2 -OpTypeBool %3 + OpSource OpenCL 12 + OpMemoryModel Physical64 OpenCL + OpSourceExtension "PlaceholderExtensionName" + OpEntryPoint Kernel $1 + OpExecutionMode $1 LocalSizeHint 1 1 1 +%2 = OpTypeVoid +%3 = OpTypeBool ; commment -OpTypeInt %4 8 0 ; comment -OpTypeInt %5 8 1 -OpTypeInt %6 16 0 -OpTypeInt %7 16 1 -OpTypeInt %8 32 0 -OpTypeInt %9 32 1 -OpTypeInt %10 64 0 -OpTypeInt %11 64 1 -OpTypeFloat %12 16 -OpTypeFloat %13 32 -OpTypeFloat %14 64 -OpTypeVector %15 4 2 +%4 = OpTypeInt 8 0 ; comment +%5 = OpTypeInt 8 1 +%6 = OpTypeInt 16 0 +%7 = OpTypeInt 16 1 +%8 = OpTypeInt 32 0 +%9 = OpTypeInt 32 1 +%10 = OpTypeInt 64 0 +%11 = OpTypeInt 64 1 +%12 = OpTypeFloat 16 +%13 = OpTypeFloat 32 +%14 = OpTypeFloat 64 +%15 = OpTypeVector 4 2 )"; spv_text_t text = {textStr, strlen(textStr)}; @@ -283,20 +283,20 @@ class GLSingleFloatTest TEST_P(GLSingleFloatTest, GLSLExtSingleFloatParamTest) { const std::string spirv = R"( -OpCapability Shader -OpExtInstImport %glsl450 "GLSL.std.450" -OpMemoryModel Logical Simple -OpEntryPoint Vertex $main "main" -OpTypeVoid %void -OpTypeFloat %float 32 -OpConstant $float %const1.5 1.5 -OpTypeFunction %fnMain %void -OpFunction $void %main None $fnMain -OpLabel %lbMain -OpExtInst $float %result $glsl450 )" + + OpCapability Shader + %glsl450 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical Simple + OpEntryPoint Vertex $main "main" + %void = OpTypeVoid + %float = OpTypeFloat 32 +%const1.5 = OpConstant $float 1.5 + %fnMain = OpTypeFunction $void + %main = OpFunction $void None $fnMain + %lbMain = OpLabel + %result = OpExtInst $float $glsl450 )" + std::string(GetParam().inst) + R"( $const1.5 -OpReturn -OpFunctionEnd + OpReturn + OpFunctionEnd )"; this->text.str = spirv.c_str(); @@ -308,7 +308,7 @@ OpFunctionEnd << spirv << std::endl << "Test case for : " << GetParam().inst << std::endl; std::vector expected_contains({ - 12/*OpExtInst*/ | 6 << 16, 4/*%float*/, 9 /*%result*/, 1 /*%glsl450*/, + 12/*OpExtInst*/ | 6 << 16, 4/*%float*/, 8 /*%result*/, 1 /*%glsl450*/, GetParam().value, 5 /*const1.5*/}); EXPECT_TRUE(std::search(this->binary->code, this->binary->code + this->binary->wordCount, @@ -342,3 +342,22 @@ TEST_F(TextToBinaryTest, StringSpace) { spvDiagnosticPrint(diagnostic); } } + +TEST_F(TextToBinaryTest, UnknownBeginningOfInsruction) { + SetText(R"( + OpSource OpenCL 12 + OpMemoryModel Physical64 OpenCL +Google +)"); + + EXPECT_EQ(SPV_ERROR_INVALID_TEXT, + spvTextToBinary(&text, opcodeTable, operandTable, extInstTable, + &binary, &diagnostic)); + EXPECT_EQ(4, diagnostic->position.line + 1); + EXPECT_EQ(1, diagnostic->position.column + 1); + EXPECT_STREQ( + "Expected or at the beginning of an instruction, " + "found 'Google'.", + diagnostic->error); + if (binary) spvBinaryDestroy(binary); +} -- 2.7.4