//===- 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 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 = RangeSpec::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 std::vector<std::string> RangeSpec::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; } /// 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