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 @@ -579,8 +579,8 @@ * ``%`` is an optional scanf-style matching format specifier to indicate what number format to match (e.g. hex number). Currently accepted - format specifier are ``%u``, ``%x`` and ``%X``. If absent, the format - specifier defaults to ``%u``. + format specifier 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. diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -40,10 +40,14 @@ // Numeric expression handling code. //===----------------------------------------------------------------------===// +class FileCheckNumExprVal; + /// Bitfield representing the format a numeric expression value should be /// printed into for matching. Used to represent both explicit format /// specifiers as well as implicit format from using numeric variables. struct FileCheckNumExprFmt { + /// Value is a signed integer. + unsigned Signed : 1; /// Value should be printed as hex number. unsigned Hex : 1; /// Value should be printed using upper case letters, only used for hex @@ -70,23 +74,98 @@ /// Return the string representation of \p Value in the format represented by /// this instance. - std::string getMatchingString(uint64_t Value) const; + llvm::Optional + getMatchingString(FileCheckNumExprVal &Value) const; /// Return the value corresponding to string representation \p StrVal - /// according to the matching format represented by this instance or nothing - /// if \p StrVal does not correspond to a valid and representable value. - llvm::Optional valueFromStringRepr(StringRef StrVal) const; - + /// according to the matching format represented by this instance or an + /// invalid value if \p StrVal does not correspond to a valid and + /// representable value. + FileCheckNumExprVal valueFromStringRepr(StringRef StrVal) const; }; /// Initializer for numeric expression without format. -const FileCheckNumExprFmt FmtNone = {0, 0, 0, 0}; +const FileCheckNumExprFmt FmtNone = {0, 0, 0, 0, 0}; /// Initializer for numeric expression matched as unsigned value. -const FileCheckNumExprFmt FmtUnsigned = {0, 0, 1, 0}; +const FileCheckNumExprFmt FmtUnsigned = {0, 0, 0, 1, 0}; +/// Initializer for numeric expression matched as signed value. +const FileCheckNumExprFmt FmtSigned = {1, 0, 0, 1, 0}; /// Initializer for numeric expression matched as lower case hex value. -const FileCheckNumExprFmt FmtLowHex = {1, 0, 1, 0}; +const FileCheckNumExprFmt FmtLowHex = {0, 1, 0, 1, 0}; /// Initializer for numeric expression matched as capital case hex value. -const FileCheckNumExprFmt FmtCapHex = {1, 1, 1, 0}; +const FileCheckNumExprFmt FmtCapHex = {0, 1, 1, 1, 0}; + +/// Class representing a numeric value. +class FileCheckNumExprVal { +private: + union { + int64_t SignedValue; + uint64_t UnsignedValue; + }; + + /// Whether value is signed (and thus is stored in SignedValue) or not (in + /// which case it is stored in UnsignedValue). + bool Signed; + + /// Whether this holds an actual value. Examples of where this would be + /// false: + /// - underflow or overflow of one of the binary operation in the expression; + /// - unset variable's value. + bool Valid; + +public: + /// Constructor for an invalid value. + FileCheckNumExprVal() : Valid(false) {} + + /// Constructor for a signed value. + explicit FileCheckNumExprVal(int64_t Val) + : SignedValue(Val), Signed(true), Valid(true) {} + + /// Constructor for an unsigned value. + explicit FileCheckNumExprVal(uint64_t Val) + : UnsignedValue(Val), Signed(false), Valid(true) {} + + /// Define equality to be true only if both values are valid and they have + /// the same signedness and corresponding value. Tentative bit is ignored to + /// allow the evaluation of a numeric expression using variables with + /// tentative value to compare equal to a matched value. + bool operator==(const FileCheckNumExprVal &other); + bool operator!=(const FileCheckNumExprVal &other) { + return !(*this == other); + } + + bool isSigned() const { return Signed; } + + bool isValid() const { return Valid; } + + /// Return the signed value. Must only be called if value is signed in the + /// first place. + int64_t getSignedValue() const { + assert(Signed); + return SignedValue; + } + + /// Return the unsigned value. Must only be called if value is unsigned in + /// the first place. + uint64_t getUnsignedValue() const { + assert(!Signed); + return UnsignedValue; + } + + /// Convert value to a signed value, or mark value invalid if not possible + /// (original value was not within range for a signed integer). + void convertSigned(); + + /// Convert value to an unsigned value, or mark value invalid if not possible + /// (original value was not within range for an unsigned integer). + void convertUnsigned(); + + /// Return an invalid value in case of underflow or overflow. + friend FileCheckNumExprVal operator+(const FileCheckNumExprVal &lhs, + const FileCheckNumExprVal &rhs); + friend FileCheckNumExprVal operator-(const FileCheckNumExprVal &lhs, + const FileCheckNumExprVal &rhs); +}; /// Base class representing the AST of a given numeric expression. class FileCheckNumExprAST { @@ -94,7 +173,7 @@ virtual ~FileCheckNumExprAST() = default; /// Evaluate the value of the expression represented by this AST. - virtual llvm::Optional eval() const = 0; + virtual FileCheckNumExprVal eval() const = 0; /// Return implicit format of this AST, FmtConflict if implicit formats of /// the AST's components conflict and Fmt none if the AST has no implicit @@ -112,15 +191,18 @@ class FileCheckNumExprLiteral : public FileCheckNumExprAST { private: /// Actual value of the literal. - uint64_t Value; + FileCheckNumExprVal Value; public: + /// Constructor for a signed literal. + FileCheckNumExprLiteral(int64_t Val) : Value(Val) {} + /// Constructor for an unsigned literal. FileCheckNumExprLiteral(uint64_t Val) : Value(Val) {} /// Evaluate the value of the expression represented by this AST. Therefore, /// return the literal's value. - llvm::Optional eval() const { return Value; } + FileCheckNumExprVal eval() const { return Value; } /// Return implicit format of this AST, therefore FmtNone. FileCheckNumExprFmt getImplicitFmt() const { return FmtNone; } @@ -169,11 +251,8 @@ /// FileCheckNumExpr with a null AST. std::shared_ptr NumExpr; - /// Whether variable is defined and thus Value is set. - bool Defined; - - /// Value of numeric variable if defined. - uint64_t Value; + /// Value of numeric variable. + FileCheckNumExprVal Value; /// Line number where this variable is defined. Used to determine whether a /// variable is defined on the same line as a given use. @@ -184,13 +263,14 @@ /// the numeric expression represented by NumExpr. FileCheckNumExprVar(StringRef Name, std::shared_ptr NumExpr, unsigned DefLineNumber) - : Name(Name), NumExpr(NumExpr), Defined(false), - DefLineNumber(DefLineNumber) {} + : Name(Name), NumExpr(NumExpr), DefLineNumber(DefLineNumber) { + Value = FileCheckNumExprVal(); + } /// Constructor for numeric variable \p Name with a known \p Value at parse /// time (e.g. the @LINE numeric variable). - explicit FileCheckNumExprVar(StringRef Name, uint64_t Value) - : Name(Name), NumExpr(nullptr), Defined(true), Value(Value) {} + FileCheckNumExprVar(StringRef Name, FileCheckNumExprVal &Value) + : Name(Name), NumExpr(nullptr), Value(Value) {} /// Return name of that numeric variable. StringRef getName() const { return Name; } @@ -201,7 +281,7 @@ /// Evaluate the value of the expression represented by this AST. Therefore, /// return this variable's value or the value of its associated numeric /// expression if any. - llvm::Optional eval() const; + FileCheckNumExprVal eval() const; /// Return whether this variable's value is known at match time, when /// performing the substitutions. @@ -215,7 +295,7 @@ /// Set value of this numeric variable if not defined. Return whether /// variable was already defined. - bool setValue(uint64_t Value); + bool setValue(FileCheckNumExprVal Value); /// Clear value of this numeric variable. Return whether value was already /// undefined. @@ -226,7 +306,8 @@ }; /// Type of functions evaluating a given binary operation. -using binop_eval_t = uint64_t (*)(uint64_t, uint64_t); +using binop_eval_t = FileCheckNumExprVal (*)(const FileCheckNumExprVal &, + const FileCheckNumExprVal &); /// Class representing a single binary operation in the AST of a numeric /// expression. @@ -250,7 +331,7 @@ /// Evaluate the value of the binary operation represented by this AST. Uses /// EvalBinop to perform the binary operation on the values of recursively /// evaluating the left and right operands. - llvm::Optional eval() const; + FileCheckNumExprVal eval() const; /// Return implicit format of this AST, FmtConflict if implicit formats of /// the AST's components conflict and Fmt none if the AST has no implicit 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 @@ -26,7 +26,8 @@ /// Define format equality: formats are equal if all bits are identical. bool FileCheckNumExprFmt::operator==(const FileCheckNumExprFmt &other) { - return Valid == other.Valid && Hex == other.Hex && Cap == other.Cap; + return Valid == other.Valid && Conflict == other.Conflict && + Signed == other.Signed && Hex == other.Hex && Cap == other.Cap; } /// Return wildcard regexp StringRef to match any value in the format @@ -38,32 +39,177 @@ return StringRef("[[:digit:]A-F]+"); else return StringRef("[[:digit:]a-f]+"); - } else + } else if (Signed) + return StringRef("-?[[:digit:]]+"); + else return StringRef("[[:digit:]]+"); } /// Return the string representation of \p Value in the format represented by /// this instance. -std::string FileCheckNumExprFmt::getMatchingString(uint64_t Value) const { - assert(Valid && !Conflict && "Trying to match value with invalid format"); +llvm::Optional +FileCheckNumExprFmt::getMatchingString(FileCheckNumExprVal &Value) const { + if (Signed) + Value.convertSigned(); + else + Value.convertUnsigned(); + + assert(!Conflict); + // Conversion error (e.g. negative value converted to unsigned). + if (!Valid) + return llvm::None; + if (Hex) - return utohexstr(Value, !Cap); + return utohexstr(Value.getUnsignedValue(), !Cap); + else if (Signed) + return itostr(Value.getSignedValue()); else - return utostr(Value); + return utostr(Value.getUnsignedValue()); } /// Return the value corresponding to string representation \p StrVal according /// to the matching format represented by this instance or nothing if \p StrVal /// does not correspond to a valid and representable value. -llvm::Optional +FileCheckNumExprVal FileCheckNumExprFmt::valueFromStringRepr(StringRef StrVal) const { unsigned Radix = Hex ? 16 : 10; - uint64_t Value; + if (Signed) { + int64_t SignedValue; - if (StrVal.getAsInteger(Radix, Value)) - return llvm::None; + if (StrVal.getAsInteger(Radix, SignedValue)) + return FileCheckNumExprVal(); - return Value; + return FileCheckNumExprVal(SignedValue); + } else { + uint64_t UnsignedValue; + + if (StrVal.getAsInteger(Radix, UnsignedValue)) + return FileCheckNumExprVal(); + + return FileCheckNumExprVal(UnsignedValue); + } +} + +/// Equality operator: 2 values are equal if both are valid and have same +/// signedness and corresponding value. Tentative bit is ignored to allow the +/// evaluation of a numeric expression using variables with tentative value to +/// compare equal to a matched value. +bool FileCheckNumExprVal::operator==(const FileCheckNumExprVal &other) { + if (!Valid || !other.Valid) + return false; + + if (Signed) + return SignedValue == other.SignedValue; + else + return UnsignedValue == other.UnsignedValue; +} + +/// Convert value to a signed value, or mark value invalid if not possible +/// (original value was not within range for a signed integer). +void FileCheckNumExprVal::convertSigned() { + if (!Valid || Signed) + return; + + if (UnsignedValue > std::numeric_limits::max()) { + Valid = false; + return; + } + + SignedValue = UnsignedValue; + Signed = true; +} + +/// Convert value to an unsigned value, or mark value invalid if not possible +/// (original value was not within range for an unsigned integer). +void FileCheckNumExprVal::convertUnsigned() { + if (!Valid || !Signed) + return; + + if (SignedValue < 0) { + Valid = false; + return; + } + + UnsignedValue = SignedValue; + Signed = false; + return; +} + +/// Return an invalid value in case of underflow or overflow. +llvm::FileCheckNumExprVal llvm:: +operator+(const llvm::FileCheckNumExprVal &Op1, + const llvm::FileCheckNumExprVal &Op2) { + // Operands must be valid. + if (!Op1.Valid || !Op2.Valid) + return FileCheckNumExprVal(); + + // Operands must have same sign. + if (Op1.Signed != Op2.Signed) + return FileCheckNumExprVal(); + + if (Op1.Signed) { + int64_t Val1 = Op1.SignedValue; + int64_t Val2 = Op2.SignedValue; + + // Op1 + Op2 > max int64_t. + if (Val1 > 0 && Val2 > 0 && + Val1 > (std::numeric_limits::max() - Val2)) + return FileCheckNumExprVal(); + + // Op1 + Op2 < min int64_t. + if (Val1 < 0 && Val2 < 0 && + Val1 < (std::numeric_limits::min() - Val2)) + return FileCheckNumExprVal(); + + return FileCheckNumExprVal(Val1 + Val2); + } else { + uint64_t Val1 = Op1.UnsignedValue; + uint64_t Val2 = Op2.UnsignedValue; + + // Op1 + Op2 > max uint64_t. + if (Val1 > std::numeric_limits::max() - Val2) + return FileCheckNumExprVal(); + + return FileCheckNumExprVal(Val1 + Val2); + } +} + +/// Return an invalid value in case of underflow or overflow. +llvm::FileCheckNumExprVal llvm:: +operator-(const llvm::FileCheckNumExprVal &Op1, + const llvm::FileCheckNumExprVal &Op2) { + // Operands must be valid. + if (!Op1.Valid || !Op2.Valid) + return FileCheckNumExprVal(); + + // Operands must have same sign. + if (Op1.Signed != Op2.Signed) + return FileCheckNumExprVal(); + + if (Op1.Signed) { + int64_t Val1 = Op1.SignedValue; + int64_t Val2 = Op2.SignedValue; + + // Op1 - Op2 > max int64_t. + if (Val1 > 0 && Val2 < 0 && + Val1 > (std::numeric_limits::max() + Val2)) + return FileCheckNumExprVal(); + + // Op1 - Op2 < min int64_t. + if (Val1 < 0 && Val2 > 0 && + Val1 < (std::numeric_limits::min() + Val2)) + return FileCheckNumExprVal(); + + return FileCheckNumExprVal(Val1 - Val2); + } else { + uint64_t Val1 = Op1.UnsignedValue; + uint64_t Val2 = Op2.UnsignedValue; + + // Op1 < Op2. + if (Val1 < Val2) + return FileCheckNumExprVal(); + return FileCheckNumExprVal(Val1 - Val2); + } } /// Generic constructor for a numeric expression whose equality constraint is @@ -82,15 +228,15 @@ /// Evaluate the value of the expression represented by this AST. Therefore, /// return this variable's value or the value of its associated numeric /// expression if any. -llvm::Optional FileCheckNumExprVar::eval() const { - if (Defined) +FileCheckNumExprVal FileCheckNumExprVar::eval() const { + if (Value.isValid()) return Value; assert(NumExpr != nullptr); if (NumExpr->getAST() != nullptr) return NumExpr->getAST()->eval(); - return llvm::None; + return FileCheckNumExprVal(); } /// Return whether this variable's value is known at match time, when @@ -110,43 +256,46 @@ /// Append numeric variable's name to UndefVarNames if undefined. void FileCheckNumExprVar::appendUndefVarNames( std::vector &UndefVarNames) const { - if (!Defined) + if (!Value.isValid()) UndefVarNames.emplace_back(Name); } /// Set value of this numeric variable if not defined. Return whether the /// variable was already defined. -bool FileCheckNumExprVar::setValue(uint64_t Value) { - if (Defined) +bool FileCheckNumExprVar::setValue(FileCheckNumExprVal Value) { + if (this->Value.isValid()) return true; + assert(Value.isValid() && "Setting invalid value"); this->Value = Value; - this->Defined = true; return false; } /// Clear value of this numeric variable. Return whether value was already /// undefined. bool FileCheckNumExprVar::clearValue() { - if (!Defined) + if (!Value.isValid()) return true; - Defined = false; + Value = FileCheckNumExprVal(); return false; } /// Evaluate the value of the binary operation represented by this AST. Uses /// EvalBinop to perform the binary operation on the values of recursively /// evaluating the left and right operands. -llvm::Optional FileCheckASTBinop::eval() const { - llvm::Optional OptOpl = Opl->eval(); - llvm::Optional OptOpr = Opr->eval(); +FileCheckNumExprVal FileCheckASTBinop::eval() const { + FileCheckNumExprVal LVal = Opl->eval(); + FileCheckNumExprVal RVal = Opr->eval(); + + // Integer promotion. + if (!LVal.isSigned() || !RVal.isSigned()) { + LVal.convertUnsigned(); + RVal.convertUnsigned(); + } - // Uses undefined variable. - if (!OptOpl.hasValue()) - return llvm::None; - if (!OptOpr.hasValue()) - return llvm::None; + if (!LVal.isValid() || !RVal.isValid()) + return FileCheckNumExprVal(); - return EvalBinop(OptOpl.getValue(), OptOpr.getValue()); + return EvalBinop(LVal, RVal); } /// Return implicit format of this AST, FmtConflict if implicit formats of the @@ -180,11 +329,12 @@ if (IsNumExpr) { assert(NumExpr->getAST() != nullptr && "Substituting empty numeric expression"); - llvm::Optional EvaluatedValue = NumExpr->getAST()->eval(); - if (!EvaluatedValue.hasValue()) + FileCheckNumExprVal EvaluatedValue = NumExpr->getAST()->eval(); + if (!EvaluatedValue.isValid()) return llvm::None; + FileCheckNumExprFmt Fmt = NumExpr->getEffectiveFmt(); - return Fmt.getMatchingString(EvaluatedValue.getValue()); + return Fmt.getMatchingString(EvaluatedValue); } else { // Look up the value and escape it so that we can put it into the // regex. @@ -363,16 +513,21 @@ if (AO != LegacyLiteral && AO != Any) return nullptr; unsigned Radix = (AO == LegacyLiteral) ? 10 : 0; - uint64_t LiteralValue; - if (Expr.consumeInteger(Radix, LiteralValue)) - return nullptr; - return std::make_shared(LiteralValue); + int64_t SignedLiteralValue; + uint64_t UnsignedLiteralValue; + StringRef SaveExpr = Expr; + // Accept both signed and unsigned literal. + if (AO != LegacyLiteral && !Expr.consumeInteger(Radix, SignedLiteralValue)) { + return std::make_shared(SignedLiteralValue); + } else { + Expr = SaveExpr; + if (!Expr.consumeInteger(Radix, UnsignedLiteralValue)) { + return std::make_shared(UnsignedLiteralValue); + } else + return nullptr; + } } -static uint64_t doAdd(uint64_t Opl, uint64_t Opr) { return Opl + Opr; } - -static uint64_t doSub(uint64_t Opl, uint64_t Opr) { return Opl - Opr; } - /// Parse \p Expr for a binary operation. The left operand of this binary /// operation is given in \p Opl and \p Legacy indicates whether we are parsing /// a legacy numeric expression. Return the class representing this binary @@ -393,10 +548,10 @@ binop_eval_t EvalBinop; switch (Operator) { case '+': - EvalBinop = doAdd; + EvalBinop = operator+; break; case '-': - EvalBinop = doSub; + EvalBinop = operator-; break; default: SM.PrintMessage(oploc, SourceMgr::DK_Error, @@ -462,6 +617,9 @@ case 'u': ExplicitFmt = FmtUnsigned; break; + case 'd': + ExplicitFmt = FmtSigned; + break; case 'x': ExplicitFmt = FmtLowHex; break; @@ -571,8 +729,9 @@ // Create fake @LINE pseudo variable definition. StringRef LinePseudo = "@LINE"; uint64_t LineNumber64 = this->LineNumber; + auto LineNumberVal = FileCheckNumExprVal(LineNumber64); auto LinePseudoVar = - std::make_shared(LinePseudo, LineNumber64); + std::make_shared(LinePseudo, LineNumberVal); Context->GlobalNumericVariableTable[LinePseudo] = LinePseudoVar; if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines)) @@ -891,8 +1050,12 @@ for (const auto &Subst : Substs) { // Substitute and check for failure (e.g. use of undefined variable). llvm::Optional OptValue = Subst.getSubstitute(); - if (!OptValue.hasValue()) + if (!OptValue.hasValue()) { + SM.PrintMessage(SMLoc::getFromPointer(Subst.getSubstString().data()), + SourceMgr::DK_Error, + "Unable to substitute variable or numeric expression"); return StringRef::npos; + } std::string Value = OptValue.getValue(); // Plop it into the regex at the adjusted offset. @@ -931,12 +1094,12 @@ StringRef MatchedValue = MatchInfo[CaptureParen]; assert(NumVarDef->getNumExpr() != nullptr); FileCheckNumExprFmt Fmt = NumVarDef->getNumExpr()->getEffectiveFmt(); - llvm::Optional OptValue = Fmt.valueFromStringRepr(MatchedValue); - if (!OptValue.hasValue()) { + FileCheckNumExprVal Value = Fmt.valueFromStringRepr(MatchedValue); + if (!Value.isValid()) { SM.PrintMessage(SMLoc::getFromPointer(MatchedValue.data()), SourceMgr::DK_Error, "Unable to represent numeric value"); } - if (NumVarDef->setValue(OptValue.getValue())) + if (NumVarDef->setValue(Value)) assert(false && "Numeric variable redefined"); } @@ -2033,15 +2196,15 @@ ErrorFound = true; continue; } - llvm::Optional OptValue = NumExpr->getAST()->eval(); - if (!OptValue.hasValue()) { + FileCheckNumExprVal Value = NumExpr->getAST()->eval(); + if (!Value.isValid()) { SM.PrintMessage(SMLoc::getFromPointer(CmdlineDefRef.data()), SourceMgr::DK_Error, "Unable to represent numeric value"); ErrorFound = true; continue; } - NumVarDef->setValue(OptValue.getValue()); + NumVarDef->setValue(Value); // Record this variable definition. GlobalNumericVariableTable[NumVarDef->getName()] = NumVarDef; 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 @@ -11,6 +11,8 @@ ; RUN: not FileCheck -DPATVAR=foobar -D#PATVAR=42 -check-prefix CONFLICT -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix CLI-NUM-CONFLICT %s ; RUN: not FileCheck -check-prefix DEF-EXPR-FAIL -input-file %s %s ; RUN: not FileCheck -check-prefixes CHECK,FMT-CONFLICT -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix FMT-CONFLICT-MSG %s +; RUN: not FileCheck -check-prefix OVERFLOW -input-file %s %s 2>&1 | FileCheck -check-prefix OVERFLOW-MSG %s +; RUN: not FileCheck -check-prefix UNDERFLOW -input-file %s %s 2>&1 | FileCheck -check-prefix UNDERFLOW-MSG %s ; We ensure we attemt to match all lines with digits by using CHECK-NEXT @@ -37,26 +39,28 @@ ; Numeric variable definition with explicit matching format DEF FMT +-30 c D ; CHECK-LABEL: DEF FMT -; CHECK-NEXT: [[#%x,VAR2:]] -; CHECK-NEXT: [[#%X,VAR3:]] +; CHECK-NEXT: [[#%d,VAR2:]] +; CHECK-NEXT: [[#%x,VAR3:]] +; CHECK-NEXT: [[#%X,VAR4:]] ; Numeric variable definition with explicit matching format in alternate spacing DEF FMT ALT SPC -c -c -c -c -c +-30 +-30 +-30 +-30 +-30 ; CHECK-LABEL: DEF FMT ALT SPC -; CHECK-NEXT: [[#%x, VAR2a:]] -; CHECK-NEXT: [[# %x, VAR2b:]] -; CHECK-NEXT: [[# %x , VAR2c:]] -; CHECK-NEXT: [[# %x , VAR2d :]] -; CHECK-NEXT: [[# %x , VAR2e : ]] +; CHECK-NEXT: [[#%d, VAR2a:]] +; CHECK-NEXT: [[# %d, VAR2b:]] +; CHECK-NEXT: [[# %d , VAR2c:]] +; CHECK-NEXT: [[# %d , VAR2d :]] +; CHECK-NEXT: [[# %d , VAR2e : ]] ; Numeric expressions in explicit matching format and default matching rule using ; variables defined on other lines @@ -64,26 +68,36 @@ 11 12 10 +-30 +-29 +-31 c d b 1a +1a D E C 1B +1B ; CHECK-LABEL: USE DEF FMT IMPL MATCH ; CHECK-NEXT: [[#%u,VAR1]] ; CHECK-NEXT: [[#%u,VAR1+1]] ; CHECK-NEXT: [[#%u,VAR1-1]] -; CHECK-NEXT: [[#%x,VAR2]] -; CHECK-NEXT: [[#%x,VAR2+1]] -; CHECK-NEXT: [[#%x,VAR2-1]] -; CHECK-NEXT: [[#%x,VAR2+14]] -; CHECK-NEXT: [[#%X,VAR3]] -; CHECK-NEXT: [[#%X,VAR3+1]] -; CHECK-NEXT: [[#%X,VAR3-1]] -; CHECK-NEXT: [[#%X,VAR3+14]] +; CHECK-NEXT: [[#%d,VAR2]] +; CHECK-NEXT: [[#%d,VAR2+1]] +; CHECK-NEXT: [[#%d,VAR2-1]] +; CHECK-NEXT: [[#%x,VAR3]] +; CHECK-NEXT: [[#%x,VAR3+1]] +; CHECK-NEXT: [[#%x,VAR3-1]] +; CHECK-NEXT: [[#%x,VAR3+0xe]] +; CHECK-NEXT: [[#%x,VAR3+0xE]] +; CHECK-NEXT: [[#%X,VAR4]] +; CHECK-NEXT: [[#%X,VAR4+1]] +; CHECK-NEXT: [[#%X,VAR4-1]] +; CHECK-NEXT: [[#%X,VAR4+0xe]] +; CHECK-NEXT: [[#%X,VAR4+0xE]] ; Numeric expressions in explicit matching format and default matching rule using @@ -128,14 +142,19 @@ 11 12 10 +-30 +-29 +-31 c d b 1a +1a D E C 1B +1B ; CHECK-LABEL: USE IMPL FMT IMPL MATCH ; CHECK-NEXT: [[#VAR1]] ; CHECK-NEXT: [[#VAR1+1]] @@ -143,18 +162,23 @@ ; CHECK-NEXT: [[#VAR2]] ; CHECK-NEXT: [[#VAR2+1]] ; CHECK-NEXT: [[#VAR2-1]] -; CHECK-NEXT: [[#VAR2+14]] ; CHECK-NEXT: [[#VAR3]] ; CHECK-NEXT: [[#VAR3+1]] ; CHECK-NEXT: [[#VAR3-1]] -; CHECK-NEXT: [[#VAR3+14]] +; CHECK-NEXT: [[#VAR3+0xe]] +; CHECK-NEXT: [[#VAR3+0xE]] +; CHECK-NEXT: [[#VAR4]] +; CHECK-NEXT: [[#VAR4+1]] +; CHECK-NEXT: [[#VAR4-1]] +; CHECK-NEXT: [[#VAR4+0xe]] +; CHECK-NEXT: [[#VAR4+0xE]] ; Explicit format override implicit format conflicts VAR USE IMPL OVERRIDE FMT CONFLICT 23 ; CHECK-LABEL: VAR USE IMPL OVERRIDE FMT CONFLICT -; CHECK-NEXT: [[# %u, VAR1 + VAR2]] +; CHECK-NEXT: [[# %u, VAR1 + VAR3]] ; Numeric expressions using variables defined on the command-line and an @@ -172,12 +196,16 @@ b B 12 +12 +13 13 ; CHECK-LABEL: USE CONV FMT IMPL MATCH ; CHECK-NEXT: [[# %x, VAR1]] ; CHECK-NEXT: [[# %X, VAR1]] -; CHECK-NEXT: [[# %u, VAR2]] ; CHECK-NEXT: [[# %u, VAR3]] +; CHECK-NEXT: [[# %d, VAR3]] +; CHECK-NEXT: [[# %u, VAR4]] +; CHECK-NEXT: [[# %d, VAR4]] ; Numeric variable definition with unsupported matching format @@ -274,3 +302,23 @@ ; FMT-CONFLICT-MSG: numeric-expression.txt:[[#@LINE-1]]:25: error: Variables with conflicting format specifier: need an explicit one ; FMT-CONFLICT-MSG-NEXT: {{F}}MT-CONFLICT-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}} ; FMT-CONFLICT-MSG-NEXT: {{^ \^$}} + + +; Numeric expression with overflow +OVERFLOW +BIGVAR=10000000000000000 +; OVERFLOW-LABEL: OVERFLOW +; OVERFLOW-NEXT: BIGVAR: [[#BIGVAR:0x8000000000000000+0x8000000000000000]] +; OVERFLOW-MSG: numeric-expression.txt:[[#@LINE-1]]:29: error: Unable to substitute variable or numeric expression +; OVERFLOW-MSG-NEXT: {{O}}VERFLOW-NEXT: BIGVAR: {{\[\[#BIGVAR:0x8000000000000000\+0x8000000000000000\]\]}} +; OVERFLOW-MSG-NEXT: {{^ \^$}} + + +; Numeric expression with underflow +UNDERFLOW +TINYVAR=-10000000000000000 +; UNDERFLOW-LABEL: UNDERFLOW +; UNDERFLOW-NEXT: TINYVAR: [[#%d,TINYVAR:-0x8000000000000000-0x8000000000000000]] +; UNDERFLOW-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: Unable to substitute variable or numeric expression +; UNDERFLOW-MSG-NEXT: {{U}}NDERFLOW-NEXT: TINYVAR: {{\[\[#%d,TINYVAR:-0x8000000000000000-0x8000000000000000\]\]}} +; UNDERFLOW-MSG-NEXT: {{^ \^$}}