From: David Neto Date: Wed, 16 Sep 2015 22:32:54 +0000 (-0400) Subject: Assembler support for simple mask expressions X-Git-Tag: upstream/2018.6~1511^2~131 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=36b0c0f6b3a350c73c9ca79244e420d424b670a2;p=platform%2Fupstream%2FSPIRV-Tools.git Assembler support for simple mask expressions For example, support combining mask enums with "|", such as "NotNaN|AllowRecip" for the fast math mode. This is supported for mask values that don't modify the expected operand pattern: - fast math mode - function control - loop control - selection control TODO: disassembler support to print them as mask expressions. --- diff --git a/source/text.cpp b/source/text.cpp index 5776b1b..d44950a 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -24,25 +24,24 @@ // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +#include "text.h" + #include #include #include #include +#include #include +#include +#include -#include - -#include "bitwisecast.h" #include "binary.h" +#include "bitwisecast.h" #include "diagnostic.h" #include "ext_inst.h" +#include #include "opcode.h" #include "operand.h" -#include "text.h" - -#include -#include -#include using spvutils::BitwiseCast; @@ -375,6 +374,41 @@ bool isIdType(spv_operand_type_t type) { } // anonymous namespace +spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable, + const spv_operand_type_t type, + const char *textValue, uint32_t *pValue) { + if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT; + size_t text_length = strlen(textValue); + if (text_length == 0) return SPV_ERROR_INVALID_TEXT; + const char *text_end = textValue + text_length; + + // We only support mask expressions in ASCII, so the separator value is a + // char. + const char separator = '|'; + + // Accumulate the result by interpreting one word at a time, scanning + // from left to right. + uint32_t value = 0; + const char *begin = textValue; // The left end of the current word. + const char *end = nullptr; // One character past the end of the current word. + do { + end = std::find(begin, text_end, separator); + + spv_operand_desc entry = nullptr; + if (spvOperandTableNameLookup(operandTable, type, begin, end - begin, + &entry)) { + return SPV_ERROR_INVALID_TEXT; + } + value |= entry->value; + + // Advance to the next word by skipping over the separator. + begin = end + 1; + } while (end != text_end); + + *pValue = value; + return SPV_SUCCESS; +} + spv_result_t spvTextEncodeOperand( const spv_operand_type_t type, const char *textValue, const spv_operand_table operandTable, const spv_ext_inst_table extInstTable, @@ -535,6 +569,20 @@ spv_result_t spvTextEncodeOperand( case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: assert(0 && " Handle optional optional image operands"); break; + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: { + uint32_t value; + if (spvTextParseMaskOperand(operandTable, type, textValue, &value)) { + DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '" << textValue + << "'."; + return SPV_ERROR_INVALID_TEXT; + } + if (auto error = spvBinaryEncodeU32(value, pInst, position, pDiagnostic)) + return error; + // TODO(dneto): So far, masks don't modify the expected operand pattern. + } break; default: { // NOTE: All non literal operands are handled here using the operand // table. diff --git a/source/text.h b/source/text.h index f7b9240..320e785 100644 --- a/source/text.h +++ b/source/text.h @@ -178,6 +178,24 @@ uint32_t spvNamedIdAssignOrGet(spv_named_id_table table, const char *textValue, /// @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 '|', +/// where each term a named enum value for the given type. No whitespace +/// is permitted. +/// +/// On success, the value is written to pValue. +/// +/// @param[in] operandTable operand lookup table +/// @param[in] type of the operand +/// @param[in] textValue word of text to be parsed +/// @param[out] pValue where the resulting value is written +/// +/// @return result code +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 diff --git a/test/TextToBinary.Annotation.cpp b/test/TextToBinary.Annotation.cpp index 8087379..554dcb8 100644 --- a/test/TextToBinary.Annotation.cpp +++ b/test/TextToBinary.Annotation.cpp @@ -39,6 +39,7 @@ namespace { using spvtest::MakeInstruction; using spvtest::MakeVector; using ::testing::Eq; +using test_fixture::TextToBinaryTest; // Test OpDecorate @@ -236,6 +237,19 @@ INSTANTIATE_TEST_CASE_P(TextToBinaryDecorateFPFastMathMode, OpDecorateEnumTest, #undef CASE // clang-format on +TEST_F(TextToBinaryTest, CombinedFPFastMathMask) { + // Sample a single combination. This ensures we've integrated + // the instruction parsing logic with spvTextParseMask. + const std::string input = "OpDecorate %1 FPFastMathMode NotNaN|NotInf|NSZ"; + const uint32_t expected_enum = spv::DecorationFPFastMathMode; + const uint32_t expected_mask = spv::FPFastMathModeNotNaNMask | + spv::FPFastMathModeNotInfMask | + spv::FPFastMathModeNSZMask; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(spv::OpDecorate, {1, expected_enum, expected_mask}))); +} + // Test OpDecorate Linkage // A single test case for a linkage diff --git a/test/TextToBinary.ControlFlow.cpp b/test/TextToBinary.ControlFlow.cpp index ca0243e..4ff33b7 100644 --- a/test/TextToBinary.ControlFlow.cpp +++ b/test/TextToBinary.ControlFlow.cpp @@ -36,6 +36,7 @@ namespace { using spvtest::MakeInstruction; using ::testing::Eq; +using test_fixture::TextToBinaryTest; // An example case for an enumerated value. template @@ -67,7 +68,13 @@ INSTANTIATE_TEST_CASE_P(TextToBinarySelectionMerge, OpSelectionMergeTest, #undef CASE // clang-format on -// TODO(dneto): Combination of selection control masks. +TEST_F(OpSelectionMergeTest, CombinedSelectionControlMask) { + const std::string input = "OpSelectionMerge %1 Flatten|DontFlatten"; + const uint32_t expected_mask = + spv::SelectionControlFlattenMask | spv::SelectionControlDontFlattenMask; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(spv::OpSelectionMerge, {1, expected_mask}))); +} // Test OpLoopMerge @@ -91,7 +98,13 @@ INSTANTIATE_TEST_CASE_P(TextToBinaryLoopMerge, OpLoopMergeTest, #undef CASE // clang-format on -// TODO(dneto): Combination of loop control masks. +TEST_F(OpLoopMergeTest, CombinedLoopControlMask) { + const std::string input = "OpLoopMerge %1 Unroll|DontUnroll"; + const uint32_t expected_mask = + spv::LoopControlUnrollMask | spv::LoopControlDontUnrollMask; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(spv::OpLoopMerge, {1, expected_mask}))); +} // TODO(dneto): OpPhi // TODO(dneto): OpLoopMerge diff --git a/test/TextToBinary.Function.cpp b/test/TextToBinary.Function.cpp index 7deb2fb..cebbf19 100644 --- a/test/TextToBinary.Function.cpp +++ b/test/TextToBinary.Function.cpp @@ -36,6 +36,7 @@ namespace { using spvtest::MakeInstruction; using ::testing::Eq; +using test_fixture::TextToBinaryTest;; // An example case for an enumerated value. template @@ -70,7 +71,17 @@ INSTANTIATE_TEST_CASE_P(TextToBinaryFunctionTest, OpFunctionControlTest, #undef CASE // clang-format on -// TODO(dneto): Combination of function control masks. +TEST_F(TextToBinaryTest, CombinedFunctionControlMask) { + // Sample a single combination. This ensures we've integrated + // the instruction parsing logic with spvTextParseMask. + const std::string input = + "%result_id = OpFunction %result_type Inline|Pure|Const %function_type"; + const uint32_t expected_mask = spv::FunctionControlInlineMask | + spv::FunctionControlPureMask | + spv::FunctionControlConstMask; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(spv::OpFunction, {1, 2, expected_mask, 3}))); +} // TODO(dneto): OpFunctionParameter // TODO(dneto): OpFunctionEnd diff --git a/test/TextToBinary.cpp b/test/TextToBinary.cpp index 48ed58e..cbad557 100644 --- a/test/TextToBinary.cpp +++ b/test/TextToBinary.cpp @@ -51,6 +51,76 @@ TEST(GetWord, Simple) { EXPECT_EQ("abc", spvGetWord("abc\n")); } +// An mask parsing test case. +struct MaskCase { + const spv_operand_type_t which_enum; + const uint32_t expected_value; + const char* expression; +}; + +using GoodMaskParseTest = ::testing::TestWithParam; + +TEST_P(GoodMaskParseTest, GoodMaskExpressions) { + spv_operand_table operandTable; + ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&operandTable)); + + uint32_t value; + EXPECT_EQ(SPV_SUCCESS, + spvTextParseMaskOperand(operandTable, GetParam().which_enum, + GetParam().expression, &value)); + EXPECT_EQ(GetParam().expected_value, value); +} + +INSTANTIATE_TEST_CASE_P( + ParseMask, GoodMaskParseTest, + ::testing::ValuesIn(std::vector{ + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 0, "None"}, + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 1, "NotNaN"}, + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 2, "NotInf"}, + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotNaN|NotInf"}, + // Mask experssions are symmetric. + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN"}, + // Repeating a value has no effect. + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN|NotInf"}, + // Using 3 operands still works. + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 0x13, "NotInf|NotNaN|Fast"}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, 0, "None"}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, 1, "Flatten"}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, 2, "DontFlatten"}, + // Weirdly, you can specify to flatten and don't flatten a selection. + {SPV_OPERAND_TYPE_SELECTION_CONTROL, 3, "Flatten|DontFlatten"}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, 0, "None"}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, 1, "Unroll"}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, 2, "DontUnroll"}, + // Weirdly, you can specify to unroll and don't unroll a loop. + {SPV_OPERAND_TYPE_LOOP_CONTROL, 3, "Unroll|DontUnroll"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 0, "None"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 1, "Inline"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 2, "DontInline"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 4, "Pure"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 8, "Const"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 0xd, "Inline|Const|Pure"}, + })); + +using BadFPFastMathMaskParseTest = ::testing::TestWithParam; + +TEST_P(BadFPFastMathMaskParseTest, BadMaskExpressions) { + spv_operand_table operandTable; + ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&operandTable)); + + uint32_t value; + EXPECT_NE(SPV_SUCCESS, spvTextParseMaskOperand(operandTable, + SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, + GetParam(), &value)); +} + +INSTANTIATE_TEST_CASE_P(ParseMask, BadFPFastMathMaskParseTest, + ::testing::ValuesIn(std::vector{ + nullptr, "", "NotValidEnum", "|", "NotInf|", + "|NotInf", "NotInf||NotNaN", + "Unroll" // A good word, but for the wrong enum + })); + // TODO(dneto): Aliasing like this relies on undefined behaviour. Fix this. union char_word_t { char cs[4];