diff --git a/flang/include/flang/Common/format.h b/flang/include/flang/Common/format.h index 1650f56140b4d..848aa9809e4b7 100644 --- a/flang/include/flang/Common/format.h +++ b/flang/include/flang/Common/format.h @@ -12,6 +12,7 @@ #include "Fortran-consts.h" #include "enum-set.h" #include +#include // Define a FormatValidator class template to validate a format expression // of a given CHAR type. To enable use in runtime library code as well as @@ -28,6 +29,71 @@ namespace Fortran::common { +// AddOverflow and MulOverflow are copied from +// llvm/include/llvm/Support/MathExtras.h and specialised to int64_t. + +/// Add two signed integers, computing the two's complement truncated result, +/// returning true if overflow occurred. +static inline bool AddOverflow(int64_t X, int64_t Y, int64_t &Result) { +#if __has_builtin(__builtin_add_overflow) + return __builtin_add_overflow(X, Y, &Result); +#else + // Perform the unsigned addition. + const uint64_t UX = static_cast(X); + const uint64_t UY = static_cast(Y); + const uint64_t UResult = UX + UY; + + // Convert to signed. + Result = static_cast(UResult); + + // Adding two positive numbers should result in a positive number. + if (X > 0 && Y > 0) { + return Result <= 0; + } + // Adding two negatives should result in a negative number. + if (X < 0 && Y < 0) { + return Result >= 0; + } + return false; +#endif +} + +/// Multiply two signed integers, computing the two's complement truncated +/// result, returning true if an overflow occurred. +static inline bool MulOverflow(int64_t X, int64_t Y, int64_t &Result) { +#if __has_builtin(__builtin_mul_overflow) + return __builtin_mul_overflow(X, Y, &Result); +#else + // Perform the unsigned multiplication on absolute values. + const uint64_t UX = + X < 0 ? (0 - static_cast(X)) : static_cast(X); + const uint64_t UY = + Y < 0 ? (0 - static_cast(Y)) : static_cast(Y); + const uint64_t UResult = UX * UY; + + // Convert to signed. + const bool IsNegative = (X < 0) ^ (Y < 0); + Result = IsNegative ? (0 - UResult) : UResult; + + // If any of the args was 0, result is 0 and no overflow occurs. + if (UX == 0 || UY == 0) { + return false; + } + + // UX and UY are in [1, 2^n], where n is the number of digits. + // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for + // positive) divided by an argument compares to the other. + if (IsNegative) { + return UX > (static_cast(std::numeric_limits::max()) + + uint64_t(1)) / + UY; + } else { + return UX > + (static_cast(std::numeric_limits::max())) / UY; + } +#endif +} + struct FormatMessage { const char *text; // message text; may have one %s argument const char *arg; // optional %s argument value @@ -214,16 +280,18 @@ template void FormatValidator::NextToken() { case '7': case '8': case '9': { - int64_t lastValue; const CHAR *lastCursor; integerValue_ = 0; bool overflow{false}; do { - lastValue = integerValue_; lastCursor = cursor_; - integerValue_ = 10 * integerValue_ + c - '0'; - if (lastValue > integerValue_) { - overflow = true; + if (!overflow) { + overflow = + MulOverflow(static_cast(10), integerValue_, integerValue_); + } + if (!overflow) { + overflow = AddOverflow( + integerValue_, static_cast(c - '0'), integerValue_); } c = NextChar(); } while (c >= '0' && c <= '9');