From 13804e5d63acab8b0cd29a81e0a94a96ec0c8f85 Mon Sep 17 00:00:00 2001 From: Andrew Woloszyn Date: Tue, 22 Sep 2015 15:50:33 -0400 Subject: [PATCH] All values now represent symbolic names instead of mixed with numeric. Also removed un-necessary heap-allocation of spv_named_id_table. This removed the necessity to expose a function to create/destroy it and simplified the interface. --- source/text.cpp | 148 +++++++++++++++++++++++-------------------- source/text.h | 73 --------------------- syntax.md | 14 ++-- test/BinaryToText.cpp | 2 +- test/ExtInstGLSLstd450.cpp | 8 +-- test/ImmediateInt.cpp | 92 +++++++++++++++------------ test/NamedId.cpp | 57 +++++++++++++++++ test/TestFixture.h | 12 +++- test/TextDestroy.cpp | 32 +++++----- test/TextToBinary.Image.cpp | 28 ++++---- test/TextToBinary.Memory.cpp | 2 +- test/TextToBinary.cpp | 2 +- 12 files changed, 241 insertions(+), 229 deletions(-) diff --git a/source/text.cpp b/source/text.cpp index 00b4ca7..ae4596b 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -47,9 +48,7 @@ using spvutils::BitwiseCast; // Structures -struct spv_named_id_table_t { - std::unordered_map namedIds; -}; +using spv_named_id_table = std::unordered_map; // Text API @@ -72,39 +71,12 @@ std::string spvGetWord(const char *str) { return ""; // Make certain compilers happy. } -spv_named_id_table spvNamedIdTableCreate() { - return new spv_named_id_table_t(); -} - -void spvNamedIdTableDestory(spv_named_id_table table) { delete table; } - -uint32_t spvNamedIdAssignOrGet(spv_named_id_table table, const char *textValue, +uint32_t spvNamedIdAssignOrGet(spv_named_id_table* table, const char *textValue, uint32_t *pBound) { - if (table->namedIds.end() == table->namedIds.find(textValue)) { - table->namedIds[textValue] = *pBound; - } - return table->namedIds[textValue]; -} - -int32_t spvTextIsNamedId(const char *textValue) { - // TODO: Strengthen the parsing of textValue to only include allow names that - // match: ([a-z]|[A-Z])(_|[a-z]|[A-Z]|[0-9])* - switch (textValue[0]) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return false; - default: - break; + if (table->end() == table->find(textValue)) { + (*table)[std::string(textValue)] = *pBound; } - return true; + return (*table)[textValue]; } spv_result_t spvTextAdvanceLine(const spv_text text, spv_position position) { @@ -125,6 +97,22 @@ spv_result_t spvTextAdvanceLine(const spv_text text, spv_position position) { } } +bool spvIsValidIDCharacter(const char value) { + return value == '_' || 0 != ::isalnum(value); +} + +// Returns true if the given string represents a valid ID name. +bool spvIsValidID(const char* textValue) { + const char* c = textValue; + for (; *c != '\0'; ++c) { + if (!spvIsValidIDCharacter(*c)) { + return false; + } + } + // If the string was empty, then the ID also is not valid. + return c != textValue; +} + spv_result_t spvTextAdvance(const spv_text text, spv_position position) { // NOTE: Consume white space, otherwise don't advance. switch (text->str[position->index]) { @@ -409,10 +397,25 @@ spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable, return SPV_SUCCESS; } + + +/// @brief Translate an Opcode operand to binary form +/// +/// @param[in] type of the operand +/// @param[in] textValue word of text to be parsed +/// @param[in] operandTable operand lookup table +/// @param[in,out] namedIdTable table of named ID's +/// @param[out] pInst return binary Opcode +/// @param[in,out] pExpectedOperands the operand types expected +/// @param[in,out] pBound current highest defined ID value +/// @param[in] pPosition used in diagnostic on error +/// @param[out] pDiagnostic populated on error +/// +/// @return result code spv_result_t spvTextEncodeOperand( const spv_operand_type_t type, const char *textValue, const spv_operand_table operandTable, const spv_ext_inst_table extInstTable, - spv_named_id_table namedIdTable, spv_instruction_t *pInst, + spv_named_id_table* namedIdTable, spv_instruction_t *pInst, spv_operand_pattern_t *pExpectedOperands, uint32_t *pBound, const spv_position position, spv_diagnostic *pDiagnostic) { // NOTE: Handle immediate int in the stream @@ -430,7 +433,6 @@ spv_result_t spvTextEncodeOperand( position->index += size; pInst->words[pInst->wordCount] = immediateInt; pInst->wordCount += 1; - if (isIdType(type)) *pBound = std::max(*pBound, immediateInt + 1); return SPV_SUCCESS; } @@ -442,22 +444,16 @@ spv_result_t spvTextEncodeOperand( case SPV_OPERAND_TYPE_EXECUTION_SCOPE: { if ('%' == textValue[0]) { textValue++; - } - // TODO: Force all ID's to be prefixed with '%'. - uint32_t id = 0; - if (spvTextIsNamedId(textValue)) { - id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound); } else { - if (spvTextToUInt32(textValue, &id) != SPV_SUCCESS) { - if (spvOperandIsOptional(type)) { - return SPV_FAILED_MATCH; - } else { - DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '" - << textValue << "'."; - return SPV_ERROR_INVALID_TEXT; - } - } + DIAGNOSTIC << "Expected id to start with %."; + return SPV_ERROR_INVALID_TEXT; + } + if (!spvIsValidID(textValue)) { + DIAGNOSTIC << "Invalid ID " << textValue; + return SPV_ERROR_INVALID_TEXT; } + const uint32_t id = + spvNamedIdAssignOrGet(namedIdTable, textValue, pBound); pInst->words[pInst->wordCount++] = id; *pBound = std::max(*pBound, id + 1); } break; @@ -609,7 +605,7 @@ namespace { /// leaves position pointing to the error in text. spv_result_t encodeInstructionStartingWithImmediate( const spv_text text, const spv_operand_table operandTable, - const spv_ext_inst_table extInstTable, spv_named_id_table namedIdTable, + const spv_ext_inst_table extInstTable, spv_named_id_table* namedIdTable, uint32_t *pBound, spv_instruction_t *pInst, spv_position position, spv_diagnostic *pDiagnostic) { std::string firstWord; @@ -684,10 +680,23 @@ spv_result_t encodeInstructionStartingWithImmediate( } // anonymous namespace +/// @brief Translate single Opcode and operands to binary form +/// +/// @param[in] text stream to translate +/// @param[in] format the assembly syntax format of text +/// @param[in] opcodeTable Opcode lookup table +/// @param[in] operandTable operand lookup table +/// @param[in,out] namedIdTable table of named ID's +/// @param[in,out] pBound current highest defined ID value +/// @param[out] pInst returned binary Opcode +/// @param[in,out] pPosition in the text stream +/// @param[out] pDiagnostic populated on failure +/// +/// @return result code spv_result_t spvTextEncodeOpcode( const spv_text text, spv_assembly_syntax_format_t format, const spv_opcode_table opcodeTable, const spv_operand_table operandTable, - const spv_ext_inst_table extInstTable, spv_named_id_table namedIdTable, + const spv_ext_inst_table extInstTable, spv_named_id_table* namedIdTable, uint32_t *pBound, spv_instruction_t *pInst, spv_position position, spv_diagnostic *pDiagnostic) { @@ -898,29 +907,28 @@ spv_result_t spvTextToBinaryInternal(const spv_text text, return SPV_ERROR_INVALID_TEXT; } - spv_named_id_table namedIdTable = spvNamedIdTableCreate(); - if (!namedIdTable) return SPV_ERROR_OUT_OF_MEMORY; + // This causes namedIdTable to get cleaned up as soon as it is no + // longer necessary. + { + spv_named_id_table namedIdTable; + spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE; + while (text->length > position.index) { + spv_instruction_t inst = {}; + inst.extInstType = extInstType; + + if (spvTextEncodeOpcode(text, format, opcodeTable, operandTable, + extInstTable, &namedIdTable, &bound, &inst, + &position, pDiagnostic)) { + return SPV_ERROR_INVALID_TEXT; + } + extInstType = inst.extInstType; - spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE; - while (text->length > position.index) { - spv_instruction_t inst = {}; - inst.extInstType = extInstType; + instructions.push_back(inst); - if (spvTextEncodeOpcode(text, format, opcodeTable, operandTable, - extInstTable, namedIdTable, &bound, &inst, - &position, pDiagnostic)) { - spvNamedIdTableDestory(namedIdTable); - return SPV_ERROR_INVALID_TEXT; + if (spvTextAdvance(text, &position)) break; } - extInstType = inst.extInstType; - - instructions.push_back(inst); - - if (spvTextAdvance(text, &position)) break; } - spvNamedIdTableDestory(namedIdTable); - size_t totalSize = SPV_INDEX_INSTRUCTION; for (auto &inst : instructions) { totalSize += inst.wordCount; diff --git a/source/text.h b/source/text.h index 320e785..2589c3d 100644 --- a/source/text.h +++ b/source/text.h @@ -58,11 +58,6 @@ typedef struct spv_literal_t { } value; } spv_literal_t; -struct spv_named_id_table_t; - -// Types - -typedef spv_named_id_table_t *spv_named_id_table; // Functions @@ -151,33 +146,6 @@ spv_result_t spvTextToUInt32(const char *textValue, uint32_t *pValue); /// @return result code spv_result_t spvTextToLiteral(const char *textValue, spv_literal_t *pLiteral); -/// @brief Create a named ID table -/// -/// @return named ID table -spv_named_id_table spvNamedIdTableCreate(); - -/// @brief Free a named ID table -/// -/// @param table named ID table -void spvNamedIdTableDestory(spv_named_id_table table); - -/// @brief Lookup or assign a named ID -/// -/// @param table named ID table -/// @param textValue name value -/// @param pBound upper ID bound, used for assigning new ID's -/// -/// @return the new ID assossiated with the named ID -uint32_t spvNamedIdAssignOrGet(spv_named_id_table table, const char *textValue, - uint32_t *pBound); - -/// @brief Determine if a name has an assossiated ID -/// -/// @param textValue name value -/// -/// @return zero on failure, non-zero otherwise -int32_t spvTextIsNamedId(const char *textValue); - /// @brief Parses a mask expression string for the given operand type. /// /// A mask expression is a sequence of one or more terms separated by '|', @@ -195,45 +163,4 @@ int32_t spvTextIsNamedId(const char *textValue); spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable, const spv_operand_type_t type, const char *textValue, uint32_t *pValue); - -/// @brief Translate an Opcode operand to binary form -/// -/// @param[in] type of the operand -/// @param[in] textValue word of text to be parsed -/// @param[in] operandTable operand lookup table -/// @param[in,out] namedIdTable table of named ID's -/// @param[out] pInst return binary Opcode -/// @param[in,out] pExpectedOperands the operand types expected -/// @param[in,out] pBound current highest defined ID value -/// @param[in] pPosition used in diagnostic on error -/// @param[out] pDiagnostic populated on error -/// -/// @return result code -spv_result_t spvTextEncodeOperand( - const spv_operand_type_t type, const char *textValue, - const spv_operand_table operandTable, const spv_ext_inst_table extInstTable, - spv_named_id_table namedIdTable, spv_instruction_t *pInst, - spv_operand_pattern_t *pExpectedOperands, uint32_t *pBound, - const spv_position_t *pPosition, spv_diagnostic *pDiagnostic); - -/// @brief Translate single Opcode and operands to binary form -/// -/// @param[in] text stream to translate -/// @param[in] format the assembly syntax format of text -/// @param[in] opcodeTable Opcode lookup table -/// @param[in] operandTable operand lookup table -/// @param[in,out] namedIdTable table of named ID's -/// @param[in,out] pBound current highest defined ID value -/// @param[out] pInst returned binary Opcode -/// @param[in,out] pPosition in the text stream -/// @param[out] pDiagnostic populated on failure -/// -/// @return result code -spv_result_t spvTextEncodeOpcode( - const spv_text text, spv_assembly_syntax_format_t format, - const spv_opcode_table opcodeTable, 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_t *pPosition, - spv_diagnostic *pDiagnostic); - #endif diff --git a/syntax.md b/syntax.md index f36849a..2bc3e69 100644 --- a/syntax.md +++ b/syntax.md @@ -75,12 +75,12 @@ An ID definition pertains to the `` of an OpCode, and ID usage is any input to an OpCode. All IDs are prefixed with `%`. To differentiate between defs and uses, we suggest using the second format shown in the above example. -## Named IDs - -The assembler also supports named IDs, or virtual IDs, which greatly improves -the readability of the assembly. The same ID definition and usage prefixes -apply. Names must begin with an character in the range `[a-z|A-Z]`. The -following example will result in identical SPIR-V binary as the example above. +The ID names do not necessarily have to be numerical. Furthermore to avoid +aliasing names, if a name is numerical, it will not necessarily map to the +corresponding numerical id in the generated spirv. The same ID definition and +usage prefixes apply. Names may contain any character in +['0-9|a-z|A-Z|\_'] The following example will result in identical SPIR-V binary +as the example above. ``` OpCapability Shader @@ -95,6 +95,8 @@ following example will result in identical SPIR-V binary as the example above. OpFunctionEnd ``` + + ## Arbitrary Integers diff --git a/test/BinaryToText.cpp b/test/BinaryToText.cpp index 6fc0b8b..a3bdc33 100644 --- a/test/BinaryToText.cpp +++ b/test/BinaryToText.cpp @@ -57,7 +57,7 @@ class BinaryToText : public ::testing::Test { %12 = OpTypeFloat 16 %13 = OpTypeFloat 32 %14 = OpTypeFloat 64 -%15 = OpTypeVector 4 2 +%15 = OpTypeVector %4 2 )"; spv_text_t text = {textStr, strlen(textStr)}; spv_diagnostic diagnostic = nullptr; diff --git a/test/ExtInstGLSLstd450.cpp b/test/ExtInstGLSLstd450.cpp index f0cc3d4..09dcf56 100644 --- a/test/ExtInstGLSLstd450.cpp +++ b/test/ExtInstGLSLstd450.cpp @@ -69,8 +69,8 @@ OpEntryPoint Vertex %2 "main" "\n" + std::string(GetParam().constGenInst) + R"( %6 = OpTypeFunction %3 %2 = OpFunction %3 None %6 -%8 = OpLabel -%9 = OpExtInst )" + GetParam().extInstRetType + +%7 = OpLabel +%8 = OpExtInst )" + GetParam().extInstRetType + " %1 " + GetParam().extInstOpName + " " + GetParam().extInstOperandVars + R"( OpReturn @@ -80,7 +80,7 @@ OpFunctionEnd R"(; SPIR-V ; Version: 99 ; Generator: Khronos -; Bound: 10 +; Bound: 9 ; Schema: 0)"; spv_binary binary; spv_diagnostic diagnostic; @@ -100,7 +100,7 @@ OpFunctionEnd // the generated SPIR-V binary. std::vector expected_contains( {12 /*OpExtInst*/ | GetParam().extInstLength << 16, 4 /*return type*/, - 9 /*result id*/, 1 /*glsl450 import*/, GetParam().extInstOpcode}); + 8 /*result id*/, 1 /*glsl450 import*/, GetParam().extInstOpcode}); for (uint32_t operand : GetParam().extInstOperandIds) { expected_contains.push_back(operand); } diff --git a/test/ImmediateInt.cpp b/test/ImmediateInt.cpp index 35589e2..bcc5103 100644 --- a/test/ImmediateInt.cpp +++ b/test/ImmediateInt.cpp @@ -34,8 +34,10 @@ namespace { +using spvtest::MakeInstruction; using spvtest::TextToBinaryTest; using ::testing::ElementsAre; +using ::testing::Eq; using ::testing::HasSubstr; using ::testing::StrEq; @@ -66,27 +68,34 @@ TEST_F(TextToBinaryTest, ImmediateIntOperand) { using ImmediateIntTest = TextToBinaryTest; TEST_F(ImmediateIntTest, AnyWordInSimpleStatement) { - const SpirvVector original = - CompileSuccessfully("OpConstant %1 %2 123", kCAF); // TODO(deki): uncomment assertions below and make them pass. - EXPECT_EQ(original, CompileSuccessfully("!0x0004002B %1 %2 123", kCAF)); - EXPECT_EQ(original, CompileSuccessfully("OpConstant !1 %2 123", kCAF)); - EXPECT_EQ(original, CompileSuccessfully("OpConstant %1 !2 123", kCAF)); - EXPECT_EQ(original, CompileSuccessfully("OpConstant %1 %2 !123", kCAF)); + EXPECT_THAT(CompiledInstructions("!0x0004002B %a %b 123", kCAF), + Eq(MakeInstruction(spv::OpConstant, {1, 2, 123}))); + EXPECT_THAT(CompiledInstructions("OpConstant !1 %b 123", kCAF), + Eq(MakeInstruction(spv::OpConstant, {1, 1, 123}))); + EXPECT_THAT(CompiledInstructions("OpConstant %1 !2 123", kCAF), + Eq(MakeInstruction(spv::OpConstant, {1, 2, 123}))); + EXPECT_THAT(CompiledInstructions("OpConstant %a %b !123", kCAF), + Eq(MakeInstruction(spv::OpConstant, {1, 2, 123}))); // EXPECT_EQ(original, CompileSuccessfully("!0x0004002B %1 !2 123", kCAF)); - EXPECT_EQ(original, CompileSuccessfully("OpConstant !1 %2 !123", kCAF)); + EXPECT_THAT(CompiledInstructions("OpConstant !1 %b !123", kCAF), + Eq(MakeInstruction(spv::OpConstant, {1, 1, 123}))); // EXPECT_EQ(original, CompileSuccessfully("!0x0004002B !1 !2 !123", kCAF)); } TEST_F(ImmediateIntTest, AnyWordAfterEqualsAndOpCode) { - const SpirvVector original = - CompileSuccessfully("%2 = OpArrayLength %12 %1 123"); - EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength !12 %1 123")); - EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength %12 !1 123")); - EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength %12 %1 !123")); - EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength %12 !1 !123")); - EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength !12 !1 123")); - EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength !12 !1 !123")); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 %c 123"), + Eq(MakeInstruction(spv::OpArrayLength, {2, 1, 2, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 123"), + Eq(MakeInstruction(spv::OpArrayLength, {1, 2, 3, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b %c !123"), + Eq(MakeInstruction(spv::OpArrayLength, {1, 2, 3, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 !123"), + Eq(MakeInstruction(spv::OpArrayLength, {1, 2, 3, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 123"), + Eq(MakeInstruction(spv::OpArrayLength, {2, 1, 3, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 !123"), + Eq(MakeInstruction(spv::OpArrayLength, {2, 1, 3, 123}))); } TEST_F(ImmediateIntTest, ResultIdInAssignment) { @@ -103,19 +112,19 @@ TEST_F(ImmediateIntTest, OpCodeInAssignment) { // Literal integers after ! are handled correctly. TEST_F(ImmediateIntTest, IntegerFollowingImmediate) { - const SpirvVector original = CompileSuccessfully( + const SpirvVector original = CompiledInstructions( "OpTypeInt %1 8 1", kCAF); // TODO(deki): uncomment assertions below and make them pass. // EXPECT_EQ(original, CompileSuccessfully("!0x00040015 1 8 1", kCAF)); - EXPECT_EQ(original, CompileSuccessfully("OpTypeInt !1 8 1", kCAF)); + EXPECT_EQ(original, CompiledInstructions("OpTypeInt !1 8 1", kCAF)); // 64-bit integer literal. - EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 5000000000", kCAF), - CompileSuccessfully("OpConstant %10 !1 5000000000", kCAF)); + EXPECT_EQ(CompiledInstructions("OpConstant %10 %2 5000000000", kCAF), + CompiledInstructions("OpConstant %10 !2 5000000000", kCAF)); // Negative integer. - EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 -123", kCAF), - CompileSuccessfully("OpConstant %10 !1 -123", kCAF)); + EXPECT_EQ(CompiledInstructions("OpConstant %10 %2 -123", kCAF), + CompiledInstructions("OpConstant %10 !2 -123", kCAF)); // Hex value(s). // EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 0x12345678", kCAF), @@ -127,16 +136,16 @@ TEST_F(ImmediateIntTest, IntegerFollowingImmediate) { // Literal floats after ! are handled correctly. TEST_F(ImmediateIntTest, FloatFollowingImmediate) { - EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 0.123", kCAF), - CompileSuccessfully("OpConstant %10 !1 0.123", kCAF)); - EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 -0.5", kCAF), - CompileSuccessfully("OpConstant %10 !1 -0.5", kCAF)); + EXPECT_EQ(CompiledInstructions("OpConstant %10 %2 0.123", kCAF), + CompiledInstructions("OpConstant %10 !2 0.123", kCAF)); + EXPECT_EQ(CompiledInstructions("OpConstant %10 %2 -0.5", kCAF), + CompiledInstructions("OpConstant %10 !2 -0.5", kCAF)); // 64-bit float. EXPECT_EQ( - CompileSuccessfully( - "OpConstant %10 %1 9999999999999999999999999999999999999999.9", kCAF), - CompileSuccessfully( - "OpConstant %10 !1 9999999999999999999999999999999999999999.9", + CompiledInstructions( + "OpConstant %10 %2 9999999999999999999999999999999999999999.9", kCAF), + CompiledInstructions( + "OpConstant %10 !2 9999999999999999999999999999999999999999.9", kCAF)); } @@ -145,17 +154,18 @@ TEST_F(ImmediateIntTest, StringFollowingImmediate) { // Try a variety of strings, including empty and single-character. for (std::string name : {"", "s", "longish", "really looooooooooooooooong"}) { const SpirvVector original = - CompileSuccessfully("OpMemberName %10 4 \"" + name + "\"", kCAF); - EXPECT_EQ(original, - CompileSuccessfully("OpMemberName %10 !4 \"" + name + "\"", kCAF)) + CompiledInstructions("OpMemberName %10 4 \"" + name + "\"", kCAF); + EXPECT_EQ(original, CompiledInstructions( + "OpMemberName %10 !4 \"" + name + "\"", kCAF)) << name; EXPECT_EQ(original, - CompileSuccessfully("OpMemberName !10 !4 \"" + name + "\"", kCAF)) + CompiledInstructions("OpMemberName !1 !4 \"" + name + "\"", kCAF)) << name; const uint32_t wordCount = 4 + name.size() / 4; const uint32_t firstWord = spvOpcodeMake(wordCount, spv::OpMemberName); - EXPECT_EQ(original, CompileSuccessfully("!" + std::to_string(firstWord) + - " %10 !4 \"" + name + "\"", kCAF)) + EXPECT_EQ(original, CompiledInstructions("!" + std::to_string(firstWord) + + " %10 !4 \"" + name + "\"", + kCAF)) << name; } } @@ -171,9 +181,9 @@ TEST_F(ImmediateIntTest, IdFollowingImmediate) { // ! after ! is handled correctly. TEST_F(ImmediateIntTest, ImmediateFollowingImmediate) { const SpirvVector original = - CompileSuccessfully("OpTypeMatrix %11 %10 7", kCAF); - EXPECT_EQ(original, CompileSuccessfully("OpTypeMatrix %11 !10 !7", kCAF)); - EXPECT_EQ(original, CompileSuccessfully("!0x00040018 %11 !10 !7", kCAF)); + CompiledInstructions("OpTypeMatrix %a %b 7", kCAF); + EXPECT_EQ(original, CompiledInstructions("OpTypeMatrix %a !2 !7", kCAF)); + EXPECT_EQ(original, CompiledInstructions("!0x00040018 %a !2 !7", kCAF)); } TEST_F(ImmediateIntTest, InvalidStatement) { @@ -186,7 +196,7 @@ TEST_F(ImmediateIntTest, InvalidStatementBetweenValidOnes) { EXPECT_THAT(Subvector(CompileSuccessfully( "OpTypeFloat %10 32 !5 !6 !7 OpEmitVertex", kCAF), kFirstInstruction), - ElementsAre(spvOpcodeMake(3, spv::OpTypeFloat), 10, 32, 5, 6, 7, + ElementsAre(spvOpcodeMake(3, spv::OpTypeFloat), 1, 32, 5, 6, 7, spvOpcodeMake(1, spv::OpEmitVertex))); } @@ -224,11 +234,11 @@ OpCopyMemorySized %3 %4 %1 TEST_F(ImmediateIntTest, NextAssignmentRecognized) { const SpirvVector original = CompileSuccessfully(R"( %1 = OpLoad %10 %2 None -%4 = OpFunctionCall %10 %3 123 +%4 = OpFunctionCall %10 %3 %123 )"); const SpirvVector alternate = CompileSuccessfully(R"( %1 = OpLoad %10 %2 !0 -%4 = OpFunctionCall %10 %3 123 +%4 = OpFunctionCall %10 %3 %123 )"); EXPECT_EQ(original, alternate); } diff --git a/test/NamedId.cpp b/test/NamedId.cpp index cc88c72..ea20918 100644 --- a/test/NamedId.cpp +++ b/test/NamedId.cpp @@ -25,6 +25,9 @@ // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. #include "UnitSPIRV.h" +#include "TestFixture.h" + +#include namespace { @@ -72,4 +75,58 @@ TEST(NamedId, Default) { spvBinaryDestroy(binary); } +struct IdCheckCase { + std::string id; + bool valid; +}; + +using IdValidityTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(IdValidityTest, IdTypes) { + std::string input = GetParam().id + " = OpTypeVoid"; + SetText(input); + if (GetParam().valid) { + CompileSuccessfully(input); + } else { + CompileFailure(input); + } +} + +INSTANTIATE_TEST_CASE_P( + ValidAndInvalidIds, IdValidityTest, + ::testing::ValuesIn(std::vector({{"%1", true}, + {"%2abc", true}, + {"%3Def", true}, + {"%4GHI", true}, + {"%5_j_k", true}, + {"%6J_M", true}, + {"%n", true}, + {"%O", true}, + {"%p7", true}, + {"%Q8", true}, + {"%R_S", true}, + {"%T_10_U", true}, + {"%V_11", true}, + {"%W_X_13", true}, + {"%_A", true}, + {"%_", true}, + {"%__", true}, + {"%A_", true}, + {"%_A_", true}, + + {"%@", false}, + {"%!", false}, + {"%ABC!", false}, + {"%__A__@", false}, + {"%%", false}, + {"%-", false}, + {"%foo_@_bar", false}, + {"%", false}, + + {"5", false}, + {"32", false}, + {"foo", false}, + {"a%bar", false}}))); + } // anonymous namespace diff --git a/test/TestFixture.h b/test/TestFixture.h index 8d27ab4..7bb5a85 100644 --- a/test/TestFixture.h +++ b/test/TestFixture.h @@ -129,8 +129,9 @@ class TextToBinaryTestBase : public T { // Compiles SPIR-V text, asserts success, and returns the words representing // the instructions. In particular, skip the words in the SPIR-V header. - SpirvVector CompiledInstructions(const std::string& text) { - const SpirvVector code = CompileSuccessfully(text); + SpirvVector CompiledInstructions(const std::string& text, + spv_assembly_syntax_format_t format) { + const SpirvVector code = CompileSuccessfully(text, format); SpirvVector result; // Extract just the instructions. // If the code fails to compile, then return the empty vector. @@ -140,6 +141,13 @@ class TextToBinaryTestBase : public T { return result; } + // Compiles SPIR-V text with the default assembly format, asserts success, and + // returns the words representing the instructions. In particular, skip the + // words in the SPIR-V header. + SpirvVector CompiledInstructions(const std::string& text) { + return CompiledInstructions(text, SPV_ASSEMBLY_SYNTAX_FORMAT_DEFAULT); + } + void SetText(const std::string& code) { textString = code; text.str = textString.c_str(); diff --git a/test/TextDestroy.cpp b/test/TextDestroy.cpp index e1e99b7..892e3fd 100644 --- a/test/TextDestroy.cpp +++ b/test/TextDestroy.cpp @@ -44,22 +44,22 @@ TEST(TextDestroy, Default) { OpSource OpenCL 12 OpMemoryModel Physical64 OpenCL OpSourceExtension "PlaceholderExtensionName" - OpEntryPoint Kernel 0 "" - OpExecutionMode 0 LocalSizeHint 1 1 1 - OpTypeVoid 1 - OpTypeBool 2 - OpTypeInt 3 8 0 - OpTypeInt 4 8 1 - OpTypeInt 5 16 0 - OpTypeInt 6 16 1 - OpTypeInt 7 32 0 - OpTypeInt 8 32 1 - OpTypeInt 9 64 0 - OpTypeInt 10 64 1 - OpTypeFloat 11 16 - OpTypeFloat 12 32 - OpTypeFloat 13 64 - OpTypeVector 14 3 2 + OpEntryPoint Kernel %0 "" + OpExecutionMode %0 LocalSizeHint 1 1 1 + OpTypeVoid %1 + OpTypeBool %2 + OpTypeInt %3 8 0 + OpTypeInt %4 8 1 + OpTypeInt %5 16 0 + OpTypeInt %6 16 1 + OpTypeInt %7 32 0 + OpTypeInt %8 32 1 + OpTypeInt %9 64 0 + OpTypeInt %10 64 1 + OpTypeFloat %11 16 + OpTypeFloat %12 32 + OpTypeFloat %13 64 + OpTypeVector %14 %3 2 )"; spv_binary binary = nullptr; diff --git a/test/TextToBinary.Image.cpp b/test/TextToBinary.Image.cpp index b6ecdfc..d20f8ed 100644 --- a/test/TextToBinary.Image.cpp +++ b/test/TextToBinary.Image.cpp @@ -70,12 +70,12 @@ INSTANTIATE_TEST_CASE_P( {"", {}}, // Test each kind, alone. {"Bias %5", {MASK(Bias), 5}}, - {"Lod %10", {MASK(Lod), 10}}, - {"Grad %11 %12", {MASK(Grad), 11, 12}}, - {"ConstOffset %13", {MASK(ConstOffset), 13}}, - {"Offset %14", {MASK(Offset), 14}}, - {"ConstOffsets %15", {MASK(ConstOffsets), 15}}, - {"Sample %16", {MASK(Sample), 16}}, + {"Lod %10", {MASK(Lod), 5}}, + {"Grad %11 %12", {MASK(Grad), 5, 6}}, + {"ConstOffset %13", {MASK(ConstOffset), 5}}, + {"Offset %14", {MASK(Offset), 5}}, + {"ConstOffsets %15", {MASK(ConstOffsets), 5}}, + {"Sample %16", {MASK(Sample), 5}}, })); #undef MASK #define MASK(NAME) static_cast(spv::ImageOperands##NAME##Mask) @@ -85,28 +85,28 @@ INSTANTIATE_TEST_CASE_P( // TODO(dneto): Rev32 adds many more values, and rearranges their // values. // Test adjacent pairs, so we can easily debug the values when it fails. - {"Bias|Lod %10 %11", {MASK(Bias) | MASK(Lod), 10, 11}}, - {"Lod|Grad %12 %13 %14", {MASK(Lod) | MASK(Grad), 12, 13, 14}}, + {"Bias|Lod %10 %11", {MASK(Bias) | MASK(Lod), 5, 6}}, + {"Lod|Grad %12 %13 %14", {MASK(Lod) | MASK(Grad), 5, 6, 7}}, {"Grad|ConstOffset %15 %16 %17", - {MASK(Grad) | MASK(ConstOffset), 15, 16, 17}}, + {MASK(Grad) | MASK(ConstOffset), 5, 6, 7}}, {"ConstOffset|Offset %18 %19", - {MASK(ConstOffset) | MASK(Offset), 18, 19}}, + {MASK(ConstOffset) | MASK(Offset), 5, 6}}, {"Offset|ConstOffsets %20 %21", - {MASK(Offset) | MASK(ConstOffsets), 20, 21}}, + {MASK(Offset) | MASK(ConstOffsets), 5, 6}}, {"ConstOffsets|Sample %22 %23", - {MASK(ConstOffsets) | MASK(Sample), 22, 23}}, + {MASK(ConstOffsets) | MASK(Sample), 5, 6}}, // Test all masks together. {"Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample" " %5 %10 %11 %12 %13 %14 %15 %16", {MASK(Bias) | MASK(Lod) | MASK(Grad) | MASK(ConstOffset) | MASK(Offset) | MASK(ConstOffsets) | MASK(Sample), - 5, 10, 11, 12, 13, 14, 15, 16}}, + 5, 6, 7, 8, 9, 10, 11, 12}}, // The same, but with mask value names reversed. {"Sample|ConstOffsets|Offset|ConstOffset|Grad|Lod|Bias" " %5 %10 %11 %12 %13 %14 %15 %16", {MASK(Bias) | MASK(Lod) | MASK(Grad) | MASK(ConstOffset) | MASK(Offset) | MASK(ConstOffsets) | MASK(Sample), - 5, 10, 11, 12, 13, 14, 15, 16}}})); + 5, 6, 7, 8, 9, 10, 11, 12}}})); #undef MASK } // anonymous namespace diff --git a/test/TextToBinary.Memory.cpp b/test/TextToBinary.Memory.cpp index 2261097..df53b43 100644 --- a/test/TextToBinary.Memory.cpp +++ b/test/TextToBinary.Memory.cpp @@ -81,7 +81,7 @@ using StorageClassTest = spvtest::TextToBinaryTestBase< TEST_P(StorageClassTest, AnyStorageClass) { std::string input = "%1 = OpVariable %2 " + GetParam().name(); EXPECT_THAT(CompiledInstructions(input), - Eq(MakeInstruction(spv::OpVariable, {2, 1, GetParam().value()}))); + Eq(MakeInstruction(spv::OpVariable, {1, 2, GetParam().value()}))); } // clang-format off diff --git a/test/TextToBinary.cpp b/test/TextToBinary.cpp index c1446a6..f1823fa 100644 --- a/test/TextToBinary.cpp +++ b/test/TextToBinary.cpp @@ -152,7 +152,7 @@ TEST(TextToBinary, Default) { %12 = OpTypeFloat 16 %13 = OpTypeFloat 32 %14 = OpTypeFloat 64 -%15 = OpTypeVector 4 2 +%15 = OpTypeVector %4 2 )"; spv_opcode_table opcodeTable; -- 2.7.4