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,68 @@ 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 { + ST_EndOfFormatString, + ST_Unknown, + ST_WideChar, + ST_Int, + ST_UInt, + ST_Long, + ST_ULong, + ST_LongLong, + ST_ULongLong, + ST_IntMax, + ST_UIntMax, + 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; + bool SignSensitive; + + 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. + static const char *EnsureCompatible(const char *Expected, const char *Fmt); + + PrintfStyleFormatReader(const char *Fmt, bool SignSensitive = false) + : Fmt(Fmt), SignSensitive(SignSensitive) {} + + 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 +103,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 +143,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); } }; @@ -125,6 +181,14 @@ return format_object(Fmt, Vals...); } +template +format_object format_chk(const char *Expected, const char *Fmt, + const Ts &...Vals) { + const char *const Verified = + PrintfStyleFormatReader::EnsureCompatible(Expected, Fmt); + return format(Verified, Vals...); +} + /// This is a helper class for left_justify, right_justify, and center_justify. class FormattedString { public: 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 @@ -163,6 +163,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,335 @@ +//===-- Support/FoldingSet.cpp - Uniquing Hash Set --------------*- 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 ArgLength { + AL_ShortShort, + AL_Short, + AL_Default, + AL_Long, + AL_LongLong, + AL_IntMax, + AL_Size, + AL_Ptrdiff, + AL_LongDouble, + AL_End, +}; + +enum SpecifierChar { + SC_Int, + SC_UInt, + SC_Float, + SC_Char, + SC_String, + SC_VoidPointer, + SC_Count, + SC_End, +}; + +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_UInt = llvm::PrintfStyleFormatReader::ST_UInt; +constexpr auto ST_Long = llvm::PrintfStyleFormatReader::ST_Long; +constexpr auto ST_ULong = llvm::PrintfStyleFormatReader::ST_ULong; +constexpr auto ST_LongLong = llvm::PrintfStyleFormatReader::ST_LongLong; +constexpr auto ST_ULongLong = llvm::PrintfStyleFormatReader::ST_ULongLong; +constexpr auto ST_IntMax = llvm::PrintfStyleFormatReader::ST_IntMax; +constexpr auto ST_UIntMax = llvm::PrintfStyleFormatReader::ST_UIntMax; +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_UInt + ST_UInt, + ST_UInt, + ST_UInt, + ST_ULong, + ST_ULongLong, + ST_UIntMax, + ST_Size, + ST_Ptrdiff, + ST_Unknown, + }, + { + // SC_Float + ST_Unknown, + ST_Unknown, + ST_Double, + ST_Unknown, + 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, + }, +}; + +const char *next(const char *str, char c) { + while (*str) { + if (*str == c) + break; + str++; + } + return str; +} +} // namespace + +namespace llvm { + +void PrintfStyleFormatReader::RefillSpecifierQueue() { + Fmt = next(Fmt, '%'); + if (*Fmt == '\0') { + 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; + + // skip flags + StringRef FlagChars = "+- #0"; + while (FlagChars.find(*Fmt) != llvm::StringRef::npos) + Fmt++; + + // skip width + if (*Fmt == '*') { + Specifiers.push_back(ST_Int); + Fmt++; + } else + while (*Fmt >= '0' && *Fmt <= '9') + Fmt++; + + // skip precision + if (*Fmt == '.') { + 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 + SpecifierChar SC; + switch (*Fmt++) { + case 'd': + case 'i': + SC = SC_Int; + break; + + case 'u': + case 'o': + case 'x': + case 'X': + SC = SC_UInt; + 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; + if (!SignSensitive) { + switch (Spec) { + case ST_UInt: + Spec = ST_Int; + break; + case ST_ULong: + Spec = ST_Long; + break; + case ST_ULongLong: + Spec = ST_LongLong; + break; + case ST_UIntMax: + Spec = ST_IntMax; + break; + default: + break; + } + } + + 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); + while (SpecifierType ST = FFR.NextSpecifier()) + if (ST == ST_Unknown || ST != EFR.NextSpecifier()) + return Expected; + 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 @@ -220,7 +220,7 @@ break; std::string Name; raw_string_ostream OS(Name); - OS << format(Format.c_str(), unsigned(From)); + OS << format_chk("", Format.c_str(), unsigned(From)); Record *Rec = Records.getDef(OS.str()); if (!Rec) PrintFatalError(Loc, "No def named '" + Name + "': " + 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_UInt = llvm::PrintfStyleFormatReader::ST_UInt; +constexpr auto ST_Long = llvm::PrintfStyleFormatReader::ST_Long; +constexpr auto ST_ULong = llvm::PrintfStyleFormatReader::ST_ULong; +constexpr auto ST_LongLong = llvm::PrintfStyleFormatReader::ST_LongLong; +constexpr auto ST_ULongLong = llvm::PrintfStyleFormatReader::ST_ULongLong; +constexpr auto ST_IntMax = llvm::PrintfStyleFormatReader::ST_IntMax; +constexpr auto ST_UIntMax = llvm::PrintfStyleFormatReader::ST_UIntMax; +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, bool SignSensitive = true) { + STVec Result; + PrintfStyleFormatReader Reader(Fmt, SignSensitive); + while (auto Spec = Reader.NextSpecifier()) { + Result.push_back(Spec); + } + return Result; +} + +void ExpectEq(const char *Fmt, const STVec &RHS) { + EXPECT_EQ(ParseFormatString(Fmt), RHS); +} + +void ExpectEq(const char *Fmt, bool SignSensitive, const STVec &RHS) { + EXPECT_EQ(ParseFormatString(Fmt, SignSensitive), 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, IntFlags) { + ExpectEq("% i", {ST_Int}); + ExpectEq("%+i", {ST_Int}); + ExpectEq("%-i", {ST_Int}); + ExpectEq("%#i", {ST_Int}); + ExpectEq("%0i", {ST_Int}); +} + +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", false, {ST_Int}); + ExpectEq("%hhi", true, {ST_Int}); + ExpectEq("%hhd", false, {ST_Int}); + ExpectEq("%hhd", true, {ST_Int}); + ExpectEq("%hi", false, {ST_Int}); + ExpectEq("%hi", true, {ST_Int}); + ExpectEq("%hd", false, {ST_Int}); + ExpectEq("%hd", true, {ST_Int}); + ExpectEq("%i", false, {ST_Int}); + ExpectEq("%i", true, {ST_Int}); + ExpectEq("%d", false, {ST_Int}); + ExpectEq("%d", true, {ST_Int}); + ExpectEq("%li", false, {ST_Long}); + ExpectEq("%li", true, {ST_Long}); + ExpectEq("%ld", false, {ST_Long}); + ExpectEq("%ld", true, {ST_Long}); + ExpectEq("%lli", false, {ST_LongLong}); + ExpectEq("%lli", true, {ST_LongLong}); + ExpectEq("%lld", false, {ST_LongLong}); + ExpectEq("%lld", true, {ST_LongLong}); + ExpectEq("%ji", false, {ST_IntMax}); + ExpectEq("%ji", true, {ST_IntMax}); + ExpectEq("%jd", false, {ST_IntMax}); + ExpectEq("%jd", true, {ST_IntMax}); + ExpectEq("%zi", false, {ST_Size}); + ExpectEq("%zi", true, {ST_Size}); + ExpectEq("%zd", false, {ST_Size}); + ExpectEq("%zd", true, {ST_Size}); + ExpectEq("%ti", false, {ST_Ptrdiff}); + ExpectEq("%ti", true, {ST_Ptrdiff}); + ExpectEq("%td", false, {ST_Ptrdiff}); + ExpectEq("%td", true, {ST_Ptrdiff}); + + ExpectEq("%Li", false, {ST_Unknown}); + ExpectEq("%Li", true, {ST_Unknown}); + ExpectEq("%Ld", false, {ST_Unknown}); + ExpectEq("%Ld", true, {ST_Unknown}); +} + +TEST(FormatReader, UIntSpecifiers) { + ExpectEq("%hhu", false, {ST_Int}); + ExpectEq("%hhu", true, {ST_UInt}); + ExpectEq("%hho", false, {ST_Int}); + ExpectEq("%hho", true, {ST_UInt}); + ExpectEq("%hhx", false, {ST_Int}); + ExpectEq("%hhx", true, {ST_UInt}); + ExpectEq("%hhX", false, {ST_Int}); + ExpectEq("%hhX", true, {ST_UInt}); + ExpectEq("%hu", false, {ST_Int}); + ExpectEq("%hu", true, {ST_UInt}); + ExpectEq("%ho", false, {ST_Int}); + ExpectEq("%ho", true, {ST_UInt}); + ExpectEq("%hx", false, {ST_Int}); + ExpectEq("%hx", true, {ST_UInt}); + ExpectEq("%hX", false, {ST_Int}); + ExpectEq("%hX", true, {ST_UInt}); + ExpectEq("%u", false, {ST_Int}); + ExpectEq("%u", true, {ST_UInt}); + ExpectEq("%o", false, {ST_Int}); + ExpectEq("%o", true, {ST_UInt}); + ExpectEq("%x", false, {ST_Int}); + ExpectEq("%x", true, {ST_UInt}); + ExpectEq("%X", false, {ST_Int}); + ExpectEq("%X", true, {ST_UInt}); + ExpectEq("%lu", false, {ST_Long}); + ExpectEq("%lu", true, {ST_ULong}); + ExpectEq("%lo", false, {ST_Long}); + ExpectEq("%lo", true, {ST_ULong}); + ExpectEq("%lx", false, {ST_Long}); + ExpectEq("%lx", true, {ST_ULong}); + ExpectEq("%lX", false, {ST_Long}); + ExpectEq("%lX", true, {ST_ULong}); + ExpectEq("%llu", false, {ST_LongLong}); + ExpectEq("%llu", true, {ST_ULongLong}); + ExpectEq("%llo", false, {ST_LongLong}); + ExpectEq("%llo", true, {ST_ULongLong}); + ExpectEq("%llx", false, {ST_LongLong}); + ExpectEq("%llx", true, {ST_ULongLong}); + ExpectEq("%llX", false, {ST_LongLong}); + ExpectEq("%llX", true, {ST_ULongLong}); + ExpectEq("%ju", false, {ST_IntMax}); + ExpectEq("%ju", true, {ST_UIntMax}); + ExpectEq("%jo", false, {ST_IntMax}); + ExpectEq("%jo", true, {ST_UIntMax}); + ExpectEq("%jx", false, {ST_IntMax}); + ExpectEq("%jx", true, {ST_UIntMax}); + ExpectEq("%jX", false, {ST_IntMax}); + ExpectEq("%jX", true, {ST_UIntMax}); + ExpectEq("%zu", false, {ST_Size}); + ExpectEq("%zu", true, {ST_Size}); + ExpectEq("%zo", false, {ST_Size}); + ExpectEq("%zo", true, {ST_Size}); + ExpectEq("%zx", false, {ST_Size}); + ExpectEq("%zx", true, {ST_Size}); + ExpectEq("%zX", false, {ST_Size}); + ExpectEq("%zX", true, {ST_Size}); + ExpectEq("%tu", false, {ST_Ptrdiff}); + ExpectEq("%tu", true, {ST_Ptrdiff}); + ExpectEq("%to", false, {ST_Ptrdiff}); + ExpectEq("%to", true, {ST_Ptrdiff}); + ExpectEq("%tx", false, {ST_Ptrdiff}); + ExpectEq("%tx", true, {ST_Ptrdiff}); + ExpectEq("%tX", false, {ST_Ptrdiff}); + ExpectEq("%tX", true, {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_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("%la", {ST_Unknown}); + ExpectEq("%le", {ST_Unknown}); + ExpectEq("%lf", {ST_Unknown}); + ExpectEq("%lg", {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())