mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 00:40:14 +00:00
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.
This commit is contained in:
parent
388c40d9c6
commit
36b0c0f6b3
@ -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 <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <libspirv/libspirv.h>
|
||||
|
||||
#include "bitwisecast.h"
|
||||
#include "binary.h"
|
||||
#include "bitwisecast.h"
|
||||
#include "diagnostic.h"
|
||||
#include "ext_inst.h"
|
||||
#include <libspirv/libspirv.h>
|
||||
#include "opcode.h"
|
||||
#include "operand.h"
|
||||
#include "text.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -36,6 +36,7 @@ namespace {
|
||||
|
||||
using spvtest::MakeInstruction;
|
||||
using ::testing::Eq;
|
||||
using test_fixture::TextToBinaryTest;
|
||||
|
||||
// An example case for an enumerated value.
|
||||
template <typename E>
|
||||
@ -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
|
||||
|
@ -36,6 +36,7 @@ namespace {
|
||||
|
||||
using spvtest::MakeInstruction;
|
||||
using ::testing::Eq;
|
||||
using test_fixture::TextToBinaryTest;;
|
||||
|
||||
// An example case for an enumerated value.
|
||||
template <typename E>
|
||||
@ -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
|
||||
|
@ -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<MaskCase>;
|
||||
|
||||
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<MaskCase>{
|
||||
{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<const char*>;
|
||||
|
||||
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<const char*>{
|
||||
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];
|
||||
|
Loading…
Reference in New Issue
Block a user