Subzero: Allow per-method controls.

Several aspects of compilation can now be controlled with fine per-function granularity:

- Focus -timing on individual functions
- Only translate certain functions
- Enable verbosity only for certain functions
- Force O2 translation for certain functions (with Om1 default)

In addition, -test-status limits the output of -verbose=status.  This is just used to enable lit testing of the RangeSpec class.

The main motivation here is to enable bisection debugging of a PNaCl application running in the browser.  The initial use is to control O2 versus Om1, and could be extended to control things like address mode inference and advanced phi lowering, possibly even controlling at the granularity of the instruction numbers.

BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4370
R=eholk@chromium.org, jpp@chromium.org

Review URL: https://codereview.chromium.org/1900543002 .
diff --git a/src/IceRangeSpec.cpp b/src/IceRangeSpec.cpp
new file mode 100644
index 0000000..e12ce6d
--- /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