[llvm] r366860 - FileCheck [8/12]: Define numeric var from expr

Thomas Preud'homme via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 24 00:36:41 PDT 2019


Hi Douglas,

Thanks for letting me know. Do you know what compiler is that /usr/local/bin/c++? I think adding an std::move should fix the issue but I'd like to beable to reproduce the issue to make sure it will fix the issue.

Best regards,

Thomas
________________________________
From: douglas.yung at sony.com <douglas.yung at sony.com>
Sent: 24 July 2019 08:23
To: Thomas Preud'homme <thomasp at graphcore.ai>
Cc: llvm-commits at lists.llvm.org <llvm-commits at lists.llvm.org>
Subject: RE: [llvm] r366860 - FileCheck [8/12]: Define numeric var from expr

Hi Thomas,

Your commit is causing build failures on quite a few of the build bots:

http://lab.llvm.org:8011/builders/clang-cmake-aarch64-quick/builds/19358

[4/2175] Building CXX object lib/Support/CMakeFiles/LLVMSupport.dir/FileCheck.cpp.o
FAILED: lib/Support/CMakeFiles/LLVMSupport.dir/FileCheck.cpp.o
/usr/local/bin/c++   -DGTEST_HAS_RTTI=0 -D_DEBUG -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Ilib/Support -I/home/buildslave/buildslave/clang-cmake-aarch64-quick/llvm/lib/Support -I/usr/include/libxml2 -Iinclude -I/home/buildslave/buildslave/clang-cmake-aarch64-quick/llvm/include -fPIC -fvisibility-inlines-hidden -Werror=date-time -std=c++11 -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-maybe-uninitialized -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wno-comment -fdiagnostics-color -ffunction-sections -fdata-sections -O3    -UNDEBUG  -fno-exceptions -fno-rtti -MMD -MT lib/Support/CMakeFiles/LLVMSupport.dir/FileCheck.cpp.o -MF lib/Support/CMakeFiles/LLVMSupport.dir/FileCheck.cpp.o.d -o lib/Support/CMakeFiles/LLVMSupport.dir/FileCheck.cpp.o -c /home/buildslave/buildslave/clang-cmake-aarch64-quick/llvm/lib/Support/FileCheck.cpp
/home/buildslave/buildslave/clang-cmake-aarch64-quick/llvm/lib/Support/FileCheck.cpp: In static member function ‘static llvm::Expected<std::unique_ptr<llvm::FileCheckExpressionAST> > llvm::FileCheckPattern::parseNumericSubstitutionBlock(llvm::StringRef, llvm::Optional<llvm::FileCheckNumericVariable*>&, bool, llvm::Optional<long unsigned int>, llvm::FileCheckPatternContext*, const llvm::SourceMgr&)’:
/home/buildslave/buildslave/clang-cmake-aarch64-quick/llvm/lib/Support/FileCheck.cpp:345:10: error: could not convert ‘ExpressionAST’ from ‘std::unique_ptr<llvm::FileCheckExpressionAST>’ to ‘llvm::Expected<std::unique_ptr<llvm::FileCheckExpressionAST> >’
   return ExpressionAST;
          ^~~~~~~~~~~~~

Can you take a look?

Douglas Yung

-----Original Message-----
From: llvm-commits <llvm-commits-bounces at lists.llvm.org> On Behalf Of Thomas Preud'homme via llvm-commits
Sent: Tuesday, July 23, 2019 15:42
To: llvm-commits at lists.llvm.org
Subject: [llvm] r366860 - FileCheck [8/12]: Define numeric var from expr

Author: thopre
Date: Tue Jul 23 15:41:38 2019
New Revision: 366860

URL: http://llvm.org/viewvc/llvm-project?rev=366860&view=rev
Log:
FileCheck [8/12]: Define numeric var from expr

Summary:
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch lift the restriction for a
numeric expression to either be a variable definition or a numeric
expression to try to match.

This commit allows a numeric variable to be set to the result of the
evaluation of a numeric expression after it has been matched
successfully. When it happens, the variable is allowed to be used on
the same line since its value is known at match time.

It also makes use of this possibility to reuse the parsing code to
parse a command-line definition by crafting a mirror string of the
-D option with the equal sign replaced by a colon sign, e.g. for option
'-D#NUMVAL=10' it creates the string
'-D#NUMVAL=10 (parsed as [[#NUMVAL:10]])' where the numeric expression
is parsed to define NUMVAL. This result in a few tests needing updating
for the location diagnostics on top of the tests for the new feature.

It also enables empty numeric expression which match any number without
defining a variable. This is done here rather than in commit #5 of the
patch series because it requires to dissociate automatic regex insertion
in RegExStr from variable definition which would make commit #5 even
bigger than it already is.

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/D60388

Modified:
    llvm/trunk/docs/CommandGuide/FileCheck.rst
    llvm/trunk/include/llvm/Support/FileCheck.h
    llvm/trunk/lib/Support/FileCheck.cpp
    llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt
    llvm/trunk/test/FileCheck/numeric-defines.txt
    llvm/trunk/test/FileCheck/numeric-expression.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=366860&r1=366859&r2=366860&view=diff
==============================================================================
--- llvm/trunk/docs/CommandGuide/FileCheck.rst (original)
+++ llvm/trunk/docs/CommandGuide/FileCheck.rst Tue Jul 23 15:41:38 2019
@@ -107,12 +107,12 @@ 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 EXPRESSION>
+.. option:: -D#<NUMVAR>=<NUMERIC EXPRESSION>

   Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating
-  ``<VALUE EXPRESSION>`` that can be used in ``CHECK:`` lines. See section
-  ``FileCheck Numeric Variables and Expressions`` for details on the format
-  and meaning of ``<VALUE EXPRESSION>``.
+  ``<NUMERIC EXPRESSION>`` that can be used in ``CHECK:`` lines. See section
+  ``FileCheck Numeric Variables and Expressions`` for details on supported
+  numeric expressions.

 .. option:: -version

@@ -625,11 +625,27 @@ but would not match the text:

 due to ``7`` being unequal to ``5 + 1``.

