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/IceCfg.cpp b/src/IceCfg.cpp
index 285b489..0ff94b4 100644
--- a/src/IceCfg.cpp
+++ b/src/IceCfg.cpp
@@ -197,20 +197,25 @@
 void Cfg::translate() {
   if (hasError())
     return;
+  // Cache the possibly-overridden optimization level once translation begins.
+  // It would be nicer to do this in the constructor, but we need to wait until
+  // after setFunctionName() has a chance to be called.
+  OptimizationLevel =
+      getFlags().matchForceO2(getFunctionName(), getSequenceNumber())
+          ? Opt_2
+          : getFlags().getOptLevel();
   if (BuildDefs::timers()) {
-    const std::string TimingFocusOn = getFlags().getTimingFocusOn();
-    if (!TimingFocusOn.empty()) {
-      const std::string Name = getFunctionName().toString();
-      if (TimingFocusOn == "*" || TimingFocusOn == Name) {
-        setFocusedTiming();
-        getContext()->resetTimer(GlobalContext::TSK_Default);
-      }
+    if (getFlags().matchTimingFocus(getFunctionName(), getSequenceNumber())) {
+      setFocusedTiming();
+      getContext()->resetTimer(GlobalContext::TSK_Default);
     }
   }
   if (BuildDefs::dump()) {
-    if (isVerbose(IceV_Status)) {
+    if (isVerbose(IceV_Status) &&
+        getFlags().matchTestStatus(getFunctionName(), getSequenceNumber())) {
       getContext()->getStrDump() << ">>>Translating "
-                                 << getFunctionNameAndSize() << "\n";
+                                 << getFunctionNameAndSize()
+                                 << " seq=" << getSequenceNumber() << "\n";
     }
   }
   TimerMarker T_func(getContext(), getFunctionName().toStringOrEmpty());
@@ -222,7 +227,7 @@
     profileBlocks();
     // TODO(jpp): this is fragile, at best. Figure out a better way of
     // detecting exit functions.
-    if (GlobalContext::matchSymbolName(getFunctionName(), "exit")) {
+    if (getFunctionName().toStringOrEmpty() == "exit") {
       addCallToProfileSummary();
     }
     dump("Profiled CFG");
diff --git a/src/IceCfg.h b/src/IceCfg.h
index a34a9ee..7c53b1d 100644
--- a/src/IceCfg.h
+++ b/src/IceCfg.h
@@ -40,6 +40,7 @@
 
   GlobalContext *getContext() const { return Ctx; }
   uint32_t getSequenceNumber() const { return SequenceNumber; }
+  OptLevel getOptLevel() const { return OptimizationLevel; }
 
   static constexpr VerboseMask defaultVerboseMask() {
     return (IceV_NO_PER_PASS_DUMP_BEYOND << 1) - 1;
@@ -296,7 +297,8 @@
   void findRematerializable();
 
   GlobalContext *Ctx;
-  uint32_t SequenceNumber;             /// output order for emission
+  uint32_t SequenceNumber; /// output order for emission
+  OptLevel OptimizationLevel = Opt_m1;
   uint32_t ConstantBlindingCookie = 0; /// cookie for constant blinding
   VerboseMask VMask;
   GlobalString FunctionName;
diff --git a/src/IceClFlags.cpp b/src/IceClFlags.cpp
index 042fc36..6127fec 100644
--- a/src/IceClFlags.cpp
+++ b/src/IceClFlags.cpp
@@ -193,6 +193,11 @@
                                         DisableInternalObj);
   OutFlags.setDisableHybridAssembly(DisableHybridAssemblyObj ||
                                     (OutFileTypeObj != Ice::FT_Iasm));
+  OutFlags.ForceO2.init(OutFlags.getForceO2String());
+  OutFlags.TestStatus.init(OutFlags.getTestStatusString());
+  OutFlags.TimingFocus.init(OutFlags.getTimingFocusOnString());
+  OutFlags.TranslateOnly.init(OutFlags.getTranslateOnlyString());
+  OutFlags.VerboseFocus.init(OutFlags.getVerboseFocusOnString());
 }
 
 } // end of namespace Ice
diff --git a/src/IceClFlags.def b/src/IceClFlags.def
index 58f1777..0ae8e09 100644
--- a/src/IceClFlags.def
+++ b/src/IceClFlags.def
@@ -149,6 +149,9 @@
   X(ForceMemIntrinOpt, bool, dev_opt_flag, "fmem-intrin-opt",                  \
     cl::desc("Force optimization of memory intrinsics."))                      \
                                                                                \
+  X(ForceO2String, std::string, dev_opt_flag, "force-O2",                      \
+    cl::desc("Force -O2 for certain functions (assumes -Om1)"), cl::init(""))  \
+                                                                               \
   X(FunctionSections, bool, dev_opt_flag, "ffunction-sections",                \
     cl::desc("Emit functions into separate sections"))                         \
                                                                                \
@@ -282,15 +285,18 @@
              "frame in bytes (for testing)."),                                 \
     cl::init(0))                                                               \
                                                                                \
