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
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:
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
}
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(
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>>
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;
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;
// 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?
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);
/// 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
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
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
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'",
// 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());
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());
// 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.
// 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());