+The syntax also supports an empty expression, equivalent to writing {{[0-9]+}},
+for cases where the input must contain a numeric value but the value itself
+does not matter:
+
+.. code-block:: gas
+
+    ; CHECK-NOT: mov r0, r[[#]]
+
+to check that a value is synthesized rather than moved around.
+
+A numeric variable can also be defined to the result of a numeric expression,
+in which case the numeric expression is checked and if verified the variable is
+assigned to the value. The unified syntax for both defining numeric variables
+and checking a numeric expression is thus ``[[#<NUMVAR>: <expr>]]`` with each
+element as described previously.
+
 The ``--enable-var-scope`` option has the same effect on numeric variables as
 on string variables.

 Important note: In its current implementation, an expression cannot use a
-numeric variable defined on the same line.
+numeric variable with a non-empty expression defined on the same line.

 FileCheck Pseudo Numeric Variables
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Modified: llvm/trunk/include/llvm/Support/FileCheck.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FileCheck.h?rev=366860&r1=366859&r2=366860&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FileCheck.h (original)
+++ llvm/trunk/include/llvm/Support/FileCheck.h Tue Jul 23 15:41:38 2019
@@ -94,6 +94,11 @@ private:
   /// Name of the numeric variable.
   StringRef Name;

+  /// Pointer to expression defining this numeric variable. Null for pseudo
+  /// variable whose value is known at parse time (e.g. @LINE pseudo variable)
+  /// or cleared local variable.
+  FileCheckExpressionAST *ExpressionAST;
+
   /// Value of numeric variable, if defined, or None otherwise.
   Optional<uint64_t> Value;

@@ -104,10 +109,14 @@ private:

 public:
   /// Constructor for a variable \p Name defined at line \p DefLineNumber or
-  /// defined before input is parsed if DefLineNumber is None.
+  /// defined before input is parsed if \p DefLineNumber is None. If not null,
+  /// the value set with setValue must match the result of evaluating
+  /// \p ExpressionAST.
   FileCheckNumericVariable(StringRef Name,
-                           Optional<size_t> DefLineNumber = None)
-      : Name(Name), DefLineNumber(DefLineNumber) {}
+                           Optional<size_t> DefLineNumber = None,
+                           FileCheckExpressionAST *ExpressionAST = nullptr)
+      : Name(Name), ExpressionAST(ExpressionAST), DefLineNumber(DefLineNumber) {
+  }

   /// \returns name of this numeric variable.
   StringRef getName() const { return Name; }
@@ -115,12 +124,25 @@ public:
   /// \returns this variable's value.
   Optional<uint64_t> getValue() const { return Value; }

-  /// Sets value of this numeric variable to \p NewValue.
-  void setValue(uint64_t NewValue) { Value = NewValue; }
+  /// \returns the pointer to the expression defining this numeric variable, if
+  /// any, or null otherwise.
+  FileCheckExpressionAST *getExpressionAST() const { return ExpressionAST; }
+
+  /// \returns whether this variable's value is known when performing the
+  /// substitutions of the line where it is defined.
+  bool isValueKnownAtMatchTime() const;
+
+  /// Sets value of this numeric variable to \p NewValue. Triggers an assertion
+  /// failure if the variable is defined by an expression and the expression
+  /// cannot be evaluated to be equal to \p NewValue.
+  void setValue(uint64_t NewValue);

   /// Clears value of this numeric variable, regardless of whether it is
   /// currently defined or not.
-  void clearValue() { Value = None; }
+  void clearValue() {
+    Value = None;
+    ExpressionAST = nullptr;
+  }

   /// \returns the line number where this variable is defined, if any, or None
   /// if defined before input is parsed.
@@ -507,27 +529,22 @@ public:
   /// \p Str from the variable name.
   static Expected<VariableProperties> parseVariable(StringRef &Str,
                                                     const SourceMgr &SM);
-  /// Parses \p Expr for the name of a numeric variable to be defined at line
-  /// \p LineNumber or before input is parsed if \p LineNumber is None.
-  /// \returns a pointer to the class instance representing that variable,
-  /// creating it if needed, or an error holding a diagnostic against \p SM
-  /// should defining such a variable be invalid.
-  static Expected<FileCheckNumericVariable *> parseNumericVariableDefinition(
-      StringRef &Expr, FileCheckPatternContext *Context,
-      Optional<size_t> LineNumber, const SourceMgr &SM);
-  /// Parses \p Expr for a numeric substitution block. Parameter
+  /// Parses \p Expr for a numeric substitution block at line \p LineNumber,
+  /// or before input is parsed if \p LineNumber is None. Parameter
   /// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE
-  /// expression. \returns a pointer to the class instance representing the AST
-  /// of the expression whose value must be substituted, or an error holding a
-  /// diagnostic against \p SM if parsing fails. If substitution was
-  /// successful, sets \p DefinedNumericVariable to point to the class
-  /// representing the numeric variable being defined in this numeric
+  /// expression and \p Context points to the class instance holding the live
+  /// string and numeric variables. \returns a pointer to the class instance
+  /// representing the AST of the expression whose value must be substitued, or
+  /// an error holding a diagnostic against \p SM if parsing fails. If
+  /// substitution was successful, sets \p DefinedNumericVariable to point to
+  /// the class representing the numeric variable defined in this numeric
   /// substitution block, or None if this block does not define any variable.
-  Expected<std::unique_ptr<FileCheckExpressionAST>>
+  static Expected<std::unique_ptr<FileCheckExpressionAST>>
   parseNumericSubstitutionBlock(
       StringRef Expr,
       Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
-      bool IsLegacyLineExpr, const SourceMgr &SM) const;
+      bool IsLegacyLineExpr, Optional<size_t> LineNumber,
+      FileCheckPatternContext *Context, const SourceMgr &SM);
   /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
   /// instance accordingly.
   ///
@@ -581,28 +598,49 @@ private:
   /// was not found.
   size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);

-  /// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use.
-  /// \returns the pointer to the class instance representing that variable if
-  /// successful, or an error holding a diagnostic against \p SM otherwise.
-  Expected<std::unique_ptr<FileCheckNumericVariableUse>>
+  /// Parses \p Expr for the name of a numeric variable to be defined at line
+  /// \p LineNumber, or before input is parsed if \p LineNumber is None.
+  /// \returns a pointer to the class instance representing that variable,
+  /// creating it if needed, or an error holding a diagnostic against \p SM
+  /// should defining such a variable be invalid.
+  static Expected<FileCheckNumericVariable *> parseNumericVariableDefinition(
+      StringRef &Expr, FileCheckPatternContext *Context,
+      Optional<size_t> LineNumber, FileCheckExpressionAST *ExpressionAST,
+      const SourceMgr &SM);
+  /// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use
+  /// at line \p LineNumber, or before input is parsed if \p LineNumber is
+  /// None. Parameter \p Context points to the class instance holding the live
+  /// string and numeric variables. \returns the pointer to the class instance
+  /// representing that variable if successful, or an error holding a
+  /// diagnostic against \p SM otherwise.
+  static Expected<std::unique_ptr<FileCheckNumericVariableUse>>
   parseNumericVariableUse(StringRef Name, bool IsPseudo,
-                          const SourceMgr &SM) const;
+                          Optional<size_t> LineNumber,
+                          FileCheckPatternContext *Context,
+                          const SourceMgr &SM);
   enum class AllowedOperand { LineVar, Literal, Any };
-  /// Parses \p Expr for use of a numeric operand. Accepts both literal values
-  /// and numeric variables, depending on the value of \p AO. \returns the
-  /// class representing that operand in the AST of the expression or an error
-  /// holding a diagnostic against \p SM otherwise.
-  Expected<std::unique_ptr<FileCheckExpressionAST>>
+  /// Parses \p Expr for use of a numeric operand at line \p LineNumber, or
+  /// before input is parsed if \p LineNumber is None. Accepts both literal
+  /// values and numeric variables, depending on the value of \p AO. Parameter
+  /// \p Context points to the class instance holding the live string and
+  /// numeric variables. \returns the class representing that operand in the
+  /// AST of the expression or an error holding a diagnostic against \p SM
+  /// otherwise.
+  static Expected<std::unique_ptr<FileCheckExpressionAST>>
   parseNumericOperand(StringRef &Expr, AllowedOperand AO,
-                      const SourceMgr &SM) const;
-  /// Parses \p Expr for a binary operation. The left operand of this binary
+                      Optional<size_t> LineNumber,
+                      FileCheckPatternContext *Context, const SourceMgr &SM);
+  /// Parses \p Expr for a binary operation at line \p LineNumber, or before
+  /// input is parsed if \p LineNumber is None. The left operand of this binary
   /// operation is given in \p LeftOp and \p IsLegacyLineExpr indicates whether