+  X(TestStatusString, std::string, dev_opt_flag, "test-status",                \
+    cl::desc("Testing flag for -verbose=status"), cl::init(":"))               \
+                                                                               \
   X(TimeEachFunction, bool, dev_opt_flag, "timing-funcs",                      \
     cl::desc("Print total translation time for each function"))                \
                                                                                \
-  X(TimingFocusOn, std::string, dev_opt_flag, "timing-focus",                  \
-    cl::desc("Break down timing for a specific function (use '*' for all)"),   \
+  X(TimingFocusOnString, std::string, dev_opt_flag, "timing-focus",            \
+    cl::desc("Break down timing for specific functions (use ':' for all)"),    \
     cl::init(""))                                                              \
                                                                                \
-  X(TranslateOnly, std::string, dev_opt_flag, "translate-only",                \
-    cl::desc("Translate only the given function"), cl::init(""))               \
+  X(TranslateOnlyString, std::string, dev_opt_flag, "translate-only",          \
+    cl::desc("Translate only the given functions"), cl::init(":"))             \
                                                                                \
   X(UseNonsfi, bool, dev_opt_flag, "nonsfi", cl::desc("Enable Non-SFI mode"))  \
                                                                                \
@@ -335,9 +341,9 @@
                    "Use all verbose options except 'regalloc,global_init'"),   \
         clEnumValN(Ice::IceV_None, "none", "No verbosity"), clEnumValEnd))     \
                                                                                \
-  X(VerboseFocusOn, std::string, dev_opt_flag, "verbose-focus",                \
-    cl::desc("Override with -verbose=none except for the specified function"), \
-    cl::init(""))
+  X(VerboseFocusOnString, std::string, dev_opt_flag, "verbose-focus",          \
+    cl::desc("Override with -verbose=none except for specified functions"),    \
+    cl::init(":"))
 //#define X(Name, Type, ClType, ...)
 
 } // end of namespace Ice
diff --git a/src/IceClFlags.h b/src/IceClFlags.h
index 0a2fc56..4615903 100644
--- a/src/IceClFlags.h
+++ b/src/IceClFlags.h
@@ -18,6 +18,7 @@
 #include "IceDefs.h"
 #include "IceBuildDefs.h"
 #include "IceClFlags.def"
+#include "IceRangeSpec.h"
 #include "IceTypes.h"
 
 #ifdef __clang__
@@ -150,12 +151,36 @@
   void setGenerateUnitTestMessages(bool NewValue) {
     GenerateUnitTestMessages = NewValue;
   }
