From 7cd0745c95f1fd9353532be1d0073915e0cacce2 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Mon, 17 Oct 2016 22:49:24 +0000 Subject: Resubmit "Add support for advanced number formatting." This resubmits commits 284425 and r284428, which were reverted in r284429 due to some infinite recursion caused by an incorrect selection of function overloads. Reproduced the failure on Linux using GCC 4.8.4, and confirmed that with the new patch the tests path on GCC as well as MSVC. So hopefully this fixes everything. llvm-svn: 284436 --- llvm/lib/Support/NativeFormatting.cpp | 388 +++++++++++++++++++++++++++------- 1 file changed, 308 insertions(+), 80 deletions(-) (limited to 'llvm/lib/Support/NativeFormatting.cpp') diff --git a/llvm/lib/Support/NativeFormatting.cpp b/llvm/lib/Support/NativeFormatting.cpp index 9989488ddcc..084e6bf598c 100644 --- a/llvm/lib/Support/NativeFormatting.cpp +++ b/llvm/lib/Support/NativeFormatting.cpp @@ -9,107 +9,251 @@ #include "llvm/Support/NativeFormatting.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Format.h" using namespace llvm; +static bool isHexStyle(IntegerStyle S) { + switch (S) { + case IntegerStyle::HexLowerNoPrefix: + case IntegerStyle::HexLowerPrefix: + case IntegerStyle::HexUpperNoPrefix: + case IntegerStyle::HexUpperPrefix: + return true; + default: + return false; + } + LLVM_BUILTIN_UNREACHABLE; +} + +static HexStyle intHexStyleToHexStyle(IntegerStyle S) { + assert(isHexStyle(S)); + switch (S) { + case IntegerStyle::HexLowerNoPrefix: + return HexStyle::Lower; + case IntegerStyle::HexLowerPrefix: + return HexStyle::PrefixLower; + case IntegerStyle::HexUpperNoPrefix: + return HexStyle::Upper; + case IntegerStyle::HexUpperPrefix: + return HexStyle::PrefixUpper; + default: + break; + } + LLVM_BUILTIN_UNREACHABLE; +} + +static void writePadding(raw_ostream &S, Optional FieldWidth, + size_t Chars) { + if (!FieldWidth.hasValue()) + return; + + int Pad = *FieldWidth - Chars; + if (Pad > 0) + S.indent(Pad); +} + template static int format_to_buffer(T Value, char (&Buffer)[N]) { char *EndPtr = std::end(Buffer); char *CurPtr = EndPtr; - while (Value) { + do { *--CurPtr = '0' + char(Value % 10); Value /= 10; - } + } while (Value); return EndPtr - CurPtr; } -void llvm::write_ulong(raw_ostream &S, unsigned long N, std::size_t MinWidth) { - // Zero is a special case. - if (N == 0) { - if (MinWidth > 0) - S.indent(MinWidth - 1); - S << '0'; - return; - } +static void repeat_char(raw_ostream &S, char C, size_t Times) { + for (size_t I = 0; I < Times; ++I) + S << C; +} - char NumberBuffer[20]; - int Len = format_to_buffer(N, NumberBuffer); - int Pad = (MinWidth == 0) ? 0 : MinWidth - Len; - if (Pad > 0) - S.indent(Pad); - S.write(std::end(NumberBuffer) - Len, Len); +static void writeWithCommas(raw_ostream &S, ArrayRef Buffer) { + assert(!Buffer.empty()); + + ArrayRef ThisGroup; + int InitialDigits = ((Buffer.size() - 1) % 3) + 1; + ThisGroup = Buffer.take_front(InitialDigits); + S.write(ThisGroup.data(), ThisGroup.size()); + + Buffer = Buffer.drop_front(InitialDigits); + assert(Buffer.size() % 3 == 0); + while (!Buffer.empty()) { + S << ','; + ThisGroup = Buffer.take_front(3); + S.write(ThisGroup.data(), 3); + Buffer = Buffer.drop_front(3); + } } -void llvm::write_long(raw_ostream &S, long N, std::size_t MinWidth) { - if (N >= 0) { - write_ulong(S, static_cast(N), MinWidth); +template +static void write_unsigned_impl(raw_ostream &S, T N, IntegerStyle Style, + Optional Precision, Optional Width, + bool IsNegative) { + static_assert(std::is_unsigned::value, "Value is not unsigned!"); + + if (Style == IntegerStyle::Exponent) { + write_double(S, static_cast(N), FloatStyle::Exponent, Precision, + Width); + return; + } else if (Style == IntegerStyle::ExponentUpper) { + write_double(S, static_cast(N), FloatStyle::ExponentUpper, + Precision, Width); + return; + } else if (isHexStyle(Style)) { + write_hex(S, N, intHexStyleToHexStyle(Style), Precision, Width); return; } - unsigned long UN = -(unsigned long)N; - if (MinWidth > 0) - --MinWidth; + size_t Prec = Precision.getValueOr(getDefaultPrecision(Style)); + char NumberBuffer[128]; + std::memset(NumberBuffer, '0', sizeof(NumberBuffer)); - char NumberBuffer[20]; - int Len = format_to_buffer(UN, NumberBuffer); - int Pad = (MinWidth == 0) ? 0 : MinWidth - Len; - if (Pad > 0) - S.indent(Pad); - S.write('-'); - S.write(std::end(NumberBuffer) - Len, Len); -} + size_t Len = 0; + Len = format_to_buffer(N, NumberBuffer); -void llvm::write_ulonglong(raw_ostream &S, unsigned long long N, - std::size_t MinWidth) { - // Output using 32-bit div/mod when possible. - if (N == static_cast(N)) { - write_ulong(S, static_cast(N), MinWidth); - return; + bool WriteDecimal = + ((Style == IntegerStyle::Fixed || Style == IntegerStyle::Percent) && + Prec > 0); + + size_t LeadingZeros = 0; + if ((Style == IntegerStyle::Integer || Style == IntegerStyle::Number) && + Prec > 0) { + if (Prec > Len) + LeadingZeros = Prec - Len; } - char NumberBuffer[32]; - int Len = format_to_buffer(N, NumberBuffer); - int Pad = (MinWidth == 0) ? 0 : MinWidth - Len; - if (Pad > 0) - S.indent(Pad); - S.write(std::end(NumberBuffer) - Len, Len); + Len += LeadingZeros; + + // One for the decimal sign, one for each point of precision. + size_t DecimalChars = WriteDecimal ? 1 + Prec : 0; + + // One character for the negative sign. + size_t Neg = (IsNegative) ? 1 : 0; + + // One comma for each group of 3 digits. + size_t Commas = (Style != IntegerStyle::Number) ? 0 : (Len - 1) / 3; + + size_t PercentChars = 0; + if (Style == IntegerStyle::Percent) { + // For all numbers except 0, we append two additional 0s. + PercentChars = (N == 0) ? 1 : 3; + } + + writePadding(S, Width, Len + DecimalChars + Neg + Commas + PercentChars); + + if (IsNegative) + S << '-'; + if (Style == IntegerStyle::Number) { + writeWithCommas(S, ArrayRef(std::end(NumberBuffer) - Len, Len)); + } else { + S.write(std::end(NumberBuffer) - Len, Len); + if (Style == IntegerStyle::Percent && N != 0) { + // Rather than multiply by 100, write the characters manually, in case the + // multiplication would overflow. + S << "00"; + } + } + + if (WriteDecimal) { + S << '.'; + repeat_char(S, '0', Prec); + } + if (Style == IntegerStyle::Percent) + S << '%'; +} + +template +static void write_unsigned(raw_ostream &S, T N, IntegerStyle Style, + Optional Precision, Optional Width, + bool IsNegative = false) { + // Output using 32-bit div/mod if possible. + if (N == static_cast(N)) + write_unsigned_impl(S, static_cast(N), Style, Precision, Width, + IsNegative); + else + write_unsigned_impl(S, N, Style, Precision, Width, IsNegative); } -void llvm::write_longlong(raw_ostream &S, long long N, std::size_t MinWidth) { +template +static void write_signed(raw_ostream &S, T N, IntegerStyle Style, + Optional Precision, Optional Width) { + static_assert(std::is_signed::value, "Value is not signed!"); + + using UnsignedT = typename std::make_unsigned::type; + if (N >= 0) { - write_ulonglong(S, static_cast(N), MinWidth); + write_unsigned(S, static_cast(N), Style, Precision, Width); return; } - // Avoid undefined behavior on INT64_MIN with a cast. - unsigned long long UN = -(unsigned long long)N; - if (MinWidth > 0) - --MinWidth; + UnsignedT UN = -(UnsignedT)N; + if (isHexStyle(Style)) { + static_assert(sizeof(UnsignedT) == sizeof(T), + "Types do not have the same size!"); + std::memcpy(&UN, &N, sizeof(N)); + write_hex(S, UN, intHexStyleToHexStyle(Style), Precision, Width); + return; + } + write_unsigned(S, UN, Style, Precision, Width, true); +} - char NumberBuffer[32]; - int Len = format_to_buffer(UN, NumberBuffer); - int Pad = (MinWidth == 0) ? 0 : MinWidth - Len; - if (Pad > 0) - S.indent(Pad); - S.write('-'); - S.write(std::end(NumberBuffer) - Len, Len); +void llvm::write_integer(raw_ostream &S, unsigned int N, IntegerStyle Style, + Optional Precision, Optional Width) { + write_unsigned(S, N, Style, Precision, Width); } -void llvm::write_hex(raw_ostream &S, unsigned long long N, std::size_t MinWidth, - bool Upper, bool Prefix) { +void llvm::write_integer(raw_ostream &S, int N, IntegerStyle Style, + Optional Precision, Optional Width) { + write_signed(S, N, Style, Precision, Width); +} + +void llvm::write_integer(raw_ostream &S, unsigned long N, IntegerStyle Style, + Optional Precision, Optional Width) { + write_unsigned(S, N, Style, Precision, Width); +} + +void llvm::write_integer(raw_ostream &S, long N, IntegerStyle Style, + Optional Precision, Optional Width) { + write_signed(S, N, Style, Precision, Width); +} + +void llvm::write_integer(raw_ostream &S, unsigned long long N, + IntegerStyle Style, Optional Precision, + Optional Width) { + write_unsigned(S, N, Style, Precision, Width); +} + +void llvm::write_integer(raw_ostream &S, long long N, IntegerStyle Style, + Optional Precision, Optional Width) { + write_signed(S, N, Style, Precision, Width); +} + +void llvm::write_hex(raw_ostream &S, uint64_t N, HexStyle Style, + Optional Precision, Optional Width) { + constexpr size_t kMaxWidth = 128u; + + size_t Prec = + std::min(kMaxWidth, Precision.getValueOr(getDefaultPrecision(Style))); + unsigned Nibbles = (64 - countLeadingZeros(N) + 3) / 4; + bool Prefix = + (Style == HexStyle::PrefixLower || Style == HexStyle::PrefixUpper); + bool Upper = (Style == HexStyle::Upper || Style == HexStyle::PrefixUpper); unsigned PrefixChars = Prefix ? 2 : 0; - unsigned Width = std::max(static_cast(MinWidth), - std::max(1u, Nibbles) + PrefixChars); + unsigned NumChars = std::max(static_cast(Prec), + std::max(1u, Nibbles) + PrefixChars); - char NumberBuffer[20] = "0x0000000000000000"; - if (!Prefix) - NumberBuffer[1] = '0'; - char *EndPtr = NumberBuffer + Width; + char NumberBuffer[kMaxWidth]; + ::memset(NumberBuffer, '0', llvm::array_lengthof(NumberBuffer)); + if (Prefix) + NumberBuffer[1] = 'x'; + char *EndPtr = NumberBuffer + NumChars; char *CurPtr = EndPtr; while (N) { unsigned char x = static_cast(N) % 16; @@ -117,22 +261,37 @@ void llvm::write_hex(raw_ostream &S, unsigned long long N, std::size_t MinWidth, N /= 16; } - S.write(NumberBuffer, Width); + writePadding(S, Width, NumChars); + S.write(NumberBuffer, NumChars); } -void llvm::write_double(raw_ostream &S, double N, std::size_t MinWidth, - std::size_t MinDecimals, FloatStyle Style) { - char Letter = (Style == FloatStyle::Exponent) ? 'e' : 'f'; +void llvm::write_double(raw_ostream &S, double N, FloatStyle Style, + Optional Precision, Optional Width) { + size_t Prec = Precision.getValueOr(getDefaultPrecision(Style)); + + if (std::isnan(N)) { + writePadding(S, Width, 3); + S << "nan"; + return; + } else if (std::isinf(N)) { + writePadding(S, Width, 3); + S << "INF"; + return; + } + + char Letter; + if (Style == FloatStyle::Exponent) + Letter = 'e'; + else if (Style == FloatStyle::ExponentUpper) + Letter = 'E'; + else + Letter = 'f'; + SmallString<8> Spec; llvm::raw_svector_ostream Out(Spec); - Out << '%'; - if (MinWidth > 0) - Out << MinWidth; - if (MinDecimals > 0) - Out << '.' << MinDecimals; - Out << Letter; - - if (Style == FloatStyle::Exponent) { + Out << "%." << Prec << Letter; + + if (Style == FloatStyle::Exponent || Style == FloatStyle::ExponentUpper) { #ifdef _WIN32 // On MSVCRT and compatible, output of %e is incompatible to Posix // by default. Number of exponent digits should be at least 2. "%+03d" @@ -140,7 +299,9 @@ void llvm::write_double(raw_ostream &S, double N, std::size_t MinWidth, #if defined(__MINGW32__) // FIXME: It should be generic to C++11. if (N == 0.0 && std::signbit(N)) { - S << "-0.000000e+00"; + const char *NegativeZero = "-0.000000e+00"; + writePadding(S, Width, strlen(NegativeZero)); + S << NegativeZero; return; } #else @@ -148,16 +309,19 @@ void llvm::write_double(raw_ostream &S, double N, std::size_t MinWidth, // negative zero if (fpcl == _FPCLASS_NZ) { - S << "-0.000000e+00"; + const char *NegativeZero = "-0.000000e+00"; + writePadding(S, Width, strlen(NegativeZero)); + S << NegativeZero; return; } #endif - char buf[16]; + char buf[32]; unsigned len; len = format(Spec.c_str(), N).snprint(buf, sizeof(buf)); if (len <= sizeof(buf) - 2) { - if (len >= 5 && buf[len - 5] == 'e' && buf[len - 3] == '0') { + if (len >= 5 && (buf[len - 5] == 'e' || buf[len - 5] == 'E') && + buf[len - 3] == '0') { int cs = buf[len - 4]; if (cs == '+' || cs == '-') { int c1 = buf[len - 2]; @@ -171,11 +335,75 @@ void llvm::write_double(raw_ostream &S, double N, std::size_t MinWidth, } } } + writePadding(S, Width, len); S << buf; return; } #endif } - S << format(Spec.c_str(), N); + if (Style == FloatStyle::Percent) + N *= 100.0; + + char Buf[32]; + unsigned Len; + Len = format(Spec.c_str(), N).snprint(Buf, sizeof(Buf)); + if (Style == FloatStyle::Percent) + ++Len; + writePadding(S, Width, Len); + S << Buf; + if (Style == FloatStyle::Percent) + S << '%'; +} + +IntegerStyle llvm::hexStyleToIntHexStyle(HexStyle S) { + switch (S) { + case HexStyle::Upper: + return IntegerStyle::HexUpperNoPrefix; + case HexStyle::Lower: + return IntegerStyle::HexLowerNoPrefix; + case HexStyle::PrefixUpper: + return IntegerStyle::HexUpperPrefix; + case HexStyle::PrefixLower: + return IntegerStyle::HexLowerPrefix; + } + LLVM_BUILTIN_UNREACHABLE; +} + +size_t llvm::getDefaultPrecision(FloatStyle Style) { + switch (Style) { + case FloatStyle::Exponent: + case FloatStyle::ExponentUpper: + return 6; // Number of decimal places. + case FloatStyle::Fixed: + case FloatStyle::Percent: + return 2; // Number of decimal places. + } + LLVM_BUILTIN_UNREACHABLE; +} + +size_t llvm::getDefaultPrecision(IntegerStyle Style) { + switch (Style) { + case IntegerStyle::Exponent: + case IntegerStyle::ExponentUpper: + return 6; // Number of decimal places. + case IntegerStyle::Number: + case IntegerStyle::Integer: + return 0; // Minimum number of digits required. + case IntegerStyle::Fixed: + return 2; // Number of decimal places. + case IntegerStyle::Percent: + return 0; // Number of decimal places. + case IntegerStyle::HexLowerNoPrefix: + case IntegerStyle::HexLowerPrefix: + case IntegerStyle::HexUpperNoPrefix: + case IntegerStyle::HexUpperPrefix: + return getDefaultPrecision(intHexStyleToHexStyle(Style)); + } + LLVM_BUILTIN_UNREACHABLE; +} + +size_t llvm::getDefaultPrecision(HexStyle) { + // Number of digits in the resulting string. + return 0; } -- cgit v1.2.3