-  /// we are parsing a legacy @LINE expression. \returns the class representing
-  /// the binary operation in the AST of the expression, or an error holding a
-  /// diagnostic against \p SM otherwise.
-  Expected<std::unique_ptr<FileCheckExpressionAST>>
+  /// we are parsing a legacy @LINE expression. Parameter \p Context points to
+  /// the class instance holding the live string and numeric variables.
+  /// \returns the class representing the binary operation in the AST of the
+  /// expression, or an error holding a diagnostic against \p SM otherwise.
+  static Expected<std::unique_ptr<FileCheckExpressionAST>>
   parseBinop(StringRef &Expr, std::unique_ptr<FileCheckExpressionAST> LeftOp,
-             bool IsLegacyLineExpr, const SourceMgr &SM) const;
+             bool IsLegacyLineExpr, Optional<size_t> LineNumber,
+             FileCheckPatternContext *Context, const SourceMgr &SM);
 };

 //===----------------------------------------------------------------------===//

Modified: llvm/trunk/lib/Support/FileCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/FileCheck.cpp?rev=366860&r1=366859&r2=366860&view=diff
==============================================================================
--- llvm/trunk/lib/Support/FileCheck.cpp (original)
+++ llvm/trunk/lib/Support/FileCheck.cpp Tue Jul 23 15:41:38 2019
@@ -24,11 +24,38 @@

 using namespace llvm;

+bool FileCheckNumericVariable::isValueKnownAtMatchTime() const {
+  if (Value)
+    return true;
+
+  return ExpressionAST != nullptr;
+}
+
+void FileCheckNumericVariable::setValue(uint64_t NewValue) {
+  if (ExpressionAST != nullptr) {
+    // Caller is expected to call setValue only if substitution was successful.
+    assert(NewValue == cantFail(ExpressionAST->eval(),
+                                "Failed to evaluate associated expression when "
+                                "sanity checking value") &&
+           "Value being set to different from variable evaluation");
+  }
+  Value = NewValue;
+  // Clear pointer to AST to ensure it is not used after the numeric
+  // substitution defining this variable is processed since it's the
+  // substitution that owns the pointer.
+  ExpressionAST = nullptr;
+}
+
 Expected<uint64_t> FileCheckNumericVariableUse::eval() const {
   Optional<uint64_t> Value = NumericVariable->getValue();
   if (Value)
     return *Value;
-  return make_error<FileCheckUndefVarError>(Name);
+
+  FileCheckExpressionAST *ExpressionAST = NumericVariable->getExpressionAST();
+  if (!ExpressionAST)
+    return make_error<FileCheckUndefVarError>(Name);
+
+  return ExpressionAST->eval();
 }

 Expected<uint64_t> FileCheckASTBinop::eval() const {
@@ -114,7 +141,8 @@ char FileCheckNotFoundError::ID = 0;
 Expected<FileCheckNumericVariable *>
 FileCheckPattern::parseNumericVariableDefinition(
     StringRef &Expr, FileCheckPatternContext *Context,
-    Optional<size_t> LineNumber, const SourceMgr &SM) {
+    Optional<size_t> LineNumber, FileCheckExpressionAST *ExpressionAST,
+    const SourceMgr &SM) {
   Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
   if (!ParseVarResult)
     return ParseVarResult.takeError();
@@ -141,14 +169,17 @@ FileCheckPattern::parseNumericVariableDe
   if (VarTableIter != Context->GlobalNumericVariableTable.end())
     DefinedNumericVariable = VarTableIter->second;
   else
-    DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber);
+    DefinedNumericVariable =
+        Context->makeNumericVariable(Name, LineNumber, ExpressionAST);

   return DefinedNumericVariable;
 }

 Expected<std::unique_ptr<FileCheckNumericVariableUse>>
 FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo,
-                                          const SourceMgr &SM) const {
+                                          Optional<size_t> LineNumber,
+                                          FileCheckPatternContext *Context,
+                                          const SourceMgr &SM) {
   if (IsPseudo && !Name.equals("@LINE"))
     return FileCheckErrorDiagnostic::get(
         SM, Name, "invalid pseudo numeric variable '" + Name + "'");
@@ -171,24 +202,29 @@ FileCheckPattern::parseNumericVariableUs
   }

   Optional<size_t> DefLineNumber = NumericVariable->getDefLineNumber();
-  if (DefLineNumber && LineNumber && *DefLineNumber == *LineNumber)
+  if (DefLineNumber && LineNumber && *DefLineNumber == *LineNumber &&
+      !NumericVariable->isValueKnownAtMatchTime())
     return FileCheckErrorDiagnostic::get(
         SM, Name,
-        "numeric variable '" + Name + "' defined on the same line as used");
+        "numeric variable '" + Name +
+            "' defined from input on the same line as used");

   return llvm::make_unique<FileCheckNumericVariableUse>(Name, NumericVariable);
 }

 Expected<std::unique_ptr<FileCheckExpressionAST>>
 FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO,