+  bool matchForceO2(GlobalString Name, uint32_t Number) const {
+    return ForceO2.match(Name, Number);
+  }
+  bool matchTestStatus(GlobalString Name, uint32_t Number) const {
+    return TestStatus.match(Name, Number);
+  }
+  bool matchTimingFocus(GlobalString Name, uint32_t Number) const {
+    return TimingFocus.match(Name, Number);
+  }
+  bool matchTranslateOnly(GlobalString Name, uint32_t Number) const {
+    return TranslateOnly.match(Name, Number);
+  }
+  bool matchVerboseFocusOn(GlobalString Name, uint32_t Number) const {
+    return VerboseFocus.match(Name, Number);
+  }
+  bool matchVerboseFocusOn(const std::string &Name, uint32_t Number) const {
+    return VerboseFocus.match(Name, Number);
+  }
 
 private:
   std::string AppName;
 
   /// Initialized to false; not set by the command line.
   bool GenerateUnitTestMessages;
+
+  RangeSpec ForceO2;
+  RangeSpec TestStatus;
+  RangeSpec TimingFocus;
+  RangeSpec TranslateOnly;
+  RangeSpec VerboseFocus;
 };
 
 inline const ClFlags &getFlags() { return ClFlags::Flags; }
diff --git a/src/IceELFObjectWriter.cpp b/src/IceELFObjectWriter.cpp
index 38919cc..bb3e0ed 100644
--- a/src/IceELFObjectWriter.cpp
+++ b/src/IceELFObjectWriter.cpp
@@ -288,10 +288,9 @@
 // Partition the Vars list by SectionType into VarsBySection. If TranslateOnly
 // is non-empty, then only the TranslateOnly variable is kept for emission.
 void partitionGlobalsBySection(const VariableDeclarationList &Vars,
-                               VariableDeclarationPartition VarsBySection[],
-                               const std::string &TranslateOnly) {
+                               VariableDeclarationPartition VarsBySection[]) {
   for (VariableDeclaration *Var : Vars) {
-    if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
+    if (getFlags().matchTranslateOnly(Var->getName(), 0)) {
       size_t Section = classifyGlobalSection(Var);
       assert(Section < ELFObjectWriter::NumSectionTypes);
       VarsBySection[Section].push_back(Var);
@@ -310,7 +309,7 @@
   VariableDeclarationPartition VarsBySection[ELFObjectWriter::NumSectionTypes];
   for (auto &SectionList : VarsBySection)
     SectionList.reserve(Vars.size());
-  partitionGlobalsBySection(Vars, VarsBySection, getFlags().getTranslateOnly());
+  partitionGlobalsBySection(Vars, VarsBySection);
   size_t I = 0;
   for (auto &SectionList : VarsBySection) {
     writeDataOfType(static_cast<SectionType>(I++), SectionList, RelocationKind,
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp
index fdb5642..058825b 100644
--- a/src/IceGlobalContext.cpp
+++ b/src/IceGlobalContext.cpp
@@ -367,18 +367,17 @@
     CfgLocalAllocatorScope _(Func.get());
     // Reset per-function stats being accumulated in TLS.
     resetStats();
-    // Set verbose level to none if the current function does NOT
-    // match the -verbose-focus command-line option.
-    if (!matchSymbolName(Func->getFunctionName(),
-                         getFlags().getVerboseFocusOn()))
+    // Set verbose level to none if the current function does NOT match the
+    // -verbose-focus command-line option.
+    if (!getFlags().matchVerboseFocusOn(Func->getFunctionName(),
+                                        Func->getSequenceNumber()))
       Func->setVerbose(IceV_None);
-    // Disable translation if -notranslate is specified, or if the
-    // current function matches the -translate-only option.  If
-    // translation is disabled, just dump the high-level IR and
-    // continue.
+    // Disable translation if -notranslate is specified, or if the current
+    // function matches the -translate-only option.  If translation is disabled,
+    // just dump the high-level IR and continue.
     if (getFlags().getDisableTranslation() ||
-        !matchSymbolName(Func->getFunctionName(),
-                         getFlags().getTranslateOnly())) {
+        !getFlags().matchTranslateOnly(Func->getFunctionName(),
+                                       Func->getSequenceNumber())) {
       Func->dump();
       continue; // Func goes out of scope and gets deleted
     }
@@ -467,7 +466,7 @@
   TimerMarker T(TimerStack::TT_emitGlobalInitializers, this);
   const bool DumpGlobalVariables =
       BuildDefs::dump() && (getFlags().getVerbose() & IceV_GlobalInit) &&
-      getFlags().getVerboseFocusOn().empty();
+      getFlags().matchVerboseFocusOn("", 0);
   if (DumpGlobalVariables) {
     OstreamLocker L(this);
     Ostream &Stream = getStrDump();
@@ -532,11 +531,6 @@
   lowerGlobals(ProfileDataSection);
 }
 
-bool GlobalContext::matchSymbolName(const GlobalString &SymbolName,
-                                    const std::string &Match) {
-  return Match.empty() || Match == SymbolName.toString();
-}
-
 void GlobalContext::emitItems() {
   const bool Threaded = !getFlags().isSequential();
   // Pending is a vector containing the reassembled, ordered list of
@@ -690,7 +684,7 @@
   if (!BuildDefs::dump())
     return;
   const bool DumpCounts = (getFlags().getVerbose() & IceV_ConstPoolStats) &&
-                          getFlags().getVerboseFocusOn().empty();
+                          getFlags().matchVerboseFocusOn("", 0);
   if (!DumpCounts)
     return;
 
@@ -1024,7 +1018,7 @@
   switch (StackID) {
   case GlobalContext::TSK_Default:
     Active = getFlags().getSubzeroTimingEnabled() ||
-             !getFlags().getTimingFocusOn().empty();
+             !getFlags().getTimingFocusOnString().empty();
     break;
   case GlobalContext::TSK_Funcs:
     Active = getFlags().getTimeEachFunction();
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h
index 848fe12..c216f82 100644
--- a/src/IceGlobalContext.h
+++ b/src/IceGlobalContext.h
@@ -453,14 +453,6 @@
 
   void dumpConstantLookupCounts();
 
-  /// Utility function to match a symbol name against a match string. This is
-  /// used in a few cases where we want to take some action on a particular
-  /// function or symbol based on a command-line argument, such as changing the
-  /// verbose level for a particular function. An empty Match argument means
-  /// match everything. Returns true if there is a match.
-  static bool matchSymbolName(const GlobalString &SymbolName,
-                              const std::string &Match);
-
   /// DisposeGlobalVariablesAfterLowering controls whether the memory used by
   /// GlobaleVariables can be reclaimed right after they have been lowered.
   /// @{
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
diff --git a/src/IceRangeSpec.h b/src/IceRangeSpec.h
new file mode 100644
index 0000000..cdd9c35
--- /dev/null
+++ b/src/IceRangeSpec.h
@@ -0,0 +1,73 @@
+//===- subzero/src/IceRangeSpec.h - Include/exclude specs -------*- C++ -*-===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Declares a class for specifying inclusion/exclusion of values, such
+/// as functions to match.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICERANGESPEC_H
+#define SUBZERO_SRC_ICERANGESPEC_H
+
+#include "IceStringPool.h"
+
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "llvm/ADT/BitVector.h"
+
+namespace Ice {
+
+class RangeSpec {
+  RangeSpec(const RangeSpec &) = delete;
+  RangeSpec &operator=(const RangeSpec &) = delete;
+
+public:
+  static constexpr char DELIM_LIST = ',';
+  static constexpr char DELIM_RANGE = ':';
+  static constexpr uint32_t RangeMax = std::numeric_limits<uint32_t>::max();
+  RangeSpec() = default;
+  struct Desc {
+    // Set of names explicitly provided.
+    std::unordered_set<std::string> Names;
+    // Set of numbers explicitly provided.
+    llvm::BitVector Numbers;
+    // The smallest X for which the open-ended interval "X:" was provided.  This
+    // is needed because the intervals are parsed before we know the largest
+    // number that might be matched against, and we can't make the Numbers
+    // bitvector infinitely long.
+    uint32_t AllFrom = RangeMax;
+    // Whether a clause was explicitly provided.
+    bool IsExplicit = false;
+  };
+  void init(const std::string &Spec);
+  bool match(const std::string &Name, uint32_t Number) const;
+  bool match(GlobalString Name, uint32_t Number) const {
+    return match(Name.toStringOrEmpty(), Number);
+  }
+  // Returns true if any RangeSpec object has had init() called with an explicit
+  // name rather than (or in addition to) a numeric range.  If so, we want to
+  // construct explicit names for functions even in a non-DUMP build so that
+  // matching on function name works correctly.  Note that this is not
+  // thread-safe, so we count on all this being handled by the startup thread.
+  static bool hasNames() { return HasNames; }
+
+private:
+  void include(const std::string &Token);
+  void exclude(const std::string &Token);
+  Desc Includes;
+  Desc Excludes;
+  static bool HasNames;
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICERANGESPEC_H
diff --git a/src/IceStringPool.h b/src/IceStringPool.h
index 4b5f6da..c046485 100644
--- a/src/IceStringPool.h
+++ b/src/IceStringPool.h
@@ -17,6 +17,8 @@
 #ifndef SUBZERO_SRC_ICESTRINGPOOL_H
 #define SUBZERO_SRC_ICESTRINGPOOL_H
 
+#include "IceDefs.h" // Ostream
+
 #include <cstdint> // uintptr_t
 #include <string>
 
diff --git a/src/IceTargetLowering.h b/src/IceTargetLowering.h
index abf6987..2c7adc8 100644
--- a/src/IceTargetLowering.h
+++ b/src/IceTargetLowering.h
@@ -181,7 +181,7 @@
   virtual std::unique_ptr<Assembler> createAssembler() const = 0;
 
   void translate() {
-    switch (getFlags().getOptLevel()) {
+    switch (Func->getOptLevel()) {
     case Opt_m1:
       translateOm1();
       break;
diff --git a/src/IceTargetLoweringARM32.cpp b/src/IceTargetLoweringARM32.cpp
index 8c0aca6..a36fe99 100644
--- a/src/IceTargetLoweringARM32.cpp
+++ b/src/IceTargetLoweringARM32.cpp
@@ -6817,10 +6817,9 @@
   } break;
   case FT_Asm:
   case FT_Iasm: {
-    const std::string TranslateOnly = getFlags().getTranslateOnly();
     OstreamLocker _(Ctx);
     for (const VariableDeclaration *Var : Vars) {
-      if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
+      if (getFlags().matchTranslateOnly(Var->getName(), 0)) {
         emitGlobal(*Var, SectionSuffix);
       }
     }
diff --git a/src/IceTargetLoweringMIPS32.cpp b/src/IceTargetLoweringMIPS32.cpp
index 238b7bb..1bc15e3 100644
--- a/src/IceTargetLoweringMIPS32.cpp
+++ b/src/IceTargetLoweringMIPS32.cpp
@@ -1211,10 +1211,9 @@
   } break;
   case FT_Asm:
   case FT_Iasm: {
-    const std::string TranslateOnly = getFlags().getTranslateOnly();
     OstreamLocker L(Ctx);
     for (const VariableDeclaration *Var : Vars) {
-      if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
+      if (getFlags().matchTranslateOnly(Var->getName(), 0)) {
         emitGlobal(*Var, SectionSuffix);
       }
     }
diff --git a/src/IceTargetLoweringX86BaseImpl.h b/src/IceTargetLoweringX86BaseImpl.h
index 38cef5f..0907f87 100644
--- a/src/IceTargetLoweringX86BaseImpl.h
+++ b/src/IceTargetLoweringX86BaseImpl.h
@@ -1399,7 +1399,7 @@
   const uint32_t Alignment =
       std::max(AlignmentParam, Traits::X86_STACK_ALIGNMENT_BYTES);
   const bool OverAligned = Alignment > Traits::X86_STACK_ALIGNMENT_BYTES;
-  const bool OptM1 = getFlags().getOptLevel() == Opt_m1;
+  const bool OptM1 = Func->getOptLevel() == Opt_m1;
   const bool AllocaWithKnownOffset = Instr->getKnownFrameOffset();
   const bool UseFramePointer =
       hasFramePointer() || OverAligned || !AllocaWithKnownOffset || OptM1;
@@ -1529,7 +1529,7 @@
                                                   int32_t Src1) {
   // Disable this optimization for Om1 and O0, just to keep things simple
   // there.
-  if (getFlags().getOptLevel() < Opt_1)
+  if (Func->getOptLevel() < Opt_1)
     return false;
   Type Ty = Dest->getType();
   if (Src1 == -1) {
@@ -2223,7 +2223,7 @@
   case InstArithmetic::Sdiv:
     // TODO(stichnot): Enable this after doing better performance and cross
     // testing.
-    if (false && getFlags().getOptLevel() >= Opt_1) {
+    if (false && Func->getOptLevel() >= Opt_1) {
       // Optimize division by constant power of 2, but not for Om1 or O0, just
       // to keep things simple there.
       if (auto *C = llvm::dyn_cast<ConstantInteger32>(Src1)) {
@@ -2313,7 +2313,7 @@
   case InstArithmetic::Srem: {
     // TODO(stichnot): Enable this after doing better performance and cross
     // testing.
-    if (false && getFlags().getOptLevel() >= Opt_1) {
+    if (false && Func->getOptLevel() >= Opt_1) {
       // Optimize mod by constant power of 2, but not for Om1 or O0, just to
       // keep things simple there.
       if (auto *C = llvm::dyn_cast<ConstantInteger32>(Src1)) {
@@ -4306,7 +4306,7 @@
                                                          Operand *PtrToMem,
                                                          Operand *Expected,
                                                          Operand *Desired) {
-  if (getFlags().getOptLevel() == Opt_m1)
+  if (Func->getOptLevel() == Opt_m1)
     return false;
   // Peek ahead a few instructions and see how Dest is used.
   // It's very common to have:
@@ -7016,7 +7016,7 @@
 }
 
 template <typename TraitsType> void TargetX86Base<TraitsType>::postLower() {
-  if (getFlags().getOptLevel() == Opt_m1)
+  if (Func->getOptLevel() == Opt_m1)
     return;
   markRedefinitions();
   Context.availabilityUpdate();
@@ -7417,10 +7417,9 @@
   } break;
   case FT_Asm:
   case FT_Iasm: {
-    const std::string TranslateOnly = getFlags().getTranslateOnly();
     OstreamLocker L(Ctx);
     for (const VariableDeclaration *Var : Vars) {
-      if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
+      if (getFlags().matchTranslateOnly(Var->getName(), 0)) {
         emitGlobal(*Var, SectionSuffix);
       }
     }
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index 0a8e52a..69498ce 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -22,6 +22,7 @@
 #include "IceGlobalInits.h"
 #include "IceInst.h"
 #include "IceOperand.h"
+#include "IceRangeSpec.h"
 
 #ifdef __clang__
 #pragma clang diagnostic push
@@ -481,10 +482,9 @@
       // - DUMP is enabled
       // - The symbol is external
       // - The -timing-funcs flag is enabled
-      // - The -timing-focus flag is enabled
+      // - Some RangeSpec is initialized with actual names
       if (Ice::BuildDefs::dump() || !Decl->isInternal() ||
-          Ice::getFlags().getTimeEachFunction() ||
-          !Ice::getFlags().getTimingFocusOn().empty()) {
+          Ice::RangeSpec::hasNames() || Ice::getFlags().getTimeEachFunction()) {
         Decl->setName(Ctx, Translator.createUnnamedName(Prefix, NameIndex));
       } else {
         Decl->setName(Ctx);