mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 19:50:05 +00:00
Build "spec id->default val str" mapping from string
Add function `ParseDefaultValuesString()` to build the spec id->default value string mapping required by `SetSpecConstantDefaultValuePass`.
This commit is contained in:
parent
5ac63523d7
commit
66f5b4bfc5
@ -15,6 +15,8 @@
|
||||
#include "set_spec_constant_default_value_pass.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@ -23,6 +25,7 @@
|
||||
#include "util/parse_number.h"
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "make_unique.h"
|
||||
#include "type_manager.h"
|
||||
#include "types.h"
|
||||
|
||||
@ -32,6 +35,7 @@ namespace opt {
|
||||
namespace {
|
||||
using spvutils::NumberType;
|
||||
using spvutils::EncodeNumberStatus;
|
||||
using spvutils::ParseNumber;
|
||||
using spvutils::ParseAndEncodeNumber;
|
||||
|
||||
// Given a numeric value in a null-terminated c string and the expected type of
|
||||
@ -248,5 +252,58 @@ bool SetSpecConstantDefaultValuePass::Process(ir::Module* module) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
// Returns true if the given char is ':', '\0' or considered as blank space
|
||||
// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' ').
|
||||
bool IsSeparator(char ch) {
|
||||
return std::strchr(":\0", ch) || std::isspace(ch) != 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<SetSpecConstantDefaultValuePass::SpecIdToValueStrMap>
|
||||
SetSpecConstantDefaultValuePass::ParseDefaultValuesString(const char* str) {
|
||||
if (!str) return nullptr;
|
||||
|
||||
auto spec_id_to_value = MakeUnique<SpecIdToValueStrMap>();
|
||||
|
||||
// The parsing loop, break when points to the end.
|
||||
while (*str) {
|
||||
// Find the spec id.
|
||||
while (std::isspace(*str)) str++; // skip leading spaces.
|
||||
const char* entry_begin = str;
|
||||
while (!IsSeparator(*str)) str++;
|
||||
const char* entry_end = str;
|
||||
std::string spec_id_str(entry_begin, entry_end - entry_begin);
|
||||
uint32_t spec_id = 0;
|
||||
if (!ParseNumber(spec_id_str.c_str(), &spec_id)) {
|
||||
// The spec id is not a valid uint32 number.
|
||||
return nullptr;
|
||||
}
|
||||
auto iter = spec_id_to_value->find(spec_id);
|
||||
if (iter != spec_id_to_value->end()) {
|
||||
// Same spec id has been defined before
|
||||
return nullptr;
|
||||
}
|
||||
// Find the ':', spaces between the spec id and the ':' are not allowed.
|
||||
if (*str++ != ':') {
|
||||
// ':' not found
|
||||
return nullptr;
|
||||
}
|
||||
// Find the value string
|
||||
const char* val_begin = str;
|
||||
while (!IsSeparator(*str)) str++;
|
||||
const char* val_end = str;
|
||||
if (val_end == val_begin) {
|
||||
// Value string is empty.
|
||||
return nullptr;
|
||||
}
|
||||
// Update the mapping with spec id and value string.
|
||||
(*spec_id_to_value)[spec_id] = std::string(val_begin, val_end - val_begin);
|
||||
|
||||
// Skip trailing spaces.
|
||||
while (std::isspace(*str)) str++;
|
||||
}
|
||||
|
||||
return spec_id_to_value;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -43,6 +43,42 @@ class SetSpecConstantDefaultValuePass : public Pass {
|
||||
const char* name() const override { return "set-spec-const-default-value"; }
|
||||
bool Process(ir::Module*) override;
|
||||
|
||||
// Parses the given null-terminated C string to get a mapping from Spec Id to
|
||||
// default value strings. Returns a unique pointer of the mapping from spec
|
||||
// ids to spec constant default value strings built from the given |str| on
|
||||
// success. Returns a nullptr if the given string is not valid for building
|
||||
// the mapping.
|
||||
// A valid string for building the mapping should follow the rule below:
|
||||
//
|
||||
// "<spec id A>:<default value for A> <spec id B>:<default value for B> ..."
|
||||
// Example:
|
||||
// "200:0x11 201:3.14 202:1.4728"
|
||||
//
|
||||
// Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t',
|
||||
// '\f', '\v'). Each entry corresponds to a Spec Id and default value pair.
|
||||
// Multiple spaces between, before or after entries are allowed. However,
|
||||
// spaces are not allowed within spec id or the default value string because
|
||||
// spaces are always considered as delimiter to separate entries.
|
||||
//
|
||||
// In each entry, the spec id and value string is separated by ':'. Missing
|
||||
// ':' in any entry is invalid. And it is invalid to have blank spaces in
|
||||
// between the spec id and ':' or the default value and ':'.
|
||||
//
|
||||
// <spec id>: specifies the spec id value.
|
||||
// The text must represent a valid uint32_t number.
|
||||
// Hex format with '0x' prefix is allowed.
|
||||
// Empty <spec id> is not allowed.
|
||||
// One spec id value can only be defined once, multiple default values
|
||||
// defined for the same spec id is not allowed. Spec ids with same value
|
||||
// but different formats (e.g. 0x100 and 256) are considered the same.
|
||||
//
|
||||
// <default value>: the default value string.
|
||||
// Spaces before and after default value text is allowed.
|
||||
// Spaces within the text is not allowed.
|
||||
// Empty <default value> is not allowed.
|
||||
static std::unique_ptr<SpecIdToValueStrMap> ParseDefaultValuesString(
|
||||
const char* str);
|
||||
|
||||
private:
|
||||
// The mapping from spec ids to their default values to be set.
|
||||
const SpecIdToValueStrMap spec_id_to_value_;
|
||||
|
@ -14,12 +14,121 @@
|
||||
|
||||
#include "pass_fixture.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
namespace {
|
||||
using namespace spvtools;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
using SpecIdToValueStrMap =
|
||||
opt::SetSpecConstantDefaultValuePass::SpecIdToValueStrMap;
|
||||
|
||||
struct DefaultValuesStringParsingTestCase {
|
||||
const char* default_values_str;
|
||||
bool expect_success;
|
||||
SpecIdToValueStrMap expected_map;
|
||||
};
|
||||
|
||||
using DefaultValuesStringParsingTest =
|
||||
::testing::TestWithParam<DefaultValuesStringParsingTestCase>;
|
||||
|
||||
TEST_P(DefaultValuesStringParsingTest, TestCase) {
|
||||
const auto& tc = GetParam();
|
||||
auto actual_map =
|
||||
opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
|
||||
tc.default_values_str);
|
||||
if (tc.expect_success) {
|
||||
EXPECT_NE(nullptr, actual_map);
|
||||
if (actual_map) EXPECT_THAT(*actual_map, Eq(tc.expected_map));
|
||||
} else {
|
||||
EXPECT_EQ(nullptr, actual_map);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ValidString, DefaultValuesStringParsingTest,
|
||||
::testing::ValuesIn(std::vector<DefaultValuesStringParsingTestCase>{
|
||||
// 0. empty map
|
||||
{"", true, SpecIdToValueStrMap{}},
|
||||
// 1. one pair
|
||||
{"100:1024", true, SpecIdToValueStrMap{{100, "1024"}}},
|
||||
// 2. two pairs
|
||||
{"100:1024 200:2048", true,
|
||||
SpecIdToValueStrMap{{100, "1024"}, {200, "2048"}}},
|
||||
// 3. spaces between entries
|
||||
{"100:1024 \n \r \t \v \f 200:2048", true,
|
||||
SpecIdToValueStrMap{{100, "1024"}, {200, "2048"}}},
|
||||
// 4. \t, \n, \r and spaces before spec id
|
||||
{" \n \r\t \t \v \f 100:1024", true,
|
||||
SpecIdToValueStrMap{{100, "1024"}}},
|
||||
// 5. \t, \n, \r and spaces after value string
|
||||
{"100:1024 \n \r\t \t \v \f ", true,
|
||||
SpecIdToValueStrMap{{100, "1024"}}},
|
||||
// 6. maximum spec id
|
||||
{"4294967295:0", true, SpecIdToValueStrMap{{4294967295, "0"}}},
|
||||
// 7. minimum spec id
|
||||
{"0:100", true, SpecIdToValueStrMap{{0, "100"}}},
|
||||
// 8. random content without spaces are allowed
|
||||
{"200:random_stuff", true, SpecIdToValueStrMap{{200, "random_stuff"}}},
|
||||
// 9. support hex format spec id (just because we use the
|
||||
// ParseNumber() utility)
|
||||
{"0x100:1024", true, SpecIdToValueStrMap{{256, "1024"}}},
|
||||
// 10. multiple entries
|
||||
{"101:1 102:2 103:3 104:4 200:201 9999:1000 0x100:333", true,
|
||||
SpecIdToValueStrMap{{101, "1"},
|
||||
{102, "2"},
|
||||
{103, "3"},
|
||||
{104, "4"},
|
||||
{200, "201"},
|
||||
{9999, "1000"},
|
||||
{256, "333"}}},
|
||||
// 11. default value in hex float format
|
||||
{"100:0x0.3p10", true, SpecIdToValueStrMap{{100, "0x0.3p10"}}},
|
||||
// 12. default value in decimal float format
|
||||
{"100:1.5e-13", true, SpecIdToValueStrMap{{100, "1.5e-13"}}},
|
||||
}));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
InvalidString, DefaultValuesStringParsingTest,
|
||||
::testing::ValuesIn(std::vector<DefaultValuesStringParsingTestCase>{
|
||||
// 0. missing default value
|
||||
{"100:", false, SpecIdToValueStrMap{}},
|
||||
// 1. spec id is not an integer
|
||||
{"100.0:200", false, SpecIdToValueStrMap{}},
|
||||
// 2. spec id is not a number
|
||||
{"something_not_a_number:1", false, SpecIdToValueStrMap{}},
|
||||
// 3. only spec id number
|
||||
{"100", false, SpecIdToValueStrMap{}},
|
||||
// 4. same spec id defined multiple times
|
||||
{"100:20 100:21", false, SpecIdToValueStrMap{}},
|
||||
// 5. Multiple definition of an identical spec id in different forms
|
||||
// is not allowed
|
||||
{"0x100:100 256:200", false, SpecIdToValueStrMap{}},
|
||||
// 6. empty spec id
|
||||
{":3", false, SpecIdToValueStrMap{}},
|
||||
// 7. only colon
|
||||
{":", false, SpecIdToValueStrMap{}},
|
||||
// 8. spec id overflow
|
||||
{"4294967296:200", false, SpecIdToValueStrMap{}},
|
||||
// 9. spec id less than 0
|
||||
{"-1:200", false, SpecIdToValueStrMap{}},
|
||||
// 10. nullptr
|
||||
{nullptr, false, SpecIdToValueStrMap{}},
|
||||
// 11. only a number is invalid
|
||||
{"1234", false, SpecIdToValueStrMap{}},
|
||||
// 12. invalid entry separator
|
||||
{"12:34;23:14", false, SpecIdToValueStrMap{}},
|
||||
// 13. invalid spec id and default value separator
|
||||
{"12@34", false, SpecIdToValueStrMap{}},
|
||||
// 14. spaces before colon
|
||||
{"100 :1024", false, SpecIdToValueStrMap{}},
|
||||
// 15. spaces after colon
|
||||
{"100: 1024", false, SpecIdToValueStrMap{}},
|
||||
// 16. spec id represented in hex float format is invalid
|
||||
{"0x3p10:200", false, SpecIdToValueStrMap{}},
|
||||
}));
|
||||
|
||||
struct SetSpecConstantDefaultValueTestCase {
|
||||
const char* code;
|
||||
SpecIdToValueStrMap default_values;
|
||||
|
Loading…
Reference in New Issue
Block a user