-                                      const SourceMgr &SM) const {
+                                      Optional<size_t> LineNumber,
+                                      FileCheckPatternContext *Context,
+                                      const SourceMgr &SM) {
   if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {
     // Try to parse as a numeric variable use.
     Expected<FileCheckPattern::VariableProperties> ParseVarResult =
         parseVariable(Expr, SM);
     if (ParseVarResult)
       return parseNumericVariableUse(ParseVarResult->Name,
-                                     ParseVarResult->IsPseudo, SM);
+                                     ParseVarResult->IsPseudo, LineNumber,
+                                     Context, SM);
     if (AO == AllowedOperand::LineVar)
       return ParseVarResult.takeError();
     // Ignore the error and retry parsing as a literal.
@@ -212,10 +248,10 @@ static uint64_t sub(uint64_t LeftOp, uin
   return LeftOp - RightOp;
 }

-Expected<std::unique_ptr<FileCheckExpressionAST>>
-FileCheckPattern::parseBinop(StringRef &Expr,
-                             std::unique_ptr<FileCheckExpressionAST> LeftOp,
-                             bool IsLegacyLineExpr, const SourceMgr &SM) const {
+Expected<std::unique_ptr<FileCheckExpressionAST>> FileCheckPattern::parseBinop(
+    StringRef &Expr, std::unique_ptr<FileCheckExpressionAST> LeftOp,
+    bool IsLegacyLineExpr, Optional<size_t> LineNumber,
+    FileCheckPatternContext *Context, const SourceMgr &SM) {
   Expr = Expr.ltrim(SpaceChars);
   if (Expr.empty())
     return std::move(LeftOp);
@@ -246,7 +282,7 @@ FileCheckPattern::parseBinop(StringRef &
   AllowedOperand AO =
       IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any;
   Expected<std::unique_ptr<FileCheckExpressionAST>> RightOpResult =
-      parseNumericOperand(Expr, AO, SM);
+      parseNumericOperand(Expr, AO, LineNumber, Context, SM);
   if (!RightOpResult)
     return RightOpResult;

@@ -259,50 +295,54 @@ Expected<std::unique_ptr<FileCheckExpres
 FileCheckPattern::parseNumericSubstitutionBlock(
     StringRef Expr,
     Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
-    bool IsLegacyLineExpr, const SourceMgr &SM) const {
-  // Parse the numeric variable definition.
+    bool IsLegacyLineExpr, Optional<size_t> LineNumber,
+    FileCheckPatternContext *Context, const SourceMgr &SM) {
+  std::unique_ptr<FileCheckExpressionAST> ExpressionAST = nullptr;
+  StringRef DefExpr = StringRef();
   DefinedNumericVariable = None;
+  // Save variable definition expression if any.
   size_t DefEnd = Expr.find(':');
   if (DefEnd != StringRef::npos) {
-    StringRef DefExpr = Expr.substr(0, DefEnd);
-    StringRef UseExpr = Expr.substr(DefEnd + 1);
+    DefExpr = Expr.substr(0, DefEnd);
+    Expr = Expr.substr(DefEnd + 1);
+  }

-    UseExpr = UseExpr.ltrim(SpaceChars);
-    if (!UseExpr.empty())
-      return FileCheckErrorDiagnostic::get(
-          SM, UseExpr,
-          "unexpected string after variable definition: '" + UseExpr + "'");
+  // Parse the expression itself.
+  Expr = Expr.ltrim(SpaceChars);
+  if (!Expr.empty()) {
+    // The first operand in a legacy @LINE expression is always the @LINE
+    // pseudo variable.
+    AllowedOperand AO =
+        IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;
+    Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult =
+        parseNumericOperand(Expr, AO, LineNumber, Context, SM);
+    while (ParseResult && !Expr.empty()) {
+      ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr,
+                               LineNumber, Context, SM);
+      // Legacy @LINE expressions only allow 2 operands.
+      if (ParseResult && IsLegacyLineExpr && !Expr.empty())
+        return FileCheckErrorDiagnostic::get(
+            SM, Expr,
+            "unexpected characters at end of expression '" + Expr + "'");
+    }
+    if (!ParseResult)
+      return ParseResult;
+    ExpressionAST = std::move(*ParseResult);
+  }

+  // Parse the numeric variable definition.
+  if (DefEnd != StringRef::npos) {
     DefExpr = DefExpr.ltrim(SpaceChars);
     Expected<FileCheckNumericVariable *> ParseResult =
-        parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM);
+        parseNumericVariableDefinition(DefExpr, Context, LineNumber,
+                                       ExpressionAST.get(), SM);
+
     if (!ParseResult)
       return ParseResult.takeError();
     DefinedNumericVariable = *ParseResult;
-
-    return nullptr;
   }

-  // Parse the expression itself.
-  Expr = Expr.ltrim(SpaceChars);
-  // The first operand in a legacy @LINE expression is always the @LINE pseudo
-  // variable.
-  AllowedOperand AO =
-      IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;
-  Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult =
-      parseNumericOperand(Expr, AO, SM);
-  while (ParseResult && !Expr.empty()) {
-    ParseResult =
-        parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, SM);
-    // Legacy @LINE expressions only allow 2 operands.
-    if (ParseResult && IsLegacyLineExpr && !Expr.empty())
-      return FileCheckErrorDiagnostic::get(
-          SM, Expr,
-          "unexpected characters at end of expression '" + Expr + "'");
-  }
-  if (!ParseResult)
-    return ParseResult;
-  return std::move(*ParseResult);
+  return ExpressionAST;
 }

 bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
@@ -385,14 +425,15 @@ bool FileCheckPattern::parsePattern(Stri
       continue;
     }

-    // String and numeric substitution blocks. String substitution blocks come
+    // String and numeric substitution blocks. Pattern substitution blocks come
     // in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some
     // other regex) and assigns it to the string variable 'foo'. The latter
-    // substitutes foo's value. Numeric substitution blocks work the same way
-    // as string ones, but start with a '#' sign after the double brackets.
-    // Both string and numeric variable names must satisfy the regular
-    // expression "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch
-    // some common errors.
+    // substitutes foo's value. Numeric substitution blocks recognize the same
+    // form as string ones, but start with a '#' sign after the double
+    // brackets. They also accept a combined form which sets a numeric variable
+    // to the evaluation of an expression. Both string and numeric variable
+    // names 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
@@ -413,6 +454,7 @@ bool FileCheckPattern::parsePattern(Stri
       PatternStr = UnparsedPatternStr.substr(End + 2);

       bool IsDefinition = false;
+      bool SubstNeeded = false;
       // Whether the substitution block is a legacy use of @LINE with string
       // substitution block syntax.
       bool IsLegacyLineExpr = false;
@@ -443,6 +485,7 @@ bool FileCheckPattern::parsePattern(Stri
         bool IsPseudo = ParseVarResult->IsPseudo;

         IsDefinition = (VarEndIdx != StringRef::npos);
+        SubstNeeded = !IsDefinition;
         if (IsDefinition) {
           if ((IsPseudo || !MatchStr.consume_front(":"))) {
             SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
@@ -477,22 +520,61 @@ bool FileCheckPattern::parsePattern(Stri
       if (IsNumBlock) {
         Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult =
             parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
-                                          IsLegacyLineExpr, SM);
+                                          IsLegacyLineExpr, LineNumber, Context,
+                                          SM);
         if (!ParseResult) {
           logAllUnhandledErrors(ParseResult.takeError(), errs());
           return true;
         }
         ExpressionAST = std::move(*ParseResult);
+        SubstNeeded = ExpressionAST != nullptr;
         if (DefinedNumericVariable) {
           IsDefinition = true;
           DefName = (*DefinedNumericVariable)->getName();
-          MatchRegexp = StringRef("[0-9]+");
-        } else
+        }
+        if (SubstNeeded)
           SubstStr = MatchStr;
+        else
+          MatchRegexp = "[0-9]+";
+      }
+
+      // Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].
+      if (IsDefinition) {
+        RegExStr += '(';
+        ++SubstInsertIdx;
+
+        if (IsNumBlock) {
+          FileCheckNumericVariableMatch NumericVariableDefinition = {
+              *DefinedNumericVariable, CurParen};
+          NumericVariableDefs[DefName] = NumericVariableDefinition;
+          // This store is done here rather than in match() to allow
+          // parseNumericVariableUse() to get the pointer to the class instance
+          // of the right variable definition corresponding to a given numeric
+          // variable use.
+          Context->GlobalNumericVariableTable[DefName] =
+              *DefinedNumericVariable;
+        } else {
+          VariableDefs[DefName] = CurParen;
+          // Mark string variable as defined to detect collisions between
+          // string and numeric variables in parseNumericVariableUse() and
+          // defineCmdlineVariables() when the latter is created later than the
+          // former. We cannot reuse GlobalVariableTable for this by populating
+          // it with an empty string since we would then lose the ability to
+          // detect the use of an undefined variable in match().
+          Context->DefinedVariableTable[DefName] = true;
+        }
+
+        ++CurParen;
       }

+      if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM))
+        return true;
+
+      if (IsDefinition)
+        RegExStr += ')';
+
       // Handle substitutions: [[foo]] and [[#<foo expr>]].
-      if (!IsDefinition) {
+      if (SubstNeeded) {
         // Handle substitution of string variables that were defined earlier on
         // the same line by emitting a backreference. Expressions do not
         // support substituting a numeric variable defined on the same line.
@@ -515,37 +597,7 @@ bool FileCheckPattern::parsePattern(Stri
                   : Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
           Substitutions.push_back(Substitution);
         }
-        continue;
-      }
-
-      // Handle variable definitions: [[<def>:(...)]] and
-      // [[#(...)<def>:(...)]].
-      if (IsNumBlock) {
-        FileCheckNumericVariableMatch NumericVariableDefinition = {
-            *DefinedNumericVariable, CurParen};
-        NumericVariableDefs[DefName] = NumericVariableDefinition;
-        // This store is done here rather than in match() to allow
-        // parseNumericVariableUse() to get the pointer to the class instance
-        // of the right variable definition corresponding to a given numeric
-        // variable use.
-        Context->GlobalNumericVariableTable[DefName] = *DefinedNumericVariable;
-      } else {
-        VariableDefs[DefName] = CurParen;
-        // Mark the string variable as defined to detect collisions between
-        // string and numeric variables in parseNumericVariableUse() and
-        // DefineCmdlineVariables() when the latter is created later than the
-        // former. We cannot reuse GlobalVariableTable for this by populating
-        // it with an empty string since we would then lose the ability to
-        // detect the use of an undefined variable in match().
-        Context->DefinedVariableTable[DefName] = true;
       }
-      RegExStr += '(';
-      ++CurParen;
-
-      if (AddRegExToRegEx(MatchRegexp, CurParen, SM))
-        return true;
-
-      RegExStr += ')';
     }

     // Handle fixed string matches.
@@ -1745,11 +1797,32 @@ Error FileCheckPatternContext::defineCmd
   unsigned I = 0;
   Error Errs = Error::success();
   std::string CmdlineDefsDiag;
-  StringRef Prefix1 = "Global define #";
-  StringRef Prefix2 = ": ";
-  for (StringRef CmdlineDef : CmdlineDefines)
-    CmdlineDefsDiag +=
-        (Prefix1 + Twine(++I) + Prefix2 + CmdlineDef + "\n").str();
+  SmallVector<std::pair<size_t, size_t>, 4> CmdlineDefsIndices;
+  for (StringRef CmdlineDef : CmdlineDefines) {
+    std::string DefPrefix = ("Global define #" + Twine(++I) + ": ").str();
+    size_t EqIdx = CmdlineDef.find('=');
+    if (EqIdx == StringRef::npos) {
+      CmdlineDefsIndices.push_back(std::make_pair(CmdlineDefsDiag.size(), 0));
+      continue;
+    }
+    // Numeric variable definition.
+    if (CmdlineDef[0] == '#') {
+      // Append a copy of the command-line definition adapted to use the same
+      // format as in the input file to be able to reuse
+      // parseNumericSubstitutionBlock.
+      CmdlineDefsDiag += (DefPrefix + CmdlineDef + " (parsed as: [[").str();
+      std::string SubstitutionStr = CmdlineDef;
+      SubstitutionStr[EqIdx] = ':';
+      CmdlineDefsIndices.push_back(
+          std::make_pair(CmdlineDefsDiag.size(), SubstitutionStr.size()));
+      CmdlineDefsDiag += (SubstitutionStr + Twine("]])\n")).str();
+    } else {
+      CmdlineDefsDiag += DefPrefix;
+      CmdlineDefsIndices.push_back(
+          std::make_pair(CmdlineDefsDiag.size(), CmdlineDef.size()));
+      CmdlineDefsDiag += (CmdlineDef + "\n").str();
+    }
+  }

   // Create a buffer with fake command line content in order to display
   // parsing diagnostic with location information and point to the
@@ -1759,14 +1832,10 @@ Error FileCheckPatternContext::defineCmd
   StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();
   SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc());

