[llvm] r360665 - Reinstate "FileCheck [5/12]: Introduce regular numeric variables"

Thomas Preud'homme via llvm-commits llvm-commits at lists.llvm.org
Tue May 14 04:58:30 PDT 2019


Author: thopre
Date: Tue May 14 04:58:30 2019
New Revision: 360665

URL: http://llvm.org/viewvc/llvm-project?rev=360665&view=rev
Log:
Reinstate "FileCheck [5/12]: Introduce regular numeric variables"

This reinstates r360578 (git e47362c1ec1ea31b626336cc05822035601c3e57),
reverted in r360653 (git 004393681c25e34e921adccc69ae6378090dee54),
with a fix for the list added in FileCheck.rst to build without error.

Copyright:
    - Linaro (changes up to diff 183612 of revision D55940)
    - GraphCore (changes in later versions of revision D55940 and
                 in new revision created off D55940)

Reviewers: jhenderson, chandlerc, jdenny, probinson, grimar,
arichardson, rnk

Subscribers: hiraditya, llvm-commits, probinson, dblaikie, grimar,
arichardson, tra, rnk, kristina, hfinkel, rogfer01, JonChesterfield

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D60385

Added:
    llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt
    llvm/trunk/test/FileCheck/numeric-defines.txt
    llvm/trunk/test/FileCheck/numeric-expression.txt
    llvm/trunk/test/FileCheck/var-scope.txt
Removed:
    llvm/trunk/test/FileCheck/regex-scope.txt
Modified:
    llvm/trunk/docs/CommandGuide/FileCheck.rst
    llvm/trunk/include/llvm/Support/FileCheck.h
    llvm/trunk/lib/Support/FileCheck.cpp
    llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt
    llvm/trunk/test/FileCheck/verbose.txt
    llvm/trunk/unittests/Support/FileCheckTest.cpp

Modified: llvm/trunk/docs/CommandGuide/FileCheck.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/CommandGuide/FileCheck.rst?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/docs/CommandGuide/FileCheck.rst (original)
+++ llvm/trunk/docs/CommandGuide/FileCheck.rst Tue May 14 04:58:30 2019
@@ -105,6 +105,11 @@ and from the command line.
   Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
   used in ``CHECK:`` lines.
 
+.. option:: -D#<NUMVAR>=<VALUE>
+
+  Sets a filecheck numeric variable ``NUMVAR`` to ``<VALUE>`` that can be used
+  in ``CHECK:`` lines.
+
 .. option:: -version
 
  Show the version number of this program.
@@ -560,8 +565,51 @@ CHECK-LABEL block. Global variables are
 This makes it easier to ensure that individual tests are not affected
 by variables set in preceding tests.
 
