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 |