-  SmallVector<StringRef, 4> CmdlineDefsDiagVec;
-  CmdlineDefsDiagRef.split(CmdlineDefsDiagVec, '\n', -1 /*MaxSplit*/,
-                           false /*KeepEmpty*/);
-  for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) {
-    unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
-    StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
-    size_t EqIdx = CmdlineDef.find('=');
-    if (EqIdx == StringRef::npos) {
+  for (std::pair<size_t, size_t> CmdlineDefIndices : CmdlineDefsIndices) {
+    StringRef CmdlineDef = CmdlineDefsDiagRef.substr(CmdlineDefIndices.first,
+                                                     CmdlineDefIndices.second);
+    if (CmdlineDef.empty()) {
       Errs = joinErrors(
           std::move(Errs),
           FileCheckErrorDiagnostic::get(
@@ -1776,31 +1845,35 @@ Error FileCheckPatternContext::defineCmd

     // Numeric variable definition.
     if (CmdlineDef[0] == '#') {
-      StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
-      Expected<FileCheckNumericVariable *> ParseResult =
-          FileCheckPattern::parseNumericVariableDefinition(CmdlineName, this,
-                                                           None, SM);
-      if (!ParseResult) {
-        Errs = joinErrors(std::move(Errs), ParseResult.takeError());
+      // Now parse the definition both to check that the syntax is correct and
+      // to create the necessary class instance.
+      StringRef CmdlineDefExpr = CmdlineDef.substr(1);
+      Optional<FileCheckNumericVariable *> DefinedNumericVariable;
+      Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionASTResult =
+          FileCheckPattern::parseNumericSubstitutionBlock(
+              CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM);
+      if (!ExpressionASTResult) {
+        Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError());
         continue;
       }
-
-      StringRef CmdlineVal = CmdlineDef.substr(EqIdx + 1);
-      uint64_t Val;
-      if (CmdlineVal.getAsInteger(10, Val)) {
-        Errs = joinErrors(std::move(Errs),
-                          FileCheckErrorDiagnostic::get(
-                              SM, CmdlineVal,
-                              "invalid value in numeric variable definition '" +
-                                  CmdlineVal + "'"));
+      std::unique_ptr<FileCheckExpressionAST> ExpressionAST =
+          std::move(*ExpressionASTResult);
+      // Now evaluate the expression whose value this variable should be set
+      // to, since the expression of a command-line variable definition should
+      // only use variables defined earlier on the command-line. If not, this
+      // is an error and we report it.
+      Expected<uint64_t> Value = ExpressionAST->eval();
+      if (!Value) {
+        Errs = joinErrors(std::move(Errs), Value.takeError());
         continue;
       }
-      FileCheckNumericVariable *DefinedNumericVariable = *ParseResult;
-      DefinedNumericVariable->setValue(Val);
+
+      assert(DefinedNumericVariable && "No variable defined");
+      (*DefinedNumericVariable)->setValue(*Value);

       // Record this variable definition.
-      GlobalNumericVariableTable[DefinedNumericVariable->getName()] =
-          DefinedNumericVariable;
+      GlobalNumericVariableTable[(*DefinedNumericVariable)->getName()] =
+          *DefinedNumericVariable;
     } else {
       // String variable definition.
       std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
@@ -1837,7 +1910,7 @@ Error FileCheckPatternContext::defineCmd
       }
       GlobalVariableTable.insert(CmdlineNameVal);
       // Mark the string variable as defined to detect collisions between
-      // string and numeric variables in DefineCmdlineVariables when the latter
+      // string and numeric variables in defineCmdlineVariables when the latter
       // is created later than the former. We cannot reuse GlobalVariableTable
       // for this by populating it with an empty string since we would then
       // lose the ability to detect the use of an undefined variable in

Modified: llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt?rev=366860&r1=366859&r2=366860&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt (original)
+++ llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt Tue Jul 23 15:41:38 2019
@@ -4,30 +4,22 @@
 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 variable name
-NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10
-NUMERRCLIFMT-NEXT: {{^                   \^$}}
+NUMERRCLIFMT: Global defines:1:46: error: invalid variable name
+NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10 (parsed as: {{\[\[#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: definition of pseudo numeric variable unsupported
-NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10
-NUMERRCLIPSEUDO-NEXT: {{^                   \^$}}
+NUMERRCLIPSEUDO: Global defines:1:45: error: definition of pseudo numeric variable unsupported
+NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10 (parsed as: {{\[\[#@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:25: error: unexpected characters after numeric variable name
-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: {{^                          \^$}}
+NUMERRCLITRAIL: Global defines:1:51: error: unexpected characters after numeric variable name
+NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10 (parsed as: {{\[\[#VALUE\+2:10\]\]}})
+NUMERRCLITRAIL-NEXT: {{^                                                  \^$}}

Modified: llvm/trunk/test/FileCheck/numeric-defines.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-defines.txt?rev=366860&r1=366859&r2=366860&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-defines.txt (original)
+++ llvm/trunk/test/FileCheck/numeric-defines.txt Tue Jul 23 15:41:38 2019
@@ -1,22 +1,38 @@
 ; 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 "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 "NUMVAL" equal to "12"
+RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefixes CHECKNUM1,CHECKNUM2 -input-file %s %s
+RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix CHECKNUM1 -input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG1
+RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix CHECKNUM2 -input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG2
+RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix NUMNOT -input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG1
+RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG2
+RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s
+
+Numeric value #1 = 8
+Numeric value #2 = 12
+CHECKNUM1: Numeric value #1 = [[#NUMVAL1]]
+CHECKNUM2: Numeric value #2 = [[#NUMVAL2]]
+NUMNOT-NOT: Numeric value #1 = [[#NUMVAL1]]
+NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]]
+
+NUMERRMSG1: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM1: expected string not found in input
+NUMERRMSG1: defines.txt:1:1: note: scanning from here
+NUMERRMSG1: defines.txt:1:1: note: with "NUMVAL1" equal to "7"
+NUMERRMSG1: defines.txt:[[#@LINE-10]]:1: note: possible intended match here
+
+NUMERRMSG2: defines.txt:[[#@LINE-9]]:12: error: CHECKNUM2: expected string not found in input
+NUMERRMSG2: defines.txt:1:1: note: scanning from here
+NUMERRMSG2: defines.txt:1:1: note: with "NUMVAL2" equal to "8"
+NUMERRMSG2: defines.txt:[[#@LINE-14]]:1: note: possible intended match here
+
+NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
+NOT-NUMERRMSG1: defines.txt:[[#@LINE-18]]:1: note: found here
+NOT-NUMERRMSG1: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL1" equal to "8"
+
+NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
+NOT-NUMERRMSG2: defines.txt:[[#@LINE-21]]:1: note: found here
+NOT-NUMERRMSG2: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL2" equal to "12"

Modified: llvm/trunk/test/FileCheck/numeric-expression.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-expression.txt?rev=366860&r1=366859&r2=366860&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-expression.txt (original)
+++ llvm/trunk/test/FileCheck/numeric-expression.txt Tue Jul 23 15:41:38 2019
@@ -82,6 +82,20 @@ CHECK-LABEL: USE MULTI VAR
 CHECK-NEXT: [[#VAR2:]]
 CHECK-NEXT: [[#VAR1+VAR2]]

+; Numeric expression using a variable defined from a numeric expression.
+DEF EXPR GOOD MATCH
+42
+41 43
+; CHECK-LABEL: DEF EXPR GOOD MATCH
+; CHECK-NEXT: [[# VAR42:VAR1+31]]
+; CHECK-NEXT: [[# VAR41: VAR42-1]] [[# VAR41 + 2]]
+
+; Empty numeric expression.
+EMPTY NUM EXPR
+foo 104 bar
+; CHECK-LABEL: EMPTY NUM EXPR
+; CHECK-NEXT: foo [[#]] bar
+
 ; Numeric expression using undefined variables.
 RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \
 RUN:   | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s
@@ -145,9 +159,9 @@ CLI-STR-CONFLICT-NEXT: {{^
 INPUT-NUM-CONFLICT: numeric-expression.txt:[[#@LINE-7]]:22: error: string variable with name 'STRVAR' already exists
 INPUT-NUM-CONFLICT-NEXT: CONFLICT4: redef2 {{\[\[#STRVAR:\]\]}}
 INPUT-NUM-CONFLICT-NEXT: {{^                     \^$}}
-CLI-NUM-CONFLICT: Global defines:2:20: error: string variable with name 'STRVAR' already exists
-CLI-NUM-CONFLICT-NEXT: Global define #2: #STRVAR=42
-CLI-NUM-CONFLICT-NEXT: {{^                   \^$}}
+CLI-NUM-CONFLICT: Global defines:2:45: error: string variable with name 'STRVAR' already exists
+CLI-NUM-CONFLICT-NEXT: Global define #2: #STRVAR=42 (parsed as: {{\[\[#STRVAR:42\]\]}})
+CLI-NUM-CONFLICT-NEXT: {{^                                            \^$}}

 ; Numeric variable definition with too big value.
 RUN: not FileCheck --check-prefix BIGVAL --input-file %s %s 2>&1 \
@@ -160,3 +174,18 @@ BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]]
 BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: Unable to represent numeric value
 BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000
 BIGVAL-MSG-NEXT: {{^        \^$}}
+
+; Verify that when a variable is set to an expression the expression is still
+; checked.
+RUN: not FileCheck --check-prefix DEF-EXPR-FAIL --input-file %s %s 2>&1 \
+RUN:   | FileCheck --strict-whitespace --check-prefix DEF-EXPR-FAIL-MSG %s
+
+DEF EXPR WRONG MATCH
+20
+43
+DEF-EXPR-FAIL-LABEL: DEF EXPR WRONG MATCH
+DEF-EXPR-FAIL-NEXT: [[# VAR20:]]
+DEF-EXPR-FAIL-NEXT: [[# VAR42: VAR20+22]]
+DEF-EXPR-FAIL-MSG: numeric-expression.txt:[[#@LINE-1]]:21: error: {{D}}EF-EXPR-FAIL-NEXT: is not on the line after the previous match
+DEF-EXPR-FAIL-MSG-NEXT: {{D}}EF-EXPR-FAIL-NEXT: {{\[\[# VAR42: VAR20\+22\]\]}}
+DEF-EXPR-FAIL-MSG-NEXT: {{^                    \^$}}

Modified: llvm/trunk/unittests/Support/FileCheckTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/FileCheckTest.cpp?rev=366860&r1=366859&r2=366860&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/FileCheckTest.cpp (original)
+++ llvm/trunk/unittests/Support/FileCheckTest.cpp Tue Jul 23 15:41:38 2019
@@ -49,15 +49,21 @@ expectUndefErrors(std::unordered_set<std
   EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
 }

+// Return whether Err contains any FileCheckUndefVarError whose associated name
+// is not ExpectedUndefVarName.
 static void expectUndefError(const Twine &ExpectedUndefVarName, Error Err) {
   expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err));
 }

+uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
+
 TEST_F(FileCheckTest, NumericVariable) {
-  // Undefined variable: getValue and eval fail, error returned by eval holds
-  // the name of the undefined variable.
+  // Undefined variable: isValueKnownAtMatchTime returns false, getValue and
+  // eval fail, error returned by eval holds the name of the undefined
+  // variable.
   FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1);
   EXPECT_EQ("FOO", FooVar.getName());
+  EXPECT_FALSE(FooVar.isValueKnownAtMatchTime());
   FileCheckNumericVariableUse FooVarUse =
       FileCheckNumericVariableUse("FOO", &FooVar);
   EXPECT_FALSE(FooVar.getValue());
@@ -67,7 +73,9 @@ TEST_F(FileCheckTest, NumericVariable) {

   FooVar.setValue(42);

-  // Defined variable: getValue and eval return value set.
+  // Defined variable: isValueKnownAtMatchTime returns true, getValue and eval
+  // return value set.
+  EXPECT_TRUE(FooVar.isValueKnownAtMatchTime());
   Optional<uint64_t> Value = FooVar.getValue();
   EXPECT_TRUE(bool(Value));
   EXPECT_EQ(42U, *Value);
@@ -75,24 +83,51 @@ TEST_F(FileCheckTest, NumericVariable) {
   EXPECT_TRUE(bool(EvalResult));
   EXPECT_EQ(42U, *EvalResult);

+  // Variable defined by numeric expression: isValueKnownAtMatchTime
+  // returns true, getValue and eval return value of expression, setValue
+  // clears expression.
+  std::unique_ptr<FileCheckNumericVariableUse> FooVarUsePtr =
+      llvm::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar);
+  std::unique_ptr<FileCheckExpressionLiteral> One =
+      llvm::make_unique<FileCheckExpressionLiteral>(1);
+  FileCheckASTBinop Binop =
+      FileCheckASTBinop(doAdd, std::move(FooVarUsePtr), std::move(One));
+  FileCheckNumericVariable FoobarExprVar =
+      FileCheckNumericVariable("FOOBAR", 2, &Binop);
+  EXPECT_TRUE(FoobarExprVar.isValueKnownAtMatchTime());
+  EXPECT_FALSE(FoobarExprVar.getValue());
+  FileCheckNumericVariableUse FoobarExprVarUse =
+      FileCheckNumericVariableUse("FOOBAR", &FoobarExprVar);
+  EvalResult = FoobarExprVarUse.eval();
+  EXPECT_TRUE(bool(EvalResult));
+  EXPECT_EQ(43U, *EvalResult);
+  EXPECT_TRUE(FoobarExprVar.getExpressionAST());
+  FoobarExprVar.setValue(43);
+  EXPECT_FALSE(FoobarExprVar.getExpressionAST());
+  FoobarExprVar = FileCheckNumericVariable("FOOBAR", 2, &Binop);
+  EXPECT_TRUE(FoobarExprVar.getExpressionAST());
+
   // Clearing variable: getValue and eval fail. Error returned by eval holds
   // the name of the cleared variable.
   FooVar.clearValue();
-  Value = FooVar.getValue();
-  EXPECT_FALSE(Value);
+  FoobarExprVar.clearValue();
+  EXPECT_FALSE(FoobarExprVar.getExpressionAST());
+  EXPECT_FALSE(FooVar.getValue());
+  EXPECT_FALSE(FoobarExprVar.getValue());
   EvalResult = FooVarUse.eval();
   EXPECT_FALSE(EvalResult);
   expectUndefError("FOO", EvalResult.takeError());
+  EvalResult = FoobarExprVarUse.eval();
+  EXPECT_FALSE(EvalResult);
+  expectUndefError("FOOBAR", EvalResult.takeError());
 }

-uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
-
 TEST_F(FileCheckTest, Binop) {
-  FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO");
+  FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1);
   FooVar.setValue(42);
   std::unique_ptr<FileCheckNumericVariableUse> FooVarUse =
       llvm::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar);
-  FileCheckNumericVariable BarVar = FileCheckNumericVariable("BAR");
+  FileCheckNumericVariable BarVar = FileCheckNumericVariable("BAR", 2);
   BarVar.setValue(18);
   std::unique_ptr<FileCheckNumericVariableUse> BarVarUse =
       llvm::make_unique<FileCheckNumericVariableUse>("BAR", &BarVar);
@@ -237,19 +272,13 @@ public:
     P = FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);
   }

-  bool parseNumVarDefExpect(StringRef Expr) {
-    StringRef ExprBufferRef = bufferize(SM, Expr);
-    return errorToBool(FileCheckPattern::parseNumericVariableDefinition(
-                           ExprBufferRef, &Context, LineNumber, SM)
-                           .takeError());
-  }
-
   bool parseSubstExpect(StringRef Expr) {
     StringRef ExprBufferRef = bufferize(SM, Expr);
     Optional<FileCheckNumericVariable *> DefinedNumericVariable;
-    return errorToBool(P.parseNumericSubstitutionBlock(
-                            ExprBufferRef, DefinedNumericVariable, false, SM)
-                           .takeError());
+    return errorToBool(
+        P.parseNumericSubstitutionBlock(ExprBufferRef, DefinedNumericVariable,
+                                        false, LineNumber - 1, &Context, SM)
+            .takeError());
   }

   bool parsePatternExpect(StringRef Pattern) {
@@ -264,19 +293,6 @@ public:
   }
 };

-TEST_F(FileCheckTest, ParseNumericVariableDefinition) {
-  PatternTester Tester;
-
-  // Invalid definition of pseudo.
-  EXPECT_TRUE(Tester.parseNumVarDefExpect("@LINE"));
-
-  // Conflict with pattern variable.
-  EXPECT_TRUE(Tester.parseNumVarDefExpect("BAR"));
-
-  // Defined variable.
-  EXPECT_FALSE(Tester.parseNumVarDefExpect("FOO"));
-}
-
 TEST_F(FileCheckTest, ParseExpr) {
   PatternTester Tester;

@@ -287,17 +303,18 @@ TEST_F(FileCheckTest, ParseExpr) {
   EXPECT_TRUE(Tester.parseSubstExpect("@FOO:"));
   EXPECT_TRUE(Tester.parseSubstExpect("@LINE:"));

+  // Conflict with pattern variable.
+  EXPECT_TRUE(Tester.parseSubstExpect("BAR:"));
+
   // Garbage after name of variable being defined.
   EXPECT_TRUE(Tester.parseSubstExpect("VAR GARBAGE:"));

-  // Variable defined to numeric expression.
-  EXPECT_TRUE(Tester.parseSubstExpect("VAR1: FOO"));
-
   // Acceptable variable definition.
   EXPECT_FALSE(Tester.parseSubstExpect("VAR1:"));
   EXPECT_FALSE(Tester.parseSubstExpect("  VAR2:"));
   EXPECT_FALSE(Tester.parseSubstExpect("VAR3  :"));
   EXPECT_FALSE(Tester.parseSubstExpect("VAR3:  "));
+  EXPECT_FALSE(Tester.parsePatternExpect("[[#FOOBAR: FOO+1]]"));

   // Numeric expression.

@@ -310,9 +327,21 @@ TEST_F(FileCheckTest, ParseExpr) {
   EXPECT_FALSE(Tester.parseSubstExpect("FOO"));
   EXPECT_FALSE(Tester.parseSubstExpect("UNDEF"));

-  // Use variable defined on same line.
-  EXPECT_FALSE(Tester.parsePatternExpect("[[#LINE1VAR:]]"));
-  EXPECT_TRUE(Tester.parseSubstExpect("LINE1VAR"));
+  // Valid empty expression.
+  EXPECT_FALSE(Tester.parseSubstExpect(""));
+
+  // Valid use of variable defined on the same line from expression. Note that
+  // the same pattern object is used for the parsePatternExpect and
+  // parseSubstExpect since no initNextPattern is called, thus appearing as
+  // being on the same line from the pattern's point of view.
+  EXPECT_FALSE(Tester.parsePatternExpect("[[#LINE1VAR:FOO+1]]"));
+  EXPECT_FALSE(Tester.parseSubstExpect("LINE1VAR"));
+
+  // Invalid use of variable defined on same line from input. As above, the
+  // absence of a call to initNextPattern makes it appear to be on the same
+  // line from the pattern's point of view.
+  EXPECT_FALSE(Tester.parsePatternExpect("[[#LINE2VAR:]]"));
+  EXPECT_TRUE(Tester.parseSubstExpect("LINE2VAR"));

   // Unsupported operator.
   EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2"));
@@ -323,6 +352,7 @@ TEST_F(FileCheckTest, ParseExpr) {
   // Valid expression.
   EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5"));
   EXPECT_FALSE(Tester.parseSubstExpect("FOO+4"));
+  EXPECT_FALSE(Tester.parseSubstExpect("FOOBAR"));
   Tester.initNextPattern();
   EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]"));
   EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]"));
@@ -354,7 +384,6 @@ TEST_F(FileCheckTest, ParsePattern) {
   EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]"));
   EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]"));
   EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]"));
-  EXPECT_TRUE(Tester.parsePatternExpect("[[#YUP:@LINE]]"));

   // Valid numeric expressions and numeric variable definition.
   EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO]]"));
@@ -365,9 +394,16 @@ TEST_F(FileCheckTest, ParsePattern) {
 TEST_F(FileCheckTest, Match) {
   PatternTester Tester;

+  // Check matching an empty expression only matches a number.
+  Tester.parsePatternExpect("[[#]]");
+  EXPECT_TRUE(Tester.matchExpect("FAIL"));
+  EXPECT_FALSE(Tester.matchExpect("18"));
+
   // Check matching a definition only matches a number.
+  Tester.initNextPattern();
   Tester.parsePatternExpect("[[#NUMVAR:]]");
   EXPECT_TRUE(Tester.matchExpect("FAIL"));
+  EXPECT_TRUE(Tester.matchExpect(""));
   EXPECT_FALSE(Tester.matchExpect("18"));

   // Check matching the variable defined matches the correct number only
@@ -381,16 +417,16 @@ TEST_F(FileCheckTest, Match) {
   // the correct value for @LINE.
   Tester.initNextPattern();
   EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]"));
-  // Ok, @LINE is 4 now.
-  EXPECT_FALSE(Tester.matchExpect("4"));
+  // Ok, @LINE is 5 now.
+  EXPECT_FALSE(Tester.matchExpect("5"));
   Tester.initNextPattern();
-  // @LINE is now 5, match with substitution failure.
+  // @LINE is now 6, match with substitution failure.
   EXPECT_FALSE(Tester.parsePatternExpect("[[#UNKNOWN]]"));
   EXPECT_TRUE(Tester.matchExpect("FOO"));
   Tester.initNextPattern();
-  // Check that @LINE is 6 as expected.
+  // Check that @LINE is 7 as expected.
   EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]"));
-  EXPECT_FALSE(Tester.matchExpect("6"));
+  EXPECT_FALSE(Tester.matchExpect("7"));
 }

 TEST_F(FileCheckTest, Substitution) {
@@ -410,9 +446,9 @@ TEST_F(FileCheckTest, Substitution) {

   // Substitutions of defined pseudo and non-pseudo numeric variables return
   // the right value.
-  FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE");
+  FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 1);
+  FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 1);
   LineVar.setValue(42);
-  FileCheckNumericVariable NVar = FileCheckNumericVariable("N");
   NVar.setValue(10);
   auto LineVarUse =
       llvm::make_unique<FileCheckNumericVariableUse>("@LINE", &LineVar);
@@ -494,22 +530,29 @@ TEST_F(FileCheckTest, FileCheckContext)

   // Define local variables from command-line.
   GlobalDefines.clear();
+  // Clear local variables to remove dummy numeric variable x that
+  // parseNumericSubstitutionBlock would have created and stored in
+  // GlobalNumericVariableTable.
+  Cxt.clearLocalVars();
   GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
   GlobalDefines.emplace_back(std::string("EmptyVar="));
-  GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
+  GlobalDefines.emplace_back(std::string("#LocalNumVar1=18"));
+  GlobalDefines.emplace_back(std::string("#LocalNumVar2=LocalNumVar1+2"));
   EXPECT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));

   // Check defined variables are present and undefined is absent.
   StringRef LocalVarStr = "LocalVar";
-  StringRef LocalNumVarRef = bufferize(SM, "LocalNumVar");
+  StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
+  StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
   StringRef EmptyVarStr = "EmptyVar";
   StringRef UnknownVarStr = "UnknownVar";
   Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
   FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1);
   Optional<FileCheckNumericVariable *> DefinedNumericVariable;
   Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionAST =
-      P.parseNumericSubstitutionBlock(LocalNumVarRef, DefinedNumericVariable,
-                                      /*IsLegacyLineExpr=*/false, SM);
+      P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
+                                      /*IsLegacyLineExpr=*/false,
+                                      /*LineNumber=*/1, &Cxt, SM);
   EXPECT_TRUE(bool(LocalVar));
   EXPECT_EQ(*LocalVar, "FOO");
   Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
@@ -518,6 +561,14 @@ TEST_F(FileCheckTest, FileCheckContext)
   Expected<uint64_t> ExpressionVal = (*ExpressionAST)->eval();
   EXPECT_TRUE(bool(ExpressionVal));
   EXPECT_EQ(*ExpressionVal, 18U);
+  ExpressionAST =
+      P.parseNumericSubstitutionBlock(LocalNumVar2Ref, DefinedNumericVariable,
+                                      /*IsLegacyLineExpr=*/false,
+                                      /*LineNumber=*/1, &Cxt, SM);
+  EXPECT_TRUE(bool(ExpressionAST));
+  ExpressionVal = (*ExpressionAST)->eval();
+  EXPECT_TRUE(bool(ExpressionVal));
+  EXPECT_EQ(*ExpressionVal, 20U);
   EXPECT_TRUE(bool(EmptyVar));
   EXPECT_EQ(*EmptyVar, "");
   EXPECT_TRUE(errorToBool(UnknownVar.takeError()));
@@ -533,7 +584,14 @@ TEST_F(FileCheckTest, FileCheckContext)
   EXPECT_TRUE(errorToBool((*ExpressionAST)->eval().takeError()));
   P = FileCheckPattern(Check::CheckPlain, &Cxt, 2);
   ExpressionAST = P.parseNumericSubstitutionBlock(
-      LocalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM);
+      LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
+      /*LineNumber=*/2, &Cxt, SM);
+  EXPECT_TRUE(bool(ExpressionAST));
+  ExpressionVal = (*ExpressionAST)->eval();
+  EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
+  ExpressionAST = P.parseNumericSubstitutionBlock(
+      LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
+      /*LineNumber=*/2, &Cxt, SM);
   EXPECT_TRUE(bool(ExpressionAST));
   ExpressionVal = (*ExpressionAST)->eval();
   EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
@@ -554,7 +612,8 @@ TEST_F(FileCheckTest, FileCheckContext)
   EXPECT_EQ(*GlobalVar, "BAR");
   P = FileCheckPattern(Check::CheckPlain, &Cxt, 3);
   ExpressionAST = P.parseNumericSubstitutionBlock(
-      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM);
+      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
+      /*LineNumber=*/3, &Cxt, SM);
   EXPECT_TRUE(bool(ExpressionAST));
   ExpressionVal = (*ExpressionAST)->eval();
   EXPECT_TRUE(bool(ExpressionVal));
@@ -565,7 +624,8 @@ TEST_F(FileCheckTest, FileCheckContext)
   EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError()));
   P = FileCheckPattern(Check::CheckPlain, &Cxt, 4);
   ExpressionAST = P.parseNumericSubstitutionBlock(
-      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM);
+      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
+      /*LineNumber=*/4, &Cxt, SM);
   EXPECT_TRUE(bool(ExpressionAST));
   ExpressionVal = (*ExpressionAST)->eval();
   EXPECT_TRUE(bool(ExpressionVal));


_______________________________________________
llvm-commits mailing list
llvm-commits at lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits


** We have updated our privacy policy, which contains important information about how we collect and process your personal data. To read the policy, please click here<http://www.graphcore.ai/privacy> **

This email and its attachments are intended solely for the addressed recipients and may contain confidential or legally privileged information.
If you are not the intended recipient you must not copy, distribute or disseminate this email in any way; to do so may be unlawful.

Any personal data/special category personal data herein are processed in accordance with UK data protection legislation.
All associated feasible security measures are in place. Further details are available from the Privacy Notice on the website and/or from the Company.

Graphcore Limited (registered in England and Wales with registration number 10185006) is registered at 107 Cheapside, London, UK, EC2V 6DN.
This message was scanned for viruses upon transmission. However Graphcore accepts no liability for any such transmission.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20190724/f1ac6f7e/attachment-0001.html>


More information about the llvm-commits mailing list