[llvm][Support] formatv: non-negative-plus for integral numbers (#185008)
The older `format()` allows you to print a `+` sign for non-negative
integral numbers upon request.
Examples:
```c++
format("%+d", 255); // -> "+255"
format("%+d", -12); // -> "-12"
```
This change adds the ability to do the same with `formatv()`:
```c++
formatv("{0:+d}", 255); // -> "+255"
formatv("{0:+d}", -12); // -> "-12"
```
The default behaviour is not changed. That means, for a format specifier
like "{0:d}" we still print the positive integer without a "+" prefix.
The special case of "+0" is exactly like it is handled with `format()`:
```c++
format("%+d", 0); // -> "+0"
```
This work is related to #35980.
This commit is contained in:
parent
0c6bca6e77
commit
bcd8e64884
@ -112,6 +112,9 @@ protected:
|
||||
/// | X+ / X | Hex + prefix, upper | 42 | 0x2A | Minimum # digits |
|
||||
/// | N / n | Digit grouped number | 123456 | 123,456 | Ignored |
|
||||
/// | D / d | Integer | 100000 | 100000 | Ignored |
|
||||
/// |+D / +d | Integer with + prefix| 100000 | +100000 | Ignored |
|
||||
/// | | for numbers => 0 | | | |
|
||||
/// | + | Same as +D / +d | | | |
|
||||
/// | (empty) | Same as D / d | | | |
|
||||
/// ==========================================================================
|
||||
///
|
||||
@ -130,6 +133,10 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// A + prefix indicates that a plus sign shall be
|
||||
// prefixed to non-negative numbers.
|
||||
bool NonNegativePlus = Style.consume_front('+');
|
||||
|
||||
IntegerStyle IS = IntegerStyle::Integer;
|
||||
if (Style.consume_front("N") || Style.consume_front("n"))
|
||||
IS = IntegerStyle::Number;
|
||||
@ -138,7 +145,11 @@ public:
|
||||
|
||||
Style.consumeInteger(10, Digits);
|
||||
assert(Style.empty() && "Invalid integral format style!");
|
||||
write_integer(Stream, V, Digits, IS);
|
||||
|
||||
// We currently only support the + for integer style numbers.
|
||||
NonNegativePlus = NonNegativePlus && IS == IntegerStyle::Integer;
|
||||
|
||||
write_integer(Stream, V, Digits, IS, NonNegativePlus);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -27,17 +27,18 @@ LLVM_ABI size_t getDefaultPrecision(FloatStyle Style);
|
||||
LLVM_ABI bool isPrefixedHexStyle(HexPrintStyle S);
|
||||
|
||||
LLVM_ABI void write_integer(raw_ostream &S, unsigned int N, size_t MinDigits,
|
||||
IntegerStyle Style);
|
||||
IntegerStyle Style, bool NonNegativePlus = false);
|
||||
LLVM_ABI void write_integer(raw_ostream &S, int N, size_t MinDigits,
|
||||
IntegerStyle Style);
|
||||
IntegerStyle Style, bool NonNegativePlus = false);
|
||||
LLVM_ABI void write_integer(raw_ostream &S, unsigned long N, size_t MinDigits,
|
||||
IntegerStyle Style);
|
||||
IntegerStyle Style, bool NonNegativePlus = false);
|
||||
LLVM_ABI void write_integer(raw_ostream &S, long N, size_t MinDigits,
|
||||
IntegerStyle Style);
|
||||
IntegerStyle Style, bool NonNegativePlus = false);
|
||||
LLVM_ABI void write_integer(raw_ostream &S, unsigned long long N,
|
||||
size_t MinDigits, IntegerStyle Style);
|
||||
size_t MinDigits, IntegerStyle Style,
|
||||
bool NonNegativePlus = false);
|
||||
LLVM_ABI void write_integer(raw_ostream &S, long long N, size_t MinDigits,
|
||||
IntegerStyle Style);
|
||||
IntegerStyle Style, bool NonNegativePlus = false);
|
||||
|
||||
LLVM_ABI void write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
|
||||
std::optional<size_t> Width = std::nullopt);
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cassert>
|
||||
#include <cinttypes>
|
||||
@ -64,7 +65,7 @@ static void printOperand(raw_ostream &OS, const DIDumpOptions &DumpOpts,
|
||||
// The offsets are all encoded in a unsigned form, but in practice
|
||||
// consumers use them signed. It's most certainly legacy due to
|
||||
// the lack of signed variants in the first Dwarf standards.
|
||||
OS << format(" %+" PRId64, int64_t(Operand));
|
||||
OS << formatv(" {0:+d}", int64_t(Operand));
|
||||
break;
|
||||
case CFIProgram::OT_FactoredCodeOffset: // Always Unsigned
|
||||
if (P.codeAlign())
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
|
||||
#include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
@ -153,7 +154,7 @@ static bool printOp(const DWARFExpression::Operation *Op, raw_ostream &OS,
|
||||
static_cast<uint8_t>(Expr->getData()[Offset++]));
|
||||
} else {
|
||||
if (Signed)
|
||||
OS << format(" %+" PRId64, (int64_t)Op->getRawOperand(Operand));
|
||||
OS << formatv(" {0:+d}", (int64_t)Op->getRawOperand(Operand));
|
||||
else if (Op->getCode() != DW_OP_entry_value &&
|
||||
Op->getCode() != DW_OP_GNU_entry_value)
|
||||
OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand));
|
||||
@ -257,7 +258,7 @@ static bool printCompactDWARFExpr(
|
||||
raw_svector_ostream S(Stack.emplace_back().String);
|
||||
S << RegName;
|
||||
if (Offset)
|
||||
S << format("%+" PRId64, Offset);
|
||||
S << formatv("{0:+d}", Offset);
|
||||
break;
|
||||
}
|
||||
case dwarf::DW_OP_entry_value:
|
||||
@ -310,7 +311,7 @@ static bool printCompactDWARFExpr(
|
||||
raw_svector_ostream S(Stack.emplace_back().String);
|
||||
S << RegName;
|
||||
if (Offset)
|
||||
S << format("%+" PRId64, Offset);
|
||||
S << formatv("{0:+d}", Offset);
|
||||
} else {
|
||||
return UnknownOpcode(OS, Opcode, std::nullopt);
|
||||
}
|
||||
@ -364,7 +365,7 @@ bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS,
|
||||
if (!RegName.empty()) {
|
||||
if ((Opcode >= DW_OP_breg0 && Opcode <= DW_OP_breg31) ||
|
||||
Opcode == DW_OP_bregx || SubOpcode == DW_OP_LLVM_aspace_bregx)
|
||||
OS << ' ' << RegName << format("%+" PRId64, Operands[OpNum]);
|
||||
OS << ' ' << RegName << formatv("{0:+d}", int64_t(Operands[OpNum]));
|
||||
else
|
||||
OS << ' ' << RegName.data();
|
||||
|
||||
|
||||
@ -53,7 +53,8 @@ static void writeWithCommas(raw_ostream &S, ArrayRef<char> Buffer) {
|
||||
|
||||
template <typename T>
|
||||
static void write_unsigned_impl(raw_ostream &S, T N, size_t MinDigits,
|
||||
IntegerStyle Style, bool IsNegative) {
|
||||
IntegerStyle Style, bool IsNegative,
|
||||
bool NonNegativePlus) {
|
||||
static_assert(std::is_unsigned_v<T>, "Value is not unsigned!");
|
||||
|
||||
char NumberBuffer[128];
|
||||
@ -61,6 +62,8 @@ static void write_unsigned_impl(raw_ostream &S, T N, size_t MinDigits,
|
||||
|
||||
if (IsNegative)
|
||||
S << '-';
|
||||
else if (NonNegativePlus)
|
||||
S << '+';
|
||||
|
||||
if (Len < MinDigits && Style != IntegerStyle::Number) {
|
||||
for (size_t I = Len; I < MinDigits; ++I)
|
||||
@ -76,59 +79,61 @@ static void write_unsigned_impl(raw_ostream &S, T N, size_t MinDigits,
|
||||
|
||||
template <typename T>
|
||||
static void write_unsigned(raw_ostream &S, T N, size_t MinDigits,
|
||||
IntegerStyle Style, bool IsNegative = false) {
|
||||
IntegerStyle Style, bool IsNegative = false,
|
||||
bool NonNegativePlus = false) {
|
||||
// Output using 32-bit div/mod if possible.
|
||||
if (N == static_cast<uint32_t>(N))
|
||||
write_unsigned_impl(S, static_cast<uint32_t>(N), MinDigits, Style,
|
||||
IsNegative);
|
||||
IsNegative, NonNegativePlus);
|
||||
else
|
||||
write_unsigned_impl(S, N, MinDigits, Style, IsNegative);
|
||||
write_unsigned_impl(S, N, MinDigits, Style, IsNegative, NonNegativePlus);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void write_signed(raw_ostream &S, T N, size_t MinDigits,
|
||||
IntegerStyle Style) {
|
||||
IntegerStyle Style, bool NonNegativePlus = false) {
|
||||
static_assert(std::is_signed_v<T>, "Value is not signed!");
|
||||
|
||||
using UnsignedT = std::make_unsigned_t<T>;
|
||||
|
||||
if (N >= 0) {
|
||||
write_unsigned(S, static_cast<UnsignedT>(N), MinDigits, Style);
|
||||
write_unsigned(S, static_cast<UnsignedT>(N), MinDigits, Style, false,
|
||||
NonNegativePlus);
|
||||
return;
|
||||
}
|
||||
|
||||
UnsignedT UN = -(UnsignedT)N;
|
||||
write_unsigned(S, UN, MinDigits, Style, true);
|
||||
write_unsigned(S, UN, MinDigits, Style, true, NonNegativePlus);
|
||||
}
|
||||
|
||||
void llvm::write_integer(raw_ostream &S, unsigned int N, size_t MinDigits,
|
||||
IntegerStyle Style) {
|
||||
write_unsigned(S, N, MinDigits, Style);
|
||||
IntegerStyle Style, bool NonNegativePlus) {
|
||||
write_unsigned(S, N, MinDigits, Style, false, NonNegativePlus);
|
||||
}
|
||||
|
||||
void llvm::write_integer(raw_ostream &S, int N, size_t MinDigits,
|
||||
IntegerStyle Style) {
|
||||
write_signed(S, N, MinDigits, Style);
|
||||
IntegerStyle Style, bool NonNegativePlus) {
|
||||
write_signed(S, N, MinDigits, Style, NonNegativePlus);
|
||||
}
|
||||
|
||||
void llvm::write_integer(raw_ostream &S, unsigned long N, size_t MinDigits,
|
||||
IntegerStyle Style) {
|
||||
write_unsigned(S, N, MinDigits, Style);
|
||||
IntegerStyle Style, bool NonNegativePlus) {
|
||||
write_unsigned(S, N, MinDigits, Style, false, NonNegativePlus);
|
||||
}
|
||||
|
||||
void llvm::write_integer(raw_ostream &S, long N, size_t MinDigits,
|
||||
IntegerStyle Style) {
|
||||
write_signed(S, N, MinDigits, Style);
|
||||
IntegerStyle Style, bool NonNegativePlus) {
|
||||
write_signed(S, N, MinDigits, Style, NonNegativePlus);
|
||||
}
|
||||
|
||||
void llvm::write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits,
|
||||
IntegerStyle Style) {
|
||||
write_unsigned(S, N, MinDigits, Style);
|
||||
IntegerStyle Style, bool NonNegativePlus) {
|
||||
write_unsigned(S, N, MinDigits, Style, false, NonNegativePlus);
|
||||
}
|
||||
|
||||
void llvm::write_integer(raw_ostream &S, long long N, size_t MinDigits,
|
||||
IntegerStyle Style) {
|
||||
write_signed(S, N, MinDigits, Style);
|
||||
IntegerStyle Style, bool NonNegativePlus) {
|
||||
write_signed(S, N, MinDigits, Style, NonNegativePlus);
|
||||
}
|
||||
|
||||
void llvm::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
|
||||
|
||||
@ -391,7 +391,7 @@ TEST(FormatVariadicTest, IntegralHexFormatting) {
|
||||
}
|
||||
|
||||
template <typename FormatTy>
|
||||
std::string printToString(unsigned MaxN, FormatTy &&Fmt) {
|
||||
static std::string printToString(FormatTy &&Fmt, unsigned MaxN = 100) {
|
||||
std::vector<char> Dst(MaxN + 2);
|
||||
int N = Fmt.snprint(Dst.data(), Dst.size());
|
||||
Dst.back() = 0;
|
||||
@ -404,9 +404,8 @@ TEST(FormatAndFormatvTest, EquivalentHexFormatting) {
|
||||
|
||||
// Here's the old format() way of printing a hex number with
|
||||
// dynamic width and precision, both being the same.
|
||||
EXPECT_EQ(
|
||||
"0x00000000ff",
|
||||
printToString(100, format("0x%*.*" PRIx64, HexDigits, HexDigits, N)));
|
||||
EXPECT_EQ("0x00000000ff",
|
||||
printToString(format("0x%*.*" PRIx64, HexDigits, HexDigits, N)));
|
||||
|
||||
// Now, do the same with formatv()
|
||||
EXPECT_EQ("0x00000000ff",
|
||||
@ -414,6 +413,49 @@ TEST(FormatAndFormatvTest, EquivalentHexFormatting) {
|
||||
.str());
|
||||
}
|
||||
|
||||
TEST(FormatAndFormatvTest, NonNegativePlusInteger) {
|
||||
EXPECT_EQ("-255", printToString(format("%+d", -255)));
|
||||
EXPECT_EQ("+255", printToString(format("%+d", 255)));
|
||||
EXPECT_EQ("+0", printToString(format("%+d", 0)));
|
||||
EXPECT_EQ("-7", printToString(format("%+d", -7)));
|
||||
|
||||
// Check that +d works for signed and unsigned integral values.
|
||||
// Ensure the default is not changed (a + is not added without requesting it).
|
||||
EXPECT_EQ("+1", formatv("{0:+d}", static_cast<unsigned int>(1)).str());
|
||||
EXPECT_EQ("+2", formatv("{0:+d}", static_cast<int>(2)).str());
|
||||
EXPECT_EQ("+3", formatv("{0:+d}", static_cast<unsigned long>(3)).str());
|
||||
EXPECT_EQ("+4", formatv("{0:+d}", static_cast<long>(4)).str());
|
||||
EXPECT_EQ("+5", formatv("{0:+d}", static_cast<unsigned long long>(5)).str());
|
||||
EXPECT_EQ("+6", formatv("{0:+d}", static_cast<long long>(6)).str());
|
||||
EXPECT_EQ("-7", formatv("{0:+d}", static_cast<int>(-7)).str());
|
||||
EXPECT_EQ("-8", formatv("{0:+d}", static_cast<long>(-8)).str());
|
||||
EXPECT_EQ("-9", formatv("{0:+d}", static_cast<long long>(-9)).str());
|
||||
|
||||
EXPECT_EQ("11", formatv("{0:d}", static_cast<unsigned int>(11)).str());
|
||||
EXPECT_EQ("22", formatv("{0:d}", static_cast<int>(22)).str());
|
||||
EXPECT_EQ("33", formatv("{0:d}", static_cast<unsigned long>(33)).str());
|
||||
EXPECT_EQ("44", formatv("{0:d}", static_cast<long>(44)).str());
|
||||
EXPECT_EQ("55", formatv("{0:d}", static_cast<unsigned long long>(55)).str());
|
||||
EXPECT_EQ("66", formatv("{0:d}", static_cast<long long>(66)).str());
|
||||
EXPECT_EQ("-77", formatv("{0:d}", static_cast<int>(-77)).str());
|
||||
EXPECT_EQ("-88", formatv("{0:d}", static_cast<long>(-88)).str());
|
||||
EXPECT_EQ("-99", formatv("{0:d}", static_cast<long long>(-99)).str());
|
||||
|
||||
// Ensure that 0 is also prefixed with + (old behaviour from format(), see
|
||||
// above).
|
||||
EXPECT_EQ("+0", formatv("{0:+d}", 0).str());
|
||||
EXPECT_EQ("0", formatv("{0:d}", 0).str());
|
||||
|
||||
// Ensure that an empty or otherwise empty format string is also working as
|
||||
// expected
|
||||
EXPECT_EQ("+333", formatv("{0:+}", 333).str());
|
||||
EXPECT_EQ("444", formatv("{0:}", 444).str());
|
||||
|
||||
// Try with width modifier as well, to ensure that the + is still present and
|
||||
// that the width is correct.
|
||||
EXPECT_EQ(" -1", formatv("{0,4:+d}", -1).str());
|
||||
EXPECT_EQ(" +1", formatv("{0,4:+d}", 1).str());
|
||||
}
|
||||
TEST(FormatVariadicTest, PointerFormatting) {
|
||||
// 1. Trivial cases. Hex is default. Default Precision is pointer width.
|
||||
if (sizeof(void *) == 4) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user