-FileCheck Numeric Expressions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+FileCheck Numeric Variables and Expressions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:program:`FileCheck` also allows checking for numeric values that satisfy a
+numeric expression constraint based on numeric variables. This allows
+``CHECK:`` directives to verify a numeric relation between two numbers, such as
+the need for consecutive registers to be used.
+
+The syntax to check a numeric expression constraint is
+``[[#<NUMVAR><op><offset>]]`` where:
+
+* ``<NUMVAR>`` is the name of a numeric variable defined on the command line.
+
+* ``<op>`` is an optional numeric operation to perform on the value of
+  ``<NUMVAR>``. Currently supported numeric operations are ``+`` and ``-``.
+
+* ``<offset>`` is the immediate value that constitutes the second operand of
+  the numeric operation <op>. It must be present if ``<op>`` is present,
+  absent otherwise.
+
+For example:
+
+.. code-block:: llvm
+
+    ; CHECK: add r[[#REG]], r[[#REG]], r[[#REG+1]]
+
+The above example would match the line:
+
+.. code-block:: llvm
+
+    add r5, r5, r6
+
+but would not match the line:
+
+.. code-block:: llvm
+
+    add r5, r5, r7
+
+due to ``7`` being unequal to ``5 + 1``.
+
+The ``--enable-var-scope`` option has the same effect on numeric variables as
+on pattern variables.
+
+FileCheck Pseudo Numeric Variables
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Sometimes there's a need to verify output that contains line numbers of the
 match file, e.g. when testing compiler diagnostics.  This introduces a certain
@@ -569,11 +617,9 @@ fragility of the match file structure, a
 line numbers in the same file, which have to be updated whenever line numbers
 change due to text addition or deletion.
 
-To support this case, FileCheck allows using ``[[#@LINE]]``,
-``[[#@LINE+<offset>]]`` and ``[[#@LINE-<offset>]]`` numeric expressions in
-patterns, with an arbitrary number of spaces between each element of the
-expression. These expressions expand to the number of the line where a pattern
-is located (with an optional integer offset).
+To support this case, FileCheck understands the ``@LINE`` pseudo numeric
+variable which evaluates to the line number of the CHECK pattern where it is
+found.
 
 This way match patterns can be put near the relevant test lines and include
 relative line number references, for example:

Modified: llvm/trunk/include/llvm/Support/FileCheck.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FileCheck.h?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FileCheck.h (original)
+++ llvm/trunk/include/llvm/Support/FileCheck.h Tue May 14 04:58:30 2019
@@ -40,20 +40,67 @@ struct FileCheckRequest {
 // Numeric expression handling code.
 //===----------------------------------------------------------------------===//
 
-/// Class representing a numeric expression.
+/// Class representing a numeric variable with a given value in a numeric
+/// expression.
+class FileCheckNumericVariable {
+private:
+  /// Name of the numeric variable.
+  StringRef Name;
+
+  /// Value of numeric variable, if defined, or None otherwise.
+  llvm::Optional<uint64_t> Value;
+
+public:
+  /// Constructor for numeric variable \p Name with a known \p Value at parse
+  /// time (e.g. the @LINE numeric variable).
+  FileCheckNumericVariable(StringRef Name, uint64_t Value)
+      : Name(Name), Value(Value) {}
+
+  /// \returns name of that numeric variable.
+  StringRef getName() const { return Name; }
+
+  /// \returns value of this numeric variable.
+  llvm::Optional<uint64_t> getValue() const { return Value; }
+
+  /// Sets value of this numeric variable if not defined. \returns whether the
+  /// variable was already defined.
+  bool setValue(uint64_t Value);
+
+  /// Clears value of this numeric variable. \returns whether the variable was
+  /// already undefined.
+  bool clearValue();
+};
+
+/// Type of functions evaluating a given binary operation.
+using binop_eval_t = uint64_t (*)(uint64_t, uint64_t);
+
+/// Class representing a numeric expression consisting of either a single
+/// numeric variable or a binary operation between a numeric variable and an
+/// immediate.
 class FileCheckNumExpr {
 private:
-  /// Value of the numeric expression.
-  uint64_t Value;
+  /// Left operand.
+  FileCheckNumericVariable *LeftOp;
+
+  /// Right operand.
+  uint64_t RightOp;
+
+  /// Pointer to function that can evaluate this binary operation.
+  binop_eval_t EvalBinop;
 
 public:
-  /// Constructor for a numeric expression with a known value at parse time,
-  /// e.g. the implicit numeric expression defining the @LINE numeric pseudo
-  /// variable.
-  explicit FileCheckNumExpr(uint64_t Value) : Value(Value) {}
+  FileCheckNumExpr(binop_eval_t EvalBinop,
+                   FileCheckNumericVariable *OperandLeft, uint64_t OperandRight)
+      : LeftOp(OperandLeft), RightOp(OperandRight), EvalBinop(EvalBinop) {}
+
+  /// Evaluates the value of this numeric expression, using EvalBinop to
+  /// perform the binary operation it consists of. \returns None if the numeric
+  /// variable used is undefined, or the expression value otherwise.
+  llvm::Optional<uint64_t> eval() const;
 
-  /// \returns the value being matched against.
-  uint64_t getValue() const { return Value; }
+  /// \returns the name of the undefined variable used in this expression if
+  /// any or an empty string otherwise.
+  StringRef getUndefVarName() const;
 };
 
 class FileCheckPatternContext;
@@ -105,9 +152,9 @@ public:
   size_t getIndex() const { return InsertIdx; }
 
   /// \returns the result of the substitution represented by this class
-  /// instance or None if substitution failed. For a numeric expression we
-  /// substitute it by its value. For a pattern variable we simply replace it
-  /// by the text its definition matched.
+  /// instance or None if substitution failed. Numeric expressions are
+  /// substituted by their values. Pattern variables are simply replaced by the
+  /// text their definition matched.
   llvm::Optional<std::string> getResult() const;
 
   /// \returns the name of the undefined variable used in this substitution, if
@@ -175,19 +222,35 @@ private:
   /// Back-references are used for uses after any the other definition.
   StringMap<StringRef> GlobalVariableTable;
 
+  /// Map of all pattern variables defined so far. Used at parse time to detect
+  /// a name conflict between a numeric variable and a pattern variable when
+  /// the former is defined on a later line than the latter.
+  StringMap<bool> DefinedVariableTable;
+
+  /// When matching a given pattern, this holds the pointers to the classes
+  /// representing the last definitions of numeric variables defined in
+  /// previous patterns. Earlier definition of the variables, if any, have
+  /// their own class instance not referenced by this table.
+  StringMap<FileCheckNumericVariable *> GlobalNumericVariableTable;
+
   /// Vector holding pointers to all parsed numeric expressions. Used to
   /// automatically free the numeric expressions once they are guaranteed to no
   /// longer be used.
   std::vector<std::unique_ptr<FileCheckNumExpr>> NumExprs;
 
+  /// Vector holding pointers to all parsed numeric variables. Used to
+  /// automatically free them once they are guaranteed to no longer be used.
+  std::vector<std::unique_ptr<FileCheckNumericVariable>> NumericVariables;
+
 public:
   /// \returns the value of pattern variable \p VarName or None if no such
   /// variable has been defined.
   llvm::Optional<StringRef> getPatternVarValue(StringRef VarName);
 
-  /// Defines pattern variables from definitions given on the command line,
-  /// passed as a vector of VAR=VAL strings in \p CmdlineDefines. Reports any
-  /// error to \p SM and \returns whether an error occured.
+  /// Defines pattern and numeric variables from definitions given on the
+  /// command line, passed as a vector of [#]VAR=VAL strings in
+  /// \p CmdlineDefines. Reports any error to \p SM and \returns whether an
+  /// error occured.
   bool defineCmdlineVariables(std::vector<std::string> &CmdlineDefines,
                               SourceMgr &SM);
 
@@ -198,7 +261,13 @@ public:
 private:
   /// Makes a new numeric expression instance and registers it for destruction
   /// when the context is destroyed.
-  template <class... Types> FileCheckNumExpr *makeNumExpr(Types... Args);
+  FileCheckNumExpr *makeNumExpr(binop_eval_t EvalBinop,
+                                FileCheckNumericVariable *OperandLeft,
+                                uint64_t OperandRight);
+
+  /// Makes a new numeric variable and registers it for destruction when the
+  /// context is destroyed.
+  FileCheckNumericVariable *makeNumericVariable(StringRef Name, uint64_t Value);
 };
 
 class FileCheckPattern {
@@ -214,12 +283,12 @@ class FileCheckPattern {
 
   /// Entries in this vector represent uses of a pattern variable or a numeric
   /// expression in the pattern that need to be substituted in the regexp
-  /// pattern at match time, e.g. "foo[[bar]]baz[[#@LINE+1]]". In this case,
-  /// the RegExStr will contain "foobaz" and we'll get two entries in this
-  /// vector that tells us to insert the value of pattern variable "bar" at
-  /// offset 3 and the value of numeric expression "@LINE+1" at offset 6. Uses
-  /// are represented by a FileCheckPatternSubstitution class to abstract
-  /// whether it is a pattern variable or a numeric expression.
+  /// pattern at match time, e.g. "foo[[bar]]baz[[#N+1]]". In this case, the
+  /// RegExStr will contain "foobaz" and we'll get two entries in this vector
+  /// that tells us to insert the value of pattern variable "bar" at offset 3
+  /// and the value of numeric expression "N+1" at offset 6. Uses are
+  /// represented by a FileCheckPatternSubstitution class to abstract whether
+  /// it is a pattern variable or a numeric expression.
   std::vector<FileCheckPatternSubstitution> Substitutions;
 
   /// Maps names of pattern variables defined in a pattern to the parenthesized
@@ -233,8 +302,12 @@ class FileCheckPattern {
   /// iterating over values.
   std::map<StringRef, unsigned> VariableDefs;
 
-  /// Pointer to the class instance shared by all patterns holding a table with
-  /// the values of live variables at the start of any given CHECK line.
+  /// Pointer to a class instance holding the global state shared by all
+  /// patterns:
+  /// - separate tables with the values of live pattern and numeric variables
+  ///   respectively at the start of any given CHECK line;
+  /// - table holding whether a pattern variable has been defined at any given
+  ///   point during the parsing phase.
   FileCheckPatternContext *Context;
 
   Check::FileCheckType CheckTy;
@@ -262,11 +335,13 @@ public:
   /// character that is part of the variable name. Otherwise, only
   /// \returns true.
   static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx);
-  /// Parses a numeric expression involving pseudo variable \p Name with the
-  /// string corresponding to the operation being performed in \p Trailer.
-  /// \returns the class representing the numeric expression or nullptr if
-  /// parsing fails in which case errors are reported on \p SM.
-  FileCheckNumExpr *parseNumericExpression(StringRef Name, StringRef Trailer,
+  /// Parses a numeric expression involving (pseudo if \p IsPseudo is true)
+  /// variable \p Name with the string corresponding to the operation being
+  /// performed in \p Trailer. \returns the class representing the numeric
+  /// expression or nullptr if parsing fails in which case errors are reported
+  /// on \p SM.
+  FileCheckNumExpr *parseNumericExpression(StringRef Name, bool IsPseudo,
+                                           StringRef Trailer,
                                            const SourceMgr &SM) const;
   /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
   /// instance accordingly.

Modified: llvm/trunk/lib/Support/FileCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/FileCheck.cpp?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/lib/Support/FileCheck.cpp (original)
+++ llvm/trunk/lib/Support/FileCheck.cpp Tue May 14 04:58:30 2019
@@ -24,24 +24,54 @@
 
 using namespace llvm;
 
+bool FileCheckNumericVariable::setValue(uint64_t NewValue) {
+  if (Value)
+    return true;
+  Value = NewValue;
+  return false;
+}
+
+bool FileCheckNumericVariable::clearValue() {
+  if (!Value)
+    return true;
+  Value = llvm::None;
+  return false;
+}
+
+llvm::Optional<uint64_t> FileCheckNumExpr::eval() const {
+  llvm::Optional<uint64_t> LeftOp = this->LeftOp->getValue();
+  // Variable is undefined.
+  if (!LeftOp)
+    return llvm::None;
+  return EvalBinop(*LeftOp, RightOp);
+}
+
+StringRef FileCheckNumExpr::getUndefVarName() const {
+  if (!LeftOp->getValue())
+    return LeftOp->getName();
+  return StringRef();
+}
+
 llvm::Optional<std::string> FileCheckPatternSubstitution::getResult() const {
   if (IsNumExpr) {
-    return utostr(NumExpr->getValue());
-  } else {
-    // Look up the value and escape it so that we can put it into the
-    // regex.
-    llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
-    if (!VarVal)
+    llvm::Optional<uint64_t> EvaluatedValue = NumExpr->eval();
+    if (!EvaluatedValue)
       return llvm::None;
-    return Regex::escape(*VarVal);
+    return utostr(*EvaluatedValue);
   }
+
+  // Look up the value and escape it so that we can put it into the regex.
+  llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
+  if (!VarVal)
+    return llvm::None;
+  return Regex::escape(*VarVal);
 }
 
 StringRef FileCheckPatternSubstitution::getUndefVarName() const {
-  // Parsing guarantees only @LINE is ever referenced and it is not undefined
-  // by ClearLocalVars.
   if (IsNumExpr)
-    return StringRef();
+    // Although a use of an undefined numeric variable is detected at parse
+    // time, a numeric variable can be undefined later by ClearLocalVariables.
+    return NumExpr->getUndefVarName();
 
   if (!Context->getPatternVarValue(FromStr))
     return FromStr;
@@ -91,31 +121,70 @@ static char popFront(StringRef &S) {
   return C;
 }
 
+static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
+  return LeftOp + RightOp;
+}
+static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
+  return LeftOp - RightOp;
+}
+
 FileCheckNumExpr *
-FileCheckPattern::parseNumericExpression(StringRef Name, StringRef Trailer,
+FileCheckPattern::parseNumericExpression(StringRef Name, bool IsPseudo,
+                                         StringRef Trailer,
                                          const SourceMgr &SM) const {
-  if (!Name.equals("@LINE")) {
+  if (IsPseudo && !Name.equals("@LINE")) {
     SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
                     "invalid pseudo numeric variable '" + Name + "'");
     return nullptr;
   }
 
-  // Check if this is a supported operation and select function to perform it.
+  // This method is indirectly called from ParsePattern for all numeric
+  // variable definitions and uses in the order in which they appear in the
+  // CHECK pattern. For each definition, the pointer to the class instance of
+  // the corresponding numeric variable definition is stored in
+  // GlobalNumericVariableTable. Therefore, the pointer we get below is for the
+  // class instance corresponding to the last definition of this variable use.
+  auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
+  if (VarTableIter == Context->GlobalNumericVariableTable.end()) {
+    SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
+                    "using undefined numeric variable '" + Name + "'");
+    return nullptr;
+  }
+
+  FileCheckNumericVariable *LeftOp = VarTableIter->second;
+
+  // Check if this is a supported operation and select a function to perform
+  // it.
   Trailer = Trailer.ltrim(SpaceChars);
-  if (Trailer.empty())
-    return Context->makeNumExpr(LineNumber);
+  if (Trailer.empty()) {
+    return Context->makeNumExpr(add, LeftOp, 0);
+  }
   SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data());
   char Operator = popFront(Trailer);
+  binop_eval_t EvalBinop;
+  switch (Operator) {
+  case '+':
+    EvalBinop = add;
+    break;
+  case '-':
+    EvalBinop = sub;
+    break;
+  default:
+    SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
+                    Twine("unsupported numeric operation '") + Twine(Operator) +
+                        "'");
+    return nullptr;
+  }
 
   // Parse right operand.
   Trailer = Trailer.ltrim(SpaceChars);
   if (Trailer.empty()) {
     SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
-                    "missing operand in numeric expression '" + Trailer + "'");
+                    "missing operand in numeric expression");
     return nullptr;
   }
-  uint64_t Offset;
-  if (Trailer.consumeInteger(10, Offset)) {
+  uint64_t RightOp;
+  if (Trailer.consumeInteger(10, RightOp)) {
     SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
                     "invalid offset in numeric expression '" + Trailer + "'");
     return nullptr;
@@ -128,31 +197,24 @@ FileCheckPattern::parseNumericExpression
     return nullptr;
   }
 
-  uint64_t Value;
-  switch (Operator) {
-  case '+':
-    Value = LineNumber + Offset;
-    break;
-  case '-':
-    Value = LineNumber - Offset;
-    break;
-  default:
-    SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
-                    Twine("unsupported numeric operation '") + Twine(Operator) +
-                        "'");
-    return nullptr;
-  }
-  return Context->makeNumExpr(Value);
+  return Context->makeNumExpr(EvalBinop, LeftOp, RightOp);
 }
 
 bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
-                           SourceMgr &SM, unsigned LineNumber,
-                           const FileCheckRequest &Req) {
+                                    SourceMgr &SM, unsigned LineNumber,
+                                    const FileCheckRequest &Req) {
   bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
 
   this->LineNumber = LineNumber;
   PatternLoc = SMLoc::getFromPointer(PatternStr.data());
 
+  // Create fake @LINE pseudo variable definition.
+  StringRef LinePseudo = "@LINE";
+  uint64_t LineNumber64 = LineNumber;
+  FileCheckNumericVariable *LinePseudoVar =
+      Context->makeNumericVariable(LinePseudo, LineNumber64);
+  Context->GlobalNumericVariableTable[LinePseudo] = LinePseudoVar;
+
   if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
     // Ignore trailing whitespace.
     while (!PatternStr.empty() &&
@@ -230,10 +292,10 @@ bool FileCheckPattern::ParsePattern(Stri
     // forms: [[foo:.*]] and [[foo]]. The former matches .* (or some other
     // regex) and assigns it to the FileCheck variable 'foo'. The latter
     // substitutes foo's value. Numeric expressions start with a '#' sign after
-    // the double brackets and only have the substitution form. Pattern
-    // variables must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*"
-    // to be valid, as this helps catch some common errors. Numeric expressions
-    // only support the @LINE pseudo numeric variable.
+    // the double brackets and only have the substitution form. Both pattern
+    // and numeric variables must satisfy the regular expression
+    // "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch some common
+    // errors.
     if (PatternStr.startswith("[[")) {
       StringRef UnparsedPatternStr = PatternStr.substr(2);
       // Find the closing bracket pair ending the match.  End is going to be an
@@ -283,21 +345,33 @@ bool FileCheckPattern::ParsePattern(Stri
       StringRef Trailer = MatchStr.substr(TrailIdx);
       bool IsVarDef = (VarEndIdx != StringRef::npos);
 
-      if (IsVarDef && (IsPseudo || !Trailer.consume_front(":"))) {
-        SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
-                        SourceMgr::DK_Error,
-                        "invalid name in pattern variable definition");
-        return true;
+      if (IsVarDef) {
+        if (IsPseudo || !Trailer.consume_front(":")) {
+          SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
+                          SourceMgr::DK_Error,
+                          "invalid name in pattern variable definition");
+          return true;
+        }
+
+        // Detect collisions between pattern and numeric variables when the
+        // former is created later than the latter.
+        if (Context->GlobalNumericVariableTable.find(Name) !=
+            Context->GlobalNumericVariableTable.end()) {
+          SM.PrintMessage(
+              SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error,
+              "numeric variable with name '" + Name + "' already exists");
+          return true;
+        }
       }
 
-      if (!IsVarDef && IsPseudo) {
-        NumExpr = parseNumericExpression(Name, Trailer, SM);
+      if (IsNumExpr || (!IsVarDef && IsPseudo)) {
+        NumExpr = parseNumericExpression(Name, IsPseudo, Trailer, SM);
         if (NumExpr == nullptr)
           return true;
         IsNumExpr = true;
       }
 
-      // Handle [[foo]].
+      // Handle variable use: [[foo]] and [[#<foo expr>]].
       if (!IsVarDef) {
         // Handle use of pattern variables that were defined earlier on the
         // same line by emitting a backreference.
@@ -566,12 +640,22 @@ FileCheckPatternContext::getPatternVarVa
   return VarIter->second;
 }
 
-template <class... Types>
-FileCheckNumExpr *FileCheckPatternContext::makeNumExpr(Types... Args) {
-  NumExprs.emplace_back(new FileCheckNumExpr(Args...));
+FileCheckNumExpr *
+FileCheckPatternContext::makeNumExpr(binop_eval_t EvalBinop,
+                                     FileCheckNumericVariable *OperandLeft,
+                                     uint64_t OperandRight) {
+  NumExprs.push_back(llvm::make_unique<FileCheckNumExpr>(EvalBinop, OperandLeft,
+                                                         OperandRight));
   return NumExprs.back().get();
 }
 
+FileCheckNumericVariable *
+FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) {
+  NumericVariables.push_back(
+      llvm::make_unique<FileCheckNumericVariable>(Name, Value));
+  return NumericVariables.back().get();
+}
+
 size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {
   // Offset keeps track of the current offset within the input Str
   size_t Offset = 0;
@@ -1445,7 +1529,7 @@ Regex llvm::FileCheck::buildCheckPrefixR
 
 bool FileCheckPatternContext::defineCmdlineVariables(
     std::vector<std::string> &CmdlineDefines, SourceMgr &SM) {
-  assert(GlobalVariableTable.empty() &&
+  assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&
          "Overriding defined variable with command-line variable definitions");
 
   if (CmdlineDefines.empty())
@@ -1463,6 +1547,9 @@ bool FileCheckPatternContext::defineCmdl
     CmdlineDefsDiag +=
         (Prefix1 + Twine(++I) + Prefix2 + CmdlineDef + "\n").str();
 
+  // Create a buffer with fake command line content in order to display
+  // parsing diagnostic with location information and point to the
+  // global definition with invalid syntax.
   std::unique_ptr<MemoryBuffer> CmdLineDefsDiagBuffer =
       MemoryBuffer::getMemBufferCopy(CmdlineDefsDiag, "Global defines");
   StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();
@@ -1472,27 +1559,91 @@ bool FileCheckPatternContext::defineCmdl
   CmdlineDefsDiagRef.split(CmdlineDefsDiagVec, '\n', -1 /*MaxSplit*/,
                            false /*KeepEmpty*/);
   for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) {
-    unsigned NameStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
-    if (CmdlineDefDiag.substr(NameStart).find('=') == StringRef::npos) {
-      SM.PrintMessage(SMLoc::getFromPointer(CmdlineDefDiag.data()),
+    unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
+    StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
+    if (CmdlineDef.find('=') == StringRef::npos) {
+      SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()),
                       SourceMgr::DK_Error,
                       "Missing equal sign in global definition");
       ErrorFound = true;
       continue;
     }
-    std::pair<StringRef, StringRef> CmdlineNameVal =
-        CmdlineDefDiag.substr(NameStart).split('=');
-    StringRef Name = CmdlineNameVal.first;
-    bool IsPseudo;
-    unsigned TrailIdx;
-    if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) || IsPseudo ||
-        TrailIdx != Name.size() || Name.empty()) {
-      SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
-                      "invalid name for variable definition '" + Name + "'");
-      ErrorFound = true;
-      continue;
+
+    // Numeric variable definition.
+    if (CmdlineDef[0] == '#') {
+      bool IsPseudo;
+      unsigned TrailIdx;
+      size_t EqIdx = CmdlineDef.find('=');
+      StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
+      if (FileCheckPattern::parseVariable(CmdlineName, IsPseudo, TrailIdx) ||
+          IsPseudo || TrailIdx != CmdlineName.size() || CmdlineName.empty()) {
+        SM.PrintMessage(SMLoc::getFromPointer(CmdlineName.data()),
+                        SourceMgr::DK_Error,
+                        "invalid name in numeric variable definition '" +
+                            CmdlineName + "'");
+        ErrorFound = true;
+        continue;
+      }
+
+      // Detect collisions between pattern and numeric variables when the
+      // latter is created later than the former.
+      if (DefinedVariableTable.find(CmdlineName) !=
+          DefinedVariableTable.end()) {
+        SM.PrintMessage(
+            SMLoc::getFromPointer(CmdlineName.data()), SourceMgr::DK_Error,
+            "pattern variable with name '" + CmdlineName + "' already exists");
+        ErrorFound = true;
+        continue;
+      }
+
+      StringRef CmdlineVal = CmdlineDef.substr(EqIdx + 1);
+      uint64_t Val;
+      if (CmdlineVal.getAsInteger(10, Val)) {
+        SM.PrintMessage(SMLoc::getFromPointer(CmdlineVal.data()),
+                        SourceMgr::DK_Error,
+                        "invalid value in numeric variable definition '" +
+                            CmdlineVal + "'");
+        ErrorFound = true;
+        continue;
+      }
+      auto DefinedNumericVariable = makeNumericVariable(CmdlineName, Val);
+
+      // Record this variable definition.
+      GlobalNumericVariableTable[CmdlineName] = DefinedNumericVariable;
+    } else {
+      // Pattern variable definition.
+      std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
+      StringRef Name = CmdlineNameVal.first;
+      bool IsPseudo;
+      unsigned TrailIdx;
+      if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) ||
+          IsPseudo || TrailIdx != Name.size() || Name.empty()) {
+        SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
+                        "invalid name in pattern variable definition '" + Name +
+                            "'");
+        ErrorFound = true;
+        continue;
+      }
+
+      // Detect collisions between pattern and numeric variables when the
+      // former is created later than the latter.
+      if (GlobalNumericVariableTable.find(Name) !=
+          GlobalNumericVariableTable.end()) {
+        SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
+                        "numeric variable with name '" + Name +
+                            "' already exists");
+        ErrorFound = true;
+        continue;
+      }
+      GlobalVariableTable.insert(CmdlineNameVal);
+      // Mark the pattern variable as defined to detect collisions between
+      // pattern and numeric variables in DefineCmdlineVariables when the
+      // latter is created later than the former. We cannot reuse
+      // GlobalVariableTable for that by populating it with an empty string
+      // since we would then lose the ability to detect the use of an undefined
+      // variable in Match().
+      DefinedVariableTable[Name] = true;
     }
-    GlobalVariableTable.insert(CmdlineNameVal);
   }
 
   return ErrorFound;
@@ -1504,8 +1655,22 @@ void FileCheckPatternContext::clearLocal
     if (Var.first()[0] != '$')
       LocalPatternVars.push_back(Var.first());
 
+  // Numeric expression substitution reads the value of a variable directly,
+  // not via GlobalNumericVariableTable. Therefore, we clear local variables by
+  // clearing their value which will lead to a numeric expression substitution
+  // failure. We also mark the variable for removal from
+  // GlobalNumericVariableTable since this is what defineCmdlineVariables
+  // checks to decide that no global variable has been defined.
+  for (const auto &Var : GlobalNumericVariableTable)
+    if (Var.first()[0] != '$') {
+      Var.getValue()->clearValue();
+      LocalNumericVars.push_back(Var.first());
+    }
+
   for (const auto &Var : LocalPatternVars)
     GlobalVariableTable.erase(Var);
+  for (const auto &Var : LocalNumericVars)
+    GlobalNumericVariableTable.erase(Var);
 }
 
 bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,
@@ -1538,7 +1703,10 @@ bool llvm::FileCheck::CheckInput(SourceM
       ++j;
     }
 
-    if (Req.EnableVarScope)
+    // Do not clear the first region as it's the one before the first
+    // CHECK-LABEL and it would clear variables defined on the command-line
+    // before they get used.
+    if (i != 0 && Req.EnableVarScope)
       PatternContext.clearLocalVars();
 
     for (; i != j; ++i) {

Added: llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt?rev=360665&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt (added)
+++ llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt Tue May 14 04:58:30 2019
@@ -0,0 +1,33 @@
+; Test incorrect syntax for -D# option is correctly diagnosed.
+
+; Invalid variable name: starts with a digit.
+RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT
+
+NUMERRCLIFMT: Global defines:1:20: error: invalid name in numeric variable definition '10VALUE'
+NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10
+NUMERRCLIFMT-NEXT: {{^                   \^$}}
+
+; Invalid definition of pseudo variable.
+RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIPSEUDO
+
+NUMERRCLIPSEUDO: Global defines:1:20: error: invalid name in numeric variable definition '@VALUE'
+NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10
+NUMERRCLIPSEUDO-NEXT: {{^                   \^$}}
+
+; Invalid definition of an expression.
+RUN: not FileCheck -D#VALUE+2=10 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NUMERRCLITRAIL
+
+NUMERRCLITRAIL: Global defines:1:20: error: invalid name in numeric variable definition 'VALUE+2'
+NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10
+NUMERRCLITRAIL-NEXT: {{^                   \^$}}
+
+; Invalid value: numeric expression instead of literal.
+RUN: not FileCheck -D#VALUE1=3 -D#VALUE2='VALUE1 + 2' --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIEXPR
+
+NUMERRCLIEXPR: Global defines:2:27: error: invalid value in numeric variable definition 'VALUE1 + 2'
+NUMERRCLIEXPR-NEXT: Global define #2: #VALUE2=VALUE1 + 2
+NUMERRCLIEXPR-NEXT: {{^                          \^$}}

Added: llvm/trunk/test/FileCheck/numeric-defines.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-defines.txt?rev=360665&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-defines.txt (added)
+++ llvm/trunk/test/FileCheck/numeric-defines.txt Tue May 14 04:58:30 2019
@@ -0,0 +1,22 @@
+; Test functionality of -D# option: numeric variables are defined to the right
+; value and CHECK directives using them match as expected given the value set.
+
+RUN: FileCheck -D#NUMVAL=12 --check-prefix CHECKNUM --input-file %s %s
+RUN: not FileCheck -D#NUMVAL=8 --check-prefix CHECKNUM --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG
+RUN: not FileCheck -D#NUMVAL=12 --check-prefix NUMNOT --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG
+RUN: FileCheck -D#NUMVAL=8 --check-prefixes NUMNOT --input-file %s %s
+
+Numeric value = 12
+CHECKNUM: Numeric value = [[#NUMVAL]]
+NUMNOT-NOT: Numeric value = [[#NUMVAL]]
+
+NUMERRMSG: defines.txt:[[#@LINE-3]]:11: error: CHECKNUM: expected string not found in input
+NUMERRMSG: defines.txt:1:1: note: scanning from here
+NUMERRMSG: defines.txt:1:1: note: with numeric expression "NUMVAL" equal to "8"
+NUMERRMSG: defines.txt:[[#@LINE-7]]:1: note: possible intended match here
+
+NOT-NUMERRMSG: defines.txt:[[#@LINE-7]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
+NOT-NUMERRMSG: defines.txt:[[#@LINE-10]]:1: note: found here
+NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with numeric expression "NUMVAL" equal to "12"

Added: llvm/trunk/test/FileCheck/numeric-expression.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-expression.txt?rev=360665&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-expression.txt (added)
+++ llvm/trunk/test/FileCheck/numeric-expression.txt Tue May 14 04:58:30 2019
@@ -0,0 +1,95 @@
+RUN: FileCheck -D#VAR1=11 --input-file %s %s
+
+; We use CHECK-NEXT directives to force a match on all lines with digits.
+
+; Numeric expressions using variables defined on the command-line without
+; spaces
+USE NO SPC
+11
+12
+10
+CHECK-LABEL: USE NO SPC
+CHECK-NEXT: [[#VAR1]]
+CHECK-NEXT: [[#VAR1+1]]
+CHECK-NEXT: [[#VAR1-1]]
+
+; Numeric expressions using variables defined on the command-line in alternate
+; spacing
+USE ALT SPC
+11
+11
+12
+12
+12
+12
+10
+10
+10
+10
+CHECK-LABEL: USE ALT SPC
+CHECK-NEXT: [[# VAR1]]
+CHECK-NEXT: [[# VAR1 ]]
+CHECK-NEXT: [[# VAR1+1]]
+CHECK-NEXT: [[# VAR1 +1]]
+CHECK-NEXT: [[# VAR1 + 1]]
+CHECK-NEXT: [[# VAR1 + 1 ]]
+CHECK-NEXT: [[# VAR1-1]]
+CHECK-NEXT: [[# VAR1 -1]]
+CHECK-NEXT: [[# VAR1 - 1]]
+CHECK-NEXT: [[# VAR1 - 1 ]]
+
+; Numeric expressions using variables defined on the command-line and an
+; immediate interpreted as an unsigned value
+; Note: 9223372036854775819 = 0x8000000000000000 + 11
+;       9223372036854775808 = 0x8000000000000000
+USE UNSIGNED IMM
+9223372036854775819
+CHECK-LABEL: USE UNSIGNED IMM
+CHECK-NEXT: [[#VAR1+9223372036854775808]]
+
+; Numeric expression using undefined variable
+RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \
+RUN:   | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s
+
+UNDEF VAR USE
+UNDEFVAR: 11
+UNDEF-USE-LABEL: UNDEF VAR USE
+UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR]]
+UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:30: error: using undefined numeric variable 'UNDEFVAR'
+UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR\]\]}}
+UNDEF-USE-MSG-NEXT: {{^                             \^$}}
+
+; Numeric expression with unsupported operator
+RUN: not FileCheck -D#VAR1=11 --check-prefixes CHECK,INVAL-OP --input-file %s %s 2>&1 \
+RUN:   | FileCheck --strict-whitespace --check-prefix INVAL-OP-MSG %s
+
+INVALID OPERATOR
+VAR1*2: 22
+INVAL-OP-LABEL: INVALID OPERATOR
+INVAL-OP-NEXT: VAR1*2: [[#VAR1*2]]
+INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: unsupported numeric operation '*'
+INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: VAR1*2: {{\[\[#VAR1\*2\]\]}}
+INVAL-OP-MSG-NEXT: {{^                              \^$}}
+
+; Name conflict between Numeric variable definition and pattern variable
+; definition
+RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 --check-prefixes CONFLICT,CONFLICT1 --input-file %s %s 2>&1 \
+RUN:   | FileCheck --strict-whitespace --check-prefix CLI-INPUT-PAT-CONFLICT %s
+RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 -DNUMVAR=foobar --check-prefix CONFLICT --input-file %s %s 2>&1 \
+RUN:   | FileCheck --strict-whitespace --check-prefix CLI-CLI-PAT-CONFLICT %s
+RUN: not FileCheck -D#VAR1=11 -DPATVAR=foobar -D#PATVAR=42 --check-prefix CONFLICT --input-file %s %s 2>&1 \
+RUN:   | FileCheck --strict-whitespace --check-prefix CLI-CLI-NUM-CONFLICT %s
+
+PATVAR NUMVAR CONFLICT
+foobar
+CONFLICT-LABEL: PATVAR NUMVAR CONFLICT
+CONFLICT1-NEXT: [[NUMVAR:foo.*]]
+CLI-INPUT-PAT-CONFLICT: numeric-expression.txt:[[#@LINE-1]]:19: error: numeric variable with name 'NUMVAR' already exists
+CLI-INPUT-PAT-CONFLICT-NEXT: {{C}}ONFLICT1-NEXT: {{\[\[NUMVAR:foo\.\*\]\]}}
+CLI-INPUT-PAT-CONFLICT-NEXT: {{^                  \^$}}
+CLI-CLI-PAT-CONFLICT: Global defines:3:19: error: numeric variable with name 'NUMVAR' already exists
+CLI-CLI-PAT-CONFLICT-NEXT: Global define #3: NUMVAR=foobar
+CLI-CLI-PAT-CONFLICT-NEXT: {{^                  \^$}}
+CLI-CLI-NUM-CONFLICT: Global defines:3:20: error: pattern variable with name 'PATVAR' already exists
+CLI-CLI-NUM-CONFLICT-NEXT: Global define #3: #PATVAR=42
+CLI-CLI-NUM-CONFLICT-NEXT: {{^                   \^$}}

Modified: llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt (original)
+++ llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt Tue May 14 04:58:30 2019
@@ -28,7 +28,7 @@ ERRCLIVAR2: Missing pattern variable nam
 RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \
 RUN:   | FileCheck %s --strict-whitespace --check-prefix ERRCLIFMT
 
-ERRCLIFMT: Global defines:1:19: error: invalid name for variable definition '10VALUE'
+ERRCLIFMT: Global defines:1:19: error: invalid name in pattern variable definition '10VALUE'
 ERRCLIFMT-NEXT: Global define #1: 10VALUE=10
 ERRCLIFMT-NEXT: {{^                  \^$}}
 
@@ -36,7 +36,7 @@ ERRCLIFMT-NEXT: {{^                  \^$
 RUN: not FileCheck -D at VALUE=10 --input-file %s %s 2>&1 \
 RUN:   | FileCheck %s --strict-whitespace --check-prefix ERRCLIPSEUDO
 
-ERRCLIPSEUDO: Global defines:1:19: error: invalid name for variable definition '@VALUE'
+ERRCLIPSEUDO: Global defines:1:19: error: invalid name in pattern variable definition '@VALUE'
 ERRCLIPSEUDO-NEXT: Global define #1: @VALUE=10
 ERRCLIPSEUDO-NEXT: {{^                  \^$}}
 
@@ -44,6 +44,6 @@ ERRCLIPSEUDO-NEXT: {{^
 RUN: not FileCheck -D'VALUE + 2=10' --input-file %s %s 2>&1 \
 RUN:   | FileCheck %s --strict-whitespace --check-prefix ERRCLITRAIL
 
-ERRCLITRAIL: Global defines:1:19: error: invalid name for variable definition 'VALUE + 2'
+ERRCLITRAIL: Global defines:1:19: error: invalid name in pattern variable definition 'VALUE + 2'
 ERRCLITRAIL-NEXT: Global define #1: VALUE + 2=10
 ERRCLITRAIL-NEXT: {{^                  \^$}}

Removed: llvm/trunk/test/FileCheck/regex-scope.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/regex-scope.txt?rev=360664&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/regex-scope.txt (original)
+++ llvm/trunk/test/FileCheck/regex-scope.txt (removed)
@@ -1,23 +0,0 @@
-// RUN: FileCheck -input-file %s %s
-// RUN: FileCheck -check-prefixes CHECK,GLOBAL -input-file %s %s
-// RUN: FileCheck -check-prefixes CHECK,LOCAL -input-file %s %s
-// RUN: FileCheck -check-prefixes CHECK,GLOBAL --enable-var-scope -input-file %s %s
-// RUN: not FileCheck -check-prefixes CHECK,LOCAL --enable-var-scope -input-file %s %s
-
-local
-global
-; CHECK: [[LOCAL:loc.*]]
-; CHECK: [[$GLOBAL:glo.*]]
-
-local2
-global2
-; CHECK: [[LOCAL]]2
-; CHECK: [[$GLOBAL]]2
-
-barrier:
-; CHECK-LABEL: barrier
-
-local3
-global3
-; LOCAL: [[LOCAL]]3
-; GLOBAL: [[$GLOBAL]]3

Added: llvm/trunk/test/FileCheck/var-scope.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/var-scope.txt?rev=360665&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/var-scope.txt (added)
+++ llvm/trunk/test/FileCheck/var-scope.txt Tue May 14 04:58:30 2019
@@ -0,0 +1,35 @@
+; Test that variables not starting with dollar sign get undefined after a
+; CHECK-LABEL directive iff --enable-var-scope is used.
+
+RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,LOCAL3,GLOBAL --input-file %s %s
+RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s
+RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \
+RUN:   | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL %s
+RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL2 --enable-var-scope --input-file %s %s 2>&1 \
+RUN:   | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCNUM %s
+RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL3 --enable-var-scope --input-file %s %s 2>&1 \
+RUN:   | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL,ERRUNDEFLOCNUM %s
+
+local1
+global1
+CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM]]
+CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM]]
+
+local2
+global2
+CHECK: [[LOCAL]][[#LOCNUM+1]]
+CHECK: [[$GLOBAL]][[#$GLOBNUM+1]]
+
+barrier:
+CHECK-LABEL: barrier
+
+local3
+global3
+LOCAL1: [[LOCAL]]3
+LOCAL2: local[[#LOCNUM+2]]
+LOCAL3: [[LOCAL]][[#LOCNUM+2]]
+GLOBAL: [[$GLOBAL]][[#$GLOBNUM+2]]
+
+ERRUNDEF: expected string not found in input
+ERRUNDEFLOCAL: uses undefined variable "LOCAL"
+ERRUNDEFLOCNUM: uses undefined variable "LOCNUM"

Modified: llvm/trunk/test/FileCheck/verbose.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/verbose.txt?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/verbose.txt (original)
+++ llvm/trunk/test/FileCheck/verbose.txt Tue May 14 04:58:30 2019
@@ -1,6 +1,6 @@
-; RUN: FileCheck -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s
-; RUN: FileCheck -v -input-file %s %s 2>&1 | FileCheck -check-prefix V %s
-; RUN: FileCheck -vv -input-file %s %s 2>&1 | FileCheck -check-prefixes V,VV %s
+; RUN: FileCheck -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s
+; RUN: FileCheck -v -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix V %s
+; RUN: FileCheck -vv -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefixes V,VV %s
 
 foo
 bar
@@ -29,6 +29,39 @@ VV-NEXT: verbose.txt:[[@LINE-22]]:1: not
 VV-NEXT: {{^}}bar{{$}}
 VV-NEXT: {{^}}^{{$}}
 
+NUMVAR:42
+NUMVAR - 1:41
+CHECK: NUMVAR:[[#NUMVAR]]
+CHECK-NOT: [[#NUMVAR + 1]]
+CHECK-NEXT: NUMVAR - 1:[[#NUMVAR - 1]]
+
+V:      verbose.txt:[[#@LINE-4]]:8: remark: {{C}}HECK: expected string found in input
+V-NEXT: {{C}}HECK: {{NUMVAR:[[][[]#NUMVAR[]][]]$}}
+V-NEXT: {{^       \^$}}
+V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here
+V-NEXT: {{^}}NUMVAR:42{{$}}
+V-NEXT: {{^}}^~~~~~~~~{{$}}
+V-NEXT: verbose.txt:[[#@LINE-12]]:1: note: with numeric expression "NUMVAR" equal to "42"
+V-NEXT: {{^}}NUMVAR:42{{$}}
+V-NEXT: {{^}}^~~~~~~~~{{$}}
+
+V-NEXT: verbose.txt:[[#@LINE-12]]:13: remark: {{C}}HECK-NEXT: expected string found in input
+V-NEXT: {{C}}HECK-NEXT: {{NUMVAR - 1:[[][[]#NUMVAR - 1[]][]]$}}
+V-NEXT: {{^            \^$}}
+V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: found here
+V-NEXT: {{^}}NUMVAR - 1:41{{$}}
+V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}
+V-NEXT: verbose.txt:[[#@LINE-21]]:1: note: with numeric expression "NUMVAR - 1" equal to "41"
+V-NEXT: {{^}}NUMVAR - 1:41{{$}}
+V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}
+
+VV-NEXT: verbose.txt:[[#@LINE-23]]:12: remark: {{C}}HECK-NOT: excluded string not found in input
+VV-NEXT: {{C}}HECK-NOT: {{[[][[]#NUMVAR [+] 1[]][]]$}}
+VV-NEXT: {{^           \^$}}
+VV-NEXT: verbose.txt:[[#@LINE-28]]:1: note: scanning from here
+VV-NEXT: {{^}}NUMVAR - 1:41{{$}}
+VV-NEXT: {{^}}^{{$}}
+
 before empty
 
 after empty
@@ -99,7 +132,7 @@ V-NEXT: {{^}}^~~{{$}}
 
 VV-NEXT: verbose.txt:[[@LINE-9]]:19: remark: implicit EOF: expected string found in input
 VV-NEXT: {{C}}HECK-NOT: {{[{][{]z[}][}]yx$}}
-VV-NEXT:   {{^                 \^$}}
+VV-NEXT:   {{^                  \^$}}
 VV-NEXT: verbose.txt:[[@LINE+13]]:1: note: found here
 VV-NOT:  {{.}}
 VV:      {{^\^$}}

Modified: llvm/trunk/unittests/Support/FileCheckTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/FileCheckTest.cpp?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/FileCheckTest.cpp (original)
+++ llvm/trunk/unittests/Support/FileCheckTest.cpp Tue May 14 04:58:30 2019
@@ -14,6 +14,57 @@ namespace {
 
 class FileCheckTest : public ::testing::Test {};
 
+TEST_F(FileCheckTest, NumericVariable) {
+  FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
+  EXPECT_EQ("FOO", FooVar.getName());
+
+  // Defined variable: getValue returns a value, setValue fails and value
+  // remains unchanged.
+  llvm::Optional<uint64_t> Value = FooVar.getValue();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ(42U, *Value);
+  EXPECT_TRUE(FooVar.setValue(43));
+  Value = FooVar.getValue();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ(42U, *Value);
+
+  // Clearing variable: getValue fails, clearValue again fails.
+  EXPECT_FALSE(FooVar.clearValue());
+  Value = FooVar.getValue();
+  EXPECT_FALSE(Value);
+  EXPECT_TRUE(FooVar.clearValue());
+
+  // Undefined variable: setValue works, getValue returns value set.
+  EXPECT_FALSE(FooVar.setValue(43));
+  Value = FooVar.getValue();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ(43U, *Value);
+}
+
+uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
+
+TEST_F(FileCheckTest, NumExpr) {
+  FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
+  FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &FooVar, 18);
+
+  // Defined variable: eval returns right value, no undefined variable
+  // returned.
+  llvm::Optional<uint64_t> Value = NumExpr.eval();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ(60U, *Value);
+  StringRef UndefVar = NumExpr.getUndefVarName();
+  EXPECT_EQ("", UndefVar);
+
+  // Undefined variable: eval fails, undefined variable returned. We call
+  // getUndefVarName first to check that it can be called without calling
+  // eval() first.
+  FooVar.clearValue();
+  UndefVar = NumExpr.getUndefVarName();
+  EXPECT_EQ("FOO", UndefVar);
+  Value = NumExpr.eval();
+  EXPECT_FALSE(Value);
+}
+
 TEST_F(FileCheckTest, ValidVarNameStart) {
   EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('a'));
   EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('G'));
@@ -90,22 +141,38 @@ TEST_F(FileCheckTest, ParseVar) {
   EXPECT_EQ(TrailIdx, VarName.size() - 1);
 }
 
+static StringRef bufferize(SourceMgr &SM, StringRef Str) {
+  std::unique_ptr<MemoryBuffer> Buffer =
+      MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
+  StringRef StrBufferRef = Buffer->getBuffer();
+  SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
+  return StrBufferRef;
+}
+
 class ExprTester {
 private:
   SourceMgr SM;
+  FileCheckRequest Req;
   FileCheckPatternContext Context;
   FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
 
 public:
+  ExprTester() {
+    std::vector<std::string> GlobalDefines;
+    GlobalDefines.emplace_back(std::string("#FOO=42"));
+    Context.defineCmdlineVariables(GlobalDefines, SM);
+    // Call ParsePattern to have @LINE defined.
+    P.ParsePattern("N/A", "CHECK", SM, 1, Req);
+  }
+
   bool parseExpect(std::string &VarName, std::string &Trailer) {
+    bool IsPseudo = VarName[0] == '@';
     std::string NameTrailer = VarName + Trailer;
-    std::unique_ptr<MemoryBuffer> Buffer =
-        MemoryBuffer::getMemBufferCopy(NameTrailer, "TestBuffer");
-    StringRef NameTrailerRef = Buffer->getBuffer();
-    SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
+    StringRef NameTrailerRef = bufferize(SM, NameTrailer);
     StringRef VarNameRef = NameTrailerRef.substr(0, VarName.size());
     StringRef TrailerRef = NameTrailerRef.substr(VarName.size());
-    return P.parseNumericExpression(VarNameRef, TrailerRef, SM) == nullptr;
+    return P.parseNumericExpression(VarNameRef, IsPseudo, TrailerRef, SM) ==
+           nullptr;
   }
 };
 
@@ -121,6 +188,14 @@ TEST_F(FileCheckTest, ParseExpr) {
   Trailer = "";
   EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
 
+  // Defined variable.
+  VarName = "FOO";
+  EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
+
+  // Undefined variable.
+  VarName = "UNDEF";
+  EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
+
   // Wrong Pseudovar.
   VarName = "@FOO";
   EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
@@ -153,19 +228,38 @@ TEST_F(FileCheckTest, Substitution) {
   GlobalDefines.emplace_back(std::string("FOO=BAR"));
   Context.defineCmdlineVariables(GlobalDefines, SM);
 
-  FileCheckPatternSubstitution Substitution =
+  // Substitution of undefined pattern variable fails.
+  FileCheckPatternSubstitution PatternSubstitution =
       FileCheckPatternSubstitution(&Context, "VAR404", 42);
-  EXPECT_FALSE(Substitution.getResult());
+  EXPECT_FALSE(PatternSubstitution.getResult());
 
-  FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
-  Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
-  llvm::Optional<std::string> Value = Substitution.getResult();
+  // Substitutions of defined pseudo and non-pseudo numeric variables return
+  // the right value.
+  FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
+  FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10);
+  FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0);
+  FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3);
+  FileCheckPatternSubstitution SubstitutionLine =
+      FileCheckPatternSubstitution(&Context, "@LINE", &NumExprLine, 12);
+  FileCheckPatternSubstitution SubstitutionN =
+      FileCheckPatternSubstitution(&Context, "N", &NumExprN, 30);
+  llvm::Optional<std::string> Value = SubstitutionLine.getResult();
   EXPECT_TRUE(Value);
   EXPECT_EQ("42", *Value);
+  Value = SubstitutionN.getResult();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ("13", *Value);
 
+  // Substitution of undefined numeric variable fails.
+  LineVar.clearValue();
+  EXPECT_FALSE(SubstitutionLine.getResult());
+  NVar.clearValue();
+  EXPECT_FALSE(SubstitutionN.getResult());
+
+  // Substitution of defined pattern variable returns the right value.
   FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
-  Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
-  Value = Substitution.getResult();
+  PatternSubstitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
+  Value = PatternSubstitution.getResult();
   EXPECT_TRUE(Value);
   EXPECT_EQ("BAR", *Value);
 }
@@ -177,19 +271,32 @@ TEST_F(FileCheckTest, UndefVars) {
   GlobalDefines.emplace_back(std::string("FOO=BAR"));
   Context.defineCmdlineVariables(GlobalDefines, SM);
 
+  // getUndefVarName() on a pattern variable substitution with an undefined
+  // variable returns that variable.
   FileCheckPatternSubstitution Substitution =
       FileCheckPatternSubstitution(&Context, "VAR404", 42);
   StringRef UndefVar = Substitution.getUndefVarName();
   EXPECT_EQ("VAR404", UndefVar);
 
-  FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
-  Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
+  // getUndefVarName() on a pattern variable substitution with a defined
+  // variable returns an empty string.
+  Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
   UndefVar = Substitution.getUndefVarName();
   EXPECT_EQ("", UndefVar);
 
-  Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
+  // getUndefVarName() on a numeric expression substitution with a defined
+  // variable returns an empty string.
+  FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
+  FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0);
+  Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
   UndefVar = Substitution.getUndefVarName();
   EXPECT_EQ("", UndefVar);
+
+  // getUndefVarName() on a numeric expression substitution with an undefined
+  // variable returns that variable.
+  LineVar.clearValue();
+  UndefVar = Substitution.getUndefVarName();
+  EXPECT_EQ("@LINE", UndefVar);
 }
 
 TEST_F(FileCheckTest, FileCheckContext) {
@@ -197,36 +304,71 @@ TEST_F(FileCheckTest, FileCheckContext)
   std::vector<std::string> GlobalDefines;
   SourceMgr SM;
 
-  // Missing equal sign
+  // Missing equal sign.
   GlobalDefines.emplace_back(std::string("LocalVar"));
   EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#LocalNumVar"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
 
-  // Empty variable
+  // Empty variable name.
   GlobalDefines.clear();
   GlobalDefines.emplace_back(std::string("=18"));
   EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#=18"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
 
-  // Invalid variable
+  // Invalid variable name.
   GlobalDefines.clear();
   GlobalDefines.emplace_back(std::string("18LocalVar=18"));
   EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#18LocalNumVar=18"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+
+  // Name conflict between pattern and numeric variable.
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("LocalVar=18"));
+  GlobalDefines.emplace_back(std::string("#LocalVar=36"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  Cxt = FileCheckPatternContext();
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
+  GlobalDefines.emplace_back(std::string("LocalNumVar=36"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  Cxt = FileCheckPatternContext();
+
+  // Invalid numeric value for numeric variable.
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#LocalNumVar=x"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
 
   // Define local variables from command-line.
   GlobalDefines.clear();
   GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
   GlobalDefines.emplace_back(std::string("EmptyVar="));
+  GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
   bool GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
   EXPECT_FALSE(GotError);
 
   // Check defined variables are present and undefined is absent.
   StringRef LocalVarStr = "LocalVar";
+  StringRef LocalNumVarRef = bufferize(SM, "LocalNumVar");
   StringRef EmptyVarStr = "EmptyVar";
   StringRef UnknownVarStr = "UnknownVar";
   llvm::Optional<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
+  FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt);
+  FileCheckNumExpr *NumExpr =
+      P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM);
   llvm::Optional<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
   llvm::Optional<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
   EXPECT_TRUE(LocalVar);
   EXPECT_EQ(*LocalVar, "FOO");
+  EXPECT_TRUE(NumExpr);
+  llvm::Optional<uint64_t> NumExprVal = NumExpr->eval();
+  EXPECT_TRUE(NumExprVal);
+  EXPECT_EQ(*NumExprVal, 18U);
   EXPECT_TRUE(EmptyVar);
   EXPECT_EQ(*EmptyVar, "");
   EXPECT_FALSE(UnknownVar);
@@ -235,21 +377,46 @@ TEST_F(FileCheckTest, FileCheckContext)
   Cxt.clearLocalVars();
   LocalVar = Cxt.getPatternVarValue(LocalVarStr);
   EXPECT_FALSE(LocalVar);
+  // Check a numeric expression's evaluation fails if called after clearing of
+  // local variables, if it was created before. This is important because local
+  // variable clearing due to --enable-var-scope happens after numeric
+  // expressions are linked to the numeric variables they use.
+  EXPECT_FALSE(NumExpr->eval());
+  P = FileCheckPattern(Check::CheckPlain, &Cxt);
+  NumExpr =
+      P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM);
+  EXPECT_FALSE(NumExpr);
   EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
   EXPECT_FALSE(EmptyVar);
 
   // Redefine global variables and check variables are defined again.
   GlobalDefines.emplace_back(std::string("$GlobalVar=BAR"));
+  GlobalDefines.emplace_back(std::string("#$GlobalNumVar=36"));
   GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
   EXPECT_FALSE(GotError);
   StringRef GlobalVarStr = "$GlobalVar";
+  StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");
   llvm::Optional<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
   EXPECT_TRUE(GlobalVar);
   EXPECT_EQ(*GlobalVar, "BAR");
+  P = FileCheckPattern(Check::CheckPlain, &Cxt);
+  NumExpr =
+      P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
+  EXPECT_TRUE(NumExpr);
+  NumExprVal = NumExpr->eval();
+  EXPECT_TRUE(NumExprVal);
+  EXPECT_EQ(*NumExprVal, 36U);
 
   // Clear local variables and check global variables remain defined.
   Cxt.clearLocalVars();
   GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
   EXPECT_TRUE(GlobalVar);
+  P = FileCheckPattern(Check::CheckPlain, &Cxt);
+  NumExpr =
+      P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
+  EXPECT_TRUE(NumExpr);
+  NumExprVal = NumExpr->eval();
+  EXPECT_TRUE(NumExprVal);
+  EXPECT_EQ(*NumExprVal, 36U);
 }
 } // namespace




More information about the llvm-commits mailing list