aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Support/FileCheck.cpp
diff options
context:
space:
mode:
authorThomas Preud'homme <thomasp@graphcore.ai>2019-03-05 23:20:29 +0000
committerThomas Preud'homme <thomasp@graphcore.ai>2020-01-24 14:15:28 +0000
commit8e96697c7df688e33a49b7883d20c4563eca0306 (patch)
tree1968c6e011a36c84c7eaac83c24a21bc60c310f6 /llvm/lib/Support/FileCheck.cpp
parent0a002f679be4a9d363e6f21b707493c18158930b (diff)
downloadllvm-8e96697c7df688e33a49b7883d20c4563eca0306.zip
llvm-8e96697c7df688e33a49b7883d20c4563eca0306.tar.gz
llvm-8e96697c7df688e33a49b7883d20c4563eca0306.tar.bz2
FileCheck [9/12]: Add support for matching formats
Summary: This patch is part of a patch series to add support for FileCheck numeric expressions. This specific patch adds support for selecting a matching format to match a numeric value against (ie. decimal, hex lower case letters or hex upper case letters). This commit allows to select what format a numeric value should be matched against. The following formats are supported: decimal value, lower case hex value and upper case hex value. Matching formats impact both the format of numeric value to be matched as well as the format of accepted numbers in a definition with empty numeric expression constraint. Default for absence of format is decimal value unless the numeric expression constraint is non null and use a variable in which case the format is the one used to define that variable. Conclict of format in case of several variable being used is diagnosed and forces the user to select a matching format explicitely. This commit also enables immediates in numeric expressions to be in any radix known to StringRef's GetAsInteger method, except for legacy numeric expressions (ie [[@LINE+<offset>]] which only support decimal immediates. 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 Reviewed By: jhenderson, arichardson Subscribers: daltenty, MaskRay, hiraditya, llvm-commits, probinson, dblaikie, grimar, arichardson, kristina, hfinkel, rogfer01, JonChesterfield Tags: #llvm Differential Revision: https://reviews.llvm.org/D60389
Diffstat (limited to 'llvm/lib/Support/FileCheck.cpp')
-rw-r--r--llvm/lib/Support/FileCheck.cpp197
1 files changed, 160 insertions, 37 deletions
diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp
index 2261ecc..d6351e8 100644
--- a/llvm/lib/Support/FileCheck.cpp
+++ b/llvm/lib/Support/FileCheck.cpp
@@ -25,6 +25,47 @@
using namespace llvm;
+Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
+ switch (Value) {
+ case Kind::Unsigned:
+ return StringRef("[0-9]+");
+ case Kind::HexUpper:
+ return StringRef("[0-9A-F]+");
+ case Kind::HexLower:
+ return StringRef("[0-9a-f]+");
+ default:
+ return createStringError(std::errc::invalid_argument,
+ "trying to match value with invalid format");
+ }
+}
+
+Expected<std::string>
+ExpressionFormat::getMatchingString(uint64_t IntegerValue) const {
+ switch (Value) {
+ case Kind::Unsigned:
+ return utostr(IntegerValue);
+ case Kind::HexUpper:
+ return utohexstr(IntegerValue, /*LowerCase=*/false);
+ case Kind::HexLower:
+ return utohexstr(IntegerValue, /*LowerCase=*/true);
+ default:
+ return createStringError(std::errc::invalid_argument,
+ "trying to match value with invalid format");
+ }
+}
+
+Expected<uint64_t>
+ExpressionFormat::valueFromStringRepr(StringRef StrVal,
+ const SourceMgr &SM) const {
+ bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
+ uint64_t IntegerValue;
+ if (StrVal.getAsInteger(Hex ? 16 : 10, IntegerValue))
+ return ErrorDiagnostic::get(SM, StrVal,
+ "unable to represent numeric value");
+
+ return IntegerValue;
+}
+
Expected<uint64_t> NumericVariableUse::eval() const {
Optional<uint64_t> Value = Variable->getValue();
if (Value)
@@ -51,11 +92,28 @@ Expected<uint64_t> BinaryOperation::eval() const {
return EvalBinop(*LeftOp, *RightOp);
}
+ExpressionFormat BinaryOperation::getImplicitFormat() const {
+ ExpressionFormat LeftFormat = LeftOperand->getImplicitFormat();
+ ExpressionFormat RightFormat = RightOperand->getImplicitFormat();
+
+ ExpressionFormat Format =
+ LeftFormat != ExpressionFormat::Kind::NoFormat ? LeftFormat : RightFormat;
+ if (LeftFormat != ExpressionFormat::Kind::NoFormat &&
+ RightFormat != ExpressionFormat::Kind::NoFormat &&
+ LeftFormat != RightFormat)
+ Format = ExpressionFormat(ExpressionFormat::Kind::Conflict);
+
+ return Format;
+}
+
Expected<std::string> NumericSubstitution::getResult() const {
- Expected<uint64_t> EvaluatedValue = ExpressionASTPointer->eval();
+ assert(ExpressionPointer->getAST() != nullptr &&
+ "Substituting empty expression");
+ Expected<uint64_t> EvaluatedValue = ExpressionPointer->getAST()->eval();
if (!EvaluatedValue)
return EvaluatedValue.takeError();
- return utostr(*EvaluatedValue);
+ ExpressionFormat Format = ExpressionPointer->getFormat();
+ return Format.getMatchingString(*EvaluatedValue);
}
Expected<std::string> StringSubstitution::getResult() const {
@@ -113,7 +171,8 @@ char NotFoundError::ID = 0;
Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context,
- Optional<size_t> LineNumber, const SourceMgr &SM) {
+ Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
+ const SourceMgr &SM) {
Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
if (!ParseVarResult)
return ParseVarResult.takeError();
@@ -137,10 +196,14 @@ Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
NumericVariable *DefinedNumericVariable;
auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
- if (VarTableIter != Context->GlobalNumericVariableTable.end())
+ if (VarTableIter != Context->GlobalNumericVariableTable.end()) {
DefinedNumericVariable = VarTableIter->second;
- else
- DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber);
+ if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat)
+ return ErrorDiagnostic::get(
+ SM, Expr, "format different from previous variable definition");
+ } else
+ DefinedNumericVariable =
+ Context->makeNumericVariable(Name, ImplicitFormat, LineNumber);
return DefinedNumericVariable;
}
@@ -165,7 +228,8 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
if (VarTableIter != Context->GlobalNumericVariableTable.end())
NumericVariable = VarTableIter->second;
else {
- NumericVariable = Context->makeNumericVariable(Name);
+ NumericVariable = Context->makeNumericVariable(
+ Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
Context->GlobalNumericVariableTable[Name] = NumericVariable;
}
@@ -198,7 +262,8 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
// Otherwise, parse it as a literal.
uint64_t LiteralValue;
- if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue))
+ if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
+ LiteralValue))
return std::make_unique<ExpressionLiteral>(LiteralValue);
return ErrorDiagnostic::get(SM, Expr,
@@ -244,7 +309,7 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
// The second operand in a legacy @LINE expression is always a literal.
AllowedOperand AO =
- IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any;
+ IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
parseNumericOperand(Expr, AO, LineNumber, Context, SM);
if (!RightOpResult)
@@ -255,13 +320,47 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
std::move(*RightOpResult));
}
-Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
+Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM) {
std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr;
StringRef DefExpr = StringRef();
DefinedNumericVariable = None;
+ ExpressionFormat ExplicitFormat = ExpressionFormat();
+
+ // Parse format specifier.
+ size_t FormatSpecEnd = Expr.find(',');
+ if (FormatSpecEnd != StringRef::npos) {
+ Expr = Expr.ltrim(SpaceChars);
+ if (!Expr.consume_front("%"))
+ return ErrorDiagnostic::get(
+ SM, Expr, "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 '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");
+ }
+
+ Expr = Expr.ltrim(SpaceChars);
+ if (!Expr.consume_front(","))
+ return ErrorDiagnostic::get(
+ SM, Expr, "invalid matching format specification in expression");
+ }
+
// Save variable definition expression if any.
size_t DefEnd = Expr.find(':');
if (DefEnd != StringRef::npos) {
@@ -271,6 +370,7 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
// Parse the expression itself.
Expr = Expr.ltrim(SpaceChars);
+ StringRef UseExpr = Expr;
if (!Expr.empty()) {
// The first operand in a legacy @LINE expression is always the @LINE
// pseudo variable.
@@ -288,22 +388,44 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
"unexpected characters at end of expression '" + Expr + "'");
}
if (!ParseResult)
- return ParseResult;
+ return ParseResult.takeError();
ExpressionASTPointer = std::move(*ParseResult);
}
+ // Select format of the expression, i.e. (i) its explicit format, if any,
+ // otherwise (ii) its implicit format, if any, otherwise (iii) the default
+ // format (unsigned). Error out in case of conflicting implicit format
+ // without explicit format.
+ ExpressionFormat Format,
+ ImplicitFormat = ExpressionASTPointer
+ ? ExpressionASTPointer->getImplicitFormat()
+ : ExpressionFormat(ExpressionFormat::Kind::NoFormat);
+ if (bool(ExplicitFormat))
+ Format = ExplicitFormat;
+ else if (ImplicitFormat == ExpressionFormat::Kind::Conflict)
+ return ErrorDiagnostic::get(
+ SM, UseExpr,
+ "variables with conflicting format specifier: need an explicit one");
+ else if (bool(ImplicitFormat))
+ Format = ImplicitFormat;
+ else
+ Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
+
+ std::unique_ptr<Expression> ExpressionPointer =
+ std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);
+
// Parse the numeric variable definition.
if (DefEnd != StringRef::npos) {
DefExpr = DefExpr.ltrim(SpaceChars);
- Expected<NumericVariable *> ParseResult =
- parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM);
+ Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition(
+ DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM);
if (!ParseResult)
return ParseResult.takeError();
DefinedNumericVariable = *ParseResult;
}
- return std::move(ExpressionASTPointer);
+ return std::move(ExpressionPointer);
}
bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
@@ -476,10 +598,10 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
}
// Parse numeric substitution block.
- std::unique_ptr<ExpressionAST> ExpressionASTPointer;
+ std::unique_ptr<Expression> ExpressionPointer;
Optional<NumericVariable *> DefinedNumericVariable;
if (IsNumBlock) {
- Expected<std::unique_ptr<ExpressionAST>> ParseResult =
+ Expected<std::unique_ptr<Expression>> ParseResult =
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
IsLegacyLineExpr, LineNumber, Context,
SM);
@@ -487,16 +609,18 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
logAllUnhandledErrors(ParseResult.takeError(), errs());
return true;
}
- ExpressionASTPointer = std::move(*ParseResult);
- SubstNeeded = ExpressionASTPointer != nullptr;
+ ExpressionPointer = std::move(*ParseResult);
+ SubstNeeded = ExpressionPointer->getAST() != nullptr;
if (DefinedNumericVariable) {
IsDefinition = true;
DefName = (*DefinedNumericVariable)->getName();
}
if (SubstNeeded)
SubstStr = MatchStr;
- else
- MatchRegexp = "[0-9]+";
+ else {
+ ExpressionFormat Format = ExpressionPointer->getFormat();
+ MatchRegexp = cantFail(Format.getWildcardRegex());
+ }
}
// Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].
@@ -554,8 +678,7 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
Substitution *Substitution =
IsNumBlock
? Context->makeNumericSubstitution(
- SubstStr, std::move(ExpressionASTPointer),
- SubstInsertIdx)
+ SubstStr, std::move(ExpressionPointer), SubstInsertIdx)
: Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
Substitutions.push_back(Substitution);
}
@@ -676,11 +799,11 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
NumericVariableMatch.DefinedNumericVariable;
StringRef MatchedValue = MatchInfo[CaptureParenGroup];
- uint64_t Val;
- if (MatchedValue.getAsInteger(10, Val))
- return ErrorDiagnostic::get(SM, MatchedValue,
- "Unable to represent numeric value");
- DefinedNumericVariable->setValue(Val);
+ ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat();
+ Expected<uint64_t> Value = Format.valueFromStringRepr(MatchedValue, SM);
+ if (!Value)
+ return Value.takeError();
+ DefinedNumericVariable->setValue(*Value);
}
// Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
@@ -837,10 +960,10 @@ FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
}
Substitution *FileCheckPatternContext::makeNumericSubstitution(
- StringRef ExpressionStr,
- std::unique_ptr<ExpressionAST> ExpressionASTPointer, size_t InsertIdx) {
+ StringRef ExpressionStr, std::unique_ptr<Expression> Expression,
+ size_t InsertIdx) {
Substitutions.push_back(std::make_unique<NumericSubstitution>(
- this, ExpressionStr, std::move(ExpressionASTPointer), InsertIdx));
+ this, ExpressionStr, std::move(Expression), InsertIdx));
return Substitutions.back().get();
}
@@ -1104,7 +1227,8 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
void FileCheckPatternContext::createLineVariable() {
assert(!LineVariable && "@LINE pseudo numeric variable already created");
StringRef LineName = "@LINE";
- LineVariable = makeNumericVariable(LineName);
+ LineVariable = makeNumericVariable(
+ LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
GlobalNumericVariableTable[LineName] = LineVariable;
}
@@ -1815,20 +1939,19 @@ Error FileCheckPatternContext::defineCmdlineVariables(
// to create the necessary class instance.
StringRef CmdlineDefExpr = CmdlineDef.substr(1);
Optional<NumericVariable *> DefinedNumericVariable;
- Expected<std::unique_ptr<ExpressionAST>> ExpressionASTResult =
+ Expected<std::unique_ptr<Expression>> ExpressionResult =
Pattern::parseNumericSubstitutionBlock(
CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM);
- if (!ExpressionASTResult) {
- Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError());
+ if (!ExpressionResult) {
+ Errs = joinErrors(std::move(Errs), ExpressionResult.takeError());
continue;
}
- std::unique_ptr<ExpressionAST> ExpressionASTPointer =
- std::move(*ExpressionASTResult);
+ std::unique_ptr<Expression> Expression = std::move(*ExpressionResult);
// 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 = ExpressionASTPointer->eval();
+ Expected<uint64_t> Value = Expression->getAST()->eval();
if (!Value) {
Errs = joinErrors(std::move(Errs), Value.takeError());
continue;