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``
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
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
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
as:
@ -747,11 +754,12 @@ does not matter:
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 ``[[#%<fmtspec>,<NUMVAR>: <expr>]]``
with each element as described previously. One can use this syntax to make a
testcase more self-describing by using variables instead of values:
in which case the numeric expression constraint 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
``[[#%<fmtspec>,<NUMVAR>: <constraint> <expr>]]`` with each element as
described previously. One can use this syntax to make a testcase more
self-describing by using variables instead of values:
.. code-block:: gas

View File

@ -448,8 +448,9 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
}
Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
StringRef &Expr, AllowedOperand AO, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM) {
StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint,
Optional<size_t> LineNumber, FileCheckPatternContext *Context,
const SourceMgr &SM) {
if (Expr.startswith("(")) {
if (AO != AllowedOperand::Any)
return ErrorDiagnostic::get(
@ -497,8 +498,11 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()),
SignedLiteralValue);
return ErrorDiagnostic::get(SM, Expr,
"invalid operand format '" + Expr + "'");
return ErrorDiagnostic::get(
SM, Expr,
Twine("invalid ") +
(MaybeInvalidConstraint ? "matching constraint or " : "") +
"operand format");
}
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");
// Note: parseNumericOperand handles nested opening parentheses.
Expected<std::unique_ptr<ExpressionAST>> SubExprResult =
parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM);
Expected<std::unique_ptr<ExpressionAST>> SubExprResult = parseNumericOperand(
Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
Context, SM);
Expr = Expr.ltrim(SpaceChars);
while (SubExprResult && !Expr.empty() && !Expr.startswith(")")) {
StringRef OrigExpr = Expr;
@ -568,7 +573,8 @@ Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
AllowedOperand AO =
IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
parseNumericOperand(RemainingExpr, AO, LineNumber, Context, SM);
parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false,
LineNumber, Context, SM);
if (!RightOpResult)
return RightOpResult;
@ -606,8 +612,9 @@ Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName,
// Parse the argument, which is an arbitary expression.
StringRef OuterBinOpExpr = Expr;
Expected<std::unique_ptr<ExpressionAST>> Arg =
parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM);
Expected<std::unique_ptr<ExpressionAST>> Arg = parseNumericOperand(
Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
Context, SM);
while (Arg && !Expr.empty()) {
Expr = Expr.ltrim(SpaceChars);
// Have we reached an argument terminator?
@ -702,17 +709,27 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
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.
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);
StringRef OuterBinOpExpr = Expr;
// 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<ExpressionAST>> ParseResult =
parseNumericOperand(Expr, AO, LineNumber, Context, SM);
Expected<std::unique_ptr<ExpressionAST>> ParseResult = parseNumericOperand(
Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM);
while (ParseResult && !Expr.empty()) {
ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult),
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
/// before input is parsed if \p LineNumber is None. Accepts literal values,
/// numeric variables and function calls, 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. If \p Expr starts with a "(" this function will attempt to
/// parse a parenthesized expression.
/// \p MaybeInvalidConstraint indicates whether the text being parsed could
/// be an invalid constraint. \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. If \p Expr starts with a "(" this
/// function will attempt to parse a parenthesized expression.
static Expected<std::unique_ptr<ExpressionAST>>
parseNumericOperand(StringRef &Expr, AllowedOperand AO,
parseNumericOperand(StringRef &Expr, AllowedOperand AO, bool ConstraintParsed,
Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
/// 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 '*'
51
52 BAD10: [[@LINE-x]]
53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format 'x'
54
55 BAD11: [[@LINE-1x]]
56 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x'
57
53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format
54 ERR10-NEXT: 52 {{B}}AD10: {{\[\[@LINE-x\]\]}}
55 ERR10-NEXT: {{^}} ^{{$}}
56
57 BAD11: [[@LINE-1x]]
58 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x'
59
; RUN: %ProtectFileCheckOutput \
; RUN: not FileCheck -check-prefix BAD12 -input-file %s %s 2>&1 \
; RUN: | FileCheck -check-prefix ERR12 %s
61
62 BAD12: [[#@LINE-1]] NOT HERE
63 ERR12: note: with "@LINE-1" equal to "61"
64
63
64 BAD12: [[#@LINE-1]] NOT HERE
65 ERR12: note: with "@LINE-1" equal to "63"
66
; RUN: %ProtectFileCheckOutput \
; RUN: not FileCheck --check-prefix BAD13 --input-file %s %s 2>&1 \
; RUN: | FileCheck --check-prefix ERR13 %s
68
69 BAD13: [[@LINE-0xA]]
70 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA'
71
72 CHECK: [[#@LINE]] CHECK
73 CHECK: [[# @LINE]] CHECK
74 CHECK: [[# @LINE ]] CHECK
75
76 CHECK: [[#@LINE-1]]
77 CHECK: [[# @LINE-1]] CHECK
78 CHECK: [[# @LINE -1]] CHECK
79 CHECK: [[# @LINE - 1]] CHECK
80 CHECK: [[# @LINE - 1 ]] CHECK
70
71 BAD13: [[@LINE-0xA]]
72 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA'
73
74 CHECK: [[#@LINE]] CHECK
75 CHECK: [[# @LINE]] CHECK
76 CHECK: [[# @LINE ]] CHECK
77
78 CHECK: [[#@LINE-1]]
79 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-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
; variables defined on other lines.
USE CONV FMT IMPL MATCH // CHECK-LABEL: USE CONV FMT IMPL MATCH

View File

@ -902,7 +902,7 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
Tester.initNextPattern();
// Invalid variable name.
expectDiagnosticError("invalid operand format '%VAR'",
expectDiagnosticError("invalid matching constraint or operand format",
Tester.parseSubst("%VAR").takeError());
expectDiagnosticError("invalid pseudo numeric variable '@FOO'",
@ -937,6 +937,10 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
// Valid empty expression.
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.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), 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)),
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.
expectDiagnosticError("invalid matching format specification in expression",
Tester.parseSubst("X,FOO:").takeError());
@ -968,12 +979,12 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
// Errors in RHS operand are bubbled up by parseBinop() to
// parseNumericSubstitutionBlock().
expectDiagnosticError("invalid operand format '%VAR'",
expectDiagnosticError("invalid operand format",
Tester.parseSubst("@LINE+%VAR").takeError());
// Invalid legacy @LINE expression with non literal rhs.
expectDiagnosticError(
"invalid operand format '@LINE'",
"invalid operand format",
Tester.parseSubst("@LINE+@LINE", /*IsLegacyNumExpr=*/true).takeError());
// 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
// 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());
expectDiagnosticError("unsupported operation ')'",
Tester.parseSubst("1)").takeError());