diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -622,12 +622,23 @@ as: * a numeric operand, or - * an expression followed by an operator and a numeric operand. + * a function call, or + * an expression followed by an operator and an expression. A numeric operand is a previously defined numeric variable, or an integer literal. The supported operators are ``+`` and ``-``. Spaces are accepted before, after and between any of these elements. +The syntax of a function call is ``!()`` where: + +* ``name`` is a predefined string literal. Acceptable values are: + + * umax - Return the largest of two unsigned operands. + * umin - Return the smallest of two unsigned operands. + * umul - Return the multiplication of two unsigned operands. + +* ```` is a comma seperated list of expressions. + For example: .. code-block:: llvm diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -185,6 +185,9 @@ // FileCheck input canonicalization. constexpr StringLiteral SpaceChars = " \t"; +// StringRef holding the prefix used to identify the name of a function. +constexpr StringLiteral CallPrefix = "!"; + // Parsing helper function that strips the first character in S and returns it. static char popFront(StringRef &S) { char C = S.front(); @@ -273,6 +276,13 @@ Expected> Pattern::parseNumericOperand( StringRef &Expr, AllowedOperand AO, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM) { + if (Expr.startswith(CallPrefix)) { + if (AO != AllowedOperand::Any) + return ErrorDiagnostic::get( + SM, Expr, "function call not permitted here"); + return parseCallExpr(Expr, LineNumber, Context, SM); + } + if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) { // Try to parse as a numeric variable use. Expected ParseVarResult = @@ -308,6 +318,18 @@ return LeftOp - RightOp; } +static uint64_t umax(uint64_t LeftOp, uint64_t RightOp) { + return std::max(LeftOp, RightOp); +} + +static uint64_t umin(uint64_t LeftOp, uint64_t RightOp) { + return std::min(LeftOp, RightOp); +} + +static uint64_t umul(uint64_t LeftOp, uint64_t RightOp) { + return LeftOp * RightOp; +} + Expected> Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr, std::unique_ptr LeftOp, @@ -352,6 +374,77 @@ std::move(*RightOpResult)); } +Expected> +Pattern::parseCallExpr(StringRef &Expr, Optional LineNumber, + FileCheckPatternContext *Context, const SourceMgr &SM) { + Expr = Expr.ltrim(SpaceChars); // TODO: do I need this + assert(Expr.startswith(CallPrefix)); + Expr.consume_front(CallPrefix); + + SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); + + size_t FuncNameEnd = Expr.find('('); + if (FuncNameEnd == StringRef::npos) + return ErrorDiagnostic::get( + SM, OpLoc, "missing '(' at start of call expression"); + + StringRef FuncName = Expr.take_front(FuncNameEnd); + auto OptFunc = StringSwitch>(FuncName) + .Case("umax", umax) + .Case("umin", umin) + .Case("umul", umul) + .Default(None); + + // @( is used to enforce operator precedence. + if (!OptFunc && (FuncName != "")) + return ErrorDiagnostic::get( + SM, OpLoc, Twine("unsupported function '") + FuncName + "'"); + + // String funcion name along with leading '('; + Expr = Expr.drop_front(FuncNameEnd + 1); + Expr = Expr.ltrim(SpaceChars); + + // TODO: what about end of string + SmallVector, 4> Args; + while (!Expr.startswith(")")) { + Expected> Arg = + parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM); + + if (!Arg) + return ErrorDiagnostic::get(SM, OpLoc, "bad call syntax - no arg"); + + Args.push_back(std::move(*Arg)); + Expr = Expr.ltrim(SpaceChars); + + if (!Expr.startswith(")") && !Expr.startswith(",")) + return ErrorDiagnostic::get(SM, OpLoc, Twine("bad call syntax - no seperator '") + Expr + "'"); + + if (Expr.consume_front(",")) + Expr = Expr.ltrim(SpaceChars); + } + + if (!Expr.consume_front(")")) + return ErrorDiagnostic::get( + SM, OpLoc, "missing ')' at end of call expression"); + + // @( is used to enforce operator precedence. + if (FuncName == "") { + if (Args.size() != 1) + return ErrorDiagnostic::get( + SM, OpLoc, Twine("invalid function signature '") + FuncName + "'"); + + return std::move(Args[0]); + } + + // TODO: support more signatuture types. + if (Args.size() != 2) + return ErrorDiagnostic::get( + SM, OpLoc, Twine("invalid function signature '") + FuncName + "'"); + + return std::make_unique(Expr, *OptFunc, std::move(Args[0]), + std::move(Args[1])); +} + Expected> Pattern::parseNumericSubstitutionBlock( StringRef Expr, Optional &DefinedNumericVariable, bool IsLegacyLineExpr, Optional LineNumber, @@ -361,9 +454,11 @@ DefinedNumericVariable = None; ExpressionFormat ExplicitFormat = ExpressionFormat(); - // Parse format specifier. + // Parse format specifier (NOTE: ',' is also an argument seperator). size_t FormatSpecEnd = Expr.find(','); - if (FormatSpecEnd != StringRef::npos) { + size_t FunctionStart = Expr.find(CallPrefix); + if (FormatSpecEnd != StringRef::npos && + FormatSpecEnd < FunctionStart) { Expr = Expr.ltrim(SpaceChars); if (!Expr.consume_front("%")) return ErrorDiagnostic::get( diff --git a/llvm/lib/Support/FileCheckImpl.h b/llvm/lib/Support/FileCheckImpl.h --- a/llvm/lib/Support/FileCheckImpl.h +++ b/llvm/lib/Support/FileCheckImpl.h @@ -684,6 +684,11 @@ std::unique_ptr LeftOp, bool IsLegacyLineExpr, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); + + /// Complete me! + static Expected> + parseCallExpr(StringRef &Expr, Optional LineNumber, + FileCheckPatternContext *Context, const SourceMgr &SM); }; //===----------------------------------------------------------------------===// diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -67,14 +67,25 @@ 11 12 10 +11 +99 +7 +11 +77 c d b 1a +ff +a +c0 D E C 1B +FF +A +D0 11 11 11 @@ -87,14 +98,25 @@ CHECK-NEXT: [[#%u,VAR1]] CHECK-NEXT: [[#%u,VAR1+1]] CHECK-NEXT: [[#%u,VAR1-1]] +CHECK-NEXT: [[#%u,!umax(VAR1,7)]] +CHECK-NEXT: [[#%u,!umax(VAR1,99)]] +CHECK-NEXT: [[#%u,!umin(VAR1,7)]] +CHECK-NEXT: [[#%u,!umin(VAR1,99)]] +CHECK-NEXT: [[#%u,!umul(VAR1,7)]] CHECK-NEXT: [[#%x,VAR2]] CHECK-NEXT: [[#%x,VAR2+1]] CHECK-NEXT: [[#%x,VAR2-1]] CHECK-NEXT: [[#%x,VAR2+14]] +CHECK-NEXT: [[#%x,!umax(VAR2,255)]] +CHECK-NEXT: [[#%x,!umin(VAR2,10)]] +CHECK-NEXT: [[#%x,!umul(VAR2,16)]] CHECK-NEXT: [[#%X,VAR3]] CHECK-NEXT: [[#%X,VAR3+1]] CHECK-NEXT: [[#%X,VAR3-1]] CHECK-NEXT: [[#%X,VAR3+14]] +CHECK-NEXT: [[#%X,!umax(VAR3,255)]] +CHECK-NEXT: [[#%X,!umin(VAR3,10)]] +CHECK-NEXT: [[#%X,!umul(VAR3,16)]] CHECK-NEXT: [[#%u,VAR1a]] CHECK-NEXT: [[#%u,VAR1b]] CHECK-NEXT: [[#%u,VAR1c]] @@ -118,6 +140,14 @@ 10 10 10 +22 +22 +22 +22 +22 +22 +22 +22 CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC CHECK-NEXT: [[#%u, VAR1]] CHECK-NEXT: [[# %u, VAR1]] @@ -134,6 +164,13 @@ CHECK-NEXT: [[# %u , VAR1 -1]] CHECK-NEXT: [[# %u , VAR1 - 1]] CHECK-NEXT: [[# %u , VAR1 - 1 ]] +CHECK-NEXT: [[#%u, !umul(VAR1,2)]] +CHECK-NEXT: [[# %u, !umul(VAR1,2)]] +CHECK-NEXT: [[# %u , !umul(VAR1,2)]] +CHECK-NEXT: [[# %u , !umul(VAR1, 2)]] +CHECK-NEXT: [[# %u , !umul( VAR1, 2)]] +CHECK-NEXT: [[# %u , !umul( VAR1,2)]] +CHECK-NEXT: [[# %u , !umul(VAR1,2) ]] ; Numeric expressions in implicit matching format and default matching rule using ; variables defined on other lines. @@ -141,26 +178,44 @@ 11 12 10 +99 +7 +77 c d b 1a +ff +a +c0 D E C 1B +FF +A +D0 CHECK-LABEL: USE IMPL FMT IMPL MATCH CHECK-NEXT: [[#VAR1]] CHECK-NEXT: [[#VAR1+1]] CHECK-NEXT: [[#VAR1-1]] +CHECK-NEXT: [[#!umax(VAR1,99)]] +CHECK-NEXT: [[#!umin(VAR1,7)]] +CHECK-NEXT: [[#!umul(VAR1,7)]] CHECK-NEXT: [[#VAR2]] CHECK-NEXT: [[#VAR2+1]] CHECK-NEXT: [[#VAR2-1]] CHECK-NEXT: [[#VAR2+14]] +CHECK-NEXT: [[#!umax(VAR2,255)]] +CHECK-NEXT: [[#!umin(VAR2,10)]] +CHECK-NEXT: [[#!umul(VAR2,16)]] CHECK-NEXT: [[#VAR3]] CHECK-NEXT: [[#VAR3+1]] CHECK-NEXT: [[#VAR3-1]] CHECK-NEXT: [[#VAR3+14]] +CHECK-NEXT: [[#!umax(VAR3,255)]] +CHECK-NEXT: [[#!umin(VAR3,10)]] +CHECK-NEXT: [[#!umul(VAR3,16)]] ; Numeric expressions using variables defined on other lines and an immediate ; interpreted as an unsigned value.