diff --git a/llvm/include/llvm/Support/Format.h b/llvm/include/llvm/Support/Format.h --- a/llvm/include/llvm/Support/Format.h +++ b/llvm/include/llvm/Support/Format.h @@ -33,11 +33,63 @@ namespace llvm { +/// Utility class that parses printf-style format strings to yield the expected +/// C type(s) of each specifier. This class is used to verify that a format +/// string unknown at compile-time is equivalent to another format string (which +/// itself is hopefully known at compile-time). +class PrintfStyleFormatReader { +public: + enum SpecifierType : char { + ST_EndOfFormatString, + ST_Unknown, + ST_WideChar, + ST_Int, + ST_Long, + ST_LongLong, + ST_IntMax, + ST_Size, + ST_Ptrdiff, + ST_Double, + ST_LongDouble, + ST_CString, + ST_WideCString, + ST_VoidPointer, + ST_Count_Char, + ST_Count_Short, + ST_Count_Int, + ST_Count_Long, + ST_Count_LongLong, + ST_Count_IntMax, + ST_Count_Size, + ST_Count_Ptrdiff + }; + +private: + const char *Fmt; + llvm::SmallVector SpecifierQueue; + + void RefillSpecifierQueue(); + +public: + /// Verify that the format specifiers in \p Fmt consume no more arguments than + /// those in \p Expected, and that all consumed arguments have a compatible + /// type. If \p Fmt is compatible with \p Expected in this way, \p Fmt is + /// returned. Otherwise, \p Expected is returned. + static const char *EnsureCompatible(const char *Expected, const char *Fmt); + + PrintfStyleFormatReader(const char *Fmt) : Fmt(Fmt) {} + + SpecifierType NextSpecifier() { + if (SpecifierQueue.empty()) + RefillSpecifierQueue(); + return SpecifierQueue.pop_back_val(); + } +}; + /// This is a helper class used for handling formatted output. It is the /// abstract base class of a templated derived class. class format_object_base { protected: - const char *Fmt; ~format_object_base() = default; // Disallow polymorphic deletion. format_object_base(const format_object_base &) = default; virtual void home(); // Out of line virtual method. @@ -46,7 +98,7 @@ virtual int snprint(char *Buffer, unsigned BufferSize) const = 0; public: - format_object_base(const char *fmt) : Fmt(fmt) {} + format_object_base() = default; /// Format the object into the specified buffer. On success, this returns /// the length of the formatted string. If the buffer is too small, this @@ -86,28 +138,27 @@ }; template <> struct validate_format_parameters<> {}; -template -class format_object final : public format_object_base { - std::tuple Vals; - - template - int snprint_tuple(char *Buffer, unsigned BufferSize, - std::index_sequence) const { +template auto format_capture(const char *Fmt, Ts... Vals) { + validate_format_parameters(); + return [=](char *Buffer, unsigned BufferSize) { #ifdef _MSC_VER - return _snprintf(Buffer, BufferSize, Fmt, std::get(Vals)...); + return _snprintf(Buffer, BufferSize, Fmt, Vals...); #else - return snprintf(Buffer, BufferSize, Fmt, std::get(Vals)...); + return snprintf(Buffer, BufferSize, Fmt, Vals...); #endif - } + }; +} + +template +class format_object final : public format_object_base { + decltype(format_capture("", std::declval()...)) Format; public: - format_object(const char *fmt, const Ts &... vals) - : format_object_base(fmt), Vals(vals...) { - validate_format_parameters(); - } + format_object(const char *Fmt, const Ts &...vals) + : Format(format_capture(Fmt, vals...)) {} int snprint(char *Buffer, unsigned BufferSize) const override { - return snprint_tuple(Buffer, BufferSize, std::index_sequence_for()); + return Format(Buffer, BufferSize); } }; diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -172,6 +172,7 @@ FileUtilities.cpp FileOutputBuffer.cpp FoldingSet.cpp + Format.cpp FormattedStream.cpp FormatVariadic.cpp GlobPattern.cpp diff --git a/llvm/lib/Support/Format.cpp b/llvm/lib/Support/Format.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Support/Format.cpp @@ -0,0 +1,356 @@ +//===- Format.cpp - Efficient printf-style formatting for streams -*- C++ -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the non-template part of Format.h, which is used to +// provide a type-safe-ish interface to printf-style formatting. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Format.h" + +namespace { +/// Enum representation of a printf-style length specifier. +enum ArgLength : char { + /// Corresponds to 'hh' length specifier. + AL_ShortShort, + /// Corresponds to 'h' length specifier. + AL_Short, + /// Corresponds to default length specifier. + AL_Default, + /// Corresponds to 'l' length specifier. + AL_Long, + /// Corresponds to 'll' length specifier. + AL_LongLong, + /// Corresponds to 'j' length specifier. + AL_IntMax, + /// Corresponds to 'z' length specifier. + AL_Size, + /// Corresponds to 't' length specifier. + AL_Ptrdiff, + /// Corresponds to 'L' length specifier. + AL_LongDouble, + /// First invalid value of \p ArgLength. + AL_End, +}; + +/// Enum representation of a printf-style specifier. +enum SpecifierChar : char { + /// Corresponds to any of 'd', 'i', 'u', 'o', 'x' or 'X' specifiers. + SC_Int, + /// Corresponds to any of 'f', 'F', 'e', 'E', 'g', 'G', 'a' or 'A' specifiers. + SC_Float, + /// Corresponds to 'c' specifier. + SC_Char, + /// Corresponds to 's' specifier. + SC_String, + /// Corresponds to 'p' specifier. + SC_VoidPointer, + /// Corresponds to 'n' specifier. + SC_Count, + /// First invalid value of \p SpecifierChar. + SC_End, +}; + +constexpr uint64_t SpecifierBit(char C) { return 1 << (C - 0x40); } + +template +constexpr /* consteval */ uint64_t SpecifierMask(const char (&Specifiers)[N]) { + uint64_t Mask = 0; + for (const char *I = std::begin(Specifiers); I != std::end(Specifiers); ++I) { + if (*I == 0) + break; + Mask |= SpecifierBit(*I); + } + return Mask; +} + +constexpr auto ST_Unknown = llvm::PrintfStyleFormatReader::ST_Unknown; +constexpr auto ST_WideChar = llvm::PrintfStyleFormatReader::ST_WideChar; +constexpr auto ST_Int = llvm::PrintfStyleFormatReader::ST_Int; +constexpr auto ST_Long = llvm::PrintfStyleFormatReader::ST_Long; +constexpr auto ST_LongLong = llvm::PrintfStyleFormatReader::ST_LongLong; +constexpr auto ST_IntMax = llvm::PrintfStyleFormatReader::ST_IntMax; +constexpr auto ST_Size = llvm::PrintfStyleFormatReader::ST_Size; +constexpr auto ST_Ptrdiff = llvm::PrintfStyleFormatReader::ST_Ptrdiff; +constexpr auto ST_Double = llvm::PrintfStyleFormatReader::ST_Double; +constexpr auto ST_LongDouble = llvm::PrintfStyleFormatReader::ST_LongDouble; +constexpr auto ST_CString = llvm::PrintfStyleFormatReader::ST_CString; +constexpr auto ST_WideCString = llvm::PrintfStyleFormatReader::ST_WideCString; +constexpr auto ST_VoidPointer = llvm::PrintfStyleFormatReader::ST_VoidPointer; +constexpr auto ST_Count_Char = llvm::PrintfStyleFormatReader::ST_Count_Char; +constexpr auto ST_Count_Short = llvm::PrintfStyleFormatReader::ST_Count_Short; +constexpr auto ST_Count_Int = llvm::PrintfStyleFormatReader::ST_Count_Int; +constexpr auto ST_Count_Long = llvm::PrintfStyleFormatReader::ST_Count_Long; +constexpr auto ST_Count_LongLong = + llvm::PrintfStyleFormatReader::ST_Count_LongLong; +constexpr auto ST_Count_IntMax = llvm::PrintfStyleFormatReader::ST_Count_IntMax; +constexpr auto ST_Count_Size = llvm::PrintfStyleFormatReader::ST_Count_Size; +constexpr auto ST_Count_Ptrdiff = + llvm::PrintfStyleFormatReader::ST_Count_Ptrdiff; + +llvm::PrintfStyleFormatReader::SpecifierType SpecifierTable[SC_End][AL_End] = { + { + // SC_Int + ST_Int, + ST_Int, + ST_Int, + ST_Long, + ST_LongLong, + ST_IntMax, + ST_Size, + ST_Ptrdiff, + ST_Unknown, + }, + { + // SC_Float + ST_Unknown, + ST_Unknown, + ST_Double, + ST_Double, + ST_Unknown, + ST_Unknown, + ST_Unknown, + ST_Unknown, + ST_LongDouble, + }, + { + // SC_Char + ST_Unknown, + ST_Unknown, + ST_Int, + ST_WideChar, + ST_Unknown, + ST_Unknown, + ST_Unknown, + ST_Unknown, + ST_Unknown, + }, + { + // SC_String + ST_Unknown, + ST_Unknown, + ST_CString, + ST_WideCString, + ST_Unknown, + ST_Unknown, + ST_Unknown, + ST_Unknown, + ST_Unknown, + }, + { + // SC_VoidPointer + ST_Unknown, + ST_Unknown, + ST_VoidPointer, + ST_Unknown, + ST_Unknown, + ST_Unknown, + ST_Unknown, + ST_Unknown, + ST_Unknown, + }, + { + // SC_Count + ST_Count_Char, + ST_Count_Short, + ST_Count_Int, + ST_Count_Long, + ST_Count_LongLong, + ST_Count_IntMax, + ST_Count_Size, + ST_Count_Ptrdiff, + ST_Unknown, + }, +}; +} // namespace + +namespace llvm { + +void PrintfStyleFormatReader::RefillSpecifierQueue() { + if (auto PercentPtr = strchr(Fmt, '%')) { + Fmt = PercentPtr; + } else { + SpecifierQueue.push_back(ST_EndOfFormatString); + return; + } + + if (*++Fmt == '%') { + // %% case: skip and try again + Fmt++; + RefillSpecifierQueue(); + return; + } + + // Push ST_Unknown to SpecifierQueue. If we bail out early, this is what + // the caller gets. Fill in real specifiers to Specifiers: if we + // successfully get to the end, then swap Specifiers with SpecifierQueue. + SpecifierQueue.push_back(ST_Unknown); + llvm::SmallVector Specifiers; + + // Bitfield keeping track of which specifier characters are allowed given + // flags and precision settings. Each bit tells whether ascii character + // 0x40 + is allowed as a specifier. '%', which has an ASCII value + // less than 0x40 and does not allow any customization, is handled by a check + // above. The starting value contains all standard specifiers. + uint64_t ValidSpecifiers = SpecifierMask("diuoxXfFeEgGaAcspn"); + + // update specifier mask based on flags + bool ReadAllFlags = false; + while (!ReadAllFlags) { + switch (*Fmt) { + case '+': + case '-': + case ' ': + // valid for all specifiers + ++Fmt; + break; + case '#': + ValidSpecifiers &= SpecifierMask("xXaAeEfFgG"); + ++Fmt; + break; + case '0': + ValidSpecifiers &= SpecifierMask("diouxXaAeEfFgG"); + ++Fmt; + break; + default: + ReadAllFlags = true; + break; + } + } + + // skip width + if (*Fmt == '*') { + Specifiers.push_back(ST_Int); + Fmt++; + } else + while (*Fmt >= '0' && *Fmt <= '9') + Fmt++; + + // test precision + if (*Fmt == '.') { + ValidSpecifiers &= SpecifierMask("diouxXaAeEfFgGs"); + Fmt++; + if (*Fmt == '*') { + Specifiers.push_back(ST_Int); + Fmt++; + } else + while (*Fmt >= '0' && *Fmt <= '9') + Fmt++; + } + + // parse length + bool FoundLength = false; + ArgLength AL = AL_Default; + while (!FoundLength) { + ArgLength NewAL; + switch (*Fmt) { + case 'h': + NewAL = AL_Short; + break; + case 'l': + NewAL = AL_Long; + break; + case 'j': + NewAL = AL_IntMax; + break; + case 'z': + NewAL = AL_Size; + break; + case 't': + NewAL = AL_Ptrdiff; + break; + case 'L': + NewAL = AL_LongDouble; + break; + default: + FoundLength = true; + continue; + } + + if (NewAL == AL_Long && AL == AL_Long) + AL = AL_LongLong; + else if (NewAL == AL_Short && AL == AL_Short) + AL = AL_ShortShort; + else if (AL == AL_Default) + AL = NewAL; + else + return; + Fmt++; + } + + // parse specifier; verify that the character is a valid specifier given + // restrictions imposed by by the use of flags and precision values + char Next = *Fmt++; + if (Next < 0x40 || (SpecifierBit(Next) & ValidSpecifiers) == 0) + return; + + SpecifierChar SC; + switch (Next) { + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': + SC = SC_Int; + break; + + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + SC = SC_Float; + break; + + case 'c': + SC = SC_Char; + break; + + case 's': + SC = SC_String; + break; + + case 'p': + SC = SC_VoidPointer; + break; + + case 'n': + SC = SC_Count; + break; + + default: + return; + } + + auto Spec = SpecifierTable[SC][AL]; + if (Spec == ST_Unknown) + return; + + Specifiers.push_back(Spec); + std::reverse(Specifiers.begin(), Specifiers.end()); + std::swap(Specifiers, SpecifierQueue); +} + +const char *PrintfStyleFormatReader::EnsureCompatible(const char *Expected, + const char *Fmt) { + PrintfStyleFormatReader EFR(Expected); + PrintfStyleFormatReader FFR(Fmt); + SpecifierType EST; + do { + EST = EFR.NextSpecifier(); + if (EST != FFR.NextSpecifier()) + return Expected; + } while (EST); + return Fmt; +} + +} // namespace llvm diff --git a/llvm/lib/TableGen/SetTheory.cpp b/llvm/lib/TableGen/SetTheory.cpp --- a/llvm/lib/TableGen/SetTheory.cpp +++ b/llvm/lib/TableGen/SetTheory.cpp @@ -210,21 +210,26 @@ PrintFatalError(Loc, "To out of range"); RecordKeeper &Records = - cast(Expr->getOperator())->getDef()->getRecords(); + cast(Expr->getOperator())->getDef()->getRecords(); Step *= From <= To ? 1 : -1; + const char FallbackFmt[] = "%u"; while (true) { if (Step > 0 && From > To) break; else if (Step < 0 && From < To) break; + const char *const VerifiedFmt = PrintfStyleFormatReader::EnsureCompatible( + FallbackFmt, Format.c_str()); + if (VerifiedFmt == FallbackFmt) + PrintFatalError(Loc, "Format string '" + Format + + "' is incompatible with '%u'!"); std::string Name; - raw_string_ostream OS(Name); - OS << format(Format.c_str(), unsigned(From)); - Record *Rec = Records.getDef(OS.str()); + raw_string_ostream(Name) << format(VerifiedFmt, unsigned(From)); + Record *Rec = Records.getDef(Name); if (!Rec) - PrintFatalError(Loc, "No def named '" + Name + "': " + - Expr->getAsString()); + PrintFatalError(Loc, + "No def named '" + Name + "': " + Expr->getAsString()); // Try to reevaluate Rec in case it is a set. if (const RecVec *Result = ST.expand(Rec)) Elts.insert(Result->begin(), Result->end()); diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -40,6 +40,7 @@ FileCollectorTest.cpp FileOutputBufferTest.cpp FileUtilitiesTest.cpp + FormatChkTest.cpp FormatVariadicTest.cpp FSUniqueIDTest.cpp GlobPatternTest.cpp diff --git a/llvm/unittests/Support/FormatChkTest.cpp b/llvm/unittests/Support/FormatChkTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Support/FormatChkTest.cpp @@ -0,0 +1,315 @@ +//===- FormatChkTest.cpp - Unit tests for checked string formatting -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Format.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +namespace { + +constexpr auto ST_Unknown = llvm::PrintfStyleFormatReader::ST_Unknown; +constexpr auto ST_WideChar = llvm::PrintfStyleFormatReader::ST_WideChar; +constexpr auto ST_Int = llvm::PrintfStyleFormatReader::ST_Int; +constexpr auto ST_Long = llvm::PrintfStyleFormatReader::ST_Long; +constexpr auto ST_LongLong = llvm::PrintfStyleFormatReader::ST_LongLong; +constexpr auto ST_IntMax = llvm::PrintfStyleFormatReader::ST_IntMax; +constexpr auto ST_Size = llvm::PrintfStyleFormatReader::ST_Size; +constexpr auto ST_Ptrdiff = llvm::PrintfStyleFormatReader::ST_Ptrdiff; +constexpr auto ST_Double = llvm::PrintfStyleFormatReader::ST_Double; +constexpr auto ST_LongDouble = llvm::PrintfStyleFormatReader::ST_LongDouble; +constexpr auto ST_CString = llvm::PrintfStyleFormatReader::ST_CString; +constexpr auto ST_WideCString = llvm::PrintfStyleFormatReader::ST_WideCString; +constexpr auto ST_VoidPointer = llvm::PrintfStyleFormatReader::ST_VoidPointer; +constexpr auto ST_Count_Char = llvm::PrintfStyleFormatReader::ST_Count_Char; +constexpr auto ST_Count_Short = llvm::PrintfStyleFormatReader::ST_Count_Short; +constexpr auto ST_Count_Int = llvm::PrintfStyleFormatReader::ST_Count_Int; +constexpr auto ST_Count_Long = llvm::PrintfStyleFormatReader::ST_Count_Long; +constexpr auto ST_Count_LongLong = + llvm::PrintfStyleFormatReader::ST_Count_LongLong; +constexpr auto ST_Count_IntMax = llvm::PrintfStyleFormatReader::ST_Count_IntMax; +constexpr auto ST_Count_Size = llvm::PrintfStyleFormatReader::ST_Count_Size; +constexpr auto ST_Count_Ptrdiff = + llvm::PrintfStyleFormatReader::ST_Count_Ptrdiff; + +using STVec = std::vector; + +STVec ParseFormatString(const char *Fmt) { + STVec Result; + PrintfStyleFormatReader Reader(Fmt); + while (auto Spec = Reader.NextSpecifier()) { + Result.push_back(Spec); + } + return Result; +} + +void ExpectEq(const char *Fmt, const STVec &RHS) { + EXPECT_EQ(ParseFormatString(Fmt), RHS); +} + +} // namespace + +TEST(FormatReader, EmptyFormatString) { + EXPECT_EQ(ParseFormatString(""), + std::vector()); +} + +TEST(FormatReader, PercentEscape) { + EXPECT_EQ(ParseFormatString("%%"), + std::vector()); +} + +TEST(FormatReader, PercentAtEnd) { ExpectEq("%", {ST_Unknown}); } + +TEST(FormatReader, PercentWithWidth) { ExpectEq("%ll%", {ST_Unknown}); } + +TEST(FormatReader, OneFormat) { + ExpectEq("%i xx", {ST_Int}); + ExpectEq("yy %i", {ST_Int}); + ExpectEq("yy %i xx", {ST_Int}); +} + +TEST(FormatReader, TwoFormats) { + ExpectEq("%i yy %f xx", {ST_Int, ST_Double}); + ExpectEq("zz %i yy %f", {ST_Int, ST_Double}); + ExpectEq("zz %i yy %f xx", {ST_Int, ST_Double}); +} + +TEST(FormatReader, PoundFlagValid) { + ExpectEq("%#x", {ST_Int}); + ExpectEq("%#X", {ST_Int}); + ExpectEq("%#a", {ST_Double}); + ExpectEq("%#A", {ST_Double}); + ExpectEq("%#e", {ST_Double}); + ExpectEq("%#E", {ST_Double}); + ExpectEq("%#f", {ST_Double}); + ExpectEq("%#F", {ST_Double}); + ExpectEq("%#g", {ST_Double}); + ExpectEq("%#G", {ST_Double}); + + ExpectEq("%#p", {ST_Unknown}); + ExpectEq("%#i", {ST_Unknown}); + ExpectEq("%#c", {ST_Unknown}); + ExpectEq("%#s", {ST_Unknown}); + ExpectEq("%#d", {ST_Unknown}); + ExpectEq("%#u", {ST_Unknown}); + ExpectEq("%#o", {ST_Unknown}); + ExpectEq("%#n", {ST_Unknown}); +} + +TEST(FormatReader, ZeroFlagValid) { + ExpectEq("%0x", {ST_Int}); + ExpectEq("%0X", {ST_Int}); + ExpectEq("%0i", {ST_Int}); + ExpectEq("%0d", {ST_Int}); + ExpectEq("%0u", {ST_Int}); + ExpectEq("%0o", {ST_Int}); + ExpectEq("%0a", {ST_Double}); + ExpectEq("%0A", {ST_Double}); + ExpectEq("%0e", {ST_Double}); + ExpectEq("%0E", {ST_Double}); + ExpectEq("%0f", {ST_Double}); + ExpectEq("%0F", {ST_Double}); + ExpectEq("%0g", {ST_Double}); + ExpectEq("%0G", {ST_Double}); + + ExpectEq("%0p", {ST_Unknown}); + ExpectEq("%0n", {ST_Unknown}); + ExpectEq("%0c", {ST_Unknown}); + ExpectEq("%0s", {ST_Unknown}); +} + +TEST(FormatReader, PrecisionValid) { + ExpectEq("%.1x", {ST_Int}); + ExpectEq("%.1X", {ST_Int}); + ExpectEq("%.1i", {ST_Int}); + ExpectEq("%.1d", {ST_Int}); + ExpectEq("%.1u", {ST_Int}); + ExpectEq("%.1o", {ST_Int}); + ExpectEq("%.1a", {ST_Double}); + ExpectEq("%.1A", {ST_Double}); + ExpectEq("%.1e", {ST_Double}); + ExpectEq("%.1E", {ST_Double}); + ExpectEq("%.1f", {ST_Double}); + ExpectEq("%.1F", {ST_Double}); + ExpectEq("%.1g", {ST_Double}); + ExpectEq("%.1G", {ST_Double}); + ExpectEq("%.1s", {ST_CString}); + + ExpectEq("%.1p", {ST_Unknown}); + ExpectEq("%.1n", {ST_Unknown}); + ExpectEq("%.1c", {ST_Unknown}); +} + +TEST(FormatReader, LongWidth) { + ExpectEq("%1li", {ST_Long}); + ExpectEq("%11li", {ST_Long}); + ExpectEq("%1111li", {ST_Long}); + ExpectEq("%10li", {ST_Long}); + ExpectEq("%*li", {ST_Int, ST_Long}); + ExpectEq("%*l!", {ST_Unknown}); +} + +TEST(FormatReader, LongPrecision) { + ExpectEq("%.1li", {ST_Long}); + ExpectEq("%.11li", {ST_Long}); + ExpectEq("%.1111li", {ST_Long}); + ExpectEq("%.10li", {ST_Long}); + ExpectEq("%.*li", {ST_Int, ST_Long}); + ExpectEq("%.*l!", {ST_Unknown}); + + ExpectEq("%1.1li", {ST_Long}); + ExpectEq("%11.11li", {ST_Long}); + ExpectEq("%111.1111li", {ST_Long}); + ExpectEq("%110.10li", {ST_Long}); + ExpectEq("%1.*li", {ST_Int, ST_Long}); + ExpectEq("%1.*l!", {ST_Unknown}); + + ExpectEq("%*.*li", {ST_Int, ST_Int, ST_Long}); + ExpectEq("%*.*l!", {ST_Unknown}); +} + +TEST(FormatReader, IntSpecifiers) { + ExpectEq("%hhi", {ST_Int}); + ExpectEq("%hhd", {ST_Int}); + ExpectEq("%hi", {ST_Int}); + ExpectEq("%hd", {ST_Int}); + ExpectEq("%i", {ST_Int}); + ExpectEq("%d", {ST_Int}); + ExpectEq("%li", {ST_Long}); + ExpectEq("%ld", {ST_Long}); + ExpectEq("%lli", {ST_LongLong}); + ExpectEq("%lld", {ST_LongLong}); + ExpectEq("%ji", {ST_IntMax}); + ExpectEq("%jd", {ST_IntMax}); + ExpectEq("%zi", {ST_Size}); + ExpectEq("%zd", {ST_Size}); + ExpectEq("%ti", {ST_Ptrdiff}); + ExpectEq("%td", {ST_Ptrdiff}); + + ExpectEq("%Li", {ST_Unknown}); + ExpectEq("%Ld", {ST_Unknown}); +} + +TEST(FormatReader, UIntSpecifiers) { + ExpectEq("%hhu", {ST_Int}); + ExpectEq("%hho", {ST_Int}); + ExpectEq("%hhx", {ST_Int}); + ExpectEq("%hhX", {ST_Int}); + ExpectEq("%hu", {ST_Int}); + ExpectEq("%ho", {ST_Int}); + ExpectEq("%hx", {ST_Int}); + ExpectEq("%hX", {ST_Int}); + ExpectEq("%u", {ST_Int}); + ExpectEq("%o", {ST_Int}); + ExpectEq("%x", {ST_Int}); + ExpectEq("%X", {ST_Int}); + ExpectEq("%lu", {ST_Long}); + ExpectEq("%lo", {ST_Long}); + ExpectEq("%lx", {ST_Long}); + ExpectEq("%lX", {ST_Long}); + ExpectEq("%llu", {ST_LongLong}); + ExpectEq("%llo", {ST_LongLong}); + ExpectEq("%llx", {ST_LongLong}); + ExpectEq("%llX", {ST_LongLong}); + ExpectEq("%ju", {ST_IntMax}); + ExpectEq("%jo", {ST_IntMax}); + ExpectEq("%jx", {ST_IntMax}); + ExpectEq("%jX", {ST_IntMax}); + ExpectEq("%zu", {ST_Size}); + ExpectEq("%zo", {ST_Size}); + ExpectEq("%zx", {ST_Size}); + ExpectEq("%zX", {ST_Size}); + ExpectEq("%tu", {ST_Ptrdiff}); + ExpectEq("%to", {ST_Ptrdiff}); + ExpectEq("%tx", {ST_Ptrdiff}); + ExpectEq("%tX", {ST_Ptrdiff}); + + ExpectEq("%Lu", {ST_Unknown}); + ExpectEq("%Lo", {ST_Unknown}); + ExpectEq("%Lx", {ST_Unknown}); + ExpectEq("%LX", {ST_Unknown}); +} + +TEST(FormatReader, FloatSpecifiers) { + ExpectEq("%a", {ST_Double}); + ExpectEq("%e", {ST_Double}); + ExpectEq("%f", {ST_Double}); + ExpectEq("%g", {ST_Double}); + ExpectEq("%la", {ST_Double}); + ExpectEq("%le", {ST_Double}); + ExpectEq("%lf", {ST_Double}); + ExpectEq("%lg", {ST_Double}); + + ExpectEq("%La", {ST_LongDouble}); + ExpectEq("%Le", {ST_LongDouble}); + ExpectEq("%Lf", {ST_LongDouble}); + ExpectEq("%Lg", {ST_LongDouble}); + + ExpectEq("%ha", {ST_Unknown}); + ExpectEq("%he", {ST_Unknown}); + ExpectEq("%hf", {ST_Unknown}); + ExpectEq("%hg", {ST_Unknown}); + ExpectEq("%hha", {ST_Unknown}); + ExpectEq("%hhe", {ST_Unknown}); + ExpectEq("%hhf", {ST_Unknown}); + ExpectEq("%hhg", {ST_Unknown}); + ExpectEq("%lla", {ST_Unknown}); + ExpectEq("%lle", {ST_Unknown}); + ExpectEq("%llf", {ST_Unknown}); + ExpectEq("%llg", {ST_Unknown}); +} + +TEST(FormatReader, CharSpecifiers) { + ExpectEq("%hhc", {ST_Unknown}); + ExpectEq("%hc", {ST_Unknown}); + ExpectEq("%c", {ST_Int}); + ExpectEq("%lc", {ST_WideChar}); + ExpectEq("%llc", {ST_Unknown}); + ExpectEq("%jc", {ST_Unknown}); + ExpectEq("%zc", {ST_Unknown}); + ExpectEq("%tc", {ST_Unknown}); + ExpectEq("%Lc", {ST_Unknown}); +} + +TEST(FormatReader, StringSpecifiers) { + ExpectEq("%hhs", {ST_Unknown}); + ExpectEq("%hs", {ST_Unknown}); + ExpectEq("%s", {ST_CString}); + ExpectEq("%ls", {ST_WideCString}); + ExpectEq("%lls", {ST_Unknown}); + ExpectEq("%js", {ST_Unknown}); + ExpectEq("%zs", {ST_Unknown}); + ExpectEq("%ts", {ST_Unknown}); + ExpectEq("%Ls", {ST_Unknown}); +} + +TEST(FormatReader, VoidPointerSpecifiers) { + ExpectEq("%hhp", {ST_Unknown}); + ExpectEq("%hp", {ST_Unknown}); + ExpectEq("%p", {ST_VoidPointer}); + ExpectEq("%lp", {ST_Unknown}); + ExpectEq("%llp", {ST_Unknown}); + ExpectEq("%jp", {ST_Unknown}); + ExpectEq("%zp", {ST_Unknown}); + ExpectEq("%tp", {ST_Unknown}); + ExpectEq("%Lp", {ST_Unknown}); +} + +TEST(FormatReader, CountSpecifiers) { + ExpectEq("%hhn", {ST_Count_Char}); + ExpectEq("%hn", {ST_Count_Short}); + ExpectEq("%n", {ST_Count_Int}); + ExpectEq("%ln", {ST_Count_Long}); + ExpectEq("%lln", {ST_Count_LongLong}); + ExpectEq("%jn", {ST_Count_IntMax}); + ExpectEq("%zn", {ST_Count_Size}); + ExpectEq("%tn", {ST_Count_Ptrdiff}); + ExpectEq("%Ln", {ST_Unknown}); +} diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDag.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchDag.cpp --- a/llvm/utils/TableGen/GlobalISel/GIMatchDag.cpp +++ b/llvm/utils/TableGen/GlobalISel/GIMatchDag.cpp @@ -61,10 +61,11 @@ const char *ToFmt = "Node%p:d%d:s"; if (E->getFromMO()->isDef() && !E->getToMO()->isDef()) std::swap(FromFmt, ToFmt); - auto From = format(FromFmt, E->getFromMI(), E->getFromMO()->getIdx()); - auto To = format(ToFmt, E->getToMI(), E->getToMO()->getIdx()); - if (E->getFromMO()->isDef() && !E->getToMO()->isDef()) - std::swap(From, To); + auto FromF = format(FromFmt, E->getFromMI(), E->getFromMO()->getIdx()); + auto ToF = format(ToFmt, E->getToMI(), E->getToMO()->getIdx()); + bool Swap = E->getFromMO()->isDef() && !E->getToMO()->isDef(); + auto &From = Swap ? ToF : FromF; + auto &To = Swap ? FromF : ToF; OS << " " << From << " -> " << To << " [label=\"$" << E->getName(); if (E->getFromMO()->isDef() == E->getToMO()->isDef())