| Index: src/IceRangeSpec.cpp
|
| diff --git a/src/IceRangeSpec.cpp b/src/IceRangeSpec.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e12ce6d84cd4f0758f3dc61ec7a45b3f95502aa9
|
| --- /dev/null
|
| +++ b/src/IceRangeSpec.cpp
|
| @@ -0,0 +1,161 @@
|
| +//===- subzero/src/IceRangeSpec.cpp - Include/exclude specification -------===//
|
| +//
|
| +// The Subzero Code Generator
|
| +//
|
| +// This file is distributed under the University of Illinois Open Source
|
| +// License. See LICENSE.TXT for details.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +///
|
| +/// \file
|
| +/// \brief Implements a class for specifying sets of names and number ranges to
|
| +/// match against. This is specified as a comma-separated list of clauses.
|
| +/// Each clause optionally starts with '-' to indicate exclusion instead of
|
| +/// inclusion. A clause can be a name, or a numeric range X:Y, or a single
|
| +/// number X. The X:Y form indicates a range of numbers greater than or equal
|
| +/// to X and strictly less than Y. A missing "X" is taken to be 0, and a
|
| +/// missing "Y" is taken to be infinite. E.g., "0:" and ":" specify the entire
|
| +/// set.
|
| +///
|
| +/// This is essentially the same implementation as in szbuild.py, except that
|
| +/// regular expressions are not used for the names.
|
| +///
|
| +//===----------------------------------------------------------------------===//
|
| +
|
| +#include "IceRangeSpec.h"
|
| +#include "IceStringPool.h"
|
| +
|
| +#include <cctype>
|
| +#include <string>
|
| +#include <unordered_set>
|
| +#include <vector>
|
| +
|
| +namespace Ice {
|
| +
|
| +bool RangeSpec::HasNames = false;
|
| +
|
| +namespace {
|
| +
|
| +/// Helper function to tokenize a string into a vector of string tokens, given a
|
| +/// single delimiter character. An empty string produces an empty token vector.
|
| +/// Zero-length tokens are allowed, e.g. ",a,,,b," may tokenize to
|
| +/// {"","a","","","b",""}.
|
| +std::vector<std::string> tokenize(const std::string &Spec, char Delimiter) {
|
| + std::vector<std::string> Tokens;
|
| + if (!Spec.empty()) {
|
| + std::string::size_type StartPos = 0;
|
| + std::string::size_type DelimPos = 0;
|
| + while (DelimPos != std::string::npos) {
|
| + DelimPos = Spec.find(Delimiter, StartPos);
|
| + Tokens.emplace_back(Spec.substr(StartPos, DelimPos - StartPos));
|
| + StartPos = DelimPos + 1;
|
| + }
|
| + }
|
| + return Tokens;
|
| +}
|
| +
|
| +/// Helper function to parse "X" or "X:Y" into First and Last.
|
| +/// - "X" is treated as "X:X+1".
|
| +/// - ":Y" is treated as "0:Y".
|
| +/// - "X:" is treated as "X:inf"
|
| +///
|
| +/// Behavior is undefined if "X" or "Y" is not a proper number (since std::stoul
|
| +/// throws an exception).
|
| +///
|
| +/// If the string doesn't contain 1 or 2 ':' delimiters, or X>=Y,
|
| +/// report_fatal_error is called.
|
| +void getRange(const std::string &Token, uint32_t *First, uint32_t *Last) {
|
| + bool Error = false;
|
| + auto Tokens = tokenize(Token, RangeSpec::DELIM_RANGE);
|
| + if (Tokens.size() == 1) {
|
| + *First = std::stoul(Tokens[0]);
|
| + *Last = *First + 1;
|
| + } else if (Tokens.size() == 2) {
|
| + *First = Tokens[0].empty() ? 0 : std::stoul(Tokens[0]);
|
| + *Last = Tokens[1].empty() ? RangeSpec::RangeMax : std::stoul(Tokens[1]);
|
| + } else {
|
| + Error = true;
|
| + }
|
| + if (*First >= *Last) {
|
| + Error = true;
|
| + }
|
| + if (Error) {
|
| + llvm::report_fatal_error("Invalid range " + Token);
|
| + }
|
| +}
|
| +
|
| +/// Helper function to add one token to the include or exclude set. The token
|
| +/// is examined and then treated as either a numeric range or a single name.
|
| +void record(const std::string &Token, RangeSpec::Desc *D) {
|
| + if (Token.empty())
|
| + return;
|
| + // Mark that an include or exclude was explicitly given. This affects the
|
| + // default decision when matching a value that wasn't explicitly provided in
|
| + // the include or exclude list.
|
| + D->IsExplicit = true;
|
| + // A range is identified by starting with a digit or a ':'.
|
| + if (Token[0] == RangeSpec::DELIM_RANGE || std::isdigit(Token[0])) {
|
| + uint32_t First, Last;
|
| + getRange(Token, &First, &Last);
|
| + if (Last == RangeSpec::RangeMax) {
|
| + D->AllFrom = std::min(D->AllFrom, First);
|
| + } else {
|
| + if (Last >= D->Numbers.size())
|
| + D->Numbers.resize(Last + 1);
|
| + D->Numbers.set(First, Last);
|
| + }
|
| + } else {
|
| + // Otherwise treat it as a single name.
|
| + D->Names.insert(Token);
|
| + }
|
| +}
|
| +
|
| +} // end of anonymous namespace
|
| +
|
| +/// Initialize the RangeSpec with the given string. Calling init multiple times
|
| +/// (e.g. init("A");init("B");) is equivalent to init("A,B"); .
|
| +void RangeSpec::init(const std::string &Spec) {
|
| + auto Tokens = tokenize(Spec, DELIM_LIST);
|
| + for (const auto &Token : Tokens) {
|
| + if (Token[0] == '-') {
|
| + exclude(Token.substr(1));
|
| + } else {
|
| + include(Token);
|
| + }
|
| + }
|
| + if (!Includes.Names.empty() || !Excludes.Names.empty())
|
| + HasNames = true;
|
| +}
|
| +
|
| +/// Determine whether the given Name/Number combo match the specification given
|
| +/// to the init() method. Explicit excludes take precedence over explicit
|
| +/// includes. If the combo doesn't match any explicit include or exclude:
|
| +/// - false if the init() string is empty (no explicit includes or excludes)
|
| +/// - true if there is at least one explicit exclude and no explicit includes
|
| +/// - false otherwise (at least one explicit include)
|
| +bool RangeSpec::match(const std::string &Name, uint32_t Number) const {
|
| + // No match if it is explicitly excluded by name or number.
|
| + if (Excludes.Names.find(Name) != Excludes.Names.end())
|
| + return false;
|
| + if (Number >= Excludes.AllFrom)
|
| + return false;
|
| + if (Number < Excludes.Numbers.size() && Excludes.Numbers[Number])
|
| + return false;
|
| +
|
| + // Positive match if it is explicitly included by name or number.
|
| + if (Includes.Names.find(Name) != Includes.Names.end())
|
| + return true;
|
| + if (Number >= Includes.AllFrom)
|
| + return true;
|
| + if (Number < Includes.Numbers.size() && Includes.Numbers[Number])
|
| + return true;
|
| +
|
| + // Otherwise use the default decision.
|
| + return Excludes.IsExplicit && !Includes.IsExplicit;
|
| +}
|
| +
|
| +void RangeSpec::include(const std::string &Token) { record(Token, &Includes); }
|
| +
|
| +void RangeSpec::exclude(const std::string &Token) { record(Token, &Excludes); }
|
| +
|
| +} // end of namespace Ice
|
|
|