OLD | NEW |
(Empty) | |
| 1 //===- subzero/src/IceRangeSpec.cpp - Include/exclude specification -------===// |
| 2 // |
| 3 // The Subzero Code Generator |
| 4 // |
| 5 // This file is distributed under the University of Illinois Open Source |
| 6 // License. See LICENSE.TXT for details. |
| 7 // |
| 8 //===----------------------------------------------------------------------===// |
| 9 /// |
| 10 /// \file |
| 11 /// \brief Implements a class for specifying sets of names and number ranges to |
| 12 /// match against. This is specified as a comma-separated list of clauses. |
| 13 /// Each clause optionally starts with '-' to indicate exclusion instead of |
| 14 /// inclusion. A clause can be a name, or a numeric range X:Y, or a single |
| 15 /// number X. The X:Y form indicates a range of numbers greater than or equal |
| 16 /// to X and strictly less than Y. A missing "X" is taken to be 0, and a |
| 17 /// missing "Y" is taken to be infinite. E.g., "0:" and ":" specify the entire |
| 18 /// set. |
| 19 /// |
| 20 /// This is essentially the same implementation as in szbuild.py, except that |
| 21 /// regular expressions are not used for the names. |
| 22 /// |
| 23 //===----------------------------------------------------------------------===// |
| 24 |
| 25 #include "IceRangeSpec.h" |
| 26 #include "IceStringPool.h" |
| 27 |
| 28 #include <cctype> |
| 29 #include <string> |
| 30 #include <unordered_set> |
| 31 #include <vector> |
| 32 |
| 33 namespace Ice { |
| 34 |
| 35 bool RangeSpec::HasNames = false; |
| 36 |
| 37 namespace { |
| 38 |
| 39 /// Helper function to tokenize a string into a vector of string tokens, given a |
| 40 /// single delimiter character. An empty string produces an empty token vector. |
| 41 /// Zero-length tokens are allowed, e.g. ",a,,,b," may tokenize to |
| 42 /// {"","a","","","b",""}. |
| 43 std::vector<std::string> tokenize(const std::string &Spec, char Delimiter) { |
| 44 std::vector<std::string> Tokens; |
| 45 if (!Spec.empty()) { |
| 46 std::string::size_type StartPos = 0; |
| 47 std::string::size_type DelimPos = 0; |
| 48 while (DelimPos != std::string::npos) { |
| 49 DelimPos = Spec.find(Delimiter, StartPos); |
| 50 Tokens.emplace_back(Spec.substr(StartPos, DelimPos - StartPos)); |
| 51 StartPos = DelimPos + 1; |
| 52 } |
| 53 } |
| 54 return Tokens; |
| 55 } |
| 56 |
| 57 /// Helper function to parse "X" or "X:Y" into First and Last. |
| 58 /// - "X" is treated as "X:X+1". |
| 59 /// - ":Y" is treated as "0:Y". |
| 60 /// - "X:" is treated as "X:inf" |
| 61 /// |
| 62 /// Behavior is undefined if "X" or "Y" is not a proper number (since std::stoul |
| 63 /// throws an exception). |
| 64 /// |
| 65 /// If the string doesn't contain 1 or 2 ':' delimiters, or X>=Y, |
| 66 /// report_fatal_error is called. |
| 67 void getRange(const std::string &Token, uint32_t *First, uint32_t *Last) { |
| 68 bool Error = false; |
| 69 auto Tokens = tokenize(Token, RangeSpec::DELIM_RANGE); |
| 70 if (Tokens.size() == 1) { |
| 71 *First = std::stoul(Tokens[0]); |
| 72 *Last = *First + 1; |
| 73 } else if (Tokens.size() == 2) { |
| 74 *First = Tokens[0].empty() ? 0 : std::stoul(Tokens[0]); |
| 75 *Last = Tokens[1].empty() ? RangeSpec::RangeMax : std::stoul(Tokens[1]); |
| 76 } else { |
| 77 Error = true; |
| 78 } |
| 79 if (*First >= *Last) { |
| 80 Error = true; |
| 81 } |
| 82 if (Error) { |
| 83 llvm::report_fatal_error("Invalid range " + Token); |
| 84 } |
| 85 } |
| 86 |
| 87 /// Helper function to add one token to the include or exclude set. The token |
| 88 /// is examined and then treated as either a numeric range or a single name. |
| 89 void record(const std::string &Token, RangeSpec::Desc *D) { |
| 90 if (Token.empty()) |
| 91 return; |
| 92 // Mark that an include or exclude was explicitly given. This affects the |
| 93 // default decision when matching a value that wasn't explicitly provided in |
| 94 // the include or exclude list. |
| 95 D->IsExplicit = true; |
| 96 // A range is identified by starting with a digit or a ':'. |
| 97 if (Token[0] == RangeSpec::DELIM_RANGE || std::isdigit(Token[0])) { |
| 98 uint32_t First, Last; |
| 99 getRange(Token, &First, &Last); |
| 100 if (Last == RangeSpec::RangeMax) { |
| 101 D->AllFrom = std::min(D->AllFrom, First); |
| 102 } else { |
| 103 if (Last >= D->Numbers.size()) |
| 104 D->Numbers.resize(Last + 1); |
| 105 D->Numbers.set(First, Last); |
| 106 } |
| 107 } else { |
| 108 // Otherwise treat it as a single name. |
| 109 D->Names.insert(Token); |
| 110 } |
| 111 } |
| 112 |
| 113 } // end of anonymous namespace |
| 114 |
| 115 /// Initialize the RangeSpec with the given string. Calling init multiple times |
| 116 /// (e.g. init("A");init("B");) is equivalent to init("A,B"); . |
| 117 void RangeSpec::init(const std::string &Spec) { |
| 118 auto Tokens = tokenize(Spec, DELIM_LIST); |
| 119 for (const auto &Token : Tokens) { |
| 120 if (Token[0] == '-') { |
| 121 exclude(Token.substr(1)); |
| 122 } else { |
| 123 include(Token); |
| 124 } |
| 125 } |
| 126 if (!Includes.Names.empty() || !Excludes.Names.empty()) |
| 127 HasNames = true; |
| 128 } |
| 129 |
| 130 /// Determine whether the given Name/Number combo match the specification given |
| 131 /// to the init() method. Explicit excludes take precedence over explicit |
| 132 /// includes. If the combo doesn't match any explicit include or exclude: |
| 133 /// - false if the init() string is empty (no explicit includes or excludes) |
| 134 /// - true if there is at least one explicit exclude and no explicit includes |
| 135 /// - false otherwise (at least one explicit include) |
| 136 bool RangeSpec::match(const std::string &Name, uint32_t Number) const { |
| 137 // No match if it is explicitly excluded by name or number. |
| 138 if (Excludes.Names.find(Name) != Excludes.Names.end()) |
| 139 return false; |
| 140 if (Number >= Excludes.AllFrom) |
| 141 return false; |
| 142 if (Number < Excludes.Numbers.size() && Excludes.Numbers[Number]) |
| 143 return false; |
| 144 |
| 145 // Positive match if it is explicitly included by name or number. |
| 146 if (Includes.Names.find(Name) != Includes.Names.end()) |
| 147 return true; |
| 148 if (Number >= Includes.AllFrom) |
| 149 return true; |
| 150 if (Number < Includes.Numbers.size() && Includes.Numbers[Number]) |
| 151 return true; |
| 152 |
| 153 // Otherwise use the default decision. |
| 154 return Excludes.IsExplicit && !Includes.IsExplicit; |
| 155 } |
| 156 |
| 157 void RangeSpec::include(const std::string &Token) { record(Token, &Includes); } |
| 158 |
| 159 void RangeSpec::exclude(const std::string &Token) { record(Token, &Excludes); } |
| 160 |
| 161 } // end of namespace Ice |
OLD | NEW |