diff --git a/llvm/include/llvm/Support/FormatProviders.h b/llvm/include/llvm/Support/FormatProviders.h index 3377781873b8..78eeec76cf79 100644 --- a/llvm/include/llvm/Support/FormatProviders.h +++ b/llvm/include/llvm/Support/FormatProviders.h @@ -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); } }; diff --git a/llvm/include/llvm/Support/NativeFormatting.h b/llvm/include/llvm/Support/NativeFormatting.h index 45336511ae9b..ae9134b5e154 100644 --- a/llvm/include/llvm/Support/NativeFormatting.h +++ b/llvm/include/llvm/Support/NativeFormatting.h @@ -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 Width = std::nullopt); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp index 4d879b69c387..0b74f352b4bb 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp @@ -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 #include @@ -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()) diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp index fcd2316c30ae..5a3c1dd05719 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp @@ -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 #include @@ -153,7 +154,7 @@ static bool printOp(const DWARFExpression::Operation *Op, raw_ostream &OS, static_cast(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(); diff --git a/llvm/lib/Support/NativeFormatting.cpp b/llvm/lib/Support/NativeFormatting.cpp index 7a6473043419..97e6a681480f 100644 --- a/llvm/lib/Support/NativeFormatting.cpp +++ b/llvm/lib/Support/NativeFormatting.cpp @@ -53,7 +53,8 @@ static void writeWithCommas(raw_ostream &S, ArrayRef Buffer) { template 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, "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 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(N)) write_unsigned_impl(S, static_cast(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 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, "Value is not signed!"); using UnsignedT = std::make_unsigned_t; if (N >= 0) { - write_unsigned(S, static_cast(N), MinDigits, Style); + write_unsigned(S, static_cast(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, diff --git a/llvm/unittests/Support/FormatVariadicTest.cpp b/llvm/unittests/Support/FormatVariadicTest.cpp index a3e9841536fd..914b5f022e25 100644 --- a/llvm/unittests/Support/FormatVariadicTest.cpp +++ b/llvm/unittests/Support/FormatVariadicTest.cpp @@ -391,7 +391,7 @@ TEST(FormatVariadicTest, IntegralHexFormatting) { } template -std::string printToString(unsigned MaxN, FormatTy &&Fmt) { +static std::string printToString(FormatTy &&Fmt, unsigned MaxN = 100) { std::vector 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(1)).str()); + EXPECT_EQ("+2", formatv("{0:+d}", static_cast(2)).str()); + EXPECT_EQ("+3", formatv("{0:+d}", static_cast(3)).str()); + EXPECT_EQ("+4", formatv("{0:+d}", static_cast(4)).str()); + EXPECT_EQ("+5", formatv("{0:+d}", static_cast(5)).str()); + EXPECT_EQ("+6", formatv("{0:+d}", static_cast(6)).str()); + EXPECT_EQ("-7", formatv("{0:+d}", static_cast(-7)).str()); + EXPECT_EQ("-8", formatv("{0:+d}", static_cast(-8)).str()); + EXPECT_EQ("-9", formatv("{0:+d}", static_cast(-9)).str()); + + EXPECT_EQ("11", formatv("{0:d}", static_cast(11)).str()); + EXPECT_EQ("22", formatv("{0:d}", static_cast(22)).str()); + EXPECT_EQ("33", formatv("{0:d}", static_cast(33)).str()); + EXPECT_EQ("44", formatv("{0:d}", static_cast(44)).str()); + EXPECT_EQ("55", formatv("{0:d}", static_cast(55)).str()); + EXPECT_EQ("66", formatv("{0:d}", static_cast(66)).str()); + EXPECT_EQ("-77", formatv("{0:d}", static_cast(-77)).str()); + EXPECT_EQ("-88", formatv("{0:d}", static_cast(-88)).str()); + EXPECT_EQ("-99", formatv("{0:d}", static_cast(-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) {