[llvm] r360578 - FileCheck [5/12]: Introduce regular numeric variables

Thomas Preud'homme via llvm-commits llvm-commits at lists.llvm.org
Mon May 13 05:39:08 PDT 2019


Author: thopre
Date: Mon May 13 05:39:08 2019
New Revision: 360578

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

Summary:
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch introduces regular numeric
variables which can be set on the command-line.

This commit introduces regular numeric variable that can be set on the
command-line with the -D option to a numeric value. They can then be
used in CHECK patterns in numeric expression with the same shape as
@LINE numeric expression, ie. VAR, VAR+offset or VAR-offset where offset
is an integer literal.

The commit also enable strict whitespace in the verbose.txt testcase to
check that the position or the location diagnostics. It fixes one of the
existing CHECK in the process which was not accurately testing a
location diagnostic (ie. the diagnostic was correct, not the CHECK).

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=360578&r1=360577&r2=360578&view=diff
==============================================================================
--- llvm/trunk/docs/CommandGuide/FileCheck.rst (original)
+++ llvm/trunk/docs/CommandGuide/FileCheck.rst Mon May 13 05:39:08 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=360578&r1=360577&r2=360578&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FileCheck.h (original)
+++ llvm/trunk/include/llvm/Support/FileCheck.h Mon May 13 05:39:08 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=360578&r1=360577&r2=360578&view=diff
==============================================================================
--- llvm/trunk/lib/Support/FileCheck.cpp (original)
+++ llvm/trunk/lib/Support/FileCheck.cpp Mon May 13 05:39:08 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=360578&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt (added)
+++ llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt Mon May 13 05:39:08 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=360578&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-defines.txt (added)
+++ llvm/trunk/test/FileCheck/numeric-defines.txt Mon May 13 05:39:08 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=360578&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-expression.txt (added)
+++ llvm/trunk/test/FileCheck/numeric-expression.txt Mon May 13 05:39:08 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=360578&r1=360577&r2=360578&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt (original)
+++ llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt Mon May 13 05:39:08 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=360577&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=360578&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/var-scope.txt (added)
+++ llvm/trunk/test/FileCheck/var-scope.txt Mon May 13 05:39:08 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=360578&r1=360577&r2=360578&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/verbose.txt (original)
+++ llvm/trunk/test/FileCheck/verbose.txt Mon May 13 05:39:08 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=360578&r1=360577&r2=360578&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/FileCheckTest.cpp (original)
+++ llvm/trunk/unittests/Support/FileCheckTest.cpp Mon May 13 05:39:08 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