FileCheck [11/12]: Add matching constraint specification

This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch adds support for specifying the
matching constraint for a numeric expression, ie. how the value being
matched should relate to the numeric expression.

This commit only adds the equality constraint where the numeric value
matched must be equal to the numeric expression. It is the default
matching constraint used when not specified. It is added to provision
other matching constraint (e.g. inequality relations).

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)

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D60391
This commit is contained in:
Thomas Preud'homme 2019-07-17 16:15:52 +01:00
parent 9daccb7a47
commit 47934c7cf9
6 changed files with 137 additions and 50 deletions

View File

@ -670,7 +670,8 @@ For example:
would match ``mov r5, 0xF0F0`` and set ``REG`` to the value ``5`` and ``IMM`` would match ``mov r5, 0xF0F0`` and set ``REG`` to the value ``5`` and ``IMM``
to the value ``0xF0F0``. to the value ``0xF0F0``.
The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where: The syntax of a numeric substitution is
``[[#%<fmtspec>: <constraint> <expr>]]`` where:
* ``%<fmtspec>`` is the same matching format specifier as for defining numeric * ``%<fmtspec>`` is the same matching format specifier as for defining numeric
variables but acting as a printf-style format to indicate how a numeric variables but acting as a printf-style format to indicate how a numeric
@ -680,6 +681,12 @@ The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where:
is used. In case of conflict between matching formats of several numeric is used. In case of conflict between matching formats of several numeric
variables the format specifier is mandatory. variables the format specifier is mandatory.
* ``<constraint>`` is the constraint describing how the value to match must
relate to the value of the numeric expression. The only currently accepted
constraint is ``==`` for an exact match and is the default if
``<constraint>`` is not provided. No matching constraint must be specified
when the ``<expr>`` is empty.
* ``<expr>`` is an expression. An expression is in turn recursively defined * ``<expr>`` is an expression. An expression is in turn recursively defined
as: as:
@ -747,11 +754,12 @@ does not matter:
to check that a value is synthesized rather than moved around. 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, 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 in which case the numeric expression constraint is checked and if verified the
assigned to the value. The unified syntax for both defining numeric variables variable is assigned to the value. The unified syntax for both defining numeric
and checking a numeric expression is thus ``[[#%<fmtspec>,<NUMVAR>: <expr>]]`` variables and checking a numeric expression is thus
with each element as described previously. One can use this syntax to make a ``[[#%<fmtspec>,<NUMVAR>: <constraint> <expr>]]`` with each element as
testcase more self-describing by using variables instead of values: described previously. One can use this syntax to make a testcase more
self-describing by using variables instead of values:
.. code-block:: gas .. code-block:: gas

View File

@ -448,8 +448,9 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
} }
Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand( Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
StringRef &Expr, AllowedOperand AO, Optional<size_t> LineNumber, StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint,
FileCheckPatternContext *Context, const SourceMgr &SM) { Optional<size_t> LineNumber, FileCheckPatternContext *Context,
const SourceMgr &SM) {
if (Expr.startswith("(")) { if (Expr.startswith("(")) {
if (AO != AllowedOperand::Any) if (AO != AllowedOperand::Any)
return ErrorDiagnostic::get( return ErrorDiagnostic::get(
@ -497,8 +498,11 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()), return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()),
SignedLiteralValue); SignedLiteralValue);
return ErrorDiagnostic::get(SM, Expr, return ErrorDiagnostic::get(
"invalid operand format '" + Expr + "'"); SM, Expr,
Twine("invalid ") +
(MaybeInvalidConstraint ? "matching constraint or " : "") +
"operand format");
} }
Expected<std::unique_ptr<ExpressionAST>> Expected<std::unique_ptr<ExpressionAST>>
@ -514,8 +518,9 @@ Pattern::parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber,
return ErrorDiagnostic::get(SM, Expr, "missing operand in expression"); return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
// Note: parseNumericOperand handles nested opening parentheses. // Note: parseNumericOperand handles nested opening parentheses.
Expected<std::unique_ptr<ExpressionAST>> SubExprResult = Expected<std::unique_ptr<ExpressionAST>> SubExprResult = parseNumericOperand(
parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM); Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
Context, SM);
Expr = Expr.ltrim(SpaceChars); Expr = Expr.ltrim(SpaceChars);
while (SubExprResult && !Expr.empty() && !Expr.startswith(")")) { while (SubExprResult && !Expr.empty() && !Expr.startswith(")")) {
StringRef OrigExpr = Expr; StringRef OrigExpr = Expr;
@ -568,7 +573,8 @@ Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
AllowedOperand AO = AllowedOperand AO =
IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any; IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
Expected<std::unique_ptr<ExpressionAST>> RightOpResult = Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
parseNumericOperand(RemainingExpr, AO, LineNumber, Context, SM); parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false,
LineNumber, Context, SM);
if (!RightOpResult) if (!RightOpResult)
return RightOpResult; return RightOpResult;
@ -606,8 +612,9 @@ Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName,
// Parse the argument, which is an arbitary expression. // Parse the argument, which is an arbitary expression.
StringRef OuterBinOpExpr = Expr; StringRef OuterBinOpExpr = Expr;
Expected<std::unique_ptr<ExpressionAST>> Arg = Expected<std::unique_ptr<ExpressionAST>> Arg = parseNumericOperand(
parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM); Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
Context, SM);
while (Arg && !Expr.empty()) { while (Arg && !Expr.empty()) {
Expr = Expr.ltrim(SpaceChars); Expr = Expr.ltrim(SpaceChars);
// Have we reached an argument terminator? // Have we reached an argument terminator?
@ -702,17 +709,27 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
Expr = Expr.substr(DefEnd + 1); Expr = Expr.substr(DefEnd + 1);
} }
// Parse matching constraint.
Expr = Expr.ltrim(SpaceChars);
bool HasParsedValidConstraint = false;
if (Expr.consume_front("=="))
HasParsedValidConstraint = true;
// Parse the expression itself. // Parse the expression itself.
Expr = Expr.ltrim(SpaceChars); Expr = Expr.ltrim(SpaceChars);
if (!Expr.empty()) { if (Expr.empty()) {
if (HasParsedValidConstraint)
return ErrorDiagnostic::get(
SM, Expr, "empty numeric expression should not have a constraint");
} else {
Expr = Expr.rtrim(SpaceChars); Expr = Expr.rtrim(SpaceChars);
StringRef OuterBinOpExpr = Expr; StringRef OuterBinOpExpr = Expr;
// The first operand in a legacy @LINE expression is always the @LINE // The first operand in a legacy @LINE expression is always the @LINE
// pseudo variable. // pseudo variable.
AllowedOperand AO = AllowedOperand AO =
IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any; IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;
Expected<std::unique_ptr<ExpressionAST>> ParseResult = Expected<std::unique_ptr<ExpressionAST>> ParseResult = parseNumericOperand(
parseNumericOperand(Expr, AO, LineNumber, Context, SM); Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM);
while (ParseResult && !Expr.empty()) { while (ParseResult && !Expr.empty()) {
ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult), ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult),
IsLegacyLineExpr, LineNumber, Context, SM); IsLegacyLineExpr, LineNumber, Context, SM);

View File

@ -728,13 +728,14 @@ private:
/// Parses \p Expr for use of a numeric operand at line \p LineNumber, or /// Parses \p Expr for use of a numeric operand at line \p LineNumber, or
/// before input is parsed if \p LineNumber is None. Accepts literal values, /// before input is parsed if \p LineNumber is None. Accepts literal values,
/// numeric variables and function calls, depending on the value of \p AO. /// numeric variables and function calls, depending on the value of \p AO.
/// Parameter \p Context points to the class instance holding the live string /// \p MaybeInvalidConstraint indicates whether the text being parsed could
/// and numeric variables. \returns the class representing that operand in the /// be an invalid constraint. \p Context points to the class instance holding
/// AST of the expression or an error holding a diagnostic against \p SM /// the live string and numeric variables. \returns the class representing
/// otherwise. If \p Expr starts with a "(" this function will attempt to /// that operand in the AST of the expression or an error holding a
/// parse a parenthesized expression. /// diagnostic against \p SM otherwise. If \p Expr starts with a "(" this
/// function will attempt to parse a parenthesized expression.
static Expected<std::unique_ptr<ExpressionAST>> static Expected<std::unique_ptr<ExpressionAST>>
parseNumericOperand(StringRef &Expr, AllowedOperand AO, parseNumericOperand(StringRef &Expr, AllowedOperand AO, bool ConstraintParsed,
Optional<size_t> LineNumber, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM); FileCheckPatternContext *Context, const SourceMgr &SM);
/// Parses and updates \p RemainingExpr for a binary operation at line /// Parses and updates \p RemainingExpr for a binary operation at line

View File

@ -50,31 +50,33 @@
50 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported operation '*' 50 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported operation '*'
51 51
52 BAD10: [[@LINE-x]] 52 BAD10: [[@LINE-x]]
53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format 'x' 53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format
54 54 ERR10-NEXT: 52 {{B}}AD10: {{\[\[@LINE-x\]\]}}
55 BAD11: [[@LINE-1x]] 55 ERR10-NEXT: {{^}} ^{{$}}
56 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x' 56
57 57 BAD11: [[@LINE-1x]]
58 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x'
59
; RUN: %ProtectFileCheckOutput \ ; RUN: %ProtectFileCheckOutput \
; RUN: not FileCheck -check-prefix BAD12 -input-file %s %s 2>&1 \ ; RUN: not FileCheck -check-prefix BAD12 -input-file %s %s 2>&1 \
; RUN: | FileCheck -check-prefix ERR12 %s ; RUN: | FileCheck -check-prefix ERR12 %s
61 63
62 BAD12: [[#@LINE-1]] NOT HERE 64 BAD12: [[#@LINE-1]] NOT HERE
63 ERR12: note: with "@LINE-1" equal to "61" 65 ERR12: note: with "@LINE-1" equal to "63"
64 66
; RUN: %ProtectFileCheckOutput \ ; RUN: %ProtectFileCheckOutput \
; RUN: not FileCheck --check-prefix BAD13 --input-file %s %s 2>&1 \ ; RUN: not FileCheck --check-prefix BAD13 --input-file %s %s 2>&1 \
; RUN: | FileCheck --check-prefix ERR13 %s ; RUN: | FileCheck --check-prefix ERR13 %s
68 70
69 BAD13: [[@LINE-0xA]] 71 BAD13: [[@LINE-0xA]]
70 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA' 72 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA'
71 73
72 CHECK: [[#@LINE]] CHECK
73 CHECK: [[# @LINE]] CHECK
74 CHECK: [[#@LINE]] CHECK 74 CHECK: [[#@LINE]] CHECK
75 75 CHECK: [[# @LINE]] CHECK
76 CHECK: [[#@LINE-1]] 76 CHECK: [[# @LINE ]] CHECK
77 CHECK: [[# @LINE-1]] CHECK 77
78 CHECK: [[# @LINE -1]] CHECK 78 CHECK: [[#@LINE-1]]
79 CHECK: [[# @LINE-1]] CHECK 79 CHECK: [[# @LINE-1]] CHECK
80 CHECK: [[# @LINE -1]] CHECK 80 CHECK: [[# @LINE -1]] CHECK
81 CHECK: [[# @LINE - 1]] CHECK
82 CHECK: [[# @LINE - 1 ]] CHECK

View File

@ -154,6 +154,54 @@ USE IMPL FMT IMPL MATCH UNSIGNED IMM
CHECK-LABEL: USE IMPL FMT IMPL MATCH UNSIGNED IMM CHECK-LABEL: USE IMPL FMT IMPL MATCH UNSIGNED IMM
CHECK-NEXT: [[#UNSI+0x8000000000000000]] CHECK-NEXT: [[#UNSI+0x8000000000000000]]
; Numeric expressions in default matching format and explicit matching rule using
; variables defined on other lines.
USE DEF FMT EXPL MATCH // CHECK-LABEL: USE DEF FMT EXPL MATCH
11 // CHECK-NEXT: {{^}}[[#==UNSI]]
11 // CHECK-NEXT: {{^}}[[# == UNSI]]
12 // CHECK-NEXT: {{^}}[[#UNSI2: == UNSI + 1]]
12 // CHECK-NEXT: {{^}}[[#==UNSI2]]
; Numeric expressions in default matching format and explicit matching rule using
; variable defined on other lines with match failure.
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck --check-prefix NUMEXPR-CONSTRAINT-NOMATCH --input-file %s %s 2>&1 \
RUN: | FileCheck --check-prefix NUMEXPR-CONSTRAINT-NOMATCH-MSG --strict-whitespace %s
USE DEF FMT EXPL NO MATCH
12
NUMEXPR-CONSTRAINT-NOMATCH-LABEL: USE DEF FMT EXPL NO MATCH
NUMEXPR-CONSTRAINT-NOMATCH-NEXT: [[#==UNSI]]
NUMEXPR-CONSTRAINT-NOMATCH-MSG: numeric-expression.txt:[[#@LINE-1]]:34: error: {{N}}UMEXPR-CONSTRAINT-NOMATCH-NEXT: expected string not found in input
NUMEXPR-CONSTRAINT-NOMATCH-MSG-NEXT: {{N}}UMEXPR-CONSTRAINT-NOMATCH-NEXT: {{\[\[#==UNSI\]\]}}
NUMEXPR-CONSTRAINT-NOMATCH-MSG-NEXT: {{^}} ^{{$}}
; Empty numeric expression with matching constraint.
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck --check-prefix EMPTY-NUMEXPR-CONSTRAINT --input-file %s %s 2>&1 \
RUN: | FileCheck --check-prefix EMPTY-NUMEXPR-CONSTRAINT-MSG --strict-whitespace %s
EMPTY NUMEXPR USE WITH CONSTRAINT
18
EMPTY-NUMEXPR-CONSTRAINT-LABEL: EMPTY NUMEXPR USE WITH CONSTRAINT
EMPTY-NUMEXPR-CONSTRAINT-NEXT: [[# ==]]
EMPTY-NUMEXPR-CONSTRAINT-MSG: numeric-expression.txt:[[#@LINE-1]]:38: error: empty numeric expression should not have a constraint
EMPTY-NUMEXPR-CONSTRAINT-MSG-NEXT: {{E}}MPTY-NUMEXPR-CONSTRAINT-NEXT: {{\[\[# ==\]\]}}
EMPTY-NUMEXPR-CONSTRAINT-MSG-NEXT: {{^}} ^{{$}}
; Definition from empty numeric expression with matching constraint.
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck --check-prefix EMPTY-NUMDEF-CONSTRAINT --input-file %s %s 2>&1 \
RUN: | FileCheck --check-prefix EMPTY-NUMDEF-CONSTRAINT-MSG %s
EMPTY NUMEXPR DEF WITH CONSTRAINT
18
EMPTY-NUMDEF-CONSTRAINT-LABEL: EMPTY NUMEXPR CONSTRAINT
EMPTY-NUMDEF-CONSTRAINT-NEXT: [[#VARDEF: ==]]
EMPTY-NUMDEF-CONSTRAINT-MSG: numeric-expression.txt:[[#@LINE-1]]:44: error: empty numeric expression should not have a constraint
EMPTY-NUMDEF-CONSTRAINT-MSG-NEXT: {{E}}MPTY-NUMDEF-CONSTRAINT-NEXT: {{\[\[#VARDEF: ==\]\]}}
EMPTY-NUMDEF-CONSTRAINT-MSG-NEXT: {{^}} ^{{$}}
; Numeric expressions with matching format overriding the implicit format of ; Numeric expressions with matching format overriding the implicit format of
; variables defined on other lines. ; variables defined on other lines.
USE CONV FMT IMPL MATCH // CHECK-LABEL: USE CONV FMT IMPL MATCH USE CONV FMT IMPL MATCH // CHECK-LABEL: USE CONV FMT IMPL MATCH

View File

@ -902,7 +902,7 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
Tester.initNextPattern(); Tester.initNextPattern();
// Invalid variable name. // Invalid variable name.
expectDiagnosticError("invalid operand format '%VAR'", expectDiagnosticError("invalid matching constraint or operand format",
Tester.parseSubst("%VAR").takeError()); Tester.parseSubst("%VAR").takeError());
expectDiagnosticError("invalid pseudo numeric variable '@FOO'", expectDiagnosticError("invalid pseudo numeric variable '@FOO'",
@ -937,6 +937,10 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
// Valid empty expression. // Valid empty expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst(""), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst(""), Succeeded());
// Invalid equality matching constraint with empty expression.
expectDiagnosticError("empty numeric expression should not have a constraint",
Tester.parseSubst("==").takeError());
// Valid single operand expression. // Valid single operand expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("18"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst("18"), Succeeded());
@ -947,6 +951,13 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MinInt64)), EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MinInt64)),
Succeeded()); Succeeded());
// Valid optional matching constraint.
EXPECT_THAT_EXPECTED(Tester.parseSubst("==FOO"), Succeeded());
// Invalid matching constraint.
expectDiagnosticError("invalid matching constraint or operand format",
Tester.parseSubst("+=FOO").takeError());
// Invalid format. // Invalid format.
expectDiagnosticError("invalid matching format specification in expression", expectDiagnosticError("invalid matching format specification in expression",
Tester.parseSubst("X,FOO:").takeError()); Tester.parseSubst("X,FOO:").takeError());
@ -968,12 +979,12 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
// Errors in RHS operand are bubbled up by parseBinop() to // Errors in RHS operand are bubbled up by parseBinop() to
// parseNumericSubstitutionBlock(). // parseNumericSubstitutionBlock().
expectDiagnosticError("invalid operand format '%VAR'", expectDiagnosticError("invalid operand format",
Tester.parseSubst("@LINE+%VAR").takeError()); Tester.parseSubst("@LINE+%VAR").takeError());
// Invalid legacy @LINE expression with non literal rhs. // Invalid legacy @LINE expression with non literal rhs.
expectDiagnosticError( expectDiagnosticError(
"invalid operand format '@LINE'", "invalid operand format",
Tester.parseSubst("@LINE+@LINE", /*IsLegacyNumExpr=*/true).takeError()); Tester.parseSubst("@LINE+@LINE", /*IsLegacyNumExpr=*/true).takeError());
// Invalid legacy @LINE expression made of a single literal. // Invalid legacy @LINE expression made of a single literal.
@ -1046,7 +1057,7 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
// Test more closing than opening parentheses. The diagnostic messages are // Test more closing than opening parentheses. The diagnostic messages are
// not ideal, but for now simply check that we reject invalid input. // not ideal, but for now simply check that we reject invalid input.
expectDiagnosticError("invalid operand format ')'", expectDiagnosticError("invalid matching constraint or operand format",
Tester.parseSubst(")").takeError()); Tester.parseSubst(")").takeError());
expectDiagnosticError("unsupported operation ')'", expectDiagnosticError("unsupported operation ')'",
Tester.parseSubst("1)").takeError()); Tester.parseSubst("1)").takeError());