[FileCheck] Add precision to format specifier

Add printf-style precision specifier to pad numbers to a given number of
digits when matching them if the value is smaller than the given
precision. This works on both empty numeric expression (e.g. variable
definition from input) and when matching a numeric expression. The
syntax is as follows:

[[#%.<precision><format specifier>, ...]

where <format specifier> is optional and ... can be a variable
definition or not with an empty expression or not. In the absence of a
precision specifier, a variable definition will accept leading zeros.

Reviewed By: jhenderson, grimar

Differential Revision: https://reviews.llvm.org/D81667
This commit is contained in:
Thomas Preud'homme 2020-06-11 16:14:24 +01:00
parent 719548d63d
commit 998709b7d5
5 changed files with 326 additions and 113 deletions

View File

@ -730,35 +730,60 @@ numeric expression constraint based on those variables via a numeric
substitution. This allows ``CHECK:`` directives to verify a numeric relation
between two numbers, such as the need for consecutive registers to be used.
The syntax to define a numeric variable is ``[[#%<fmtspec>,<NUMVAR>:]]`` where:
The syntax to capture a numeric value is
``[[#%<fmtspec>,<NUMVAR>:]]`` where:
* ``%<fmtspec>`` is an optional scanf-style matching format specifier to
indicate what number format to match (e.g. hex number). Currently accepted
format specifiers are ``%u``, ``%d``, ``%x`` and ``%X``. If absent, the
format specifier defaults to ``%u``.
* ``%<fmtspec>,`` is an optional format specifier to indicate what number
format to match and the minimum number of digits to expect.
* ``<NUMVAR>:`` is an optional definition of variable ``<NUMVAR>`` from the
captured value.
The syntax of ``<fmtspec>`` is: ``.<precision><conversion specifier>`` where:
* ``.<precision>`` is an optional printf-style precision specifier in which
``<precision>`` indicates the minimum number of digits that the value matched
must have, expecting leading zeros if needed.
* ``<conversion specifier>`` is an optional scanf-style conversion specifier
to indicate what number format to match (e.g. hex number). Currently
accepted format specifiers are ``%u``, ``%d``, ``%x`` and ``%X``. If absent,
the format specifier defaults to ``%u``.
* ``<NUMVAR>`` is the name of the numeric variable to define to the matching
value.
For example:
.. code-block:: llvm
; CHECK: mov r[[#REG:]], 0x[[#%X,IMM:]]
; CHECK: mov r[[#REG:]], 0x[[#%.8X,ADDR:]]
would match ``mov r5, 0x0000FEFE`` and set ``REG`` to the value ``5`` and
``ADDR`` to the value ``0xFEFE``. Note that due to the precision it would fail
to match ``mov r5, 0xFEFE``.
As a result of the numeric variable definition being optional, it is possible
to only check that a numeric value is present in a given format. This can be
useful when the value itself is not useful, for instance:
.. code-block:: gas
; CHECK-NOT: mov r0, r[[#]]
to check that a value is synthesized rather than moved around.
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>: <constraint> <expr>]]`` where:
``[[#%<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
expression value should be matched against. If absent, the format specifier
is inferred from the matching format of the numeric variable(s) used by the
expression constraint if any, and defaults to ``%u`` if no numeric variable
is used. In case of conflict between matching formats of several numeric
variables the format specifier is mandatory.
* ``<fmtspec>`` is the same format specifier as for defining a variable but
in this context indicating how a numeric expression value should be matched
against. If absent, both components of the format specifier are inferred from
the matching format of the numeric variable(s) used by the expression
constraint if any, and defaults to ``%u`` if no numeric variable is used,
denoting that the value should be unsigned with no leading zeros. In case of
conflict between format specifiers of several numeric variables, the
conversion specifier becomes mandatory but the precision specifier remains
optional.
* ``<constraint>`` is the constraint describing how the value to match must
relate to the value of the numeric expression. The only currently accepted
@ -824,20 +849,11 @@ but would not match the text:
Due to ``7`` being unequal to ``5 + 1`` and ``a0463443`` being unequal to
``a0463440 + 7``.
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 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
variable is assigned to the value. The unified syntax for both checking a
numeric expression and capturing its value into a numeric variable 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:

View File

@ -43,16 +43,28 @@ StringRef ExpressionFormat::toString() const {
llvm_unreachable("unknown expression format");
}
Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
Expected<std::string> ExpressionFormat::getWildcardRegex() const {
auto CreatePrecisionRegex = [this](StringRef S) {
return (S + Twine('{') + Twine(Precision) + "}").str();
};
switch (Value) {
case Kind::Unsigned:
return StringRef("[0-9]+");
if (Precision)
return CreatePrecisionRegex("([1-9][0-9]*)?[0-9]");
return std::string("[0-9]+");
case Kind::Signed:
return StringRef("-?[0-9]+");
if (Precision)
return CreatePrecisionRegex("-?([1-9][0-9]*)?[0-9]");
return std::string("-?[0-9]+");
case Kind::HexUpper:
return StringRef("[0-9A-F]+");
if (Precision)
return CreatePrecisionRegex("([1-9A-F][0-9A-F]*)?[0-9A-F]");
return std::string("[0-9A-F]+");
case Kind::HexLower:
return StringRef("[0-9a-f]+");
if (Precision)
return CreatePrecisionRegex("([1-9a-f][0-9a-f]*)?[0-9a-f]");
return std::string("[0-9a-f]+");
default:
return createStringError(std::errc::invalid_argument,
"trying to match value with invalid format");
@ -61,27 +73,47 @@ Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
Expected<std::string>
ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const {
uint64_t AbsoluteValue;
StringRef SignPrefix = IntegerValue.isNegative() ? "-" : "";
if (Value == Kind::Signed) {
Expected<int64_t> SignedValue = IntegerValue.getSignedValue();
if (!SignedValue)
return SignedValue.takeError();
return itostr(*SignedValue);
if (*SignedValue < 0)
AbsoluteValue = cantFail(IntegerValue.getAbsolute().getUnsignedValue());
else
AbsoluteValue = *SignedValue;
} else {
Expected<uint64_t> UnsignedValue = IntegerValue.getUnsignedValue();
if (!UnsignedValue)
return UnsignedValue.takeError();
AbsoluteValue = *UnsignedValue;
}
Expected<uint64_t> UnsignedValue = IntegerValue.getUnsignedValue();
if (!UnsignedValue)
return UnsignedValue.takeError();
std::string AbsoluteValueStr;
switch (Value) {
case Kind::Unsigned:
return utostr(*UnsignedValue);
case Kind::Signed:
AbsoluteValueStr = utostr(AbsoluteValue);
break;
case Kind::HexUpper:
return utohexstr(*UnsignedValue, /*LowerCase=*/false);
case Kind::HexLower:
return utohexstr(*UnsignedValue, /*LowerCase=*/true);
AbsoluteValueStr = utohexstr(AbsoluteValue, Value == Kind::HexLower);
break;
default:
return createStringError(std::errc::invalid_argument,
"trying to match value with invalid format");
}
if (Precision > AbsoluteValueStr.size()) {
unsigned LeadingZeros = Precision - AbsoluteValueStr.size();
return (Twine(SignPrefix) + std::string(LeadingZeros, '0') +
AbsoluteValueStr)
.str();
}
return (Twine(SignPrefix) + AbsoluteValueStr).str();
}
Expected<ExpressionValue>
@ -720,41 +752,59 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
StringRef DefExpr = StringRef();
DefinedNumericVariable = None;
ExpressionFormat ExplicitFormat = ExpressionFormat();
unsigned Precision = 0;
// Parse format specifier (NOTE: ',' is also an argument seperator).
size_t FormatSpecEnd = Expr.find(',');
size_t FunctionStart = Expr.find('(');
if (FormatSpecEnd != StringRef::npos && FormatSpecEnd < FunctionStart) {
Expr = Expr.ltrim(SpaceChars);
if (!Expr.consume_front("%"))
StringRef FormatExpr = Expr.take_front(FormatSpecEnd);
Expr = Expr.drop_front(FormatSpecEnd + 1);
FormatExpr = FormatExpr.trim(SpaceChars);
if (!FormatExpr.consume_front("%"))
return ErrorDiagnostic::get(
SM, Expr, "invalid matching format specification in expression");
SM, FormatExpr,
"invalid matching format specification in expression");
// Check for unknown matching format specifier and set matching format in
// class instance representing this expression.
SMLoc fmtloc = SMLoc::getFromPointer(Expr.data());
switch (popFront(Expr)) {
case 'u':
ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
break;
case 'd':
ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Signed);
break;
case 'x':
ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower);
break;
case 'X':
ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper);
break;
default:
return ErrorDiagnostic::get(SM, fmtloc,
"invalid format specifier in expression");
// Parse precision.
if (FormatExpr.consume_front(".")) {
if (FormatExpr.consumeInteger(10, Precision))
return ErrorDiagnostic::get(SM, FormatExpr,
"invalid precision in format specifier");
}
Expr = Expr.ltrim(SpaceChars);
if (!Expr.consume_front(","))
if (!FormatExpr.empty()) {
// Check for unknown matching format specifier and set matching format in
// class instance representing this expression.
SMLoc FmtLoc = SMLoc::getFromPointer(FormatExpr.data());
switch (popFront(FormatExpr)) {
case 'u':
ExplicitFormat =
ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision);
break;
case 'd':
ExplicitFormat =
ExpressionFormat(ExpressionFormat::Kind::Signed, Precision);
break;
case 'x':
ExplicitFormat =
ExpressionFormat(ExpressionFormat::Kind::HexLower, Precision);
break;
case 'X':
ExplicitFormat =
ExpressionFormat(ExpressionFormat::Kind::HexUpper, Precision);
break;
default:
return ErrorDiagnostic::get(SM, FmtLoc,
"invalid format specifier in expression");
}
}
FormatExpr = FormatExpr.ltrim(SpaceChars);
if (!FormatExpr.empty())
return ErrorDiagnostic::get(
SM, Expr, "invalid matching format specification in expression");
SM, FormatExpr,
"invalid matching format specification in expression");
}
// Save variable definition expression if any.
@ -814,7 +864,7 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
Format = *ImplicitFormat;
}
if (!Format)
Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision);
std::unique_ptr<Expression> ExpressionPointer =
std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);
@ -948,7 +998,7 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
bool IsLegacyLineExpr = false;
StringRef DefName;
StringRef SubstStr;
StringRef MatchRegexp;
std::string MatchRegexp;
size_t SubstInsertIdx = RegExStr.size();
// Parse string variable or legacy @LINE expression.
@ -992,7 +1042,7 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
return true;
}
DefName = Name;
MatchRegexp = MatchStr;
MatchRegexp = MatchStr.str();
} else {
if (IsPseudo) {
MatchStr = OrigMatchStr;

View File

@ -53,15 +53,17 @@ struct ExpressionFormat {
private:
Kind Value;
unsigned Precision = 0;
public:
/// Evaluates a format to true if it can be used in a match.
explicit operator bool() const { return Value != Kind::NoFormat; }
/// Define format equality: formats are equal if neither is NoFormat and
/// their kinds are the same.
/// their kinds and precision are the same.
bool operator==(const ExpressionFormat &Other) const {
return Value != Kind::NoFormat && Value == Other.Value;
return Value != Kind::NoFormat && Value == Other.Value &&
Precision == Other.Precision;
}
bool operator!=(const ExpressionFormat &Other) const {
@ -76,12 +78,14 @@ public:
StringRef toString() const;
ExpressionFormat() : Value(Kind::NoFormat){};
explicit ExpressionFormat(Kind Value) : Value(Value){};
explicit ExpressionFormat(Kind Value) : Value(Value), Precision(0){};
explicit ExpressionFormat(Kind Value, unsigned Precision)
: Value(Value), Precision(Precision){};
/// \returns a wildcard regular expression StringRef that matches any value
/// in the format represented by this instance, or an error if the format is
/// NoFormat.
Expected<StringRef> getWildcardRegex() const;
/// \returns a wildcard regular expression string that matches any value in
/// the format represented by this instance and no other value, or an error
/// if the format is NoFormat.
Expected<std::string> getWildcardRegex() const;
/// \returns the string representation of \p Value in the format represented
/// by this instance, or an error if conversion to this format failed or the

View File

@ -49,8 +49,32 @@ INVALID-FMT-SPEC-MSG2: numeric-expression.txt:[[#@LINE-4]]:37: error: invalid fo
INVALID-FMT-SPEC-MSG2-NEXT: {{I}}NVALID-FMT-SPEC2-NEXT: INVVAR2={{\[\[#%hhd,INVVAR2:\]\]}}
INVALID-FMT-SPEC-MSG2-NEXT: {{^}} ^{{$}}
; Numeric expressions in explicit matching format and default matching rule using
; variables defined on other lines without spaces.
; Numeric variable definition with precision specifier.
DEF PREC FMT // CHECK-LABEL: DEF PREC FMT
00000022 // CHECK-NEXT: {{^}}[[#%.8,PADDED_UNSI:]]
323232323 // CHECK-NEXT: {{^}}[[#%.8,PADDED_UNSI2:]]
00000018 // CHECK-NEXT: {{^}}[[#%.8u,PADDED_UNSI3:]]
181818181 // CHECK-NEXT: {{^}}[[#%.8u,PADDED_UNSI4:]]
0000000f // CHECK-NEXT: {{^}}[[#%.8x,PADDED_LHEX:]]
fffffffff // CHECK-NEXT: {{^}}[[#%.8x,PADDED_LHEX2:]]
0000000E // CHECK-NEXT: {{^}}[[#%.8X,PADDED_UHEX:]]
EEEEEEEEE // CHECK-NEXT: {{^}}[[#%.8X,PADDED_UHEX2:]]
-00000055 // CHECK-NEXT: {{^}}[[#%.8d,PADDED_SIGN:]]
-555555555 // CHECK-NEXT: {{^}}[[#%.8d,PADDED_SIGN2:]]
; Numeric variable definition with precision specifier with value not padded
; enough.
RUN: FileCheck --check-prefix INVALID-PADDING-DEF --input-file %s %s
FAIL DEF PREC FMT // INVALID-PADDING-DEF-LABEL: FAIL DEF PREC FMT
INVALID_PADDED_UNSI: 0000022 // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_UNSI: [[#%.8,INVALID_PADDED_UNSI:]]
INVALID_PADDED_UNSI2: 0000018 // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_UNSI2: [[#%.8u,INVALID_PADDED_UNSI2:]]
INVALID_PADDED_LHEX: 000000f // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_LHEX: [[#%.8x,INVALID_PADDED_LHEX:]]
INVALID_PADDED_UHEX: 000000E // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_UHEX: [[#%.8X,INVALID_PADDED_UHEX:]]
INVALID_PADDED_SIGN: -0000055 // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_SIGN: [[#%.8d,INVALID_PADDED_SIGN:]]
; Numeric expressions with explicit matching format and default matching rule
; using variables defined on other lines without spaces.
USE EXPL FMT IMPL MATCH // CHECK-LABEL: USE EXPL FMT IMPL MATCH
11 // CHECK-NEXT: {{^}}[[#%u,UNSI]]
12 // CHECK-NEXT: {{^}}[[#%u,UNSI+1]]
@ -92,8 +116,8 @@ B // CHECK-NEXT: {{^}}[[#%X,sub(UHEX,2)]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIc]]
c // CHECK-NEXT: {{^}}[[#%x,LHEXa]]
; Numeric expressions in explicit matching format and default matching rule using
; variables defined on other lines with different spacing.
; Numeric expressions with explicit matching format and default matching rule
; using variables defined on other lines with different spacing.
USE EXPL FMT IMPL MATCH SPC // CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC
11 // CHECK-NEXT: {{^}}[[#%u, UNSI]]
11 // CHECK-NEXT: {{^}}[[# %u, UNSI]]
@ -120,8 +144,35 @@ USE EXPL FMT IMPL MATCH SPC // CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC
13 // CHECK-NEXT: {{^}}[[# %u , add (UNSI,2)]]
104 // CHECK-NEXT: {{^}}[[# %u , UNSI + sub( add (100 , UNSI+ 1 ), 20) +1 ]]
; Numeric expressions in implicit matching format and default matching rule using
; variables defined on other lines.
; Numeric expressions with explicit matching format, precision, and default
; matching rule using variables defined on other lines without spaces.
USE EXPL FMT WITH PREC IMPL MATCH // CHECK-LABEL: USE EXPL FMT WITH PREC IMPL MATCH
11 // CHECK-NEXT: {{^}}[[#%.1u,UNSI]]
00000011 // CHECK-NEXT: {{^}}[[#%.8u,UNSI]]
1c // CHECK-NEXT: {{^}}[[#%.1x,LHEX+16]]
0000000c // CHECK-NEXT: {{^}}[[#%.8x,LHEX]]
1D // CHECK-NEXT: {{^}}[[#%.1X,UHEX+16]]
0000000D // CHECK-NEXT: {{^}}[[#%.8X,UHEX]]
-30 // CHECK-NEXT: {{^}}[[#%.1d,SIGN]]
-00000030 // CHECK-NEXT: {{^}}[[#%.8d,SIGN]]
; Numeric expressions with explicit matching format, precision and wrong
; padding, and default matching rule using variables defined on other lines
; without spaces.
RUN: FileCheck --check-prefixes CHECK,INVALID-PADDING-EXPL-USE --input-file %s %s
FAIL USE IMPL FMT WITH PREC EXPL MATCH // INVALID-PADDING-EXPL-USE-LABEL: FAIL USE IMPL FMT WITH PREC IMPL MATCH
INVALID UNSI+1: 0000012 // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID UNSI+1: [[#%.8u,UNSI+1]]
INVALID UNSI-1: 000000010 // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID UNSI-1: [[#%.8u,UNSI-1]]
INVALID LHEX+1: 000000d // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID LHEX+1: [[#%.8x,LHEX+1]]
INVALID LHEX-1: 00000000b // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID LHEX-1: [[#%.8x,LHEX-1]]
INVALID UHEX+1: 000000E // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID UHEX+1: [[#%.8X,UHEX+1]]
INVALID UHEX-1: 00000000C // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID UHEX-1: [[#%.8X,UHEX-1]]
INVALID SIGN+1: -0000029 // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID SIGN+1: [[#%.8d,SIGN+1]]
INVALID SIGN-1: -000000031 // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID SIGN-1: [[#%.8d,SIGN-1]]
; Numeric expressions with implicit matching format and default matching rule
; using variables defined on other lines.
USE IMPL FMT IMPL MATCH // CHECK-LABEL: USE IMPL FMT IMPL MATCH
11 // CHECK-NEXT: {{^}}[[#UNSI]]
12 // CHECK-NEXT: {{^}}[[#UNSI+1]]
@ -146,6 +197,36 @@ A // CHECK-NEXT: {{^}}[[#min(UHEX,10)]]
-29 // CHECK-NEXT: {{^}}[[#SIGN+1]]
-31 // CHECK-NEXT: {{^}}[[#SIGN-1]]
; Numeric expressions with implicit matching format, precision, and default
; matching rule using variables defined on other lines.
USE IMPL FMT WITH PREC IMPL MATCH // CHECK-LABEL: USE IMPL FMT WITH PREC IMPL MATCH
00000023 // CHECK-NEXT: {{^}}[[#PADDED_UNSI+1]]
323232324 // CHECK-NEXT: {{^}}[[#PADDED_UNSI2+1]]
00000019 // CHECK-NEXT: {{^}}[[#PADDED_UNSI3+1]]
181818182 // CHECK-NEXT: {{^}}[[#PADDED_UNSI4+1]]
00000010 // CHECK-NEXT: {{^}}[[#PADDED_LHEX+1]]
1000000000 // CHECK-NEXT: {{^}}[[#PADDED_LHEX2+1]]
0000000F // CHECK-NEXT: {{^}}[[#PADDED_UHEX+1]]
EEEEEEEEF // CHECK-NEXT: {{^}}[[#PADDED_UHEX2+1]]
-00000054 // CHECK-NEXT: {{^}}[[#PADDED_SIGN+1]]
-555555554 // CHECK-NEXT: {{^}}[[#PADDED_SIGN2+1]]
; Numeric expression with implicit matching format, precision and wrong amount
; of padding, and default matching rule using variables defined on other lines.
RUN: FileCheck --check-prefixes CHECK,INVALID-PADDING-IMPL-USE --input-file %s %s
FAIL USE IMPL FMT WITH PREC IMPL MATCH // INVALID-PADDING-IMPL-USE-LABEL: FAIL USE IMPL FMT WITH PREC IMPL MATCH
INVALID PADDED_UNSI+1: 0000023 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UNSI+1: [[#PADDED_UNSI+1]]
INVALID PADDED_UNSI-1: 000000021 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UNSI-1: [[#PADDED_UNSI-1]]
INVALID PADDED_UNSI3+1: 0000019 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UNSI3+1: [[#PADDED_UNSI3+1]]
INVALID PADDED_UNSI3-1: 000000017 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UNSI3-1: [[#PADDED_UNSI3-1]]
INVALID PADDED_LHEX+1: 0000010 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_LHEX+1: [[#PADDED_LHEX+1]]
INVALID PADDED_LHEX-1: 00000000e // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_LHEX-1: [[#PADDED_LHEX-1]]
INVALID PADDED_UHEX+1: 000000F // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UHEX+1: [[#PADDED_UHEX+1]]
INVALID PADDED_UHEX-1: 00000000D // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UHEX-1: [[#PADDED_UHEX-1]]
INVALID PADDED_SIGN+1: -0000054 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_SIGN+1: [[#PADDED_SIGN+1]]
INVALID PADDED_SIGN-1: -000000056 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_SIGN-1: [[#PADDED_SIGN-1]]
; Numeric expressions using variables defined on other lines and an immediate
; interpreted as an unsigned value.
; Note: 9223372036854775819 = 0x8000000000000000 + 11

View File

@ -86,7 +86,9 @@ constexpr uint64_t AbsoluteMinInt64 =
constexpr uint64_t AbsoluteMaxInt64 = static_cast<uint64_t>(MaxInt64);
struct ExpressionFormatParameterisedFixture
: public ::testing::TestWithParam<ExpressionFormat::Kind> {
: public ::testing::TestWithParam<
std::pair<ExpressionFormat::Kind, unsigned>> {
unsigned Precision;
bool Signed;
bool AllowHex;
bool AllowUpperHex;
@ -105,12 +107,13 @@ struct ExpressionFormatParameterisedFixture
SourceMgr SM;
void SetUp() override {
ExpressionFormat::Kind Kind = GetParam();
ExpressionFormat::Kind Kind;
std::tie(Kind, Precision) = GetParam();
AllowHex = Kind == ExpressionFormat::Kind::HexLower ||
Kind == ExpressionFormat::Kind::HexUpper;
AllowUpperHex = Kind == ExpressionFormat::Kind::HexUpper;
Signed = Kind == ExpressionFormat::Kind::Signed;
Format = ExpressionFormat(Kind);
Format = ExpressionFormat(Kind, Precision);
if (!AllowHex) {
MaxUint64Str = std::to_string(MaxUint64);
@ -133,17 +136,42 @@ struct ExpressionFormatParameterisedFixture
FirstInvalidCharDigits = "gG";
}
void checkWildcardRegexMatch(StringRef Input) {
void checkWildcardRegexMatch(StringRef Input,
unsigned TrailExtendTo = 0) const {
SmallVector<StringRef, 4> Matches;
ASSERT_TRUE(WildcardRegex.match(Input, &Matches))
<< "Wildcard regex does not match " << Input;
EXPECT_EQ(Matches[0], Input);
std::string ExtendedInput = Input.str();
if (TrailExtendTo > Input.size()) {
ExtendedInput.append(TrailExtendTo - Input.size(), Input[0]);
}
ASSERT_TRUE(WildcardRegex.match(ExtendedInput, &Matches))
<< "Wildcard regex does not match " << ExtendedInput;
EXPECT_EQ(Matches[0], ExtendedInput);
}
void checkWildcardRegexMatchFailure(StringRef Input) {
void checkWildcardRegexMatchFailure(StringRef Input) const {
EXPECT_FALSE(WildcardRegex.match(Input));
}
void checkWildcardRegexCharMatchFailure(StringRef Chars) const {
for (auto C : Chars)
EXPECT_FALSE(WildcardRegex.match(StringRef(&C, 1)));
}
std::string padWithLeadingZeros(StringRef NumStr) const {
bool Negative = NumStr.startswith("-");
if (NumStr.size() - unsigned(Negative) >= Precision)
return NumStr.str();
std::string PaddedStr;
if (Negative) {
PaddedStr = "-";
NumStr = NumStr.drop_front();
}
PaddedStr.append(Precision - NumStr.size(), '0');
PaddedStr.append(NumStr.str());
return PaddedStr;
}
template <class T> void checkMatchingString(T Val, StringRef ExpectedStr) {
Expected<std::string> MatchingString =
Format.getMatchingString(ExpressionValue(Val));
@ -188,49 +216,62 @@ struct ExpressionFormatParameterisedFixture
TEST_P(ExpressionFormatParameterisedFixture, FormatGetWildcardRegex) {
// Wildcard regex is valid.
Expected<StringRef> WildcardPattern = Format.getWildcardRegex();
Expected<std::string> WildcardPattern = Format.getWildcardRegex();
ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded());
WildcardRegex = Regex((Twine("^") + *WildcardPattern).str());
WildcardRegex = Regex((Twine("^") + *WildcardPattern + "$").str());
ASSERT_TRUE(WildcardRegex.isValid());
// Does not match empty string.
checkWildcardRegexMatchFailure("");
// Matches all decimal digits and matches several of them.
checkWildcardRegexMatch("0123456789");
StringRef LongNumber = "12345678901234567890";
checkWildcardRegexMatch(LongNumber);
// Matches negative digits.
LongNumber = "-12345678901234567890";
if (Signed)
checkWildcardRegexMatch("-42");
checkWildcardRegexMatch(LongNumber);
else
checkWildcardRegexMatchFailure("-42");
checkWildcardRegexMatchFailure(LongNumber);
// Check non digits or digits with wrong casing are not matched.
if (AllowHex) {
checkWildcardRegexMatch(AcceptedHexOnlyDigits);
checkWildcardRegexMatchFailure(RefusedHexOnlyDigits);
checkWildcardRegexMatch(AcceptedHexOnlyDigits, 16);
checkWildcardRegexCharMatchFailure(RefusedHexOnlyDigits);
}
checkWildcardRegexMatchFailure(FirstInvalidCharDigits);
checkWildcardRegexCharMatchFailure(FirstInvalidCharDigits);
// Check leading zeros are only accepted if number of digits is less than the
// precision.
LongNumber = "01234567890123456789";
if (Precision) {
checkWildcardRegexMatch(LongNumber.take_front(Precision));
checkWildcardRegexMatchFailure(LongNumber.take_front(Precision - 1));
if (Precision < LongNumber.size())
checkWildcardRegexMatchFailure(LongNumber.take_front(Precision + 1));
} else
checkWildcardRegexMatch(LongNumber);
}
TEST_P(ExpressionFormatParameterisedFixture, FormatGetMatchingString) {
checkMatchingString(0, "0");
checkMatchingString(9, "9");
checkMatchingString(0, padWithLeadingZeros("0"));
checkMatchingString(9, padWithLeadingZeros("9"));
if (Signed) {
checkMatchingString(-5, "-5");
checkMatchingString(-5, padWithLeadingZeros("-5"));
checkMatchingStringFailure(MaxUint64);
checkMatchingString(MaxInt64, MaxInt64Str);
checkMatchingString(MinInt64, MinInt64Str);
checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str));
checkMatchingString(MinInt64, padWithLeadingZeros(MinInt64Str));
} else {
checkMatchingStringFailure(-5);
checkMatchingString(MaxUint64, MaxUint64Str);
checkMatchingString(MaxInt64, MaxInt64Str);
checkMatchingString(MaxUint64, padWithLeadingZeros(MaxUint64Str));
checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str));
checkMatchingStringFailure(MinInt64);
}
checkMatchingString(10, TenStr);
checkMatchingString(15, FifteenStr);
checkMatchingString(10, padWithLeadingZeros(TenStr));
checkMatchingString(15, padWithLeadingZeros(FifteenStr));
}
TEST_P(ExpressionFormatParameterisedFixture, FormatValueFromStringRepr) {
@ -257,12 +298,25 @@ TEST_P(ExpressionFormatParameterisedFixture, FormatBoolOperator) {
EXPECT_TRUE(bool(Format));
}
INSTANTIATE_TEST_CASE_P(AllowedExplicitExpressionFormat,
ExpressionFormatParameterisedFixture,
::testing::Values(ExpressionFormat::Kind::Unsigned,
ExpressionFormat::Kind::Signed,
ExpressionFormat::Kind::HexLower,
ExpressionFormat::Kind::HexUpper), );
INSTANTIATE_TEST_CASE_P(
AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture,
::testing::Values(std::make_pair(ExpressionFormat::Kind::Unsigned, 0),
std::make_pair(ExpressionFormat::Kind::Signed, 0),
std::make_pair(ExpressionFormat::Kind::HexLower, 0),
std::make_pair(ExpressionFormat::Kind::HexUpper, 0),
std::make_pair(ExpressionFormat::Kind::Unsigned, 1),
std::make_pair(ExpressionFormat::Kind::Signed, 1),
std::make_pair(ExpressionFormat::Kind::HexLower, 1),
std::make_pair(ExpressionFormat::Kind::HexUpper, 1),
std::make_pair(ExpressionFormat::Kind::Unsigned, 16),
std::make_pair(ExpressionFormat::Kind::Signed, 16),
std::make_pair(ExpressionFormat::Kind::HexLower, 16),
std::make_pair(ExpressionFormat::Kind::HexUpper, 16),
std::make_pair(ExpressionFormat::Kind::Unsigned, 20),
std::make_pair(ExpressionFormat::Kind::Signed, 20)), );
TEST_F(FileCheckTest, NoFormatProperties) {
ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
@ -985,6 +1039,10 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
EXPECT_FALSE(Tester.parsePattern("[[#%x, VAR_LOWER_HEX:]]"));
EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, VAR_UPPER_HEX:"), Succeeded());
// Acceptable variable definition with precision specifier.
EXPECT_FALSE(Tester.parsePattern("[[#%.8X, PADDED_ADDR:]]"));
EXPECT_FALSE(Tester.parsePattern("[[#%.8, PADDED_NUM:]]"));
// Acceptable variable definition from a numeric expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded());
@ -1094,6 +1152,10 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded());
// Valid expression with precision specifier.
EXPECT_THAT_EXPECTED(Tester.parseSubst("%.8u, FOO"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("%.8, FOO"), Succeeded());
// Valid legacy @LINE expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true),
Succeeded());